From 4fb101c5db8862fb69943159b3802c99df9de507 Mon Sep 17 00:00:00 2001 From: Viktoria Polyakova Date: Sun, 25 Jan 2026 08:57:38 +0000 Subject: [PATCH] Init --- .gitignore | 1 + backend/.eslintrc.js.old | 30 + backend/.gitignore | 45 + backend/.gitlab-ci.yml | 145 + backend/.prettierrc | 4 + backend/.yarn/releases/yarn-4.9.1.cjs | 948 + backend/.yarnrc.yml | 5 + backend/README.md | 26 + .../http-client.env.json.dist | 104 + backend/artifacts/_resources/test.png | Bin 0 -> 98415 bytes backend/artifacts/account-settings.http | 5 + backend/artifacts/auth.http | 92 + .../automation/activity-automation.http | 93 + backend/artifacts/automation/automation.http | 11 + .../automation/change-stage-automation.http | 47 + .../automation/email-automation.http | 63 + .../artifacts/automation/task-automation.http | 57 + backend/artifacts/board.http | 35 + backend/artifacts/constructor.http | 72 + backend/artifacts/crm.http | 10 + backend/artifacts/demo.http | 5 + backend/artifacts/entity-type.http | 76 + backend/artifacts/entity.http | 108 + backend/artifacts/extension.http | 15 + backend/artifacts/feed-items.http | 5 + backend/artifacts/feedback.http | 17 + backend/artifacts/field-settings.http | 18 + backend/artifacts/field-value.http | 14 + backend/artifacts/fields.http | 48 + backend/artifacts/file-link.http | 5 + backend/artifacts/forms.http | 31 + backend/artifacts/identity.http | 17 + backend/artifacts/import.http | 5 + backend/artifacts/mail-messages.http | 30 + backend/artifacts/mailbox-settings.http | 76 + backend/artifacts/mailbox.http | 5 + backend/artifacts/migration.http | 5 + backend/artifacts/module/module.http | 17 + backend/artifacts/note.http | 27 + backend/artifacts/notification.http | 23 + backend/artifacts/partner.http | 11 + backend/artifacts/products/order-status.http | 5 + backend/artifacts/products/order.http | 92 + .../artifacts/products/product-category.http | 32 + backend/artifacts/products/product-price.http | 29 + backend/artifacts/products/product.http | 86 + .../artifacts/products/shipment-status.http | 5 + backend/artifacts/products/shipment.http | 17 + backend/artifacts/products/stock.http | 18 + backend/artifacts/products/warehouse.http | 31 + backend/artifacts/rabbit.http | 5 + backend/artifacts/rms.http | 10 + backend/artifacts/stage.http | 47 + backend/artifacts/storage/file-link.http | 12 + backend/artifacts/storage/file.http | 24 + backend/artifacts/subtasks.http | 27 + backend/artifacts/task-comment-like.http | 11 + backend/artifacts/task-comment.http | 33 + backend/artifacts/task-settings.http | 39 + backend/artifacts/task-type.http | 21 + backend/artifacts/task.http | 134 + backend/artifacts/user.http | 73 + backend/doc/camunda.md | 32 + backend/doc/commands.md | 11 + backend/doc/setup-local.md | 56 + backend/eslint.config.js | 35 + backend/knip.json | 4 + backend/nest-cli.json | 11 + backend/newrelic.js | 53 + backend/package-lock.json | 18536 ++++++++++++ backend/package.json | 135 + .../Board/Filter/EntityBoardCardFilter.ts | 57 + .../Entity/Board/Filter/EntityFieldFilter.ts | 10 + .../Entity/Board/Filter/EntitySorting.ts | 7 + .../Board/Filter/entity-task-filter.enum.ts | 7 + .../Entity/Board/entity-board.controller.ts | 86 + .../Entity/CreateEntityController.ts | 26 + .../Entity/CreateSimpleEntityController.ts | 26 + .../Entity/DeleteEntityController.ts | 20 + .../Documents/GetEntityDocumentsController.ts | 25 + .../Entity/Files/AddEntityFilesController.ts | 28 + .../Entity/Files/GetEntityFilesController.ts | 25 + .../Controller/Entity/GetEntityController.ts | 40 + .../GetEntitiesImportTemplateController.ts | 31 + .../Import/UploadEntitiesImportController.ts | 45 + .../Entity/List/entity-list.controller.ts | 97 + .../Entity/UpdateEntityController.ts | 27 + .../Entity/UpdateEntityFieldController.ts | 27 + .../CreateExternalLinkController.ts | 26 + .../FileLink/DeleteFileLinkController.ts | 20 + .../FileLink/DeleteFileLinksController.ts | 22 + .../GetTimeBoardCalendarController.ts | 29 + .../GetTimeBoardCalendarMetaController.ts | 29 + .../TimeBoard/GetTimeBoardController.ts | 28 + .../TimeBoard/GetTimeBoardItemController.ts | 31 + .../TimeBoard/GetTimeBoardMetaController.ts | 26 + backend/src/CRM/Model/Entity/Entity.ts | 177 + .../Model/ExternalEntity/ExternalEntity.ts | 49 + .../Model/ExternalEntity/ExternalSystem.ts | 16 + .../ExternalEntity/ExternalSystemCode.ts | 3 + .../CRM/Model/ExternalEntity/UIDataRecord.ts | 12 + backend/src/CRM/Model/FileLink/FileLink.ts | 64 + .../Controller/Auth/AuthCallbackController.ts | 24 + .../Controller/Auth/AuthConnectController.ts | 20 + .../Auth/AuthDisconnectController.ts | 20 + .../Settings/CreateSettingsController.ts | 27 + .../Settings/DeleteSettingsController.ts | 20 + .../Settings/GetSettingsController.ts | 23 + .../CRM/Salesforce/Model/IntegrationData.ts | 11 + .../Model/Settings/SalesforceSettings.ts | 35 + .../src/CRM/Salesforce/SalesforceModule.ts | 29 + .../Service/SalesforceIntegrationService.ts | 222 + .../Settings/CreateSalesforceSettingsDto.ts | 16 + .../Service/Settings/SalesforceSettingsDto.ts | 27 + .../Settings/SalesforceSettingsService.ts | 26 + .../Salesforce/Util/SalesforceCodeToType.ts | 34 + .../CRM/Salesforce/Util/SalesforceUIFields.ts | 36 + .../BaseTaskBoard/BaseTaskBoardFilter.ts | 58 + .../BaseTaskBoard/TaskBoardQueryHelper.ts | 106 + .../CRM/Service/BaseTaskBoard/TaskSorting.ts | 5 + .../BaseTaskBoard/UserTimeAllocation.ts | 14 + .../Entity/CommonEntityBoardCardService.ts | 267 + .../Batch/delete-entities-batch-filter.dto.ts | 12 + .../src/CRM/Service/Entity/Dto/Batch/index.ts | 2 + .../Batch/update-entities-batch-filter.dto.ts | 33 + .../Entity/Dto/Board/CommonEntityCard.ts | 107 + .../Entity/Dto/Board/EntityBoardCard.ts | 59 + .../Entity/Dto/Board/EntityBoardMeta.ts | 17 + .../Entity/Dto/Board/EntityBoardStageMeta.ts | 12 + .../Entity/Dto/Board/ProjectEntityCard.ts | 125 + .../Service/Entity/Dto/Board/TasksCount.ts | 22 + .../src/CRM/Service/Entity/Dto/Board/index.ts | 6 + .../CRM/Service/Entity/Dto/CreateEntityDto.ts | 48 + .../Entity/Dto/CreateSimpleEntityDto.ts | 51 + .../src/CRM/Service/Entity/Dto/EntityDto.ts | 210 + .../CRM/Service/Entity/Dto/EntitySimpleDto.ts | 15 + .../Entity/Dto/Files/AddEntityFilesDto.ts | 6 + .../src/CRM/Service/Entity/Dto/Files/index.ts | 1 + .../Service/Entity/Dto/List/EntityListItem.ts | 120 + .../Service/Entity/Dto/List/EntityListMeta.ts | 12 + .../src/CRM/Service/Entity/Dto/List/index.ts | 2 + .../CRM/Service/Entity/Dto/UpdateEntityDto.ts | 49 + backend/src/CRM/Service/Entity/Dto/index.ts | 9 + .../CRM/Service/Entity/EntityBoardService.ts | 874 + .../src/CRM/Service/Entity/EntityService.ts | 1212 + .../Entity/ProjectEntityBoardCardService.ts | 151 + .../Service/Entity/entity-service.emitter.ts | 238 + .../Service/Entity/entity-service.handler.ts | 251 + .../Entity/enums/task-indicator.enum.ts | 6 + .../src/CRM/Service/Entity/errors/index.ts | 2 + .../errors/readonly-field-changed.error.ts | 9 + .../errors/required-field-empty.error.ts | 9 + .../ExternalEntity/CreateExternalEntityDto.ts | 11 + .../CreateExternalEntityResult.ts | 10 + .../ExternalEntity/ExternalEntityDto.ts | 47 + .../ExternalEntity/ExternalEntityService.ts | 65 + .../ExternalEntity/ExternalSystemDto.ts | 24 + .../ExternalEntity/ExternalSystemService.ts | 23 + .../src/CRM/Service/FileLink/FileLinkDto.ts | 67 + .../CRM/Service/FileLink/FileLinkService.ts | 163 + .../src/CRM/Service/Import/ImportService.ts | 470 + .../CRM/Service/TimeBoard/TaskGroupByTime.ts | 8 + .../Service/TimeBoard/TaskOrActivityCard.ts | 4 + .../TimeBoard/TimeBoardCalendarMeta.ts | 10 + .../CRM/Service/TimeBoard/TimeBoardFilter.ts | 12 + .../CRM/Service/TimeBoard/TimeBoardMeta.ts | 29 + .../CRM/Service/TimeBoard/TimeBoardService.ts | 627 + .../Service/TimeBoard/TimeBoardStageMeta.ts | 16 + .../activity-card/activity-card.controller.ts | 66 + .../activity-card/activity-card.service.ts | 244 + .../dto/activity-calendar-meta.dto.ts | 12 + .../dto/activity-card-filter.dto.ts | 11 + .../dto/activity-card-meta.dto.ts | 20 + .../dto/activity-card-type-meta.dto.ts | 14 + .../activity-card/dto/activity-card.dto.ts | 19 + backend/src/CRM/activity-card/dto/index.ts | 5 + backend/src/CRM/activity-card/index.ts | 3 + .../activity-type/activity-type.controller.ts | 54 + .../activity-type/activity-type.service.ts | 102 + .../activity-type/dto/activity-type.dto.ts | 20 + .../dto/create-activity-type.dto.ts | 4 + backend/src/CRM/activity-type/dto/index.ts | 3 + .../dto/update-activity-type.dto.ts | 4 + .../entities/activity-type.entity.ts | 39 + .../src/CRM/activity-type/entities/index.ts | 1 + backend/src/CRM/activity-type/index.ts | 4 + .../src/CRM/activity/activity.controller.ts | 38 + backend/src/CRM/activity/activity.handler.ts | 71 + backend/src/CRM/activity/activity.service.ts | 325 + backend/src/CRM/activity/dto/activity.dto.ts | 23 + .../CRM/activity/dto/create-activity.dto.ts | 23 + backend/src/CRM/activity/dto/index.ts | 3 + .../CRM/activity/dto/update-activity.dto.ts | 26 + .../CRM/activity/entities/activity.entity.ts | 107 + backend/src/CRM/activity/entities/index.ts | 1 + backend/src/CRM/activity/index.ts | 5 + .../src/CRM/base-task/base-task.service.ts | 64 + .../src/CRM/base-task/dto/base-task.dto.ts | 70 + .../CRM/base-task/dto/create-base-task.dto.ts | 48 + backend/src/CRM/base-task/dto/index.ts | 3 + .../CRM/base-task/dto/update-base-task.dto.ts | 35 + .../base-task/entities/base-task.entity.ts | 89 + backend/src/CRM/base-task/entities/index.ts | 1 + backend/src/CRM/base-task/enums/index.ts | 1 + .../src/CRM/base-task/enums/task-view.enum.ts | 4 + backend/src/CRM/base-task/index.ts | 4 + .../CRM/board-stage/board-stage.controller.ts | 120 + .../CRM/board-stage/board-stage.service.ts | 331 + .../CRM/board-stage/dto/board-stage.dto.ts | 35 + .../board-stage/dto/create-board-stage.dto.ts | 21 + backend/src/CRM/board-stage/dto/index.ts | 5 + .../dto/process-board-stage.dto.ts | 18 + .../board-stage/dto/update-board-stage.dto.ts | 12 + .../dto/update-board-stages.dto.ts | 10 + .../entities/board-stage.entity.ts | 91 + backend/src/CRM/board-stage/entities/index.ts | 1 + .../enums/board-stage-code.enum.ts | 24 + .../enums/board-stage-type.enum.ts | 6 + backend/src/CRM/board-stage/enums/index.ts | 2 + backend/src/CRM/board-stage/index.ts | 6 + .../CRM/board-stage/types/grouped-stages.ts | 3 + backend/src/CRM/board-stage/types/index.ts | 1 + backend/src/CRM/board/board.controller.ts | 60 + backend/src/CRM/board/board.handler.ts | 25 + backend/src/CRM/board/board.service.ts | 428 + backend/src/CRM/board/dto/board-filter.dto.ts | 16 + backend/src/CRM/board/dto/board.dto.ts | 52 + backend/src/CRM/board/dto/create-board.dto.ts | 11 + backend/src/CRM/board/dto/index.ts | 4 + backend/src/CRM/board/dto/update-board.dto.ts | 5 + .../src/CRM/board/entities/board.entity.ts | 123 + backend/src/CRM/board/entities/index.ts | 1 + .../src/CRM/board/enums/board-type.enum.ts | 4 + backend/src/CRM/board/enums/index.ts | 1 + backend/src/CRM/board/index.ts | 3 + .../CRM/common/enums/entity-category.enum.ts | 15 + backend/src/CRM/common/enums/index.ts | 4 + .../enums/permission-object-type.enum.ts | 5 + .../CRM/common/enums/sequence-name.enum.ts | 9 + .../src/CRM/common/enums/sort-order.enum.ts | 4 + .../events/activity/activity-created.event.ts | 28 + .../common/events/activity/activity.event.ts | 11 + .../src/CRM/common/events/activity/index.ts | 2 + .../board-stage/board-stage-deleted.event.ts | 11 + .../events/board-stage/board-stage.event.ts | 11 + .../CRM/common/events/board-stage/index.ts | 2 + .../CRM/common/events/board/board.event.ts | 11 + backend/src/CRM/common/events/board/index.ts | 1 + .../CRM/common/events/crm-event-type.enum.ts | 29 + .../events/entity-type/entity-type.event.ts | 11 + .../CRM/common/events/entity-type/index.ts | 1 + .../events/entity/entity-created.event.ts | 29 + .../events/entity/entity-import.event.ts | 17 + .../entity/entity-owner-changed.event.ts | 25 + .../entity/entity-price.update.event.ts | 11 + .../CRM/common/events/entity/entity.event.ts | 29 + backend/src/CRM/common/events/entity/index.ts | 5 + .../file-link/file-link-created.event.ts | 11 + .../events/file-link/file-link.event.ts | 15 + .../src/CRM/common/events/file-link/index.ts | 2 + backend/src/CRM/common/events/index.ts | 9 + backend/src/CRM/common/events/note/index.ts | 2 + .../common/events/note/note-created.event.ts | 18 + .../src/CRM/common/events/note/note.event.ts | 11 + backend/src/CRM/common/events/task/index.ts | 6 + .../events/task/task-comment-created.event.ts | 11 + .../common/events/task/task-created.event.ts | 23 + .../events/task/task-ext-upsert.event.ts | 19 + .../CRM/common/events/task/task-ext.event.ts | 17 + .../common/events/task/task-updated.event.ts | 11 + .../src/CRM/common/events/task/task.event.ts | 19 + backend/src/CRM/common/index.ts | 2 + backend/src/CRM/crm.module.ts | 216 + .../CRM/entity-link/dto/entity-link.dto.ts | 22 + backend/src/CRM/entity-link/dto/index.ts | 1 + .../entities/entity-link.entity.ts | 36 + backend/src/CRM/entity-link/entities/index.ts | 1 + .../CRM/entity-link/entity-link.service.ts | 151 + .../dto/entity-search-filter.dto.ts | 38 + .../dto/entity-search-for-call-result.dto.ts | 14 + .../dto/entity-search-full-result.dto.ts | 12 + .../dto/entity-search-result.dto.ts | 12 + backend/src/CRM/entity-search/dto/index.ts | 4 + .../entity-search/entity-search.controller.ts | 60 + .../entity-search/entity-search.service.ts | 258 + backend/src/CRM/entity-search/index.ts | 3 + .../dto/create-entity-type-link.dto.ts | 10 + .../dto/entity-type-link.dto.ts | 16 + backend/src/CRM/entity-type-link/dto/index.ts | 3 + .../dto/update-entity-type-link.dto.ts | 4 + .../entities/entity-type-link.entity.ts | 41 + .../CRM/entity-type-link/entities/index.ts | 1 + .../entity-type-link.service.ts | 87 + .../entity-type/dto/create-entity-type.dto.ts | 68 + .../dto/entity-type-section.dto.ts | 18 + .../CRM/entity-type/dto/entity-type.dto.ts | 91 + backend/src/CRM/entity-type/dto/index.ts | 5 + .../dto/update-entity-type-fields.dto.ts | 15 + .../entity-type/dto/update-entity-type.dto.ts | 71 + .../entities/entity-type.entity.ts | 85 + backend/src/CRM/entity-type/entities/index.ts | 1 + .../CRM/entity-type/entity-type.controller.ts | 90 + .../CRM/entity-type/entity-type.service.ts | 403 + backend/src/CRM/entity-type/enums/index.ts | 1 + .../entity-type/enums/section-view.enum.ts | 5 + .../entity-type-used-in-formula.error.ts | 12 + backend/src/CRM/entity-type/errors/index.ts | 1 + backend/src/CRM/entity-type/index.ts | 4 + backend/src/CRM/feature/dto/feature.dto.ts | 27 + backend/src/CRM/feature/dto/index.ts | 1 + .../entities/entity-type-feature.entity.ts | 19 + .../CRM/feature/entities/feature.entity.ts | 29 + backend/src/CRM/feature/entities/index.ts | 2 + .../feature/entity-type-feature.service.ts | 68 + .../CRM/feature/enums/feature-code.enum.ts | 9 + backend/src/CRM/feature/enums/index.ts | 1 + backend/src/CRM/feature/feature.controller.ts | 23 + backend/src/CRM/feature/feature.service.ts | 37 + backend/src/CRM/feature/index.ts | 6 + .../src/CRM/identity/dto/identity-pool.dto.ts | 14 + backend/src/CRM/identity/dto/index.ts | 1 + .../src/CRM/identity/identity.controller.ts | 32 + backend/src/CRM/identity/identity.service.ts | 32 + .../src/CRM/identity/types/identity-pool.ts | 16 + backend/src/CRM/identity/types/index.ts | 1 + backend/src/CRM/note/dto/create-note.dto.ts | 10 + backend/src/CRM/note/dto/index.ts | 3 + backend/src/CRM/note/dto/note.dto.ts | 30 + backend/src/CRM/note/dto/update-note.dto.ts | 10 + backend/src/CRM/note/entities/index.ts | 1 + backend/src/CRM/note/entities/note.entity.ts | 52 + backend/src/CRM/note/index.ts | 5 + backend/src/CRM/note/note.controller.ts | 96 + backend/src/CRM/note/note.handler.ts | 21 + backend/src/CRM/note/note.service.ts | 206 + .../src/CRM/reporting/common/enums/index.ts | 2 + .../common/enums/report-user-type.enum.ts | 4 + .../common/enums/sales-pipeline-type.enum.ts | 16 + .../helpers/entity-query-builder.helper.ts | 65 + .../src/CRM/reporting/common/helpers/index.ts | 1 + backend/src/CRM/reporting/common/index.ts | 3 + .../src/CRM/reporting/common/types/index.ts | 2 + .../common/types/owner-date-value.ts | 5 + .../common/types/report-row-owner.ts | 1 + .../comparative-report.controller.ts | 27 + .../comparative/comparative-report.service.ts | 360 + .../dto/comparative-report-cell.dto.ts | 22 + .../dto/comparative-report-filter.dto.ts | 39 + .../dto/comparative-report-row.dto.ts | 14 + .../dto/comparative-report-value.dto.ts | 14 + .../comparative/dto/comparative-report.dto.ts | 14 + .../CRM/reporting/comparative/dto/index.ts | 5 + .../types/comparative-report-cell.ts | 53 + .../types/comparative-report-row.ts | 36 + .../types/comparative-report-value.ts | 31 + .../comparative/types/comparative-report.ts | 38 + .../CRM/reporting/comparative/types/index.ts | 4 + .../src/CRM/reporting/crm-reporting.module.ts | 49 + .../CRM/reporting/crm-reporting.service.ts | 259 + .../customer/customer-report.controller.ts | 28 + .../customer/customer-report.service.ts | 305 + .../dto/customer-report-field-meta.dto.ts | 12 + .../customer/dto/customer-report-field.dto.ts | 16 + .../dto/customer-report-filter.dto.ts | 31 + .../customer/dto/customer-report-meta.dto.ts | 15 + .../customer/dto/customer-report-row.dto.ts | 51 + .../customer/dto/customer-report.dto.ts | 15 + .../src/CRM/reporting/customer/dto/index.ts | 6 + .../enums/customer-report-type.enum.ts | 5 + .../src/CRM/reporting/customer/enums/index.ts | 1 + .../types/customer-report-field-meta.ts | 15 + .../customer/types/customer-report-field.ts | 17 + .../customer/types/customer-report-meta.ts | 22 + .../customer/types/customer-report-row.ts | 81 + .../customer/types/customer-report.ts | 24 + .../src/CRM/reporting/customer/types/index.ts | 5 + .../dashboard/dashboard.controller.ts | 126 + .../reporting/dashboard/dashboard.service.ts | 935 + .../dashboard/dto/dashboard-filter.dto.ts | 20 + .../dto/entity-summary-report.dto.ts | 17 + .../src/CRM/reporting/dashboard/dto/index.ts | 11 + .../dto/sales-pipeline-filter.dto.ts | 25 + .../dto/sales-pipeline-report-row.dto.ts | 37 + .../dto/sales-pipeline-report.dto.ts | 29 + .../dashboard/dto/sales-plan-report.dto.ts | 10 + .../dashboard/dto/sales-plan-value.dto.ts | 16 + .../dto/sellers-rating-report.dto.ts | 13 + .../dashboard/dto/task-summary-report.dto.ts | 20 + .../dashboard/dto/top-sellers-report.dto.ts | 16 + .../dashboard/dto/user-quantity-amount.dto.ts | 10 + .../dto/crm-general-report-entity.dto.ts | 42 + .../dto/crm-general-report-field-meta.dto.ts | 18 + ...rm-general-report-field-option-meta.dto.ts | 21 + .../dto/crm-general-report-field-value.dto.ts | 26 + .../dto/crm-general-report-field.dto.ts | 24 + .../dto/crm-general-report-meta.dto.ts | 7 + .../general/dto/crm-general-report-row.dto.ts | 38 + .../dto/crm-general-report-task.dto.ts | 27 + .../general/dto/crm-general-report.dto.ts | 30 + ...neral-report-filter-visibility-call.dto.ts | 9 + ...ral-report-filter-visibility-entity.dto.ts | 24 + ...port-filter-visibility-field-option.dto.ts | 13 + ...eral-report-filter-visibility-field.dto.ts | 22 + ...ral-report-filter-visibility-fields.dto.ts | 15 + ...neral-report-filter-visibility-task.dto.ts | 24 + .../general-report-filter-visibility.dto.ts | 41 + .../general/dto/general-report-filter.dto.ts | 45 + .../general/dto/general-report-row.dto.ts | 33 + .../general/dto/general-report.dto.ts | 18 + .../src/CRM/reporting/general/dto/index.ts | 19 + .../general/enums/general-report-type.enum.ts | 5 + .../src/CRM/reporting/general/enums/index.ts | 1 + .../general/general-report.controller.ts | 24 + .../general/general-report.service.ts | 823 + backend/src/CRM/reporting/general/index.ts | 2 + .../types/crm-general-report-entity.ts | 70 + .../types/crm-general-report-field-meta.ts | 23 + .../crm-general-report-field-option-meta.ts | 18 + .../types/crm-general-report-field-value.ts | 28 + .../general/types/crm-general-report-field.ts | 37 + .../general/types/crm-general-report-meta.ts | 14 + .../general/types/crm-general-report-row.ts | 65 + .../general/types/crm-general-report-task.ts | 30 + .../general/types/crm-general-report.ts | 35 + .../general/types/general-report-row.ts | 83 + .../reporting/general/types/general-report.ts | 31 + .../src/CRM/reporting/general/types/index.ts | 11 + .../src/CRM/reporting/project/dto/index.ts | 12 + .../dto/project-entities-report-filter.dto.ts | 28 + .../dto/project-entities-report-meta.dto.ts | 8 + .../dto/project-entities-report-row.dto.ts | 41 + .../dto/project-entities-report.dto.ts | 15 + .../dto/project-report-field-meta.dto.ts | 12 + .../project/dto/project-report-field.dto.ts | 16 + .../project/dto/project-report-item.dto.ts | 12 + .../project/dto/project-stage-item.dto.ts | 13 + .../project-task-user-report-filter.dto.ts | 28 + .../dto/project-task-user-report-row.dto.ts | 31 + .../project-task-user-report-total-row.dto.ts | 27 + .../dto/project-task-user-report.dto.ts | 12 + .../project/project-report.controller.ts | 46 + .../project/project-report.service.ts | 350 + .../src/CRM/reporting/project/types/index.ts | 10 + .../types/project-entities-report-meta.ts | 14 + .../types/project-entities-report-row.ts | 52 + .../project/types/project-entities-report.ts | 19 + .../types/project-report-field-meta.ts | 15 + .../project/types/project-report-field.ts | 17 + .../project/types/project-report-item.ts | 19 + .../project/types/project-stage-item.ts | 16 + .../types/project-task-user-report-row.ts | 43 + .../project-task-user-report-total-row.ts | 50 + .../project/types/project-task-user-report.ts | 17 + backend/src/CRM/sales-plan/dto/index.ts | 2 + .../sales-plan/dto/sales-plan-progress.dto.ts | 42 + .../src/CRM/sales-plan/dto/sales-plan.dto.ts | 31 + backend/src/CRM/sales-plan/entities/index.ts | 1 + .../sales-plan/entities/sales-plan.entity.ts | 76 + .../CRM/sales-plan/sales-plan.controller.ts | 80 + .../src/CRM/sales-plan/sales-plan.module.ts | 23 + .../src/CRM/sales-plan/sales-plan.service.ts | 177 + backend/src/CRM/task-board/dto/index.ts | 6 + .../CRM/task-board/dto/task-board-card.dto.ts | 51 + .../task-board/dto/task-board-filter.dto.ts | 11 + .../CRM/task-board/dto/task-board-meta.dto.ts | 17 + .../dto/task-board-stage-meta.dto.ts | 17 + .../task-board/dto/task-calendar-meta.dto.ts | 8 + .../CRM/task-board/dto/task-list-meta.dto.ts | 13 + backend/src/CRM/task-board/index.ts | 3 + .../CRM/task-board/task-board.controller.ts | 113 + .../src/CRM/task-board/task-board.service.ts | 384 + .../src/CRM/task-comment-like/dto/index.ts | 1 + .../dto/task-comment-like.dto.ts | 12 + .../CRM/task-comment-like/entities/index.ts | 1 + .../entities/task-comment-like.entity.ts | 24 + backend/src/CRM/task-comment-like/index.ts | 4 + .../task-comment-like.controller.ts | 43 + .../task-comment-like.service.ts | 46 + .../dto/create-task-comment.dto.ts | 13 + backend/src/CRM/task-comment/dto/index.ts | 4 + .../dto/task-comment-result.dto.ts | 12 + .../CRM/task-comment/dto/task-comment.dto.ts | 32 + .../dto/update-task-comment.dto.ts | 8 + .../src/CRM/task-comment/entities/index.ts | 1 + .../entities/task-comment.entity.ts | 63 + backend/src/CRM/task-comment/index.ts | 4 + .../task-comment/task-comment.controller.ts | 66 + .../CRM/task-comment/task-comment.service.ts | 152 + .../dto/create-task-settings.dto.ts | 5 + backend/src/CRM/task-settings/dto/index.ts | 3 + .../task-settings/dto/task-settings.dto.ts | 23 + .../dto/update-task-settings.dto.ts | 5 + .../src/CRM/task-settings/entities/index.ts | 1 + .../entities/task-settings.entity.ts | 39 + backend/src/CRM/task-settings/enums/index.ts | 2 + .../enums/task-field-code.enum.ts | 8 + .../enums/task-settings-type.enum.ts | 5 + .../task-settings/task-settings.controller.ts | 40 + .../task-settings/task-settings.handler.ts | 22 + .../CRM/task-settings/task-settings.module.ts | 17 + .../task-settings/task-settings.service.ts | 48 + .../dto/create-task-subtask.dto.ts | 4 + backend/src/CRM/task-subtask/dto/index.ts | 3 + .../CRM/task-subtask/dto/task-subtask.dto.ts | 28 + .../dto/update-task-subtask.dto.ts | 12 + .../src/CRM/task-subtask/entities/index.ts | 1 + .../entities/task-subtask.entity.ts | 48 + backend/src/CRM/task-subtask/index.ts | 4 + .../task-subtask/task-subtask.controller.ts | 49 + .../CRM/task-subtask/task-subtask.service.ts | 104 + backend/src/CRM/task/dto/create-task.dto.ts | 53 + backend/src/CRM/task/dto/index.ts | 3 + backend/src/CRM/task/dto/task.dto.ts | 61 + backend/src/CRM/task/dto/update-task.dto.ts | 55 + backend/src/CRM/task/entities/index.ts | 1 + backend/src/CRM/task/entities/task.entity.ts | 152 + backend/src/CRM/task/index.ts | 5 + backend/src/CRM/task/task.controller.ts | 47 + backend/src/CRM/task/task.handler.ts | 83 + backend/src/CRM/task/task.service.ts | 584 + .../CreateContactAndLeadController.ts | 28 + .../MailMessage/GetAttachmentController.ts | 44 + .../MailMessage/GetMailMessageController.ts | 26 + .../MailMessage/GetMailThreadController.ts | 26 + .../GetMailboxMessagesController.ts | 27 + .../MailMessage/GetMailboxMessagesFilter.ts | 16 + .../GetSectionMessagesController.ts | 28 + .../MailMessage/GetSectionMessagesFilter.ts | 16 + .../Mailbox/GetMailboxesInfoController.ts | 22 + .../Mailbox/SeenMailThreadController.ts | 24 + .../Mailbox/SendMailMessageController.ts | 32 + .../Mailbox/SpamMailThreadController.ts | 24 + .../Mailbox/TrashMailThreadController.ts | 24 + .../Mailbox/UnseenMailThreadController.ts | 24 + .../Mailbox/UnspamMailThreadController.ts | 24 + .../Mailbox/UntrashMailThreadController.ts | 24 + .../GmailAuthCallbackController.ts | 18 + .../GmailAuthConnectController.ts | 22 + .../GetMailboxSettingsManualController.ts | 25 + .../UpdateMailboxSettingsManualController.ts | 27 + backend/src/Mailing/MailingModule.ts | 170 + .../Mailing/Model/MailMessage/MailMessage.ts | 141 + .../Model/MailMessage/MailMessageFolder.ts | 15 + .../MailMessage/MailMessageWithFolders.ts | 12 + .../MailboxGmail/MailboxSettingsGmail.ts | 24 + .../MailboxManual/MailboxSettingsManual.ts | 93 + .../MailMessage/Dto/CreateContactLeadDto.ts | 44 + .../Service/MailMessage/Dto/MailMessageDto.ts | 105 + .../Service/MailMessage/MailMessageInfo.ts | 88 + .../Service/MailMessage/MailMessageService.ts | 675 + .../Service/MailMessage/MailThreadInfo.ts | 16 + .../Service/MailMessage/MailThreadResult.ts | 17 + .../Mailbox/Dto/mailbox-full-info.dto.ts | 59 + .../Mailbox/Dto/mailbox-section.dto.ts | 35 + .../Mailbox/Dto/mailbox-short-info.dto.ts | 26 + .../Service/Mailbox/Dto/mailboxes-info.dto.ts | 12 + .../Mailing/Service/Mailbox/MailboxService.ts | 281 + .../MailboxGmail/MailboxGmailService.ts | 724 + .../Dto/MailboxSettingsManualDto.ts | 61 + .../Dto/UpdateMailboxSettingsManualDto.ts | 38 + .../MailboxManual/MailboxManualService.ts | 803 + .../common/dto/delete-mailbox-query.ts | 9 + backend/src/Mailing/common/dto/index.ts | 2 + .../common/dto/send-mail-message.dto.ts | 36 + backend/src/Mailing/common/enums/index.ts | 3 + .../common/enums/mailbox-folder-type.enum.ts | 10 + .../common/enums/mailbox-provider.enum.ts | 4 + .../common/enums/mailbox-state.enum.ts | 8 + backend/src/Mailing/common/events/index.ts | 3 + .../common/events/mail-event-type.enum.ts | 6 + .../common/events/mail-message/index.ts | 2 + .../mail-message-received.event.ts | 26 + .../events/mail-message/mail-message.event.ts | 13 + .../Mailing/common/events/mailbox/index.ts | 1 + .../common/events/mailbox/mailbox.event.ts | 9 + backend/src/Mailing/common/index.ts | 5 + .../src/Mailing/common/types/email-address.ts | 9 + .../Mailing/common/types/folder-messages.ts | 4 + .../Mailing/common/types/imap-sync-info.ts | 4 + backend/src/Mailing/common/types/index.ts | 9 + .../types/mail-message-attachment.dto.ts | 5 + .../common/types/mail-message-external.ts | 22 + .../types/mail-message-payload-external.ts | 8 + .../common/types/mailbox-folder-external.ts | 10 + .../common/types/mailbox-sync-messages.ts | 7 + .../common/types/mailbox-sync-result.ts | 9 + backend/src/Mailing/common/utils/index.ts | 1 + .../common/utils/mailbox-folder-type.util.ts | 78 + backend/src/Mailing/config/index.ts | 1 + backend/src/Mailing/config/mail.config.ts | 52 + .../src/Mailing/mail-message-builder/index.ts | 1 + .../mail-message-builder.service.ts | 59 + .../Mailing/mail-message-payload/dto/index.ts | 1 + .../dto/mail-message-payload.dto.ts | 21 + .../mail-message-payload/entities/index.ts | 1 + .../entities/mail-message-payload.entity.ts | 89 + .../src/Mailing/mail-message-payload/index.ts | 3 + .../mail-message-payload.service.ts | 50 + .../Mailing/mail-provider/decorators/index.ts | 1 + .../decorators/mail-integration.decorator.ts | 5 + backend/src/Mailing/mail-provider/index.ts | 3 + .../mail-provider/mail-provider.registry.ts | 36 + .../src/Mailing/mail-provider/types/index.ts | 1 + .../mail-provider/types/mail-provider.ts | 45 + .../src/Mailing/mailbox-folder/dto/index.ts | 1 + .../mailbox-folder/dto/mailbox-folder.dto.ts | 33 + .../Mailing/mailbox-folder/entities/index.ts | 1 + .../entities/mailbox-folder.entity.ts | 130 + backend/src/Mailing/mailbox-folder/index.ts | 3 + .../mailbox-folder/mailbox-folder.service.ts | 223 + .../dto/create-mailbox-signature.dto.ts | 15 + .../Mailing/mailbox-signature/dto/index.ts | 4 + .../dto/mailbox-signature-filter.dto.ts | 9 + .../dto/mailbox-signature.dto.ts | 29 + .../dto/update-mailbox-signature.dto.ts | 7 + .../mailbox-signature/entities/index.ts | 2 + .../mailbox-signature-mailbox.entity.ts | 19 + .../entities/mailbox-signature.entity.ts | 69 + .../src/Mailing/mailbox-signature/index.ts | 4 + .../mailbox-signature.controller.ts | 83 + .../mailbox-signature.service.ts | 130 + .../Mailing/mailbox/dto/create-mailbox.dto.ts | 18 + backend/src/Mailing/mailbox/dto/index.ts | 4 + .../dto/mailbox-entity-settings.dto.ts | 44 + .../src/Mailing/mailbox/dto/mailbox.dto.ts | 47 + .../Mailing/mailbox/dto/update-mailbox.dto.ts | 13 + backend/src/Mailing/mailbox/entities/index.ts | 3 + .../mailbox-accessible-user.entity.ts | 19 + .../mailbox-entity-settings.entity.ts | 110 + .../mailbox/entities/mailbox.entity.ts | 126 + backend/src/Mailing/mailbox/index.ts | 4 + .../src/Mailing/mailbox/mailbox.controller.ts | 65 + backend/src/Mailing/mailbox/services/index.ts | 5 + .../mailbox-accessible-user.service.ts | 56 + .../mailbox-entity-settings.service.ts | 70 + .../mailbox/services/mailbox-lock.service.ts | 41 + .../mailbox/services/mailbox.handler.ts | 43 + .../mailbox/services/mailbox.service.ts | 296 + backend/src/Mailing/subscription/dto/index.ts | 1 + .../subscription/dto/unsubscribe.dto.ts | 8 + backend/src/Mailing/subscription/index.ts | 2 + .../subscription/unsubscribe.controller.ts | 15 + .../dto/become-partner-feedback.dto.ts | 11 + .../dto/contact-us-feedback.dto.ts | 6 + .../src/Mailing/system-mailing/dto/index.ts | 5 + .../system-mailing/dto/send-feedback.dto.ts | 17 + .../dto/trial-expired-feedback.dto.ts | 8 + .../dto/user-limit-feedback.dto.ts | 6 + .../enums/feedback-type.enum.ts | 6 + .../src/Mailing/system-mailing/enums/index.ts | 1 + backend/src/Mailing/system-mailing/index.ts | 5 + .../public-system-mailing.controller.ts.ts | 19 + .../system-mailing.controller.ts | 19 + .../system-mailing/system-mailing.service.ts | 58 + .../templates/auth/password_recovery.html | 222 + .../templates/feedback/become_partner.html | 51 + .../templates/feedback/contact_us.html | 31 + .../templates/feedback/trial_expired.html | 63 + .../templates/feedback/user_limit.html | 55 + backend/src/app.module.ts | 83 + backend/src/common/common.module.ts | 49 + backend/src/common/config/common.config.ts | 27 + backend/src/common/config/index.ts | 1 + .../src/common/constants/frontend-route.ts | 23 + backend/src/common/constants/index.ts | 2 + .../src/common/constants/paging-default.ts | 4 + backend/src/common/decorators/index.ts | 2 + .../common/decorators/subdomain.decorator.ts | 8 + .../decorators/transform-to-dto.decorator.ts | 4 + .../src/common/dto/date/date-period.dto.ts | 14 + backend/src/common/dto/date/index.ts | 1 + .../src/common/dto/expand/expand-query.dto.ts | 10 + backend/src/common/dto/expand/index.ts | 1 + .../src/common/dto/filter/boolean-filter.ts | 8 + backend/src/common/dto/filter/date-filter.ts | 14 + .../common/dto/filter/date-period-filter.ts | 21 + .../src/common/dto/filter/exists-filter.ts | 10 + backend/src/common/dto/filter/index.ts | 8 + .../src/common/dto/filter/number-filter.ts | 14 + .../src/common/dto/filter/select-filter.ts | 9 + .../src/common/dto/filter/simple-filter.ts | 28 + .../src/common/dto/filter/string-filter.ts | 15 + backend/src/common/dto/index.ts | 6 + .../dto/paging/chat-paging-query.dto.ts | 39 + .../dto/paging/cursor-paging-query.dto.ts | 20 + backend/src/common/dto/paging/index.ts | 3 + .../src/common/dto/paging/paging-meta.dto.ts | 21 + .../src/common/dto/paging/paging-query.dto.ts | 33 + backend/src/common/dto/quantity-amount.dto.ts | 21 + backend/src/common/dto/sorting/index.ts | 3 + .../common/dto/sorting/manual-sorting.dto.ts | 14 + .../common/dto/sorting/sort-order-list.dto.ts | 9 + .../src/common/dto/sorting/sort-order.dto.ts | 12 + backend/src/common/enums/currency.enum.ts | 55 + backend/src/common/enums/date-format.enum.ts | 8 + .../enums/date-period-filter-type.enum.ts | 12 + .../common/enums/exists-filter-type.enum.ts | 4 + .../src/common/enums/file-link-source.enum.ts | 15 + .../src/common/enums/group-by-date.enum.ts | 7 + .../src/common/enums/http-body-type.enum.ts | 5 + backend/src/common/enums/http-method.enum.ts | 9 + backend/src/common/enums/index.ts | 11 + backend/src/common/enums/object-state.enum.ts | 6 + .../common/enums/simple-filter-type.enum.ts | 8 + .../common/enums/string-filter-type.enum.ts | 5 + .../common/enums/user-notification.enum.ts | 5 + .../src/common/errors/bad-request.error.ts | 9 + backend/src/common/errors/forbidden.error.ts | 9 + backend/src/common/errors/index.ts | 5 + .../src/common/errors/invalid-phone.error.ts | 9 + backend/src/common/errors/not-found.error.ts | 21 + backend/src/common/errors/service.error.ts | 22 + .../common/filters/all-exceptions.filter.ts | 57 + backend/src/common/filters/index.ts | 1 + backend/src/common/index.ts | 12 + backend/src/common/interceptors/index.ts | 2 + .../interceptors/logging.interceptor.ts | 21 + .../transform-to-dto.interceptor.ts | 22 + .../extract-subdomain.middleware.ts | 8 + backend/src/common/middleware/index.ts | 1 + .../common/modules/cache/cache.constants.ts | 3 + .../modules/cache/cache.module-definition.ts | 6 + .../src/common/modules/cache/cache.module.ts | 9 + .../src/common/modules/cache/cache.service.ts | 36 + backend/src/common/modules/cache/index.ts | 4 + .../interfaces/cache-module.interface.ts | 4 + .../interfaces/cache-service.interface.ts | 8 + .../common/modules/cache/interfaces/index.ts | 4 + .../interfaces/ioredis-provider.interface.ts | 3 + .../cache/interfaces/store.interface.ts | 7 + .../common/modules/cache/providers/index.ts | 2 + .../cache/providers/ioredis.provider.ts | 52 + .../modules/cache/providers/stub.provider.ts | 9 + .../cache/types/cache-provider.type.ts | 1 + .../src/common/modules/cache/types/index.ts | 1 + .../modules/global-http/dns-cache.service.ts | 27 + .../modules/global-http/global-http.module.ts | 21 + .../src/common/modules/global-http/index.ts | 2 + backend/src/common/modules/index.ts | 4 + .../src/common/modules/token/errors/index.ts | 1 + .../token/errors/invalid-token.error.ts | 8 + backend/src/common/modules/token/index.ts | 2 + .../src/common/modules/token/token.module.ts | 17 + .../src/common/modules/token/token.service.ts | 25 + .../src/common/modules/url-generator/index.ts | 2 + .../url-generator/url-generator.module.ts | 8 + .../url-generator/url-generator.service.ts | 34 + backend/src/common/types/date/date-period.ts | 69 + backend/src/common/types/date/index.ts | 1 + backend/src/common/types/events/index.ts | 1 + .../src/common/types/events/service.event.ts | 34 + backend/src/common/types/index.ts | 5 + backend/src/common/types/quantity-amount.ts | 26 + backend/src/common/types/sort-order.ts | 1 + backend/src/common/types/task-queue.ts | 31 + backend/src/common/utils/array.util.ts | 7 + backend/src/common/utils/date.util.ts | 189 + backend/src/common/utils/index.ts | 11 + backend/src/common/utils/number.util.ts | 18 + backend/src/common/utils/object.util.ts | 11 + backend/src/common/utils/password.util.ts | 92 + backend/src/common/utils/phone.util.ts | 16 + backend/src/common/utils/promise.util.ts | 9 + backend/src/common/utils/propagation.util.ts | 26 + backend/src/common/utils/string.util.ts | 62 + backend/src/common/utils/tree.util.ts | 25 + backend/src/common/utils/url.util.ts | 37 + backend/src/config/application.config.ts | 30 + backend/src/config/index.ts | 1 + .../src/database/backup/empty.2024-07-23.sql | 9734 ++++++ backend/src/database/common/index.ts | 1 + backend/src/database/common/utils/index.ts | 1 + .../src/database/common/utils/tsquery.util.ts | 28 + .../src/database/config/database.config.ts | 37 + backend/src/database/config/index.ts | 1 + backend/src/database/database.module.ts | 40 + backend/src/database/index.ts | 6 + .../1737731260000-AddLoginSecurityFields.ts | 19 + ...744637701399-AddMailboxSettingsImapflow.ts | 27 + .../1744982861922-AlterMailboxFolder.ts | 15 + .../1744990391092-AlterMailboxFolder.ts | 13 + .../1745220421131-AlterMailboxFolder.ts | 13 + .../migrations/1745245997361-AlterUser.ts | 13 + .../archive/1667672722508-AddAccount.ts | 22 + .../archive/1667743637921-AddUser.ts | 23 + .../archive/1667755763616-AddUserAccount.ts | 26 + .../archive/1668609239887-AddEntityType.ts | 26 + .../archive/1668679480636-AddBoard.ts | 24 + .../migrations/archive/1668679994594-Note.ts | 24 + .../archive/1668680006964-AddStage.ts | 28 + .../archive/1668693976313-FeedsView.ts | 15 + .../archive/1668759141152-AddEntity.ts | 30 + .../1668769413399-AddFeedItemsSequence.ts | 14 + .../archive/1668770161278-AddTaskType.ts | 22 + .../1668957885188-AddSortOrderToBoard.ts | 11 + .../archive/1669035936818-AddActivity.ts | 32 + .../archive/1669035943607-AddTask.ts | 31 + .../archive/1669126197933-AddFieldGroup.ts | 25 + .../archive/1669127718002-AddField.ts | 28 + .../archive/1669130700529-AddTaskToFeed.ts | 19 + .../1669192730867-AddNoteToEntityFK.ts | 13 + .../archive/1669197308978-AddFieldOption.ts | 25 + .../archive/1669210346088-AddTasksView.ts | 18 + .../1669279080953-FixActivityConstraints.ts | 21 + .../archive/1669370353662-SplitUserName.ts | 23 + .../archive/1669372200327-AddUserPhone.ts | 14 + .../archive/1669535731810-UserProfile.ts | 22 + .../archive/1669545773479-EntityTypeLink.ts | 25 + .../archive/1669565827895-EntityLink.ts | 27 + .../1669629567395-RemoveUserAccount.ts | 30 + .../archive/1669706986248-AddFeature.ts | 19 + .../1669707814502-AddEntityTypeToFeature.ts | 19 + .../1669711256398-AddDefaultFeatures.ts | 20 + .../archive/1669732421801-AddFieldValue.ts | 27 + .../1669799906268-FixEntityLinkColumns.ts | 15 + .../1669880194355-ChangeFeatureName_Notes.ts | 13 + .../archive/1669966161211-BigintToInteger.ts | 43 + .../1669973222725-ForeignKeyToInteger.ts | 38 + ...91106433-AddFieldTypeColumnToFieldValue.ts | 20 + .../1670576080218-AddExternalEntity.ts | 24 + .../archive/1670840721696-ExternalSystem.ts | 23 + .../1670846440118-AddExternalSystems.ts | 15 + .../archive/1670936878487-AddFileInfo.ts | 27 + .../archive/1670939571185-AddFileStorePath.ts | 14 + .../archive/1671006792499-AddStageIdToTask.ts | 15 + .../1671030345135-ChangeFileInfoIdType.ts | 32 + .../1671089754889-AddEntityTypeSortOrder.ts | 13 + .../archive/1671096971264-RenameTaskType.ts | 22 + .../archive/1671108685879-NoteFiles.ts | 19 + .../archive/1671196063822-FileInfoCascade.ts | 15 + .../1671196394406-NoteFileRemoveFileKey.ts | 13 + .../1671203263368-TaskAndActivityFiles.ts | 24 + .../1671438176416-RemoveKeyFromFileInfo.ts | 13 + .../1671439005617-AddIsUsedToFileInfo.ts | 13 + .../archive/1671440838639-DropNoteFileLink.ts | 13 + .../1671521628139-BoardRecordIdNull.ts | 13 + .../archive/1671533557027-UserActive.ts | 13 + .../1671619787598-UserObjectPermission.ts | 33 + .../archive/1671914250074-ChangeCardView.ts | 13 + .../archive/1672064100473-EntityIdCascade.ts | 16 + .../1672154773200-AddSalesforceSettings.ts | 22 + ...72224404851-AddRefreshTokenToSalesforce.ts | 13 + ...672242451242-ExternalSystemUrlTemplates.ts | 21 + .../1672306523845-AddDataToExternalEntity.ts | 13 + ...1672386954959-AddUIDataToExternalEntity.ts | 14 + .../1672928537580-CascadeStageIdInTask.ts | 18 + .../1673251726994-AddStageToAllTasks.ts | 19 + .../1673339773307-EnableFileStorageFeature.ts | 13 + ...17366210-RenameCardViewToEntityCategory.ts | 13 + .../archive/1673447442894-AddSubscription.ts | 26 + .../1673514341599-AddCreatedAtToFileLink.ts | 15 + ...73515827427-AddSequenceToSubscriptionId.ts | 15 + ...969196807-AddMailboxAndProviderSettings.ts | 53 + .../archive/1674130775550-AddMailboxState.ts | 13 + .../1674138959333-AddPlannedTimeToTask.ts | 68 + ...674203801914-ChangeMailboxSettingsGmail.ts | 16 + ...74492197867-AddHistoryIdToGmailSettings.ts | 14 + ...74546589359-AddLastSyncToManualSettings.ts | 14 + .../1674560582007-AddMailMessageBase.ts | 64 + .../archive/1674572622632-AddTaskSettings.ts | 23 + .../archive/1674637903317-ChangeMailbox.ts | 15 + .../1674638261068-AddMailboxAccessibleUser.ts | 21 + .../1674648741806-AddMailboxFolderInfo.ts | 15 + .../1674660419230-AddMailMessageReplyTo.ts | 14 + .../archive/1674661928759-AddMailMessageCc.ts | 14 + ...32906697-AddMailMessagePayloadSortOrder.ts | 14 + .../1674741639286-MakeStartEndDateNullable.ts | 16 + .../archive/1674742927551-AddSubtaskTable.ts | 24 + ...1674747324757-AddMailMessagePayloadSize.ts | 14 + .../1674822646459-AddTaskCommentTable.ts | 26 + ...1674823506430-DeleteSyncDaysFromMailbox.ts | 14 + .../1674831146207-AddTaskCommentLikeTable.ts | 22 + .../archive/1675076470234-ImapSync.ts | 14 + .../1675080091774-AddSystemColumnToBoard.ts | 18 + ...6921624-AddMailMessagePayloadExternalId.ts | 14 + .../1675090441216-AddTaskSettingsIdToTask.ts | 74 + ...75176424905-AddMailMessageHasAttachment.ts | 14 + ...1675178566416-MailboxFolderTypeNullable.ts | 13 + .../1675259924922-AddMailMessageMessageId.ts | 13 + ...75340097111-DropGroupMessageFromMailbox.ts | 13 + .../1675349215128-AddCodeToEntityType.ts | 30 + .../archive/1675350490867-AddCodeToBoard.ts | 34 + .../1675412897506-AddMailMessageReferences.ts | 15 + ...858313415-AddContactEntityTypeToMailbox.ts | 15 + ...675858907059-AddLeadEntityTypeToMailbox.ts | 15 + .../1675932022996-AddLeadBoardIdToMailbox.ts | 15 + ...1675933206380-AddContactIdToMailMessage.ts | 15 + ...675939938059-MailMessageThreadIdNotNull.ts | 14 + .../1675949522751-AddErrorToMailbox.ts | 14 + .../1675958088984-AddSeenToMailMessage.ts | 14 + ...76281117335-AddSetNullOnDeleteToMailbox.ts | 20 + ...676283110539-AlterMailMessageSentToNull.ts | 13 + .../1676299431922-AddRecipientToNote.ts | 18 + .../1676370561831-AddMailToFeedItem.ts | 41 + .../1676383947580-TurnOnChatFeature.ts | 15 + .../1676385805343-AddChatFeedItemType.ts | 41 + .../1676823429937-AddAutomationTables.ts | 66 + .../1676866745580-AddAutomationStageTable.ts | 20 + ...676872004900-AddTaskActionSettingsTable.ts | 27 + .../1676884562402-RemoveResultFromTask.ts | 69 + ...93533-AddChangeStageActionSettingsTable.ts | 22 + .../1676992655279-AddAutomationConditions.ts | 56 + .../archive/1676994397217-AddNotification.ts | 32 + .../1677079993131-AddUserIdToNotification.ts | 15 + .../1677581691766-AddMailboxSignature.ts | 35 + .../1677744619246-AddNotificationHighlight.ts | 13 + ...AlterNotificationSetDescriptionNullable.ts | 13 + .../1677765215861-AddNotificationSettings.ts | 46 + .../1678093755605-AddExternalSystems.ts | 25 + .../1678096716231-AddAccountSettings.ts | 24 + ...78113775031-AddExactTimeTriggerSettings.ts | 21 + ...94605837-AddDefaultNotificationSettings.ts | 55 + ...5724304-AddResolveDateToTaskAndActivity.ts | 14 + .../1678286876322-AddResolveDateToAllTasks.ts | 71 + ...775-AlterNotificationHighlightToTagName.ts | 13 + ...411991-FillResolvedDateForTaskAndAction.ts | 15 + ...1678696011650-AddPlanNameToSubscription.ts | 15 + ...78697720944-UpdateSubscriptionTo50Users.ts | 13 + .../archive/1678720323399-AddCodeToField.ts | 18 + .../archive/1678891987277-AddDepartment.ts | 29 + ...1678963689149-AddStartsInToNotification.ts | 13 + .../1679291439089-AddScheduledAction.ts | 26 + .../1679494426598-MakeFieldGroupOptional.ts | 14 + .../1679578329175-AddColorToFieldOption.ts | 14 + ...3365997-AlterMailMessageContactEntityId.ts | 54 + .../archive/1679929245833-AddUserAvatarId.ts | 15 + .../archive/1679931642588-AddActiveToField.ts | 14 + .../archive/1679931904542-AddAccountLogo.ts | 13 + .../1680499051021-AddParticipantsToEntity.ts | 14 + .../archive/1680763220747-MigrateProjects.ts | 160 + .../1681141545739-AddEmailActionSettings.ts | 27 + .../archive/1681224301468-AddProjectFields.ts | 104 + .../1681289039535-AddEntityListSettings.ts | 26 + .../1681483040117-AddScheduledMailMessage.ts | 35 + ...32037710-AddEmailsPerDayColumnToMailbox.ts | 14 + ...681828967422-AddWeightToTaskAndActivity.ts | 39 + .../1681832187113-AddWeightToAllTasks.ts | 74 + ...142878-ResetWeightForTasksAndActivities.ts | 34 + .../archive/1682002593036-AddEntityWeight.ts | 29 + .../1682083589639-AddAccountIdTETFeature.ts | 21 + .../archive/1682348048312-AddRMS.ts | 33 + .../archive/1682350735553-AddIndustries.ts | 24 + .../archive/1682433824946-AddDemoMarker.ts | 20 + .../1682507153940-AddDocumentTemplate.ts | 44 + .../1682518277268-DeleteFileInfoWithUser.ts | 17 + .../archive/1682589692981-AddIndexes.ts | 30 + .../archive/1683016482581-AddIndexes.ts | 54 + .../1683205194466-AddTaskBoardIdToBoard.ts | 15 + ...1683517583707-DeleteProjectEntityBoards.ts | 14 + .../1683731671898-AddFeatureDocuments.ts | 17 + ...3797890507-AddDocumentTemplateGenerated.ts | 13 + .../1683802969000-AddDocumentInFeed.ts | 42 + .../archive/1683875863921-TrimUsersNames.ts | 15 + .../1684249775346-AddAllTasksStageId.ts | 77 + .../1684317847183-AddCreatedByToFileLink.ts | 19 + .../1685001497108-ChangeFileInfoHash.ts | 15 + .../1685595302584-AddParticipantsToBoard.ts | 36 + .../archive/1685604837960-AddChatModel.ts | 109 + .../1685689401123-AddDefaultChatProvider.ts | 21 + .../archive/1686048795624-AddBoardIdToTask.ts | 87 + .../1686061937533-AlterChatMEssageFile.ts | 19 + .../1686297344564-AlterMailMessageEntityId.ts | 17 + ...75887-AddSignatureToEmailActionSettings.ts | 14 + .../1686643536303-AddReplayToInChatMessage.ts | 15 + .../1686736715335-AddChatPinnedMessage.ts | 15 + .../1686816157824-AddChatPinnedMessage.ts | 25 + .../1686824143539-AddChatMessageReaction.ts | 26 + .../archive/1686840724427-AddProducts.ts | 60 + .../1686904432256-AddChatIdToStatus.ts | 27 + .../1686930758334-RenameProductField.ts | 14 + .../1687015795997-AlterChatMessageReply.ts | 16 + .../1687350416742-RemoveIdFromSubscription.ts | 17 + ...57599-AddSubscriptionExternalCustomerId.ts | 13 + .../archive/1687790975332-AddOrder.ts | 45 + .../archive/1687793191931-FixOrder.ts | 19 + .../archive/1687877020115-AddTaxIncluded.ts | 14 + .../1687943824933-AddChatProviderTwilio.ts | 22 + .../1687954149882-AlterChatProviderUser.ts | 17 + .../archive/1687962117509-AddWarehouse.ts | 24 + .../1687965328992-AddIsDeletedToWarehouse.ts | 14 + .../archive/1688025794222-AlterChatUser.ts | 13 + .../archive/1688044274695-AddStocks.ts | 23 + .../1688053486248-AddChatUserExternalName.ts | 13 + .../1688112039219-AlterChatProviderTwilio.ts | 13 + .../1688130606571-AlterChatProviderUser.ts | 22 + .../archive/1688136613049-AlterChat.ts | 16 + .../archive/1688138872050-AddOrderStatus.ts | 24 + .../archive/1688139271540-AddShipment.ts | 55 + .../1688140521166-AddStatusIdToOrder.ts | 14 + .../archive/1688388514670-AddReservation.ts | 30 + .../1688390259595-AlterFileInfoCreatedBy.ts | 16 + .../1688394200229-FixStatusIdInOrder.ts | 14 + .../archive/1688472386401-AddShipmentDate.ts | 14 + .../1688543908016-AddChatProviderMessenger.ts | 21 + .../archive/1688567846856-AlterChatUser.ts | 16 + .../1688996628275-FixCategoryIdConstraint.ts | 19 + .../1689059395581-AddChatProviderStatus.ts | 14 + ...9068374394-AddUserIdToMessengerProvider.ts | 14 + .../1689081064483-AddWarehouseIdToOrder.ts | 15 + .../archive/1689087134759-AddModules.ts | 34 + .../archive/1689170448447-AddProductType.ts | 14 + .../archive/1689243925753-FixOrderStatuses.ts | 53 + .../1689259268310-DeleteShipmentStatus.ts | 26 + .../1689337185167-AddProductsFeature.ts | 13 + .../1689508562776-AddIsActiveToReservation.ts | 14 + .../1689763128902-RemoveNoteRecipientId.ts | 43 + .../1689774963182-AddUpdatedAtToProduct.ts | 14 + .../archive/1689860682052-AddSchedule.ts | 57 + .../1689933154489-AlterSchedulePerformer.ts | 21 + .../archive/1690208012261-AddProductModule.ts | 23 + ...0456178510-MigrateModuleToProductModule.ts | 24 + .../archive/1690467527775-RemoveModule.ts | 15 + .../1690469860109-AddProductPermissions.ts | 28 + .../1690543599386-DropProductPermissions.ts | 13 + .../1690817128717-AlterProductModule.ts | 13 + .../1690973831680-AddSectionIdToProducts.ts | 29 + ...56504886-AddSectionIdToOrderAndShipment.ts | 23 + .../1691061102493-DeleteShipmentStatus.ts | 14 + ...061408646-AlterTableStockToProductStock.ts | 13 + ...1139996885-AddProductsSectionEntityType.ts | 30 + .../1691155049107-AddRentalInterval.ts | 22 + .../1691397636905-AlterProductsSectionType.ts | 13 + .../archive/1691411118591-AddRentalOrder.ts | 44 + .../1691414938591-AddRentalSchedule.ts | 25 + .../1691657890280-AddRentalOrderPeriod.ts | 24 + ...678125349-AddRentalOrderPeriodAccountId.ts | 15 + .../archive/1691754596482-AlterRentalOrder.ts | 19 + .../1691755141714-AlterRentalOrderItem.ts | 19 + ...692002092660-RentalScheduleAddSectionId.ts | 17 + ...15943-RenameRentalScheduleToRentalEvent.ts | 13 + ...42159-AddProductsSectionEnableWarehouse.ts | 18 + .../1692172254434-AddOrdersOrderNumber.ts | 26 + ...1692172318353-AddRentalOrderOrderNumber.ts | 26 + ...92283603851-AlterOrderItemCascadeDelete.ts | 16 + .../1692343747646-AlterProductStock.ts | 17 + .../1692354371998-FixFieldValueType.ts | 13 + ...2604044210-ProductsSectionEnableBarcode.ts | 13 + .../1692708295281-AlterOrderStatusNull.ts | 13 + .../1692885285551-RefactorScheduler.ts | 21 + .../1692890628636-RenameScheduleEvent.ts | 13 + .../archive/1692975377102-AlterSchedule.ts | 16 + .../archive/1693218884137-UserPosition.ts | 13 + ...693232990040-ScheduleAppointmentOrderId.ts | 15 + .../archive/1693485238189-OrderStatusColor.ts | 17 + .../1693556962547-CascadeDeleteShipment.ts | 17 + .../archive/1694085886365-SchedulerIcon.ts | 13 + ...166234404-BoardCleanProjectParticipants.ts | 15 + .../archive/1695040324876-AppointmentTitle.ts | 13 + .../archive/1695046445852-EntityClosedAt.ts | 13 + .../archive/1695201739381-SalesPlan.ts | 27 + .../1695287859742-ChatDeleteCascadeEntity.ts | 15 + .../1695382850916-SalesPlanAlterAmount.ts | 13 + .../archive/1695742049917-VoximplantUser.ts | 22 + ...95743140564-VoximplantUserPrimaryColumn.ts | 15 + .../1695808984339-VoximplantUserPassword.ts | 13 + .../1695810676148-VoximplantAccount.ts | 20 + .../1695820387969-AlterVoximplantUser.ts | 15 + .../archive/1696500815450-DemoData.ts | 20 + .../archive/1697019761609-VoximplantCall.ts | 30 + .../1697028866185-AlterVoximplantUser.ts | 13 + .../archive/1697115016543-RefactorDemoData.ts | 23 + ...40579544-SchedulerEventPerformerCascade.ts | 16 + .../1697452411558-VoximplantUserActive.ts | 13 + .../1697541300418-VoximplantAccount.ts | 20 + .../1697541767120-VoximplantAccountAlter.ts | 14 + .../1697543064652-VoximplantAccountEmail.ts | 13 + .../1697556130148-VoximplantAccountKey.ts | 15 + ...702819805-VoximplantCallAlterExternalId.ts | 18 + .../1698135013349-ReportingOptimization.ts | 14 + .../1698421539770-VoximplantScenarios.ts | 57 + .../1698663785490-OrderStatusColors.ts | 17 + .../1699457673085-AddEntityEventModel.ts | 21 + .../1700060543771-AddSchedulePerformerId.ts | 16 + .../1700060859571-AlterSchedulePerformer.ts | 17 + .../archive/1700230572219-AlterMailbox.ts | 15 + .../1700395051542-AlterExternalEntity.ts | 16 + .../1700475817946-AllEventsMigrationScript.ts | 62 + ...00591906266-TelephonyCallsToEntityEvent.ts | 16 + ...1700663233866-AlterSchedulerAppointment.ts | 25 + .../archive/1700729760783-AlterSchedule.ts | 13 + .../archive/1700733045104-AlterSchedule.ts | 13 + .../archive/1700735236205-AlterSchedule.ts | 13 + ...00741072037-ProductOrdersToEntityEvents.ts | 31 + ...1700820935837-AlterSchedulerAppointment.ts | 21 + .../1700836699189-ShipmentsToEntityEvents.ts | 21 + .../1701264274255-CallsToEntityEventsFix.ts | 20 + ...7712747-DeleteEntityEventTelephonyCalls.ts | 13 + .../1701701891843-AlterVoximplantCall.ts | 13 + .../1702542289418-UpdateModulesIcons.ts | 23 + .../1702637665853-AlterActivityType.ts | 13 + .../1702970939958-AlterAccountSettings.ts | 13 + .../1703085253100-AlterProductPrice.ts | 13 + .../1703488850551-AlterAccountSettings.ts | 13 + .../archive/1703502036545-FieldSettings.ts | 36 + .../1703761495779-AlterFieldStageSettings.ts | 14 + .../archive/1703850851646-AlterShipment.ts | 23 + .../archive/1703857140848-AlterReservation.ts | 15 + ...76929122-AlterChangeStageActionSettings.ts | 15 + .../archive/1704282894747-AlterEntity.ts | 13 + .../archive/1706795467082-TestAccount.ts | 17 + .../1708088397272-EntityStageChange.ts | 25 + .../1708433846254-EntityChangeHistoryInit.ts | 28 + .../archive/1708589222946-UserAnalyticsId.ts | 16 + ...08952321460-OptimizeNotificationIndexes.ts | 15 + .../1709047301377-DBOptimizationIndex.ts | 13 + .../1709048922111-StageAccountIdIndex.ts | 13 + ...1709110989045-ScheduledMailMessageIndex.ts | 15 + .../archive/1709111575857-AddIndexes.ts | 22 + ...1709280253891-ChatProviderCascadeDelete.ts | 15 + .../1709736232826-ChatEntityRemoveCascade.ts | 15 + .../archive/1709805560320-ChatUserExternal.ts | 37 + .../1710162901881-EntityCopiedCount.ts | 13 + .../archive/1710758264055-OrderCancelAfter.ts | 19 + ...1710759144910-ProductSectionCancelAfter.ts | 13 + .../1710864090375-ScheduledActionCreatedBy.ts | 20 + .../archive/1710927112868-TutorialGroup.ts | 21 + .../archive/1710929893275-TutorialItem.ts | 24 + .../archive/1710939538331-TutorialItemUser.ts | 19 + .../1711033243401-TutorialItemProduct.ts | 22 + .../1711087326245-MailMessageIndexes.ts | 16 + .../1711540999340-EntityActionSettings.ts | 13 + .../1711541635402-ActionSettingsRename.ts | 16 + .../archive/1711706670268-ScheduledAction.ts | 13 + .../1711962655915-AutomationActionSettings.ts | 16 + .../archive/1712575547663-FieldValue.ts | 13 + .../1713167989297-DeleteAllFormulaFields.ts | 13 + .../1713257835467-FixEntityTypeSortOrder.ts | 20 + ...713258622876-FixEntityTypeLinkSortOrder.ts | 20 + .../1713971186799-ChatProviderTransport.ts | 17 + .../archive/1714382561376-WazzupProvider.ts | 23 + .../1714557065128-ProductPricePrecision.ts | 13 + ...4663341741-WazzupProviderRemoveChatType.ts | 13 + .../1714730508391-WazzupProviderTransport.ts | 14 + .../1714732587962-RemoveWazzupProviders.ts | 13 + .../1714734600334-ChatProviderTypeRename.ts | 14 + .../1715602468891-ChatProviderUserType.ts | 13 + .../1715610371002-AlterChatUserExternal.ts | 32 + .../archive/1715856544173-AccountApiAccess.ts | 19 + .../1715856948928-AlterAccountApiAccess.ts | 13 + .../archive/1716299180820-VoximplantNumber.ts | 30 + .../1716384743872-VoximplantPhoneNumber.ts | 13 + .../1716465152984-VoximplantNumberDefaults.ts | 14 + .../1716466922606-VoximplantCallNumber.ts | 15 + ...16469802549-VoximplantCallNumberDefault.ts | 15 + .../archive/1717599382958-AddForms.ts | 52 + .../1717746424353-SiteFormPageSortOrder.ts | 14 + .../archive/1717773341006-FormSiteLink.ts | 22 + .../1718030543491-SiteFormEntityType.ts | 25 + .../1718098299378-SiteFormRemoveConsent.ts | 16 + .../archive/1718098642901-SiteFormConsent.ts | 24 + .../1718115282972-SiteFormGratitude.ts | 22 + .../archive/1718118622975-AlterSiteForm.ts | 21 + .../1718120948980-AlterSiteFormField.ts | 18 + .../1718266194827-NotificationRemoveTag.ts | 13 + .../archive/1718613418648-AlterSiteForm.ts | 15 + .../1718715571983-AlterSiteFormField.ts | 13 + .../1718724129043-AutomationProcess.ts | 23 + .../1718793150525-AlterSiteFormField.ts | 14 + .../1718793757895-AlterSiteFormField.ts | 14 + .../1718798058177-AlterAutomationProcess.ts | 17 + .../1718801950089-AlterAutomationProcess.ts | 14 + .../1718880290844-AlterSiteFormField.ts | 13 + .../1719213418299-AlterSiteFormField.ts | 14 + .../1719302707526-AlterSiteFormField.ts | 38 + .../archive/1719324782700-RenameSubtask.ts | 13 + ...1719325474889-AlterTaskSubtaskSortOrder.ts | 13 + .../1719393706903-AlterAutomationProcess.ts | 14 + .../1719396909800-AutomationEntityType.ts | 24 + ...1719404585119-AlterAutomationEntityType.ts | 19 + ...1719411072571-AlterAutomationEntityType.ts | 13 + ...1719414260257-AlterAutomationEntityType.ts | 13 + ...1719502764234-AlterAutomationEntityType.ts | 13 + ...1719502897605-AlterAutomationEntityType.ts | 13 + .../archive/1719833217147-AlterEntityType.ts | 15 + .../1719995612500-UpdateCopiesCreatedAt.ts | 47 + .../archive/1720076313493-AlterSiteForm.ts | 19 + .../1720077438733-AlterSiteFormEntityType.ts | 25 + .../archive/1720194016236-AppSumoLicense.ts | 23 + .../archive/1720194705296-AppSumoPreset.ts | 19 + .../archive/1720423072348-AppSumoPresets.ts | 17 + .../archive/1720423711324-RenameAppSumo.ts | 14 + .../1720434041376-AppsumoLicenseUnique.ts | 13 + .../1720446828855-AlterReadyMadeSolution.ts | 20 + .../1720530507278-RenameAppsumoPreset.ts | 13 + .../archive/1720530714621-AlterAppsumoTier.ts | 13 + .../1720531175011-RenameSubscription.ts | 13 + .../archive/1720596831169-AlterAppsumoTier.ts | 15 + ...1720597683965-UpdateAccountSubscription.ts | 13 + .../1720610809785-AlterAppsumoLicense.ts | 13 + .../archive/1720619947686-AlterAppsumoTier.ts | 17 + ...1720778334472-AlterAutomationEntityType.ts | 16 + .../archive/1721221679342-VoximplantSip.ts | 20 + .../1722240313665-DBMemoryOptimization.ts | 13 + .../1722240423838-DBIndexOptimization.ts | 17 + .../1722250997991-FieldSettingsIndexes.ts | 18 + .../1722251334680-AccountApiAccessIndex.ts | 13 + ...2325875311-UpdateProjectFieldsSortOrder.ts | 13 + .../1722340602724-DepartmentIndexes.ts | 13 + .../1722500514353-EntityListSettingsIndex.ts | 14 + .../1722870704258-AlterDepartmentIsActive.ts | 13 + .../1722871843239-AlterUserDepartment.ts | 16 + .../1723373462514-AddMailMessageScheduled.ts | 30 + ...1723386042005-AlterMailMessageScheduled.ts | 13 + ...1723641840096-AlterMailMessageScheduled.ts | 13 + .../1723814067070-MailboxEmailsPerDay.ts | 13 + .../archive/1724058794170-AlterFieldGroup.ts | 13 + .../archive/1724422282832-EntityUpdatedAt.ts | 15 + .../1724657864701-AlterVoximplantSipName.ts | 15 + .../1724658513639-VoximplantSipUser.ts | 21 + .../archive/1724668825212-EntityBoardId.ts | 19 + .../1724744316233-FieldGroupDefault.ts | 24 + .../1724747098462-DeprtmentIdIdentity.ts | 22 + .../1724761173125-DepartmentSettings.ts | 23 + .../archive/1724944661564-UserIdIdentity.ts | 22 + .../1724945283499-AccountIdIdentity.ts | 22 + ...724946396552-DocumentTemplateIdIdentity.ts | 22 + .../1724946751431-NotificationIdIdentity.ts | 22 + ...46917573-NotificationSettingsIdIdentity.ts | 22 + ...5740-NotificationTypeSettingsIdIdentity.ts | 22 + ...725008953241-EntityTypeActionTypeRename.ts | 27 + .../archive/1725024403154-ChatIdIdentity.ts | 22 + ...1725024779580-ChatMessageFileIdIdentity.ts | 22 + ...024956416-ChatMessageReactionIdIdentity.ts | 22 + .../1725025200036-ChatMessageIdIdentity.ts | 22 + .../1725025378404-ChatProviderIdIdentity.ts | 22 + .../1725025624010-ChatUserIdIdentity.ts | 22 + ...1725269507813-ChatProviderMessagePerDay.ts | 15 + .../1725622826309-MailboxLatsActiveAt.ts | 13 + ...725980172270-EntityCopiedFromConstrains.ts | 17 + .../1726478454833-ChatMessageScheduled.ts | 29 + ...1727179988017-AlterChatMessageScheduled.ts | 16 + .../1727265648107-MailFolderOptimization.ts | 17 + .../archive/1727682923696-AlterSiteForm.ts | 13 + .../1727766355582-TaskCommentIdIdentity.ts | 22 + .../1727775973380-AlterAutomationProcess.ts | 35 + .../archive/1728296331516-RenameStage.ts | 13 + .../archive/1728312582354-AlterBoardStage.ts | 13 + .../archive/1728384950674-BoardIdIdentity.ts | 22 + .../archive/1728402696162-AlterBoard.ts | 15 + .../archive/1728465792633-AlterBoardStage.ts | 15 + .../archive/1728468439082-AlterBoardStage.ts | 13 + ...1728471228996-AlterAutomationEntityType.ts | 17 + .../1728892125136-AlterAccountSettings.ts | 13 + .../1728981267739-AlterObjectPermission.ts | 24 + ...8984932906-AlterObjectPermissionIndices.ts | 18 + .../1728988723314-AlterObjectPermission.ts | 23 + .../1729001229086-AlterObjectPermission.ts | 17 + .../1729680141757-UpdateTutorialItem.ts | 29 + .../archive/1729758131725-AlterMailMessage.ts | 13 + .../1729853713049-RemoveUnusedIndexes.ts | 14 + .../1729856828756-ActivityTypeIdIdentity.ts | 22 + .../1729857179487-EntityTypeIdIdentity.ts | 22 + .../1729859612700-FieldValueIdIdentity.ts | 22 + .../archive/1730103007231-EntitySearch.ts | 66 + ...730212128907-ScheduleAppointmentIndexes.ts | 15 + .../archive/1730472621229-TaskIndexes.ts | 13 + ...1730796442786-AlterMailMessageScheduled.ts | 19 + .../archive/1730988793369-AlterSchedule.ts | 13 + .../1731239585214-UsersAccessibleUsers.ts | 19 + .../archive/1731318295236-FieldIndex.ts | 14 + .../1731491092637-WarehouseIdIdentity.ts | 22 + .../1731491294395-ShipmentItemIdIdentity.ts | 22 + .../1731491510990-ShipmentIdIdentity.ts | 22 + .../1731491686717-ReservationIdIdentity.ts | 22 + ...1731502099273-ProductCategoryIdIdentity.ts | 22 + .../1731502604723-ProductIdIdentity.ts | 22 + .../1731502805263-ProductPriceIdIdentity.ts | 22 + .../archive/1731503055930-OrderIdIdentity.ts | 22 + .../1731503302276-OrderItemIdIdentity.ts | 22 + .../1731503975362-OrderStatusIdIdentity.ts | 22 + .../archive/1731512848862-AlterWarehouse.ts | 14 + .../archive/1731682593963-AddVersion.ts | 19 + ...029352587-ObjectPermissionDepartmentFix.ts | 103 + .../1732272936256-TaskSettingsIdIdentity.ts | 22 + .../1732281701728-OptimizationIndexes.ts | 31 + .../1732521216471-AlterAccountSettings.ts | 13 + .../1732627160688-IndexOptimization.ts | 31 + .../archive/1732628496619-BoardIndexes.ts | 25 + .../1732697048964-IndexOptimization.ts | 15 + .../1732716375115-EntityTypeLinkIdIdentity.ts | 22 + .../1732783039132-EntityTypeLinkOrphans.ts | 22 + .../archive/1732866955213-AlterSiteForm.ts | 13 + ...1732876120184-UpdateEntityBoardAndStage.ts | 54 + .../archive/1732889802526-FrontendObject.ts | 23 + ...36867799-UpdateScheduleAppointmentOwner.ts | 15 + .../1733393739852-ConditionIdIdentity.ts | 22 + ...94028781-ScheduledMailMessageIdIdentity.ts | 22 + .../1733394215019-TriggerIdIdentity.ts | 22 + ...1733394597308-ActionScheduledIdIdentity.ts | 22 + .../archive/1733394799717-ActionIdIdentity.ts | 22 + .../1733394948832-AutomationIdIdentity.ts | 22 + .../archive/1733395768414-NoteIdIdentity.ts | 22 + .../1733395930995-ActivityIdIdentity.ts | 22 + .../archive/1733395960444-TaskIdIdentity.ts | 22 + .../1733396343770-TaskSubtaskIdIdentity.ts | 22 + .../1733397036241-MailMessageIdIdentity.ts | 22 + ...3397284175-MailMessagePayloadIdIdentity.ts | 22 + .../1733397518270-MailboxFolderIdIdentity.ts | 22 + .../1733397748405-MailboxIdIdentity.ts | 22 + ...733397911411-MailboxSignatureIdIdentity.ts | 22 + .../archive/1734009141643-AlterEntityValue.ts | 23 + .../archive/1734081841312-AlterUserProfile.ts | 13 + .../archive/1734097700161-AlterEntityLink.ts | 13 + .../1734349336443-EntityLinkIdIdentity.ts | 22 + .../1734352488667-RemoveEntityListSettings.ts | 14 + ...734429865379-ScheduleAppointmentIndexes.ts | 20 + ...1734600811704-AlterChatMessageScheduled.ts | 15 + .../archive/1734688459744-AlterField.ts | 13 + .../archive/1734708417316-VersionIndexes.ts | 13 + .../1734945712418-AlterSiteFormField.ts | 32 + .../1735140161718-AddGoogleCalendar.ts | 27 + .../1735289134467-AlterGoogleCalendar.ts | 13 + .../archive/1735290249052-AlterTask.ts | 18 + .../1735573056216-IndexGoogleCalendar.ts | 13 + .../1736239080782-AlterGoogleCalendar.ts | 13 + .../1736262949465-AlterGoogleCalendar.ts | 15 + .../1736324153278-GoogleCalendarIndexes.ts | 13 + .../1736330451213-AlterGoogleCalendar.ts | 13 + .../1736331095918-GoogleCalendarIndexes.ts | 13 + .../1736339315376-AlterGoogleCalendar.ts | 13 + .../archive/1736496705277-AlterTask.ts | 13 + .../archive/1736513599793-IndexesTask.ts | 13 + .../1736773476711-GoogleCalendarLinked.ts | 22 + ...36781493022-IndexesGoogleCalendarLinked.ts | 14 + .../1736842860059-AlterGoogleCalendar.ts | 13 + .../1736948650910-AlterScheduleAppointment.ts | 13 + .../1737033530086-AlterAccountSubscription.ts | 13 + .../1737039160966-AddSubscriptionDiscount.ts | 19 + ...1737103293642-UpdateAccountSubscription.ts | 13 + .../1737103460794-AlterAccountSubscription.ts | 13 + ...737103855502-InsertSubscriptionDiscount.ts | 14 + .../1737378761002-AddGoogleCalendarAccount.ts | 29 + ...1737971712981-AlterSubscriptionDiscount.ts | 13 + ...737973113027-UpdateSubscriptionDiscount.ts | 18 + .../1738660778536-RemoveOldAutomation.ts | 27 + .../archive/1738676409194-AlterUserProfile.ts | 14 + .../1739788991506-AlterGoogleCalendar.ts | 16 + .../1739802958618-AlterAccountSettings.ts | 13 + .../archive/1739891080452-AddUserToken.ts | 27 + .../archive/1740402538635-AddUserCalendar.ts | 39 + .../archive/1740472617092-AlterSchedule.ts | 14 + ...79281651-AddScheduleTimeIntervalService.ts | 25 + .../archive/1740579698409-AlterSiteForm.ts | 13 + .../archive/1740583763973-AlterSiteForm.ts | 14 + .../1740654525458-AddSiteFormSchedule.ts | 21 + .../archive/1741610604818-AlterSiteForm.ts | 13 + .../archive/1741701747438-UpdateSchedule.ts | 13 + .../archive/1741702060670-AlterSchedule.ts | 13 + ...477677993-AddChatProviderEntitySettings.ts | 29 + ...2395334-AlterChatProviderEntitySettings.ts | 15 + ...5071601-AlterChatProviderEntitySettings.ts | 13 + .../1743084130609-AlterProductStock.ts | 13 + .../1743592762254-AddMailboxEntitySettings.ts | 32 + ...43595365132-InsertMailboxEntitySettings.ts | 16 + .../archive/1743595431125-AlterMailbox.ts | 18 + .../archive/1743690907799-AlterEntityLink.ts | 22 + .../archive/1743771835815-EntityIdIdentity.ts | 22 + .../archive/1744127440588-AlterEntity.ts | 13 + .../1744296868607-AlterMailboxFolder.ts | 14 + ...1744374434852-AlterMailboxSignatureLink.ts | 13 + .../1744382844583-AlterMailboxSignature.ts | 13 + backend/src/database/nestjs-logger.ts | 41 + backend/src/database/services/index.ts | 1 + .../database/services/sequence-id.service.ts | 18 + .../src/database/typeorm-migration.config.ts | 21 + .../src/documentation/api-documentation.ts | 45 + .../config/documentation.config.ts | 12 + backend/src/documentation/index.ts | 1 + backend/src/main.ts | 116 + .../src/modules/analytics/analytics.module.ts | 11 + .../modules/analytics/analytics.service.ts | 83 + .../analytics/config/analytics.config.ts | 14 + .../automation-core.controller.ts | 25 + .../automation-core.service.ts | 163 + .../automation/automation-core/index.ts | 3 + .../automation/automation-core/types/index.ts | 1 + .../types/process-definition-version.ts | 10 + .../automation-entity-type.controller.ts | 75 + .../automation-entity-type.handler.ts | 125 + .../automation-entity-type.service.ts | 465 + .../action-activity-create-settings.dto.ts | 34 + .../action-chat-send-amwork-settings.dto.ts | 20 + .../actions/action-chat-send-settings.dto.ts | 29 + .../actions/action-email-send-settings.dto.ts | 36 + .../action-entity-create-settings.dto.ts | 30 + ...entity-linked-stage-change-settings.dto.ts | 19 + ...-entity-responsible-change-settings.dto.ts | 10 + ...action-entity-stage-change-settings.dto.ts | 15 + .../actions/action-send-options-entity.dto.ts | 11 + .../actions/action-send-options-value.dto.ts | 9 + .../dto/actions/action-send-options.dto.ts | 27 + .../action-task-create-settings.dto.ts | 34 + .../dto/actions/index.ts | 12 + .../dto/automation-entity-type-filter.dto.ts | 24 + .../dto/automation-entity-type.dto.ts | 84 + .../dto/create-automation-entity-type.dto.ts | 20 + .../dto/entity-type-action.dto.ts | 65 + .../dto/entity-type-condition.dto.ts | 19 + .../automation-entity-type/dto/index.ts | 7 + .../dto/update-automation-entity-type.dto.ts | 22 + .../entities/automation-entity-type.entity.ts | 120 + .../automation-entity-type/entities/index.ts | 1 + .../enums/change-stage-type.enum.ts | 5 + .../enums/deadline-type.enum.ts | 8 + .../enums/entity-type-action-type.enum.ts | 33 + .../enums/entity-type-trigger.enum.ts | 5 + .../automation-entity-type/enums/index.ts | 4 + .../helpers/action.helper.ts | 30 + .../automation-entity-type/helpers/index.ts | 1 + .../automation-entity-type/index.ts | 7 + .../templates/simple_automation.bpmn.template | 181 + .../automation-http.controller.ts | 25 + .../automation-http.handler.ts | 46 + .../automation-http.service.ts | 93 + .../dto/action-http-call-settings.dto.ts | 25 + .../automation/automation-http/dto/index.ts | 2 + .../automation-http/dto/process-data.dto.ts | 8 + .../enums/http-action-type.enum.ts | 3 + .../automation/automation-http/enums/index.ts | 1 + .../automation/automation-http/index.ts | 5 + .../automation-process.controller.ts | 91 + .../automation-process.handler.ts | 45 + .../automation-process.service.ts | 249 + .../dto/automation-process-filter.dto.ts | 31 + .../dto/automation-process.dto.ts | 44 + .../dto/create-automation-process.dto.ts | 11 + .../automation-process/dto/index.ts | 4 + .../dto/update-automation-process.dto.ts | 7 + .../entities/automation-process.entity.ts | 122 + .../automation-process/entities/index.ts | 1 + .../errors/automation-process.error.ts | 13 + .../automation-process/errors/index.ts | 1 + .../automation/automation-process/index.ts | 7 + .../automation-process/types/index.ts | 1 + .../types/readonly-process.ts | 3 + .../automation-utils.controller.ts | 35 + .../automation/automation-utils/index.ts | 1 + .../modules/automation/automation.module.ts | 54 + .../decorators/automation-worker.decorator.ts | 5 + .../automation/common/decorators/index.ts | 2 + .../decorators/on-automation-job.decorator.ts | 5 + .../common/dto/action/action-settings.dto.ts | 9 + .../automation/common/dto/action/index.ts | 1 + .../automation-entity-condition.dto.ts | 24 + .../automation-field-condition.dto.ts | 10 + .../automation/common/dto/condition/index.ts | 2 + .../modules/automation/common/dto/index.ts | 2 + .../enums/automation-process-type.enum.ts | 4 + .../modules/automation/common/enums/index.ts | 1 + .../events/automation-event-type.enum.ts | 6 + .../automation/common/events/core/index.ts | 2 + .../common/events/core/send-message.event.ts | 11 + .../common/events/core/send-signal.event.ts | 11 + .../entity-type/entity-type-apply.event.ts | 17 + .../common/events/entity-type/index.ts | 1 + .../modules/automation/common/events/index.ts | 4 + .../automation/common/events/process/index.ts | 1 + .../events/process/process-start.event.ts | 13 + .../src/modules/automation/common/index.ts | 7 + .../automation-handler.interface.ts | 17 + .../automation/common/interfaces/index.ts | 1 + .../modules/automation/common/types/index.ts | 2 + .../common/types/message.interface.ts | 5 + .../common/types/signal.interface.ts | 7 + .../common/utils/automation-condition.util.ts | 147 + .../common/utils/automation-delay.util.ts | 5 + .../modules/automation/common/utils/index.ts | 2 + .../automation/config/automation.config.ts | 12 + backend/src/modules/automation/index.ts | 5 + .../config/data-enrichment.config.ts | 14 + .../data-enrichment.controller.ts | 46 + .../data-enrichment/data-enrichment.module.ts | 15 + .../data-enrichment.service.ts | 206 + .../dto/bank-requisites.dto.ts | 51 + .../modules/data-enrichment/dto/fio.dto.ts | 19 + .../src/modules/data-enrichment/dto/index.ts | 3 + .../organization-requisites-address.dto.ts | 9 + .../organization-requisites-managment.dto.ts | 19 + .../dto/organization-requisites-name.dto.ts | 14 + .../dto/organization-requisites-state.dto.ts | 21 + .../dto/organization-requisites.dto.ts | 140 + .../dto/phone-user-info.dto.ts | 20 + .../modules/data-enrichment/enums/index.ts | 4 + .../data-enrichment/enums/opf-type.enum.ts | 18 + .../enums/org-branch-type.enum.ts | 6 + .../data-enrichment/enums/org-status.enum.ts | 12 + .../data-enrichment/enums/org-type.enum.ts | 6 + .../get-ru-country-name-by-iso-code.helper.ts | 23 + ...et-utc-offset-by-ru-country-name.helper.ts | 21 + .../modules/data-enrichment/helpers/index.ts | 2 + .../data-enrichment/types/bank-requisites.ts | 56 + .../types/dadata-bank-requisites.ts | 13 + .../types/dadata-org-requisites.ts | 45 + .../types/dadata-suggestion.ts | 5 + .../types/dadata-suggestions.ts | 5 + .../modules/data-enrichment/types/index.ts | 9 + .../types/organization-requisites.ts | 172 + .../types/phone-user-info-fincalculator.ts | 25 + .../types/phone-user-info-geohelper.ts | 113 + .../types/phone-user-info-sp-nova.ts | 30 + .../data-enrichment/types/phone-user-info.ts | 24 + .../common/errors/document-template.error.ts | 9 + .../modules/documents/common/errors/index.ts | 1 + backend/src/modules/documents/common/index.ts | 1 + .../documents/config/documents.config.ts | 12 + backend/src/modules/documents/config/index.ts | 1 + .../document-generation.controller.ts | 36 + .../document-generation.service.ts | 499 + .../dto/check-document-missing-field.dto.ts | 16 + .../dto/check-document-result.dto.ts | 20 + .../dto/check-document.dto.ts | 17 + .../dto/create-document.dto.ts | 11 + .../document-generation/dto/index.ts | 4 + .../enums/document-type.enum.ts | 4 + .../document-generation/enums/index.ts | 2 + .../enums/russian-case.enum.ts | 8 + .../documents/document-generation/index.ts | 4 + .../document-generation/types/index.ts | 1 + .../document-generation/types/russian-name.ts | 30 + .../document-template.controller.ts | 65 + .../document-template.service.ts | 149 + .../dto/create-document-template.dto.ts | 21 + .../dto/document-template-info.dto.ts | 14 + .../dto/document-template.dto.ts | 39 + .../documents/document-template/dto/index.ts | 4 + .../dto/update-document-template.dto.ts | 21 + .../document-template-access.entity.ts | 19 + .../document-template-entity-type.entity.ts | 19 + .../entities/document-template.entity.ts | 39 + .../document-template/entities/index.ts | 3 + .../documents/document-template/index.ts | 4 + .../src/modules/documents/documents.module.ts | 35 + backend/src/modules/documents/index.ts | 4 + .../dto/create-entity-event.dto.ts | 5 + .../dto/delete-entity-event.dto.ts | 5 + .../entity-event/dto/entity-event-data.dto.ts | 27 + .../entity-event/dto/entity-event-item.dto.ts | 27 + .../entity-event/dto/entity-event.dto.ts | 33 + .../dto/get-entity-event.result.ts | 17 + .../modules/entity/entity-event/dto/index.ts | 7 + .../dto/update-entity-event.dto.ts | 29 + .../entities/entity-event.entity.ts | 50 + .../entity/entity-event/entities/index.ts | 1 + .../entity-event/entity-event.controller.ts | 31 + .../entity-event/entity-event.handler.ts | 269 + .../entity-event/entity-event.module.ts | 31 + .../entity-event/entity-event.service.ts | 261 + .../enums/entity-event-filter.enum.ts | 13 + .../enums/entity-event-type.enum.ts | 12 + .../entity/entity-event/enums/index.ts | 2 + .../common/enums/field-type.enum.ts | 37 + .../entity/entity-field/common/enums/index.ts | 1 + .../common/events/field-event-type.enum.ts | 5 + .../common/events/field/field.event.ts | 15 + .../entity-field/common/events/field/index.ts | 1 + .../entity-field/common/events/index.ts | 2 + .../entity/entity-field/common/index.ts | 3 + .../entity-field/common/utils/formula.util.ts | 20 + .../entity/entity-field/common/utils/index.ts | 1 + .../entity-field/entity-field.module.ts | 34 + .../field-group/dto/create-field-group.dto.ts | 11 + .../field-group/dto/field-group.dto.ts | 27 + .../entity-field/field-group/dto/index.ts | 3 + .../field-group/dto/update-field-group.dto.ts | 12 + .../entities/field-group.entity.ts | 70 + .../field-group/entities/index.ts | 1 + .../enums/field-group-code.enum.ts | 5 + .../entity-field/field-group/enums/index.ts | 1 + .../field-group/field-group.service.ts | 95 + .../entity/entity-field/field-group/index.ts | 3 + .../dto/create-field-option.dto.ts | 11 + .../field-option/dto/field-option.dto.ts | 32 + .../entity-field/field-option/dto/index.ts | 3 + .../dto/update-field-option.dto.ts | 19 + .../entities/field-option.entity.ts | 63 + .../field-option/entities/index.ts | 1 + .../field-option/field-option.service.ts | 122 + .../entity/entity-field/field-option/index.ts | 3 + .../field-settings/dto/field-settings.dto.ts | 42 + .../entity-field/field-settings/dto/index.ts | 2 + .../dto/update-field-settings.dto.ts | 5 + .../entities/field-stage-settings.entity.ts | 38 + .../entities/field-user-settings.entity.ts | 28 + .../field-settings/entities/index.ts | 2 + .../field-settings/enums/field-access.enum.ts | 7 + .../field-settings/enums/index.ts | 1 + .../field-settings.controller.ts | 37 + .../field-settings/field-settings.service.ts | 172 + .../entity-field/field-settings/index.ts | 6 + .../field-settings/types/field-settings.ts | 33 + .../field-settings/types/index.ts | 1 + .../field-value/dto/create-field-value.dto.ts | 5 + .../field-value/dto/field-value.dto.ts | 34 + .../entity-field/field-value/dto/index.ts | 4 + .../field-value/dto/simple-field-value.dto.ts | 36 + .../field-value/dto/update-field-value.dto.ts | 12 + .../entities/field-value.entity.ts | 91 + .../field-value/entities/index.ts | 1 + .../field-value/field-value.handler.ts | 15 + .../field-value/field-value.service.ts | 515 + .../entity/entity-field/field-value/index.ts | 5 + .../field-value/types/field-payload.ts | 24 + .../entity-field/field-value/types/index.ts | 1 + .../field/dto/check-formula.dto.ts | 16 + .../field/dto/create-field.dto.ts | 27 + .../entity-field/field/dto/field.dto.ts | 83 + .../field/dto/fields-settings.dto.ts | 11 + .../entity/entity-field/field/dto/index.ts | 5 + .../field/dto/update-field.dto.ts | 25 + .../field/entities/field.entity.ts | 119 + .../entity-field/field/entities/index.ts | 1 + .../field/enums/field-code.enum.ts | 15 + .../field/enums/field-format.enum.ts | 5 + .../entity/entity-field/field/enums/index.ts | 2 + .../errors/duplicate-field-name.error.ts | 13 + .../errors/field-used-in-formula.error.ts | 13 + .../formula-circular-dependency.error.ts | 13 + .../entity/entity-field/field/errors/index.ts | 3 + .../entity-field/field/field.controller.ts | 34 + .../entity-field/field/field.service.ts | 426 + .../entity/entity-field/field/index.ts | 6 + .../field/types/expandable-field.ts | 1 + .../entity/entity-field/field/types/index.ts | 1 + .../entity/entity-info/dto/entity-info.dto.ts | 64 + .../modules/entity/entity-info/dto/index.ts | 1 + .../entity-info/entity-info.controller.ts | 25 + .../entity/entity-info/entity-info.module.ts | 16 + .../entity/entity-info/entity-info.service.ts | 95 + .../src/modules/entity/entity-info/index.ts | 4 + .../dto/create-entity-stage-history.dto.ts | 16 + .../dto/entity-stage-history.dto.ts | 25 + .../entities/entity-stage-history.entity.ts | 44 + .../entity-stage-history.handler.ts | 29 + .../entity-stage-history.module.ts | 12 + .../entity-stage-history.service.ts | 22 + backend/src/modules/entity/entity.module.ts | 11 + backend/src/modules/forms/forms.module.ts | 59 + .../forms/site-form-builder/dto/index.ts | 18 + .../dto/public-site-form-consent.dto.ts | 28 + ...public-site-form-field-entity-field.dto.ts | 27 + ...ublic-site-form-field-schedule-date.dto.ts | 9 + ...ublic-site-form-field-schedule-spot.dto.ts | 12 + ...ublic-site-form-field-schedule-time.dto.ts | 11 + .../public-site-form-field-schedule.dto.ts | 11 + .../dto/public-site-form-field.dto.ts | 63 + .../dto/public-site-form-gratitude.dto.ts | 18 + .../dto/public-site-form-option.dto.ts | 21 + .../dto/public-site-form-page.dto.ts | 23 + .../dto/public-site-form.dto.ts | 45 + .../dto/site-form-analytic-data.dto.ts | 11 + .../dto/site-form-data-plain.dto.ts | 11 + .../dto/site-form-data.dto.ts | 25 + .../dto/site-form-field-data.dto.ts | 19 + .../dto/site-form-file-upload-request.ts | 6 + .../dto/site-form-file-upload-result.ts | 29 + .../dto/site-form-result.dto.ts | 13 + .../modules/forms/site-form-builder/index.ts | 3 + .../site-form-builder.controller.ts | 109 + .../site-form-builder.service.ts | 577 + .../dto/create-site-form-consent.dto.ts | 11 + .../forms/site-form-consent/dto/index.ts | 3 + .../dto/site-form-consent.dto.ts | 32 + .../dto/update-site-form-consent.dto.ts | 7 + .../forms/site-form-consent/entities/index.ts | 1 + .../entities/site-form-consent.entity.ts | 70 + .../modules/forms/site-form-consent/index.ts | 4 + .../site-form-consent.controller.ts | 50 + .../site-form-consent.service.ts | 68 + .../dto/create-site-form-field.dto.ts | 12 + .../forms/site-form-field/dto/index.ts | 5 + .../dto/site-form-field-entity-field.dto.ts | 22 + .../dto/site-form-field-entity-name.dto.ts | 8 + .../dto/site-form-field.dto.ts | 50 + .../dto/update-site-form-field.dto.ts | 12 + .../forms/site-form-field/entities/index.ts | 1 + .../entities/site-form-field.entity.ts | 142 + .../forms/site-form-field/enums/index.ts | 1 + .../enums/site-form-field-type.enum.ts | 9 + .../modules/forms/site-form-field/index.ts | 5 + .../site-form-field.controller.ts | 65 + .../site-form-field.service.ts | 102 + .../dto/create-site-form-gratitude.dto.ts | 9 + .../forms/site-form-gratitude/dto/index.ts | 3 + .../dto/site-form-gratitude.dto.ts | 22 + .../dto/update-site-form-gratitude.dto.ts | 7 + .../site-form-gratitude/entities/index.ts | 1 + .../entities/site-form-gratitude.entity.ts | 45 + .../forms/site-form-gratitude/index.ts | 4 + .../site-form-gratitude.controller.ts | 50 + .../site-form-gratitude.service.ts | 68 + .../dto/create-site-form-page.dto.ts | 14 + .../modules/forms/site-form-page/dto/index.ts | 3 + .../site-form-page/dto/site-form-page.dto.ts | 25 + .../dto/update-site-form-page.dto.ts | 22 + .../forms/site-form-page/entities/index.ts | 1 + .../entities/site-form-page.entity.ts | 57 + .../src/modules/forms/site-form-page/index.ts | 4 + .../site-form-page.controller.ts | 85 + .../site-form-page/site-form-page.service.ts | 131 + .../site-form-page/types/expandable-field.ts | 1 + .../forms/site-form-page/types/index.ts | 1 + .../site-form/dto/create-site-form.dto.ts | 45 + .../src/modules/forms/site-form/dto/index.ts | 5 + .../dto/site-form-entity-type.dto.ts | 17 + .../site-form/dto/site-form-schedule.dto.ts | 8 + .../forms/site-form/dto/site-form.dto.ts | 109 + .../site-form/dto/update-site-form.dto.ts | 62 + .../modules/forms/site-form/entities/index.ts | 3 + .../entities/site-form-entity-type.entity.ts | 44 + .../entities/site-form-schedule.entity.ts | 35 + .../site-form/entities/site-form.entity.ts | 204 + .../modules/forms/site-form/enums/index.ts | 1 + .../site-form/enums/site-form-type.enum.ts | 4 + backend/src/modules/forms/site-form/index.ts | 5 + .../modules/forms/site-form/services/index.ts | 3 + .../services/site-form-entity-type.service.ts | 91 + .../services/site-form-schedule.service.ts | 87 + .../site-form/services/site-form.service.ts | 153 + .../forms/site-form/site-form.controller.ts | 81 + .../forms/site-form/types/expandable-field.ts | 1 + .../modules/forms/site-form/types/index.ts | 1 + .../frontend-event/frontend-event.gateway.ts | 50 + .../frontend-event/frontend-event.module.ts | 9 + .../frontend-event/frontend-event.service.ts | 122 + .../dto/create-frontend-object.dto.ts | 5 + .../dto/frontend-object-filter.dto.ts | 8 + .../dto/frontend-object.dto.ts | 16 + .../src/modules/frontend-object/dto/index.ts | 3 + .../entities/frontend-object.entity.ts | 35 + .../modules/frontend-object/entities/index.ts | 1 + .../frontend-object.controller.ts | 65 + .../frontend-object/frontend-object.module.ts | 15 + .../frontend-object.service.ts | 30 + .../account-api-access.controller.ts | 47 + .../account-api-access.service.ts | 53 + .../dto/account-api-access.dto.ts | 12 + .../iam/account-api-access/dto/index.ts | 1 + .../entities/account-api-access.entity.ts | 29 + .../iam/account-api-access/entities/index.ts | 1 + .../guards/api-access.guard.ts | 42 + .../iam/account-api-access/guards/index.ts | 1 + .../modules/iam/account-api-access/index.ts | 3 + .../account-settings.controller.ts | 29 + .../account-settings.service.ts | 55 + .../dto/account-settings.dto.ts | 66 + .../dto/create-account-settings.dto.ts | 4 + .../modules/iam/account-settings/dto/index.ts | 3 + .../dto/update-account-settings.dto.ts | 4 + .../entities/account-settings.entity.ts | 141 + .../iam/account-settings/entities/index.ts | 1 + .../src/modules/iam/account-settings/index.ts | 4 + .../account-subscription.controller.ts | 41 + .../account-subscription.service.ts | 88 + .../dto/account-subscription.dto.ts | 37 + .../dto/create-account-subscription.dto.ts | 8 + .../iam/account-subscription/dto/index.ts | 3 + .../dto/update-account-subscription.dto.ts | 9 + .../entities/account-subscription.entity.ts | 99 + .../account-subscription/entities/index.ts | 1 + .../modules/iam/account-subscription/index.ts | 4 + .../modules/iam/account/account.controller.ts | 76 + .../modules/iam/account/account.service.ts | 237 + .../modules/iam/account/dto/account.dto.ts | 25 + .../iam/account/dto/create-account.dto.ts | 42 + .../iam/account/dto/find-filter.dto.ts | 9 + backend/src/modules/iam/account/dto/index.ts | 3 + .../iam/account/entities/account.entity.ts | 48 + .../src/modules/iam/account/entities/index.ts | 1 + .../iam/account/types/expandable-field.ts | 1 + .../src/modules/iam/account/types/index.ts | 1 + .../authentication.controller.ts | 74 + .../authentication/authentication.service.ts | 194 + .../dto/decode-logic-link.dto.ts | 5 + .../modules/iam/authentication/dto/index.ts | 6 + .../iam/authentication/dto/jwt-token.ts | 33 + .../iam/authentication/dto/login-link.dto.ts | 17 + .../dto/recovery-user-password.dto.ts | 8 + .../dto/reset-user-password.dto.ts | 12 + .../iam/authentication/dto/user-login.dto.ts | 12 + .../iam/authentication/errors/index.ts | 1 + .../errors/invalid-login-link.error.ts | 8 + .../modules/iam/authentication/types/index.ts | 1 + .../types/recovery-token-payload.ts | 4 + .../authorization/authorization.service.ts | 123 + .../src/modules/iam/authorization/index.ts | 1 + .../api-access-required.decorator.ts | 7 + .../auth-data-prefetch.decorator.ts | 4 + .../decorators/current-auth.decorator.ts | 18 + .../decorators/ga-client-id.decorator.ts | 10 + .../modules/iam/common/decorators/index.ts | 6 + .../decorators/jwt-authorized.decorator.ts | 26 + .../decorators/user-access.decorator.ts | 5 + backend/src/modules/iam/common/enums/index.ts | 4 + .../common/enums/permission-action.enum.ts | 8 + .../iam/common/enums/permission-level.enum.ts | 7 + .../iam/common/enums/phone-format.enum.ts | 4 + .../iam/common/enums/user-role.enum.ts | 6 + .../src/modules/iam/common/errors/index.ts | 4 + .../common/errors/invalid-subdomain.error.ts | 12 + .../iam/common/errors/token-expired.error.ts | 8 + .../common/errors/token-not-found.error.ts | 8 + .../common/errors/token-not-passed.error.ts | 8 + .../events/account/account-created.event.ts | 39 + .../iam/common/events/account/index.ts | 1 + .../department/department-deleted.event.ts | 11 + .../iam/common/events/department/index.ts | 1 + .../iam/common/events/iam-event-type.enum.ts | 8 + .../src/modules/iam/common/events/index.ts | 4 + .../modules/iam/common/events/user/index.ts | 4 + .../common/events/user/user-created.event.ts | 9 + .../common/events/user/user-deleted.event.ts | 11 + .../common/events/user/user-login.event.ts | 15 + .../user/user-password-recovery.event.ts | 11 + .../src/modules/iam/common/guards/index.ts | 2 + .../iam/common/guards/jwt-token.guard.ts | 52 + .../iam/common/guards/user-access.guard.ts | 39 + backend/src/modules/iam/common/index.ts | 8 + .../interceptors/auth-data.interceptor.ts | 35 + .../modules/iam/common/interceptors/index.ts | 1 + .../interfaces/authorizable.interface.ts | 5 + .../modules/iam/common/interfaces/index.ts | 1 + .../src/modules/iam/common/types/auth-data.ts | 11 + .../iam/common/types/authorizable-object.ts | 8 + .../modules/iam/common/types/data-prefetch.ts | 4 + backend/src/modules/iam/common/types/index.ts | 7 + .../iam/common/types/simple-authorizable.ts | 10 + .../modules/iam/common/types/token-payload.ts | 7 + .../iam/common/types/user-access-options.ts | 6 + .../modules/iam/common/types/user-rights.ts | 23 + .../department-settings.controller.ts | 67 + .../department-settings.service.ts | 84 + .../dto/create-department-settings.dto.ts | 6 + .../dto/department-settings.dto.ts | 28 + .../iam/department-settings/dto/index.ts | 3 + .../dto/update-department-settings.dto.ts | 6 + .../entities/department-settings.entity.ts | 73 + .../iam/department-settings/entities/index.ts | 1 + .../modules/iam/department-settings/index.ts | 4 + .../iam/department/department.controller.ts | 61 + .../iam/department/department.service.ts | 172 + .../department/dto/create-department.dto.ts | 11 + .../department/dto/delete-department.dto.ts | 9 + .../iam/department/dto/department.dto.ts | 31 + .../src/modules/iam/department/dto/index.ts | 4 + .../department/dto/update-department.dto.ts | 11 + .../department/entities/department.entity.ts | 67 + .../modules/iam/department/entities/index.ts | 1 + backend/src/modules/iam/department/index.ts | 4 + .../iam/department/types/expandable-field.ts | 1 + .../src/modules/iam/department/types/index.ts | 1 + backend/src/modules/iam/iam.module.ts | 120 + .../iam/object-permission/dto/index.ts | 1 + .../dto/object-permission.dto.ts | 41 + .../iam/object-permission/entities/index.ts | 1 + .../entities/object-permission.entity.ts | 168 + .../iam/object-permission/errors/index.ts | 1 + .../errors/permission-not-valid.error.ts | 9 + .../modules/iam/object-permission/index.ts | 4 + .../object-permission.service.ts | 146 + .../dto/current-discount.dto.ts | 17 + .../iam/subscription-discount/dto/index.ts | 1 + .../subscription-discount/entities/index.ts | 1 + .../entities/subscription-discount.entity.ts | 26 + .../iam/subscription-discount/index.ts | 6 + ...public-subscription-discount.controller.ts | 26 + .../subscription-discount.controller.ts | 24 + .../subscription-discount.service.ts | 60 + .../types/current-discount.ts | 21 + .../iam/subscription-discount/types/index.ts | 1 + .../modules/iam/user-calendar/dto/index.ts | 2 + .../dto/user-calendar-interval.dto.ts | 16 + .../user-calendar/dto/user-calendar.dto.ts | 25 + .../iam/user-calendar/entities/index.ts | 2 + .../entities/user-calendar-interval.entity.ts | 51 + .../entities/user-calendar.entity.ts | 76 + .../src/modules/iam/user-calendar/index.ts | 4 + .../iam/user-calendar/services/index.ts | 2 + .../user-calendar-interval.service.ts | 70 + .../services/user-calendar.service.ts | 103 + .../user-calendar/user-calendar.controller.ts | 59 + .../src/modules/iam/user-profile/dto/index.ts | 2 + .../dto/update-user-profile.dto.ts | 5 + .../iam/user-profile/dto/user-profile.dto.ts | 28 + .../iam/user-profile/entities/index.ts | 1 + .../entities/user-profile.entity.ts | 62 + .../user-profile/user-profile.controller.ts | 37 + .../iam/user-profile/user-profile.service.ts | 47 + .../user-token/dto/create-user-token.dto.ts | 4 + .../src/modules/iam/user-token/dto/index.ts | 3 + .../user-token/dto/user-access-token.dto.ts | 13 + .../iam/user-token/dto/user-token.dto.ts | 26 + .../modules/iam/user-token/entities/index.ts | 1 + .../user-token/entities/user-token.entity.ts | 71 + backend/src/modules/iam/user-token/index.ts | 5 + .../src/modules/iam/user-token/types/index.ts | 1 + .../iam/user-token/types/user-access-token.ts | 19 + .../iam/user-token/user-token.controller.ts | 52 + .../iam/user-token/user-token.service.ts | 99 + .../iam/user/dto/change-user-password.dto.ts | 12 + .../modules/iam/user/dto/create-user.dto.ts | 26 + backend/src/modules/iam/user/dto/index.ts | 5 + .../modules/iam/user/dto/update-user.dto.ts | 24 + .../iam/user/dto/user-find-filter.dto.ts | 9 + backend/src/modules/iam/user/dto/user.dto.ts | 70 + .../src/modules/iam/user/entities/index.ts | 2 + .../modules/iam/user/entities/user.entity.ts | 181 + .../entities/users-accessible-users.entity.ts | 15 + .../iam/user/errors/bad-credentials.error.ts | 9 + .../iam/user/errors/email-occupied.error.ts | 21 + backend/src/modules/iam/user/errors/index.ts | 3 + .../iam/user/errors/user-not-active.error.ts | 17 + .../iam/user/types/expandable-field.ts | 1 + backend/src/modules/iam/user/types/index.ts | 1 + .../src/modules/iam/user/user.controller.ts | 155 + backend/src/modules/iam/user/user.handler.ts | 19 + backend/src/modules/iam/user/user.service.ts | 363 + backend/src/modules/iam/working-time/index.ts | 1 + .../iam/working-time/working-time.service.ts | 90 + .../integration/appsumo/appsumo.controller.ts | 29 + .../integration/appsumo/appsumo.module.ts | 18 + .../integration/appsumo/appsumo.service.ts | 196 + .../appsumo/config/appsumo.config.ts | 16 + .../integration/appsumo/config/index.ts | 1 + .../entities/appsumo-license.entity.ts | 75 + .../appsumo/entities/appsumo-tier.entity.ts | 26 + .../integration/appsumo/entities/index.ts | 2 + .../appsumo/enums/appsumo-event-type.enum.ts | 7 + .../enums/appsumo-license-status.enum.ts | 5 + .../integration/appsumo/enums/index.ts | 2 + .../src/modules/integration/appsumo/index.ts | 6 + .../appsumo/types/appsumo-license-response.ts | 7 + .../appsumo/types/appsumo-token-response.ts | 8 + .../appsumo/types/appsumo-webhook-request.ts | 14 + .../appsumo/types/appsumo-webhook-response.ts | 7 + .../integration/appsumo/types/index.ts | 4 + .../integration/google/auth/auth.module.ts | 9 + .../integration/google/auth/auth.service.ts | 74 + .../google/calendar/calendar.controller.ts | 101 + .../google/calendar/calendar.emitter.ts | 119 + .../google/calendar/calendar.handler.ts | 179 + .../google/calendar/calendar.module.ts | 25 + .../google/calendar/calendar.service.ts | 867 + .../calendar/dto/calendar-access.dto.ts | 13 + .../google/calendar/dto/calendar-info.dto.ts | 35 + .../dto/create-google-calendar.dto.ts | 24 + .../dto/google-calendar-linked.dto.ts | 14 + .../calendar/dto/google-calendar.dto.ts | 52 + .../integration/google/calendar/dto/index.ts | 6 + .../dto/update-google-calendar.dto.ts | 7 + .../google-calendar-account.entity.ts | 57 + .../entities/google-calendar-linked.entity.ts | 50 + .../entities/google-calendar.entity.ts | 160 + .../google/calendar/entities/index.ts | 3 + .../calendar/enums/calendar-type.enum.ts | 4 + .../google/calendar/enums/index.ts | 1 + .../calendar/public-calendar.controller.ts | 36 + .../google/calendar/types/calendar-access.ts | 19 + .../google/calendar/types/calendar-event.ts | 10 + .../google/calendar/types/calendar-info.ts | 46 + .../calendar/types/calendar-upsert-event.ts | 12 + .../types/event-extended-properties.ts | 4 + .../google/calendar/types/index.ts | 5 + .../google/calendar/utils/appointment.util.ts | 30 + .../google/calendar/utils/event.util.ts | 45 + .../google/calendar/utils/index.ts | 2 + .../integration/google/google.config.ts | 20 + .../integration/google/google.module.ts | 10 + .../modules/integration/integration.module.ts | 12 + .../modules/integration/stripe/dto/index.ts | 4 + .../stripe/dto/subscription-feature.dto.ts | 12 + .../stripe/dto/subscription-order.dto.ts | 27 + .../stripe/dto/subscription-plan.dto.ts | 54 + .../stripe/dto/subscription-price.dto.ts | 20 + .../stripe/public-stripe.controller.ts | 25 + .../integration/stripe/stripe.controller.ts | 31 + .../integration/stripe/stripe.module.ts | 14 + .../integration/stripe/stripe.service.ts | 219 + .../modules/inventory/common/enums/index.ts | 1 + .../enums/permission-object-type.enum.ts | 6 + .../modules/inventory/common/events/index.ts | 5 + .../common/events/product-order/index.ts | 2 + .../product-order-created.event.ts | 10 + .../product-order/product-order.event.ts | 11 + .../common/events/products-event-type.enum.ts | 11 + .../common/events/products-section/index.ts | 1 + .../products-section.event.ts | 9 + .../common/events/rental-order/index.ts | 2 + .../rental-order-created.event.ts | 10 + .../events/rental-order/rental-order.event.ts | 11 + .../inventory/common/events/shipment/index.ts | 4 + .../events/shipment/shipment-created.event.ts | 13 + .../events/shipment/shipment-deleted.event.ts | 11 + .../shipment/shipment-status-changed.event.ts | 11 + .../common/events/shipment/shipment.event.ts | 13 + backend/src/modules/inventory/common/index.ts | 2 + .../inventory-reporting/dto/index.ts | 4 + .../dto/inventory-report-filter.dto.ts | 50 + .../dto/inventory-report-row.dto.ts | 70 + .../dto/inventory-report-user-cell.dto.ts | 16 + .../dto/inventory-report.dto.ts | 17 + .../inventory-reporting/enums/index.ts | 1 + .../enums/products-report-type.enum.ts | 5 + .../inventory-reporting.controller.ts | 24 + .../inventory-reporting.module.ts | 25 + .../inventory-reporting.service.ts | 351 + .../inventory-reporting/types/index.ts | 3 + .../types/inventory-report-row.ts | 135 + .../types/inventory-report-user-cell.ts | 20 + .../types/inventory-report.ts | 30 + .../src/modules/inventory/inventory.module.ts | 47 + .../dto/create-order-status.dto.ts | 23 + .../inventory/order-status/dto/index.ts | 2 + .../order-status/dto/order-status.dto.ts | 35 + .../inventory/order-status/entities/index.ts | 1 + .../entities/order-status.entity.ts | 42 + .../inventory/order-status/enums/index.ts | 1 + .../enums/order-status-code.enum.ts | 7 + .../order-status/order-status.controller.ts | 25 + .../order-status/order-status.module.ts | 16 + .../order-status/order-status.service.ts | 62 + .../inventory/order/dto/create-order.dto.ts | 13 + .../src/modules/inventory/order/dto/index.ts | 5 + .../inventory/order/dto/order-filter.dto.ts | 8 + .../inventory/order/dto/order-item.dto.ts | 67 + .../modules/inventory/order/dto/order.dto.ts | 106 + .../inventory/order/dto/update-order.dto.ts | 12 + .../modules/inventory/order/entities/index.ts | 2 + .../order/entities/order-item.entity.ts | 129 + .../inventory/order/entities/order.entity.ts | 176 + .../modules/inventory/order/helper/index.ts | 1 + .../inventory/order/helper/order.helper.ts | 30 + .../inventory/order/order.controller.ts | 90 + .../modules/inventory/order/order.module.ts | 33 + .../order/services/order-item.service.ts | 80 + .../inventory/order/services/order.handler.ts | 27 + .../inventory/order/services/order.service.ts | 376 + .../inventory/order/types/expandable-field.ts | 1 + .../modules/inventory/order/types/index.ts | 1 + .../dto/create-product-category.dto.ts | 4 + .../inventory/product-category/dto/index.ts | 3 + .../dto/product-category.dto.ts | 33 + .../dto/update-product-category.dto.ts | 4 + .../product-category/entities/index.ts | 1 + .../entities/product-category.entity.ts | 83 + .../product-category.controller.ts | 60 + .../product-category.module.ts | 16 + .../product-category.service.ts | 142 + .../dto/create-product-price.dto.ts | 4 + .../inventory/product-price/dto/index.ts | 3 + .../product-price/dto/product-price.dto.ts | 36 + .../dto/update-product-price.dto.ts | 4 + .../inventory/product-price/entities/index.ts | 1 + .../entities/product-price.entity.ts | 70 + .../inventory/product-price/errors/index.ts | 1 + .../errors/invalid-discount.error.ts | 9 + .../product-price/product-price.controller.ts | 56 + .../product-price/product-price.module.ts | 16 + .../product-price/product-price.service.ts | 127 + .../dto/create-product-stock.dto.ts | 4 + .../inventory/product-stock/dto/index.ts | 4 + .../product-stock/dto/product-stock.dto.ts | 20 + .../dto/update-product-stock.dto.ts | 15 + .../dto/update-product-stocks.dto.ts | 10 + .../inventory/product-stock/entities/index.ts | 1 + .../entities/product-stock.entity.ts | 48 + .../product-stock/product-stock.controller.ts | 30 + .../product-stock/product-stock.module.ts | 19 + .../product-stock/product-stock.service.ts | 188 + .../product/dto/create-product.dto.ts | 28 + .../product/dto/file-upload-request.ts | 6 + .../product/dto/get-products.meta.ts | 12 + .../product/dto/get-products.result.ts | 20 + .../modules/inventory/product/dto/index.ts | 8 + .../inventory/product/dto/product-info.dto.ts | 17 + .../inventory/product/dto/product.dto.ts | 113 + .../inventory/product/dto/products-filter.ts | 29 + .../product/dto/update-product.dto.ts | 11 + .../inventory/product/entities/index.ts | 1 + .../product/entities/product.entity.ts | 148 + .../modules/inventory/product/enums/index.ts | 1 + .../product/enums/product-type.enum.ts | 5 + .../inventory/product/product.controller.ts | 122 + .../inventory/product/product.module.ts | 32 + .../inventory/product/product.service.ts | 298 + .../dto/create-products-section.dto.ts | 8 + .../inventory/products-section/dto/index.ts | 5 + .../products-section/dto/link-modules.dto.ts | 14 + .../dto/products-section-entity-type.dto.ts | 17 + .../dto/products-section.dto.ts | 69 + .../dto/update-products-section.dto.ts | 9 + .../products-section/entities/index.ts | 2 + .../products-section-entity-type.entity.ts | 29 + .../entities/products-section.entity.ts | 122 + .../inventory/products-section/enums/index.ts | 1 + .../enums/products-section-type.enum.ts | 4 + .../products-section.controller.ts | 85 + .../products-section.module.ts | 28 + .../products-section-linker.service.ts | 47 + .../services/products-section.service.ts | 168 + .../dto/rental-interval.dto.ts | 20 + .../entities/rental-interval-type.enum.ts | 3 + .../entities/rental-interval.entity.ts | 44 + .../rental-interval.controller.ts | 39 + .../rental-interval/rental-interval.module.ts | 16 + .../rental-interval.service.ts | 30 + .../dto/create-rental-order-item.dto.ts | 5 + .../dto/create-rental-order.dto.ts | 23 + .../inventory/rental-order/dto/index.ts | 7 + .../rental-order/dto/rental-order-filter.ts | 16 + .../rental-order/dto/rental-order-item.dto.ts | 37 + .../rental-order/dto/rental-order.dto.ts | 88 + .../dto/update-rental-order-item.dto.ts | 11 + .../dto/update-rental-order.dto.ts | 19 + .../inventory/rental-order/entities/index.ts | 3 + .../entities/rental-order-item.entity.ts | 90 + .../entities/rental-order-period.entity.ts | 36 + .../entities/rental-order.entity.ts | 155 + .../inventory/rental-order/enums/index.ts | 1 + .../enums/rental-order-status.enum.ts | 10 + .../rental-order/rental-order.controller.ts | 93 + .../rental-order/rental-order.module.ts | 29 + .../inventory/rental-order/services/index.ts | 2 + .../services/rental-order-item.service.ts | 96 + .../services/rental-order.service.ts | 342 + .../dto/check-rental-status.dto.ts | 19 + .../inventory/rental-schedule/dto/index.ts | 4 + .../dto/product-rental-status.dto.ts | 25 + .../rental-schedule/dto/rental-event.dto.ts | 54 + .../dto/rental-schedule.dto.ts | 20 + .../rental-schedule/entities/index.ts | 1 + .../entities/rental-event.entity.ts | 83 + .../inventory/rental-schedule/enums/index.ts | 1 + .../enums/rental-schedule-status.enum.ts | 5 + .../rental-schedule.controller.ts | 65 + .../rental-schedule/rental-schedule.module.ts | 25 + .../rental-schedule.service.ts | 358 + .../inventory/rental-schedule/types/index.ts | 2 + .../types/product-rental-status.ts | 23 + .../rental-schedule/types/rental-schedule.ts | 22 + .../inventory/reservation/dto/index.ts | 1 + .../reservation/dto/reservation.dto.ts | 17 + .../inventory/reservation/entities/index.ts | 1 + .../entities/reservation.entity.ts | 64 + .../reservation/reservation.module.ts | 14 + .../reservation/reservation.service.ts | 93 + .../shipment/dto/change-status-query.ts | 9 + .../modules/inventory/shipment/dto/index.ts | 5 + .../shipment/dto/shipment-filter.dto.ts | 11 + .../shipment/dto/shipment-item.dto.ts | 16 + .../shipment/dto/shipment-result.dto.ts | 14 + .../inventory/shipment/dto/shipment.dto.ts | 54 + .../inventory/shipment/entities/index.ts | 2 + .../shipment/entities/shipment-item.entity.ts | 32 + .../shipment/entities/shipment.entity.ts | 112 + .../inventory/shipment/shipment.controller.ts | 52 + .../inventory/shipment/shipment.module.ts | 30 + .../inventory/shipment/shipment.service.ts | 362 + .../modules/inventory/shipment/types/index.ts | 1 + .../shipment/types/shipment-result.ts | 23 + .../warehouse/dto/create-warehouse.dto.ts | 4 + .../modules/inventory/warehouse/dto/index.ts | 3 + .../warehouse/dto/update-warehouse.dto.ts | 4 + .../inventory/warehouse/dto/warehouse.dto.ts | 21 + .../inventory/warehouse/entities/index.ts | 1 + .../warehouse/entities/warehouse.entity.ts | 84 + .../warehouse/warehouse.controller.ts | 70 + .../inventory/warehouse/warehouse.module.ts | 30 + .../inventory/warehouse/warehouse.service.ts | 170 + .../dto/create-mail-message-scheduled.dto.ts | 12 + .../mail/mail-message-scheduled/dto/index.ts | 3 + .../dto/mail-message-scheduled-filter.dto.ts | 49 + .../dto/mail-message-scheduled.dto.ts | 41 + .../mail-message-scheduled/entities/index.ts | 1 + .../entities/mail-message-scheduled.entity.ts | 84 + .../mail/mail-message-scheduled/index.ts | 1 + .../mail-message-scheduled.controller.ts | 67 + .../mail-message-scheduled.handler.ts | 69 + .../mail-message-scheduled.module.ts | 19 + .../mail-message-scheduled.service.ts | 369 + .../imapflow/config/imapflow.config.ts | 16 + .../mail-providers/imapflow/config/index.ts | 1 + .../dto/create-mailbox-imapflow.dto.ts | 12 + .../create-mailbox-settings-imapflow.dto.ts | 17 + .../mail/mail-providers/imapflow/dto/index.ts | 6 + .../imapflow/dto/mailbox-imapflow.dto.ts | 9 + .../dto/mailbox-settings-imapflow.dto.ts | 28 + .../dto/update-mailbox-imapflow.dto.ts | 9 + .../update-mailbox-settings-imapflow.dto.ts | 20 + .../mail-providers/imapflow/entities/index.ts | 1 + .../mailbox-settings-imapflow.entity.ts | 107 + .../mail-providers/imapflow/errors/index.ts | 1 + .../imapflow/errors/mail-connection.error.ts | 9 + .../imapflow/imapflow.controller.ts | 55 + .../imapflow/imapflow.module.ts | 23 + .../imapflow/imapflow.service.ts | 1076 + .../mail/mail-providers/imapflow/index.ts | 7 + .../imapflow/types/imapflow-sync-info.ts | 4 + .../mail-providers/imapflow/types/index.ts | 2 + .../imapflow/types/mailbox-imapflow.ts | 21 + .../src/modules/mail/mail-providers/index.ts | 2 + .../mail-providers/mail-providers.module.ts | 8 + backend/src/modules/mail/mail.module.ts | 9 + .../chat-message-scheduled.controller.ts | 67 + .../chat-message-scheduled.handler.ts | 55 + .../chat-message-scheduled.module.ts | 34 + .../chat-message-scheduled.service.ts | 436 + .../dto/chat-message-scheduled-filter.dto.ts | 44 + .../dto/chat-message-scheduled.dto.ts | 43 + .../dto/create-chat-message-scheduled.dto.ts | 17 + .../chat-message-scheduled/dto/index.ts | 3 + .../entities/chat-message-scheduled.entity.ts | 84 + .../chat-message-scheduled/entities/index.ts | 1 + .../multichat/chat-message-scheduled/index.ts | 6 + .../chat-message/chat-message.controller.ts | 119 + .../chat-message/chat-message.module.ts | 43 + .../chat-message/dto/chat-message-file.dto.ts | 51 + .../dto/chat-message-reaction.dto.ts | 22 + .../dto/chat-message-user-status.dto.ts | 20 + .../chat-message/dto/chat-message.dto.ts | 67 + .../dto/chat-messages-filter.dto.ts | 9 + .../dto/chat-messages-result.dto.ts | 20 + .../multichat/chat-message/dto/index.ts | 7 + .../chat-message/dto/send-chat-message.dto.ts | 18 + .../entities/chat-message-file.entity.ts | 76 + .../entities/chat-message-reaction.entity.ts | 38 + .../chat-message-user-status.entity.ts | 47 + .../entities/chat-message.entity.ts | 119 + .../multichat/chat-message/entities/index.ts | 4 + .../services/chat-message-file.service.ts | 76 + .../services/chat-message-reaction.service.ts | 27 + .../chat-message-user-status.service.ts | 53 + .../services/chat-message.service.ts | 522 + .../services/chat-notification.service.ts | 133 + .../multichat/chat-message/services/index.ts | 5 + .../chat-message/types/expandable-field.ts | 1 + .../multichat/chat-message/types/index.ts | 1 + .../chat-provider-user.module.ts | 12 + .../chat-provider-user.service.ts | 72 + .../entities/chat-provider-user.entity.ts | 24 + .../chat-provider-user/entities/index.ts | 1 + .../enums/chat-provider-user-type.enum.ts | 5 + .../chat-provider-user/enums/index.ts | 1 + .../multichat/chat-provider-user/index.ts | 4 + .../chat-provider/chat-provider.controller.ts | 26 + .../chat-provider/chat-provider.module.ts | 25 + .../const/chat-provider-defaults.ts | 8 + .../multichat/chat-provider/const/index.ts | 1 + .../dto/chat-provider-entity-settings.dto.ts | 44 + .../chat-provider/dto/chat-provider.dto.ts | 56 + .../dto/create-chat-provider.dto.ts | 20 + .../multichat/chat-provider/dto/index.ts | 4 + .../dto/update-chat-provider.dto.ts | 15 + .../chat-provider-entity-settings.entity.ts | 110 + .../entities/chat-provider.entity.ts | 130 + .../multichat/chat-provider/entities/index.ts | 2 + .../modules/multichat/chat-provider/index.ts | 6 + .../chat-provider-entity-settings.service.ts | 70 + .../services/chat-provider.handler.ts | 43 + .../services/chat-provider.service.ts | 329 + .../multichat/chat-provider/services/index.ts | 3 + .../chat-provider/types/expandable-field.ts | 6 + .../multichat/chat-provider/types/index.ts | 1 + .../multichat/chat-user/chat-user.module.ts | 12 + .../multichat/chat-user/chat-user.service.ts | 195 + .../chat-user/dto/chat-user-external.dto.ts | 56 + .../multichat/chat-user/dto/chat-user.dto.ts | 31 + .../modules/multichat/chat-user/dto/index.ts | 2 + .../entities/chat-user-external.entity.ts | 96 + .../chat-user/entities/chat-user.entity.ts | 47 + .../multichat/chat-user/entities/index.ts | 2 + .../src/modules/multichat/chat-user/index.ts | 4 + .../modules/multichat/chat/chat.controller.ts | 231 + .../src/modules/multichat/chat/chat.module.ts | 34 + ...chat-find-by-message-content-filter.dto.ts | 13 + .../chat/dto/chat-find-filter.dto.ts | 30 + .../chat/dto/chat-find-personal-filter.dto.ts | 14 + .../modules/multichat/chat/dto/chat.dto.ts | 73 + .../chat/dto/create-contact-lead.dto.ts | 44 + .../chat/dto/create-external-chat.dto.ts | 33 + .../chat/dto/create-group-chat.dto.ts | 21 + .../chat/dto/create-personal-chat.dto.ts | 12 + .../chat/dto/find-chats-full-result.dto.ts | 20 + .../src/modules/multichat/chat/dto/index.ts | 10 + .../chat/dto/update-group-chat.dto.ts | 19 + .../entities/chat-pinned-message.entity.ts | 25 + .../multichat/chat/entities/chat.entity.ts | 164 + .../modules/multichat/chat/entities/index.ts | 2 + .../services/chat-pinned-message.service.ts | 21 + .../multichat/chat/services/chat.handler.ts | 51 + .../multichat/chat/services/chat.service.ts | 925 + .../modules/multichat/chat/services/index.ts | 3 + .../common/enums/chat-message-status.enum.ts | 4 + .../common/enums/chat-provider-status.enum.ts | 6 + .../enums/chat-provider-transport.enum.ts | 9 + .../common/enums/chat-provider-type.enum.ts | 6 + .../multichat/common/enums/chat-type.enum.ts | 4 + .../common/enums/chat-user-role.enum.ts | 7 + .../modules/multichat/common/enums/index.ts | 6 + .../chat-message-created.event.ts | 27 + .../chat-message-updated.event.ts | 11 + .../events/chat-message/chat-message.event.ts | 11 + .../common/events/chat-message/index.ts | 3 + .../chat-provider/chat-provider.event.ts | 15 + .../common/events/chat-provider/index.ts | 1 + .../common/events/chat/chat.event.ts | 13 + .../multichat/common/events/chat/index.ts | 1 + .../modules/multichat/common/events/index.ts | 4 + .../events/multichat-event-type.enum.ts | 11 + backend/src/modules/multichat/common/index.ts | 2 + .../modules/multichat/multichat.controller.ts | 33 + .../src/modules/multichat/multichat.module.ts | 27 + .../providers/chat-provider-proxy.service.ts | 78 + .../facebook/config/facebook.config.ts | 20 + .../providers/facebook/controllers/index.ts | 2 + .../messenger-provider.controller.ts | 95 + .../public-messenger-provider.controller.ts | 45 + .../dto/create-messenger-provider.dto.ts | 22 + .../multichat/providers/facebook/dto/index.ts | 3 + .../facebook/dto/messenger-provider.dto.ts | 14 + .../dto/update-messenger-provider.dto.ts | 3 + .../chat-provider-messenger.entity.ts | 65 + .../providers/facebook/entities/index.ts | 1 + .../facebook/facebook-provider.module.ts | 29 + .../facebook/messenger-provider.service.ts | 573 + .../multichat/providers/providers.module.ts | 14 + .../providers/twilio/controllers/index.ts | 2 + .../public-twilio-provider.controller.ts | 18 + .../controllers/twilio-provider.controller.ts | 78 + .../twilio/dto/create-twilio-provider.dto.ts | 18 + .../multichat/providers/twilio/dto/index.ts | 3 + .../twilio/dto/twilio-provider.dto.ts | 14 + .../twilio/dto/update-twilio-provider.dto.ts | 19 + .../entities/chat-provider-twilio.entity.ts | 54 + .../providers/twilio/entities/index.ts | 1 + .../twilio/twilio-provider.module.ts | 26 + .../twilio/twilio-provider.service.ts | 311 + .../multichat/providers/twilio/types/index.ts | 1 + .../twilio/types/twilio-request-body.ts | 16 + .../providers/wazzup/config/index.ts | 1 + .../providers/wazzup/config/wazzup.config.ts | 11 + .../providers/wazzup/controllers/index.ts | 2 + .../public-wazzup-provider.controller.ts | 18 + .../controllers/wazzup-provider.controller.ts | 87 + .../wazzup/dto/create-wazzup-provider.dto.ts | 23 + .../multichat/providers/wazzup/dto/index.ts | 4 + .../wazzup/dto/update-wazzup-provider.dto.ts | 3 + .../wazzup/dto/wazzup-channel.dto.ts | 26 + .../wazzup/dto/wazzup-provider.dto.ts | 14 + .../entities/chat-provider-wazzup.entity.ts | 60 + .../providers/wazzup/entities/index.ts | 1 + .../multichat/providers/wazzup/enums/index.ts | 5 + .../wazzup/enums/wazzup-channel-state.enum.ts | 32 + .../wazzup/enums/wazzup-chat-type.enum.ts | 20 + .../enums/wazzup-message-status.enum.ts | 20 + .../wazzup/enums/wazzup-message-type.enum.ts | 38 + .../wazzup/enums/wazzup-transport.enum.ts | 21 + .../multichat/providers/wazzup/types/index.ts | 8 + .../providers/wazzup/types/wazzup-channel.ts | 20 + .../wazzup/types/wazzup-connect-request.ts | 13 + .../wazzup/types/wazzup-message-contact.ts | 6 + .../wazzup/types/wazzup-message-error.ts | 4 + .../providers/wazzup/types/wazzup-message.ts | 20 + .../types/wazzup-send-message-response.ts | 4 + .../wazzup/types/wazzup-send-message.ts | 23 + .../wazzup/types/wazzup-webhook-request.ts | 6 + .../wazzup/wazzup-provider.module.ts | 31 + .../wazzup/wazzup-provider.service.ts | 444 + .../notification/common/events/index.ts | 2 + .../events/notification-event-type.enum.ts | 4 + .../common/events/notification/index.ts | 1 + .../notification/notification-unseen.event.ts | 11 + .../src/modules/notification/common/index.ts | 1 + .../dto/notification-settings.dto.ts | 16 + .../dto/notification-type-settings.dto.ts | 34 + .../entities/notification-settings.entity.ts | 42 + .../notification-type-follow-user.entity.ts | 19 + .../notification-type-settings.entity.ts | 79 + .../notification-settings.controller.ts | 31 + .../notification-settings.service.ts | 200 + .../notification/notification.module.ts | 35 + .../dto/create-notification.dto.ts | 47 + .../notification/notification/dto/index.ts | 3 + .../notification/dto/notification.dto.ts | 66 + .../dto/notifications-result.dto.ts | 17 + .../notification/entities/index.ts | 1 + .../entities/notification.entity.ts | 104 + .../notification/notification/enums/index.ts | 1 + .../enums/notification-type.enum.ts | 17 + .../notification-event.handler.ts | 170 + .../notification/notification-scheduler.ts | 172 + .../notification/notification.controller.ts | 45 + .../notification/notification.service.ts | 94 + backend/src/modules/partner/dto/index.ts | 3 + .../modules/partner/dto/partner-lead.dto.ts | 51 + .../modules/partner/dto/partner-login.dto.ts | 12 + .../partner/dto/partner-summary.dto.ts | 37 + .../src/modules/partner/partner.controller.ts | 47 + backend/src/modules/partner/partner.module.ts | 16 + .../src/modules/partner/partner.service.ts | 228 + backend/src/modules/partner/types/index.ts | 3 + .../src/modules/partner/types/partner-lead.ts | 37 + .../modules/partner/types/partner-summary.ts | 30 + backend/src/modules/partner/types/partner.ts | 27 + .../modules/scheduler/common/enums/index.ts | 2 + .../enums/permission-object-type.enum.ts | 3 + .../enums/schedule-appointment-status.enum.ts | 6 + .../modules/scheduler/common/events/index.ts | 4 + .../events/schedule-appointment/index.ts | 5 + .../schedule-appointment-created.event.ts | 20 + .../schedule-appointment-ext-upsert.event.ts | 24 + .../schedule-appointment-ext.event.ts | 17 + .../schedule-appointment-updated.event.ts | 3 + .../schedule-appointment.event.ts | 23 + .../common/events/schedule-performer/index.ts | 2 + .../schedule-performer-deleted.event.ts | 11 + .../schedule-performer.event.ts | 15 + .../scheduler/common/events/schedule/index.ts | 2 + .../events/schedule/schedule-updated.event.ts | 12 + .../common/events/schedule/schedule.event.ts | 11 + .../events/scheduler-event-type.enum.ts | 12 + backend/src/modules/scheduler/common/index.ts | 2 + .../dto/create-schedule-appointment.dto.ts | 31 + .../schedule-appointment/dto/index.ts | 6 + .../dto/schedule-appointment-filter.dto.ts | 52 + .../dto/schedule-appointment-result.dto.ts | 18 + .../dto/schedule-appointment-statistic.dto.ts | 26 + .../dto/schedule-appointment.dto.ts | 84 + .../dto/update-schedule-appointment.dto.ts | 18 + .../schedule-appointment/entities/index.ts | 1 + .../entities/schedule-appointment.entity.ts | 219 + .../appointment-dates-conflict.error.ts | 9 + .../errors/appointment-day-limit.error.ts | 14 + .../errors/appointment-intersection.error.ts | 9 + .../appointment-out-of-work-time.error.ts | 9 + .../schedule-appointment/errors/index.ts | 4 + .../scheduler/schedule-appointment/index.ts | 7 + .../schedule-appointment.controller.ts | 174 + .../schedule-appointment.handler.ts | 22 + .../schedule-appointment.service.ts | 962 + .../types/expandable-field.ts | 1 + .../schedule-appointment/types/index.ts | 4 + .../types/schedule-appointment-result.ts | 23 + .../types/schedule-appointment-statistic.ts | 40 + .../schedule-appointment/types/spot.ts | 4 + .../dtos/create-schedule-performer.dto.ts | 4 + .../schedule-performer/dtos/index.ts | 3 + .../dtos/schedule-performer.dto.ts | 24 + .../dtos/update-schedule-performer.dto.ts | 5 + .../schedule-performer/entities/index.ts | 1 + .../entities/schedule-performer.entity.ts | 64 + .../schedule-performer/enums/index.ts | 1 + .../enums/schedule-performer-type.enum.ts | 4 + .../scheduler/schedule-performer/index.ts | 5 + .../schedule-performer.handler.ts | 29 + .../schedule-performer.service.ts | 214 + .../scheduler/schedule-reporting/dto/index.ts | 3 + .../dto/schedule-report-filter.dto.ts | 29 + .../dto/schedule-report-row.dto.ts | 39 + .../dto/schedule-report.dto.ts | 10 + .../schedule-reporting/enums/index.ts | 1 + .../enums/schedule-report-type.enum.ts | 6 + .../scheduler/schedule-reporting/index.ts | 5 + .../schedule-reporting.controller.ts | 27 + .../schedule-reporting.service.ts | 249 + .../schedule-reporting/types/index.ts | 2 + .../types/schedule-report-row.ts | 62 + .../types/schedule-report.ts | 23 + .../schedule/dto/create-schedule.dto.ts | 16 + .../modules/scheduler/schedule/dto/index.ts | 5 + .../schedule/dto/schedule-filter.dto.ts | 9 + .../dto/schedule-time-interval.dto.ts | 16 + .../scheduler/schedule/dto/schedule.dto.ts | 70 + .../schedule/dto/update-schedule.dto.ts | 16 + .../scheduler/schedule/entities/index.ts | 2 + .../entities/schedule-time-interval.entity.ts | 51 + .../schedule/entities/schedule.entity.ts | 152 + .../modules/scheduler/schedule/enums/index.ts | 1 + .../schedule/enums/schedule-type.enum.ts | 4 + .../src/modules/scheduler/schedule/index.ts | 5 + .../scheduler/schedule/schedule.controller.ts | 70 + .../scheduler/schedule/services/index.ts | 2 + .../schedule-time-interval.service.ts | 70 + .../schedule/services/schedule.service.ts | 263 + .../src/modules/scheduler/scheduler.module.ts | 45 + .../account-setup/account-setup.module.ts | 67 + .../account-setup/account-setup.service.ts | 349 + .../config/account-setup.config.ts | 24 + .../setup/account-setup/config/index.ts | 1 + .../modules/setup/account-setup/dto/index.ts | 1 + .../account-setup/dto/setup-account.dto.ts | 40 + .../public-account-setup.controller.ts | 22 + .../setup/account-setup/services/crm/index.ts | 7 + .../services/crm/rms-activity.service.ts | 77 + .../services/crm/rms-board.service.ts | 113 + .../services/crm/rms-entity-type.service.ts | 50 + .../services/crm/rms-entity.service.ts | 71 + .../services/crm/rms-field.service.ts | 266 + .../services/crm/rms-note.service.ts | 34 + .../services/crm/rms-task.service.ts | 137 + .../setup/account-setup/services/index.ts | 5 + .../services/setup-crm.service.ts | 81 + .../services/setup-iam.service.ts | 106 + .../services/setup-products.service.ts | 338 + .../services/setup-scheduler.service.ts | 149 + .../setup/common/enums/demo-data-type.enum.ts | 9 + .../src/modules/setup/common/enums/index.ts | 3 + .../setup/common/enums/industry-code.enum.ts | 8 + .../common/enums/rmx-module-prefix.enum.ts | 5 + backend/src/modules/setup/common/index.ts | 2 + .../src/modules/setup/common/types/index.ts | 1 + .../modules/setup/common/types/rms-modules.ts | 5 + .../setup/demo-data/demo-data.controller.ts | 28 + .../setup/demo-data/demo-data.module.ts | 19 + .../setup/demo-data/demo-data.service.ts | 88 + .../demo-data/entities/demo-data.entity.ts | 30 + .../src/modules/setup/rms/dto/industry.dto.ts | 35 + .../setup/rms/dto/ready-made-solution.dto.ts | 17 + .../setup/rms/entities/industry.entity.ts | 21 + .../entities/ready-made-solution.entity.ts | 29 + .../src/modules/setup/rms/rms.controller.ts | 20 + backend/src/modules/setup/rms/rms.module.ts | 18 + .../setup/rms/services/industry.service.ts | 33 + .../modules/setup/rms/services/rms.service.ts | 39 + backend/src/modules/setup/setup.module.ts | 11 + .../dto/create-test-accounts-query.ts | 20 + .../test-data/entities/test-account.entity.ts | 7 + .../test-data/public-test-data.controller.ts | 38 + .../setup/test-data/test-data.module.ts | 16 + .../setup/test-data/test-data.service.ts | 96 + .../src/modules/storage/config/aws.config.ts | 20 + .../storage/dto/file-info-result.dto.ts | 34 + .../storage/dto/file-upload-request.ts | 6 + .../modules/storage/dto/file-upload-result.ts | 39 + .../modules/storage/dto/image-options.dto.ts | 14 + backend/src/modules/storage/dto/index.ts | 4 + .../storage/entities/file-info.entity.ts | 66 + backend/src/modules/storage/entities/index.ts | 1 + backend/src/modules/storage/enums/index.ts | 1 + .../modules/storage/enums/mime-type.enum.ts | 10 + .../storage/providers/aws-s3.provider.ts | 60 + .../src/modules/storage/providers/index.ts | 1 + .../storage/storage-public.controller.ts | 56 + .../modules/storage/storage-url.service.ts | 34 + .../src/modules/storage/storage.controller.ts | 88 + backend/src/modules/storage/storage.module.ts | 27 + .../src/modules/storage/storage.service.ts | 332 + .../modules/storage/types/file-info-result.ts | 33 + backend/src/modules/storage/types/index.ts | 3 + .../src/modules/storage/types/storage-file.ts | 29 + .../modules/storage/types/temporary-file.ts | 3 + .../modules/telephony/common/events/index.ts | 2 + .../common/events/telephony-call/index.ts | 3 + .../telephony-call-created.event.ts | 11 + .../telephony-call-updated.event.ts | 13 + .../telephony-call/telephony-call.event.ts | 11 + .../events/telephony-event-type.enum.ts | 4 + backend/src/modules/telephony/common/index.ts | 1 + .../src/modules/telephony/telephony.module.ts | 10 + .../common/enums/call-direction.enum.ts | 4 + .../common/enums/call-status.enum.ts | 8 + .../voximplant/common/enums/index.ts | 2 + .../voximplant/common/errors/index.ts | 1 + .../common/errors/voximplant-error.ts | 9 + .../telephony/voximplant/common/index.ts | 3 + .../voximplant/common/types/index.ts | 1 + .../voximplant-application-param.interface.ts | 4 + .../voximplant/config/voximplant.config.ts | 16 + .../voximplant-account/dto/index.ts | 1 + .../dto/voximplant-account.dto.ts | 50 + .../voximplant-account/entities/index.ts | 1 + .../entities/voximplant-account.entity.ts | 81 + .../voximplant/voximplant-account/index.ts | 4 + .../voximplant-account.controller.ts | 35 + .../voximplant-account.service.ts | 135 + .../dto/create-voximplant-call-ext.dto.ts | 15 + .../dto/create-voximplant-call.dto.ts | 16 + .../voximplant/voximplant-call/dto/index.ts | 6 + .../dto/update-voximplant-call-ext.dto.ts | 7 + .../dto/update-voximplant-call.dto.ts | 15 + .../dto/voximplant-call-list.dto.ts | 20 + .../dto/voximplant-call.dto.ts | 108 + .../voximplant-call/entities/index.ts | 1 + .../entities/voximplant-call.entity.ts | 155 + .../voximplant/voximplant-call/index.ts | 6 + .../voximplant/voximplant-call/types/index.ts | 1 + .../types/voximplant-call-list.ts | 23 + .../voximplant-call-public.controller.ts | 33 + .../voximplant-call.controller.ts | 44 + .../voximplant-call.service.ts | 180 + .../voximplant/voximplant-core/index.ts | 2 + .../voximplant-core.controller.ts | 25 + .../voximplant-core.service.ts | 172 + .../dto/create-voximplant-number.dto.ts | 9 + .../voximplant/voximplant-number/dto/index.ts | 5 + .../voximplant-number/dto/phone-number.dto.ts | 28 + .../dto/update-voximplant-number.dto.ts | 7 + .../dto/voximplant-number-filter.dto.ts | 9 + .../dto/voximplant-number.dto.ts | 28 + .../voximplant-number/entities/index.ts | 2 + .../entities/voximplant-number-user.entity.ts | 19 + .../entities/voximplant-number.entity.ts | 48 + .../voximplant/voximplant-number/index.ts | 5 + .../voximplant-number/services/index.ts | 2 + .../voximplant-number-user.service.ts | 60 + .../services/voximplant-number.service.ts | 135 + .../types/expandable-field.ts | 1 + .../voximplant-number/types/index.ts | 2 + .../voximplant-number/types/phone-number.ts | 29 + .../voximplant-number.controller.ts | 88 + .../dto/call-history-report-filter.dto.ts | 35 + .../dto/call-history-report.dto.ts | 12 + .../dto/call-report-block.dto.ts | 26 + .../dto/call-report-filter.dto.ts | 46 + .../dto/call-report-row.dto.ts | 14 + .../dto/call-report.dto.ts | 14 + .../voximplant-reporting/dto/index.ts | 6 + .../voximplant-reporting/enums/index.ts | 1 + .../enums/telephony-report-type.enum.ts | 5 + .../voximplant/voximplant-reporting/index.ts | 4 + .../types/call-history-report.ts | 17 + .../types/call-report-block.ts | 63 + .../types/call-report-row.ts | 26 + .../voximplant-reporting/types/call-report.ts | 30 + .../voximplant-reporting/types/index.ts | 4 + .../voximplant-reporting.controller.ts | 36 + .../voximplant-reporting.service.ts | 334 + .../voximplant-scenario/dto/index.ts | 4 + .../dto/voximplant-scenario-entity.dto.ts | 44 + .../dto/voximplant-scenario-note.dto.ts | 19 + .../dto/voximplant-scenario-task.dto.ts | 86 + .../dto/voximplant-scenarios.dto.ts | 33 + .../voximplant-scenario/entities/index.ts | 3 + .../voximplant-scenario-entity.entity.ts | 59 + .../voximplant-scenario-note.entity.ts | 33 + .../voximplant-scenario-task.entity.ts | 107 + .../voximplant-scenario/enums/index.ts | 1 + .../enums/scenario-type.enum.ts | 7 + .../voximplant/voximplant-scenario/index.ts | 6 + .../voximplant-scenario/types/index.ts | 1 + .../types/voximplant-scenarios.ts | 34 + .../voximplant-scenario.controller.ts | 49 + .../voximplant-scenario.service.ts | 243 + .../dto/create-voximplant-sip.dto.ts | 29 + .../voximplant/voximplant-sip/dto/index.ts | 5 + .../dto/update-voximplant-sip.dto.ts | 5 + .../dto/voximplant-sip-filter.dto.ts | 9 + .../dto/voximplant-sip-registration.dto.ts | 85 + .../voximplant-sip/dto/voximplant-sip.dto.ts | 40 + .../voximplant-sip/entities/index.ts | 2 + .../entities/voximplant-sip-user.entity.ts | 19 + .../entities/voximplant-sip.entity.ts | 58 + .../voximplant/voximplant-sip/enums/index.ts | 1 + .../enums/pbx-provider-type.enum.ts | 12 + .../voximplant/voximplant-sip/index.ts | 6 + .../voximplant-sip/services/index.ts | 2 + .../services/voximplant-sip-user.service.ts | 60 + .../services/voximplant-sip.service.ts | 192 + .../voximplant-sip/types/expandable-field.ts | 1 + .../voximplant/voximplant-sip/types/index.ts | 2 + .../types/voximplant-sip-registration.ts | 82 + .../voximplant-sip.controller.ts | 93 + .../dto/create-voximplant-user.dto.ts | 4 + .../dto/create-voximplant-users-batch.dto.ts | 9 + .../voximplant/voximplant-user/dto/index.ts | 6 + .../dto/update-voximplant-user.dto.ts | 4 + .../voximplant-user/dto/users-queue.dto.ts | 31 + .../dto/voximplant-user-sip-data.dto.ts | 22 + .../dto/voximplant-user.dto.ts | 22 + .../voximplant-user/entities/index.ts | 1 + .../entities/voximplant-user.entity.ts | 52 + .../voximplant/voximplant-user/index.ts | 5 + .../voximplant-user-public.controller.ts | 21 + .../voximplant-user.controller.ts | 87 + .../voximplant-user.service.ts | 253 + .../telephony/voximplant/voximplant.module.ts | 92 + .../src/modules/tutorial/common/dto/index.ts | 1 + .../common/dto/tutorial-filter.dto.ts | 21 + .../modules/tutorial/common/enums/index.ts | 1 + .../enums/tutorial-product-type.enum.ts | 10 + backend/src/modules/tutorial/common/index.ts | 2 + .../tutorial/config/tutorial.config.ts | 12 + .../modules/tutorial/tutorial-core/index.ts | 2 + .../tutorial-core/tutorial-core.controller.ts | 33 + .../tutorial-core/tutorial-core.service.ts | 55 + .../tutorial-core/tutorial-defaults.ts | 157 + .../dto/create-tutorial-group.dto.ts | 5 + .../tutorial/tutorial-group/dto/index.ts | 3 + .../tutorial-group/dto/tutorial-group.dto.ts | 35 + .../dto/update-tutorial-group.dto.ts | 5 + .../tutorial/tutorial-group/entities/index.ts | 1 + .../entities/tutorial-group.entity.ts | 60 + .../modules/tutorial/tutorial-group/index.ts | 4 + .../tutorial-group.controller.ts | 101 + .../tutorial-group/tutorial-group.service.ts | 119 + .../tutorial-group/types/expandable-field.ts | 3 + .../tutorial/tutorial-group/types/index.ts | 1 + .../dto/create-tutorial-item.dto.ts | 11 + .../tutorial/tutorial-item/dto/index.ts | 4 + .../dto/tutorial-item-product.dto.ts | 20 + .../tutorial-item/dto/tutorial-item.dto.ts | 55 + .../dto/update-tutorial-item.dto.ts | 7 + .../tutorial/tutorial-item/entities/index.ts | 3 + .../entities/tutorial-item-product.entity.ts | 37 + .../entities/tutorial-item-user.entity.ts | 15 + .../entities/tutorial-item.entity.ts | 81 + .../modules/tutorial/tutorial-item/index.ts | 5 + .../tutorial-item/tutorial-item.controller.ts | 94 + .../tutorial-item/tutorial-item.handler.ts | 35 + .../tutorial-item/tutorial-item.service.ts | 145 + .../src/modules/tutorial/tutorial.module.ts | 28 + backend/src/support/config/index.ts | 1 + backend/src/support/config/support.config.ts | 12 + .../src/support/health/health.controller.ts | 23 + backend/src/support/health/health.module.ts | 10 + backend/src/support/health/index.ts | 2 + .../support/heapdump/heapdump.controller.ts | 15 + .../src/support/heapdump/heapdump.module.ts | 10 + .../src/support/heapdump/heapdump.service.ts | 26 + backend/src/support/heapdump/index.ts | 3 + backend/src/support/support.module.ts | 17 + .../support/version/dto/create-version.dto.ts | 13 + .../version/dto/current-version.dto.ts | 8 + backend/src/support/version/dto/index.ts | 3 + .../src/support/version/dto/version.dto.ts | 16 + backend/src/support/version/entities/index.ts | 1 + .../version/entities/version.entity.ts | 33 + backend/src/support/version/index.ts | 3 + .../src/support/version/version.controller.ts | 51 + backend/src/support/version/version.module.ts | 13 + .../src/support/version/version.service.ts | 73 + backend/src/types/environment.d.ts | 99 + backend/src/types/express.d.ts | 15 + backend/src/types/imapflow/index.d.ts | 394 + backend/src/types/reset.d.ts | 1 + backend/tsconfig.build.json | 17 + backend/tsconfig.json | 56 + backend/var/jwt/private.pem | 28 + backend/var/jwt/public.pem | 9 + backend/var/voximplant/credentials.json | 1 + backend/yarn.lock | 14269 +++++++++ crm.mcmed.ru | 143 + frontend/.gitattributes | 1 + frontend/.gitignore | 26 + frontend/.gitlab-ci.yml | 177 + frontend/.npmrc | 2 + frontend/.prettierignore | 24 + frontend/.prettierrc.json | 22 + frontend/.storybook/main.ts | 24 + frontend/.storybook/preview.ts | 62 + frontend/README.md | 93 + frontend/eslint.config.mjs | 107 + frontend/index.html | 52 + frontend/package-lock.json | 15539 ++++++++++ frontend/package.json | 205 + .../favicons/amwork/apple_touch_120x120.png | Bin 0 -> 1079 bytes .../favicons/amwork/apple_touch_152x152.png | Bin 0 -> 1397 bytes .../favicons/amwork/apple_touch_167x167.png | Bin 0 -> 1527 bytes .../favicons/amwork/apple_touch_180x180.png | Bin 0 -> 1675 bytes .../public/favicons/amwork/favicon_16x16.png | Bin 0 -> 236 bytes .../public/favicons/amwork/favicon_16x16.svg | 6 + .../public/favicons/amwork/favicon_32x32.png | Bin 0 -> 399 bytes .../public/favicons/amwork/favicon_32x32.svg | 6 + frontend/public/favicons/favicon.png | Bin 0 -> 29498 bytes .../favicons/mywork/apple_touch_120x120.png | Bin 0 -> 1079 bytes .../favicons/mywork/apple_touch_152x152.png | Bin 0 -> 1397 bytes .../favicons/mywork/apple_touch_167x167.png | Bin 0 -> 1527 bytes .../favicons/mywork/apple_touch_180x180.png | Bin 0 -> 1675 bytes .../public/favicons/mywork/favicon_16x16.png | Bin 0 -> 236 bytes .../public/favicons/mywork/favicon_16x16.svg | 6 + .../public/favicons/mywork/favicon_32x32.png | Bin 0 -> 399 bytes .../public/favicons/mywork/favicon_32x32.svg | 6 + .../favicons/proma/apple_touch_120x120.png | Bin 0 -> 896 bytes .../favicons/proma/apple_touch_152x152.png | Bin 0 -> 1132 bytes .../favicons/proma/apple_touch_167x167.png | Bin 0 -> 1234 bytes .../favicons/proma/apple_touch_180x180.png | Bin 0 -> 1312 bytes .../public/favicons/proma/favicon_16x16.png | Bin 0 -> 253 bytes .../public/favicons/proma/favicon_16x16.svg | 8 + .../public/favicons/proma/favicon_32x32.png | Bin 0 -> 356 bytes .../public/favicons/proma/favicon_32x32.svg | 8 + .../Geologica/SemiBold/GeologicaSemiBold.ttf | Bin 0 -> 343100 bytes .../fonts/Nunito/Regular/NunitoRegular.ttf | Bin 0 -> 132204 bytes .../fonts/Nunito/SemiBold/NunitoSemiBold.ttf | Bin 0 -> 132156 bytes .../TimesNewRoman/Bold/TimesNewRomanBold.ttf | Bin 0 -> 842168 bytes .../Regular/TimesNewRomanRegular.ttf | Bin 0 -> 834452 bytes frontend/public/images/bpmn/en/bpmn.svg | 370 + frontend/public/images/bpmn/es/bpmn.svg | 379 + frontend/public/images/bpmn/fr/bpmn.svg | 379 + frontend/public/images/bpmn/pl/bpmn.svg | 379 + frontend/public/images/bpmn/ru/bpmn.svg | 311 + .../images/builder/deals-view/board.png | Bin 0 -> 254162 bytes .../public/images/builder/deals-view/list.png | Bin 0 -> 718916 bytes .../images/builder/scheduler-view/board.png | Bin 0 -> 210917 bytes .../builder/scheduler-view/schedule.png | Bin 0 -> 304864 bytes .../ImportEntitiesModal/import_entities.svg | 166 + .../public/images/dashboard/chart_plug.png | Bin 0 -> 59137 bytes frontend/public/images/demo/analytics.png | Bin 0 -> 71586 bytes frontend/public/images/demo/mail.png | Bin 0 -> 91968 bytes .../images/invoice/mywork_signature.png | Bin 0 -> 31350 bytes .../images/invoice/mywork_square_logo.png | Bin 0 -> 1651 bytes .../public/images/invoice/mywork_stamp.png | Bin 0 -> 55533 bytes frontend/public/images/login/en/login.png | Bin 0 -> 560224 bytes frontend/public/images/login/login.svg | 498 + frontend/public/images/login/ru/login.png | Bin 0 -> 364950 bytes frontend/public/locales/en/common.json | 395 + .../public/locales/en/component.card.json | 344 + .../locales/en/component.entity-board.json | 23 + .../public/locales/en/component.section.json | 191 + .../public/locales/en/module.automation.json | 368 + frontend/public/locales/en/module.bpmn.json | 174 + .../public/locales/en/module.builder.json | 867 + frontend/public/locales/en/module.fields.json | 122 + .../public/locales/en/module.mailing.json | 234 + .../public/locales/en/module.multichat.json | 71 + frontend/public/locales/en/module.notes.json | 49 + .../locales/en/module.notifications.json | 79 + .../public/locales/en/module.products.json | 502 + .../public/locales/en/module.reporting.json | 432 + .../public/locales/en/module.scheduler.json | 219 + .../public/locales/en/module.telephony.json | 242 + .../public/locales/en/module.tutorial.json | 55 + .../locales/en/page.board-settings.json | 35 + .../public/locales/en/page.dashboard.json | 18 + frontend/public/locales/en/page.login.json | 24 + frontend/public/locales/en/page.settings.json | 902 + frontend/public/locales/en/page.system.json | 19 + frontend/public/locales/en/page.tasks.json | 135 + .../locales/en/store.field-groups-store.json | 9 + .../public/locales/en/store.fields-store.json | 8 + frontend/public/locales/es/common.json | 395 + .../public/locales/es/component.card.json | 344 + .../locales/es/component.entity-board.json | 23 + .../public/locales/es/component.section.json | 191 + .../public/locales/es/module.automation.json | 368 + frontend/public/locales/es/module.bpmn.json | 174 + .../public/locales/es/module.builder.json | 867 + frontend/public/locales/es/module.fields.json | 122 + .../public/locales/es/module.mailing.json | 234 + .../public/locales/es/module.multichat.json | 71 + frontend/public/locales/es/module.notes.json | 49 + .../locales/es/module.notifications.json | 79 + .../public/locales/es/module.products.json | 502 + .../public/locales/es/module.reporting.json | 433 + .../public/locales/es/module.scheduler.json | 219 + .../public/locales/es/module.telephony.json | 242 + .../public/locales/es/module.tutorial.json | 55 + .../locales/es/page.board-settings.json | 33 + .../public/locales/es/page.dashboard.json | 18 + frontend/public/locales/es/page.login.json | 24 + frontend/public/locales/es/page.settings.json | 902 + frontend/public/locales/es/page.system.json | 19 + frontend/public/locales/es/page.tasks.json | 135 + .../locales/es/store.field-groups-store.json | 9 + .../public/locales/es/store.fields-store.json | 8 + frontend/public/locales/fr/common.json | 395 + .../public/locales/fr/component.card.json | 344 + .../locales/fr/component.entity-board.json | 23 + .../public/locales/fr/component.section.json | 191 + .../public/locales/fr/module.automation.json | 368 + frontend/public/locales/fr/module.bpmn.json | 174 + .../public/locales/fr/module.builder.json | 867 + frontend/public/locales/fr/module.fields.json | 122 + .../public/locales/fr/module.mailing.json | 234 + .../public/locales/fr/module.multichat.json | 71 + frontend/public/locales/fr/module.notes.json | 49 + .../locales/fr/module.notifications.json | 79 + .../public/locales/fr/module.products.json | 502 + .../public/locales/fr/module.reporting.json | 432 + .../public/locales/fr/module.scheduler.json | 219 + .../public/locales/fr/module.telephony.json | 242 + .../public/locales/fr/module.tutorial.json | 55 + .../locales/fr/page.board-settings.json | 33 + .../public/locales/fr/page.dashboard.json | 18 + frontend/public/locales/fr/page.login.json | 24 + frontend/public/locales/fr/page.settings.json | 902 + frontend/public/locales/fr/page.system.json | 19 + frontend/public/locales/fr/page.tasks.json | 135 + .../locales/fr/store.field-groups-store.json | 9 + .../public/locales/fr/store.fields-store.json | 8 + frontend/public/locales/pl/common.json | 395 + .../public/locales/pl/component.card.json | 344 + .../locales/pl/component.entity-board.json | 23 + .../public/locales/pl/component.section.json | 190 + .../public/locales/pl/module.automation.json | 368 + frontend/public/locales/pl/module.bpmn.json | 174 + .../public/locales/pl/module.builder.json | 867 + frontend/public/locales/pl/module.fields.json | 122 + .../public/locales/pl/module.mailing.json | 234 + .../public/locales/pl/module.multichat.json | 71 + frontend/public/locales/pl/module.notes.json | 49 + .../locales/pl/module.notifications.json | 79 + .../public/locales/pl/module.products.json | 502 + .../public/locales/pl/module.reporting.json | 432 + .../public/locales/pl/module.scheduler.json | 219 + .../public/locales/pl/module.telephony.json | 242 + .../public/locales/pl/module.tutorial.json | 55 + .../locales/pl/page.board-settings.json | 33 + .../public/locales/pl/page.dashboard.json | 18 + frontend/public/locales/pl/page.login.json | 24 + frontend/public/locales/pl/page.settings.json | 901 + frontend/public/locales/pl/page.system.json | 19 + frontend/public/locales/pl/page.tasks.json | 135 + .../locales/pl/store.field-groups-store.json | 9 + .../public/locales/pl/store.fields-store.json | 8 + frontend/public/locales/ru/common.json | 395 + .../public/locales/ru/component.card.json | 344 + .../locales/ru/component.entity-board.json | 23 + .../public/locales/ru/component.section.json | 191 + .../public/locales/ru/module.automation.json | 368 + frontend/public/locales/ru/module.bpmn.json | 174 + .../public/locales/ru/module.builder.json | 866 + frontend/public/locales/ru/module.fields.json | 122 + .../public/locales/ru/module.mailing.json | 234 + .../public/locales/ru/module.multichat.json | 71 + frontend/public/locales/ru/module.notes.json | 49 + .../locales/ru/module.notifications.json | 79 + .../public/locales/ru/module.products.json | 502 + .../public/locales/ru/module.reporting.json | 435 + .../public/locales/ru/module.scheduler.json | 219 + .../public/locales/ru/module.telephony.json | 242 + .../public/locales/ru/module.tutorial.json | 55 + .../locales/ru/page.board-settings.json | 33 + .../public/locales/ru/page.dashboard.json | 18 + frontend/public/locales/ru/page.login.json | 24 + frontend/public/locales/ru/page.settings.json | 902 + frontend/public/locales/ru/page.system.json | 19 + frontend/public/locales/ru/page.tasks.json | 135 + .../locales/ru/store.field-groups-store.json | 9 + .../public/locales/ru/store.fields-store.json | 8 + frontend/public/robots.txt | 4 + frontend/public/sounds/incoming_call.mp3 | Bin 0 -> 92466 bytes frontend/public/sounds/notification.mp3 | Bin 0 -> 45139 bytes frontend/src/app/App.tsx | 143 + frontend/src/app/api/ApiRoutes.ts | 83 + frontend/src/app/api/AppQueryKeys.ts | 50 + frontend/src/app/api/BaseApi/BaseApi.ts | 53 + frontend/src/app/api/BoardApi/BoardApi.ts | 53 + frontend/src/app/api/BoardApi/BoardApiUtil.ts | 154 + frontend/src/app/api/DadataApi/DadataApi.ts | 29 + .../app/api/EntityTypeApi/EntityTypeApi.ts | 67 + frontend/src/app/api/FeatureApi/FeatureApi.ts | 13 + .../src/app/api/FeedbackApi/FeedbackApi.ts | 17 + frontend/src/app/api/FileApi/FileApi.ts | 66 + .../api/FileApi/queries/useGetFileInfos.ts | 15 + frontend/src/app/api/FormApi/FormApi.ts | 47 + .../FrontendObjectsApi/FrontendObjectsApi.ts | 31 + .../GeneralSettingsApi/GeneralSettingsApi.ts | 60 + .../src/app/api/IdentityApi/IdentityApi.ts | 32 + frontend/src/app/api/StageApi/StageApi.ts | 76 + frontend/src/app/api/StageApi/StageApiUtil.ts | 149 + .../api/SubscriptionApi/SubscriptionApi.ts | 78 + .../queries/useGetSubscriptionForAccount.ts | 11 + .../queries/useUpdateSubscription.ts | 23 + frontend/src/app/api/UserApi/UserApi.ts | 78 + .../app/api/UserProfileApi/UserProfileApi.ts | 28 + .../helpers/invalidateUserProfilesInCache.ts | 5 + .../queries/useGetUserProfiles.ts | 19 + frontend/src/app/api/UtilityApi/UtilityApi.ts | 17 + frontend/src/app/api/VersionApi/VersionApi.ts | 17 + .../useGetLatestFrontendVersionPolling.ts | 12 + frontend/src/app/api/dtos/Board/BoardDto.ts | 16 + .../src/app/api/dtos/Board/CreateBoardDto.ts | 38 + .../src/app/api/dtos/Board/UpdateBoardDto.ts | 9 + .../src/app/api/dtos/Builder/FeatureDto.ts | 15 + .../app/api/dtos/CreateContactAndLeadDto.ts | 13 + frontend/src/app/api/dtos/Entity/EntityDto.ts | 25 + .../api/dtos/EntityEvent/ContactInfoDto.ts | 8 + .../dtos/EntityEvent/EntityEventItemDto.ts | 20 + .../app/api/dtos/EntityEvent/EntityInfoDto.ts | 10 + .../app/api/dtos/EntityLink/EntityLinkDto.ts | 15 + .../app/api/dtos/EntityType/EntityTypeDto.ts | 17 + .../dtos/EntityType/UpdateEntityTypeDto.ts | 49 + .../EntityType/UpdateEntityTypeFieldsModel.ts | 14 + .../src/app/api/dtos/FeedItem/FeedItemDto.ts | 8 + .../src/app/api/dtos/Feedback/FeedbackType.ts | 4 + .../app/api/dtos/Feedback/SendFeedbackDto.ts | 15 + .../api/dtos/Feedback/TrialExpiredFeedback.ts | 17 + .../api/dtos/Feedback/UserLimitFeedback.ts | 13 + .../dtos/FileInfoResult/FileInfoResultDto.ts | 11 + .../src/app/api/dtos/FileLink/FileLinkDto.ts | 13 + .../app/api/dtos/Form/SendContactUsFormDto.ts | 17 + .../FrontendObject/CreateFrontendObjectDto.ts | 9 + .../dtos/FrontendObject/FrontendObjectDto.ts | 5 + .../api/dtos/GeneralSettings/AccountDto.ts | 9 + .../GeneralSettings/AccountSettingsDto.ts | 16 + .../UpdateAccountSettingsDto.ts | 15 + .../dtos/Permission/ObjectPermissionDto.ts | 70 + .../api/dtos/Profile/UpdateUserProfileDto.ts | 15 + .../app/api/dtos/Profile/UserProfileDto.ts | 23 + .../dtos/SiteForm/SiteFormAnalyticDataDto.ts | 11 + .../app/api/dtos/SiteForm/SiteFormDataDto.ts | 13 + .../api/dtos/SiteForm/SiteFormFieldDataDto.ts | 9 + .../api/dtos/SiteForm/SiteFormResultDto.ts | 11 + .../src/app/api/dtos/Stage/CreateStageDto.ts | 17 + frontend/src/app/api/dtos/Stage/StageDto.ts | 23 + .../src/app/api/dtos/Stage/UpdateStageDto.ts | 19 + .../api/dtos/Subscription/SubscriptionDto.ts | 12 + .../Subscription/SubscriptionFeatureDto.ts | 4 + .../dtos/Subscription/SubscriptionPlanDto.ts | 16 + .../dtos/Subscription/SubscriptionPriceDto.ts | 15 + .../Subscription/UpdateSubscriptionDto.ts | 9 + .../api/dtos/User/ChangeUserPasswordDto.ts | 9 + .../src/app/api/dtos/User/CreateUserDto.ts | 39 + .../src/app/api/dtos/User/UpdateUserDto.ts | 58 + frontend/src/app/api/dtos/User/UserDto.ts | 19 + .../src/app/api/dtos/Version/VersionDto.ts | 5 + frontend/src/app/api/dtos/index.tsx | 46 + frontend/src/app/api/index.tsx | 27 + frontend/src/app/config/i18n/i18n.ts | 56 + frontend/src/app/config/knip/knip.ts | 49 + frontend/src/app/config/newrelic/newrelic.ts | 37 + .../app/config/storybook/I18nDecorator.tsx | 20 + .../app/config/storybook/RouterDecorator.tsx | 8 + .../app/config/storybook/StyleDecorator.tsx | 4 + frontend/src/app/index.tsx | 4 + frontend/src/app/pages/HomePage/HomePage.tsx | 42 + frontend/src/app/pages/index.tsx | 1 + frontend/src/app/routes/AppBoundary.tsx | 30 + frontend/src/app/routes/createRouter.tsx | 379 + frontend/src/app/routes/index.tsx | 3 + frontend/src/app/routes/routes.ts | 645 + frontend/src/app/store/AppStore.ts | 89 + frontend/src/app/store/EntityTypeStore.ts | 185 + frontend/src/app/store/FeatureStore.ts | 25 + .../src/app/store/GeneralSettingsFormData.ts | 46 + .../src/app/store/GeneralSettingsStore.ts | 185 + frontend/src/app/store/IconStore.tsx | 412 + frontend/src/app/store/IdentityStore.ts | 129 + .../src/app/store/RecordSingularityStore.ts | 22 + frontend/src/app/store/RecordStateStore.ts | 44 + frontend/src/app/store/SettingsStore.ts | 22 + frontend/src/app/store/SubscriptionStore.ts | 100 + frontend/src/app/store/UserProfileStore.ts | 43 + frontend/src/app/store/UserStore.ts | 153 + frontend/src/app/store/WatchdogStore.ts | 98 + frontend/src/app/store/index.tsx | 13 + frontend/src/app/styles/emoji-picker.css | 13 + frontend/src/app/styles/fonts.css | 23 + frontend/src/app/styles/main.css | 46 + frontend/src/app/styles/react-pdf.css | 4 + frontend/src/app/styles/reset.css | 237 + frontend/src/app/styles/toastify.css | 3 + frontend/src/app/styles/variables.css | 189 + frontend/src/declarations/bpmn-js.d.ts | 11 + .../src/declarations/custom-elements.d.ts | 13 + frontend/src/declarations/emoji-mart.d.ts | 90 + frontend/src/declarations/env.d.ts | 100 + frontend/src/declarations/reset.d.ts | 1 + frontend/src/declarations/svgr.d.ts | 7 + frontend/src/declarations/vite.env.d.ts | 5 + frontend/src/declarations/voxengine.d.ts | 24797 ++++++++++++++++ frontend/src/index.tsx | 102 + .../src/modules/auth/api/AuthApi/AuthApi.ts | 27 + .../src/modules/auth/api/AuthApiRoutes.ts | 6 + frontend/src/modules/auth/api/index.tsx | 1 + frontend/src/modules/auth/index.tsx | 2 + .../pages/LoginLinkPage/LoginLinkPage.tsx | 43 + .../auth/pages/LoginPage/LoginPage.tsx | 12 + frontend/src/modules/auth/pages/index.tsx | 2 + .../auth/shared/assets/hide_password.svg | 5 + .../src/modules/auth/shared/assets/index.tsx | 2 + .../auth/shared/assets/show_password.svg | 5 + frontend/src/modules/auth/shared/index.tsx | 2 + .../components/LoginButton/LoginButton.tsx | 54 + .../lib/components/LoginForm/LoginForm.tsx | 200 + .../lib/components/LoginInput/LoginInput.tsx | 124 + .../LoginOrDelimiter/LoginOrDelimiter.tsx | 38 + .../SuggestionLink/SuggestionLink.tsx | 51 + .../auth/shared/lib/components/index.tsx | 1 + .../src/modules/auth/shared/lib/index.tsx | 1 + frontend/src/modules/auth/store/AuthStore.ts | 219 + frontend/src/modules/auth/store/index.tsx | 1 + .../templates/LoginTemplate/LoginTemplate.tsx | 126 + frontend/src/modules/auth/templates/index.tsx | 1 + .../automation/api/AutomationApiRoutes.ts | 7 + .../AutomationEntityTypeApi.ts | 55 + .../AutomationEntityTypeDto.ts | 16 + .../CreateAutomationEntityTypeDto.ts | 36 + .../UpdateAutomationEntityType.ts | 36 + .../src/modules/automation/api/dtos/index.tsx | 3 + frontend/src/modules/automation/api/index.tsx | 2 + frontend/src/modules/automation/index.tsx | 2 + .../automation/shared/assets/activity.svg | 20 + .../shared/assets/change_of_stage.svg | 8 + .../shared/assets/create_entity.svg | 6 + .../automation/shared/assets/index.tsx | 6 + .../modules/automation/shared/assets/mail.svg | 8 + .../modules/automation/shared/assets/plus.svg | 8 + .../modules/automation/shared/assets/task.svg | 8 + .../src/modules/automation/shared/index.tsx | 1 + .../AutomationBlock/AddAutomationButton.tsx | 51 + .../AutomationBlock/AutomationBlock.tsx | 425 + .../AutomationBlock/HeaderDropdown.tsx | 64 + .../AutomationBlock/HeaderDropdownList.tsx | 34 + .../shared/lib/components/Block/Block.tsx | 12 + .../ExternalChatProvidersSelect.tsx | 75 + .../AddActivityAutomationModal.tsx | 102 + .../AddActivityAutomationModalContent.tsx | 96 + .../AddTaskAutomationModal.tsx | 102 + .../AddTaskAutomationModalContent.tsx | 81 + .../Modals/AutomationModalTemplate.tsx | 312 + .../ChangeLinkedStageAutomationModal.tsx | 96 + ...hangeLinkedStageAutomationModalContent.tsx | 142 + .../ChangeResponsibleAutomationModal.tsx | 81 + ...hangeResponsibleAutomationModalContent.tsx | 37 + .../ChangeStageAutomationModal.tsx | 91 + .../ChangeStageAutomationModalContent.tsx | 93 + .../CreateEntityAutomationModal.tsx | 102 + .../CreateEntityAutomationModalContent.tsx | 92 + .../RequestHttpAutomationModal.tsx | 96 + .../RequestHttpAutomationModalContent.tsx | 81 + .../SendEmailAutomationModal.tsx | 101 + .../SendEmailAutomationModalContent.tsx | 272 + .../SendExternalChatAutomationModal.tsx | 91 + ...SendExternalChatAutomationModalContent.tsx | 234 + .../SendInternalChatAutomationModal.tsx | 97 + ...SendInternalChatAutomationModalContent.tsx | 69 + .../AutomationBooleanRadioSelect.tsx | 22 + .../AutomationFormItem/AutomationFormItem.tsx | 27 + .../AutomationRadioButton.tsx | 30 + .../AutomationSendOptionsBlock.tsx | 131 + .../EntitySendOptionsBlock.tsx | 50 + .../components/index.tsx | 1 + .../AutomationUserSelect.tsx | 55 + .../ConditionsBlock/ConditionsBlock.tsx | 65 + .../EntityTypeFieldFilterBoolean.tsx | 40 + .../EntityTypeFieldFilterDate.tsx | 36 + .../EntityTypeFieldFilterNumber.tsx | 58 + ...ntityTypeFieldFilterParticipantsSelect.tsx | 32 + .../EntityTypeFieldFilterSelect.tsx | 43 + .../EntityTypeFieldFilterString.tsx | 74 + .../EntityTypeFieldsConditions.tsx | 204 + .../EntityTypeFieldsConditionsSwitch.tsx | 98 + .../ConditionsBlock/components/index.tsx | 1 + .../DeferStartSelect/DeferStartSelect.tsx | 87 + .../DeleteAutomationModal.tsx | 48 + .../DescriptionBlock/DescriptionBlock.tsx | 27 + .../DueDateSelect/DueDateSelect.tsx | 100 + .../components/TemplateList/TemplateList.tsx | 252 + .../TriggerSelect/TriggerSelect.tsx | 17 + .../WrapperWithLeftOffset.tsx | 9 + .../components/Modals/components/index.tsx | 13 + .../ActionTypeBlock/ActionTypeBlock.tsx | 45 + .../AutomationSidebar/AutomationSidebar.tsx | 46 + .../ListAutomationSidebar.tsx | 37 + .../shared/lib/components/index.tsx | 27 + .../lib/helpers/getActionSendVariant.ts | 20 + .../helpers/getActionTypeEntityTypeInfos.tsx | 56 + .../lib/helpers/getSendEmailActionSettings.ts | 68 + .../getSendExternalChatActionSettings.ts | 87 + .../automation/shared/lib/helpers/index.tsx | 4 + .../automation/shared/lib/hooks/index.tsx | 5 + .../lib/hooks/useGetAutomationDelayOptions.ts | 40 + .../lib/hooks/useGetDeferStartOptions.ts | 27 + .../shared/lib/hooks/useGetDueDateOptions.ts | 36 + .../shared/lib/hooks/useGetTriggerOptions.ts | 39 + ...seInitializeSendEmailActionSettingsForm.ts | 42 + .../useInitializeSendExternalChatForm.ts | 51 + .../modules/automation/shared/lib/index.tsx | 12 + .../ActionSendOptionsForm.ts | 17 + .../AutomationEntityType/ActionSendVariant.ts | 6 + .../ActionActivityCreateSettings.ts | 12 + .../ActionChatSendAmworkSettings.ts | 8 + .../ActionSettings/ActionChatSendSettings.ts | 11 + .../ActionSettings/ActionCommonSettings.ts | 5 + .../ActionSettings/ActionEmailSendSettings.ts | 13 + .../ActionEntityCreateSettings.ts | 10 + .../ActionEntityLinkedStageChangeSettings.ts | 8 + .../ActionEntityResponsibleChangeSettings.ts | 5 + .../ActionEntityStageChangeSettings.ts | 7 + .../ActionSettings/ActionHttpCallSettings.ts | 9 + .../ActionSettings/ActionSendOptions.ts | 9 + .../ActionSettings/ActionSendOptionsEntity.ts | 6 + .../ActionSettings/ActionSendOptionsValue.ts | 5 + .../ActionTaskCreateSettings.ts | 12 + .../ActionTypeEntityTypeInfo.ts | 12 + .../AutomationEntityType.ts | 98 + .../AutomationEntityTypeDeadline.ts | 12 + .../AutomationEntityTypeTemplateFormData.ts | 79 + .../Condition/AutomationFieldCondition.ts | 7 + .../Condition/EntityTypeCondition.ts | 6 + .../Condition/EntityTypeConditionModel.ts | 47 + .../EntityTypeFieldConditionFormData.ts | 127 + .../AutomationEntityType/DeadlineType.ts | 8 + .../AutomationEntityType/EntityTypeAction.ts | 27 + .../lib/models/AutomationModalBaseProps.ts | 11 + .../shared/lib/models/ResponsibleUser.ts | 12 + .../shared/lib/models/ResponsibleUserType.ts | 5 + .../automation/shared/lib/models/index.tsx | 26 + .../automation/store/AutomationStore.ts | 159 + .../src/modules/automation/store/index.tsx | 1 + .../AutomationUtilsApi/AutomationUtilsApi.ts | 26 + .../AutomationsProcessesApi.ts | 67 + .../queries/useCreateAutomationProcess.ts | 22 + .../queries/useDeleteAutomationProcess.ts | 24 + .../queries/useGetAutomationProcess.ts | 10 + .../queries/useGetAutomationsProcesses.ts | 12 + .../queries/useUpdateAutomationProcess.ts | 38 + .../bpmn/api/BpmnAutomationsApiRoutes.ts | 11 + .../bpmn/api/BpmnAutomationsQueryKeys.ts | 16 + .../AutomationProcessDto.ts | 14 + .../CreateAutomationProcessDto.ts | 18 + .../UpdateAutomationProcessDto.ts | 18 + frontend/src/modules/bpmn/api/dtos/index.tsx | 3 + frontend/src/modules/bpmn/api/index.tsx | 7 + .../modules/bpmn/extension/ExecutableFix.js | 37 + .../extension/WorkspaceContextPadProvider.js | 225 + .../bpmn/extension/WorkspacePalette.js | 113 + .../bpmn/extension/WorkspaceRenderer.js | 89 + frontend/src/modules/bpmn/extension/index.tsx | 13 + frontend/src/modules/bpmn/index.tsx | 1 + .../AutomationProcessesPage.tsx | 137 + .../ListSectionAutomationProcessesPage.tsx | 130 + .../AutomationProcessesSettingsHeader.tsx | 90 + .../components/index.tsx | 1 + frontend/src/modules/bpmn/pages/index.tsx | 2 + .../src/modules/bpmn/resources/workspace.json | 94 + .../src/modules/bpmn/shared/assets/bpmn.svg | 32 + .../bpmn/shared/assets/center_diagram.svg | 5 + .../src/modules/bpmn/shared/assets/index.tsx | 4 + .../bpmn/shared/assets/start_process.svg | 4 + .../bpmn/shared/assets/stop_process.svg | 3 + frontend/src/modules/bpmn/shared/index.tsx | 2 + .../AutomationProcessSchemaErrorWarning.tsx | 34 + .../AutomationProcesses.tsx | 59 + .../AutomationProcessesModeler.tsx | 463 + .../CenterDiagramViewButton.tsx | 54 + .../CreateAutomationButton.tsx | 43 + .../CreateAutomationProcessModal.tsx | 106 + .../CreateWorkspaceServiceTaskMenu.tsx | 111 + .../CreateWorkspaceStartEventMenu.tsx | 130 + .../PaletteSubMenu/PaletteSubMenu.tsx | 75 + .../PaletteSubMenuIcon/PaletteSubMenuIcon.tsx | 15 + .../PaletteSubMenuItem/PaletteSubMenuItem.tsx | 25 + .../PopupFormItem/PopupFormItem.tsx | 27 + .../PopupHeaderIcon/PopupHeaderIcon.tsx | 14 + .../ProcessControls/ProcessControls.tsx | 140 + .../ProcessElementPopup.tsx | 109 + .../WorkspaceDelayEventPopup.tsx | 118 + .../WorkspaceSequenceFlowPopup.tsx | 185 + .../ServiceTaskAddActivityPopup.tsx | 83 + .../ServiceTaskAddTaskPopup.tsx | 83 + .../ServiceTaskChangeLinkedStagePopup.tsx | 82 + .../ServiceTaskChangeResponsiblePopup.tsx | 69 + .../ServiceTaskChangeStagePopup.tsx | 84 + .../ServiceTaskCreateEntityPopup.tsx | 88 + .../ServiceTaskPopupTemplate.tsx | 61 + .../ServiceTaskRequestHttpPopup.tsx | 93 + .../ServiceTaskSendEmailPopup.tsx | 98 + .../ServiceTaskSendExternalChatPopup.tsx | 76 + .../ServiceTaskSendInternalChatPopup.tsx | 81 + .../WorkspaceServiceTaskPopupSwitch.tsx | 94 + .../WorkspaceStartEventPopup.tsx | 155 + .../components/index.tsx | 9 + .../AutomationProcessesTable.tsx | 79 + .../NoAutomationProcessesBlock.tsx | 55 + .../AutomationProcessStatusCell.tsx | 78 + .../AutomatonProcessNameCell.tsx | 123 + .../DeleteAutomationProcessCell.tsx | 60 + .../DeleteAutomationProcessWarning.tsx | 36 + .../components/index.tsx | 4 + .../BPMNRequestSplashscreen.tsx | 86 + .../bpmn/shared/lib/components/index.tsx | 10 + .../helpers/checkIfCanAddConditionToFlow.ts | 27 + ...eckIfElementHasServiceTasksInContextPad.ts | 11 + .../getCrmEventTypeByEntityTypeTrigger.ts | 14 + .../lib/helpers/getParsedZeebeInputString.ts | 14 + ...kZeebeInputTargetByEntityTypeActionType.ts | 38 + .../modules/bpmn/shared/lib/helpers/index.tsx | 7 + .../shared/lib/helpers/safeSetElementTitle.ts | 11 + .../lib/helpers/updateServiceTaskSettings.ts | 49 + .../modules/bpmn/shared/lib/hooks/index.tsx | 5 + .../hooks/useAutomationProcessesColumns.tsx | 81 + .../lib/hooks/useAutomationProcessesData.ts | 5 + .../lib/hooks/useGetWorkspaceEventOptions.ts | 54 + .../useGetWorkspaceServiceTaskOptions.ts | 114 + ...useHandleTranslateModelerElementsTitles.ts | 400 + .../src/modules/bpmn/shared/lib/index.tsx | 5 + .../lib/models/AutomationEntityCondition.ts | 8 + .../AutomationProcesses/AutomationProcess.ts | 55 + .../AutomationProcessInputTargetType.ts | 7 + .../AutomationProcessRow.ts | 36 + .../AutomationProcessType.ts | 4 + .../AutomationProcessesColumnsIds.ts | 6 + .../AutomationProcessesColumnsSizes.ts | 15 + .../bpmn/shared/lib/models/BpmnJsType.ts | 36 + .../lib/models/CommonServiceTaskPopupProps.ts | 12 + .../bpmn/shared/lib/models/EventBusEvent.ts | 5 + .../bpmn/shared/lib/models/ProcessMinimap.ts | 4 + .../bpmn/shared/lib/models/ZeebeType.ts | 5 + .../bpmn/shared/lib/models/constants.ts | 5 + .../modules/bpmn/shared/lib/models/index.tsx | 17 + .../shared/lib/utils/BpmnAutomationsUtil.ts | 11 + .../shared/lib/utils/EventMessageNameUtil.ts | 21 + .../modules/bpmn/shared/lib/utils/index.tsx | 2 + frontend/src/modules/bpmn/styles/bpmn-js.css | 483 + .../modules/builder/api/BuilderApiRoutes.ts | 7 + .../modules/builder/api/BuilderQueryKeys.ts | 8 + .../builder/api/SiteFormApi/SiteFormApi.ts | 71 + .../SiteFormApi/queries/useDeleteSiteForm.ts | 16 + .../SiteFormApi/queries/useGetSiteForms.ts | 9 + .../CreateSiteFormConsentDto.ts | 17 + .../CreateSiteForm/CreateSiteFormDto.ts | 62 + .../CreateSiteForm/CreateSiteFormFieldDto.ts | 29 + .../CreateSiteFormGratitudeDto.ts | 13 + .../CreateSiteForm/CreateSiteFormPageDto.ts | 14 + .../api/dtos/SiteForm/SiteFormConsentDto.ts | 10 + .../builder/api/dtos/SiteForm/SiteFormDto.ts | 29 + .../dtos/SiteForm/SiteFormEntityTypeDto.ts | 7 + .../api/dtos/SiteForm/SiteFormFieldDto.ts | 14 + .../SiteForm/SiteFormFieldEntityFieldDto.ts | 9 + .../SiteForm/SiteFormFieldEntityNameDto.ts | 3 + .../dtos/SiteForm/SiteFormFieldTextMetaDto.ts | 5 + .../api/dtos/SiteForm/SiteFormGratitudeDto.ts | 8 + .../api/dtos/SiteForm/SiteFormPageDto.ts | 9 + .../api/dtos/SiteForm/SiteFormScheduleDto.ts | 3 + .../UpdateSiteFormConsentDto.ts | 17 + .../UpdateSiteForm/UpdateSiteFormDto.ts | 62 + .../UpdateSiteForm/UpdateSiteFormFieldDto.ts | 32 + .../UpdateSiteFormGratitudeDto.ts | 13 + .../UpdateSiteForm/UpdateSiteFormPageDto.ts | 17 + .../src/modules/builder/api/dtos/index.tsx | 20 + frontend/src/modules/builder/api/index.tsx | 4 + frontend/src/modules/builder/index.tsx | 9 + .../pages/BuilderHomePage/BuilderHomePage.tsx | 31 + .../BuilderJourneyPicker.tsx | 370 + .../BuilderModuleOption.tsx | 216 + .../PickJourneyLink/PickJourneyLink.tsx | 71 + .../BuilderJourneyPicker/components/index.tsx | 2 + .../WorkspaceEditor/WorkspaceEditor.tsx | 221 + ...tityTypeFieldUsedInFormulaWarningModal.tsx | 32 + .../WorkspaceItem/WorkspaceItem.tsx | 149 + .../WorkspaceEditor/components/index.tsx | 2 + .../BuilderHomePage/components/index.tsx | 2 + .../EtSectionBuilderPage.tsx | 177 + .../EtSectionBuilderStep1.tsx | 189 + .../EtSectionBuilderStep2.tsx | 142 + .../EtSectionBuilderStep3.tsx | 125 + .../EtSectionBuilderStep4.tsx | 279 + .../TaskFieldsSelector/TaskFieldsSelector.tsx | 39 + .../EtSectionBuilderPage/components/index.tsx | 4 + .../HeadlessSiteFormBuilder.tsx | 270 + .../HeadlessSiteFormBuilderPage.tsx | 27 + .../HeadlessSiteFormBuilderStep2.tsx | 58 + .../HeadlessSiteFormElementsSidebar.tsx | 271 + .../HeadlessSiteFormElementsTree.tsx | 101 + .../components/index.tsx | 2 + .../HeadlessSiteFormBuilderStep3.tsx | 156 + .../HeadlessSiteFormFieldsTable.tsx | 83 + .../HeadlessSiteFormPlainFieldsTable.tsx | 65 + .../components/index.tsx | 2 + .../components/index.tsx | 2 + .../OnlineBookingSiteFormBuilder.tsx | 402 + .../OnlineBookingSiteFormBuilderPage.tsx | 27 + .../OnlineBookingSiteFormBuilderStep1.tsx | 243 + ...lineBookingSiteFormBuilderScheduleItem.tsx | 111 + .../components/index.tsx | 1 + .../OnlineBookingSiteFormBuilderStep2.tsx | 73 + .../components/index.tsx | 1 + .../ProductsSectionBuilderPage.tsx | 198 + ...ProductsSectionBuilderLinkSectionsStep.tsx | 165 + ...ProductsSectionBuilderScheduleSettings.tsx | 129 + .../ProductsSectionBuilderStep1.tsx | 147 + .../ProductsSectionBuilderStep2.tsx | 50 + .../ProductsSectionBuilderStep3.tsx | 176 + .../components/index.tsx | 5 + .../SchedulerBuilderPage.tsx | 123 + .../SchedulerBuilderStep1.tsx | 71 + .../ContentWrapper/ContentWrapper.tsx | 6 + .../FormItemWrapper/FormItemWrapper.tsx | 8 + .../components/RowWrapper/RowWrapper.tsx | 6 + .../SchedulerBuilderStep1BottomBlock.tsx | 184 + .../SchedulerBuilderStep1TopBlock.tsx | 276 + .../SchedulerBuilderStep1TypeBlock.tsx | 107 + .../components/index.tsx | 3 + .../SchedulerBuilderStep2.tsx | 97 + .../SchedulerBuilderStep3.tsx | 87 + .../SchedulerBuilderPage/components/index.tsx | 3 + .../SiteFormBuilderPage/SiteFormBuilder.tsx | 380 + .../SiteFormBuilderPage.tsx | 27 + .../SiteFormBuilderStep1.tsx | 205 + ...iteFormBuilderLinkedEntityItemCheckbox.tsx | 28 + .../SiteFormBuilderLinkedEntityItemRadio.tsx | 34 + ...iteFormBuilderLinkedEntityItemTemplate.tsx | 136 + .../SiteFormBuilderStep1/components/index.tsx | 2 + .../SiteFormBuilderStep2.tsx | 70 + .../SiteFormElementsSidebar.tsx | 331 + .../EntityTypeBlockTemplate.tsx | 84 + .../EntityTypeFieldsGroupsBlock.tsx | 143 + .../FieldAttributesBlock.tsx | 72 + .../FormElementBlockButton.tsx | 131 + .../FormElementCardNameBlock.tsx | 103 + .../FormElementCollapsibleBlock.tsx | 41 + .../FormElementFieldBlock.tsx | 125 + .../FormFieldElements/FormFieldElements.tsx | 209 + .../components/index.tsx | 5 + .../SiteFormElementsTree.tsx | 118 + .../SiteFormElementName.tsx | 28 + .../SiteFormDelimiterFieldElement.tsx | 43 + .../SiteFormElementTemplate.tsx | 246 + .../SiteFormEntityFieldElement.tsx | 183 + .../SiteFormSchedulerFieldElement.tsx | 70 + .../SiteFormElementsSwitch.tsx | 123 + .../SiteFormElementsTree/components/index.tsx | 2 + .../SiteFormBuilderStep2/components/index.tsx | 4 + .../SiteFormBuilderStep3.tsx | 34 + .../BlockTemplate/BlockTemplate.tsx | 38 + .../ShowInFormControl/ShowInFormControl.tsx | 43 + .../SiteFormConsentBlock.tsx | 39 + .../SiteFormConsentControls.tsx | 118 + .../SiteFormConsentSkeleton.tsx | 175 + .../SiteFormConsentBlock/components/index.tsx | 2 + .../SiteFormGratitudeBlock.tsx | 111 + .../SiteFormBuilderStep3/components/index.tsx | 2 + .../SiteFormBuilderStep4.tsx | 48 + .../SiteFormClientPreview.tsx | 101 + .../DeviceSelector/DeviceSelector.tsx | 120 + .../SiteFormClientPreviewOnDevices.tsx | 93 + .../SiteFormComputerPreview.tsx | 47 + .../SiteFormContentTabs.tsx | 88 + .../SiteFormPhonePreview.tsx | 83 + .../SiteFormTabletPreview.tsx | 60 + .../components/index.tsx | 1 + .../SiteFormCustomizationSidebar.tsx | 120 + .../AdvancedSettingsDelimiter.tsx | 59 + .../ClientButtonCustomizationBlock.tsx | 138 + .../CustomCSSBlock/CustomCSSBlock.tsx | 59 + .../CustomizationBlockTemplate.tsx | 59 + .../CustomizationSlider.tsx | 101 + .../FieldsCustomizationBlock.tsx | 47 + .../FormButtonCustomizationBlock.tsx | 138 + .../FormLayoutCustomizationBlock.tsx | 79 + .../HeaderCustomizationBlock.tsx | 36 + .../ModalOverlayCustomizationBlock.tsx | 57 + .../OrientationRowSelect.tsx | 144 + .../PoweredByLogoBlock/PoweredByLogoBlock.tsx | 50 + .../RadioGroupControl/RadioGroupControl.tsx | 73 + .../SiteFormCustomizationTextInput.tsx | 37 + .../SizeCustomizationElementTemplate.tsx | 43 + .../components/index.tsx | 10 + .../SiteFormBuilderStep4/components/index.tsx | 2 + .../SiteFormBuilderStep5.tsx | 215 + .../AnalyticsEventCode/AnalyticsEventCode.tsx | 43 + .../AnalyticsEventsTable.tsx | 82 + .../components/CodeBlock/CodeBlock.tsx | 22 + .../SiteFormCodeBlock/SiteFormCodeBlock.tsx | 68 + .../SiteFormBuilderStep5/components/index.tsx | 3 + .../SiteFormBuilderPage/components/index.tsx | 10 + frontend/src/modules/builder/pages/index.tsx | 7 + .../shared/assets/advanced_settings.svg | 5 + .../builder/shared/assets/align_center.svg | 5 + .../builder/shared/assets/align_left.svg | 4 + .../builder/shared/assets/align_right.svg | 4 + .../builder/shared/assets/border_rounded.svg | 3 + .../builder/shared/assets/border_square.svg | 3 + .../builder/shared/assets/builder_tab.svg | 14 + .../builder/shared/assets/calendar.svg | 11 + .../builder/shared/assets/card_name.svg | 11 + .../shared/assets/computer_preview.svg | 6 + .../shared/assets/consent_skeleton_bottom.svg | 11 + .../shared/assets/consent_skeleton_top.svg | 15 + .../modules/builder/shared/assets/date.svg | 11 + .../builder/shared/assets/delimiter.svg | 5 + .../builder/shared/assets/expand_more.svg | 5 + .../modules/builder/shared/assets/file.svg | 5 + .../builder/shared/assets/file_image.svg | 5 + .../shared/assets/gratitude_skeleton.svg | 7 + .../modules/builder/shared/assets/header.svg | 3 + .../builder/shared/assets/home_button.svg | 4 + .../modules/builder/shared/assets/index.tsx | 45 + .../modules/builder/shared/assets/link.svg | 5 + .../builder/shared/assets/long_text.svg | 16 + .../modules/builder/shared/assets/mail.svg | 14 + .../builder/shared/assets/multiselect.svg | 7 + .../shared/assets/multiselect_caret.svg | 8 + .../modules/builder/shared/assets/number.svg | 8 + .../shared/assets/orientation_horizontal.svg | 4 + .../shared/assets/orientation_vertical.svg | 4 + .../modules/builder/shared/assets/phone.svg | 14 + .../builder/shared/assets/phone_preview.svg | 5 + .../builder/shared/assets/scheduler.svg | 11 + .../modules/builder/shared/assets/select.svg | 5 + .../builder/shared/assets/select_caret.svg | 5 + .../builder/shared/assets/short_text.svg | 8 + .../builder/shared/assets/step_back.svg | 5 + .../builder/shared/assets/step_ellipse.svg | 9 + .../builder/shared/assets/step_next.svg | 5 + .../modules/builder/shared/assets/switch.svg | 5 + .../builder/shared/assets/switch_image.svg | 19 + .../builder/shared/assets/tablet_preview.svg | 4 + .../shared/assets/text_align_center.svg | 6 + .../builder/shared/assets/text_align_left.svg | 6 + .../shared/assets/text_align_right.svg | 6 + .../modules/builder/shared/assets/wazzup.svg | 9 + .../builder/shared/assets/workspace_tab.svg | 14 + frontend/src/modules/builder/shared/index.tsx | 2 + .../BuilderNav/NavStepItem/NavStepItem.tsx | 148 + .../BuilderNav/NavStepsList/NavStepsList.tsx | 75 + .../BuilderPageRoot/BuilderPageRoot.tsx | 26 + .../BuilderStepBiggerTitle.tsx | 11 + .../BuilderStepBox/BuilderStepBox.tsx | 10 + .../BuilderStepCheckboxItemWrapper.tsx | 23 + .../BuilderStepControls.tsx | 252 + .../BuilderStepItemLabel.tsx | 32 + .../BuilderStepOutlinedSection.tsx | 19 + .../BuilderStepSubtitle.tsx | 10 + .../BuilderStepTitle/BuilderStepTitle.tsx | 8 + .../ColorRowSelect/ColorRowSelect.tsx | 170 + .../EntityTypeUsedInFormulaWarningModal.tsx | 32 + .../GiantOutlinedInput/GiantOutlinedInput.tsx | 151 + .../GiantOutlinedInputSkeleton.tsx | 20 + .../GiantOutlinedTextarea.tsx | 112 + .../GiantUnderlinedInput.tsx | 183 + .../HorizontalStepMotion.tsx | 37 + .../LinkRadiosDelimiter.tsx | 7 + .../LinkRadiosWrapper/LinkRadiosWrapper.tsx | 9 + .../LoadableSectionImage.tsx | 81 + .../NameInputSkeleton/NameInputSkeleton.tsx | 17 + .../components/NoLinksBlock/NoLinksBlock.tsx | 21 + .../components/RadioWrapper/RadioWrapper.tsx | 15 + .../RadiosSkeleton/RadiosSkeleton.tsx | 43 + .../SectionIconPicker/SectionIconPicker.tsx | 146 + .../SiteFormBuilderPageStepRoot.tsx | 6 + .../components/SwitchBlock/SwitchBlock.tsx | 56 + .../builder/shared/lib/components/index.tsx | 32 + .../generateEtSectionBuilderNavSteps.ts | 29 + .../generateFormLayoutRadioOptions.tsx | 69 + ...generateHeadlessSiteFormBuilderNavSteps.ts | 23 + ...ateOnlineBookingSiteFormBuilderNavSteps.ts | 35 + .../generateProductsSectionBuilderNavSteps.ts | 62 + .../generateRentalIntervalStartTimeOptions.ts | 12 + .../generateRentalIntervalTypeOptions.ts | 10 + .../generateSchedulerBuilderNavSteps.ts | 23 + ...nerateSchedulerBuilderTimePeriodOptions.ts | 28 + .../generateSiteFormBuilderNavSteps.ts | 35 + .../shared/lib/helpers/getJourneyLink.ts | 86 + ...etSiteFormElementPlaceholderByFieldType.ts | 23 + .../builder/shared/lib/helpers/index.tsx | 13 + .../mapModuleCategoryToEntityCategory.ts | 41 + .../builder/shared/lib/hooks/index.tsx | 6 + .../lib/hooks/useAdditionalModulesOptions.tsx | 92 + .../hooks/useBuilderMarketplaceOptions.tsx | 167 + .../hooks/useGetAdditionalStorageOptions.tsx | 58 + ...tProductsSectionAfterCancelDelayOptions.ts | 82 + .../lib/hooks/useGetTasksFieldsOptions.ts | 22 + .../lib/hooks/useModulesCategoriesOptions.tsx | 203 + .../src/modules/builder/shared/lib/index.tsx | 5 + .../lib/models/AdditionalModuleCategory.ts | 9 + .../BuilderAdditionalStorageCategory.ts | 5 + .../lib/models/BuilderMarketplaceCategory.ts | 14 + .../shared/lib/models/BuilderNavStep.ts | 6 + .../builder/shared/lib/models/BuilderTabs.ts | 4 + .../lib/models/EtSectionBuilderFormData.ts | 182 + .../lib/models/EtSectionBuilderModel.ts | 19 + .../shared/lib/models/ModuleCategory.ts | 17 + .../shared/lib/models/ModuleOptionExtra.ts | 11 + .../shared/lib/models/NoLinkRadioValue.ts | 1 + .../shared/lib/models/PreviewDevice.ts | 5 + .../builder/shared/lib/models/PreviewTab.ts | 5 + .../models/SchedulerSectionBuilderFormData.ts | 254 + .../Design/SiteFormButtonDesignFormData.ts | 65 + .../SiteFormClientButtonDesignFormData.ts | 76 + .../Design/SiteFormDesignCustomCSSFormData.ts | 33 + .../Design/SiteFormFieldsDesignFormData.ts | 68 + .../Design/SiteFormHeaderDesignFormData.ts | 51 + .../Design/SiteFormLayoutDesignFormData.ts | 101 + .../SiteFormModalOverlayDesignFormData.ts | 46 + .../HeadlessSiteFormElementsFormData.ts | 144 + .../OnlineBookingSiteFormElementsFormData.ts | 251 + ...neBookingSiteFormInitializationFormData.ts | 163 + .../FormData/SiteFormConsentFormData.ts | 86 + .../FormData/SiteFormDesignFormData.ts | 102 + .../FormData/SiteFormElementsFieldModel.ts | 110 + .../FormData/SiteFormElementsFormData.ts | 193 + .../FormData/SiteFormElementsPageFormData.ts | 48 + .../FormData/SiteFormElementsTitleFormData.ts | 28 + .../FormData/SiteFormEntityTypeLinkModel.ts | 90 + .../FormData/SiteFormFieldEntityFieldModel.ts | 50 + .../FormData/SiteFormGratitudeFormData.ts | 61 + .../SiteFormInitializationFormData.ts | 140 + .../shared/lib/models/SiteForm/SiteForm.ts | 101 + .../lib/models/SiteForm/SiteFormConsent.ts | 31 + .../SiteForm/SiteFormDesign/SiteFormBorder.ts | 4 + .../SiteFormDesign/SiteFormCustomCSS.ts | 9 + .../SiteForm/SiteFormDesign/SiteFormDesign.ts | 40 + .../SiteFormDesignClientButton.ts | 29 + .../SiteFormDesign/SiteFormDesignFields.ts | 27 + .../SiteFormDesignFormButton.ts | 24 + .../SiteFormDesignFormLayout.ts | 42 + .../SiteFormDesign/SiteFormDesignHeader.ts | 14 + .../SiteFormDesignModalOverlay.ts | 11 + .../SiteFormDesign/SiteFormFontSize.ts | 5 + .../SiteFormDesign/SiteFormOrientation.ts | 4 + .../SiteFormDesign/SiteFormPosition.ts | 5 + .../SiteFormDesign/SiteFormTextOrientation.ts | 5 + .../SiteForm/SiteFormDesign/SiteFormView.ts | 4 + .../lib/models/SiteForm/SiteFormEntityType.ts | 26 + .../SiteForm/SiteFormField/SiteFormField.ts | 53 + .../SiteFormField/SiteFormFieldEntityField.ts | 26 + .../SiteFormField/SiteFormFieldEntityName.ts | 15 + .../SiteFormField/SiteFormFieldTextMeta.ts | 16 + .../SiteFormField/SiteFormFieldTextView.ts | 4 + .../SiteFormField/SiteFormFieldType.ts | 10 + .../lib/models/SiteForm/SiteFormGratitude.ts | 25 + .../lib/models/SiteForm/SiteFormPage.ts | 30 + .../lib/models/SiteForm/SiteFormSchedule.ts | 17 + .../lib/models/SiteForm/SiteFormType.ts | 4 + .../builder/shared/lib/models/index.tsx | 47 + .../lib/types/SchedulerIntervalSource.ts | 4 + .../SiteForm/AddSiteFormCardNameHandler.ts | 9 + .../types/SiteForm/AddSiteFormFieldHandler.ts | 18 + .../builder/shared/lib/types/index.tsx | 9 + .../modules/builder/store/BuilderNavStore.ts | 63 + .../builder/store/EtSectionBuilderStore.ts | 280 + .../store/HeadlessSiteFormBuilderStore.ts | 143 + .../OnlineBookingSiteFormBuilderStore.ts | 218 + .../builder/store/SchedulerBuilderStore.ts | 183 + .../builder/store/SiteFormBuilderStore.ts | 189 + frontend/src/modules/builder/store/index.tsx | 6 + .../BuilderPageTemplate.tsx | 73 + .../BuilderStepTemplate.tsx | 104 + .../BuilderWithHorizontalNavPageTemplate.tsx | 60 + .../BuilderWithVerticalNavPageTemplate.tsx | 79 + .../src/modules/builder/templates/index.tsx | 4 + frontend/src/modules/card/CardApiRoutes.ts | 12 + .../EntityDocumentApi/EntityDocumentApi.ts | 45 + .../src/modules/card/api/FeedApi/FeedApi.ts | 34 + .../src/modules/card/api/NoteApi/NoteApi.ts | 39 + .../modules/card/api/dtos/CreateNoteDto.ts | 9 + frontend/src/modules/card/api/dtos/NoteDto.ts | 10 + .../modules/card/api/dtos/UpdateNoteDto.ts | 4 + frontend/src/modules/card/api/dtos/index.tsx | 3 + frontend/src/modules/card/api/index.tsx | 5 + frontend/src/modules/card/index.tsx | 4 + .../card/pages/AddCardPage/AddCardPage.tsx | 147 + .../modules/card/pages/CardPage/CardPage.tsx | 729 + frontend/src/modules/card/pages/index.tsx | 2 + .../card/shared/assets/ChatTags/avito_tag.svg | 24 + .../assets/ChatTags/fb_messenger_tag.svg | 18 + .../shared/assets/ChatTags/instagram_tag.svg | 31 + .../shared/assets/ChatTags/telegram_tag.svg | 19 + .../card/shared/assets/ChatTags/vk_tag.svg | 5 + .../shared/assets/ChatTags/whatsapp_tag.svg | 25 + .../shared/assets/FeedControl/activity.svg | 5 + .../card/shared/assets/FeedControl/chat.svg | 9 + .../shared/assets/FeedControl/documents.svg | 5 + .../card/shared/assets/FeedControl/filter.svg | 7 + .../card/shared/assets/FeedControl/note.svg | 17 + .../card/shared/assets/FeedControl/plus.svg | 6 + .../shared/assets/FeedControl/show_more.svg | 5 + .../card/shared/assets/FeedControl/task.svg | 5 + .../card/shared/assets/FeedControl/visit.svg | 11 + .../shared/assets/FeedIcons/blue_file.svg | 7 + .../shared/assets/FeedIcons/call_incoming.svg | 11 + .../shared/assets/FeedIcons/call_missed.svg | 11 + .../shared/assets/FeedIcons/call_outgoing.svg | 11 + .../shared/assets/FeedIcons/mail_blue.svg | 16 + .../shared/assets/FeedIcons/task_green.svg | 7 + .../assets/FeedIcons/task_grey_dark.svg | 7 + .../assets/FeedIcons/task_grey_light.svg | 7 + .../card/shared/assets/FeedIcons/task_red.svg | 7 + .../shared/assets/FeedIcons/violet_pencil.svg | 20 + .../card/shared/assets/account_tree_empty.svg | 5 + .../modules/card/shared/assets/airtable.svg | 24 + .../src/modules/card/shared/assets/amocrm.svg | 30 + .../card/shared/assets/arrows_exchange.svg | 8 + .../src/modules/card/shared/assets/bitrix.svg | 37 + .../src/modules/card/shared/assets/clip.svg | 5 + .../src/modules/card/shared/assets/clock.svg | 14 + .../card/shared/assets/close_cross.svg | 5 + .../modules/card/shared/assets/document.svg | 5 + .../src/modules/card/shared/assets/excel.svg | 38 + .../modules/card/shared/assets/facebook.svg | 33 + .../card/shared/assets/feed_calendar.svg | 8 + .../modules/card/shared/assets/forward.svg | 5 + .../modules/card/shared/assets/freshsales.svg | 43 + .../modules/card/shared/assets/hubspot.svg | 30 + .../src/modules/card/shared/assets/index.tsx | 59 + .../modules/card/shared/assets/instagram.svg | 12 + .../modules/card/shared/assets/linkedin.svg | 12 + .../modules/card/shared/assets/microsoft.svg | 16 + .../src/modules/card/shared/assets/monday.svg | 24 + .../src/modules/card/shared/assets/notion.svg | 15 + .../src/modules/card/shared/assets/oracle.svg | 12 + .../modules/card/shared/assets/pipedrive.svg | 31 + .../src/modules/card/shared/assets/reply.svg | 5 + .../card/shared/assets/ringing_phone.svg | 14 + .../src/modules/card/shared/assets/rocket.svg | 5 + .../modules/card/shared/assets/salesforce.svg | 5 + .../src/modules/card/shared/assets/sap.svg | 13 + .../modules/card/shared/assets/sugarcrm.svg | 15 + .../shared/assets/three_dots_vertical.svg | 5 + .../src/modules/card/shared/assets/timer.svg | 14 + .../src/modules/card/shared/assets/tune.svg | 5 + .../modules/card/shared/assets/twitter.svg | 12 + .../modules/card/shared/assets/zendesk.svg | 12 + .../src/modules/card/shared/assets/zoho.svg | 46 + frontend/src/modules/card/shared/index.tsx | 2 + .../lib/components/ActionMenu/ActionMenu.tsx | 122 + .../ActionsDropdown/ActionsDropdown.tsx | 200 + .../lib/components/BudgetBlock/BudgetRoot.tsx | 14 + .../BudgetBlock/CommonBudgetBlock.tsx | 21 + .../BudgetBlock/ProjectBudgetBlock.tsx | 29 + .../CardComponent/CardComponent.tsx | 723 + .../CardNameBlock/CardNameBlock.tsx | 286 + .../CardPageHeader/CardPageHeader.tsx | 143 + .../CardPageTasksProjectHeaderControls.tsx | 52 + .../lib/components/CardStages/CardStages.tsx | 208 + .../lib/components/CardStages/StageItem.tsx | 126 + .../components/CardStages/SystemStageItem.tsx | 86 + .../CardSystemInfoBlock.tsx | 75 + .../CardSystemInfoBlockItem.tsx | 47 + .../CardSystemInfoBlock/components/index.tsx | 1 + .../ChangeLinkedResponsiblesModal.tsx | 196 + .../EntityTypeLinksBlock.tsx | 72 + .../card/shared/lib/components/Feed/Feed.tsx | 328 + .../ActivityTypeControl.tsx | 280 + .../ActivityTypeItem/ActivityTypeItem.tsx | 150 + .../components/AddTypeBlock/AddTypeBlock.tsx | 61 + .../ActivityTypeControl/components/index.tsx | 2 + .../components/ActivityTypeControl/index.tsx | 5 + .../Feed/components/CallBlock/CallBlock.tsx | 279 + .../DocumentBlock/DocumentBlock.tsx | 50 + .../components/FeedControl/FeedControl.tsx | 391 + .../FeedControlTab/FeedControlTab.tsx | 183 + .../MoreTabsDropdown/MoreTabsDropdown.tsx | 93 + .../AddActivityTabPanel.tsx | 220 + .../AddNoteTabPanel/AddNoteTabPanel.tsx | 139 + .../TextEditorWrapper/TextEditorWrapper.tsx | 34 + .../AddTaskTab/AddTaskModal/AddTaskModal.tsx | 407 + .../AddTaskModalFormItem.tsx | 41 + .../AddTaskModal/components/index.tsx | 1 + .../AddTaskModalHeader/AddTaskModalHeader.tsx | 40 + .../Tabs/AddTaskTab/Subtask/SubtaskItem.tsx | 168 + .../Tabs/AddTaskTab/Subtask/SubtasksBlock.tsx | 142 + .../Tabs/AddTaskTab/Subtask/SubtasksList.tsx | 91 + .../TaskSettingsDropdown.tsx | 87 + .../TasksBoardSelector/TasksBoardSelector.tsx | 113 + .../CreateDocumentsTab/CreateDocumentsTab.tsx | 128 + .../AddTemplatePlaceholder.tsx | 47 + .../CreateDocumentsDrawer.tsx | 315 + .../CreateDocumentsDrawerContent.tsx | 93 + .../components/index.tsx | 1 + .../CreateDocumentsHint.tsx | 93 + .../GeneratedDocumentItem.tsx | 130 + .../GeneratedDocumentsList.tsx | 182 + .../MissingTagsList/MissingTagsList.tsx | 47 + .../SelectFormatsHint/SelectFormatsHint.tsx | 91 + .../CreateDocumentsTab/components/index.tsx | 1 + .../FeedControl/components/index.tsx | 14 + .../FeedGroupList/FeedGroupList.tsx | 95 + .../FeedItem/ActivityBlock/ActivityBlock.tsx | 269 + .../AttachmentsBlock/AttachmentsBlock.tsx | 130 + .../AttachmentsBlock/AttachmentsMailBlock.tsx | 133 + .../DateGroupHeaderLeftBlock.tsx | 26 + .../FeedItem/Common/FeedItem/FeedItem.tsx | 225 + .../FeedItemLeftBlock/FeedItemLeftBlock.tsx | 47 + .../FeedItemShowMoreButton.tsx | 50 + .../HeaderControlsDropdown.tsx | 105 + .../InnerHTMLNormalizer.tsx | 12 + .../components/FeedItem/Common/Item/Item.tsx | 42 + .../ItemInfo/AuthorBlock/AuthorBlock.tsx | 30 + .../Common/ItemInfo/DateBlock/DateBlock.tsx | 47 + .../Common/ItemInfo/InfoBlock/InfoBlock.tsx | 163 + .../MailFileFeedItem/MailFileFeedItem.tsx | 192 + .../Common/ResultBlock/ResultBlock.tsx | 140 + .../TextEditorControls/TextEditorControls.tsx | 109 + .../Feed/components/FeedItem/Common/index.tsx | 12 + .../Feed/components/FeedItem/index.tsx | 1 + .../components/FeedItemList/FeedItemList.tsx | 124 + .../FeedSkeleton/FeedBlockSkeleton.tsx | 49 + .../components/FeedSkeleton/FeedSkeleton.tsx | 45 + .../components/FiltersBlock/FiltersBlock.tsx | 155 + .../MailThreadBlock/MailThreadBlock.tsx | 90 + .../MailMessageBlock/MailMessageBlock.tsx | 456 + .../ReplyControls/ReplyControls.tsx | 128 + .../MailThreadBlock/components/index.tsx | 1 + .../Feed/components/NoteBlock/NoteBlock.tsx | 80 + .../NoteBlockTemplate/NoteBlockTemplate.tsx | 104 + .../components/NoteBlock/components/index.tsx | 1 + .../components/PlannerBody/PlannerBody.tsx | 8 + .../Feed/components/TaskBlock/TaskBlock.tsx | 257 + .../TaskItemBottom/TaskItemBottom.tsx | 173 + .../components/TaskBlock/components/index.tsx | 1 + .../Feed/components/TimePicker/TimePicker.tsx | 88 + .../lib/components/Feed/components/index.tsx | 13 + .../card/shared/lib/components/Feed/index.tsx | 1 + .../components/FocusButton/FocusButton.tsx | 86 + .../LinkedEntitiesBlock.tsx | 169 + .../AddLinkedEntityButton.tsx | 179 + .../LinkedEntitiesBlock/components/index.tsx | 1 + .../LinkedEntitiesHeaderLinkItem.tsx | 124 + .../LinkedEntitiesHeaderLinks.tsx | 61 + .../components/MessengerTag/MessengerTag.tsx | 49 + .../components/MiniCard/ExternalMiniCard.tsx | 205 + .../lib/components/MiniCard/MiniCard.tsx | 713 + .../components/CardBlock/CardBlock.tsx | 18 + .../SearchEntitiesBlock.tsx | 216 + .../components/MiniCard/components/index.tsx | 2 + .../OverviewComponent/OverviewComponent.tsx | 250 + .../RelocateCardButton/RelocateCardButton.tsx | 127 + .../ScrollController/ScrollButton.tsx | 58 + .../ScrollController/ScrollController.tsx | 48 + ...dFormulaCircularDependencyWarningModal.tsx | 49 + .../FieldUsedInFormulaWarningModal.tsx | 47 + .../MutationWarningModal.tsx | 32 + .../card/shared/lib/components/index.tsx | 15 + .../generateProductsSectionOrderTabValue.ts | 11 + .../lib/helpers/getCallItemIconAndStatus.tsx | 37 + .../getCardPageTabsWithProductsSections.tsx | 134 + .../shared/lib/helpers/getFeedItemColors.tsx | 39 + .../lib/helpers/getMessengerTagIcon.tsx | 35 + .../modules/card/shared/lib/helpers/index.tsx | 10 + .../shared/lib/helpers/isFeedItemOutlined.ts | 6 + .../modules/card/shared/lib/hooks/index.tsx | 5 + .../shared/lib/hooks/useFeedItemOutline.ts | 22 + .../card/shared/lib/hooks/useGetChatTags.ts | 29 + .../lib/hooks/useGetProjectTaskBoardId.ts | 10 + .../lib/hooks/useGetUserWorkingTimeSlots.ts | 108 + .../lib/hooks/useRequisitesSuggestions.ts | 297 + .../src/modules/card/shared/lib/index.tsx | 5 + .../card/shared/lib/models/CallType.ts | 6 + .../card/shared/lib/models/CardSavedEvent.ts | 7 + .../modules/card/shared/lib/models/CardTab.ts | 11 + .../modules/card/shared/lib/models/ChatTag.ts | 6 + .../lib/models/DocumentTemplateError.ts | 1 + .../card/shared/lib/models/EditTextProps.ts | 8 + .../card/shared/lib/models/FeedItemMeta.ts | 4 + .../card/shared/lib/models/FeedItemType.ts | 14 + .../shared/lib/models/MutationWarningCode.ts | 4 + .../card/shared/lib/models/TaskStatus.ts | 5 + .../shared/lib/models/TaskTimeStatusIcons.tsx | 10 + .../modules/card/shared/lib/models/index.tsx | 12 + .../shared/lib/models/orderIdQueryParam.ts | 2 + .../card/shared/lib/types/FindChatHandler.ts | 4 + .../modules/card/shared/lib/types/index.tsx | 1 + .../src/modules/card/store/CardFilesStore.ts | 36 + frontend/src/modules/card/store/CardStore.ts | 718 + .../modules/card/store/CreateDocumentStore.ts | 122 + .../card/store/EntityTypeLinksStore.ts | 44 + frontend/src/modules/card/store/FeedStore.ts | 328 + .../modules/card/store/LinkedEntityStore.ts | 324 + frontend/src/modules/card/store/index.tsx | 6 + .../api/FieldHelperApi/FieldHelperApi.ts | 15 + .../queries/useGetPhoneUserInfo.ts | 15 + .../api/FieldSettingsApi/FieldSettingsApi.ts | 40 + .../queries/useGetFieldSettings.ts | 11 + .../src/modules/fields/api/FieldsApiRoutes.ts | 6 + .../src/modules/fields/api/FieldsQueryKeys.ts | 11 + .../fields/api/dtos/CheckFormulaDto.ts | 11 + .../src/modules/fields/api/dtos/FieldDto.ts | 43 + .../modules/fields/api/dtos/FieldGroupDto.ts | 18 + .../modules/fields/api/dtos/FieldOptionDto.ts | 17 + .../dtos/FieldSettings/FieldSettingsDto.ts | 11 + .../FieldSettings/UpdateFieldSettingsDto.ts | 26 + .../modules/fields/api/dtos/FieldValueDto.ts | 15 + .../fields/api/dtos/PhoneUserInfoDto.ts | 15 + .../fields/api/dtos/SimpleFieldValueDto.ts | 16 + .../src/modules/fields/api/dtos/index.tsx | 9 + frontend/src/modules/fields/api/index.tsx | 4 + .../CardFieldHelperContext.ts | 15 + .../context/CardFieldHelperContext/index.tsx | 3 + .../useCardFieldHelperContext.ts | 6 + .../DuplicatesContext/DuplicatesContext.ts | 11 + .../context/DuplicatesContext/index.tsx | 3 + .../DuplicatesContext/useDuplicatesContext.ts | 6 + .../MakeCallContext/MakeCallContext.ts | 11 + .../MakeCallContext/MakeCallProvider.tsx | 28 + .../fields/context/MakeCallContext/index.tsx | 2 + .../MakeCallContext/useMakeCallContext.ts | 5 + .../SendEmailContext/SendEmailContext.ts | 8 + .../fields/context/SendEmailContext/index.tsx | 3 + .../SendEmailContext/useSendEmailContext.ts | 6 + frontend/src/modules/fields/context/index.tsx | 4 + frontend/src/modules/fields/index.tsx | 4 + .../fields/shared/assets/clear_entry.svg | 7 + .../fields/shared/assets/edit_formula.svg | 5 + .../fields/shared/assets/edit_text.svg | 5 + .../fields/shared/assets/field_filled.svg | 5 + .../fields/shared/assets/important_field.svg | 5 + .../modules/fields/shared/assets/index.tsx | 13 + .../src/modules/fields/shared/assets/link.svg | 8 + .../fields/shared/assets/list_select.svg | 5 + .../fields/shared/assets/mandatory_field.svg | 5 + .../fields/shared/assets/phone_call.svg | 14 + .../fields/shared/assets/send_email.svg | 5 + .../modules/fields/shared/assets/telegram.svg | 5 + .../fields/shared/assets/tune_field.svg | 5 + .../fields/shared/assets/whats_app.svg | 5 + frontend/src/modules/fields/shared/index.tsx | 2 + .../lib/components/EditFields/EditFields.tsx | 161 + .../AnalyticsSubgroupControls.tsx | 70 + .../EditFieldFormGroupComponent.tsx | 258 + .../EditFieldsTab/EditFieldsTab.tsx | 359 + .../FieldFormulaSettingsButton.tsx | 101 + .../FieldFormulaSettingsModal.tsx | 256 + .../FieldFormulaSettingsModalFields.tsx | 269 + .../FieldFormulaSettingsModalKeys.tsx | 192 + .../FormulaKeyButton/FormulaKeyButton.tsx | 79 + .../FormulaKeysButtons/FormulaKeysButtons.tsx | 27 + .../components/index.tsx | 1 + .../FieldSettingsModal/FieldSettingsModal.tsx | 441 + .../FieldSettingsGroup/FieldSettingsGroup.tsx | 56 + .../FieldSettingsSubGroup.tsx | 55 + .../FieldSettingsModal/components/index.tsx | 2 + .../components/OptionBlock/OptionBlock.tsx | 85 + .../RequisitesSubgroupControls.tsx | 82 + .../SelectOptions/SelectOptions.tsx | 241 + .../EditFields/components/index.tsx | 4 + .../FieldLinkWrapper/FieldLinkWrapper.tsx | 55 + .../FieldTextInput/FieldTextInput.tsx | 49 + .../FieldTextPrimitive/FieldTextPrimitive.tsx | 484 + .../components/FieldValue/FieldIndicator.tsx | 62 + .../FieldValue/FieldSelectWrapper.tsx | 9 + .../CheckedMultiselectFieldValueComp.tsx | 78 + .../ColoredMultiselectFieldValueComp.tsx | 63 + .../ColoredSelectFieldValueComp.tsx | 86 + .../FieldValueComps/DateFieldValueComp.tsx | 58 + .../FieldValueComps/FileFieldValueComp.tsx | 97 + .../FieldValueComps/FormulaFieldValueComp.tsx | 79 + .../FieldValueComps/LinkFieldValueComp.tsx | 62 + .../MultiselectFieldValueComp.tsx | 57 + .../ChecklistFieldValueComp.tsx | 47 + .../ChecklistFieldValueCompItem.tsx | 79 + .../EmailFieldValueComp.tsx | 108 + .../EmailFieldInput/EmailFieldInput.tsx | 165 + .../EmailFieldValueComp/components/index.tsx | 1 + .../MultitextFieldValueComp.tsx | 78 + .../PhoneFieldValueComp.tsx | 95 + .../PhoneFieldValueCompItem.tsx | 91 + .../TelegramLinkWrapper.tsx | 14 + .../TimezoneWrapper/TimezoneWrapper.tsx | 43 + .../WhatsAppLinkWrapper.tsx | 48 + .../PhoneFieldValueComp/components/index.tsx | 4 + .../MultitextFieldValueCompTemplate.tsx | 55 + .../MultitextFieldsWrapper.tsx | 12 + .../MultitextInputWrapper.tsx | 14 + .../components/index.tsx | 2 + .../FieldValueComps/NumberFieldValueComp.tsx | 74 + .../ParticipantFieldValueComp.tsx | 64 + .../ParticipantsFieldValueComp.tsx | 89 + .../ProjectFieldsBlock/ProjectFieldsBlock.tsx | 89 + .../ProjectFieldsSettingsComponent.tsx | 99 + .../ProjectSystemFieldValueFormGroup.tsx | 44 + .../RichTextFieldValueComp.tsx | 62 + .../FieldValueComps/SelectFieldValueComp.tsx | 77 + .../FieldValueComps/SwitchFieldValueComp.tsx | 56 + .../FieldValueComps/TextFieldValueComp.tsx | 259 + .../FieldValue/FieldValueFormGroup.tsx | 152 + .../FieldValue/FieldValueSwitch.tsx | 292 + .../FieldValue/FieldValueTemplate.tsx | 138 + .../OrganizationAutocompleteField.tsx | 143 + .../OrganizationAutocompleteField/README.md | 80 + .../PhoneFieldControls/ConditionalPopover.tsx | 36 + .../PhoneFieldControls/PhoneFieldControls.tsx | 605 + .../RequisitesSuggestions.tsx | 208 + .../lib/components/ShowFields/ShowFields.tsx | 79 + .../MoreTabsDropdown/MoreTabsDropdown.tsx | 100 + .../ShowFieldsTab/ShowFieldsTab.tsx | 297 + .../ShowFields/components/index.tsx | 1 + .../lib/components/ShowFiles/ShowFiles.tsx | 119 + .../components/TimezoneHint/TimezoneHint.tsx | 82 + .../fields/shared/lib/components/index.tsx | 28 + .../FormulaField/generateFormulaFieldCode.ts | 7 + .../FormulaField/getFormulaFieldKeys.tsx | 149 + .../generateTelegramExternalLink.ts | 7 + .../generateWhatsAppExternalLink.ts | 7 + .../sanitizeAndModifyPhoneNumber.ts | 11 + .../shared/lib/helpers/getFieldTypes.ts | 16 + .../fields/shared/lib/helpers/index.tsx | 6 + .../useGetCreateExternalChatHandler.ts | 75 + .../modules/fields/shared/lib/hooks/index.tsx | 1 + .../src/modules/fields/shared/lib/index.tsx | 6 + .../lib/models/AnalyticsFieldSubgroup.ts | 52 + .../fields/shared/lib/models/Field/Field.ts | 266 + .../shared/lib/models/Field/FieldCode.ts | 99 + .../shared/lib/models/Field/FieldForm.ts | 11 + .../lib/models/Field/projectFieldsCodes.ts | 9 + .../lib/models/FieldGroup/FieldGroup.ts | 112 + .../lib/models/FieldGroup/FieldGroupForm.ts | 9 + .../shared/lib/models/FieldGroupCode.ts | 5 + .../shared/lib/models/FieldGroupsErrorCode.ts | 4 + .../lib/models/FieldOption/FieldOption.ts | 79 + .../lib/models/FieldSettings/FieldSettings.ts | 95 + .../shared/lib/models/FieldSpecialTab.ts | 5 + .../models/FieldValue/ChecklistFieldValue.ts | 81 + .../ColoredMultiselectFieldValue.ts | 63 + .../FieldValue/ColoredSelectFieldValue.ts | 59 + .../lib/models/FieldValue/DateFieldValue.ts | 60 + .../lib/models/FieldValue/FieldValue.ts | 54 + .../models/FieldValue/FieldValueBaseProps.ts | 18 + .../lib/models/FieldValue/FileFieldValue.ts | 69 + .../models/FieldValue/FormulaFieldValue.ts | 51 + .../lib/models/FieldValue/LinkFieldValue.ts | 51 + .../FieldValue/MultiselectFieldValue.ts | 59 + .../models/FieldValue/MultitextFieldValue.ts | 132 + .../lib/models/FieldValue/NumberFieldValue.ts | 51 + .../FieldValue/ParticipantFieldValue.ts | 65 + .../FieldValue/ParticipantsFieldValue.ts | 63 + .../lib/models/FieldValue/SelectFieldValue.ts | 56 + .../lib/models/FieldValue/SwitchFieldValue.ts | 50 + .../lib/models/FieldValue/TextFieldValue.ts | 58 + .../shared/lib/models/FieldsStoreErrorCode.ts | 5 + .../FormulaField/FormulaFieldClasses.ts | 10 + .../FormulaField/FormulaFieldMathElement.ts | 8 + .../FormulaFieldMathElementType.ts | 8 + .../FormulaFieldMathElementValue.ts | 10 + .../lib/models/FormulaField/FormulaKey.ts | 10 + .../fields/shared/lib/models/PhoneUserInfo.ts | 25 + .../lib/models/ProjectFieldsSettings.ts | 21 + .../lib/models/RequisitesFieldSubgroup.ts | 63 + .../fields/shared/lib/models/index.tsx | 35 + .../fields/shared/lib/types/ArrayValue.ts | 1 + .../ChecklistFieldValuePrimitive.ts | 8 + .../ColoredMultiselectFieldValuePrimitive.ts | 3 + .../ColoredSelectFieldValuePrimitive.ts | 3 + .../FieldValue/DateFieldValuePrimitive.ts | 5 + .../FieldValue/FileFieldValuePrimitive.ts | 1 + .../FieldValue/FormulaFieldValuePrimitive.ts | 4 + .../FieldValue/LinkFieldValuePrimitive.ts | 3 + .../MultiselectFieldValuePrimitive.ts | 3 + .../MultitextFieldValuePrimitive.ts | 3 + .../FieldValue/NumberFieldValuePrimitive.ts | 4 + .../ParticipantFieldValuePrimitive.ts | 4 + .../ParticipantsFieldValuePrimitive.ts | 1 + .../FieldValue/PossiblePrimitiveFieldValue.ts | 26 + .../FieldValue/SelectFieldValuePrimitive.ts | 3 + .../FieldValue/SwitchFieldValuePrimitive.ts | 3 + .../FieldValue/TextFieldValuePrimitive.ts | 3 + .../FormulaField/AppendMathElementHandler.ts | 5 + .../shared/lib/types/MultiOptionsValue.ts | 1 + .../GenerateExternalChatLinkHandler.ts | 7 + .../shared/lib/types/PossibleFieldValue.ts | 34 + .../fields/shared/lib/types/PrimitiveValue.ts | 1 + .../shared/lib/types/SingleOptionValue.ts | 3 + .../modules/fields/shared/lib/types/index.tsx | 25 + .../fields/shared/lib/utils/FieldUtil.ts | 137 + .../modules/fields/shared/lib/utils/index.tsx | 1 + .../modules/fields/store/FieldFormulaStore.ts | 515 + .../modules/fields/store/FieldGroupsStore.ts | 202 + .../modules/fields/store/FieldOptionStore.ts | 65 + .../store/FieldSettings/FieldSettingsForm.ts | 57 + .../store/FieldSettings/FieldSettingsStore.ts | 98 + .../modules/fields/store/FieldValuesStore.ts | 107 + .../src/modules/fields/store/FieldsStore.ts | 297 + frontend/src/modules/fields/store/index.tsx | 7 + .../context/GanttContext/GanttContext.ts | 37 + .../context/GanttContext/useGanttContext.ts | 11 + frontend/src/modules/gantt/context/index.tsx | 2 + frontend/src/modules/gantt/index.tsx | 2 + frontend/src/modules/gantt/shared/index.tsx | 1 + .../DragAreaIndicator/DragAreaIndicator.tsx | 48 + .../DragResizeManager/DragResizeManager.tsx | 294 + .../GanttBarLinkMenu/GanttBarLinkMenu.tsx | 108 + .../lib/components/GanttBody/GanttBody.tsx | 40 + .../lib/components/GanttChart/GanttChart.tsx | 144 + .../GanttTable/TableBody/TableBody.tsx | 113 + .../EmptyTableBody/EmptyTableBody.tsx | 46 + .../components/TableRows/TableRows.tsx | 93 + .../components/TableCell/TableCell.tsx | 421 + .../components/TableRows/components/index.tsx | 1 + .../GanttTable/TableBody/components/index.tsx | 2 + .../GanttTable/TableHeader/TableHeader.tsx | 101 + .../GanttViewComponent/GanttViewComponent.tsx | 194 + .../GanttViewSelect/GanttViewSelect.tsx | 195 + .../RecordsBarList/RecordsBarList.tsx | 19 + .../MinimizedRecordBar/MinimizedRecordBar.tsx | 98 + .../components/RecordBar/RecordBar.tsx | 490 + .../RecordGroupBar/RecordGroupBar.tsx | 78 + .../RecordsBarList/components/index.tsx | 2 + .../RecordsBarsThumbsList.tsx | 20 + .../RecordBarThumb/RecordBarThumb.tsx | 119 + .../components/index.tsx | 1 + .../RecordsContentDivider.tsx | 86 + .../RowSelectionIndicator.tsx | 50 + .../TasksDependencies/TasksDependencies.tsx | 15 + .../TaskDependenceArrow.tsx | 119 + .../TasksDependencies/components/index.tsx | 1 + .../components/TimeAxis/TimeAxis/TimeAxis.tsx | 191 + .../TimeAxisTodayIndicator.tsx | 79 + .../TimeAxisTodayLine/TimeAxisTodayLine.tsx | 57 + .../gantt/shared/lib/components/index.tsx | 2 + .../lib/helpers/generateTimelineRoute.ts | 50 + .../shared/lib/helpers/getGanttViewConfigs.ts | 45 + .../gantt/shared/lib/helpers/index.tsx | 2 + .../src/modules/gantt/shared/lib/index.tsx | 6 + .../modules/gantt/shared/lib/models/Bar.ts | 78 + .../gantt/shared/lib/models/BarClickEvent.ts | 12 + .../gantt/shared/lib/models/Dependence.ts | 8 + .../gantt/shared/lib/models/GanttItem.ts | 50 + .../gantt/shared/lib/models/GanttRecord.ts | 202 + .../shared/lib/models/GanttViewConfig.ts | 7 + .../shared/lib/models/GanttViewValues.ts | 10 + .../modules/gantt/shared/lib/models/Major.ts | 6 + .../gantt/shared/lib/models/MajorAmp.ts | 7 + .../modules/gantt/shared/lib/models/Minor.ts | 7 + .../gantt/shared/lib/models/MinorAmp.ts | 7 + .../shared/lib/models/TableResizeEvent.ts | 7 + .../gantt/shared/lib/models/constants.ts | 15 + .../modules/gantt/shared/lib/models/index.tsx | 25 + .../lib/services/GanttStorageService.ts | 15 + .../gantt/shared/lib/services/index.tsx | 1 + .../gantt/shared/lib/types/DependenceType.ts | 1 + .../gantt/shared/lib/types/GanttView.ts | 8 + .../gantt/shared/lib/types/MoveType.ts | 1 + .../lib/types/TimelineRouteGenerator.ts | 3 + .../modules/gantt/shared/lib/types/index.tsx | 4 + .../gantt/shared/lib/utils/GanttAxisUtil.ts | 301 + .../gantt/shared/lib/utils/GanttUtil.ts | 101 + .../modules/gantt/shared/lib/utils/index.tsx | 2 + .../src/modules/gantt/store/AutoScroller.ts | 81 + .../src/modules/gantt/store/GanttStore.ts | 1012 + frontend/src/modules/gantt/store/index.tsx | 2 + .../api/MailMessageApi/MailMessageApi.ts | 84 + .../mailing/api/MailboxApi/MailboxApi.ts | 173 + .../MailboxSettingsApi/MailboxSettingsApi.ts | 89 + .../MailboxSignatureSettingsApi.ts | 42 + .../invalidateMailboxesSignaturesInCache.ts | 7 + .../queries/useGetMailboxesSignatures.ts | 9 + .../modules/mailing/api/MailingApiRoutes.ts | 31 + .../modules/mailing/api/MailingQueryKeys.ts | 8 + .../api/dtos/MailMessage/MailMessageDto.ts | 20 + .../dtos/MailMessage/MailMessagePayloadDto.ts | 10 + .../dtos/MailMessage/SendMailMessageDto.ts | 38 + .../api/dtos/MailThread/MailThreadInfoDto.ts | 6 + .../api/dtos/Mailbox/CreateMailboxDto.ts | 11 + .../mailing/api/dtos/Mailbox/MailboxDto.ts | 15 + .../dtos/Mailbox/MailboxSettingsManualDto.ts | 8 + .../api/dtos/Mailbox/UpdateMailboxDto.ts | 26 + .../Mailbox/UpdateMailboxSettingsManualDto.ts | 30 + .../dtos/MailboxInfo/MailboxFolderInfoDto.ts | 9 + .../dtos/MailboxInfo/MailboxFullInfoDto.ts | 12 + .../dtos/MailboxInfo/MailboxMessageInfoDto.ts | 15 + .../dtos/MailboxInfo/MailboxSectionInfoDto.ts | 9 + .../dtos/MailboxInfo/MailboxShortInfoDto.ts | 6 + .../CreateMailboxSignatureDto.ts | 13 + .../MailboxSignature/MailboxSignatureDto.ts | 17 + .../UpdateMailboxSignatureDto.ts | 13 + .../src/modules/mailing/api/dtos/index.tsx | 17 + frontend/src/modules/mailing/api/index.tsx | 10 + frontend/src/modules/mailing/index.tsx | 4 + .../mailing/pages/MailingPage/MailingPage.tsx | 429 + .../AttachmentItem/AttachmentItem.tsx | 165 + .../AttachmentList/AttachmentList.tsx | 64 + .../AttachmentsBlock/AttachmentsBlock.tsx | 118 + .../components/ClipIcon/ClipIcon.tsx | 21 + .../CollapsibleList/CollapsibleList.tsx | 223 + .../CollapsibleListItem.tsx | 91 + .../CreateMessageButton.tsx | 33 + .../EmptyMessagePlug/EmptyMessagePlug.tsx | 68 + .../components/Mailbox/Mailbox.tsx | 30 + .../MessageControlButton.tsx | 133 + .../components/MessageItem/MessageItem.tsx | 238 + .../components/MessagePanel/MessagePanel.tsx | 186 + .../NoMailboxesPanelBlock.tsx | 77 + .../ReplyControls/ReplyControls.tsx | 148 + .../components/SearchBlock/SearchBlock.tsx | 125 + .../components/Section/Section.tsx | 75 + .../components/Sidebar/Sidebar.tsx | 240 + .../SidebarDelimiter/SidebarDelimiter.tsx | 11 + .../components/Sidebar/components/index.tsx | 1 + .../Skeletons/MessagePanelSkeleton.tsx | 19 + .../Skeletons/SidebarSectionSkeleton.tsx | 68 + .../components/Skeletons/SidebarSkeleton.tsx | 26 + .../MailingPage/components/Thread/Thread.tsx | 346 + .../ThreadMessage/ThreadMessage.tsx | 294 + .../pages/MailingPage/components/index.tsx | 3 + .../MailingSettingsPage.tsx | 302 + .../Buttons/AddButton/AddButton.tsx | 80 + .../Buttons/ControlButton/ControlButton.tsx | 149 + .../components/Caption/Caption.tsx | 11 + .../components/MailboxItem/MailboxItem.tsx | 239 + .../components/MailboxList/MailboxList.tsx | 90 + .../MailboxListSkeleton.tsx | 16 + .../DeleteMailboxModal/DeleteMailboxModal.tsx | 72 + .../MailboxAddressModal.tsx | 148 + .../MailboxProviderModal.tsx | 95 + .../EditorApproveButtonSkeleton.tsx | 11 + .../MailboxSignatureEditor.tsx | 243 + .../MailboxSignatureEditorSkeleton.tsx | 29 + .../MailboxSignatureModal.tsx | 153 + .../MailboxSignatureSidebar.tsx | 114 + .../MailboxSignaturesSidebarSkeleton.tsx | 16 + .../Modals/ModalTitle/ModalTitle.tsx | 8 + .../UpdateMailboxModal/UpdateMailboxModal.tsx | 526 + .../MailingSettingsPage/components/index.tsx | 8 + frontend/src/modules/mailing/pages/index.tsx | 3 + .../modules/mailing/shared/assets/arrow.svg | 3 + .../mailing/shared/assets/calendar_add.svg | 24 + .../mailing/shared/assets/clip_medium.svg | 5 + .../mailing/shared/assets/clip_small.svg | 5 + .../modules/mailing/shared/assets/close.svg | 5 + .../mailing/shared/assets/contact_add.svg | 14 + .../modules/mailing/shared/assets/cross.svg | 5 + .../mailing/shared/assets/double_arrow.svg | 5 + .../modules/mailing/shared/assets/draft.svg | 5 + .../modules/mailing/shared/assets/error.svg | 5 + .../modules/mailing/shared/assets/forward.svg | 8 + .../modules/mailing/shared/assets/gmail.svg | 7 + .../modules/mailing/shared/assets/google.svg | 20 + .../modules/mailing/shared/assets/inbox.svg | 5 + .../modules/mailing/shared/assets/inboxes.svg | 5 + .../modules/mailing/shared/assets/index.tsx | 31 + .../modules/mailing/shared/assets/mail.svg | 9 + .../modules/mailing/shared/assets/message.svg | 8 + .../mailing/shared/assets/no_email_yet.svg | 207 + .../mailing/shared/assets/opened_message.svg | 6 + .../mailing/shared/assets/reconnect.svg | 5 + .../modules/mailing/shared/assets/reply.svg | 8 + .../modules/mailing/shared/assets/sent.svg | 5 + .../modules/mailing/shared/assets/spam.svg | 5 + .../mailing/shared/assets/spam_header.svg | 5 + .../mailing/shared/assets/text_format.svg | 5 + .../modules/mailing/shared/assets/trash.svg | 5 + .../mailing/shared/assets/trash_header.svg | 5 + .../modules/mailing/shared/assets/unfold.svg | 5 + .../modules/mailing/shared/assets/unread.svg | 6 + .../modules/mailing/shared/assets/unspam.svg | 5 + .../modules/mailing/shared/assets/untrash.svg | 5 + frontend/src/modules/mailing/shared/index.tsx | 2 + .../SendEmailModal/SendEmailModal.tsx | 838 + .../AddressMultiInput/AddressMultiInput.tsx | 345 + .../ChangesNotSavedModal.tsx | 37 + .../components/Editor/EditorContent.tsx | 25 + .../Editor/EmailSignatureEditor.tsx | 207 + .../components/Editor/EmailTextEditor.tsx | 153 + .../components/Input/InputWrapper.tsx | 14 + .../components/Input/PseudoInputLabel.tsx | 6 + .../Input/PseudoInputLabelWrapper.tsx | 8 + .../components/Input/PseudoInputWrapper.tsx | 16 + .../InvalidEmailAddressModal.tsx | 51 + .../MultiAddressMailField.tsx | 47 + .../SendEmailSettingsDropdown.tsx | 138 + .../TextFormatButton/TextFormatButton.tsx | 35 + .../SendEmailModal/components/index.tsx | 11 + .../mailing/shared/lib/components/index.tsx | 2 + .../lib/helpers/downloadFileFromMessage.ts | 22 + .../shared/lib/helpers/getDemoMailboxes.ts | 23 + .../shared/lib/helpers/getDemoMessages.ts | 66 + .../shared/lib/helpers/getDemoSections.ts | 40 + .../lib/helpers/getIconByFolderType.tsx | 32 + .../lib/helpers/getMessageContentToDisplay.ts | 33 + .../lib/helpers/getMockMailMessageInfos.ts | 19 + .../mailing/shared/lib/helpers/index.tsx | 7 + .../mailing/shared/lib/hooks/index.tsx | 1 + .../shared/lib/hooks/useMessageControls.ts | 94 + .../src/modules/mailing/shared/lib/index.tsx | 4 + .../lib/models/MailMessage/MailMessage.ts | 78 + .../lib/models/MailMessage/MailMessageInfo.ts | 65 + .../models/MailMessage/MailMessagePayload.ts | 35 + .../shared/lib/models/Mailbox/Mailbox.ts | 93 + .../lib/models/Mailbox/MailboxFolderInfo.ts | 32 + .../lib/models/Mailbox/MailboxFolderType.ts | 10 + .../lib/models/Mailbox/MailboxFullInfo.ts | 39 + .../lib/models/Mailbox/MailboxProvider.ts | 4 + .../lib/models/Mailbox/MailboxSectionInfo.ts | 30 + .../models/Mailbox/MailboxSettingsManual.ts | 37 + .../lib/models/Mailbox/MailboxShortInfo.ts | 28 + .../lib/models/Mailbox/MailboxSignature.ts | 34 + .../shared/lib/models/Mailbox/MailboxState.ts | 8 + .../lib/models/Mailbox/MailboxesInfo.ts | 19 + .../lib/models/Mailing/MailThreadInfo.ts | 48 + .../lib/models/Mailing/MailingPageSettings.ts | 3 + .../models/MessageHeader/MessageHeaderInfo.ts | 6 + .../MessageHeader/MessageHeaderObject.ts | 6 + .../SendEmail/SendEmailModalSettings.ts | 6 + .../mailing/shared/lib/models/index.tsx | 18 + .../mailing/store/MailMessageBlockStore.ts | 65 + .../modules/mailing/store/MailMessageStore.ts | 90 + .../mailing/store/MailboxSettingsStore.ts | 223 + .../store/MailboxSignatureSettingsStore.ts | 102 + .../src/modules/mailing/store/MailboxStore.ts | 343 + .../src/modules/mailing/store/SidebarStore.ts | 52 + frontend/src/modules/mailing/store/index.tsx | 6 + .../modules/multichat/api/ChatApi/ChatApi.ts | 175 + .../ChatApi/helpers/clearChatEntityInCache.ts | 54 + .../api/ChatApi/helpers/deleteChatInCache.ts | 33 + .../multichat/api/ChatApi/helpers/getChat.ts | 11 + .../api/ChatApi/helpers/refetchChats.ts | 5 + .../helpers/updateChatLastMessageInCache.ts | 99 + .../api/ChatApi/helpers/upsertChatInCache.ts | 41 + .../modules/multichat/api/ChatApi/index.tsx | 6 + .../queries/useCreateContactFromChat.ts | 42 + .../api/ChatApi/queries/useCreateGroupChat.ts | 27 + .../ChatApi/queries/useCreatePersonalChat.ts | 27 + .../api/ChatApi/queries/useDeleteChat.ts | 20 + .../api/ChatApi/queries/useFindFullChats.ts | 27 + .../useFindFullChatsByMessageContent.ts | 28 + .../queries/useFindFullPersonalChats.ts | 27 + .../api/ChatApi/queries/useGetChat.ts | 9 + .../api/ChatApi/queries/useGetChatExists.ts | 9 + .../api/ChatApi/queries/useGetChats.ts | 25 + .../queries/useMarkAllChatMessagesAsRead.ts | 16 + .../api/ChatApi/queries/useUpdateGroupChat.ts | 25 + .../api/ChatMessageApi/ChatMessageApi.ts | 165 + .../helpers/addChatMessageToCache.ts | 30 + .../helpers/deleteChatMessageInCache.ts | 30 + .../helpers/deleteChatMessagesCache.ts | 6 + .../ChatMessageApi/helpers/getChatMessage.ts | 17 + .../helpers/updateChatMessageStatus.ts | 20 + .../helpers/updateChatMessagesInCache.ts | 30 + .../queries/useDeleteChatMessage.ts | 28 + .../queries/useGetChatMessages.ts | 18 + .../queries/useReactToChatMessage.ts | 29 + .../queries/useSendChatMessage.ts | 38 + .../queries/useUnreactToChatMessage.ts | 29 + .../queries/useUpdateChatMessage.ts | 39 + .../queries/useUpdateChatMessagesStatus.ts | 75 + .../api/ChatProviderApi/ChatProviderApi.ts | 13 + .../helpers/addToProviderUnseenCount.ts | 19 + .../helpers/invalidateChatProvidersInCache.ts | 5 + .../queries/useGetChatProviders.ts | 11 + .../queries/useGetExternalChatProviders.ts | 23 + .../FbMessengerProviderSettingsApi.ts | 56 + .../api/MultichatApi/MultichatApi.ts | 12 + .../helpers/addToTotalUnseenCount.ts | 8 + .../queries/useGetMultichatUnseenCount.ts | 10 + .../multichat/api/MultichatApiRoutes.ts | 51 + .../multichat/api/MultichatQueryKeys.ts | 31 + .../TwilioProviderSettingsApi.ts | 56 + .../WazzupProviderSettingsApi.ts | 73 + .../multichat/api/dtos/Chat/ChatDto.ts | 21 + .../Chat/ChatFindByMessageContentFilterDto.ts | 11 + .../api/dtos/Chat/ChatFindFilterDto.ts | 17 + .../dtos/Chat/ChatFindPersonalFilterDto.ts | 11 + .../api/dtos/Chat/CreateExternalChatDto.ts | 23 + .../api/dtos/Chat/CreateGroupChatDto.ts | 13 + .../api/dtos/Chat/CreatePersonalChatDto.ts | 9 + .../api/dtos/Chat/FindChatsFullResultDto.ts | 7 + .../api/dtos/Chat/UpdateGroupChatDto.ts | 13 + .../api/dtos/ChatMessage/ChatMessageDto.ts | 16 + .../dtos/ChatMessage/ChatMessageFileDto.ts | 11 + .../ChatMessage/ChatMessageReactionDto.ts | 5 + .../ChatMessage/ChatMessageUserStatusDto.ts | 7 + .../dtos/ChatMessage/ChatMessagesResultDto.ts | 7 + .../dtos/ChatMessage/SendChatMessageDto.ts | 13 + .../dtos/ChatMessage/UpdateChatMessageDto.ts | 13 + .../api/dtos/ChatProvider/ChatProviderDto.ts | 9 + .../api/dtos/ChatUser/ChatUserDto.ts | 17 + .../api/dtos/ChatUser/ChatUserExternalDto.ts | 29 + .../MessengerProviderSettingsDto.ts | 45 + .../FbMessenger/UpdateMessengerProviderDto.ts | 30 + .../dtos/Twilio/CreateTwilioProviderDto.ts | 42 + .../dtos/Twilio/TwilioProviderSettingsDto.ts | 45 + .../Twilio/UpdateTwilioProviderSettings.ts | 38 + .../dtos/Wazzup/CreateWazzupProviderDto.ts | 53 + .../dtos/Wazzup/UpdateWazzupProviderDto.ts | 30 + .../api/dtos/Wazzup/WazzupChanelDto.ts | 9 + .../api/dtos/Wazzup/WazzupProviderDto.ts | 19 + .../src/modules/multichat/api/dtos/index.tsx | 31 + frontend/src/modules/multichat/api/index.tsx | 35 + .../multichat/context/MultichatContext.ts | 19 + .../multichat/context/MultichatProvider.tsx | 58 + .../src/modules/multichat/context/index.tsx | 3 + .../multichat/context/useMultichatContext.ts | 117 + .../multichat/events/ChatEventHandler.ts | 172 + .../src/modules/multichat/events/index.tsx | 1 + frontend/src/modules/multichat/index.tsx | 26 + .../pages/MultichatPage/MultichatPage.tsx | 86 + .../src/modules/multichat/pages/index.tsx | 1 + .../multichat/shared/assets/document.svg | 5 + .../multichat/shared/assets/download.svg | 5 + .../facebook_messenger_provider_indicator.svg | 7 + .../multichat/shared/assets/fullscreen.svg | 5 + .../modules/multichat/shared/assets/image.svg | 5 + .../modules/multichat/shared/assets/index.tsx | 13 + .../modules/multichat/shared/assets/more.svg | 5 + .../shared/assets/multichat_button.svg | 8 + .../modules/multichat/shared/assets/read.svg | 5 + .../shared/assets/read_chat_message.svg | 5 + .../modules/multichat/shared/assets/reply.svg | 5 + .../modules/multichat/shared/assets/send.svg | 5 + .../multichat/shared/assets/unread.svg | 5 + .../shared/assets/unread_chat_message.svg | 5 + .../assets/whatsapp_provider_indicator.svg | 8 + .../src/modules/multichat/shared/index.tsx | 2 + .../MultichatButton/MultichatButton.tsx | 92 + .../MultichatControl/MultichatControl.tsx | 82 + .../ChatControlsMenu/ChatControlsMenu.tsx | 179 + .../ChatMessageContextMenu.tsx | 105 + .../ChatMessageFileBlock.tsx | 397 + .../ChatMessageFileList.tsx | 47 + .../ChatMessageItem/ChatMessageItem.tsx | 201 + .../ChatMessageReaction.tsx | 118 + .../ChatMessageReactionsList.tsx | 50 + .../ChatMessageReactionsPanel.tsx | 86 + .../ChatMessageReplyBlock.tsx | 141 + .../ChatMessagesList/ChatMessagesList.tsx | 155 + .../ChatMessagesPanel/ChatMessagesPanel.tsx | 114 + .../ChatMessagesPanelHeader.tsx | 338 + .../CreateChatContactButton.tsx | 137 + .../SendChatMessageBlock.tsx | 155 + .../SendChatMessageTextarea.tsx | 279 + .../SendChatMessageWithFilesModal.tsx | 73 + .../Skeletons/ChatMessageItemSkeleton.tsx | 84 + .../Skeletons/ChatMessagesListSkeleton.tsx | 20 + .../Skeletons/MessagesPanelHeaderSkeleton.tsx | 8 + .../ChatHeaderProviderTab.tsx | 118 + .../ChatsHeaderProvidersTabs.tsx | 163 + .../ChatsHeaderSearchBlock.tsx | 58 + .../ChatsSidebar/ChatsPanel/ChatsPanel.tsx | 162 + .../ChatsPanelBlock/ChatsPanelBlock.tsx | 270 + .../ChatsSidebar/ChatsSidebar.tsx | 192 + .../ChatsSidebarHeader/ChatsSidebarHeader.tsx | 43 + .../ChatsSmallPanelBlock.tsx | 102 + .../ChatsSmallProvidersTab.tsx | 97 + .../CreateAmworkChatButton.tsx | 130 + .../AddAvatarAndNameModal.tsx | 60 + .../SelectAmworkContactsModal.tsx | 69 + .../components/index.tsx | 2 + .../FoundChatsPanel/FoundChatsPanel.tsx | 211 + .../FoundChatsGroup/FoundChatsGroup.tsx | 122 + .../FoundChatsPanel/components/index.tsx | 2 + .../PanelBlockHeaderLastMessageInfo.tsx | 54 + .../ProviderIcon/ProviderIcon.tsx | 109 + .../ChatsHeaderProvidersTabSkeleton.tsx | 20 + .../Skeletons/ChatsPanelSkeleton.tsx | 21 + .../UnseenCountTag/UnseenCountTag.tsx | 22 + .../UnseenCountTagSmallView.tsx | 25 + .../MultichatControlHeader.tsx | 130 + .../NoSelectedChatPlug/NoSelectedChatPlug.tsx | 36 + .../StyledResizeHandler.tsx | 37 + .../MultichatControl/components/index.tsx | 6 + .../MultichatMobileControl.tsx | 75 + .../MultichatModal/MultichatModal.tsx | 59 + .../multichat/shared/lib/components/index.tsx | 5 + .../helpers/findChatWithProviderTransport.ts | 13 + .../lib/helpers/generateRandomWidthInRange.ts | 12 + .../lib/helpers/getChatLastMessageSnippet.tsx | 38 + .../lib/helpers/getDefaultModalBounds.ts | 23 + .../helpers/getLastMessageTimeFormatted.ts | 11 + .../shared/lib/helpers/getMessagesGroups.ts | 34 + .../shared/lib/helpers/getNumberFromPx.ts | 1 + .../helpers/getReplyBlockMessageSnippet.ts | 26 + .../multichat/shared/lib/helpers/index.tsx | 11 + .../lib/helpers/parseChatMessageText.tsx | 24 + .../shared/lib/helpers/renderChatButton.tsx | 16 + .../helpers/renderProviderIndicatorIcon.tsx | 83 + .../multichat/shared/lib/hooks/index.tsx | 1 + .../shared/lib/hooks/useGetChatsViewInfo.ts | 34 + .../modules/multichat/shared/lib/index.tsx | 4 + .../multichat/shared/lib/models/Chat/Chat.ts | 143 + .../shared/lib/models/Chat/ChatType.ts | 4 + .../lib/models/Chat/FIndChatsFullResult.ts | 20 + .../lib/models/ChatMessage/ChatMessage.ts | 80 + .../lib/models/ChatMessage/ChatMessageFile.ts | 46 + .../ChatMessage/ChatMessageGroupedReaction.ts | 4 + .../models/ChatMessage/ChatMessageReaction.ts | 25 + .../models/ChatMessage/ChatMessageStatus.ts | 4 + .../ChatMessage/ChatMessageUserStatus.ts | 27 + .../models/ChatMessage/ChatMessagesResult.ts | 20 + .../lib/models/ChatMessage/MessagesGroup.ts | 7 + .../lib/models/ChatProvider/ChatProvider.ts | 33 + .../models/ChatProvider/ChatProviderStatus.ts | 6 + .../ChatProvider/ChatProviderTransport.ts | 9 + .../models/ChatProvider/ChatProviderType.ts | 6 + .../shared/lib/models/ChatUser/ChatUser.ts | 66 + .../lib/models/ChatUser/ChatUserExternal.ts | 54 + .../lib/models/ChatUser/ChatUserRole.ts | 7 + .../shared/lib/models/Events/ChatEvent.ts | 13 + .../models/Events/ChatMessageCreatedEvent.ts | 28 + .../lib/models/Events/ChatMessageEvent.ts | 17 + .../models/Events/ChatMessageUpdatedEvent.ts | 18 + .../lib/models/Events/MultichatEvents.ts | 8 + .../FbMessenger/MessengerProviderSettings.ts | 69 + .../models/Twilio/TwilioProviderSettings.ts | 86 + .../shared/lib/models/Wazzup/WazzupChanel.ts | 33 + .../lib/models/Wazzup/WazzupChanelState.ts | 15 + .../lib/models/Wazzup/WazzupProvider.ts | 96 + .../lib/models/Wazzup/WazzupTransport.ts | 9 + .../multichat/shared/lib/models/index.tsx | 25 + .../src/modules/notes/api/NoteApi/NoteApi.ts | 21 + frontend/src/modules/notes/api/index.tsx | 1 + frontend/src/modules/notes/index.tsx | 2 + .../notes/pages/NotesPage/NotesPage.tsx | 45 + frontend/src/modules/notes/pages/index.tsx | 1 + .../src/modules/notes/shared/assets/bold.svg | 5 + .../notes/shared/assets/bullet_list.svg | 20 + .../src/modules/notes/shared/assets/close.svg | 5 + .../modules/notes/shared/assets/delete.svg | 20 + .../modules/notes/shared/assets/duplicate.svg | 8 + .../modules/notes/shared/assets/expand.svg | 14 + .../modules/notes/shared/assets/folder.svg | 5 + .../src/modules/notes/shared/assets/h1.svg | 5 + .../src/modules/notes/shared/assets/h2.svg | 5 + .../src/modules/notes/shared/assets/h3.svg | 5 + .../src/modules/notes/shared/assets/index.tsx | 24 + .../modules/notes/shared/assets/insert.svg | 6 + .../modules/notes/shared/assets/italic.svg | 5 + .../modules/notes/shared/assets/minimize.svg | 5 + .../notes/shared/assets/module_icon.svg | 22 + .../src/modules/notes/shared/assets/more.svg | 11 + .../notes/shared/assets/ordered_list.svg | 17 + .../src/modules/notes/shared/assets/pin.svg | 8 + .../notes/shared/assets/pin_filled.svg | 5 + .../src/modules/notes/shared/assets/redo.svg | 5 + .../shared/assets/remove_quick_property.svg | 7 + .../notes/shared/assets/strikethrough.svg | 11 + .../src/modules/notes/shared/assets/text.svg | 8 + .../notes/shared/assets/underlined.svg | 8 + .../src/modules/notes/shared/assets/undo.svg | 5 + frontend/src/modules/notes/shared/index.tsx | 2 + .../lib/components/NoteEditor/NoteEditor.tsx | 201 + .../EditorToolbar/EditorToolbar.tsx | 348 + .../components/TopNoteInfo/TopNoteInfo.tsx | 185 + .../NoteEditor/components/index.tsx | 2 + .../NoteFolderSelector/NoteFolderSelector.tsx | 76 + .../components/FolderBlock/FolderBlock.tsx | 79 + .../NoteFolderSelector/components/index.tsx | 1 + .../components/NoteSelector/NoteSelector.tsx | 96 + .../components/NoteBlock/NoteBlock.tsx | 193 + .../components/NoteGroup/NoteGroup.tsx | 71 + .../NoteSelector/components/index.tsx | 1 + .../NotesActionDropdown.tsx | 178 + .../lib/components/NotesModal/NotesModal.tsx | 74 + .../NotesPageHeader/NotesPageHeader.tsx | 81 + .../notes/shared/lib/components/index.tsx | 5 + .../shared/lib/helpers/formatNoteDate.ts | 24 + .../shared/lib/helpers/getNoteHeading.ts | 40 + .../notes/shared/lib/helpers/index.tsx | 3 + .../lib/helpers/shortenQuickNoteTitle.ts | 5 + .../src/modules/notes/shared/lib/index.tsx | 4 + .../notes/shared/lib/models/Note/Note.ts | 44 + .../notes/shared/lib/models/Note/StartNote.ts | 8 + .../modules/notes/shared/lib/models/index.tsx | 2 + .../notes/shared/lib/types/FormattingType.ts | 3 + .../notes/shared/lib/types/GroupedNotes.ts | 8 + .../notes/shared/lib/types/NoteFolders.ts | 1 + .../modules/notes/shared/lib/types/index.tsx | 3 + frontend/src/modules/notes/store/NoteStore.ts | 253 + frontend/src/modules/notes/store/index.tsx | 1 + .../api/NotificationsApi/NotificationsApi.ts | 45 + .../api/NotificationsApi/index.tsx | 1 + .../api/NotificationsApiRoutes.ts | 9 + .../NotificationsSettingsApi.ts | 22 + .../api/NotificationsSettingsApi/index.tsx | 1 + .../notifications/api/dtos/NotificationDto.ts | 45 + .../api/dtos/NotificationSettingsDto.ts | 11 + .../api/dtos/NotificationTypeSettingsDto.ts | 24 + .../api/dtos/NotificationsMeta.ts | 3 + .../api/dtos/NotificationsResult.ts | 7 + .../modules/notifications/api/dtos/index.tsx | 5 + .../src/modules/notifications/api/index.tsx | 3 + frontend/src/modules/notifications/index.tsx | 3 + .../notifications/shared/assets/after.svg | 5 + .../notifications/shared/assets/bell.svg | 5 + .../notifications/shared/assets/close.svg | 5 + .../notifications/shared/assets/cog.svg | 5 + .../notifications/shared/assets/index.tsx | 7 + .../notifications/shared/assets/overdue.svg | 5 + .../notifications/shared/assets/read_all.svg | 5 + .../notifications/shared/assets/sound.svg | 5 + .../modules/notifications/shared/index.tsx | 1 + .../NotificationsButton.tsx | 62 + .../NotificationsPanelDrawer.tsx | 116 + .../BlockHeaderAnnotation.tsx | 113 + .../ChatNotificationBlock.tsx | 204 + .../CloseToastButton/CloseToastButton.tsx | 38 + .../NotificationBlock/NotificationBlock.tsx | 356 + .../NotificationBlockSkeleton.tsx | 15 + .../NotificationSettings.tsx | 73 + .../NotificationSettingsModal.tsx | 352 + .../NotificationModalDelimiter.tsx | 7 + .../NotificationTypeBlock.tsx | 13 + .../components/index.tsx | 2 + .../NotificationsSettingsModalSkeleton.tsx | 84 + .../components/PanelHeader/PanelHeader.tsx | 36 + .../ReadAllButton/ReadAllButton.tsx | 42 + .../components/SoundButton/SoundButton.tsx | 45 + .../components/index.tsx | 4 + .../shared/lib/components/index.tsx | 2 + .../lib/helpers/getNotificationDateFormat.ts | 10 + .../lib/helpers/getSettingTitleByType.ts | 39 + .../shared/lib/helpers/index.tsx | 2 + .../notifications/shared/lib/hooks/index.tsx | 1 + .../hooks/useGetNotificationsDelayOptions.ts | 40 + .../notifications/shared/lib/index.tsx | 3 + .../lib/models/Notification/Notification.ts | 70 + .../Notification/NotificationLocalSettings.ts | 3 + .../Notification/NotificationSettings.ts | 16 + .../NotificationType/NotificationType.ts | 17 + .../NotificationType/NotificationTypeColor.ts | 72 + .../NotificationTypeSettings.ts | 42 + .../lib/models/NotificationsSettingsLsKey.ts | 1 + .../shared/lib/models/ToastClassName.ts | 1 + .../notifications/shared/lib/models/index.tsx | 7 + .../store/NotificationsSettingsStore.ts | 53 + .../notifications/store/NotificationsStore.ts | 131 + .../store/ToastNotificationsStore.tsx | 131 + .../src/modules/notifications/store/index.tsx | 3 + .../partner/api/PartnerApi/PartnerApi.tsx | 44 + .../modules/partner/api/PartnerApiRoutes.ts | 6 + .../partner/api/dtos/PartnerLeadDto.ts | 29 + .../partner/api/dtos/PartnerSummaryDto.ts | 24 + .../src/modules/partner/api/dtos/index.tsx | 2 + frontend/src/modules/partner/api/index.tsx | 2 + frontend/src/modules/partner/index.tsx | 2 + .../pages/PartnerInfoPage/PartnerInfoPage.tsx | 50 + .../components/Block/Block.tsx | 16 + .../BlockSkeleton/BlockSkeleton.tsx | 15 + .../PartnerLeadsBlock/PartnerLeadsBlock.tsx | 112 + .../PartnerSummaryBlock.tsx | 97 + .../PartnerInfoPage/components/index.tsx | 3 + frontend/src/modules/partner/pages/index.tsx | 1 + .../modules/partner/shared/assets/index.tsx | 1 + .../modules/partner/shared/assets/partner.svg | 8 + frontend/src/modules/partner/shared/index.tsx | 2 + .../src/modules/partner/shared/lib/index.tsx | 1 + .../partner/shared/lib/models/PartnerLead.ts | 46 + .../shared/lib/models/PartnerSummary.ts | 37 + .../partner/shared/lib/models/index.tsx | 2 + .../partner/store/PartnerInfoStore.tsx | 43 + frontend/src/modules/partner/store/index.tsx | 1 + .../PartnerPageTemplate.tsx | 53 + .../components/Container/Container.tsx | 26 + .../components/Header/Header.tsx | 14 + .../components/LogoutButton/LogoutButton.tsx | 19 + .../PartnerPageTemplate/components/index.tsx | 3 + .../src/modules/partner/templates/index.tsx | 1 + .../products/api/ProductApi/ProductApi.ts | 165 + .../helpers/invalidateGetProductsQuery.ts | 5 + .../helpers/removeImagesFromProductCache.ts | 21 + .../api/ProductApi/queries/useAddProduct.ts | 17 + .../ProductApi/queries/useDeleteProduct.ts | 22 + .../api/ProductApi/queries/useGetProduct.ts | 19 + .../api/ProductApi/queries/useGetProducts.ts | 32 + .../ProductApi/queries/useUpdateProduct.ts | 28 + .../queries/useUpdateProductStocks.ts | 66 + .../queries/useUploadProductImages.ts | 29 + .../ProductCategoryApi.ts | 72 + .../api/ProductOrderApi/ProductOrderApi.ts | 105 + .../invalidateEntityProductOrdersInCache.ts | 7 + .../queries/useDeleteEntityProductOrder.ts | 32 + .../queries/useGetEntityProductOrders.ts | 10 + .../ProductOrderStatusApi.ts | 13 + .../api/ProductPriceApi/ProductPriceApi.ts | 67 + .../ProductRentalOrderApi.ts | 150 + ...alidateEntityRentalProductOrdersInCache.ts | 13 + .../queries/useChangeRentalOrderStatus.ts | 36 + .../useDeleteEntityRentalProductOrder.ts | 32 + .../useGetEntityRentalProductOrders.ts | 17 + .../queries/useSearchRentalOrders.ts | 11 + .../ProductSectionRentalIntervalApi.ts | 40 + .../modules/products/api/ProductsApiRoutes.ts | 59 + .../modules/products/api/ProductsQueryKeys.ts | 62 + .../ProductsSectionApi/ProductsSectionApi.ts | 67 + .../helpers/invalidateProductsSections.ts | 5 + .../queries/useDeleteProductsSection.ts | 16 + .../queries/useGetProductsSection.ts | 9 + .../queries/useGetProductsSections.ts | 12 + .../api/RentalScheduleApi/RentalApi.ts | 60 + .../queries/useGetProductRentals.ts | 34 + .../queries/useGetRentals.ts | 41 + .../products/api/ShipmentApi/ShipmentApi.ts | 64 + .../queries/useChangeShipmentStatus.ts | 54 + .../ShipmentApi/queries/useGetShipments.ts | 13 + .../products/api/WarehouseApi/WarehouseApi.ts | 68 + .../api/dtos/Product/CreateProductDto.ts | 41 + .../api/dtos/Product/GetProductsResultDto.ts | 12 + .../products/api/dtos/Product/ProductDto.ts | 23 + .../api/dtos/Product/ProductInfoDto.ts | 9 + .../api/dtos/Product/RentalScheduleDto.ts | 8 + .../api/dtos/Product/UpdateProductDto.ts | 19 + .../CreateProductCategoryDto.ts | 11 + .../ProductCategory/ProductCategoryDto.ts | 8 + .../UpdateProductCategoryDto.ts | 7 + .../api/dtos/ProductOrder/CreateOrderDto.ts | 32 + .../api/dtos/ProductOrder/OrderDto.ts | 19 + .../api/dtos/ProductOrder/OrderItemDto.ts | 76 + .../api/dtos/ProductOrder/ReservationDto.ts | 9 + .../api/dtos/ProductOrder/UpdateOrderDto.ts | 29 + .../ProductPrice/CreateProductPriceDto.ts | 15 + .../api/dtos/ProductPrice/ProductPriceDto.ts | 9 + .../ProductPrice/UpdateProductPriceDto.ts | 15 + .../CheckRentalStatusDto.ts | 11 + .../CreateRentalOrderDto.ts | 31 + .../CreateRentalOrderItemDto.ts | 9 + .../dtos/ProductRentalOrder/DatePeriodDto.ts | 45 + .../ProductRentalStatusDto.ts | 8 + .../dtos/ProductRentalOrder/RentalEventDto.ts | 12 + .../dtos/ProductRentalOrder/RentalOrderDto.ts | 19 + .../ProductRentalOrder/RentalOrderItemDto.ts | 34 + .../UpdateRentalOrderDto.ts | 28 + .../UpdateRentalOrderItemDto.ts | 34 + .../RentalIntervalDto.ts | 12 + .../CreateProductsSectionDto.ts | 29 + .../ProductsSection/ProductsSectionDto.ts | 14 + .../UpdateProductsSectionDto.ts | 25 + .../api/dtos/Rental/GetRentalsResultDto.ts | 12 + .../products/api/dtos/Rental/RentalDto.ts | 12 + .../api/dtos/Rental/RentalOrderFilter.ts | 12 + .../dtos/Shipment/GetShipmentsResultDto.ts | 12 + .../products/api/dtos/Shipment/ShipmentDto.ts | 15 + .../api/dtos/Shipment/ShipmentItemDto.ts | 5 + .../products/api/dtos/Stock/StockDto.ts | 6 + .../products/api/dtos/Stock/UpdateStockDto.ts | 12 + .../api/dtos/Stock/UpdateStocksDto.ts | 9 + .../api/dtos/Warehouse/CreateWarehouseDto.ts | 7 + .../api/dtos/Warehouse/UpdateWarehouseDto.ts | 7 + .../api/dtos/Warehouse/WarehouseDto.ts | 8 + .../src/modules/products/api/dtos/index.tsx | 42 + frontend/src/modules/products/api/index.tsx | 37 + frontend/src/modules/products/index.tsx | 18 + .../CardProductsOrder.tsx | 260 + .../CardRentalProductsOrder.tsx | 277 + .../CardProductsOrderComponent.tsx | 322 + .../CardProductsOrderBlock.tsx | 319 + .../CardProductsOrderBlockHeader.tsx | 110 + .../CardProductsOrderTable.tsx | 36 + .../AvailableCellTable/AvailableCellTable.tsx | 30 + .../AvailableHeadCell/AvailableHeadCell.tsx | 26 + .../CardOrderAmountCell.tsx | 51 + .../CardOrderAvailableCell.tsx | 89 + .../cells/CurrencyCell/CurrencyCell.tsx | 5 + .../components/cells/index.tsx | 4 + .../components/index.tsx | 6 + .../DeleteOrderOrClearItemsWarningModal.tsx | 69 + .../DeleteOrderWarningModal.tsx | 64 + .../ReturnStocksWarningModal.tsx | 81 + .../CardProductsWarehouseBlock.tsx | 174 + .../WarehouseTable/WarehouseTable.tsx | 36 + .../components/index.tsx | 1 + .../components/index.tsx | 5 + .../CardRentalProductsOrderComponent.tsx | 261 + .../CardRentalProductsOrderBlock.tsx | 229 + .../CardRentalOrderAmountCell.tsx | 46 + .../CardRentalProductsOrderBlockHeader.tsx | 81 + .../RentalCardProductsOrderTable.tsx | 38 + .../RentalOrderPeriodsControl.tsx | 207 + .../RentalOrderPeriodsControlItem.tsx | 109 + .../components/index.tsx | 6 + ...eteRentalOrderOrClearItemsWarningModal.tsx | 61 + .../DeleteRentalOrderWarningModal.tsx | 56 + .../CardRentalProductsWarehouseBlock.tsx | 168 + .../RentalWarehouseTable.tsx | 36 + .../components/index.tsx | 1 + .../components/index.tsx | 4 + .../components/index.tsx | 8 + .../CardProductsOrders.tsx | 55 + .../ProductSectionOrdersItem.tsx | 68 + .../ProductSectionOrdersItemTable.tsx | 40 + .../RentalProductSectionOrdersItem.tsx | 56 + .../RentalProductSectionOrdersItemTable.tsx | 40 + .../ProductSectionOrdersDateCell.tsx | 29 + .../ProductSectionOrdersWarehouseCell.tsx | 29 + .../components/index.tsx | 6 + .../pages/ProductPage/ProductPage.tsx | 424 + .../ProductActionsDropdown.tsx | 74 + .../ProductDescription/ProductDescription.tsx | 110 + .../ProductCalendarBlock.styles.tsx | 175 + .../ProductCalendarBlock.tsx | 211 + .../components/ProductFeed/ProductFeed.tsx | 98 + .../ProductImagesBlock/ProductImagesBlock.tsx | 137 + .../AddImageBlock/AddImageBlock.tsx | 138 + .../ProductImagesBlock/components/index.tsx | 1 + .../ProductPricesBlock/AddPriceForm.tsx | 70 + .../ProductPricesBlock/ProductPriceBlock.tsx | 103 + .../ProductPricesBlock/ProductPricesBlock.tsx | 94 + .../ProductStocksBlock/ProductStocksBlock.tsx | 104 + .../ProductFormGroup/ProductFormGroup.tsx | 23 + .../ProductImageItem/ProductImageItem.tsx | 54 + .../ProductMainImage/ProductMainImage.tsx | 16 + .../ProductNameBlock/ProductNameBlock.tsx | 85 + .../pages/ProductPage/components/index.tsx | 6 + .../products/pages/ProductsPage/Products.tsx | 383 + .../pages/ProductsPage/ProductsPage.tsx | 352 + .../AddPhotoBlock/AddPhotoBlock.tsx | 104 + .../AddProductModal/AddProductModal.tsx | 275 + .../AddProductModalFormGroup.tsx | 28 + .../AddProductModalSkuField.tsx | 68 + .../AddProductModalWarning.tsx | 32 + .../CreateStocks/CreateStocksBlock.tsx | 64 + .../CreateStocks/CreateStocksModal.tsx | 43 + .../CreateStocks/CreateStocksTable.tsx | 34 + .../AddProductModal/components/index.tsx | 4 + .../AddWarehousePlaceholder.tsx | 26 + .../PhotoBlockItem/PhotoBlockItem.tsx | 94 + .../components/Prices/ProductPriceItem.tsx | 46 + .../components/Prices/ProductPriceList.tsx | 49 + .../ProductsSearchBlock.tsx | 120 + .../ProductStocksInput/ProductStocksInput.tsx | 63 + .../ProductStocksModal/ProductStocksModal.tsx | 118 + .../ProductStocksTable/ProductStocksTable.tsx | 39 + .../ProductsPageSecondaryHeader.tsx | 70 + .../ProductsTable/ProductsTable.tsx | 77 + .../ProductsTableBody/ProductsTableBody.tsx | 23 + .../ProductsTableHead/ProductsTableHead.tsx | 78 + .../ProductsTableSettingsDrawer.tsx | 48 + .../ProductStocksCell/ProductStocksCell.tsx | 55 + .../ProductsTableHeadCell.tsx | 120 + .../pages/ProductsPage/components/index.tsx | 9 + .../pages/ShipmentPage/ShipmentPage.tsx | 67 + .../ProductBarcodesControl.tsx | 298 + .../ProductDoesNotExistWarningModal.tsx | 44 + .../ProductIsNotInOrderWarningModal.tsx | 50 + .../RentalShipmentComponent.tsx | 188 + .../RentalShipmentPageSecondaryHeader.tsx | 80 + .../RentalShipmentTable.tsx | 52 + .../components/index.tsx | 2 + .../ShipmentComponent/ShipmentComponent.tsx | 177 + .../ShipmentPageSecondaryHeader.tsx | 91 + .../ShipmentTable/ShipmentTable.tsx | 66 + .../ShipmentAvailableCell.tsx | 18 + .../ShipmentComponent/components/index.tsx | 3 + .../components/ShipmentRoot/ShipmentRoot.tsx | 6 + .../SomeProductsNotCheckedWarningModal.tsx | 31 + .../cells/CheckHeaderCell/CheckHeaderCell.tsx | 25 + .../cells/CheckRowCell/CheckRowCell.tsx | 24 + .../pages/ShipmentPage/components/index.tsx | 7 + .../pages/ShipmentsPage/Shipments.tsx | 40 + .../RentalShipmentsComponent.tsx | 140 + .../ShipmentsComponent/ShipmentsComponent.tsx | 69 + .../ShipmentsTable/ShipmentsTable.tsx | 200 + .../ShipmentsTablePagination.tsx | 26 + .../ShipmentDateCell/ShipmentDateCell.tsx | 57 + .../ShipmentsComponent/components/index.tsx | 2 + .../pages/ShipmentsPage/components/index.tsx | 3 + .../pages/TimetablePage/Timetable.tsx | 102 + .../components/Calendar/Calendar.tsx | 163 + .../CalendarComponent.styles.tsx | 125 + .../CalendarComponent/CalendarComponent.tsx | 134 + .../CalendarDateCell/CalendarDateCell.tsx | 66 + .../CalendarPeriodControl.tsx | 87 + .../CalendarPeriodControlTitle.tsx | 50 + .../CalendarResourceHeader.tsx | 67 + .../CalendarResourceLabel.tsx | 31 + .../CalendarToolbar/CalendarToolbar.tsx | 110 + .../HideEmptyResourcesListItem.tsx | 38 + .../components/RentEvent/RentEvent.tsx | 136 + .../RentEventDetails/RentEventDetails.tsx | 144 + .../pages/TimetablePage/components/index.tsx | 7 + frontend/src/modules/products/pages/index.tsx | 41 + .../modules/products/shared/assets/arrow.svg | 5 + .../products/shared/assets/created_at.svg | 5 + .../products/shared/assets/decrement.svg | 5 + .../modules/products/shared/assets/delete.svg | 7 + .../shared/assets/hide_empty_resources.svg | 5 + .../products/shared/assets/increment.svg | 5 + .../modules/products/shared/assets/index.tsx | 12 + .../products/shared/assets/scan_barcode.svg | 5 + .../products/shared/assets/settings.svg | 12 + .../products/shared/assets/shipped_at.svg | 5 + .../shared/assets/show_empty_resources.svg | 5 + .../modules/products/shared/assets/unfold.svg | 5 + .../products/shared/assets/warehouses.svg | 5 + .../src/modules/products/shared/index.tsx | 2 + .../AddPlaceholderTemplate.tsx | 57 + .../BlockFooterControls.tsx | 76 + .../OrderStatusSelect/OrderStatusSelect.tsx | 87 + .../ProductCategoriesBlock.tsx | 82 + .../CategoryBlock/CategoryBlock.tsx | 169 + .../CategoryBlockSkeleton.tsx | 10 + .../DeleteCategoryWarningModal.tsx | 49 + .../SubcategoryBlock/SubcategoryBlock.tsx | 120 + .../SubcategoryList/SubcategoryList.tsx | 56 + .../components/index.tsx | 2 + .../ProductCategoriesSelect.tsx | 249 + .../ProductWarehouseBlockTemplateHeader.tsx | 83 + .../ProductWarehousesSelect.tsx | 268 + .../ProductsOrderComponentRoot.tsx | 7 + .../ProductsOrderTotalBlock.tsx | 48 + .../ProductsSettingsButton.tsx | 137 + .../QuantityControlButton.tsx | 50 + .../RemoveSelectedBlock.tsx | 52 + .../RentalOrderStatusSelect.tsx | 57 + .../ReservationsModal/ReservationsModal.tsx | 137 + .../AddStockPlaceholder.tsx | 27 + .../ReservationsTable/ReservationsTable.tsx | 34 + .../WarehouseNameCell/WarehouseNameCell.tsx | 61 + .../ReservationsModal/components/index.tsx | 3 + .../StockQuantityBLock/StockQuantityBlock.tsx | 22 + .../WarehousesBlock/WarehousesBlock.tsx | 92 + .../DeleteWarehouseModal.tsx | 84 + .../WarehousePageBlockSkeleton.tsx | 10 + .../WarehousesBlockComponent.tsx | 137 + .../WarehousesBlock/components/index.tsx | 2 + .../AddProductItemCell/AddProductItemCell.tsx | 91 + .../cells/AmountCellRoot/AmountCellRoot.tsx | 32 + .../CardOrderQuantityCellSwitch.tsx | 79 + .../cells/CreateStockCell/CreateStockCell.tsx | 19 + .../components/cells/NameCell/NameCell.tsx | 64 + .../ProductPriceCell/ProductPriceCell.tsx | 45 + .../ProductsOrderDeleteItemCell.tsx | 92 + .../ProductsOrderDiscountCell.tsx | 76 + .../ProductsOrderPriceCell.tsx | 160 + .../ProductsOrderPriceHeadCell.tsx | 41 + .../ProductsOrderTaxHeaderCell.tsx | 56 + .../cells/QuantityCell/QuantityCell.tsx | 37 + .../QuantityCellWithModal.tsx | 106 + .../RentalAvailabilityCell.tsx | 74 + .../ReservationQuantityCell.tsx | 65 + .../components/cells/StockCell/StockCell.tsx | 53 + .../products/shared/lib/components/index.tsx | 40 + .../lib/helpers/checkDuplicateProductSku.ts | 25 + .../shared/lib/helpers/generateOrderName.ts | 9 + .../lib/helpers/generateProductTypeOptions.ts | 14 + .../generateRentalOrderPeriodsControlTitle.ts | 16 + .../generateRentalOrderStatusOptions.ts | 69 + .../shared/lib/helpers/getCategoryById.ts | 20 + .../helpers/getDefaultProductsColumnSize.ts | 13 + .../lib/helpers/getProductCategoryName.ts | 19 + .../lib/helpers/getProductsPageTabs.tsx | 84 + .../products/shared/lib/helpers/index.tsx | 10 + .../lib/helpers/transformCalendarEvents.ts | 11 + .../helpers/transformProductsToResources.ts | 12 + .../products/shared/lib/hooks/index.tsx | 15 + .../lib/hooks/useAvailableCellColumns.tsx | 89 + .../shared/lib/hooks/useCardOrderColumns.tsx | 238 + .../lib/hooks/useCreateStocksColumns.tsx | 38 + .../shared/lib/hooks/usePriceCellColumns.tsx | 47 + .../lib/hooks/useProductStocksColumns.tsx | 79 + .../shared/lib/hooks/useProductsColumns.tsx | 134 + .../hooks/useProductsSectionOrdersColumns.tsx | 155 + .../lib/hooks/useRentalCardOrderColumns.tsx | 197 + .../useRentalProductsSectionOrdersColumns.tsx | 178 + .../lib/hooks/useRentalShipmentColumns.tsx | 134 + .../lib/hooks/useRentalWarehouseColumns.tsx | 168 + .../lib/hooks/useReservationsColumns.tsx | 88 + .../shared/lib/hooks/useShipmentColumns.tsx | 110 + .../shared/lib/hooks/useShipmentsColumns.tsx | 154 + .../shared/lib/hooks/useWarehouseColumns.tsx | 270 + .../src/modules/products/shared/lib/index.tsx | 4 + .../AvailableCellColumnsIds.ts | 6 + .../AvailableCellColumnsSizes.ts | 8 + .../shared/lib/models/CalendarEvent.ts | 10 + .../shared/lib/models/CalendarGridView.ts | 8 + .../shared/lib/models/CalendarResource.ts | 5 + .../CardOrderColumns/CardOrderColumnsIds.ts | 11 + .../CardOrderColumns/CardOrderColumnsSizes.ts | 20 + .../models/CardProductOrderPageSettings.ts | 5 + .../shared/lib/models/CheckBarcodeResult.ts | 4 + .../shared/lib/models/CreateStockRow.ts | 41 + .../CreateStocksColumnsIds.ts | 4 + .../CreateStocksColumnsSizes.ts | 10 + .../shared/lib/models/Events/EventShort.ts | 10 + .../models/LastSelectedWarehouseSettings.ts | 6 + .../lib/models/OrderBlockComponentProps.ts | 25 + .../lib/models/OrderBlockFilterProps.ts | 12 + .../shared/lib/models/OrderMutationRights.ts | 4 + .../PriceCellColumns/PriceCellColumnsIds.ts | 5 + .../PriceCellColumns/PriceCellColumnsSizes.ts | 8 + .../products/shared/lib/models/PriceForm.ts | 73 + .../lib/models/Product/GetProductsMeta.ts | 7 + .../lib/models/Product/GetProductsResult.ts | 17 + .../shared/lib/models/Product/Product.ts | 131 + .../shared/lib/models/Product/ProductInfo.ts | 15 + .../shared/lib/models/Product/ProductRow.ts | 92 + .../shared/lib/models/Product/ProductType.ts | 5 + .../lib/models/Product/RentalProductRow.ts | 22 + .../lib/models/Product/RentalSchedule.ts | 35 + .../shared/lib/models/Product/RentalStatus.ts | 5 + .../models/ProductCategory/ProductCategory.ts | 32 + .../shared/lib/models/ProductOrder/Order.ts | 77 + .../lib/models/ProductOrder/OrderItem.ts | 55 + .../lib/models/ProductOrder/OrderItemRow.ts | 162 + .../lib/models/ProductOrder/Reservation.ts | 27 + .../models/ProductOrderStatus/OrderStatus.ts | 9 + .../ProductOrderStatus/OrderStatusCode.ts | 7 + .../ProductOrderStatus/StatusTransition.ts | 32 + .../lib/models/ProductPrice/ProductPrice.ts | 28 + .../models/ProductRentalOrder/DatePeriod.ts | 23 + .../ProductRentalOrder/ProductRentalStatus.ts | 35 + .../models/ProductRentalOrder/RentalEvent.ts | 47 + .../models/ProductRentalOrder/RentalOrder.ts | 82 + .../ProductRentalOrder/RentalOrderItem.ts | 34 + .../ProductRentalOrder/RentalOrderItemRow.ts | 89 + .../ProductRentalOrder/RentalOrderStatus.ts | 10 + .../ProductSectionOrdersColumnsIds.ts | 3 + .../ProductSectionOrdersColumnsSizes.ts | 5 + .../ProductRentalIntervalFormData.ts | 28 + .../RentalInterval.ts | 20 + .../RentalIntervalType.ts | 3 + .../ProductStocksColumnsIds.ts | 7 + .../ProductStocksColumnsSizes.ts | 13 + .../ProductsColumns/ProductsColumnsIds.ts | 10 + .../ProductsColumns/ProductsColumnsSizes.ts | 15 + .../lib/models/ProductsPageQueryParams.ts | 4 + .../shared/lib/models/ProductsPageTabs.ts | 6 + .../ProductSectionBuilderFormData.ts | 119 + .../models/ProductsSection/ProductsSection.ts | 71 + .../ProductsSection/ProductsSectionType.ts | 4 + .../ProductsSectionOrdersCommonColumnsIds.ts | 8 + ...ProductsSectionOrdersCommonColumnsSizes.ts | 17 + .../lib/models/Rental/GetRentalsResult.ts | 19 + .../shared/lib/models/Rental/Rental.ts | 47 + .../RentalCardOrderColumnsIds.ts | 10 + .../RentalCardOrderColumnsSizes.ts | 21 + .../RentalShipmentColumnsIds.ts | 9 + .../RentalShipmentColumnsSizes.ts | 15 + .../RentalWarehouseColumnsIds.ts | 7 + .../RentalWarehouseColumnsSizes.ts | 19 + .../lib/models/Reservation/ReservationRow.ts | 60 + .../ReservationsColumnsIds.ts | 7 + .../ReservationsColumnsSizes.ts | 13 + .../shared/lib/models/ResourceViewMode.ts | 4 + .../lib/models/Shipment/GetShipmentsResult.ts | 17 + .../models/Shipment/RentalShipmentItemRow.ts | 57 + .../shared/lib/models/Shipment/Shipment.ts | 78 + .../lib/models/Shipment/ShipmentItem.ts | 21 + .../lib/models/Shipment/ShipmentItemRow.ts | 25 + .../shared/lib/models/Shipment/ShipmentRow.ts | 12 + .../ShipmentColumns/ShipmentColumnsIds.ts | 7 + .../ShipmentColumns/ShipmentColumnsSizes.ts | 10 + .../ShipmentsColumns/ShipmentsColumnsIds.ts | 8 + .../ShipmentsColumns/ShipmentsColumnsSizes.ts | 11 + .../ShipmentsTable/ShipmentsTableSettings.ts | 4 + .../ShipmentsTable/ShipmentsTablesSettings.ts | 5 + .../products/shared/lib/models/Stock/Stock.ts | 28 + .../products/shared/lib/models/StockRow.ts | 23 + .../TableSettings/ProductTableSettings.ts | 7 + .../TableSettings/ProductTablesSettings.ts | 5 + .../products/shared/lib/models/TaxStrategy.ts | 4 + .../lib/models/UtcDatesRangeValueModel.ts | 6 + .../shared/lib/models/Warehouse/Warehouse.ts | 29 + .../WarehouseColumns/WarehouseColumnsIds.ts | 7 + .../WarehouseColumns/WarehouseColumnsSizes.ts | 16 + .../lib/models/fullCalendarLicenseKey.ts | 1 + .../products/shared/lib/models/index.tsx | 88 + .../lib/models/shipmentsTableSettingsKey.ts | 1 + .../products/store/AddProductModalStore.ts | 127 + .../products/store/OrderStatusStore.ts | 66 + .../src/modules/products/store/OrderStore.ts | 584 + .../products/store/ProductCategoryStore.ts | 141 + .../products/store/ProductStockBlockStore.ts | 54 + .../products/store/ProductsModuleStore.ts | 23 + .../store/ProductsSectionBuilderStore.ts | 165 + .../products/store/RentalOrderStore.ts | 540 + .../products/store/RentalShipmentStore.ts | 123 + .../modules/products/store/ShipmentStore.ts | 130 + .../modules/products/store/WarehouseStore.ts | 109 + frontend/src/modules/products/store/index.tsx | 11 + .../ProductPageTemplate.tsx | 111 + .../ProductSectionOrdersItemTemplate.tsx | 89 + .../ProductsOrderBlockTemplate.tsx | 135 + .../ProductsOrderPanelTemplate.tsx | 113 + .../ProductsOrderPanelsTemplate.tsx | 33 + .../ProductsOrderResizeHandler.tsx | 21 + .../components/index.tsx | 1 + .../ProductsWarehouseBlockTemplate.tsx | 131 + .../ShipmentPageSecondaryHeaderTemplate.tsx | 110 + .../src/modules/products/templates/index.tsx | 7 + .../api/DashboardApi/DashboardApi.ts | 146 + .../queries/useGetActivitiesSummaryReport.ts | 21 + .../queries/useGetEntitySummaryReport.ts | 21 + .../queries/useGetPipelineReport.ts | 17 + .../api/DashboardApi/queries/useGetRating.ts | 31 + .../queries/useGetSalesPlanReport.ts | 21 + .../queries/useGetTasksSummaryReport.ts | 21 + .../DashboardApi/queries/useGetTopSellers.ts | 21 + .../api/GoalSettingsApi/GoalSettingsApi.ts | 112 + .../reporting/api/ReportApi/ReportApi.ts | 112 + .../queries/useGetCallHistoryReport.ts | 21 + .../queries/useGetComparativeReport.ts | 11 + .../ReportApi/queries/useGetCustomerReport.ts | 20 + .../ReportApi/queries/useGetGeneralReport.ts | 11 + .../queries/useGetProductsGeneralReport.ts | 11 + .../queries/useGetProjectEntities.ts | 16 + .../queries/useGetProjectEntitiesReport.ts | 11 + .../queries/useGetProjectTaskUserReport.ts | 11 + .../ReportApi/queries/useGetScheduleReport.ts | 11 + .../queries/useGetTelephonyReport.ts | 11 + .../reporting/api/ReportingApiRoutes.ts | 26 + .../reporting/api/ReportingQueryKeys.ts | 78 + .../Dashboard/Pipeline/PipelineReportDto.ts | 9 + .../Pipeline/SalesPipelineReportRowDto.ts | 12 + .../reporting/api/dtos/DatePeriodDto.ts | 4 + .../reporting/api/dtos/DatePeriodFilter.ts | 7 + .../dtos/EntitySummary/EntitySummaryReport.ts | 8 + .../dtos/EntitySummary/EntitySummaryValue.ts | 4 + .../reporting/api/dtos/ReportFilter.ts | 8 + .../Comparative/ComparativeReportCellDto.ts | 9 + .../Comparative/ComparativeReportDto.ts | 7 + .../Comparative/ComparativeReportFilterDto.ts | 31 + .../Comparative/ComparativeReportRowDto.ts | 6 + .../Comparative/ComparativeReportValueDto.ts | 7 + .../Reports/Customer/CustomerReportDto.ts | 8 + .../Customer/CustomerReportFieldDto.ts | 5 + .../Customer/CustomerReportFieldMetaDto.ts | 4 + .../Customer/CustomerReportFilterDto.ts | 19 + .../Reports/Customer/CustomerReportMetaDto.ts | 7 + .../Reports/Customer/CustomerReportRowDto.ts | 18 + .../Reports/General/CallReportBlockDto.ts | 29 + .../dtos/Reports/General/GeneralReportDto.ts | 9 + .../Reports/General/GeneralReportEntityDto.ts | 10 + .../Reports/General/GeneralReportFieldDto.ts | 13 + .../General/GeneralReportFieldMetaDto.ts | 13 + .../GeneralReportFieldOptionMetaDto.ts | 7 + .../General/GeneralReportFieldValueDto.ts | 13 + .../Reports/General/GeneralReportFilterDto.ts | 35 + .../GeneralReportFilterVisibilityCallDto.ts | 9 + .../GeneralReportFilterVisibilityDto.ts | 21 + .../GeneralReportFilterVisibilityEntityDto.ts | 20 + .../GeneralReportFilterVisibilityFieldDto.ts | 14 + ...ralReportFilterVisibilityFieldOptionDto.ts | 11 + .../GeneralReportFilterVisibilityFieldsDto.ts | 12 + .../GeneralReportFilterVisibilityTaskDto.ts | 20 + .../Reports/General/GeneralReportMetaDto.ts | 9 + .../Reports/General/GeneralReportRowDto.ts | 14 + .../Reports/General/GeneralReportTaskDto.ts | 6 + .../dtos/Reports/PossibleReportFilterDto.ts | 12 + .../Products/General/ProductsReportDto.ts | 7 + .../General/ProductsReportFilterDto.ts | 37 + .../Products/General/ProductsReportRowDto.ts | 18 + .../General/ProductsReportUserCellDto.ts | 6 + .../Projects/ProjectEntitiesReportDto.ts | 8 + .../ProjectEntitiesReportFilterDto.ts | 24 + .../Projects/ProjectEntitiesReportMetaDto.ts | 5 + .../Projects/ProjectEntitiesReportRowDto.ts | 15 + .../Reports/Projects/ProjectReportFieldDto.ts | 5 + .../Projects/ProjectReportFieldMetaDto.ts | 4 + .../Projects/ProjectTaskUserReportDto.ts | 7 + .../Projects/ProjectTaskUserReportRowDto.ts | 11 + .../ProjectTaskUserReportRowTotalDto.ts | 10 + .../Projects/ProjectsReportFilterDto.ts | 24 + .../Schedule/ScheduleReportFilterDto.ts | 19 + .../dtos/Reports/Telephony/CallDuration.ts | 4 + .../Reports/Telephony/CallHistoryReportDto.ts | 7 + .../Telephony/CallHistoryReportFilterDto.ts | 29 + .../Telephony/CallHistoryReportItemDto.ts | 15 + .../Reports/Telephony/TelephonyReportDto.ts | 7 + .../Telephony/TelephonyReportFilterDto.ts | 32 + .../Telephony/TelephonyReportRowDto.ts | 7 + .../reporting/api/dtos/SalesPlanDto.ts | 8 + .../api/dtos/SalesPlanProgressDto.ts | 7 + .../reporting/api/dtos/SalesPlanReport.ts | 6 + .../reporting/api/dtos/SellersRatingReport.ts | 7 + .../reporting/api/dtos/TasksSummaryReport.ts | 6 + .../reporting/api/dtos/TopSellersReport.ts | 7 + .../src/modules/reporting/api/dtos/index.tsx | 63 + frontend/src/modules/reporting/api/index.tsx | 21 + frontend/src/modules/reporting/index.tsx | 2 + .../pages/DashboardPage/Dashboard.tsx | 464 + .../AnalyticsBlock/AnalyticsBlock.tsx | 51 + .../ResolveIconSwitch/ResolveIconSwitch.tsx | 68 + .../AnalyticsBlock/components/Unit/Unit.tsx | 100 + .../components/UnitBody/UnitBody.tsx | 66 + .../components/UnitsList/UnitsList.tsx | 88 + .../AnalyticsBlock/components/index.tsx | 1 + .../DashboardControls/DashboardControls.tsx | 163 + .../DashboardTypeTooltip.tsx | 29 + .../DataUpdateSelect/DataUpdateSelect.tsx | 101 + .../DashboardControls/components/index.tsx | 2 + .../components/GoalChart/GoalChart.tsx | 27 + .../GoalSettingsLink/GoalSettingsLink.tsx | 73 + .../components/GoalChart/components/index.tsx | 1 + .../LeadsStatusChart/LeadsStatusChart.tsx | 156 + .../components/Column/Column.tsx | 155 + .../LeadsStatusChart/components/index.tsx | 1 + .../components/RatingChart/RatingChart.tsx | 78 + .../components/RatingItem/RatingItem.tsx | 109 + .../components/RatingList/RatingList.tsx | 60 + .../RatingChart/components/index.tsx | 1 + .../SalesPipelineIndicators.tsx | 159 + .../components/StageRow/StageRow.tsx | 211 + .../components/index.tsx | 1 + .../TopSellersChart/TopSellersChart.tsx | 229 + .../TrafficLightReport/TrafficLightReport.tsx | 190 + .../pages/DashboardPage/components/index.tsx | 8 + .../GoalSettingsPage/GoalSettingsPage.tsx | 305 + .../components/ControlsRow/ControlsRow.tsx | 183 + .../GoalSettingsForm/GoalSettingsForm.tsx | 363 + .../GoalSettingsFormItem.tsx | 121 + .../components/GroupIcon/GroupIcon.tsx | 28 + .../PeriodControl/PeriodControl.tsx | 79 + .../GoalSettingsPage/components/index.tsx | 2 + .../reporting/pages/ReportsPage/Reports.tsx | 193 + .../ReportsNavigationSidebar.tsx | 227 + .../TabLists/ComparisonReportTabList.tsx | 29 + .../TabLists/CustomerReportTabList.tsx | 32 + .../TabLists/GeneralReportTabList.tsx | 23 + .../ProductsGeneralReportTabListEntity.tsx | 74 + .../ProductsGeneralReportTabListSection.tsx | 70 + .../TabLists/ProjectReportTabList.tsx | 69 + .../TabLists/ScheduleReportTabList.tsx | 57 + .../TabLists/TelephonyReportTabList.tsx | 28 + .../CollapsibleGroup/CollapsibleGroup.tsx | 102 + .../CollapsibleTab/CollapsibleTab.tsx | 103 + .../DynamicScheduleTabList.tsx | 92 + .../ProjectEntitiesTabs.tsx | 69 + .../components/StyledTab/StyledTab.tsx | 39 + .../components/TabLists/components/index.tsx | 5 + .../TitleWrapperSwitch/TitleWrapperSwitch.tsx | 65 + .../components/index.tsx | 8 + .../TabPanels/ComparativeReportTabPanel.tsx | 30 + .../TabPanels/CustomerReportTabPanel.tsx | 31 + .../TabPanels/GeneralReportTabPanel.tsx | 28 + .../ProductsGeneralReportTabPanel.tsx | 41 + .../TabPanels/ProjectReportTabPanel.tsx | 30 + .../TabPanels/ScheduleReportTabPanel.tsx | 32 + .../TabPanels/TelephonyReportTabPanel.tsx | 33 + .../pages/ReportsPage/components/index.tsx | 8 + .../src/modules/reporting/pages/index.tsx | 4 + .../assets/ReportsPage/call_incoming.svg | 5 + .../shared/assets/ReportsPage/call_missed.svg | 5 + .../assets/ReportsPage/call_outgoing.svg | 8 + .../shared/assets/ReportsPage/caret.svg | 3 + .../shared/assets/ReportsPage/decline.svg | 5 + .../assets/ReportsPage/filters_menu.svg | 14 + .../assets/ReportsPage/frameless_caret.svg | 5 + .../shared/assets/ReportsPage/growth.svg | 5 + .../assets/ReportsPage/scroll_arrow.svg | 5 + .../assets/SalesAnalytics/average_amount.svg | 13 + .../assets/SalesAnalytics/average_term.svg | 22 + .../assets/SalesAnalytics/increased_sales.svg | 6 + .../shared/assets/SalesAnalytics/wallet.svg | 11 + .../shared/assets/Statistics/lost.svg | 4 + .../shared/assets/Statistics/new.svg | 8 + .../shared/assets/Statistics/total.svg | 8 + .../shared/assets/Statistics/won.svg | 4 + .../assets/TasksAndActivities/completed.svg | 8 + .../assets/TasksAndActivities/expired.svg | 8 + .../shared/assets/TasksAndActivities/no.svg | 6 + .../assets/TasksAndActivities/total.svg | 12 + .../modules/reporting/shared/assets/arrow.svg | 8 + .../reporting/shared/assets/arrow_back.svg | 5 + .../reporting/shared/assets/arrow_down.svg | 4 + .../modules/reporting/shared/assets/index.tsx | 33 + .../reporting/shared/assets/pie_plug.svg | 39 + .../reporting/shared/assets/product_row.svg | 6 + .../reporting/shared/assets/speedometer.svg | 11 + .../shared/assets/speedometer_plug.svg | 26 + .../shared/assets/toggle_expanded.svg | 5 + .../shared/assets/top_sellers_plug.svg | 153 + .../modules/reporting/shared/assets/total.svg | 11 + .../shared/assets/traffic_light_plug.svg | 9 + .../modules/reporting/shared/assets/tune.svg | 5 + .../reporting/shared/assets/update.svg | 18 + .../src/modules/reporting/shared/index.tsx | 2 + .../shared/lib/components/Block/Block.tsx | 32 + .../components/BlockHeader/BlockHeader.tsx | 114 + .../lib/components/ChartPlug/ChartPlug.tsx | 47 + .../CountupComponent/CountupComponent.tsx | 66 + .../lib/components/NameLink/NameLink.tsx | 18 + .../CallHistoryTimePicker.tsx | 98 + .../components/Delimiter/Delimiter.tsx | 10 + .../DoubleDigitNumberInput.tsx | 150 + .../components/index.tsx | 1 + .../ComparativeCompositeHeader.tsx | 59 + .../ComparativeReportValueCell.tsx | 57 + .../ReportValueCellPart.tsx | 112 + .../components/index.tsx | 1 + .../HeadTitleCellWithToggle.tsx | 59 + .../RowParticipantCellSwitch.tsx | 81 + .../ContactParticipant/ContactParticipant.tsx | 86 + .../UserParticipant/UserParticipant.tsx | 42 + .../components/index.tsx | 2 + .../Reports/RowRecordCell/RowRecordCell.tsx | 48 + .../Reports/RowTitleCell/RowTitleCell.tsx | 120 + .../Reports/RowTitleIcon/RowTitleIcon.tsx | 25 + .../components/Speedometer/Speedometer.tsx | 263 + .../lib/components/ViewSwitch/ViewSwitch.tsx | 49 + .../reporting/shared/lib/components/index.tsx | 15 + .../shared/lib/helpers/calculatePercent.ts | 15 + .../lib/helpers/convertPercentToAngle.ts | 9 + .../shared/lib/helpers/exportReportToXlsx.ts | 122 + .../shared/lib/helpers/externalTooltip.ts | 167 + .../formatQuantityAmountWithCurrency.ts | 14 + .../formatQuantityAmountWithMinutes.ts | 15 + .../formatQuantityAmountWithoutCurrency.ts | 5 + .../lib/helpers/formatTaskCountPlannedTime.ts | 15 + .../lib/helpers/generateActivitiesUnits.ts | 38 + ...generateArrayOfYearsFromAccountCreation.ts | 10 + .../generateCompositeProductsReportValue.ts | 14 + .../lib/helpers/generateEntitiesUnits.ts | 69 + .../helpers/generateFieldOptionColumnId.ts | 18 + ...erateGeneralReportSettingsObjStorageKey.ts | 9 + .../lib/helpers/generateSalesAnalytics.ts | 58 + .../shared/lib/helpers/generateTasksUnits.ts | 38 + .../lib/helpers/getCustomerReportSections.ts | 17 + .../shared/lib/helpers/getDoughnutData.ts | 34 + .../shared/lib/helpers/getLinkedCategories.ts | 7 + .../shared/lib/helpers/getPieData.ts | 31 + .../shared/lib/helpers/htmlLegendPlugin.ts | 72 + .../reporting/shared/lib/helpers/index.tsx | 23 + .../lib/helpers/secondsToHoursAndMinutes.ts | 16 + .../Comparative/useGenerateMonthOptions.ts | 18 + .../useGetComparativeReportColumns.tsx | 309 + .../useGetComparativeReportTableData.ts | 95 + .../Customer/useGetCustomerReportColumns.tsx | 120 + .../Customer/useGetCustomerReportTableData.ts | 21 + .../General/useGenerateStageOptions.ts | 21 + .../General/useGetGeneralReportColumns.tsx | 327 + .../General/useGetGeneralReportTableData.ts | 183 + .../useGetProductsGeneralReportColumns.tsx | 230 + .../useGetProductsGeneralReportTableData.ts | 140 + .../useGetProjectEntitiesReportColumns.tsx | 141 + .../useGetProjectEntitiesReportTableData.ts | 22 + .../useGetProjectTaskUserReportColumns.tsx | 83 + .../useGetProjectTaskUserReportTableData.ts | 24 + .../Schedule/useGetScheduleReportColumns.tsx | 75 + .../Schedule/useGetScheduleReportTableData.ts | 96 + .../Telephony/useGenerateDirectionOptions.ts | 19 + .../useGetCallHistoryReportColumns.tsx | 91 + .../useGetCallHistoryReportTableData.ts | 18 + .../useGetTelephonyReportColumns.tsx | 100 + .../useGetTelephonyReportTableData.ts | 152 + .../Reports/useGetOwnerFieldsFilterOptions.ts | 28 + .../Reports/useSaveReportColumnsVisibility.ts | 115 + .../reporting/shared/lib/hooks/index.tsx | 27 + .../shared/lib/hooks/useGetReportTable.ts | 40 + .../modules/reporting/shared/lib/index.tsx | 5 + .../lib/models/Analytics/AnalyticsColors.ts | 6 + .../models/Analytics/AnalyticsIconsNames.ts | 14 + .../lib/models/Analytics/AnalyticsUnit.ts | 8 + .../models/Analytics/AnalyticsValueType.ts | 6 + .../models/Analytics/SalesAnalyticsUnit.ts | 14 + .../Analytics/SalesPipelineAnalyticsModel.ts | 6 + .../lib/models/AnalyticsBlockPalette.ts | 8 + .../shared/lib/models/AutoUpdateMode.ts | 7 + .../shared/lib/models/AutoUpdateTime.ts | 13 + .../reporting/shared/lib/models/ChartType.ts | 5 + .../shared/lib/models/DashboardFilter.ts | 16 + .../lib/models/DashboardFilterSettings.ts | 6 + .../lib/models/DepartmentWithUsersOption.ts | 13 + .../shared/lib/models/EntityReport.ts | 24 + .../lib/models/GoalSettingsPageSettings.ts | 6 + .../models/Leads/LeadsStatusChartPalette.ts | 7 + .../reporting/shared/lib/models/Periods.ts | 4 + .../lib/models/Pipeline/PipelineReport.ts | 29 + .../lib/models/Pipeline/PipelineReportType.ts | 8 + .../models/Pipeline/SalesPipelineReportRow.ts | 50 + .../shared/lib/models/QuantityAmount.ts | 4 + .../shared/lib/models/ReportingOption.ts | 6 + .../lib/models/Reports/CallReportBlock.ts | 45 + .../Reports/Comparative/ComparativeReport.ts | 39 + .../Comparative/ComparativeReportCell.ts | 44 + .../Comparative/ComparativeReportRow.ts | 23 + .../Comparative/ComparativeReportType.ts | 7 + .../Comparative/ComparativeReportValue.ts | 30 + .../models/Reports/Customer/CustomerReport.ts | 23 + .../Reports/Customer/CustomerReportField.ts | 25 + .../Customer/CustomerReportFieldMeta.ts | 22 + .../Reports/Customer/CustomerReportMeta.ts | 22 + .../Reports/Customer/CustomerReportRow.ts | 68 + .../Reports/Customer/CustomerReportType.ts | 5 + .../models/Reports/General/GeneralReport.ts | 45 + .../General/GeneralReportColumnMeta.ts | 5 + .../General/GeneralReportColumnMetaType.ts | 4 + .../General/GeneralReportColumnsIds.ts | 30 + .../Reports/General/GeneralReportEntity.ts | 48 + .../Reports/General/GeneralReportField.ts | 29 + .../Reports/General/GeneralReportFieldMeta.ts | 26 + .../General/GeneralReportFieldOptionMeta.ts | 26 + .../General/GeneralReportFieldValue.ts | 28 + .../Reports/General/GeneralReportMeta.ts | 14 + .../Reports/General/GeneralReportRow.ts | 39 + .../Reports/General/GeneralReportSettings.ts | 11 + .../Reports/General/GeneralReportTask.ts | 27 + .../Reports/General/GeneralReportType.ts | 5 + .../models/Reports/General/ReportStageType.ts | 7 + .../lib/models/Reports/PossibleReportType.ts | 18 + .../Reports/Products/General/ProductReport.ts | 47 + .../Products/General/ProductReportRow.ts | 85 + .../Products/General/ProductReportUserCell.ts | 23 + .../Products/General/ProductsReportType.ts | 5 + .../Products/productsGeneralReportSections.ts | 7 + .../reportSectionToProductsReportTypeMap.ts | 9 + .../Reports/Projects/BoardHasEntitiesState.ts | 4 + .../Reports/Projects/ProjectEntitiesReport.ts | 23 + .../Projects/ProjectEntitiesReportRow.ts | 57 + .../Reports/Projects/ProjectReportField.ts | 25 + .../Projects/ProjectReportFieldMeta.ts | 22 + .../Reports/Projects/ProjectReportItem.ts | 6 + .../Reports/Projects/ProjectReportMeta.ts | 14 + .../Reports/Projects/ProjectReportType.ts | 4 + .../Reports/Projects/ProjectStageItem.ts | 6 + .../Reports/Projects/ProjectTaskUserReport.ts | 20 + .../Projects/ProjectTaskUserReportRow.ts | 47 + .../Projects/ProjectTaskUserReportTotalRow.ts | 39 + .../shared/lib/models/Reports/RenderTabs.ts | 6 + .../models/Reports/ReportFilterSettings.ts | 15 + .../lib/models/Reports/ReportTabPanelProps.ts | 9 + .../models/Reports/ReportTableColumnMeta.ts | 7 + .../lib/models/Reports/ReportTemplateProps.ts | 6 + .../lib/models/Reports/ReportsColumnsIds.ts | 3 + .../lib/models/Reports/ReportsSection.ts | 35 + .../ReportSectionToScheduleReportTypeMap.ts | 10 + .../models/Reports/Schedule/ScheduleReport.ts | 24 + .../Reports/Schedule/ScheduleReportDto.ts | 6 + .../Reports/Schedule/ScheduleReportRow.ts | 51 + .../Reports/Schedule/ScheduleReportRowDto.ts | 13 + .../Reports/Schedule/ScheduleReportType.ts | 6 + .../Reports/Telephony/CallHistoryReport.ts | 20 + .../Telephony/CallHistoryReportItem.ts | 59 + .../Telephony/CallHistoryReportType.ts | 3 + .../Reports/Telephony/CallParticipant.ts | 1 + .../Telephony/DurationFilterSelectModel.ts | 6 + .../Telephony/ExtendedCallDirections.ts | 5 + .../Reports/Telephony/TelephonyReport.ts | 39 + .../Reports/Telephony/TelephonyReportBlock.ts | 42 + .../Reports/Telephony/TelephonyReportRow.ts | 26 + .../Reports/Telephony/TelephonyReportType.ts | 4 + .../shared/lib/models/SalesPipelineFilter.ts | 16 + .../lib/models/SalesPipelineFilterSettings.ts | 6 + .../lib/models/SalesPlan/SalesGoalModel.ts | 15 + .../lib/models/SalesPlan/SalesGoalSettings.ts | 17 + .../shared/lib/models/SalesPlan/SalesPlan.ts | 28 + .../SalesPlan/SalesPlanPeriodFilterType.ts | 8 + .../lib/models/SalesPlan/SalesPlanProgress.ts | 37 + .../models/SalesPlan/SalesPlanReportModel.ts | 19 + .../lib/models/SalesPlan/SalesPlanValue.ts | 5 + .../lib/models/SalesPlan/SalesPlanWithUser.ts | 6 + .../shared/lib/models/SalesValues.ts | 4 + .../models/SubdepartmentWithUsersOption.ts | 9 + .../shared/lib/models/Tasks/TasksReport.ts | 24 + .../lib/models/TopSellers/SellersRating.ts | 20 + .../lib/models/TopSellers/TopSellerData.ts | 9 + .../lib/models/TopSellers/TopSellers.ts | 23 + .../lib/models/TopSellers/TopSellersUser.ts | 5 + .../models/TopSellers/TopSellersWithOthers.ts | 7 + .../lib/models/TrafficLightColorStyles.ts | 5 + .../shared/lib/models/TrafficLightColors.ts | 5 + .../shared/lib/models/UserQuantityAmount.ts | 5 + .../shared/lib/models/doughnutBGColors.ts | 8 + .../shared/lib/models/doughnutBorderColors.ts | 8 + .../reporting/shared/lib/models/index.tsx | 95 + .../shared/lib/models/pieBGColors.ts | 8 + .../shared/lib/types/FormGroupType.ts | 1 + .../reporting/shared/lib/types/GoalType.ts | 1 + .../reporting/shared/lib/types/LeadsUnit.ts | 9 + .../shared/lib/types/ReportRowType.ts | 1 + .../ComparativeReportSyntheticRow.ts | 4 + .../Customer/CustomerReportSyntheticRow.ts | 4 + .../General/GeneralReportSyntheticRow.ts | 4 + .../ProductsGeneralReportSyntheticRow.ts | 4 + .../ProjectEntitiesReportSyntheticRow.ts | 4 + .../ProjectTaskUserReportSyntheticRow.ts | 6 + .../lib/types/Reports/ReportSyntheticRow.ts | 11 + .../Schedule/ScheduleReportSyntheticRow.ts | 4 + .../CallHistoryReportSyntheticRow.ts | 4 + .../Telephony/TelephonyReportSyntheticRow.ts | 4 + .../reporting/shared/lib/types/TasksUnit.ts | 9 + .../reporting/shared/lib/types/index.tsx | 15 + .../GeneralReportTemplateSettingsStore.ts | 70 + .../reporting/store/GoalSettingsStore.ts | 174 + .../src/modules/reporting/store/index.tsx | 2 + .../CallHistoryReportTemplate.tsx | 279 + .../components/CheckboxWrapper.tsx | 12 + .../components/TimePickerGroup.tsx | 77 + .../components/index.tsx | 2 + .../ComparativeReportTemplate.tsx | 275 + .../CustomerReportTemplate.tsx | 233 + .../GeneralReportTemplate.tsx | 525 + .../ProductsGeneralReportTemplate.tsx | 324 + .../ProjectEntitiesReportTemplate.tsx | 255 + .../ProjectTaskUserReportTemplate.tsx | 241 + .../ScheduleReportTemplate.tsx | 210 + .../TelephonyReportTemplate.tsx | 227 + .../ReportRootTemplate/ReportRootTemplate.tsx | 302 + .../ReportSettingsDrawer.tsx | 215 + .../components/ReportTable/ReportTable.tsx | 330 + .../templates/Reports/components/index.tsx | 2 + .../src/modules/reporting/templates/index.tsx | 10 + .../scheduler/api/ScheduleApi/ScheduleApi.ts | 46 + .../helpers/deleteScheduleInCache.ts | 9 + .../api/ScheduleApi/helpers/getSchedule.ts | 11 + .../helpers/invalidateScheduleInCache.ts | 5 + .../helpers/resetAllSchedulesQueries.ts | 6 + .../helpers/upsertScheduleToCache.ts | 23 + .../scheduler/api/ScheduleApi/index.tsx | 9 + .../ScheduleApi/queries/useDeleteSchedule.ts | 16 + .../api/ScheduleApi/queries/useGetSchedule.ts | 18 + .../ScheduleApi/queries/useGetSchedules.ts | 13 + .../ScheduleAppointmentApi.ts | 119 + .../helpers/addScheduleAppointmentToCache.ts | 64 + .../invalidateSchedulerAppointmentsCache.ts | 11 + .../invalidateSchedulerStatisticsCache.ts | 7 + .../invalidateSchedulerTotalVisitsCache.ts | 11 + .../updateScheduleAppointmentInCache.ts | 90 + .../api/ScheduleAppointmentApi/index.tsx | 14 + .../queries/useGetFullScheduleAppointments.ts | 24 + .../useGetInfiniteScheduleAppointments.ts | 25 + .../queries/useGetLastScheduleAppointment.ts | 18 + .../queries/useGetScheduleAppointment.ts | 9 + .../queries/useGetScheduleAppointmentCount.ts | 17 + .../queries/useGetScheduleAppointments.ts | 17 + .../useGetScheduleAppointmentsStatistics.ts | 21 + .../queries/useGetSchedulerTotalVisits.ts | 10 + .../ScheduleAppointmentCardListApi.tsx | 32 + .../scheduler/api/SchedulerApiRoutes.ts | 19 + .../scheduler/api/SchedulerQueryKeys.ts | 38 + .../api/dtos/Schedule/CreateScheduleDto.ts | 60 + .../api/dtos/Schedule/ScheduleDto.ts | 21 + .../api/dtos/Schedule/UpdateScheduleDto.ts | 60 + .../CreateScheduleAppointmentDto.ts | 54 + .../ScheduleAppointmentDto.ts | 23 + .../ScheduleAppointmentEntityInfoDto.ts | 14 + .../ScheduleAppointmentResultDto.ts | 7 + .../ScheduleAppointmentStatisticsDto.ts | 12 + .../UpdateScheduleAppointmentDto.ts | 65 + .../CreateSchedulePerformerDto.ts | 22 + .../SchedulePerformer/SchedulePerformerDto.ts | 9 + .../UpdateSchedulePerformerDto.ts | 22 + .../src/modules/scheduler/api/dtos/index.tsx | 12 + frontend/src/modules/scheduler/api/index.tsx | 4 + frontend/src/modules/scheduler/index.tsx | 21 + .../AppointmentCardListPage.tsx | 369 + .../SchedulerBoardViewPage.tsx | 496 + .../SchedulerBoardComponent.tsx | 190 + .../SchedulerBoardHeader.tsx | 98 + .../SchedulerBoardSecondaryHeader.tsx | 86 + .../components/StatsFooter/StatsFooter.tsx | 138 + .../components/index.tsx | 4 + .../pages/SchedulerPage/SchedulerPage.tsx | 212 + .../SchedulerSettingsButton.tsx | 128 + .../pages/SchedulerPage/components/index.tsx | 1 + .../SchedulerScheduleViewPage.tsx | 444 + .../AppointmentEventComponent.tsx | 163 + .../PerformerResourceLabel.tsx | 89 + .../VisitsScheduler/VisitsScheduler.tsx | 186 + .../VisitsSchedulerComponent.styles.tsx | 112 + .../VisitsSchedulerComponent.tsx | 178 + .../VisitsSchedulerToolbar.tsx | 86 + .../components/index.tsx | 1 + .../src/modules/scheduler/pages/index.tsx | 1 + .../modules/scheduler/shared/assets/index.tsx | 3 + .../modules/scheduler/shared/assets/info.svg | 5 + .../scheduler/shared/assets/settings.svg | 12 + .../modules/scheduler/shared/assets/to.svg | 8 + .../src/modules/scheduler/shared/index.tsx | 2 + .../AddAppointment/AddAppointmentControl.tsx | 179 + .../AddAppointment/AddAppointmentDrawer.tsx | 202 + .../AddAppointment/AddAppointmentModal.tsx | 207 + .../AddAppointmentDrawerControls.tsx | 88 + .../AddAppointmentDrawerHeader.tsx | 21 + .../AddAppointmentWarningModal.tsx | 31 + .../AppointmentBlock/AppointmentBlock.tsx | 80 + .../AppointmentDateAndTimeFormItem.tsx | 209 + .../AppointmentDuplicateWarningModal.tsx | 88 + .../AppointmentEntityBlock.tsx | 269 + .../AppointmentEntityBlockHeader.tsx | 172 + .../AppointmentFormItem.tsx | 29 + .../AppointmentIntersectWarningModal.tsx | 31 + .../AppointmentServiceBlockItemTable.tsx | 46 + .../AppointmentServicesBlock.tsx | 111 + .../AppointmentServicesBlockItem.tsx | 65 + .../AppointmentServicesSearchBlock.tsx | 262 + .../AppointmentVisitParameters.tsx | 220 + .../AppointmentsTabsListHeader.tsx | 226 + .../AppointmentsTabsListHeaderComponent.tsx | 237 + .../CompletedAppointmentsCount.tsx | 55 + .../RepeatingAppointmentsIntervalBlock.tsx | 80 + .../RepeatingAppointmentsListBlock.tsx | 30 + .../RepeatingAppointmentsDateItem.tsx | 116 + .../RepeatingAppointmentsDatesSelect.tsx | 194 + .../components/index.tsx | 1 + .../AppointmentServiceAmountCell.tsx | 26 + .../AppointmentsHistoryServicesCell.tsx | 70 + .../AppointmentHistoryServicesTable.tsx | 56 + .../components/index.tsx | 1 + .../AddAppointment/components/index.tsx | 12 + .../AppointmentGeneralInformation.tsx | 170 + .../PreviousAppointmentsInfoDisplay.tsx | 92 + .../components/index.tsx | 1 + .../AppointmentPlannedVisits.tsx | 106 + .../AppointmentPlannedVisit.tsx | 237 + .../components/index.tsx | 1 + .../AppointmentVisitsHistoryTable.tsx | 138 + .../AppointmentEventHoverCard.tsx | 253 + .../AppointmentEventHoverCardBlock.tsx | 39 + .../AppointmentEventHoverCardEntityBlock.tsx | 71 + .../AppointmentEventHoverCardFormGroup.tsx | 54 + .../AppointmentEventHoverCardService.tsx | 41 + .../components/index.tsx | 4 + .../LocalTimeWarning/LocalTimeWarning.tsx | 49 + .../AppointmentCell/AppointmentCell.tsx | 202 + .../AppointmentIndexCell.tsx | 16 + .../AppointmentPeriodHeaderCell.tsx | 10 + .../UserOrDepartmentViewCell.tsx | 57 + .../SchedulerSearchBlock.tsx | 112 + .../SchedulerStatsSettingsDrawer.tsx | 102 + .../StatsIndicator/StatsIndicator.tsx | 65 + .../scheduler/shared/lib/components/index.tsx | 16 + .../generateSlotsMap.ts | 47 + .../generateTimePeriodOptions.ts | 37 + .../generateAppointmentEvents.ts | 15 + .../helpers/generatePerformersResources.ts | 23 + .../generateSchedulerLinkedEntityTypeTab.ts | 4 + .../lib/helpers/getAppointmentStatusColor.ts | 17 + .../shared/lib/helpers/getOrderItemAmount.ts | 10 + .../scheduler/shared/lib/helpers/index.tsx | 10 + .../useGetSchedulerBoardPageColumns.tsx | 110 + .../useGetSchedulerBoardPageRows.ts | 100 + .../scheduler/shared/lib/hooks/index.tsx | 11 + .../useAppointmentHistoryServicesColumns.tsx | 84 + .../useAppointmentServiceBlockColumns.tsx | 87 + .../hooks/useAppointmentsHistoryColumns.tsx | 142 + .../useGetBoardSchedulerBusinessHours.ts | 74 + ...useGetRepeatingAppointmentsDatesDisplay.ts | 60 + ...GetRepeatingAppointmentsIntervalOptions.ts | 33 + .../useGetScheduleAppointmentStatuses.ts | 50 + .../useGetSchedulerPerformersBusinessHours.ts | 171 + .../lib/hooks/useSchedulerStatisticsFilter.ts | 52 + .../modules/scheduler/shared/lib/index.tsx | 5 + .../AppointmentHistoryColumnsIds.ts | 8 + .../AppointmentHistoryColumnsSizes.ts | 17 + .../AppointmentHistoryServicesColumnsIds.ts | 7 + .../AppointmentHistoryServicesColumnsSizes.ts | 12 + .../AppointmentServiceBlockColumnsIds.ts | 6 + .../AppointmentServiceBlockColumnsSizes.ts | 15 + .../models/RepeatingAppointmentsParameters.ts | 157 + .../Schedule/GetSchedulesQueryParams.ts | 3 + .../shared/lib/models/Schedule/Schedule.ts | 153 + .../lib/models/Schedule/SchedulePerformer.ts | 40 + .../models/Schedule/SchedulePerformerType.ts | 4 + .../lib/models/Schedule/ScheduleType.ts | 4 + .../AddAppointmentPreset.ts | 9 + .../GetScheduleAppointmentCountQueryParams.ts | 10 + .../GetScheduleAppointmentsQueryParams.ts | 23 + .../ScheduleAppointment.ts | 87 + .../ScheduleAppointmentDuplicateError.ts | 9 + .../ScheduleAppointmentEntityInfo.ts | 65 + .../ScheduleAppointmentIntersectError.ts | 5 + .../ScheduleAppointmentOrderItemRow.ts | 98 + .../ScheduleAppointmentResult.ts | 17 + .../ScheduleAppointmentStatisticsType.ts | 9 + .../ScheduleAppointmentStatus.ts | 6 + .../ScheduleAppointmentsLimit.ts | 1 + .../ScheduleBoardAppointmentRow.ts | 5 + .../ScheduleAppointmentModalTabs.ts | 5 + .../lib/models/SchedulerCardListSettings.ts | 8 + .../models/SchedulerEvent/SchedulerEvent.ts | 11 + .../models/SchedulerEvent/SchedulerEvents.ts | 5 + .../shared/lib/models/SchedulerQueryParams.ts | 5 + .../models/SchedulerScheduleViewSettings.ts | 8 + .../SchedulerStatisticsFilterSettings.ts | 8 + .../lib/models/SchedulersBoardViewSettings.ts | 10 + .../lib/models/VisitParametersFormData.ts | 161 + .../scheduler/shared/lib/models/index.tsx | 36 + .../types/CreateBoardAppointmentHandler.ts | 9 + .../types/RepeatingAppointmentDateValue.ts | 6 + .../lib/types/RepeatingAppointmentInterval.ts | 6 + .../types/ScheduleAppointmentsExpandParam.ts | 6 + .../scheduler/shared/lib/types/index.tsx | 4 + .../store/AddAppointmentModalStore.ts | 546 + .../store/AppointmentCardListPageStore.ts | 153 + .../store/AppointmentEventHoverCardStore.ts | 127 + .../scheduler/store/AppointmentOrderStore.ts | 246 + .../store/SchedulerEventHandlerStore.ts | 48 + .../src/modules/scheduler/store/index.tsx | 5 + .../EntitiesImportApi/EntitiesImportApi.ts | 25 + .../section/api/EntityApi/EntityApi.ts | 345 + .../modules/section/api/SectionApiRoutes.ts | 25 + .../section/api/dtos/CommonEntityCardDto.ts | 16 + .../api/dtos/Entity/CreateSimpleEntityDto.ts | 46 + .../api/dtos/Entity/DeleteEntitiesBatchDto.ts | 37 + .../api/dtos/Entity/EntityListItemDto.ts | 16 + .../Entity/FullEntitiesSearchResultDto.ts | 7 + .../api/dtos/Entity/SearchByFieldFilter.ts | 15 + .../api/dtos/Entity/SearchByValueResultDto.ts | 6 + .../api/dtos/Entity/UpdateEntitiesBatchDto.ts | 46 + .../api/dtos/Entity/UpdateEntityDto.ts | 32 + .../api/dtos/EntityBoardCardDataDto.ts | 4 + .../section/api/dtos/EntityBoardCardDto.ts | 33 + .../section/api/dtos/ProjectEntityCardDto.ts | 17 + .../src/modules/section/api/dtos/index.tsx | 12 + frontend/src/modules/section/api/index.tsx | 4 + frontend/src/modules/section/index.tsx | 4 + .../EntitiesBoardSettingsPage.tsx | 129 + .../TasksBoardSettingsPage.tsx | 103 + .../BoardSettings/BoardSettings.tsx | 179 + .../BoardSettingsHeader.tsx | 144 + .../DeleteStageWarningModal.tsx | 51 + .../components/StageColumn/StageColumn.tsx | 255 + .../BoardSettingsPage/components/index.tsx | 2 + .../EntitiesListAutomationPage.tsx | 110 + .../EntitiesListPage/EntitiesListPage.tsx | 251 + .../pages/EntitiesPage/EntitiesPage.tsx | 542 + .../CardsTotalBlock/CardsTotalBlock.tsx | 88 + .../ReportsSettingsButton.tsx | 28 + .../pages/EntitiesPage/components/index.tsx | 2 + .../pages/EverythingPage/EverythingPage.tsx | 220 + .../ProjectsTimelinePage.tsx | 144 + frontend/src/modules/section/pages/index.tsx | 7 + .../shared/assets/automation_bpmn_tab.svg | 39 + .../shared/assets/automation_new_tab.svg | 17 + .../modules/section/shared/assets/close.svg | 5 + .../modules/section/shared/assets/delete.svg | 5 + .../shared/assets/download_example.svg | 5 + .../section/shared/assets/drag_indicator.svg | 5 + .../modules/section/shared/assets/excel.svg | 15 + .../modules/section/shared/assets/import.svg | 5 + .../modules/section/shared/assets/index.tsx | 12 + .../section/shared/assets/manage_accounts.svg | 5 + .../section/shared/assets/responsible.svg | 5 + .../modules/section/shared/assets/stage.svg | 5 + .../modules/section/shared/assets/upload.svg | 5 + frontend/src/modules/section/shared/index.tsx | 2 + .../AddCardButton/AddCardButton.tsx | 81 + .../EntitiesBoard/EntitiesBoard.tsx | 164 + .../components/CardItemCommon.styles.tsx | 89 + .../EntitiesColumn/EntitiesColumn.styles.tsx | 178 + .../EntitiesColumn/EntitiesColumn.tsx | 181 + .../CommonEntityCardItem.styles.tsx | 105 + .../EntityCardItem/CommonEntityCardItem.tsx | 133 + .../CommonEntityCardItemPriceBlock.tsx | 29 + .../ProjectEntityCardItem.styles.tsx | 25 + .../ProjectEntityCardItem.tsx | 136 + .../components/DateBlock/DateBlock.tsx | 39 + .../ParticipantsBlock/ParticipantsBlock.tsx | 135 + .../TaskIndicator/TaskIndicator.tsx | 44 + .../components/index.tsx | 3 + .../EntitiesBoard/components/index.tsx | 1 + .../EntitiesFilterButton.tsx | 110 + .../EntitiesFilterDrawer.tsx | 474 + .../FieldFilterItems/FieldFilterBoolean.tsx | 74 + .../FieldFilterItems/FieldFilterDate.tsx | 71 + .../FieldFilterItems/FieldFilterExists.tsx | 69 + .../FieldFilterItems/FieldFilterNumber.tsx | 86 + .../FieldFilterParticipants.tsx | 39 + .../FieldFilterItems/FieldFilterSelect.tsx | 92 + .../FieldFilterItems/FieldFilterString.tsx | 103 + .../FieldsFilterItemSwitch.tsx | 186 + .../FilterSystemStagesBlock.tsx | 24 + .../EntitiesFilterButton/components/index.tsx | 2 + .../components/EntitiesList/EntitiesList.tsx | 133 + .../components/BatchActions/BatchActions.tsx | 235 + .../BatchActions/BatchActionsCloseButton.tsx | 46 + .../BatchActions/ChangeResponsibleAction.tsx | 140 + .../BatchActions/ChangeStageAction.tsx | 80 + .../components/BatchActions/DeleteAction.tsx | 45 + .../ActionModalBlock/ActionModalBlock.tsx | 21 + .../components/BatchAction/BatchAction.tsx | 87 + .../LinkedEntitiesSelect.tsx | 76 + .../SelectWrapper/SelectWrapper.tsx | 5 + .../BatchActions/components/index.tsx | 4 + .../DraggableColumnHeader.tsx | 214 + .../EntitiesListAutomationColumn.tsx | 57 + .../components/SectionTable/SectionTable.tsx | 457 + .../SectionTable/SectionTableComponent.tsx | 221 + .../SectionTableSettingsDrawer.tsx | 97 + .../SelectAllCheckbox/SelectAllCheckbox.tsx | 112 + .../components/cells/FieldCell/FieldCell.tsx | 68 + .../FieldUnavailable/FieldUnavailable.tsx | 32 + .../components/cells/NameCell/NameCell.tsx | 141 + .../components/cells/OwnerCell/OwnerCell.tsx | 58 + .../components/cells/StageCell/StageCell.tsx | 60 + .../EntitiesList/components/index.tsx | 13 + .../components/SearchBlock/SearchBlock.tsx | 20 + .../components/SearchBox/SearchBox.tsx | 249 + .../EntitySearchItem/EntitySearchItem.tsx | 187 + .../EntitySearchList/EntitySearchList.tsx | 45 + .../LoadMoreButton/LoadMoreButton.tsx | 59 + .../components/SearchBox/components/index.tsx | 2 + .../SearchBlock/components/index.tsx | 1 + .../SectionHeader/SectionHeaderControls.tsx | 31 + .../SectionHeaderControlsSkeleton.tsx | 9 + .../SectionHeader/components/index.tsx | 1 + .../SectionHeaderCenterBlock.tsx | 8 + .../SectionHeaderWithBoardsControls.tsx | 43 + .../SectionSettingsButton.tsx | 188 + .../Buttons/DownloadExampleButton.tsx | 81 + .../components/Buttons/IconWrapper.tsx | 10 + .../Buttons/ImportEntitiesButton.tsx | 30 + .../Buttons/UploadImportFileButton.tsx | 145 + .../components/GearIcon/GearIcon.tsx | 14 + .../components/IconWrapper/IconWrapper.tsx | 7 + .../ImportEntitiesModal.tsx | 169 + .../ImportFileBlock/ImportFileBlock.tsx | 88 + .../ImportInBackgroundInfoModal.tsx | 41 + .../components/index.tsx | 5 + .../SwipeableContainer/SwipeableContainer.tsx | 15 + .../section/shared/lib/components/index.tsx | 15 + .../getCreatedAtFilter.ts | 28 + .../getEntitiesFinalSortingValue.ts | 10 + .../getEntitiesSortingOptions.ts | 26 + .../getEntityFieldFilter.ts | 144 + .../getFieldFilterFormModel.ts | 130 + ...nerateEntitiesListSettingsObjStorageKey.ts | 15 + .../SectionTable/generateFieldColumnId.ts | 1 + .../SectionTable/getDefaultEtColumnSize.ts | 28 + .../lib/helpers/SectionTable/getFieldName.ts | 24 + .../section/shared/lib/helpers/index.tsx | 9 + .../SectionTable/useGetSectionTableData.ts | 44 + .../SectionTable/useSectionTableColumns.tsx | 266 + .../section/shared/lib/hooks/index.tsx | 10 + .../useEntitiesBoardSettingsPageTabs.tsx | 53 + .../useEntitiesListAutomationPageTabs.tsx | 28 + .../src/modules/section/shared/lib/index.tsx | 6 + .../EntitiesFilterDrawerAsyncStateProps.ts | 8 + .../EntityCardsFilterSettings.tsx | 10 + .../EntitiesSettings/EntityListSettings.ts | 12 + .../lib/models/Entity/CommonEntityCard.ts | 73 + .../lib/models/Entity/EntityBoardCard.ts | 105 + .../lib/models/Entity/EntityBoardCardData.ts | 4 + .../shared/lib/models/Entity/EntityForm.ts | 105 + .../lib/models/Entity/EntityListItem.ts | 88 + .../lib/models/Entity/EntitySearchFilter.ts | 36 + .../models/Entity/FullEntitiesSearchResult.ts | 19 + .../lib/models/Entity/ProjectEntityCard.ts | 45 + .../lib/models/Entity/SearchByFieldResult.ts | 6 + .../lib/models/Entity/SearchByValueResult.ts | 19 + .../shared/lib/models/Entity/TasksCount.ts | 13 + .../EntityFieldFilters/EntityFieldFilter.ts | 7 + .../EntityFieldFilterDate.ts | 9 + .../EntityFieldFilterNumber.ts | 9 + .../EntityFieldFilterString.ts | 12 + .../EntityFieldFilters/StringFilterType.ts | 5 + .../EntityFilter/EntityBoardCardFilter.ts | 38 + .../lib/models/EntityFilter/EntitySorting.ts | 7 + .../models/EntityFilter/EntityTaskFilter.ts | 7 + .../shared/lib/models/EntitySearchModel.ts | 32 + .../models/SectionTable/EtTableSettings.ts | 11 + .../models/SectionTable/EtTablesSettings.ts | 5 + .../SectionTableColumnsIds.ts | 6 + .../SectionTableColumnsSizes.ts | 17 + .../models/SectionTable/SectionTableRow.ts | 14 + .../shared/lib/models/TaskIndicator.ts | 6 + .../shared/lib/models/TaskIndicatorColor.ts | 8 + .../section/shared/lib/models/index.tsx | 29 + .../FieldFilterFormType.ts | 1 + .../FieldFilterModelState.ts | 1 + .../shared/lib/types/MinMaxColumnSize.ts | 1 + .../section/shared/lib/types/index.tsx | 3 + .../shared/lib/utils/EntitiesPageUtil.tsx | 132 + .../section/shared/lib/utils/EntityApiUtil.ts | 90 + .../section/shared/lib/utils/index.tsx | 2 + .../section/store/BoardSettingsStore.ts | 135 + .../section/store/EntitiesCardStore.ts | 487 + .../section/store/EntitiesFilterStore.ts | 14 + .../section/store/EntitiesImportStore.ts | 45 + .../section/store/EntitiesListPageStore.ts | 265 + .../store/EntitiesListSettingsStore.ts | 63 + .../store/ProjectsTimelinePageStore.ts | 305 + frontend/src/modules/section/store/index.tsx | 7 + .../AccountApiAccessApi.ts | 26 + .../queries/useCreateAccountApiAccess.ts | 23 + .../queries/useGetAccountApiAccess.ts | 9 + .../queries/useRecreateAccountApiAccess.ts | 23 + .../BillingLifetimePromoApi.ts | 35 + .../useGetBillingLifetimePromoPrices.ts | 9 + .../DepartmentsSettingsApi.ts | 56 + .../queries/useGetDepartmentSettings.ts | 24 + .../queries/useGetDepartmentsSettings.ts | 19 + .../DocumentTemplateApi.ts | 53 + .../GoogleCalendarIntegrationApi.ts | 78 + ...lidateGoogleCalendarIntegrationsInCache.ts | 5 + .../useGetGoogleCalendarIntegrations.ts | 11 + .../SalesforceProviderSettingsApi.ts | 37 + .../modules/settings/api/SettingsApiRoutes.ts | 42 + .../modules/settings/api/SettingsQueryKeys.ts | 25 + .../api/UserCalendarApi/UserCalendarApi.ts | 53 + .../api/UserTokensApi/UserTokensApi.ts | 29 + .../queries/useCreateUserAccessToken.ts | 22 + .../queries/useDeleteUserAccessToken.ts | 22 + .../queries/useGetUserAccessTokens.ts | 9 + .../settings/api/dtos/AccountApiAccessDto.ts | 4 + .../api/dtos/Calendar/CalendarAccessDto.ts | 6 + .../api/dtos/Calendar/CalendarInfoDto.ts | 9 + .../dtos/Calendar/CreateGoogleCalendarDto.ts | 40 + .../api/dtos/Calendar/GoogleCalendarDto.ts | 17 + .../dtos/Calendar/GoogleCalendarLinkedDto.ts | 6 + .../dtos/Calendar/UpdateGoogleCalendarDto.ts | 34 + .../settings/api/dtos/CurrentDiscountDto.ts | 7 + .../CreateDepartmentDto.ts | 11 + .../dtos/DepartmentsSettings/DepartmentDto.ts | 18 + .../DepartmentSettingsDto.ts | 11 + .../UpdateDepartmentDto.ts | 12 + .../dtos/DocumentTemplate/CheckDocumentDto.ts | 13 + .../CheckDocumentMissingFieldDto.ts | 11 + .../CheckDocumentResultDto.ts | 13 + .../DocumentTemplate/CreateDocumentDto.ts | 16 + .../CreateDocumentTemplateDto.ts | 13 + .../DocumentTemplate/DocumentTemplateDto.ts | 10 + .../DocumentTemplate/DocumentTemplateInfo.ts | 4 + .../UpdateDocumentTemplateDto.ts | 13 + .../Salesforce/CreateSalesforceSettingsDto.ts | 11 + .../api/dtos/UserCalendar/UserCalendarDto.ts | 8 + .../UserCalendar/UserCalendarIntervalDto.ts | 7 + .../api/dtos/UserToken/CreateUserTokenDto.ts | 4 + .../api/dtos/UserToken/UserAccessTokenDto.ts | 6 + .../api/dtos/UserToken/UserTokenDto.ts | 7 + .../src/modules/settings/api/dtos/index.tsx | 27 + frontend/src/modules/settings/api/index.tsx | 17 + frontend/src/modules/settings/index.tsx | 6 + .../AccountApiAccessPage.tsx | 23 + .../ApiAccessBlock/ApiAccessBlock.tsx | 201 + .../ApiTokensList/ApiTokensList.tsx | 102 + .../CreateUserTokenBlock.tsx | 161 + .../AccountApiAccessPage/components/index.tsx | 2 + .../pages/BillingPage/CommonBillingPage.tsx | 48 + .../MyworkRequestInvoiceBillingPage.tsx | 76 + .../pages/BillingPage/StripeBillingPage.tsx | 104 + .../BillingLifetimePromoPlans.tsx | 116 + .../AnnualDealPricingBlock.tsx | 43 + .../LifetimeDealPlanBlock.tsx | 86 + .../MonthlyDealPricingBlock.tsx | 225 + .../MyworkBillingPromoPlans.styles.tsx | 76 + .../PriceRiseCounter/PriceRiseCounter.tsx | 189 + .../PricingBlockWithUsersSelector.tsx | 395 + .../components/index.tsx | 5 + .../CurrentSubscriptionBlock.tsx | 133 + .../MyworkRequestInvoiceForm.tsx | 504 + .../MyworkInvoicePdf.styles.ts | 114 + .../MyworkInvoicePdf/MyworkInvoicePdf.tsx | 315 + .../components/index.tsx | 1 + .../PaymentResultModal/PaymentResultModal.tsx | 40 + .../pages/BillingPage/components/index.tsx | 4 + .../DocumentCreationFieldsPage.tsx | 119 + .../EntityTypeSection/EntityTypeSection.tsx | 179 + .../components/FieldBlock/FieldBlock.tsx | 59 + .../components/FieldBlock/FieldBlockRoot.tsx | 11 + .../components/FieldBlock/FieldName.tsx | 9 + .../components/FieldBlock/OrderFieldBlock.tsx | 49 + .../FieldBlock/SystemFieldBlock.tsx | 46 + .../FieldCodeBlock/FieldCodeBlock.tsx | 37 + .../FieldGroupSection/FieldGroupSection.tsx | 48 + .../FieldsGroupWrapper/FieldsGroupWrapper.tsx | 22 + .../NumberToWordSelector.tsx | 156 + .../OrderFields/OrderSection/OrderSection.tsx | 167 + .../OrderSectionExampleWrapper.tsx | 57 + .../OrderSectionTableExample.tsx | 93 + .../OrderSectionTextParagraph.tsx | 17 + .../ProjectFieldsBlock/ProjectFieldsBlock.tsx | 74 + .../RussianCaseSelector.tsx | 171 + .../SectionTemplate/SectionTemplate.tsx | 62 + .../SystemSection/SystemSection.tsx | 53 + .../components/index.tsx | 3 + .../DocumentTemplatesPage.tsx | 106 + .../AddDocumentTemplateButton.tsx | 74 + .../DocumentTemplateItem.tsx | 236 + .../DocumentTemplateItemSkeleton.tsx | 10 + .../components/index.tsx | 3 + .../GeneralSettingsPage.tsx | 71 + .../GeneralSettingsPageFormGroup.tsx | 34 + .../GeneralSettingsPageMainBlock.tsx | 217 + .../GeneralSettingsPageTopBlock.tsx | 141 + .../SettingsBlock/SettingsBlock.tsx | 17 + .../GeneralSettingsPage/components/index.tsx | 4 + .../GoogleCalendarRedirectPage.tsx | 25 + .../IntegrationsPage/IntegrationsPage.tsx | 152 + .../AccountActivityFormGroup.tsx | 50 + .../AccountAvailableFormGroup.tsx | 92 + .../ChangesNotSavedWarningModal.tsx | 29 + .../EntitySettingsFormGroup.tsx | 190 + .../IntegrationAccountItem.tsx | 120 + .../IntegrationAccountsList.tsx | 36 + .../IntegrationForm/IntegrationForm.tsx | 25 + .../IntegrationFormGroup.tsx | 41 + .../IntegrationInfoFeatureList.tsx | 48 + .../IntegrationInfoLink.tsx | 72 + .../IntegrationInfoModalTemplate.tsx | 71 + .../IntegrationInfoText.tsx | 22 + .../IntegrationInfoTitle.tsx | 8 + .../IntegrationItem/IntegrationItem.tsx | 165 + .../IntegrationManageModalTemplate.tsx | 36 + .../IntegrationOrderedList.styles.tsx | 27 + .../IntegrationRadioItem.tsx | 49 + .../Albato/AlbatoItem/AlbatoItem.tsx | 33 + .../AlbatoManualModal/AlbatoManualModal.tsx | 82 + .../ApixDrive/ApixDriveItem/ApixDriveItem.tsx | 28 + .../ApixDriveManualModal.tsx | 215 + .../FbMessengerItem/FbMessengerItem.tsx | 45 + .../FbMessengerConnectModal.tsx | 183 + .../FbMessengerFirstInfoModal.tsx | 53 + .../FbMessengerManageModal.tsx | 65 + .../FbMessengerModalTemplate.tsx | 40 + .../FbMessengerModalsQueue.tsx | 93 + .../GoogleCalendarItem/GoogleCalendarItem.tsx | 44 + .../GoogleCalendarConnectModal.tsx | 345 + .../GoogleCalendarConnectModalForm.tsx | 205 + .../GoogleCalendarLinkedItemsGroup.tsx | 104 + .../GoogleCalendarResponsibleFormGroup.tsx | 69 + .../components/index.tsx | 1 + .../GoogleCalendarManageModal.tsx | 68 + .../GoogleCalendarModalTemplate.tsx | 64 + .../GoogleCalendarModalsQueue.tsx | 49 + .../Integrations/Make/MakeItem/MakeItem.tsx | 28 + .../MakeManualModal/MakeManualModal.tsx | 111 + .../Integrations/OneC/OneCItem/OneCItem.tsx | 28 + .../OneCManualModal/OneCManualModal.tsx | 191 + .../RequestIntegrationItem.tsx | 35 + .../SalesforceItem/SalesforceItem.tsx | 81 + .../SalesforceModal/SalesforceModal.tsx | 171 + .../Tilda/TildaItem/TildaItem.tsx | 33 + .../TildaManualModal/TildaManualModal.tsx | 75 + .../TwilioWhatsAppItem/TwilioWhatsAppItem.tsx | 45 + .../TwilioWhatsAppConnectModal.tsx | 205 + .../TwilioWhatsAppFirstInfoModal.tsx | 37 + .../TwilioWhatsAppManageModal.tsx | 60 + .../TwilioWhatsAppModalTemplate.tsx | 53 + .../TwilioWhatsAppModalsQueue.tsx | 110 + .../TwilioWhatsAppSecondInfoModal.tsx | 68 + .../TwilioWhatsAppThirdInfoModal.tsx | 152 + .../Wazzup/WazzupItem/WazzupItem.tsx | 47 + .../WazzupConnectModal/WazzupConnectModal.tsx | 232 + .../WazzupFirstInfoModal.tsx | 57 + .../WazzupManageModal/WazzupManageModal.tsx | 59 + .../WazzupModalTemplate.tsx | 54 + .../WazzupModalsQueue/WazzupModalsQueue.tsx | 88 + .../Wordpress/WordpressItem/WordpressItem.tsx | 33 + .../WordpressManualModal.tsx | 95 + .../IntegrationsGroup/IntegrationsGroup.tsx | 52 + .../MessagesPerDaySelect.tsx | 107 + .../ProviderSipRegistrationItem.tsx | 44 + .../ProvidersSipRegistrationItemsList.tsx | 194 + .../SipRegistrationGuideModal.tsx | 100 + .../TelephonyIntegrationGuide.tsx | 29 + ...TelephonyIntegrationGuideInternational.tsx | 116 + .../TelephonyIntegrationGuideRU.tsx | 175 + .../IntegrationsPage/components/index.tsx | 16 + .../pages/SuperadminPage/SuperadminPage.tsx | 62 + .../components/AccountBlock/AccountBlock.tsx | 101 + .../AccountSearchBox/AccountSearchBox.tsx | 205 + .../AccountSearchBox/AccountSearchItem.tsx | 81 + .../AccountSearchBox/AccountSearchList.tsx | 44 + .../SubscriptionEditModal.tsx | 137 + .../pages/SuperadminPage/components/index.tsx | 3 + .../EditDepartmentsPage.tsx | 102 + .../DeleteDepartmentWarningModal.tsx | 177 + .../DepartmentBlock/DepartmentBlock.tsx | 314 + .../SubdepartmentItem/SubdepartmentItem.tsx | 235 + .../SubdepartmentsList/SubdepartmentsList.tsx | 52 + .../EditDepartmentsPage/components/index.tsx | 1 + .../UsersPage/EditUserPage/EditUserPage.tsx | 596 + .../EditUserFormGroup/EditUserFormGroup.tsx | 40 + .../EditUserPageGrid/EditUserPageGrid.tsx | 9 + .../ObjectPermissionsList.tsx | 111 + .../PermissionItem/ObjectPermissionsItem.tsx | 423 + .../ProductsPermissionsItem.tsx | 247 + .../PermissionItemColumnTitle.tsx | 11 + .../PermissionItemHeader.tsx | 50 + .../PermissionItemRowTitle.tsx | 11 + .../WarehousesPermissionsBlock.tsx | 50 + .../PermissionItem/components/index.tsx | 4 + .../EditUserPage/components/index.tsx | 3 + .../UsersSettingsPage/UsersSettingsPage.tsx | 130 + .../DepartmentBlock/DepartmentBlock.tsx | 95 + .../components/HeaderButton/HeaderButton.tsx | 43 + .../RemoveUserModal/RemoveUserModal.tsx | 84 + .../components/Skeleton/UsersPageSkeleton.tsx | 54 + .../SubdepartmentBlock/SubdepartmentBlock.tsx | 67 + .../components/UserItem/UserItem.tsx | 117 + .../components/UserList/UserList.tsx | 21 + .../UserSettingsPageTitle.tsx | 19 + .../UsersSettingsPage/components/index.tsx | 5 + frontend/src/modules/settings/pages/index.tsx | 14 + .../modules/settings/shared/assets/albato.svg | 5 + .../settings/shared/assets/albato_small.svg | 6 + .../settings/shared/assets/apix_drive.svg | 1916 ++ .../shared/assets/apix_drive_small.svg | 1 + .../settings/shared/assets/beeline.svg | 29 + .../shared/assets/fb_messenger_large.svg | 20 + .../shared/assets/fb_messenger_small.svg | 20 + .../settings/shared/assets/feature.svg | 5 + .../shared/assets/google_calendar.svg | 19 + .../shared/assets/google_calendar_small.svg | 17 + .../modules/settings/shared/assets/index.tsx | 34 + .../modules/settings/shared/assets/key.svg | 34 + .../modules/settings/shared/assets/make.svg | 22 + .../settings/shared/assets/make_small.svg | 14 + .../settings/shared/assets/mango_office.svg | 35 + .../settings/shared/assets/megafon.svg | 11 + .../modules/settings/shared/assets/mgts.svg | 11 + .../modules/settings/shared/assets/mts.svg | 10 + .../modules/settings/shared/assets/one_c.svg | 9 + .../settings/shared/assets/price_label.svg | 31 + .../settings/shared/assets/rostelecom.svg | 11 + .../settings/shared/assets/salesforce.svg | 9 + .../modules/settings/shared/assets/tele2.svg | 5 + .../shared/assets/telephony_small.svg | 15 + .../modules/settings/shared/assets/tilda.svg | 36 + .../settings/shared/assets/tilda_small.svg | 7 + .../modules/settings/shared/assets/uis.svg | 5 + .../settings/shared/assets/unknown.svg | 27 + .../settings/shared/assets/wazzup_large.svg | 20 + .../settings/shared/assets/wazzup_small.svg | 9 + .../settings/shared/assets/whatsapp_large.svg | 15 + .../settings/shared/assets/whatsapp_small.svg | 25 + .../settings/shared/assets/wordpress.svg | 31 + .../shared/assets/wordpress_small.svg | 6 + .../settings/shared/assets/zadarma.svg | 39 + .../src/modules/settings/shared/index.tsx | 2 + .../RequestSetupFormButton.tsx | 33 + .../RequestSetupFormModal.tsx | 170 + .../RequestSetupForm/components/index.tsx | 1 + .../SettingsPageTitle/SettingsPageTitle.tsx | 9 + .../SettingsSidebar/SettingsSidebar.tsx | 27 + .../SettingsSidebarItem.tsx | 106 + .../SettingsSidebarItemGroup.tsx | 48 + .../settings/shared/lib/components/index.tsx | 7 + .../OrderFields/generateOrderItemsFields.ts | 43 + .../generateOrderSpecificItemFields.ts | 43 + .../OrderFields/generateOrderSystemFields.ts | 19 + .../convertPriceToRussianLocaleString.ts | 25 + .../lib/helpers/getAllTimezoneOptions.ts | 35 + .../lib/helpers/getCurrenciesOptions.ts | 8 + .../shared/lib/helpers/getLanguageOptions.ts | 13 + .../lib/helpers/getPhoneFormatOptions.ts | 9 + .../lib/helpers/getStatusNameWithHint.tsx | 49 + .../lib/helpers/getWorkingDaysOptions.ts | 12 + .../settings/shared/lib/helpers/index.tsx | 12 + .../helpers/removeFileExtensionFromName.ts | 5 + .../shared/lib/helpers/removeSpecialChars.ts | 7 + .../lib/hooks/Billing/useAppSumoTierData.ts | 43 + .../hooks/Billing/useGetCurrentDiscount.ts | 9 + .../UserToken/useUserTokenTableColumns.tsx | 72 + .../settings/shared/lib/hooks/index.tsx | 4 + .../lib/hooks/useGetDateFormatOptions.ts | 23 + .../src/modules/settings/shared/lib/index.tsx | 5 + .../lib/models/Billing/AppSumoTierData.ts | 7 + .../models/Billing/LifetimeDealPricingPlan.ts | 27 + .../lib/models/Calendar/CalendarAccess.ts | 19 + .../lib/models/Calendar/CalendarInfo.ts | 37 + .../shared/lib/models/CurrentDiscount.ts | 22 + .../models/DepartmentSettings/Department.ts | 36 + .../DepartmentSettings/DepartmentSettings.ts | 16 + .../CheckEntityDocumentResult.ts | 22 + .../DocumentTemplate/DocumentMissingField.ts | 23 + .../lib/models/Documents/DocumentType.ts | 4 + .../shared/lib/models/Documents/OrderField.ts | 5 + .../lib/models/Documents/RussianCase.ts | 14 + .../lib/models/Documents/SystemFieldType.ts | 6 + .../lib/models/GeneralSettings/Account.ts | 44 + .../GeneralSettings/AccountApiAccess.ts | 19 + .../models/GeneralSettings/AccountSettings.ts | 62 + .../Calendar/CalendarLinkedProcessRadio.ts | 5 + .../Integrations/Calendar/CalendarType.ts | 4 + .../Integrations/Calendar/GoogleCalendar.ts | 64 + .../Calendar/GoogleCalendarLinked.ts | 20 + .../GoogleCalendarConnectModalInitialForm.ts | 13 + .../IntegrationSettingsAccount.ts | 10 + .../shared/lib/models/MyworkInvoicePdfData.ts | 25 + .../lib/models/ObjectPermissionModel.ts | 12 + .../models/Salesforce/SalesforceSettings.ts | 6 + .../lib/models/UserCalendar/UserCalendar.ts | 25 + .../UserCalendar/UserCalendarInterval.ts | 26 + .../lib/models/UserToken/UserAccessToken.ts | 19 + .../shared/lib/models/UserToken/UserToken.ts | 32 + .../settings/shared/lib/models/constants.ts | 17 + .../settings/shared/lib/models/index.tsx | 46 + .../lib/types/RequestSetupFormTitleKey.ts | 6 + .../settings/shared/lib/types/index.tsx | 1 + .../store/DepartmentsSettingsStore.ts | 154 + .../settings/store/DocumentTemplateStore.ts | 71 + .../modules/settings/store/EditUserStore.ts | 312 + .../settings/store/EntitySettingsStore.ts | 92 + .../FbMessengerConnectModalStore.ts | 93 + .../FbMessengerProviderSettingsStore.ts | 101 + .../SalesforceProviderSettingsStore.ts | 29 + .../TwilioWhatsAppConnectModalStore.ts | 126 + .../TwilioWhatsAppProviderSettingsStore.ts | 103 + .../settings/store/UserCalendarStore.ts | 81 + .../store/Wazzup/WazzupConnectModalStore.ts | 278 + .../Wazzup/WazzupProviderSettingsStore.ts | 64 + frontend/src/modules/settings/store/index.tsx | 11 + .../SettingsPageTemplate.tsx | 256 + .../src/modules/settings/templates/index.tsx | 1 + .../api/ActivityBoardApi/ActivityBoardApi.ts | 56 + .../api/ActivityTypeApi/ActivityTypeApi.ts | 42 + .../src/modules/tasks/api/TaskApi/TaskApi.ts | 188 + .../tasks/api/TaskBoardApi/TaskBoardApi.ts | 131 + .../api/TaskSettingsApi/TaskSettingsApi.ts | 36 + .../src/modules/tasks/api/TasksApiRoutes.ts | 49 + .../api/TasksCalendarApi/TasksCalendarApi.ts | 122 + .../helpers/addTaskToCalendarCache.ts | 44 + .../helpers/deleteTaskFromCalendarCache.ts | 44 + .../helpers/updateTaskInCalendarCache.ts | 59 + .../queries/useAddTaskToCalendar.ts | 32 + .../queries/useDeleteTaskFromCalendar.ts | 60 + .../queries/useGetActivitiesForCalendar.ts | 17 + .../queries/useGetTasksForCalendar.ts | 17 + .../queries/useGetTasksForCalendarCount.ts | 23 + .../useGetTimeBoardTasksForCalendar.ts | 17 + .../useGetTimeBoardTasksForCalendarCount.ts | 17 + .../queries/useUpdateActivityInCalendar.ts | 33 + .../queries/useUpdateTaskInCalendar.ts | 39 + .../src/modules/tasks/api/TasksQueryKeys.ts | 34 + .../tasks/api/TimeBoardApi/TimeBoardApi.ts | 59 + .../api/dtos/Activity/ActivityCardDto.ts | 5 + .../tasks/api/dtos/Activity/ActivityDto.ts | 7 + .../api/dtos/Activity/CreateActivityDto.ts | 6 + .../api/dtos/Activity/UpdateActivityDto.ts | 37 + .../api/dtos/ActivityType/ActivityTypeDto.ts | 6 + .../ActivityType/CreateActivityTypeDto.ts | 3 + .../ActivityType/UpdateActivityTypeDto.ts | 3 + .../tasks/api/dtos/BaseTask/BaseTaskDto.ts | 23 + .../api/dtos/BaseTask/CreateBaseTaskDto.ts | 9 + .../tasks/api/dtos/Task/CreateTaskDto.ts | 13 + .../api/dtos/Task/Subtask/CreateSubtaskDto.ts | 21 + .../api/dtos/Task/Subtask/UpdateSubtaskDto.ts | 24 + .../tasks/api/dtos/Task/TaskCardDto.ts | 10 + .../Task/TaskComments/CreateTaskCommentDto.ts | 11 + .../dtos/Task/TaskComments/TaskCommentDto.ts | 21 + .../Task/TaskComments/UpdateTaskCommentDto.ts | 7 + .../modules/tasks/api/dtos/Task/TaskDto.ts | 12 + .../tasks/api/dtos/Task/UpdateTaskDto.ts | 47 + .../dtos/TaskFilter/ActivityCardFilterDto.ts | 67 + .../dtos/TaskFilter/BaseTaskBoardFilterDto.ts | 50 + .../api/dtos/TaskFilter/TaskBoardFilterDto.ts | 67 + .../api/dtos/TaskFilter/TimeBoardFilterDto.ts | 68 + .../TasksForCalendarQueryParamsDto.ts | 9 + .../TaskSettings/CreateTaskSettingsDto.ts | 14 + .../api/dtos/TaskSettings/TaskSettingsDto.ts | 16 + .../TaskSettings/TaskSettingsIdentifier.ts | 31 + .../TaskSettings/UpdateTaskSettingsDto.ts | 9 + frontend/src/modules/tasks/api/dtos/index.tsx | 26 + frontend/src/modules/tasks/api/index.tsx | 23 + .../TasksCalendarContext.ts | 9 + .../useTasksCalendarContext.ts | 6 + frontend/src/modules/tasks/context/index.tsx | 3 + frontend/src/modules/tasks/index.tsx | 5 + .../pages/ActivitiesPage/ActivitiesPage.tsx | 228 + .../ActivitiesPageView/ActivitiesPageView.tsx | 149 + .../pages/ActivitiesPage/components/index.tsx | 1 + .../tasks/pages/TasksPage/TasksPage.tsx | 501 + .../pages/TimeBoardPage/TimeBoardPage.tsx | 246 + .../TimeBoardView/TimeBoardView.tsx | 183 + .../pages/TimeBoardPage/components/index.tsx | 1 + .../tasks/pages/TimelinePage/TimelinePage.tsx | 184 + frontend/src/modules/tasks/pages/index.tsx | 4 + .../tasks/shared/assets/account_tree.svg | 5 + .../tasks/shared/assets/active_like.svg | 5 + .../tasks/shared/assets/attach_file.svg | 5 + .../modules/tasks/shared/assets/change.svg | 5 + .../tasks/shared/assets/close_cross.svg | 5 + .../modules/tasks/shared/assets/complete.svg | 8 + .../src/modules/tasks/shared/assets/copy.svg | 5 + .../src/modules/tasks/shared/assets/done.svg | 5 + .../src/modules/tasks/shared/assets/error.svg | 11 + .../tasks/shared/assets/inactive_like.svg | 5 + .../src/modules/tasks/shared/assets/index.tsx | 18 + .../tasks/shared/assets/load_more_arrow.svg | 3 + .../src/modules/tasks/shared/assets/plus.svg | 5 + .../modules/tasks/shared/assets/scale_bar.svg | 5 + .../modules/tasks/shared/assets/schedule.svg | 5 + .../src/modules/tasks/shared/assets/share.svg | 5 + .../src/modules/tasks/shared/assets/tick.svg | 3 + .../tasks/shared/assets/timeline_tab.svg | 9 + .../tasks/shared/assets/total_spend_time.svg | 4 + frontend/src/modules/tasks/shared/index.tsx | 2 + .../AddTaskButton/AddTaskButton.tsx | 58 + .../BoardParticipants/BoardParticipants.tsx | 162 + .../EmptyListBlock/EmptyListBlock.tsx | 68 + .../EntityTasksBoard/EntityTasksBoard.tsx | 95 + .../EntityTasksList/EntityTasksList.tsx | 73 + .../TaskColumnsWrapper/TaskColumnsWrapper.tsx | 9 + .../TaskEventAvatarBlock.tsx | 147 + .../TaskItem/ActivityItem/ActivityItem.tsx | 204 + .../components/TaskItem/TaskItem/TaskItem.tsx | 290 + .../AddDescriptionPlaceholder.tsx | 65 + .../components/Comments/AddComment.tsx | 102 + .../components/Comments/CommentItem.tsx | 329 + .../components/Comments/CommentsBlock.tsx | 88 + .../CompleteButton/CompleteButton.tsx | 40 + .../PlannedTimeBlock/PlannedTimeBlock.tsx | 69 + .../SelectEntityButton/SelectEntityButton.tsx | 158 + .../Skeletons/UpdateTaskModalSkeleton.tsx | 234 + .../TaskControlsBlock/TaskControlsBlock.tsx | 158 + .../UpdateTaskModal.styles.tsx | 128 + .../UpdateTaskModal/UpdateTaskModal.tsx | 833 + .../TaskItem/TaskItem/components/index.tsx | 7 + .../lib/components/TasksBoard/TasksBoard.tsx | 175 + .../TasksCalendarView/TasksCalendarView.tsx | 464 + .../DayCellContent/DayCellContent.tsx | 62 + .../DayHeaderContent/DayHeaderDayView.tsx | 57 + .../DayHeaderContent/DayHeaderMonthView.tsx | 26 + .../DayHeaderContent/DayHeaderWeekView.tsx | 72 + .../components/MoreLink/MoreLink.tsx | 78 + .../TaskEvent/TaskEventAgendaView.tsx | 215 + .../TaskEvent/TaskEventGridView.tsx | 228 + .../TasksCalendar/TasksCalendar.tsx | 370 + .../TasksCalendarAgendaView.tsx | 165 + .../TasksCalendarSidebar.tsx | 288 + .../TasksCalendarViewSelect.tsx | 158 + .../TasksCalendarSidebar/components/index.tsx | 1 + .../TasksCalendarStylesWrapper.tsx | 265 + .../TasksCalendarWrapper.tsx | 204 + .../TasksCalendarView/components/index.tsx | 1 + .../ActivitiesPageCalendarView.tsx | 55 + .../TasksPageCalendarView.tsx | 64 + .../TimeBoardPageCalendarView.tsx | 83 + .../components/TasksColumn/TasksColumn.tsx | 414 + .../AddQuickTaskBlock/AddQuickTaskBlock.tsx | 325 + .../TimeAllocationCard/TimeAllocationCard.tsx | 219 + .../TasksColumn/components/index.tsx | 5 + .../lib/components/TasksCount/TasksCount.tsx | 53 + .../TasksFilterButton/TasksFilterButton.tsx | 119 + .../TaskEntitiesSearchBlock.tsx | 189 + .../TasksFilterDrawer/TasksFilterDrawer.tsx | 483 + .../TasksFilterButton/components/index.tsx | 2 + .../lib/components/TasksList/TasksList.tsx | 282 + .../TasksListSettingsDrawer.tsx | 58 + .../components/TasksTable/TasksTable.tsx | 86 + .../TasksTableBody/TasksTableBody.tsx | 33 + .../TasksTableHead/TasksTableHead.tsx | 78 + .../TasksTableHeadCell/TasksTableHeadCell.tsx | 122 + .../TaskActionHeaderCell.tsx | 25 + .../TaskCheckboxCell/TaskCheckboxCell.tsx | 33 + .../TaskCheckboxHeaderCell.tsx | 31 + .../cells/TaskDeleteCell/TaskDeleteCell.tsx | 40 + .../cells/TaskEndDateCell/TaskEndDateCell.tsx | 78 + .../TaskLinkedEntityCell.tsx | 34 + .../TaskPlannedTimeCell.tsx | 57 + .../TaskResponsibleCell.tsx | 60 + .../cells/TaskStageCell/TaskStageCell.tsx | 75 + .../TaskStartDateCell/TaskStartDateCell.tsx | 77 + .../cells/TaskTitleCell/TaskTitleCell.tsx | 152 + .../components/TasksList/components/index.tsx | 13 + .../TasksPageHeader/TasksPageHeader.tsx | 117 + .../TasksSettingsButton.tsx | 126 + .../UpdateTaskDrawer.styles.tsx | 70 + .../UpdateTaskDrawer/UpdateTaskDrawer.tsx | 798 + .../UpdateTaskDrawerSkeleton.tsx | 86 + .../UpdateTaskDrawer/components/index.tsx | 1 + .../tasks/shared/lib/components/index.tsx | 36 + .../shared/lib/helpers/calculateEndDate.ts | 20 + .../shared/lib/helpers/calculateStartDate.ts | 18 + .../lib/helpers/findSavedCalendarFilter.ts | 20 + .../lib/helpers/findSavedTasksFilter.ts | 22 + .../shared/lib/helpers/generateDatesRange.ts | 19 + .../lib/helpers/generateTaskCalendarEvents.ts | 30 + .../lib/helpers/generateTasksCalendarRoute.ts | 53 + .../lib/helpers/getInitialCalendarView.ts | 18 + .../shared/lib/helpers/getIsTaskAllDay.ts | 94 + .../getRepeatingTaskDatesByInterval.ts | 89 + .../shared/lib/helpers/getTaskStatusColor.ts | 28 + .../tasks/shared/lib/helpers/getTaskTitle.ts | 19 + .../lib/helpers/getTasksDefaultColumnSize.ts | 17 + .../lib/helpers/getTasksFinalSortingValue.ts | 10 + .../lib/helpers/getTasksSortingOptions.ts | 18 + .../shared/lib/helpers/groupEventsByDate.ts | 15 + .../lib/helpers/hasDescriptionUserContent.ts | 16 + .../tasks/shared/lib/helpers/index.tsx | 17 + .../TasksList/useGetTasksListColumns.tsx | 169 + .../hooks/TasksList/useGetTasksListData.ts | 24 + .../modules/tasks/shared/lib/hooks/index.tsx | 6 + .../shared/lib/hooks/useCalendarNavigation.ts | 106 + .../hooks/useGetAllTasksFilterItemsOptions.ts | 64 + .../lib/hooks/useGetCalendarQueryParams.ts | 27 + .../shared/lib/hooks/useGetGroupOptions.ts | 19 + .../useGetRepeatingTasksIntervalOptions.ts | 32 + .../src/modules/tasks/shared/lib/index.tsx | 5 + .../lib/models/Activity/ActivityType.ts | 32 + .../shared/lib/models/BaseTask/BaseTask.ts | 510 + .../shared/lib/models/BaseTask/TaskGroup.ts | 55 + .../shared/lib/models/BaseTask/TaskView.ts | 4 + .../lib/models/BaseTask/TasksBoardType.ts | 5 + .../tasks/shared/lib/models/DeadlineType.ts | 8 + .../lib/models/Task/DateStringObject.ts | 6 + .../tasks/shared/lib/models/Task/Subtask.ts | 6 + .../shared/lib/models/Task/TaskComment.ts | 41 + .../lib/models/TaskCalendar/AddTaskPreset.ts | 6 + .../TaskCalendar/TaskCalendarFilterForm.ts | 11 + .../models/TaskCalendar/TaskCalendarMeta.ts | 11 + .../TaskCalendar/TaskCalendarViewType.ts | 6 + .../lib/models/TaskCalendar/TaskColorType.ts | 4 + .../models/TaskCalendar/TaskDeadlineType.ts | 4 + .../shared/lib/models/TaskCalendar/index.tsx | 6 + .../shared/lib/models/TaskColorStyles.ts | 5 + .../models/TaskFilter/ActivityCardsFilter.ts | 48 + .../models/TaskFilter/BaseTaskBoardFilter.ts | 50 + .../lib/models/TaskFilter/TaskBoardFilter.ts | 48 + .../lib/models/TaskFilter/TaskSorting.ts | 5 + .../lib/models/TaskFilter/TimeBoardFilter.ts | 49 + .../models/TaskFilterDrawerAsyncStateProps.ts | 8 + .../tasks/shared/lib/models/TaskHexColors.ts | 6 + .../tasks/shared/lib/models/TaskPalette.ts | 26 + .../lib/models/TaskSettings/TaskFieldCode.ts | 17 + .../lib/models/TaskSettings/TaskSettings.ts | 32 + .../models/TaskSettings/TaskSettingsType.ts | 5 + .../tasks/shared/lib/models/TaskViewStyles.ts | 5 + .../lib/models/TasksCardsFilterSettings.ts | 14 + .../shared/lib/models/TasksFilterType.ts | 5 + .../shared/lib/models/TasksList/TaskRow.ts | 66 + .../lib/models/TasksList/TasksColumnsIds.ts | 12 + .../lib/models/TasksList/TasksColumnsSizes.ts | 14 + .../TasksList/TasksDefaultColumnsSizes.ts | 14 + .../models/TasksList/TasksTableSettings.ts | 9 + .../models/TasksList/TasksTablesSettings.ts | 5 + .../tasks/shared/lib/models/TasksTab.ts | 6 + .../modules/tasks/shared/lib/models/index.tsx | 33 + .../tasks/shared/lib/models/queryParams.ts | 1 + .../shared/lib/types/RepeatingTaskInterval.ts | 6 + .../lib/types/TaskCalendarRouteGenerator.ts | 15 + .../shared/lib/types/TasksCalendarUseType.ts | 1 + .../lib/types/TasksCalendarViewCommonProps.ts | 19 + .../modules/tasks/shared/lib/types/index.tsx | 4 + .../tasks/store/ActivitiesPageStore.ts | 396 + .../modules/tasks/store/ActivityTypeStore.ts | 89 + .../store/CalendarServerEventServiceStore.ts | 86 + .../modules/tasks/store/CalendarViewStore.tsx | 44 + .../modules/tasks/store/TaskCommentsStore.ts | 99 + .../modules/tasks/store/TaskSettingsStore.ts | 89 + .../tasks/store/TasksBoardPageStore.ts | 379 + .../modules/tasks/store/TasksFilterStore.ts | 43 + .../modules/tasks/store/TasksGroupStore.ts | 134 + .../modules/tasks/store/TasksListPageStore.ts | 250 + .../src/modules/tasks/store/TasksStore.ts | 67 + .../tasks/store/TasksTimelinePageStore.ts | 285 + .../modules/tasks/store/TimeBoardPageStore.ts | 483 + .../tasks/store/UpdateTaskModalStore.ts | 160 + frontend/src/modules/tasks/store/index.tsx | 14 + .../templates/ItemTemplate/ItemTemplate.tsx | 85 + .../components/ItemTemplate.styles.tsx | 83 + .../ItemTemplate/components/index.tsx | 1 + .../TasksBoardPageTemplate.tsx | 86 + .../TasksPageTemplate/TasksPageTemplate.tsx | 205 + .../src/modules/tasks/templates/index.tsx | 3 + .../telephony/api/TelephonyApiRoutes.ts | 33 + .../telephony/api/TelephonyQueryKeys.ts | 38 + .../api/VoximplantApi/VoximplantApi.ts | 156 + .../queries/useCreateVoximplantUser.ts | 30 + .../queries/useDeleteVoximplantUser.ts | 19 + .../queries/useGetVoximplantCalls.ts | 18 + .../useGetVoximplantUserSIPSettings.ts | 9 + .../queries/useGetVoximplantUsers.ts | 9 + .../queries/usePatchVoximplantUser.ts | 21 + .../VoximplantNumbersApi.ts | 57 + ...invalidateVoximplantPhoneNumbersInCache.ts | 7 + .../queries/useCreateVoximplantPhoneNumber.ts | 36 + .../queries/useDeleteVoximplantPhoneNumber.ts | 34 + .../useGetVoximplantAvailablePhoneNumbers.ts | 9 + .../queries/useGetVoximplantPhoneNumbers.ts | 10 + .../queries/useUpdateVoximplantPhoneNumber.ts | 41 + .../api/VoximplantSIPApi/VoximplantSIPApi.ts | 90 + ...validateVoximplantSIPRegistrationsCache.ts | 12 + .../useCreateVoximplantSIPRegistration.ts | 27 + .../useDeleteVoximplantSIPRegistration.ts | 25 + ...etVoximplantSIPRegistrationByExternalId.ts | 10 + .../useGetVoximplantSIPRegistrations.ts | 16 + ...seGetVoximplantSIPRegistrationsExpanded.ts | 16 + .../useUpdateVoximplantSIPRegistration.ts | 32 + .../Voximplant/CreateVoximplantUserDto.ts | 7 + .../Voximplant/UpdateVoximplantCallDto.ts | 22 + .../Voximplant/UpdateVoximplantUserDto.ts | 7 + .../dtos/Voximplant/VoximplantAccountDto.ts | 9 + .../api/dtos/Voximplant/VoximplantCallDto.ts | 20 + .../dtos/Voximplant/VoximplantCallListDto.ts | 7 + .../dtos/Voximplant/VoximplantSIPDataDto.ts | 5 + .../api/dtos/Voximplant/VoximplantUserDto.ts | 5 + .../CreateVoximplantNumberDto.ts | 13 + .../dtos/VoximplantNumbers/PhoneNumberDto.ts | 6 + .../UpdateVoximplantNumberDto.ts | 13 + .../VoximplantNumbers/VoximplantNumberDto.ts | 8 + .../VoximplantSIP/CreateVoximplantSIPDto.ts | 33 + .../VoximplantSIP/UpdateVoximplantSIPDto.ts | 33 + .../dtos/VoximplantSIP/VoximplantSIPDto.ts | 12 + .../VoximplantSIPRegistrationDto.ts | 18 + .../VoximplantScenarioEntityDto.ts | 30 + .../VoximplantScenarioNoteDto.ts | 11 + .../VoximplantScenarioTaskDto.ts | 54 + .../VoximplantScenariosDto.ts | 24 + .../src/modules/telephony/api/dtos/index.tsx | 20 + frontend/src/modules/telephony/api/index.tsx | 21 + .../TelephonyContext/TelephonyContext.ts | 13 + .../TelephonyContext/TelephonyProvider.tsx | 42 + .../TelephonyContext/useTelephonyContext.ts | 89 + .../src/modules/telephony/context/index.tsx | 2 + frontend/src/modules/telephony/index.tsx | 23 + .../CallsConfiguringScenariosPage.tsx | 76 + .../CallTypeBlock/CallTypeBlock.tsx | 43 + .../CallTypeGroup/CallTypeGroup.tsx | 38 + .../ConfiguringScenariosHeaderControls.tsx | 48 + .../EntityScenarioGroup.tsx | 122 + .../IncomingCallsBlock/IncomingCallsBlock.tsx | 52 + .../IncomingKnownMissingScenarioBlock.tsx | 33 + .../IncomingUnknownMissingScenarioBlock.tsx | 156 + .../IncomingUnknownScenarioBlock.tsx | 40 + .../TaskAndActivitiesScenarioGroup.tsx | 164 + .../components/MinutesInput/MinutesInput.tsx | 48 + .../components/index.tsx | 1 + .../IncomingCallsBlock/components/index.tsx | 3 + .../OutgoingCallsBlock/OutgoingCallsBlock.tsx | 43 + .../OutgoingUnansweredScenarioBlock.tsx | 77 + .../OutgoingUnknownScenarioBlock.tsx | 36 + .../OutgoingCallsBlock/components/index.tsx | 2 + .../RadioBlockWrapper/RadioBlockWrapper.tsx | 81 + .../SelectWrapper/SelectWrapper.tsx | 27 + .../SelectWrapperTemplate.tsx | 60 + .../SelectsWrapper/SelectsWrapper.tsx | 7 + .../components/index.tsx | 3 + .../CallsSettingsAccountPage.tsx | 210 + .../AccountInfoBlock/AccountInfoBlock.tsx | 62 + .../VoximplantBalance/VoximplantBalance.tsx | 59 + .../VoximplantNumbersBlock.tsx | 219 + .../VoximplantNumbersBlockTable.tsx | 50 + .../DeleteVoximplantNumberWarningModal.tsx | 32 + .../VoximplantNumbersBlockStateCell.tsx | 111 + .../VoximplantNumbersBlockUsersCell.tsx | 57 + .../VoximplantSubscriptionFee.tsx | 33 + .../components/index.tsx | 5 + .../CallsSettingsUsersPage.tsx | 102 + .../AddCallsUserModal/AddCallsUserModal.tsx | 87 + .../CallsSettingsUsersPageTitle.tsx | 37 + .../RemoveCallsUserWarningModal.tsx | 35 + .../TelephonyDepartmentBlock.tsx | 93 + .../TelephonySubdepartmentBlock.tsx | 63 + .../TelephonyUserItem/TelephonyUserItem.tsx | 152 + .../UserSIPSettingsFormItem.tsx | 76 + .../UserSIPSettingsModal.tsx | 84 + .../UserSIPSettingsModalSkeleton.tsx | 40 + .../TelephonyUserItem/components/index.tsx | 1 + .../TelephonyUsersList/TelephonyUsersList.tsx | 27 + .../components/index.tsx | 4 + .../CallsSipRegistrationsPage.tsx | 136 + .../AddSipRegistrationModal.tsx | 324 + .../CallsSipRegistrationsPageTitle.tsx | 54 + .../DeleteSIPRegistrationWarningModal.tsx | 36 + ...LinkToVoximplantSipRegistrationsPortal.tsx | 23 + .../SipRegistrationItem.tsx | 200 + .../SipRegistrationModalFormItem.tsx | 38 + .../components/index.tsx | 3 + .../src/modules/telephony/pages/index.tsx | 8 + .../shared/assets/add_to_call_large.svg | 5 + .../shared/assets/add_to_call_small.svg | 5 + .../telephony/shared/assets/backspace.svg | 10 + .../telephony/shared/assets/beeline_mini.svg | 29 + .../telephony/shared/assets/call_large.svg | 5 + .../telephony/shared/assets/call_medium.svg | 5 + .../telephony/shared/assets/call_small.svg | 5 + .../telephony/shared/assets/close_small.svg | 5 + .../telephony/shared/assets/entity_link.svg | 5 + .../modules/telephony/shared/assets/fold.svg | 5 + .../telephony/shared/assets/hang_large.svg | 5 + .../telephony/shared/assets/hang_small.svg | 5 + .../modules/telephony/shared/assets/index.tsx | 28 + .../shared/assets/mango_office_mini.svg | 29 + .../telephony/shared/assets/megafon_mini.svg | 8 + .../telephony/shared/assets/mgts_mini.svg | 25 + .../telephony/shared/assets/mts_mini.svg | 18 + .../telephony/shared/assets/mute_large.svg | 5 + .../telephony/shared/assets/mute_small.svg | 5 + .../shared/assets/recent_call_incoming.svg | 5 + .../shared/assets/recent_call_outgoing.svg | 8 + .../shared/assets/redirect_large.svg | 5 + .../shared/assets/redirect_small.svg | 5 + .../shared/assets/rostelecom_mini.svg | 9 + .../telephony/shared/assets/tele2_mini.svg | 6 + .../telephony/shared/assets/uis_mini.svg | 6 + .../telephony/shared/assets/unfold.svg | 5 + .../telephony/shared/assets/unknown_mini.svg | 27 + .../telephony/shared/assets/zadarma_mini.svg | 41 + .../src/modules/telephony/shared/index.tsx | 1 + .../AcceptCallButton/AcceptCallButton.tsx | 53 + .../AddToCallButton/AddToCallButton.tsx | 32 + .../Buttons/CallButton/CallButton.tsx | 54 + .../Buttons/HangUpButton/HangUpButton.tsx | 51 + .../Buttons/MuteButton/MuteButton.tsx | 19 + .../TelephonyFunctionalButton.tsx | 106 + .../TelephonyModuleButton.tsx | 83 + .../Buttons/TransferButton/TransferButton.tsx | 119 + .../TransferConfirmationControls.tsx | 42 + .../TransferButton/components/index.tsx | 1 + .../TelephonyModal/TelephonyModal.tsx | 139 + .../ActiveCallControl/ActiveCallControl.tsx | 85 + .../CallControlTemplate.tsx | 187 + .../CreateEntityBlock/CreateEntityBlock.tsx | 189 + .../EllipsisAnimationBlock.tsx | 55 + .../EntitiesLinksBlock/EntitiesLinksBlock.tsx | 60 + .../components/EntityLink/EntityLink.tsx | 92 + .../PhoneNumberTitle/PhoneNumberTitle.tsx | 8 + .../CallControlTemplate/components/index.tsx | 4 + .../DraggableTelephonyControl.tsx | 109 + .../IncomingCallControl.tsx | 88 + .../OutgoingCallInitializer.tsx | 172 + .../BackspaceButton/BackspaceButton.tsx | 39 + .../components/DigitButton/DigitButton.tsx | 49 + .../OutgoingCallInitializerTabsListHeader.tsx | 73 + .../components/index.tsx | 3 + .../components/tabs/KeysTab/KeysTab.tsx | 441 + .../PotentialEntitiesLinksBlock.tsx | 64 + .../PotentialEntityLink.tsx | 32 + .../tabs/KeysTab/components/index.tsx | 1 + .../tabs/RecentCallsTab/RecentCallsTab.tsx | 119 + .../components/RecentCall/RecentCall.tsx | 274 + .../tabs/RecentCallsTab/components/index.tsx | 1 + .../TelephonyModalHeader.tsx | 166 + .../HeaderControlButtonBase.tsx | 19 + .../TelephonyModalHeader/components/index.tsx | 1 + .../TelephonyModal/components/index.tsx | 5 + .../telephony/shared/lib/components/index.tsx | 2 + .../shared/lib/helpers/formatCallTime.ts | 15 + .../lib/helpers/formatTelephonyPhoneNumber.ts | 16 + .../lib/helpers/getMiniPbxIconByType.tsx | 48 + .../lib/helpers/getRecentCallsGroups.ts | 35 + .../telephony/shared/lib/helpers/index.tsx | 4 + .../useGetVoximplantNumbersBlockData.ts | 47 + .../useVoximplantNumbersBlockColumns.tsx | 76 + .../telephony/shared/lib/hooks/index.tsx | 4 + .../lib/hooks/useFormatRecentCallDate.ts | 24 + .../shared/lib/hooks/usePhoneCallTimer.tsx | 21 + .../modules/telephony/shared/lib/index.tsx | 5 + .../shared/lib/models/CallDirection.ts | 4 + .../telephony/shared/lib/models/CallStatus.ts | 8 + .../shared/lib/models/RecentCallsGroup.ts | 7 + .../models/TelephonyModal/CallFromNumber.ts | 7 + .../models/TelephonyModal/CallFromSipRegId.ts | 7 + .../OutgoingCallInitializerTabs.ts | 4 + .../TelephonyFunctionalButtonProps.ts | 14 + .../TelephonyModal/TelephonyModalPosition.ts | 6 + .../TelephonyModal/TelephonyModalSize.ts | 6 + .../models/Voximplant/VoximplantAccount.ts | 41 + .../lib/models/Voximplant/VoximplantCall.ts | 92 + .../models/Voximplant/VoximplantCallList.ts | 17 + .../models/Voximplant/VoximplantSIPData.ts | 21 + .../lib/models/Voximplant/VoximplantUser.ts | 21 + .../models/VoximplantNumbers/PhoneNumber.ts | 28 + .../VoximplantNumbers/VoximplantNumber.ts | 29 + .../VoximplantNumberBlockColumnsIds.ts | 5 + .../VoximplantNumbers/VoximplantNumberRow.ts | 32 + .../lib/models/VoximplantNumbersSettings.ts | 6 + .../models/VoximplantSIP/PbxProviderType.ts | 14 + .../lib/models/VoximplantSIP/VoximplantSIP.ts | 39 + .../VoximplantSIPRegistration.ts | 77 + .../ScenarioAutoCreateState.ts | 4 + .../VoximplantScenarios/ScenarioType.ts | 7 + .../TaskAndActivityCreateMode.ts | 5 + .../VoximplantScenarioEntity.ts | 47 + .../VoximplantScenarioNote.ts | 25 + .../VoximplantScenarioTask.ts | 89 + .../VoximplantScenarios.ts | 29 + .../telephony/shared/lib/models/index.tsx | 30 + .../models/voximplantNumbersSettingsKey.ts | 1 + .../shared/lib/types/TelephonyButtonSize.ts | 1 + .../TelephonyModal/SetLastCallFromHandler.ts | 3 + .../telephony/shared/lib/types/index.tsx | 2 + .../shared/lib/utils/ModalBoundsUtil.ts | 45 + .../telephony/shared/lib/utils/index.tsx | 1 + .../IncomingCallsFormDatas.ts | 100 + .../IncomingKnownMissingFormData.ts | 31 + .../IncomingUnknownFormData.ts | 78 + .../IncomingUnknownMissingFormData.ts | 113 + .../TaskAndActivityFormData.ts | 152 + .../OutgoingCallsFormDatas.ts | 72 + .../OutgoingUnansweredFormData.ts | 46 + .../OutgoingUnknownFormData.ts | 80 + .../store/VoximplantConnectorStore.ts | 589 + .../store/VoximplantScenariosStore.ts | 104 + .../src/modules/telephony/store/index.tsx | 8 + .../tutorial/api/TutorialApi/TutorialApi.ts | 190 + ...nvalidateGetExpandedTutorialGroupsQuery.ts | 13 + .../queries/useCreateTutorialGroup.ts | 22 + .../queries/useCreateTutorialItem.ts | 27 + .../queries/useDeleteTutorialGroup.ts | 21 + .../queries/useDeleteTutorialItem.ts | 28 + .../queries/useGetExpandedTutorialGroups.ts | 16 + .../queries/useGetTutorialCount.ts | 9 + .../queries/useUpdateTutorialGroupName.ts | 37 + .../queries/useUpdateTutorialItem.ts | 69 + .../modules/tutorial/api/TutorialApiRoutes.ts | 18 + .../modules/tutorial/api/TutorialQueryKeys.ts | 13 + .../modules/tutorial/api/dtos/SortOrderDto.ts | 9 + .../tutorial/api/dtos/SortOrderListDto.ts | 9 + .../TutorialGroup/CreateTutorialGroupDto.ts | 9 + .../dtos/TutorialGroup/TutorialGroupDto.ts | 9 + .../TutorialGroup/UpdateTutorialGroupDto.ts | 12 + .../TutorialItem/CreateTutorialItemDto.ts | 18 + .../api/dtos/TutorialItem/TutorialItemDto.ts | 14 + .../TutorialItem/UpdateTutorialItemDto.ts | 18 + .../src/modules/tutorial/api/dtos/index.tsx | 8 + frontend/src/modules/tutorial/api/index.tsx | 15 + frontend/src/modules/tutorial/index.tsx | 1 + .../tutorial/shared/assets/caret_down.svg | 5 + .../modules/tutorial/shared/assets/index.tsx | 3 + .../shared/assets/tutorial_button.svg | 5 + .../tutorial/shared/assets/tutorial_empty.svg | 9 + .../src/modules/tutorial/shared/index.tsx | 1 + .../TutorialButton/TutorialButton.tsx | 128 + .../TutorialDrawer/TutorialDrawer.tsx | 185 + .../TutorialDrawerContent.tsx | 48 + .../TutorialGroupBlock/TutorialGroupBlock.tsx | 128 + .../TutorialItemBlock/TutorialItemBlock.tsx | 66 + .../components/index.tsx | 1 + .../TutorialDrawerEditMode.tsx | 302 + .../CreateTutorialGroupBlock.tsx | 98 + .../CreateTutorialItemBlock.tsx | 94 + .../TutorialEditGroupForm.tsx | 109 + .../TutorialEditGroupItemForm.tsx | 219 + .../TutorialEditGroupItemsForms.tsx | 183 + .../TutorialGroupItemFormInput.tsx | 110 + .../TutorialGroupNameBlock.tsx | 200 + .../components/index.tsx | 2 + .../TutorialDrawerEmpty.tsx | 36 + .../TutorialDrawer/components/index.tsx | 3 + .../tutorial/shared/lib/components/index.tsx | 1 + .../tutorial/shared/lib/hooks/index.tsx | 3 + .../lib/hooks/useGetTutorialProductsGroups.ts | 27 + .../hooks/useGetTutorialProductsOptions.ts | 68 + .../lib/hooks/useTutorialItemOutlined.ts | 9 + .../src/modules/tutorial/shared/lib/index.tsx | 3 + .../lib/models/TutorialGroup/TutorialGroup.ts | 29 + .../models/TutorialGroup/TutorialGroupForm.ts | 72 + .../lib/models/TutorialItem/TutorialItem.ts | 43 + .../models/TutorialItem/TutorialItemForm.ts | 64 + .../TutorialItem/TutorialItemProduct.ts | 6 + .../TutorialSettings/TutorialGroupSettings.ts | 4 + .../TutorialGroupsSettings.ts | 5 + .../TutorialGroupsSettingsKey.ts | 1 + .../TutorialLastOpenedProduct.ts | 7 + .../TutorialSettings/TutorialSettings.ts | 5 + .../TutorialSettings/TutorialSettingsKey.ts | 1 + .../tutorial/shared/lib/models/index.tsx | 11 + .../shared/lib/types/editModeStoreHandlers.ts | 45 + .../tutorial/shared/lib/types/index.tsx | 8 + .../tutorial/store/TutorialEditModeStore.ts | 365 + frontend/src/modules/tutorial/store/index.tsx | 1 + .../assets/docs-images/branches_example.png | Bin 0 -> 52006 bytes .../assets/docs-images/commit_prefixes.png | Bin 0 -> 50441 bytes .../assets/docs-images/deploy_storybook.png | Bin 0 -> 135096 bytes .../src/shared/assets/docs-images/index.tsx | 7 + .../project_and_task_board_relation.png | Bin 0 -> 652973 bytes .../assets/docs-images/requests_example.png | Bin 0 -> 109204 bytes .../assets/docs-images/vercel_preview.png | Bin 0 -> 135523 bytes frontend/src/shared/assets/icons/actions.svg | 5 + .../shared/assets/icons/activities_board.svg | 8 + frontend/src/shared/assets/icons/add_blue.svg | 5 + .../src/shared/assets/icons/add_circle.svg | 7 + .../src/shared/assets/icons/add_circled.svg | 6 + .../src/shared/assets/icons/add_photo.svg | 5 + .../src/shared/assets/icons/add_square.svg | 6 + frontend/src/shared/assets/icons/add_user.svg | 14 + .../src/shared/assets/icons/all_chats.svg | 5 + .../shared/assets/icons/all_chats_grey.svg | 10 + .../src/shared/assets/icons/arrow_back.svg | 6 + .../shared/assets/icons/arrow_back_small.svg | 5 + .../shared/assets/icons/arrow_drop_down.svg | 6 + frontend/src/shared/assets/icons/avito.svg | 14 + .../src/shared/assets/icons/avito_grey.svg | 24 + frontend/src/shared/assets/icons/big_plus.svg | 8 + .../shared/assets/icons/block_file_doc.svg | 15 + .../assets/icons/block_file_fallback.svg | 12 + .../shared/assets/icons/block_file_pdf.svg | 16 + .../src/shared/assets/icons/broken_player.svg | 5 + .../src/shared/assets/icons/builder/box.svg | 6 + .../shared/assets/icons/builder/briefcase.svg | 5 + .../assets/icons/builder/building_1.svg | 10 + .../assets/icons/builder/building_2.svg | 5 + .../assets/icons/builder/building_3.svg | 8 + .../src/shared/assets/icons/builder/bulb.svg | 23 + .../shared/assets/icons/builder/burger.svg | 5 + .../assets/icons/builder/calendar_1.svg | 10 + .../assets/icons/builder/calendar_2.svg | 8 + .../assets/icons/builder/calendar_3.svg | 5 + .../assets/icons/builder/calendar_4.svg | 11 + .../src/shared/assets/icons/builder/call.svg | 14 + .../assets/icons/builder/cards/albato.svg | 8 + .../assets/icons/builder/cards/apix_drive.svg | 30 + .../icons/builder/cards/automatisation.svg | 15 + .../assets/icons/builder/cards/companies.svg | 6 + .../assets/icons/builder/cards/contacts.svg | 12 + .../icons/builder/cards/contractors.svg | 15 + .../shared/assets/icons/builder/cards/crm.svg | 18 + .../assets/icons/builder/cards/documents.svg | 6 + .../assets/icons/builder/cards/facebook.svg | 11 + .../assets/icons/builder/cards/finances.svg | 17 + .../assets/icons/builder/cards/forms.svg | 12 + .../shared/assets/icons/builder/cards/hr.svg | 6 + .../assets/icons/builder/cards/hundred_gb.svg | 8 + .../assets/icons/builder/cards/index.tsx | 34 + .../assets/icons/builder/cards/mailing.svg | 15 + .../assets/icons/builder/cards/make.svg | 44 + .../assets/icons/builder/cards/marketing.svg | 14 + .../assets/icons/builder/cards/messenger.svg | 9 + .../assets/icons/builder/cards/one_c.svg | 14 + .../assets/icons/builder/cards/one_tb.svg | 8 + .../assets/icons/builder/cards/partners.svg | 6 + .../assets/icons/builder/cards/production.svg | 12 + .../assets/icons/builder/cards/projects.svg | 24 + .../assets/icons/builder/cards/rent.svg | 9 + .../assets/icons/builder/cards/salesforce.svg | 14 + .../assets/icons/builder/cards/scheduler.svg | 12 + .../assets/icons/builder/cards/suppliers.svg | 9 + .../assets/icons/builder/cards/telephony.svg | 15 + .../assets/icons/builder/cards/ten_gb.svg | 8 + .../assets/icons/builder/cards/tilda.svg | 12 + .../assets/icons/builder/cards/universal.svg | 30 + .../assets/icons/builder/cards/warehouse.svg | 7 + .../icons/builder/cards/website_chat.svg | 9 + .../assets/icons/builder/cards/whatsapp.svg | 11 + .../assets/icons/builder/cards/wordpress.svg | 8 + .../shared/assets/icons/builder/chart_1.svg | 10 + .../shared/assets/icons/builder/chart_2.svg | 17 + .../shared/assets/icons/builder/clock_1.svg | 15 + .../shared/assets/icons/builder/clock_2.svg | 14 + .../src/shared/assets/icons/builder/crown.svg | 17 + .../shared/assets/icons/builder/equalizer.svg | 5 + .../src/shared/assets/icons/builder/hand.svg | 5 + .../shared/assets/icons/builder/in_out.svg | 8 + .../src/shared/assets/icons/builder/index.tsx | 51 + .../shared/assets/icons/builder/lightning.svg | 5 + .../src/shared/assets/icons/builder/mail.svg | 14 + .../shared/assets/icons/builder/product_1.svg | 8 + .../shared/assets/icons/builder/product_2.svg | 8 + .../shared/assets/icons/builder/rocket_1.svg | 5 + .../shared/assets/icons/builder/rocket_2.svg | 5 + .../shared/assets/icons/builder/rocket_3.svg | 11 + .../assets/icons/builder/settings_1.svg | 20 + .../assets/icons/builder/settings_2.svg | 20 + .../assets/icons/builder/settings_3.svg | 5 + .../assets/icons/builder/settings_4.svg | 8 + .../shared/assets/icons/builder/shapes_1.svg | 11 + .../shared/assets/icons/builder/shapes_2.svg | 9 + .../shared/assets/icons/builder/shapes_3.svg | 6 + .../shared/assets/icons/builder/shapes_4.svg | 5 + .../shared/assets/icons/builder/site_form.svg | 11 + .../src/shared/assets/icons/builder/sound.svg | 5 + .../shared/assets/icons/builder/star_1.svg | 10 + .../shared/assets/icons/builder/star_2.svg | 5 + .../shared/assets/icons/builder/star_3.svg | 10 + .../shared/assets/icons/builder/target_1.svg | 5 + .../shared/assets/icons/builder/target_2.svg | 38 + .../shared/assets/icons/builder/tick_1.svg | 5 + .../shared/assets/icons/builder/tick_2.svg | 8 + .../src/shared/assets/icons/builder/tie_1.svg | 23 + .../src/shared/assets/icons/builder/tie_2.svg | 14 + .../shared/assets/icons/builder/user_1.svg | 9 + .../shared/assets/icons/builder/user_2.svg | 11 + .../shared/assets/icons/builder/user_3.svg | 11 + .../shared/assets/icons/builder/user_4.svg | 8 + frontend/src/shared/assets/icons/calendar.svg | 5 + .../shared/assets/icons/calendar_end_date.svg | 8 + frontend/src/shared/assets/icons/caret.svg | 3 + .../src/shared/assets/icons/caret_down.svg | 5 + frontend/src/shared/assets/icons/check.svg | 5 + .../src/shared/assets/icons/check_done.svg | 3 + frontend/src/shared/assets/icons/checkbox.svg | 5 + .../src/shared/assets/icons/checkmark.svg | 5 + frontend/src/shared/assets/icons/checkout.svg | 5 + .../src/shared/assets/icons/chevron_left.svg | 4 + frontend/src/shared/assets/icons/clear.svg | 14 + .../src/shared/assets/icons/clear_circled.svg | 5 + .../src/shared/assets/icons/clear_cross.svg | 5 + .../assets/icons/clear_cross_x_small.svg | 4 + .../shared/assets/icons/clear_participant.svg | 7 + .../src/shared/assets/icons/clear_select.svg | 7 + .../src/shared/assets/icons/clip_large.svg | 5 + .../src/shared/assets/icons/clip_medium.svg | 5 + .../src/shared/assets/icons/clip_small.svg | 5 + .../src/shared/assets/icons/close_modal.svg | 5 + .../src/shared/assets/icons/copy_content.svg | 5 + .../assets/icons/copy_content_white.svg | 5 + frontend/src/shared/assets/icons/create.svg | 5 + .../src/shared/assets/icons/cross-red.svg | 5 + .../assets/icons/dashed_frame_large.svg | 3 + .../assets/icons/dashed_frame_medium.svg | 3 + .../src/shared/assets/icons/delete_demo.svg | 11 + .../src/shared/assets/icons/delete_tag.svg | 7 + .../src/shared/assets/icons/dots_medium.svg | 5 + .../src/shared/assets/icons/dots_small.svg | 5 + .../shared/assets/icons/download_medium.svg | 5 + .../shared/assets/icons/download_small.svg | 5 + frontend/src/shared/assets/icons/drag.svg | 5 + .../src/shared/assets/icons/drag_field.svg | 5 + .../src/shared/assets/icons/emoji_medium.svg | 5 + .../src/shared/assets/icons/emoji_small.svg | 5 + frontend/src/shared/assets/icons/envelope.svg | 8 + frontend/src/shared/assets/icons/expand.svg | 5 + frontend/src/shared/assets/icons/facebook.svg | 19 + .../src/shared/assets/icons/facebook_grey.svg | 8 + .../src/shared/assets/icons/file_audio.svg | 5 + .../src/shared/assets/icons/file_document.svg | 5 + .../src/shared/assets/icons/file_image.svg | 5 + .../src/shared/assets/icons/file_video.svg | 5 + .../shared/assets/icons/files/file_doc.svg | 8 + .../shared/assets/icons/files/file_img.svg | 12 + .../shared/assets/icons/files/file_pdf.svg | 8 + .../shared/assets/icons/files/file_ppt.svg | 8 + .../shared/assets/icons/files/file_xls.svg | 8 + .../shared/assets/icons/filled_arrow_down.svg | 5 + frontend/src/shared/assets/icons/filter.svg | 26 + frontend/src/shared/assets/icons/format.svg | 5 + frontend/src/shared/assets/icons/gear.svg | 7 + .../src/shared/assets/icons/google_logo.svg | 14 + .../assets/icons/google_logo_disabled.svg | 5 + .../shared/assets/icons/green_plus_filled.svg | 5 + frontend/src/shared/assets/icons/group.svg | 11 + .../shared/assets/icons/horizontal_dots.svg | 5 + frontend/src/shared/assets/icons/index.tsx | 156 + .../src/shared/assets/icons/info_large.svg | 5 + .../src/shared/assets/icons/info_medium.svg | 5 + .../src/shared/assets/icons/info_small.svg | 5 + .../src/shared/assets/icons/instagram.svg | 31 + .../shared/assets/icons/instagram_grey.svg | 15 + frontend/src/shared/assets/icons/link.svg | 5 + .../shared/assets/icons/logos/logo_amwork.svg | 36 + .../assets/icons/logos/logo_amwork_text.svg | 26 + .../shared/assets/icons/logos/logo_flower.svg | 8 + .../assets/icons/logos/logo_flower_round.svg | 51 + .../icons/logos/logo_flower_round_grey.svg | 51 + .../shared/assets/icons/logos/logo_mywork.svg | 26 + .../assets/icons/logos/logo_mywork_text.svg | 26 + .../assets/icons/logos/logo_platforma500.svg | 44 + .../icons/logos/logo_platforma500_text.svg | 37 + .../shared/assets/icons/logos/logo_proma.svg | 32 + .../logos/logo_proma_minified_inverted.svg | 17 + .../assets/icons/logos/logo_proma_text.svg | 17 + frontend/src/shared/assets/icons/mail_tab.svg | 9 + frontend/src/shared/assets/icons/menu.svg | 5 + frontend/src/shared/assets/icons/minus.svg | 3 + .../icons/modal_close_cross_primary.svg | 6 + .../icons/modal_close_cross_secondary.svg | 5 + .../icons/modal_close_cross_tertiary.svg | 5 + .../shared/assets/icons/modal_remove_user.svg | 7 + .../shared/assets/icons/modal_trashbin.svg | 21 + .../src/shared/assets/icons/modal_warning.svg | 12 + frontend/src/shared/assets/icons/more.svg | 5 + .../shared/assets/icons/multi_messenger.svg | 8 + .../src/shared/assets/icons/multichat_tab.svg | 7 + .../src/shared/assets/icons/not_found.svg | 234 + .../assets/icons/outlined_input_error.svg | 5 + frontend/src/shared/assets/icons/pause.svg | 5 + .../src/shared/assets/icons/pencil_medium.svg | 5 + .../src/shared/assets/icons/pencil_small.svg | 5 + frontend/src/shared/assets/icons/percent.svg | 5 + frontend/src/shared/assets/icons/play.svg | 5 + frontend/src/shared/assets/icons/plus.svg | 8 + .../src/shared/assets/icons/plus_outlined.svg | 7 + .../src/shared/assets/icons/plus_primary.svg | 5 + .../shared/assets/icons/plus_secondary.svg | 8 + .../src/shared/assets/icons/project_board.svg | 11 + .../shared/assets/icons/scrollbar_arrow.svg | 5 + frontend/src/shared/assets/icons/search.svg | 8 + .../src/shared/assets/icons/search_small.svg | 8 + frontend/src/shared/assets/icons/selected.svg | 5 + .../shared/assets/icons/selected_group.svg | 5 + .../assets/icons/settings_two_lines.svg | 8 + .../shared/assets/icons/settings_x_small.svg | 5 + .../shared/assets/icons/solid_frame_large.svg | 3 + .../assets/icons/solid_frame_medium.svg | 3 + frontend/src/shared/assets/icons/subgroup.svg | 11 + .../assets/icons/subheader_settings.svg | 5 + .../shared/assets/icons/supervisor_star.svg | 5 + .../shared/assets/icons/table_settings.svg | 5 + .../shared/assets/icons/tabs/board_tab.svg | 6 + .../shared/assets/icons/tabs/calendar_tab.svg | 19 + .../assets/icons/tabs/dashboard_tab.svg | 7 + .../src/shared/assets/icons/tabs/index.tsx | 9 + .../src/shared/assets/icons/tabs/list_tab.svg | 9 + .../shared/assets/icons/tabs/overview_tab.svg | 5 + .../shared/assets/icons/tabs/products_tab.svg | 6 + .../shared/assets/icons/tabs/reports_tab.svg | 4 + .../assets/icons/tabs/shipments_tab.svg | 12 + .../assets/icons/tabs/tasks_calendar_tab.svg | 15 + frontend/src/shared/assets/icons/target.svg | 5 + .../src/shared/assets/icons/tasks_board.svg | 8 + frontend/src/shared/assets/icons/telegram.svg | 19 + .../src/shared/assets/icons/telegram_grey.svg | 8 + .../src/shared/assets/icons/textarea_save.svg | 5 + .../src/shared/assets/icons/time_board.svg | 14 + .../src/shared/assets/icons/time_from.svg | 5 + .../shared/assets/icons/time_management.svg | 233 + .../shared/assets/icons/trashbin_medium.svg | 5 + .../shared/assets/icons/trashbin_small.svg | 5 + frontend/src/shared/assets/icons/union.svg | 5 + .../shared/assets/icons/universal_file.svg | 7 + frontend/src/shared/assets/icons/upload.svg | 5 + frontend/src/shared/assets/icons/user.svg | 8 + .../shared/assets/icons/visibility_off.svg | 5 + .../src/shared/assets/icons/visibility_on.svg | 5 + frontend/src/shared/assets/icons/vk.svg | 5 + frontend/src/shared/assets/icons/vk_grey.svg | 5 + frontend/src/shared/assets/icons/warning.svg | 5 + frontend/src/shared/assets/icons/whatsapp.svg | 25 + .../src/shared/assets/icons/whatsapp_grey.svg | 15 + frontend/src/shared/assets/index.tsx | 2 + frontend/src/shared/docs/Intro.mdx | 806 + frontend/src/shared/docs/Organisation.mdx | 25 + frontend/src/shared/docs/Pipelines.mdx | 32 + frontend/src/shared/docs/Structure.mdx | 289 + frontend/src/shared/docs/Styles.mdx | 31 + frontend/src/shared/docs/bpmn/Схема BPMN.png | Bin 0 -> 496663 bytes .../shared/docs/code-style/ModelsAndDtos.mdx | 140 + .../shared/docs/code-style/WorkWithApi.mdx | 96 + .../shared/docs/fields/Устройство полей.png | Bin 0 -> 292261 bytes frontend/src/shared/docs/modules/Builder.mdx | 91 + .../src/shared/docs/onboarding/Backend.mdx | 124 + .../src/shared/docs/onboarding/Frontend.mdx | 168 + .../telephony/Номер VS SIP-регистрация.png | Bin 0 -> 333019 bytes .../telephony/Общая информация о телефонии | 2090 ++ .../План по подключению телефонии.png | Bin 0 -> 384565 bytes ...одключение телефонии со стороны клиента.png | Bin 0 -> 141661 bytes ...док входа в Voximplant и путь после входа.png | Bin 0 -> 150797 bytes .../shared/hoc/RequireAuth/RequireAuth.tsx | 57 + .../RequirePermission/RequirePermission.tsx | 117 + .../shared/hoc/RequireRole/RequireRole.tsx | 58 + .../RequireSuperadmin/RequireSuperadmin.tsx | 43 + frontend/src/shared/hoc/index.tsx | 8 + frontend/src/shared/hoc/withPage/withPage.tsx | 14 + frontend/src/shared/index.tsx | 5 + .../ActionsHeaderCell/ActionsHeaderCell.tsx | 23 + .../ApplyToAllButton/ApplyToAllButton.tsx | 55 + .../AvatarCircle/AvatarCircle.stories.tsx | 69 + .../components/AvatarCircle/AvatarCircle.tsx | 287 + .../BackLink/ArrowBackLink.stories.tsx | 20 + .../lib/components/BackLink/ArrowBackLink.tsx | 103 + .../lib/components/BaseTable/BaseTable.tsx | 28 + .../components/BaseTable/BaseTableBody.tsx | 37 + .../BaseTable/BaseTableBodyCell.tsx | 24 + .../components/BaseTable/BaseTableBodyRow.tsx | 71 + .../BaseTable/BaseTableBodyRowFilled.tsx | 18 + .../components/BaseTable/BaseTableHead.tsx | 24 + .../BaseTable/BaseTableHeadCell.tsx | 50 + .../components/BaseTable/BaseTableHeadRow.tsx | 62 + .../components/BaseTable/BaseTableRoot.tsx | 24 + .../BoardItemPrimary.stories.tsx | 35 + .../BoardItemPrimary/BoardItemPrimary.tsx | 382 + .../BoardItemSecondary.stories.tsx | 20 + .../BoardItemSecondary/BoardItemSecondary.tsx | 136 + .../BoardListWithButtons.stories.tsx | 38 + .../BoardListWithButtons.tsx | 38 + .../BoardListWithLinks.stories.tsx | 41 + .../BoardListWithLinks/BoardListWithLinks.tsx | 158 + .../TasksBoardList/TasksBoardList.stories.tsx | 38 + .../TasksBoardList/TasksBoardList.tsx | 178 + .../components/BoardList/BoardList.tsx | 18 + .../BoardListItemWrapper.tsx | 14 + .../BoardListRoot/BoardListRoot.tsx | 22 + .../BoardNameStyle/BoardNameStyle.tsx | 20 + .../components/BoardList/components/index.tsx | 4 + .../EntitiesBoardPicker.stories.tsx | 39 + .../EntitiesBoardPicker.tsx | 100 + .../EntityPicker/EntitiesAndBoardsPicker.tsx | 107 + .../EntitiesAndBoardsList.tsx | 228 + .../EntityPicker/components/index.tsx | 1 + .../TasksBoardPicker/TasksBoardPicker.tsx | 92 + .../components/BoardPicker/BoardPicker.tsx | 141 + .../components/Controls/Controls.tsx | 158 + .../BoardPicker/components/index.tsx | 1 + .../lib/components/BoardPicker/index.tsx | 1 + .../AddFilledButton/AddFilledButton.tsx | 84 + .../AddSmallCircleButton.stories.tsx | 22 + .../AddSmallCircleButton.tsx | 55 + .../Buttons/BuilderButton/BuilderButton.tsx | 74 + .../BuilderButtonIcon/BuilderButtonIcon.tsx | 27 + .../BuilderButton/components/index.tsx | 1 + .../ClearRoundButton.stories.tsx | 16 + .../ClearRoundButton/ClearRoundButton.tsx | 65 + .../ConnectGoogleButton.tsx | 86 + .../ControlButton/ControlButton.stories.tsx | 29 + .../Buttons/ControlButton/ControlButton.tsx | 111 + .../Buttons/CopyButton/CopyButton.tsx | 61 + .../Buttons/CreateButton/CreateButton.tsx | 225 + .../DefaultColorSelectButton.tsx | 67 + .../DeleteButton/DeleteButton.stories.tsx | 28 + .../Buttons/DeleteButton/DeleteButton.tsx | 145 + .../DownloadButton/DownloadButton.stories.tsx | 22 + .../Buttons/DownloadButton/DownloadButton.tsx | 81 + .../Buttons/MenuButton/MenuButton.tsx | 53 + .../PlusIconButton/PlusIconButton.stories.tsx | 20 + .../Buttons/PlusIconButton/PlusIconButton.tsx | 88 + .../PrimaryButton/PrimaryButton.stories.tsx | 62 + .../Buttons/PrimaryButton/PrimaryButton.tsx | 340 + .../AddRoundButton/AddRoundButton.tsx | 10 + .../CreateRoundButton/CreateRoundButton.tsx | 10 + .../RoundButton/RoundButton.stories.tsx | 22 + .../Buttons/RoundButton/RoundButton.tsx | 53 + .../SaveCancelButtons.stories.tsx | 20 + .../SaveCancelButtons/SaveCancelButtons.tsx | 45 + .../CalendarPeriodControl.tsx | 91 + .../CalendarPeriodControlTitle.tsx | 52 + .../components/index.tsx | 1 + .../CardCopiedCountTagLink.tsx | 87 + .../ChangePeriodControls.tsx | 73 + .../ChangesUnsavedBlocker.tsx | 73 + .../lib/components/ColorBox/ColorBox.tsx | 69 + .../ColorPicker/ColorPicker.stories.tsx | 21 + .../components/ColorPicker/ColorPicker.tsx | 99 + .../components/ColoredBlock/ColoredBlock.tsx | 48 + .../ColumnCount/ColumnCount.stories.tsx | 19 + .../ColumnCount/ColumnCount/ColumnCount.tsx | 74 + .../ColumnCountSkeleton.stories.tsx | 23 + .../ColumnCountSkeleton.tsx | 11 + .../ColumnsCountGhost/ColumnCountGhost.tsx | 10 + .../ColumnsVisibilitySettingsGrid.tsx | 50 + .../components/ContextMenu/ContextMenu.tsx | 127 + .../ContextMenuItem/ContextMenuItem.tsx | 50 + .../ContextMenu/components/index.tsx | 1 + .../CreateContactModal/CreateContactModal.tsx | 254 + .../CreatedAtDateSelect.tsx | 168 + .../CustomizeButton/CustomizeButton.tsx | 103 + .../DefaultHeader/DefaultHeader.tsx | 222 + .../components/AccountBlock/AccountBlock.tsx | 51 + .../AccountBlock/AccountBlockSkeleton.tsx | 9 + .../components/AvatarBlock/AvatarBlock.tsx | 101 + .../DeleteDemoButton/DeleteDemoButton.tsx | 52 + .../HeaderDelimiter/HeaderDelimiter.tsx | 9 + .../components/TrialButton/TrialButton.tsx | 78 + .../DefaultHeader/components/index.tsx | 4 + .../components/DelaySelect/DelaySelect.tsx | 133 + .../CustomIntervalInputGroup.tsx | 132 + .../IntervalInput/IntervalInput.tsx | 42 + .../SubmitIntervalButton.tsx | 25 + .../components/index.tsx | 2 + .../DelaySelect/components/index.tsx | 1 + .../DepartmentsSelect/DepartmentsSelect.tsx | 198 + .../DepartmentsSelectItem.tsx | 75 + .../DepartmentsSelect/components/index.tsx | 2 + .../DraggableResizableControl.tsx | 94 + .../DropdownListFunctional.tsx | 89 + .../ListItem/DropdownListItem.tsx | 15 + .../DropdownList/ListItem/ListItemCommon.tsx | 52 + .../DropdownList/ListItem/ListItemLink.tsx | 12 + .../DropdownList/ListItem/ListItemText.tsx | 14 + .../EmptyTableBlock/EmptyTableBlock.tsx | 27 + .../EntitiesSuggestionsPopover.tsx | 65 + .../AddAsNewButton/AddAsNewButton.tsx | 70 + .../EntitiesSuggestionItem.tsx | 130 + .../components/index.tsx | 3 + .../ErrorBoundary/ErrorBoundary.tsx | 41 + .../ExpandButton/ExpandButton.stories.tsx | 20 + .../components/ExpandButton/ExpandButton.tsx | 45 + .../FieldGroupTabs/FieldGroupTabs.tsx | 75 + .../EditableTabTitle/EditableTabTitle.tsx | 185 + .../FileInput/FileInput.stories.tsx | 32 + .../lib/components/FileInput/FileInput.tsx | 260 + .../BlockFileItem/BlockFileItem.tsx | 189 + .../BlockFileListRoot/BlockFileListRoot.tsx | 7 + .../CompactFileList/CompactFileList.tsx | 43 + .../components/FileFeedItem/FileFeedItem.tsx | 306 + .../components/FileIcon/FileIcon.tsx | 52 + .../components/FileItem/FileItem.tsx | 214 + .../FileListWrapper/FileListWrapper.tsx | 6 + .../components/FileSize/FileSize.tsx | 38 + .../Lists/BlockFileList/BlockFileList.tsx | 28 + .../Lists/CommonFileList/CommonFileList.tsx | 27 + .../InputBlocksFileList.tsx | 48 + .../Lists/InputFileList/InputFileList.tsx | 36 + .../Lists/MixedFileList/MixedFileList.tsx | 54 + .../components/FileInput/components/index.tsx | 15 + .../Form/CurrencySelect/CurrencySelect.tsx | 57 + .../InputWithPercent/InputWithPercent.tsx | 56 + .../Form/Input/MyInput/MyInput.stories.tsx | 47 + .../components/Form/Input/MyInput/MyInput.tsx | 194 + .../components/ErrorIcon/ErrorIcon.tsx | 41 + .../components/IconWrapper/IconWrapper.tsx | 34 + .../MyInput/components/Loader/Loader.tsx | 42 + .../VisibilityIcon/VisibilityIcon.tsx | 28 + .../Form/Input/MyInput/components/index.tsx | 3 + .../Input/MyInputNumber/MyInputNumber.tsx | 133 + .../MyInputWithLimitedLength.tsx | 81 + .../OutlinedSearchInput.stories.tsx | 28 + .../OutlinedSearchInput.tsx | 128 + .../Form/Input/SearchInput/SearchInput.tsx | 218 + .../components/StyledInput/StyledInput.tsx | 138 + .../StyledInputWrapper/StyledInputWrapper.tsx | 33 + .../Form/Input/components/index.tsx | 2 + .../Form/KeyValueInput/KeyValueInput.tsx | 100 + .../MultiselectWithCheckboxes.tsx | 214 + .../MultiselectWithChekboxes.stories.tsx | 25 + .../MultiselectCheckIcon.tsx | 29 + .../MultiselectOptionItem.tsx | 103 + .../MultiselectOptionsList.tsx | 198 + .../components/index.tsx | 3 + .../Form/MyCheckbox/MyCheckbox.stories.tsx | 70 + .../components/Form/MyCheckbox/MyCheckbox.tsx | 198 + .../MyCheckbox/MyCheckboxWithBooleanModel.tsx | 36 + .../MyCheckboxWithCheckboxModel.tsx | 48 + .../Form/MyChecklist/MyChecklist.tsx | 76 + .../MyMultiselectColored.stories.tsx | 24 + .../MyMultiselectColored.tsx | 259 + .../MyMultiselectColoredTag.tsx | 99 + .../Form/MyMultiselect/components/index.tsx | 1 + .../Form/MyRadio/MyRadio.stories.tsx | 47 + .../lib/components/Form/MyRadio/MyRadio.tsx | 125 + .../MySelect/MySelect/MySelect.stories.tsx | 43 + .../Form/MySelect/MySelect/MySelect.tsx | 356 + .../MySelectColored.stories.tsx | 25 + .../MySelectColored/MySelectColored.tsx | 170 + .../MySelectCustomTemplate.tsx | 149 + .../MySelect/MyUsersSelect/MyUsersSelect.tsx | 133 + .../components/ClearButton/ClearButton.tsx | 54 + .../components/ColoredBlock/ColoredBlock.tsx | 40 + .../FilledArrowIcon/FilledArrowIcon.tsx | 37 + .../MySelectSearchBlock.tsx | 97 + .../MySelectStyledDropdown.tsx | 24 + .../MySelectTitle/MySelectTitle.tsx | 497 + .../NoOptionsMessage/NoOptionsMessage.tsx | 17 + .../Form/MySelect/components/index.tsx | 6 + .../MySwitch/MySwitch/MySwitch.stories.tsx | 32 + .../Form/MySwitch/MySwitch/MySwitch.tsx | 172 + .../MySwitchWithModel/MySwitchWithModel.tsx | 67 + .../components/Form/MyTextarea/MyTextArea.tsx | 256 + .../MyTimePickerInput/MyTimePickerInput.tsx | 146 + .../TimePickerSelectMenu.tsx | 152 + .../MyTimePickerInput/components/index.tsx | 1 + .../Form/PhoneFieldInput/PhoneFieldInput.tsx | 263 + .../PhoneNumberInput/PhoneNumberInput.tsx | 116 + .../components/Form/UserPicker/UserPicker.tsx | 250 + .../components/CheckIcon/CheckIcon.tsx | 31 + .../SelectAllBlock/SelectAllBlock.tsx | 62 + .../UserDropdownItem/UserDropdownItem.tsx | 147 + .../components/UserList/UserList.tsx | 481 + .../components/UserView/UserView.tsx | 49 + .../Form/UserPicker/components/index.tsx | 5 + .../WeekIntervalsInput/WeekIntervalsInput.tsx | 191 + .../components/AddItemForm/AddItemForm.tsx | 113 + .../Form/components/Form.styles.tsx | 93 + .../Form/components/FormItem/FormItem.tsx | 103 + .../FormItemLabel/FormItemLabel.tsx | 26 + .../SelectOptionItem/SelectOptionItem.tsx | 143 + .../SelectOptionsList/SelectOptionsList.tsx | 47 + .../lib/components/Form/components/index.tsx | 8 + .../FunctionalTextEditor.stories.tsx | 56 + .../FunctionalTextEditor.tsx | 458 + .../Buttons/RightButton/RightButton.tsx | 100 + .../AddFileControl/AddFileControl.tsx | 30 + .../FormatTextControl/FormatTextControl.tsx | 41 + .../InsertEmojiControl/InsertEmojiControl.tsx | 36 + .../components/Toolbar/Toolbar.tsx | 58 + .../FunctionalTextEditor/components/index.tsx | 5 + .../HeaderRoundButton/HeaderRoundButton.tsx | 90 + .../lib/components/Hint/Hint.stories.tsx | 19 + .../src/shared/lib/components/Hint/Hint.tsx | 64 + .../DoubleRightLegacy/DoubleRightLegacy.tsx | 22 + .../PlusIconSquareLegacy.tsx | 22 + .../Icons/SearchLegacy/SearchLegacy.tsx | 12 + .../Icons/SentIconLegacy/SentIconLegacy.tsx | 17 + .../ImagePreviewModal/ImagePreviewModal.tsx | 79 + .../LinkContactModal/LinkContactModal.tsx | 189 + .../LinkedEntityTag.stories.tsx | 20 + .../LinkedEntityTag/LinkedEntityTag.tsx | 68 + .../DefaultLoader/DefaultLoader.stories.tsx | 17 + .../Loaders/DefaultLoader/DefaultLoader.tsx | 86 + .../Loaders/MiniLoader/MiniLoader.stories.tsx | 25 + .../Loaders/MiniLoader/MiniLoader.tsx | 96 + .../ModalLoader/ModalLoader.stories.tsx | 12 + .../Loaders/ModalLoader/ModalLoader.tsx | 12 + .../Loaders/SquaresLoader/SquaresLoader.tsx | 48 + .../WholePageLoaderWithLogo.stories.tsx | 16 + .../WholePageLoaderWithLogo.tsx | 124 + .../components/LogoLink/LogoLink.stories.tsx | 16 + .../lib/components/LogoLink/LogoLink.tsx | 95 + .../DialogModalPrimary.stories.tsx | 33 + .../DialogModalPrimary/DialogModalPrimary.tsx | 313 + .../DialogModalSecondary.stories.tsx | 34 + .../DialogModalSecondary.tsx | 240 + .../FileSizeWarningModal.tsx | 32 + .../Modals/ImageEditModal/ImageEditModal.tsx | 129 + .../OverlayingModal/OverlayingModal.tsx | 116 + .../lib/components/Modals/Portal/Portal.tsx | 29 + .../RequestAdditionalStorageFormModal.tsx | 213 + .../RequestBpmnFormModal.tsx | 170 + .../UpdateModal/ReloadModal/ReloadModal.tsx | 31 + .../Modals/UpdateModal/UpdateModal.tsx | 91 + .../UpdateModal/VersionModal/VersionModal.tsx | 25 + .../AddUserToPlanModal/AddUserToPlanModal.tsx | 102 + .../RequestBillingFormModal.tsx | 209 + .../SubscriptionPeriodOverModal.tsx | 145 + .../WarningModal/WarningModal.stories.tsx | 28 + .../Modals/WarningModal/WarningModal.tsx | 77 + .../HeadlessFormItem/HeadlessFormItem.tsx | 25 + .../ModalAnnotation/ModalAnnotation.tsx | 14 + .../ModalContentTitle/ModalContentTitle.tsx | 10 + .../ModalTrashBinIcon/ModalTrashBinIcon.tsx | 21 + .../ModalWarningIcon/ModalWarningIcon.tsx | 21 + .../components/Modals/components/index.tsx | 5 + .../MultitextDuplicatesManager.tsx | 124 + .../DuplicatesForbiddenModal.tsx | 33 + .../DuplicatesWarningModal.tsx | 34 + .../components/index.tsx | 2 + .../MyColorPicker/MyColorPicker.tsx | 97 + .../MyColorPickerDefaultButton.tsx | 72 + .../MyColorPicker/components/index.tsx | 1 + .../MyDatePeriodPicker/MyDatePeriodPicker.tsx | 144 + .../DatePeriodSegmentTooltip.tsx | 19 + .../DatePeriodSegmentedControl.tsx | 80 + .../MyDatePeriodPicker/components/index.tsx | 1 + .../MyDatePicker/MyDatePicker.tsx | 48 + .../MyDatePickerDropdown.tsx | 61 + .../MyDatePickerRange/MyDatePickerRange.tsx | 109 + .../MyDatePickerSelect/MyDatePickerSelect.tsx | 143 + .../MyDatePickerWithTime.tsx | 364 + .../DatePickerSelectCalendarIcon.tsx | 28 + .../StyledDatePicker/StyledDatePicker.tsx | 42 + .../MyDatePicker/components/index.tsx | 3 + .../components/MyDrawer/MyDrawer.stories.tsx | 34 + .../lib/components/MyDrawer/MyDrawer.tsx | 236 + .../MyDrawerHeaderTitle.tsx | 8 + .../components/MyDrawer/components/index.tsx | 1 + .../lib/components/MyDropdown/MyDropdown.tsx | 47 + .../MyDropdownItem/MyDropdownItem.tsx | 74 + .../MyDropdownList/MyDropdownList.tsx | 131 + .../MyDropdownTitleWrapper.tsx | 9 + .../MyDropdown/components/index.tsx | 2 + .../MyEmojiPicker/MyEmojiPicker.stories.tsx | 20 + .../MyEmojiPicker/MyEmojiPicker.tsx | 126 + .../MyHoverCard/MyHoverCard.stories.tsx | 53 + .../components/MyHoverCard/MyHoverCard.tsx | 93 + .../MyIndicator/MyIndicator.stories.tsx | 24 + .../components/MyIndicator/MyIndicator.tsx | 101 + .../components/UnseenCount/UnseenCount.tsx | 15 + .../MyMacScrollbar/MyMacScrollbar.tsx | 14 + .../MyPopover/MyPopover.stories.tsx | 48 + .../lib/components/MyPopover/MyPopover.tsx | 128 + .../MySegmentedControl/MySegmentedControl.tsx | 57 + .../MyFloatingTooltip/MyFloatingTooltip.tsx | 22 + .../MyTooltip/MyTooltip/MyTooltip.stories.tsx | 28 + .../MyTooltip/MyTooltip/MyTooltip.tsx | 76 + .../PageSecondaryHeader.tsx | 46 + .../components/PageTracker/PageTracker.tsx | 7 + .../ParticipantsSelect/ParticipantsSelect.tsx | 204 + .../ParticipantsSelectWithCreateButton.tsx | 63 + .../ParticipantsAvatarRows.tsx | 330 + .../ParticipantsSelect/components/index.tsx | 1 + .../PdfViewerModal/PdfViewerModal.tsx | 81 + .../components/PencilButton/PencilButton.tsx | 122 + .../PickerButton/PickerButton.stories.tsx | 22 + .../components/PickerButton/PickerButton.tsx | 272 + .../RoundedDashedFrame/RoundedDashedFrame.tsx | 102 + .../PickerButton/components/index.tsx | 2 + .../PlannedTimePicker/PlannedTimePicker.tsx | 180 + .../PlannedTimeNumberInput.tsx | 144 + .../PlannedTimePicker/components/index.tsx | 1 + .../shared/lib/components/Player/Player.tsx | 383 + .../PlayerControls/PlayerControls.tsx | 72 + .../PlayerPlaybackSelect.tsx | 105 + .../PlayerSkeleton/PlayerSkeleton.tsx | 63 + .../components/PlayerTrack/PlayerTrack.tsx | 96 + .../components/Player/components/index.tsx | 4 + .../components/ProfileModal/ProfileModal.tsx | 457 + .../components/AvatarBlock/AvatarBlock.tsx | 7 + .../components/AvatarButton/AvatarButton.tsx | 50 + .../ChangePasswordBlock.tsx | 65 + .../components/FormWrapper/FormWrapper.tsx | 10 + .../MainInfoBlock/MainInfoBlock.tsx | 9 + .../MainInfoWrapper/MainInfoWrapper.tsx | 10 + .../ProfileModalFormItem.tsx | 27 + .../ProfileModalSkeleton.tsx | 99 + .../SecondaryInfoBlock/SecondaryInfoBlock.tsx | 8 + .../ProfileModal/components/index.tsx | 12 + .../lib/components/Scrollbar/Scrollbar.tsx | 75 + .../SectionPagination/SectionPagination.tsx | 79 + .../ShowMoreButton/ShowMoreButton.stories.tsx | 20 + .../ShowMoreButton/ShowMoreButton.tsx | 97 + .../shared/lib/components/Sidebar/Sidebar.tsx | 314 + .../SidebarHoverCard/SidebarHoverCard.tsx | 38 + .../components/SidebarItem/SidebarItem.tsx | 127 + .../SidebarItemBoard/SidebarItemBoard.tsx | 106 + .../SidebarItemCategory.tsx | 112 + .../SidebarItemSkeleton.tsx | 54 + .../SidebarMailItem/SidebarMailItem.tsx | 188 + .../components/Sidebar/components/index.tsx | 5 + .../SpanWithEllipsis/SpanWithEllipsis.tsx | 75 + .../components/StagesSelect/StagesSelect.tsx | 478 + .../lib/components/StyledLink/StyledLink.tsx | 19 + .../lib/components/Subheader/Subheader.tsx | 106 + .../components/Subheader/SubheaderButton.tsx | 126 + .../components/SubheaderTab/SubheaderTab.tsx | 139 + .../SubheaderTabs/SubheaderTabs.tsx | 62 + .../SubheaderTabsSkeleton.tsx | 32 + .../components/Subheader/components/index.tsx | 1 + .../TabSelector/TabSelector.stories.tsx | 40 + .../components/TabSelector/TabSelector.tsx | 36 + .../TabSelector/components/Tab/Tab.tsx | 151 + .../TabSelectorSkeleton.tsx | 17 + .../TabSelector/components/index.tsx | 2 + .../TableSkeleton/TableSkeleton.tsx | 109 + .../TextHighlighter/TextHighlighter.tsx | 74 + .../ToastContainer/ToastContainer.tsx | 22 + .../components/TotalTag/TotalTag.stories.tsx | 19 + .../lib/components/TotalTag/TotalTag.tsx | 30 + .../UsersMultiselect/UsersMultiselect.tsx | 94 + .../UsersMultiselectDropdown.tsx | 134 + .../VideoPlayerModal/VideoPlayerModal.tsx | 74 + frontend/src/shared/lib/components/index.tsx | 244 + .../CreateButtonSkeleton.tsx | 24 + .../skeletons/LabelSkeleton/LabelSkeleton.tsx | 11 + .../ModuleNameSkeleton/ModuleNameSkeleton.tsx | 40 + .../MyCheckboxSkeleton/MyCheckboxSkeleton.tsx | 33 + .../MyRadioSkeleton/MyRadioSkeleton.tsx | 33 + .../PickerButtonSkeleton.tsx | 43 + .../RoundButtonSkeleton.tsx | 26 + .../lib/helpers/CurrencyFormatterHelper.ts | 81 + .../src/shared/lib/helpers/JsonStateHelper.ts | 43 + .../lib/helpers/areRoutesPathnamesEqual.ts | 7 + .../shared/lib/helpers/arraysShallowEqual.ts | 16 + .../src/shared/lib/helpers/batchRequest.ts | 18 + .../helpers/calculateEndOfWordIdxByNumber.ts | 16 + .../lib/helpers/capitalizeFirstLetter.ts | 3 + .../lib/helpers/checkDatesRangesOverlap.ts | 13 + frontend/src/shared/lib/helpers/clamp.ts | 5 + .../lib/helpers/dangerouslySetQueryParams.ts | 15 + frontend/src/shared/lib/helpers/debounce.ts | 16 + frontend/src/shared/lib/helpers/delay.ts | 1 + .../src/shared/lib/helpers/delayResolve.ts | 16 + .../helpers/extractFileExtensionFromType.ts | 19 + .../src/shared/lib/helpers/followElement.ts | 19 + .../src/shared/lib/helpers/formatBytes.ts | 11 + .../src/shared/lib/helpers/formatSeconds.ts | 10 + .../helpers/formatSecondsToHoursAndMinutes.ts | 17 + .../generateCountryCodeFromLanguage.ts | 21 + .../shared/lib/helpers/generateMockOptions.ts | 13 + .../helpers/generateMyDatePickerRangeTitle.ts | 24 + .../lib/helpers/generateMyDatePickerTitle.ts | 10 + .../helpers/generateSegmentedControlItems.ts | 24 + .../lib/helpers/getBlockFileItemIcon.tsx | 10 + .../shared/lib/helpers/getDHMSFromSeconds.ts | 23 + .../lib/helpers/getFileFeedItemIcon.tsx | 23 + .../getProperFromAndToDatesForFilter.ts | 35 + .../src/shared/lib/helpers/getTimeString.ts | 16 + frontend/src/shared/lib/helpers/index.tsx | 43 + .../lib/helpers/insertBrTagsInsteadEmptyPs.ts | 5 + .../shared/lib/helpers/insertTextAtCaret.ts | 24 + .../src/shared/lib/helpers/isFileTypeImage.ts | 4 + .../src/shared/lib/helpers/isFileTypePdf.ts | 2 + .../src/shared/lib/helpers/isFileTypeVideo.ts | 4 + .../lib/helpers/isPathnameOnEntitySection.ts | 14 + .../shared/lib/helpers/removeBrTagsFromStr.ts | 5 + .../lib/helpers/renderTodayWithIndicator.tsx | 18 + .../src/shared/lib/helpers/setCaretToPos.ts | 11 + .../lib/helpers/setTextareaSelectionRange.ts | 12 + .../src/shared/lib/helpers/shallowEqual.ts | 17 + frontend/src/shared/lib/helpers/throttle.ts | 16 + .../src/shared/lib/helpers/truncateNumber.ts | 19 + .../shared/lib/helpers/useGetPeriodOptions.ts | 50 + .../shared/lib/helpers/validateColorHex.ts | 1 + .../src/shared/lib/helpers/validateForm.ts | 58 + frontend/src/shared/lib/hooks/index.tsx | 29 + .../shared/lib/hooks/useAutoFocusOnMount.ts | 22 + .../lib/hooks/useCheckBrowserSupport.ts | 26 + .../lib/hooks/useCheckProjectOwnerOrAdmin.ts | 57 + .../lib/hooks/useConditionalWindowScroll.ts | 28 + .../src/shared/lib/hooks/useDropdownWidth.ts | 18 + .../shared/lib/hooks/useEditorOptimized.ts | 45 + .../shared/lib/hooks/useErrorMessageIdle.ts | 29 + .../lib/hooks/useFadedHorizontalScroll.ts | 43 + .../hooks/useGetDHMDateStringFromSeconds.ts | 45 + .../lib/hooks/useGetEntityEmailOptions.tsx | 55 + .../lib/hooks/useGetLocalBusinessHours.ts | 20 + .../lib/hooks/useGetMediaBlobObjectUrl.ts | 19 + .../src/shared/lib/hooks/useGrabScroll.ts | 58 + frontend/src/shared/lib/hooks/useHasMore.ts | 15 + .../src/shared/lib/hooks/useLeftBlockWidth.ts | 24 + frontend/src/shared/lib/hooks/useMobile.ts | 4 + .../src/shared/lib/hooks/useModalControl.ts | 23 + .../src/shared/lib/hooks/usePageTracking.ts | 11 + frontend/src/shared/lib/hooks/usePersistFn.ts | 16 + .../lib/hooks/useQueryParamModalControl.ts | 30 + .../shared/lib/hooks/useScrollWindowToTop.ts | 10 + frontend/src/shared/lib/hooks/useSendEmail.ts | 45 + frontend/src/shared/lib/hooks/useSize.ts | 34 + frontend/src/shared/lib/hooks/useTitle.tsx | 19 + .../src/shared/lib/hooks/useToggleControl.ts | 25 + .../shared/lib/hooks/useTracePropsUpdate.ts | 29 + .../shared/lib/hooks/useTransformScroll.ts | 33 + .../src/shared/lib/hooks/useTypedParams.ts | 19 + .../src/shared/lib/hooks/useUploadFiles.ts | 142 + frontend/src/shared/lib/index.tsx | 8 + .../lib/mixins/DropdownScrollbar.mixin.tsx | 27 + .../mixins/FadedHorizontalScroll.mixin.tsx | 36 + .../shared/lib/mixins/HideScrollbar.mixin.tsx | 36 + .../lib/mixins/InnerHTMLNormalizer.mixin.tsx | 161 + .../src/shared/lib/mixins/NoSelect.mixin.tsx | 8 + .../lib/mixins/SkeletonAnimation.mixin.tsx | 22 + .../mixins/SkeletonAnimationLegacy.mixin.tsx | 15 + .../lib/mixins/TableScrollbarMixin.mixin.tsx | 26 + .../src/shared/lib/mixins/Truncate.mixin.tsx | 7 + frontend/src/shared/lib/mixins/index.tsx | 12 + .../src/shared/lib/models/Auth/JwtToken.ts | 9 + .../lib/models/Automation/ChangeStageType.ts | 5 + .../models/Automation/EntityTypeActionType.ts | 12 + .../models/Automation/EntityTypeTrigger.ts | 5 + frontend/src/shared/lib/models/Board/Board.ts | 60 + .../src/shared/lib/models/Board/BoardType.ts | 4 + .../src/shared/lib/models/BusinessHours.ts | 4 + .../lib/models/Button/PrimaryButtonVariant.ts | 10 + .../src/shared/lib/models/CalendarView.tsx | 6 + .../shared/lib/models/CallInfo/CallInfo.ts | 82 + .../shared/lib/models/CallInfo/ContactInfo.ts | 30 + .../shared/lib/models/CallInfo/EntityInfo.ts | 35 + .../CommonQueryParams/CommonQueryParams.ts | 10 + .../src/shared/lib/models/CrmEventType.ts | 5 + .../Dadata/DadataBankRequisitesOpfType.ts | 18 + .../Dadata/DadataBankRequisitesSuggestion.ts | 16 + .../Dadata/DadataOrgRequisitesSuggestion.ts | 50 + ...DadataOrgRequisitesSuggestionBranchType.ts | 6 + .../DadataOrgRequisitesSuggestionStatus.ts | 12 + .../DadataOrgRequisitesSuggestionType.ts | 6 + .../models/Dadata/DadataSuggestionsType.ts | 4 + frontend/src/shared/lib/models/DataStore.ts | 8 + frontend/src/shared/lib/models/DateFormat.ts | 8 + .../lib/models/DatePeriodFilterModel.ts | 18 + .../shared/lib/models/DatePeriodFilterType.ts | 12 + .../models/DatePeriodType/DatePeriodType.ts | 5 + .../src/shared/lib/models/DayNumberMap.ts | 12 + .../lib/models/DepartmentWithUsersOption.ts | 11 + .../models/DraggableResizableControlBounds.ts | 16 + .../models/Entity/EntitiesBoardSettingsTab.ts | 4 + .../src/shared/lib/models/Entity/Entity.ts | 211 + .../shared/lib/models/Entity/EntityInfo.ts | 51 + .../shared/lib/models/Entity/EntityLink.ts | 68 + .../lib/models/Entity/ExternalEntity.ts | 23 + .../lib/models/Entity/ExternalSystemCode.ts | 22 + .../lib/models/EntityCreatedAtFilter.ts | 13 + .../lib/models/EntityType/EntityCategory.ts | 15 + .../lib/models/EntityType/EntityType.ts | 176 + .../lib/models/EntityType/EntityTypeLink.ts | 4 + .../shared/lib/models/EntityType/Section.ts | 8 + .../src/shared/lib/models/Error/ErrorCode.ts | 23 + .../shared/lib/models/Error/ServiceError.ts | 7 + .../shared/lib/models/Event/EntityEvent.ts | 8 + .../shared/lib/models/Feature/FeatureCode.ts | 9 + .../src/shared/lib/models/Feed/FeedGroup.ts | 7 + .../src/shared/lib/models/Feed/FeedItem.ts | 81 + .../shared/lib/models/Feed/FeedItemFilter.ts | 9 + .../shared/lib/models/Field/FieldFormat.ts | 5 + .../src/shared/lib/models/Field/FieldType.ts | 23 + .../src/shared/lib/models/File/FileModel.ts | 9 + .../shared/lib/models/FileInfo/FileInfo.tsx | 47 + .../shared/lib/models/FileLink/FileLink.ts | 33 + .../shared/lib/models/Filter/BooleanFilter.ts | 3 + .../models/Filter/BooleanFilterFormData.ts | 31 + .../shared/lib/models/Filter/DateFilter.ts | 4 + .../lib/models/Filter/DateFilterFormData.ts | 46 + .../shared/lib/models/Filter/ExistsFilter.ts | 5 + .../lib/models/Filter/ExistsFilterFormData.ts | 32 + .../lib/models/Filter/ExistsFilterType.ts | 4 + .../shared/lib/models/Filter/NumberFilter.ts | 4 + .../lib/models/Filter/NumberFilterFormData.ts | 37 + .../lib/models/Filter/PossibleFilter.ts | 14 + .../models/Filter/PossibleFilterFormData.ts | 14 + .../shared/lib/models/Filter/SelectFilter.ts | 3 + .../lib/models/Filter/SelectFilterFormData.ts | 31 + .../lib/models/Filter/SimpleFilterType.ts | 8 + .../shared/lib/models/Filter/StringFilter.ts | 7 + .../lib/models/Filter/StringFilterFormData.ts | 40 + .../lib/models/Filter/StringFilterType.ts | 5 + .../shared/lib/models/Form/BooleanModel.ts | 48 + .../lib/models/Form/CheckedInputModel.ts | 27 + .../lib/models/Form/KeyValueListModel.ts | 91 + .../models/Form/MyCheckbox/CheckboxModel.ts | 62 + .../Form/MyCheckbox/MyCheckboxVariant.ts | 1 + .../lib/models/Form/MyInput/InputModel.ts | 206 + .../models/Form/MyInput/MyInputFontSize.ts | 1 + .../lib/models/Form/MyInput/MyInputVariant.ts | 1 + .../Form/MyMultiselect/MultiselectModel.ts | 85 + .../models/Form/MyRadio/MyRadioColorType.ts | 1 + .../Form/MySelect/MySelectOptionValueType.ts | 3 + .../Form/MySelect/MySelectTitleRootVariant.ts | 10 + .../lib/models/Form/MySelect/SelectModel.ts | 63 + .../src/shared/lib/models/Form/NumberModel.ts | 28 + .../TimePickerSelectMenuPopoverProps.ts | 9 + .../models/Form/Validation/ValidationCode.ts | 14 + .../models/Form/Validation/ValidationError.ts | 9 + .../models/Form/Validation/ValidationFunc.ts | 1 + .../models/Form/Validation/ValidationRule.ts | 143 + .../lib/models/Form/WeekIntervalListModel.ts | 98 + .../src/shared/lib/models/FrontendObject.ts | 22 + .../FunctionalTextEditor/FTEShowHTMLProps.ts | 5 + frontend/src/shared/lib/models/HttpMethod.ts | 9 + frontend/src/shared/lib/models/Icon/Icon.ts | 7 + .../src/shared/lib/models/Icon/IconName.ts | 51 + frontend/src/shared/lib/models/Language.ts | 7 + .../src/shared/lib/models/ManualSorting.ts | 13 + .../src/shared/lib/models/MediaBreakpoints.ts | 6 + .../lib/models/Multichat/EntitySettings.tsx | 12 + .../MyDatePicker/MyDatePickerCommonProps.ts | 8 + .../MyDatePicker/MyDatePickerRangeType.ts | 3 + .../lib/models/MyDatePicker/UtcDateValue.ts | 4 + .../models/MyDatePicker/UtcDatesRangeValue.ts | 3 + frontend/src/shared/lib/models/Note/Note.ts | 49 + frontend/src/shared/lib/models/ObjectState.ts | 7 + frontend/src/shared/lib/models/Option.ts | 7 + frontend/src/shared/lib/models/PagingMeta.ts | 9 + .../shared/lib/models/Permission/Action.ts | 8 + .../lib/models/Permission/ObjectPermission.ts | 89 + .../lib/models/Permission/PermissionHelper.ts | 18 + .../lib/models/Permission/PermissionLevel.ts | 7 + .../models/Permission/PermissionObjectType.ts | 10 + .../lib/models/Permission/UserRights.ts | 11 + frontend/src/shared/lib/models/PhoneFormat.ts | 4 + .../shared/lib/models/Profile/UserProfile.ts | 29 + frontend/src/shared/lib/models/RecordState.ts | 11 + .../lib/models/SearchDuplicatesProps.ts | 6 + .../shared/lib/models/Section/SectionView.ts | 8 + .../lib/models/SiteForm/SiteFormResult.ts | 19 + frontend/src/shared/lib/models/Stage/Stage.ts | 65 + .../src/shared/lib/models/Stage/StageCode.ts | 9 + .../src/shared/lib/models/Stage/StageGroup.ts | 8 + .../models/SubdepartmentWithUsersOption.ts | 7 + .../src/shared/lib/models/SubscriberStore.ts | 8 + .../lib/models/Subscription/AppSumoTiers.ts | 7 + .../models/Subscription/BillingInterval.ts | 4 + .../lib/models/Subscription/Subscription.ts | 48 + .../Subscription/SubscriptionFeature.ts | 19 + .../models/Subscription/SubscriptionPlan.ts | 64 + .../models/Subscription/SubscriptionPrice.ts | 29 + .../src/shared/lib/models/Tabs/TabModel.ts | 10 + .../src/shared/lib/models/TaskTimeStatus.ts | 6 + frontend/src/shared/lib/models/Time.ts | 6 + .../models/Tutorial/TutorialProductType.ts | 10 + frontend/src/shared/lib/models/User/Avatar.ts | 15 + frontend/src/shared/lib/models/User/User.ts | 186 + .../src/shared/lib/models/User/UserRole.ts | 6 + frontend/src/shared/lib/models/UtcDate.ts | 617 + .../src/shared/lib/models/Version/Version.ts | 22 + frontend/src/shared/lib/models/WeekDays.ts | 9 + frontend/src/shared/lib/models/constants.ts | 1 + frontend/src/shared/lib/models/index.tsx | 138 + .../lib/services/LastSchedulerService.ts | 52 + .../shared/lib/services/LastSectionService.ts | 53 + .../lib/services/LastTasksBoardService.ts | 32 + .../shared/lib/services/ServerEventService.ts | 33 + .../src/shared/lib/services/StorageService.ts | 13 + frontend/src/shared/lib/services/index.tsx | 5 + frontend/src/shared/lib/types/AnyObject.ts | 3 + frontend/src/shared/lib/types/BillingPath.ts | 4 + .../lib/types/CalculateActiveStrategy.ts | 6 + frontend/src/shared/lib/types/CommonFields.ts | 5 + frontend/src/shared/lib/types/CommonKeys.ts | 1 + frontend/src/shared/lib/types/CompanyName.ts | 1 + frontend/src/shared/lib/types/Currency.ts | 275 + .../shared/lib/types/EntityBoardLinkType.ts | 1 + .../src/shared/lib/types/MinMaxColumnSize.ts | 1 + frontend/src/shared/lib/types/Nullable.ts | 1 + .../src/shared/lib/types/NullableObject.ts | 1 + frontend/src/shared/lib/types/Optional.ts | 1 + .../shared/lib/types/PlayerPlaybackRate.ts | 1 + frontend/src/shared/lib/types/QueryParams.ts | 3 + .../src/shared/lib/types/ShowHideHandlers.ts | 5 + .../shared/lib/types/TimePickerSelectStep.ts | 1 + .../types/VoximplantIntegrationGuideType.ts | 1 + frontend/src/shared/lib/types/index.tsx | 16 + frontend/src/shared/lib/utils/AvatarUtil.ts | 70 + frontend/src/shared/lib/utils/ColorUtil.ts | 388 + .../src/shared/lib/utils/ConvertTimeUtil.ts | 141 + frontend/src/shared/lib/utils/EnvUtil.ts | 813 + frontend/src/shared/lib/utils/FileUtil.ts | 43 + frontend/src/shared/lib/utils/GTMUtil.ts | 127 + .../src/shared/lib/utils/JwtParserUtil.ts | 30 + frontend/src/shared/lib/utils/MathUtil.ts | 21 + frontend/src/shared/lib/utils/MonthUtil.ts | 16 + .../src/shared/lib/utils/SectionLinkUtil.ts | 21 + .../src/shared/lib/utils/TimePickerUtil.ts | 71 + frontend/src/shared/lib/utils/TokenUtil.ts | 30 + .../src/shared/lib/utils/UriCodingUtil.ts | 9 + .../src/shared/lib/utils/UrlTemplateUtil.ts | 13 + frontend/src/shared/lib/utils/UrlUtil.ts | 101 + frontend/src/shared/lib/utils/UuidUtil.ts | 21 + frontend/src/shared/lib/utils/index.tsx | 17 + .../BrowserNotSupportedPage.tsx | 118 + .../pages/System/ErrorPage/ErrorPage.tsx | 173 + .../System/ForbiddenPage/ForbiddenPage.tsx | 50 + .../System/NotFoundPage/NotFoundPage.tsx | 58 + .../ErrorCodeTitle/ErrorCodeTitle.tsx | 49 + .../SystemPageContent/SystemPageContent.tsx | 8 + .../SystemPageRoot/SystemPageRoot.tsx | 11 + .../shared/pages/System/components/index.tsx | 3 + frontend/src/shared/pages/index.tsx | 4 + .../FilterButtonWithClearIconTemplate.tsx | 78 + .../FilterDrawerTemplate.tsx | 37 + .../ControlsErrorMessage.tsx | 42 + .../FilterDelimiter/FilterDelimiter.tsx | 5 + .../FilterDrawerControls/FilterControls.tsx | 64 + .../FilterItemWrapper/FilterItemWrapper.tsx | 59 + .../FilterItemsBlock/FilterItemsBlock.tsx | 9 + .../FilterDrawerTemplate/components/index.tsx | 5 + .../LeftNavTemplate/LeftNavTemplate.tsx | 102 + .../MailingAndChatPageTemplate.tsx | 69 + .../PageTemplateWithSubheader.tsx | 63 + frontend/src/shared/templates/index.tsx | 11 + frontend/tsconfig.json | 42 + frontend/vite.config.ts | 71 + frontend/yarn.lock | 9659 ++++++ nginx.conf | 126 + start.sh | 13 + stop.sh | 11 + 7657 files changed, 497012 insertions(+) create mode 100644 .gitignore create mode 100644 backend/.eslintrc.js.old create mode 100644 backend/.gitignore create mode 100644 backend/.gitlab-ci.yml create mode 100644 backend/.prettierrc create mode 100644 backend/.yarn/releases/yarn-4.9.1.cjs create mode 100644 backend/.yarnrc.yml create mode 100644 backend/README.md create mode 100644 backend/artifacts/_environment-config/http-client.env.json.dist create mode 100644 backend/artifacts/_resources/test.png create mode 100644 backend/artifacts/account-settings.http create mode 100644 backend/artifacts/auth.http create mode 100644 backend/artifacts/automation/activity-automation.http create mode 100644 backend/artifacts/automation/automation.http create mode 100644 backend/artifacts/automation/change-stage-automation.http create mode 100644 backend/artifacts/automation/email-automation.http create mode 100644 backend/artifacts/automation/task-automation.http create mode 100644 backend/artifacts/board.http create mode 100644 backend/artifacts/constructor.http create mode 100644 backend/artifacts/crm.http create mode 100644 backend/artifacts/demo.http create mode 100644 backend/artifacts/entity-type.http create mode 100644 backend/artifacts/entity.http create mode 100644 backend/artifacts/extension.http create mode 100644 backend/artifacts/feed-items.http create mode 100644 backend/artifacts/feedback.http create mode 100644 backend/artifacts/field-settings.http create mode 100644 backend/artifacts/field-value.http create mode 100644 backend/artifacts/fields.http create mode 100644 backend/artifacts/file-link.http create mode 100644 backend/artifacts/forms.http create mode 100644 backend/artifacts/identity.http create mode 100644 backend/artifacts/import.http create mode 100644 backend/artifacts/mail-messages.http create mode 100644 backend/artifacts/mailbox-settings.http create mode 100644 backend/artifacts/mailbox.http create mode 100644 backend/artifacts/migration.http create mode 100644 backend/artifacts/module/module.http create mode 100644 backend/artifacts/note.http create mode 100644 backend/artifacts/notification.http create mode 100644 backend/artifacts/partner.http create mode 100644 backend/artifacts/products/order-status.http create mode 100644 backend/artifacts/products/order.http create mode 100644 backend/artifacts/products/product-category.http create mode 100644 backend/artifacts/products/product-price.http create mode 100644 backend/artifacts/products/product.http create mode 100644 backend/artifacts/products/shipment-status.http create mode 100644 backend/artifacts/products/shipment.http create mode 100644 backend/artifacts/products/stock.http create mode 100644 backend/artifacts/products/warehouse.http create mode 100644 backend/artifacts/rabbit.http create mode 100644 backend/artifacts/rms.http create mode 100644 backend/artifacts/stage.http create mode 100644 backend/artifacts/storage/file-link.http create mode 100644 backend/artifacts/storage/file.http create mode 100644 backend/artifacts/subtasks.http create mode 100644 backend/artifacts/task-comment-like.http create mode 100644 backend/artifacts/task-comment.http create mode 100644 backend/artifacts/task-settings.http create mode 100644 backend/artifacts/task-type.http create mode 100644 backend/artifacts/task.http create mode 100644 backend/artifacts/user.http create mode 100644 backend/doc/camunda.md create mode 100644 backend/doc/commands.md create mode 100644 backend/doc/setup-local.md create mode 100644 backend/eslint.config.js create mode 100644 backend/knip.json create mode 100644 backend/nest-cli.json create mode 100644 backend/newrelic.js create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/src/CRM/Controller/Entity/Board/Filter/EntityBoardCardFilter.ts create mode 100644 backend/src/CRM/Controller/Entity/Board/Filter/EntityFieldFilter.ts create mode 100644 backend/src/CRM/Controller/Entity/Board/Filter/EntitySorting.ts create mode 100644 backend/src/CRM/Controller/Entity/Board/Filter/entity-task-filter.enum.ts create mode 100644 backend/src/CRM/Controller/Entity/Board/entity-board.controller.ts create mode 100644 backend/src/CRM/Controller/Entity/CreateEntityController.ts create mode 100644 backend/src/CRM/Controller/Entity/CreateSimpleEntityController.ts create mode 100644 backend/src/CRM/Controller/Entity/DeleteEntityController.ts create mode 100644 backend/src/CRM/Controller/Entity/Documents/GetEntityDocumentsController.ts create mode 100644 backend/src/CRM/Controller/Entity/Files/AddEntityFilesController.ts create mode 100644 backend/src/CRM/Controller/Entity/Files/GetEntityFilesController.ts create mode 100644 backend/src/CRM/Controller/Entity/GetEntityController.ts create mode 100644 backend/src/CRM/Controller/Entity/Import/GetEntitiesImportTemplateController.ts create mode 100644 backend/src/CRM/Controller/Entity/Import/UploadEntitiesImportController.ts create mode 100644 backend/src/CRM/Controller/Entity/List/entity-list.controller.ts create mode 100644 backend/src/CRM/Controller/Entity/UpdateEntityController.ts create mode 100644 backend/src/CRM/Controller/Entity/UpdateEntityFieldController.ts create mode 100644 backend/src/CRM/Controller/ExternalEntity/CreateExternalLinkController.ts create mode 100644 backend/src/CRM/Controller/FileLink/DeleteFileLinkController.ts create mode 100644 backend/src/CRM/Controller/FileLink/DeleteFileLinksController.ts create mode 100644 backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarController.ts create mode 100644 backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarMetaController.ts create mode 100644 backend/src/CRM/Controller/TimeBoard/GetTimeBoardController.ts create mode 100644 backend/src/CRM/Controller/TimeBoard/GetTimeBoardItemController.ts create mode 100644 backend/src/CRM/Controller/TimeBoard/GetTimeBoardMetaController.ts create mode 100644 backend/src/CRM/Model/Entity/Entity.ts create mode 100644 backend/src/CRM/Model/ExternalEntity/ExternalEntity.ts create mode 100644 backend/src/CRM/Model/ExternalEntity/ExternalSystem.ts create mode 100644 backend/src/CRM/Model/ExternalEntity/ExternalSystemCode.ts create mode 100644 backend/src/CRM/Model/ExternalEntity/UIDataRecord.ts create mode 100644 backend/src/CRM/Model/FileLink/FileLink.ts create mode 100644 backend/src/CRM/Salesforce/Controller/Auth/AuthCallbackController.ts create mode 100644 backend/src/CRM/Salesforce/Controller/Auth/AuthConnectController.ts create mode 100644 backend/src/CRM/Salesforce/Controller/Auth/AuthDisconnectController.ts create mode 100644 backend/src/CRM/Salesforce/Controller/Settings/CreateSettingsController.ts create mode 100644 backend/src/CRM/Salesforce/Controller/Settings/DeleteSettingsController.ts create mode 100644 backend/src/CRM/Salesforce/Controller/Settings/GetSettingsController.ts create mode 100644 backend/src/CRM/Salesforce/Model/IntegrationData.ts create mode 100644 backend/src/CRM/Salesforce/Model/Settings/SalesforceSettings.ts create mode 100644 backend/src/CRM/Salesforce/SalesforceModule.ts create mode 100644 backend/src/CRM/Salesforce/Service/SalesforceIntegrationService.ts create mode 100644 backend/src/CRM/Salesforce/Service/Settings/CreateSalesforceSettingsDto.ts create mode 100644 backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsDto.ts create mode 100644 backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsService.ts create mode 100644 backend/src/CRM/Salesforce/Util/SalesforceCodeToType.ts create mode 100644 backend/src/CRM/Salesforce/Util/SalesforceUIFields.ts create mode 100644 backend/src/CRM/Service/BaseTaskBoard/BaseTaskBoardFilter.ts create mode 100644 backend/src/CRM/Service/BaseTaskBoard/TaskBoardQueryHelper.ts create mode 100644 backend/src/CRM/Service/BaseTaskBoard/TaskSorting.ts create mode 100644 backend/src/CRM/Service/BaseTaskBoard/UserTimeAllocation.ts create mode 100644 backend/src/CRM/Service/Entity/CommonEntityBoardCardService.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Batch/delete-entities-batch-filter.dto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Batch/index.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Batch/update-entities-batch-filter.dto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/CommonEntityCard.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/EntityBoardCard.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/EntityBoardMeta.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/EntityBoardStageMeta.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/ProjectEntityCard.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/TasksCount.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Board/index.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/CreateEntityDto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/CreateSimpleEntityDto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/EntityDto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/EntitySimpleDto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Files/AddEntityFilesDto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/Files/index.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/List/EntityListItem.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/List/EntityListMeta.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/List/index.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/UpdateEntityDto.ts create mode 100644 backend/src/CRM/Service/Entity/Dto/index.ts create mode 100644 backend/src/CRM/Service/Entity/EntityBoardService.ts create mode 100644 backend/src/CRM/Service/Entity/EntityService.ts create mode 100644 backend/src/CRM/Service/Entity/ProjectEntityBoardCardService.ts create mode 100644 backend/src/CRM/Service/Entity/entity-service.emitter.ts create mode 100644 backend/src/CRM/Service/Entity/entity-service.handler.ts create mode 100644 backend/src/CRM/Service/Entity/enums/task-indicator.enum.ts create mode 100644 backend/src/CRM/Service/Entity/errors/index.ts create mode 100644 backend/src/CRM/Service/Entity/errors/readonly-field-changed.error.ts create mode 100644 backend/src/CRM/Service/Entity/errors/required-field-empty.error.ts create mode 100644 backend/src/CRM/Service/ExternalEntity/CreateExternalEntityDto.ts create mode 100644 backend/src/CRM/Service/ExternalEntity/CreateExternalEntityResult.ts create mode 100644 backend/src/CRM/Service/ExternalEntity/ExternalEntityDto.ts create mode 100644 backend/src/CRM/Service/ExternalEntity/ExternalEntityService.ts create mode 100644 backend/src/CRM/Service/ExternalEntity/ExternalSystemDto.ts create mode 100644 backend/src/CRM/Service/ExternalEntity/ExternalSystemService.ts create mode 100644 backend/src/CRM/Service/FileLink/FileLinkDto.ts create mode 100644 backend/src/CRM/Service/FileLink/FileLinkService.ts create mode 100644 backend/src/CRM/Service/Import/ImportService.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TaskGroupByTime.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TaskOrActivityCard.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TimeBoardCalendarMeta.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TimeBoardFilter.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TimeBoardMeta.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TimeBoardService.ts create mode 100644 backend/src/CRM/Service/TimeBoard/TimeBoardStageMeta.ts create mode 100644 backend/src/CRM/activity-card/activity-card.controller.ts create mode 100644 backend/src/CRM/activity-card/activity-card.service.ts create mode 100644 backend/src/CRM/activity-card/dto/activity-calendar-meta.dto.ts create mode 100644 backend/src/CRM/activity-card/dto/activity-card-filter.dto.ts create mode 100644 backend/src/CRM/activity-card/dto/activity-card-meta.dto.ts create mode 100644 backend/src/CRM/activity-card/dto/activity-card-type-meta.dto.ts create mode 100644 backend/src/CRM/activity-card/dto/activity-card.dto.ts create mode 100644 backend/src/CRM/activity-card/dto/index.ts create mode 100644 backend/src/CRM/activity-card/index.ts create mode 100644 backend/src/CRM/activity-type/activity-type.controller.ts create mode 100644 backend/src/CRM/activity-type/activity-type.service.ts create mode 100644 backend/src/CRM/activity-type/dto/activity-type.dto.ts create mode 100644 backend/src/CRM/activity-type/dto/create-activity-type.dto.ts create mode 100644 backend/src/CRM/activity-type/dto/index.ts create mode 100644 backend/src/CRM/activity-type/dto/update-activity-type.dto.ts create mode 100644 backend/src/CRM/activity-type/entities/activity-type.entity.ts create mode 100644 backend/src/CRM/activity-type/entities/index.ts create mode 100644 backend/src/CRM/activity-type/index.ts create mode 100644 backend/src/CRM/activity/activity.controller.ts create mode 100644 backend/src/CRM/activity/activity.handler.ts create mode 100644 backend/src/CRM/activity/activity.service.ts create mode 100644 backend/src/CRM/activity/dto/activity.dto.ts create mode 100644 backend/src/CRM/activity/dto/create-activity.dto.ts create mode 100644 backend/src/CRM/activity/dto/index.ts create mode 100644 backend/src/CRM/activity/dto/update-activity.dto.ts create mode 100644 backend/src/CRM/activity/entities/activity.entity.ts create mode 100644 backend/src/CRM/activity/entities/index.ts create mode 100644 backend/src/CRM/activity/index.ts create mode 100644 backend/src/CRM/base-task/base-task.service.ts create mode 100644 backend/src/CRM/base-task/dto/base-task.dto.ts create mode 100644 backend/src/CRM/base-task/dto/create-base-task.dto.ts create mode 100644 backend/src/CRM/base-task/dto/index.ts create mode 100644 backend/src/CRM/base-task/dto/update-base-task.dto.ts create mode 100644 backend/src/CRM/base-task/entities/base-task.entity.ts create mode 100644 backend/src/CRM/base-task/entities/index.ts create mode 100644 backend/src/CRM/base-task/enums/index.ts create mode 100644 backend/src/CRM/base-task/enums/task-view.enum.ts create mode 100644 backend/src/CRM/base-task/index.ts create mode 100644 backend/src/CRM/board-stage/board-stage.controller.ts create mode 100644 backend/src/CRM/board-stage/board-stage.service.ts create mode 100644 backend/src/CRM/board-stage/dto/board-stage.dto.ts create mode 100644 backend/src/CRM/board-stage/dto/create-board-stage.dto.ts create mode 100644 backend/src/CRM/board-stage/dto/index.ts create mode 100644 backend/src/CRM/board-stage/dto/process-board-stage.dto.ts create mode 100644 backend/src/CRM/board-stage/dto/update-board-stage.dto.ts create mode 100644 backend/src/CRM/board-stage/dto/update-board-stages.dto.ts create mode 100644 backend/src/CRM/board-stage/entities/board-stage.entity.ts create mode 100644 backend/src/CRM/board-stage/entities/index.ts create mode 100644 backend/src/CRM/board-stage/enums/board-stage-code.enum.ts create mode 100644 backend/src/CRM/board-stage/enums/board-stage-type.enum.ts create mode 100644 backend/src/CRM/board-stage/enums/index.ts create mode 100644 backend/src/CRM/board-stage/index.ts create mode 100644 backend/src/CRM/board-stage/types/grouped-stages.ts create mode 100644 backend/src/CRM/board-stage/types/index.ts create mode 100644 backend/src/CRM/board/board.controller.ts create mode 100644 backend/src/CRM/board/board.handler.ts create mode 100644 backend/src/CRM/board/board.service.ts create mode 100644 backend/src/CRM/board/dto/board-filter.dto.ts create mode 100644 backend/src/CRM/board/dto/board.dto.ts create mode 100644 backend/src/CRM/board/dto/create-board.dto.ts create mode 100644 backend/src/CRM/board/dto/index.ts create mode 100644 backend/src/CRM/board/dto/update-board.dto.ts create mode 100644 backend/src/CRM/board/entities/board.entity.ts create mode 100644 backend/src/CRM/board/entities/index.ts create mode 100644 backend/src/CRM/board/enums/board-type.enum.ts create mode 100644 backend/src/CRM/board/enums/index.ts create mode 100644 backend/src/CRM/board/index.ts create mode 100644 backend/src/CRM/common/enums/entity-category.enum.ts create mode 100644 backend/src/CRM/common/enums/index.ts create mode 100644 backend/src/CRM/common/enums/permission-object-type.enum.ts create mode 100644 backend/src/CRM/common/enums/sequence-name.enum.ts create mode 100644 backend/src/CRM/common/enums/sort-order.enum.ts create mode 100644 backend/src/CRM/common/events/activity/activity-created.event.ts create mode 100644 backend/src/CRM/common/events/activity/activity.event.ts create mode 100644 backend/src/CRM/common/events/activity/index.ts create mode 100644 backend/src/CRM/common/events/board-stage/board-stage-deleted.event.ts create mode 100644 backend/src/CRM/common/events/board-stage/board-stage.event.ts create mode 100644 backend/src/CRM/common/events/board-stage/index.ts create mode 100644 backend/src/CRM/common/events/board/board.event.ts create mode 100644 backend/src/CRM/common/events/board/index.ts create mode 100644 backend/src/CRM/common/events/crm-event-type.enum.ts create mode 100644 backend/src/CRM/common/events/entity-type/entity-type.event.ts create mode 100644 backend/src/CRM/common/events/entity-type/index.ts create mode 100644 backend/src/CRM/common/events/entity/entity-created.event.ts create mode 100644 backend/src/CRM/common/events/entity/entity-import.event.ts create mode 100644 backend/src/CRM/common/events/entity/entity-owner-changed.event.ts create mode 100644 backend/src/CRM/common/events/entity/entity-price.update.event.ts create mode 100644 backend/src/CRM/common/events/entity/entity.event.ts create mode 100644 backend/src/CRM/common/events/entity/index.ts create mode 100644 backend/src/CRM/common/events/file-link/file-link-created.event.ts create mode 100644 backend/src/CRM/common/events/file-link/file-link.event.ts create mode 100644 backend/src/CRM/common/events/file-link/index.ts create mode 100644 backend/src/CRM/common/events/index.ts create mode 100644 backend/src/CRM/common/events/note/index.ts create mode 100644 backend/src/CRM/common/events/note/note-created.event.ts create mode 100644 backend/src/CRM/common/events/note/note.event.ts create mode 100644 backend/src/CRM/common/events/task/index.ts create mode 100644 backend/src/CRM/common/events/task/task-comment-created.event.ts create mode 100644 backend/src/CRM/common/events/task/task-created.event.ts create mode 100644 backend/src/CRM/common/events/task/task-ext-upsert.event.ts create mode 100644 backend/src/CRM/common/events/task/task-ext.event.ts create mode 100644 backend/src/CRM/common/events/task/task-updated.event.ts create mode 100644 backend/src/CRM/common/events/task/task.event.ts create mode 100644 backend/src/CRM/common/index.ts create mode 100644 backend/src/CRM/crm.module.ts create mode 100644 backend/src/CRM/entity-link/dto/entity-link.dto.ts create mode 100644 backend/src/CRM/entity-link/dto/index.ts create mode 100644 backend/src/CRM/entity-link/entities/entity-link.entity.ts create mode 100644 backend/src/CRM/entity-link/entities/index.ts create mode 100644 backend/src/CRM/entity-link/entity-link.service.ts create mode 100644 backend/src/CRM/entity-search/dto/entity-search-filter.dto.ts create mode 100644 backend/src/CRM/entity-search/dto/entity-search-for-call-result.dto.ts create mode 100644 backend/src/CRM/entity-search/dto/entity-search-full-result.dto.ts create mode 100644 backend/src/CRM/entity-search/dto/entity-search-result.dto.ts create mode 100644 backend/src/CRM/entity-search/dto/index.ts create mode 100644 backend/src/CRM/entity-search/entity-search.controller.ts create mode 100644 backend/src/CRM/entity-search/entity-search.service.ts create mode 100644 backend/src/CRM/entity-search/index.ts create mode 100644 backend/src/CRM/entity-type-link/dto/create-entity-type-link.dto.ts create mode 100644 backend/src/CRM/entity-type-link/dto/entity-type-link.dto.ts create mode 100644 backend/src/CRM/entity-type-link/dto/index.ts create mode 100644 backend/src/CRM/entity-type-link/dto/update-entity-type-link.dto.ts create mode 100644 backend/src/CRM/entity-type-link/entities/entity-type-link.entity.ts create mode 100644 backend/src/CRM/entity-type-link/entities/index.ts create mode 100644 backend/src/CRM/entity-type-link/entity-type-link.service.ts create mode 100644 backend/src/CRM/entity-type/dto/create-entity-type.dto.ts create mode 100644 backend/src/CRM/entity-type/dto/entity-type-section.dto.ts create mode 100644 backend/src/CRM/entity-type/dto/entity-type.dto.ts create mode 100644 backend/src/CRM/entity-type/dto/index.ts create mode 100644 backend/src/CRM/entity-type/dto/update-entity-type-fields.dto.ts create mode 100644 backend/src/CRM/entity-type/dto/update-entity-type.dto.ts create mode 100644 backend/src/CRM/entity-type/entities/entity-type.entity.ts create mode 100644 backend/src/CRM/entity-type/entities/index.ts create mode 100644 backend/src/CRM/entity-type/entity-type.controller.ts create mode 100644 backend/src/CRM/entity-type/entity-type.service.ts create mode 100644 backend/src/CRM/entity-type/enums/index.ts create mode 100644 backend/src/CRM/entity-type/enums/section-view.enum.ts create mode 100644 backend/src/CRM/entity-type/errors/entity-type-used-in-formula.error.ts create mode 100644 backend/src/CRM/entity-type/errors/index.ts create mode 100644 backend/src/CRM/entity-type/index.ts create mode 100644 backend/src/CRM/feature/dto/feature.dto.ts create mode 100644 backend/src/CRM/feature/dto/index.ts create mode 100644 backend/src/CRM/feature/entities/entity-type-feature.entity.ts create mode 100644 backend/src/CRM/feature/entities/feature.entity.ts create mode 100644 backend/src/CRM/feature/entities/index.ts create mode 100644 backend/src/CRM/feature/entity-type-feature.service.ts create mode 100644 backend/src/CRM/feature/enums/feature-code.enum.ts create mode 100644 backend/src/CRM/feature/enums/index.ts create mode 100644 backend/src/CRM/feature/feature.controller.ts create mode 100644 backend/src/CRM/feature/feature.service.ts create mode 100644 backend/src/CRM/feature/index.ts create mode 100644 backend/src/CRM/identity/dto/identity-pool.dto.ts create mode 100644 backend/src/CRM/identity/dto/index.ts create mode 100644 backend/src/CRM/identity/identity.controller.ts create mode 100644 backend/src/CRM/identity/identity.service.ts create mode 100644 backend/src/CRM/identity/types/identity-pool.ts create mode 100644 backend/src/CRM/identity/types/index.ts create mode 100644 backend/src/CRM/note/dto/create-note.dto.ts create mode 100644 backend/src/CRM/note/dto/index.ts create mode 100644 backend/src/CRM/note/dto/note.dto.ts create mode 100644 backend/src/CRM/note/dto/update-note.dto.ts create mode 100644 backend/src/CRM/note/entities/index.ts create mode 100644 backend/src/CRM/note/entities/note.entity.ts create mode 100644 backend/src/CRM/note/index.ts create mode 100644 backend/src/CRM/note/note.controller.ts create mode 100644 backend/src/CRM/note/note.handler.ts create mode 100644 backend/src/CRM/note/note.service.ts create mode 100644 backend/src/CRM/reporting/common/enums/index.ts create mode 100644 backend/src/CRM/reporting/common/enums/report-user-type.enum.ts create mode 100644 backend/src/CRM/reporting/common/enums/sales-pipeline-type.enum.ts create mode 100644 backend/src/CRM/reporting/common/helpers/entity-query-builder.helper.ts create mode 100644 backend/src/CRM/reporting/common/helpers/index.ts create mode 100644 backend/src/CRM/reporting/common/index.ts create mode 100644 backend/src/CRM/reporting/common/types/index.ts create mode 100644 backend/src/CRM/reporting/common/types/owner-date-value.ts create mode 100644 backend/src/CRM/reporting/common/types/report-row-owner.ts create mode 100644 backend/src/CRM/reporting/comparative/comparative-report.controller.ts create mode 100644 backend/src/CRM/reporting/comparative/comparative-report.service.ts create mode 100644 backend/src/CRM/reporting/comparative/dto/comparative-report-cell.dto.ts create mode 100644 backend/src/CRM/reporting/comparative/dto/comparative-report-filter.dto.ts create mode 100644 backend/src/CRM/reporting/comparative/dto/comparative-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/comparative/dto/comparative-report-value.dto.ts create mode 100644 backend/src/CRM/reporting/comparative/dto/comparative-report.dto.ts create mode 100644 backend/src/CRM/reporting/comparative/dto/index.ts create mode 100644 backend/src/CRM/reporting/comparative/types/comparative-report-cell.ts create mode 100644 backend/src/CRM/reporting/comparative/types/comparative-report-row.ts create mode 100644 backend/src/CRM/reporting/comparative/types/comparative-report-value.ts create mode 100644 backend/src/CRM/reporting/comparative/types/comparative-report.ts create mode 100644 backend/src/CRM/reporting/comparative/types/index.ts create mode 100644 backend/src/CRM/reporting/crm-reporting.module.ts create mode 100644 backend/src/CRM/reporting/crm-reporting.service.ts create mode 100644 backend/src/CRM/reporting/customer/customer-report.controller.ts create mode 100644 backend/src/CRM/reporting/customer/customer-report.service.ts create mode 100644 backend/src/CRM/reporting/customer/dto/customer-report-field-meta.dto.ts create mode 100644 backend/src/CRM/reporting/customer/dto/customer-report-field.dto.ts create mode 100644 backend/src/CRM/reporting/customer/dto/customer-report-filter.dto.ts create mode 100644 backend/src/CRM/reporting/customer/dto/customer-report-meta.dto.ts create mode 100644 backend/src/CRM/reporting/customer/dto/customer-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/customer/dto/customer-report.dto.ts create mode 100644 backend/src/CRM/reporting/customer/dto/index.ts create mode 100644 backend/src/CRM/reporting/customer/enums/customer-report-type.enum.ts create mode 100644 backend/src/CRM/reporting/customer/enums/index.ts create mode 100644 backend/src/CRM/reporting/customer/types/customer-report-field-meta.ts create mode 100644 backend/src/CRM/reporting/customer/types/customer-report-field.ts create mode 100644 backend/src/CRM/reporting/customer/types/customer-report-meta.ts create mode 100644 backend/src/CRM/reporting/customer/types/customer-report-row.ts create mode 100644 backend/src/CRM/reporting/customer/types/customer-report.ts create mode 100644 backend/src/CRM/reporting/customer/types/index.ts create mode 100644 backend/src/CRM/reporting/dashboard/dashboard.controller.ts create mode 100644 backend/src/CRM/reporting/dashboard/dashboard.service.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/dashboard-filter.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/entity-summary-report.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/index.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/sales-pipeline-filter.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/sales-plan-report.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/sales-plan-value.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/sellers-rating-report.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/task-summary-report.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/top-sellers-report.dto.ts create mode 100644 backend/src/CRM/reporting/dashboard/dto/user-quantity-amount.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-entity.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-field-meta.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-field-option-meta.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-field-value.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-field.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-meta.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report-task.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/crm-general-report.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility-call.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility-entity.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field-option.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility-fields.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility-task.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter-visibility.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-filter.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/general-report.dto.ts create mode 100644 backend/src/CRM/reporting/general/dto/index.ts create mode 100644 backend/src/CRM/reporting/general/enums/general-report-type.enum.ts create mode 100644 backend/src/CRM/reporting/general/enums/index.ts create mode 100644 backend/src/CRM/reporting/general/general-report.controller.ts create mode 100644 backend/src/CRM/reporting/general/general-report.service.ts create mode 100644 backend/src/CRM/reporting/general/index.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-entity.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-field-meta.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-field-option-meta.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-field-value.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-field.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-meta.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-row.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report-task.ts create mode 100644 backend/src/CRM/reporting/general/types/crm-general-report.ts create mode 100644 backend/src/CRM/reporting/general/types/general-report-row.ts create mode 100644 backend/src/CRM/reporting/general/types/general-report.ts create mode 100644 backend/src/CRM/reporting/general/types/index.ts create mode 100644 backend/src/CRM/reporting/project/dto/index.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-entities-report-filter.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-entities-report-meta.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-entities-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-entities-report.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-report-field-meta.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-report-field.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-report-item.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-stage-item.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-task-user-report-filter.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-task-user-report-row.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-task-user-report-total-row.dto.ts create mode 100644 backend/src/CRM/reporting/project/dto/project-task-user-report.dto.ts create mode 100644 backend/src/CRM/reporting/project/project-report.controller.ts create mode 100644 backend/src/CRM/reporting/project/project-report.service.ts create mode 100644 backend/src/CRM/reporting/project/types/index.ts create mode 100644 backend/src/CRM/reporting/project/types/project-entities-report-meta.ts create mode 100644 backend/src/CRM/reporting/project/types/project-entities-report-row.ts create mode 100644 backend/src/CRM/reporting/project/types/project-entities-report.ts create mode 100644 backend/src/CRM/reporting/project/types/project-report-field-meta.ts create mode 100644 backend/src/CRM/reporting/project/types/project-report-field.ts create mode 100644 backend/src/CRM/reporting/project/types/project-report-item.ts create mode 100644 backend/src/CRM/reporting/project/types/project-stage-item.ts create mode 100644 backend/src/CRM/reporting/project/types/project-task-user-report-row.ts create mode 100644 backend/src/CRM/reporting/project/types/project-task-user-report-total-row.ts create mode 100644 backend/src/CRM/reporting/project/types/project-task-user-report.ts create mode 100644 backend/src/CRM/sales-plan/dto/index.ts create mode 100644 backend/src/CRM/sales-plan/dto/sales-plan-progress.dto.ts create mode 100644 backend/src/CRM/sales-plan/dto/sales-plan.dto.ts create mode 100644 backend/src/CRM/sales-plan/entities/index.ts create mode 100644 backend/src/CRM/sales-plan/entities/sales-plan.entity.ts create mode 100644 backend/src/CRM/sales-plan/sales-plan.controller.ts create mode 100644 backend/src/CRM/sales-plan/sales-plan.module.ts create mode 100644 backend/src/CRM/sales-plan/sales-plan.service.ts create mode 100644 backend/src/CRM/task-board/dto/index.ts create mode 100644 backend/src/CRM/task-board/dto/task-board-card.dto.ts create mode 100644 backend/src/CRM/task-board/dto/task-board-filter.dto.ts create mode 100644 backend/src/CRM/task-board/dto/task-board-meta.dto.ts create mode 100644 backend/src/CRM/task-board/dto/task-board-stage-meta.dto.ts create mode 100644 backend/src/CRM/task-board/dto/task-calendar-meta.dto.ts create mode 100644 backend/src/CRM/task-board/dto/task-list-meta.dto.ts create mode 100644 backend/src/CRM/task-board/index.ts create mode 100644 backend/src/CRM/task-board/task-board.controller.ts create mode 100644 backend/src/CRM/task-board/task-board.service.ts create mode 100644 backend/src/CRM/task-comment-like/dto/index.ts create mode 100644 backend/src/CRM/task-comment-like/dto/task-comment-like.dto.ts create mode 100644 backend/src/CRM/task-comment-like/entities/index.ts create mode 100644 backend/src/CRM/task-comment-like/entities/task-comment-like.entity.ts create mode 100644 backend/src/CRM/task-comment-like/index.ts create mode 100644 backend/src/CRM/task-comment-like/task-comment-like.controller.ts create mode 100644 backend/src/CRM/task-comment-like/task-comment-like.service.ts create mode 100644 backend/src/CRM/task-comment/dto/create-task-comment.dto.ts create mode 100644 backend/src/CRM/task-comment/dto/index.ts create mode 100644 backend/src/CRM/task-comment/dto/task-comment-result.dto.ts create mode 100644 backend/src/CRM/task-comment/dto/task-comment.dto.ts create mode 100644 backend/src/CRM/task-comment/dto/update-task-comment.dto.ts create mode 100644 backend/src/CRM/task-comment/entities/index.ts create mode 100644 backend/src/CRM/task-comment/entities/task-comment.entity.ts create mode 100644 backend/src/CRM/task-comment/index.ts create mode 100644 backend/src/CRM/task-comment/task-comment.controller.ts create mode 100644 backend/src/CRM/task-comment/task-comment.service.ts create mode 100644 backend/src/CRM/task-settings/dto/create-task-settings.dto.ts create mode 100644 backend/src/CRM/task-settings/dto/index.ts create mode 100644 backend/src/CRM/task-settings/dto/task-settings.dto.ts create mode 100644 backend/src/CRM/task-settings/dto/update-task-settings.dto.ts create mode 100644 backend/src/CRM/task-settings/entities/index.ts create mode 100644 backend/src/CRM/task-settings/entities/task-settings.entity.ts create mode 100644 backend/src/CRM/task-settings/enums/index.ts create mode 100644 backend/src/CRM/task-settings/enums/task-field-code.enum.ts create mode 100644 backend/src/CRM/task-settings/enums/task-settings-type.enum.ts create mode 100644 backend/src/CRM/task-settings/task-settings.controller.ts create mode 100644 backend/src/CRM/task-settings/task-settings.handler.ts create mode 100644 backend/src/CRM/task-settings/task-settings.module.ts create mode 100644 backend/src/CRM/task-settings/task-settings.service.ts create mode 100644 backend/src/CRM/task-subtask/dto/create-task-subtask.dto.ts create mode 100644 backend/src/CRM/task-subtask/dto/index.ts create mode 100644 backend/src/CRM/task-subtask/dto/task-subtask.dto.ts create mode 100644 backend/src/CRM/task-subtask/dto/update-task-subtask.dto.ts create mode 100644 backend/src/CRM/task-subtask/entities/index.ts create mode 100644 backend/src/CRM/task-subtask/entities/task-subtask.entity.ts create mode 100644 backend/src/CRM/task-subtask/index.ts create mode 100644 backend/src/CRM/task-subtask/task-subtask.controller.ts create mode 100644 backend/src/CRM/task-subtask/task-subtask.service.ts create mode 100644 backend/src/CRM/task/dto/create-task.dto.ts create mode 100644 backend/src/CRM/task/dto/index.ts create mode 100644 backend/src/CRM/task/dto/task.dto.ts create mode 100644 backend/src/CRM/task/dto/update-task.dto.ts create mode 100644 backend/src/CRM/task/entities/index.ts create mode 100644 backend/src/CRM/task/entities/task.entity.ts create mode 100644 backend/src/CRM/task/index.ts create mode 100644 backend/src/CRM/task/task.controller.ts create mode 100644 backend/src/CRM/task/task.handler.ts create mode 100644 backend/src/CRM/task/task.service.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/CreateContactAndLeadController.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetAttachmentController.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetMailMessageController.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesController.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesFilter.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetSectionMessagesController.ts create mode 100644 backend/src/Mailing/Controller/MailMessage/GetSectionMessagesFilter.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/GetMailboxesInfoController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/SeenMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/SendMailMessageController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/SpamMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/TrashMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/UnseenMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/UnspamMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/Mailbox/UntrashMailThreadController.ts create mode 100644 backend/src/Mailing/Controller/MailboxGmail/GmailAuthCallbackController.ts create mode 100644 backend/src/Mailing/Controller/MailboxGmail/GmailAuthConnectController.ts create mode 100644 backend/src/Mailing/Controller/MailboxManual/GetMailboxSettingsManualController.ts create mode 100644 backend/src/Mailing/Controller/MailboxManual/UpdateMailboxSettingsManualController.ts create mode 100644 backend/src/Mailing/MailingModule.ts create mode 100644 backend/src/Mailing/Model/MailMessage/MailMessage.ts create mode 100644 backend/src/Mailing/Model/MailMessage/MailMessageFolder.ts create mode 100644 backend/src/Mailing/Model/MailMessage/MailMessageWithFolders.ts create mode 100644 backend/src/Mailing/Model/MailboxGmail/MailboxSettingsGmail.ts create mode 100644 backend/src/Mailing/Model/MailboxManual/MailboxSettingsManual.ts create mode 100644 backend/src/Mailing/Service/MailMessage/Dto/CreateContactLeadDto.ts create mode 100644 backend/src/Mailing/Service/MailMessage/Dto/MailMessageDto.ts create mode 100644 backend/src/Mailing/Service/MailMessage/MailMessageInfo.ts create mode 100644 backend/src/Mailing/Service/MailMessage/MailMessageService.ts create mode 100644 backend/src/Mailing/Service/MailMessage/MailThreadInfo.ts create mode 100644 backend/src/Mailing/Service/MailMessage/MailThreadResult.ts create mode 100644 backend/src/Mailing/Service/Mailbox/Dto/mailbox-full-info.dto.ts create mode 100644 backend/src/Mailing/Service/Mailbox/Dto/mailbox-section.dto.ts create mode 100644 backend/src/Mailing/Service/Mailbox/Dto/mailbox-short-info.dto.ts create mode 100644 backend/src/Mailing/Service/Mailbox/Dto/mailboxes-info.dto.ts create mode 100644 backend/src/Mailing/Service/Mailbox/MailboxService.ts create mode 100644 backend/src/Mailing/Service/MailboxGmail/MailboxGmailService.ts create mode 100644 backend/src/Mailing/Service/MailboxManual/Dto/MailboxSettingsManualDto.ts create mode 100644 backend/src/Mailing/Service/MailboxManual/Dto/UpdateMailboxSettingsManualDto.ts create mode 100644 backend/src/Mailing/Service/MailboxManual/MailboxManualService.ts create mode 100644 backend/src/Mailing/common/dto/delete-mailbox-query.ts create mode 100644 backend/src/Mailing/common/dto/index.ts create mode 100644 backend/src/Mailing/common/dto/send-mail-message.dto.ts create mode 100644 backend/src/Mailing/common/enums/index.ts create mode 100644 backend/src/Mailing/common/enums/mailbox-folder-type.enum.ts create mode 100644 backend/src/Mailing/common/enums/mailbox-provider.enum.ts create mode 100644 backend/src/Mailing/common/enums/mailbox-state.enum.ts create mode 100644 backend/src/Mailing/common/events/index.ts create mode 100644 backend/src/Mailing/common/events/mail-event-type.enum.ts create mode 100644 backend/src/Mailing/common/events/mail-message/index.ts create mode 100644 backend/src/Mailing/common/events/mail-message/mail-message-received.event.ts create mode 100644 backend/src/Mailing/common/events/mail-message/mail-message.event.ts create mode 100644 backend/src/Mailing/common/events/mailbox/index.ts create mode 100644 backend/src/Mailing/common/events/mailbox/mailbox.event.ts create mode 100644 backend/src/Mailing/common/index.ts create mode 100644 backend/src/Mailing/common/types/email-address.ts create mode 100644 backend/src/Mailing/common/types/folder-messages.ts create mode 100644 backend/src/Mailing/common/types/imap-sync-info.ts create mode 100644 backend/src/Mailing/common/types/index.ts create mode 100644 backend/src/Mailing/common/types/mail-message-attachment.dto.ts create mode 100644 backend/src/Mailing/common/types/mail-message-external.ts create mode 100644 backend/src/Mailing/common/types/mail-message-payload-external.ts create mode 100644 backend/src/Mailing/common/types/mailbox-folder-external.ts create mode 100644 backend/src/Mailing/common/types/mailbox-sync-messages.ts create mode 100644 backend/src/Mailing/common/types/mailbox-sync-result.ts create mode 100644 backend/src/Mailing/common/utils/index.ts create mode 100644 backend/src/Mailing/common/utils/mailbox-folder-type.util.ts create mode 100644 backend/src/Mailing/config/index.ts create mode 100644 backend/src/Mailing/config/mail.config.ts create mode 100644 backend/src/Mailing/mail-message-builder/index.ts create mode 100644 backend/src/Mailing/mail-message-builder/mail-message-builder.service.ts create mode 100644 backend/src/Mailing/mail-message-payload/dto/index.ts create mode 100644 backend/src/Mailing/mail-message-payload/dto/mail-message-payload.dto.ts create mode 100644 backend/src/Mailing/mail-message-payload/entities/index.ts create mode 100644 backend/src/Mailing/mail-message-payload/entities/mail-message-payload.entity.ts create mode 100644 backend/src/Mailing/mail-message-payload/index.ts create mode 100644 backend/src/Mailing/mail-message-payload/mail-message-payload.service.ts create mode 100644 backend/src/Mailing/mail-provider/decorators/index.ts create mode 100644 backend/src/Mailing/mail-provider/decorators/mail-integration.decorator.ts create mode 100644 backend/src/Mailing/mail-provider/index.ts create mode 100644 backend/src/Mailing/mail-provider/mail-provider.registry.ts create mode 100644 backend/src/Mailing/mail-provider/types/index.ts create mode 100644 backend/src/Mailing/mail-provider/types/mail-provider.ts create mode 100644 backend/src/Mailing/mailbox-folder/dto/index.ts create mode 100644 backend/src/Mailing/mailbox-folder/dto/mailbox-folder.dto.ts create mode 100644 backend/src/Mailing/mailbox-folder/entities/index.ts create mode 100644 backend/src/Mailing/mailbox-folder/entities/mailbox-folder.entity.ts create mode 100644 backend/src/Mailing/mailbox-folder/index.ts create mode 100644 backend/src/Mailing/mailbox-folder/mailbox-folder.service.ts create mode 100644 backend/src/Mailing/mailbox-signature/dto/create-mailbox-signature.dto.ts create mode 100644 backend/src/Mailing/mailbox-signature/dto/index.ts create mode 100644 backend/src/Mailing/mailbox-signature/dto/mailbox-signature-filter.dto.ts create mode 100644 backend/src/Mailing/mailbox-signature/dto/mailbox-signature.dto.ts create mode 100644 backend/src/Mailing/mailbox-signature/dto/update-mailbox-signature.dto.ts create mode 100644 backend/src/Mailing/mailbox-signature/entities/index.ts create mode 100644 backend/src/Mailing/mailbox-signature/entities/mailbox-signature-mailbox.entity.ts create mode 100644 backend/src/Mailing/mailbox-signature/entities/mailbox-signature.entity.ts create mode 100644 backend/src/Mailing/mailbox-signature/index.ts create mode 100644 backend/src/Mailing/mailbox-signature/mailbox-signature.controller.ts create mode 100644 backend/src/Mailing/mailbox-signature/mailbox-signature.service.ts create mode 100644 backend/src/Mailing/mailbox/dto/create-mailbox.dto.ts create mode 100644 backend/src/Mailing/mailbox/dto/index.ts create mode 100644 backend/src/Mailing/mailbox/dto/mailbox-entity-settings.dto.ts create mode 100644 backend/src/Mailing/mailbox/dto/mailbox.dto.ts create mode 100644 backend/src/Mailing/mailbox/dto/update-mailbox.dto.ts create mode 100644 backend/src/Mailing/mailbox/entities/index.ts create mode 100644 backend/src/Mailing/mailbox/entities/mailbox-accessible-user.entity.ts create mode 100644 backend/src/Mailing/mailbox/entities/mailbox-entity-settings.entity.ts create mode 100644 backend/src/Mailing/mailbox/entities/mailbox.entity.ts create mode 100644 backend/src/Mailing/mailbox/index.ts create mode 100644 backend/src/Mailing/mailbox/mailbox.controller.ts create mode 100644 backend/src/Mailing/mailbox/services/index.ts create mode 100644 backend/src/Mailing/mailbox/services/mailbox-accessible-user.service.ts create mode 100644 backend/src/Mailing/mailbox/services/mailbox-entity-settings.service.ts create mode 100644 backend/src/Mailing/mailbox/services/mailbox-lock.service.ts create mode 100644 backend/src/Mailing/mailbox/services/mailbox.handler.ts create mode 100644 backend/src/Mailing/mailbox/services/mailbox.service.ts create mode 100644 backend/src/Mailing/subscription/dto/index.ts create mode 100644 backend/src/Mailing/subscription/dto/unsubscribe.dto.ts create mode 100644 backend/src/Mailing/subscription/index.ts create mode 100644 backend/src/Mailing/subscription/unsubscribe.controller.ts create mode 100644 backend/src/Mailing/system-mailing/dto/become-partner-feedback.dto.ts create mode 100644 backend/src/Mailing/system-mailing/dto/contact-us-feedback.dto.ts create mode 100644 backend/src/Mailing/system-mailing/dto/index.ts create mode 100644 backend/src/Mailing/system-mailing/dto/send-feedback.dto.ts create mode 100644 backend/src/Mailing/system-mailing/dto/trial-expired-feedback.dto.ts create mode 100644 backend/src/Mailing/system-mailing/dto/user-limit-feedback.dto.ts create mode 100644 backend/src/Mailing/system-mailing/enums/feedback-type.enum.ts create mode 100644 backend/src/Mailing/system-mailing/enums/index.ts create mode 100644 backend/src/Mailing/system-mailing/index.ts create mode 100644 backend/src/Mailing/system-mailing/public-system-mailing.controller.ts.ts create mode 100644 backend/src/Mailing/system-mailing/system-mailing.controller.ts create mode 100644 backend/src/Mailing/system-mailing/system-mailing.service.ts create mode 100644 backend/src/Mailing/system-mailing/templates/auth/password_recovery.html create mode 100644 backend/src/Mailing/system-mailing/templates/feedback/become_partner.html create mode 100644 backend/src/Mailing/system-mailing/templates/feedback/contact_us.html create mode 100644 backend/src/Mailing/system-mailing/templates/feedback/trial_expired.html create mode 100644 backend/src/Mailing/system-mailing/templates/feedback/user_limit.html create mode 100644 backend/src/app.module.ts create mode 100644 backend/src/common/common.module.ts create mode 100644 backend/src/common/config/common.config.ts create mode 100644 backend/src/common/config/index.ts create mode 100644 backend/src/common/constants/frontend-route.ts create mode 100644 backend/src/common/constants/index.ts create mode 100644 backend/src/common/constants/paging-default.ts create mode 100644 backend/src/common/decorators/index.ts create mode 100644 backend/src/common/decorators/subdomain.decorator.ts create mode 100644 backend/src/common/decorators/transform-to-dto.decorator.ts create mode 100644 backend/src/common/dto/date/date-period.dto.ts create mode 100644 backend/src/common/dto/date/index.ts create mode 100644 backend/src/common/dto/expand/expand-query.dto.ts create mode 100644 backend/src/common/dto/expand/index.ts create mode 100644 backend/src/common/dto/filter/boolean-filter.ts create mode 100644 backend/src/common/dto/filter/date-filter.ts create mode 100644 backend/src/common/dto/filter/date-period-filter.ts create mode 100644 backend/src/common/dto/filter/exists-filter.ts create mode 100644 backend/src/common/dto/filter/index.ts create mode 100644 backend/src/common/dto/filter/number-filter.ts create mode 100644 backend/src/common/dto/filter/select-filter.ts create mode 100644 backend/src/common/dto/filter/simple-filter.ts create mode 100644 backend/src/common/dto/filter/string-filter.ts create mode 100644 backend/src/common/dto/index.ts create mode 100644 backend/src/common/dto/paging/chat-paging-query.dto.ts create mode 100644 backend/src/common/dto/paging/cursor-paging-query.dto.ts create mode 100644 backend/src/common/dto/paging/index.ts create mode 100644 backend/src/common/dto/paging/paging-meta.dto.ts create mode 100644 backend/src/common/dto/paging/paging-query.dto.ts create mode 100644 backend/src/common/dto/quantity-amount.dto.ts create mode 100644 backend/src/common/dto/sorting/index.ts create mode 100644 backend/src/common/dto/sorting/manual-sorting.dto.ts create mode 100644 backend/src/common/dto/sorting/sort-order-list.dto.ts create mode 100644 backend/src/common/dto/sorting/sort-order.dto.ts create mode 100644 backend/src/common/enums/currency.enum.ts create mode 100644 backend/src/common/enums/date-format.enum.ts create mode 100644 backend/src/common/enums/date-period-filter-type.enum.ts create mode 100644 backend/src/common/enums/exists-filter-type.enum.ts create mode 100644 backend/src/common/enums/file-link-source.enum.ts create mode 100644 backend/src/common/enums/group-by-date.enum.ts create mode 100644 backend/src/common/enums/http-body-type.enum.ts create mode 100644 backend/src/common/enums/http-method.enum.ts create mode 100644 backend/src/common/enums/index.ts create mode 100644 backend/src/common/enums/object-state.enum.ts create mode 100644 backend/src/common/enums/simple-filter-type.enum.ts create mode 100644 backend/src/common/enums/string-filter-type.enum.ts create mode 100644 backend/src/common/enums/user-notification.enum.ts create mode 100644 backend/src/common/errors/bad-request.error.ts create mode 100644 backend/src/common/errors/forbidden.error.ts create mode 100644 backend/src/common/errors/index.ts create mode 100644 backend/src/common/errors/invalid-phone.error.ts create mode 100644 backend/src/common/errors/not-found.error.ts create mode 100644 backend/src/common/errors/service.error.ts create mode 100644 backend/src/common/filters/all-exceptions.filter.ts create mode 100644 backend/src/common/filters/index.ts create mode 100644 backend/src/common/index.ts create mode 100644 backend/src/common/interceptors/index.ts create mode 100644 backend/src/common/interceptors/logging.interceptor.ts create mode 100644 backend/src/common/interceptors/transform-to-dto.interceptor.ts create mode 100644 backend/src/common/middleware/extract-subdomain.middleware.ts create mode 100644 backend/src/common/middleware/index.ts create mode 100644 backend/src/common/modules/cache/cache.constants.ts create mode 100644 backend/src/common/modules/cache/cache.module-definition.ts create mode 100644 backend/src/common/modules/cache/cache.module.ts create mode 100644 backend/src/common/modules/cache/cache.service.ts create mode 100644 backend/src/common/modules/cache/index.ts create mode 100644 backend/src/common/modules/cache/interfaces/cache-module.interface.ts create mode 100644 backend/src/common/modules/cache/interfaces/cache-service.interface.ts create mode 100644 backend/src/common/modules/cache/interfaces/index.ts create mode 100644 backend/src/common/modules/cache/interfaces/ioredis-provider.interface.ts create mode 100644 backend/src/common/modules/cache/interfaces/store.interface.ts create mode 100644 backend/src/common/modules/cache/providers/index.ts create mode 100644 backend/src/common/modules/cache/providers/ioredis.provider.ts create mode 100644 backend/src/common/modules/cache/providers/stub.provider.ts create mode 100644 backend/src/common/modules/cache/types/cache-provider.type.ts create mode 100644 backend/src/common/modules/cache/types/index.ts create mode 100644 backend/src/common/modules/global-http/dns-cache.service.ts create mode 100644 backend/src/common/modules/global-http/global-http.module.ts create mode 100644 backend/src/common/modules/global-http/index.ts create mode 100644 backend/src/common/modules/index.ts create mode 100644 backend/src/common/modules/token/errors/index.ts create mode 100644 backend/src/common/modules/token/errors/invalid-token.error.ts create mode 100644 backend/src/common/modules/token/index.ts create mode 100644 backend/src/common/modules/token/token.module.ts create mode 100644 backend/src/common/modules/token/token.service.ts create mode 100644 backend/src/common/modules/url-generator/index.ts create mode 100644 backend/src/common/modules/url-generator/url-generator.module.ts create mode 100644 backend/src/common/modules/url-generator/url-generator.service.ts create mode 100644 backend/src/common/types/date/date-period.ts create mode 100644 backend/src/common/types/date/index.ts create mode 100644 backend/src/common/types/events/index.ts create mode 100644 backend/src/common/types/events/service.event.ts create mode 100644 backend/src/common/types/index.ts create mode 100644 backend/src/common/types/quantity-amount.ts create mode 100644 backend/src/common/types/sort-order.ts create mode 100644 backend/src/common/types/task-queue.ts create mode 100644 backend/src/common/utils/array.util.ts create mode 100644 backend/src/common/utils/date.util.ts create mode 100644 backend/src/common/utils/index.ts create mode 100644 backend/src/common/utils/number.util.ts create mode 100644 backend/src/common/utils/object.util.ts create mode 100644 backend/src/common/utils/password.util.ts create mode 100644 backend/src/common/utils/phone.util.ts create mode 100644 backend/src/common/utils/promise.util.ts create mode 100644 backend/src/common/utils/propagation.util.ts create mode 100644 backend/src/common/utils/string.util.ts create mode 100644 backend/src/common/utils/tree.util.ts create mode 100644 backend/src/common/utils/url.util.ts create mode 100644 backend/src/config/application.config.ts create mode 100644 backend/src/config/index.ts create mode 100644 backend/src/database/backup/empty.2024-07-23.sql create mode 100644 backend/src/database/common/index.ts create mode 100644 backend/src/database/common/utils/index.ts create mode 100644 backend/src/database/common/utils/tsquery.util.ts create mode 100644 backend/src/database/config/database.config.ts create mode 100644 backend/src/database/config/index.ts create mode 100644 backend/src/database/database.module.ts create mode 100644 backend/src/database/index.ts create mode 100644 backend/src/database/migrations/1737731260000-AddLoginSecurityFields.ts create mode 100644 backend/src/database/migrations/1744637701399-AddMailboxSettingsImapflow.ts create mode 100644 backend/src/database/migrations/1744982861922-AlterMailboxFolder.ts create mode 100644 backend/src/database/migrations/1744990391092-AlterMailboxFolder.ts create mode 100644 backend/src/database/migrations/1745220421131-AlterMailboxFolder.ts create mode 100644 backend/src/database/migrations/1745245997361-AlterUser.ts create mode 100644 backend/src/database/migrations/archive/1667672722508-AddAccount.ts create mode 100644 backend/src/database/migrations/archive/1667743637921-AddUser.ts create mode 100644 backend/src/database/migrations/archive/1667755763616-AddUserAccount.ts create mode 100644 backend/src/database/migrations/archive/1668609239887-AddEntityType.ts create mode 100644 backend/src/database/migrations/archive/1668679480636-AddBoard.ts create mode 100644 backend/src/database/migrations/archive/1668679994594-Note.ts create mode 100644 backend/src/database/migrations/archive/1668680006964-AddStage.ts create mode 100644 backend/src/database/migrations/archive/1668693976313-FeedsView.ts create mode 100644 backend/src/database/migrations/archive/1668759141152-AddEntity.ts create mode 100644 backend/src/database/migrations/archive/1668769413399-AddFeedItemsSequence.ts create mode 100644 backend/src/database/migrations/archive/1668770161278-AddTaskType.ts create mode 100644 backend/src/database/migrations/archive/1668957885188-AddSortOrderToBoard.ts create mode 100644 backend/src/database/migrations/archive/1669035936818-AddActivity.ts create mode 100644 backend/src/database/migrations/archive/1669035943607-AddTask.ts create mode 100644 backend/src/database/migrations/archive/1669126197933-AddFieldGroup.ts create mode 100644 backend/src/database/migrations/archive/1669127718002-AddField.ts create mode 100644 backend/src/database/migrations/archive/1669130700529-AddTaskToFeed.ts create mode 100644 backend/src/database/migrations/archive/1669192730867-AddNoteToEntityFK.ts create mode 100644 backend/src/database/migrations/archive/1669197308978-AddFieldOption.ts create mode 100644 backend/src/database/migrations/archive/1669210346088-AddTasksView.ts create mode 100644 backend/src/database/migrations/archive/1669279080953-FixActivityConstraints.ts create mode 100644 backend/src/database/migrations/archive/1669370353662-SplitUserName.ts create mode 100644 backend/src/database/migrations/archive/1669372200327-AddUserPhone.ts create mode 100644 backend/src/database/migrations/archive/1669535731810-UserProfile.ts create mode 100644 backend/src/database/migrations/archive/1669545773479-EntityTypeLink.ts create mode 100644 backend/src/database/migrations/archive/1669565827895-EntityLink.ts create mode 100644 backend/src/database/migrations/archive/1669629567395-RemoveUserAccount.ts create mode 100644 backend/src/database/migrations/archive/1669706986248-AddFeature.ts create mode 100644 backend/src/database/migrations/archive/1669707814502-AddEntityTypeToFeature.ts create mode 100644 backend/src/database/migrations/archive/1669711256398-AddDefaultFeatures.ts create mode 100644 backend/src/database/migrations/archive/1669732421801-AddFieldValue.ts create mode 100644 backend/src/database/migrations/archive/1669799906268-FixEntityLinkColumns.ts create mode 100644 backend/src/database/migrations/archive/1669880194355-ChangeFeatureName_Notes.ts create mode 100644 backend/src/database/migrations/archive/1669966161211-BigintToInteger.ts create mode 100644 backend/src/database/migrations/archive/1669973222725-ForeignKeyToInteger.ts create mode 100644 backend/src/database/migrations/archive/1669991106433-AddFieldTypeColumnToFieldValue.ts create mode 100644 backend/src/database/migrations/archive/1670576080218-AddExternalEntity.ts create mode 100644 backend/src/database/migrations/archive/1670840721696-ExternalSystem.ts create mode 100644 backend/src/database/migrations/archive/1670846440118-AddExternalSystems.ts create mode 100644 backend/src/database/migrations/archive/1670936878487-AddFileInfo.ts create mode 100644 backend/src/database/migrations/archive/1670939571185-AddFileStorePath.ts create mode 100644 backend/src/database/migrations/archive/1671006792499-AddStageIdToTask.ts create mode 100644 backend/src/database/migrations/archive/1671030345135-ChangeFileInfoIdType.ts create mode 100644 backend/src/database/migrations/archive/1671089754889-AddEntityTypeSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1671096971264-RenameTaskType.ts create mode 100644 backend/src/database/migrations/archive/1671108685879-NoteFiles.ts create mode 100644 backend/src/database/migrations/archive/1671196063822-FileInfoCascade.ts create mode 100644 backend/src/database/migrations/archive/1671196394406-NoteFileRemoveFileKey.ts create mode 100644 backend/src/database/migrations/archive/1671203263368-TaskAndActivityFiles.ts create mode 100644 backend/src/database/migrations/archive/1671438176416-RemoveKeyFromFileInfo.ts create mode 100644 backend/src/database/migrations/archive/1671439005617-AddIsUsedToFileInfo.ts create mode 100644 backend/src/database/migrations/archive/1671440838639-DropNoteFileLink.ts create mode 100644 backend/src/database/migrations/archive/1671521628139-BoardRecordIdNull.ts create mode 100644 backend/src/database/migrations/archive/1671533557027-UserActive.ts create mode 100644 backend/src/database/migrations/archive/1671619787598-UserObjectPermission.ts create mode 100644 backend/src/database/migrations/archive/1671914250074-ChangeCardView.ts create mode 100644 backend/src/database/migrations/archive/1672064100473-EntityIdCascade.ts create mode 100644 backend/src/database/migrations/archive/1672154773200-AddSalesforceSettings.ts create mode 100644 backend/src/database/migrations/archive/1672224404851-AddRefreshTokenToSalesforce.ts create mode 100644 backend/src/database/migrations/archive/1672242451242-ExternalSystemUrlTemplates.ts create mode 100644 backend/src/database/migrations/archive/1672306523845-AddDataToExternalEntity.ts create mode 100644 backend/src/database/migrations/archive/1672386954959-AddUIDataToExternalEntity.ts create mode 100644 backend/src/database/migrations/archive/1672928537580-CascadeStageIdInTask.ts create mode 100644 backend/src/database/migrations/archive/1673251726994-AddStageToAllTasks.ts create mode 100644 backend/src/database/migrations/archive/1673339773307-EnableFileStorageFeature.ts create mode 100644 backend/src/database/migrations/archive/1673417366210-RenameCardViewToEntityCategory.ts create mode 100644 backend/src/database/migrations/archive/1673447442894-AddSubscription.ts create mode 100644 backend/src/database/migrations/archive/1673514341599-AddCreatedAtToFileLink.ts create mode 100644 backend/src/database/migrations/archive/1673515827427-AddSequenceToSubscriptionId.ts create mode 100644 backend/src/database/migrations/archive/1673969196807-AddMailboxAndProviderSettings.ts create mode 100644 backend/src/database/migrations/archive/1674130775550-AddMailboxState.ts create mode 100644 backend/src/database/migrations/archive/1674138959333-AddPlannedTimeToTask.ts create mode 100644 backend/src/database/migrations/archive/1674203801914-ChangeMailboxSettingsGmail.ts create mode 100644 backend/src/database/migrations/archive/1674492197867-AddHistoryIdToGmailSettings.ts create mode 100644 backend/src/database/migrations/archive/1674546589359-AddLastSyncToManualSettings.ts create mode 100644 backend/src/database/migrations/archive/1674560582007-AddMailMessageBase.ts create mode 100644 backend/src/database/migrations/archive/1674572622632-AddTaskSettings.ts create mode 100644 backend/src/database/migrations/archive/1674637903317-ChangeMailbox.ts create mode 100644 backend/src/database/migrations/archive/1674638261068-AddMailboxAccessibleUser.ts create mode 100644 backend/src/database/migrations/archive/1674648741806-AddMailboxFolderInfo.ts create mode 100644 backend/src/database/migrations/archive/1674660419230-AddMailMessageReplyTo.ts create mode 100644 backend/src/database/migrations/archive/1674661928759-AddMailMessageCc.ts create mode 100644 backend/src/database/migrations/archive/1674732906697-AddMailMessagePayloadSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1674741639286-MakeStartEndDateNullable.ts create mode 100644 backend/src/database/migrations/archive/1674742927551-AddSubtaskTable.ts create mode 100644 backend/src/database/migrations/archive/1674747324757-AddMailMessagePayloadSize.ts create mode 100644 backend/src/database/migrations/archive/1674822646459-AddTaskCommentTable.ts create mode 100644 backend/src/database/migrations/archive/1674823506430-DeleteSyncDaysFromMailbox.ts create mode 100644 backend/src/database/migrations/archive/1674831146207-AddTaskCommentLikeTable.ts create mode 100644 backend/src/database/migrations/archive/1675076470234-ImapSync.ts create mode 100644 backend/src/database/migrations/archive/1675080091774-AddSystemColumnToBoard.ts create mode 100644 backend/src/database/migrations/archive/1675086921624-AddMailMessagePayloadExternalId.ts create mode 100644 backend/src/database/migrations/archive/1675090441216-AddTaskSettingsIdToTask.ts create mode 100644 backend/src/database/migrations/archive/1675176424905-AddMailMessageHasAttachment.ts create mode 100644 backend/src/database/migrations/archive/1675178566416-MailboxFolderTypeNullable.ts create mode 100644 backend/src/database/migrations/archive/1675259924922-AddMailMessageMessageId.ts create mode 100644 backend/src/database/migrations/archive/1675340097111-DropGroupMessageFromMailbox.ts create mode 100644 backend/src/database/migrations/archive/1675349215128-AddCodeToEntityType.ts create mode 100644 backend/src/database/migrations/archive/1675350490867-AddCodeToBoard.ts create mode 100644 backend/src/database/migrations/archive/1675412897506-AddMailMessageReferences.ts create mode 100644 backend/src/database/migrations/archive/1675858313415-AddContactEntityTypeToMailbox.ts create mode 100644 backend/src/database/migrations/archive/1675858907059-AddLeadEntityTypeToMailbox.ts create mode 100644 backend/src/database/migrations/archive/1675932022996-AddLeadBoardIdToMailbox.ts create mode 100644 backend/src/database/migrations/archive/1675933206380-AddContactIdToMailMessage.ts create mode 100644 backend/src/database/migrations/archive/1675939938059-MailMessageThreadIdNotNull.ts create mode 100644 backend/src/database/migrations/archive/1675949522751-AddErrorToMailbox.ts create mode 100644 backend/src/database/migrations/archive/1675958088984-AddSeenToMailMessage.ts create mode 100644 backend/src/database/migrations/archive/1676281117335-AddSetNullOnDeleteToMailbox.ts create mode 100644 backend/src/database/migrations/archive/1676283110539-AlterMailMessageSentToNull.ts create mode 100644 backend/src/database/migrations/archive/1676299431922-AddRecipientToNote.ts create mode 100644 backend/src/database/migrations/archive/1676370561831-AddMailToFeedItem.ts create mode 100644 backend/src/database/migrations/archive/1676383947580-TurnOnChatFeature.ts create mode 100644 backend/src/database/migrations/archive/1676385805343-AddChatFeedItemType.ts create mode 100644 backend/src/database/migrations/archive/1676823429937-AddAutomationTables.ts create mode 100644 backend/src/database/migrations/archive/1676866745580-AddAutomationStageTable.ts create mode 100644 backend/src/database/migrations/archive/1676872004900-AddTaskActionSettingsTable.ts create mode 100644 backend/src/database/migrations/archive/1676884562402-RemoveResultFromTask.ts create mode 100644 backend/src/database/migrations/archive/1676895093533-AddChangeStageActionSettingsTable.ts create mode 100644 backend/src/database/migrations/archive/1676992655279-AddAutomationConditions.ts create mode 100644 backend/src/database/migrations/archive/1676994397217-AddNotification.ts create mode 100644 backend/src/database/migrations/archive/1677079993131-AddUserIdToNotification.ts create mode 100644 backend/src/database/migrations/archive/1677581691766-AddMailboxSignature.ts create mode 100644 backend/src/database/migrations/archive/1677744619246-AddNotificationHighlight.ts create mode 100644 backend/src/database/migrations/archive/1677752600091-AlterNotificationSetDescriptionNullable.ts create mode 100644 backend/src/database/migrations/archive/1677765215861-AddNotificationSettings.ts create mode 100644 backend/src/database/migrations/archive/1678093755605-AddExternalSystems.ts create mode 100644 backend/src/database/migrations/archive/1678096716231-AddAccountSettings.ts create mode 100644 backend/src/database/migrations/archive/1678113775031-AddExactTimeTriggerSettings.ts create mode 100644 backend/src/database/migrations/archive/1678194605837-AddDefaultNotificationSettings.ts create mode 100644 backend/src/database/migrations/archive/1678285724304-AddResolveDateToTaskAndActivity.ts create mode 100644 backend/src/database/migrations/archive/1678286876322-AddResolveDateToAllTasks.ts create mode 100644 backend/src/database/migrations/archive/1678347902775-AlterNotificationHighlightToTagName.ts create mode 100644 backend/src/database/migrations/archive/1678367411991-FillResolvedDateForTaskAndAction.ts create mode 100644 backend/src/database/migrations/archive/1678696011650-AddPlanNameToSubscription.ts create mode 100644 backend/src/database/migrations/archive/1678697720944-UpdateSubscriptionTo50Users.ts create mode 100644 backend/src/database/migrations/archive/1678720323399-AddCodeToField.ts create mode 100644 backend/src/database/migrations/archive/1678891987277-AddDepartment.ts create mode 100644 backend/src/database/migrations/archive/1678963689149-AddStartsInToNotification.ts create mode 100644 backend/src/database/migrations/archive/1679291439089-AddScheduledAction.ts create mode 100644 backend/src/database/migrations/archive/1679494426598-MakeFieldGroupOptional.ts create mode 100644 backend/src/database/migrations/archive/1679578329175-AddColorToFieldOption.ts create mode 100644 backend/src/database/migrations/archive/1679583365997-AlterMailMessageContactEntityId.ts create mode 100644 backend/src/database/migrations/archive/1679929245833-AddUserAvatarId.ts create mode 100644 backend/src/database/migrations/archive/1679931642588-AddActiveToField.ts create mode 100644 backend/src/database/migrations/archive/1679931904542-AddAccountLogo.ts create mode 100644 backend/src/database/migrations/archive/1680499051021-AddParticipantsToEntity.ts create mode 100644 backend/src/database/migrations/archive/1680763220747-MigrateProjects.ts create mode 100644 backend/src/database/migrations/archive/1681141545739-AddEmailActionSettings.ts create mode 100644 backend/src/database/migrations/archive/1681224301468-AddProjectFields.ts create mode 100644 backend/src/database/migrations/archive/1681289039535-AddEntityListSettings.ts create mode 100644 backend/src/database/migrations/archive/1681483040117-AddScheduledMailMessage.ts create mode 100644 backend/src/database/migrations/archive/1681732037710-AddEmailsPerDayColumnToMailbox.ts create mode 100644 backend/src/database/migrations/archive/1681828967422-AddWeightToTaskAndActivity.ts create mode 100644 backend/src/database/migrations/archive/1681832187113-AddWeightToAllTasks.ts create mode 100644 backend/src/database/migrations/archive/1681900142878-ResetWeightForTasksAndActivities.ts create mode 100644 backend/src/database/migrations/archive/1682002593036-AddEntityWeight.ts create mode 100644 backend/src/database/migrations/archive/1682083589639-AddAccountIdTETFeature.ts create mode 100644 backend/src/database/migrations/archive/1682348048312-AddRMS.ts create mode 100644 backend/src/database/migrations/archive/1682350735553-AddIndustries.ts create mode 100644 backend/src/database/migrations/archive/1682433824946-AddDemoMarker.ts create mode 100644 backend/src/database/migrations/archive/1682507153940-AddDocumentTemplate.ts create mode 100644 backend/src/database/migrations/archive/1682518277268-DeleteFileInfoWithUser.ts create mode 100644 backend/src/database/migrations/archive/1682589692981-AddIndexes.ts create mode 100644 backend/src/database/migrations/archive/1683016482581-AddIndexes.ts create mode 100644 backend/src/database/migrations/archive/1683205194466-AddTaskBoardIdToBoard.ts create mode 100644 backend/src/database/migrations/archive/1683517583707-DeleteProjectEntityBoards.ts create mode 100644 backend/src/database/migrations/archive/1683731671898-AddFeatureDocuments.ts create mode 100644 backend/src/database/migrations/archive/1683797890507-AddDocumentTemplateGenerated.ts create mode 100644 backend/src/database/migrations/archive/1683802969000-AddDocumentInFeed.ts create mode 100644 backend/src/database/migrations/archive/1683875863921-TrimUsersNames.ts create mode 100644 backend/src/database/migrations/archive/1684249775346-AddAllTasksStageId.ts create mode 100644 backend/src/database/migrations/archive/1684317847183-AddCreatedByToFileLink.ts create mode 100644 backend/src/database/migrations/archive/1685001497108-ChangeFileInfoHash.ts create mode 100644 backend/src/database/migrations/archive/1685595302584-AddParticipantsToBoard.ts create mode 100644 backend/src/database/migrations/archive/1685604837960-AddChatModel.ts create mode 100644 backend/src/database/migrations/archive/1685689401123-AddDefaultChatProvider.ts create mode 100644 backend/src/database/migrations/archive/1686048795624-AddBoardIdToTask.ts create mode 100644 backend/src/database/migrations/archive/1686061937533-AlterChatMEssageFile.ts create mode 100644 backend/src/database/migrations/archive/1686297344564-AlterMailMessageEntityId.ts create mode 100644 backend/src/database/migrations/archive/1686310775887-AddSignatureToEmailActionSettings.ts create mode 100644 backend/src/database/migrations/archive/1686643536303-AddReplayToInChatMessage.ts create mode 100644 backend/src/database/migrations/archive/1686736715335-AddChatPinnedMessage.ts create mode 100644 backend/src/database/migrations/archive/1686816157824-AddChatPinnedMessage.ts create mode 100644 backend/src/database/migrations/archive/1686824143539-AddChatMessageReaction.ts create mode 100644 backend/src/database/migrations/archive/1686840724427-AddProducts.ts create mode 100644 backend/src/database/migrations/archive/1686904432256-AddChatIdToStatus.ts create mode 100644 backend/src/database/migrations/archive/1686930758334-RenameProductField.ts create mode 100644 backend/src/database/migrations/archive/1687015795997-AlterChatMessageReply.ts create mode 100644 backend/src/database/migrations/archive/1687350416742-RemoveIdFromSubscription.ts create mode 100644 backend/src/database/migrations/archive/1687351857599-AddSubscriptionExternalCustomerId.ts create mode 100644 backend/src/database/migrations/archive/1687790975332-AddOrder.ts create mode 100644 backend/src/database/migrations/archive/1687793191931-FixOrder.ts create mode 100644 backend/src/database/migrations/archive/1687877020115-AddTaxIncluded.ts create mode 100644 backend/src/database/migrations/archive/1687943824933-AddChatProviderTwilio.ts create mode 100644 backend/src/database/migrations/archive/1687954149882-AlterChatProviderUser.ts create mode 100644 backend/src/database/migrations/archive/1687962117509-AddWarehouse.ts create mode 100644 backend/src/database/migrations/archive/1687965328992-AddIsDeletedToWarehouse.ts create mode 100644 backend/src/database/migrations/archive/1688025794222-AlterChatUser.ts create mode 100644 backend/src/database/migrations/archive/1688044274695-AddStocks.ts create mode 100644 backend/src/database/migrations/archive/1688053486248-AddChatUserExternalName.ts create mode 100644 backend/src/database/migrations/archive/1688112039219-AlterChatProviderTwilio.ts create mode 100644 backend/src/database/migrations/archive/1688130606571-AlterChatProviderUser.ts create mode 100644 backend/src/database/migrations/archive/1688136613049-AlterChat.ts create mode 100644 backend/src/database/migrations/archive/1688138872050-AddOrderStatus.ts create mode 100644 backend/src/database/migrations/archive/1688139271540-AddShipment.ts create mode 100644 backend/src/database/migrations/archive/1688140521166-AddStatusIdToOrder.ts create mode 100644 backend/src/database/migrations/archive/1688388514670-AddReservation.ts create mode 100644 backend/src/database/migrations/archive/1688390259595-AlterFileInfoCreatedBy.ts create mode 100644 backend/src/database/migrations/archive/1688394200229-FixStatusIdInOrder.ts create mode 100644 backend/src/database/migrations/archive/1688472386401-AddShipmentDate.ts create mode 100644 backend/src/database/migrations/archive/1688543908016-AddChatProviderMessenger.ts create mode 100644 backend/src/database/migrations/archive/1688567846856-AlterChatUser.ts create mode 100644 backend/src/database/migrations/archive/1688996628275-FixCategoryIdConstraint.ts create mode 100644 backend/src/database/migrations/archive/1689059395581-AddChatProviderStatus.ts create mode 100644 backend/src/database/migrations/archive/1689068374394-AddUserIdToMessengerProvider.ts create mode 100644 backend/src/database/migrations/archive/1689081064483-AddWarehouseIdToOrder.ts create mode 100644 backend/src/database/migrations/archive/1689087134759-AddModules.ts create mode 100644 backend/src/database/migrations/archive/1689170448447-AddProductType.ts create mode 100644 backend/src/database/migrations/archive/1689243925753-FixOrderStatuses.ts create mode 100644 backend/src/database/migrations/archive/1689259268310-DeleteShipmentStatus.ts create mode 100644 backend/src/database/migrations/archive/1689337185167-AddProductsFeature.ts create mode 100644 backend/src/database/migrations/archive/1689508562776-AddIsActiveToReservation.ts create mode 100644 backend/src/database/migrations/archive/1689763128902-RemoveNoteRecipientId.ts create mode 100644 backend/src/database/migrations/archive/1689774963182-AddUpdatedAtToProduct.ts create mode 100644 backend/src/database/migrations/archive/1689860682052-AddSchedule.ts create mode 100644 backend/src/database/migrations/archive/1689933154489-AlterSchedulePerformer.ts create mode 100644 backend/src/database/migrations/archive/1690208012261-AddProductModule.ts create mode 100644 backend/src/database/migrations/archive/1690456178510-MigrateModuleToProductModule.ts create mode 100644 backend/src/database/migrations/archive/1690467527775-RemoveModule.ts create mode 100644 backend/src/database/migrations/archive/1690469860109-AddProductPermissions.ts create mode 100644 backend/src/database/migrations/archive/1690543599386-DropProductPermissions.ts create mode 100644 backend/src/database/migrations/archive/1690817128717-AlterProductModule.ts create mode 100644 backend/src/database/migrations/archive/1690973831680-AddSectionIdToProducts.ts create mode 100644 backend/src/database/migrations/archive/1691056504886-AddSectionIdToOrderAndShipment.ts create mode 100644 backend/src/database/migrations/archive/1691061102493-DeleteShipmentStatus.ts create mode 100644 backend/src/database/migrations/archive/1691061408646-AlterTableStockToProductStock.ts create mode 100644 backend/src/database/migrations/archive/1691139996885-AddProductsSectionEntityType.ts create mode 100644 backend/src/database/migrations/archive/1691155049107-AddRentalInterval.ts create mode 100644 backend/src/database/migrations/archive/1691397636905-AlterProductsSectionType.ts create mode 100644 backend/src/database/migrations/archive/1691411118591-AddRentalOrder.ts create mode 100644 backend/src/database/migrations/archive/1691414938591-AddRentalSchedule.ts create mode 100644 backend/src/database/migrations/archive/1691657890280-AddRentalOrderPeriod.ts create mode 100644 backend/src/database/migrations/archive/1691678125349-AddRentalOrderPeriodAccountId.ts create mode 100644 backend/src/database/migrations/archive/1691754596482-AlterRentalOrder.ts create mode 100644 backend/src/database/migrations/archive/1691755141714-AlterRentalOrderItem.ts create mode 100644 backend/src/database/migrations/archive/1692002092660-RentalScheduleAddSectionId.ts create mode 100644 backend/src/database/migrations/archive/1692014115943-RenameRentalScheduleToRentalEvent.ts create mode 100644 backend/src/database/migrations/archive/1692170842159-AddProductsSectionEnableWarehouse.ts create mode 100644 backend/src/database/migrations/archive/1692172254434-AddOrdersOrderNumber.ts create mode 100644 backend/src/database/migrations/archive/1692172318353-AddRentalOrderOrderNumber.ts create mode 100644 backend/src/database/migrations/archive/1692283603851-AlterOrderItemCascadeDelete.ts create mode 100644 backend/src/database/migrations/archive/1692343747646-AlterProductStock.ts create mode 100644 backend/src/database/migrations/archive/1692354371998-FixFieldValueType.ts create mode 100644 backend/src/database/migrations/archive/1692604044210-ProductsSectionEnableBarcode.ts create mode 100644 backend/src/database/migrations/archive/1692708295281-AlterOrderStatusNull.ts create mode 100644 backend/src/database/migrations/archive/1692885285551-RefactorScheduler.ts create mode 100644 backend/src/database/migrations/archive/1692890628636-RenameScheduleEvent.ts create mode 100644 backend/src/database/migrations/archive/1692975377102-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1693218884137-UserPosition.ts create mode 100644 backend/src/database/migrations/archive/1693232990040-ScheduleAppointmentOrderId.ts create mode 100644 backend/src/database/migrations/archive/1693485238189-OrderStatusColor.ts create mode 100644 backend/src/database/migrations/archive/1693556962547-CascadeDeleteShipment.ts create mode 100644 backend/src/database/migrations/archive/1694085886365-SchedulerIcon.ts create mode 100644 backend/src/database/migrations/archive/1694166234404-BoardCleanProjectParticipants.ts create mode 100644 backend/src/database/migrations/archive/1695040324876-AppointmentTitle.ts create mode 100644 backend/src/database/migrations/archive/1695046445852-EntityClosedAt.ts create mode 100644 backend/src/database/migrations/archive/1695201739381-SalesPlan.ts create mode 100644 backend/src/database/migrations/archive/1695287859742-ChatDeleteCascadeEntity.ts create mode 100644 backend/src/database/migrations/archive/1695382850916-SalesPlanAlterAmount.ts create mode 100644 backend/src/database/migrations/archive/1695742049917-VoximplantUser.ts create mode 100644 backend/src/database/migrations/archive/1695743140564-VoximplantUserPrimaryColumn.ts create mode 100644 backend/src/database/migrations/archive/1695808984339-VoximplantUserPassword.ts create mode 100644 backend/src/database/migrations/archive/1695810676148-VoximplantAccount.ts create mode 100644 backend/src/database/migrations/archive/1695820387969-AlterVoximplantUser.ts create mode 100644 backend/src/database/migrations/archive/1696500815450-DemoData.ts create mode 100644 backend/src/database/migrations/archive/1697019761609-VoximplantCall.ts create mode 100644 backend/src/database/migrations/archive/1697028866185-AlterVoximplantUser.ts create mode 100644 backend/src/database/migrations/archive/1697115016543-RefactorDemoData.ts create mode 100644 backend/src/database/migrations/archive/1697440579544-SchedulerEventPerformerCascade.ts create mode 100644 backend/src/database/migrations/archive/1697452411558-VoximplantUserActive.ts create mode 100644 backend/src/database/migrations/archive/1697541300418-VoximplantAccount.ts create mode 100644 backend/src/database/migrations/archive/1697541767120-VoximplantAccountAlter.ts create mode 100644 backend/src/database/migrations/archive/1697543064652-VoximplantAccountEmail.ts create mode 100644 backend/src/database/migrations/archive/1697556130148-VoximplantAccountKey.ts create mode 100644 backend/src/database/migrations/archive/1697702819805-VoximplantCallAlterExternalId.ts create mode 100644 backend/src/database/migrations/archive/1698135013349-ReportingOptimization.ts create mode 100644 backend/src/database/migrations/archive/1698421539770-VoximplantScenarios.ts create mode 100644 backend/src/database/migrations/archive/1698663785490-OrderStatusColors.ts create mode 100644 backend/src/database/migrations/archive/1699457673085-AddEntityEventModel.ts create mode 100644 backend/src/database/migrations/archive/1700060543771-AddSchedulePerformerId.ts create mode 100644 backend/src/database/migrations/archive/1700060859571-AlterSchedulePerformer.ts create mode 100644 backend/src/database/migrations/archive/1700230572219-AlterMailbox.ts create mode 100644 backend/src/database/migrations/archive/1700395051542-AlterExternalEntity.ts create mode 100644 backend/src/database/migrations/archive/1700475817946-AllEventsMigrationScript.ts create mode 100644 backend/src/database/migrations/archive/1700591906266-TelephonyCallsToEntityEvent.ts create mode 100644 backend/src/database/migrations/archive/1700663233866-AlterSchedulerAppointment.ts create mode 100644 backend/src/database/migrations/archive/1700729760783-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1700733045104-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1700735236205-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1700741072037-ProductOrdersToEntityEvents.ts create mode 100644 backend/src/database/migrations/archive/1700820935837-AlterSchedulerAppointment.ts create mode 100644 backend/src/database/migrations/archive/1700836699189-ShipmentsToEntityEvents.ts create mode 100644 backend/src/database/migrations/archive/1701264274255-CallsToEntityEventsFix.ts create mode 100644 backend/src/database/migrations/archive/1701437712747-DeleteEntityEventTelephonyCalls.ts create mode 100644 backend/src/database/migrations/archive/1701701891843-AlterVoximplantCall.ts create mode 100644 backend/src/database/migrations/archive/1702542289418-UpdateModulesIcons.ts create mode 100644 backend/src/database/migrations/archive/1702637665853-AlterActivityType.ts create mode 100644 backend/src/database/migrations/archive/1702970939958-AlterAccountSettings.ts create mode 100644 backend/src/database/migrations/archive/1703085253100-AlterProductPrice.ts create mode 100644 backend/src/database/migrations/archive/1703488850551-AlterAccountSettings.ts create mode 100644 backend/src/database/migrations/archive/1703502036545-FieldSettings.ts create mode 100644 backend/src/database/migrations/archive/1703761495779-AlterFieldStageSettings.ts create mode 100644 backend/src/database/migrations/archive/1703850851646-AlterShipment.ts create mode 100644 backend/src/database/migrations/archive/1703857140848-AlterReservation.ts create mode 100644 backend/src/database/migrations/archive/1703876929122-AlterChangeStageActionSettings.ts create mode 100644 backend/src/database/migrations/archive/1704282894747-AlterEntity.ts create mode 100644 backend/src/database/migrations/archive/1706795467082-TestAccount.ts create mode 100644 backend/src/database/migrations/archive/1708088397272-EntityStageChange.ts create mode 100644 backend/src/database/migrations/archive/1708433846254-EntityChangeHistoryInit.ts create mode 100644 backend/src/database/migrations/archive/1708589222946-UserAnalyticsId.ts create mode 100644 backend/src/database/migrations/archive/1708952321460-OptimizeNotificationIndexes.ts create mode 100644 backend/src/database/migrations/archive/1709047301377-DBOptimizationIndex.ts create mode 100644 backend/src/database/migrations/archive/1709048922111-StageAccountIdIndex.ts create mode 100644 backend/src/database/migrations/archive/1709110989045-ScheduledMailMessageIndex.ts create mode 100644 backend/src/database/migrations/archive/1709111575857-AddIndexes.ts create mode 100644 backend/src/database/migrations/archive/1709280253891-ChatProviderCascadeDelete.ts create mode 100644 backend/src/database/migrations/archive/1709736232826-ChatEntityRemoveCascade.ts create mode 100644 backend/src/database/migrations/archive/1709805560320-ChatUserExternal.ts create mode 100644 backend/src/database/migrations/archive/1710162901881-EntityCopiedCount.ts create mode 100644 backend/src/database/migrations/archive/1710758264055-OrderCancelAfter.ts create mode 100644 backend/src/database/migrations/archive/1710759144910-ProductSectionCancelAfter.ts create mode 100644 backend/src/database/migrations/archive/1710864090375-ScheduledActionCreatedBy.ts create mode 100644 backend/src/database/migrations/archive/1710927112868-TutorialGroup.ts create mode 100644 backend/src/database/migrations/archive/1710929893275-TutorialItem.ts create mode 100644 backend/src/database/migrations/archive/1710939538331-TutorialItemUser.ts create mode 100644 backend/src/database/migrations/archive/1711033243401-TutorialItemProduct.ts create mode 100644 backend/src/database/migrations/archive/1711087326245-MailMessageIndexes.ts create mode 100644 backend/src/database/migrations/archive/1711540999340-EntityActionSettings.ts create mode 100644 backend/src/database/migrations/archive/1711541635402-ActionSettingsRename.ts create mode 100644 backend/src/database/migrations/archive/1711706670268-ScheduledAction.ts create mode 100644 backend/src/database/migrations/archive/1711962655915-AutomationActionSettings.ts create mode 100644 backend/src/database/migrations/archive/1712575547663-FieldValue.ts create mode 100644 backend/src/database/migrations/archive/1713167989297-DeleteAllFormulaFields.ts create mode 100644 backend/src/database/migrations/archive/1713257835467-FixEntityTypeSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1713258622876-FixEntityTypeLinkSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1713971186799-ChatProviderTransport.ts create mode 100644 backend/src/database/migrations/archive/1714382561376-WazzupProvider.ts create mode 100644 backend/src/database/migrations/archive/1714557065128-ProductPricePrecision.ts create mode 100644 backend/src/database/migrations/archive/1714663341741-WazzupProviderRemoveChatType.ts create mode 100644 backend/src/database/migrations/archive/1714730508391-WazzupProviderTransport.ts create mode 100644 backend/src/database/migrations/archive/1714732587962-RemoveWazzupProviders.ts create mode 100644 backend/src/database/migrations/archive/1714734600334-ChatProviderTypeRename.ts create mode 100644 backend/src/database/migrations/archive/1715602468891-ChatProviderUserType.ts create mode 100644 backend/src/database/migrations/archive/1715610371002-AlterChatUserExternal.ts create mode 100644 backend/src/database/migrations/archive/1715856544173-AccountApiAccess.ts create mode 100644 backend/src/database/migrations/archive/1715856948928-AlterAccountApiAccess.ts create mode 100644 backend/src/database/migrations/archive/1716299180820-VoximplantNumber.ts create mode 100644 backend/src/database/migrations/archive/1716384743872-VoximplantPhoneNumber.ts create mode 100644 backend/src/database/migrations/archive/1716465152984-VoximplantNumberDefaults.ts create mode 100644 backend/src/database/migrations/archive/1716466922606-VoximplantCallNumber.ts create mode 100644 backend/src/database/migrations/archive/1716469802549-VoximplantCallNumberDefault.ts create mode 100644 backend/src/database/migrations/archive/1717599382958-AddForms.ts create mode 100644 backend/src/database/migrations/archive/1717746424353-SiteFormPageSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1717773341006-FormSiteLink.ts create mode 100644 backend/src/database/migrations/archive/1718030543491-SiteFormEntityType.ts create mode 100644 backend/src/database/migrations/archive/1718098299378-SiteFormRemoveConsent.ts create mode 100644 backend/src/database/migrations/archive/1718098642901-SiteFormConsent.ts create mode 100644 backend/src/database/migrations/archive/1718115282972-SiteFormGratitude.ts create mode 100644 backend/src/database/migrations/archive/1718118622975-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1718120948980-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1718266194827-NotificationRemoveTag.ts create mode 100644 backend/src/database/migrations/archive/1718613418648-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1718715571983-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1718724129043-AutomationProcess.ts create mode 100644 backend/src/database/migrations/archive/1718793150525-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1718793757895-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1718798058177-AlterAutomationProcess.ts create mode 100644 backend/src/database/migrations/archive/1718801950089-AlterAutomationProcess.ts create mode 100644 backend/src/database/migrations/archive/1718880290844-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1719213418299-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1719302707526-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1719324782700-RenameSubtask.ts create mode 100644 backend/src/database/migrations/archive/1719325474889-AlterTaskSubtaskSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1719393706903-AlterAutomationProcess.ts create mode 100644 backend/src/database/migrations/archive/1719396909800-AutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719404585119-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719411072571-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719414260257-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719502764234-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719502897605-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719833217147-AlterEntityType.ts create mode 100644 backend/src/database/migrations/archive/1719995612500-UpdateCopiesCreatedAt.ts create mode 100644 backend/src/database/migrations/archive/1720076313493-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1720077438733-AlterSiteFormEntityType.ts create mode 100644 backend/src/database/migrations/archive/1720194016236-AppSumoLicense.ts create mode 100644 backend/src/database/migrations/archive/1720194705296-AppSumoPreset.ts create mode 100644 backend/src/database/migrations/archive/1720423072348-AppSumoPresets.ts create mode 100644 backend/src/database/migrations/archive/1720423711324-RenameAppSumo.ts create mode 100644 backend/src/database/migrations/archive/1720434041376-AppsumoLicenseUnique.ts create mode 100644 backend/src/database/migrations/archive/1720446828855-AlterReadyMadeSolution.ts create mode 100644 backend/src/database/migrations/archive/1720530507278-RenameAppsumoPreset.ts create mode 100644 backend/src/database/migrations/archive/1720530714621-AlterAppsumoTier.ts create mode 100644 backend/src/database/migrations/archive/1720531175011-RenameSubscription.ts create mode 100644 backend/src/database/migrations/archive/1720596831169-AlterAppsumoTier.ts create mode 100644 backend/src/database/migrations/archive/1720597683965-UpdateAccountSubscription.ts create mode 100644 backend/src/database/migrations/archive/1720610809785-AlterAppsumoLicense.ts create mode 100644 backend/src/database/migrations/archive/1720619947686-AlterAppsumoTier.ts create mode 100644 backend/src/database/migrations/archive/1720778334472-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1721221679342-VoximplantSip.ts create mode 100644 backend/src/database/migrations/archive/1722240313665-DBMemoryOptimization.ts create mode 100644 backend/src/database/migrations/archive/1722240423838-DBIndexOptimization.ts create mode 100644 backend/src/database/migrations/archive/1722250997991-FieldSettingsIndexes.ts create mode 100644 backend/src/database/migrations/archive/1722251334680-AccountApiAccessIndex.ts create mode 100644 backend/src/database/migrations/archive/1722325875311-UpdateProjectFieldsSortOrder.ts create mode 100644 backend/src/database/migrations/archive/1722340602724-DepartmentIndexes.ts create mode 100644 backend/src/database/migrations/archive/1722500514353-EntityListSettingsIndex.ts create mode 100644 backend/src/database/migrations/archive/1722870704258-AlterDepartmentIsActive.ts create mode 100644 backend/src/database/migrations/archive/1722871843239-AlterUserDepartment.ts create mode 100644 backend/src/database/migrations/archive/1723373462514-AddMailMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1723386042005-AlterMailMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1723641840096-AlterMailMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1723814067070-MailboxEmailsPerDay.ts create mode 100644 backend/src/database/migrations/archive/1724058794170-AlterFieldGroup.ts create mode 100644 backend/src/database/migrations/archive/1724422282832-EntityUpdatedAt.ts create mode 100644 backend/src/database/migrations/archive/1724657864701-AlterVoximplantSipName.ts create mode 100644 backend/src/database/migrations/archive/1724658513639-VoximplantSipUser.ts create mode 100644 backend/src/database/migrations/archive/1724668825212-EntityBoardId.ts create mode 100644 backend/src/database/migrations/archive/1724744316233-FieldGroupDefault.ts create mode 100644 backend/src/database/migrations/archive/1724747098462-DeprtmentIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1724761173125-DepartmentSettings.ts create mode 100644 backend/src/database/migrations/archive/1724944661564-UserIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1724945283499-AccountIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1724946396552-DocumentTemplateIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1724946751431-NotificationIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1724946917573-NotificationSettingsIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1724946935740-NotificationTypeSettingsIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725008953241-EntityTypeActionTypeRename.ts create mode 100644 backend/src/database/migrations/archive/1725024403154-ChatIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725024779580-ChatMessageFileIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725024956416-ChatMessageReactionIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725025200036-ChatMessageIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725025378404-ChatProviderIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725025624010-ChatUserIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1725269507813-ChatProviderMessagePerDay.ts create mode 100644 backend/src/database/migrations/archive/1725622826309-MailboxLatsActiveAt.ts create mode 100644 backend/src/database/migrations/archive/1725980172270-EntityCopiedFromConstrains.ts create mode 100644 backend/src/database/migrations/archive/1726478454833-ChatMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1727179988017-AlterChatMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1727265648107-MailFolderOptimization.ts create mode 100644 backend/src/database/migrations/archive/1727682923696-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1727766355582-TaskCommentIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1727775973380-AlterAutomationProcess.ts create mode 100644 backend/src/database/migrations/archive/1728296331516-RenameStage.ts create mode 100644 backend/src/database/migrations/archive/1728312582354-AlterBoardStage.ts create mode 100644 backend/src/database/migrations/archive/1728384950674-BoardIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1728402696162-AlterBoard.ts create mode 100644 backend/src/database/migrations/archive/1728465792633-AlterBoardStage.ts create mode 100644 backend/src/database/migrations/archive/1728468439082-AlterBoardStage.ts create mode 100644 backend/src/database/migrations/archive/1728471228996-AlterAutomationEntityType.ts create mode 100644 backend/src/database/migrations/archive/1728892125136-AlterAccountSettings.ts create mode 100644 backend/src/database/migrations/archive/1728981267739-AlterObjectPermission.ts create mode 100644 backend/src/database/migrations/archive/1728984932906-AlterObjectPermissionIndices.ts create mode 100644 backend/src/database/migrations/archive/1728988723314-AlterObjectPermission.ts create mode 100644 backend/src/database/migrations/archive/1729001229086-AlterObjectPermission.ts create mode 100644 backend/src/database/migrations/archive/1729680141757-UpdateTutorialItem.ts create mode 100644 backend/src/database/migrations/archive/1729758131725-AlterMailMessage.ts create mode 100644 backend/src/database/migrations/archive/1729853713049-RemoveUnusedIndexes.ts create mode 100644 backend/src/database/migrations/archive/1729856828756-ActivityTypeIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1729857179487-EntityTypeIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1729859612700-FieldValueIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1730103007231-EntitySearch.ts create mode 100644 backend/src/database/migrations/archive/1730212128907-ScheduleAppointmentIndexes.ts create mode 100644 backend/src/database/migrations/archive/1730472621229-TaskIndexes.ts create mode 100644 backend/src/database/migrations/archive/1730796442786-AlterMailMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1730988793369-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1731239585214-UsersAccessibleUsers.ts create mode 100644 backend/src/database/migrations/archive/1731318295236-FieldIndex.ts create mode 100644 backend/src/database/migrations/archive/1731491092637-WarehouseIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731491294395-ShipmentItemIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731491510990-ShipmentIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731491686717-ReservationIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731502099273-ProductCategoryIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731502604723-ProductIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731502805263-ProductPriceIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731503055930-OrderIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731503302276-OrderItemIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731503975362-OrderStatusIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1731512848862-AlterWarehouse.ts create mode 100644 backend/src/database/migrations/archive/1731682593963-AddVersion.ts create mode 100644 backend/src/database/migrations/archive/1732029352587-ObjectPermissionDepartmentFix.ts create mode 100644 backend/src/database/migrations/archive/1732272936256-TaskSettingsIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1732281701728-OptimizationIndexes.ts create mode 100644 backend/src/database/migrations/archive/1732521216471-AlterAccountSettings.ts create mode 100644 backend/src/database/migrations/archive/1732627160688-IndexOptimization.ts create mode 100644 backend/src/database/migrations/archive/1732628496619-BoardIndexes.ts create mode 100644 backend/src/database/migrations/archive/1732697048964-IndexOptimization.ts create mode 100644 backend/src/database/migrations/archive/1732716375115-EntityTypeLinkIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1732783039132-EntityTypeLinkOrphans.ts create mode 100644 backend/src/database/migrations/archive/1732866955213-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1732876120184-UpdateEntityBoardAndStage.ts create mode 100644 backend/src/database/migrations/archive/1732889802526-FrontendObject.ts create mode 100644 backend/src/database/migrations/archive/1733136867799-UpdateScheduleAppointmentOwner.ts create mode 100644 backend/src/database/migrations/archive/1733393739852-ConditionIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733394028781-ScheduledMailMessageIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733394215019-TriggerIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733394597308-ActionScheduledIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733394799717-ActionIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733394948832-AutomationIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733395768414-NoteIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733395930995-ActivityIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733395960444-TaskIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733396343770-TaskSubtaskIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733397036241-MailMessageIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733397284175-MailMessagePayloadIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733397518270-MailboxFolderIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733397748405-MailboxIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1733397911411-MailboxSignatureIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1734009141643-AlterEntityValue.ts create mode 100644 backend/src/database/migrations/archive/1734081841312-AlterUserProfile.ts create mode 100644 backend/src/database/migrations/archive/1734097700161-AlterEntityLink.ts create mode 100644 backend/src/database/migrations/archive/1734349336443-EntityLinkIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1734352488667-RemoveEntityListSettings.ts create mode 100644 backend/src/database/migrations/archive/1734429865379-ScheduleAppointmentIndexes.ts create mode 100644 backend/src/database/migrations/archive/1734600811704-AlterChatMessageScheduled.ts create mode 100644 backend/src/database/migrations/archive/1734688459744-AlterField.ts create mode 100644 backend/src/database/migrations/archive/1734708417316-VersionIndexes.ts create mode 100644 backend/src/database/migrations/archive/1734945712418-AlterSiteFormField.ts create mode 100644 backend/src/database/migrations/archive/1735140161718-AddGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1735289134467-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1735290249052-AlterTask.ts create mode 100644 backend/src/database/migrations/archive/1735573056216-IndexGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1736239080782-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1736262949465-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1736324153278-GoogleCalendarIndexes.ts create mode 100644 backend/src/database/migrations/archive/1736330451213-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1736331095918-GoogleCalendarIndexes.ts create mode 100644 backend/src/database/migrations/archive/1736339315376-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1736496705277-AlterTask.ts create mode 100644 backend/src/database/migrations/archive/1736513599793-IndexesTask.ts create mode 100644 backend/src/database/migrations/archive/1736773476711-GoogleCalendarLinked.ts create mode 100644 backend/src/database/migrations/archive/1736781493022-IndexesGoogleCalendarLinked.ts create mode 100644 backend/src/database/migrations/archive/1736842860059-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1736948650910-AlterScheduleAppointment.ts create mode 100644 backend/src/database/migrations/archive/1737033530086-AlterAccountSubscription.ts create mode 100644 backend/src/database/migrations/archive/1737039160966-AddSubscriptionDiscount.ts create mode 100644 backend/src/database/migrations/archive/1737103293642-UpdateAccountSubscription.ts create mode 100644 backend/src/database/migrations/archive/1737103460794-AlterAccountSubscription.ts create mode 100644 backend/src/database/migrations/archive/1737103855502-InsertSubscriptionDiscount.ts create mode 100644 backend/src/database/migrations/archive/1737378761002-AddGoogleCalendarAccount.ts create mode 100644 backend/src/database/migrations/archive/1737971712981-AlterSubscriptionDiscount.ts create mode 100644 backend/src/database/migrations/archive/1737973113027-UpdateSubscriptionDiscount.ts create mode 100644 backend/src/database/migrations/archive/1738660778536-RemoveOldAutomation.ts create mode 100644 backend/src/database/migrations/archive/1738676409194-AlterUserProfile.ts create mode 100644 backend/src/database/migrations/archive/1739788991506-AlterGoogleCalendar.ts create mode 100644 backend/src/database/migrations/archive/1739802958618-AlterAccountSettings.ts create mode 100644 backend/src/database/migrations/archive/1739891080452-AddUserToken.ts create mode 100644 backend/src/database/migrations/archive/1740402538635-AddUserCalendar.ts create mode 100644 backend/src/database/migrations/archive/1740472617092-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1740479281651-AddScheduleTimeIntervalService.ts create mode 100644 backend/src/database/migrations/archive/1740579698409-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1740583763973-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1740654525458-AddSiteFormSchedule.ts create mode 100644 backend/src/database/migrations/archive/1741610604818-AlterSiteForm.ts create mode 100644 backend/src/database/migrations/archive/1741701747438-UpdateSchedule.ts create mode 100644 backend/src/database/migrations/archive/1741702060670-AlterSchedule.ts create mode 100644 backend/src/database/migrations/archive/1742477677993-AddChatProviderEntitySettings.ts create mode 100644 backend/src/database/migrations/archive/1742912395334-AlterChatProviderEntitySettings.ts create mode 100644 backend/src/database/migrations/archive/1743065071601-AlterChatProviderEntitySettings.ts create mode 100644 backend/src/database/migrations/archive/1743084130609-AlterProductStock.ts create mode 100644 backend/src/database/migrations/archive/1743592762254-AddMailboxEntitySettings.ts create mode 100644 backend/src/database/migrations/archive/1743595365132-InsertMailboxEntitySettings.ts create mode 100644 backend/src/database/migrations/archive/1743595431125-AlterMailbox.ts create mode 100644 backend/src/database/migrations/archive/1743690907799-AlterEntityLink.ts create mode 100644 backend/src/database/migrations/archive/1743771835815-EntityIdIdentity.ts create mode 100644 backend/src/database/migrations/archive/1744127440588-AlterEntity.ts create mode 100644 backend/src/database/migrations/archive/1744296868607-AlterMailboxFolder.ts create mode 100644 backend/src/database/migrations/archive/1744374434852-AlterMailboxSignatureLink.ts create mode 100644 backend/src/database/migrations/archive/1744382844583-AlterMailboxSignature.ts create mode 100644 backend/src/database/nestjs-logger.ts create mode 100644 backend/src/database/services/index.ts create mode 100644 backend/src/database/services/sequence-id.service.ts create mode 100644 backend/src/database/typeorm-migration.config.ts create mode 100644 backend/src/documentation/api-documentation.ts create mode 100644 backend/src/documentation/config/documentation.config.ts create mode 100644 backend/src/documentation/index.ts create mode 100644 backend/src/main.ts create mode 100644 backend/src/modules/analytics/analytics.module.ts create mode 100644 backend/src/modules/analytics/analytics.service.ts create mode 100644 backend/src/modules/analytics/config/analytics.config.ts create mode 100644 backend/src/modules/automation/automation-core/automation-core.controller.ts create mode 100644 backend/src/modules/automation/automation-core/automation-core.service.ts create mode 100644 backend/src/modules/automation/automation-core/index.ts create mode 100644 backend/src/modules/automation/automation-core/types/index.ts create mode 100644 backend/src/modules/automation/automation-core/types/process-definition-version.ts create mode 100644 backend/src/modules/automation/automation-entity-type/automation-entity-type.controller.ts create mode 100644 backend/src/modules/automation/automation-entity-type/automation-entity-type.handler.ts create mode 100644 backend/src/modules/automation/automation-entity-type/automation-entity-type.service.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-activity-create-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-amwork-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-email-send-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-create-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-linked-stage-change-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-responsible-change-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-stage-change-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-entity.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-value.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/action-task-create-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/actions/index.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/automation-entity-type-filter.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/automation-entity-type.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/create-automation-entity-type.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/entity-type-action.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/entity-type-condition.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/index.ts create mode 100644 backend/src/modules/automation/automation-entity-type/dto/update-automation-entity-type.dto.ts create mode 100644 backend/src/modules/automation/automation-entity-type/entities/automation-entity-type.entity.ts create mode 100644 backend/src/modules/automation/automation-entity-type/entities/index.ts create mode 100644 backend/src/modules/automation/automation-entity-type/enums/change-stage-type.enum.ts create mode 100644 backend/src/modules/automation/automation-entity-type/enums/deadline-type.enum.ts create mode 100644 backend/src/modules/automation/automation-entity-type/enums/entity-type-action-type.enum.ts create mode 100644 backend/src/modules/automation/automation-entity-type/enums/entity-type-trigger.enum.ts create mode 100644 backend/src/modules/automation/automation-entity-type/enums/index.ts create mode 100644 backend/src/modules/automation/automation-entity-type/helpers/action.helper.ts create mode 100644 backend/src/modules/automation/automation-entity-type/helpers/index.ts create mode 100644 backend/src/modules/automation/automation-entity-type/index.ts create mode 100644 backend/src/modules/automation/automation-entity-type/templates/simple_automation.bpmn.template create mode 100644 backend/src/modules/automation/automation-http/automation-http.controller.ts create mode 100644 backend/src/modules/automation/automation-http/automation-http.handler.ts create mode 100644 backend/src/modules/automation/automation-http/automation-http.service.ts create mode 100644 backend/src/modules/automation/automation-http/dto/action-http-call-settings.dto.ts create mode 100644 backend/src/modules/automation/automation-http/dto/index.ts create mode 100644 backend/src/modules/automation/automation-http/dto/process-data.dto.ts create mode 100644 backend/src/modules/automation/automation-http/enums/http-action-type.enum.ts create mode 100644 backend/src/modules/automation/automation-http/enums/index.ts create mode 100644 backend/src/modules/automation/automation-http/index.ts create mode 100644 backend/src/modules/automation/automation-process/automation-process.controller.ts create mode 100644 backend/src/modules/automation/automation-process/automation-process.handler.ts create mode 100644 backend/src/modules/automation/automation-process/automation-process.service.ts create mode 100644 backend/src/modules/automation/automation-process/dto/automation-process-filter.dto.ts create mode 100644 backend/src/modules/automation/automation-process/dto/automation-process.dto.ts create mode 100644 backend/src/modules/automation/automation-process/dto/create-automation-process.dto.ts create mode 100644 backend/src/modules/automation/automation-process/dto/index.ts create mode 100644 backend/src/modules/automation/automation-process/dto/update-automation-process.dto.ts create mode 100644 backend/src/modules/automation/automation-process/entities/automation-process.entity.ts create mode 100644 backend/src/modules/automation/automation-process/entities/index.ts create mode 100644 backend/src/modules/automation/automation-process/errors/automation-process.error.ts create mode 100644 backend/src/modules/automation/automation-process/errors/index.ts create mode 100644 backend/src/modules/automation/automation-process/index.ts create mode 100644 backend/src/modules/automation/automation-process/types/index.ts create mode 100644 backend/src/modules/automation/automation-process/types/readonly-process.ts create mode 100644 backend/src/modules/automation/automation-utils/automation-utils.controller.ts create mode 100644 backend/src/modules/automation/automation-utils/index.ts create mode 100644 backend/src/modules/automation/automation.module.ts create mode 100644 backend/src/modules/automation/common/decorators/automation-worker.decorator.ts create mode 100644 backend/src/modules/automation/common/decorators/index.ts create mode 100644 backend/src/modules/automation/common/decorators/on-automation-job.decorator.ts create mode 100644 backend/src/modules/automation/common/dto/action/action-settings.dto.ts create mode 100644 backend/src/modules/automation/common/dto/action/index.ts create mode 100644 backend/src/modules/automation/common/dto/condition/automation-entity-condition.dto.ts create mode 100644 backend/src/modules/automation/common/dto/condition/automation-field-condition.dto.ts create mode 100644 backend/src/modules/automation/common/dto/condition/index.ts create mode 100644 backend/src/modules/automation/common/dto/index.ts create mode 100644 backend/src/modules/automation/common/enums/automation-process-type.enum.ts create mode 100644 backend/src/modules/automation/common/enums/index.ts create mode 100644 backend/src/modules/automation/common/events/automation-event-type.enum.ts create mode 100644 backend/src/modules/automation/common/events/core/index.ts create mode 100644 backend/src/modules/automation/common/events/core/send-message.event.ts create mode 100644 backend/src/modules/automation/common/events/core/send-signal.event.ts create mode 100644 backend/src/modules/automation/common/events/entity-type/entity-type-apply.event.ts create mode 100644 backend/src/modules/automation/common/events/entity-type/index.ts create mode 100644 backend/src/modules/automation/common/events/index.ts create mode 100644 backend/src/modules/automation/common/events/process/index.ts create mode 100644 backend/src/modules/automation/common/events/process/process-start.event.ts create mode 100644 backend/src/modules/automation/common/index.ts create mode 100644 backend/src/modules/automation/common/interfaces/automation-handler.interface.ts create mode 100644 backend/src/modules/automation/common/interfaces/index.ts create mode 100644 backend/src/modules/automation/common/types/index.ts create mode 100644 backend/src/modules/automation/common/types/message.interface.ts create mode 100644 backend/src/modules/automation/common/types/signal.interface.ts create mode 100644 backend/src/modules/automation/common/utils/automation-condition.util.ts create mode 100644 backend/src/modules/automation/common/utils/automation-delay.util.ts create mode 100644 backend/src/modules/automation/common/utils/index.ts create mode 100644 backend/src/modules/automation/config/automation.config.ts create mode 100644 backend/src/modules/automation/index.ts create mode 100644 backend/src/modules/data-enrichment/config/data-enrichment.config.ts create mode 100644 backend/src/modules/data-enrichment/data-enrichment.controller.ts create mode 100644 backend/src/modules/data-enrichment/data-enrichment.module.ts create mode 100644 backend/src/modules/data-enrichment/data-enrichment.service.ts create mode 100644 backend/src/modules/data-enrichment/dto/bank-requisites.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/fio.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/index.ts create mode 100644 backend/src/modules/data-enrichment/dto/organization-requisites-address.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/organization-requisites-managment.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/organization-requisites-name.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/organization-requisites-state.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/organization-requisites.dto.ts create mode 100644 backend/src/modules/data-enrichment/dto/phone-user-info.dto.ts create mode 100644 backend/src/modules/data-enrichment/enums/index.ts create mode 100644 backend/src/modules/data-enrichment/enums/opf-type.enum.ts create mode 100644 backend/src/modules/data-enrichment/enums/org-branch-type.enum.ts create mode 100644 backend/src/modules/data-enrichment/enums/org-status.enum.ts create mode 100644 backend/src/modules/data-enrichment/enums/org-type.enum.ts create mode 100644 backend/src/modules/data-enrichment/helpers/get-ru-country-name-by-iso-code.helper.ts create mode 100644 backend/src/modules/data-enrichment/helpers/get-utc-offset-by-ru-country-name.helper.ts create mode 100644 backend/src/modules/data-enrichment/helpers/index.ts create mode 100644 backend/src/modules/data-enrichment/types/bank-requisites.ts create mode 100644 backend/src/modules/data-enrichment/types/dadata-bank-requisites.ts create mode 100644 backend/src/modules/data-enrichment/types/dadata-org-requisites.ts create mode 100644 backend/src/modules/data-enrichment/types/dadata-suggestion.ts create mode 100644 backend/src/modules/data-enrichment/types/dadata-suggestions.ts create mode 100644 backend/src/modules/data-enrichment/types/index.ts create mode 100644 backend/src/modules/data-enrichment/types/organization-requisites.ts create mode 100644 backend/src/modules/data-enrichment/types/phone-user-info-fincalculator.ts create mode 100644 backend/src/modules/data-enrichment/types/phone-user-info-geohelper.ts create mode 100644 backend/src/modules/data-enrichment/types/phone-user-info-sp-nova.ts create mode 100644 backend/src/modules/data-enrichment/types/phone-user-info.ts create mode 100644 backend/src/modules/documents/common/errors/document-template.error.ts create mode 100644 backend/src/modules/documents/common/errors/index.ts create mode 100644 backend/src/modules/documents/common/index.ts create mode 100644 backend/src/modules/documents/config/documents.config.ts create mode 100644 backend/src/modules/documents/config/index.ts create mode 100644 backend/src/modules/documents/document-generation/document-generation.controller.ts create mode 100644 backend/src/modules/documents/document-generation/document-generation.service.ts create mode 100644 backend/src/modules/documents/document-generation/dto/check-document-missing-field.dto.ts create mode 100644 backend/src/modules/documents/document-generation/dto/check-document-result.dto.ts create mode 100644 backend/src/modules/documents/document-generation/dto/check-document.dto.ts create mode 100644 backend/src/modules/documents/document-generation/dto/create-document.dto.ts create mode 100644 backend/src/modules/documents/document-generation/dto/index.ts create mode 100644 backend/src/modules/documents/document-generation/enums/document-type.enum.ts create mode 100644 backend/src/modules/documents/document-generation/enums/index.ts create mode 100644 backend/src/modules/documents/document-generation/enums/russian-case.enum.ts create mode 100644 backend/src/modules/documents/document-generation/index.ts create mode 100644 backend/src/modules/documents/document-generation/types/index.ts create mode 100644 backend/src/modules/documents/document-generation/types/russian-name.ts create mode 100644 backend/src/modules/documents/document-template/document-template.controller.ts create mode 100644 backend/src/modules/documents/document-template/document-template.service.ts create mode 100644 backend/src/modules/documents/document-template/dto/create-document-template.dto.ts create mode 100644 backend/src/modules/documents/document-template/dto/document-template-info.dto.ts create mode 100644 backend/src/modules/documents/document-template/dto/document-template.dto.ts create mode 100644 backend/src/modules/documents/document-template/dto/index.ts create mode 100644 backend/src/modules/documents/document-template/dto/update-document-template.dto.ts create mode 100644 backend/src/modules/documents/document-template/entities/document-template-access.entity.ts create mode 100644 backend/src/modules/documents/document-template/entities/document-template-entity-type.entity.ts create mode 100644 backend/src/modules/documents/document-template/entities/document-template.entity.ts create mode 100644 backend/src/modules/documents/document-template/entities/index.ts create mode 100644 backend/src/modules/documents/document-template/index.ts create mode 100644 backend/src/modules/documents/documents.module.ts create mode 100644 backend/src/modules/documents/index.ts create mode 100644 backend/src/modules/entity/entity-event/dto/create-entity-event.dto.ts create mode 100644 backend/src/modules/entity/entity-event/dto/delete-entity-event.dto.ts create mode 100644 backend/src/modules/entity/entity-event/dto/entity-event-data.dto.ts create mode 100644 backend/src/modules/entity/entity-event/dto/entity-event-item.dto.ts create mode 100644 backend/src/modules/entity/entity-event/dto/entity-event.dto.ts create mode 100644 backend/src/modules/entity/entity-event/dto/get-entity-event.result.ts create mode 100644 backend/src/modules/entity/entity-event/dto/index.ts create mode 100644 backend/src/modules/entity/entity-event/dto/update-entity-event.dto.ts create mode 100644 backend/src/modules/entity/entity-event/entities/entity-event.entity.ts create mode 100644 backend/src/modules/entity/entity-event/entities/index.ts create mode 100644 backend/src/modules/entity/entity-event/entity-event.controller.ts create mode 100644 backend/src/modules/entity/entity-event/entity-event.handler.ts create mode 100644 backend/src/modules/entity/entity-event/entity-event.module.ts create mode 100644 backend/src/modules/entity/entity-event/entity-event.service.ts create mode 100644 backend/src/modules/entity/entity-event/enums/entity-event-filter.enum.ts create mode 100644 backend/src/modules/entity/entity-event/enums/entity-event-type.enum.ts create mode 100644 backend/src/modules/entity/entity-event/enums/index.ts create mode 100644 backend/src/modules/entity/entity-field/common/enums/field-type.enum.ts create mode 100644 backend/src/modules/entity/entity-field/common/enums/index.ts create mode 100644 backend/src/modules/entity/entity-field/common/events/field-event-type.enum.ts create mode 100644 backend/src/modules/entity/entity-field/common/events/field/field.event.ts create mode 100644 backend/src/modules/entity/entity-field/common/events/field/index.ts create mode 100644 backend/src/modules/entity/entity-field/common/events/index.ts create mode 100644 backend/src/modules/entity/entity-field/common/index.ts create mode 100644 backend/src/modules/entity/entity-field/common/utils/formula.util.ts create mode 100644 backend/src/modules/entity/entity-field/common/utils/index.ts create mode 100644 backend/src/modules/entity/entity-field/entity-field.module.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/dto/create-field-group.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/dto/field-group.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/dto/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/dto/update-field-group.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/entities/field-group.entity.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/entities/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/enums/field-group-code.enum.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/enums/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/field-group.service.ts create mode 100644 backend/src/modules/entity/entity-field/field-group/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/dto/create-field-option.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/dto/field-option.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/dto/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/dto/update-field-option.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/entities/field-option.entity.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/entities/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/field-option.service.ts create mode 100644 backend/src/modules/entity/entity-field/field-option/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/dto/field-settings.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/dto/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/dto/update-field-settings.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/entities/field-stage-settings.entity.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/entities/field-user-settings.entity.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/entities/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/enums/field-access.enum.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/enums/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/field-settings.controller.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/field-settings.service.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/types/field-settings.ts create mode 100644 backend/src/modules/entity/entity-field/field-settings/types/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/dto/create-field-value.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/dto/field-value.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/dto/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/dto/simple-field-value.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/dto/update-field-value.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/entities/field-value.entity.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/entities/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/field-value.handler.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/field-value.service.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/index.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/types/field-payload.ts create mode 100644 backend/src/modules/entity/entity-field/field-value/types/index.ts create mode 100644 backend/src/modules/entity/entity-field/field/dto/check-formula.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field/dto/create-field.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field/dto/field.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field/dto/fields-settings.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field/dto/index.ts create mode 100644 backend/src/modules/entity/entity-field/field/dto/update-field.dto.ts create mode 100644 backend/src/modules/entity/entity-field/field/entities/field.entity.ts create mode 100644 backend/src/modules/entity/entity-field/field/entities/index.ts create mode 100644 backend/src/modules/entity/entity-field/field/enums/field-code.enum.ts create mode 100644 backend/src/modules/entity/entity-field/field/enums/field-format.enum.ts create mode 100644 backend/src/modules/entity/entity-field/field/enums/index.ts create mode 100644 backend/src/modules/entity/entity-field/field/errors/duplicate-field-name.error.ts create mode 100644 backend/src/modules/entity/entity-field/field/errors/field-used-in-formula.error.ts create mode 100644 backend/src/modules/entity/entity-field/field/errors/formula-circular-dependency.error.ts create mode 100644 backend/src/modules/entity/entity-field/field/errors/index.ts create mode 100644 backend/src/modules/entity/entity-field/field/field.controller.ts create mode 100644 backend/src/modules/entity/entity-field/field/field.service.ts create mode 100644 backend/src/modules/entity/entity-field/field/index.ts create mode 100644 backend/src/modules/entity/entity-field/field/types/expandable-field.ts create mode 100644 backend/src/modules/entity/entity-field/field/types/index.ts create mode 100644 backend/src/modules/entity/entity-info/dto/entity-info.dto.ts create mode 100644 backend/src/modules/entity/entity-info/dto/index.ts create mode 100644 backend/src/modules/entity/entity-info/entity-info.controller.ts create mode 100644 backend/src/modules/entity/entity-info/entity-info.module.ts create mode 100644 backend/src/modules/entity/entity-info/entity-info.service.ts create mode 100644 backend/src/modules/entity/entity-info/index.ts create mode 100644 backend/src/modules/entity/entity-stage-history/dto/create-entity-stage-history.dto.ts create mode 100644 backend/src/modules/entity/entity-stage-history/dto/entity-stage-history.dto.ts create mode 100644 backend/src/modules/entity/entity-stage-history/entities/entity-stage-history.entity.ts create mode 100644 backend/src/modules/entity/entity-stage-history/entity-stage-history.handler.ts create mode 100644 backend/src/modules/entity/entity-stage-history/entity-stage-history.module.ts create mode 100644 backend/src/modules/entity/entity-stage-history/entity-stage-history.service.ts create mode 100644 backend/src/modules/entity/entity.module.ts create mode 100644 backend/src/modules/forms/forms.module.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/index.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-consent.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-field-entity-field.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-date.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-spot.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-time.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-field.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-gratitude.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-option.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form-page.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/public-site-form.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-analytic-data.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-data-plain.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-data.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-field-data.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-request.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-result.ts create mode 100644 backend/src/modules/forms/site-form-builder/dto/site-form-result.dto.ts create mode 100644 backend/src/modules/forms/site-form-builder/index.ts create mode 100644 backend/src/modules/forms/site-form-builder/site-form-builder.controller.ts create mode 100644 backend/src/modules/forms/site-form-builder/site-form-builder.service.ts create mode 100644 backend/src/modules/forms/site-form-consent/dto/create-site-form-consent.dto.ts create mode 100644 backend/src/modules/forms/site-form-consent/dto/index.ts create mode 100644 backend/src/modules/forms/site-form-consent/dto/site-form-consent.dto.ts create mode 100644 backend/src/modules/forms/site-form-consent/dto/update-site-form-consent.dto.ts create mode 100644 backend/src/modules/forms/site-form-consent/entities/index.ts create mode 100644 backend/src/modules/forms/site-form-consent/entities/site-form-consent.entity.ts create mode 100644 backend/src/modules/forms/site-form-consent/index.ts create mode 100644 backend/src/modules/forms/site-form-consent/site-form-consent.controller.ts create mode 100644 backend/src/modules/forms/site-form-consent/site-form-consent.service.ts create mode 100644 backend/src/modules/forms/site-form-field/dto/create-site-form-field.dto.ts create mode 100644 backend/src/modules/forms/site-form-field/dto/index.ts create mode 100644 backend/src/modules/forms/site-form-field/dto/site-form-field-entity-field.dto.ts create mode 100644 backend/src/modules/forms/site-form-field/dto/site-form-field-entity-name.dto.ts create mode 100644 backend/src/modules/forms/site-form-field/dto/site-form-field.dto.ts create mode 100644 backend/src/modules/forms/site-form-field/dto/update-site-form-field.dto.ts create mode 100644 backend/src/modules/forms/site-form-field/entities/index.ts create mode 100644 backend/src/modules/forms/site-form-field/entities/site-form-field.entity.ts create mode 100644 backend/src/modules/forms/site-form-field/enums/index.ts create mode 100644 backend/src/modules/forms/site-form-field/enums/site-form-field-type.enum.ts create mode 100644 backend/src/modules/forms/site-form-field/index.ts create mode 100644 backend/src/modules/forms/site-form-field/site-form-field.controller.ts create mode 100644 backend/src/modules/forms/site-form-field/site-form-field.service.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/dto/create-site-form-gratitude.dto.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/dto/index.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/dto/site-form-gratitude.dto.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/dto/update-site-form-gratitude.dto.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/entities/index.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/entities/site-form-gratitude.entity.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/index.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/site-form-gratitude.controller.ts create mode 100644 backend/src/modules/forms/site-form-gratitude/site-form-gratitude.service.ts create mode 100644 backend/src/modules/forms/site-form-page/dto/create-site-form-page.dto.ts create mode 100644 backend/src/modules/forms/site-form-page/dto/index.ts create mode 100644 backend/src/modules/forms/site-form-page/dto/site-form-page.dto.ts create mode 100644 backend/src/modules/forms/site-form-page/dto/update-site-form-page.dto.ts create mode 100644 backend/src/modules/forms/site-form-page/entities/index.ts create mode 100644 backend/src/modules/forms/site-form-page/entities/site-form-page.entity.ts create mode 100644 backend/src/modules/forms/site-form-page/index.ts create mode 100644 backend/src/modules/forms/site-form-page/site-form-page.controller.ts create mode 100644 backend/src/modules/forms/site-form-page/site-form-page.service.ts create mode 100644 backend/src/modules/forms/site-form-page/types/expandable-field.ts create mode 100644 backend/src/modules/forms/site-form-page/types/index.ts create mode 100644 backend/src/modules/forms/site-form/dto/create-site-form.dto.ts create mode 100644 backend/src/modules/forms/site-form/dto/index.ts create mode 100644 backend/src/modules/forms/site-form/dto/site-form-entity-type.dto.ts create mode 100644 backend/src/modules/forms/site-form/dto/site-form-schedule.dto.ts create mode 100644 backend/src/modules/forms/site-form/dto/site-form.dto.ts create mode 100644 backend/src/modules/forms/site-form/dto/update-site-form.dto.ts create mode 100644 backend/src/modules/forms/site-form/entities/index.ts create mode 100644 backend/src/modules/forms/site-form/entities/site-form-entity-type.entity.ts create mode 100644 backend/src/modules/forms/site-form/entities/site-form-schedule.entity.ts create mode 100644 backend/src/modules/forms/site-form/entities/site-form.entity.ts create mode 100644 backend/src/modules/forms/site-form/enums/index.ts create mode 100644 backend/src/modules/forms/site-form/enums/site-form-type.enum.ts create mode 100644 backend/src/modules/forms/site-form/index.ts create mode 100644 backend/src/modules/forms/site-form/services/index.ts create mode 100644 backend/src/modules/forms/site-form/services/site-form-entity-type.service.ts create mode 100644 backend/src/modules/forms/site-form/services/site-form-schedule.service.ts create mode 100644 backend/src/modules/forms/site-form/services/site-form.service.ts create mode 100644 backend/src/modules/forms/site-form/site-form.controller.ts create mode 100644 backend/src/modules/forms/site-form/types/expandable-field.ts create mode 100644 backend/src/modules/forms/site-form/types/index.ts create mode 100644 backend/src/modules/frontend-event/frontend-event.gateway.ts create mode 100644 backend/src/modules/frontend-event/frontend-event.module.ts create mode 100644 backend/src/modules/frontend-event/frontend-event.service.ts create mode 100644 backend/src/modules/frontend-object/dto/create-frontend-object.dto.ts create mode 100644 backend/src/modules/frontend-object/dto/frontend-object-filter.dto.ts create mode 100644 backend/src/modules/frontend-object/dto/frontend-object.dto.ts create mode 100644 backend/src/modules/frontend-object/dto/index.ts create mode 100644 backend/src/modules/frontend-object/entities/frontend-object.entity.ts create mode 100644 backend/src/modules/frontend-object/entities/index.ts create mode 100644 backend/src/modules/frontend-object/frontend-object.controller.ts create mode 100644 backend/src/modules/frontend-object/frontend-object.module.ts create mode 100644 backend/src/modules/frontend-object/frontend-object.service.ts create mode 100644 backend/src/modules/iam/account-api-access/account-api-access.controller.ts create mode 100644 backend/src/modules/iam/account-api-access/account-api-access.service.ts create mode 100644 backend/src/modules/iam/account-api-access/dto/account-api-access.dto.ts create mode 100644 backend/src/modules/iam/account-api-access/dto/index.ts create mode 100644 backend/src/modules/iam/account-api-access/entities/account-api-access.entity.ts create mode 100644 backend/src/modules/iam/account-api-access/entities/index.ts create mode 100644 backend/src/modules/iam/account-api-access/guards/api-access.guard.ts create mode 100644 backend/src/modules/iam/account-api-access/guards/index.ts create mode 100644 backend/src/modules/iam/account-api-access/index.ts create mode 100644 backend/src/modules/iam/account-settings/account-settings.controller.ts create mode 100644 backend/src/modules/iam/account-settings/account-settings.service.ts create mode 100644 backend/src/modules/iam/account-settings/dto/account-settings.dto.ts create mode 100644 backend/src/modules/iam/account-settings/dto/create-account-settings.dto.ts create mode 100644 backend/src/modules/iam/account-settings/dto/index.ts create mode 100644 backend/src/modules/iam/account-settings/dto/update-account-settings.dto.ts create mode 100644 backend/src/modules/iam/account-settings/entities/account-settings.entity.ts create mode 100644 backend/src/modules/iam/account-settings/entities/index.ts create mode 100644 backend/src/modules/iam/account-settings/index.ts create mode 100644 backend/src/modules/iam/account-subscription/account-subscription.controller.ts create mode 100644 backend/src/modules/iam/account-subscription/account-subscription.service.ts create mode 100644 backend/src/modules/iam/account-subscription/dto/account-subscription.dto.ts create mode 100644 backend/src/modules/iam/account-subscription/dto/create-account-subscription.dto.ts create mode 100644 backend/src/modules/iam/account-subscription/dto/index.ts create mode 100644 backend/src/modules/iam/account-subscription/dto/update-account-subscription.dto.ts create mode 100644 backend/src/modules/iam/account-subscription/entities/account-subscription.entity.ts create mode 100644 backend/src/modules/iam/account-subscription/entities/index.ts create mode 100644 backend/src/modules/iam/account-subscription/index.ts create mode 100644 backend/src/modules/iam/account/account.controller.ts create mode 100644 backend/src/modules/iam/account/account.service.ts create mode 100644 backend/src/modules/iam/account/dto/account.dto.ts create mode 100644 backend/src/modules/iam/account/dto/create-account.dto.ts create mode 100644 backend/src/modules/iam/account/dto/find-filter.dto.ts create mode 100644 backend/src/modules/iam/account/dto/index.ts create mode 100644 backend/src/modules/iam/account/entities/account.entity.ts create mode 100644 backend/src/modules/iam/account/entities/index.ts create mode 100644 backend/src/modules/iam/account/types/expandable-field.ts create mode 100644 backend/src/modules/iam/account/types/index.ts create mode 100644 backend/src/modules/iam/authentication/authentication.controller.ts create mode 100644 backend/src/modules/iam/authentication/authentication.service.ts create mode 100644 backend/src/modules/iam/authentication/dto/decode-logic-link.dto.ts create mode 100644 backend/src/modules/iam/authentication/dto/index.ts create mode 100644 backend/src/modules/iam/authentication/dto/jwt-token.ts create mode 100644 backend/src/modules/iam/authentication/dto/login-link.dto.ts create mode 100644 backend/src/modules/iam/authentication/dto/recovery-user-password.dto.ts create mode 100644 backend/src/modules/iam/authentication/dto/reset-user-password.dto.ts create mode 100644 backend/src/modules/iam/authentication/dto/user-login.dto.ts create mode 100644 backend/src/modules/iam/authentication/errors/index.ts create mode 100644 backend/src/modules/iam/authentication/errors/invalid-login-link.error.ts create mode 100644 backend/src/modules/iam/authentication/types/index.ts create mode 100644 backend/src/modules/iam/authentication/types/recovery-token-payload.ts create mode 100644 backend/src/modules/iam/authorization/authorization.service.ts create mode 100644 backend/src/modules/iam/authorization/index.ts create mode 100644 backend/src/modules/iam/common/decorators/api-access-required.decorator.ts create mode 100644 backend/src/modules/iam/common/decorators/auth-data-prefetch.decorator.ts create mode 100644 backend/src/modules/iam/common/decorators/current-auth.decorator.ts create mode 100644 backend/src/modules/iam/common/decorators/ga-client-id.decorator.ts create mode 100644 backend/src/modules/iam/common/decorators/index.ts create mode 100644 backend/src/modules/iam/common/decorators/jwt-authorized.decorator.ts create mode 100644 backend/src/modules/iam/common/decorators/user-access.decorator.ts create mode 100644 backend/src/modules/iam/common/enums/index.ts create mode 100644 backend/src/modules/iam/common/enums/permission-action.enum.ts create mode 100644 backend/src/modules/iam/common/enums/permission-level.enum.ts create mode 100644 backend/src/modules/iam/common/enums/phone-format.enum.ts create mode 100644 backend/src/modules/iam/common/enums/user-role.enum.ts create mode 100644 backend/src/modules/iam/common/errors/index.ts create mode 100644 backend/src/modules/iam/common/errors/invalid-subdomain.error.ts create mode 100644 backend/src/modules/iam/common/errors/token-expired.error.ts create mode 100644 backend/src/modules/iam/common/errors/token-not-found.error.ts create mode 100644 backend/src/modules/iam/common/errors/token-not-passed.error.ts create mode 100644 backend/src/modules/iam/common/events/account/account-created.event.ts create mode 100644 backend/src/modules/iam/common/events/account/index.ts create mode 100644 backend/src/modules/iam/common/events/department/department-deleted.event.ts create mode 100644 backend/src/modules/iam/common/events/department/index.ts create mode 100644 backend/src/modules/iam/common/events/iam-event-type.enum.ts create mode 100644 backend/src/modules/iam/common/events/index.ts create mode 100644 backend/src/modules/iam/common/events/user/index.ts create mode 100644 backend/src/modules/iam/common/events/user/user-created.event.ts create mode 100644 backend/src/modules/iam/common/events/user/user-deleted.event.ts create mode 100644 backend/src/modules/iam/common/events/user/user-login.event.ts create mode 100644 backend/src/modules/iam/common/events/user/user-password-recovery.event.ts create mode 100644 backend/src/modules/iam/common/guards/index.ts create mode 100644 backend/src/modules/iam/common/guards/jwt-token.guard.ts create mode 100644 backend/src/modules/iam/common/guards/user-access.guard.ts create mode 100644 backend/src/modules/iam/common/index.ts create mode 100644 backend/src/modules/iam/common/interceptors/auth-data.interceptor.ts create mode 100644 backend/src/modules/iam/common/interceptors/index.ts create mode 100644 backend/src/modules/iam/common/interfaces/authorizable.interface.ts create mode 100644 backend/src/modules/iam/common/interfaces/index.ts create mode 100644 backend/src/modules/iam/common/types/auth-data.ts create mode 100644 backend/src/modules/iam/common/types/authorizable-object.ts create mode 100644 backend/src/modules/iam/common/types/data-prefetch.ts create mode 100644 backend/src/modules/iam/common/types/index.ts create mode 100644 backend/src/modules/iam/common/types/simple-authorizable.ts create mode 100644 backend/src/modules/iam/common/types/token-payload.ts create mode 100644 backend/src/modules/iam/common/types/user-access-options.ts create mode 100644 backend/src/modules/iam/common/types/user-rights.ts create mode 100644 backend/src/modules/iam/department-settings/department-settings.controller.ts create mode 100644 backend/src/modules/iam/department-settings/department-settings.service.ts create mode 100644 backend/src/modules/iam/department-settings/dto/create-department-settings.dto.ts create mode 100644 backend/src/modules/iam/department-settings/dto/department-settings.dto.ts create mode 100644 backend/src/modules/iam/department-settings/dto/index.ts create mode 100644 backend/src/modules/iam/department-settings/dto/update-department-settings.dto.ts create mode 100644 backend/src/modules/iam/department-settings/entities/department-settings.entity.ts create mode 100644 backend/src/modules/iam/department-settings/entities/index.ts create mode 100644 backend/src/modules/iam/department-settings/index.ts create mode 100644 backend/src/modules/iam/department/department.controller.ts create mode 100644 backend/src/modules/iam/department/department.service.ts create mode 100644 backend/src/modules/iam/department/dto/create-department.dto.ts create mode 100644 backend/src/modules/iam/department/dto/delete-department.dto.ts create mode 100644 backend/src/modules/iam/department/dto/department.dto.ts create mode 100644 backend/src/modules/iam/department/dto/index.ts create mode 100644 backend/src/modules/iam/department/dto/update-department.dto.ts create mode 100644 backend/src/modules/iam/department/entities/department.entity.ts create mode 100644 backend/src/modules/iam/department/entities/index.ts create mode 100644 backend/src/modules/iam/department/index.ts create mode 100644 backend/src/modules/iam/department/types/expandable-field.ts create mode 100644 backend/src/modules/iam/department/types/index.ts create mode 100644 backend/src/modules/iam/iam.module.ts create mode 100644 backend/src/modules/iam/object-permission/dto/index.ts create mode 100644 backend/src/modules/iam/object-permission/dto/object-permission.dto.ts create mode 100644 backend/src/modules/iam/object-permission/entities/index.ts create mode 100644 backend/src/modules/iam/object-permission/entities/object-permission.entity.ts create mode 100644 backend/src/modules/iam/object-permission/errors/index.ts create mode 100644 backend/src/modules/iam/object-permission/errors/permission-not-valid.error.ts create mode 100644 backend/src/modules/iam/object-permission/index.ts create mode 100644 backend/src/modules/iam/object-permission/object-permission.service.ts create mode 100644 backend/src/modules/iam/subscription-discount/dto/current-discount.dto.ts create mode 100644 backend/src/modules/iam/subscription-discount/dto/index.ts create mode 100644 backend/src/modules/iam/subscription-discount/entities/index.ts create mode 100644 backend/src/modules/iam/subscription-discount/entities/subscription-discount.entity.ts create mode 100644 backend/src/modules/iam/subscription-discount/index.ts create mode 100644 backend/src/modules/iam/subscription-discount/public-subscription-discount.controller.ts create mode 100644 backend/src/modules/iam/subscription-discount/subscription-discount.controller.ts create mode 100644 backend/src/modules/iam/subscription-discount/subscription-discount.service.ts create mode 100644 backend/src/modules/iam/subscription-discount/types/current-discount.ts create mode 100644 backend/src/modules/iam/subscription-discount/types/index.ts create mode 100644 backend/src/modules/iam/user-calendar/dto/index.ts create mode 100644 backend/src/modules/iam/user-calendar/dto/user-calendar-interval.dto.ts create mode 100644 backend/src/modules/iam/user-calendar/dto/user-calendar.dto.ts create mode 100644 backend/src/modules/iam/user-calendar/entities/index.ts create mode 100644 backend/src/modules/iam/user-calendar/entities/user-calendar-interval.entity.ts create mode 100644 backend/src/modules/iam/user-calendar/entities/user-calendar.entity.ts create mode 100644 backend/src/modules/iam/user-calendar/index.ts create mode 100644 backend/src/modules/iam/user-calendar/services/index.ts create mode 100644 backend/src/modules/iam/user-calendar/services/user-calendar-interval.service.ts create mode 100644 backend/src/modules/iam/user-calendar/services/user-calendar.service.ts create mode 100644 backend/src/modules/iam/user-calendar/user-calendar.controller.ts create mode 100644 backend/src/modules/iam/user-profile/dto/index.ts create mode 100644 backend/src/modules/iam/user-profile/dto/update-user-profile.dto.ts create mode 100644 backend/src/modules/iam/user-profile/dto/user-profile.dto.ts create mode 100644 backend/src/modules/iam/user-profile/entities/index.ts create mode 100644 backend/src/modules/iam/user-profile/entities/user-profile.entity.ts create mode 100644 backend/src/modules/iam/user-profile/user-profile.controller.ts create mode 100644 backend/src/modules/iam/user-profile/user-profile.service.ts create mode 100644 backend/src/modules/iam/user-token/dto/create-user-token.dto.ts create mode 100644 backend/src/modules/iam/user-token/dto/index.ts create mode 100644 backend/src/modules/iam/user-token/dto/user-access-token.dto.ts create mode 100644 backend/src/modules/iam/user-token/dto/user-token.dto.ts create mode 100644 backend/src/modules/iam/user-token/entities/index.ts create mode 100644 backend/src/modules/iam/user-token/entities/user-token.entity.ts create mode 100644 backend/src/modules/iam/user-token/index.ts create mode 100644 backend/src/modules/iam/user-token/types/index.ts create mode 100644 backend/src/modules/iam/user-token/types/user-access-token.ts create mode 100644 backend/src/modules/iam/user-token/user-token.controller.ts create mode 100644 backend/src/modules/iam/user-token/user-token.service.ts create mode 100644 backend/src/modules/iam/user/dto/change-user-password.dto.ts create mode 100644 backend/src/modules/iam/user/dto/create-user.dto.ts create mode 100644 backend/src/modules/iam/user/dto/index.ts create mode 100644 backend/src/modules/iam/user/dto/update-user.dto.ts create mode 100644 backend/src/modules/iam/user/dto/user-find-filter.dto.ts create mode 100644 backend/src/modules/iam/user/dto/user.dto.ts create mode 100644 backend/src/modules/iam/user/entities/index.ts create mode 100644 backend/src/modules/iam/user/entities/user.entity.ts create mode 100644 backend/src/modules/iam/user/entities/users-accessible-users.entity.ts create mode 100644 backend/src/modules/iam/user/errors/bad-credentials.error.ts create mode 100644 backend/src/modules/iam/user/errors/email-occupied.error.ts create mode 100644 backend/src/modules/iam/user/errors/index.ts create mode 100644 backend/src/modules/iam/user/errors/user-not-active.error.ts create mode 100644 backend/src/modules/iam/user/types/expandable-field.ts create mode 100644 backend/src/modules/iam/user/types/index.ts create mode 100644 backend/src/modules/iam/user/user.controller.ts create mode 100644 backend/src/modules/iam/user/user.handler.ts create mode 100644 backend/src/modules/iam/user/user.service.ts create mode 100644 backend/src/modules/iam/working-time/index.ts create mode 100644 backend/src/modules/iam/working-time/working-time.service.ts create mode 100644 backend/src/modules/integration/appsumo/appsumo.controller.ts create mode 100644 backend/src/modules/integration/appsumo/appsumo.module.ts create mode 100644 backend/src/modules/integration/appsumo/appsumo.service.ts create mode 100644 backend/src/modules/integration/appsumo/config/appsumo.config.ts create mode 100644 backend/src/modules/integration/appsumo/config/index.ts create mode 100644 backend/src/modules/integration/appsumo/entities/appsumo-license.entity.ts create mode 100644 backend/src/modules/integration/appsumo/entities/appsumo-tier.entity.ts create mode 100644 backend/src/modules/integration/appsumo/entities/index.ts create mode 100644 backend/src/modules/integration/appsumo/enums/appsumo-event-type.enum.ts create mode 100644 backend/src/modules/integration/appsumo/enums/appsumo-license-status.enum.ts create mode 100644 backend/src/modules/integration/appsumo/enums/index.ts create mode 100644 backend/src/modules/integration/appsumo/index.ts create mode 100644 backend/src/modules/integration/appsumo/types/appsumo-license-response.ts create mode 100644 backend/src/modules/integration/appsumo/types/appsumo-token-response.ts create mode 100644 backend/src/modules/integration/appsumo/types/appsumo-webhook-request.ts create mode 100644 backend/src/modules/integration/appsumo/types/appsumo-webhook-response.ts create mode 100644 backend/src/modules/integration/appsumo/types/index.ts create mode 100644 backend/src/modules/integration/google/auth/auth.module.ts create mode 100644 backend/src/modules/integration/google/auth/auth.service.ts create mode 100644 backend/src/modules/integration/google/calendar/calendar.controller.ts create mode 100644 backend/src/modules/integration/google/calendar/calendar.emitter.ts create mode 100644 backend/src/modules/integration/google/calendar/calendar.handler.ts create mode 100644 backend/src/modules/integration/google/calendar/calendar.module.ts create mode 100644 backend/src/modules/integration/google/calendar/calendar.service.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/calendar-access.dto.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/calendar-info.dto.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/create-google-calendar.dto.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/google-calendar-linked.dto.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/google-calendar.dto.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/index.ts create mode 100644 backend/src/modules/integration/google/calendar/dto/update-google-calendar.dto.ts create mode 100644 backend/src/modules/integration/google/calendar/entities/google-calendar-account.entity.ts create mode 100644 backend/src/modules/integration/google/calendar/entities/google-calendar-linked.entity.ts create mode 100644 backend/src/modules/integration/google/calendar/entities/google-calendar.entity.ts create mode 100644 backend/src/modules/integration/google/calendar/entities/index.ts create mode 100644 backend/src/modules/integration/google/calendar/enums/calendar-type.enum.ts create mode 100644 backend/src/modules/integration/google/calendar/enums/index.ts create mode 100644 backend/src/modules/integration/google/calendar/public-calendar.controller.ts create mode 100644 backend/src/modules/integration/google/calendar/types/calendar-access.ts create mode 100644 backend/src/modules/integration/google/calendar/types/calendar-event.ts create mode 100644 backend/src/modules/integration/google/calendar/types/calendar-info.ts create mode 100644 backend/src/modules/integration/google/calendar/types/calendar-upsert-event.ts create mode 100644 backend/src/modules/integration/google/calendar/types/event-extended-properties.ts create mode 100644 backend/src/modules/integration/google/calendar/types/index.ts create mode 100644 backend/src/modules/integration/google/calendar/utils/appointment.util.ts create mode 100644 backend/src/modules/integration/google/calendar/utils/event.util.ts create mode 100644 backend/src/modules/integration/google/calendar/utils/index.ts create mode 100644 backend/src/modules/integration/google/google.config.ts create mode 100644 backend/src/modules/integration/google/google.module.ts create mode 100644 backend/src/modules/integration/integration.module.ts create mode 100644 backend/src/modules/integration/stripe/dto/index.ts create mode 100644 backend/src/modules/integration/stripe/dto/subscription-feature.dto.ts create mode 100644 backend/src/modules/integration/stripe/dto/subscription-order.dto.ts create mode 100644 backend/src/modules/integration/stripe/dto/subscription-plan.dto.ts create mode 100644 backend/src/modules/integration/stripe/dto/subscription-price.dto.ts create mode 100644 backend/src/modules/integration/stripe/public-stripe.controller.ts create mode 100644 backend/src/modules/integration/stripe/stripe.controller.ts create mode 100644 backend/src/modules/integration/stripe/stripe.module.ts create mode 100644 backend/src/modules/integration/stripe/stripe.service.ts create mode 100644 backend/src/modules/inventory/common/enums/index.ts create mode 100644 backend/src/modules/inventory/common/enums/permission-object-type.enum.ts create mode 100644 backend/src/modules/inventory/common/events/index.ts create mode 100644 backend/src/modules/inventory/common/events/product-order/index.ts create mode 100644 backend/src/modules/inventory/common/events/product-order/product-order-created.event.ts create mode 100644 backend/src/modules/inventory/common/events/product-order/product-order.event.ts create mode 100644 backend/src/modules/inventory/common/events/products-event-type.enum.ts create mode 100644 backend/src/modules/inventory/common/events/products-section/index.ts create mode 100644 backend/src/modules/inventory/common/events/products-section/products-section.event.ts create mode 100644 backend/src/modules/inventory/common/events/rental-order/index.ts create mode 100644 backend/src/modules/inventory/common/events/rental-order/rental-order-created.event.ts create mode 100644 backend/src/modules/inventory/common/events/rental-order/rental-order.event.ts create mode 100644 backend/src/modules/inventory/common/events/shipment/index.ts create mode 100644 backend/src/modules/inventory/common/events/shipment/shipment-created.event.ts create mode 100644 backend/src/modules/inventory/common/events/shipment/shipment-deleted.event.ts create mode 100644 backend/src/modules/inventory/common/events/shipment/shipment-status-changed.event.ts create mode 100644 backend/src/modules/inventory/common/events/shipment/shipment.event.ts create mode 100644 backend/src/modules/inventory/common/index.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/dto/index.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/dto/inventory-report-filter.dto.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/dto/inventory-report-row.dto.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/dto/inventory-report-user-cell.dto.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/dto/inventory-report.dto.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/enums/index.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/enums/products-report-type.enum.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/inventory-reporting.controller.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/inventory-reporting.module.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/inventory-reporting.service.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/types/index.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/types/inventory-report-row.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/types/inventory-report-user-cell.ts create mode 100644 backend/src/modules/inventory/inventory-reporting/types/inventory-report.ts create mode 100644 backend/src/modules/inventory/inventory.module.ts create mode 100644 backend/src/modules/inventory/order-status/dto/create-order-status.dto.ts create mode 100644 backend/src/modules/inventory/order-status/dto/index.ts create mode 100644 backend/src/modules/inventory/order-status/dto/order-status.dto.ts create mode 100644 backend/src/modules/inventory/order-status/entities/index.ts create mode 100644 backend/src/modules/inventory/order-status/entities/order-status.entity.ts create mode 100644 backend/src/modules/inventory/order-status/enums/index.ts create mode 100644 backend/src/modules/inventory/order-status/enums/order-status-code.enum.ts create mode 100644 backend/src/modules/inventory/order-status/order-status.controller.ts create mode 100644 backend/src/modules/inventory/order-status/order-status.module.ts create mode 100644 backend/src/modules/inventory/order-status/order-status.service.ts create mode 100644 backend/src/modules/inventory/order/dto/create-order.dto.ts create mode 100644 backend/src/modules/inventory/order/dto/index.ts create mode 100644 backend/src/modules/inventory/order/dto/order-filter.dto.ts create mode 100644 backend/src/modules/inventory/order/dto/order-item.dto.ts create mode 100644 backend/src/modules/inventory/order/dto/order.dto.ts create mode 100644 backend/src/modules/inventory/order/dto/update-order.dto.ts create mode 100644 backend/src/modules/inventory/order/entities/index.ts create mode 100644 backend/src/modules/inventory/order/entities/order-item.entity.ts create mode 100644 backend/src/modules/inventory/order/entities/order.entity.ts create mode 100644 backend/src/modules/inventory/order/helper/index.ts create mode 100644 backend/src/modules/inventory/order/helper/order.helper.ts create mode 100644 backend/src/modules/inventory/order/order.controller.ts create mode 100644 backend/src/modules/inventory/order/order.module.ts create mode 100644 backend/src/modules/inventory/order/services/order-item.service.ts create mode 100644 backend/src/modules/inventory/order/services/order.handler.ts create mode 100644 backend/src/modules/inventory/order/services/order.service.ts create mode 100644 backend/src/modules/inventory/order/types/expandable-field.ts create mode 100644 backend/src/modules/inventory/order/types/index.ts create mode 100644 backend/src/modules/inventory/product-category/dto/create-product-category.dto.ts create mode 100644 backend/src/modules/inventory/product-category/dto/index.ts create mode 100644 backend/src/modules/inventory/product-category/dto/product-category.dto.ts create mode 100644 backend/src/modules/inventory/product-category/dto/update-product-category.dto.ts create mode 100644 backend/src/modules/inventory/product-category/entities/index.ts create mode 100644 backend/src/modules/inventory/product-category/entities/product-category.entity.ts create mode 100644 backend/src/modules/inventory/product-category/product-category.controller.ts create mode 100644 backend/src/modules/inventory/product-category/product-category.module.ts create mode 100644 backend/src/modules/inventory/product-category/product-category.service.ts create mode 100644 backend/src/modules/inventory/product-price/dto/create-product-price.dto.ts create mode 100644 backend/src/modules/inventory/product-price/dto/index.ts create mode 100644 backend/src/modules/inventory/product-price/dto/product-price.dto.ts create mode 100644 backend/src/modules/inventory/product-price/dto/update-product-price.dto.ts create mode 100644 backend/src/modules/inventory/product-price/entities/index.ts create mode 100644 backend/src/modules/inventory/product-price/entities/product-price.entity.ts create mode 100644 backend/src/modules/inventory/product-price/errors/index.ts create mode 100644 backend/src/modules/inventory/product-price/errors/invalid-discount.error.ts create mode 100644 backend/src/modules/inventory/product-price/product-price.controller.ts create mode 100644 backend/src/modules/inventory/product-price/product-price.module.ts create mode 100644 backend/src/modules/inventory/product-price/product-price.service.ts create mode 100644 backend/src/modules/inventory/product-stock/dto/create-product-stock.dto.ts create mode 100644 backend/src/modules/inventory/product-stock/dto/index.ts create mode 100644 backend/src/modules/inventory/product-stock/dto/product-stock.dto.ts create mode 100644 backend/src/modules/inventory/product-stock/dto/update-product-stock.dto.ts create mode 100644 backend/src/modules/inventory/product-stock/dto/update-product-stocks.dto.ts create mode 100644 backend/src/modules/inventory/product-stock/entities/index.ts create mode 100644 backend/src/modules/inventory/product-stock/entities/product-stock.entity.ts create mode 100644 backend/src/modules/inventory/product-stock/product-stock.controller.ts create mode 100644 backend/src/modules/inventory/product-stock/product-stock.module.ts create mode 100644 backend/src/modules/inventory/product-stock/product-stock.service.ts create mode 100644 backend/src/modules/inventory/product/dto/create-product.dto.ts create mode 100644 backend/src/modules/inventory/product/dto/file-upload-request.ts create mode 100644 backend/src/modules/inventory/product/dto/get-products.meta.ts create mode 100644 backend/src/modules/inventory/product/dto/get-products.result.ts create mode 100644 backend/src/modules/inventory/product/dto/index.ts create mode 100644 backend/src/modules/inventory/product/dto/product-info.dto.ts create mode 100644 backend/src/modules/inventory/product/dto/product.dto.ts create mode 100644 backend/src/modules/inventory/product/dto/products-filter.ts create mode 100644 backend/src/modules/inventory/product/dto/update-product.dto.ts create mode 100644 backend/src/modules/inventory/product/entities/index.ts create mode 100644 backend/src/modules/inventory/product/entities/product.entity.ts create mode 100644 backend/src/modules/inventory/product/enums/index.ts create mode 100644 backend/src/modules/inventory/product/enums/product-type.enum.ts create mode 100644 backend/src/modules/inventory/product/product.controller.ts create mode 100644 backend/src/modules/inventory/product/product.module.ts create mode 100644 backend/src/modules/inventory/product/product.service.ts create mode 100644 backend/src/modules/inventory/products-section/dto/create-products-section.dto.ts create mode 100644 backend/src/modules/inventory/products-section/dto/index.ts create mode 100644 backend/src/modules/inventory/products-section/dto/link-modules.dto.ts create mode 100644 backend/src/modules/inventory/products-section/dto/products-section-entity-type.dto.ts create mode 100644 backend/src/modules/inventory/products-section/dto/products-section.dto.ts create mode 100644 backend/src/modules/inventory/products-section/dto/update-products-section.dto.ts create mode 100644 backend/src/modules/inventory/products-section/entities/index.ts create mode 100644 backend/src/modules/inventory/products-section/entities/products-section-entity-type.entity.ts create mode 100644 backend/src/modules/inventory/products-section/entities/products-section.entity.ts create mode 100644 backend/src/modules/inventory/products-section/enums/index.ts create mode 100644 backend/src/modules/inventory/products-section/enums/products-section-type.enum.ts create mode 100644 backend/src/modules/inventory/products-section/products-section.controller.ts create mode 100644 backend/src/modules/inventory/products-section/products-section.module.ts create mode 100644 backend/src/modules/inventory/products-section/services/products-section-linker.service.ts create mode 100644 backend/src/modules/inventory/products-section/services/products-section.service.ts create mode 100644 backend/src/modules/inventory/rental-interval/dto/rental-interval.dto.ts create mode 100644 backend/src/modules/inventory/rental-interval/entities/rental-interval-type.enum.ts create mode 100644 backend/src/modules/inventory/rental-interval/entities/rental-interval.entity.ts create mode 100644 backend/src/modules/inventory/rental-interval/rental-interval.controller.ts create mode 100644 backend/src/modules/inventory/rental-interval/rental-interval.module.ts create mode 100644 backend/src/modules/inventory/rental-interval/rental-interval.service.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/create-rental-order-item.dto.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/create-rental-order.dto.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/index.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/rental-order-filter.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/rental-order-item.dto.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/rental-order.dto.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/update-rental-order-item.dto.ts create mode 100644 backend/src/modules/inventory/rental-order/dto/update-rental-order.dto.ts create mode 100644 backend/src/modules/inventory/rental-order/entities/index.ts create mode 100644 backend/src/modules/inventory/rental-order/entities/rental-order-item.entity.ts create mode 100644 backend/src/modules/inventory/rental-order/entities/rental-order-period.entity.ts create mode 100644 backend/src/modules/inventory/rental-order/entities/rental-order.entity.ts create mode 100644 backend/src/modules/inventory/rental-order/enums/index.ts create mode 100644 backend/src/modules/inventory/rental-order/enums/rental-order-status.enum.ts create mode 100644 backend/src/modules/inventory/rental-order/rental-order.controller.ts create mode 100644 backend/src/modules/inventory/rental-order/rental-order.module.ts create mode 100644 backend/src/modules/inventory/rental-order/services/index.ts create mode 100644 backend/src/modules/inventory/rental-order/services/rental-order-item.service.ts create mode 100644 backend/src/modules/inventory/rental-order/services/rental-order.service.ts create mode 100644 backend/src/modules/inventory/rental-schedule/dto/check-rental-status.dto.ts create mode 100644 backend/src/modules/inventory/rental-schedule/dto/index.ts create mode 100644 backend/src/modules/inventory/rental-schedule/dto/product-rental-status.dto.ts create mode 100644 backend/src/modules/inventory/rental-schedule/dto/rental-event.dto.ts create mode 100644 backend/src/modules/inventory/rental-schedule/dto/rental-schedule.dto.ts create mode 100644 backend/src/modules/inventory/rental-schedule/entities/index.ts create mode 100644 backend/src/modules/inventory/rental-schedule/entities/rental-event.entity.ts create mode 100644 backend/src/modules/inventory/rental-schedule/enums/index.ts create mode 100644 backend/src/modules/inventory/rental-schedule/enums/rental-schedule-status.enum.ts create mode 100644 backend/src/modules/inventory/rental-schedule/rental-schedule.controller.ts create mode 100644 backend/src/modules/inventory/rental-schedule/rental-schedule.module.ts create mode 100644 backend/src/modules/inventory/rental-schedule/rental-schedule.service.ts create mode 100644 backend/src/modules/inventory/rental-schedule/types/index.ts create mode 100644 backend/src/modules/inventory/rental-schedule/types/product-rental-status.ts create mode 100644 backend/src/modules/inventory/rental-schedule/types/rental-schedule.ts create mode 100644 backend/src/modules/inventory/reservation/dto/index.ts create mode 100644 backend/src/modules/inventory/reservation/dto/reservation.dto.ts create mode 100644 backend/src/modules/inventory/reservation/entities/index.ts create mode 100644 backend/src/modules/inventory/reservation/entities/reservation.entity.ts create mode 100644 backend/src/modules/inventory/reservation/reservation.module.ts create mode 100644 backend/src/modules/inventory/reservation/reservation.service.ts create mode 100644 backend/src/modules/inventory/shipment/dto/change-status-query.ts create mode 100644 backend/src/modules/inventory/shipment/dto/index.ts create mode 100644 backend/src/modules/inventory/shipment/dto/shipment-filter.dto.ts create mode 100644 backend/src/modules/inventory/shipment/dto/shipment-item.dto.ts create mode 100644 backend/src/modules/inventory/shipment/dto/shipment-result.dto.ts create mode 100644 backend/src/modules/inventory/shipment/dto/shipment.dto.ts create mode 100644 backend/src/modules/inventory/shipment/entities/index.ts create mode 100644 backend/src/modules/inventory/shipment/entities/shipment-item.entity.ts create mode 100644 backend/src/modules/inventory/shipment/entities/shipment.entity.ts create mode 100644 backend/src/modules/inventory/shipment/shipment.controller.ts create mode 100644 backend/src/modules/inventory/shipment/shipment.module.ts create mode 100644 backend/src/modules/inventory/shipment/shipment.service.ts create mode 100644 backend/src/modules/inventory/shipment/types/index.ts create mode 100644 backend/src/modules/inventory/shipment/types/shipment-result.ts create mode 100644 backend/src/modules/inventory/warehouse/dto/create-warehouse.dto.ts create mode 100644 backend/src/modules/inventory/warehouse/dto/index.ts create mode 100644 backend/src/modules/inventory/warehouse/dto/update-warehouse.dto.ts create mode 100644 backend/src/modules/inventory/warehouse/dto/warehouse.dto.ts create mode 100644 backend/src/modules/inventory/warehouse/entities/index.ts create mode 100644 backend/src/modules/inventory/warehouse/entities/warehouse.entity.ts create mode 100644 backend/src/modules/inventory/warehouse/warehouse.controller.ts create mode 100644 backend/src/modules/inventory/warehouse/warehouse.module.ts create mode 100644 backend/src/modules/inventory/warehouse/warehouse.service.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/dto/create-mail-message-scheduled.dto.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/dto/index.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled-filter.dto.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled.dto.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/entities/index.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/entities/mail-message-scheduled.entity.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/index.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.controller.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.handler.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.module.ts create mode 100644 backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.service.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/config/imapflow.config.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/config/index.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-imapflow.dto.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-settings-imapflow.dto.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/index.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-imapflow.dto.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-settings-imapflow.dto.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-imapflow.dto.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-settings-imapflow.dto.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/entities/index.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/entities/mailbox-settings-imapflow.entity.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/errors/index.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/errors/mail-connection.error.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/imapflow.controller.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/imapflow.module.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/imapflow.service.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/index.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/types/imapflow-sync-info.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/types/index.ts create mode 100644 backend/src/modules/mail/mail-providers/imapflow/types/mailbox-imapflow.ts create mode 100644 backend/src/modules/mail/mail-providers/index.ts create mode 100644 backend/src/modules/mail/mail-providers/mail-providers.module.ts create mode 100644 backend/src/modules/mail/mail.module.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.controller.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.handler.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.module.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.service.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled-filter.dto.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled.dto.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/dto/create-chat-message-scheduled.dto.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/dto/index.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/entities/chat-message-scheduled.entity.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/entities/index.ts create mode 100644 backend/src/modules/multichat/chat-message-scheduled/index.ts create mode 100644 backend/src/modules/multichat/chat-message/chat-message.controller.ts create mode 100644 backend/src/modules/multichat/chat-message/chat-message.module.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/chat-message-file.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/chat-message-reaction.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/chat-message-user-status.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/chat-message.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/chat-messages-filter.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/chat-messages-result.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/index.ts create mode 100644 backend/src/modules/multichat/chat-message/dto/send-chat-message.dto.ts create mode 100644 backend/src/modules/multichat/chat-message/entities/chat-message-file.entity.ts create mode 100644 backend/src/modules/multichat/chat-message/entities/chat-message-reaction.entity.ts create mode 100644 backend/src/modules/multichat/chat-message/entities/chat-message-user-status.entity.ts create mode 100644 backend/src/modules/multichat/chat-message/entities/chat-message.entity.ts create mode 100644 backend/src/modules/multichat/chat-message/entities/index.ts create mode 100644 backend/src/modules/multichat/chat-message/services/chat-message-file.service.ts create mode 100644 backend/src/modules/multichat/chat-message/services/chat-message-reaction.service.ts create mode 100644 backend/src/modules/multichat/chat-message/services/chat-message-user-status.service.ts create mode 100644 backend/src/modules/multichat/chat-message/services/chat-message.service.ts create mode 100644 backend/src/modules/multichat/chat-message/services/chat-notification.service.ts create mode 100644 backend/src/modules/multichat/chat-message/services/index.ts create mode 100644 backend/src/modules/multichat/chat-message/types/expandable-field.ts create mode 100644 backend/src/modules/multichat/chat-message/types/index.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/chat-provider-user.module.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/chat-provider-user.service.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/entities/chat-provider-user.entity.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/entities/index.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/enums/chat-provider-user-type.enum.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/enums/index.ts create mode 100644 backend/src/modules/multichat/chat-provider-user/index.ts create mode 100644 backend/src/modules/multichat/chat-provider/chat-provider.controller.ts create mode 100644 backend/src/modules/multichat/chat-provider/chat-provider.module.ts create mode 100644 backend/src/modules/multichat/chat-provider/const/chat-provider-defaults.ts create mode 100644 backend/src/modules/multichat/chat-provider/const/index.ts create mode 100644 backend/src/modules/multichat/chat-provider/dto/chat-provider-entity-settings.dto.ts create mode 100644 backend/src/modules/multichat/chat-provider/dto/chat-provider.dto.ts create mode 100644 backend/src/modules/multichat/chat-provider/dto/create-chat-provider.dto.ts create mode 100644 backend/src/modules/multichat/chat-provider/dto/index.ts create mode 100644 backend/src/modules/multichat/chat-provider/dto/update-chat-provider.dto.ts create mode 100644 backend/src/modules/multichat/chat-provider/entities/chat-provider-entity-settings.entity.ts create mode 100644 backend/src/modules/multichat/chat-provider/entities/chat-provider.entity.ts create mode 100644 backend/src/modules/multichat/chat-provider/entities/index.ts create mode 100644 backend/src/modules/multichat/chat-provider/index.ts create mode 100644 backend/src/modules/multichat/chat-provider/services/chat-provider-entity-settings.service.ts create mode 100644 backend/src/modules/multichat/chat-provider/services/chat-provider.handler.ts create mode 100644 backend/src/modules/multichat/chat-provider/services/chat-provider.service.ts create mode 100644 backend/src/modules/multichat/chat-provider/services/index.ts create mode 100644 backend/src/modules/multichat/chat-provider/types/expandable-field.ts create mode 100644 backend/src/modules/multichat/chat-provider/types/index.ts create mode 100644 backend/src/modules/multichat/chat-user/chat-user.module.ts create mode 100644 backend/src/modules/multichat/chat-user/chat-user.service.ts create mode 100644 backend/src/modules/multichat/chat-user/dto/chat-user-external.dto.ts create mode 100644 backend/src/modules/multichat/chat-user/dto/chat-user.dto.ts create mode 100644 backend/src/modules/multichat/chat-user/dto/index.ts create mode 100644 backend/src/modules/multichat/chat-user/entities/chat-user-external.entity.ts create mode 100644 backend/src/modules/multichat/chat-user/entities/chat-user.entity.ts create mode 100644 backend/src/modules/multichat/chat-user/entities/index.ts create mode 100644 backend/src/modules/multichat/chat-user/index.ts create mode 100644 backend/src/modules/multichat/chat/chat.controller.ts create mode 100644 backend/src/modules/multichat/chat/chat.module.ts create mode 100644 backend/src/modules/multichat/chat/dto/chat-find-by-message-content-filter.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/chat-find-filter.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/chat-find-personal-filter.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/chat.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/create-contact-lead.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/create-external-chat.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/create-group-chat.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/create-personal-chat.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/find-chats-full-result.dto.ts create mode 100644 backend/src/modules/multichat/chat/dto/index.ts create mode 100644 backend/src/modules/multichat/chat/dto/update-group-chat.dto.ts create mode 100644 backend/src/modules/multichat/chat/entities/chat-pinned-message.entity.ts create mode 100644 backend/src/modules/multichat/chat/entities/chat.entity.ts create mode 100644 backend/src/modules/multichat/chat/entities/index.ts create mode 100644 backend/src/modules/multichat/chat/services/chat-pinned-message.service.ts create mode 100644 backend/src/modules/multichat/chat/services/chat.handler.ts create mode 100644 backend/src/modules/multichat/chat/services/chat.service.ts create mode 100644 backend/src/modules/multichat/chat/services/index.ts create mode 100644 backend/src/modules/multichat/common/enums/chat-message-status.enum.ts create mode 100644 backend/src/modules/multichat/common/enums/chat-provider-status.enum.ts create mode 100644 backend/src/modules/multichat/common/enums/chat-provider-transport.enum.ts create mode 100644 backend/src/modules/multichat/common/enums/chat-provider-type.enum.ts create mode 100644 backend/src/modules/multichat/common/enums/chat-type.enum.ts create mode 100644 backend/src/modules/multichat/common/enums/chat-user-role.enum.ts create mode 100644 backend/src/modules/multichat/common/enums/index.ts create mode 100644 backend/src/modules/multichat/common/events/chat-message/chat-message-created.event.ts create mode 100644 backend/src/modules/multichat/common/events/chat-message/chat-message-updated.event.ts create mode 100644 backend/src/modules/multichat/common/events/chat-message/chat-message.event.ts create mode 100644 backend/src/modules/multichat/common/events/chat-message/index.ts create mode 100644 backend/src/modules/multichat/common/events/chat-provider/chat-provider.event.ts create mode 100644 backend/src/modules/multichat/common/events/chat-provider/index.ts create mode 100644 backend/src/modules/multichat/common/events/chat/chat.event.ts create mode 100644 backend/src/modules/multichat/common/events/chat/index.ts create mode 100644 backend/src/modules/multichat/common/events/index.ts create mode 100644 backend/src/modules/multichat/common/events/multichat-event-type.enum.ts create mode 100644 backend/src/modules/multichat/common/index.ts create mode 100644 backend/src/modules/multichat/multichat.controller.ts create mode 100644 backend/src/modules/multichat/multichat.module.ts create mode 100644 backend/src/modules/multichat/providers/chat-provider-proxy.service.ts create mode 100644 backend/src/modules/multichat/providers/facebook/config/facebook.config.ts create mode 100644 backend/src/modules/multichat/providers/facebook/controllers/index.ts create mode 100644 backend/src/modules/multichat/providers/facebook/controllers/messenger-provider.controller.ts create mode 100644 backend/src/modules/multichat/providers/facebook/controllers/public-messenger-provider.controller.ts create mode 100644 backend/src/modules/multichat/providers/facebook/dto/create-messenger-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/facebook/dto/index.ts create mode 100644 backend/src/modules/multichat/providers/facebook/dto/messenger-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/facebook/dto/update-messenger-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/facebook/entities/chat-provider-messenger.entity.ts create mode 100644 backend/src/modules/multichat/providers/facebook/entities/index.ts create mode 100644 backend/src/modules/multichat/providers/facebook/facebook-provider.module.ts create mode 100644 backend/src/modules/multichat/providers/facebook/messenger-provider.service.ts create mode 100644 backend/src/modules/multichat/providers/providers.module.ts create mode 100644 backend/src/modules/multichat/providers/twilio/controllers/index.ts create mode 100644 backend/src/modules/multichat/providers/twilio/controllers/public-twilio-provider.controller.ts create mode 100644 backend/src/modules/multichat/providers/twilio/controllers/twilio-provider.controller.ts create mode 100644 backend/src/modules/multichat/providers/twilio/dto/create-twilio-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/twilio/dto/index.ts create mode 100644 backend/src/modules/multichat/providers/twilio/dto/twilio-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/twilio/dto/update-twilio-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/twilio/entities/chat-provider-twilio.entity.ts create mode 100644 backend/src/modules/multichat/providers/twilio/entities/index.ts create mode 100644 backend/src/modules/multichat/providers/twilio/twilio-provider.module.ts create mode 100644 backend/src/modules/multichat/providers/twilio/twilio-provider.service.ts create mode 100644 backend/src/modules/multichat/providers/twilio/types/index.ts create mode 100644 backend/src/modules/multichat/providers/twilio/types/twilio-request-body.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/config/index.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/config/wazzup.config.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/controllers/index.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/controllers/public-wazzup-provider.controller.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/controllers/wazzup-provider.controller.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/dto/create-wazzup-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/dto/index.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/dto/update-wazzup-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/dto/wazzup-channel.dto.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/dto/wazzup-provider.dto.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/entities/chat-provider-wazzup.entity.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/entities/index.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/enums/index.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/enums/wazzup-channel-state.enum.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/enums/wazzup-chat-type.enum.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-status.enum.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-type.enum.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/enums/wazzup-transport.enum.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/index.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-channel.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-connect-request.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-message-contact.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-message-error.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-message.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message-response.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/types/wazzup-webhook-request.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/wazzup-provider.module.ts create mode 100644 backend/src/modules/multichat/providers/wazzup/wazzup-provider.service.ts create mode 100644 backend/src/modules/notification/common/events/index.ts create mode 100644 backend/src/modules/notification/common/events/notification-event-type.enum.ts create mode 100644 backend/src/modules/notification/common/events/notification/index.ts create mode 100644 backend/src/modules/notification/common/events/notification/notification-unseen.event.ts create mode 100644 backend/src/modules/notification/common/index.ts create mode 100644 backend/src/modules/notification/notification-settings/dto/notification-settings.dto.ts create mode 100644 backend/src/modules/notification/notification-settings/dto/notification-type-settings.dto.ts create mode 100644 backend/src/modules/notification/notification-settings/entities/notification-settings.entity.ts create mode 100644 backend/src/modules/notification/notification-settings/entities/notification-type-follow-user.entity.ts create mode 100644 backend/src/modules/notification/notification-settings/entities/notification-type-settings.entity.ts create mode 100644 backend/src/modules/notification/notification-settings/notification-settings.controller.ts create mode 100644 backend/src/modules/notification/notification-settings/notification-settings.service.ts create mode 100644 backend/src/modules/notification/notification.module.ts create mode 100644 backend/src/modules/notification/notification/dto/create-notification.dto.ts create mode 100644 backend/src/modules/notification/notification/dto/index.ts create mode 100644 backend/src/modules/notification/notification/dto/notification.dto.ts create mode 100644 backend/src/modules/notification/notification/dto/notifications-result.dto.ts create mode 100644 backend/src/modules/notification/notification/entities/index.ts create mode 100644 backend/src/modules/notification/notification/entities/notification.entity.ts create mode 100644 backend/src/modules/notification/notification/enums/index.ts create mode 100644 backend/src/modules/notification/notification/enums/notification-type.enum.ts create mode 100644 backend/src/modules/notification/notification/notification-event.handler.ts create mode 100644 backend/src/modules/notification/notification/notification-scheduler.ts create mode 100644 backend/src/modules/notification/notification/notification.controller.ts create mode 100644 backend/src/modules/notification/notification/notification.service.ts create mode 100644 backend/src/modules/partner/dto/index.ts create mode 100644 backend/src/modules/partner/dto/partner-lead.dto.ts create mode 100644 backend/src/modules/partner/dto/partner-login.dto.ts create mode 100644 backend/src/modules/partner/dto/partner-summary.dto.ts create mode 100644 backend/src/modules/partner/partner.controller.ts create mode 100644 backend/src/modules/partner/partner.module.ts create mode 100644 backend/src/modules/partner/partner.service.ts create mode 100644 backend/src/modules/partner/types/index.ts create mode 100644 backend/src/modules/partner/types/partner-lead.ts create mode 100644 backend/src/modules/partner/types/partner-summary.ts create mode 100644 backend/src/modules/partner/types/partner.ts create mode 100644 backend/src/modules/scheduler/common/enums/index.ts create mode 100644 backend/src/modules/scheduler/common/enums/permission-object-type.enum.ts create mode 100644 backend/src/modules/scheduler/common/enums/schedule-appointment-status.enum.ts create mode 100644 backend/src/modules/scheduler/common/events/index.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-appointment/index.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-created.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext-upsert.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-updated.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-performer/index.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer-deleted.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule/index.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule/schedule-updated.event.ts create mode 100644 backend/src/modules/scheduler/common/events/schedule/schedule.event.ts create mode 100644 backend/src/modules/scheduler/common/events/scheduler-event-type.enum.ts create mode 100644 backend/src/modules/scheduler/common/index.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/create-schedule-appointment.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/index.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-filter.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-result.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-statistic.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/dto/update-schedule-appointment.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/entities/index.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/entities/schedule-appointment.entity.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/errors/appointment-dates-conflict.error.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/errors/appointment-day-limit.error.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/errors/appointment-intersection.error.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/errors/appointment-out-of-work-time.error.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/errors/index.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/index.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/schedule-appointment.controller.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/schedule-appointment.handler.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/schedule-appointment.service.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/types/expandable-field.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/types/index.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-result.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-statistic.ts create mode 100644 backend/src/modules/scheduler/schedule-appointment/types/spot.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/dtos/create-schedule-performer.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/dtos/index.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/dtos/schedule-performer.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/dtos/update-schedule-performer.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/entities/index.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/entities/schedule-performer.entity.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/enums/index.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/enums/schedule-performer-type.enum.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/index.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/schedule-performer.handler.ts create mode 100644 backend/src/modules/scheduler/schedule-performer/schedule-performer.service.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/dto/index.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-filter.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-row.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/dto/schedule-report.dto.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/enums/index.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/enums/schedule-report-type.enum.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/index.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/schedule-reporting.controller.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/schedule-reporting.service.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/types/index.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/types/schedule-report-row.ts create mode 100644 backend/src/modules/scheduler/schedule-reporting/types/schedule-report.ts create mode 100644 backend/src/modules/scheduler/schedule/dto/create-schedule.dto.ts create mode 100644 backend/src/modules/scheduler/schedule/dto/index.ts create mode 100644 backend/src/modules/scheduler/schedule/dto/schedule-filter.dto.ts create mode 100644 backend/src/modules/scheduler/schedule/dto/schedule-time-interval.dto.ts create mode 100644 backend/src/modules/scheduler/schedule/dto/schedule.dto.ts create mode 100644 backend/src/modules/scheduler/schedule/dto/update-schedule.dto.ts create mode 100644 backend/src/modules/scheduler/schedule/entities/index.ts create mode 100644 backend/src/modules/scheduler/schedule/entities/schedule-time-interval.entity.ts create mode 100644 backend/src/modules/scheduler/schedule/entities/schedule.entity.ts create mode 100644 backend/src/modules/scheduler/schedule/enums/index.ts create mode 100644 backend/src/modules/scheduler/schedule/enums/schedule-type.enum.ts create mode 100644 backend/src/modules/scheduler/schedule/index.ts create mode 100644 backend/src/modules/scheduler/schedule/schedule.controller.ts create mode 100644 backend/src/modules/scheduler/schedule/services/index.ts create mode 100644 backend/src/modules/scheduler/schedule/services/schedule-time-interval.service.ts create mode 100644 backend/src/modules/scheduler/schedule/services/schedule.service.ts create mode 100644 backend/src/modules/scheduler/scheduler.module.ts create mode 100644 backend/src/modules/setup/account-setup/account-setup.module.ts create mode 100644 backend/src/modules/setup/account-setup/account-setup.service.ts create mode 100644 backend/src/modules/setup/account-setup/config/account-setup.config.ts create mode 100644 backend/src/modules/setup/account-setup/config/index.ts create mode 100644 backend/src/modules/setup/account-setup/dto/index.ts create mode 100644 backend/src/modules/setup/account-setup/dto/setup-account.dto.ts create mode 100644 backend/src/modules/setup/account-setup/public-account-setup.controller.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/index.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-activity.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-board.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-entity-type.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-entity.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-field.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-note.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/crm/rms-task.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/index.ts create mode 100644 backend/src/modules/setup/account-setup/services/setup-crm.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/setup-iam.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/setup-products.service.ts create mode 100644 backend/src/modules/setup/account-setup/services/setup-scheduler.service.ts create mode 100644 backend/src/modules/setup/common/enums/demo-data-type.enum.ts create mode 100644 backend/src/modules/setup/common/enums/index.ts create mode 100644 backend/src/modules/setup/common/enums/industry-code.enum.ts create mode 100644 backend/src/modules/setup/common/enums/rmx-module-prefix.enum.ts create mode 100644 backend/src/modules/setup/common/index.ts create mode 100644 backend/src/modules/setup/common/types/index.ts create mode 100644 backend/src/modules/setup/common/types/rms-modules.ts create mode 100644 backend/src/modules/setup/demo-data/demo-data.controller.ts create mode 100644 backend/src/modules/setup/demo-data/demo-data.module.ts create mode 100644 backend/src/modules/setup/demo-data/demo-data.service.ts create mode 100644 backend/src/modules/setup/demo-data/entities/demo-data.entity.ts create mode 100644 backend/src/modules/setup/rms/dto/industry.dto.ts create mode 100644 backend/src/modules/setup/rms/dto/ready-made-solution.dto.ts create mode 100644 backend/src/modules/setup/rms/entities/industry.entity.ts create mode 100644 backend/src/modules/setup/rms/entities/ready-made-solution.entity.ts create mode 100644 backend/src/modules/setup/rms/rms.controller.ts create mode 100644 backend/src/modules/setup/rms/rms.module.ts create mode 100644 backend/src/modules/setup/rms/services/industry.service.ts create mode 100644 backend/src/modules/setup/rms/services/rms.service.ts create mode 100644 backend/src/modules/setup/setup.module.ts create mode 100644 backend/src/modules/setup/test-data/dto/create-test-accounts-query.ts create mode 100644 backend/src/modules/setup/test-data/entities/test-account.entity.ts create mode 100644 backend/src/modules/setup/test-data/public-test-data.controller.ts create mode 100644 backend/src/modules/setup/test-data/test-data.module.ts create mode 100644 backend/src/modules/setup/test-data/test-data.service.ts create mode 100644 backend/src/modules/storage/config/aws.config.ts create mode 100644 backend/src/modules/storage/dto/file-info-result.dto.ts create mode 100644 backend/src/modules/storage/dto/file-upload-request.ts create mode 100644 backend/src/modules/storage/dto/file-upload-result.ts create mode 100644 backend/src/modules/storage/dto/image-options.dto.ts create mode 100644 backend/src/modules/storage/dto/index.ts create mode 100644 backend/src/modules/storage/entities/file-info.entity.ts create mode 100644 backend/src/modules/storage/entities/index.ts create mode 100644 backend/src/modules/storage/enums/index.ts create mode 100644 backend/src/modules/storage/enums/mime-type.enum.ts create mode 100644 backend/src/modules/storage/providers/aws-s3.provider.ts create mode 100644 backend/src/modules/storage/providers/index.ts create mode 100644 backend/src/modules/storage/storage-public.controller.ts create mode 100644 backend/src/modules/storage/storage-url.service.ts create mode 100644 backend/src/modules/storage/storage.controller.ts create mode 100644 backend/src/modules/storage/storage.module.ts create mode 100644 backend/src/modules/storage/storage.service.ts create mode 100644 backend/src/modules/storage/types/file-info-result.ts create mode 100644 backend/src/modules/storage/types/index.ts create mode 100644 backend/src/modules/storage/types/storage-file.ts create mode 100644 backend/src/modules/storage/types/temporary-file.ts create mode 100644 backend/src/modules/telephony/common/events/index.ts create mode 100644 backend/src/modules/telephony/common/events/telephony-call/index.ts create mode 100644 backend/src/modules/telephony/common/events/telephony-call/telephony-call-created.event.ts create mode 100644 backend/src/modules/telephony/common/events/telephony-call/telephony-call-updated.event.ts create mode 100644 backend/src/modules/telephony/common/events/telephony-call/telephony-call.event.ts create mode 100644 backend/src/modules/telephony/common/events/telephony-event-type.enum.ts create mode 100644 backend/src/modules/telephony/common/index.ts create mode 100644 backend/src/modules/telephony/telephony.module.ts create mode 100644 backend/src/modules/telephony/voximplant/common/enums/call-direction.enum.ts create mode 100644 backend/src/modules/telephony/voximplant/common/enums/call-status.enum.ts create mode 100644 backend/src/modules/telephony/voximplant/common/enums/index.ts create mode 100644 backend/src/modules/telephony/voximplant/common/errors/index.ts create mode 100644 backend/src/modules/telephony/voximplant/common/errors/voximplant-error.ts create mode 100644 backend/src/modules/telephony/voximplant/common/index.ts create mode 100644 backend/src/modules/telephony/voximplant/common/types/index.ts create mode 100644 backend/src/modules/telephony/voximplant/common/types/voximplant-application-param.interface.ts create mode 100644 backend/src/modules/telephony/voximplant/config/voximplant.config.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/dto/voximplant-account.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/entities/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/entities/voximplant-account.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call-ext.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call-ext.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call-list.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/entities/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/entities/voximplant-call.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/types/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/types/voximplant-call-list.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call-public.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-core/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/dto/create-voximplant-number.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/dto/phone-number.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/dto/update-voximplant-number.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number-filter.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/entities/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number-user.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/services/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number-user.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/types/expandable-field.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/types/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/types/phone-number.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-number/voximplant-number.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report-filter.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-block.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-filter.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-row.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/enums/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/enums/telephony-report-type.enum.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-history-report.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-block.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-row.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/types/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-entity.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-note.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-task.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenarios.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/entities/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-entity.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-note.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-task.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/enums/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/enums/scenario-type.enum.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/types/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/types/voximplant-scenarios.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/dto/create-voximplant-sip.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/dto/update-voximplant-sip.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-filter.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-registration.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/entities/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip-user.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/enums/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/enums/pbx-provider-type.enum.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/services/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip-user.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/types/expandable-field.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/types/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/types/voximplant-sip-registration.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-sip/voximplant-sip.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-user.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-users-batch.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/update-voximplant-user.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/users-queue.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user-sip-data.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user.dto.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/entities/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/entities/voximplant-user.entity.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/index.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user-public.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.controller.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.service.ts create mode 100644 backend/src/modules/telephony/voximplant/voximplant.module.ts create mode 100644 backend/src/modules/tutorial/common/dto/index.ts create mode 100644 backend/src/modules/tutorial/common/dto/tutorial-filter.dto.ts create mode 100644 backend/src/modules/tutorial/common/enums/index.ts create mode 100644 backend/src/modules/tutorial/common/enums/tutorial-product-type.enum.ts create mode 100644 backend/src/modules/tutorial/common/index.ts create mode 100644 backend/src/modules/tutorial/config/tutorial.config.ts create mode 100644 backend/src/modules/tutorial/tutorial-core/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-core/tutorial-core.controller.ts create mode 100644 backend/src/modules/tutorial/tutorial-core/tutorial-core.service.ts create mode 100644 backend/src/modules/tutorial/tutorial-core/tutorial-defaults.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/dto/create-tutorial-group.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/dto/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/dto/tutorial-group.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/dto/update-tutorial-group.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/entities/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/entities/tutorial-group.entity.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/tutorial-group.controller.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/tutorial-group.service.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/types/expandable-field.ts create mode 100644 backend/src/modules/tutorial/tutorial-group/types/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/dto/create-tutorial-item.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/dto/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/dto/tutorial-item-product.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/dto/tutorial-item.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/dto/update-tutorial-item.dto.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/entities/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-product.entity.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-user.entity.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/entities/tutorial-item.entity.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/index.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/tutorial-item.controller.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/tutorial-item.handler.ts create mode 100644 backend/src/modules/tutorial/tutorial-item/tutorial-item.service.ts create mode 100644 backend/src/modules/tutorial/tutorial.module.ts create mode 100644 backend/src/support/config/index.ts create mode 100644 backend/src/support/config/support.config.ts create mode 100644 backend/src/support/health/health.controller.ts create mode 100644 backend/src/support/health/health.module.ts create mode 100644 backend/src/support/health/index.ts create mode 100644 backend/src/support/heapdump/heapdump.controller.ts create mode 100644 backend/src/support/heapdump/heapdump.module.ts create mode 100644 backend/src/support/heapdump/heapdump.service.ts create mode 100644 backend/src/support/heapdump/index.ts create mode 100644 backend/src/support/support.module.ts create mode 100644 backend/src/support/version/dto/create-version.dto.ts create mode 100644 backend/src/support/version/dto/current-version.dto.ts create mode 100644 backend/src/support/version/dto/index.ts create mode 100644 backend/src/support/version/dto/version.dto.ts create mode 100644 backend/src/support/version/entities/index.ts create mode 100644 backend/src/support/version/entities/version.entity.ts create mode 100644 backend/src/support/version/index.ts create mode 100644 backend/src/support/version/version.controller.ts create mode 100644 backend/src/support/version/version.module.ts create mode 100644 backend/src/support/version/version.service.ts create mode 100644 backend/src/types/environment.d.ts create mode 100644 backend/src/types/express.d.ts create mode 100644 backend/src/types/imapflow/index.d.ts create mode 100644 backend/src/types/reset.d.ts create mode 100644 backend/tsconfig.build.json create mode 100644 backend/tsconfig.json create mode 100644 backend/var/jwt/private.pem create mode 100644 backend/var/jwt/public.pem create mode 100644 backend/var/voximplant/credentials.json create mode 100644 backend/yarn.lock create mode 100644 crm.mcmed.ru create mode 100644 frontend/.gitattributes create mode 100644 frontend/.gitignore create mode 100644 frontend/.gitlab-ci.yml create mode 100644 frontend/.npmrc create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc.json create mode 100644 frontend/.storybook/main.ts create mode 100644 frontend/.storybook/preview.ts create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.mjs create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/favicons/amwork/apple_touch_120x120.png create mode 100644 frontend/public/favicons/amwork/apple_touch_152x152.png create mode 100644 frontend/public/favicons/amwork/apple_touch_167x167.png create mode 100644 frontend/public/favicons/amwork/apple_touch_180x180.png create mode 100644 frontend/public/favicons/amwork/favicon_16x16.png create mode 100644 frontend/public/favicons/amwork/favicon_16x16.svg create mode 100644 frontend/public/favicons/amwork/favicon_32x32.png create mode 100644 frontend/public/favicons/amwork/favicon_32x32.svg create mode 100644 frontend/public/favicons/favicon.png create mode 100644 frontend/public/favicons/mywork/apple_touch_120x120.png create mode 100644 frontend/public/favicons/mywork/apple_touch_152x152.png create mode 100644 frontend/public/favicons/mywork/apple_touch_167x167.png create mode 100644 frontend/public/favicons/mywork/apple_touch_180x180.png create mode 100644 frontend/public/favicons/mywork/favicon_16x16.png create mode 100644 frontend/public/favicons/mywork/favicon_16x16.svg create mode 100644 frontend/public/favicons/mywork/favicon_32x32.png create mode 100644 frontend/public/favicons/mywork/favicon_32x32.svg create mode 100644 frontend/public/favicons/proma/apple_touch_120x120.png create mode 100644 frontend/public/favicons/proma/apple_touch_152x152.png create mode 100644 frontend/public/favicons/proma/apple_touch_167x167.png create mode 100644 frontend/public/favicons/proma/apple_touch_180x180.png create mode 100644 frontend/public/favicons/proma/favicon_16x16.png create mode 100644 frontend/public/favicons/proma/favicon_16x16.svg create mode 100644 frontend/public/favicons/proma/favicon_32x32.png create mode 100644 frontend/public/favicons/proma/favicon_32x32.svg create mode 100644 frontend/public/fonts/Geologica/SemiBold/GeologicaSemiBold.ttf create mode 100644 frontend/public/fonts/Nunito/Regular/NunitoRegular.ttf create mode 100644 frontend/public/fonts/Nunito/SemiBold/NunitoSemiBold.ttf create mode 100644 frontend/public/fonts/TimesNewRoman/Bold/TimesNewRomanBold.ttf create mode 100644 frontend/public/fonts/TimesNewRoman/Regular/TimesNewRomanRegular.ttf create mode 100644 frontend/public/images/bpmn/en/bpmn.svg create mode 100644 frontend/public/images/bpmn/es/bpmn.svg create mode 100644 frontend/public/images/bpmn/fr/bpmn.svg create mode 100644 frontend/public/images/bpmn/pl/bpmn.svg create mode 100644 frontend/public/images/bpmn/ru/bpmn.svg create mode 100644 frontend/public/images/builder/deals-view/board.png create mode 100644 frontend/public/images/builder/deals-view/list.png create mode 100644 frontend/public/images/builder/scheduler-view/board.png create mode 100644 frontend/public/images/builder/scheduler-view/schedule.png create mode 100644 frontend/public/images/components/ImportEntitiesModal/import_entities.svg create mode 100644 frontend/public/images/dashboard/chart_plug.png create mode 100644 frontend/public/images/demo/analytics.png create mode 100644 frontend/public/images/demo/mail.png create mode 100644 frontend/public/images/invoice/mywork_signature.png create mode 100644 frontend/public/images/invoice/mywork_square_logo.png create mode 100644 frontend/public/images/invoice/mywork_stamp.png create mode 100644 frontend/public/images/login/en/login.png create mode 100644 frontend/public/images/login/login.svg create mode 100644 frontend/public/images/login/ru/login.png create mode 100644 frontend/public/locales/en/common.json create mode 100644 frontend/public/locales/en/component.card.json create mode 100644 frontend/public/locales/en/component.entity-board.json create mode 100644 frontend/public/locales/en/component.section.json create mode 100644 frontend/public/locales/en/module.automation.json create mode 100644 frontend/public/locales/en/module.bpmn.json create mode 100644 frontend/public/locales/en/module.builder.json create mode 100644 frontend/public/locales/en/module.fields.json create mode 100644 frontend/public/locales/en/module.mailing.json create mode 100644 frontend/public/locales/en/module.multichat.json create mode 100644 frontend/public/locales/en/module.notes.json create mode 100644 frontend/public/locales/en/module.notifications.json create mode 100644 frontend/public/locales/en/module.products.json create mode 100644 frontend/public/locales/en/module.reporting.json create mode 100644 frontend/public/locales/en/module.scheduler.json create mode 100644 frontend/public/locales/en/module.telephony.json create mode 100644 frontend/public/locales/en/module.tutorial.json create mode 100644 frontend/public/locales/en/page.board-settings.json create mode 100644 frontend/public/locales/en/page.dashboard.json create mode 100644 frontend/public/locales/en/page.login.json create mode 100644 frontend/public/locales/en/page.settings.json create mode 100644 frontend/public/locales/en/page.system.json create mode 100644 frontend/public/locales/en/page.tasks.json create mode 100644 frontend/public/locales/en/store.field-groups-store.json create mode 100644 frontend/public/locales/en/store.fields-store.json create mode 100644 frontend/public/locales/es/common.json create mode 100644 frontend/public/locales/es/component.card.json create mode 100644 frontend/public/locales/es/component.entity-board.json create mode 100644 frontend/public/locales/es/component.section.json create mode 100644 frontend/public/locales/es/module.automation.json create mode 100644 frontend/public/locales/es/module.bpmn.json create mode 100644 frontend/public/locales/es/module.builder.json create mode 100644 frontend/public/locales/es/module.fields.json create mode 100644 frontend/public/locales/es/module.mailing.json create mode 100644 frontend/public/locales/es/module.multichat.json create mode 100644 frontend/public/locales/es/module.notes.json create mode 100644 frontend/public/locales/es/module.notifications.json create mode 100644 frontend/public/locales/es/module.products.json create mode 100644 frontend/public/locales/es/module.reporting.json create mode 100644 frontend/public/locales/es/module.scheduler.json create mode 100644 frontend/public/locales/es/module.telephony.json create mode 100644 frontend/public/locales/es/module.tutorial.json create mode 100644 frontend/public/locales/es/page.board-settings.json create mode 100644 frontend/public/locales/es/page.dashboard.json create mode 100644 frontend/public/locales/es/page.login.json create mode 100644 frontend/public/locales/es/page.settings.json create mode 100644 frontend/public/locales/es/page.system.json create mode 100644 frontend/public/locales/es/page.tasks.json create mode 100644 frontend/public/locales/es/store.field-groups-store.json create mode 100644 frontend/public/locales/es/store.fields-store.json create mode 100644 frontend/public/locales/fr/common.json create mode 100644 frontend/public/locales/fr/component.card.json create mode 100644 frontend/public/locales/fr/component.entity-board.json create mode 100644 frontend/public/locales/fr/component.section.json create mode 100644 frontend/public/locales/fr/module.automation.json create mode 100644 frontend/public/locales/fr/module.bpmn.json create mode 100644 frontend/public/locales/fr/module.builder.json create mode 100644 frontend/public/locales/fr/module.fields.json create mode 100644 frontend/public/locales/fr/module.mailing.json create mode 100644 frontend/public/locales/fr/module.multichat.json create mode 100644 frontend/public/locales/fr/module.notes.json create mode 100644 frontend/public/locales/fr/module.notifications.json create mode 100644 frontend/public/locales/fr/module.products.json create mode 100644 frontend/public/locales/fr/module.reporting.json create mode 100644 frontend/public/locales/fr/module.scheduler.json create mode 100644 frontend/public/locales/fr/module.telephony.json create mode 100644 frontend/public/locales/fr/module.tutorial.json create mode 100644 frontend/public/locales/fr/page.board-settings.json create mode 100644 frontend/public/locales/fr/page.dashboard.json create mode 100644 frontend/public/locales/fr/page.login.json create mode 100644 frontend/public/locales/fr/page.settings.json create mode 100644 frontend/public/locales/fr/page.system.json create mode 100644 frontend/public/locales/fr/page.tasks.json create mode 100644 frontend/public/locales/fr/store.field-groups-store.json create mode 100644 frontend/public/locales/fr/store.fields-store.json create mode 100644 frontend/public/locales/pl/common.json create mode 100644 frontend/public/locales/pl/component.card.json create mode 100644 frontend/public/locales/pl/component.entity-board.json create mode 100644 frontend/public/locales/pl/component.section.json create mode 100644 frontend/public/locales/pl/module.automation.json create mode 100644 frontend/public/locales/pl/module.bpmn.json create mode 100644 frontend/public/locales/pl/module.builder.json create mode 100644 frontend/public/locales/pl/module.fields.json create mode 100644 frontend/public/locales/pl/module.mailing.json create mode 100644 frontend/public/locales/pl/module.multichat.json create mode 100644 frontend/public/locales/pl/module.notes.json create mode 100644 frontend/public/locales/pl/module.notifications.json create mode 100644 frontend/public/locales/pl/module.products.json create mode 100644 frontend/public/locales/pl/module.reporting.json create mode 100644 frontend/public/locales/pl/module.scheduler.json create mode 100644 frontend/public/locales/pl/module.telephony.json create mode 100644 frontend/public/locales/pl/module.tutorial.json create mode 100644 frontend/public/locales/pl/page.board-settings.json create mode 100644 frontend/public/locales/pl/page.dashboard.json create mode 100644 frontend/public/locales/pl/page.login.json create mode 100644 frontend/public/locales/pl/page.settings.json create mode 100644 frontend/public/locales/pl/page.system.json create mode 100644 frontend/public/locales/pl/page.tasks.json create mode 100644 frontend/public/locales/pl/store.field-groups-store.json create mode 100644 frontend/public/locales/pl/store.fields-store.json create mode 100644 frontend/public/locales/ru/common.json create mode 100644 frontend/public/locales/ru/component.card.json create mode 100644 frontend/public/locales/ru/component.entity-board.json create mode 100644 frontend/public/locales/ru/component.section.json create mode 100644 frontend/public/locales/ru/module.automation.json create mode 100644 frontend/public/locales/ru/module.bpmn.json create mode 100644 frontend/public/locales/ru/module.builder.json create mode 100644 frontend/public/locales/ru/module.fields.json create mode 100644 frontend/public/locales/ru/module.mailing.json create mode 100644 frontend/public/locales/ru/module.multichat.json create mode 100644 frontend/public/locales/ru/module.notes.json create mode 100644 frontend/public/locales/ru/module.notifications.json create mode 100644 frontend/public/locales/ru/module.products.json create mode 100644 frontend/public/locales/ru/module.reporting.json create mode 100644 frontend/public/locales/ru/module.scheduler.json create mode 100644 frontend/public/locales/ru/module.telephony.json create mode 100644 frontend/public/locales/ru/module.tutorial.json create mode 100644 frontend/public/locales/ru/page.board-settings.json create mode 100644 frontend/public/locales/ru/page.dashboard.json create mode 100644 frontend/public/locales/ru/page.login.json create mode 100644 frontend/public/locales/ru/page.settings.json create mode 100644 frontend/public/locales/ru/page.system.json create mode 100644 frontend/public/locales/ru/page.tasks.json create mode 100644 frontend/public/locales/ru/store.field-groups-store.json create mode 100644 frontend/public/locales/ru/store.fields-store.json create mode 100644 frontend/public/robots.txt create mode 100644 frontend/public/sounds/incoming_call.mp3 create mode 100644 frontend/public/sounds/notification.mp3 create mode 100644 frontend/src/app/App.tsx create mode 100644 frontend/src/app/api/ApiRoutes.ts create mode 100644 frontend/src/app/api/AppQueryKeys.ts create mode 100644 frontend/src/app/api/BaseApi/BaseApi.ts create mode 100644 frontend/src/app/api/BoardApi/BoardApi.ts create mode 100644 frontend/src/app/api/BoardApi/BoardApiUtil.ts create mode 100644 frontend/src/app/api/DadataApi/DadataApi.ts create mode 100644 frontend/src/app/api/EntityTypeApi/EntityTypeApi.ts create mode 100644 frontend/src/app/api/FeatureApi/FeatureApi.ts create mode 100644 frontend/src/app/api/FeedbackApi/FeedbackApi.ts create mode 100644 frontend/src/app/api/FileApi/FileApi.ts create mode 100644 frontend/src/app/api/FileApi/queries/useGetFileInfos.ts create mode 100644 frontend/src/app/api/FormApi/FormApi.ts create mode 100644 frontend/src/app/api/FrontendObjectsApi/FrontendObjectsApi.ts create mode 100644 frontend/src/app/api/GeneralSettingsApi/GeneralSettingsApi.ts create mode 100644 frontend/src/app/api/IdentityApi/IdentityApi.ts create mode 100644 frontend/src/app/api/StageApi/StageApi.ts create mode 100644 frontend/src/app/api/StageApi/StageApiUtil.ts create mode 100644 frontend/src/app/api/SubscriptionApi/SubscriptionApi.ts create mode 100644 frontend/src/app/api/SubscriptionApi/queries/useGetSubscriptionForAccount.ts create mode 100644 frontend/src/app/api/SubscriptionApi/queries/useUpdateSubscription.ts create mode 100644 frontend/src/app/api/UserApi/UserApi.ts create mode 100644 frontend/src/app/api/UserProfileApi/UserProfileApi.ts create mode 100644 frontend/src/app/api/UserProfileApi/helpers/invalidateUserProfilesInCache.ts create mode 100644 frontend/src/app/api/UserProfileApi/queries/useGetUserProfiles.ts create mode 100644 frontend/src/app/api/UtilityApi/UtilityApi.ts create mode 100644 frontend/src/app/api/VersionApi/VersionApi.ts create mode 100644 frontend/src/app/api/VersionApi/queries/useGetLatestFrontendVersionPolling.ts create mode 100644 frontend/src/app/api/dtos/Board/BoardDto.ts create mode 100644 frontend/src/app/api/dtos/Board/CreateBoardDto.ts create mode 100644 frontend/src/app/api/dtos/Board/UpdateBoardDto.ts create mode 100644 frontend/src/app/api/dtos/Builder/FeatureDto.ts create mode 100644 frontend/src/app/api/dtos/CreateContactAndLeadDto.ts create mode 100644 frontend/src/app/api/dtos/Entity/EntityDto.ts create mode 100644 frontend/src/app/api/dtos/EntityEvent/ContactInfoDto.ts create mode 100644 frontend/src/app/api/dtos/EntityEvent/EntityEventItemDto.ts create mode 100644 frontend/src/app/api/dtos/EntityEvent/EntityInfoDto.ts create mode 100644 frontend/src/app/api/dtos/EntityLink/EntityLinkDto.ts create mode 100644 frontend/src/app/api/dtos/EntityType/EntityTypeDto.ts create mode 100644 frontend/src/app/api/dtos/EntityType/UpdateEntityTypeDto.ts create mode 100644 frontend/src/app/api/dtos/EntityType/UpdateEntityTypeFieldsModel.ts create mode 100644 frontend/src/app/api/dtos/FeedItem/FeedItemDto.ts create mode 100644 frontend/src/app/api/dtos/Feedback/FeedbackType.ts create mode 100644 frontend/src/app/api/dtos/Feedback/SendFeedbackDto.ts create mode 100644 frontend/src/app/api/dtos/Feedback/TrialExpiredFeedback.ts create mode 100644 frontend/src/app/api/dtos/Feedback/UserLimitFeedback.ts create mode 100644 frontend/src/app/api/dtos/FileInfoResult/FileInfoResultDto.ts create mode 100644 frontend/src/app/api/dtos/FileLink/FileLinkDto.ts create mode 100644 frontend/src/app/api/dtos/Form/SendContactUsFormDto.ts create mode 100644 frontend/src/app/api/dtos/FrontendObject/CreateFrontendObjectDto.ts create mode 100644 frontend/src/app/api/dtos/FrontendObject/FrontendObjectDto.ts create mode 100644 frontend/src/app/api/dtos/GeneralSettings/AccountDto.ts create mode 100644 frontend/src/app/api/dtos/GeneralSettings/AccountSettingsDto.ts create mode 100644 frontend/src/app/api/dtos/GeneralSettings/UpdateAccountSettingsDto.ts create mode 100644 frontend/src/app/api/dtos/Permission/ObjectPermissionDto.ts create mode 100644 frontend/src/app/api/dtos/Profile/UpdateUserProfileDto.ts create mode 100644 frontend/src/app/api/dtos/Profile/UserProfileDto.ts create mode 100644 frontend/src/app/api/dtos/SiteForm/SiteFormAnalyticDataDto.ts create mode 100644 frontend/src/app/api/dtos/SiteForm/SiteFormDataDto.ts create mode 100644 frontend/src/app/api/dtos/SiteForm/SiteFormFieldDataDto.ts create mode 100644 frontend/src/app/api/dtos/SiteForm/SiteFormResultDto.ts create mode 100644 frontend/src/app/api/dtos/Stage/CreateStageDto.ts create mode 100644 frontend/src/app/api/dtos/Stage/StageDto.ts create mode 100644 frontend/src/app/api/dtos/Stage/UpdateStageDto.ts create mode 100644 frontend/src/app/api/dtos/Subscription/SubscriptionDto.ts create mode 100644 frontend/src/app/api/dtos/Subscription/SubscriptionFeatureDto.ts create mode 100644 frontend/src/app/api/dtos/Subscription/SubscriptionPlanDto.ts create mode 100644 frontend/src/app/api/dtos/Subscription/SubscriptionPriceDto.ts create mode 100644 frontend/src/app/api/dtos/Subscription/UpdateSubscriptionDto.ts create mode 100644 frontend/src/app/api/dtos/User/ChangeUserPasswordDto.ts create mode 100644 frontend/src/app/api/dtos/User/CreateUserDto.ts create mode 100644 frontend/src/app/api/dtos/User/UpdateUserDto.ts create mode 100644 frontend/src/app/api/dtos/User/UserDto.ts create mode 100644 frontend/src/app/api/dtos/Version/VersionDto.ts create mode 100644 frontend/src/app/api/dtos/index.tsx create mode 100644 frontend/src/app/api/index.tsx create mode 100644 frontend/src/app/config/i18n/i18n.ts create mode 100644 frontend/src/app/config/knip/knip.ts create mode 100644 frontend/src/app/config/newrelic/newrelic.ts create mode 100644 frontend/src/app/config/storybook/I18nDecorator.tsx create mode 100644 frontend/src/app/config/storybook/RouterDecorator.tsx create mode 100644 frontend/src/app/config/storybook/StyleDecorator.tsx create mode 100644 frontend/src/app/index.tsx create mode 100644 frontend/src/app/pages/HomePage/HomePage.tsx create mode 100644 frontend/src/app/pages/index.tsx create mode 100644 frontend/src/app/routes/AppBoundary.tsx create mode 100644 frontend/src/app/routes/createRouter.tsx create mode 100644 frontend/src/app/routes/index.tsx create mode 100644 frontend/src/app/routes/routes.ts create mode 100644 frontend/src/app/store/AppStore.ts create mode 100644 frontend/src/app/store/EntityTypeStore.ts create mode 100644 frontend/src/app/store/FeatureStore.ts create mode 100644 frontend/src/app/store/GeneralSettingsFormData.ts create mode 100644 frontend/src/app/store/GeneralSettingsStore.ts create mode 100644 frontend/src/app/store/IconStore.tsx create mode 100644 frontend/src/app/store/IdentityStore.ts create mode 100644 frontend/src/app/store/RecordSingularityStore.ts create mode 100644 frontend/src/app/store/RecordStateStore.ts create mode 100644 frontend/src/app/store/SettingsStore.ts create mode 100644 frontend/src/app/store/SubscriptionStore.ts create mode 100644 frontend/src/app/store/UserProfileStore.ts create mode 100644 frontend/src/app/store/UserStore.ts create mode 100644 frontend/src/app/store/WatchdogStore.ts create mode 100644 frontend/src/app/store/index.tsx create mode 100644 frontend/src/app/styles/emoji-picker.css create mode 100644 frontend/src/app/styles/fonts.css create mode 100644 frontend/src/app/styles/main.css create mode 100644 frontend/src/app/styles/react-pdf.css create mode 100644 frontend/src/app/styles/reset.css create mode 100644 frontend/src/app/styles/toastify.css create mode 100644 frontend/src/app/styles/variables.css create mode 100644 frontend/src/declarations/bpmn-js.d.ts create mode 100644 frontend/src/declarations/custom-elements.d.ts create mode 100644 frontend/src/declarations/emoji-mart.d.ts create mode 100644 frontend/src/declarations/env.d.ts create mode 100644 frontend/src/declarations/reset.d.ts create mode 100644 frontend/src/declarations/svgr.d.ts create mode 100644 frontend/src/declarations/vite.env.d.ts create mode 100644 frontend/src/declarations/voxengine.d.ts create mode 100644 frontend/src/index.tsx create mode 100644 frontend/src/modules/auth/api/AuthApi/AuthApi.ts create mode 100644 frontend/src/modules/auth/api/AuthApiRoutes.ts create mode 100644 frontend/src/modules/auth/api/index.tsx create mode 100644 frontend/src/modules/auth/index.tsx create mode 100644 frontend/src/modules/auth/pages/LoginLinkPage/LoginLinkPage.tsx create mode 100644 frontend/src/modules/auth/pages/LoginPage/LoginPage.tsx create mode 100644 frontend/src/modules/auth/pages/index.tsx create mode 100644 frontend/src/modules/auth/shared/assets/hide_password.svg create mode 100644 frontend/src/modules/auth/shared/assets/index.tsx create mode 100644 frontend/src/modules/auth/shared/assets/show_password.svg create mode 100644 frontend/src/modules/auth/shared/index.tsx create mode 100644 frontend/src/modules/auth/shared/lib/components/LoginButton/LoginButton.tsx create mode 100644 frontend/src/modules/auth/shared/lib/components/LoginForm/LoginForm.tsx create mode 100644 frontend/src/modules/auth/shared/lib/components/LoginInput/LoginInput.tsx create mode 100644 frontend/src/modules/auth/shared/lib/components/LoginOrDelimiter/LoginOrDelimiter.tsx create mode 100644 frontend/src/modules/auth/shared/lib/components/SuggestionLink/SuggestionLink.tsx create mode 100644 frontend/src/modules/auth/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/auth/shared/lib/index.tsx create mode 100644 frontend/src/modules/auth/store/AuthStore.ts create mode 100644 frontend/src/modules/auth/store/index.tsx create mode 100644 frontend/src/modules/auth/templates/LoginTemplate/LoginTemplate.tsx create mode 100644 frontend/src/modules/auth/templates/index.tsx create mode 100644 frontend/src/modules/automation/api/AutomationApiRoutes.ts create mode 100644 frontend/src/modules/automation/api/AutomationEntityTypeApi/AutomationEntityTypeApi.ts create mode 100644 frontend/src/modules/automation/api/dtos/AutomationEntityType/AutomationEntityTypeDto.ts create mode 100644 frontend/src/modules/automation/api/dtos/AutomationEntityType/CreateAutomationEntityTypeDto.ts create mode 100644 frontend/src/modules/automation/api/dtos/AutomationEntityType/UpdateAutomationEntityType.ts create mode 100644 frontend/src/modules/automation/api/dtos/index.tsx create mode 100644 frontend/src/modules/automation/api/index.tsx create mode 100644 frontend/src/modules/automation/index.tsx create mode 100644 frontend/src/modules/automation/shared/assets/activity.svg create mode 100644 frontend/src/modules/automation/shared/assets/change_of_stage.svg create mode 100644 frontend/src/modules/automation/shared/assets/create_entity.svg create mode 100644 frontend/src/modules/automation/shared/assets/index.tsx create mode 100644 frontend/src/modules/automation/shared/assets/mail.svg create mode 100644 frontend/src/modules/automation/shared/assets/plus.svg create mode 100644 frontend/src/modules/automation/shared/assets/task.svg create mode 100644 frontend/src/modules/automation/shared/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/AutomationBlock/AddAutomationButton.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/AutomationBlock/AutomationBlock.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/AutomationBlock/HeaderDropdown.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/AutomationBlock/HeaderDropdownList.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Block/Block.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/ExternalChatProvidersSelect/ExternalChatProvidersSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/AddActivityAutomationModal/AddActivityAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/AddActivityAutomationModal/AddActivityAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/AddTaskAutomationModal/AddTaskAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/AddTaskAutomationModal/AddTaskAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/AutomationModalTemplate.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/ChangeLinkedStageAutomationModal/ChangeLinkedStageAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/ChangeLinkedStageAutomationModal/ChangeLinkedStageAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/ChangeResponsibleAutomationModal/ChangeResponsibleAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/ChangeResponsibleAutomationModal/ChangeResponsibleAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/ChangeStageAutomationModal/ChangeStageAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/ChangeStageAutomationModal/ChangeStageAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/CreateEntityAutomationModal/CreateEntityAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/CreateEntityAutomationModal/CreateEntityAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/RequestHttpAutomationModal/RequestHttpAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/RequestHttpAutomationModal/RequestHttpAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/SendEmailAutomationModal/SendEmailAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/SendEmailAutomationModal/SendEmailAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/SendExternalChatAutomationModal/SendExternalChatAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/SendExternalChatAutomationModal/SendExternalChatAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/SendInternalChatAutomationModal/SendInternalChatAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/SendInternalChatAutomationModal/SendInternalChatAutomationModalContent.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationBooleanRadioSelect/AutomationBooleanRadioSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationFormItem/AutomationFormItem.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationRadioButton/AutomationRadioButton.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationSendOptionsBlock/AutomationSendOptionsBlock.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationSendOptionsBlock/components/EntitySendOptionsBlock/EntitySendOptionsBlock.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationSendOptionsBlock/components/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/AutomationUserSelect/AutomationUserSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/ConditionsBlock.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldFilterBoolean/EntityTypeFieldFilterBoolean.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldFilterDate/EntityTypeFieldFilterDate.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldFilterNumber/EntityTypeFieldFilterNumber.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldFilterParticipantsSelect/EntityTypeFieldFilterParticipantsSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldFilterSelect/EntityTypeFieldFilterSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldFilterString/EntityTypeFieldFilterString.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldsConditions/EntityTypeFieldsConditions.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/EntityTypeFieldsConditionsSwitch/EntityTypeFieldsConditionsSwitch.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/ConditionsBlock/components/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/DeferStartSelect/DeferStartSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/DeleteAutomationModal/DeleteAutomationModal.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/DescriptionBlock/DescriptionBlock.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/DueDateSelect/DueDateSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/TemplateList/TemplateList.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/TriggerSelect/TriggerSelect.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/WrapperWithLeftOffset/WrapperWithLeftOffset.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Modals/components/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Sidebar/ActionTypeBlock/ActionTypeBlock.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Sidebar/AutomationSidebar/AutomationSidebar.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/Sidebar/ListAutomationSidebar/ListAutomationSidebar.tsx create mode 100644 frontend/src/modules/automation/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/helpers/getActionSendVariant.ts create mode 100644 frontend/src/modules/automation/shared/lib/helpers/getActionTypeEntityTypeInfos.tsx create mode 100644 frontend/src/modules/automation/shared/lib/helpers/getSendEmailActionSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/helpers/getSendExternalChatActionSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/hooks/useGetAutomationDelayOptions.ts create mode 100644 frontend/src/modules/automation/shared/lib/hooks/useGetDeferStartOptions.ts create mode 100644 frontend/src/modules/automation/shared/lib/hooks/useGetDueDateOptions.ts create mode 100644 frontend/src/modules/automation/shared/lib/hooks/useGetTriggerOptions.ts create mode 100644 frontend/src/modules/automation/shared/lib/hooks/useInitializeSendEmailActionSettingsForm.ts create mode 100644 frontend/src/modules/automation/shared/lib/hooks/useInitializeSendExternalChatForm.ts create mode 100644 frontend/src/modules/automation/shared/lib/index.tsx create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSendOptionsForm.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSendVariant.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionActivityCreateSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionChatSendAmworkSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionChatSendSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionCommonSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionEmailSendSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionEntityCreateSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionEntityLinkedStageChangeSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionEntityResponsibleChangeSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionEntityStageChangeSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionHttpCallSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionSendOptions.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionSendOptionsEntity.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionSendOptionsValue.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionSettings/ActionTaskCreateSettings.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/ActionTypeEntityTypeInfo.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/AutomationEntityType.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/AutomationEntityTypeDeadline.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/AutomationEntityTypeTemplateFormData.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/Condition/AutomationFieldCondition.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/Condition/EntityTypeCondition.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/Condition/EntityTypeConditionModel.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/Condition/EntityTypeFieldConditionFormData.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/DeadlineType.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationEntityType/EntityTypeAction.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/AutomationModalBaseProps.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/ResponsibleUser.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/ResponsibleUserType.ts create mode 100644 frontend/src/modules/automation/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/automation/store/AutomationStore.ts create mode 100644 frontend/src/modules/automation/store/index.tsx create mode 100644 frontend/src/modules/bpmn/api/AutomationUtilsApi/AutomationUtilsApi.ts create mode 100644 frontend/src/modules/bpmn/api/AutomationsProcessesApi/AutomationsProcessesApi.ts create mode 100644 frontend/src/modules/bpmn/api/AutomationsProcessesApi/queries/useCreateAutomationProcess.ts create mode 100644 frontend/src/modules/bpmn/api/AutomationsProcessesApi/queries/useDeleteAutomationProcess.ts create mode 100644 frontend/src/modules/bpmn/api/AutomationsProcessesApi/queries/useGetAutomationProcess.ts create mode 100644 frontend/src/modules/bpmn/api/AutomationsProcessesApi/queries/useGetAutomationsProcesses.ts create mode 100644 frontend/src/modules/bpmn/api/AutomationsProcessesApi/queries/useUpdateAutomationProcess.ts create mode 100644 frontend/src/modules/bpmn/api/BpmnAutomationsApiRoutes.ts create mode 100644 frontend/src/modules/bpmn/api/BpmnAutomationsQueryKeys.ts create mode 100644 frontend/src/modules/bpmn/api/dtos/AutomationProcesses/AutomationProcessDto.ts create mode 100644 frontend/src/modules/bpmn/api/dtos/AutomationProcesses/CreateAutomationProcessDto.ts create mode 100644 frontend/src/modules/bpmn/api/dtos/AutomationProcesses/UpdateAutomationProcessDto.ts create mode 100644 frontend/src/modules/bpmn/api/dtos/index.tsx create mode 100644 frontend/src/modules/bpmn/api/index.tsx create mode 100644 frontend/src/modules/bpmn/extension/ExecutableFix.js create mode 100644 frontend/src/modules/bpmn/extension/WorkspaceContextPadProvider.js create mode 100644 frontend/src/modules/bpmn/extension/WorkspacePalette.js create mode 100644 frontend/src/modules/bpmn/extension/WorkspaceRenderer.js create mode 100644 frontend/src/modules/bpmn/extension/index.tsx create mode 100644 frontend/src/modules/bpmn/index.tsx create mode 100644 frontend/src/modules/bpmn/pages/AutomationProcessesPage/AutomationProcessesPage.tsx create mode 100644 frontend/src/modules/bpmn/pages/AutomationProcessesPage/ListSectionAutomationProcessesPage.tsx create mode 100644 frontend/src/modules/bpmn/pages/AutomationProcessesPage/components/AutomationProcessesSettingsHeader/AutomationProcessesSettingsHeader.tsx create mode 100644 frontend/src/modules/bpmn/pages/AutomationProcessesPage/components/index.tsx create mode 100644 frontend/src/modules/bpmn/pages/index.tsx create mode 100644 frontend/src/modules/bpmn/resources/workspace.json create mode 100644 frontend/src/modules/bpmn/shared/assets/bpmn.svg create mode 100644 frontend/src/modules/bpmn/shared/assets/center_diagram.svg create mode 100644 frontend/src/modules/bpmn/shared/assets/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/assets/start_process.svg create mode 100644 frontend/src/modules/bpmn/shared/assets/stop_process.svg create mode 100644 frontend/src/modules/bpmn/shared/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessSchemaErrorWarning/AutomationProcessSchemaErrorWarning.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcesses/AutomationProcesses.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/AutomationProcessesModeler.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/CenterDiagramViewButton/CenterDiagramViewButton.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/CreateAutomationButton/CreateAutomationButton.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/CreateAutomationProcessModal/CreateAutomationProcessModal.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/CreateWorkspaceServiceTaskMenu/CreateWorkspaceServiceTaskMenu.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/CreateWorkspaceStartEventMenu/CreateWorkspaceStartEventMenu.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/PaletteSubMenu/PaletteSubMenu.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/PaletteSubMenuIcon/PaletteSubMenuIcon.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/PaletteSubMenuItem/PaletteSubMenuItem.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/PopupFormItem/PopupFormItem.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/PopupHeaderIcon/PopupHeaderIcon.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/ProcessControls/ProcessControls.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/ProcessElementPopup/ProcessElementPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceDelayEventPopup/WorkspaceDelayEventPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceSequenceFlowPopup/WorkspaceSequenceFlowPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskAddActivityPopup/ServiceTaskAddActivityPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskAddTaskPopup/ServiceTaskAddTaskPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskChangeLinkedStagePopup/ServiceTaskChangeLinkedStagePopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskChangeResponsiblePopup/ServiceTaskChangeResponsiblePopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskChangeStagePopup/ServiceTaskChangeStagePopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskCreateEntityPopup/ServiceTaskCreateEntityPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskPopupTemplate/ServiceTaskPopupTemplate.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskRequestHttpPopup/ServiceTaskRequestHttpPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskSendEmailPopup/ServiceTaskSendEmailPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskSendExternalChatPopup/ServiceTaskSendExternalChatPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/ServiceTaskSendInternalChatPopup/ServiceTaskSendInternalChatPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceServiceTask/WorkspaceServiceTaskPopupSwitch/WorkspaceServiceTaskPopupSwitch.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/WorkspaceStartEventPopup/WorkspaceStartEventPopup.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesModeler/components/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/AutomationProcessesTable.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/components/NoAutomationProcessesBlock/NoAutomationProcessesBlock.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/components/cells/AutomationProcessStatusCell/AutomationProcessStatusCell.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/components/cells/AutomatonProcessNameCell/AutomatonProcessNameCell.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/components/cells/DeleteAutomationProcessCell/DeleteAutomationProcessCell.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/components/cells/DeleteAutomationProcessWarning/DeleteAutomationProcessWarning.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/AutomationProcessesTable/components/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/BPMNRequestSplashscreen/BPMNRequestSplashscreen.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/checkIfCanAddConditionToFlow.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/checkIfElementHasServiceTasksInContextPad.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/getCrmEventTypeByEntityTypeTrigger.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/getParsedZeebeInputString.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/getServiceTaskZeebeInputTargetByEntityTypeActionType.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/safeSetElementTitle.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/helpers/updateServiceTaskSettings.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/hooks/useAutomationProcessesColumns.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/hooks/useAutomationProcessesData.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/hooks/useGetWorkspaceEventOptions.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/hooks/useGetWorkspaceServiceTaskOptions.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/hooks/useHandleTranslateModelerElementsTitles.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationEntityCondition.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationProcesses/AutomationProcess.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationProcesses/AutomationProcessInputTargetType.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationProcesses/AutomationProcessRow.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationProcesses/AutomationProcessType.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationProcesses/AutomationProcessesColumnsIds.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/AutomationProcesses/AutomationProcessesColumnsSizes.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/BpmnJsType.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/CommonServiceTaskPopupProps.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/EventBusEvent.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/ProcessMinimap.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/ZeebeType.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/constants.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/bpmn/shared/lib/utils/BpmnAutomationsUtil.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/utils/EventMessageNameUtil.ts create mode 100644 frontend/src/modules/bpmn/shared/lib/utils/index.tsx create mode 100644 frontend/src/modules/bpmn/styles/bpmn-js.css create mode 100644 frontend/src/modules/builder/api/BuilderApiRoutes.ts create mode 100644 frontend/src/modules/builder/api/BuilderQueryKeys.ts create mode 100644 frontend/src/modules/builder/api/SiteFormApi/SiteFormApi.ts create mode 100644 frontend/src/modules/builder/api/SiteFormApi/queries/useDeleteSiteForm.ts create mode 100644 frontend/src/modules/builder/api/SiteFormApi/queries/useGetSiteForms.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/CreateSiteForm/CreateSiteFormConsentDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/CreateSiteForm/CreateSiteFormDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/CreateSiteForm/CreateSiteFormFieldDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/CreateSiteForm/CreateSiteFormGratitudeDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/CreateSiteForm/CreateSiteFormPageDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormConsentDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormEntityTypeDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormFieldDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormFieldEntityFieldDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormFieldEntityNameDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormFieldTextMetaDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormGratitudeDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormPageDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/SiteFormScheduleDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/UpdateSiteForm/UpdateSiteFormConsentDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/UpdateSiteForm/UpdateSiteFormDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/UpdateSiteForm/UpdateSiteFormFieldDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/UpdateSiteForm/UpdateSiteFormGratitudeDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/SiteForm/UpdateSiteForm/UpdateSiteFormPageDto.ts create mode 100644 frontend/src/modules/builder/api/dtos/index.tsx create mode 100644 frontend/src/modules/builder/api/index.tsx create mode 100644 frontend/src/modules/builder/index.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/BuilderHomePage.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/BuilderJourneyPicker/BuilderJourneyPicker.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/BuilderJourneyPicker/components/BuilderModuleOption/BuilderModuleOption.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/BuilderJourneyPicker/components/PickJourneyLink/PickJourneyLink.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/BuilderJourneyPicker/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/WorkspaceEditor/WorkspaceEditor.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/WorkspaceEditor/components/EntityTypeFieldUsedInFormulaWarningModal/EntityTypeFieldUsedInFormulaWarningModal.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/WorkspaceEditor/components/WorkspaceItem/WorkspaceItem.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/WorkspaceEditor/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/BuilderHomePage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/EtSectionBuilderPage.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/components/EtSectionBuilderStep1/EtSectionBuilderStep1.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/components/EtSectionBuilderStep2/EtSectionBuilderStep2.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/components/EtSectionBuilderStep3/EtSectionBuilderStep3.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/components/EtSectionBuilderStep4/EtSectionBuilderStep4.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/components/TaskFieldsSelector/TaskFieldsSelector.tsx create mode 100644 frontend/src/modules/builder/pages/EtSectionBuilderPage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/HeadlessSiteFormBuilder.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/HeadlessSiteFormBuilderPage.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep2/HeadlessSiteFormBuilderStep2.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep2/components/HeadlessSiteFormElementsSidebar/HeadlessSiteFormElementsSidebar.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep2/components/HeadlessSiteFormElementsTree/HeadlessSiteFormElementsTree.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep2/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep3/HeadlessSiteFormBuilderStep3.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep3/components/HeadlessSiteFormFieldsTable/HeadlessSiteFormFieldsTable.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep3/components/HeadlessSiteFormPlainFieldsTable/HeadlessSiteFormPlainFieldsTable.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/HeadlessSiteFormBuilderStep3/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/HeadlessSiteFormBuilderPage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/OnlineBookingSiteFormBuilder.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/OnlineBookingSiteFormBuilderPage.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/components/OnlineBookingSiteFormBuilderStep1/OnlineBookingSiteFormBuilderStep1.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/components/OnlineBookingSiteFormBuilderStep1/components/OnlineBookingSiteFormBuilderScheduleItem/OnlineBookingSiteFormBuilderScheduleItem.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/components/OnlineBookingSiteFormBuilderStep1/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/components/OnlineBookingSiteFormBuilderStep2/OnlineBookingSiteFormBuilderStep2.tsx create mode 100644 frontend/src/modules/builder/pages/OnlineBookingSiteFormBuilderPage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/ProductsSectionBuilderPage.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/components/ProductsSectionBuilderLinkSectionsStep/ProductsSectionBuilderLinkSectionsStep.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/components/ProductsSectionBuilderScheduleSettings/ProductsSectionBuilderScheduleSettings.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/components/ProductsSectionBuilderStep1/ProductsSectionBuilderStep1.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/components/ProductsSectionBuilderStep2/ProductsSectionBuilderStep2.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/components/ProductsSectionBuilderStep3/ProductsSectionBuilderStep3.tsx create mode 100644 frontend/src/modules/builder/pages/ProductsSectionBuilderPage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/SchedulerBuilderPage.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/SchedulerBuilderStep1.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/ContentWrapper/ContentWrapper.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/FormItemWrapper/FormItemWrapper.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/RowWrapper/RowWrapper.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/SchedulerBuilderStep1BottomBlock/SchedulerBuilderStep1BottomBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/SchedulerBuilderStep1TopBlock/SchedulerBuilderStep1TopBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/SchedulerBuilderStep1TypeBlock/SchedulerBuilderStep1TypeBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep1/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep2/SchedulerBuilderStep2.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/SchedulerBuilderStep3/SchedulerBuilderStep3.tsx create mode 100644 frontend/src/modules/builder/pages/SchedulerBuilderPage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/SiteFormBuilder.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/SiteFormBuilderPage.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep1/SiteFormBuilderStep1.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep1/components/SiteFormBuilderLinkedEntityItemCheckbox/SiteFormBuilderLinkedEntityItemCheckbox.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep1/components/SiteFormBuilderLinkedEntityItemRadio/SiteFormBuilderLinkedEntityItemRadio.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep1/components/SiteFormBuilderLinkedEntityItemTemplate/SiteFormBuilderLinkedEntityItemTemplate.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep1/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/SiteFormBuilderStep2.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/SiteFormElementsSidebar.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/EntityTypeBlockTemplate/EntityTypeBlockTemplate.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/EntityTypeFieldsGroupsBlock/EntityTypeFieldsGroupsBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/FieldAttributesBlock/FieldAttributesBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/FormElementBlockButton/FormElementBlockButton.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/FormElementCardNameBlock/FormElementCardNameBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/FormElementCollapsibleBlock/FormElementCollapsibleBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/FormElementFieldBlock/FormElementFieldBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/FormFieldElements/FormFieldElements.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsSidebar/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/SiteFormElementsTree.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/SiteFormElementName/SiteFormElementName.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/SiteFormElements/SiteFormDelimiterFieldElement/SiteFormDelimiterFieldElement.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/SiteFormElements/SiteFormElementTemplate/SiteFormElementTemplate.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/SiteFormElements/SiteFormEntityFieldElement/SiteFormEntityFieldElement.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/SiteFormElements/SiteFormSchedulerFieldElement/SiteFormSchedulerFieldElement.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/SiteFormElementsSwitch/SiteFormElementsSwitch.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/SiteFormElementsTree/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep2/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/SiteFormBuilderStep3.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/BlockTemplate/BlockTemplate.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/ShowInFormControl/ShowInFormControl.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/SiteFormConsentBlock/SiteFormConsentBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/SiteFormConsentBlock/components/SiteFormConsentControls/SiteFormConsentControls.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/SiteFormConsentBlock/components/SiteFormConsentSkeleton/SiteFormConsentSkeleton.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/SiteFormConsentBlock/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/SiteFormGratitudeBlock/SiteFormGratitudeBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep3/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/SiteFormBuilderStep4.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/SiteFormClientPreview.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/DeviceSelector/DeviceSelector.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/SiteFormClientPreviewOnDevices/SiteFormClientPreviewOnDevices.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/SiteFormComputerPreview/SiteFormComputerPreview.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/SiteFormContentTabs/SiteFormContentTabs.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/SiteFormPhonePreview/SiteFormPhonePreview.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/SiteFormTabletPreview/SiteFormTabletPreview.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormClientPreview/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/SiteFormCustomizationSidebar.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/AdvancedSettingsDelimiter/AdvancedSettingsDelimiter.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/ClientButtonCustomizationBlock/ClientButtonCustomizationBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/CustomCSSBlock/CustomCSSBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/CustomizationBlockTemplate/CustomizationBlockTemplate.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/CustomizationSlider/CustomizationSlider.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/FieldsCustomizationBlock/FieldsCustomizationBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/FormButtonCustomizationBlock/FormButtonCustomizationBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/FormLayoutCustomizationBlock/FormLayoutCustomizationBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/HeaderCustomizationBlock/HeaderCustomizationBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/ModalOverlayCustomizationBlock/ModalOverlayCustomizationBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/OrientationRowSelect/OrientationRowSelect.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/PoweredByLogoBlock/PoweredByLogoBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/RadioGroupControl/RadioGroupControl.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/SiteFormCustomizationTextInput/SiteFormCustomizationTextInput.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/SizeCustomizationElementTemplate/SizeCustomizationElementTemplate.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/SiteFormCustomizationSidebar/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep4/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep5/SiteFormBuilderStep5.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep5/components/AnalyticsEventCode/AnalyticsEventCode.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep5/components/AnalyticsEventsTable/AnalyticsEventsTable.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep5/components/CodeBlock/CodeBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep5/components/SiteFormCodeBlock/SiteFormCodeBlock.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/SiteFormBuilderStep5/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/SiteFormBuilderPage/components/index.tsx create mode 100644 frontend/src/modules/builder/pages/index.tsx create mode 100644 frontend/src/modules/builder/shared/assets/advanced_settings.svg create mode 100644 frontend/src/modules/builder/shared/assets/align_center.svg create mode 100644 frontend/src/modules/builder/shared/assets/align_left.svg create mode 100644 frontend/src/modules/builder/shared/assets/align_right.svg create mode 100644 frontend/src/modules/builder/shared/assets/border_rounded.svg create mode 100644 frontend/src/modules/builder/shared/assets/border_square.svg create mode 100644 frontend/src/modules/builder/shared/assets/builder_tab.svg create mode 100644 frontend/src/modules/builder/shared/assets/calendar.svg create mode 100644 frontend/src/modules/builder/shared/assets/card_name.svg create mode 100644 frontend/src/modules/builder/shared/assets/computer_preview.svg create mode 100644 frontend/src/modules/builder/shared/assets/consent_skeleton_bottom.svg create mode 100644 frontend/src/modules/builder/shared/assets/consent_skeleton_top.svg create mode 100644 frontend/src/modules/builder/shared/assets/date.svg create mode 100644 frontend/src/modules/builder/shared/assets/delimiter.svg create mode 100644 frontend/src/modules/builder/shared/assets/expand_more.svg create mode 100644 frontend/src/modules/builder/shared/assets/file.svg create mode 100644 frontend/src/modules/builder/shared/assets/file_image.svg create mode 100644 frontend/src/modules/builder/shared/assets/gratitude_skeleton.svg create mode 100644 frontend/src/modules/builder/shared/assets/header.svg create mode 100644 frontend/src/modules/builder/shared/assets/home_button.svg create mode 100644 frontend/src/modules/builder/shared/assets/index.tsx create mode 100644 frontend/src/modules/builder/shared/assets/link.svg create mode 100644 frontend/src/modules/builder/shared/assets/long_text.svg create mode 100644 frontend/src/modules/builder/shared/assets/mail.svg create mode 100644 frontend/src/modules/builder/shared/assets/multiselect.svg create mode 100644 frontend/src/modules/builder/shared/assets/multiselect_caret.svg create mode 100644 frontend/src/modules/builder/shared/assets/number.svg create mode 100644 frontend/src/modules/builder/shared/assets/orientation_horizontal.svg create mode 100644 frontend/src/modules/builder/shared/assets/orientation_vertical.svg create mode 100644 frontend/src/modules/builder/shared/assets/phone.svg create mode 100644 frontend/src/modules/builder/shared/assets/phone_preview.svg create mode 100644 frontend/src/modules/builder/shared/assets/scheduler.svg create mode 100644 frontend/src/modules/builder/shared/assets/select.svg create mode 100644 frontend/src/modules/builder/shared/assets/select_caret.svg create mode 100644 frontend/src/modules/builder/shared/assets/short_text.svg create mode 100644 frontend/src/modules/builder/shared/assets/step_back.svg create mode 100644 frontend/src/modules/builder/shared/assets/step_ellipse.svg create mode 100644 frontend/src/modules/builder/shared/assets/step_next.svg create mode 100644 frontend/src/modules/builder/shared/assets/switch.svg create mode 100644 frontend/src/modules/builder/shared/assets/switch_image.svg create mode 100644 frontend/src/modules/builder/shared/assets/tablet_preview.svg create mode 100644 frontend/src/modules/builder/shared/assets/text_align_center.svg create mode 100644 frontend/src/modules/builder/shared/assets/text_align_left.svg create mode 100644 frontend/src/modules/builder/shared/assets/text_align_right.svg create mode 100644 frontend/src/modules/builder/shared/assets/wazzup.svg create mode 100644 frontend/src/modules/builder/shared/assets/workspace_tab.svg create mode 100644 frontend/src/modules/builder/shared/index.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderNav/NavStepItem/NavStepItem.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderNav/NavStepsList/NavStepsList.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderPageRoot/BuilderPageRoot.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepBiggerTitle/BuilderStepBiggerTitle.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepBox/BuilderStepBox.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepCheckboxItemWrapper/BuilderStepCheckboxItemWrapper.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepControls/BuilderStepControls.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepItemLabel/BuilderStepItemLabel.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepOutlinedSection/BuilderStepOutlinedSection.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepSubtitle/BuilderStepSubtitle.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/BuilderStep/BuilderStepTitle/BuilderStepTitle.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/ColorRowSelect/ColorRowSelect.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/EntityTypeUsedInFormulaWarningModal/EntityTypeUsedInFormulaWarningModal.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/GiantOutlinedInput/GiantOutlinedInput.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/GiantOutlinedInputSkeleton/GiantOutlinedInputSkeleton.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/GiantOutlinedTextarea/GiantOutlinedTextarea.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/GiantUnderlinedInput/GiantUnderlinedInput.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/HorizontalStepMotion/HorizontalStepMotion.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/LinkRadiosDelimiter/LinkRadiosDelimiter.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/LinkRadiosWrapper/LinkRadiosWrapper.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/LoadableSectionImage/LoadableSectionImage.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/NameInputSkeleton/NameInputSkeleton.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/NoLinksBlock/NoLinksBlock.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/RadioWrapper/RadioWrapper.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/RadiosSkeleton/RadiosSkeleton.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/SectionIconPicker/SectionIconPicker.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/SiteFormBuilderPageStepRoot/SiteFormBuilderPageStepRoot.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/SwitchBlock/SwitchBlock.tsx create mode 100644 frontend/src/modules/builder/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateEtSectionBuilderNavSteps.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateFormLayoutRadioOptions.tsx create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateHeadlessSiteFormBuilderNavSteps.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateOnlineBookingSiteFormBuilderNavSteps.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateProductsSectionBuilderNavSteps.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateRentalIntervalStartTimeOptions.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateRentalIntervalTypeOptions.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateSchedulerBuilderNavSteps.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateSchedulerBuilderTimePeriodOptions.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/generateSiteFormBuilderNavSteps.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/getJourneyLink.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/getSiteFormElementPlaceholderByFieldType.ts create mode 100644 frontend/src/modules/builder/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/builder/shared/lib/helpers/mapModuleCategoryToEntityCategory.ts create mode 100644 frontend/src/modules/builder/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/builder/shared/lib/hooks/useAdditionalModulesOptions.tsx create mode 100644 frontend/src/modules/builder/shared/lib/hooks/useBuilderMarketplaceOptions.tsx create mode 100644 frontend/src/modules/builder/shared/lib/hooks/useGetAdditionalStorageOptions.tsx create mode 100644 frontend/src/modules/builder/shared/lib/hooks/useGetProductsSectionAfterCancelDelayOptions.ts create mode 100644 frontend/src/modules/builder/shared/lib/hooks/useGetTasksFieldsOptions.ts create mode 100644 frontend/src/modules/builder/shared/lib/hooks/useModulesCategoriesOptions.tsx create mode 100644 frontend/src/modules/builder/shared/lib/index.tsx create mode 100644 frontend/src/modules/builder/shared/lib/models/AdditionalModuleCategory.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/BuilderAdditionalStorageCategory.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/BuilderMarketplaceCategory.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/BuilderNavStep.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/BuilderTabs.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/EtSectionBuilderFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/EtSectionBuilderModel.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/ModuleCategory.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/ModuleOptionExtra.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/NoLinkRadioValue.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/PreviewDevice.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/PreviewTab.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SchedulerSectionBuilderFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormButtonDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormClientButtonDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormDesignCustomCSSFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormFieldsDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormHeaderDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormLayoutDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/Design/SiteFormModalOverlayDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/HeadlessSiteFormElementsFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/OnlineBookingSiteFormElementsFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/OnlineBookingSiteFormInitializationFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormConsentFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormDesignFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormElementsFieldModel.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormElementsFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormElementsPageFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormElementsTitleFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormEntityTypeLinkModel.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormFieldEntityFieldModel.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormGratitudeFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/FormData/SiteFormInitializationFormData.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteForm.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormConsent.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormBorder.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormCustomCSS.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesign.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesignClientButton.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesignFields.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesignFormButton.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesignFormLayout.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesignHeader.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormDesignModalOverlay.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormFontSize.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormOrientation.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormPosition.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormTextOrientation.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormDesign/SiteFormView.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormEntityType.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormField/SiteFormField.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormField/SiteFormFieldEntityField.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormField/SiteFormFieldEntityName.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormField/SiteFormFieldTextMeta.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormField/SiteFormFieldTextView.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormField/SiteFormFieldType.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormGratitude.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormPage.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormSchedule.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/SiteForm/SiteFormType.ts create mode 100644 frontend/src/modules/builder/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/builder/shared/lib/types/SchedulerIntervalSource.ts create mode 100644 frontend/src/modules/builder/shared/lib/types/SiteForm/AddSiteFormCardNameHandler.ts create mode 100644 frontend/src/modules/builder/shared/lib/types/SiteForm/AddSiteFormFieldHandler.ts create mode 100644 frontend/src/modules/builder/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/builder/store/BuilderNavStore.ts create mode 100644 frontend/src/modules/builder/store/EtSectionBuilderStore.ts create mode 100644 frontend/src/modules/builder/store/HeadlessSiteFormBuilderStore.ts create mode 100644 frontend/src/modules/builder/store/OnlineBookingSiteFormBuilderStore.ts create mode 100644 frontend/src/modules/builder/store/SchedulerBuilderStore.ts create mode 100644 frontend/src/modules/builder/store/SiteFormBuilderStore.ts create mode 100644 frontend/src/modules/builder/store/index.tsx create mode 100644 frontend/src/modules/builder/templates/BuilderPageTemplate/BuilderPageTemplate.tsx create mode 100644 frontend/src/modules/builder/templates/BuilderStepTemplate/BuilderStepTemplate.tsx create mode 100644 frontend/src/modules/builder/templates/BuilderWithHorizontalNavPageTemplate/BuilderWithHorizontalNavPageTemplate.tsx create mode 100644 frontend/src/modules/builder/templates/BuilderWithVerticalNavPageTemplate/BuilderWithVerticalNavPageTemplate.tsx create mode 100644 frontend/src/modules/builder/templates/index.tsx create mode 100644 frontend/src/modules/card/CardApiRoutes.ts create mode 100644 frontend/src/modules/card/api/EntityDocumentApi/EntityDocumentApi.ts create mode 100644 frontend/src/modules/card/api/FeedApi/FeedApi.ts create mode 100644 frontend/src/modules/card/api/NoteApi/NoteApi.ts create mode 100644 frontend/src/modules/card/api/dtos/CreateNoteDto.ts create mode 100644 frontend/src/modules/card/api/dtos/NoteDto.ts create mode 100644 frontend/src/modules/card/api/dtos/UpdateNoteDto.ts create mode 100644 frontend/src/modules/card/api/dtos/index.tsx create mode 100644 frontend/src/modules/card/api/index.tsx create mode 100644 frontend/src/modules/card/index.tsx create mode 100644 frontend/src/modules/card/pages/AddCardPage/AddCardPage.tsx create mode 100644 frontend/src/modules/card/pages/CardPage/CardPage.tsx create mode 100644 frontend/src/modules/card/pages/index.tsx create mode 100644 frontend/src/modules/card/shared/assets/ChatTags/avito_tag.svg create mode 100644 frontend/src/modules/card/shared/assets/ChatTags/fb_messenger_tag.svg create mode 100644 frontend/src/modules/card/shared/assets/ChatTags/instagram_tag.svg create mode 100644 frontend/src/modules/card/shared/assets/ChatTags/telegram_tag.svg create mode 100644 frontend/src/modules/card/shared/assets/ChatTags/vk_tag.svg create mode 100644 frontend/src/modules/card/shared/assets/ChatTags/whatsapp_tag.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/activity.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/chat.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/documents.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/filter.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/note.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/plus.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/show_more.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/task.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedControl/visit.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/blue_file.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/call_incoming.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/call_missed.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/call_outgoing.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/mail_blue.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/task_green.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/task_grey_dark.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/task_grey_light.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/task_red.svg create mode 100644 frontend/src/modules/card/shared/assets/FeedIcons/violet_pencil.svg create mode 100644 frontend/src/modules/card/shared/assets/account_tree_empty.svg create mode 100644 frontend/src/modules/card/shared/assets/airtable.svg create mode 100644 frontend/src/modules/card/shared/assets/amocrm.svg create mode 100644 frontend/src/modules/card/shared/assets/arrows_exchange.svg create mode 100644 frontend/src/modules/card/shared/assets/bitrix.svg create mode 100644 frontend/src/modules/card/shared/assets/clip.svg create mode 100644 frontend/src/modules/card/shared/assets/clock.svg create mode 100644 frontend/src/modules/card/shared/assets/close_cross.svg create mode 100644 frontend/src/modules/card/shared/assets/document.svg create mode 100644 frontend/src/modules/card/shared/assets/excel.svg create mode 100644 frontend/src/modules/card/shared/assets/facebook.svg create mode 100644 frontend/src/modules/card/shared/assets/feed_calendar.svg create mode 100644 frontend/src/modules/card/shared/assets/forward.svg create mode 100644 frontend/src/modules/card/shared/assets/freshsales.svg create mode 100644 frontend/src/modules/card/shared/assets/hubspot.svg create mode 100644 frontend/src/modules/card/shared/assets/index.tsx create mode 100644 frontend/src/modules/card/shared/assets/instagram.svg create mode 100644 frontend/src/modules/card/shared/assets/linkedin.svg create mode 100644 frontend/src/modules/card/shared/assets/microsoft.svg create mode 100644 frontend/src/modules/card/shared/assets/monday.svg create mode 100644 frontend/src/modules/card/shared/assets/notion.svg create mode 100644 frontend/src/modules/card/shared/assets/oracle.svg create mode 100644 frontend/src/modules/card/shared/assets/pipedrive.svg create mode 100644 frontend/src/modules/card/shared/assets/reply.svg create mode 100644 frontend/src/modules/card/shared/assets/ringing_phone.svg create mode 100644 frontend/src/modules/card/shared/assets/rocket.svg create mode 100644 frontend/src/modules/card/shared/assets/salesforce.svg create mode 100644 frontend/src/modules/card/shared/assets/sap.svg create mode 100644 frontend/src/modules/card/shared/assets/sugarcrm.svg create mode 100644 frontend/src/modules/card/shared/assets/three_dots_vertical.svg create mode 100644 frontend/src/modules/card/shared/assets/timer.svg create mode 100644 frontend/src/modules/card/shared/assets/tune.svg create mode 100644 frontend/src/modules/card/shared/assets/twitter.svg create mode 100644 frontend/src/modules/card/shared/assets/zendesk.svg create mode 100644 frontend/src/modules/card/shared/assets/zoho.svg create mode 100644 frontend/src/modules/card/shared/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/ActionMenu/ActionMenu.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/ActionsDropdown/ActionsDropdown.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/BudgetBlock/BudgetRoot.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/BudgetBlock/CommonBudgetBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/BudgetBlock/ProjectBudgetBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardComponent/CardComponent.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardNameBlock/CardNameBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardPageHeader/CardPageHeader.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardPageHeaderControls/CardPageTasksProjectHeaderControls.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardStages/CardStages.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardStages/StageItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardStages/SystemStageItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardSystemInfoBlock/CardSystemInfoBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardSystemInfoBlock/components/CardSystemInfoBlockItem/CardSystemInfoBlockItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/CardSystemInfoBlock/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/ChangeLinkedResponsiblesModal/ChangeLinkedResponsiblesModal.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/EntityTypeLinksBlock/EntityTypeLinksBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/Feed.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/ActivityTypeControl/ActivityTypeControl.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/ActivityTypeControl/components/ActivityTypeItem/ActivityTypeItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/ActivityTypeControl/components/AddTypeBlock/AddTypeBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/ActivityTypeControl/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/ActivityTypeControl/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/CallBlock/CallBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/DocumentBlock/DocumentBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/FeedControl.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/FeedControlTab/FeedControlTab.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/MoreTabsDropdown/MoreTabsDropdown.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/TabPanels/AddActivityTabPanel/AddActivityTabPanel.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/TabPanels/AddNoteTabPanel/AddNoteTabPanel.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/TabPanels/TextEditorWrapper/TextEditorWrapper.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/AddTaskModal/AddTaskModal.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/AddTaskModal/components/AddTaskModalFormItem/AddTaskModalFormItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/AddTaskModal/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/AddTaskModalHeader/AddTaskModalHeader.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/Subtask/SubtaskItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/Subtask/SubtasksBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/Subtask/SubtasksList.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/TaskSettingsDropdown/TaskSettingsDropdown.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/AddTaskTab/TasksBoardSelector/TasksBoardSelector.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/CreateDocumentsTab.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/AddTemplatePlaceholder/AddTemplatePlaceholder.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/CreateDocumentsDrawer/CreateDocumentsDrawer.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/CreateDocumentsDrawer/components/CreateDocumentsDrawerContent/CreateDocumentsDrawerContent.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/CreateDocumentsDrawer/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/CreateDocumentsHint/CreateDocumentsHint.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/GeneratedDocumentItem/GeneratedDocumentItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/GeneratedDocumentsList/GeneratedDocumentsList.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/MissingTagsList/MissingTagsList.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/SelectFormatsHint/SelectFormatsHint.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/Tabs/CreateDocumentsTab/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedControl/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedGroupList/FeedGroupList.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/ActivityBlock/ActivityBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/AttachmentsBlock/AttachmentsBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/AttachmentsBlock/AttachmentsMailBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/DateGroupHeaderLeftBlock/DateGroupHeaderLeftBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/FeedItem/FeedItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/FeedItemLeftBlock/FeedItemLeftBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/FeedItemShowMoreButton/FeedItemShowMoreButton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/HeaderControlsDropdown/HeaderControlsDropdown.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/InnerHTMLNormalizer/InnerHTMLNormalizer.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/Item/Item.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/ItemInfo/AuthorBlock/AuthorBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/ItemInfo/DateBlock/DateBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/ItemInfo/InfoBlock/InfoBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/MailFileFeedItem/MailFileFeedItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/ResultBlock/ResultBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/TextEditorControls/TextEditorControls.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/Common/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItem/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedItemList/FeedItemList.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedSkeleton/FeedBlockSkeleton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FeedSkeleton/FeedSkeleton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/FiltersBlock/FiltersBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/MailThreadBlock/MailThreadBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/MailThreadBlock/components/MailMessageBlock/MailMessageBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/MailThreadBlock/components/ReplyControls/ReplyControls.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/MailThreadBlock/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/NoteBlock/NoteBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/NoteBlock/components/NoteBlockTemplate/NoteBlockTemplate.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/NoteBlock/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/PlannerBody/PlannerBody.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/TaskBlock/TaskBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/TaskBlock/components/TaskItemBottom/TaskItemBottom.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/TaskBlock/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/TimePicker/TimePicker.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/Feed/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/FocusButton/FocusButton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/LinkedEntitiesBlock/LinkedEntitiesBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/LinkedEntitiesBlock/components/AddLinkedEntityButton/AddLinkedEntityButton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/LinkedEntitiesBlock/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/LinkedEntitiesHeaderLinks/LinkedEntitiesHeaderLinkItem.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/LinkedEntitiesHeaderLinks/LinkedEntitiesHeaderLinks.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/MessengerTag/MessengerTag.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/MiniCard/ExternalMiniCard.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/MiniCard/MiniCard.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/MiniCard/components/CardBlock/CardBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/MiniCard/components/SearchEntitiesBlock/SearchEntitiesBlock.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/MiniCard/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/OverviewComponent/OverviewComponent.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/RelocateCardButton/RelocateCardButton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/ScrollController/ScrollButton.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/ScrollController/ScrollController.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/WarningModals/FieldFormulaCircularDependencyWarningModal/FieldFormulaCircularDependencyWarningModal.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/WarningModals/FieldUsedInFormulaWarningModal/FieldUsedInFormulaWarningModal.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/WarningModals/MutationWarningModal/MutationWarningModal.tsx create mode 100644 frontend/src/modules/card/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/helpers/generateProductsSectionOrderTabValue.ts create mode 100644 frontend/src/modules/card/shared/lib/helpers/getCallItemIconAndStatus.tsx create mode 100644 frontend/src/modules/card/shared/lib/helpers/getCardPageTabsWithProductsSections.tsx create mode 100644 frontend/src/modules/card/shared/lib/helpers/getFeedItemColors.tsx create mode 100644 frontend/src/modules/card/shared/lib/helpers/getMessengerTagIcon.tsx create mode 100644 frontend/src/modules/card/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/helpers/isFeedItemOutlined.ts create mode 100644 frontend/src/modules/card/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/hooks/useFeedItemOutline.ts create mode 100644 frontend/src/modules/card/shared/lib/hooks/useGetChatTags.ts create mode 100644 frontend/src/modules/card/shared/lib/hooks/useGetProjectTaskBoardId.ts create mode 100644 frontend/src/modules/card/shared/lib/hooks/useGetUserWorkingTimeSlots.ts create mode 100644 frontend/src/modules/card/shared/lib/hooks/useRequisitesSuggestions.ts create mode 100644 frontend/src/modules/card/shared/lib/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/models/CallType.ts create mode 100644 frontend/src/modules/card/shared/lib/models/CardSavedEvent.ts create mode 100644 frontend/src/modules/card/shared/lib/models/CardTab.ts create mode 100644 frontend/src/modules/card/shared/lib/models/ChatTag.ts create mode 100644 frontend/src/modules/card/shared/lib/models/DocumentTemplateError.ts create mode 100644 frontend/src/modules/card/shared/lib/models/EditTextProps.ts create mode 100644 frontend/src/modules/card/shared/lib/models/FeedItemMeta.ts create mode 100644 frontend/src/modules/card/shared/lib/models/FeedItemType.ts create mode 100644 frontend/src/modules/card/shared/lib/models/MutationWarningCode.ts create mode 100644 frontend/src/modules/card/shared/lib/models/TaskStatus.ts create mode 100644 frontend/src/modules/card/shared/lib/models/TaskTimeStatusIcons.tsx create mode 100644 frontend/src/modules/card/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/card/shared/lib/models/orderIdQueryParam.ts create mode 100644 frontend/src/modules/card/shared/lib/types/FindChatHandler.ts create mode 100644 frontend/src/modules/card/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/card/store/CardFilesStore.ts create mode 100644 frontend/src/modules/card/store/CardStore.ts create mode 100644 frontend/src/modules/card/store/CreateDocumentStore.ts create mode 100644 frontend/src/modules/card/store/EntityTypeLinksStore.ts create mode 100644 frontend/src/modules/card/store/FeedStore.ts create mode 100644 frontend/src/modules/card/store/LinkedEntityStore.ts create mode 100644 frontend/src/modules/card/store/index.tsx create mode 100644 frontend/src/modules/fields/api/FieldHelperApi/FieldHelperApi.ts create mode 100644 frontend/src/modules/fields/api/FieldHelperApi/queries/useGetPhoneUserInfo.ts create mode 100644 frontend/src/modules/fields/api/FieldSettingsApi/FieldSettingsApi.ts create mode 100644 frontend/src/modules/fields/api/FieldSettingsApi/queries/useGetFieldSettings.ts create mode 100644 frontend/src/modules/fields/api/FieldsApiRoutes.ts create mode 100644 frontend/src/modules/fields/api/FieldsQueryKeys.ts create mode 100644 frontend/src/modules/fields/api/dtos/CheckFormulaDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/FieldDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/FieldGroupDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/FieldOptionDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/FieldSettings/FieldSettingsDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/FieldSettings/UpdateFieldSettingsDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/FieldValueDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/PhoneUserInfoDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/SimpleFieldValueDto.ts create mode 100644 frontend/src/modules/fields/api/dtos/index.tsx create mode 100644 frontend/src/modules/fields/api/index.tsx create mode 100644 frontend/src/modules/fields/context/CardFieldHelperContext/CardFieldHelperContext.ts create mode 100644 frontend/src/modules/fields/context/CardFieldHelperContext/index.tsx create mode 100644 frontend/src/modules/fields/context/CardFieldHelperContext/useCardFieldHelperContext.ts create mode 100644 frontend/src/modules/fields/context/DuplicatesContext/DuplicatesContext.ts create mode 100644 frontend/src/modules/fields/context/DuplicatesContext/index.tsx create mode 100644 frontend/src/modules/fields/context/DuplicatesContext/useDuplicatesContext.ts create mode 100644 frontend/src/modules/fields/context/MakeCallContext/MakeCallContext.ts create mode 100644 frontend/src/modules/fields/context/MakeCallContext/MakeCallProvider.tsx create mode 100644 frontend/src/modules/fields/context/MakeCallContext/index.tsx create mode 100644 frontend/src/modules/fields/context/MakeCallContext/useMakeCallContext.ts create mode 100644 frontend/src/modules/fields/context/SendEmailContext/SendEmailContext.ts create mode 100644 frontend/src/modules/fields/context/SendEmailContext/index.tsx create mode 100644 frontend/src/modules/fields/context/SendEmailContext/useSendEmailContext.ts create mode 100644 frontend/src/modules/fields/context/index.tsx create mode 100644 frontend/src/modules/fields/index.tsx create mode 100644 frontend/src/modules/fields/shared/assets/clear_entry.svg create mode 100644 frontend/src/modules/fields/shared/assets/edit_formula.svg create mode 100644 frontend/src/modules/fields/shared/assets/edit_text.svg create mode 100644 frontend/src/modules/fields/shared/assets/field_filled.svg create mode 100644 frontend/src/modules/fields/shared/assets/important_field.svg create mode 100644 frontend/src/modules/fields/shared/assets/index.tsx create mode 100644 frontend/src/modules/fields/shared/assets/link.svg create mode 100644 frontend/src/modules/fields/shared/assets/list_select.svg create mode 100644 frontend/src/modules/fields/shared/assets/mandatory_field.svg create mode 100644 frontend/src/modules/fields/shared/assets/phone_call.svg create mode 100644 frontend/src/modules/fields/shared/assets/send_email.svg create mode 100644 frontend/src/modules/fields/shared/assets/telegram.svg create mode 100644 frontend/src/modules/fields/shared/assets/tune_field.svg create mode 100644 frontend/src/modules/fields/shared/assets/whats_app.svg create mode 100644 frontend/src/modules/fields/shared/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/EditFields.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/AnalyticsSubgroupControls/AnalyticsSubgroupControls.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/EditFieldFormGroupComponent/EditFieldFormGroupComponent.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/EditFieldsTab/EditFieldsTab.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/FieldFormulaSettingsButton.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/components/FieldFormulaSettingsModal/FieldFormulaSettingsModal.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/components/FieldFormulaSettingsModalFields/FieldFormulaSettingsModalFields.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/components/FieldFormulaSettingsModalKeys/FieldFormulaSettingsModalKeys.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/components/FormulaKeyButton/FormulaKeyButton.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/components/FormulaKeysButtons/FormulaKeysButtons.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldFormulaSettingsButton/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldSettingsModal/FieldSettingsModal.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldSettingsModal/components/FieldSettingsGroup/FieldSettingsGroup.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldSettingsModal/components/FieldSettingsSubGroup/FieldSettingsSubGroup.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/FieldSettingsModal/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/OptionBlock/OptionBlock.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/RequisitesSubgroupControls/RequisitesSubgroupControls.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/SelectOptions/SelectOptions.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/EditFields/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldLinkWrapper/FieldLinkWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldTextInput/FieldTextInput.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldTextPrimitive/FieldTextPrimitive.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldIndicator.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldSelectWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/CheckedMultiselectFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ColoredMultiselectFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ColoredSelectFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/DateFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/FileFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/FormulaFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/LinkFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultiselectFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/ChecklistFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/ChecklistFieldValueCompItem.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/EmailFieldValueComp/EmailFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/EmailFieldValueComp/components/EmailFieldInput/EmailFieldInput.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/EmailFieldValueComp/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/MultitextFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/PhoneFieldValueComp/PhoneFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/PhoneFieldValueComp/components/PhoneFieldValueCompItem/PhoneFieldValueCompItem.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/PhoneFieldValueComp/components/TelegramLinkWrapper/TelegramLinkWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/PhoneFieldValueComp/components/TimezoneWrapper/TimezoneWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/PhoneFieldValueComp/components/WhatsAppLinkWrapper/WhatsAppLinkWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/PhoneFieldValueComp/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/components/MultitextFieldValueCompTemplate/MultitextFieldValueCompTemplate.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/components/MultitextFieldsWrapper/MultitextFieldsWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/components/MultitextInputWrapper/MultitextInputWrapper.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/MultitextFieldValueComps/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/NumberFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ParticipantFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ParticipantsFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ProjectFields/ProjectFieldsBlock/ProjectFieldsBlock.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ProjectFields/ProjectFieldsSettingsComponent/ProjectFieldsSettingsComponent.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/ProjectFields/ProjectSystemFieldValueFormGroup/ProjectSystemFieldValueFormGroup.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/RichTextFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/SelectFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/SwitchFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueComps/TextFieldValueComp.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueFormGroup.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueSwitch.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/FieldValue/FieldValueTemplate.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/OrganizationAutocompleteField/OrganizationAutocompleteField.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/OrganizationAutocompleteField/README.md create mode 100644 frontend/src/modules/fields/shared/lib/components/PhoneFieldControls/ConditionalPopover.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/PhoneFieldControls/PhoneFieldControls.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/RequisitesSuggestions/RequisitesSuggestions.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/ShowFields/ShowFields.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/ShowFields/components/MoreTabsDropdown/MoreTabsDropdown.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/ShowFields/components/ShowFieldsTab/ShowFieldsTab.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/ShowFields/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/ShowFiles/ShowFiles.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/TimezoneHint/TimezoneHint.tsx create mode 100644 frontend/src/modules/fields/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/helpers/FormulaField/generateFormulaFieldCode.ts create mode 100644 frontend/src/modules/fields/shared/lib/helpers/FormulaField/getFormulaFieldKeys.tsx create mode 100644 frontend/src/modules/fields/shared/lib/helpers/PhoneField/generateTelegramExternalLink.ts create mode 100644 frontend/src/modules/fields/shared/lib/helpers/PhoneField/generateWhatsAppExternalLink.ts create mode 100644 frontend/src/modules/fields/shared/lib/helpers/PhoneField/sanitizeAndModifyPhoneNumber.ts create mode 100644 frontend/src/modules/fields/shared/lib/helpers/getFieldTypes.ts create mode 100644 frontend/src/modules/fields/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/hooks/PhoneField/useGetCreateExternalChatHandler.ts create mode 100644 frontend/src/modules/fields/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/models/AnalyticsFieldSubgroup.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/Field/Field.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/Field/FieldCode.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/Field/FieldForm.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/Field/projectFieldsCodes.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldGroup/FieldGroup.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldGroup/FieldGroupForm.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldGroupCode.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldGroupsErrorCode.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldOption/FieldOption.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldSettings/FieldSettings.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldSpecialTab.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/ChecklistFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/ColoredMultiselectFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/ColoredSelectFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/DateFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/FieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/FieldValueBaseProps.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/FileFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/FormulaFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/LinkFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/MultiselectFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/MultitextFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/NumberFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/ParticipantFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/ParticipantsFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/SelectFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/SwitchFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldValue/TextFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FieldsStoreErrorCode.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FormulaField/FormulaFieldClasses.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FormulaField/FormulaFieldMathElement.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FormulaField/FormulaFieldMathElementType.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FormulaField/FormulaFieldMathElementValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/FormulaField/FormulaKey.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/PhoneUserInfo.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/ProjectFieldsSettings.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/RequisitesFieldSubgroup.ts create mode 100644 frontend/src/modules/fields/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/types/ArrayValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/ChecklistFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/ColoredMultiselectFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/ColoredSelectFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/DateFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/FileFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/FormulaFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/LinkFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/MultiselectFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/MultitextFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/NumberFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/ParticipantFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/ParticipantsFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/PossiblePrimitiveFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/SelectFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/SwitchFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FieldValue/TextFieldValuePrimitive.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/FormulaField/AppendMathElementHandler.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/MultiOptionsValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/PhoneField/GenerateExternalChatLinkHandler.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/PossibleFieldValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/PrimitiveValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/SingleOptionValue.ts create mode 100644 frontend/src/modules/fields/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/fields/shared/lib/utils/FieldUtil.ts create mode 100644 frontend/src/modules/fields/shared/lib/utils/index.tsx create mode 100644 frontend/src/modules/fields/store/FieldFormulaStore.ts create mode 100644 frontend/src/modules/fields/store/FieldGroupsStore.ts create mode 100644 frontend/src/modules/fields/store/FieldOptionStore.ts create mode 100644 frontend/src/modules/fields/store/FieldSettings/FieldSettingsForm.ts create mode 100644 frontend/src/modules/fields/store/FieldSettings/FieldSettingsStore.ts create mode 100644 frontend/src/modules/fields/store/FieldValuesStore.ts create mode 100644 frontend/src/modules/fields/store/FieldsStore.ts create mode 100644 frontend/src/modules/fields/store/index.tsx create mode 100644 frontend/src/modules/gantt/context/GanttContext/GanttContext.ts create mode 100644 frontend/src/modules/gantt/context/GanttContext/useGanttContext.ts create mode 100644 frontend/src/modules/gantt/context/index.tsx create mode 100644 frontend/src/modules/gantt/index.tsx create mode 100644 frontend/src/modules/gantt/shared/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/DragAreaIndicator/DragAreaIndicator.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/DragResizeManager/DragResizeManager.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttBarLinkMenu/GanttBarLinkMenu.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttBody/GanttBody.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttChart/GanttChart.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableBody/TableBody.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableBody/components/EmptyTableBody/EmptyTableBody.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableBody/components/TableRows/TableRows.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableBody/components/TableRows/components/TableCell/TableCell.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableBody/components/TableRows/components/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableBody/components/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttTable/TableHeader/TableHeader.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttViewComponent/GanttViewComponent.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/GanttViewSelect/GanttViewSelect.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarList/RecordsBarList.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarList/components/MinimizedRecordBar/MinimizedRecordBar.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarList/components/RecordBar/RecordBar.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarList/components/RecordGroupBar/RecordGroupBar.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarList/components/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarsThumbsList/RecordsBarsThumbsList.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarsThumbsList/components/RecordBarThumb/RecordBarThumb.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsBarsThumbsList/components/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RecordsContentDivider/RecordsContentDivider.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/RowSelectionIndicator/RowSelectionIndicator.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/TasksDependencies/TasksDependencies.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/TasksDependencies/components/TaskDependenceArrow/TaskDependenceArrow.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/TasksDependencies/components/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/TimeAxis/TimeAxis/TimeAxis.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/TimeAxis/TimeAxisTodayIndicator/TimeAxisTodayIndicator.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/TimeAxis/TimeAxisTodayLine/TimeAxisTodayLine.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/helpers/generateTimelineRoute.ts create mode 100644 frontend/src/modules/gantt/shared/lib/helpers/getGanttViewConfigs.ts create mode 100644 frontend/src/modules/gantt/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/models/Bar.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/BarClickEvent.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/Dependence.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/GanttItem.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/GanttRecord.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/GanttViewConfig.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/GanttViewValues.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/Major.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/MajorAmp.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/Minor.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/MinorAmp.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/TableResizeEvent.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/constants.ts create mode 100644 frontend/src/modules/gantt/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/services/GanttStorageService.ts create mode 100644 frontend/src/modules/gantt/shared/lib/services/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/types/DependenceType.ts create mode 100644 frontend/src/modules/gantt/shared/lib/types/GanttView.ts create mode 100644 frontend/src/modules/gantt/shared/lib/types/MoveType.ts create mode 100644 frontend/src/modules/gantt/shared/lib/types/TimelineRouteGenerator.ts create mode 100644 frontend/src/modules/gantt/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/gantt/shared/lib/utils/GanttAxisUtil.ts create mode 100644 frontend/src/modules/gantt/shared/lib/utils/GanttUtil.ts create mode 100644 frontend/src/modules/gantt/shared/lib/utils/index.tsx create mode 100644 frontend/src/modules/gantt/store/AutoScroller.ts create mode 100644 frontend/src/modules/gantt/store/GanttStore.ts create mode 100644 frontend/src/modules/gantt/store/index.tsx create mode 100644 frontend/src/modules/mailing/api/MailMessageApi/MailMessageApi.ts create mode 100644 frontend/src/modules/mailing/api/MailboxApi/MailboxApi.ts create mode 100644 frontend/src/modules/mailing/api/MailboxSettingsApi/MailboxSettingsApi.ts create mode 100644 frontend/src/modules/mailing/api/MailboxSignatureSettingsApi/MailboxSignatureSettingsApi.ts create mode 100644 frontend/src/modules/mailing/api/MailboxSignatureSettingsApi/helpers/invalidateMailboxesSignaturesInCache.ts create mode 100644 frontend/src/modules/mailing/api/MailboxSignatureSettingsApi/queries/useGetMailboxesSignatures.ts create mode 100644 frontend/src/modules/mailing/api/MailingApiRoutes.ts create mode 100644 frontend/src/modules/mailing/api/MailingQueryKeys.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailMessage/MailMessageDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailMessage/MailMessagePayloadDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailMessage/SendMailMessageDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailThread/MailThreadInfoDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/Mailbox/CreateMailboxDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/Mailbox/MailboxDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/Mailbox/MailboxSettingsManualDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/Mailbox/UpdateMailboxDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/Mailbox/UpdateMailboxSettingsManualDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxInfo/MailboxFolderInfoDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxInfo/MailboxFullInfoDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxInfo/MailboxMessageInfoDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxInfo/MailboxSectionInfoDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxInfo/MailboxShortInfoDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxSignature/CreateMailboxSignatureDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxSignature/MailboxSignatureDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/MailboxSignature/UpdateMailboxSignatureDto.ts create mode 100644 frontend/src/modules/mailing/api/dtos/index.tsx create mode 100644 frontend/src/modules/mailing/api/index.tsx create mode 100644 frontend/src/modules/mailing/index.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/MailingPage.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/AttachmentItem/AttachmentItem.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/AttachmentList/AttachmentList.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/AttachmentsBlock/AttachmentsBlock.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/ClipIcon/ClipIcon.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/CollapsibleList/CollapsibleList.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/CollapsibleListItem/CollapsibleListItem.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/CreateMessageButton/CreateMessageButton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/EmptyMessagePlug/EmptyMessagePlug.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Mailbox/Mailbox.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/MessageControlButton/MessageControlButton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/MessageItem/MessageItem.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/MessagePanel/MessagePanel.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/NoMailboxesPanelBlock/NoMailboxesPanelBlock.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/ReplyControls/ReplyControls.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/SearchBlock/SearchBlock.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Section/Section.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Sidebar/Sidebar.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Sidebar/components/SidebarDelimiter/SidebarDelimiter.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Sidebar/components/index.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Skeletons/MessagePanelSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Skeletons/SidebarSectionSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Skeletons/SidebarSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/Thread/Thread.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/ThreadMessage/ThreadMessage.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingPage/components/index.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/MailingSettingsPage.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Buttons/AddButton/AddButton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Buttons/ControlButton/ControlButton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Caption/Caption.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/MailboxItem/MailboxItem.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/MailboxList/MailboxList.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/MailboxListSkeleton/MailboxListSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/DeleteMailboxModal/DeleteMailboxModal.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxAddressModal/MailboxAddressModal.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxProviderModal/MailboxProviderModal.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxSignatureModal/EditorApproveButtonSkeleton/EditorApproveButtonSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxSignatureModal/MailboxSignatureEditor/MailboxSignatureEditor.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxSignatureModal/MailboxSignatureEditorSkeleton/MailboxSignatureEditorSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxSignatureModal/MailboxSignatureModal.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxSignatureModal/MailboxSignatureModal/MailboxSignatureSidebar.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/MailboxSignatureModal/MailboxSignaturesSidebarSkeleton/MailboxSignaturesSidebarSkeleton.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/ModalTitle/ModalTitle.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/Modals/UpdateMailboxModal/UpdateMailboxModal.tsx create mode 100644 frontend/src/modules/mailing/pages/MailingSettingsPage/components/index.tsx create mode 100644 frontend/src/modules/mailing/pages/index.tsx create mode 100644 frontend/src/modules/mailing/shared/assets/arrow.svg create mode 100644 frontend/src/modules/mailing/shared/assets/calendar_add.svg create mode 100644 frontend/src/modules/mailing/shared/assets/clip_medium.svg create mode 100644 frontend/src/modules/mailing/shared/assets/clip_small.svg create mode 100644 frontend/src/modules/mailing/shared/assets/close.svg create mode 100644 frontend/src/modules/mailing/shared/assets/contact_add.svg create mode 100644 frontend/src/modules/mailing/shared/assets/cross.svg create mode 100644 frontend/src/modules/mailing/shared/assets/double_arrow.svg create mode 100644 frontend/src/modules/mailing/shared/assets/draft.svg create mode 100644 frontend/src/modules/mailing/shared/assets/error.svg create mode 100644 frontend/src/modules/mailing/shared/assets/forward.svg create mode 100644 frontend/src/modules/mailing/shared/assets/gmail.svg create mode 100644 frontend/src/modules/mailing/shared/assets/google.svg create mode 100644 frontend/src/modules/mailing/shared/assets/inbox.svg create mode 100644 frontend/src/modules/mailing/shared/assets/inboxes.svg create mode 100644 frontend/src/modules/mailing/shared/assets/index.tsx create mode 100644 frontend/src/modules/mailing/shared/assets/mail.svg create mode 100644 frontend/src/modules/mailing/shared/assets/message.svg create mode 100644 frontend/src/modules/mailing/shared/assets/no_email_yet.svg create mode 100644 frontend/src/modules/mailing/shared/assets/opened_message.svg create mode 100644 frontend/src/modules/mailing/shared/assets/reconnect.svg create mode 100644 frontend/src/modules/mailing/shared/assets/reply.svg create mode 100644 frontend/src/modules/mailing/shared/assets/sent.svg create mode 100644 frontend/src/modules/mailing/shared/assets/spam.svg create mode 100644 frontend/src/modules/mailing/shared/assets/spam_header.svg create mode 100644 frontend/src/modules/mailing/shared/assets/text_format.svg create mode 100644 frontend/src/modules/mailing/shared/assets/trash.svg create mode 100644 frontend/src/modules/mailing/shared/assets/trash_header.svg create mode 100644 frontend/src/modules/mailing/shared/assets/unfold.svg create mode 100644 frontend/src/modules/mailing/shared/assets/unread.svg create mode 100644 frontend/src/modules/mailing/shared/assets/unspam.svg create mode 100644 frontend/src/modules/mailing/shared/assets/untrash.svg create mode 100644 frontend/src/modules/mailing/shared/index.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/SendEmailModal.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/AddressMultiInput/AddressMultiInput.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/ChangesNotSavedModal/ChangesNotSavedModal.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Editor/EditorContent.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Editor/EmailSignatureEditor.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Editor/EmailTextEditor.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Input/InputWrapper.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Input/PseudoInputLabel.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Input/PseudoInputLabelWrapper.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/Input/PseudoInputWrapper.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/InvalidEmailAddressModal/InvalidEmailAddressModal.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/MultiAddressMailField/MultiAddressMailField.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/SendEmailSettingsDropdown/SendEmailSettingsDropdown.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/TextFormatButton/TextFormatButton.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/SendEmailModal/components/index.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/downloadFileFromMessage.ts create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/getDemoMailboxes.ts create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/getDemoMessages.ts create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/getDemoSections.ts create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/getIconByFolderType.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/getMessageContentToDisplay.ts create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/getMockMailMessageInfos.ts create mode 100644 frontend/src/modules/mailing/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/hooks/useMessageControls.ts create mode 100644 frontend/src/modules/mailing/shared/lib/index.tsx create mode 100644 frontend/src/modules/mailing/shared/lib/models/MailMessage/MailMessage.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/MailMessage/MailMessageInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/MailMessage/MailMessagePayload.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/Mailbox.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxFolderInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxFolderType.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxFullInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxProvider.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxSectionInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxSettingsManual.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxShortInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxSignature.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxState.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailbox/MailboxesInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailing/MailThreadInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/Mailing/MailingPageSettings.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/MessageHeader/MessageHeaderInfo.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/MessageHeader/MessageHeaderObject.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/SendEmail/SendEmailModalSettings.ts create mode 100644 frontend/src/modules/mailing/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/mailing/store/MailMessageBlockStore.ts create mode 100644 frontend/src/modules/mailing/store/MailMessageStore.ts create mode 100644 frontend/src/modules/mailing/store/MailboxSettingsStore.ts create mode 100644 frontend/src/modules/mailing/store/MailboxSignatureSettingsStore.ts create mode 100644 frontend/src/modules/mailing/store/MailboxStore.ts create mode 100644 frontend/src/modules/mailing/store/SidebarStore.ts create mode 100644 frontend/src/modules/mailing/store/index.tsx create mode 100644 frontend/src/modules/multichat/api/ChatApi/ChatApi.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/helpers/clearChatEntityInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/helpers/deleteChatInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/helpers/getChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/helpers/refetchChats.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/helpers/updateChatLastMessageInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/helpers/upsertChatInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/index.tsx create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useCreateContactFromChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useCreateGroupChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useCreatePersonalChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useDeleteChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useFindFullChats.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useFindFullChatsByMessageContent.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useFindFullPersonalChats.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useGetChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useGetChatExists.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useGetChats.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useMarkAllChatMessagesAsRead.ts create mode 100644 frontend/src/modules/multichat/api/ChatApi/queries/useUpdateGroupChat.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/ChatMessageApi.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/helpers/addChatMessageToCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/helpers/deleteChatMessageInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/helpers/deleteChatMessagesCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/helpers/getChatMessage.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/helpers/updateChatMessageStatus.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/helpers/updateChatMessagesInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useDeleteChatMessage.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useGetChatMessages.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useReactToChatMessage.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useSendChatMessage.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useUnreactToChatMessage.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useUpdateChatMessage.ts create mode 100644 frontend/src/modules/multichat/api/ChatMessageApi/queries/useUpdateChatMessagesStatus.ts create mode 100644 frontend/src/modules/multichat/api/ChatProviderApi/ChatProviderApi.ts create mode 100644 frontend/src/modules/multichat/api/ChatProviderApi/helpers/addToProviderUnseenCount.ts create mode 100644 frontend/src/modules/multichat/api/ChatProviderApi/helpers/invalidateChatProvidersInCache.ts create mode 100644 frontend/src/modules/multichat/api/ChatProviderApi/queries/useGetChatProviders.ts create mode 100644 frontend/src/modules/multichat/api/ChatProviderApi/queries/useGetExternalChatProviders.ts create mode 100644 frontend/src/modules/multichat/api/FbMessengerProviderSettingsApi/FbMessengerProviderSettingsApi.ts create mode 100644 frontend/src/modules/multichat/api/MultichatApi/MultichatApi.ts create mode 100644 frontend/src/modules/multichat/api/MultichatApi/helpers/addToTotalUnseenCount.ts create mode 100644 frontend/src/modules/multichat/api/MultichatApi/queries/useGetMultichatUnseenCount.ts create mode 100644 frontend/src/modules/multichat/api/MultichatApiRoutes.ts create mode 100644 frontend/src/modules/multichat/api/MultichatQueryKeys.ts create mode 100644 frontend/src/modules/multichat/api/TwilioProviderSettingsApi/TwilioProviderSettingsApi.ts create mode 100644 frontend/src/modules/multichat/api/WazzupProviderSettingsApi/WazzupProviderSettingsApi.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/ChatDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/ChatFindByMessageContentFilterDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/ChatFindFilterDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/ChatFindPersonalFilterDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/CreateExternalChatDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/CreateGroupChatDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/CreatePersonalChatDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/FindChatsFullResultDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Chat/UpdateGroupChatDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/ChatMessageDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/ChatMessageFileDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/ChatMessageReactionDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/ChatMessageUserStatusDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/ChatMessagesResultDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/SendChatMessageDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatMessage/UpdateChatMessageDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatProvider/ChatProviderDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatUser/ChatUserDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/ChatUser/ChatUserExternalDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/FbMessenger/MessengerProviderSettingsDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/FbMessenger/UpdateMessengerProviderDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Twilio/CreateTwilioProviderDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Twilio/TwilioProviderSettingsDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Twilio/UpdateTwilioProviderSettings.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Wazzup/CreateWazzupProviderDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Wazzup/UpdateWazzupProviderDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Wazzup/WazzupChanelDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/Wazzup/WazzupProviderDto.ts create mode 100644 frontend/src/modules/multichat/api/dtos/index.tsx create mode 100644 frontend/src/modules/multichat/api/index.tsx create mode 100644 frontend/src/modules/multichat/context/MultichatContext.ts create mode 100644 frontend/src/modules/multichat/context/MultichatProvider.tsx create mode 100644 frontend/src/modules/multichat/context/index.tsx create mode 100644 frontend/src/modules/multichat/context/useMultichatContext.ts create mode 100644 frontend/src/modules/multichat/events/ChatEventHandler.ts create mode 100644 frontend/src/modules/multichat/events/index.tsx create mode 100644 frontend/src/modules/multichat/index.tsx create mode 100644 frontend/src/modules/multichat/pages/MultichatPage/MultichatPage.tsx create mode 100644 frontend/src/modules/multichat/pages/index.tsx create mode 100644 frontend/src/modules/multichat/shared/assets/document.svg create mode 100644 frontend/src/modules/multichat/shared/assets/download.svg create mode 100644 frontend/src/modules/multichat/shared/assets/facebook_messenger_provider_indicator.svg create mode 100644 frontend/src/modules/multichat/shared/assets/fullscreen.svg create mode 100644 frontend/src/modules/multichat/shared/assets/image.svg create mode 100644 frontend/src/modules/multichat/shared/assets/index.tsx create mode 100644 frontend/src/modules/multichat/shared/assets/more.svg create mode 100644 frontend/src/modules/multichat/shared/assets/multichat_button.svg create mode 100644 frontend/src/modules/multichat/shared/assets/read.svg create mode 100644 frontend/src/modules/multichat/shared/assets/read_chat_message.svg create mode 100644 frontend/src/modules/multichat/shared/assets/reply.svg create mode 100644 frontend/src/modules/multichat/shared/assets/send.svg create mode 100644 frontend/src/modules/multichat/shared/assets/unread.svg create mode 100644 frontend/src/modules/multichat/shared/assets/unread_chat_message.svg create mode 100644 frontend/src/modules/multichat/shared/assets/whatsapp_provider_indicator.svg create mode 100644 frontend/src/modules/multichat/shared/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatButton/MultichatButton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/MultichatControl.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatControlsMenu/ChatControlsMenu.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageContextMenu/ChatMessageContextMenu.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageFileBlock/ChatMessageFileBlock.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageFileList/ChatMessageFileList.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageItem/ChatMessageItem.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageReaction/ChatMessageReaction.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageReactionsList/ChatMessageReactionsList.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageReactionsPanel/ChatMessageReactionsPanel.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessageReplyBlock/ChatMessageReplyBlock.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessagesList/ChatMessagesList.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessagesPanel/ChatMessagesPanel.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/ChatMessagesPanelHeader/ChatMessagesPanelHeader.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/CreateChatContactButton/CreateChatContactButton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/SendChatMessageBlock/SendChatMessageBlock.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/SendChatMessageTextarea/SendChatMessageTextarea.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/SendChatMessageWithFilesModal/SendChatMessageWithFilesModal.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/Skeletons/ChatMessageItemSkeleton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/Skeletons/ChatMessagesListSkeleton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatMessagesPanel/Skeletons/MessagesPanelHeaderSkeleton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsHeaderProvidersTab/ChatHeaderProviderTab.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsHeaderProvidersTabs/ChatsHeaderProvidersTabs.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsHeaderSearchBlock/ChatsHeaderSearchBlock.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsPanel/ChatsPanel.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsPanelBlock/ChatsPanelBlock.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsSidebar/ChatsSidebar.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsSidebarHeader/ChatsSidebarHeader.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsSmallPanelBlock/ChatsSmallPanelBlock.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ChatsSmallProvidersTab/ChatsSmallProvidersTab.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/CreateAmworkChatButton/CreateAmworkChatButton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/CreateAmworkChatButton/components/AddAvatarAndNameModal/AddAvatarAndNameModal.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/CreateAmworkChatButton/components/SelectAmworkContactsModal/SelectAmworkContactsModal.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/CreateAmworkChatButton/components/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/FoundChatsPanel/FoundChatsPanel.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/FoundChatsPanel/components/FoundChatsGroup/FoundChatsGroup.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/FoundChatsPanel/components/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/PanelBlockHeaderLastMessageInfo/PanelBlockHeaderLastMessageInfo.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/ProviderIcon/ProviderIcon.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/Skeletons/ChatsHeaderProvidersTabSkeleton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/Skeletons/ChatsPanelSkeleton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/UnseenCountTag/UnseenCountTag.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/ChatsSidebar/UnseenCountTagSmallView/UnseenCountTagSmallView.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/MultichatControlHeader/MultichatControlHeader.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/NoSelectedChatPlug/NoSelectedChatPlug.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/StyledResizeHandler/StyledResizeHandler.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatControl/components/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatMobileControl/MultichatMobileControl.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/MultichatModal/MultichatModal.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/findChatWithProviderTransport.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/generateRandomWidthInRange.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/getChatLastMessageSnippet.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/getDefaultModalBounds.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/getLastMessageTimeFormatted.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/getMessagesGroups.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/getNumberFromPx.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/getReplyBlockMessageSnippet.ts create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/parseChatMessageText.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/renderChatButton.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/helpers/renderProviderIndicatorIcon.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/hooks/useGetChatsViewInfo.ts create mode 100644 frontend/src/modules/multichat/shared/lib/index.tsx create mode 100644 frontend/src/modules/multichat/shared/lib/models/Chat/Chat.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Chat/ChatType.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Chat/FIndChatsFullResult.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessage.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessageFile.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessageGroupedReaction.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessageReaction.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessageStatus.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessageUserStatus.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/ChatMessagesResult.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatMessage/MessagesGroup.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatProvider/ChatProvider.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatProvider/ChatProviderStatus.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatProvider/ChatProviderTransport.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatProvider/ChatProviderType.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatUser/ChatUser.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatUser/ChatUserExternal.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/ChatUser/ChatUserRole.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Events/ChatEvent.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Events/ChatMessageCreatedEvent.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Events/ChatMessageEvent.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Events/ChatMessageUpdatedEvent.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Events/MultichatEvents.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/FbMessenger/MessengerProviderSettings.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Twilio/TwilioProviderSettings.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Wazzup/WazzupChanel.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Wazzup/WazzupChanelState.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Wazzup/WazzupProvider.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/Wazzup/WazzupTransport.ts create mode 100644 frontend/src/modules/multichat/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/notes/api/NoteApi/NoteApi.ts create mode 100644 frontend/src/modules/notes/api/index.tsx create mode 100644 frontend/src/modules/notes/index.tsx create mode 100644 frontend/src/modules/notes/pages/NotesPage/NotesPage.tsx create mode 100644 frontend/src/modules/notes/pages/index.tsx create mode 100644 frontend/src/modules/notes/shared/assets/bold.svg create mode 100644 frontend/src/modules/notes/shared/assets/bullet_list.svg create mode 100644 frontend/src/modules/notes/shared/assets/close.svg create mode 100644 frontend/src/modules/notes/shared/assets/delete.svg create mode 100644 frontend/src/modules/notes/shared/assets/duplicate.svg create mode 100644 frontend/src/modules/notes/shared/assets/expand.svg create mode 100644 frontend/src/modules/notes/shared/assets/folder.svg create mode 100644 frontend/src/modules/notes/shared/assets/h1.svg create mode 100644 frontend/src/modules/notes/shared/assets/h2.svg create mode 100644 frontend/src/modules/notes/shared/assets/h3.svg create mode 100644 frontend/src/modules/notes/shared/assets/index.tsx create mode 100644 frontend/src/modules/notes/shared/assets/insert.svg create mode 100644 frontend/src/modules/notes/shared/assets/italic.svg create mode 100644 frontend/src/modules/notes/shared/assets/minimize.svg create mode 100644 frontend/src/modules/notes/shared/assets/module_icon.svg create mode 100644 frontend/src/modules/notes/shared/assets/more.svg create mode 100644 frontend/src/modules/notes/shared/assets/ordered_list.svg create mode 100644 frontend/src/modules/notes/shared/assets/pin.svg create mode 100644 frontend/src/modules/notes/shared/assets/pin_filled.svg create mode 100644 frontend/src/modules/notes/shared/assets/redo.svg create mode 100644 frontend/src/modules/notes/shared/assets/remove_quick_property.svg create mode 100644 frontend/src/modules/notes/shared/assets/strikethrough.svg create mode 100644 frontend/src/modules/notes/shared/assets/text.svg create mode 100644 frontend/src/modules/notes/shared/assets/underlined.svg create mode 100644 frontend/src/modules/notes/shared/assets/undo.svg create mode 100644 frontend/src/modules/notes/shared/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteEditor/NoteEditor.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteEditor/components/EditorToolbar/EditorToolbar.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteEditor/components/TopNoteInfo/TopNoteInfo.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteEditor/components/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteFolderSelector/NoteFolderSelector.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteFolderSelector/components/FolderBlock/FolderBlock.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteFolderSelector/components/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteSelector/NoteSelector.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteSelector/components/NoteBlock/NoteBlock.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteSelector/components/NoteGroup/NoteGroup.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NoteSelector/components/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NotesActionDropdown/NotesActionDropdown.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NotesModal/NotesModal.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/NotesPageHeader/NotesPageHeader.tsx create mode 100644 frontend/src/modules/notes/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/helpers/formatNoteDate.ts create mode 100644 frontend/src/modules/notes/shared/lib/helpers/getNoteHeading.ts create mode 100644 frontend/src/modules/notes/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/helpers/shortenQuickNoteTitle.ts create mode 100644 frontend/src/modules/notes/shared/lib/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/models/Note/Note.ts create mode 100644 frontend/src/modules/notes/shared/lib/models/Note/StartNote.ts create mode 100644 frontend/src/modules/notes/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/notes/shared/lib/types/FormattingType.ts create mode 100644 frontend/src/modules/notes/shared/lib/types/GroupedNotes.ts create mode 100644 frontend/src/modules/notes/shared/lib/types/NoteFolders.ts create mode 100644 frontend/src/modules/notes/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/notes/store/NoteStore.ts create mode 100644 frontend/src/modules/notes/store/index.tsx create mode 100644 frontend/src/modules/notifications/api/NotificationsApi/NotificationsApi.ts create mode 100644 frontend/src/modules/notifications/api/NotificationsApi/index.tsx create mode 100644 frontend/src/modules/notifications/api/NotificationsApiRoutes.ts create mode 100644 frontend/src/modules/notifications/api/NotificationsSettingsApi/NotificationsSettingsApi.ts create mode 100644 frontend/src/modules/notifications/api/NotificationsSettingsApi/index.tsx create mode 100644 frontend/src/modules/notifications/api/dtos/NotificationDto.ts create mode 100644 frontend/src/modules/notifications/api/dtos/NotificationSettingsDto.ts create mode 100644 frontend/src/modules/notifications/api/dtos/NotificationTypeSettingsDto.ts create mode 100644 frontend/src/modules/notifications/api/dtos/NotificationsMeta.ts create mode 100644 frontend/src/modules/notifications/api/dtos/NotificationsResult.ts create mode 100644 frontend/src/modules/notifications/api/dtos/index.tsx create mode 100644 frontend/src/modules/notifications/api/index.tsx create mode 100644 frontend/src/modules/notifications/index.tsx create mode 100644 frontend/src/modules/notifications/shared/assets/after.svg create mode 100644 frontend/src/modules/notifications/shared/assets/bell.svg create mode 100644 frontend/src/modules/notifications/shared/assets/close.svg create mode 100644 frontend/src/modules/notifications/shared/assets/cog.svg create mode 100644 frontend/src/modules/notifications/shared/assets/index.tsx create mode 100644 frontend/src/modules/notifications/shared/assets/overdue.svg create mode 100644 frontend/src/modules/notifications/shared/assets/read_all.svg create mode 100644 frontend/src/modules/notifications/shared/assets/sound.svg create mode 100644 frontend/src/modules/notifications/shared/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsButton/NotificationsButton.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/NotificationsPanelDrawer.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/BlockHeaderAnnotation/BlockHeaderAnnotation.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/ChatNotificationBlock/ChatNotificationBlock.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/CloseToastButton/CloseToastButton.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationBlock/NotificationBlock.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationBlockSkeleton/NotificationBlockSkeleton.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationSettings/NotificationSettings.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationSettingsModal/NotificationSettingsModal.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationSettingsModal/components/NotificationModalDelimiter/NotificationModalDelimiter.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationSettingsModal/components/NotificationsTypeBlock/NotificationTypeBlock.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationSettingsModal/components/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/NotificationsSettingsModalSkeleton/NotificationsSettingsModalSkeleton.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/PanelHeader/PanelHeader.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/ReadAllButton/ReadAllButton.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/SoundButton/SoundButton.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/NotificationsPanelDrawer/components/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/helpers/getNotificationDateFormat.ts create mode 100644 frontend/src/modules/notifications/shared/lib/helpers/getSettingTitleByType.ts create mode 100644 frontend/src/modules/notifications/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/hooks/useGetNotificationsDelayOptions.ts create mode 100644 frontend/src/modules/notifications/shared/lib/index.tsx create mode 100644 frontend/src/modules/notifications/shared/lib/models/Notification/Notification.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/Notification/NotificationLocalSettings.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/Notification/NotificationSettings.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/NotificationType/NotificationType.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/NotificationType/NotificationTypeColor.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/NotificationType/NotificationTypeSettings.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/NotificationsSettingsLsKey.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/ToastClassName.ts create mode 100644 frontend/src/modules/notifications/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/notifications/store/NotificationsSettingsStore.ts create mode 100644 frontend/src/modules/notifications/store/NotificationsStore.ts create mode 100644 frontend/src/modules/notifications/store/ToastNotificationsStore.tsx create mode 100644 frontend/src/modules/notifications/store/index.tsx create mode 100644 frontend/src/modules/partner/api/PartnerApi/PartnerApi.tsx create mode 100644 frontend/src/modules/partner/api/PartnerApiRoutes.ts create mode 100644 frontend/src/modules/partner/api/dtos/PartnerLeadDto.ts create mode 100644 frontend/src/modules/partner/api/dtos/PartnerSummaryDto.ts create mode 100644 frontend/src/modules/partner/api/dtos/index.tsx create mode 100644 frontend/src/modules/partner/api/index.tsx create mode 100644 frontend/src/modules/partner/index.tsx create mode 100644 frontend/src/modules/partner/pages/PartnerInfoPage/PartnerInfoPage.tsx create mode 100644 frontend/src/modules/partner/pages/PartnerInfoPage/components/Block/Block.tsx create mode 100644 frontend/src/modules/partner/pages/PartnerInfoPage/components/BlockSkeleton/BlockSkeleton.tsx create mode 100644 frontend/src/modules/partner/pages/PartnerInfoPage/components/PartnerLeadsBlock/PartnerLeadsBlock.tsx create mode 100644 frontend/src/modules/partner/pages/PartnerInfoPage/components/PartnerSummaryBlock/PartnerSummaryBlock.tsx create mode 100644 frontend/src/modules/partner/pages/PartnerInfoPage/components/index.tsx create mode 100644 frontend/src/modules/partner/pages/index.tsx create mode 100644 frontend/src/modules/partner/shared/assets/index.tsx create mode 100644 frontend/src/modules/partner/shared/assets/partner.svg create mode 100644 frontend/src/modules/partner/shared/index.tsx create mode 100644 frontend/src/modules/partner/shared/lib/index.tsx create mode 100644 frontend/src/modules/partner/shared/lib/models/PartnerLead.ts create mode 100644 frontend/src/modules/partner/shared/lib/models/PartnerSummary.ts create mode 100644 frontend/src/modules/partner/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/partner/store/PartnerInfoStore.tsx create mode 100644 frontend/src/modules/partner/store/index.tsx create mode 100644 frontend/src/modules/partner/templates/PartnerPageTemplate/PartnerPageTemplate.tsx create mode 100644 frontend/src/modules/partner/templates/PartnerPageTemplate/components/Container/Container.tsx create mode 100644 frontend/src/modules/partner/templates/PartnerPageTemplate/components/Header/Header.tsx create mode 100644 frontend/src/modules/partner/templates/PartnerPageTemplate/components/LogoutButton/LogoutButton.tsx create mode 100644 frontend/src/modules/partner/templates/PartnerPageTemplate/components/index.tsx create mode 100644 frontend/src/modules/partner/templates/index.tsx create mode 100644 frontend/src/modules/products/api/ProductApi/ProductApi.ts create mode 100644 frontend/src/modules/products/api/ProductApi/helpers/invalidateGetProductsQuery.ts create mode 100644 frontend/src/modules/products/api/ProductApi/helpers/removeImagesFromProductCache.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useAddProduct.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useDeleteProduct.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useGetProduct.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useGetProducts.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useUpdateProduct.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useUpdateProductStocks.ts create mode 100644 frontend/src/modules/products/api/ProductApi/queries/useUploadProductImages.ts create mode 100644 frontend/src/modules/products/api/ProductCategoriesApi/ProductCategoryApi.ts create mode 100644 frontend/src/modules/products/api/ProductOrderApi/ProductOrderApi.ts create mode 100644 frontend/src/modules/products/api/ProductOrderApi/helpers/invalidateEntityProductOrdersInCache.ts create mode 100644 frontend/src/modules/products/api/ProductOrderApi/queries/useDeleteEntityProductOrder.ts create mode 100644 frontend/src/modules/products/api/ProductOrderApi/queries/useGetEntityProductOrders.ts create mode 100644 frontend/src/modules/products/api/ProductOrderStatusApi/ProductOrderStatusApi.ts create mode 100644 frontend/src/modules/products/api/ProductPriceApi/ProductPriceApi.ts create mode 100644 frontend/src/modules/products/api/ProductRentalOrderApi/ProductRentalOrderApi.ts create mode 100644 frontend/src/modules/products/api/ProductRentalOrderApi/helpers/invalidateEntityRentalProductOrdersInCache.ts create mode 100644 frontend/src/modules/products/api/ProductRentalOrderApi/queries/useChangeRentalOrderStatus.ts create mode 100644 frontend/src/modules/products/api/ProductRentalOrderApi/queries/useDeleteEntityRentalProductOrder.ts create mode 100644 frontend/src/modules/products/api/ProductRentalOrderApi/queries/useGetEntityRentalProductOrders.ts create mode 100644 frontend/src/modules/products/api/ProductRentalOrderApi/queries/useSearchRentalOrders.ts create mode 100644 frontend/src/modules/products/api/ProductSectionRentalIntervalApi/ProductSectionRentalIntervalApi.ts create mode 100644 frontend/src/modules/products/api/ProductsApiRoutes.ts create mode 100644 frontend/src/modules/products/api/ProductsQueryKeys.ts create mode 100644 frontend/src/modules/products/api/ProductsSectionApi/ProductsSectionApi.ts create mode 100644 frontend/src/modules/products/api/ProductsSectionApi/helpers/invalidateProductsSections.ts create mode 100644 frontend/src/modules/products/api/ProductsSectionApi/queries/useDeleteProductsSection.ts create mode 100644 frontend/src/modules/products/api/ProductsSectionApi/queries/useGetProductsSection.ts create mode 100644 frontend/src/modules/products/api/ProductsSectionApi/queries/useGetProductsSections.ts create mode 100644 frontend/src/modules/products/api/RentalScheduleApi/RentalApi.ts create mode 100644 frontend/src/modules/products/api/RentalScheduleApi/queries/useGetProductRentals.ts create mode 100644 frontend/src/modules/products/api/RentalScheduleApi/queries/useGetRentals.ts create mode 100644 frontend/src/modules/products/api/ShipmentApi/ShipmentApi.ts create mode 100644 frontend/src/modules/products/api/ShipmentApi/queries/useChangeShipmentStatus.ts create mode 100644 frontend/src/modules/products/api/ShipmentApi/queries/useGetShipments.ts create mode 100644 frontend/src/modules/products/api/WarehouseApi/WarehouseApi.ts create mode 100644 frontend/src/modules/products/api/dtos/Product/CreateProductDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Product/GetProductsResultDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Product/ProductDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Product/ProductInfoDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Product/RentalScheduleDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Product/UpdateProductDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductCategory/CreateProductCategoryDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductCategory/ProductCategoryDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductCategory/UpdateProductCategoryDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductOrder/CreateOrderDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductOrder/OrderDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductOrder/OrderItemDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductOrder/ReservationDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductOrder/UpdateOrderDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductPrice/CreateProductPriceDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductPrice/ProductPriceDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductPrice/UpdateProductPriceDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/CheckRentalStatusDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/CreateRentalOrderDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/CreateRentalOrderItemDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/DatePeriodDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/ProductRentalStatusDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/RentalEventDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/RentalOrderDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/RentalOrderItemDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/UpdateRentalOrderDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductRentalOrder/UpdateRentalOrderItemDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductSectionRentalInterval/RentalIntervalDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductsSection/CreateProductsSectionDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductsSection/ProductsSectionDto.ts create mode 100644 frontend/src/modules/products/api/dtos/ProductsSection/UpdateProductsSectionDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Rental/GetRentalsResultDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Rental/RentalDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Rental/RentalOrderFilter.ts create mode 100644 frontend/src/modules/products/api/dtos/Shipment/GetShipmentsResultDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Shipment/ShipmentDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Shipment/ShipmentItemDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Stock/StockDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Stock/UpdateStockDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Stock/UpdateStocksDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Warehouse/CreateWarehouseDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Warehouse/UpdateWarehouseDto.ts create mode 100644 frontend/src/modules/products/api/dtos/Warehouse/WarehouseDto.ts create mode 100644 frontend/src/modules/products/api/dtos/index.tsx create mode 100644 frontend/src/modules/products/api/index.tsx create mode 100644 frontend/src/modules/products/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/CardProductsOrder.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/CardRentalProductsOrder.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/CardProductsOrderComponent.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/CardProductsOrderBlock.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/CardProductsOrderBlockHeader/CardProductsOrderBlockHeader.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/CardProductsOrderTable/CardProductsOrderTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/cells/AvailableCellTable/AvailableCellTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/cells/AvailableHeadCell/AvailableHeadCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/cells/CardOrderAmountCell/CardOrderAmountCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/cells/CardOrderAvailableCell/CardOrderAvailableCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/cells/CurrencyCell/CurrencyCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/cells/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/modals/DeleteOrderOrClearItemsWarningModal/DeleteOrderOrClearItemsWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/modals/DeleteOrderWarningModal/DeleteOrderWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsOrderBlock/components/modals/ReturnOrderStocksWarningModal/ReturnStocksWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsWarehouseBlock/CardProductsWarehouseBlock.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsWarehouseBlock/components/WarehouseTable/WarehouseTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/CardProductsWarehouseBlock/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardProductsOrderComponent/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/CardRentalProductsOrderComponent.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/CardRentalProductsOrderBlock.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/CardRentalOrderAmountCell/CardRentalOrderAmountCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/CardRentalProductsOrderBlockHeader/CardRentalProductsOrderBlockHeader.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/RentalCardProductsOrderTable/RentalCardProductsOrderTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/RentalOrderPeriodsControl/RentalOrderPeriodsControl.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/RentalOrderPeriodsControlItem/RentalOrderPeriodsControlItem.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/modals/DeleteRentalOrderOrClearItemsWarningModal/DeleteRentalOrderOrClearItemsWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsOrderBlock/components/modals/DeleteRentalOrderWarningModal/DeleteRentalOrderWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsWarehouseBlock/CardRentalProductsWarehouseBlock.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsWarehouseBlock/components/RentalWarehouseTable/RentalWarehouseTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/CardRentalProductsWarehouseBlock/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/CardRentalProductOrderComponent/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrderPage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/CardProductsOrders.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/ProductSectionOrdersItem/ProductSectionOrdersItem.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/ProductSectionOrdersItemTable/ProductSectionOrdersItemTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/RentalProductSectionOrdersItem/RentalProductSectionOrdersItem.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/RentalProductSectionOrdersItemTable/RentalProductSectionOrdersItemTable.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/cells/ProductSectionOrdersDateCell/ProductSectionOrdersDateCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/cells/ProductSectionOrdersWarehouseCell/ProductSectionOrdersWarehouseCell.tsx create mode 100644 frontend/src/modules/products/pages/CardProductsOrdersPage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/ProductPage.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductActionsDropdown/ProductActionsDropdown.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductDescription/ProductDescription.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductCalendarBlock/ProductCalendarBlock.styles.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductCalendarBlock/ProductCalendarBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductFeed.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductImagesBlock/ProductImagesBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductImagesBlock/components/AddImageBlock/AddImageBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductImagesBlock/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductPricesBlock/AddPriceForm.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductPricesBlock/ProductPriceBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductPricesBlock/ProductPricesBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFeed/ProductStocksBlock/ProductStocksBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductFormGroup/ProductFormGroup.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductImageItem/ProductImageItem.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductMainImage/ProductMainImage.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/ProductNameBlock/ProductNameBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductPage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/Products.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/ProductsPage.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddPhotoBlock/AddPhotoBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/AddProductModal.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/AddProductModalFormGroup/AddProductModalFormGroup.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/AddProductModalSkuField/AddProductModalSkuField.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/AddProductModalWarning/AddProductModalWarning.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/CreateStocks/CreateStocksBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/CreateStocks/CreateStocksModal.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/CreateStocks/CreateStocksTable.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddProductModal/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/AddWarehousePlaceholder/AddWarehousePlaceholder.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/PhotoBlockItem/PhotoBlockItem.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/Prices/ProductPriceItem.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/Prices/ProductPriceList.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductSearchBlock/ProductsSearchBlock.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductStocksInput/ProductStocksInput.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductStocksModal/ProductStocksModal.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductStocksTable/ProductStocksTable.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductsPageSecondaryHeader/ProductsPageSecondaryHeader.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductsTable/ProductsTable.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductsTableBody/ProductsTableBody.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductsTableHead/ProductsTableHead.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/ProductsTableSettingsDrawer/ProductsTableSettingsDrawer.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/cells/ProductStocksCell/ProductStocksCell.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/cells/ProductsTableHeadCell/ProductsTableHeadCell.tsx create mode 100644 frontend/src/modules/products/pages/ProductsPage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/ShipmentPage.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ProductBarcodesControl/ProductBarcodesControl.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ProductDoesNotExistWarningModal/ProductDoesNotExistWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ProductIsNotInOrderWarningModal/ProductIsNotInOrderWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/RentalShipmentComponent/RentalShipmentComponent.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/RentalShipmentComponent/components/RentalShipmentPageSecondaryHeader/RentalShipmentPageSecondaryHeader.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/RentalShipmentComponent/components/RentalShipmentTable/RentalShipmentTable.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/RentalShipmentComponent/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ShipmentComponent/ShipmentComponent.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ShipmentComponent/components/ShipmentPageSecondaryHeader/ShipmentPageSecondaryHeader.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ShipmentComponent/components/ShipmentTable/ShipmentTable.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ShipmentComponent/components/cells/ShipmentAvailableCell/ShipmentAvailableCell.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ShipmentComponent/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/ShipmentRoot/ShipmentRoot.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/SomeProductsNotCheckedWarningModal/SomeProductsNotCheckedWarningModal.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/cells/CheckHeaderCell/CheckHeaderCell.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/cells/CheckRowCell/CheckRowCell.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentPage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/Shipments.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/RentalShipmentsComponent/RentalShipmentsComponent.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/ShipmentsComponent/ShipmentsComponent.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/ShipmentsComponent/components/ShipmentsTable/ShipmentsTable.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/ShipmentsComponent/components/ShipmentsTablePagination/ShipmentsTablePagination.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/ShipmentsComponent/components/cells/ShipmentDateCell/ShipmentDateCell.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/ShipmentsComponent/components/index.tsx create mode 100644 frontend/src/modules/products/pages/ShipmentsPage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/Timetable.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/Calendar/Calendar.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarComponent/CalendarComponent.styles.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarComponent/CalendarComponent.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarDateCell/CalendarDateCell.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarPeriodControl/CalendarPeriodControl.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarPeriodControlTitle/CalendarPeriodControlTitle.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarResourceHeader/CalendarResourceHeader.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarResourceLabel/CalendarResourceLabel.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/CalendarToolbar/CalendarToolbar.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/HideEmptyResourcesListItem/HideEmptyResourcesListItem.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/RentEvent/RentEvent.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/RentEventDetails/RentEventDetails.tsx create mode 100644 frontend/src/modules/products/pages/TimetablePage/components/index.tsx create mode 100644 frontend/src/modules/products/pages/index.tsx create mode 100644 frontend/src/modules/products/shared/assets/arrow.svg create mode 100644 frontend/src/modules/products/shared/assets/created_at.svg create mode 100644 frontend/src/modules/products/shared/assets/decrement.svg create mode 100644 frontend/src/modules/products/shared/assets/delete.svg create mode 100644 frontend/src/modules/products/shared/assets/hide_empty_resources.svg create mode 100644 frontend/src/modules/products/shared/assets/increment.svg create mode 100644 frontend/src/modules/products/shared/assets/index.tsx create mode 100644 frontend/src/modules/products/shared/assets/scan_barcode.svg create mode 100644 frontend/src/modules/products/shared/assets/settings.svg create mode 100644 frontend/src/modules/products/shared/assets/shipped_at.svg create mode 100644 frontend/src/modules/products/shared/assets/show_empty_resources.svg create mode 100644 frontend/src/modules/products/shared/assets/unfold.svg create mode 100644 frontend/src/modules/products/shared/assets/warehouses.svg create mode 100644 frontend/src/modules/products/shared/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/AddPlaceholderTemplate/AddPlaceholderTemplate.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/BlockFooterControls/BlockFooterControls.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/OrderStatusSelect/OrderStatusSelect.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/ProductCategoriesBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/components/CategoryBlock/CategoryBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/components/CategoryBlockSkeleton/CategoryBlockSkeleton.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/components/DeleteCategoryWarningModal/DeleteCategoryWarningModal.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/components/SubcategoryBlock/SubcategoryBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/components/SubcategoryList/SubcategoryList.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesBlock/components/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductCategoriesSelect/ProductCategoriesSelect.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductWarehouseBlockTemplateHeader/ProductWarehouseBlockTemplateHeader.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductWarehousesSelect/ProductWarehousesSelect.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductsOrderComponentRoot/ProductsOrderComponentRoot.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductsOrderTotalBlock/ProductsOrderTotalBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ProductsSettingsButton/ProductsSettingsButton.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/QuantityControlButton/QuantityControlButton.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/RemoveSelectedBlock/RemoveSelectedBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/RentalOrderStatusSelect/RentalOrderStatusSelect.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ReservationsModal/ReservationsModal.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ReservationsModal/components/AddStockPlaceholder/AddStockPlaceholder.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ReservationsModal/components/ReservationsTable/ReservationsTable.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ReservationsModal/components/WarehouseNameCell/WarehouseNameCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/ReservationsModal/components/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/StockQuantityBLock/StockQuantityBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/WarehousesBlock/WarehousesBlock.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/WarehousesBlock/components/DeleteWarehouseModal/DeleteWarehouseModal.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/WarehousesBlock/components/WarehousePageBlockSkeleton/WarehousePageBlockSkeleton.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/WarehousesBlock/components/WarehousesBlockComponent/WarehousesBlockComponent.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/WarehousesBlock/components/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/AddProductItemCell/AddProductItemCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/AmountCellRoot/AmountCellRoot.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/CardOrderQuantityCellSwitch/CardOrderQuantityCellSwitch.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/CreateStockCell/CreateStockCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/NameCell/NameCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ProductPriceCell/ProductPriceCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ProductsOrderDeleteItemCell/ProductsOrderDeleteItemCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ProductsOrderDiscountCell/ProductsOrderDiscountCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ProductsOrderPriceCell/ProductsOrderPriceCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ProductsOrderPriceHeadCell/ProductsOrderPriceHeadCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ProductsOrderTaxHeaderCell/ProductsOrderTaxHeaderCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/QuantityCell/QuantityCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/QuantityCellWithModal/QuantityCellWithModal.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/RentalAvailabilityCell/RentalAvailabilityCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/ReservationQuantityCell/ReservationQuantityCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/cells/StockCell/StockCell.tsx create mode 100644 frontend/src/modules/products/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/helpers/checkDuplicateProductSku.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/generateOrderName.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/generateProductTypeOptions.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/generateRentalOrderPeriodsControlTitle.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/generateRentalOrderStatusOptions.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/getCategoryById.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/getDefaultProductsColumnSize.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/getProductCategoryName.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/getProductsPageTabs.tsx create mode 100644 frontend/src/modules/products/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/helpers/transformCalendarEvents.ts create mode 100644 frontend/src/modules/products/shared/lib/helpers/transformProductsToResources.ts create mode 100644 frontend/src/modules/products/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useAvailableCellColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useCardOrderColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useCreateStocksColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/usePriceCellColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useProductStocksColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useProductsColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useProductsSectionOrdersColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useRentalCardOrderColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useRentalProductsSectionOrdersColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useRentalShipmentColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useRentalWarehouseColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useReservationsColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useShipmentColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useShipmentsColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/hooks/useWarehouseColumns.tsx create mode 100644 frontend/src/modules/products/shared/lib/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/models/AvailableCellColumns/AvailableCellColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/AvailableCellColumns/AvailableCellColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CalendarEvent.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CalendarGridView.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CalendarResource.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CardOrderColumns/CardOrderColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CardOrderColumns/CardOrderColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CardProductOrderPageSettings.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CheckBarcodeResult.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CreateStockRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CreateStocksColumns/CreateStocksColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/CreateStocksColumns/CreateStocksColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Events/EventShort.ts create mode 100644 frontend/src/modules/products/shared/lib/models/LastSelectedWarehouseSettings.ts create mode 100644 frontend/src/modules/products/shared/lib/models/OrderBlockComponentProps.ts create mode 100644 frontend/src/modules/products/shared/lib/models/OrderBlockFilterProps.ts create mode 100644 frontend/src/modules/products/shared/lib/models/OrderMutationRights.ts create mode 100644 frontend/src/modules/products/shared/lib/models/PriceCellColumns/PriceCellColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/PriceCellColumns/PriceCellColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/PriceForm.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/GetProductsMeta.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/GetProductsResult.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/Product.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/ProductInfo.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/ProductRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/ProductType.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/RentalProductRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/RentalSchedule.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Product/RentalStatus.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductCategory/ProductCategory.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrder/Order.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrder/OrderItem.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrder/OrderItemRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrder/Reservation.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrderStatus/OrderStatus.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrderStatus/OrderStatusCode.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductOrderStatus/StatusTransition.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductPrice/ProductPrice.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/DatePeriod.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/ProductRentalStatus.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/RentalEvent.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/RentalOrder.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/RentalOrderItem.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/RentalOrderItemRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductRentalOrder/RentalOrderStatus.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductSectionOrdersColumns/ProductSectionOrdersColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductSectionOrdersColumns/ProductSectionOrdersColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductSectionRentalInterval/ProductRentalIntervalFormData.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductSectionRentalInterval/RentalInterval.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductSectionRentalInterval/RentalIntervalType.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductStocksColumns/ProductStocksColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductStocksColumns/ProductStocksColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsColumns/ProductsColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsColumns/ProductsColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsPageQueryParams.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsPageTabs.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsSection/ProductSectionBuilderFormData.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsSection/ProductsSection.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsSection/ProductsSectionType.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsSectionOrdersCommonColumns/ProductsSectionOrdersCommonColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ProductsSectionOrdersCommonColumns/ProductsSectionOrdersCommonColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Rental/GetRentalsResult.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Rental/Rental.ts create mode 100644 frontend/src/modules/products/shared/lib/models/RentalCardOrderColumns/RentalCardOrderColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/RentalCardOrderColumns/RentalCardOrderColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/RentalShipmentColumns/RentalShipmentColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/RentalShipmentColumns/RentalShipmentColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/RentalWarehouseColumns/RentalWarehouseColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/RentalWarehouseColumns/RentalWarehouseColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Reservation/ReservationRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ReservationsColumns/ReservationsColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ReservationsColumns/ReservationsColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ResourceViewMode.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Shipment/GetShipmentsResult.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Shipment/RentalShipmentItemRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Shipment/Shipment.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Shipment/ShipmentItem.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Shipment/ShipmentItemRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Shipment/ShipmentRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ShipmentColumns/ShipmentColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ShipmentColumns/ShipmentColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ShipmentsColumns/ShipmentsColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ShipmentsColumns/ShipmentsColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ShipmentsTable/ShipmentsTableSettings.ts create mode 100644 frontend/src/modules/products/shared/lib/models/ShipmentsTable/ShipmentsTablesSettings.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Stock/Stock.ts create mode 100644 frontend/src/modules/products/shared/lib/models/StockRow.ts create mode 100644 frontend/src/modules/products/shared/lib/models/TableSettings/ProductTableSettings.ts create mode 100644 frontend/src/modules/products/shared/lib/models/TableSettings/ProductTablesSettings.ts create mode 100644 frontend/src/modules/products/shared/lib/models/TaxStrategy.ts create mode 100644 frontend/src/modules/products/shared/lib/models/UtcDatesRangeValueModel.ts create mode 100644 frontend/src/modules/products/shared/lib/models/Warehouse/Warehouse.ts create mode 100644 frontend/src/modules/products/shared/lib/models/WarehouseColumns/WarehouseColumnsIds.ts create mode 100644 frontend/src/modules/products/shared/lib/models/WarehouseColumns/WarehouseColumnsSizes.ts create mode 100644 frontend/src/modules/products/shared/lib/models/fullCalendarLicenseKey.ts create mode 100644 frontend/src/modules/products/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/products/shared/lib/models/shipmentsTableSettingsKey.ts create mode 100644 frontend/src/modules/products/store/AddProductModalStore.ts create mode 100644 frontend/src/modules/products/store/OrderStatusStore.ts create mode 100644 frontend/src/modules/products/store/OrderStore.ts create mode 100644 frontend/src/modules/products/store/ProductCategoryStore.ts create mode 100644 frontend/src/modules/products/store/ProductStockBlockStore.ts create mode 100644 frontend/src/modules/products/store/ProductsModuleStore.ts create mode 100644 frontend/src/modules/products/store/ProductsSectionBuilderStore.ts create mode 100644 frontend/src/modules/products/store/RentalOrderStore.ts create mode 100644 frontend/src/modules/products/store/RentalShipmentStore.ts create mode 100644 frontend/src/modules/products/store/ShipmentStore.ts create mode 100644 frontend/src/modules/products/store/WarehouseStore.ts create mode 100644 frontend/src/modules/products/store/index.tsx create mode 100644 frontend/src/modules/products/templates/ProductPageTemplate/ProductPageTemplate.tsx create mode 100644 frontend/src/modules/products/templates/ProductSectionOrdersItemTemplate/ProductSectionOrdersItemTemplate.tsx create mode 100644 frontend/src/modules/products/templates/ProductsOrderBlockTemplate/ProductsOrderBlockTemplate.tsx create mode 100644 frontend/src/modules/products/templates/ProductsOrderPanelTemplate/ProductsOrderPanelTemplate.tsx create mode 100644 frontend/src/modules/products/templates/ProductsOrderPanelsTemplate/ProductsOrderPanelsTemplate.tsx create mode 100644 frontend/src/modules/products/templates/ProductsOrderPanelsTemplate/components/ProductsOrderResizeHandler/ProductsOrderResizeHandler.tsx create mode 100644 frontend/src/modules/products/templates/ProductsOrderPanelsTemplate/components/index.tsx create mode 100644 frontend/src/modules/products/templates/ProductsWarehouseBlockTemplate/ProductsWarehouseBlockTemplate.tsx create mode 100644 frontend/src/modules/products/templates/ShipmentPageSecondaryHeaderTemplate/ShipmentPageSecondaryHeaderTemplate.tsx create mode 100644 frontend/src/modules/products/templates/index.tsx create mode 100644 frontend/src/modules/reporting/api/DashboardApi/DashboardApi.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetActivitiesSummaryReport.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetEntitySummaryReport.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetPipelineReport.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetRating.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetSalesPlanReport.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetTasksSummaryReport.ts create mode 100644 frontend/src/modules/reporting/api/DashboardApi/queries/useGetTopSellers.ts create mode 100644 frontend/src/modules/reporting/api/GoalSettingsApi/GoalSettingsApi.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/ReportApi.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetCallHistoryReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetComparativeReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetCustomerReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetGeneralReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetProductsGeneralReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetProjectEntities.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetProjectEntitiesReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetProjectTaskUserReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetScheduleReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportApi/queries/useGetTelephonyReport.ts create mode 100644 frontend/src/modules/reporting/api/ReportingApiRoutes.ts create mode 100644 frontend/src/modules/reporting/api/ReportingQueryKeys.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Dashboard/Pipeline/PipelineReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Dashboard/Pipeline/SalesPipelineReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/DatePeriodDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/DatePeriodFilter.ts create mode 100644 frontend/src/modules/reporting/api/dtos/EntitySummary/EntitySummaryReport.ts create mode 100644 frontend/src/modules/reporting/api/dtos/EntitySummary/EntitySummaryValue.ts create mode 100644 frontend/src/modules/reporting/api/dtos/ReportFilter.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Comparative/ComparativeReportCellDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Comparative/ComparativeReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Comparative/ComparativeReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Comparative/ComparativeReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Comparative/ComparativeReportValueDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Customer/CustomerReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Customer/CustomerReportFieldDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Customer/CustomerReportFieldMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Customer/CustomerReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Customer/CustomerReportMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Customer/CustomerReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/CallReportBlockDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportEntityDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFieldDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFieldMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFieldOptionMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFieldValueDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityCallDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityEntityDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityFieldDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityFieldOptionDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityFieldsDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportFilterVisibilityTaskDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/General/GeneralReportTaskDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/PossibleReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Products/General/ProductsReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Products/General/ProductsReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Products/General/ProductsReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Products/General/ProductsReportUserCellDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectEntitiesReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectEntitiesReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectEntitiesReportMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectEntitiesReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectReportFieldDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectReportFieldMetaDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectTaskUserReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectTaskUserReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectTaskUserReportRowTotalDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Projects/ProjectsReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Schedule/ScheduleReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/CallDuration.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/CallHistoryReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/CallHistoryReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/CallHistoryReportItemDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/TelephonyReportDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/TelephonyReportFilterDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/Reports/Telephony/TelephonyReportRowDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/SalesPlanDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/SalesPlanProgressDto.ts create mode 100644 frontend/src/modules/reporting/api/dtos/SalesPlanReport.ts create mode 100644 frontend/src/modules/reporting/api/dtos/SellersRatingReport.ts create mode 100644 frontend/src/modules/reporting/api/dtos/TasksSummaryReport.ts create mode 100644 frontend/src/modules/reporting/api/dtos/TopSellersReport.ts create mode 100644 frontend/src/modules/reporting/api/dtos/index.tsx create mode 100644 frontend/src/modules/reporting/api/index.tsx create mode 100644 frontend/src/modules/reporting/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/Dashboard.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/AnalyticsBlock/AnalyticsBlock.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/AnalyticsBlock/components/ResolveIconSwitch/ResolveIconSwitch.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/AnalyticsBlock/components/Unit/Unit.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/AnalyticsBlock/components/UnitBody/UnitBody.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/AnalyticsBlock/components/UnitsList/UnitsList.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/AnalyticsBlock/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/DashboardControls/DashboardControls.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/DashboardControls/components/DashboardTypeTooltip/DashboardTypeTooltip.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/DashboardControls/components/DataUpdateSelect/DataUpdateSelect.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/DashboardControls/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/GoalChart/GoalChart.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/GoalChart/components/GoalSettingsLink/GoalSettingsLink.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/GoalChart/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/LeadsStatusChart/LeadsStatusChart.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/LeadsStatusChart/components/Column/Column.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/LeadsStatusChart/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/RatingChart/RatingChart.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/RatingChart/components/RatingItem/RatingItem.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/RatingChart/components/RatingList/RatingList.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/RatingChart/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/SalesPipelineIndicators/SalesPipelineIndicators.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/SalesPipelineIndicators/components/StageRow/StageRow.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/SalesPipelineIndicators/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/TopSellersChart/TopSellersChart.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/TrafficLightReport/TrafficLightReport.tsx create mode 100644 frontend/src/modules/reporting/pages/DashboardPage/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/GoalSettingsPage.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/components/ControlsRow/ControlsRow.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/components/GoalSettingsForm/GoalSettingsForm.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/components/GoalSettingsFormItem/GoalSettingsFormItem.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/components/GroupIcon/GroupIcon.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/components/PeriodControl/PeriodControl.tsx create mode 100644 frontend/src/modules/reporting/pages/GoalSettingsPage/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/Reports.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/ReportsNavigationSidebar.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/ComparisonReportTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/CustomerReportTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/GeneralReportTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/ProductsGeneralReportTabList/ProductsGeneralReportTabListEntity.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/ProductsGeneralReportTabList/ProductsGeneralReportTabListSection.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/ProjectReportTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/ScheduleReportTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/TelephonyReportTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/components/CollapsibleGroup/CollapsibleGroup.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/components/CollapsibleTab/CollapsibleTab.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/components/DynamicScheduleTabList/DynamicScheduleTabList.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/components/ProjectEntitiesTabs/ProjectEntitiesTabs.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/components/StyledTab/StyledTab.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TabLists/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/TitleWrapperSwitch/TitleWrapperSwitch.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/ReportsNavigationSidebar/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/ComparativeReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/CustomerReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/GeneralReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/ProductsGeneralReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/ProjectReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/ScheduleReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/TabPanels/TelephonyReportTabPanel.tsx create mode 100644 frontend/src/modules/reporting/pages/ReportsPage/components/index.tsx create mode 100644 frontend/src/modules/reporting/pages/index.tsx create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/call_incoming.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/call_missed.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/call_outgoing.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/caret.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/decline.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/filters_menu.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/frameless_caret.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/growth.svg create mode 100644 frontend/src/modules/reporting/shared/assets/ReportsPage/scroll_arrow.svg create mode 100644 frontend/src/modules/reporting/shared/assets/SalesAnalytics/average_amount.svg create mode 100644 frontend/src/modules/reporting/shared/assets/SalesAnalytics/average_term.svg create mode 100644 frontend/src/modules/reporting/shared/assets/SalesAnalytics/increased_sales.svg create mode 100644 frontend/src/modules/reporting/shared/assets/SalesAnalytics/wallet.svg create mode 100644 frontend/src/modules/reporting/shared/assets/Statistics/lost.svg create mode 100644 frontend/src/modules/reporting/shared/assets/Statistics/new.svg create mode 100644 frontend/src/modules/reporting/shared/assets/Statistics/total.svg create mode 100644 frontend/src/modules/reporting/shared/assets/Statistics/won.svg create mode 100644 frontend/src/modules/reporting/shared/assets/TasksAndActivities/completed.svg create mode 100644 frontend/src/modules/reporting/shared/assets/TasksAndActivities/expired.svg create mode 100644 frontend/src/modules/reporting/shared/assets/TasksAndActivities/no.svg create mode 100644 frontend/src/modules/reporting/shared/assets/TasksAndActivities/total.svg create mode 100644 frontend/src/modules/reporting/shared/assets/arrow.svg create mode 100644 frontend/src/modules/reporting/shared/assets/arrow_back.svg create mode 100644 frontend/src/modules/reporting/shared/assets/arrow_down.svg create mode 100644 frontend/src/modules/reporting/shared/assets/index.tsx create mode 100644 frontend/src/modules/reporting/shared/assets/pie_plug.svg create mode 100644 frontend/src/modules/reporting/shared/assets/product_row.svg create mode 100644 frontend/src/modules/reporting/shared/assets/speedometer.svg create mode 100644 frontend/src/modules/reporting/shared/assets/speedometer_plug.svg create mode 100644 frontend/src/modules/reporting/shared/assets/toggle_expanded.svg create mode 100644 frontend/src/modules/reporting/shared/assets/top_sellers_plug.svg create mode 100644 frontend/src/modules/reporting/shared/assets/total.svg create mode 100644 frontend/src/modules/reporting/shared/assets/traffic_light_plug.svg create mode 100644 frontend/src/modules/reporting/shared/assets/tune.svg create mode 100644 frontend/src/modules/reporting/shared/assets/update.svg create mode 100644 frontend/src/modules/reporting/shared/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Block/Block.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/BlockHeader/BlockHeader.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/ChartPlug/ChartPlug.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/CountupComponent/CountupComponent.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/NameLink/NameLink.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/CallHistoryTimePicker/CallHistoryTimePicker.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/CallHistoryTimePicker/components/Delimiter/Delimiter.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/CallHistoryTimePicker/components/DoubleDigitNumberInput/DoubleDigitNumberInput.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/CallHistoryTimePicker/components/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/ComparativeCompositeHeader/ComparativeCompositeHeader.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/ComparativeReportValueCell/ComparativeReportValueCell.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/ComparativeReportValueCell/components/ReportValueCellPart/ReportValueCellPart.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/ComparativeReportValueCell/components/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/HeadTitleCellWithToggle/HeadTitleCellWithToggle.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowParticipantCellSwitch/RowParticipantCellSwitch.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowParticipantCellSwitch/components/ContactParticipant/ContactParticipant.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowParticipantCellSwitch/components/UserParticipant/UserParticipant.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowParticipantCellSwitch/components/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowRecordCell/RowRecordCell.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowTitleCell/RowTitleCell.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Reports/RowTitleIcon/RowTitleIcon.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/Speedometer/Speedometer.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/ViewSwitch/ViewSwitch.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/calculatePercent.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/convertPercentToAngle.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/exportReportToXlsx.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/externalTooltip.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/formatQuantityAmountWithCurrency.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/formatQuantityAmountWithMinutes.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/formatQuantityAmountWithoutCurrency.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/formatTaskCountPlannedTime.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateActivitiesUnits.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateArrayOfYearsFromAccountCreation.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateCompositeProductsReportValue.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateEntitiesUnits.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateFieldOptionColumnId.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateGeneralReportSettingsObjStorageKey.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateSalesAnalytics.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/generateTasksUnits.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/getCustomerReportSections.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/getDoughnutData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/getLinkedCategories.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/getPieData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/htmlLegendPlugin.ts create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/helpers/secondsToHoursAndMinutes.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Comparative/useGenerateMonthOptions.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Comparative/useGetComparativeReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Comparative/useGetComparativeReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Customer/useGetCustomerReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Customer/useGetCustomerReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/General/useGenerateStageOptions.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/General/useGetGeneralReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/General/useGetGeneralReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Products/General/useGetProductsGeneralReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Products/General/useGetProductsGeneralReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Projects/useGetProjectEntitiesReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Projects/useGetProjectEntitiesReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Projects/useGetProjectTaskUserReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Projects/useGetProjectTaskUserReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Schedule/useGetScheduleReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Schedule/useGetScheduleReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Telephony/useGenerateDirectionOptions.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Telephony/useGetCallHistoryReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Telephony/useGetCallHistoryReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Telephony/useGetTelephonyReportColumns.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/Telephony/useGetTelephonyReportTableData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/useGetOwnerFieldsFilterOptions.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/Reports/useSaveReportColumnsVisibility.ts create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/hooks/useGetReportTable.ts create mode 100644 frontend/src/modules/reporting/shared/lib/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/models/Analytics/AnalyticsColors.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Analytics/AnalyticsIconsNames.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Analytics/AnalyticsUnit.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Analytics/AnalyticsValueType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Analytics/SalesAnalyticsUnit.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Analytics/SalesPipelineAnalyticsModel.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/AnalyticsBlockPalette.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/AutoUpdateMode.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/AutoUpdateTime.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/ChartType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/DashboardFilter.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/DashboardFilterSettings.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/DepartmentWithUsersOption.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/EntityReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/GoalSettingsPageSettings.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Leads/LeadsStatusChartPalette.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Periods.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Pipeline/PipelineReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Pipeline/PipelineReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Pipeline/SalesPipelineReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/QuantityAmount.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/ReportingOption.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/CallReportBlock.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Comparative/ComparativeReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Comparative/ComparativeReportCell.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Comparative/ComparativeReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Comparative/ComparativeReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Comparative/ComparativeReportValue.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Customer/CustomerReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Customer/CustomerReportField.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Customer/CustomerReportFieldMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Customer/CustomerReportMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Customer/CustomerReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Customer/CustomerReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportColumnMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportColumnMetaType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportColumnsIds.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportEntity.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportField.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportFieldMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportFieldOptionMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportFieldValue.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportSettings.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportTask.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/GeneralReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/General/ReportStageType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/PossibleReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Products/General/ProductReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Products/General/ProductReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Products/General/ProductReportUserCell.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Products/General/ProductsReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Products/productsGeneralReportSections.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Products/reportSectionToProductsReportTypeMap.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/BoardHasEntitiesState.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectEntitiesReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectEntitiesReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectReportField.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectReportFieldMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectReportItem.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectReportMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectStageItem.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectTaskUserReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectTaskUserReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Projects/ProjectTaskUserReportTotalRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/RenderTabs.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/ReportFilterSettings.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/ReportTabPanelProps.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/ReportTableColumnMeta.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/ReportTemplateProps.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/ReportsColumnsIds.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/ReportsSection.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Schedule/ReportSectionToScheduleReportTypeMap.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Schedule/ScheduleReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Schedule/ScheduleReportDto.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Schedule/ScheduleReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Schedule/ScheduleReportRowDto.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Schedule/ScheduleReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/CallHistoryReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/CallHistoryReportItem.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/CallHistoryReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/CallParticipant.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/DurationFilterSelectModel.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/ExtendedCallDirections.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/TelephonyReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/TelephonyReportBlock.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/TelephonyReportRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Reports/Telephony/TelephonyReportType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPipelineFilter.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPipelineFilterSettings.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesGoalModel.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesGoalSettings.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesPlan.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesPlanPeriodFilterType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesPlanProgress.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesPlanReportModel.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesPlanValue.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesPlan/SalesPlanWithUser.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SalesValues.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/SubdepartmentWithUsersOption.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/Tasks/TasksReport.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TopSellers/SellersRating.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TopSellers/TopSellerData.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TopSellers/TopSellers.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TopSellers/TopSellersUser.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TopSellers/TopSellersWithOthers.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TrafficLightColorStyles.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/TrafficLightColors.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/UserQuantityAmount.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/doughnutBGColors.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/doughnutBorderColors.ts create mode 100644 frontend/src/modules/reporting/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/reporting/shared/lib/models/pieBGColors.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/FormGroupType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/GoalType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/LeadsUnit.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/ReportRowType.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Comparative/ComparativeReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Customer/CustomerReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/General/GeneralReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Products/General/ProductsGeneralReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Projects/ProjectEntitiesReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Projects/ProjectTaskUserReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/ReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Schedule/ScheduleReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Telephony/CallHistoryReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/Reports/Telephony/TelephonyReportSyntheticRow.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/TasksUnit.ts create mode 100644 frontend/src/modules/reporting/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/reporting/store/GeneralReportTemplateSettingsStore.ts create mode 100644 frontend/src/modules/reporting/store/GoalSettingsStore.ts create mode 100644 frontend/src/modules/reporting/store/index.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/CallHistoryReportTemplate/CallHistoryReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/CallHistoryReportTemplate/components/CheckboxWrapper.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/CallHistoryReportTemplate/components/TimePickerGroup.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/CallHistoryReportTemplate/components/index.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/ComparativeReportTemplate/ComparativeReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/CustomerReportTemplate/CustomerReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/GeneralReportTemplate/GeneralReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/ProductsGeneralReportTemplate/ProductsGeneralReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/ProjectEntitiesReportTemplate/ProjectEntitiesReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/ProjectTaskUserReportTemplate/ProjectTaskUserReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/ScheduleReportTemplate/ScheduleReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/TelephonyReportTemplate/TelephonyReportTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/components/ReportRootTemplate/ReportRootTemplate.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/components/ReportSettingsDrawer/ReportSettingsDrawer.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/components/ReportTable/ReportTable.tsx create mode 100644 frontend/src/modules/reporting/templates/Reports/components/index.tsx create mode 100644 frontend/src/modules/reporting/templates/index.tsx create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/ScheduleApi.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/helpers/deleteScheduleInCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/helpers/getSchedule.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/helpers/invalidateScheduleInCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/helpers/resetAllSchedulesQueries.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/helpers/upsertScheduleToCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/index.tsx create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/queries/useDeleteSchedule.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/queries/useGetSchedule.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleApi/queries/useGetSchedules.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/ScheduleAppointmentApi.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/helpers/addScheduleAppointmentToCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/helpers/invalidateSchedulerAppointmentsCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/helpers/invalidateSchedulerStatisticsCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/helpers/invalidateSchedulerTotalVisitsCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/helpers/updateScheduleAppointmentInCache.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/index.tsx create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetFullScheduleAppointments.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetInfiniteScheduleAppointments.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetLastScheduleAppointment.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetScheduleAppointment.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetScheduleAppointmentCount.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetScheduleAppointments.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetScheduleAppointmentsStatistics.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentApi/queries/useGetSchedulerTotalVisits.ts create mode 100644 frontend/src/modules/scheduler/api/ScheduleAppointmentCardListApi/ScheduleAppointmentCardListApi.tsx create mode 100644 frontend/src/modules/scheduler/api/SchedulerApiRoutes.ts create mode 100644 frontend/src/modules/scheduler/api/SchedulerQueryKeys.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/Schedule/CreateScheduleDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/Schedule/ScheduleDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/Schedule/UpdateScheduleDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/ScheduleAppointment/CreateScheduleAppointmentDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/ScheduleAppointment/ScheduleAppointmentDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/ScheduleAppointment/ScheduleAppointmentEntityInfoDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/ScheduleAppointment/ScheduleAppointmentResultDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/ScheduleAppointment/ScheduleAppointmentStatisticsDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/ScheduleAppointment/UpdateScheduleAppointmentDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/SchedulePerformer/CreateSchedulePerformerDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/SchedulePerformer/SchedulePerformerDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/SchedulePerformer/UpdateSchedulePerformerDto.ts create mode 100644 frontend/src/modules/scheduler/api/dtos/index.tsx create mode 100644 frontend/src/modules/scheduler/api/index.tsx create mode 100644 frontend/src/modules/scheduler/index.tsx create mode 100644 frontend/src/modules/scheduler/pages/AppointmentCardListPage/AppointmentCardListPage.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerBoardViewPage/SchedulerBoardViewPage.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerBoardViewPage/components/SchedulerBoardComponent/SchedulerBoardComponent.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerBoardViewPage/components/SchedulerBoardHeader/SchedulerBoardHeader.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerBoardViewPage/components/SchedulerBoardSecondaryHeader/SchedulerBoardSecondaryHeader.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerBoardViewPage/components/StatsFooter/StatsFooter.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerBoardViewPage/components/index.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerPage/SchedulerPage.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerPage/components/SchedulerSettingsButton/SchedulerSettingsButton.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerPage/components/index.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/SchedulerScheduleViewPage.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/AppointmentEventComponent/AppointmentEventComponent.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/PerformerResourceLabel/PerformerResourceLabel.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/VisitsScheduler/VisitsScheduler.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/VisitsSchedulerComponent/VisitsSchedulerComponent.styles.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/VisitsSchedulerComponent/VisitsSchedulerComponent.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/VisitsSchedulerToolbar/VisitsSchedulerToolbar.tsx create mode 100644 frontend/src/modules/scheduler/pages/SchedulerScheduleViewPage/components/index.tsx create mode 100644 frontend/src/modules/scheduler/pages/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/assets/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/assets/info.svg create mode 100644 frontend/src/modules/scheduler/shared/assets/settings.svg create mode 100644 frontend/src/modules/scheduler/shared/assets/to.svg create mode 100644 frontend/src/modules/scheduler/shared/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/AddAppointmentControl.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/AddAppointmentDrawer.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/AddAppointmentModal.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AddAppointmentDrawerControls/AddAppointmentDrawerControls.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AddAppointmentDrawerHeader/AddAppointmentDrawerHeader.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AddAppointmentWarningModal/AddAppointmentWarningModal.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentBlock/AppointmentBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentDateAndTimeFormItem/AppointmentDateAndTimeFormItem.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentDuplicateWarningModal/AppointmentDuplicateWarningModal.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentEntityBlock/AppointmentEntityBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentEntityBlockHeader/AppointmentEntityBlockHeader.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentFormItem/AppointmentFormItem.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentIntersectWarningModal/AppointmentIntersectWarningModal.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentServiceBlockItemTable/AppointmentServiceBlockItemTable.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentServicesBlock/AppointmentServicesBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentServicesBlockItem/AppointmentServicesBlockItem.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentServicesSearchBlock/AppointmentServicesSearchBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentVisitParameters/AppointmentVisitParameters.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentsTabsListHeader/AppointmentsTabsListHeader.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/AppointmentsTabsListHeaderComponent/AppointmentsTabsListHeaderComponent.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/CompletedAppointmentsCount/CompletedAppointmentsCount.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/RepeatingAppointmentsIntervalBlock/RepeatingAppointmentsIntervalBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/RepeatingAppointmentsListBlock/RepeatingAppointmentsListBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/RepeatingAppointmentsListBlock/components/RepeatingAppointmentsDateItem/RepeatingAppointmentsDateItem.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/RepeatingAppointmentsListBlock/components/RepeatingAppointmentsDatesSelect/RepeatingAppointmentsDatesSelect.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/RepeatingAppointmentsListBlock/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/cells/AppointmentServiceAmountCell/AppointmentServiceAmountCell.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/cells/AppointmentsHistoryServicesCell/AppointmentsHistoryServicesCell.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/cells/AppointmentsHistoryServicesCell/components/AppointmentHistoryServicesTable/AppointmentHistoryServicesTable.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/cells/AppointmentsHistoryServicesCell/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentGeneralInformation/AppointmentGeneralInformation.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentGeneralInformation/components/PreviousAppointmentsInfoDisplay/PreviousAppointmentsInfoDisplay.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentGeneralInformation/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentPlannedVisits/AppointmentPlannedVisits.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentPlannedVisits/components/AppointmentPlannedVisit/AppointmentPlannedVisit.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentPlannedVisits/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AddAppointment/components/tabs/AppointmentVisitsHistoryTable/AppointmentVisitsHistoryTable.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AppointmentEventHoverCard/AppointmentEventHoverCard.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AppointmentEventHoverCard/components/AppointmentEventHoverCardBlock/AppointmentEventHoverCardBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AppointmentEventHoverCard/components/AppointmentEventHoverCardEntityBlock/AppointmentEventHoverCardEntityBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AppointmentEventHoverCard/components/AppointmentEventHoverCardFormGroup/AppointmentEventHoverCardFormGroup.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AppointmentEventHoverCard/components/AppointmentEventHoverCardService/AppointmentEventHoverCardService.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/AppointmentEventHoverCard/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/LocalTimeWarning/LocalTimeWarning.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/SchedulerBoardViewPage/AppointmentCell/AppointmentCell.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/SchedulerBoardViewPage/AppointmentIndexCell/AppointmentIndexCell.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/SchedulerBoardViewPage/AppointmentPeriodHeaderCell/AppointmentPeriodHeaderCell.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/SchedulerBoardViewPage/UserOrDepartmentViewCell/UserOrDepartmentViewCell.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/SchedulerSearchBlock/SchedulerSearchBlock.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/SchedulerStatsSettingsDrawer/SchedulerStatsSettingsDrawer.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/StatsIndicator/StatsIndicator.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/SchedulerBoardViewPage/generateSlotsMap.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/SchedulerBoardViewPage/generateTimePeriodOptions.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/SchedulerScheduleViewPage/generateAppointmentEvents.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/generatePerformersResources.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/generateSchedulerLinkedEntityTypeTab.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/getAppointmentStatusColor.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/getOrderItemAmount.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/SchedulerBoardViewPage/useGetSchedulerBoardPageColumns.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/SchedulerBoardViewPage/useGetSchedulerBoardPageRows.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useAppointmentHistoryServicesColumns.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useAppointmentServiceBlockColumns.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useAppointmentsHistoryColumns.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useGetBoardSchedulerBusinessHours.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useGetRepeatingAppointmentsDatesDisplay.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useGetRepeatingAppointmentsIntervalOptions.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useGetScheduleAppointmentStatuses.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useGetSchedulerPerformersBusinessHours.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/hooks/useSchedulerStatisticsFilter.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/models/AppointmentHistoryColumns/AppointmentHistoryColumnsIds.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/AppointmentHistoryColumns/AppointmentHistoryColumnsSizes.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/AppointmentHistoryServicesColumns/AppointmentHistoryServicesColumnsIds.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/AppointmentHistoryServicesColumns/AppointmentHistoryServicesColumnsSizes.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/AppointmentServiceBlockColumns/AppointmentServiceBlockColumnsIds.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/AppointmentServiceBlockColumns/AppointmentServiceBlockColumnsSizes.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/RepeatingAppointmentsParameters.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/Schedule/GetSchedulesQueryParams.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/Schedule/Schedule.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/Schedule/SchedulePerformer.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/Schedule/SchedulePerformerType.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/Schedule/ScheduleType.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/AddAppointmentPreset.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/GetScheduleAppointmentCountQueryParams.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/GetScheduleAppointmentsQueryParams.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointment.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentDuplicateError.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentEntityInfo.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentIntersectError.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentOrderItemRow.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentResult.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentStatisticsType.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentStatus.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleAppointmentsLimit.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointment/ScheduleBoardAppointmentRow.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/ScheduleAppointmentModal/ScheduleAppointmentModalTabs.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulerCardListSettings.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulerEvent/SchedulerEvent.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulerEvent/SchedulerEvents.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulerQueryParams.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulerScheduleViewSettings.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulerStatisticsFilterSettings.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/SchedulersBoardViewSettings.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/VisitParametersFormData.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/scheduler/shared/lib/types/CreateBoardAppointmentHandler.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/types/RepeatingAppointmentDateValue.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/types/RepeatingAppointmentInterval.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/types/ScheduleAppointmentsExpandParam.ts create mode 100644 frontend/src/modules/scheduler/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/scheduler/store/AddAppointmentModalStore.ts create mode 100644 frontend/src/modules/scheduler/store/AppointmentCardListPageStore.ts create mode 100644 frontend/src/modules/scheduler/store/AppointmentEventHoverCardStore.ts create mode 100644 frontend/src/modules/scheduler/store/AppointmentOrderStore.ts create mode 100644 frontend/src/modules/scheduler/store/SchedulerEventHandlerStore.ts create mode 100644 frontend/src/modules/scheduler/store/index.tsx create mode 100644 frontend/src/modules/section/api/EntitiesImportApi/EntitiesImportApi.ts create mode 100644 frontend/src/modules/section/api/EntityApi/EntityApi.ts create mode 100644 frontend/src/modules/section/api/SectionApiRoutes.ts create mode 100644 frontend/src/modules/section/api/dtos/CommonEntityCardDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/CreateSimpleEntityDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/DeleteEntitiesBatchDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/EntityListItemDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/FullEntitiesSearchResultDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/SearchByFieldFilter.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/SearchByValueResultDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/UpdateEntitiesBatchDto.ts create mode 100644 frontend/src/modules/section/api/dtos/Entity/UpdateEntityDto.ts create mode 100644 frontend/src/modules/section/api/dtos/EntityBoardCardDataDto.ts create mode 100644 frontend/src/modules/section/api/dtos/EntityBoardCardDto.ts create mode 100644 frontend/src/modules/section/api/dtos/ProjectEntityCardDto.ts create mode 100644 frontend/src/modules/section/api/dtos/index.tsx create mode 100644 frontend/src/modules/section/api/index.tsx create mode 100644 frontend/src/modules/section/index.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/EntitiesBoardSettingsPage.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/TasksBoardSettingsPage.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/components/BoardSettings/BoardSettings.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/components/BoardSettingsHeader/BoardSettingsHeader.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/components/DeleteStageWarningModal/DeleteStageWarningModal.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/components/StageColumn/StageColumn.tsx create mode 100644 frontend/src/modules/section/pages/BoardSettingsPage/components/index.tsx create mode 100644 frontend/src/modules/section/pages/EntitiesListAutomationPage/EntitiesListAutomationPage.tsx create mode 100644 frontend/src/modules/section/pages/EntitiesListPage/EntitiesListPage.tsx create mode 100644 frontend/src/modules/section/pages/EntitiesPage/EntitiesPage.tsx create mode 100644 frontend/src/modules/section/pages/EntitiesPage/components/CardsTotalBlock/CardsTotalBlock.tsx create mode 100644 frontend/src/modules/section/pages/EntitiesPage/components/ReportsSettingsButton/ReportsSettingsButton.tsx create mode 100644 frontend/src/modules/section/pages/EntitiesPage/components/index.tsx create mode 100644 frontend/src/modules/section/pages/EverythingPage/EverythingPage.tsx create mode 100644 frontend/src/modules/section/pages/ProjectsTimelinePage/ProjectsTimelinePage.tsx create mode 100644 frontend/src/modules/section/pages/index.tsx create mode 100644 frontend/src/modules/section/shared/assets/automation_bpmn_tab.svg create mode 100644 frontend/src/modules/section/shared/assets/automation_new_tab.svg create mode 100644 frontend/src/modules/section/shared/assets/close.svg create mode 100644 frontend/src/modules/section/shared/assets/delete.svg create mode 100644 frontend/src/modules/section/shared/assets/download_example.svg create mode 100644 frontend/src/modules/section/shared/assets/drag_indicator.svg create mode 100644 frontend/src/modules/section/shared/assets/excel.svg create mode 100644 frontend/src/modules/section/shared/assets/import.svg create mode 100644 frontend/src/modules/section/shared/assets/index.tsx create mode 100644 frontend/src/modules/section/shared/assets/manage_accounts.svg create mode 100644 frontend/src/modules/section/shared/assets/responsible.svg create mode 100644 frontend/src/modules/section/shared/assets/stage.svg create mode 100644 frontend/src/modules/section/shared/assets/upload.svg create mode 100644 frontend/src/modules/section/shared/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/AddCardButton/AddCardButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/EntitiesBoard.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/CardItemCommon.styles.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/EntitiesColumn/EntitiesColumn.styles.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/EntitiesColumn/EntitiesColumn.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/EntityCardItem/CommonEntityCardItem.styles.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/EntityCardItem/CommonEntityCardItem.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/EntityCardItem/CommonEntityCardItemPriceBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/ProjectEntityCardItem/ProjectEntityCardItem.styles.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/ProjectEntityCardItem/ProjectEntityCardItem.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/ProjectEntityCardItem/components/DateBlock/DateBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/ProjectEntityCardItem/components/ParticipantsBlock/ParticipantsBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/ProjectEntityCardItem/components/TaskIndicator/TaskIndicator.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/ProjectEntityCardItem/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesBoard/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/EntitiesFilterButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/EntitiesFilterDrawer/EntitiesFilterDrawer.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterBoolean.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterDate.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterExists.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterNumber.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterParticipants.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterSelect.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldFilterItems/FieldFilterString.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FieldsFilterItemSwitch/FieldsFilterItemSwitch.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/FilterSystemStagesBlock/FilterSystemStagesBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesFilterButton/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/EntitiesList.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/BatchActions.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/BatchActionsCloseButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/ChangeResponsibleAction.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/ChangeStageAction.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/DeleteAction.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/components/ActionModalBlock/ActionModalBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/components/BatchAction/BatchAction.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/components/LinkedEntitiesSelect/LinkedEntitiesSelect.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/components/SelectWrapper/SelectWrapper.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/BatchActions/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/DraggableColumnHeader/DraggableColumnHeader.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/EntitiesListAutomationColumn/EntitiesListAutomationColumn.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/SectionTable/SectionTable.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/SectionTable/SectionTableComponent.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/SectionTableSettingsDrawer/SectionTableSettingsDrawer.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/SelectAllCheckbox/SelectAllCheckbox.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/cells/FieldCell/FieldCell.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/cells/FieldUnavailable/FieldUnavailable.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/cells/NameCell/NameCell.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/cells/OwnerCell/OwnerCell.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/cells/StageCell/StageCell.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/EntitiesList/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/SearchBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/components/SearchBox/SearchBox.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/components/SearchBox/components/EntitySearchItem/EntitySearchItem.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/components/SearchBox/components/EntitySearchList/EntitySearchList.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/components/SearchBox/components/LoadMoreButton/LoadMoreButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/components/SearchBox/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SearchBlock/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionHeader/SectionHeaderControls.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionHeader/components/SectionHeaderControlsSkeleton/SectionHeaderControlsSkeleton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionHeader/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionHeaderCenterBlock/SectionHeaderCenterBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionHeaderWithBoards/SectionHeaderWithBoardsControls/SectionHeaderWithBoardsControls.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/SectionSettingsButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/Buttons/DownloadExampleButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/Buttons/IconWrapper.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/Buttons/ImportEntitiesButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/Buttons/UploadImportFileButton.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/GearIcon/GearIcon.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/IconWrapper/IconWrapper.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/ImportEntitiesModal/ImportEntitiesModal.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/ImportFileBlock/ImportFileBlock.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/ImportInBackgroundInfoModal/ImportInBackgroundInfoModal.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SectionSettingsButton/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/SwipeableContainer/SwipeableContainer.tsx create mode 100644 frontend/src/modules/section/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/helpers/EntitiesFilterButton/getCreatedAtFilter.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/EntitiesFilterButton/getEntitiesFinalSortingValue.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/EntitiesFilterButton/getEntitiesSortingOptions.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/EntitiesFilterButton/getEntityFieldFilter.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/EntitiesFilterButton/getFieldFilterFormModel.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/SectionTable/generateEntitiesListSettingsObjStorageKey.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/SectionTable/generateFieldColumnId.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/SectionTable/getDefaultEtColumnSize.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/SectionTable/getFieldName.ts create mode 100644 frontend/src/modules/section/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/hooks/SectionTable/useGetSectionTableData.ts create mode 100644 frontend/src/modules/section/shared/lib/hooks/SectionTable/useSectionTableColumns.tsx create mode 100644 frontend/src/modules/section/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/hooks/useEntitiesBoardSettingsPageTabs.tsx create mode 100644 frontend/src/modules/section/shared/lib/hooks/useEntitiesListAutomationPageTabs.tsx create mode 100644 frontend/src/modules/section/shared/lib/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/models/EntitiesFilterButton/EntitiesFilterDrawerAsyncStateProps.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntitiesFilterButton/EntityCardsFilterSettings.tsx create mode 100644 frontend/src/modules/section/shared/lib/models/EntitiesSettings/EntityListSettings.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/CommonEntityCard.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/EntityBoardCard.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/EntityBoardCardData.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/EntityForm.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/EntityListItem.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/EntitySearchFilter.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/FullEntitiesSearchResult.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/ProjectEntityCard.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/SearchByFieldResult.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/SearchByValueResult.ts create mode 100644 frontend/src/modules/section/shared/lib/models/Entity/TasksCount.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFieldFilters/EntityFieldFilter.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFieldFilters/EntityFieldFilterDate.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFieldFilters/EntityFieldFilterNumber.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFieldFilters/EntityFieldFilterString.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFieldFilters/StringFilterType.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFilter/EntityBoardCardFilter.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFilter/EntitySorting.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntityFilter/EntityTaskFilter.ts create mode 100644 frontend/src/modules/section/shared/lib/models/EntitySearchModel.ts create mode 100644 frontend/src/modules/section/shared/lib/models/SectionTable/EtTableSettings.ts create mode 100644 frontend/src/modules/section/shared/lib/models/SectionTable/EtTablesSettings.ts create mode 100644 frontend/src/modules/section/shared/lib/models/SectionTable/SectionTableColumns/SectionTableColumnsIds.ts create mode 100644 frontend/src/modules/section/shared/lib/models/SectionTable/SectionTableColumns/SectionTableColumnsSizes.ts create mode 100644 frontend/src/modules/section/shared/lib/models/SectionTable/SectionTableRow.ts create mode 100644 frontend/src/modules/section/shared/lib/models/TaskIndicator.ts create mode 100644 frontend/src/modules/section/shared/lib/models/TaskIndicatorColor.ts create mode 100644 frontend/src/modules/section/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/types/EntitiesFilterButton/FieldFilterFormType.ts create mode 100644 frontend/src/modules/section/shared/lib/types/EntitiesFilterButton/FieldFilterModelState.ts create mode 100644 frontend/src/modules/section/shared/lib/types/MinMaxColumnSize.ts create mode 100644 frontend/src/modules/section/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/section/shared/lib/utils/EntitiesPageUtil.tsx create mode 100644 frontend/src/modules/section/shared/lib/utils/EntityApiUtil.ts create mode 100644 frontend/src/modules/section/shared/lib/utils/index.tsx create mode 100644 frontend/src/modules/section/store/BoardSettingsStore.ts create mode 100644 frontend/src/modules/section/store/EntitiesCardStore.ts create mode 100644 frontend/src/modules/section/store/EntitiesFilterStore.ts create mode 100644 frontend/src/modules/section/store/EntitiesImportStore.ts create mode 100644 frontend/src/modules/section/store/EntitiesListPageStore.ts create mode 100644 frontend/src/modules/section/store/EntitiesListSettingsStore.ts create mode 100644 frontend/src/modules/section/store/ProjectsTimelinePageStore.ts create mode 100644 frontend/src/modules/section/store/index.tsx create mode 100644 frontend/src/modules/settings/api/AccountApiAccessApi/AccountApiAccessApi.ts create mode 100644 frontend/src/modules/settings/api/AccountApiAccessApi/queries/useCreateAccountApiAccess.ts create mode 100644 frontend/src/modules/settings/api/AccountApiAccessApi/queries/useGetAccountApiAccess.ts create mode 100644 frontend/src/modules/settings/api/AccountApiAccessApi/queries/useRecreateAccountApiAccess.ts create mode 100644 frontend/src/modules/settings/api/BillingLifetimePromoApi/BillingLifetimePromoApi.ts create mode 100644 frontend/src/modules/settings/api/BillingLifetimePromoApi/queries/useGetBillingLifetimePromoPrices.ts create mode 100644 frontend/src/modules/settings/api/DepartmentsSettingsApi/DepartmentsSettingsApi.ts create mode 100644 frontend/src/modules/settings/api/DepartmentsSettingsApi/queries/useGetDepartmentSettings.ts create mode 100644 frontend/src/modules/settings/api/DepartmentsSettingsApi/queries/useGetDepartmentsSettings.ts create mode 100644 frontend/src/modules/settings/api/DocumentTemplateApi/DocumentTemplateApi.ts create mode 100644 frontend/src/modules/settings/api/GoogleCalendarIntegrationApi/GoogleCalendarIntegrationApi.ts create mode 100644 frontend/src/modules/settings/api/GoogleCalendarIntegrationApi/helpers/invalidateGoogleCalendarIntegrationsInCache.ts create mode 100644 frontend/src/modules/settings/api/GoogleCalendarIntegrationApi/queries/useGetGoogleCalendarIntegrations.ts create mode 100644 frontend/src/modules/settings/api/SalesforceProviderSettingsApi/SalesforceProviderSettingsApi.ts create mode 100644 frontend/src/modules/settings/api/SettingsApiRoutes.ts create mode 100644 frontend/src/modules/settings/api/SettingsQueryKeys.ts create mode 100644 frontend/src/modules/settings/api/UserCalendarApi/UserCalendarApi.ts create mode 100644 frontend/src/modules/settings/api/UserTokensApi/UserTokensApi.ts create mode 100644 frontend/src/modules/settings/api/UserTokensApi/queries/useCreateUserAccessToken.ts create mode 100644 frontend/src/modules/settings/api/UserTokensApi/queries/useDeleteUserAccessToken.ts create mode 100644 frontend/src/modules/settings/api/UserTokensApi/queries/useGetUserAccessTokens.ts create mode 100644 frontend/src/modules/settings/api/dtos/AccountApiAccessDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Calendar/CalendarAccessDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Calendar/CalendarInfoDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Calendar/CreateGoogleCalendarDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Calendar/GoogleCalendarDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Calendar/GoogleCalendarLinkedDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Calendar/UpdateGoogleCalendarDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/CurrentDiscountDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DepartmentsSettings/CreateDepartmentDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DepartmentsSettings/DepartmentDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DepartmentsSettings/DepartmentSettingsDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DepartmentsSettings/UpdateDepartmentDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/CheckDocumentDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/CheckDocumentMissingFieldDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/CheckDocumentResultDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/CreateDocumentDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/CreateDocumentTemplateDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/DocumentTemplateDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/DocumentTemplateInfo.ts create mode 100644 frontend/src/modules/settings/api/dtos/DocumentTemplate/UpdateDocumentTemplateDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/Salesforce/CreateSalesforceSettingsDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/UserCalendar/UserCalendarDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/UserCalendar/UserCalendarIntervalDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/UserToken/CreateUserTokenDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/UserToken/UserAccessTokenDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/UserToken/UserTokenDto.ts create mode 100644 frontend/src/modules/settings/api/dtos/index.tsx create mode 100644 frontend/src/modules/settings/api/index.tsx create mode 100644 frontend/src/modules/settings/index.tsx create mode 100644 frontend/src/modules/settings/pages/AccountApiAccessPage/AccountApiAccessPage.tsx create mode 100644 frontend/src/modules/settings/pages/AccountApiAccessPage/components/ApiAccessBlock/ApiAccessBlock.tsx create mode 100644 frontend/src/modules/settings/pages/AccountApiAccessPage/components/ApiTokensList/ApiTokensList.tsx create mode 100644 frontend/src/modules/settings/pages/AccountApiAccessPage/components/CreateUserTokenBlock/CreateUserTokenBlock.tsx create mode 100644 frontend/src/modules/settings/pages/AccountApiAccessPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/CommonBillingPage.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/MyworkRequestInvoiceBillingPage.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/StripeBillingPage.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/BillingLifetimePromoPlans.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/AnnualDealPricingBlock/AnnualDealPricingBlock.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/LifetimeDealPlanBlock/LifetimeDealPlanBlock.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/MonthlyDealPricingBlock/MonthlyDealPricingBlock.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/MyworkBillingPromoPlansStyles/MyworkBillingPromoPlans.styles.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/PriceRiseCounter/PriceRiseCounter.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/PricingBlockWithUsersSelector/PricingBlockWithUsersSelector.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/BillingLifetimePromoPlans/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/CurrentSubscriptionBlock/CurrentSubscriptionBlock.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/MyworkRequestInvoiceForm/MyworkRequestInvoiceForm.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/MyworkRequestInvoiceForm/components/MyworkInvoicePdf/MyworkInvoicePdf.styles.ts create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/MyworkRequestInvoiceForm/components/MyworkInvoicePdf/MyworkInvoicePdf.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/MyworkRequestInvoiceForm/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/PaymentResultModal/PaymentResultModal.tsx create mode 100644 frontend/src/modules/settings/pages/BillingPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/DocumentCreationFieldsPage.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/EntityTypeSection/EntityTypeSection.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldBlock/FieldBlock.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldBlock/FieldBlockRoot.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldBlock/FieldName.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldBlock/OrderFieldBlock.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldBlock/SystemFieldBlock.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldCodeBlock/FieldCodeBlock.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldGroupSection/FieldGroupSection.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/FieldsGroupWrapper/FieldsGroupWrapper.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/NumberToWordSelector/NumberToWordSelector.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/OrderFields/OrderSection/OrderSection.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/OrderFields/OrderSectionExampleWrapper/OrderSectionExampleWrapper.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/OrderFields/OrderSectionTableExample/OrderSectionTableExample.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/OrderFields/OrderSectionTextParagraph/OrderSectionTextParagraph.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/ProjectFieldsBlock/ProjectFieldsBlock.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/RussianCaseSelector/RussianCaseSelector.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/SectionTemplate/SectionTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/SystemSection/SystemSection.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentCreationFieldsPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentTemplatesPage/DocumentTemplatesPage.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentTemplatesPage/components/AddDocumentTemplateButton/AddDocumentTemplateButton.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentTemplatesPage/components/DocumentTemplateItem/DocumentTemplateItem.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentTemplatesPage/components/DocumentTemplateItemSkeleton/DocumentTemplateItemSkeleton.tsx create mode 100644 frontend/src/modules/settings/pages/DocumentsPage/DocumentTemplatesPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/GeneralSettingsPage/GeneralSettingsPage.tsx create mode 100644 frontend/src/modules/settings/pages/GeneralSettingsPage/components/GeneralSettingsPageFormGroup/GeneralSettingsPageFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/GeneralSettingsPage/components/GeneralSettingsPageMainBlock/GeneralSettingsPageMainBlock.tsx create mode 100644 frontend/src/modules/settings/pages/GeneralSettingsPage/components/GeneralSettingsPageTopBlock/GeneralSettingsPageTopBlock.tsx create mode 100644 frontend/src/modules/settings/pages/GeneralSettingsPage/components/SettingsBlock/SettingsBlock.tsx create mode 100644 frontend/src/modules/settings/pages/GeneralSettingsPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/GoogleCalendarRedirectPage/GoogleCalendarRedirectPage.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/IntegrationsPage.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/AccountActivityFormGroup/AccountActivityFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/AccountAvailableFormGroup/AccountAvailableFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/ChangesNotSavedWarningModal/ChangesNotSavedWarningModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/EntitySettingsFormGroup/EntitySettingsFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationAccountItem/IntegrationAccountItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationAccountsList/IntegrationAccountsList.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationForm/IntegrationForm.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationFormGroup/IntegrationFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationInfoFeatureList/IntegrationInfoFeatureList.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationInfoLink/IntegrationInfoLink.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationInfoModalTemplate/IntegrationInfoModalTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationInfoText/IntegrationInfoText.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationInfoTitle/IntegrationInfoTitle.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationItem/IntegrationItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationManageModalTemplate/IntegrationManageModalTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationOrderedList/IntegrationOrderedList.styles.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationRadioItem/IntegrationRadioItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Albato/AlbatoItem/AlbatoItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Albato/modals/AlbatoManualModal/AlbatoManualModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/ApixDrive/ApixDriveItem/ApixDriveItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/ApixDrive/modals/ApixDriveManualModal/ApixDriveManualModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/FbMessenger/FbMessengerItem/FbMessengerItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/FbMessenger/modals/FbMessengerConnectModal/FbMessengerConnectModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/FbMessenger/modals/FbMessengerFirstInfoModal/FbMessengerFirstInfoModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/FbMessenger/modals/FbMessengerManageModal/FbMessengerManageModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/FbMessenger/modals/FbMessengerModalTemplate/FbMessengerModalTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/FbMessenger/modals/FbMessengerModalsQueue/FbMessengerModalsQueue.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/GoogleCalendarItem/GoogleCalendarItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarConnectModal/GoogleCalendarConnectModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarConnectModal/components/GoogleCalendarConnectModalForm/GoogleCalendarConnectModalForm.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarConnectModal/components/GoogleCalendarLinkedItemsGroup/GoogleCalendarLinkedItemsGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarConnectModal/components/GoogleCalendarResponsibleFormGroup/GoogleCalendarResponsibleFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarConnectModal/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarManageModal/GoogleCalendarManageModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarModalTemplate/GoogleCalendarModalTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/GoogleCalendar/modals/GoogleCalendarModalsQueue/GoogleCalendarModalsQueue.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Make/MakeItem/MakeItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Make/modals/MakeManualModal/MakeManualModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/OneC/OneCItem/OneCItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/OneC/modals/OneCManualModal/OneCManualModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/RequestIntegration/RequestIntegrationItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Salesforce/SalesforceItem/SalesforceItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Salesforce/modals/SalesforceModal/SalesforceModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Tilda/TildaItem/TildaItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Tilda/modals/TildaManualModal/TildaManualModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/TwilioWhatsAppItem/TwilioWhatsAppItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppConnectModal/TwilioWhatsAppConnectModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppFirstInfoModal/TwilioWhatsAppFirstInfoModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppManageModal/TwilioWhatsAppManageModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppModalTemplate/TwilioWhatsAppModalTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppModalsQueue/TwilioWhatsAppModalsQueue.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppSecondInfoModal/TwilioWhatsAppSecondInfoModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/TwilioWhatsApp/modals/TwilioWhatsAppThirdInfoModal/TwilioWhatsAppThirdInfoModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wazzup/WazzupItem/WazzupItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wazzup/modals/WazzupConnectModal/WazzupConnectModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wazzup/modals/WazzupFirstInfoModal/WazzupFirstInfoModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wazzup/modals/WazzupManageModal/WazzupManageModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wazzup/modals/WazzupModalTemplate/WazzupModalTemplate.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wazzup/modals/WazzupModalsQueue/WazzupModalsQueue.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wordpress/WordpressItem/WordpressItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/Integrations/Wordpress/modals/WordpressManualModal/WordpressManualModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/IntegrationsGroup/IntegrationsGroup.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/MessagesPerDaySelect/MessagesPerDaySelect.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/ProvidersSipRegistrationItemsList/ProviderSipRegistrationItem.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/ProvidersSipRegistrationItemsList/ProvidersSipRegistrationItemsList.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/ProvidersSipRegistrationItemsList/SipRegistrationGuideModal.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/TelephonyIntegrationGuide/TelephonyIntegrationGuide/TelephonyIntegrationGuide.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/TelephonyIntegrationGuide/TelephonyIntegrationGuideInternational/TelephonyIntegrationGuideInternational.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/TelephonyIntegrationGuide/TelephonyIntegrationGuideRU/TelephonyIntegrationGuideRU.tsx create mode 100644 frontend/src/modules/settings/pages/IntegrationsPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/SuperadminPage.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/components/AccountBlock/AccountBlock.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/components/AccountSearchBox/AccountSearchBox.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/components/AccountSearchBox/AccountSearchItem.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/components/AccountSearchBox/AccountSearchList.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/components/SubscriptionEditModal/SubscriptionEditModal.tsx create mode 100644 frontend/src/modules/settings/pages/SuperadminPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditDepartmentsPage/EditDepartmentsPage.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditDepartmentsPage/components/DeleteDepartmentWarningModal/DeleteDepartmentWarningModal.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditDepartmentsPage/components/DepartmentBlock/DepartmentBlock.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditDepartmentsPage/components/SubdepartmentItem/SubdepartmentItem.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditDepartmentsPage/components/SubdepartmentsList/SubdepartmentsList.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditDepartmentsPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/EditUserPage.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/EditUserFormGroup/EditUserFormGroup.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/EditUserPageGrid/EditUserPageGrid.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/ObjectPermissionsList/ObjectPermissionsList.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/ObjectPermissionsItem.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/ProductsPermissionsItem.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/components/PermissionItemColumnTitle/PermissionItemColumnTitle.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/components/PermissionItemHeader/PermissionItemHeader.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/components/PermissionItemRowTitle/PermissionItemRowTitle.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/components/WarehousesPermissionsBlock/WarehousesPermissionsBlock.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/PermissionItem/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/EditUserPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/UsersSettingsPage.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/DepartmentBlock/DepartmentBlock.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/HeaderButton/HeaderButton.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/RemoveUserModal/RemoveUserModal.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/Skeleton/UsersPageSkeleton.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/SubdepartmentBlock/SubdepartmentBlock.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/UserItem/UserItem.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/UserList/UserList.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/UserSettingsPageTitle/UserSettingsPageTitle.tsx create mode 100644 frontend/src/modules/settings/pages/UsersPage/UsersSettingsPage/components/index.tsx create mode 100644 frontend/src/modules/settings/pages/index.tsx create mode 100644 frontend/src/modules/settings/shared/assets/albato.svg create mode 100644 frontend/src/modules/settings/shared/assets/albato_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/apix_drive.svg create mode 100644 frontend/src/modules/settings/shared/assets/apix_drive_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/beeline.svg create mode 100644 frontend/src/modules/settings/shared/assets/fb_messenger_large.svg create mode 100644 frontend/src/modules/settings/shared/assets/fb_messenger_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/feature.svg create mode 100644 frontend/src/modules/settings/shared/assets/google_calendar.svg create mode 100644 frontend/src/modules/settings/shared/assets/google_calendar_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/index.tsx create mode 100644 frontend/src/modules/settings/shared/assets/key.svg create mode 100644 frontend/src/modules/settings/shared/assets/make.svg create mode 100644 frontend/src/modules/settings/shared/assets/make_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/mango_office.svg create mode 100644 frontend/src/modules/settings/shared/assets/megafon.svg create mode 100644 frontend/src/modules/settings/shared/assets/mgts.svg create mode 100644 frontend/src/modules/settings/shared/assets/mts.svg create mode 100644 frontend/src/modules/settings/shared/assets/one_c.svg create mode 100644 frontend/src/modules/settings/shared/assets/price_label.svg create mode 100644 frontend/src/modules/settings/shared/assets/rostelecom.svg create mode 100644 frontend/src/modules/settings/shared/assets/salesforce.svg create mode 100644 frontend/src/modules/settings/shared/assets/tele2.svg create mode 100644 frontend/src/modules/settings/shared/assets/telephony_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/tilda.svg create mode 100644 frontend/src/modules/settings/shared/assets/tilda_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/uis.svg create mode 100644 frontend/src/modules/settings/shared/assets/unknown.svg create mode 100644 frontend/src/modules/settings/shared/assets/wazzup_large.svg create mode 100644 frontend/src/modules/settings/shared/assets/wazzup_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/whatsapp_large.svg create mode 100644 frontend/src/modules/settings/shared/assets/whatsapp_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/wordpress.svg create mode 100644 frontend/src/modules/settings/shared/assets/wordpress_small.svg create mode 100644 frontend/src/modules/settings/shared/assets/zadarma.svg create mode 100644 frontend/src/modules/settings/shared/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/RequestSetupForm/RequestSetupFormButton.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/RequestSetupForm/components/RequestSetupFormModal/RequestSetupFormModal.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/RequestSetupForm/components/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/SettingsPageTitle/SettingsPageTitle.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/SettingsSidebar/SettingsSidebar.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/SettingsSidebarItem/SettingsSidebarItem.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/SettingsSidebarItemGroup/SettingsSidebarItemGroup.tsx create mode 100644 frontend/src/modules/settings/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/helpers/OrderFields/generateOrderItemsFields.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/OrderFields/generateOrderSpecificItemFields.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/OrderFields/generateOrderSystemFields.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/convertPriceToRussianLocaleString.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/getAllTimezoneOptions.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/getCurrenciesOptions.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/getLanguageOptions.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/getPhoneFormatOptions.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/getStatusNameWithHint.tsx create mode 100644 frontend/src/modules/settings/shared/lib/helpers/getWorkingDaysOptions.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/helpers/removeFileExtensionFromName.ts create mode 100644 frontend/src/modules/settings/shared/lib/helpers/removeSpecialChars.ts create mode 100644 frontend/src/modules/settings/shared/lib/hooks/Billing/useAppSumoTierData.ts create mode 100644 frontend/src/modules/settings/shared/lib/hooks/Billing/useGetCurrentDiscount.ts create mode 100644 frontend/src/modules/settings/shared/lib/hooks/UserToken/useUserTokenTableColumns.tsx create mode 100644 frontend/src/modules/settings/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/hooks/useGetDateFormatOptions.ts create mode 100644 frontend/src/modules/settings/shared/lib/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/models/Billing/AppSumoTierData.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Billing/LifetimeDealPricingPlan.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Calendar/CalendarAccess.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Calendar/CalendarInfo.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/CurrentDiscount.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/DepartmentSettings/Department.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/DepartmentSettings/DepartmentSettings.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/DocumentTemplate/CheckEntityDocumentResult.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/DocumentTemplate/DocumentMissingField.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Documents/DocumentType.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Documents/OrderField.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Documents/RussianCase.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Documents/SystemFieldType.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/GeneralSettings/Account.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/GeneralSettings/AccountApiAccess.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/GeneralSettings/AccountSettings.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Integrations/Calendar/CalendarLinkedProcessRadio.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Integrations/Calendar/CalendarType.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Integrations/Calendar/GoogleCalendar.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Integrations/Calendar/GoogleCalendarLinked.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Integrations/GoogleCalendarConnectModalInitialForm.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Integrations/IntegrationSettingsAccount.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/MyworkInvoicePdfData.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/ObjectPermissionModel.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/Salesforce/SalesforceSettings.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/UserCalendar/UserCalendar.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/UserCalendar/UserCalendarInterval.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/UserToken/UserAccessToken.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/UserToken/UserToken.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/constants.ts create mode 100644 frontend/src/modules/settings/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/settings/shared/lib/types/RequestSetupFormTitleKey.ts create mode 100644 frontend/src/modules/settings/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/settings/store/DepartmentsSettingsStore.ts create mode 100644 frontend/src/modules/settings/store/DocumentTemplateStore.ts create mode 100644 frontend/src/modules/settings/store/EditUserStore.ts create mode 100644 frontend/src/modules/settings/store/EntitySettingsStore.ts create mode 100644 frontend/src/modules/settings/store/FbMessenger/FbMessengerConnectModalStore.ts create mode 100644 frontend/src/modules/settings/store/FbMessenger/FbMessengerProviderSettingsStore.ts create mode 100644 frontend/src/modules/settings/store/Salesforce/SalesforceProviderSettingsStore.ts create mode 100644 frontend/src/modules/settings/store/TwilioWhatsApp/TwilioWhatsAppConnectModalStore.ts create mode 100644 frontend/src/modules/settings/store/TwilioWhatsApp/TwilioWhatsAppProviderSettingsStore.ts create mode 100644 frontend/src/modules/settings/store/UserCalendarStore.ts create mode 100644 frontend/src/modules/settings/store/Wazzup/WazzupConnectModalStore.ts create mode 100644 frontend/src/modules/settings/store/Wazzup/WazzupProviderSettingsStore.ts create mode 100644 frontend/src/modules/settings/store/index.tsx create mode 100644 frontend/src/modules/settings/templates/SettingsPageTemplate/SettingsPageTemplate.tsx create mode 100644 frontend/src/modules/settings/templates/index.tsx create mode 100644 frontend/src/modules/tasks/api/ActivityBoardApi/ActivityBoardApi.ts create mode 100644 frontend/src/modules/tasks/api/ActivityTypeApi/ActivityTypeApi.ts create mode 100644 frontend/src/modules/tasks/api/TaskApi/TaskApi.ts create mode 100644 frontend/src/modules/tasks/api/TaskBoardApi/TaskBoardApi.ts create mode 100644 frontend/src/modules/tasks/api/TaskSettingsApi/TaskSettingsApi.ts create mode 100644 frontend/src/modules/tasks/api/TasksApiRoutes.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/TasksCalendarApi.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/helpers/addTaskToCalendarCache.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/helpers/deleteTaskFromCalendarCache.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/helpers/updateTaskInCalendarCache.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useAddTaskToCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useDeleteTaskFromCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useGetActivitiesForCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useGetTasksForCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useGetTasksForCalendarCount.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useGetTimeBoardTasksForCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useGetTimeBoardTasksForCalendarCount.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useUpdateActivityInCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksCalendarApi/queries/useUpdateTaskInCalendar.ts create mode 100644 frontend/src/modules/tasks/api/TasksQueryKeys.ts create mode 100644 frontend/src/modules/tasks/api/TimeBoardApi/TimeBoardApi.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Activity/ActivityCardDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Activity/ActivityDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Activity/CreateActivityDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Activity/UpdateActivityDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/ActivityType/ActivityTypeDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/ActivityType/CreateActivityTypeDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/ActivityType/UpdateActivityTypeDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/BaseTask/BaseTaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/BaseTask/CreateBaseTaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/CreateTaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/Subtask/CreateSubtaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/Subtask/UpdateSubtaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/TaskCardDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/TaskComments/CreateTaskCommentDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/TaskComments/TaskCommentDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/TaskComments/UpdateTaskCommentDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/TaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/Task/UpdateTaskDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskFilter/ActivityCardFilterDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskFilter/BaseTaskBoardFilterDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskFilter/TaskBoardFilterDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskFilter/TimeBoardFilterDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskQueryParams/TasksForCalendarQueryParamsDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskSettings/CreateTaskSettingsDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskSettings/TaskSettingsDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskSettings/TaskSettingsIdentifier.ts create mode 100644 frontend/src/modules/tasks/api/dtos/TaskSettings/UpdateTaskSettingsDto.ts create mode 100644 frontend/src/modules/tasks/api/dtos/index.tsx create mode 100644 frontend/src/modules/tasks/api/index.tsx create mode 100644 frontend/src/modules/tasks/context/TasksCalendarContext/TasksCalendarContext.ts create mode 100644 frontend/src/modules/tasks/context/TasksCalendarContext/useTasksCalendarContext.ts create mode 100644 frontend/src/modules/tasks/context/index.tsx create mode 100644 frontend/src/modules/tasks/index.tsx create mode 100644 frontend/src/modules/tasks/pages/ActivitiesPage/ActivitiesPage.tsx create mode 100644 frontend/src/modules/tasks/pages/ActivitiesPage/components/ActivitiesPageView/ActivitiesPageView.tsx create mode 100644 frontend/src/modules/tasks/pages/ActivitiesPage/components/index.tsx create mode 100644 frontend/src/modules/tasks/pages/TasksPage/TasksPage.tsx create mode 100644 frontend/src/modules/tasks/pages/TimeBoardPage/TimeBoardPage.tsx create mode 100644 frontend/src/modules/tasks/pages/TimeBoardPage/components/TimeBoardView/TimeBoardView.tsx create mode 100644 frontend/src/modules/tasks/pages/TimeBoardPage/components/index.tsx create mode 100644 frontend/src/modules/tasks/pages/TimelinePage/TimelinePage.tsx create mode 100644 frontend/src/modules/tasks/pages/index.tsx create mode 100644 frontend/src/modules/tasks/shared/assets/account_tree.svg create mode 100644 frontend/src/modules/tasks/shared/assets/active_like.svg create mode 100644 frontend/src/modules/tasks/shared/assets/attach_file.svg create mode 100644 frontend/src/modules/tasks/shared/assets/change.svg create mode 100644 frontend/src/modules/tasks/shared/assets/close_cross.svg create mode 100644 frontend/src/modules/tasks/shared/assets/complete.svg create mode 100644 frontend/src/modules/tasks/shared/assets/copy.svg create mode 100644 frontend/src/modules/tasks/shared/assets/done.svg create mode 100644 frontend/src/modules/tasks/shared/assets/error.svg create mode 100644 frontend/src/modules/tasks/shared/assets/inactive_like.svg create mode 100644 frontend/src/modules/tasks/shared/assets/index.tsx create mode 100644 frontend/src/modules/tasks/shared/assets/load_more_arrow.svg create mode 100644 frontend/src/modules/tasks/shared/assets/plus.svg create mode 100644 frontend/src/modules/tasks/shared/assets/scale_bar.svg create mode 100644 frontend/src/modules/tasks/shared/assets/schedule.svg create mode 100644 frontend/src/modules/tasks/shared/assets/share.svg create mode 100644 frontend/src/modules/tasks/shared/assets/tick.svg create mode 100644 frontend/src/modules/tasks/shared/assets/timeline_tab.svg create mode 100644 frontend/src/modules/tasks/shared/assets/total_spend_time.svg create mode 100644 frontend/src/modules/tasks/shared/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/AddTaskButton/AddTaskButton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/BoardParticipants/BoardParticipants.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/EmptyListBlock/EmptyListBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/EntityTasksBoard/EntityTasksBoard.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/EntityTasksList/EntityTasksList.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskColumnsWrapper/TaskColumnsWrapper.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskEventAvatarBlock/TaskEventAvatarBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/ActivityItem/ActivityItem.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/TaskItem.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/AddDescriptionPlaceholder/AddDescriptionPlaceholder.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/Comments/AddComment.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/Comments/CommentItem.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/Comments/CommentsBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/CompleteButton/CompleteButton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/PlannedTimeBlock/PlannedTimeBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/SelectEntityButton/SelectEntityButton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/Skeletons/UpdateTaskModalSkeleton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/TaskControlsBlock/TaskControlsBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/UpdateTaskModal/UpdateTaskModal.styles.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/UpdateTaskModal/UpdateTaskModal.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TaskItem/TaskItem/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksBoard/TasksBoard.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/TasksCalendarView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/DayCellContent/DayCellContent.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/DayHeaderContent/DayHeaderDayView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/DayHeaderContent/DayHeaderMonthView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/DayHeaderContent/DayHeaderWeekView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/MoreLink/MoreLink.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TaskEvent/TaskEventAgendaView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TaskEvent/TaskEventGridView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendar/TasksCalendar.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendarAgendaView/TasksCalendarAgendaView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendarSidebar/TasksCalendarSidebar.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendarSidebar/components/TasksCalendarViewSelect/TasksCalendarViewSelect.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendarSidebar/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendarWrapper/TasksCalendarStylesWrapper.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/TasksCalendarWrapper/TasksCalendarWrapper.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarView/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarViewWrapper/ActivitiesPageCalendarView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarViewWrapper/TasksPageCalendarView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCalendarViewWrapper/TimeBoardPageCalendarView.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksColumn/TasksColumn.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksColumn/components/AddQuickTaskBlock/AddQuickTaskBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksColumn/components/TimeAllocationCard/TimeAllocationCard.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksColumn/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksCount/TasksCount.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksFilterButton/TasksFilterButton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksFilterButton/components/TaskEntitiesSearchBlock/TaskEntitiesSearchBlock.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksFilterButton/components/TasksFilterDrawer/TasksFilterDrawer.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksFilterButton/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/TasksList.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/TasksListSettingsDrawer/TasksListSettingsDrawer.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/TasksTable/TasksTable.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/TasksTableBody/TasksTableBody.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/TasksTableHead/TasksTableHead.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/TasksTableHeadCell/TasksTableHeadCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskActionHeaderCell/TaskActionHeaderCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskCheckboxCell/TaskCheckboxCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskCheckboxHeaderCell/TaskCheckboxHeaderCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskDeleteCell/TaskDeleteCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskEndDateCell/TaskEndDateCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskLinkedEntityCell/TaskLinkedEntityCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskPlannedTimeCell/TaskPlannedTimeCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskResponsibleCell/TaskResponsibleCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskStageCell/TaskStageCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskStartDateCell/TaskStartDateCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/cells/TaskTitleCell/TaskTitleCell.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksList/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksPageHeader/TasksPageHeader.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/TasksSettingsButton/TasksSettingsButton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/UpdateTaskDrawer/UpdateTaskDrawer.styles.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/UpdateTaskDrawer/UpdateTaskDrawer.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/UpdateTaskDrawer/components/UpdateTaskDrawerSkeleton/UpdateTaskDrawerSkeleton.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/UpdateTaskDrawer/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/calculateEndDate.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/calculateStartDate.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/findSavedCalendarFilter.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/findSavedTasksFilter.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/generateDatesRange.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/generateTaskCalendarEvents.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/generateTasksCalendarRoute.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getInitialCalendarView.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getIsTaskAllDay.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getRepeatingTaskDatesByInterval.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getTaskStatusColor.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getTaskTitle.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getTasksDefaultColumnSize.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getTasksFinalSortingValue.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/getTasksSortingOptions.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/groupEventsByDate.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/hasDescriptionUserContent.ts create mode 100644 frontend/src/modules/tasks/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/TasksList/useGetTasksListColumns.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/TasksList/useGetTasksListData.ts create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/useCalendarNavigation.ts create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/useGetAllTasksFilterItemsOptions.ts create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/useGetCalendarQueryParams.ts create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/useGetGroupOptions.ts create mode 100644 frontend/src/modules/tasks/shared/lib/hooks/useGetRepeatingTasksIntervalOptions.ts create mode 100644 frontend/src/modules/tasks/shared/lib/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/models/Activity/ActivityType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/BaseTask/BaseTask.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/BaseTask/TaskGroup.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/BaseTask/TaskView.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/BaseTask/TasksBoardType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/DeadlineType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/Task/DateStringObject.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/Task/Subtask.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/Task/TaskComment.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/AddTaskPreset.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/TaskCalendarFilterForm.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/TaskCalendarMeta.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/TaskCalendarViewType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/TaskColorType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/TaskDeadlineType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskCalendar/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskColorStyles.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskFilter/ActivityCardsFilter.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskFilter/BaseTaskBoardFilter.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskFilter/TaskBoardFilter.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskFilter/TaskSorting.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskFilter/TimeBoardFilter.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskFilterDrawerAsyncStateProps.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskHexColors.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskPalette.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskSettings/TaskFieldCode.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskSettings/TaskSettings.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskSettings/TaskSettingsType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TaskViewStyles.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksCardsFilterSettings.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksFilterType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksList/TaskRow.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksList/TasksColumnsIds.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksList/TasksColumnsSizes.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksList/TasksDefaultColumnsSizes.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksList/TasksTableSettings.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksList/TasksTablesSettings.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/TasksTab.ts create mode 100644 frontend/src/modules/tasks/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/tasks/shared/lib/models/queryParams.ts create mode 100644 frontend/src/modules/tasks/shared/lib/types/RepeatingTaskInterval.ts create mode 100644 frontend/src/modules/tasks/shared/lib/types/TaskCalendarRouteGenerator.ts create mode 100644 frontend/src/modules/tasks/shared/lib/types/TasksCalendarUseType.ts create mode 100644 frontend/src/modules/tasks/shared/lib/types/TasksCalendarViewCommonProps.ts create mode 100644 frontend/src/modules/tasks/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/tasks/store/ActivitiesPageStore.ts create mode 100644 frontend/src/modules/tasks/store/ActivityTypeStore.ts create mode 100644 frontend/src/modules/tasks/store/CalendarServerEventServiceStore.ts create mode 100644 frontend/src/modules/tasks/store/CalendarViewStore.tsx create mode 100644 frontend/src/modules/tasks/store/TaskCommentsStore.ts create mode 100644 frontend/src/modules/tasks/store/TaskSettingsStore.ts create mode 100644 frontend/src/modules/tasks/store/TasksBoardPageStore.ts create mode 100644 frontend/src/modules/tasks/store/TasksFilterStore.ts create mode 100644 frontend/src/modules/tasks/store/TasksGroupStore.ts create mode 100644 frontend/src/modules/tasks/store/TasksListPageStore.ts create mode 100644 frontend/src/modules/tasks/store/TasksStore.ts create mode 100644 frontend/src/modules/tasks/store/TasksTimelinePageStore.ts create mode 100644 frontend/src/modules/tasks/store/TimeBoardPageStore.ts create mode 100644 frontend/src/modules/tasks/store/UpdateTaskModalStore.ts create mode 100644 frontend/src/modules/tasks/store/index.tsx create mode 100644 frontend/src/modules/tasks/templates/ItemTemplate/ItemTemplate.tsx create mode 100644 frontend/src/modules/tasks/templates/ItemTemplate/components/ItemTemplate.styles.tsx create mode 100644 frontend/src/modules/tasks/templates/ItemTemplate/components/index.tsx create mode 100644 frontend/src/modules/tasks/templates/TasksBoardPageTemplate/TasksBoardPageTemplate.tsx create mode 100644 frontend/src/modules/tasks/templates/TasksPageTemplate/TasksPageTemplate.tsx create mode 100644 frontend/src/modules/tasks/templates/index.tsx create mode 100644 frontend/src/modules/telephony/api/TelephonyApiRoutes.ts create mode 100644 frontend/src/modules/telephony/api/TelephonyQueryKeys.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/VoximplantApi.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/queries/useCreateVoximplantUser.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/queries/useDeleteVoximplantUser.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/queries/useGetVoximplantCalls.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/queries/useGetVoximplantUserSIPSettings.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/queries/useGetVoximplantUsers.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantApi/queries/usePatchVoximplantUser.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/VoximplantNumbersApi.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/helpers/invalidateVoximplantPhoneNumbersInCache.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/queries/useCreateVoximplantPhoneNumber.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/queries/useDeleteVoximplantPhoneNumber.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/queries/useGetVoximplantAvailablePhoneNumbers.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/queries/useGetVoximplantPhoneNumbers.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantNumbersApi/queries/useUpdateVoximplantPhoneNumber.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/VoximplantSIPApi.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/helpers/invalidateVoximplantSIPRegistrationsCache.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/queries/useCreateVoximplantSIPRegistration.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/queries/useDeleteVoximplantSIPRegistration.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/queries/useGetVoximplantSIPRegistrationByExternalId.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/queries/useGetVoximplantSIPRegistrations.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/queries/useGetVoximplantSIPRegistrationsExpanded.ts create mode 100644 frontend/src/modules/telephony/api/VoximplantSIPApi/queries/useUpdateVoximplantSIPRegistration.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/CreateVoximplantUserDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/UpdateVoximplantCallDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/UpdateVoximplantUserDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/VoximplantAccountDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/VoximplantCallDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/VoximplantCallListDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/VoximplantSIPDataDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/Voximplant/VoximplantUserDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantNumbers/CreateVoximplantNumberDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantNumbers/PhoneNumberDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantNumbers/UpdateVoximplantNumberDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantNumbers/VoximplantNumberDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantSIP/CreateVoximplantSIPDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantSIP/UpdateVoximplantSIPDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantSIP/VoximplantSIPDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantSIP/VoximplantSIPRegistrationDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantScenarios/VoximplantScenarioEntityDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantScenarios/VoximplantScenarioNoteDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantScenarios/VoximplantScenarioTaskDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/VoximplantScenarios/VoximplantScenariosDto.ts create mode 100644 frontend/src/modules/telephony/api/dtos/index.tsx create mode 100644 frontend/src/modules/telephony/api/index.tsx create mode 100644 frontend/src/modules/telephony/context/TelephonyContext/TelephonyContext.ts create mode 100644 frontend/src/modules/telephony/context/TelephonyContext/TelephonyProvider.tsx create mode 100644 frontend/src/modules/telephony/context/TelephonyContext/useTelephonyContext.ts create mode 100644 frontend/src/modules/telephony/context/index.tsx create mode 100644 frontend/src/modules/telephony/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/CallsConfiguringScenariosPage.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/CallTypeBlock/CallTypeBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/CallTypeGroup/CallTypeGroup.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/ConfiguringScenariosHeaderControls/ConfiguringScenariosHeaderControls.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/EntityScenarioRadioGroup/EntityScenarioGroup.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/IncomingCallsBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/IncomingKnownMissingScenarioBlock/IncomingKnownMissingScenarioBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/IncomingUnknownMissingScenarioBlock/IncomingUnknownMissingScenarioBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/IncomingUnknownScenarioBlock/IncomingUnknownScenarioBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/TaskAndActivitiesScenarioGroup/TaskAndActivitiesScenarioGroup.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/TaskAndActivitiesScenarioGroup/components/MinutesInput/MinutesInput.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/TaskAndActivitiesScenarioGroup/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/IncomingCallsBlock/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/OutgoingCallsBlock/OutgoingCallsBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/OutgoingCallsBlock/components/OutgoingUnansweredScenarioBlock/OutgoingUnansweredScenarioBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/OutgoingCallsBlock/components/OutgoingUnknownScenarioBlock/OutgoingUnknownScenarioBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/OutgoingCallsBlock/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/RadioBlockWrapper/RadioBlockWrapper.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/SelectWrapper/SelectWrapper.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/SelectWrapperTemplate/SelectWrapperTemplate.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/SelectsWrapper/SelectsWrapper.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsConfiguringScenariosPage/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/CallsSettingsAccountPage.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/AccountInfoBlock/AccountInfoBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantBalance/VoximplantBalance.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantNumbersBlock/VoximplantNumbersBlock/VoximplantNumbersBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantNumbersBlock/VoximplantNumbersBlockTable/VoximplantNumbersBlockTable.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantNumbersBlock/cells/DeleteVoximplantNumberWarningModal/DeleteVoximplantNumberWarningModal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantNumbersBlock/cells/VoximplantNumbersBlockStateCell/VoximplantNumbersBlockStateCell.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantNumbersBlock/cells/VoximplantNumbersBlockUsersCell/VoximplantNumbersBlockUsersCell.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/VoximplantSubscriptionFee/VoximplantSubscriptionFee.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsAccountPage/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/CallsSettingsUsersPage.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/AddCallsUserModal/AddCallsUserModal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/CallsSettingsUsersPageTitle/CallsSettingsUsersPageTitle.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/RemoveCallsUserWarningModal/RemoveCallsUserWarningModal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyDepartmentBlock/TelephonyDepartmentBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonySubdepartmentBlock/TelephonySubdepartmentBlock.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyUserItem/TelephonyUserItem.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyUserItem/components/UserSIPSettingsFormItem/UserSIPSettingsFormItem.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyUserItem/components/UserSIPSettingsModal/UserSIPSettingsModal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyUserItem/components/UserSIPSettingsModalSkeleton/UserSIPSettingsModalSkeleton.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyUserItem/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/TelephonyUsersList/TelephonyUsersList.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSettingsUsersPage/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/CallsSipRegistrationsPage.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/AddSipRegistrationModal/AddSipRegistrationModal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/CallsSipRegistrationsPageTitle/CallsSipRegistrationsPageTitle.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/DeleteSIPRegistrationWarningModal/DeleteSIPRegistrationWarningModal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/LinkToVoximplantSipRegistrationsPortal/LinkToVoximplantSipRegistrationsPortal.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/SipRegistrationItem/SipRegistrationItem.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/SipRegistrationModalFormItem/SipRegistrationModalFormItem.tsx create mode 100644 frontend/src/modules/telephony/pages/CallsSipRegistrationsPage/components/index.tsx create mode 100644 frontend/src/modules/telephony/pages/index.tsx create mode 100644 frontend/src/modules/telephony/shared/assets/add_to_call_large.svg create mode 100644 frontend/src/modules/telephony/shared/assets/add_to_call_small.svg create mode 100644 frontend/src/modules/telephony/shared/assets/backspace.svg create mode 100644 frontend/src/modules/telephony/shared/assets/beeline_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/call_large.svg create mode 100644 frontend/src/modules/telephony/shared/assets/call_medium.svg create mode 100644 frontend/src/modules/telephony/shared/assets/call_small.svg create mode 100644 frontend/src/modules/telephony/shared/assets/close_small.svg create mode 100644 frontend/src/modules/telephony/shared/assets/entity_link.svg create mode 100644 frontend/src/modules/telephony/shared/assets/fold.svg create mode 100644 frontend/src/modules/telephony/shared/assets/hang_large.svg create mode 100644 frontend/src/modules/telephony/shared/assets/hang_small.svg create mode 100644 frontend/src/modules/telephony/shared/assets/index.tsx create mode 100644 frontend/src/modules/telephony/shared/assets/mango_office_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/megafon_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/mgts_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/mts_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/mute_large.svg create mode 100644 frontend/src/modules/telephony/shared/assets/mute_small.svg create mode 100644 frontend/src/modules/telephony/shared/assets/recent_call_incoming.svg create mode 100644 frontend/src/modules/telephony/shared/assets/recent_call_outgoing.svg create mode 100644 frontend/src/modules/telephony/shared/assets/redirect_large.svg create mode 100644 frontend/src/modules/telephony/shared/assets/redirect_small.svg create mode 100644 frontend/src/modules/telephony/shared/assets/rostelecom_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/tele2_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/uis_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/unfold.svg create mode 100644 frontend/src/modules/telephony/shared/assets/unknown_mini.svg create mode 100644 frontend/src/modules/telephony/shared/assets/zadarma_mini.svg create mode 100644 frontend/src/modules/telephony/shared/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/AcceptCallButton/AcceptCallButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/AddToCallButton/AddToCallButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/CallButton/CallButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/HangUpButton/HangUpButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/MuteButton/MuteButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/TelephonyFunctionalButton/TelephonyFunctionalButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/TelephonyModuleButton/TelephonyModuleButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/TransferButton/TransferButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/TransferButton/components/TransferConfirmationControlsDropdown/TransferConfirmationControls.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/Buttons/TransferButton/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/TelephonyModal.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/ActiveCallControl/ActiveCallControl.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/CallControlTemplate.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/components/CreateEntityBlock/CreateEntityBlock.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/components/EllipsisAnimationBlock/EllipsisAnimationBlock.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/components/EntitiesLinksBlock/EntitiesLinksBlock.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/components/EntityLink/EntityLink.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/components/PhoneNumberTitle/PhoneNumberTitle.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/CallControlTemplate/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/DraggableTelephonyControl/DraggableTelephonyControl.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/IncomingCallControl/IncomingCallControl.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/OutgoingCallInitializer.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/BackspaceButton/BackspaceButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/DigitButton/DigitButton.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/OutgoingCallInitializerTabsListHeader/OutgoingCallInitializerTabsListHeader.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/KeysTab/KeysTab.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/KeysTab/components/PotentialEntitiesLinksBlock/PotentialEntitiesLinksBlock.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/KeysTab/components/PotentialEntityLink/PotentialEntityLink.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/KeysTab/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/RecentCallsTab/RecentCallsTab.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/RecentCallsTab/components/RecentCall/RecentCall.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/OutgoingCallInitializer/components/tabs/RecentCallsTab/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/TelephonyModalHeader/TelephonyModalHeader.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/TelephonyModalHeader/components/HeaderControlButtonBase/HeaderControlButtonBase.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/TelephonyModalHeader/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/TelephonyModal/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/helpers/formatCallTime.ts create mode 100644 frontend/src/modules/telephony/shared/lib/helpers/formatTelephonyPhoneNumber.ts create mode 100644 frontend/src/modules/telephony/shared/lib/helpers/getMiniPbxIconByType.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/helpers/getRecentCallsGroups.ts create mode 100644 frontend/src/modules/telephony/shared/lib/helpers/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/hooks/VoximplantNumbersBlock/useGetVoximplantNumbersBlockData.ts create mode 100644 frontend/src/modules/telephony/shared/lib/hooks/VoximplantNumbersBlock/useVoximplantNumbersBlockColumns.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/hooks/useFormatRecentCallDate.ts create mode 100644 frontend/src/modules/telephony/shared/lib/hooks/usePhoneCallTimer.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/models/CallDirection.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/CallStatus.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/RecentCallsGroup.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/TelephonyModal/CallFromNumber.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/TelephonyModal/CallFromSipRegId.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/TelephonyModal/OutgoingCallInitializerTabs.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/TelephonyModal/TelephonyFunctionalButtonProps.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/TelephonyModal/TelephonyModalPosition.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/TelephonyModal/TelephonyModalSize.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/Voximplant/VoximplantAccount.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/Voximplant/VoximplantCall.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/Voximplant/VoximplantCallList.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/Voximplant/VoximplantSIPData.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/Voximplant/VoximplantUser.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantNumbers/PhoneNumber.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantNumbers/VoximplantNumber.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantNumbers/VoximplantNumberBlockColumnsIds.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantNumbers/VoximplantNumberRow.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantNumbersSettings.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantSIP/PbxProviderType.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantSIP/VoximplantSIP.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantSIP/VoximplantSIPRegistration.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/ScenarioAutoCreateState.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/ScenarioType.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/TaskAndActivityCreateMode.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/VoximplantScenarioEntity.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/VoximplantScenarioNote.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/VoximplantScenarioTask.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/VoximplantScenarios/VoximplantScenarios.ts create mode 100644 frontend/src/modules/telephony/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/models/voximplantNumbersSettingsKey.ts create mode 100644 frontend/src/modules/telephony/shared/lib/types/TelephonyButtonSize.ts create mode 100644 frontend/src/modules/telephony/shared/lib/types/TelephonyModal/SetLastCallFromHandler.ts create mode 100644 frontend/src/modules/telephony/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/telephony/shared/lib/utils/ModalBoundsUtil.ts create mode 100644 frontend/src/modules/telephony/shared/lib/utils/index.tsx create mode 100644 frontend/src/modules/telephony/store/IncomingFormDatas/IncomingCallsFormDatas.ts create mode 100644 frontend/src/modules/telephony/store/IncomingFormDatas/IncomingKnownMissingFormData.ts create mode 100644 frontend/src/modules/telephony/store/IncomingFormDatas/IncomingUnknownFormData.ts create mode 100644 frontend/src/modules/telephony/store/IncomingFormDatas/IncomingUnknownMissingFormData.ts create mode 100644 frontend/src/modules/telephony/store/IncomingFormDatas/TaskAndActivityFormData.ts create mode 100644 frontend/src/modules/telephony/store/OutgoingFormDatas/OutgoingCallsFormDatas.ts create mode 100644 frontend/src/modules/telephony/store/OutgoingFormDatas/OutgoingUnansweredFormData.ts create mode 100644 frontend/src/modules/telephony/store/OutgoingFormDatas/OutgoingUnknownFormData.ts create mode 100644 frontend/src/modules/telephony/store/VoximplantConnectorStore.ts create mode 100644 frontend/src/modules/telephony/store/VoximplantScenariosStore.ts create mode 100644 frontend/src/modules/telephony/store/index.tsx create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/TutorialApi.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/helpers/invalidateGetExpandedTutorialGroupsQuery.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useCreateTutorialGroup.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useCreateTutorialItem.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useDeleteTutorialGroup.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useDeleteTutorialItem.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useGetExpandedTutorialGroups.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useGetTutorialCount.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useUpdateTutorialGroupName.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApi/queries/useUpdateTutorialItem.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialApiRoutes.ts create mode 100644 frontend/src/modules/tutorial/api/TutorialQueryKeys.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/SortOrderDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/SortOrderListDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/TutorialGroup/CreateTutorialGroupDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/TutorialGroup/TutorialGroupDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/TutorialGroup/UpdateTutorialGroupDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/TutorialItem/CreateTutorialItemDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/TutorialItem/TutorialItemDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/TutorialItem/UpdateTutorialItemDto.ts create mode 100644 frontend/src/modules/tutorial/api/dtos/index.tsx create mode 100644 frontend/src/modules/tutorial/api/index.tsx create mode 100644 frontend/src/modules/tutorial/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/assets/caret_down.svg create mode 100644 frontend/src/modules/tutorial/shared/assets/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/assets/tutorial_button.svg create mode 100644 frontend/src/modules/tutorial/shared/assets/tutorial_empty.svg create mode 100644 frontend/src/modules/tutorial/shared/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialButton/TutorialButton.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/TutorialDrawer.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerContent/TutorialDrawerContent.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerContent/components/TutorialGroupBlock/TutorialGroupBlock.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerContent/components/TutorialItemBlock/TutorialItemBlock.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerContent/components/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/TutorialDrawerEditMode.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/CreateTutorialGroupBlock/CreateTutorialGroupBlock.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/CreateTutorialItemBlock/CreateTutorialItemBlock.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/TutorialEditGroupForm/TutorialEditGroupForm.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/TutorialEditGroupItemForm/TutorialEditGroupItemForm.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/TutorialEditGroupItemsForms/TutorialEditGroupItemsForms.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/TutorialGroupItemFormInput/TutorialGroupItemFormInput.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/TutorialGroupNameBlock/TutorialGroupNameBlock.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEditMode/components/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/TutorialDrawerEmpty/TutorialDrawerEmpty.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/TutorialDrawer/components/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/components/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/hooks/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/hooks/useGetTutorialProductsGroups.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/hooks/useGetTutorialProductsOptions.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/hooks/useTutorialItemOutlined.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialGroup/TutorialGroup.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialGroup/TutorialGroupForm.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialItem/TutorialItem.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialItem/TutorialItemForm.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialItem/TutorialItemProduct.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialSettings/TutorialGroupSettings.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialSettings/TutorialGroupsSettings.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialSettings/TutorialGroupsSettingsKey.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialSettings/TutorialLastOpenedProduct.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialSettings/TutorialSettings.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/TutorialSettings/TutorialSettingsKey.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/models/index.tsx create mode 100644 frontend/src/modules/tutorial/shared/lib/types/editModeStoreHandlers.ts create mode 100644 frontend/src/modules/tutorial/shared/lib/types/index.tsx create mode 100644 frontend/src/modules/tutorial/store/TutorialEditModeStore.ts create mode 100644 frontend/src/modules/tutorial/store/index.tsx create mode 100644 frontend/src/shared/assets/docs-images/branches_example.png create mode 100644 frontend/src/shared/assets/docs-images/commit_prefixes.png create mode 100644 frontend/src/shared/assets/docs-images/deploy_storybook.png create mode 100644 frontend/src/shared/assets/docs-images/index.tsx create mode 100644 frontend/src/shared/assets/docs-images/project_and_task_board_relation.png create mode 100644 frontend/src/shared/assets/docs-images/requests_example.png create mode 100644 frontend/src/shared/assets/docs-images/vercel_preview.png create mode 100644 frontend/src/shared/assets/icons/actions.svg create mode 100644 frontend/src/shared/assets/icons/activities_board.svg create mode 100644 frontend/src/shared/assets/icons/add_blue.svg create mode 100644 frontend/src/shared/assets/icons/add_circle.svg create mode 100644 frontend/src/shared/assets/icons/add_circled.svg create mode 100644 frontend/src/shared/assets/icons/add_photo.svg create mode 100644 frontend/src/shared/assets/icons/add_square.svg create mode 100644 frontend/src/shared/assets/icons/add_user.svg create mode 100644 frontend/src/shared/assets/icons/all_chats.svg create mode 100644 frontend/src/shared/assets/icons/all_chats_grey.svg create mode 100644 frontend/src/shared/assets/icons/arrow_back.svg create mode 100644 frontend/src/shared/assets/icons/arrow_back_small.svg create mode 100644 frontend/src/shared/assets/icons/arrow_drop_down.svg create mode 100644 frontend/src/shared/assets/icons/avito.svg create mode 100644 frontend/src/shared/assets/icons/avito_grey.svg create mode 100644 frontend/src/shared/assets/icons/big_plus.svg create mode 100644 frontend/src/shared/assets/icons/block_file_doc.svg create mode 100644 frontend/src/shared/assets/icons/block_file_fallback.svg create mode 100644 frontend/src/shared/assets/icons/block_file_pdf.svg create mode 100644 frontend/src/shared/assets/icons/broken_player.svg create mode 100644 frontend/src/shared/assets/icons/builder/box.svg create mode 100644 frontend/src/shared/assets/icons/builder/briefcase.svg create mode 100644 frontend/src/shared/assets/icons/builder/building_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/building_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/building_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/bulb.svg create mode 100644 frontend/src/shared/assets/icons/builder/burger.svg create mode 100644 frontend/src/shared/assets/icons/builder/calendar_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/calendar_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/calendar_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/calendar_4.svg create mode 100644 frontend/src/shared/assets/icons/builder/call.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/albato.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/apix_drive.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/automatisation.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/companies.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/contacts.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/contractors.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/crm.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/documents.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/facebook.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/finances.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/forms.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/hr.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/hundred_gb.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/index.tsx create mode 100644 frontend/src/shared/assets/icons/builder/cards/mailing.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/make.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/marketing.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/messenger.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/one_c.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/one_tb.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/partners.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/production.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/projects.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/rent.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/salesforce.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/scheduler.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/suppliers.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/telephony.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/ten_gb.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/tilda.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/universal.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/warehouse.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/website_chat.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/whatsapp.svg create mode 100644 frontend/src/shared/assets/icons/builder/cards/wordpress.svg create mode 100644 frontend/src/shared/assets/icons/builder/chart_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/chart_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/clock_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/clock_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/crown.svg create mode 100644 frontend/src/shared/assets/icons/builder/equalizer.svg create mode 100644 frontend/src/shared/assets/icons/builder/hand.svg create mode 100644 frontend/src/shared/assets/icons/builder/in_out.svg create mode 100644 frontend/src/shared/assets/icons/builder/index.tsx create mode 100644 frontend/src/shared/assets/icons/builder/lightning.svg create mode 100644 frontend/src/shared/assets/icons/builder/mail.svg create mode 100644 frontend/src/shared/assets/icons/builder/product_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/product_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/rocket_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/rocket_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/rocket_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/settings_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/settings_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/settings_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/settings_4.svg create mode 100644 frontend/src/shared/assets/icons/builder/shapes_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/shapes_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/shapes_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/shapes_4.svg create mode 100644 frontend/src/shared/assets/icons/builder/site_form.svg create mode 100644 frontend/src/shared/assets/icons/builder/sound.svg create mode 100644 frontend/src/shared/assets/icons/builder/star_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/star_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/star_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/target_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/target_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/tick_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/tick_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/tie_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/tie_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/user_1.svg create mode 100644 frontend/src/shared/assets/icons/builder/user_2.svg create mode 100644 frontend/src/shared/assets/icons/builder/user_3.svg create mode 100644 frontend/src/shared/assets/icons/builder/user_4.svg create mode 100644 frontend/src/shared/assets/icons/calendar.svg create mode 100644 frontend/src/shared/assets/icons/calendar_end_date.svg create mode 100644 frontend/src/shared/assets/icons/caret.svg create mode 100644 frontend/src/shared/assets/icons/caret_down.svg create mode 100644 frontend/src/shared/assets/icons/check.svg create mode 100644 frontend/src/shared/assets/icons/check_done.svg create mode 100644 frontend/src/shared/assets/icons/checkbox.svg create mode 100644 frontend/src/shared/assets/icons/checkmark.svg create mode 100644 frontend/src/shared/assets/icons/checkout.svg create mode 100644 frontend/src/shared/assets/icons/chevron_left.svg create mode 100644 frontend/src/shared/assets/icons/clear.svg create mode 100644 frontend/src/shared/assets/icons/clear_circled.svg create mode 100644 frontend/src/shared/assets/icons/clear_cross.svg create mode 100644 frontend/src/shared/assets/icons/clear_cross_x_small.svg create mode 100644 frontend/src/shared/assets/icons/clear_participant.svg create mode 100644 frontend/src/shared/assets/icons/clear_select.svg create mode 100644 frontend/src/shared/assets/icons/clip_large.svg create mode 100644 frontend/src/shared/assets/icons/clip_medium.svg create mode 100644 frontend/src/shared/assets/icons/clip_small.svg create mode 100644 frontend/src/shared/assets/icons/close_modal.svg create mode 100644 frontend/src/shared/assets/icons/copy_content.svg create mode 100644 frontend/src/shared/assets/icons/copy_content_white.svg create mode 100644 frontend/src/shared/assets/icons/create.svg create mode 100644 frontend/src/shared/assets/icons/cross-red.svg create mode 100644 frontend/src/shared/assets/icons/dashed_frame_large.svg create mode 100644 frontend/src/shared/assets/icons/dashed_frame_medium.svg create mode 100644 frontend/src/shared/assets/icons/delete_demo.svg create mode 100644 frontend/src/shared/assets/icons/delete_tag.svg create mode 100644 frontend/src/shared/assets/icons/dots_medium.svg create mode 100644 frontend/src/shared/assets/icons/dots_small.svg create mode 100644 frontend/src/shared/assets/icons/download_medium.svg create mode 100644 frontend/src/shared/assets/icons/download_small.svg create mode 100644 frontend/src/shared/assets/icons/drag.svg create mode 100644 frontend/src/shared/assets/icons/drag_field.svg create mode 100644 frontend/src/shared/assets/icons/emoji_medium.svg create mode 100644 frontend/src/shared/assets/icons/emoji_small.svg create mode 100644 frontend/src/shared/assets/icons/envelope.svg create mode 100644 frontend/src/shared/assets/icons/expand.svg create mode 100644 frontend/src/shared/assets/icons/facebook.svg create mode 100644 frontend/src/shared/assets/icons/facebook_grey.svg create mode 100644 frontend/src/shared/assets/icons/file_audio.svg create mode 100644 frontend/src/shared/assets/icons/file_document.svg create mode 100644 frontend/src/shared/assets/icons/file_image.svg create mode 100644 frontend/src/shared/assets/icons/file_video.svg create mode 100644 frontend/src/shared/assets/icons/files/file_doc.svg create mode 100644 frontend/src/shared/assets/icons/files/file_img.svg create mode 100644 frontend/src/shared/assets/icons/files/file_pdf.svg create mode 100644 frontend/src/shared/assets/icons/files/file_ppt.svg create mode 100644 frontend/src/shared/assets/icons/files/file_xls.svg create mode 100644 frontend/src/shared/assets/icons/filled_arrow_down.svg create mode 100644 frontend/src/shared/assets/icons/filter.svg create mode 100644 frontend/src/shared/assets/icons/format.svg create mode 100644 frontend/src/shared/assets/icons/gear.svg create mode 100644 frontend/src/shared/assets/icons/google_logo.svg create mode 100644 frontend/src/shared/assets/icons/google_logo_disabled.svg create mode 100644 frontend/src/shared/assets/icons/green_plus_filled.svg create mode 100644 frontend/src/shared/assets/icons/group.svg create mode 100644 frontend/src/shared/assets/icons/horizontal_dots.svg create mode 100644 frontend/src/shared/assets/icons/index.tsx create mode 100644 frontend/src/shared/assets/icons/info_large.svg create mode 100644 frontend/src/shared/assets/icons/info_medium.svg create mode 100644 frontend/src/shared/assets/icons/info_small.svg create mode 100644 frontend/src/shared/assets/icons/instagram.svg create mode 100644 frontend/src/shared/assets/icons/instagram_grey.svg create mode 100644 frontend/src/shared/assets/icons/link.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_amwork.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_amwork_text.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_flower.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_flower_round.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_flower_round_grey.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_mywork.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_mywork_text.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_platforma500.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_platforma500_text.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_proma.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_proma_minified_inverted.svg create mode 100644 frontend/src/shared/assets/icons/logos/logo_proma_text.svg create mode 100644 frontend/src/shared/assets/icons/mail_tab.svg create mode 100644 frontend/src/shared/assets/icons/menu.svg create mode 100644 frontend/src/shared/assets/icons/minus.svg create mode 100644 frontend/src/shared/assets/icons/modal_close_cross_primary.svg create mode 100644 frontend/src/shared/assets/icons/modal_close_cross_secondary.svg create mode 100644 frontend/src/shared/assets/icons/modal_close_cross_tertiary.svg create mode 100644 frontend/src/shared/assets/icons/modal_remove_user.svg create mode 100644 frontend/src/shared/assets/icons/modal_trashbin.svg create mode 100644 frontend/src/shared/assets/icons/modal_warning.svg create mode 100644 frontend/src/shared/assets/icons/more.svg create mode 100644 frontend/src/shared/assets/icons/multi_messenger.svg create mode 100644 frontend/src/shared/assets/icons/multichat_tab.svg create mode 100644 frontend/src/shared/assets/icons/not_found.svg create mode 100644 frontend/src/shared/assets/icons/outlined_input_error.svg create mode 100644 frontend/src/shared/assets/icons/pause.svg create mode 100644 frontend/src/shared/assets/icons/pencil_medium.svg create mode 100644 frontend/src/shared/assets/icons/pencil_small.svg create mode 100644 frontend/src/shared/assets/icons/percent.svg create mode 100644 frontend/src/shared/assets/icons/play.svg create mode 100644 frontend/src/shared/assets/icons/plus.svg create mode 100644 frontend/src/shared/assets/icons/plus_outlined.svg create mode 100644 frontend/src/shared/assets/icons/plus_primary.svg create mode 100644 frontend/src/shared/assets/icons/plus_secondary.svg create mode 100644 frontend/src/shared/assets/icons/project_board.svg create mode 100644 frontend/src/shared/assets/icons/scrollbar_arrow.svg create mode 100644 frontend/src/shared/assets/icons/search.svg create mode 100644 frontend/src/shared/assets/icons/search_small.svg create mode 100644 frontend/src/shared/assets/icons/selected.svg create mode 100644 frontend/src/shared/assets/icons/selected_group.svg create mode 100644 frontend/src/shared/assets/icons/settings_two_lines.svg create mode 100644 frontend/src/shared/assets/icons/settings_x_small.svg create mode 100644 frontend/src/shared/assets/icons/solid_frame_large.svg create mode 100644 frontend/src/shared/assets/icons/solid_frame_medium.svg create mode 100644 frontend/src/shared/assets/icons/subgroup.svg create mode 100644 frontend/src/shared/assets/icons/subheader_settings.svg create mode 100644 frontend/src/shared/assets/icons/supervisor_star.svg create mode 100644 frontend/src/shared/assets/icons/table_settings.svg create mode 100644 frontend/src/shared/assets/icons/tabs/board_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/calendar_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/dashboard_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/index.tsx create mode 100644 frontend/src/shared/assets/icons/tabs/list_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/overview_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/products_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/reports_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/shipments_tab.svg create mode 100644 frontend/src/shared/assets/icons/tabs/tasks_calendar_tab.svg create mode 100644 frontend/src/shared/assets/icons/target.svg create mode 100644 frontend/src/shared/assets/icons/tasks_board.svg create mode 100644 frontend/src/shared/assets/icons/telegram.svg create mode 100644 frontend/src/shared/assets/icons/telegram_grey.svg create mode 100644 frontend/src/shared/assets/icons/textarea_save.svg create mode 100644 frontend/src/shared/assets/icons/time_board.svg create mode 100644 frontend/src/shared/assets/icons/time_from.svg create mode 100644 frontend/src/shared/assets/icons/time_management.svg create mode 100644 frontend/src/shared/assets/icons/trashbin_medium.svg create mode 100644 frontend/src/shared/assets/icons/trashbin_small.svg create mode 100644 frontend/src/shared/assets/icons/union.svg create mode 100644 frontend/src/shared/assets/icons/universal_file.svg create mode 100644 frontend/src/shared/assets/icons/upload.svg create mode 100644 frontend/src/shared/assets/icons/user.svg create mode 100644 frontend/src/shared/assets/icons/visibility_off.svg create mode 100644 frontend/src/shared/assets/icons/visibility_on.svg create mode 100644 frontend/src/shared/assets/icons/vk.svg create mode 100644 frontend/src/shared/assets/icons/vk_grey.svg create mode 100644 frontend/src/shared/assets/icons/warning.svg create mode 100644 frontend/src/shared/assets/icons/whatsapp.svg create mode 100644 frontend/src/shared/assets/icons/whatsapp_grey.svg create mode 100644 frontend/src/shared/assets/index.tsx create mode 100644 frontend/src/shared/docs/Intro.mdx create mode 100644 frontend/src/shared/docs/Organisation.mdx create mode 100644 frontend/src/shared/docs/Pipelines.mdx create mode 100644 frontend/src/shared/docs/Structure.mdx create mode 100644 frontend/src/shared/docs/Styles.mdx create mode 100644 frontend/src/shared/docs/bpmn/Схема BPMN.png create mode 100644 frontend/src/shared/docs/code-style/ModelsAndDtos.mdx create mode 100644 frontend/src/shared/docs/code-style/WorkWithApi.mdx create mode 100644 frontend/src/shared/docs/fields/Устройство полей.png create mode 100644 frontend/src/shared/docs/modules/Builder.mdx create mode 100644 frontend/src/shared/docs/onboarding/Backend.mdx create mode 100644 frontend/src/shared/docs/onboarding/Frontend.mdx create mode 100644 frontend/src/shared/docs/telephony/Номер VS SIP-регистрация.png create mode 100644 frontend/src/shared/docs/telephony/Общая информация о телефонии create mode 100644 frontend/src/shared/docs/telephony/План по подключению телефонии.png create mode 100644 frontend/src/shared/docs/telephony/Подключение телефонии со стороны клиента.png create mode 100644 frontend/src/shared/docs/telephony/Порядок входа в Voximplant и путь после входа.png create mode 100644 frontend/src/shared/hoc/RequireAuth/RequireAuth.tsx create mode 100644 frontend/src/shared/hoc/RequirePermission/RequirePermission.tsx create mode 100644 frontend/src/shared/hoc/RequireRole/RequireRole.tsx create mode 100644 frontend/src/shared/hoc/RequireSuperadmin/RequireSuperadmin.tsx create mode 100644 frontend/src/shared/hoc/index.tsx create mode 100644 frontend/src/shared/hoc/withPage/withPage.tsx create mode 100644 frontend/src/shared/index.tsx create mode 100644 frontend/src/shared/lib/components/ActionsHeaderCell/ActionsHeaderCell.tsx create mode 100644 frontend/src/shared/lib/components/ApplyToAllButton/ApplyToAllButton.tsx create mode 100644 frontend/src/shared/lib/components/AvatarCircle/AvatarCircle.stories.tsx create mode 100644 frontend/src/shared/lib/components/AvatarCircle/AvatarCircle.tsx create mode 100644 frontend/src/shared/lib/components/BackLink/ArrowBackLink.stories.tsx create mode 100644 frontend/src/shared/lib/components/BackLink/ArrowBackLink.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTable.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableBody.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableBodyCell.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableBodyRow.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableBodyRowFilled.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableHead.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableHeadCell.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableHeadRow.tsx create mode 100644 frontend/src/shared/lib/components/BaseTable/BaseTableRoot.tsx create mode 100644 frontend/src/shared/lib/components/BoardItem/BoardItemPrimary/BoardItemPrimary.stories.tsx create mode 100644 frontend/src/shared/lib/components/BoardItem/BoardItemPrimary/BoardItemPrimary.tsx create mode 100644 frontend/src/shared/lib/components/BoardItem/BoardItemSecondary/BoardItemSecondary.stories.tsx create mode 100644 frontend/src/shared/lib/components/BoardItem/BoardItemSecondary/BoardItemSecondary.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/BoardListWithButtons/BoardListWithButtons.stories.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/BoardListWithButtons/BoardListWithButtons.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/BoardListWithLinks/BoardListWithLinks.stories.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/BoardListWithLinks/BoardListWithLinks.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/TasksBoardList/TasksBoardList.stories.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/TasksBoardList/TasksBoardList.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/components/BoardList/BoardList.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/components/BoardListItemWrapper/BoardListItemWrapper.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/components/BoardListRoot/BoardListRoot.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/components/BoardNameStyle/BoardNameStyle.tsx create mode 100644 frontend/src/shared/lib/components/BoardList/components/index.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/EntitiesBoardPicker/EntitiesBoardPicker.stories.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/EntitiesBoardPicker/EntitiesBoardPicker.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/EntityPicker/EntitiesAndBoardsPicker.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/EntityPicker/components/EntitiesAndBoardsList/EntitiesAndBoardsList.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/EntityPicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/TasksBoardPicker/TasksBoardPicker.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/components/BoardPicker/BoardPicker.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/components/Controls/Controls.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/BoardPicker/index.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/AddFilledButton/AddFilledButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/AddSmallCircleButton/AddSmallCircleButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/AddSmallCircleButton/AddSmallCircleButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/BuilderButton/BuilderButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/BuilderButton/components/BuilderButtonIcon/BuilderButtonIcon.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/BuilderButton/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/ClearRoundButton/ClearRoundButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/ClearRoundButton/ClearRoundButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/ConnectGoogleButton/ConnectGoogleButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/ControlButton/ControlButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/ControlButton/ControlButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/CopyButton/CopyButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/CreateButton/CreateButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/DefaultColorSelectButton/DefaultColorSelectButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/DeleteButton/DeleteButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/DeleteButton/DeleteButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/DownloadButton/DownloadButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/DownloadButton/DownloadButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/MenuButton/MenuButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/PlusIconButton/PlusIconButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/PlusIconButton/PlusIconButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/PrimaryButton/PrimaryButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/PrimaryButton/PrimaryButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/RoundButton/AddRoundButton/AddRoundButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/RoundButton/CreateRoundButton/CreateRoundButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/RoundButton/RoundButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/RoundButton/RoundButton.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/SaveCancelButtons/SaveCancelButtons.stories.tsx create mode 100644 frontend/src/shared/lib/components/Buttons/SaveCancelButtons/SaveCancelButtons.tsx create mode 100644 frontend/src/shared/lib/components/CalendarPeriodControl/CalendarPeriodControl.tsx create mode 100644 frontend/src/shared/lib/components/CalendarPeriodControl/components/CalendarPeriodControlTitle/CalendarPeriodControlTitle.tsx create mode 100644 frontend/src/shared/lib/components/CalendarPeriodControl/components/index.tsx create mode 100644 frontend/src/shared/lib/components/CardCopiedCountTag/CardCopiedCountTagLink.tsx create mode 100644 frontend/src/shared/lib/components/ChangePeriodControls/ChangePeriodControls.tsx create mode 100644 frontend/src/shared/lib/components/ChangesUnsavedBlocker/ChangesUnsavedBlocker.tsx create mode 100644 frontend/src/shared/lib/components/ColorBox/ColorBox.tsx create mode 100644 frontend/src/shared/lib/components/ColorPicker/ColorPicker.stories.tsx create mode 100644 frontend/src/shared/lib/components/ColorPicker/ColorPicker.tsx create mode 100644 frontend/src/shared/lib/components/ColoredBlock/ColoredBlock.tsx create mode 100644 frontend/src/shared/lib/components/ColumnCount/ColumnCount/ColumnCount.stories.tsx create mode 100644 frontend/src/shared/lib/components/ColumnCount/ColumnCount/ColumnCount.tsx create mode 100644 frontend/src/shared/lib/components/ColumnCount/ColumnCountSkeleton/ColumnCountSkeleton.stories.tsx create mode 100644 frontend/src/shared/lib/components/ColumnCount/ColumnCountSkeleton/ColumnCountSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/ColumnCount/ColumnsCountGhost/ColumnCountGhost.tsx create mode 100644 frontend/src/shared/lib/components/ColumnsVisibilitySettingsGrid/ColumnsVisibilitySettingsGrid.tsx create mode 100644 frontend/src/shared/lib/components/ContextMenu/ContextMenu.tsx create mode 100644 frontend/src/shared/lib/components/ContextMenu/components/ContextMenuItem/ContextMenuItem.tsx create mode 100644 frontend/src/shared/lib/components/ContextMenu/components/index.tsx create mode 100644 frontend/src/shared/lib/components/CreateContactModal/CreateContactModal.tsx create mode 100644 frontend/src/shared/lib/components/CreatedAtDateSelect/CreatedAtDateSelect.tsx create mode 100644 frontend/src/shared/lib/components/CustomizeButton/CustomizeButton.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/DefaultHeader.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/AccountBlock/AccountBlock.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/AccountBlock/AccountBlockSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/AvatarBlock/AvatarBlock.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/DeleteDemoButton/DeleteDemoButton.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/HeaderDelimiter/HeaderDelimiter.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/TrialButton/TrialButton.tsx create mode 100644 frontend/src/shared/lib/components/DefaultHeader/components/index.tsx create mode 100644 frontend/src/shared/lib/components/DelaySelect/DelaySelect.tsx create mode 100644 frontend/src/shared/lib/components/DelaySelect/components/CustomIntervalInputGroup/CustomIntervalInputGroup.tsx create mode 100644 frontend/src/shared/lib/components/DelaySelect/components/CustomIntervalInputGroup/components/IntervalInput/IntervalInput.tsx create mode 100644 frontend/src/shared/lib/components/DelaySelect/components/CustomIntervalInputGroup/components/SubmitIntervalButton/SubmitIntervalButton.tsx create mode 100644 frontend/src/shared/lib/components/DelaySelect/components/CustomIntervalInputGroup/components/index.tsx create mode 100644 frontend/src/shared/lib/components/DelaySelect/components/index.tsx create mode 100644 frontend/src/shared/lib/components/DepartmentsSelect/DepartmentsSelect.tsx create mode 100644 frontend/src/shared/lib/components/DepartmentsSelect/components/DepartmentsSelectItem/DepartmentsSelectItem.tsx create mode 100644 frontend/src/shared/lib/components/DepartmentsSelect/components/index.tsx create mode 100644 frontend/src/shared/lib/components/DraggableResizableControl/DraggableResizableControl.tsx create mode 100644 frontend/src/shared/lib/components/DropdownList/DropdownListFunctional/DropdownListFunctional.tsx create mode 100644 frontend/src/shared/lib/components/DropdownList/ListItem/DropdownListItem.tsx create mode 100644 frontend/src/shared/lib/components/DropdownList/ListItem/ListItemCommon.tsx create mode 100644 frontend/src/shared/lib/components/DropdownList/ListItem/ListItemLink.tsx create mode 100644 frontend/src/shared/lib/components/DropdownList/ListItem/ListItemText.tsx create mode 100644 frontend/src/shared/lib/components/EmptyTableBlock/EmptyTableBlock.tsx create mode 100644 frontend/src/shared/lib/components/EntitiesSuggestionsPopover/EntitiesSuggestionsPopover.tsx create mode 100644 frontend/src/shared/lib/components/EntitiesSuggestionsPopover/components/AddAsNewButton/AddAsNewButton.tsx create mode 100644 frontend/src/shared/lib/components/EntitiesSuggestionsPopover/components/EntitiesSuggestionItem/EntitiesSuggestionItem.tsx create mode 100644 frontend/src/shared/lib/components/EntitiesSuggestionsPopover/components/index.tsx create mode 100644 frontend/src/shared/lib/components/ErrorBoundary/ErrorBoundary.tsx create mode 100644 frontend/src/shared/lib/components/ExpandButton/ExpandButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/ExpandButton/ExpandButton.tsx create mode 100644 frontend/src/shared/lib/components/FieldGroupTabs/FieldGroupTabs.tsx create mode 100644 frontend/src/shared/lib/components/FieldGroupTabs/components/EditableTabTitle/EditableTabTitle.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/FileInput.stories.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/FileInput.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/BlockFileItem/BlockFileItem.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/BlockFileListRoot/BlockFileListRoot.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/CompactFileList/CompactFileList.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/FileFeedItem/FileFeedItem.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/FileIcon/FileIcon.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/FileItem/FileItem.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/FileListWrapper/FileListWrapper.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/FileSize/FileSize.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/Lists/BlockFileList/BlockFileList.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/Lists/CommonFileList/CommonFileList.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/Lists/InputBlocksFileList/InputBlocksFileList.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/Lists/InputFileList/InputFileList.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/Lists/MixedFileList/MixedFileList.tsx create mode 100644 frontend/src/shared/lib/components/FileInput/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/CurrencySelect/CurrencySelect.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/InputWithPercent/InputWithPercent.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/MyInput.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/MyInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/components/ErrorIcon/ErrorIcon.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/components/IconWrapper/IconWrapper.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/components/Loader/Loader.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/components/VisibilityIcon/VisibilityIcon.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInput/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInputNumber/MyInputNumber.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/MyInputWithLimitedLength/MyInputWithLimitedLength.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/OutlinedSearchInput/OutlinedSearchInput.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/OutlinedSearchInput/OutlinedSearchInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/SearchInput/SearchInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/components/StyledInput/StyledInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/components/StyledInputWrapper/StyledInputWrapper.tsx create mode 100644 frontend/src/shared/lib/components/Form/Input/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/KeyValueInput/KeyValueInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/MultiselectWithCheckboxes/MultiselectWithCheckboxes.tsx create mode 100644 frontend/src/shared/lib/components/Form/MultiselectWithCheckboxes/MultiselectWithChekboxes.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MultiselectWithCheckboxes/components/MultiselectCheckIcon/MultiselectCheckIcon.tsx create mode 100644 frontend/src/shared/lib/components/Form/MultiselectWithCheckboxes/components/MultiselectOptionItem/MultiselectOptionItem.tsx create mode 100644 frontend/src/shared/lib/components/Form/MultiselectWithCheckboxes/components/MultiselectOptionsList/MultiselectOptionsList.tsx create mode 100644 frontend/src/shared/lib/components/Form/MultiselectWithCheckboxes/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyCheckbox/MyCheckbox.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyCheckbox/MyCheckbox.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyCheckbox/MyCheckboxWithBooleanModel.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyCheckbox/MyCheckboxWithCheckboxModel.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyChecklist/MyChecklist.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyMultiselect/MyMultiselectColored/MyMultiselectColored.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyMultiselect/MyMultiselectColored/MyMultiselectColored.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyMultiselect/components/MyMultiselectColoredTag/MyMultiselectColoredTag.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyMultiselect/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyRadio/MyRadio.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyRadio/MyRadio.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/MySelect/MySelect.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/MySelect/MySelect.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/MySelectColored/MySelectColored.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/MySelectColored/MySelectColored.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/MySelectCustomTemplate/MySelectCustomTemplate.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/MyUsersSelect/MyUsersSelect.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/ClearButton/ClearButton.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/ColoredBlock/ColoredBlock.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/FilledArrowIcon/FilledArrowIcon.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/MySelectSearchBlock/MySelectSearchBlock.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/MySelectStyledDropdown/MySelectStyledDropdown.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/MySelectTitle/MySelectTitle.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/NoOptionsMessage/NoOptionsMessage.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySelect/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySwitch/MySwitch/MySwitch.stories.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySwitch/MySwitch/MySwitch.tsx create mode 100644 frontend/src/shared/lib/components/Form/MySwitch/MySwitchWithModel/MySwitchWithModel.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyTextarea/MyTextArea.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyTimePickerInput/MyTimePickerInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyTimePickerInput/components/TimePickerSelectMenu/TimePickerSelectMenu.tsx create mode 100644 frontend/src/shared/lib/components/Form/MyTimePickerInput/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/PhoneFieldInput/PhoneFieldInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/PhoneNumberInput/PhoneNumberInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/UserPicker.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/components/CheckIcon/CheckIcon.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/components/SelectAllBlock/SelectAllBlock.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/components/UserDropdownItem/UserDropdownItem.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/components/UserList/UserList.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/components/UserView/UserView.tsx create mode 100644 frontend/src/shared/lib/components/Form/UserPicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Form/WeekIntervalsInput/WeekIntervalsInput.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/AddItemForm/AddItemForm.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/Form.styles.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/FormItem/FormItem.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/FormItemLabel/FormItemLabel.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/SelectOptionItem/SelectOptionItem.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/SelectOptionsList/SelectOptionsList.tsx create mode 100644 frontend/src/shared/lib/components/Form/components/index.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/FunctionalTextEditor.stories.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/FunctionalTextEditor.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/components/Buttons/RightButton/RightButton.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/components/Controls/AddFileControl/AddFileControl.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/components/Controls/FormatTextControl/FormatTextControl.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/components/Controls/InsertEmojiControl/InsertEmojiControl.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/components/Toolbar/Toolbar.tsx create mode 100644 frontend/src/shared/lib/components/FunctionalTextEditor/components/index.tsx create mode 100644 frontend/src/shared/lib/components/HeaderRoundButton/HeaderRoundButton.tsx create mode 100644 frontend/src/shared/lib/components/Hint/Hint.stories.tsx create mode 100644 frontend/src/shared/lib/components/Hint/Hint.tsx create mode 100644 frontend/src/shared/lib/components/Icons/DoubleRightLegacy/DoubleRightLegacy.tsx create mode 100644 frontend/src/shared/lib/components/Icons/PlusIconSquareLegacy/PlusIconSquareLegacy.tsx create mode 100644 frontend/src/shared/lib/components/Icons/SearchLegacy/SearchLegacy.tsx create mode 100644 frontend/src/shared/lib/components/Icons/SentIconLegacy/SentIconLegacy.tsx create mode 100644 frontend/src/shared/lib/components/ImagePreviewModal/ImagePreviewModal.tsx create mode 100644 frontend/src/shared/lib/components/LinkContactModal/LinkContactModal.tsx create mode 100644 frontend/src/shared/lib/components/LinkedEntityTag/LinkedEntityTag.stories.tsx create mode 100644 frontend/src/shared/lib/components/LinkedEntityTag/LinkedEntityTag.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/DefaultLoader/DefaultLoader.stories.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/DefaultLoader/DefaultLoader.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/MiniLoader/MiniLoader.stories.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/MiniLoader/MiniLoader.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/ModalLoader/ModalLoader.stories.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/ModalLoader/ModalLoader.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/SquaresLoader/SquaresLoader.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/WholePageLoaderWithLogo/WholePageLoaderWithLogo.stories.tsx create mode 100644 frontend/src/shared/lib/components/Loaders/WholePageLoaderWithLogo/WholePageLoaderWithLogo.tsx create mode 100644 frontend/src/shared/lib/components/LogoLink/LogoLink.stories.tsx create mode 100644 frontend/src/shared/lib/components/LogoLink/LogoLink.tsx create mode 100644 frontend/src/shared/lib/components/Modals/Dialog/DialogModalPrimary/DialogModalPrimary.stories.tsx create mode 100644 frontend/src/shared/lib/components/Modals/Dialog/DialogModalPrimary/DialogModalPrimary.tsx create mode 100644 frontend/src/shared/lib/components/Modals/Dialog/DialogModalSecondary/DialogModalSecondary.stories.tsx create mode 100644 frontend/src/shared/lib/components/Modals/Dialog/DialogModalSecondary/DialogModalSecondary.tsx create mode 100644 frontend/src/shared/lib/components/Modals/FileSizeWarningModal/FileSizeWarningModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/ImageEditModal/ImageEditModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/OverlayingModal/OverlayingModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/Portal/Portal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/RequestAdditionalStorageFormModal/RequestAdditionalStorageFormModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/RequestBpmnFormModal/RequestBpmnFormModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/UpdateModal/ReloadModal/ReloadModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/UpdateModal/UpdateModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/UpdateModal/VersionModal/VersionModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/UtilityModals/AddUserToPlanModal/AddUserToPlanModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/UtilityModals/RequestBillingFormModal/RequestBillingFormModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/UtilityModals/SubscriptionPeriodOverModal/SubscriptionPeriodOverModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/WarningModal/WarningModal.stories.tsx create mode 100644 frontend/src/shared/lib/components/Modals/WarningModal/WarningModal.tsx create mode 100644 frontend/src/shared/lib/components/Modals/components/HeadlessFormItem/HeadlessFormItem.tsx create mode 100644 frontend/src/shared/lib/components/Modals/components/ModalAnnotation/ModalAnnotation.tsx create mode 100644 frontend/src/shared/lib/components/Modals/components/ModalContentTitle/ModalContentTitle.tsx create mode 100644 frontend/src/shared/lib/components/Modals/components/ModalTrashBinIcon/ModalTrashBinIcon.tsx create mode 100644 frontend/src/shared/lib/components/Modals/components/ModalWarningIcon/ModalWarningIcon.tsx create mode 100644 frontend/src/shared/lib/components/Modals/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MultitextDuplicatesManager/MultitextDuplicatesManager.tsx create mode 100644 frontend/src/shared/lib/components/MultitextDuplicatesManager/components/DuplicatesForbiddenModal/DuplicatesForbiddenModal.tsx create mode 100644 frontend/src/shared/lib/components/MultitextDuplicatesManager/components/DuplicatesWarningModal/DuplicatesWarningModal.tsx create mode 100644 frontend/src/shared/lib/components/MultitextDuplicatesManager/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MyColorPicker/MyColorPicker.tsx create mode 100644 frontend/src/shared/lib/components/MyColorPicker/components/MyColorPickerDefaultButton/MyColorPickerDefaultButton.tsx create mode 100644 frontend/src/shared/lib/components/MyColorPicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePeriodPicker/MyDatePeriodPicker.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePeriodPicker/components/DatePeriodSegmentTooltip/DatePeriodSegmentTooltip.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePeriodPicker/components/DatePeriodSegmentedControl/DatePeriodSegmentedControl.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePeriodPicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePicker/MyDatePicker.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePickerDropdown/MyDatePickerDropdown.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePickerRange/MyDatePickerRange.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePickerSelect/MyDatePickerSelect.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/MyDatePickerWithTime/MyDatePickerWithTime.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/components/DatePickerSelectCalendarIcon/DatePickerSelectCalendarIcon.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/components/StyledDatePicker/StyledDatePicker.tsx create mode 100644 frontend/src/shared/lib/components/MyDatePicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MyDrawer/MyDrawer.stories.tsx create mode 100644 frontend/src/shared/lib/components/MyDrawer/MyDrawer.tsx create mode 100644 frontend/src/shared/lib/components/MyDrawer/components/MyDrawerHeaderTitle/MyDrawerHeaderTitle.tsx create mode 100644 frontend/src/shared/lib/components/MyDrawer/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MyDropdown/MyDropdown.tsx create mode 100644 frontend/src/shared/lib/components/MyDropdown/components/MyDropdownItem/MyDropdownItem.tsx create mode 100644 frontend/src/shared/lib/components/MyDropdown/components/MyDropdownList/MyDropdownList.tsx create mode 100644 frontend/src/shared/lib/components/MyDropdown/components/MyDropdownTitleWrapper/MyDropdownTitleWrapper.tsx create mode 100644 frontend/src/shared/lib/components/MyDropdown/components/index.tsx create mode 100644 frontend/src/shared/lib/components/MyEmojiPicker/MyEmojiPicker.stories.tsx create mode 100644 frontend/src/shared/lib/components/MyEmojiPicker/MyEmojiPicker.tsx create mode 100644 frontend/src/shared/lib/components/MyHoverCard/MyHoverCard.stories.tsx create mode 100644 frontend/src/shared/lib/components/MyHoverCard/MyHoverCard.tsx create mode 100644 frontend/src/shared/lib/components/MyIndicator/MyIndicator.stories.tsx create mode 100644 frontend/src/shared/lib/components/MyIndicator/MyIndicator.tsx create mode 100644 frontend/src/shared/lib/components/MyIndicator/components/UnseenCount/UnseenCount.tsx create mode 100644 frontend/src/shared/lib/components/MyMacScrollbar/MyMacScrollbar.tsx create mode 100644 frontend/src/shared/lib/components/MyPopover/MyPopover.stories.tsx create mode 100644 frontend/src/shared/lib/components/MyPopover/MyPopover.tsx create mode 100644 frontend/src/shared/lib/components/MySegmentedControl/MySegmentedControl.tsx create mode 100644 frontend/src/shared/lib/components/MyTooltip/MyFloatingTooltip/MyFloatingTooltip.tsx create mode 100644 frontend/src/shared/lib/components/MyTooltip/MyTooltip/MyTooltip.stories.tsx create mode 100644 frontend/src/shared/lib/components/MyTooltip/MyTooltip/MyTooltip.tsx create mode 100644 frontend/src/shared/lib/components/PageSecondaryHeader/PageSecondaryHeader.tsx create mode 100644 frontend/src/shared/lib/components/PageTracker/PageTracker.tsx create mode 100644 frontend/src/shared/lib/components/ParticipantsSelect/ParticipantsSelect.tsx create mode 100644 frontend/src/shared/lib/components/ParticipantsSelect/ParticipantsSelectWithCreateButton.tsx create mode 100644 frontend/src/shared/lib/components/ParticipantsSelect/components/ParticipantsAvatarRows/ParticipantsAvatarRows.tsx create mode 100644 frontend/src/shared/lib/components/ParticipantsSelect/components/index.tsx create mode 100644 frontend/src/shared/lib/components/PdfViewerModal/PdfViewerModal.tsx create mode 100644 frontend/src/shared/lib/components/PencilButton/PencilButton.tsx create mode 100644 frontend/src/shared/lib/components/PickerButton/PickerButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/PickerButton/PickerButton.tsx create mode 100644 frontend/src/shared/lib/components/PickerButton/components/RoundedDashedFrame/RoundedDashedFrame.tsx create mode 100644 frontend/src/shared/lib/components/PickerButton/components/index.tsx create mode 100644 frontend/src/shared/lib/components/PlannedTimePicker/PlannedTimePicker.tsx create mode 100644 frontend/src/shared/lib/components/PlannedTimePicker/components/PlannedTimeNumberInput/PlannedTimeNumberInput.tsx create mode 100644 frontend/src/shared/lib/components/PlannedTimePicker/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Player/Player.tsx create mode 100644 frontend/src/shared/lib/components/Player/components/PlayerControls/PlayerControls.tsx create mode 100644 frontend/src/shared/lib/components/Player/components/PlayerPlaybackSelect/PlayerPlaybackSelect.tsx create mode 100644 frontend/src/shared/lib/components/Player/components/PlayerSkeleton/PlayerSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/Player/components/PlayerTrack/PlayerTrack.tsx create mode 100644 frontend/src/shared/lib/components/Player/components/index.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/ProfileModal.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/AvatarBlock/AvatarBlock.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/AvatarButton/AvatarButton.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/ChangePasswordBlock/ChangePasswordBlock.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/FormWrapper/FormWrapper.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/MainInfoBlock/MainInfoBlock.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/MainInfoWrapper/MainInfoWrapper.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/ProfileModalFormGroup/ProfileModalFormItem.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/ProfileModalSkeleton/ProfileModalSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/SecondaryInfoBlock/SecondaryInfoBlock.tsx create mode 100644 frontend/src/shared/lib/components/ProfileModal/components/index.tsx create mode 100644 frontend/src/shared/lib/components/Scrollbar/Scrollbar.tsx create mode 100644 frontend/src/shared/lib/components/SectionPagination/SectionPagination.tsx create mode 100644 frontend/src/shared/lib/components/ShowMoreButton/ShowMoreButton.stories.tsx create mode 100644 frontend/src/shared/lib/components/ShowMoreButton/ShowMoreButton.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/Sidebar.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/SidebarHoverCard/SidebarHoverCard.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/SidebarItem/SidebarItem.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/SidebarItemBoard/SidebarItemBoard.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/SidebarItemCategory/SidebarItemCategory.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/SidebarItemSkeleton/SidebarItemSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/SidebarMailItem/SidebarMailItem.tsx create mode 100644 frontend/src/shared/lib/components/Sidebar/components/index.tsx create mode 100644 frontend/src/shared/lib/components/SpanWithEllipsis/SpanWithEllipsis.tsx create mode 100644 frontend/src/shared/lib/components/StagesSelect/StagesSelect.tsx create mode 100644 frontend/src/shared/lib/components/StyledLink/StyledLink.tsx create mode 100644 frontend/src/shared/lib/components/Subheader/Subheader.tsx create mode 100644 frontend/src/shared/lib/components/Subheader/SubheaderButton.tsx create mode 100644 frontend/src/shared/lib/components/Subheader/components/SubheaderTab/SubheaderTab.tsx create mode 100644 frontend/src/shared/lib/components/Subheader/components/SubheaderTabs/SubheaderTabs.tsx create mode 100644 frontend/src/shared/lib/components/Subheader/components/SubheaderTabsSkeleton/SubheaderTabsSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/Subheader/components/index.tsx create mode 100644 frontend/src/shared/lib/components/TabSelector/TabSelector.stories.tsx create mode 100644 frontend/src/shared/lib/components/TabSelector/TabSelector.tsx create mode 100644 frontend/src/shared/lib/components/TabSelector/components/Tab/Tab.tsx create mode 100644 frontend/src/shared/lib/components/TabSelector/components/TabSelectorSkeleton/TabSelectorSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/TabSelector/components/index.tsx create mode 100644 frontend/src/shared/lib/components/TableSkeleton/TableSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/TextHighlighter/TextHighlighter.tsx create mode 100644 frontend/src/shared/lib/components/ToastContainer/ToastContainer.tsx create mode 100644 frontend/src/shared/lib/components/TotalTag/TotalTag.stories.tsx create mode 100644 frontend/src/shared/lib/components/TotalTag/TotalTag.tsx create mode 100644 frontend/src/shared/lib/components/UsersMultiselect/UsersMultiselect.tsx create mode 100644 frontend/src/shared/lib/components/UsersMultiselectDropdown/UsersMultiselectDropdown.tsx create mode 100644 frontend/src/shared/lib/components/VideoPlayerModal/VideoPlayerModal.tsx create mode 100644 frontend/src/shared/lib/components/index.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/CreateButtonSkeleton/CreateButtonSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/LabelSkeleton/LabelSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/ModuleNameSkeleton/ModuleNameSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/MyCheckboxSkeleton/MyCheckboxSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/MyRadioSkeleton/MyRadioSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/PickerButtonSkeleton/PickerButtonSkeleton.tsx create mode 100644 frontend/src/shared/lib/components/skeletons/RoundButtonSkeleton/RoundButtonSkeleton.tsx create mode 100644 frontend/src/shared/lib/helpers/CurrencyFormatterHelper.ts create mode 100644 frontend/src/shared/lib/helpers/JsonStateHelper.ts create mode 100644 frontend/src/shared/lib/helpers/areRoutesPathnamesEqual.ts create mode 100644 frontend/src/shared/lib/helpers/arraysShallowEqual.ts create mode 100644 frontend/src/shared/lib/helpers/batchRequest.ts create mode 100644 frontend/src/shared/lib/helpers/calculateEndOfWordIdxByNumber.ts create mode 100644 frontend/src/shared/lib/helpers/capitalizeFirstLetter.ts create mode 100644 frontend/src/shared/lib/helpers/checkDatesRangesOverlap.ts create mode 100644 frontend/src/shared/lib/helpers/clamp.ts create mode 100644 frontend/src/shared/lib/helpers/dangerouslySetQueryParams.ts create mode 100644 frontend/src/shared/lib/helpers/debounce.ts create mode 100644 frontend/src/shared/lib/helpers/delay.ts create mode 100644 frontend/src/shared/lib/helpers/delayResolve.ts create mode 100644 frontend/src/shared/lib/helpers/extractFileExtensionFromType.ts create mode 100644 frontend/src/shared/lib/helpers/followElement.ts create mode 100644 frontend/src/shared/lib/helpers/formatBytes.ts create mode 100644 frontend/src/shared/lib/helpers/formatSeconds.ts create mode 100644 frontend/src/shared/lib/helpers/formatSecondsToHoursAndMinutes.ts create mode 100644 frontend/src/shared/lib/helpers/generateCountryCodeFromLanguage.ts create mode 100644 frontend/src/shared/lib/helpers/generateMockOptions.ts create mode 100644 frontend/src/shared/lib/helpers/generateMyDatePickerRangeTitle.ts create mode 100644 frontend/src/shared/lib/helpers/generateMyDatePickerTitle.ts create mode 100644 frontend/src/shared/lib/helpers/generateSegmentedControlItems.ts create mode 100644 frontend/src/shared/lib/helpers/getBlockFileItemIcon.tsx create mode 100644 frontend/src/shared/lib/helpers/getDHMSFromSeconds.ts create mode 100644 frontend/src/shared/lib/helpers/getFileFeedItemIcon.tsx create mode 100644 frontend/src/shared/lib/helpers/getProperFromAndToDatesForFilter.ts create mode 100644 frontend/src/shared/lib/helpers/getTimeString.ts create mode 100644 frontend/src/shared/lib/helpers/index.tsx create mode 100644 frontend/src/shared/lib/helpers/insertBrTagsInsteadEmptyPs.ts create mode 100644 frontend/src/shared/lib/helpers/insertTextAtCaret.ts create mode 100644 frontend/src/shared/lib/helpers/isFileTypeImage.ts create mode 100644 frontend/src/shared/lib/helpers/isFileTypePdf.ts create mode 100644 frontend/src/shared/lib/helpers/isFileTypeVideo.ts create mode 100644 frontend/src/shared/lib/helpers/isPathnameOnEntitySection.ts create mode 100644 frontend/src/shared/lib/helpers/removeBrTagsFromStr.ts create mode 100644 frontend/src/shared/lib/helpers/renderTodayWithIndicator.tsx create mode 100644 frontend/src/shared/lib/helpers/setCaretToPos.ts create mode 100644 frontend/src/shared/lib/helpers/setTextareaSelectionRange.ts create mode 100644 frontend/src/shared/lib/helpers/shallowEqual.ts create mode 100644 frontend/src/shared/lib/helpers/throttle.ts create mode 100644 frontend/src/shared/lib/helpers/truncateNumber.ts create mode 100644 frontend/src/shared/lib/helpers/useGetPeriodOptions.ts create mode 100644 frontend/src/shared/lib/helpers/validateColorHex.ts create mode 100644 frontend/src/shared/lib/helpers/validateForm.ts create mode 100644 frontend/src/shared/lib/hooks/index.tsx create mode 100644 frontend/src/shared/lib/hooks/useAutoFocusOnMount.ts create mode 100644 frontend/src/shared/lib/hooks/useCheckBrowserSupport.ts create mode 100644 frontend/src/shared/lib/hooks/useCheckProjectOwnerOrAdmin.ts create mode 100644 frontend/src/shared/lib/hooks/useConditionalWindowScroll.ts create mode 100644 frontend/src/shared/lib/hooks/useDropdownWidth.ts create mode 100644 frontend/src/shared/lib/hooks/useEditorOptimized.ts create mode 100644 frontend/src/shared/lib/hooks/useErrorMessageIdle.ts create mode 100644 frontend/src/shared/lib/hooks/useFadedHorizontalScroll.ts create mode 100644 frontend/src/shared/lib/hooks/useGetDHMDateStringFromSeconds.ts create mode 100644 frontend/src/shared/lib/hooks/useGetEntityEmailOptions.tsx create mode 100644 frontend/src/shared/lib/hooks/useGetLocalBusinessHours.ts create mode 100644 frontend/src/shared/lib/hooks/useGetMediaBlobObjectUrl.ts create mode 100644 frontend/src/shared/lib/hooks/useGrabScroll.ts create mode 100644 frontend/src/shared/lib/hooks/useHasMore.ts create mode 100644 frontend/src/shared/lib/hooks/useLeftBlockWidth.ts create mode 100644 frontend/src/shared/lib/hooks/useMobile.ts create mode 100644 frontend/src/shared/lib/hooks/useModalControl.ts create mode 100644 frontend/src/shared/lib/hooks/usePageTracking.ts create mode 100644 frontend/src/shared/lib/hooks/usePersistFn.ts create mode 100644 frontend/src/shared/lib/hooks/useQueryParamModalControl.ts create mode 100644 frontend/src/shared/lib/hooks/useScrollWindowToTop.ts create mode 100644 frontend/src/shared/lib/hooks/useSendEmail.ts create mode 100644 frontend/src/shared/lib/hooks/useSize.ts create mode 100644 frontend/src/shared/lib/hooks/useTitle.tsx create mode 100644 frontend/src/shared/lib/hooks/useToggleControl.ts create mode 100644 frontend/src/shared/lib/hooks/useTracePropsUpdate.ts create mode 100644 frontend/src/shared/lib/hooks/useTransformScroll.ts create mode 100644 frontend/src/shared/lib/hooks/useTypedParams.ts create mode 100644 frontend/src/shared/lib/hooks/useUploadFiles.ts create mode 100644 frontend/src/shared/lib/index.tsx create mode 100644 frontend/src/shared/lib/mixins/DropdownScrollbar.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/FadedHorizontalScroll.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/HideScrollbar.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/InnerHTMLNormalizer.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/NoSelect.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/SkeletonAnimation.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/SkeletonAnimationLegacy.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/TableScrollbarMixin.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/Truncate.mixin.tsx create mode 100644 frontend/src/shared/lib/mixins/index.tsx create mode 100644 frontend/src/shared/lib/models/Auth/JwtToken.ts create mode 100644 frontend/src/shared/lib/models/Automation/ChangeStageType.ts create mode 100644 frontend/src/shared/lib/models/Automation/EntityTypeActionType.ts create mode 100644 frontend/src/shared/lib/models/Automation/EntityTypeTrigger.ts create mode 100644 frontend/src/shared/lib/models/Board/Board.ts create mode 100644 frontend/src/shared/lib/models/Board/BoardType.ts create mode 100644 frontend/src/shared/lib/models/BusinessHours.ts create mode 100644 frontend/src/shared/lib/models/Button/PrimaryButtonVariant.ts create mode 100644 frontend/src/shared/lib/models/CalendarView.tsx create mode 100644 frontend/src/shared/lib/models/CallInfo/CallInfo.ts create mode 100644 frontend/src/shared/lib/models/CallInfo/ContactInfo.ts create mode 100644 frontend/src/shared/lib/models/CallInfo/EntityInfo.ts create mode 100644 frontend/src/shared/lib/models/CommonQueryParams/CommonQueryParams.ts create mode 100644 frontend/src/shared/lib/models/CrmEventType.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataBankRequisitesOpfType.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataBankRequisitesSuggestion.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataOrgRequisitesSuggestion.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataOrgRequisitesSuggestionBranchType.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataOrgRequisitesSuggestionStatus.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataOrgRequisitesSuggestionType.ts create mode 100644 frontend/src/shared/lib/models/Dadata/DadataSuggestionsType.ts create mode 100644 frontend/src/shared/lib/models/DataStore.ts create mode 100644 frontend/src/shared/lib/models/DateFormat.ts create mode 100644 frontend/src/shared/lib/models/DatePeriodFilterModel.ts create mode 100644 frontend/src/shared/lib/models/DatePeriodFilterType.ts create mode 100644 frontend/src/shared/lib/models/DatePeriodType/DatePeriodType.ts create mode 100644 frontend/src/shared/lib/models/DayNumberMap.ts create mode 100644 frontend/src/shared/lib/models/DepartmentWithUsersOption.ts create mode 100644 frontend/src/shared/lib/models/DraggableResizableControlBounds.ts create mode 100644 frontend/src/shared/lib/models/Entity/EntitiesBoardSettingsTab.ts create mode 100644 frontend/src/shared/lib/models/Entity/Entity.ts create mode 100644 frontend/src/shared/lib/models/Entity/EntityInfo.ts create mode 100644 frontend/src/shared/lib/models/Entity/EntityLink.ts create mode 100644 frontend/src/shared/lib/models/Entity/ExternalEntity.ts create mode 100644 frontend/src/shared/lib/models/Entity/ExternalSystemCode.ts create mode 100644 frontend/src/shared/lib/models/EntityCreatedAtFilter.ts create mode 100644 frontend/src/shared/lib/models/EntityType/EntityCategory.ts create mode 100644 frontend/src/shared/lib/models/EntityType/EntityType.ts create mode 100644 frontend/src/shared/lib/models/EntityType/EntityTypeLink.ts create mode 100644 frontend/src/shared/lib/models/EntityType/Section.ts create mode 100644 frontend/src/shared/lib/models/Error/ErrorCode.ts create mode 100644 frontend/src/shared/lib/models/Error/ServiceError.ts create mode 100644 frontend/src/shared/lib/models/Event/EntityEvent.ts create mode 100644 frontend/src/shared/lib/models/Feature/FeatureCode.ts create mode 100644 frontend/src/shared/lib/models/Feed/FeedGroup.ts create mode 100644 frontend/src/shared/lib/models/Feed/FeedItem.ts create mode 100644 frontend/src/shared/lib/models/Feed/FeedItemFilter.ts create mode 100644 frontend/src/shared/lib/models/Field/FieldFormat.ts create mode 100644 frontend/src/shared/lib/models/Field/FieldType.ts create mode 100644 frontend/src/shared/lib/models/File/FileModel.ts create mode 100644 frontend/src/shared/lib/models/FileInfo/FileInfo.tsx create mode 100644 frontend/src/shared/lib/models/FileLink/FileLink.ts create mode 100644 frontend/src/shared/lib/models/Filter/BooleanFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/BooleanFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/DateFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/DateFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/ExistsFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/ExistsFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/ExistsFilterType.ts create mode 100644 frontend/src/shared/lib/models/Filter/NumberFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/NumberFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/PossibleFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/PossibleFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/SelectFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/SelectFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/SimpleFilterType.ts create mode 100644 frontend/src/shared/lib/models/Filter/StringFilter.ts create mode 100644 frontend/src/shared/lib/models/Filter/StringFilterFormData.ts create mode 100644 frontend/src/shared/lib/models/Filter/StringFilterType.ts create mode 100644 frontend/src/shared/lib/models/Form/BooleanModel.ts create mode 100644 frontend/src/shared/lib/models/Form/CheckedInputModel.ts create mode 100644 frontend/src/shared/lib/models/Form/KeyValueListModel.ts create mode 100644 frontend/src/shared/lib/models/Form/MyCheckbox/CheckboxModel.ts create mode 100644 frontend/src/shared/lib/models/Form/MyCheckbox/MyCheckboxVariant.ts create mode 100644 frontend/src/shared/lib/models/Form/MyInput/InputModel.ts create mode 100644 frontend/src/shared/lib/models/Form/MyInput/MyInputFontSize.ts create mode 100644 frontend/src/shared/lib/models/Form/MyInput/MyInputVariant.ts create mode 100644 frontend/src/shared/lib/models/Form/MyMultiselect/MultiselectModel.ts create mode 100644 frontend/src/shared/lib/models/Form/MyRadio/MyRadioColorType.ts create mode 100644 frontend/src/shared/lib/models/Form/MySelect/MySelectOptionValueType.ts create mode 100644 frontend/src/shared/lib/models/Form/MySelect/MySelectTitleRootVariant.ts create mode 100644 frontend/src/shared/lib/models/Form/MySelect/SelectModel.ts create mode 100644 frontend/src/shared/lib/models/Form/NumberModel.ts create mode 100644 frontend/src/shared/lib/models/Form/TimePicker/TimePickerSelectMenuPopoverProps.ts create mode 100644 frontend/src/shared/lib/models/Form/Validation/ValidationCode.ts create mode 100644 frontend/src/shared/lib/models/Form/Validation/ValidationError.ts create mode 100644 frontend/src/shared/lib/models/Form/Validation/ValidationFunc.ts create mode 100644 frontend/src/shared/lib/models/Form/Validation/ValidationRule.ts create mode 100644 frontend/src/shared/lib/models/Form/WeekIntervalListModel.ts create mode 100644 frontend/src/shared/lib/models/FrontendObject.ts create mode 100644 frontend/src/shared/lib/models/FunctionalTextEditor/FTEShowHTMLProps.ts create mode 100644 frontend/src/shared/lib/models/HttpMethod.ts create mode 100644 frontend/src/shared/lib/models/Icon/Icon.ts create mode 100644 frontend/src/shared/lib/models/Icon/IconName.ts create mode 100644 frontend/src/shared/lib/models/Language.ts create mode 100644 frontend/src/shared/lib/models/ManualSorting.ts create mode 100644 frontend/src/shared/lib/models/MediaBreakpoints.ts create mode 100644 frontend/src/shared/lib/models/Multichat/EntitySettings.tsx create mode 100644 frontend/src/shared/lib/models/MyDatePicker/MyDatePickerCommonProps.ts create mode 100644 frontend/src/shared/lib/models/MyDatePicker/MyDatePickerRangeType.ts create mode 100644 frontend/src/shared/lib/models/MyDatePicker/UtcDateValue.ts create mode 100644 frontend/src/shared/lib/models/MyDatePicker/UtcDatesRangeValue.ts create mode 100644 frontend/src/shared/lib/models/Note/Note.ts create mode 100644 frontend/src/shared/lib/models/ObjectState.ts create mode 100644 frontend/src/shared/lib/models/Option.ts create mode 100644 frontend/src/shared/lib/models/PagingMeta.ts create mode 100644 frontend/src/shared/lib/models/Permission/Action.ts create mode 100644 frontend/src/shared/lib/models/Permission/ObjectPermission.ts create mode 100644 frontend/src/shared/lib/models/Permission/PermissionHelper.ts create mode 100644 frontend/src/shared/lib/models/Permission/PermissionLevel.ts create mode 100644 frontend/src/shared/lib/models/Permission/PermissionObjectType.ts create mode 100644 frontend/src/shared/lib/models/Permission/UserRights.ts create mode 100644 frontend/src/shared/lib/models/PhoneFormat.ts create mode 100644 frontend/src/shared/lib/models/Profile/UserProfile.ts create mode 100644 frontend/src/shared/lib/models/RecordState.ts create mode 100644 frontend/src/shared/lib/models/SearchDuplicatesProps.ts create mode 100644 frontend/src/shared/lib/models/Section/SectionView.ts create mode 100644 frontend/src/shared/lib/models/SiteForm/SiteFormResult.ts create mode 100644 frontend/src/shared/lib/models/Stage/Stage.ts create mode 100644 frontend/src/shared/lib/models/Stage/StageCode.ts create mode 100644 frontend/src/shared/lib/models/Stage/StageGroup.ts create mode 100644 frontend/src/shared/lib/models/SubdepartmentWithUsersOption.ts create mode 100644 frontend/src/shared/lib/models/SubscriberStore.ts create mode 100644 frontend/src/shared/lib/models/Subscription/AppSumoTiers.ts create mode 100644 frontend/src/shared/lib/models/Subscription/BillingInterval.ts create mode 100644 frontend/src/shared/lib/models/Subscription/Subscription.ts create mode 100644 frontend/src/shared/lib/models/Subscription/SubscriptionFeature.ts create mode 100644 frontend/src/shared/lib/models/Subscription/SubscriptionPlan.ts create mode 100644 frontend/src/shared/lib/models/Subscription/SubscriptionPrice.ts create mode 100644 frontend/src/shared/lib/models/Tabs/TabModel.ts create mode 100644 frontend/src/shared/lib/models/TaskTimeStatus.ts create mode 100644 frontend/src/shared/lib/models/Time.ts create mode 100644 frontend/src/shared/lib/models/Tutorial/TutorialProductType.ts create mode 100644 frontend/src/shared/lib/models/User/Avatar.ts create mode 100644 frontend/src/shared/lib/models/User/User.ts create mode 100644 frontend/src/shared/lib/models/User/UserRole.ts create mode 100644 frontend/src/shared/lib/models/UtcDate.ts create mode 100644 frontend/src/shared/lib/models/Version/Version.ts create mode 100644 frontend/src/shared/lib/models/WeekDays.ts create mode 100644 frontend/src/shared/lib/models/constants.ts create mode 100644 frontend/src/shared/lib/models/index.tsx create mode 100644 frontend/src/shared/lib/services/LastSchedulerService.ts create mode 100644 frontend/src/shared/lib/services/LastSectionService.ts create mode 100644 frontend/src/shared/lib/services/LastTasksBoardService.ts create mode 100644 frontend/src/shared/lib/services/ServerEventService.ts create mode 100644 frontend/src/shared/lib/services/StorageService.ts create mode 100644 frontend/src/shared/lib/services/index.tsx create mode 100644 frontend/src/shared/lib/types/AnyObject.ts create mode 100644 frontend/src/shared/lib/types/BillingPath.ts create mode 100644 frontend/src/shared/lib/types/CalculateActiveStrategy.ts create mode 100644 frontend/src/shared/lib/types/CommonFields.ts create mode 100644 frontend/src/shared/lib/types/CommonKeys.ts create mode 100644 frontend/src/shared/lib/types/CompanyName.ts create mode 100644 frontend/src/shared/lib/types/Currency.ts create mode 100644 frontend/src/shared/lib/types/EntityBoardLinkType.ts create mode 100644 frontend/src/shared/lib/types/MinMaxColumnSize.ts create mode 100644 frontend/src/shared/lib/types/Nullable.ts create mode 100644 frontend/src/shared/lib/types/NullableObject.ts create mode 100644 frontend/src/shared/lib/types/Optional.ts create mode 100644 frontend/src/shared/lib/types/PlayerPlaybackRate.ts create mode 100644 frontend/src/shared/lib/types/QueryParams.ts create mode 100644 frontend/src/shared/lib/types/ShowHideHandlers.ts create mode 100644 frontend/src/shared/lib/types/TimePickerSelectStep.ts create mode 100644 frontend/src/shared/lib/types/VoximplantIntegrationGuideType.ts create mode 100644 frontend/src/shared/lib/types/index.tsx create mode 100644 frontend/src/shared/lib/utils/AvatarUtil.ts create mode 100644 frontend/src/shared/lib/utils/ColorUtil.ts create mode 100644 frontend/src/shared/lib/utils/ConvertTimeUtil.ts create mode 100644 frontend/src/shared/lib/utils/EnvUtil.ts create mode 100644 frontend/src/shared/lib/utils/FileUtil.ts create mode 100644 frontend/src/shared/lib/utils/GTMUtil.ts create mode 100644 frontend/src/shared/lib/utils/JwtParserUtil.ts create mode 100644 frontend/src/shared/lib/utils/MathUtil.ts create mode 100644 frontend/src/shared/lib/utils/MonthUtil.ts create mode 100644 frontend/src/shared/lib/utils/SectionLinkUtil.ts create mode 100644 frontend/src/shared/lib/utils/TimePickerUtil.ts create mode 100644 frontend/src/shared/lib/utils/TokenUtil.ts create mode 100644 frontend/src/shared/lib/utils/UriCodingUtil.ts create mode 100644 frontend/src/shared/lib/utils/UrlTemplateUtil.ts create mode 100644 frontend/src/shared/lib/utils/UrlUtil.ts create mode 100644 frontend/src/shared/lib/utils/UuidUtil.ts create mode 100644 frontend/src/shared/lib/utils/index.tsx create mode 100644 frontend/src/shared/pages/System/BrowserNotSupportedPage/BrowserNotSupportedPage.tsx create mode 100644 frontend/src/shared/pages/System/ErrorPage/ErrorPage.tsx create mode 100644 frontend/src/shared/pages/System/ForbiddenPage/ForbiddenPage.tsx create mode 100644 frontend/src/shared/pages/System/NotFoundPage/NotFoundPage.tsx create mode 100644 frontend/src/shared/pages/System/components/ErrorCodeTitle/ErrorCodeTitle.tsx create mode 100644 frontend/src/shared/pages/System/components/SystemPageContent/SystemPageContent.tsx create mode 100644 frontend/src/shared/pages/System/components/SystemPageRoot/SystemPageRoot.tsx create mode 100644 frontend/src/shared/pages/System/components/index.tsx create mode 100644 frontend/src/shared/pages/index.tsx create mode 100644 frontend/src/shared/templates/FilterButtonWithClearIconTemplate/FilterButtonWithClearIconTemplate.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/FilterDrawerTemplate.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/components/ControlsErrorMessage/ControlsErrorMessage.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/components/FilterDelimiter/FilterDelimiter.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/components/FilterDrawerControls/FilterControls.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/components/FilterItemWrapper/FilterItemWrapper.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/components/FilterItemsBlock/FilterItemsBlock.tsx create mode 100644 frontend/src/shared/templates/FilterDrawerTemplate/components/index.tsx create mode 100644 frontend/src/shared/templates/LeftNavTemplate/LeftNavTemplate.tsx create mode 100644 frontend/src/shared/templates/MailingAndChatPageTemplate/MailingAndChatPageTemplate.tsx create mode 100644 frontend/src/shared/templates/PageTemplateWithSubheader/PageTemplateWithSubheader.tsx create mode 100644 frontend/src/shared/templates/index.tsx create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 frontend/yarn.lock create mode 100644 nginx.conf create mode 100755 start.sh create mode 100755 stop.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/backend/.eslintrc.js.old b/backend/.eslintrc.js.old new file mode 100644 index 0000000..4e5bb3b --- /dev/null +++ b/backend/.eslintrc.js.old @@ -0,0 +1,30 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/consistent-type-imports': [ + 'warn', + { + prefer: 'type-imports', + fixStyle: 'inline-type-imports', + }, + ], + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'max-len': ['error', 120, 2], + }, +}; diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..9bd728e --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,45 @@ +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.run +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode + +# Http request env +artifacts/_environment-config/http-client.env.json + +# Environment configuration +.env.local + +# Yarn +.yarn/cache/ +.yarn/unplugged/ +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* \ No newline at end of file diff --git a/backend/.gitlab-ci.yml b/backend/.gitlab-ci.yml new file mode 100644 index 0000000..ea0a9a6 --- /dev/null +++ b/backend/.gitlab-ci.yml @@ -0,0 +1,145 @@ +stages: + - prepare + - build + - deploy + +default: + image: kroniak/ssh-client + interruptible: true + before_script: + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config + - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + - chmod 400 ~/.ssh/id_rsa + +.prepare_script: &prepare_script + - tar -czf sources.tar.gz . + - scp -r sources.tar.gz $USER@$SERVER:$FOLDER + +.build_script: &build_script + - ssh $USER@$SERVER "cd $FOLDER && ./build.sh" + +.deploy_script: &deploy_script + - ssh $USER@$SERVER "cd $FOLDER && ./deploy.sh" + +.staging_job: + rules: + - if: $CI_COMMIT_BRANCH == "develop" + variables: + SSH_PRIVATE_KEY: $STAGING_SSH_PRIVATE_KEY + USER: $STAGING_USER + SERVER: $STAGING_SERVER + FOLDER: /amwork/backend + environment: + name: staging + url: https://amwork.dev + +.amwork_job: + rules: + - if: $CI_COMMIT_BRANCH == "main" + variables: + SSH_PRIVATE_KEY: $AMWORK_PROD_SSH_PRIVATE_KEY + USER: $AMWORK_PROD_USER + SERVER: $AMWORK_PROD_SERVER + FOLDER: /amwork/backend + environment: + name: production/amwork + url: https://amwork.com + +.mywork_job: + rules: + - if: $CI_COMMIT_BRANCH == "main" + variables: + SSH_PRIVATE_KEY: $MYWORK_PROD_SSH_PRIVATE_KEY + USER: $MYWORK_PROD_USER + SERVER: $MYWORK_PROD_SERVER + FOLDER: /mywork/backend + environment: + name: production/mywork + url: https://mywork.app + +# Staging +staging_prepare: + extends: .staging_job + stage: prepare + script: *prepare_script +staging_build: + extends: .staging_job + stage: build + variables: + GIT_STRATEGY: none + script: *build_script +staging_deploy: + extends: .staging_job + stage: deploy + variables: + GIT_STRATEGY: none + script: *deploy_script + +# Amwork Production +amwork_prepare: + extends: .amwork_job + stage: prepare + script: *prepare_script +amwork_build: + extends: .amwork_job + stage: build + variables: + GIT_STRATEGY: none + script: *build_script +amwork_deploy: + extends: .amwork_job + stage: deploy + when: manual + variables: + GIT_STRATEGY: none + script: *deploy_script + +# Mywork Production +mywork_prepare: + extends: .mywork_job + stage: prepare + variables: + FOLDER: /apps/mywork/backend + script: *prepare_script +mywork_build: + extends: .mywork_job + stage: build + variables: + GIT_STRATEGY: none + FOLDER: /apps/mywork/backend + script: *build_script +mywork_deploy: + extends: .mywork_job + stage: deploy + when: manual + variables: + GIT_STRATEGY: none + FOLDER: /apps/mywork/backend + script: *deploy_script + +# Platforma500 Production +platforma500_prepare: + extends: .mywork_job + stage: prepare + needs: [mywork_prepare] + variables: + FOLDER: /apps/platforma500/backend + script: *prepare_script +platforma500_build: + extends: .mywork_job + stage: build + needs: [mywork_build] + variables: + GIT_STRATEGY: none + FOLDER: /apps/platforma500/backend + script: *build_script +platforma500_deploy: + extends: .mywork_job + stage: deploy + when: manual + variables: + GIT_STRATEGY: none + FOLDER: /apps/platforma500/backend + script: *deploy_script \ No newline at end of file diff --git a/backend/.prettierrc b/backend/.prettierrc new file mode 100644 index 0000000..f65aabc --- /dev/null +++ b/backend/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "printWidth": 120 +} \ No newline at end of file diff --git a/backend/.yarn/releases/yarn-4.9.1.cjs b/backend/.yarn/releases/yarn-4.9.1.cjs new file mode 100644 index 0000000..657026d --- /dev/null +++ b/backend/.yarn/releases/yarn-4.9.1.cjs @@ -0,0 +1,948 @@ +#!/usr/bin/env node +/* eslint-disable */ +//prettier-ignore +(()=>{var u7e=Object.create;var D_=Object.defineProperty;var f7e=Object.getOwnPropertyDescriptor;var A7e=Object.getOwnPropertyNames;var p7e=Object.getPrototypeOf,h7e=Object.prototype.hasOwnProperty;var ye=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var It=(t,e)=>()=>(t&&(e=t(t=0)),e);var L=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),Vt=(t,e)=>{for(var r in e)D_(t,r,{get:e[r],enumerable:!0})},g7e=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of A7e(e))!h7e.call(t,a)&&a!==r&&D_(t,a,{get:()=>e[a],enumerable:!(s=f7e(e,a))||s.enumerable});return t};var et=(t,e,r)=>(r=t!=null?u7e(p7e(t)):{},g7e(e||!t||!t.__esModule?D_(r,"default",{value:t,enumerable:!0}):r,t));var ui={};Vt(ui,{SAFE_TIME:()=>P$,S_IFDIR:()=>lx,S_IFLNK:()=>cx,S_IFMT:()=>_f,S_IFREG:()=>M2});var _f,lx,M2,cx,P$,x$=It(()=>{_f=61440,lx=16384,M2=32768,cx=40960,P$=456789e3});var or={};Vt(or,{EBADF:()=>Uo,EBUSY:()=>d7e,EEXIST:()=>w7e,EINVAL:()=>y7e,EISDIR:()=>C7e,ENOENT:()=>E7e,ENOSYS:()=>m7e,ENOTDIR:()=>I7e,ENOTEMPTY:()=>v7e,EOPNOTSUPP:()=>S7e,EROFS:()=>B7e,ERR_DIR_CLOSED:()=>b_});function wc(t,e){return Object.assign(new Error(`${t}: ${e}`),{code:t})}function d7e(t){return wc("EBUSY",t)}function m7e(t,e){return wc("ENOSYS",`${t}, ${e}`)}function y7e(t){return wc("EINVAL",`invalid argument, ${t}`)}function Uo(t){return wc("EBADF",`bad file descriptor, ${t}`)}function E7e(t){return wc("ENOENT",`no such file or directory, ${t}`)}function I7e(t){return wc("ENOTDIR",`not a directory, ${t}`)}function C7e(t){return wc("EISDIR",`illegal operation on a directory, ${t}`)}function w7e(t){return wc("EEXIST",`file already exists, ${t}`)}function B7e(t){return wc("EROFS",`read-only filesystem, ${t}`)}function v7e(t){return wc("ENOTEMPTY",`directory not empty, ${t}`)}function S7e(t){return wc("EOPNOTSUPP",`operation not supported, ${t}`)}function b_(){return wc("ERR_DIR_CLOSED","Directory handle was closed")}var ux=It(()=>{});var el={};Vt(el,{BigIntStatsEntry:()=>iE,DEFAULT_MODE:()=>k_,DirEntry:()=>P_,StatEntry:()=>nE,areStatsEqual:()=>Q_,clearStats:()=>fx,convertToBigIntStats:()=>b7e,makeDefaultStats:()=>k$,makeEmptyStats:()=>D7e});function k$(){return new nE}function D7e(){return fx(k$())}function fx(t){for(let e in t)if(Object.hasOwn(t,e)){let r=t[e];typeof r=="number"?t[e]=0:typeof r=="bigint"?t[e]=BigInt(0):x_.types.isDate(r)&&(t[e]=new Date(0))}return t}function b7e(t){let e=new iE;for(let r in t)if(Object.hasOwn(t,r)){let s=t[r];typeof s=="number"?e[r]=BigInt(s):x_.types.isDate(s)&&(e[r]=new Date(s))}return e.atimeNs=e.atimeMs*BigInt(1e6),e.mtimeNs=e.mtimeMs*BigInt(1e6),e.ctimeNs=e.ctimeMs*BigInt(1e6),e.birthtimeNs=e.birthtimeMs*BigInt(1e6),e}function Q_(t,e){if(t.atimeMs!==e.atimeMs||t.birthtimeMs!==e.birthtimeMs||t.blksize!==e.blksize||t.blocks!==e.blocks||t.ctimeMs!==e.ctimeMs||t.dev!==e.dev||t.gid!==e.gid||t.ino!==e.ino||t.isBlockDevice()!==e.isBlockDevice()||t.isCharacterDevice()!==e.isCharacterDevice()||t.isDirectory()!==e.isDirectory()||t.isFIFO()!==e.isFIFO()||t.isFile()!==e.isFile()||t.isSocket()!==e.isSocket()||t.isSymbolicLink()!==e.isSymbolicLink()||t.mode!==e.mode||t.mtimeMs!==e.mtimeMs||t.nlink!==e.nlink||t.rdev!==e.rdev||t.size!==e.size||t.uid!==e.uid)return!1;let r=t,s=e;return!(r.atimeNs!==s.atimeNs||r.mtimeNs!==s.mtimeNs||r.ctimeNs!==s.ctimeNs||r.birthtimeNs!==s.birthtimeNs)}var x_,k_,P_,nE,iE,T_=It(()=>{x_=et(ye("util")),k_=33188,P_=class{constructor(){this.name="";this.path="";this.mode=0}isBlockDevice(){return!1}isCharacterDevice(){return!1}isDirectory(){return(this.mode&61440)===16384}isFIFO(){return!1}isFile(){return(this.mode&61440)===32768}isSocket(){return!1}isSymbolicLink(){return(this.mode&61440)===40960}},nE=class{constructor(){this.uid=0;this.gid=0;this.size=0;this.blksize=0;this.atimeMs=0;this.mtimeMs=0;this.ctimeMs=0;this.birthtimeMs=0;this.atime=new Date(0);this.mtime=new Date(0);this.ctime=new Date(0);this.birthtime=new Date(0);this.dev=0;this.ino=0;this.mode=k_;this.nlink=1;this.rdev=0;this.blocks=1}isBlockDevice(){return!1}isCharacterDevice(){return!1}isDirectory(){return(this.mode&61440)===16384}isFIFO(){return!1}isFile(){return(this.mode&61440)===32768}isSocket(){return!1}isSymbolicLink(){return(this.mode&61440)===40960}},iE=class{constructor(){this.uid=BigInt(0);this.gid=BigInt(0);this.size=BigInt(0);this.blksize=BigInt(0);this.atimeMs=BigInt(0);this.mtimeMs=BigInt(0);this.ctimeMs=BigInt(0);this.birthtimeMs=BigInt(0);this.atimeNs=BigInt(0);this.mtimeNs=BigInt(0);this.ctimeNs=BigInt(0);this.birthtimeNs=BigInt(0);this.atime=new Date(0);this.mtime=new Date(0);this.ctime=new Date(0);this.birthtime=new Date(0);this.dev=BigInt(0);this.ino=BigInt(0);this.mode=BigInt(k_);this.nlink=BigInt(1);this.rdev=BigInt(0);this.blocks=BigInt(1)}isBlockDevice(){return!1}isCharacterDevice(){return!1}isDirectory(){return(this.mode&BigInt(61440))===BigInt(16384)}isFIFO(){return!1}isFile(){return(this.mode&BigInt(61440))===BigInt(32768)}isSocket(){return!1}isSymbolicLink(){return(this.mode&BigInt(61440))===BigInt(40960)}}});function T7e(t){let e,r;if(e=t.match(k7e))t=e[1];else if(r=t.match(Q7e))t=`\\\\${r[1]?".\\":""}${r[2]}`;else return t;return t.replace(/\//g,"\\")}function R7e(t){t=t.replace(/\\/g,"/");let e,r;return(e=t.match(P7e))?t=`/${e[1]}`:(r=t.match(x7e))&&(t=`/unc/${r[1]?".dot/":""}${r[2]}`),t}function Ax(t,e){return t===ue?T$(e):R_(e)}var _2,vt,Er,ue,K,Q$,P7e,x7e,k7e,Q7e,R_,T$,tl=It(()=>{_2=et(ye("path")),vt={root:"/",dot:".",parent:".."},Er={home:"~",nodeModules:"node_modules",manifest:"package.json",lockfile:"yarn.lock",virtual:"__virtual__",pnpJs:".pnp.js",pnpCjs:".pnp.cjs",pnpData:".pnp.data.json",pnpEsmLoader:".pnp.loader.mjs",rc:".yarnrc.yml",env:".env"},ue=Object.create(_2.default),K=Object.create(_2.default.posix);ue.cwd=()=>process.cwd();K.cwd=process.platform==="win32"?()=>R_(process.cwd()):process.cwd;process.platform==="win32"&&(K.resolve=(...t)=>t.length>0&&K.isAbsolute(t[0])?_2.default.posix.resolve(...t):_2.default.posix.resolve(K.cwd(),...t));Q$=function(t,e,r){return e=t.normalize(e),r=t.normalize(r),e===r?".":(e.endsWith(t.sep)||(e=e+t.sep),r.startsWith(e)?r.slice(e.length):null)};ue.contains=(t,e)=>Q$(ue,t,e);K.contains=(t,e)=>Q$(K,t,e);P7e=/^([a-zA-Z]:.*)$/,x7e=/^\/\/(\.\/)?(.*)$/,k7e=/^\/([a-zA-Z]:.*)$/,Q7e=/^\/unc\/(\.dot\/)?(.*)$/;R_=process.platform==="win32"?R7e:t=>t,T$=process.platform==="win32"?T7e:t=>t;ue.fromPortablePath=T$;ue.toPortablePath=R_});async function px(t,e){let r="0123456789abcdef";await t.mkdirPromise(e.indexPath,{recursive:!0});let s=[];for(let a of r)for(let n of r)s.push(t.mkdirPromise(t.pathUtils.join(e.indexPath,`${a}${n}`),{recursive:!0}));return await Promise.all(s),e.indexPath}async function R$(t,e,r,s,a){let n=t.pathUtils.normalize(e),c=r.pathUtils.normalize(s),f=[],p=[],{atime:h,mtime:E}=a.stableTime?{atime:md,mtime:md}:await r.lstatPromise(c);await t.mkdirpPromise(t.pathUtils.dirname(e),{utimes:[h,E]}),await F_(f,p,t,n,r,c,{...a,didParentExist:!0});for(let C of f)await C();await Promise.all(p.map(C=>C()))}async function F_(t,e,r,s,a,n,c){let f=c.didParentExist?await F$(r,s):null,p=await a.lstatPromise(n),{atime:h,mtime:E}=c.stableTime?{atime:md,mtime:md}:p,C;switch(!0){case p.isDirectory():C=await N7e(t,e,r,s,f,a,n,p,c);break;case p.isFile():C=await M7e(t,e,r,s,f,a,n,p,c);break;case p.isSymbolicLink():C=await _7e(t,e,r,s,f,a,n,p,c);break;default:throw new Error(`Unsupported file type (${p.mode})`)}return(c.linkStrategy?.type!=="HardlinkFromIndex"||!p.isFile())&&((C||f?.mtime?.getTime()!==E.getTime()||f?.atime?.getTime()!==h.getTime())&&(e.push(()=>r.lutimesPromise(s,h,E)),C=!0),(f===null||(f.mode&511)!==(p.mode&511))&&(e.push(()=>r.chmodPromise(s,p.mode&511)),C=!0)),C}async function F$(t,e){try{return await t.lstatPromise(e)}catch{return null}}async function N7e(t,e,r,s,a,n,c,f,p){if(a!==null&&!a.isDirectory())if(p.overwrite)t.push(async()=>r.removePromise(s)),a=null;else return!1;let h=!1;a===null&&(t.push(async()=>{try{await r.mkdirPromise(s,{mode:f.mode})}catch(S){if(S.code!=="EEXIST")throw S}}),h=!0);let E=await n.readdirPromise(c),C=p.didParentExist&&!a?{...p,didParentExist:!1}:p;if(p.stableSort)for(let S of E.sort())await F_(t,e,r,r.pathUtils.join(s,S),n,n.pathUtils.join(c,S),C)&&(h=!0);else(await Promise.all(E.map(async P=>{await F_(t,e,r,r.pathUtils.join(s,P),n,n.pathUtils.join(c,P),C)}))).some(P=>P)&&(h=!0);return h}async function O7e(t,e,r,s,a,n,c,f,p,h){let E=await n.checksumFilePromise(c,{algorithm:"sha1"}),C=420,S=f.mode&511,P=`${E}${S!==C?S.toString(8):""}`,I=r.pathUtils.join(h.indexPath,E.slice(0,2),`${P}.dat`),R;(ce=>(ce[ce.Lock=0]="Lock",ce[ce.Rename=1]="Rename"))(R||={});let N=1,U=await F$(r,I);if(a){let ie=U&&a.dev===U.dev&&a.ino===U.ino,Ae=U?.mtimeMs!==F7e;if(ie&&Ae&&h.autoRepair&&(N=0,U=null),!ie)if(p.overwrite)t.push(async()=>r.removePromise(s)),a=null;else return!1}let W=!U&&N===1?`${I}.${Math.floor(Math.random()*4294967296).toString(16).padStart(8,"0")}`:null,te=!1;return t.push(async()=>{if(!U&&(N===0&&await r.lockPromise(I,async()=>{let ie=await n.readFilePromise(c);await r.writeFilePromise(I,ie)}),N===1&&W)){let ie=await n.readFilePromise(c);await r.writeFilePromise(W,ie);try{await r.linkPromise(W,I)}catch(Ae){if(Ae.code==="EEXIST")te=!0,await r.unlinkPromise(W);else throw Ae}}a||await r.linkPromise(I,s)}),e.push(async()=>{U||(await r.lutimesPromise(I,md,md),S!==C&&await r.chmodPromise(I,S)),W&&!te&&await r.unlinkPromise(W)}),!1}async function L7e(t,e,r,s,a,n,c,f,p){if(a!==null)if(p.overwrite)t.push(async()=>r.removePromise(s)),a=null;else return!1;return t.push(async()=>{let h=await n.readFilePromise(c);await r.writeFilePromise(s,h)}),!0}async function M7e(t,e,r,s,a,n,c,f,p){return p.linkStrategy?.type==="HardlinkFromIndex"?O7e(t,e,r,s,a,n,c,f,p,p.linkStrategy):L7e(t,e,r,s,a,n,c,f,p)}async function _7e(t,e,r,s,a,n,c,f,p){if(a!==null)if(p.overwrite)t.push(async()=>r.removePromise(s)),a=null;else return!1;return t.push(async()=>{await r.symlinkPromise(Ax(r.pathUtils,await n.readlinkPromise(c)),s)}),!0}var md,F7e,N_=It(()=>{tl();md=new Date(456789e3*1e3),F7e=md.getTime()});function hx(t,e,r,s){let a=()=>{let n=r.shift();if(typeof n>"u")return null;let c=t.pathUtils.join(e,n);return Object.assign(t.statSync(c),{name:n,path:void 0})};return new U2(e,a,s)}var U2,N$=It(()=>{ux();U2=class{constructor(e,r,s={}){this.path=e;this.nextDirent=r;this.opts=s;this.closed=!1}throwIfClosed(){if(this.closed)throw b_()}async*[Symbol.asyncIterator](){try{let e;for(;(e=await this.read())!==null;)yield e}finally{await this.close()}}read(e){let r=this.readSync();return typeof e<"u"?e(null,r):Promise.resolve(r)}readSync(){return this.throwIfClosed(),this.nextDirent()}close(e){return this.closeSync(),typeof e<"u"?e(null):Promise.resolve()}closeSync(){this.throwIfClosed(),this.opts.onClose?.(),this.closed=!0}}});function O$(t,e){if(t!==e)throw new Error(`Invalid StatWatcher status: expected '${e}', got '${t}'`)}var L$,gx,M$=It(()=>{L$=ye("events");T_();gx=class t extends L$.EventEmitter{constructor(r,s,{bigint:a=!1}={}){super();this.status="ready";this.changeListeners=new Map;this.startTimeout=null;this.fakeFs=r,this.path=s,this.bigint=a,this.lastStats=this.stat()}static create(r,s,a){let n=new t(r,s,a);return n.start(),n}start(){O$(this.status,"ready"),this.status="running",this.startTimeout=setTimeout(()=>{this.startTimeout=null,this.fakeFs.existsSync(this.path)||this.emit("change",this.lastStats,this.lastStats)},3)}stop(){O$(this.status,"running"),this.status="stopped",this.startTimeout!==null&&(clearTimeout(this.startTimeout),this.startTimeout=null),this.emit("stop")}stat(){try{return this.fakeFs.statSync(this.path,{bigint:this.bigint})}catch{let r=this.bigint?new iE:new nE;return fx(r)}}makeInterval(r){let s=setInterval(()=>{let a=this.stat(),n=this.lastStats;Q_(a,n)||(this.lastStats=a,this.emit("change",a,n))},r.interval);return r.persistent?s:s.unref()}registerChangeListener(r,s){this.addListener("change",r),this.changeListeners.set(r,this.makeInterval(s))}unregisterChangeListener(r){this.removeListener("change",r);let s=this.changeListeners.get(r);typeof s<"u"&&clearInterval(s),this.changeListeners.delete(r)}unregisterAllChangeListeners(){for(let r of this.changeListeners.keys())this.unregisterChangeListener(r)}hasChangeListeners(){return this.changeListeners.size>0}ref(){for(let r of this.changeListeners.values())r.ref();return this}unref(){for(let r of this.changeListeners.values())r.unref();return this}}});function sE(t,e,r,s){let a,n,c,f;switch(typeof r){case"function":a=!1,n=!0,c=5007,f=r;break;default:({bigint:a=!1,persistent:n=!0,interval:c=5007}=r),f=s;break}let p=dx.get(t);typeof p>"u"&&dx.set(t,p=new Map);let h=p.get(e);return typeof h>"u"&&(h=gx.create(t,e,{bigint:a}),p.set(e,h)),h.registerChangeListener(f,{persistent:n,interval:c}),h}function yd(t,e,r){let s=dx.get(t);if(typeof s>"u")return;let a=s.get(e);typeof a>"u"||(typeof r>"u"?a.unregisterAllChangeListeners():a.unregisterChangeListener(r),a.hasChangeListeners()||(a.stop(),s.delete(e)))}function Ed(t){let e=dx.get(t);if(!(typeof e>"u"))for(let r of e.keys())yd(t,r)}var dx,O_=It(()=>{M$();dx=new WeakMap});function U7e(t){let e=t.match(/\r?\n/g);if(e===null)return U$.EOL;let r=e.filter(a=>a===`\r +`).length,s=e.length-r;return r>s?`\r +`:` +`}function Id(t,e){return e.replace(/\r?\n/g,U7e(t))}var _$,U$,Ep,Uf,Cd=It(()=>{_$=ye("crypto"),U$=ye("os");N_();tl();Ep=class{constructor(e){this.pathUtils=e}async*genTraversePromise(e,{stableSort:r=!1}={}){let s=[e];for(;s.length>0;){let a=s.shift();if((await this.lstatPromise(a)).isDirectory()){let c=await this.readdirPromise(a);if(r)for(let f of c.sort())s.push(this.pathUtils.join(a,f));else throw new Error("Not supported")}else yield a}}async checksumFilePromise(e,{algorithm:r="sha512"}={}){let s=await this.openPromise(e,"r");try{let n=Buffer.allocUnsafeSlow(65536),c=(0,_$.createHash)(r),f=0;for(;(f=await this.readPromise(s,n,0,65536))!==0;)c.update(f===65536?n:n.slice(0,f));return c.digest("hex")}finally{await this.closePromise(s)}}async removePromise(e,{recursive:r=!0,maxRetries:s=5}={}){let a;try{a=await this.lstatPromise(e)}catch(n){if(n.code==="ENOENT")return;throw n}if(a.isDirectory()){if(r){let n=await this.readdirPromise(e);await Promise.all(n.map(c=>this.removePromise(this.pathUtils.resolve(e,c))))}for(let n=0;n<=s;n++)try{await this.rmdirPromise(e);break}catch(c){if(c.code!=="EBUSY"&&c.code!=="ENOTEMPTY")throw c;nsetTimeout(f,n*100))}}else await this.unlinkPromise(e)}removeSync(e,{recursive:r=!0}={}){let s;try{s=this.lstatSync(e)}catch(a){if(a.code==="ENOENT")return;throw a}if(s.isDirectory()){if(r)for(let a of this.readdirSync(e))this.removeSync(this.pathUtils.resolve(e,a));this.rmdirSync(e)}else this.unlinkSync(e)}async mkdirpPromise(e,{chmod:r,utimes:s}={}){if(e=this.resolve(e),e===this.pathUtils.dirname(e))return;let a=e.split(this.pathUtils.sep),n;for(let c=2;c<=a.length;++c){let f=a.slice(0,c).join(this.pathUtils.sep);if(!this.existsSync(f)){try{await this.mkdirPromise(f)}catch(p){if(p.code==="EEXIST")continue;throw p}if(n??=f,r!=null&&await this.chmodPromise(f,r),s!=null)await this.utimesPromise(f,s[0],s[1]);else{let p=await this.statPromise(this.pathUtils.dirname(f));await this.utimesPromise(f,p.atime,p.mtime)}}}return n}mkdirpSync(e,{chmod:r,utimes:s}={}){if(e=this.resolve(e),e===this.pathUtils.dirname(e))return;let a=e.split(this.pathUtils.sep),n;for(let c=2;c<=a.length;++c){let f=a.slice(0,c).join(this.pathUtils.sep);if(!this.existsSync(f)){try{this.mkdirSync(f)}catch(p){if(p.code==="EEXIST")continue;throw p}if(n??=f,r!=null&&this.chmodSync(f,r),s!=null)this.utimesSync(f,s[0],s[1]);else{let p=this.statSync(this.pathUtils.dirname(f));this.utimesSync(f,p.atime,p.mtime)}}}return n}async copyPromise(e,r,{baseFs:s=this,overwrite:a=!0,stableSort:n=!1,stableTime:c=!1,linkStrategy:f=null}={}){return await R$(this,e,s,r,{overwrite:a,stableSort:n,stableTime:c,linkStrategy:f})}copySync(e,r,{baseFs:s=this,overwrite:a=!0}={}){let n=s.lstatSync(r),c=this.existsSync(e);if(n.isDirectory()){this.mkdirpSync(e);let p=s.readdirSync(r);for(let h of p)this.copySync(this.pathUtils.join(e,h),s.pathUtils.join(r,h),{baseFs:s,overwrite:a})}else if(n.isFile()){if(!c||a){c&&this.removeSync(e);let p=s.readFileSync(r);this.writeFileSync(e,p)}}else if(n.isSymbolicLink()){if(!c||a){c&&this.removeSync(e);let p=s.readlinkSync(r);this.symlinkSync(Ax(this.pathUtils,p),e)}}else throw new Error(`Unsupported file type (file: ${r}, mode: 0o${n.mode.toString(8).padStart(6,"0")})`);let f=n.mode&511;this.chmodSync(e,f)}async changeFilePromise(e,r,s={}){return Buffer.isBuffer(r)?this.changeFileBufferPromise(e,r,s):this.changeFileTextPromise(e,r,s)}async changeFileBufferPromise(e,r,{mode:s}={}){let a=Buffer.alloc(0);try{a=await this.readFilePromise(e)}catch{}Buffer.compare(a,r)!==0&&await this.writeFilePromise(e,r,{mode:s})}async changeFileTextPromise(e,r,{automaticNewlines:s,mode:a}={}){let n="";try{n=await this.readFilePromise(e,"utf8")}catch{}let c=s?Id(n,r):r;n!==c&&await this.writeFilePromise(e,c,{mode:a})}changeFileSync(e,r,s={}){return Buffer.isBuffer(r)?this.changeFileBufferSync(e,r,s):this.changeFileTextSync(e,r,s)}changeFileBufferSync(e,r,{mode:s}={}){let a=Buffer.alloc(0);try{a=this.readFileSync(e)}catch{}Buffer.compare(a,r)!==0&&this.writeFileSync(e,r,{mode:s})}changeFileTextSync(e,r,{automaticNewlines:s=!1,mode:a}={}){let n="";try{n=this.readFileSync(e,"utf8")}catch{}let c=s?Id(n,r):r;n!==c&&this.writeFileSync(e,c,{mode:a})}async movePromise(e,r){try{await this.renamePromise(e,r)}catch(s){if(s.code==="EXDEV")await this.copyPromise(r,e),await this.removePromise(e);else throw s}}moveSync(e,r){try{this.renameSync(e,r)}catch(s){if(s.code==="EXDEV")this.copySync(r,e),this.removeSync(e);else throw s}}async lockPromise(e,r){let s=`${e}.flock`,a=1e3/60,n=Date.now(),c=null,f=async()=>{let p;try{[p]=await this.readJsonPromise(s)}catch{return Date.now()-n<500}try{return process.kill(p,0),!0}catch{return!1}};for(;c===null;)try{c=await this.openPromise(s,"wx")}catch(p){if(p.code==="EEXIST"){if(!await f())try{await this.unlinkPromise(s);continue}catch{}if(Date.now()-n<60*1e3)await new Promise(h=>setTimeout(h,a));else throw new Error(`Couldn't acquire a lock in a reasonable time (via ${s})`)}else throw p}await this.writePromise(c,JSON.stringify([process.pid]));try{return await r()}finally{try{await this.closePromise(c),await this.unlinkPromise(s)}catch{}}}async readJsonPromise(e){let r=await this.readFilePromise(e,"utf8");try{return JSON.parse(r)}catch(s){throw s.message+=` (in ${e})`,s}}readJsonSync(e){let r=this.readFileSync(e,"utf8");try{return JSON.parse(r)}catch(s){throw s.message+=` (in ${e})`,s}}async writeJsonPromise(e,r,{compact:s=!1}={}){let a=s?0:2;return await this.writeFilePromise(e,`${JSON.stringify(r,null,a)} +`)}writeJsonSync(e,r,{compact:s=!1}={}){let a=s?0:2;return this.writeFileSync(e,`${JSON.stringify(r,null,a)} +`)}async preserveTimePromise(e,r){let s=await this.lstatPromise(e),a=await r();typeof a<"u"&&(e=a),await this.lutimesPromise(e,s.atime,s.mtime)}async preserveTimeSync(e,r){let s=this.lstatSync(e),a=r();typeof a<"u"&&(e=a),this.lutimesSync(e,s.atime,s.mtime)}},Uf=class extends Ep{constructor(){super(K)}}});var Hs,Ip=It(()=>{Cd();Hs=class extends Ep{getExtractHint(e){return this.baseFs.getExtractHint(e)}resolve(e){return this.mapFromBase(this.baseFs.resolve(this.mapToBase(e)))}getRealPath(){return this.mapFromBase(this.baseFs.getRealPath())}async openPromise(e,r,s){return this.baseFs.openPromise(this.mapToBase(e),r,s)}openSync(e,r,s){return this.baseFs.openSync(this.mapToBase(e),r,s)}async opendirPromise(e,r){return Object.assign(await this.baseFs.opendirPromise(this.mapToBase(e),r),{path:e})}opendirSync(e,r){return Object.assign(this.baseFs.opendirSync(this.mapToBase(e),r),{path:e})}async readPromise(e,r,s,a,n){return await this.baseFs.readPromise(e,r,s,a,n)}readSync(e,r,s,a,n){return this.baseFs.readSync(e,r,s,a,n)}async writePromise(e,r,s,a,n){return typeof r=="string"?await this.baseFs.writePromise(e,r,s):await this.baseFs.writePromise(e,r,s,a,n)}writeSync(e,r,s,a,n){return typeof r=="string"?this.baseFs.writeSync(e,r,s):this.baseFs.writeSync(e,r,s,a,n)}async closePromise(e){return this.baseFs.closePromise(e)}closeSync(e){this.baseFs.closeSync(e)}createReadStream(e,r){return this.baseFs.createReadStream(e!==null?this.mapToBase(e):e,r)}createWriteStream(e,r){return this.baseFs.createWriteStream(e!==null?this.mapToBase(e):e,r)}async realpathPromise(e){return this.mapFromBase(await this.baseFs.realpathPromise(this.mapToBase(e)))}realpathSync(e){return this.mapFromBase(this.baseFs.realpathSync(this.mapToBase(e)))}async existsPromise(e){return this.baseFs.existsPromise(this.mapToBase(e))}existsSync(e){return this.baseFs.existsSync(this.mapToBase(e))}accessSync(e,r){return this.baseFs.accessSync(this.mapToBase(e),r)}async accessPromise(e,r){return this.baseFs.accessPromise(this.mapToBase(e),r)}async statPromise(e,r){return this.baseFs.statPromise(this.mapToBase(e),r)}statSync(e,r){return this.baseFs.statSync(this.mapToBase(e),r)}async fstatPromise(e,r){return this.baseFs.fstatPromise(e,r)}fstatSync(e,r){return this.baseFs.fstatSync(e,r)}lstatPromise(e,r){return this.baseFs.lstatPromise(this.mapToBase(e),r)}lstatSync(e,r){return this.baseFs.lstatSync(this.mapToBase(e),r)}async fchmodPromise(e,r){return this.baseFs.fchmodPromise(e,r)}fchmodSync(e,r){return this.baseFs.fchmodSync(e,r)}async chmodPromise(e,r){return this.baseFs.chmodPromise(this.mapToBase(e),r)}chmodSync(e,r){return this.baseFs.chmodSync(this.mapToBase(e),r)}async fchownPromise(e,r,s){return this.baseFs.fchownPromise(e,r,s)}fchownSync(e,r,s){return this.baseFs.fchownSync(e,r,s)}async chownPromise(e,r,s){return this.baseFs.chownPromise(this.mapToBase(e),r,s)}chownSync(e,r,s){return this.baseFs.chownSync(this.mapToBase(e),r,s)}async renamePromise(e,r){return this.baseFs.renamePromise(this.mapToBase(e),this.mapToBase(r))}renameSync(e,r){return this.baseFs.renameSync(this.mapToBase(e),this.mapToBase(r))}async copyFilePromise(e,r,s=0){return this.baseFs.copyFilePromise(this.mapToBase(e),this.mapToBase(r),s)}copyFileSync(e,r,s=0){return this.baseFs.copyFileSync(this.mapToBase(e),this.mapToBase(r),s)}async appendFilePromise(e,r,s){return this.baseFs.appendFilePromise(this.fsMapToBase(e),r,s)}appendFileSync(e,r,s){return this.baseFs.appendFileSync(this.fsMapToBase(e),r,s)}async writeFilePromise(e,r,s){return this.baseFs.writeFilePromise(this.fsMapToBase(e),r,s)}writeFileSync(e,r,s){return this.baseFs.writeFileSync(this.fsMapToBase(e),r,s)}async unlinkPromise(e){return this.baseFs.unlinkPromise(this.mapToBase(e))}unlinkSync(e){return this.baseFs.unlinkSync(this.mapToBase(e))}async utimesPromise(e,r,s){return this.baseFs.utimesPromise(this.mapToBase(e),r,s)}utimesSync(e,r,s){return this.baseFs.utimesSync(this.mapToBase(e),r,s)}async lutimesPromise(e,r,s){return this.baseFs.lutimesPromise(this.mapToBase(e),r,s)}lutimesSync(e,r,s){return this.baseFs.lutimesSync(this.mapToBase(e),r,s)}async mkdirPromise(e,r){return this.baseFs.mkdirPromise(this.mapToBase(e),r)}mkdirSync(e,r){return this.baseFs.mkdirSync(this.mapToBase(e),r)}async rmdirPromise(e,r){return this.baseFs.rmdirPromise(this.mapToBase(e),r)}rmdirSync(e,r){return this.baseFs.rmdirSync(this.mapToBase(e),r)}async rmPromise(e,r){return this.baseFs.rmPromise(this.mapToBase(e),r)}rmSync(e,r){return this.baseFs.rmSync(this.mapToBase(e),r)}async linkPromise(e,r){return this.baseFs.linkPromise(this.mapToBase(e),this.mapToBase(r))}linkSync(e,r){return this.baseFs.linkSync(this.mapToBase(e),this.mapToBase(r))}async symlinkPromise(e,r,s){let a=this.mapToBase(r);if(this.pathUtils.isAbsolute(e))return this.baseFs.symlinkPromise(this.mapToBase(e),a,s);let n=this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(r),e)),c=this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(a),n);return this.baseFs.symlinkPromise(c,a,s)}symlinkSync(e,r,s){let a=this.mapToBase(r);if(this.pathUtils.isAbsolute(e))return this.baseFs.symlinkSync(this.mapToBase(e),a,s);let n=this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(r),e)),c=this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(a),n);return this.baseFs.symlinkSync(c,a,s)}async readFilePromise(e,r){return this.baseFs.readFilePromise(this.fsMapToBase(e),r)}readFileSync(e,r){return this.baseFs.readFileSync(this.fsMapToBase(e),r)}readdirPromise(e,r){return this.baseFs.readdirPromise(this.mapToBase(e),r)}readdirSync(e,r){return this.baseFs.readdirSync(this.mapToBase(e),r)}async readlinkPromise(e){return this.mapFromBase(await this.baseFs.readlinkPromise(this.mapToBase(e)))}readlinkSync(e){return this.mapFromBase(this.baseFs.readlinkSync(this.mapToBase(e)))}async truncatePromise(e,r){return this.baseFs.truncatePromise(this.mapToBase(e),r)}truncateSync(e,r){return this.baseFs.truncateSync(this.mapToBase(e),r)}async ftruncatePromise(e,r){return this.baseFs.ftruncatePromise(e,r)}ftruncateSync(e,r){return this.baseFs.ftruncateSync(e,r)}watch(e,r,s){return this.baseFs.watch(this.mapToBase(e),r,s)}watchFile(e,r,s){return this.baseFs.watchFile(this.mapToBase(e),r,s)}unwatchFile(e,r){return this.baseFs.unwatchFile(this.mapToBase(e),r)}fsMapToBase(e){return typeof e=="number"?e:this.mapToBase(e)}}});var Hf,H$=It(()=>{Ip();Hf=class extends Hs{constructor(e,{baseFs:r,pathUtils:s}){super(s),this.target=e,this.baseFs=r}getRealPath(){return this.target}getBaseFs(){return this.baseFs}mapFromBase(e){return e}mapToBase(e){return e}}});function j$(t){let e=t;return typeof t.path=="string"&&(e.path=ue.toPortablePath(t.path)),e}var q$,Yn,wd=It(()=>{q$=et(ye("fs"));Cd();tl();Yn=class extends Uf{constructor(e=q$.default){super(),this.realFs=e}getExtractHint(){return!1}getRealPath(){return vt.root}resolve(e){return K.resolve(e)}async openPromise(e,r,s){return await new Promise((a,n)=>{this.realFs.open(ue.fromPortablePath(e),r,s,this.makeCallback(a,n))})}openSync(e,r,s){return this.realFs.openSync(ue.fromPortablePath(e),r,s)}async opendirPromise(e,r){return await new Promise((s,a)=>{typeof r<"u"?this.realFs.opendir(ue.fromPortablePath(e),r,this.makeCallback(s,a)):this.realFs.opendir(ue.fromPortablePath(e),this.makeCallback(s,a))}).then(s=>{let a=s;return Object.defineProperty(a,"path",{value:e,configurable:!0,writable:!0}),a})}opendirSync(e,r){let a=typeof r<"u"?this.realFs.opendirSync(ue.fromPortablePath(e),r):this.realFs.opendirSync(ue.fromPortablePath(e));return Object.defineProperty(a,"path",{value:e,configurable:!0,writable:!0}),a}async readPromise(e,r,s=0,a=0,n=-1){return await new Promise((c,f)=>{this.realFs.read(e,r,s,a,n,(p,h)=>{p?f(p):c(h)})})}readSync(e,r,s,a,n){return this.realFs.readSync(e,r,s,a,n)}async writePromise(e,r,s,a,n){return await new Promise((c,f)=>typeof r=="string"?this.realFs.write(e,r,s,this.makeCallback(c,f)):this.realFs.write(e,r,s,a,n,this.makeCallback(c,f)))}writeSync(e,r,s,a,n){return typeof r=="string"?this.realFs.writeSync(e,r,s):this.realFs.writeSync(e,r,s,a,n)}async closePromise(e){await new Promise((r,s)=>{this.realFs.close(e,this.makeCallback(r,s))})}closeSync(e){this.realFs.closeSync(e)}createReadStream(e,r){let s=e!==null?ue.fromPortablePath(e):e;return this.realFs.createReadStream(s,r)}createWriteStream(e,r){let s=e!==null?ue.fromPortablePath(e):e;return this.realFs.createWriteStream(s,r)}async realpathPromise(e){return await new Promise((r,s)=>{this.realFs.realpath(ue.fromPortablePath(e),{},this.makeCallback(r,s))}).then(r=>ue.toPortablePath(r))}realpathSync(e){return ue.toPortablePath(this.realFs.realpathSync(ue.fromPortablePath(e),{}))}async existsPromise(e){return await new Promise(r=>{this.realFs.exists(ue.fromPortablePath(e),r)})}accessSync(e,r){return this.realFs.accessSync(ue.fromPortablePath(e),r)}async accessPromise(e,r){return await new Promise((s,a)=>{this.realFs.access(ue.fromPortablePath(e),r,this.makeCallback(s,a))})}existsSync(e){return this.realFs.existsSync(ue.fromPortablePath(e))}async statPromise(e,r){return await new Promise((s,a)=>{r?this.realFs.stat(ue.fromPortablePath(e),r,this.makeCallback(s,a)):this.realFs.stat(ue.fromPortablePath(e),this.makeCallback(s,a))})}statSync(e,r){return r?this.realFs.statSync(ue.fromPortablePath(e),r):this.realFs.statSync(ue.fromPortablePath(e))}async fstatPromise(e,r){return await new Promise((s,a)=>{r?this.realFs.fstat(e,r,this.makeCallback(s,a)):this.realFs.fstat(e,this.makeCallback(s,a))})}fstatSync(e,r){return r?this.realFs.fstatSync(e,r):this.realFs.fstatSync(e)}async lstatPromise(e,r){return await new Promise((s,a)=>{r?this.realFs.lstat(ue.fromPortablePath(e),r,this.makeCallback(s,a)):this.realFs.lstat(ue.fromPortablePath(e),this.makeCallback(s,a))})}lstatSync(e,r){return r?this.realFs.lstatSync(ue.fromPortablePath(e),r):this.realFs.lstatSync(ue.fromPortablePath(e))}async fchmodPromise(e,r){return await new Promise((s,a)=>{this.realFs.fchmod(e,r,this.makeCallback(s,a))})}fchmodSync(e,r){return this.realFs.fchmodSync(e,r)}async chmodPromise(e,r){return await new Promise((s,a)=>{this.realFs.chmod(ue.fromPortablePath(e),r,this.makeCallback(s,a))})}chmodSync(e,r){return this.realFs.chmodSync(ue.fromPortablePath(e),r)}async fchownPromise(e,r,s){return await new Promise((a,n)=>{this.realFs.fchown(e,r,s,this.makeCallback(a,n))})}fchownSync(e,r,s){return this.realFs.fchownSync(e,r,s)}async chownPromise(e,r,s){return await new Promise((a,n)=>{this.realFs.chown(ue.fromPortablePath(e),r,s,this.makeCallback(a,n))})}chownSync(e,r,s){return this.realFs.chownSync(ue.fromPortablePath(e),r,s)}async renamePromise(e,r){return await new Promise((s,a)=>{this.realFs.rename(ue.fromPortablePath(e),ue.fromPortablePath(r),this.makeCallback(s,a))})}renameSync(e,r){return this.realFs.renameSync(ue.fromPortablePath(e),ue.fromPortablePath(r))}async copyFilePromise(e,r,s=0){return await new Promise((a,n)=>{this.realFs.copyFile(ue.fromPortablePath(e),ue.fromPortablePath(r),s,this.makeCallback(a,n))})}copyFileSync(e,r,s=0){return this.realFs.copyFileSync(ue.fromPortablePath(e),ue.fromPortablePath(r),s)}async appendFilePromise(e,r,s){return await new Promise((a,n)=>{let c=typeof e=="string"?ue.fromPortablePath(e):e;s?this.realFs.appendFile(c,r,s,this.makeCallback(a,n)):this.realFs.appendFile(c,r,this.makeCallback(a,n))})}appendFileSync(e,r,s){let a=typeof e=="string"?ue.fromPortablePath(e):e;s?this.realFs.appendFileSync(a,r,s):this.realFs.appendFileSync(a,r)}async writeFilePromise(e,r,s){return await new Promise((a,n)=>{let c=typeof e=="string"?ue.fromPortablePath(e):e;s?this.realFs.writeFile(c,r,s,this.makeCallback(a,n)):this.realFs.writeFile(c,r,this.makeCallback(a,n))})}writeFileSync(e,r,s){let a=typeof e=="string"?ue.fromPortablePath(e):e;s?this.realFs.writeFileSync(a,r,s):this.realFs.writeFileSync(a,r)}async unlinkPromise(e){return await new Promise((r,s)=>{this.realFs.unlink(ue.fromPortablePath(e),this.makeCallback(r,s))})}unlinkSync(e){return this.realFs.unlinkSync(ue.fromPortablePath(e))}async utimesPromise(e,r,s){return await new Promise((a,n)=>{this.realFs.utimes(ue.fromPortablePath(e),r,s,this.makeCallback(a,n))})}utimesSync(e,r,s){this.realFs.utimesSync(ue.fromPortablePath(e),r,s)}async lutimesPromise(e,r,s){return await new Promise((a,n)=>{this.realFs.lutimes(ue.fromPortablePath(e),r,s,this.makeCallback(a,n))})}lutimesSync(e,r,s){this.realFs.lutimesSync(ue.fromPortablePath(e),r,s)}async mkdirPromise(e,r){return await new Promise((s,a)=>{this.realFs.mkdir(ue.fromPortablePath(e),r,this.makeCallback(s,a))})}mkdirSync(e,r){return this.realFs.mkdirSync(ue.fromPortablePath(e),r)}async rmdirPromise(e,r){return await new Promise((s,a)=>{r?this.realFs.rmdir(ue.fromPortablePath(e),r,this.makeCallback(s,a)):this.realFs.rmdir(ue.fromPortablePath(e),this.makeCallback(s,a))})}rmdirSync(e,r){return this.realFs.rmdirSync(ue.fromPortablePath(e),r)}async rmPromise(e,r){return await new Promise((s,a)=>{r?this.realFs.rm(ue.fromPortablePath(e),r,this.makeCallback(s,a)):this.realFs.rm(ue.fromPortablePath(e),this.makeCallback(s,a))})}rmSync(e,r){return this.realFs.rmSync(ue.fromPortablePath(e),r)}async linkPromise(e,r){return await new Promise((s,a)=>{this.realFs.link(ue.fromPortablePath(e),ue.fromPortablePath(r),this.makeCallback(s,a))})}linkSync(e,r){return this.realFs.linkSync(ue.fromPortablePath(e),ue.fromPortablePath(r))}async symlinkPromise(e,r,s){return await new Promise((a,n)=>{this.realFs.symlink(ue.fromPortablePath(e.replace(/\/+$/,"")),ue.fromPortablePath(r),s,this.makeCallback(a,n))})}symlinkSync(e,r,s){return this.realFs.symlinkSync(ue.fromPortablePath(e.replace(/\/+$/,"")),ue.fromPortablePath(r),s)}async readFilePromise(e,r){return await new Promise((s,a)=>{let n=typeof e=="string"?ue.fromPortablePath(e):e;this.realFs.readFile(n,r,this.makeCallback(s,a))})}readFileSync(e,r){let s=typeof e=="string"?ue.fromPortablePath(e):e;return this.realFs.readFileSync(s,r)}async readdirPromise(e,r){return await new Promise((s,a)=>{r?r.recursive&&process.platform==="win32"?r.withFileTypes?this.realFs.readdir(ue.fromPortablePath(e),r,this.makeCallback(n=>s(n.map(j$)),a)):this.realFs.readdir(ue.fromPortablePath(e),r,this.makeCallback(n=>s(n.map(ue.toPortablePath)),a)):this.realFs.readdir(ue.fromPortablePath(e),r,this.makeCallback(s,a)):this.realFs.readdir(ue.fromPortablePath(e),this.makeCallback(s,a))})}readdirSync(e,r){return r?r.recursive&&process.platform==="win32"?r.withFileTypes?this.realFs.readdirSync(ue.fromPortablePath(e),r).map(j$):this.realFs.readdirSync(ue.fromPortablePath(e),r).map(ue.toPortablePath):this.realFs.readdirSync(ue.fromPortablePath(e),r):this.realFs.readdirSync(ue.fromPortablePath(e))}async readlinkPromise(e){return await new Promise((r,s)=>{this.realFs.readlink(ue.fromPortablePath(e),this.makeCallback(r,s))}).then(r=>ue.toPortablePath(r))}readlinkSync(e){return ue.toPortablePath(this.realFs.readlinkSync(ue.fromPortablePath(e)))}async truncatePromise(e,r){return await new Promise((s,a)=>{this.realFs.truncate(ue.fromPortablePath(e),r,this.makeCallback(s,a))})}truncateSync(e,r){return this.realFs.truncateSync(ue.fromPortablePath(e),r)}async ftruncatePromise(e,r){return await new Promise((s,a)=>{this.realFs.ftruncate(e,r,this.makeCallback(s,a))})}ftruncateSync(e,r){return this.realFs.ftruncateSync(e,r)}watch(e,r,s){return this.realFs.watch(ue.fromPortablePath(e),r,s)}watchFile(e,r,s){return this.realFs.watchFile(ue.fromPortablePath(e),r,s)}unwatchFile(e,r){return this.realFs.unwatchFile(ue.fromPortablePath(e),r)}makeCallback(e,r){return(s,a)=>{s?r(s):e(a)}}}});var Sn,G$=It(()=>{wd();Ip();tl();Sn=class extends Hs{constructor(e,{baseFs:r=new Yn}={}){super(K),this.target=this.pathUtils.normalize(e),this.baseFs=r}getRealPath(){return this.pathUtils.resolve(this.baseFs.getRealPath(),this.target)}resolve(e){return this.pathUtils.isAbsolute(e)?K.normalize(e):this.baseFs.resolve(K.join(this.target,e))}mapFromBase(e){return e}mapToBase(e){return this.pathUtils.isAbsolute(e)?e:this.pathUtils.join(this.target,e)}}});var W$,jf,Y$=It(()=>{wd();Ip();tl();W$=vt.root,jf=class extends Hs{constructor(e,{baseFs:r=new Yn}={}){super(K),this.target=this.pathUtils.resolve(vt.root,e),this.baseFs=r}getRealPath(){return this.pathUtils.resolve(this.baseFs.getRealPath(),this.pathUtils.relative(vt.root,this.target))}getTarget(){return this.target}getBaseFs(){return this.baseFs}mapToBase(e){let r=this.pathUtils.normalize(e);if(this.pathUtils.isAbsolute(e))return this.pathUtils.resolve(this.target,this.pathUtils.relative(W$,e));if(r.match(/^\.\.\/?/))throw new Error(`Resolving this path (${e}) would escape the jail`);return this.pathUtils.resolve(this.target,e)}mapFromBase(e){return this.pathUtils.resolve(W$,this.pathUtils.relative(this.target,e))}}});var oE,V$=It(()=>{Ip();oE=class extends Hs{constructor(r,s){super(s);this.instance=null;this.factory=r}get baseFs(){return this.instance||(this.instance=this.factory()),this.instance}set baseFs(r){this.instance=r}mapFromBase(r){return r}mapToBase(r){return r}}});var Bd,rl,n0,K$=It(()=>{Bd=ye("fs");Cd();wd();O_();ux();tl();rl=4278190080,n0=class extends Uf{constructor({baseFs:r=new Yn,filter:s=null,magicByte:a=42,maxOpenFiles:n=1/0,useCache:c=!0,maxAge:f=5e3,typeCheck:p=Bd.constants.S_IFREG,getMountPoint:h,factoryPromise:E,factorySync:C}){if(Math.floor(a)!==a||!(a>1&&a<=127))throw new Error("The magic byte must be set to a round value between 1 and 127 included");super();this.fdMap=new Map;this.nextFd=3;this.isMount=new Set;this.notMount=new Set;this.realPaths=new Map;this.limitOpenFilesTimeout=null;this.baseFs=r,this.mountInstances=c?new Map:null,this.factoryPromise=E,this.factorySync=C,this.filter=s,this.getMountPoint=h,this.magic=a<<24,this.maxAge=f,this.maxOpenFiles=n,this.typeCheck=p}getExtractHint(r){return this.baseFs.getExtractHint(r)}getRealPath(){return this.baseFs.getRealPath()}saveAndClose(){if(Ed(this),this.mountInstances)for(let[r,{childFs:s}]of this.mountInstances.entries())s.saveAndClose?.(),this.mountInstances.delete(r)}discardAndClose(){if(Ed(this),this.mountInstances)for(let[r,{childFs:s}]of this.mountInstances.entries())s.discardAndClose?.(),this.mountInstances.delete(r)}resolve(r){return this.baseFs.resolve(r)}remapFd(r,s){let a=this.nextFd++|this.magic;return this.fdMap.set(a,[r,s]),a}async openPromise(r,s,a){return await this.makeCallPromise(r,async()=>await this.baseFs.openPromise(r,s,a),async(n,{subPath:c})=>this.remapFd(n,await n.openPromise(c,s,a)))}openSync(r,s,a){return this.makeCallSync(r,()=>this.baseFs.openSync(r,s,a),(n,{subPath:c})=>this.remapFd(n,n.openSync(c,s,a)))}async opendirPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.opendirPromise(r,s),async(a,{subPath:n})=>await a.opendirPromise(n,s),{requireSubpath:!1})}opendirSync(r,s){return this.makeCallSync(r,()=>this.baseFs.opendirSync(r,s),(a,{subPath:n})=>a.opendirSync(n,s),{requireSubpath:!1})}async readPromise(r,s,a,n,c){if((r&rl)!==this.magic)return await this.baseFs.readPromise(r,s,a,n,c);let f=this.fdMap.get(r);if(typeof f>"u")throw Uo("read");let[p,h]=f;return await p.readPromise(h,s,a,n,c)}readSync(r,s,a,n,c){if((r&rl)!==this.magic)return this.baseFs.readSync(r,s,a,n,c);let f=this.fdMap.get(r);if(typeof f>"u")throw Uo("readSync");let[p,h]=f;return p.readSync(h,s,a,n,c)}async writePromise(r,s,a,n,c){if((r&rl)!==this.magic)return typeof s=="string"?await this.baseFs.writePromise(r,s,a):await this.baseFs.writePromise(r,s,a,n,c);let f=this.fdMap.get(r);if(typeof f>"u")throw Uo("write");let[p,h]=f;return typeof s=="string"?await p.writePromise(h,s,a):await p.writePromise(h,s,a,n,c)}writeSync(r,s,a,n,c){if((r&rl)!==this.magic)return typeof s=="string"?this.baseFs.writeSync(r,s,a):this.baseFs.writeSync(r,s,a,n,c);let f=this.fdMap.get(r);if(typeof f>"u")throw Uo("writeSync");let[p,h]=f;return typeof s=="string"?p.writeSync(h,s,a):p.writeSync(h,s,a,n,c)}async closePromise(r){if((r&rl)!==this.magic)return await this.baseFs.closePromise(r);let s=this.fdMap.get(r);if(typeof s>"u")throw Uo("close");this.fdMap.delete(r);let[a,n]=s;return await a.closePromise(n)}closeSync(r){if((r&rl)!==this.magic)return this.baseFs.closeSync(r);let s=this.fdMap.get(r);if(typeof s>"u")throw Uo("closeSync");this.fdMap.delete(r);let[a,n]=s;return a.closeSync(n)}createReadStream(r,s){return r===null?this.baseFs.createReadStream(r,s):this.makeCallSync(r,()=>this.baseFs.createReadStream(r,s),(a,{archivePath:n,subPath:c})=>{let f=a.createReadStream(c,s);return f.path=ue.fromPortablePath(this.pathUtils.join(n,c)),f})}createWriteStream(r,s){return r===null?this.baseFs.createWriteStream(r,s):this.makeCallSync(r,()=>this.baseFs.createWriteStream(r,s),(a,{subPath:n})=>a.createWriteStream(n,s))}async realpathPromise(r){return await this.makeCallPromise(r,async()=>await this.baseFs.realpathPromise(r),async(s,{archivePath:a,subPath:n})=>{let c=this.realPaths.get(a);return typeof c>"u"&&(c=await this.baseFs.realpathPromise(a),this.realPaths.set(a,c)),this.pathUtils.join(c,this.pathUtils.relative(vt.root,await s.realpathPromise(n)))})}realpathSync(r){return this.makeCallSync(r,()=>this.baseFs.realpathSync(r),(s,{archivePath:a,subPath:n})=>{let c=this.realPaths.get(a);return typeof c>"u"&&(c=this.baseFs.realpathSync(a),this.realPaths.set(a,c)),this.pathUtils.join(c,this.pathUtils.relative(vt.root,s.realpathSync(n)))})}async existsPromise(r){return await this.makeCallPromise(r,async()=>await this.baseFs.existsPromise(r),async(s,{subPath:a})=>await s.existsPromise(a))}existsSync(r){return this.makeCallSync(r,()=>this.baseFs.existsSync(r),(s,{subPath:a})=>s.existsSync(a))}async accessPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.accessPromise(r,s),async(a,{subPath:n})=>await a.accessPromise(n,s))}accessSync(r,s){return this.makeCallSync(r,()=>this.baseFs.accessSync(r,s),(a,{subPath:n})=>a.accessSync(n,s))}async statPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.statPromise(r,s),async(a,{subPath:n})=>await a.statPromise(n,s))}statSync(r,s){return this.makeCallSync(r,()=>this.baseFs.statSync(r,s),(a,{subPath:n})=>a.statSync(n,s))}async fstatPromise(r,s){if((r&rl)!==this.magic)return this.baseFs.fstatPromise(r,s);let a=this.fdMap.get(r);if(typeof a>"u")throw Uo("fstat");let[n,c]=a;return n.fstatPromise(c,s)}fstatSync(r,s){if((r&rl)!==this.magic)return this.baseFs.fstatSync(r,s);let a=this.fdMap.get(r);if(typeof a>"u")throw Uo("fstatSync");let[n,c]=a;return n.fstatSync(c,s)}async lstatPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.lstatPromise(r,s),async(a,{subPath:n})=>await a.lstatPromise(n,s))}lstatSync(r,s){return this.makeCallSync(r,()=>this.baseFs.lstatSync(r,s),(a,{subPath:n})=>a.lstatSync(n,s))}async fchmodPromise(r,s){if((r&rl)!==this.magic)return this.baseFs.fchmodPromise(r,s);let a=this.fdMap.get(r);if(typeof a>"u")throw Uo("fchmod");let[n,c]=a;return n.fchmodPromise(c,s)}fchmodSync(r,s){if((r&rl)!==this.magic)return this.baseFs.fchmodSync(r,s);let a=this.fdMap.get(r);if(typeof a>"u")throw Uo("fchmodSync");let[n,c]=a;return n.fchmodSync(c,s)}async chmodPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.chmodPromise(r,s),async(a,{subPath:n})=>await a.chmodPromise(n,s))}chmodSync(r,s){return this.makeCallSync(r,()=>this.baseFs.chmodSync(r,s),(a,{subPath:n})=>a.chmodSync(n,s))}async fchownPromise(r,s,a){if((r&rl)!==this.magic)return this.baseFs.fchownPromise(r,s,a);let n=this.fdMap.get(r);if(typeof n>"u")throw Uo("fchown");let[c,f]=n;return c.fchownPromise(f,s,a)}fchownSync(r,s,a){if((r&rl)!==this.magic)return this.baseFs.fchownSync(r,s,a);let n=this.fdMap.get(r);if(typeof n>"u")throw Uo("fchownSync");let[c,f]=n;return c.fchownSync(f,s,a)}async chownPromise(r,s,a){return await this.makeCallPromise(r,async()=>await this.baseFs.chownPromise(r,s,a),async(n,{subPath:c})=>await n.chownPromise(c,s,a))}chownSync(r,s,a){return this.makeCallSync(r,()=>this.baseFs.chownSync(r,s,a),(n,{subPath:c})=>n.chownSync(c,s,a))}async renamePromise(r,s){return await this.makeCallPromise(r,async()=>await this.makeCallPromise(s,async()=>await this.baseFs.renamePromise(r,s),async()=>{throw Object.assign(new Error("EEXDEV: cross-device link not permitted"),{code:"EEXDEV"})}),async(a,{subPath:n})=>await this.makeCallPromise(s,async()=>{throw Object.assign(new Error("EEXDEV: cross-device link not permitted"),{code:"EEXDEV"})},async(c,{subPath:f})=>{if(a!==c)throw Object.assign(new Error("EEXDEV: cross-device link not permitted"),{code:"EEXDEV"});return await a.renamePromise(n,f)}))}renameSync(r,s){return this.makeCallSync(r,()=>this.makeCallSync(s,()=>this.baseFs.renameSync(r,s),()=>{throw Object.assign(new Error("EEXDEV: cross-device link not permitted"),{code:"EEXDEV"})}),(a,{subPath:n})=>this.makeCallSync(s,()=>{throw Object.assign(new Error("EEXDEV: cross-device link not permitted"),{code:"EEXDEV"})},(c,{subPath:f})=>{if(a!==c)throw Object.assign(new Error("EEXDEV: cross-device link not permitted"),{code:"EEXDEV"});return a.renameSync(n,f)}))}async copyFilePromise(r,s,a=0){let n=async(c,f,p,h)=>{if(a&Bd.constants.COPYFILE_FICLONE_FORCE)throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${f}' -> ${h}'`),{code:"EXDEV"});if(a&Bd.constants.COPYFILE_EXCL&&await this.existsPromise(f))throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${f}' -> '${h}'`),{code:"EEXIST"});let E;try{E=await c.readFilePromise(f)}catch{throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${f}' -> '${h}'`),{code:"EINVAL"})}await p.writeFilePromise(h,E)};return await this.makeCallPromise(r,async()=>await this.makeCallPromise(s,async()=>await this.baseFs.copyFilePromise(r,s,a),async(c,{subPath:f})=>await n(this.baseFs,r,c,f)),async(c,{subPath:f})=>await this.makeCallPromise(s,async()=>await n(c,f,this.baseFs,s),async(p,{subPath:h})=>c!==p?await n(c,f,p,h):await c.copyFilePromise(f,h,a)))}copyFileSync(r,s,a=0){let n=(c,f,p,h)=>{if(a&Bd.constants.COPYFILE_FICLONE_FORCE)throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${f}' -> ${h}'`),{code:"EXDEV"});if(a&Bd.constants.COPYFILE_EXCL&&this.existsSync(f))throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${f}' -> '${h}'`),{code:"EEXIST"});let E;try{E=c.readFileSync(f)}catch{throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${f}' -> '${h}'`),{code:"EINVAL"})}p.writeFileSync(h,E)};return this.makeCallSync(r,()=>this.makeCallSync(s,()=>this.baseFs.copyFileSync(r,s,a),(c,{subPath:f})=>n(this.baseFs,r,c,f)),(c,{subPath:f})=>this.makeCallSync(s,()=>n(c,f,this.baseFs,s),(p,{subPath:h})=>c!==p?n(c,f,p,h):c.copyFileSync(f,h,a)))}async appendFilePromise(r,s,a){return await this.makeCallPromise(r,async()=>await this.baseFs.appendFilePromise(r,s,a),async(n,{subPath:c})=>await n.appendFilePromise(c,s,a))}appendFileSync(r,s,a){return this.makeCallSync(r,()=>this.baseFs.appendFileSync(r,s,a),(n,{subPath:c})=>n.appendFileSync(c,s,a))}async writeFilePromise(r,s,a){return await this.makeCallPromise(r,async()=>await this.baseFs.writeFilePromise(r,s,a),async(n,{subPath:c})=>await n.writeFilePromise(c,s,a))}writeFileSync(r,s,a){return this.makeCallSync(r,()=>this.baseFs.writeFileSync(r,s,a),(n,{subPath:c})=>n.writeFileSync(c,s,a))}async unlinkPromise(r){return await this.makeCallPromise(r,async()=>await this.baseFs.unlinkPromise(r),async(s,{subPath:a})=>await s.unlinkPromise(a))}unlinkSync(r){return this.makeCallSync(r,()=>this.baseFs.unlinkSync(r),(s,{subPath:a})=>s.unlinkSync(a))}async utimesPromise(r,s,a){return await this.makeCallPromise(r,async()=>await this.baseFs.utimesPromise(r,s,a),async(n,{subPath:c})=>await n.utimesPromise(c,s,a))}utimesSync(r,s,a){return this.makeCallSync(r,()=>this.baseFs.utimesSync(r,s,a),(n,{subPath:c})=>n.utimesSync(c,s,a))}async lutimesPromise(r,s,a){return await this.makeCallPromise(r,async()=>await this.baseFs.lutimesPromise(r,s,a),async(n,{subPath:c})=>await n.lutimesPromise(c,s,a))}lutimesSync(r,s,a){return this.makeCallSync(r,()=>this.baseFs.lutimesSync(r,s,a),(n,{subPath:c})=>n.lutimesSync(c,s,a))}async mkdirPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.mkdirPromise(r,s),async(a,{subPath:n})=>await a.mkdirPromise(n,s))}mkdirSync(r,s){return this.makeCallSync(r,()=>this.baseFs.mkdirSync(r,s),(a,{subPath:n})=>a.mkdirSync(n,s))}async rmdirPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.rmdirPromise(r,s),async(a,{subPath:n})=>await a.rmdirPromise(n,s))}rmdirSync(r,s){return this.makeCallSync(r,()=>this.baseFs.rmdirSync(r,s),(a,{subPath:n})=>a.rmdirSync(n,s))}async rmPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.rmPromise(r,s),async(a,{subPath:n})=>await a.rmPromise(n,s))}rmSync(r,s){return this.makeCallSync(r,()=>this.baseFs.rmSync(r,s),(a,{subPath:n})=>a.rmSync(n,s))}async linkPromise(r,s){return await this.makeCallPromise(s,async()=>await this.baseFs.linkPromise(r,s),async(a,{subPath:n})=>await a.linkPromise(r,n))}linkSync(r,s){return this.makeCallSync(s,()=>this.baseFs.linkSync(r,s),(a,{subPath:n})=>a.linkSync(r,n))}async symlinkPromise(r,s,a){return await this.makeCallPromise(s,async()=>await this.baseFs.symlinkPromise(r,s,a),async(n,{subPath:c})=>await n.symlinkPromise(r,c))}symlinkSync(r,s,a){return this.makeCallSync(s,()=>this.baseFs.symlinkSync(r,s,a),(n,{subPath:c})=>n.symlinkSync(r,c))}async readFilePromise(r,s){return this.makeCallPromise(r,async()=>await this.baseFs.readFilePromise(r,s),async(a,{subPath:n})=>await a.readFilePromise(n,s))}readFileSync(r,s){return this.makeCallSync(r,()=>this.baseFs.readFileSync(r,s),(a,{subPath:n})=>a.readFileSync(n,s))}async readdirPromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.readdirPromise(r,s),async(a,{subPath:n})=>await a.readdirPromise(n,s),{requireSubpath:!1})}readdirSync(r,s){return this.makeCallSync(r,()=>this.baseFs.readdirSync(r,s),(a,{subPath:n})=>a.readdirSync(n,s),{requireSubpath:!1})}async readlinkPromise(r){return await this.makeCallPromise(r,async()=>await this.baseFs.readlinkPromise(r),async(s,{subPath:a})=>await s.readlinkPromise(a))}readlinkSync(r){return this.makeCallSync(r,()=>this.baseFs.readlinkSync(r),(s,{subPath:a})=>s.readlinkSync(a))}async truncatePromise(r,s){return await this.makeCallPromise(r,async()=>await this.baseFs.truncatePromise(r,s),async(a,{subPath:n})=>await a.truncatePromise(n,s))}truncateSync(r,s){return this.makeCallSync(r,()=>this.baseFs.truncateSync(r,s),(a,{subPath:n})=>a.truncateSync(n,s))}async ftruncatePromise(r,s){if((r&rl)!==this.magic)return this.baseFs.ftruncatePromise(r,s);let a=this.fdMap.get(r);if(typeof a>"u")throw Uo("ftruncate");let[n,c]=a;return n.ftruncatePromise(c,s)}ftruncateSync(r,s){if((r&rl)!==this.magic)return this.baseFs.ftruncateSync(r,s);let a=this.fdMap.get(r);if(typeof a>"u")throw Uo("ftruncateSync");let[n,c]=a;return n.ftruncateSync(c,s)}watch(r,s,a){return this.makeCallSync(r,()=>this.baseFs.watch(r,s,a),(n,{subPath:c})=>n.watch(c,s,a))}watchFile(r,s,a){return this.makeCallSync(r,()=>this.baseFs.watchFile(r,s,a),()=>sE(this,r,s,a))}unwatchFile(r,s){return this.makeCallSync(r,()=>this.baseFs.unwatchFile(r,s),()=>yd(this,r,s))}async makeCallPromise(r,s,a,{requireSubpath:n=!0}={}){if(typeof r!="string")return await s();let c=this.resolve(r),f=this.findMount(c);return f?n&&f.subPath==="/"?await s():await this.getMountPromise(f.archivePath,async p=>await a(p,f)):await s()}makeCallSync(r,s,a,{requireSubpath:n=!0}={}){if(typeof r!="string")return s();let c=this.resolve(r),f=this.findMount(c);return!f||n&&f.subPath==="/"?s():this.getMountSync(f.archivePath,p=>a(p,f))}findMount(r){if(this.filter&&!this.filter.test(r))return null;let s="";for(;;){let a=r.substring(s.length),n=this.getMountPoint(a,s);if(!n)return null;if(s=this.pathUtils.join(s,n),!this.isMount.has(s)){if(this.notMount.has(s))continue;try{if(this.typeCheck!==null&&(this.baseFs.statSync(s).mode&Bd.constants.S_IFMT)!==this.typeCheck){this.notMount.add(s);continue}}catch{return null}this.isMount.add(s)}return{archivePath:s,subPath:this.pathUtils.join(vt.root,r.substring(s.length))}}}limitOpenFiles(r){if(this.mountInstances===null)return;let s=Date.now(),a=s+this.maxAge,n=r===null?0:this.mountInstances.size-r;for(let[c,{childFs:f,expiresAt:p,refCount:h}]of this.mountInstances.entries())if(!(h!==0||f.hasOpenFileHandles?.())){if(s>=p){f.saveAndClose?.(),this.mountInstances.delete(c),n-=1;continue}else if(r===null||n<=0){a=p;break}f.saveAndClose?.(),this.mountInstances.delete(c),n-=1}this.limitOpenFilesTimeout===null&&(r===null&&this.mountInstances.size>0||r!==null)&&isFinite(a)&&(this.limitOpenFilesTimeout=setTimeout(()=>{this.limitOpenFilesTimeout=null,this.limitOpenFiles(null)},a-s).unref())}async getMountPromise(r,s){if(this.mountInstances){let a=this.mountInstances.get(r);if(!a){let n=await this.factoryPromise(this.baseFs,r);a=this.mountInstances.get(r),a||(a={childFs:n(),expiresAt:0,refCount:0})}this.mountInstances.delete(r),this.limitOpenFiles(this.maxOpenFiles-1),this.mountInstances.set(r,a),a.expiresAt=Date.now()+this.maxAge,a.refCount+=1;try{return await s(a.childFs)}finally{a.refCount-=1}}else{let a=(await this.factoryPromise(this.baseFs,r))();try{return await s(a)}finally{a.saveAndClose?.()}}}getMountSync(r,s){if(this.mountInstances){let a=this.mountInstances.get(r);return a||(a={childFs:this.factorySync(this.baseFs,r),expiresAt:0,refCount:0}),this.mountInstances.delete(r),this.limitOpenFiles(this.maxOpenFiles-1),this.mountInstances.set(r,a),a.expiresAt=Date.now()+this.maxAge,s(a.childFs)}else{let a=this.factorySync(this.baseFs,r);try{return s(a)}finally{a.saveAndClose?.()}}}}});var er,mx,J$=It(()=>{Cd();tl();er=()=>Object.assign(new Error("ENOSYS: unsupported filesystem access"),{code:"ENOSYS"}),mx=class t extends Ep{static{this.instance=new t}constructor(){super(K)}getExtractHint(){throw er()}getRealPath(){throw er()}resolve(){throw er()}async openPromise(){throw er()}openSync(){throw er()}async opendirPromise(){throw er()}opendirSync(){throw er()}async readPromise(){throw er()}readSync(){throw er()}async writePromise(){throw er()}writeSync(){throw er()}async closePromise(){throw er()}closeSync(){throw er()}createWriteStream(){throw er()}createReadStream(){throw er()}async realpathPromise(){throw er()}realpathSync(){throw er()}async readdirPromise(){throw er()}readdirSync(){throw er()}async existsPromise(e){throw er()}existsSync(e){throw er()}async accessPromise(){throw er()}accessSync(){throw er()}async statPromise(){throw er()}statSync(){throw er()}async fstatPromise(e){throw er()}fstatSync(e){throw er()}async lstatPromise(e){throw er()}lstatSync(e){throw er()}async fchmodPromise(){throw er()}fchmodSync(){throw er()}async chmodPromise(){throw er()}chmodSync(){throw er()}async fchownPromise(){throw er()}fchownSync(){throw er()}async chownPromise(){throw er()}chownSync(){throw er()}async mkdirPromise(){throw er()}mkdirSync(){throw er()}async rmdirPromise(){throw er()}rmdirSync(){throw er()}async rmPromise(){throw er()}rmSync(){throw er()}async linkPromise(){throw er()}linkSync(){throw er()}async symlinkPromise(){throw er()}symlinkSync(){throw er()}async renamePromise(){throw er()}renameSync(){throw er()}async copyFilePromise(){throw er()}copyFileSync(){throw er()}async appendFilePromise(){throw er()}appendFileSync(){throw er()}async writeFilePromise(){throw er()}writeFileSync(){throw er()}async unlinkPromise(){throw er()}unlinkSync(){throw er()}async utimesPromise(){throw er()}utimesSync(){throw er()}async lutimesPromise(){throw er()}lutimesSync(){throw er()}async readFilePromise(){throw er()}readFileSync(){throw er()}async readlinkPromise(){throw er()}readlinkSync(){throw er()}async truncatePromise(){throw er()}truncateSync(){throw er()}async ftruncatePromise(e,r){throw er()}ftruncateSync(e,r){throw er()}watch(){throw er()}watchFile(){throw er()}unwatchFile(){throw er()}}});var i0,z$=It(()=>{Ip();tl();i0=class extends Hs{constructor(e){super(ue),this.baseFs=e}mapFromBase(e){return ue.fromPortablePath(e)}mapToBase(e){return ue.toPortablePath(e)}}});var H7e,L_,j7e,fo,Z$=It(()=>{wd();Ip();tl();H7e=/^[0-9]+$/,L_=/^(\/(?:[^/]+\/)*?(?:\$\$virtual|__virtual__))((?:\/((?:[^/]+-)?[a-f0-9]+)(?:\/([^/]+))?)?((?:\/.*)?))$/,j7e=/^([^/]+-)?[a-f0-9]+$/,fo=class t extends Hs{static makeVirtualPath(e,r,s){if(K.basename(e)!=="__virtual__")throw new Error('Assertion failed: Virtual folders must be named "__virtual__"');if(!K.basename(r).match(j7e))throw new Error("Assertion failed: Virtual components must be ended by an hexadecimal hash");let n=K.relative(K.dirname(e),s).split("/"),c=0;for(;c{M_=et(ye("buffer")),X$=ye("url"),$$=ye("util");Ip();tl();yx=class extends Hs{constructor(e){super(ue),this.baseFs=e}mapFromBase(e){return e}mapToBase(e){if(typeof e=="string")return e;if(e instanceof URL)return(0,X$.fileURLToPath)(e);if(Buffer.isBuffer(e)){let r=e.toString();if(!q7e(e,r))throw new Error("Non-utf8 buffers are not supported at the moment. Please upvote the following issue if you encounter this error: https://github.com/yarnpkg/berry/issues/4942");return r}throw new Error(`Unsupported path type: ${(0,$$.inspect)(e)}`)}}});var see,Ho,Cp,s0,Ex,Ix,aE,Nu,Ou,tee,ree,nee,iee,H2,oee=It(()=>{see=ye("readline"),Ho=Symbol("kBaseFs"),Cp=Symbol("kFd"),s0=Symbol("kClosePromise"),Ex=Symbol("kCloseResolve"),Ix=Symbol("kCloseReject"),aE=Symbol("kRefs"),Nu=Symbol("kRef"),Ou=Symbol("kUnref"),H2=class{constructor(e,r){this[iee]=1;this[nee]=void 0;this[ree]=void 0;this[tee]=void 0;this[Ho]=r,this[Cp]=e}get fd(){return this[Cp]}async appendFile(e,r){try{this[Nu](this.appendFile);let s=(typeof r=="string"?r:r?.encoding)??void 0;return await this[Ho].appendFilePromise(this.fd,e,s?{encoding:s}:void 0)}finally{this[Ou]()}}async chown(e,r){try{return this[Nu](this.chown),await this[Ho].fchownPromise(this.fd,e,r)}finally{this[Ou]()}}async chmod(e){try{return this[Nu](this.chmod),await this[Ho].fchmodPromise(this.fd,e)}finally{this[Ou]()}}createReadStream(e){return this[Ho].createReadStream(null,{...e,fd:this.fd})}createWriteStream(e){return this[Ho].createWriteStream(null,{...e,fd:this.fd})}datasync(){throw new Error("Method not implemented.")}sync(){throw new Error("Method not implemented.")}async read(e,r,s,a){try{this[Nu](this.read);let n;return Buffer.isBuffer(e)?n=e:(e??={},n=e.buffer??Buffer.alloc(16384),r=e.offset||0,s=e.length??n.byteLength,a=e.position??null),r??=0,s??=0,s===0?{bytesRead:s,buffer:n}:{bytesRead:await this[Ho].readPromise(this.fd,n,r,s,a),buffer:n}}finally{this[Ou]()}}async readFile(e){try{this[Nu](this.readFile);let r=(typeof e=="string"?e:e?.encoding)??void 0;return await this[Ho].readFilePromise(this.fd,r)}finally{this[Ou]()}}readLines(e){return(0,see.createInterface)({input:this.createReadStream(e),crlfDelay:1/0})}async stat(e){try{return this[Nu](this.stat),await this[Ho].fstatPromise(this.fd,e)}finally{this[Ou]()}}async truncate(e){try{return this[Nu](this.truncate),await this[Ho].ftruncatePromise(this.fd,e)}finally{this[Ou]()}}utimes(e,r){throw new Error("Method not implemented.")}async writeFile(e,r){try{this[Nu](this.writeFile);let s=(typeof r=="string"?r:r?.encoding)??void 0;await this[Ho].writeFilePromise(this.fd,e,s)}finally{this[Ou]()}}async write(...e){try{if(this[Nu](this.write),ArrayBuffer.isView(e[0])){let[r,s,a,n]=e;return{bytesWritten:await this[Ho].writePromise(this.fd,r,s??void 0,a??void 0,n??void 0),buffer:r}}else{let[r,s,a]=e;return{bytesWritten:await this[Ho].writePromise(this.fd,r,s,a),buffer:r}}}finally{this[Ou]()}}async writev(e,r){try{this[Nu](this.writev);let s=0;if(typeof r<"u")for(let a of e){let n=await this.write(a,void 0,void 0,r);s+=n.bytesWritten,r+=n.bytesWritten}else for(let a of e){let n=await this.write(a);s+=n.bytesWritten}return{buffers:e,bytesWritten:s}}finally{this[Ou]()}}readv(e,r){throw new Error("Method not implemented.")}close(){if(this[Cp]===-1)return Promise.resolve();if(this[s0])return this[s0];if(this[aE]--,this[aE]===0){let e=this[Cp];this[Cp]=-1,this[s0]=this[Ho].closePromise(e).finally(()=>{this[s0]=void 0})}else this[s0]=new Promise((e,r)=>{this[Ex]=e,this[Ix]=r}).finally(()=>{this[s0]=void 0,this[Ix]=void 0,this[Ex]=void 0});return this[s0]}[(Ho,Cp,iee=aE,nee=s0,ree=Ex,tee=Ix,Nu)](e){if(this[Cp]===-1){let r=new Error("file closed");throw r.code="EBADF",r.syscall=e.name,r}this[aE]++}[Ou](){if(this[aE]--,this[aE]===0){let e=this[Cp];this[Cp]=-1,this[Ho].closePromise(e).then(this[Ex],this[Ix])}}}});function j2(t,e){e=new yx(e);let r=(s,a,n)=>{let c=s[a];s[a]=n,typeof c?.[lE.promisify.custom]<"u"&&(n[lE.promisify.custom]=c[lE.promisify.custom])};{r(t,"exists",(s,...a)=>{let c=typeof a[a.length-1]=="function"?a.pop():()=>{};process.nextTick(()=>{e.existsPromise(s).then(f=>{c(f)},()=>{c(!1)})})}),r(t,"read",(...s)=>{let[a,n,c,f,p,h]=s;if(s.length<=3){let E={};s.length<3?h=s[1]:(E=s[1],h=s[2]),{buffer:n=Buffer.alloc(16384),offset:c=0,length:f=n.byteLength,position:p}=E}if(c==null&&(c=0),f|=0,f===0){process.nextTick(()=>{h(null,0,n)});return}p==null&&(p=-1),process.nextTick(()=>{e.readPromise(a,n,c,f,p).then(E=>{h(null,E,n)},E=>{h(E,0,n)})})});for(let s of aee){let a=s.replace(/Promise$/,"");if(typeof t[a]>"u")continue;let n=e[s];if(typeof n>"u")continue;r(t,a,(...f)=>{let h=typeof f[f.length-1]=="function"?f.pop():()=>{};process.nextTick(()=>{n.apply(e,f).then(E=>{h(null,E)},E=>{h(E)})})})}t.realpath.native=t.realpath}{r(t,"existsSync",s=>{try{return e.existsSync(s)}catch{return!1}}),r(t,"readSync",(...s)=>{let[a,n,c,f,p]=s;return s.length<=3&&({offset:c=0,length:f=n.byteLength,position:p}=s[2]||{}),c==null&&(c=0),f|=0,f===0?0:(p==null&&(p=-1),e.readSync(a,n,c,f,p))});for(let s of G7e){let a=s;if(typeof t[a]>"u")continue;let n=e[s];typeof n>"u"||r(t,a,n.bind(e))}t.realpathSync.native=t.realpathSync}{let s=t.promises;for(let a of aee){let n=a.replace(/Promise$/,"");if(typeof s[n]>"u")continue;let c=e[a];typeof c>"u"||a!=="open"&&r(s,n,(f,...p)=>f instanceof H2?f[n].apply(f,p):c.call(e,f,...p))}r(s,"open",async(...a)=>{let n=await e.openPromise(...a);return new H2(n,e)})}t.read[lE.promisify.custom]=async(s,a,...n)=>({bytesRead:await e.readPromise(s,a,...n),buffer:a}),t.write[lE.promisify.custom]=async(s,a,...n)=>({bytesWritten:await e.writePromise(s,a,...n),buffer:a})}function Cx(t,e){let r=Object.create(t);return j2(r,e),r}var lE,G7e,aee,lee=It(()=>{lE=ye("util");eee();oee();G7e=new Set(["accessSync","appendFileSync","createReadStream","createWriteStream","chmodSync","fchmodSync","chownSync","fchownSync","closeSync","copyFileSync","linkSync","lstatSync","fstatSync","lutimesSync","mkdirSync","openSync","opendirSync","readlinkSync","readFileSync","readdirSync","readlinkSync","realpathSync","renameSync","rmdirSync","rmSync","statSync","symlinkSync","truncateSync","ftruncateSync","unlinkSync","unwatchFile","utimesSync","watch","watchFile","writeFileSync","writeSync"]),aee=new Set(["accessPromise","appendFilePromise","fchmodPromise","chmodPromise","fchownPromise","chownPromise","closePromise","copyFilePromise","linkPromise","fstatPromise","lstatPromise","lutimesPromise","mkdirPromise","openPromise","opendirPromise","readdirPromise","realpathPromise","readFilePromise","readdirPromise","readlinkPromise","renamePromise","rmdirPromise","rmPromise","statPromise","symlinkPromise","truncatePromise","ftruncatePromise","unlinkPromise","utimesPromise","writeFilePromise","writeSync"])});function cee(t){let e=Math.ceil(Math.random()*4294967296).toString(16).padStart(8,"0");return`${t}${e}`}function uee(){if(__)return __;let t=ue.toPortablePath(fee.default.tmpdir()),e=le.realpathSync(t);return process.once("exit",()=>{le.rmtempSync()}),__={tmpdir:t,realTmpdir:e}}var fee,Lu,__,le,Aee=It(()=>{fee=et(ye("os"));wd();tl();Lu=new Set,__=null;le=Object.assign(new Yn,{detachTemp(t){Lu.delete(t)},mktempSync(t){let{tmpdir:e,realTmpdir:r}=uee();for(;;){let s=cee("xfs-");try{this.mkdirSync(K.join(e,s))}catch(n){if(n.code==="EEXIST")continue;throw n}let a=K.join(r,s);if(Lu.add(a),typeof t>"u")return a;try{return t(a)}finally{if(Lu.has(a)){Lu.delete(a);try{this.removeSync(a)}catch{}}}}},async mktempPromise(t){let{tmpdir:e,realTmpdir:r}=uee();for(;;){let s=cee("xfs-");try{await this.mkdirPromise(K.join(e,s))}catch(n){if(n.code==="EEXIST")continue;throw n}let a=K.join(r,s);if(Lu.add(a),typeof t>"u")return a;try{return await t(a)}finally{if(Lu.has(a)){Lu.delete(a);try{await this.removePromise(a)}catch{}}}}},async rmtempPromise(){await Promise.all(Array.from(Lu.values()).map(async t=>{try{await le.removePromise(t,{maxRetries:0}),Lu.delete(t)}catch{}}))},rmtempSync(){for(let t of Lu)try{le.removeSync(t),Lu.delete(t)}catch{}}})});var q2={};Vt(q2,{AliasFS:()=>Hf,BasePortableFakeFS:()=>Uf,CustomDir:()=>U2,CwdFS:()=>Sn,FakeFS:()=>Ep,Filename:()=>Er,JailFS:()=>jf,LazyFS:()=>oE,MountFS:()=>n0,NoFS:()=>mx,NodeFS:()=>Yn,PortablePath:()=>vt,PosixFS:()=>i0,ProxiedFS:()=>Hs,VirtualFS:()=>fo,constants:()=>ui,errors:()=>or,extendFs:()=>Cx,normalizeLineEndings:()=>Id,npath:()=>ue,opendir:()=>hx,patchFs:()=>j2,ppath:()=>K,setupCopyIndex:()=>px,statUtils:()=>el,unwatchAllFiles:()=>Ed,unwatchFile:()=>yd,watchFile:()=>sE,xfs:()=>le});var bt=It(()=>{x$();ux();T_();N_();N$();O_();Cd();tl();tl();H$();Cd();G$();Y$();V$();K$();J$();wd();z$();Ip();Z$();lee();Aee()});var mee=L((A5t,dee)=>{dee.exports=gee;gee.sync=Y7e;var pee=ye("fs");function W7e(t,e){var r=e.pathExt!==void 0?e.pathExt:process.env.PATHEXT;if(!r||(r=r.split(";"),r.indexOf("")!==-1))return!0;for(var s=0;s{Cee.exports=Eee;Eee.sync=V7e;var yee=ye("fs");function Eee(t,e,r){yee.stat(t,function(s,a){r(s,s?!1:Iee(a,e))})}function V7e(t,e){return Iee(yee.statSync(t),e)}function Iee(t,e){return t.isFile()&&K7e(t,e)}function K7e(t,e){var r=t.mode,s=t.uid,a=t.gid,n=e.uid!==void 0?e.uid:process.getuid&&process.getuid(),c=e.gid!==void 0?e.gid:process.getgid&&process.getgid(),f=parseInt("100",8),p=parseInt("010",8),h=parseInt("001",8),E=f|p,C=r&h||r&p&&a===c||r&f&&s===n||r&E&&n===0;return C}});var vee=L((g5t,Bee)=>{var h5t=ye("fs"),wx;process.platform==="win32"||global.TESTING_WINDOWS?wx=mee():wx=wee();Bee.exports=U_;U_.sync=J7e;function U_(t,e,r){if(typeof e=="function"&&(r=e,e={}),!r){if(typeof Promise!="function")throw new TypeError("callback not provided");return new Promise(function(s,a){U_(t,e||{},function(n,c){n?a(n):s(c)})})}wx(t,e||{},function(s,a){s&&(s.code==="EACCES"||e&&e.ignoreErrors)&&(s=null,a=!1),r(s,a)})}function J7e(t,e){try{return wx.sync(t,e||{})}catch(r){if(e&&e.ignoreErrors||r.code==="EACCES")return!1;throw r}}});var Qee=L((d5t,kee)=>{var cE=process.platform==="win32"||process.env.OSTYPE==="cygwin"||process.env.OSTYPE==="msys",See=ye("path"),z7e=cE?";":":",Dee=vee(),bee=t=>Object.assign(new Error(`not found: ${t}`),{code:"ENOENT"}),Pee=(t,e)=>{let r=e.colon||z7e,s=t.match(/\//)||cE&&t.match(/\\/)?[""]:[...cE?[process.cwd()]:[],...(e.path||process.env.PATH||"").split(r)],a=cE?e.pathExt||process.env.PATHEXT||".EXE;.CMD;.BAT;.COM":"",n=cE?a.split(r):[""];return cE&&t.indexOf(".")!==-1&&n[0]!==""&&n.unshift(""),{pathEnv:s,pathExt:n,pathExtExe:a}},xee=(t,e,r)=>{typeof e=="function"&&(r=e,e={}),e||(e={});let{pathEnv:s,pathExt:a,pathExtExe:n}=Pee(t,e),c=[],f=h=>new Promise((E,C)=>{if(h===s.length)return e.all&&c.length?E(c):C(bee(t));let S=s[h],P=/^".*"$/.test(S)?S.slice(1,-1):S,I=See.join(P,t),R=!P&&/^\.[\\\/]/.test(t)?t.slice(0,2)+I:I;E(p(R,h,0))}),p=(h,E,C)=>new Promise((S,P)=>{if(C===a.length)return S(f(E+1));let I=a[C];Dee(h+I,{pathExt:n},(R,N)=>{if(!R&&N)if(e.all)c.push(h+I);else return S(h+I);return S(p(h,E,C+1))})});return r?f(0).then(h=>r(null,h),r):f(0)},Z7e=(t,e)=>{e=e||{};let{pathEnv:r,pathExt:s,pathExtExe:a}=Pee(t,e),n=[];for(let c=0;c{"use strict";var Tee=(t={})=>{let e=t.env||process.env;return(t.platform||process.platform)!=="win32"?"PATH":Object.keys(e).reverse().find(s=>s.toUpperCase()==="PATH")||"Path"};H_.exports=Tee;H_.exports.default=Tee});var Lee=L((y5t,Oee)=>{"use strict";var Fee=ye("path"),X7e=Qee(),$7e=Ree();function Nee(t,e){let r=t.options.env||process.env,s=process.cwd(),a=t.options.cwd!=null,n=a&&process.chdir!==void 0&&!process.chdir.disabled;if(n)try{process.chdir(t.options.cwd)}catch{}let c;try{c=X7e.sync(t.command,{path:r[$7e({env:r})],pathExt:e?Fee.delimiter:void 0})}catch{}finally{n&&process.chdir(s)}return c&&(c=Fee.resolve(a?t.options.cwd:"",c)),c}function eKe(t){return Nee(t)||Nee(t,!0)}Oee.exports=eKe});var Mee=L((E5t,q_)=>{"use strict";var j_=/([()\][%!^"`<>&|;, *?])/g;function tKe(t){return t=t.replace(j_,"^$1"),t}function rKe(t,e){return t=`${t}`,t=t.replace(/(?=(\\+?)?)\1"/g,'$1$1\\"'),t=t.replace(/(?=(\\+?)?)\1$/,"$1$1"),t=`"${t}"`,t=t.replace(j_,"^$1"),e&&(t=t.replace(j_,"^$1")),t}q_.exports.command=tKe;q_.exports.argument=rKe});var Uee=L((I5t,_ee)=>{"use strict";_ee.exports=/^#!(.*)/});var jee=L((C5t,Hee)=>{"use strict";var nKe=Uee();Hee.exports=(t="")=>{let e=t.match(nKe);if(!e)return null;let[r,s]=e[0].replace(/#! ?/,"").split(" "),a=r.split("/").pop();return a==="env"?s:s?`${a} ${s}`:a}});var Gee=L((w5t,qee)=>{"use strict";var G_=ye("fs"),iKe=jee();function sKe(t){let r=Buffer.alloc(150),s;try{s=G_.openSync(t,"r"),G_.readSync(s,r,0,150,0),G_.closeSync(s)}catch{}return iKe(r.toString())}qee.exports=sKe});var Kee=L((B5t,Vee)=>{"use strict";var oKe=ye("path"),Wee=Lee(),Yee=Mee(),aKe=Gee(),lKe=process.platform==="win32",cKe=/\.(?:com|exe)$/i,uKe=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function fKe(t){t.file=Wee(t);let e=t.file&&aKe(t.file);return e?(t.args.unshift(t.file),t.command=e,Wee(t)):t.file}function AKe(t){if(!lKe)return t;let e=fKe(t),r=!cKe.test(e);if(t.options.forceShell||r){let s=uKe.test(e);t.command=oKe.normalize(t.command),t.command=Yee.command(t.command),t.args=t.args.map(n=>Yee.argument(n,s));let a=[t.command].concat(t.args).join(" ");t.args=["/d","/s","/c",`"${a}"`],t.command=process.env.comspec||"cmd.exe",t.options.windowsVerbatimArguments=!0}return t}function pKe(t,e,r){e&&!Array.isArray(e)&&(r=e,e=null),e=e?e.slice(0):[],r=Object.assign({},r);let s={command:t,args:e,options:r,file:void 0,original:{command:t,args:e}};return r.shell?s:AKe(s)}Vee.exports=pKe});var Zee=L((v5t,zee)=>{"use strict";var W_=process.platform==="win32";function Y_(t,e){return Object.assign(new Error(`${e} ${t.command} ENOENT`),{code:"ENOENT",errno:"ENOENT",syscall:`${e} ${t.command}`,path:t.command,spawnargs:t.args})}function hKe(t,e){if(!W_)return;let r=t.emit;t.emit=function(s,a){if(s==="exit"){let n=Jee(a,e);if(n)return r.call(t,"error",n)}return r.apply(t,arguments)}}function Jee(t,e){return W_&&t===1&&!e.file?Y_(e.original,"spawn"):null}function gKe(t,e){return W_&&t===1&&!e.file?Y_(e.original,"spawnSync"):null}zee.exports={hookChildProcess:hKe,verifyENOENT:Jee,verifyENOENTSync:gKe,notFoundError:Y_}});var J_=L((S5t,uE)=>{"use strict";var Xee=ye("child_process"),V_=Kee(),K_=Zee();function $ee(t,e,r){let s=V_(t,e,r),a=Xee.spawn(s.command,s.args,s.options);return K_.hookChildProcess(a,s),a}function dKe(t,e,r){let s=V_(t,e,r),a=Xee.spawnSync(s.command,s.args,s.options);return a.error=a.error||K_.verifyENOENTSync(a.status,s),a}uE.exports=$ee;uE.exports.spawn=$ee;uE.exports.sync=dKe;uE.exports._parse=V_;uE.exports._enoent=K_});var tte=L((D5t,ete)=>{"use strict";function mKe(t,e){function r(){this.constructor=t}r.prototype=e.prototype,t.prototype=new r}function vd(t,e,r,s){this.message=t,this.expected=e,this.found=r,this.location=s,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,vd)}mKe(vd,Error);vd.buildMessage=function(t,e){var r={literal:function(h){return'"'+a(h.text)+'"'},class:function(h){var E="",C;for(C=0;C0){for(C=1,S=1;C>",b=ur(">>",!1),y=">&",F=ur(">&",!1),z=">",Z=ur(">",!1),$="<<<",oe=ur("<<<",!1),xe="<&",Te=ur("<&",!1),lt="<",Et=ur("<",!1),qt=function(O){return{type:"argument",segments:[].concat(...O)}},ir=function(O){return O},Pt="$'",gn=ur("$'",!1),Pr="'",Ir=ur("'",!1),Nr=function(O){return[{type:"text",text:O}]},nn='""',oi=ur('""',!1),wo=function(){return{type:"text",text:""}},rs='"',eo=ur('"',!1),Bo=function(O){return O},Hi=function(O){return{type:"arithmetic",arithmetic:O,quoted:!0}},to=function(O){return{type:"shell",shell:O,quoted:!0}},vo=function(O){return{type:"variable",...O,quoted:!0}},RA=function(O){return{type:"text",text:O}},pf=function(O){return{type:"arithmetic",arithmetic:O,quoted:!1}},Eh=function(O){return{type:"shell",shell:O,quoted:!1}},Ih=function(O){return{type:"variable",...O,quoted:!1}},ro=function(O){return{type:"glob",pattern:O}},jn=/^[^']/,Rs=zi(["'"],!0,!1),no=function(O){return O.join("")},lu=/^[^$"]/,cu=zi(["$",'"'],!0,!1),uu=`\\ +`,FA=ur(`\\ +`,!1),NA=function(){return""},aa="\\",la=ur("\\",!1),OA=/^[\\$"`]/,gr=zi(["\\","$",'"',"`"],!1,!1),So=function(O){return O},Me="\\a",fu=ur("\\a",!1),Cr=function(){return"a"},hf="\\b",LA=ur("\\b",!1),MA=function(){return"\b"},Au=/^[Ee]/,pu=zi(["E","e"],!1,!1),ac=function(){return"\x1B"},ve="\\f",Nt=ur("\\f",!1),lc=function(){return"\f"},Ni="\\n",io=ur("\\n",!1),Rt=function(){return` +`},xn="\\r",ca=ur("\\r",!1),ji=function(){return"\r"},Oi="\\t",Oa=ur("\\t",!1),dn=function(){return" "},Jn="\\v",hu=ur("\\v",!1),Ch=function(){return"\v"},La=/^[\\'"?]/,Ma=zi(["\\","'",'"',"?"],!1,!1),Ua=function(O){return String.fromCharCode(parseInt(O,16))},Xe="\\x",Ha=ur("\\x",!1),gf="\\u",cc=ur("\\u",!1),wn="\\U",ua=ur("\\U",!1),_A=function(O){return String.fromCodePoint(parseInt(O,16))},UA=/^[0-7]/,fa=zi([["0","7"]],!1,!1),vl=/^[0-9a-fA-f]/,Mt=zi([["0","9"],["a","f"],["A","f"]],!1,!1),kn=Ef(),Aa="{}",ja=ur("{}",!1),ns=function(){return"{}"},uc="-",gu=ur("-",!1),fc="+",qa=ur("+",!1),Li=".",Cs=ur(".",!1),Sl=function(O,J,re){return{type:"number",value:(O==="-"?-1:1)*parseFloat(J.join("")+"."+re.join(""))}},df=function(O,J){return{type:"number",value:(O==="-"?-1:1)*parseInt(J.join(""))}},Ac=function(O){return{type:"variable",...O}},wi=function(O){return{type:"variable",name:O}},Qn=function(O){return O},pc="*",Je=ur("*",!1),st="/",St=ur("/",!1),lr=function(O,J,re){return{type:J==="*"?"multiplication":"division",right:re}},ee=function(O,J){return J.reduce((re,de)=>({left:re,...de}),O)},Ie=function(O,J,re){return{type:J==="+"?"addition":"subtraction",right:re}},Oe="$((",ht=ur("$((",!1),mt="))",Dt=ur("))",!1),tr=function(O){return O},fn="$(",ai=ur("$(",!1),qi=function(O){return O},Tn="${",Ga=ur("${",!1),my=":-",t2=ur(":-",!1),Do=function(O,J){return{name:O,defaultValue:J}},yy=":-}",wh=ur(":-}",!1),r2=function(O){return{name:O,defaultValue:[]}},bo=":+",Bh=ur(":+",!1),vh=function(O,J){return{name:O,alternativeValue:J}},du=":+}",Sh=ur(":+}",!1),Ng=function(O){return{name:O,alternativeValue:[]}},Og=function(O){return{name:O}},Lg="$",Ey=ur("$",!1),mf=function(O){return e.isGlobPattern(O)},Po=function(O){return O},Dl=/^[a-zA-Z0-9_]/,Dh=zi([["a","z"],["A","Z"],["0","9"],"_"],!1,!1),Mg=function(){return By()},bl=/^[$@*?#a-zA-Z0-9_\-]/,Pl=zi(["$","@","*","?","#",["a","z"],["A","Z"],["0","9"],"_","-"],!1,!1),Iy=/^[()}<>$|&; \t"']/,HA=zi(["(",")","}","<",">","$","|","&",";"," "," ",'"',"'"],!1,!1),Cy=/^[<>&; \t"']/,wy=zi(["<",">","&",";"," "," ",'"',"'"],!1,!1),jA=/^[ \t]/,qA=zi([" "," "],!1,!1),Y=0,xt=0,GA=[{line:1,column:1}],xo=0,yf=[],dt=0,mu;if("startRule"in e){if(!(e.startRule in s))throw new Error(`Can't start parsing from rule "`+e.startRule+'".');a=s[e.startRule]}function By(){return t.substring(xt,Y)}function _g(){return If(xt,Y)}function n2(O,J){throw J=J!==void 0?J:If(xt,Y),WA([Ug(O)],t.substring(xt,Y),J)}function bh(O,J){throw J=J!==void 0?J:If(xt,Y),gi(O,J)}function ur(O,J){return{type:"literal",text:O,ignoreCase:J}}function zi(O,J,re){return{type:"class",parts:O,inverted:J,ignoreCase:re}}function Ef(){return{type:"any"}}function Wa(){return{type:"end"}}function Ug(O){return{type:"other",description:O}}function yu(O){var J=GA[O],re;if(J)return J;for(re=O-1;!GA[re];)re--;for(J=GA[re],J={line:J.line,column:J.column};rexo&&(xo=Y,yf=[]),yf.push(O))}function gi(O,J){return new vd(O,null,null,J)}function WA(O,J,re){return new vd(vd.buildMessage(O,J),O,J,re)}function Ya(){var O,J,re;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();return J!==r?(re=pa(),re===r&&(re=null),re!==r?(xt=O,J=n(re),O=J):(Y=O,O=r)):(Y=O,O=r),O}function pa(){var O,J,re,de,Ke;if(O=Y,J=Ph(),J!==r){for(re=[],de=kt();de!==r;)re.push(de),de=kt();re!==r?(de=Hg(),de!==r?(Ke=Va(),Ke===r&&(Ke=null),Ke!==r?(xt=O,J=c(J,de,Ke),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r)}else Y=O,O=r;if(O===r)if(O=Y,J=Ph(),J!==r){for(re=[],de=kt();de!==r;)re.push(de),de=kt();re!==r?(de=Hg(),de===r&&(de=null),de!==r?(xt=O,J=f(J,de),O=J):(Y=O,O=r)):(Y=O,O=r)}else Y=O,O=r;return O}function Va(){var O,J,re,de,Ke;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r)if(re=pa(),re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();de!==r?(xt=O,J=p(re),O=J):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r;return O}function Hg(){var O;return t.charCodeAt(Y)===59?(O=h,Y++):(O=r,dt===0&&wt(E)),O===r&&(t.charCodeAt(Y)===38?(O=C,Y++):(O=r,dt===0&&wt(S))),O}function Ph(){var O,J,re;return O=Y,J=YA(),J!==r?(re=jg(),re===r&&(re=null),re!==r?(xt=O,J=P(J,re),O=J):(Y=O,O=r)):(Y=O,O=r),O}function jg(){var O,J,re,de,Ke,ft,dr;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r)if(re=vy(),re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();if(de!==r)if(Ke=Ph(),Ke!==r){for(ft=[],dr=kt();dr!==r;)ft.push(dr),dr=kt();ft!==r?(xt=O,J=I(re,Ke),O=J):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;else Y=O,O=r;return O}function vy(){var O;return t.substr(Y,2)===R?(O=R,Y+=2):(O=r,dt===0&&wt(N)),O===r&&(t.substr(Y,2)===U?(O=U,Y+=2):(O=r,dt===0&&wt(W))),O}function YA(){var O,J,re;return O=Y,J=Cf(),J!==r?(re=qg(),re===r&&(re=null),re!==r?(xt=O,J=te(J,re),O=J):(Y=O,O=r)):(Y=O,O=r),O}function qg(){var O,J,re,de,Ke,ft,dr;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r)if(re=Eu(),re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();if(de!==r)if(Ke=YA(),Ke!==r){for(ft=[],dr=kt();dr!==r;)ft.push(dr),dr=kt();ft!==r?(xt=O,J=ie(re,Ke),O=J):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;else Y=O,O=r;return O}function Eu(){var O;return t.substr(Y,2)===Ae?(O=Ae,Y+=2):(O=r,dt===0&&wt(ce)),O===r&&(t.charCodeAt(Y)===124?(O=me,Y++):(O=r,dt===0&&wt(pe))),O}function Iu(){var O,J,re,de,Ke,ft;if(O=Y,J=Qh(),J!==r)if(t.charCodeAt(Y)===61?(re=Be,Y++):(re=r,dt===0&&wt(Ce)),re!==r)if(de=VA(),de!==r){for(Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();Ke!==r?(xt=O,J=g(J,de),O=J):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r;else Y=O,O=r;if(O===r)if(O=Y,J=Qh(),J!==r)if(t.charCodeAt(Y)===61?(re=Be,Y++):(re=r,dt===0&&wt(Ce)),re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();de!==r?(xt=O,J=we(J),O=J):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r;return O}function Cf(){var O,J,re,de,Ke,ft,dr,Br,_n,di,ws;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r)if(t.charCodeAt(Y)===40?(re=Ee,Y++):(re=r,dt===0&&wt(fe)),re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();if(de!==r)if(Ke=pa(),Ke!==r){for(ft=[],dr=kt();dr!==r;)ft.push(dr),dr=kt();if(ft!==r)if(t.charCodeAt(Y)===41?(dr=se,Y++):(dr=r,dt===0&&wt(X)),dr!==r){for(Br=[],_n=kt();_n!==r;)Br.push(_n),_n=kt();if(Br!==r){for(_n=[],di=qn();di!==r;)_n.push(di),di=qn();if(_n!==r){for(di=[],ws=kt();ws!==r;)di.push(ws),ws=kt();di!==r?(xt=O,J=De(Ke,_n),O=J):(Y=O,O=r)}else Y=O,O=r}else Y=O,O=r}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;else Y=O,O=r;if(O===r){for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r)if(t.charCodeAt(Y)===123?(re=Re,Y++):(re=r,dt===0&&wt(gt)),re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();if(de!==r)if(Ke=pa(),Ke!==r){for(ft=[],dr=kt();dr!==r;)ft.push(dr),dr=kt();if(ft!==r)if(t.charCodeAt(Y)===125?(dr=j,Y++):(dr=r,dt===0&&wt(rt)),dr!==r){for(Br=[],_n=kt();_n!==r;)Br.push(_n),_n=kt();if(Br!==r){for(_n=[],di=qn();di!==r;)_n.push(di),di=qn();if(_n!==r){for(di=[],ws=kt();ws!==r;)di.push(ws),ws=kt();di!==r?(xt=O,J=Fe(Ke,_n),O=J):(Y=O,O=r)}else Y=O,O=r}else Y=O,O=r}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;else Y=O,O=r;if(O===r){for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r){for(re=[],de=Iu();de!==r;)re.push(de),de=Iu();if(re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();if(de!==r){if(Ke=[],ft=Cu(),ft!==r)for(;ft!==r;)Ke.push(ft),ft=Cu();else Ke=r;if(Ke!==r){for(ft=[],dr=kt();dr!==r;)ft.push(dr),dr=kt();ft!==r?(xt=O,J=Ne(re,Ke),O=J):(Y=O,O=r)}else Y=O,O=r}else Y=O,O=r}else Y=O,O=r}else Y=O,O=r;if(O===r){for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r){if(re=[],de=Iu(),de!==r)for(;de!==r;)re.push(de),de=Iu();else re=r;if(re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();de!==r?(xt=O,J=Pe(re),O=J):(Y=O,O=r)}else Y=O,O=r}else Y=O,O=r}}}return O}function Fs(){var O,J,re,de,Ke;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r){if(re=[],de=xi(),de!==r)for(;de!==r;)re.push(de),de=xi();else re=r;if(re!==r){for(de=[],Ke=kt();Ke!==r;)de.push(Ke),Ke=kt();de!==r?(xt=O,J=Ye(re),O=J):(Y=O,O=r)}else Y=O,O=r}else Y=O,O=r;return O}function Cu(){var O,J,re;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();if(J!==r?(re=qn(),re!==r?(xt=O,J=ke(re),O=J):(Y=O,O=r)):(Y=O,O=r),O===r){for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();J!==r?(re=xi(),re!==r?(xt=O,J=ke(re),O=J):(Y=O,O=r)):(Y=O,O=r)}return O}function qn(){var O,J,re,de,Ke;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();return J!==r?(it.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(_e)),re===r&&(re=null),re!==r?(de=is(),de!==r?(Ke=xi(),Ke!==r?(xt=O,J=x(re,de,Ke),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O}function is(){var O;return t.substr(Y,2)===w?(O=w,Y+=2):(O=r,dt===0&&wt(b)),O===r&&(t.substr(Y,2)===y?(O=y,Y+=2):(O=r,dt===0&&wt(F)),O===r&&(t.charCodeAt(Y)===62?(O=z,Y++):(O=r,dt===0&&wt(Z)),O===r&&(t.substr(Y,3)===$?(O=$,Y+=3):(O=r,dt===0&&wt(oe)),O===r&&(t.substr(Y,2)===xe?(O=xe,Y+=2):(O=r,dt===0&&wt(Te)),O===r&&(t.charCodeAt(Y)===60?(O=lt,Y++):(O=r,dt===0&&wt(Et))))))),O}function xi(){var O,J,re;for(O=Y,J=[],re=kt();re!==r;)J.push(re),re=kt();return J!==r?(re=VA(),re!==r?(xt=O,J=ke(re),O=J):(Y=O,O=r)):(Y=O,O=r),O}function VA(){var O,J,re;if(O=Y,J=[],re=wf(),re!==r)for(;re!==r;)J.push(re),re=wf();else J=r;return J!==r&&(xt=O,J=qt(J)),O=J,O}function wf(){var O,J;return O=Y,J=mn(),J!==r&&(xt=O,J=ir(J)),O=J,O===r&&(O=Y,J=Gg(),J!==r&&(xt=O,J=ir(J)),O=J,O===r&&(O=Y,J=Wg(),J!==r&&(xt=O,J=ir(J)),O=J,O===r&&(O=Y,J=ss(),J!==r&&(xt=O,J=ir(J)),O=J))),O}function mn(){var O,J,re,de;return O=Y,t.substr(Y,2)===Pt?(J=Pt,Y+=2):(J=r,dt===0&&wt(gn)),J!==r?(re=yn(),re!==r?(t.charCodeAt(Y)===39?(de=Pr,Y++):(de=r,dt===0&&wt(Ir)),de!==r?(xt=O,J=Nr(re),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O}function Gg(){var O,J,re,de;return O=Y,t.charCodeAt(Y)===39?(J=Pr,Y++):(J=r,dt===0&&wt(Ir)),J!==r?(re=Bf(),re!==r?(t.charCodeAt(Y)===39?(de=Pr,Y++):(de=r,dt===0&&wt(Ir)),de!==r?(xt=O,J=Nr(re),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O}function Wg(){var O,J,re,de;if(O=Y,t.substr(Y,2)===nn?(J=nn,Y+=2):(J=r,dt===0&&wt(oi)),J!==r&&(xt=O,J=wo()),O=J,O===r)if(O=Y,t.charCodeAt(Y)===34?(J=rs,Y++):(J=r,dt===0&&wt(eo)),J!==r){for(re=[],de=xl();de!==r;)re.push(de),de=xl();re!==r?(t.charCodeAt(Y)===34?(de=rs,Y++):(de=r,dt===0&&wt(eo)),de!==r?(xt=O,J=Bo(re),O=J):(Y=O,O=r)):(Y=O,O=r)}else Y=O,O=r;return O}function ss(){var O,J,re;if(O=Y,J=[],re=ko(),re!==r)for(;re!==r;)J.push(re),re=ko();else J=r;return J!==r&&(xt=O,J=Bo(J)),O=J,O}function xl(){var O,J;return O=Y,J=Xr(),J!==r&&(xt=O,J=Hi(J)),O=J,O===r&&(O=Y,J=kh(),J!==r&&(xt=O,J=to(J)),O=J,O===r&&(O=Y,J=JA(),J!==r&&(xt=O,J=vo(J)),O=J,O===r&&(O=Y,J=vf(),J!==r&&(xt=O,J=RA(J)),O=J))),O}function ko(){var O,J;return O=Y,J=Xr(),J!==r&&(xt=O,J=pf(J)),O=J,O===r&&(O=Y,J=kh(),J!==r&&(xt=O,J=Eh(J)),O=J,O===r&&(O=Y,J=JA(),J!==r&&(xt=O,J=Ih(J)),O=J,O===r&&(O=Y,J=Sy(),J!==r&&(xt=O,J=ro(J)),O=J,O===r&&(O=Y,J=xh(),J!==r&&(xt=O,J=RA(J)),O=J)))),O}function Bf(){var O,J,re;for(O=Y,J=[],jn.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Rs));re!==r;)J.push(re),jn.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Rs));return J!==r&&(xt=O,J=no(J)),O=J,O}function vf(){var O,J,re;if(O=Y,J=[],re=kl(),re===r&&(lu.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(cu))),re!==r)for(;re!==r;)J.push(re),re=kl(),re===r&&(lu.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(cu)));else J=r;return J!==r&&(xt=O,J=no(J)),O=J,O}function kl(){var O,J,re;return O=Y,t.substr(Y,2)===uu?(J=uu,Y+=2):(J=r,dt===0&&wt(FA)),J!==r&&(xt=O,J=NA()),O=J,O===r&&(O=Y,t.charCodeAt(Y)===92?(J=aa,Y++):(J=r,dt===0&&wt(la)),J!==r?(OA.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(gr)),re!==r?(xt=O,J=So(re),O=J):(Y=O,O=r)):(Y=O,O=r)),O}function yn(){var O,J,re;for(O=Y,J=[],re=Qo(),re===r&&(jn.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Rs)));re!==r;)J.push(re),re=Qo(),re===r&&(jn.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Rs)));return J!==r&&(xt=O,J=no(J)),O=J,O}function Qo(){var O,J,re;return O=Y,t.substr(Y,2)===Me?(J=Me,Y+=2):(J=r,dt===0&&wt(fu)),J!==r&&(xt=O,J=Cr()),O=J,O===r&&(O=Y,t.substr(Y,2)===hf?(J=hf,Y+=2):(J=r,dt===0&&wt(LA)),J!==r&&(xt=O,J=MA()),O=J,O===r&&(O=Y,t.charCodeAt(Y)===92?(J=aa,Y++):(J=r,dt===0&&wt(la)),J!==r?(Au.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(pu)),re!==r?(xt=O,J=ac(),O=J):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===ve?(J=ve,Y+=2):(J=r,dt===0&&wt(Nt)),J!==r&&(xt=O,J=lc()),O=J,O===r&&(O=Y,t.substr(Y,2)===Ni?(J=Ni,Y+=2):(J=r,dt===0&&wt(io)),J!==r&&(xt=O,J=Rt()),O=J,O===r&&(O=Y,t.substr(Y,2)===xn?(J=xn,Y+=2):(J=r,dt===0&&wt(ca)),J!==r&&(xt=O,J=ji()),O=J,O===r&&(O=Y,t.substr(Y,2)===Oi?(J=Oi,Y+=2):(J=r,dt===0&&wt(Oa)),J!==r&&(xt=O,J=dn()),O=J,O===r&&(O=Y,t.substr(Y,2)===Jn?(J=Jn,Y+=2):(J=r,dt===0&&wt(hu)),J!==r&&(xt=O,J=Ch()),O=J,O===r&&(O=Y,t.charCodeAt(Y)===92?(J=aa,Y++):(J=r,dt===0&&wt(la)),J!==r?(La.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Ma)),re!==r?(xt=O,J=So(re),O=J):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=wu()))))))))),O}function wu(){var O,J,re,de,Ke,ft,dr,Br,_n,di,ws,zA;return O=Y,t.charCodeAt(Y)===92?(J=aa,Y++):(J=r,dt===0&&wt(la)),J!==r?(re=ha(),re!==r?(xt=O,J=Ua(re),O=J):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===Xe?(J=Xe,Y+=2):(J=r,dt===0&&wt(Ha)),J!==r?(re=Y,de=Y,Ke=ha(),Ke!==r?(ft=Ns(),ft!==r?(Ke=[Ke,ft],de=Ke):(Y=de,de=r)):(Y=de,de=r),de===r&&(de=ha()),de!==r?re=t.substring(re,Y):re=de,re!==r?(xt=O,J=Ua(re),O=J):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===gf?(J=gf,Y+=2):(J=r,dt===0&&wt(cc)),J!==r?(re=Y,de=Y,Ke=Ns(),Ke!==r?(ft=Ns(),ft!==r?(dr=Ns(),dr!==r?(Br=Ns(),Br!==r?(Ke=[Ke,ft,dr,Br],de=Ke):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r),de!==r?re=t.substring(re,Y):re=de,re!==r?(xt=O,J=Ua(re),O=J):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===wn?(J=wn,Y+=2):(J=r,dt===0&&wt(ua)),J!==r?(re=Y,de=Y,Ke=Ns(),Ke!==r?(ft=Ns(),ft!==r?(dr=Ns(),dr!==r?(Br=Ns(),Br!==r?(_n=Ns(),_n!==r?(di=Ns(),di!==r?(ws=Ns(),ws!==r?(zA=Ns(),zA!==r?(Ke=[Ke,ft,dr,Br,_n,di,ws,zA],de=Ke):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r)):(Y=de,de=r),de!==r?re=t.substring(re,Y):re=de,re!==r?(xt=O,J=_A(re),O=J):(Y=O,O=r)):(Y=O,O=r)))),O}function ha(){var O;return UA.test(t.charAt(Y))?(O=t.charAt(Y),Y++):(O=r,dt===0&&wt(fa)),O}function Ns(){var O;return vl.test(t.charAt(Y))?(O=t.charAt(Y),Y++):(O=r,dt===0&&wt(Mt)),O}function xh(){var O,J,re,de,Ke;if(O=Y,J=[],re=Y,t.charCodeAt(Y)===92?(de=aa,Y++):(de=r,dt===0&&wt(la)),de!==r?(t.length>Y?(Ke=t.charAt(Y),Y++):(Ke=r,dt===0&&wt(kn)),Ke!==r?(xt=re,de=So(Ke),re=de):(Y=re,re=r)):(Y=re,re=r),re===r&&(re=Y,t.substr(Y,2)===Aa?(de=Aa,Y+=2):(de=r,dt===0&&wt(ja)),de!==r&&(xt=re,de=ns()),re=de,re===r&&(re=Y,de=Y,dt++,Ke=Dy(),dt--,Ke===r?de=void 0:(Y=de,de=r),de!==r?(t.length>Y?(Ke=t.charAt(Y),Y++):(Ke=r,dt===0&&wt(kn)),Ke!==r?(xt=re,de=So(Ke),re=de):(Y=re,re=r)):(Y=re,re=r))),re!==r)for(;re!==r;)J.push(re),re=Y,t.charCodeAt(Y)===92?(de=aa,Y++):(de=r,dt===0&&wt(la)),de!==r?(t.length>Y?(Ke=t.charAt(Y),Y++):(Ke=r,dt===0&&wt(kn)),Ke!==r?(xt=re,de=So(Ke),re=de):(Y=re,re=r)):(Y=re,re=r),re===r&&(re=Y,t.substr(Y,2)===Aa?(de=Aa,Y+=2):(de=r,dt===0&&wt(ja)),de!==r&&(xt=re,de=ns()),re=de,re===r&&(re=Y,de=Y,dt++,Ke=Dy(),dt--,Ke===r?de=void 0:(Y=de,de=r),de!==r?(t.length>Y?(Ke=t.charAt(Y),Y++):(Ke=r,dt===0&&wt(kn)),Ke!==r?(xt=re,de=So(Ke),re=de):(Y=re,re=r)):(Y=re,re=r)));else J=r;return J!==r&&(xt=O,J=no(J)),O=J,O}function KA(){var O,J,re,de,Ke,ft;if(O=Y,t.charCodeAt(Y)===45?(J=uc,Y++):(J=r,dt===0&&wt(gu)),J===r&&(t.charCodeAt(Y)===43?(J=fc,Y++):(J=r,dt===0&&wt(qa))),J===r&&(J=null),J!==r){if(re=[],it.test(t.charAt(Y))?(de=t.charAt(Y),Y++):(de=r,dt===0&&wt(_e)),de!==r)for(;de!==r;)re.push(de),it.test(t.charAt(Y))?(de=t.charAt(Y),Y++):(de=r,dt===0&&wt(_e));else re=r;if(re!==r)if(t.charCodeAt(Y)===46?(de=Li,Y++):(de=r,dt===0&&wt(Cs)),de!==r){if(Ke=[],it.test(t.charAt(Y))?(ft=t.charAt(Y),Y++):(ft=r,dt===0&&wt(_e)),ft!==r)for(;ft!==r;)Ke.push(ft),it.test(t.charAt(Y))?(ft=t.charAt(Y),Y++):(ft=r,dt===0&&wt(_e));else Ke=r;Ke!==r?(xt=O,J=Sl(J,re,Ke),O=J):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;if(O===r){if(O=Y,t.charCodeAt(Y)===45?(J=uc,Y++):(J=r,dt===0&&wt(gu)),J===r&&(t.charCodeAt(Y)===43?(J=fc,Y++):(J=r,dt===0&&wt(qa))),J===r&&(J=null),J!==r){if(re=[],it.test(t.charAt(Y))?(de=t.charAt(Y),Y++):(de=r,dt===0&&wt(_e)),de!==r)for(;de!==r;)re.push(de),it.test(t.charAt(Y))?(de=t.charAt(Y),Y++):(de=r,dt===0&&wt(_e));else re=r;re!==r?(xt=O,J=df(J,re),O=J):(Y=O,O=r)}else Y=O,O=r;if(O===r&&(O=Y,J=JA(),J!==r&&(xt=O,J=Ac(J)),O=J,O===r&&(O=Y,J=hc(),J!==r&&(xt=O,J=wi(J)),O=J,O===r)))if(O=Y,t.charCodeAt(Y)===40?(J=Ee,Y++):(J=r,dt===0&&wt(fe)),J!==r){for(re=[],de=kt();de!==r;)re.push(de),de=kt();if(re!==r)if(de=so(),de!==r){for(Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();Ke!==r?(t.charCodeAt(Y)===41?(ft=se,Y++):(ft=r,dt===0&&wt(X)),ft!==r?(xt=O,J=Qn(de),O=J):(Y=O,O=r)):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r}return O}function Sf(){var O,J,re,de,Ke,ft,dr,Br;if(O=Y,J=KA(),J!==r){for(re=[],de=Y,Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();if(Ke!==r)if(t.charCodeAt(Y)===42?(ft=pc,Y++):(ft=r,dt===0&&wt(Je)),ft===r&&(t.charCodeAt(Y)===47?(ft=st,Y++):(ft=r,dt===0&&wt(St))),ft!==r){for(dr=[],Br=kt();Br!==r;)dr.push(Br),Br=kt();dr!==r?(Br=KA(),Br!==r?(xt=de,Ke=lr(J,ft,Br),de=Ke):(Y=de,de=r)):(Y=de,de=r)}else Y=de,de=r;else Y=de,de=r;for(;de!==r;){for(re.push(de),de=Y,Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();if(Ke!==r)if(t.charCodeAt(Y)===42?(ft=pc,Y++):(ft=r,dt===0&&wt(Je)),ft===r&&(t.charCodeAt(Y)===47?(ft=st,Y++):(ft=r,dt===0&&wt(St))),ft!==r){for(dr=[],Br=kt();Br!==r;)dr.push(Br),Br=kt();dr!==r?(Br=KA(),Br!==r?(xt=de,Ke=lr(J,ft,Br),de=Ke):(Y=de,de=r)):(Y=de,de=r)}else Y=de,de=r;else Y=de,de=r}re!==r?(xt=O,J=ee(J,re),O=J):(Y=O,O=r)}else Y=O,O=r;return O}function so(){var O,J,re,de,Ke,ft,dr,Br;if(O=Y,J=Sf(),J!==r){for(re=[],de=Y,Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();if(Ke!==r)if(t.charCodeAt(Y)===43?(ft=fc,Y++):(ft=r,dt===0&&wt(qa)),ft===r&&(t.charCodeAt(Y)===45?(ft=uc,Y++):(ft=r,dt===0&&wt(gu))),ft!==r){for(dr=[],Br=kt();Br!==r;)dr.push(Br),Br=kt();dr!==r?(Br=Sf(),Br!==r?(xt=de,Ke=Ie(J,ft,Br),de=Ke):(Y=de,de=r)):(Y=de,de=r)}else Y=de,de=r;else Y=de,de=r;for(;de!==r;){for(re.push(de),de=Y,Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();if(Ke!==r)if(t.charCodeAt(Y)===43?(ft=fc,Y++):(ft=r,dt===0&&wt(qa)),ft===r&&(t.charCodeAt(Y)===45?(ft=uc,Y++):(ft=r,dt===0&&wt(gu))),ft!==r){for(dr=[],Br=kt();Br!==r;)dr.push(Br),Br=kt();dr!==r?(Br=Sf(),Br!==r?(xt=de,Ke=Ie(J,ft,Br),de=Ke):(Y=de,de=r)):(Y=de,de=r)}else Y=de,de=r;else Y=de,de=r}re!==r?(xt=O,J=ee(J,re),O=J):(Y=O,O=r)}else Y=O,O=r;return O}function Xr(){var O,J,re,de,Ke,ft;if(O=Y,t.substr(Y,3)===Oe?(J=Oe,Y+=3):(J=r,dt===0&&wt(ht)),J!==r){for(re=[],de=kt();de!==r;)re.push(de),de=kt();if(re!==r)if(de=so(),de!==r){for(Ke=[],ft=kt();ft!==r;)Ke.push(ft),ft=kt();Ke!==r?(t.substr(Y,2)===mt?(ft=mt,Y+=2):(ft=r,dt===0&&wt(Dt)),ft!==r?(xt=O,J=tr(de),O=J):(Y=O,O=r)):(Y=O,O=r)}else Y=O,O=r;else Y=O,O=r}else Y=O,O=r;return O}function kh(){var O,J,re,de;return O=Y,t.substr(Y,2)===fn?(J=fn,Y+=2):(J=r,dt===0&&wt(ai)),J!==r?(re=pa(),re!==r?(t.charCodeAt(Y)===41?(de=se,Y++):(de=r,dt===0&&wt(X)),de!==r?(xt=O,J=qi(re),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O}function JA(){var O,J,re,de,Ke,ft;return O=Y,t.substr(Y,2)===Tn?(J=Tn,Y+=2):(J=r,dt===0&&wt(Ga)),J!==r?(re=hc(),re!==r?(t.substr(Y,2)===my?(de=my,Y+=2):(de=r,dt===0&&wt(t2)),de!==r?(Ke=Fs(),Ke!==r?(t.charCodeAt(Y)===125?(ft=j,Y++):(ft=r,dt===0&&wt(rt)),ft!==r?(xt=O,J=Do(re,Ke),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===Tn?(J=Tn,Y+=2):(J=r,dt===0&&wt(Ga)),J!==r?(re=hc(),re!==r?(t.substr(Y,3)===yy?(de=yy,Y+=3):(de=r,dt===0&&wt(wh)),de!==r?(xt=O,J=r2(re),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===Tn?(J=Tn,Y+=2):(J=r,dt===0&&wt(Ga)),J!==r?(re=hc(),re!==r?(t.substr(Y,2)===bo?(de=bo,Y+=2):(de=r,dt===0&&wt(Bh)),de!==r?(Ke=Fs(),Ke!==r?(t.charCodeAt(Y)===125?(ft=j,Y++):(ft=r,dt===0&&wt(rt)),ft!==r?(xt=O,J=vh(re,Ke),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===Tn?(J=Tn,Y+=2):(J=r,dt===0&&wt(Ga)),J!==r?(re=hc(),re!==r?(t.substr(Y,3)===du?(de=du,Y+=3):(de=r,dt===0&&wt(Sh)),de!==r?(xt=O,J=Ng(re),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.substr(Y,2)===Tn?(J=Tn,Y+=2):(J=r,dt===0&&wt(Ga)),J!==r?(re=hc(),re!==r?(t.charCodeAt(Y)===125?(de=j,Y++):(de=r,dt===0&&wt(rt)),de!==r?(xt=O,J=Og(re),O=J):(Y=O,O=r)):(Y=O,O=r)):(Y=O,O=r),O===r&&(O=Y,t.charCodeAt(Y)===36?(J=Lg,Y++):(J=r,dt===0&&wt(Ey)),J!==r?(re=hc(),re!==r?(xt=O,J=Og(re),O=J):(Y=O,O=r)):(Y=O,O=r)))))),O}function Sy(){var O,J,re;return O=Y,J=Yg(),J!==r?(xt=Y,re=mf(J),re?re=void 0:re=r,re!==r?(xt=O,J=Po(J),O=J):(Y=O,O=r)):(Y=O,O=r),O}function Yg(){var O,J,re,de,Ke;if(O=Y,J=[],re=Y,de=Y,dt++,Ke=Th(),dt--,Ke===r?de=void 0:(Y=de,de=r),de!==r?(t.length>Y?(Ke=t.charAt(Y),Y++):(Ke=r,dt===0&&wt(kn)),Ke!==r?(xt=re,de=So(Ke),re=de):(Y=re,re=r)):(Y=re,re=r),re!==r)for(;re!==r;)J.push(re),re=Y,de=Y,dt++,Ke=Th(),dt--,Ke===r?de=void 0:(Y=de,de=r),de!==r?(t.length>Y?(Ke=t.charAt(Y),Y++):(Ke=r,dt===0&&wt(kn)),Ke!==r?(xt=re,de=So(Ke),re=de):(Y=re,re=r)):(Y=re,re=r);else J=r;return J!==r&&(xt=O,J=no(J)),O=J,O}function Qh(){var O,J,re;if(O=Y,J=[],Dl.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Dh)),re!==r)for(;re!==r;)J.push(re),Dl.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Dh));else J=r;return J!==r&&(xt=O,J=Mg()),O=J,O}function hc(){var O,J,re;if(O=Y,J=[],bl.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Pl)),re!==r)for(;re!==r;)J.push(re),bl.test(t.charAt(Y))?(re=t.charAt(Y),Y++):(re=r,dt===0&&wt(Pl));else J=r;return J!==r&&(xt=O,J=Mg()),O=J,O}function Dy(){var O;return Iy.test(t.charAt(Y))?(O=t.charAt(Y),Y++):(O=r,dt===0&&wt(HA)),O}function Th(){var O;return Cy.test(t.charAt(Y))?(O=t.charAt(Y),Y++):(O=r,dt===0&&wt(wy)),O}function kt(){var O,J;if(O=[],jA.test(t.charAt(Y))?(J=t.charAt(Y),Y++):(J=r,dt===0&&wt(qA)),J!==r)for(;J!==r;)O.push(J),jA.test(t.charAt(Y))?(J=t.charAt(Y),Y++):(J=r,dt===0&&wt(qA));else O=r;return O}if(mu=a(),mu!==r&&Y===t.length)return mu;throw mu!==r&&Y!1}){try{return(0,rte.parse)(t,e)}catch(r){throw r.location&&(r.message=r.message.replace(/(\.)?$/,` (line ${r.location.start.line}, column ${r.location.start.column})$1`)),r}}function fE(t,{endSemicolon:e=!1}={}){return t.map(({command:r,type:s},a)=>`${Sx(r)}${s===";"?a!==t.length-1||e?";":"":" &"}`).join(" ")}function Sx(t){return`${AE(t.chain)}${t.then?` ${z_(t.then)}`:""}`}function z_(t){return`${t.type} ${Sx(t.line)}`}function AE(t){return`${X_(t)}${t.then?` ${Z_(t.then)}`:""}`}function Z_(t){return`${t.type} ${AE(t.chain)}`}function X_(t){switch(t.type){case"command":return`${t.envs.length>0?`${t.envs.map(e=>Bx(e)).join(" ")} `:""}${t.args.map(e=>$_(e)).join(" ")}`;case"subshell":return`(${fE(t.subshell)})${t.args.length>0?` ${t.args.map(e=>G2(e)).join(" ")}`:""}`;case"group":return`{ ${fE(t.group,{endSemicolon:!0})} }${t.args.length>0?` ${t.args.map(e=>G2(e)).join(" ")}`:""}`;case"envs":return t.envs.map(e=>Bx(e)).join(" ");default:throw new Error(`Unsupported command type: "${t.type}"`)}}function Bx(t){return`${t.name}=${t.args[0]?Sd(t.args[0]):""}`}function $_(t){switch(t.type){case"redirection":return G2(t);case"argument":return Sd(t);default:throw new Error(`Unsupported argument type: "${t.type}"`)}}function G2(t){return`${t.subtype} ${t.args.map(e=>Sd(e)).join(" ")}`}function Sd(t){return t.segments.map(e=>eU(e)).join("")}function eU(t){let e=(s,a)=>a?`"${s}"`:s,r=s=>s===""?"''":s.match(/[()}<>$|&;"'\n\t ]/)?s.match(/['\t\p{C}]/u)?s.match(/'/)?`"${s.replace(/["$\t\p{C}]/u,IKe)}"`:`$'${s.replace(/[\t\p{C}]/u,ite)}'`:`'${s}'`:s;switch(t.type){case"text":return r(t.text);case"glob":return t.pattern;case"shell":return e(`$(${fE(t.shell)})`,t.quoted);case"variable":return e(typeof t.defaultValue>"u"?typeof t.alternativeValue>"u"?`\${${t.name}}`:t.alternativeValue.length===0?`\${${t.name}:+}`:`\${${t.name}:+${t.alternativeValue.map(s=>Sd(s)).join(" ")}}`:t.defaultValue.length===0?`\${${t.name}:-}`:`\${${t.name}:-${t.defaultValue.map(s=>Sd(s)).join(" ")}}`,t.quoted);case"arithmetic":return`$(( ${Dx(t.arithmetic)} ))`;default:throw new Error(`Unsupported argument segment type: "${t.type}"`)}}function Dx(t){let e=a=>{switch(a){case"addition":return"+";case"subtraction":return"-";case"multiplication":return"*";case"division":return"/";default:throw new Error(`Can't extract operator from arithmetic expression of type "${a}"`)}},r=(a,n)=>n?`( ${a} )`:a,s=a=>r(Dx(a),!["number","variable"].includes(a.type));switch(t.type){case"number":return String(t.value);case"variable":return t.name;default:return`${s(t.left)} ${e(t.type)} ${s(t.right)}`}}var rte,nte,EKe,ite,IKe,ste=It(()=>{rte=et(tte());nte=new Map([["\f","\\f"],[` +`,"\\n"],["\r","\\r"],[" ","\\t"],["\v","\\v"],["\0","\\0"]]),EKe=new Map([["\\","\\\\"],["$","\\$"],['"','\\"'],...Array.from(nte,([t,e])=>[t,`"$'${e}'"`])]),ite=t=>nte.get(t)??`\\x${t.charCodeAt(0).toString(16).padStart(2,"0")}`,IKe=t=>EKe.get(t)??`"$'${ite(t)}'"`});var ate=L((U5t,ote)=>{"use strict";function CKe(t,e){function r(){this.constructor=t}r.prototype=e.prototype,t.prototype=new r}function Dd(t,e,r,s){this.message=t,this.expected=e,this.found=r,this.location=s,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,Dd)}CKe(Dd,Error);Dd.buildMessage=function(t,e){var r={literal:function(h){return'"'+a(h.text)+'"'},class:function(h){var E="",C;for(C=0;C0){for(C=1,S=1;CAe&&(Ae=W,ce=[]),ce.push(_e))}function rt(_e,x){return new Dd(_e,null,null,x)}function Fe(_e,x,w){return new Dd(Dd.buildMessage(_e,x),_e,x,w)}function Ne(){var _e,x,w,b;return _e=W,x=Pe(),x!==r?(t.charCodeAt(W)===47?(w=n,W++):(w=r,me===0&&j(c)),w!==r?(b=Pe(),b!==r?(te=_e,x=f(x,b),_e=x):(W=_e,_e=r)):(W=_e,_e=r)):(W=_e,_e=r),_e===r&&(_e=W,x=Pe(),x!==r&&(te=_e,x=p(x)),_e=x),_e}function Pe(){var _e,x,w,b;return _e=W,x=Ye(),x!==r?(t.charCodeAt(W)===64?(w=h,W++):(w=r,me===0&&j(E)),w!==r?(b=it(),b!==r?(te=_e,x=C(x,b),_e=x):(W=_e,_e=r)):(W=_e,_e=r)):(W=_e,_e=r),_e===r&&(_e=W,x=Ye(),x!==r&&(te=_e,x=S(x)),_e=x),_e}function Ye(){var _e,x,w,b,y;return _e=W,t.charCodeAt(W)===64?(x=h,W++):(x=r,me===0&&j(E)),x!==r?(w=ke(),w!==r?(t.charCodeAt(W)===47?(b=n,W++):(b=r,me===0&&j(c)),b!==r?(y=ke(),y!==r?(te=_e,x=P(),_e=x):(W=_e,_e=r)):(W=_e,_e=r)):(W=_e,_e=r)):(W=_e,_e=r),_e===r&&(_e=W,x=ke(),x!==r&&(te=_e,x=P()),_e=x),_e}function ke(){var _e,x,w;if(_e=W,x=[],I.test(t.charAt(W))?(w=t.charAt(W),W++):(w=r,me===0&&j(R)),w!==r)for(;w!==r;)x.push(w),I.test(t.charAt(W))?(w=t.charAt(W),W++):(w=r,me===0&&j(R));else x=r;return x!==r&&(te=_e,x=P()),_e=x,_e}function it(){var _e,x,w;if(_e=W,x=[],N.test(t.charAt(W))?(w=t.charAt(W),W++):(w=r,me===0&&j(U)),w!==r)for(;w!==r;)x.push(w),N.test(t.charAt(W))?(w=t.charAt(W),W++):(w=r,me===0&&j(U));else x=r;return x!==r&&(te=_e,x=P()),_e=x,_e}if(pe=a(),pe!==r&&W===t.length)return pe;throw pe!==r&&W{lte=et(ate())});var Pd=L((j5t,bd)=>{"use strict";function ute(t){return typeof t>"u"||t===null}function BKe(t){return typeof t=="object"&&t!==null}function vKe(t){return Array.isArray(t)?t:ute(t)?[]:[t]}function SKe(t,e){var r,s,a,n;if(e)for(n=Object.keys(e),r=0,s=n.length;r{"use strict";function W2(t,e){Error.call(this),this.name="YAMLException",this.reason=t,this.mark=e,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():""),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}W2.prototype=Object.create(Error.prototype);W2.prototype.constructor=W2;W2.prototype.toString=function(e){var r=this.name+": ";return r+=this.reason||"(unknown reason)",!e&&this.mark&&(r+=" "+this.mark.toString()),r};fte.exports=W2});var hte=L((G5t,pte)=>{"use strict";var Ate=Pd();function tU(t,e,r,s,a){this.name=t,this.buffer=e,this.position=r,this.line=s,this.column=a}tU.prototype.getSnippet=function(e,r){var s,a,n,c,f;if(!this.buffer)return null;for(e=e||4,r=r||75,s="",a=this.position;a>0&&`\0\r +\x85\u2028\u2029`.indexOf(this.buffer.charAt(a-1))===-1;)if(a-=1,this.position-a>r/2-1){s=" ... ",a+=5;break}for(n="",c=this.position;cr/2-1){n=" ... ",c-=5;break}return f=this.buffer.slice(a,c),Ate.repeat(" ",e)+s+f+n+` +`+Ate.repeat(" ",e+this.position-a+s.length)+"^"};tU.prototype.toString=function(e){var r,s="";return this.name&&(s+='in "'+this.name+'" '),s+="at line "+(this.line+1)+", column "+(this.column+1),e||(r=this.getSnippet(),r&&(s+=`: +`+r)),s};pte.exports=tU});var Ds=L((W5t,dte)=>{"use strict";var gte=pE(),PKe=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],xKe=["scalar","sequence","mapping"];function kKe(t){var e={};return t!==null&&Object.keys(t).forEach(function(r){t[r].forEach(function(s){e[String(s)]=r})}),e}function QKe(t,e){if(e=e||{},Object.keys(e).forEach(function(r){if(PKe.indexOf(r)===-1)throw new gte('Unknown option "'+r+'" is met in definition of "'+t+'" YAML type.')}),this.tag=t,this.kind=e.kind||null,this.resolve=e.resolve||function(){return!0},this.construct=e.construct||function(r){return r},this.instanceOf=e.instanceOf||null,this.predicate=e.predicate||null,this.represent=e.represent||null,this.defaultStyle=e.defaultStyle||null,this.styleAliases=kKe(e.styleAliases||null),xKe.indexOf(this.kind)===-1)throw new gte('Unknown kind "'+this.kind+'" is specified for "'+t+'" YAML type.')}dte.exports=QKe});var xd=L((Y5t,yte)=>{"use strict";var mte=Pd(),xx=pE(),TKe=Ds();function rU(t,e,r){var s=[];return t.include.forEach(function(a){r=rU(a,e,r)}),t[e].forEach(function(a){r.forEach(function(n,c){n.tag===a.tag&&n.kind===a.kind&&s.push(c)}),r.push(a)}),r.filter(function(a,n){return s.indexOf(n)===-1})}function RKe(){var t={scalar:{},sequence:{},mapping:{},fallback:{}},e,r;function s(a){t[a.kind][a.tag]=t.fallback[a.tag]=a}for(e=0,r=arguments.length;e{"use strict";var FKe=Ds();Ete.exports=new FKe("tag:yaml.org,2002:str",{kind:"scalar",construct:function(t){return t!==null?t:""}})});var wte=L((K5t,Cte)=>{"use strict";var NKe=Ds();Cte.exports=new NKe("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(t){return t!==null?t:[]}})});var vte=L((J5t,Bte)=>{"use strict";var OKe=Ds();Bte.exports=new OKe("tag:yaml.org,2002:map",{kind:"mapping",construct:function(t){return t!==null?t:{}}})});var kx=L((z5t,Ste)=>{"use strict";var LKe=xd();Ste.exports=new LKe({explicit:[Ite(),wte(),vte()]})});var bte=L((Z5t,Dte)=>{"use strict";var MKe=Ds();function _Ke(t){if(t===null)return!0;var e=t.length;return e===1&&t==="~"||e===4&&(t==="null"||t==="Null"||t==="NULL")}function UKe(){return null}function HKe(t){return t===null}Dte.exports=new MKe("tag:yaml.org,2002:null",{kind:"scalar",resolve:_Ke,construct:UKe,predicate:HKe,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})});var xte=L((X5t,Pte)=>{"use strict";var jKe=Ds();function qKe(t){if(t===null)return!1;var e=t.length;return e===4&&(t==="true"||t==="True"||t==="TRUE")||e===5&&(t==="false"||t==="False"||t==="FALSE")}function GKe(t){return t==="true"||t==="True"||t==="TRUE"}function WKe(t){return Object.prototype.toString.call(t)==="[object Boolean]"}Pte.exports=new jKe("tag:yaml.org,2002:bool",{kind:"scalar",resolve:qKe,construct:GKe,predicate:WKe,represent:{lowercase:function(t){return t?"true":"false"},uppercase:function(t){return t?"TRUE":"FALSE"},camelcase:function(t){return t?"True":"False"}},defaultStyle:"lowercase"})});var Qte=L(($5t,kte)=>{"use strict";var YKe=Pd(),VKe=Ds();function KKe(t){return 48<=t&&t<=57||65<=t&&t<=70||97<=t&&t<=102}function JKe(t){return 48<=t&&t<=55}function zKe(t){return 48<=t&&t<=57}function ZKe(t){if(t===null)return!1;var e=t.length,r=0,s=!1,a;if(!e)return!1;if(a=t[r],(a==="-"||a==="+")&&(a=t[++r]),a==="0"){if(r+1===e)return!0;if(a=t[++r],a==="b"){for(r++;r=0?"0b"+t.toString(2):"-0b"+t.toString(2).slice(1)},octal:function(t){return t>=0?"0"+t.toString(8):"-0"+t.toString(8).slice(1)},decimal:function(t){return t.toString(10)},hexadecimal:function(t){return t>=0?"0x"+t.toString(16).toUpperCase():"-0x"+t.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})});var Fte=L((e9t,Rte)=>{"use strict";var Tte=Pd(),eJe=Ds(),tJe=new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function rJe(t){return!(t===null||!tJe.test(t)||t[t.length-1]==="_")}function nJe(t){var e,r,s,a;return e=t.replace(/_/g,"").toLowerCase(),r=e[0]==="-"?-1:1,a=[],"+-".indexOf(e[0])>=0&&(e=e.slice(1)),e===".inf"?r===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:e===".nan"?NaN:e.indexOf(":")>=0?(e.split(":").forEach(function(n){a.unshift(parseFloat(n,10))}),e=0,s=1,a.forEach(function(n){e+=n*s,s*=60}),r*e):r*parseFloat(e,10)}var iJe=/^[-+]?[0-9]+e/;function sJe(t,e){var r;if(isNaN(t))switch(e){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===t)switch(e){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===t)switch(e){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(Tte.isNegativeZero(t))return"-0.0";return r=t.toString(10),iJe.test(r)?r.replace("e",".e"):r}function oJe(t){return Object.prototype.toString.call(t)==="[object Number]"&&(t%1!==0||Tte.isNegativeZero(t))}Rte.exports=new eJe("tag:yaml.org,2002:float",{kind:"scalar",resolve:rJe,construct:nJe,predicate:oJe,represent:sJe,defaultStyle:"lowercase"})});var nU=L((t9t,Nte)=>{"use strict";var aJe=xd();Nte.exports=new aJe({include:[kx()],implicit:[bte(),xte(),Qte(),Fte()]})});var iU=L((r9t,Ote)=>{"use strict";var lJe=xd();Ote.exports=new lJe({include:[nU()]})});var Ute=L((n9t,_te)=>{"use strict";var cJe=Ds(),Lte=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),Mte=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");function uJe(t){return t===null?!1:Lte.exec(t)!==null||Mte.exec(t)!==null}function fJe(t){var e,r,s,a,n,c,f,p=0,h=null,E,C,S;if(e=Lte.exec(t),e===null&&(e=Mte.exec(t)),e===null)throw new Error("Date resolve error");if(r=+e[1],s=+e[2]-1,a=+e[3],!e[4])return new Date(Date.UTC(r,s,a));if(n=+e[4],c=+e[5],f=+e[6],e[7]){for(p=e[7].slice(0,3);p.length<3;)p+="0";p=+p}return e[9]&&(E=+e[10],C=+(e[11]||0),h=(E*60+C)*6e4,e[9]==="-"&&(h=-h)),S=new Date(Date.UTC(r,s,a,n,c,f,p)),h&&S.setTime(S.getTime()-h),S}function AJe(t){return t.toISOString()}_te.exports=new cJe("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:uJe,construct:fJe,instanceOf:Date,represent:AJe})});var jte=L((i9t,Hte)=>{"use strict";var pJe=Ds();function hJe(t){return t==="<<"||t===null}Hte.exports=new pJe("tag:yaml.org,2002:merge",{kind:"scalar",resolve:hJe})});var Wte=L((s9t,Gte)=>{"use strict";var kd;try{qte=ye,kd=qte("buffer").Buffer}catch{}var qte,gJe=Ds(),sU=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= +\r`;function dJe(t){if(t===null)return!1;var e,r,s=0,a=t.length,n=sU;for(r=0;r64)){if(e<0)return!1;s+=6}return s%8===0}function mJe(t){var e,r,s=t.replace(/[\r\n=]/g,""),a=s.length,n=sU,c=0,f=[];for(e=0;e>16&255),f.push(c>>8&255),f.push(c&255)),c=c<<6|n.indexOf(s.charAt(e));return r=a%4*6,r===0?(f.push(c>>16&255),f.push(c>>8&255),f.push(c&255)):r===18?(f.push(c>>10&255),f.push(c>>2&255)):r===12&&f.push(c>>4&255),kd?kd.from?kd.from(f):new kd(f):f}function yJe(t){var e="",r=0,s,a,n=t.length,c=sU;for(s=0;s>18&63],e+=c[r>>12&63],e+=c[r>>6&63],e+=c[r&63]),r=(r<<8)+t[s];return a=n%3,a===0?(e+=c[r>>18&63],e+=c[r>>12&63],e+=c[r>>6&63],e+=c[r&63]):a===2?(e+=c[r>>10&63],e+=c[r>>4&63],e+=c[r<<2&63],e+=c[64]):a===1&&(e+=c[r>>2&63],e+=c[r<<4&63],e+=c[64],e+=c[64]),e}function EJe(t){return kd&&kd.isBuffer(t)}Gte.exports=new gJe("tag:yaml.org,2002:binary",{kind:"scalar",resolve:dJe,construct:mJe,predicate:EJe,represent:yJe})});var Vte=L((a9t,Yte)=>{"use strict";var IJe=Ds(),CJe=Object.prototype.hasOwnProperty,wJe=Object.prototype.toString;function BJe(t){if(t===null)return!0;var e=[],r,s,a,n,c,f=t;for(r=0,s=f.length;r{"use strict";var SJe=Ds(),DJe=Object.prototype.toString;function bJe(t){if(t===null)return!0;var e,r,s,a,n,c=t;for(n=new Array(c.length),e=0,r=c.length;e{"use strict";var xJe=Ds(),kJe=Object.prototype.hasOwnProperty;function QJe(t){if(t===null)return!0;var e,r=t;for(e in r)if(kJe.call(r,e)&&r[e]!==null)return!1;return!0}function TJe(t){return t!==null?t:{}}zte.exports=new xJe("tag:yaml.org,2002:set",{kind:"mapping",resolve:QJe,construct:TJe})});var gE=L((u9t,Xte)=>{"use strict";var RJe=xd();Xte.exports=new RJe({include:[iU()],implicit:[Ute(),jte()],explicit:[Wte(),Vte(),Jte(),Zte()]})});var ere=L((f9t,$te)=>{"use strict";var FJe=Ds();function NJe(){return!0}function OJe(){}function LJe(){return""}function MJe(t){return typeof t>"u"}$te.exports=new FJe("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:NJe,construct:OJe,predicate:MJe,represent:LJe})});var rre=L((A9t,tre)=>{"use strict";var _Je=Ds();function UJe(t){if(t===null||t.length===0)return!1;var e=t,r=/\/([gim]*)$/.exec(t),s="";return!(e[0]==="/"&&(r&&(s=r[1]),s.length>3||e[e.length-s.length-1]!=="/"))}function HJe(t){var e=t,r=/\/([gim]*)$/.exec(t),s="";return e[0]==="/"&&(r&&(s=r[1]),e=e.slice(1,e.length-s.length-1)),new RegExp(e,s)}function jJe(t){var e="/"+t.source+"/";return t.global&&(e+="g"),t.multiline&&(e+="m"),t.ignoreCase&&(e+="i"),e}function qJe(t){return Object.prototype.toString.call(t)==="[object RegExp]"}tre.exports=new _Je("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:UJe,construct:HJe,predicate:qJe,represent:jJe})});var sre=L((p9t,ire)=>{"use strict";var Qx;try{nre=ye,Qx=nre("esprima")}catch{typeof window<"u"&&(Qx=window.esprima)}var nre,GJe=Ds();function WJe(t){if(t===null)return!1;try{var e="("+t+")",r=Qx.parse(e,{range:!0});return!(r.type!=="Program"||r.body.length!==1||r.body[0].type!=="ExpressionStatement"||r.body[0].expression.type!=="ArrowFunctionExpression"&&r.body[0].expression.type!=="FunctionExpression")}catch{return!1}}function YJe(t){var e="("+t+")",r=Qx.parse(e,{range:!0}),s=[],a;if(r.type!=="Program"||r.body.length!==1||r.body[0].type!=="ExpressionStatement"||r.body[0].expression.type!=="ArrowFunctionExpression"&&r.body[0].expression.type!=="FunctionExpression")throw new Error("Failed to resolve function");return r.body[0].expression.params.forEach(function(n){s.push(n.name)}),a=r.body[0].expression.body.range,r.body[0].expression.body.type==="BlockStatement"?new Function(s,e.slice(a[0]+1,a[1]-1)):new Function(s,"return "+e.slice(a[0],a[1]))}function VJe(t){return t.toString()}function KJe(t){return Object.prototype.toString.call(t)==="[object Function]"}ire.exports=new GJe("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:WJe,construct:YJe,predicate:KJe,represent:VJe})});var Y2=L((g9t,are)=>{"use strict";var ore=xd();are.exports=ore.DEFAULT=new ore({include:[gE()],explicit:[ere(),rre(),sre()]})});var Dre=L((d9t,V2)=>{"use strict";var wp=Pd(),hre=pE(),JJe=hte(),gre=gE(),zJe=Y2(),a0=Object.prototype.hasOwnProperty,Tx=1,dre=2,mre=3,Rx=4,oU=1,ZJe=2,lre=3,XJe=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,$Je=/[\x85\u2028\u2029]/,eze=/[,\[\]\{\}]/,yre=/^(?:!|!!|![a-z\-]+!)$/i,Ere=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;function cre(t){return Object.prototype.toString.call(t)}function qf(t){return t===10||t===13}function Td(t){return t===9||t===32}function nl(t){return t===9||t===32||t===10||t===13}function dE(t){return t===44||t===91||t===93||t===123||t===125}function tze(t){var e;return 48<=t&&t<=57?t-48:(e=t|32,97<=e&&e<=102?e-97+10:-1)}function rze(t){return t===120?2:t===117?4:t===85?8:0}function nze(t){return 48<=t&&t<=57?t-48:-1}function ure(t){return t===48?"\0":t===97?"\x07":t===98?"\b":t===116||t===9?" ":t===110?` +`:t===118?"\v":t===102?"\f":t===114?"\r":t===101?"\x1B":t===32?" ":t===34?'"':t===47?"/":t===92?"\\":t===78?"\x85":t===95?"\xA0":t===76?"\u2028":t===80?"\u2029":""}function ize(t){return t<=65535?String.fromCharCode(t):String.fromCharCode((t-65536>>10)+55296,(t-65536&1023)+56320)}var Ire=new Array(256),Cre=new Array(256);for(Qd=0;Qd<256;Qd++)Ire[Qd]=ure(Qd)?1:0,Cre[Qd]=ure(Qd);var Qd;function sze(t,e){this.input=t,this.filename=e.filename||null,this.schema=e.schema||zJe,this.onWarning=e.onWarning||null,this.legacy=e.legacy||!1,this.json=e.json||!1,this.listener=e.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=t.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function wre(t,e){return new hre(e,new JJe(t.filename,t.input,t.position,t.line,t.position-t.lineStart))}function Rr(t,e){throw wre(t,e)}function Fx(t,e){t.onWarning&&t.onWarning.call(null,wre(t,e))}var fre={YAML:function(e,r,s){var a,n,c;e.version!==null&&Rr(e,"duplication of %YAML directive"),s.length!==1&&Rr(e,"YAML directive accepts exactly one argument"),a=/^([0-9]+)\.([0-9]+)$/.exec(s[0]),a===null&&Rr(e,"ill-formed argument of the YAML directive"),n=parseInt(a[1],10),c=parseInt(a[2],10),n!==1&&Rr(e,"unacceptable YAML version of the document"),e.version=s[0],e.checkLineBreaks=c<2,c!==1&&c!==2&&Fx(e,"unsupported YAML version of the document")},TAG:function(e,r,s){var a,n;s.length!==2&&Rr(e,"TAG directive accepts exactly two arguments"),a=s[0],n=s[1],yre.test(a)||Rr(e,"ill-formed tag handle (first argument) of the TAG directive"),a0.call(e.tagMap,a)&&Rr(e,'there is a previously declared suffix for "'+a+'" tag handle'),Ere.test(n)||Rr(e,"ill-formed tag prefix (second argument) of the TAG directive"),e.tagMap[a]=n}};function o0(t,e,r,s){var a,n,c,f;if(e1&&(t.result+=wp.repeat(` +`,e-1))}function oze(t,e,r){var s,a,n,c,f,p,h,E,C=t.kind,S=t.result,P;if(P=t.input.charCodeAt(t.position),nl(P)||dE(P)||P===35||P===38||P===42||P===33||P===124||P===62||P===39||P===34||P===37||P===64||P===96||(P===63||P===45)&&(a=t.input.charCodeAt(t.position+1),nl(a)||r&&dE(a)))return!1;for(t.kind="scalar",t.result="",n=c=t.position,f=!1;P!==0;){if(P===58){if(a=t.input.charCodeAt(t.position+1),nl(a)||r&&dE(a))break}else if(P===35){if(s=t.input.charCodeAt(t.position-1),nl(s))break}else{if(t.position===t.lineStart&&Nx(t)||r&&dE(P))break;if(qf(P))if(p=t.line,h=t.lineStart,E=t.lineIndent,as(t,!1,-1),t.lineIndent>=e){f=!0,P=t.input.charCodeAt(t.position);continue}else{t.position=c,t.line=p,t.lineStart=h,t.lineIndent=E;break}}f&&(o0(t,n,c,!1),lU(t,t.line-p),n=c=t.position,f=!1),Td(P)||(c=t.position+1),P=t.input.charCodeAt(++t.position)}return o0(t,n,c,!1),t.result?!0:(t.kind=C,t.result=S,!1)}function aze(t,e){var r,s,a;if(r=t.input.charCodeAt(t.position),r!==39)return!1;for(t.kind="scalar",t.result="",t.position++,s=a=t.position;(r=t.input.charCodeAt(t.position))!==0;)if(r===39)if(o0(t,s,t.position,!0),r=t.input.charCodeAt(++t.position),r===39)s=t.position,t.position++,a=t.position;else return!0;else qf(r)?(o0(t,s,a,!0),lU(t,as(t,!1,e)),s=a=t.position):t.position===t.lineStart&&Nx(t)?Rr(t,"unexpected end of the document within a single quoted scalar"):(t.position++,a=t.position);Rr(t,"unexpected end of the stream within a single quoted scalar")}function lze(t,e){var r,s,a,n,c,f;if(f=t.input.charCodeAt(t.position),f!==34)return!1;for(t.kind="scalar",t.result="",t.position++,r=s=t.position;(f=t.input.charCodeAt(t.position))!==0;){if(f===34)return o0(t,r,t.position,!0),t.position++,!0;if(f===92){if(o0(t,r,t.position,!0),f=t.input.charCodeAt(++t.position),qf(f))as(t,!1,e);else if(f<256&&Ire[f])t.result+=Cre[f],t.position++;else if((c=rze(f))>0){for(a=c,n=0;a>0;a--)f=t.input.charCodeAt(++t.position),(c=tze(f))>=0?n=(n<<4)+c:Rr(t,"expected hexadecimal character");t.result+=ize(n),t.position++}else Rr(t,"unknown escape sequence");r=s=t.position}else qf(f)?(o0(t,r,s,!0),lU(t,as(t,!1,e)),r=s=t.position):t.position===t.lineStart&&Nx(t)?Rr(t,"unexpected end of the document within a double quoted scalar"):(t.position++,s=t.position)}Rr(t,"unexpected end of the stream within a double quoted scalar")}function cze(t,e){var r=!0,s,a=t.tag,n,c=t.anchor,f,p,h,E,C,S={},P,I,R,N;if(N=t.input.charCodeAt(t.position),N===91)p=93,C=!1,n=[];else if(N===123)p=125,C=!0,n={};else return!1;for(t.anchor!==null&&(t.anchorMap[t.anchor]=n),N=t.input.charCodeAt(++t.position);N!==0;){if(as(t,!0,e),N=t.input.charCodeAt(t.position),N===p)return t.position++,t.tag=a,t.anchor=c,t.kind=C?"mapping":"sequence",t.result=n,!0;r||Rr(t,"missed comma between flow collection entries"),I=P=R=null,h=E=!1,N===63&&(f=t.input.charCodeAt(t.position+1),nl(f)&&(h=E=!0,t.position++,as(t,!0,e))),s=t.line,yE(t,e,Tx,!1,!0),I=t.tag,P=t.result,as(t,!0,e),N=t.input.charCodeAt(t.position),(E||t.line===s)&&N===58&&(h=!0,N=t.input.charCodeAt(++t.position),as(t,!0,e),yE(t,e,Tx,!1,!0),R=t.result),C?mE(t,n,S,I,P,R):h?n.push(mE(t,null,S,I,P,R)):n.push(P),as(t,!0,e),N=t.input.charCodeAt(t.position),N===44?(r=!0,N=t.input.charCodeAt(++t.position)):r=!1}Rr(t,"unexpected end of the stream within a flow collection")}function uze(t,e){var r,s,a=oU,n=!1,c=!1,f=e,p=0,h=!1,E,C;if(C=t.input.charCodeAt(t.position),C===124)s=!1;else if(C===62)s=!0;else return!1;for(t.kind="scalar",t.result="";C!==0;)if(C=t.input.charCodeAt(++t.position),C===43||C===45)oU===a?a=C===43?lre:ZJe:Rr(t,"repeat of a chomping mode identifier");else if((E=nze(C))>=0)E===0?Rr(t,"bad explicit indentation width of a block scalar; it cannot be less than one"):c?Rr(t,"repeat of an indentation width identifier"):(f=e+E-1,c=!0);else break;if(Td(C)){do C=t.input.charCodeAt(++t.position);while(Td(C));if(C===35)do C=t.input.charCodeAt(++t.position);while(!qf(C)&&C!==0)}for(;C!==0;){for(aU(t),t.lineIndent=0,C=t.input.charCodeAt(t.position);(!c||t.lineIndentf&&(f=t.lineIndent),qf(C)){p++;continue}if(t.lineIndente)&&p!==0)Rr(t,"bad indentation of a sequence entry");else if(t.lineIndente)&&(yE(t,e,Rx,!0,a)&&(I?S=t.result:P=t.result),I||(mE(t,h,E,C,S,P,n,c),C=S=P=null),as(t,!0,-1),N=t.input.charCodeAt(t.position)),t.lineIndent>e&&N!==0)Rr(t,"bad indentation of a mapping entry");else if(t.lineIndente?p=1:t.lineIndent===e?p=0:t.lineIndente?p=1:t.lineIndent===e?p=0:t.lineIndent tag; it should be "scalar", not "'+t.kind+'"'),C=0,S=t.implicitTypes.length;C tag; it should be "'+P.kind+'", not "'+t.kind+'"'),P.resolve(t.result)?(t.result=P.construct(t.result),t.anchor!==null&&(t.anchorMap[t.anchor]=t.result)):Rr(t,"cannot resolve a node with !<"+t.tag+"> explicit tag")):Rr(t,"unknown tag !<"+t.tag+">");return t.listener!==null&&t.listener("close",t),t.tag!==null||t.anchor!==null||E}function gze(t){var e=t.position,r,s,a,n=!1,c;for(t.version=null,t.checkLineBreaks=t.legacy,t.tagMap={},t.anchorMap={};(c=t.input.charCodeAt(t.position))!==0&&(as(t,!0,-1),c=t.input.charCodeAt(t.position),!(t.lineIndent>0||c!==37));){for(n=!0,c=t.input.charCodeAt(++t.position),r=t.position;c!==0&&!nl(c);)c=t.input.charCodeAt(++t.position);for(s=t.input.slice(r,t.position),a=[],s.length<1&&Rr(t,"directive name must not be less than one character in length");c!==0;){for(;Td(c);)c=t.input.charCodeAt(++t.position);if(c===35){do c=t.input.charCodeAt(++t.position);while(c!==0&&!qf(c));break}if(qf(c))break;for(r=t.position;c!==0&&!nl(c);)c=t.input.charCodeAt(++t.position);a.push(t.input.slice(r,t.position))}c!==0&&aU(t),a0.call(fre,s)?fre[s](t,s,a):Fx(t,'unknown document directive "'+s+'"')}if(as(t,!0,-1),t.lineIndent===0&&t.input.charCodeAt(t.position)===45&&t.input.charCodeAt(t.position+1)===45&&t.input.charCodeAt(t.position+2)===45?(t.position+=3,as(t,!0,-1)):n&&Rr(t,"directives end mark is expected"),yE(t,t.lineIndent-1,Rx,!1,!0),as(t,!0,-1),t.checkLineBreaks&&$Je.test(t.input.slice(e,t.position))&&Fx(t,"non-ASCII line breaks are interpreted as content"),t.documents.push(t.result),t.position===t.lineStart&&Nx(t)){t.input.charCodeAt(t.position)===46&&(t.position+=3,as(t,!0,-1));return}if(t.position"u"&&(r=e,e=null);var s=Bre(t,r);if(typeof e!="function")return s;for(var a=0,n=s.length;a"u"&&(r=e,e=null),vre(t,e,wp.extend({schema:gre},r))}function mze(t,e){return Sre(t,wp.extend({schema:gre},e))}V2.exports.loadAll=vre;V2.exports.load=Sre;V2.exports.safeLoadAll=dze;V2.exports.safeLoad=mze});var Jre=L((m9t,AU)=>{"use strict";var J2=Pd(),z2=pE(),yze=Y2(),Eze=gE(),Fre=Object.prototype.toString,Nre=Object.prototype.hasOwnProperty,Ize=9,K2=10,Cze=13,wze=32,Bze=33,vze=34,Ore=35,Sze=37,Dze=38,bze=39,Pze=42,Lre=44,xze=45,Mre=58,kze=61,Qze=62,Tze=63,Rze=64,_re=91,Ure=93,Fze=96,Hre=123,Nze=124,jre=125,jo={};jo[0]="\\0";jo[7]="\\a";jo[8]="\\b";jo[9]="\\t";jo[10]="\\n";jo[11]="\\v";jo[12]="\\f";jo[13]="\\r";jo[27]="\\e";jo[34]='\\"';jo[92]="\\\\";jo[133]="\\N";jo[160]="\\_";jo[8232]="\\L";jo[8233]="\\P";var Oze=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"];function Lze(t,e){var r,s,a,n,c,f,p;if(e===null)return{};for(r={},s=Object.keys(e),a=0,n=s.length;a0?t.charCodeAt(n-1):null,S=S&&xre(c,f)}else{for(n=0;ns&&t[C+1]!==" ",C=n);else if(!EE(c))return Ox;f=n>0?t.charCodeAt(n-1):null,S=S&&xre(c,f)}h=h||E&&n-C-1>s&&t[C+1]!==" "}return!p&&!h?S&&!a(t)?Gre:Wre:r>9&&qre(t)?Ox:h?Vre:Yre}function qze(t,e,r,s){t.dump=function(){if(e.length===0)return"''";if(!t.noCompatMode&&Oze.indexOf(e)!==-1)return"'"+e+"'";var a=t.indent*Math.max(1,r),n=t.lineWidth===-1?-1:Math.max(Math.min(t.lineWidth,40),t.lineWidth-a),c=s||t.flowLevel>-1&&r>=t.flowLevel;function f(p){return _ze(t,p)}switch(jze(e,c,t.indent,n,f)){case Gre:return e;case Wre:return"'"+e.replace(/'/g,"''")+"'";case Yre:return"|"+kre(e,t.indent)+Qre(Pre(e,a));case Vre:return">"+kre(e,t.indent)+Qre(Pre(Gze(e,n),a));case Ox:return'"'+Wze(e,n)+'"';default:throw new z2("impossible error: invalid scalar style")}}()}function kre(t,e){var r=qre(t)?String(e):"",s=t[t.length-1]===` +`,a=s&&(t[t.length-2]===` +`||t===` +`),n=a?"+":s?"":"-";return r+n+` +`}function Qre(t){return t[t.length-1]===` +`?t.slice(0,-1):t}function Gze(t,e){for(var r=/(\n+)([^\n]*)/g,s=function(){var h=t.indexOf(` +`);return h=h!==-1?h:t.length,r.lastIndex=h,Tre(t.slice(0,h),e)}(),a=t[0]===` +`||t[0]===" ",n,c;c=r.exec(t);){var f=c[1],p=c[2];n=p[0]===" ",s+=f+(!a&&!n&&p!==""?` +`:"")+Tre(p,e),a=n}return s}function Tre(t,e){if(t===""||t[0]===" ")return t;for(var r=/ [^ ]/g,s,a=0,n,c=0,f=0,p="";s=r.exec(t);)f=s.index,f-a>e&&(n=c>a?c:f,p+=` +`+t.slice(a,n),a=n+1),c=f;return p+=` +`,t.length-a>e&&c>a?p+=t.slice(a,c)+` +`+t.slice(c+1):p+=t.slice(a),p.slice(1)}function Wze(t){for(var e="",r,s,a,n=0;n=55296&&r<=56319&&(s=t.charCodeAt(n+1),s>=56320&&s<=57343)){e+=bre((r-55296)*1024+s-56320+65536),n++;continue}a=jo[r],e+=!a&&EE(r)?t[n]:a||bre(r)}return e}function Yze(t,e,r){var s="",a=t.tag,n,c;for(n=0,c=r.length;n1024&&(E+="? "),E+=t.dump+(t.condenseFlow?'"':"")+":"+(t.condenseFlow?"":" "),Rd(t,e,h,!1,!1)&&(E+=t.dump,s+=E));t.tag=a,t.dump="{"+s+"}"}function Jze(t,e,r,s){var a="",n=t.tag,c=Object.keys(r),f,p,h,E,C,S;if(t.sortKeys===!0)c.sort();else if(typeof t.sortKeys=="function")c.sort(t.sortKeys);else if(t.sortKeys)throw new z2("sortKeys must be a boolean or a function");for(f=0,p=c.length;f1024,C&&(t.dump&&K2===t.dump.charCodeAt(0)?S+="?":S+="? "),S+=t.dump,C&&(S+=cU(t,e)),Rd(t,e+1,E,!0,C)&&(t.dump&&K2===t.dump.charCodeAt(0)?S+=":":S+=": ",S+=t.dump,a+=S));t.tag=n,t.dump=a||"{}"}function Rre(t,e,r){var s,a,n,c,f,p;for(a=r?t.explicitTypes:t.implicitTypes,n=0,c=a.length;n tag resolver accepts not "'+p+'" style');t.dump=s}return!0}return!1}function Rd(t,e,r,s,a,n){t.tag=null,t.dump=r,Rre(t,r,!1)||Rre(t,r,!0);var c=Fre.call(t.dump);s&&(s=t.flowLevel<0||t.flowLevel>e);var f=c==="[object Object]"||c==="[object Array]",p,h;if(f&&(p=t.duplicates.indexOf(r),h=p!==-1),(t.tag!==null&&t.tag!=="?"||h||t.indent!==2&&e>0)&&(a=!1),h&&t.usedDuplicates[p])t.dump="*ref_"+p;else{if(f&&h&&!t.usedDuplicates[p]&&(t.usedDuplicates[p]=!0),c==="[object Object]")s&&Object.keys(t.dump).length!==0?(Jze(t,e,t.dump,a),h&&(t.dump="&ref_"+p+t.dump)):(Kze(t,e,t.dump),h&&(t.dump="&ref_"+p+" "+t.dump));else if(c==="[object Array]"){var E=t.noArrayIndent&&e>0?e-1:e;s&&t.dump.length!==0?(Vze(t,E,t.dump,a),h&&(t.dump="&ref_"+p+t.dump)):(Yze(t,E,t.dump),h&&(t.dump="&ref_"+p+" "+t.dump))}else if(c==="[object String]")t.tag!=="?"&&qze(t,t.dump,e,n);else{if(t.skipInvalid)return!1;throw new z2("unacceptable kind of an object to dump "+c)}t.tag!==null&&t.tag!=="?"&&(t.dump="!<"+t.tag+"> "+t.dump)}return!0}function zze(t,e){var r=[],s=[],a,n;for(uU(t,r,s),a=0,n=s.length;a{"use strict";var Lx=Dre(),zre=Jre();function Mx(t){return function(){throw new Error("Function "+t+" is deprecated and cannot be used.")}}Gi.exports.Type=Ds();Gi.exports.Schema=xd();Gi.exports.FAILSAFE_SCHEMA=kx();Gi.exports.JSON_SCHEMA=nU();Gi.exports.CORE_SCHEMA=iU();Gi.exports.DEFAULT_SAFE_SCHEMA=gE();Gi.exports.DEFAULT_FULL_SCHEMA=Y2();Gi.exports.load=Lx.load;Gi.exports.loadAll=Lx.loadAll;Gi.exports.safeLoad=Lx.safeLoad;Gi.exports.safeLoadAll=Lx.safeLoadAll;Gi.exports.dump=zre.dump;Gi.exports.safeDump=zre.safeDump;Gi.exports.YAMLException=pE();Gi.exports.MINIMAL_SCHEMA=kx();Gi.exports.SAFE_SCHEMA=gE();Gi.exports.DEFAULT_SCHEMA=Y2();Gi.exports.scan=Mx("scan");Gi.exports.parse=Mx("parse");Gi.exports.compose=Mx("compose");Gi.exports.addConstructor=Mx("addConstructor")});var $re=L((E9t,Xre)=>{"use strict";var Xze=Zre();Xre.exports=Xze});var tne=L((I9t,ene)=>{"use strict";function $ze(t,e){function r(){this.constructor=t}r.prototype=e.prototype,t.prototype=new r}function Fd(t,e,r,s){this.message=t,this.expected=e,this.found=r,this.location=s,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,Fd)}$ze(Fd,Error);Fd.buildMessage=function(t,e){var r={literal:function(h){return'"'+a(h.text)+'"'},class:function(h){var E="",C;for(C=0;C0){for(C=1,S=1;C({[ht]:Oe})))},Ae=function(ee){return ee},ce=function(ee){return ee},me=La("correct indentation"),pe=" ",Be=dn(" ",!1),Ce=function(ee){return ee.length===lr*St},g=function(ee){return ee.length===(lr+1)*St},we=function(){return lr++,!0},Ee=function(){return lr--,!0},fe=function(){return ca()},se=La("pseudostring"),X=/^[^\r\n\t ?:,\][{}#&*!|>'"%@`\-]/,De=Jn(["\r",` +`," "," ","?",":",",","]","[","{","}","#","&","*","!","|",">","'",'"',"%","@","`","-"],!0,!1),Re=/^[^\r\n\t ,\][{}:#"']/,gt=Jn(["\r",` +`," "," ",",","]","[","{","}",":","#",'"',"'"],!0,!1),j=function(){return ca().replace(/^ *| *$/g,"")},rt="--",Fe=dn("--",!1),Ne=/^[a-zA-Z\/0-9]/,Pe=Jn([["a","z"],["A","Z"],"/",["0","9"]],!1,!1),Ye=/^[^\r\n\t :,]/,ke=Jn(["\r",` +`," "," ",":",","],!0,!1),it="null",_e=dn("null",!1),x=function(){return null},w="true",b=dn("true",!1),y=function(){return!0},F="false",z=dn("false",!1),Z=function(){return!1},$=La("string"),oe='"',xe=dn('"',!1),Te=function(){return""},lt=function(ee){return ee},Et=function(ee){return ee.join("")},qt=/^[^"\\\0-\x1F\x7F]/,ir=Jn(['"',"\\",["\0",""],"\x7F"],!0,!1),Pt='\\"',gn=dn('\\"',!1),Pr=function(){return'"'},Ir="\\\\",Nr=dn("\\\\",!1),nn=function(){return"\\"},oi="\\/",wo=dn("\\/",!1),rs=function(){return"/"},eo="\\b",Bo=dn("\\b",!1),Hi=function(){return"\b"},to="\\f",vo=dn("\\f",!1),RA=function(){return"\f"},pf="\\n",Eh=dn("\\n",!1),Ih=function(){return` +`},ro="\\r",jn=dn("\\r",!1),Rs=function(){return"\r"},no="\\t",lu=dn("\\t",!1),cu=function(){return" "},uu="\\u",FA=dn("\\u",!1),NA=function(ee,Ie,Oe,ht){return String.fromCharCode(parseInt(`0x${ee}${Ie}${Oe}${ht}`))},aa=/^[0-9a-fA-F]/,la=Jn([["0","9"],["a","f"],["A","F"]],!1,!1),OA=La("blank space"),gr=/^[ \t]/,So=Jn([" "," "],!1,!1),Me=La("white space"),fu=/^[ \t\n\r]/,Cr=Jn([" "," ",` +`,"\r"],!1,!1),hf=`\r +`,LA=dn(`\r +`,!1),MA=` +`,Au=dn(` +`,!1),pu="\r",ac=dn("\r",!1),ve=0,Nt=0,lc=[{line:1,column:1}],Ni=0,io=[],Rt=0,xn;if("startRule"in e){if(!(e.startRule in s))throw new Error(`Can't start parsing from rule "`+e.startRule+'".');a=s[e.startRule]}function ca(){return t.substring(Nt,ve)}function ji(){return Ua(Nt,ve)}function Oi(ee,Ie){throw Ie=Ie!==void 0?Ie:Ua(Nt,ve),gf([La(ee)],t.substring(Nt,ve),Ie)}function Oa(ee,Ie){throw Ie=Ie!==void 0?Ie:Ua(Nt,ve),Ha(ee,Ie)}function dn(ee,Ie){return{type:"literal",text:ee,ignoreCase:Ie}}function Jn(ee,Ie,Oe){return{type:"class",parts:ee,inverted:Ie,ignoreCase:Oe}}function hu(){return{type:"any"}}function Ch(){return{type:"end"}}function La(ee){return{type:"other",description:ee}}function Ma(ee){var Ie=lc[ee],Oe;if(Ie)return Ie;for(Oe=ee-1;!lc[Oe];)Oe--;for(Ie=lc[Oe],Ie={line:Ie.line,column:Ie.column};OeNi&&(Ni=ve,io=[]),io.push(ee))}function Ha(ee,Ie){return new Fd(ee,null,null,Ie)}function gf(ee,Ie,Oe){return new Fd(Fd.buildMessage(ee,Ie),ee,Ie,Oe)}function cc(){var ee;return ee=_A(),ee}function wn(){var ee,Ie,Oe;for(ee=ve,Ie=[],Oe=ua();Oe!==r;)Ie.push(Oe),Oe=ua();return Ie!==r&&(Nt=ee,Ie=n(Ie)),ee=Ie,ee}function ua(){var ee,Ie,Oe,ht,mt;return ee=ve,Ie=vl(),Ie!==r?(t.charCodeAt(ve)===45?(Oe=c,ve++):(Oe=r,Rt===0&&Xe(f)),Oe!==r?(ht=Qn(),ht!==r?(mt=fa(),mt!==r?(Nt=ee,Ie=p(mt),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r),ee}function _A(){var ee,Ie,Oe;for(ee=ve,Ie=[],Oe=UA();Oe!==r;)Ie.push(Oe),Oe=UA();return Ie!==r&&(Nt=ee,Ie=h(Ie)),ee=Ie,ee}function UA(){var ee,Ie,Oe,ht,mt,Dt,tr,fn,ai;if(ee=ve,Ie=Qn(),Ie===r&&(Ie=null),Ie!==r){if(Oe=ve,t.charCodeAt(ve)===35?(ht=E,ve++):(ht=r,Rt===0&&Xe(C)),ht!==r){if(mt=[],Dt=ve,tr=ve,Rt++,fn=st(),Rt--,fn===r?tr=void 0:(ve=tr,tr=r),tr!==r?(t.length>ve?(fn=t.charAt(ve),ve++):(fn=r,Rt===0&&Xe(S)),fn!==r?(tr=[tr,fn],Dt=tr):(ve=Dt,Dt=r)):(ve=Dt,Dt=r),Dt!==r)for(;Dt!==r;)mt.push(Dt),Dt=ve,tr=ve,Rt++,fn=st(),Rt--,fn===r?tr=void 0:(ve=tr,tr=r),tr!==r?(t.length>ve?(fn=t.charAt(ve),ve++):(fn=r,Rt===0&&Xe(S)),fn!==r?(tr=[tr,fn],Dt=tr):(ve=Dt,Dt=r)):(ve=Dt,Dt=r);else mt=r;mt!==r?(ht=[ht,mt],Oe=ht):(ve=Oe,Oe=r)}else ve=Oe,Oe=r;if(Oe===r&&(Oe=null),Oe!==r){if(ht=[],mt=Je(),mt!==r)for(;mt!==r;)ht.push(mt),mt=Je();else ht=r;ht!==r?(Nt=ee,Ie=P(),ee=Ie):(ve=ee,ee=r)}else ve=ee,ee=r}else ve=ee,ee=r;if(ee===r&&(ee=ve,Ie=vl(),Ie!==r?(Oe=ja(),Oe!==r?(ht=Qn(),ht===r&&(ht=null),ht!==r?(t.charCodeAt(ve)===58?(mt=I,ve++):(mt=r,Rt===0&&Xe(R)),mt!==r?(Dt=Qn(),Dt===r&&(Dt=null),Dt!==r?(tr=fa(),tr!==r?(Nt=ee,Ie=N(Oe,tr),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r),ee===r&&(ee=ve,Ie=vl(),Ie!==r?(Oe=ns(),Oe!==r?(ht=Qn(),ht===r&&(ht=null),ht!==r?(t.charCodeAt(ve)===58?(mt=I,ve++):(mt=r,Rt===0&&Xe(R)),mt!==r?(Dt=Qn(),Dt===r&&(Dt=null),Dt!==r?(tr=fa(),tr!==r?(Nt=ee,Ie=N(Oe,tr),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r),ee===r))){if(ee=ve,Ie=vl(),Ie!==r)if(Oe=ns(),Oe!==r)if(ht=Qn(),ht!==r)if(mt=gu(),mt!==r){if(Dt=[],tr=Je(),tr!==r)for(;tr!==r;)Dt.push(tr),tr=Je();else Dt=r;Dt!==r?(Nt=ee,Ie=N(Oe,mt),ee=Ie):(ve=ee,ee=r)}else ve=ee,ee=r;else ve=ee,ee=r;else ve=ee,ee=r;else ve=ee,ee=r;if(ee===r)if(ee=ve,Ie=vl(),Ie!==r)if(Oe=ns(),Oe!==r){if(ht=[],mt=ve,Dt=Qn(),Dt===r&&(Dt=null),Dt!==r?(t.charCodeAt(ve)===44?(tr=U,ve++):(tr=r,Rt===0&&Xe(W)),tr!==r?(fn=Qn(),fn===r&&(fn=null),fn!==r?(ai=ns(),ai!==r?(Nt=mt,Dt=te(Oe,ai),mt=Dt):(ve=mt,mt=r)):(ve=mt,mt=r)):(ve=mt,mt=r)):(ve=mt,mt=r),mt!==r)for(;mt!==r;)ht.push(mt),mt=ve,Dt=Qn(),Dt===r&&(Dt=null),Dt!==r?(t.charCodeAt(ve)===44?(tr=U,ve++):(tr=r,Rt===0&&Xe(W)),tr!==r?(fn=Qn(),fn===r&&(fn=null),fn!==r?(ai=ns(),ai!==r?(Nt=mt,Dt=te(Oe,ai),mt=Dt):(ve=mt,mt=r)):(ve=mt,mt=r)):(ve=mt,mt=r)):(ve=mt,mt=r);else ht=r;ht!==r?(mt=Qn(),mt===r&&(mt=null),mt!==r?(t.charCodeAt(ve)===58?(Dt=I,ve++):(Dt=r,Rt===0&&Xe(R)),Dt!==r?(tr=Qn(),tr===r&&(tr=null),tr!==r?(fn=fa(),fn!==r?(Nt=ee,Ie=ie(Oe,ht,fn),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)}else ve=ee,ee=r;else ve=ee,ee=r}return ee}function fa(){var ee,Ie,Oe,ht,mt,Dt,tr;if(ee=ve,Ie=ve,Rt++,Oe=ve,ht=st(),ht!==r?(mt=Mt(),mt!==r?(t.charCodeAt(ve)===45?(Dt=c,ve++):(Dt=r,Rt===0&&Xe(f)),Dt!==r?(tr=Qn(),tr!==r?(ht=[ht,mt,Dt,tr],Oe=ht):(ve=Oe,Oe=r)):(ve=Oe,Oe=r)):(ve=Oe,Oe=r)):(ve=Oe,Oe=r),Rt--,Oe!==r?(ve=Ie,Ie=void 0):Ie=r,Ie!==r?(Oe=Je(),Oe!==r?(ht=kn(),ht!==r?(mt=wn(),mt!==r?(Dt=Aa(),Dt!==r?(Nt=ee,Ie=Ae(mt),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r),ee===r&&(ee=ve,Ie=st(),Ie!==r?(Oe=kn(),Oe!==r?(ht=_A(),ht!==r?(mt=Aa(),mt!==r?(Nt=ee,Ie=Ae(ht),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r),ee===r))if(ee=ve,Ie=uc(),Ie!==r){if(Oe=[],ht=Je(),ht!==r)for(;ht!==r;)Oe.push(ht),ht=Je();else Oe=r;Oe!==r?(Nt=ee,Ie=ce(Ie),ee=Ie):(ve=ee,ee=r)}else ve=ee,ee=r;return ee}function vl(){var ee,Ie,Oe;for(Rt++,ee=ve,Ie=[],t.charCodeAt(ve)===32?(Oe=pe,ve++):(Oe=r,Rt===0&&Xe(Be));Oe!==r;)Ie.push(Oe),t.charCodeAt(ve)===32?(Oe=pe,ve++):(Oe=r,Rt===0&&Xe(Be));return Ie!==r?(Nt=ve,Oe=Ce(Ie),Oe?Oe=void 0:Oe=r,Oe!==r?(Ie=[Ie,Oe],ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r),Rt--,ee===r&&(Ie=r,Rt===0&&Xe(me)),ee}function Mt(){var ee,Ie,Oe;for(ee=ve,Ie=[],t.charCodeAt(ve)===32?(Oe=pe,ve++):(Oe=r,Rt===0&&Xe(Be));Oe!==r;)Ie.push(Oe),t.charCodeAt(ve)===32?(Oe=pe,ve++):(Oe=r,Rt===0&&Xe(Be));return Ie!==r?(Nt=ve,Oe=g(Ie),Oe?Oe=void 0:Oe=r,Oe!==r?(Ie=[Ie,Oe],ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r),ee}function kn(){var ee;return Nt=ve,ee=we(),ee?ee=void 0:ee=r,ee}function Aa(){var ee;return Nt=ve,ee=Ee(),ee?ee=void 0:ee=r,ee}function ja(){var ee;return ee=Sl(),ee===r&&(ee=fc()),ee}function ns(){var ee,Ie,Oe;if(ee=Sl(),ee===r){if(ee=ve,Ie=[],Oe=qa(),Oe!==r)for(;Oe!==r;)Ie.push(Oe),Oe=qa();else Ie=r;Ie!==r&&(Nt=ee,Ie=fe()),ee=Ie}return ee}function uc(){var ee;return ee=Li(),ee===r&&(ee=Cs(),ee===r&&(ee=Sl(),ee===r&&(ee=fc()))),ee}function gu(){var ee;return ee=Li(),ee===r&&(ee=Sl(),ee===r&&(ee=qa())),ee}function fc(){var ee,Ie,Oe,ht,mt,Dt;if(Rt++,ee=ve,X.test(t.charAt(ve))?(Ie=t.charAt(ve),ve++):(Ie=r,Rt===0&&Xe(De)),Ie!==r){for(Oe=[],ht=ve,mt=Qn(),mt===r&&(mt=null),mt!==r?(Re.test(t.charAt(ve))?(Dt=t.charAt(ve),ve++):(Dt=r,Rt===0&&Xe(gt)),Dt!==r?(mt=[mt,Dt],ht=mt):(ve=ht,ht=r)):(ve=ht,ht=r);ht!==r;)Oe.push(ht),ht=ve,mt=Qn(),mt===r&&(mt=null),mt!==r?(Re.test(t.charAt(ve))?(Dt=t.charAt(ve),ve++):(Dt=r,Rt===0&&Xe(gt)),Dt!==r?(mt=[mt,Dt],ht=mt):(ve=ht,ht=r)):(ve=ht,ht=r);Oe!==r?(Nt=ee,Ie=j(),ee=Ie):(ve=ee,ee=r)}else ve=ee,ee=r;return Rt--,ee===r&&(Ie=r,Rt===0&&Xe(se)),ee}function qa(){var ee,Ie,Oe,ht,mt;if(ee=ve,t.substr(ve,2)===rt?(Ie=rt,ve+=2):(Ie=r,Rt===0&&Xe(Fe)),Ie===r&&(Ie=null),Ie!==r)if(Ne.test(t.charAt(ve))?(Oe=t.charAt(ve),ve++):(Oe=r,Rt===0&&Xe(Pe)),Oe!==r){for(ht=[],Ye.test(t.charAt(ve))?(mt=t.charAt(ve),ve++):(mt=r,Rt===0&&Xe(ke));mt!==r;)ht.push(mt),Ye.test(t.charAt(ve))?(mt=t.charAt(ve),ve++):(mt=r,Rt===0&&Xe(ke));ht!==r?(Nt=ee,Ie=j(),ee=Ie):(ve=ee,ee=r)}else ve=ee,ee=r;else ve=ee,ee=r;return ee}function Li(){var ee,Ie;return ee=ve,t.substr(ve,4)===it?(Ie=it,ve+=4):(Ie=r,Rt===0&&Xe(_e)),Ie!==r&&(Nt=ee,Ie=x()),ee=Ie,ee}function Cs(){var ee,Ie;return ee=ve,t.substr(ve,4)===w?(Ie=w,ve+=4):(Ie=r,Rt===0&&Xe(b)),Ie!==r&&(Nt=ee,Ie=y()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,5)===F?(Ie=F,ve+=5):(Ie=r,Rt===0&&Xe(z)),Ie!==r&&(Nt=ee,Ie=Z()),ee=Ie),ee}function Sl(){var ee,Ie,Oe,ht;return Rt++,ee=ve,t.charCodeAt(ve)===34?(Ie=oe,ve++):(Ie=r,Rt===0&&Xe(xe)),Ie!==r?(t.charCodeAt(ve)===34?(Oe=oe,ve++):(Oe=r,Rt===0&&Xe(xe)),Oe!==r?(Nt=ee,Ie=Te(),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r),ee===r&&(ee=ve,t.charCodeAt(ve)===34?(Ie=oe,ve++):(Ie=r,Rt===0&&Xe(xe)),Ie!==r?(Oe=df(),Oe!==r?(t.charCodeAt(ve)===34?(ht=oe,ve++):(ht=r,Rt===0&&Xe(xe)),ht!==r?(Nt=ee,Ie=lt(Oe),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)),Rt--,ee===r&&(Ie=r,Rt===0&&Xe($)),ee}function df(){var ee,Ie,Oe;if(ee=ve,Ie=[],Oe=Ac(),Oe!==r)for(;Oe!==r;)Ie.push(Oe),Oe=Ac();else Ie=r;return Ie!==r&&(Nt=ee,Ie=Et(Ie)),ee=Ie,ee}function Ac(){var ee,Ie,Oe,ht,mt,Dt;return qt.test(t.charAt(ve))?(ee=t.charAt(ve),ve++):(ee=r,Rt===0&&Xe(ir)),ee===r&&(ee=ve,t.substr(ve,2)===Pt?(Ie=Pt,ve+=2):(Ie=r,Rt===0&&Xe(gn)),Ie!==r&&(Nt=ee,Ie=Pr()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===Ir?(Ie=Ir,ve+=2):(Ie=r,Rt===0&&Xe(Nr)),Ie!==r&&(Nt=ee,Ie=nn()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===oi?(Ie=oi,ve+=2):(Ie=r,Rt===0&&Xe(wo)),Ie!==r&&(Nt=ee,Ie=rs()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===eo?(Ie=eo,ve+=2):(Ie=r,Rt===0&&Xe(Bo)),Ie!==r&&(Nt=ee,Ie=Hi()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===to?(Ie=to,ve+=2):(Ie=r,Rt===0&&Xe(vo)),Ie!==r&&(Nt=ee,Ie=RA()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===pf?(Ie=pf,ve+=2):(Ie=r,Rt===0&&Xe(Eh)),Ie!==r&&(Nt=ee,Ie=Ih()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===ro?(Ie=ro,ve+=2):(Ie=r,Rt===0&&Xe(jn)),Ie!==r&&(Nt=ee,Ie=Rs()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===no?(Ie=no,ve+=2):(Ie=r,Rt===0&&Xe(lu)),Ie!==r&&(Nt=ee,Ie=cu()),ee=Ie,ee===r&&(ee=ve,t.substr(ve,2)===uu?(Ie=uu,ve+=2):(Ie=r,Rt===0&&Xe(FA)),Ie!==r?(Oe=wi(),Oe!==r?(ht=wi(),ht!==r?(mt=wi(),mt!==r?(Dt=wi(),Dt!==r?(Nt=ee,Ie=NA(Oe,ht,mt,Dt),ee=Ie):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)):(ve=ee,ee=r)))))))))),ee}function wi(){var ee;return aa.test(t.charAt(ve))?(ee=t.charAt(ve),ve++):(ee=r,Rt===0&&Xe(la)),ee}function Qn(){var ee,Ie;if(Rt++,ee=[],gr.test(t.charAt(ve))?(Ie=t.charAt(ve),ve++):(Ie=r,Rt===0&&Xe(So)),Ie!==r)for(;Ie!==r;)ee.push(Ie),gr.test(t.charAt(ve))?(Ie=t.charAt(ve),ve++):(Ie=r,Rt===0&&Xe(So));else ee=r;return Rt--,ee===r&&(Ie=r,Rt===0&&Xe(OA)),ee}function pc(){var ee,Ie;if(Rt++,ee=[],fu.test(t.charAt(ve))?(Ie=t.charAt(ve),ve++):(Ie=r,Rt===0&&Xe(Cr)),Ie!==r)for(;Ie!==r;)ee.push(Ie),fu.test(t.charAt(ve))?(Ie=t.charAt(ve),ve++):(Ie=r,Rt===0&&Xe(Cr));else ee=r;return Rt--,ee===r&&(Ie=r,Rt===0&&Xe(Me)),ee}function Je(){var ee,Ie,Oe,ht,mt,Dt;if(ee=ve,Ie=st(),Ie!==r){for(Oe=[],ht=ve,mt=Qn(),mt===r&&(mt=null),mt!==r?(Dt=st(),Dt!==r?(mt=[mt,Dt],ht=mt):(ve=ht,ht=r)):(ve=ht,ht=r);ht!==r;)Oe.push(ht),ht=ve,mt=Qn(),mt===r&&(mt=null),mt!==r?(Dt=st(),Dt!==r?(mt=[mt,Dt],ht=mt):(ve=ht,ht=r)):(ve=ht,ht=r);Oe!==r?(Ie=[Ie,Oe],ee=Ie):(ve=ee,ee=r)}else ve=ee,ee=r;return ee}function st(){var ee;return t.substr(ve,2)===hf?(ee=hf,ve+=2):(ee=r,Rt===0&&Xe(LA)),ee===r&&(t.charCodeAt(ve)===10?(ee=MA,ve++):(ee=r,Rt===0&&Xe(Au)),ee===r&&(t.charCodeAt(ve)===13?(ee=pu,ve++):(ee=r,Rt===0&&Xe(ac)))),ee}let St=2,lr=0;if(xn=a(),xn!==r&&ve===t.length)return xn;throw xn!==r&&ve"u"?!0:typeof t=="object"&&t!==null&&!Array.isArray(t)?Object.keys(t).every(e=>sne(t[e])):!1}function pU(t,e,r){if(t===null)return`null +`;if(typeof t=="number"||typeof t=="boolean")return`${t.toString()} +`;if(typeof t=="string")return`${nne(t)} +`;if(Array.isArray(t)){if(t.length===0)return`[] +`;let s=" ".repeat(e);return` +${t.map(n=>`${s}- ${pU(n,e+1,!1)}`).join("")}`}if(typeof t=="object"&&t){let[s,a]=t instanceof _x?[t.data,!1]:[t,!0],n=" ".repeat(e),c=Object.keys(s);a&&c.sort((p,h)=>{let E=rne.indexOf(p),C=rne.indexOf(h);return E===-1&&C===-1?ph?1:0:E!==-1&&C===-1?-1:E===-1&&C!==-1?1:E-C});let f=c.filter(p=>!sne(s[p])).map((p,h)=>{let E=s[p],C=nne(p),S=pU(E,e+1,!0),P=h>0||r?n:"",I=C.length>1024?`? ${C} +${P}:`:`${C}:`,R=S.startsWith(` +`)?S:` ${S}`;return`${P}${I}${R}`}).join(e===0?` +`:"")||` +`;return r?` +${f}`:`${f}`}throw new Error(`Unsupported value type (${t})`)}function il(t){try{let e=pU(t,0,!1);return e!==` +`?e:""}catch(e){throw e.location&&(e.message=e.message.replace(/(\.)?$/,` (line ${e.location.start.line}, column ${e.location.start.column})$1`)),e}}function rZe(t){return t.endsWith(` +`)||(t+=` +`),(0,ine.parse)(t)}function iZe(t){if(nZe.test(t))return rZe(t);let e=(0,Ux.safeLoad)(t,{schema:Ux.FAILSAFE_SCHEMA,json:!0});if(e==null)return{};if(typeof e!="object")throw new Error(`Expected an indexed object, got a ${typeof e} instead. Does your file follow Yaml's rules?`);if(Array.isArray(e))throw new Error("Expected an indexed object, got an array instead. Does your file follow Yaml's rules?");return e}function ls(t){return iZe(t)}var Ux,ine,tZe,rne,_x,nZe,one=It(()=>{Ux=et($re()),ine=et(tne()),tZe=/^(?![-?:,\][{}#&*!|>'"%@` \t\r\n]).([ \t]*(?![,\][{}:# \t\r\n]).)*$/,rne=["__metadata","version","resolution","dependencies","peerDependencies","dependenciesMeta","peerDependenciesMeta","binaries"],_x=class{constructor(e){this.data=e}};il.PreserveOrdering=_x;nZe=/^(#.*(\r?\n))*?#\s+yarn\s+lockfile\s+v1\r?\n/i});var Z2={};Vt(Z2,{parseResolution:()=>bx,parseShell:()=>vx,parseSyml:()=>ls,stringifyArgument:()=>$_,stringifyArgumentSegment:()=>eU,stringifyArithmeticExpression:()=>Dx,stringifyCommand:()=>X_,stringifyCommandChain:()=>AE,stringifyCommandChainThen:()=>Z_,stringifyCommandLine:()=>Sx,stringifyCommandLineThen:()=>z_,stringifyEnvSegment:()=>Bx,stringifyRedirectArgument:()=>G2,stringifyResolution:()=>Px,stringifyShell:()=>fE,stringifyShellLine:()=>fE,stringifySyml:()=>il,stringifyValueArgument:()=>Sd});var Bc=It(()=>{ste();cte();one()});var lne=L((S9t,hU)=>{"use strict";var sZe=t=>{let e=!1,r=!1,s=!1;for(let a=0;a{if(!(typeof t=="string"||Array.isArray(t)))throw new TypeError("Expected the input to be `string | string[]`");e=Object.assign({pascalCase:!1},e);let r=a=>e.pascalCase?a.charAt(0).toUpperCase()+a.slice(1):a;return Array.isArray(t)?t=t.map(a=>a.trim()).filter(a=>a.length).join("-"):t=t.trim(),t.length===0?"":t.length===1?e.pascalCase?t.toUpperCase():t.toLowerCase():(t!==t.toLowerCase()&&(t=sZe(t)),t=t.replace(/^[_.\- ]+/,"").toLowerCase().replace(/[_.\- ]+(\w|$)/g,(a,n)=>n.toUpperCase()).replace(/\d+(\w|$)/g,a=>a.toUpperCase()),r(t))};hU.exports=ane;hU.exports.default=ane});var cne=L((D9t,oZe)=>{oZe.exports=[{name:"Agola CI",constant:"AGOLA",env:"AGOLA_GIT_REF",pr:"AGOLA_PULL_REQUEST_ID"},{name:"Appcircle",constant:"APPCIRCLE",env:"AC_APPCIRCLE"},{name:"AppVeyor",constant:"APPVEYOR",env:"APPVEYOR",pr:"APPVEYOR_PULL_REQUEST_NUMBER"},{name:"AWS CodeBuild",constant:"CODEBUILD",env:"CODEBUILD_BUILD_ARN"},{name:"Azure Pipelines",constant:"AZURE_PIPELINES",env:"TF_BUILD",pr:{BUILD_REASON:"PullRequest"}},{name:"Bamboo",constant:"BAMBOO",env:"bamboo_planKey"},{name:"Bitbucket Pipelines",constant:"BITBUCKET",env:"BITBUCKET_COMMIT",pr:"BITBUCKET_PR_ID"},{name:"Bitrise",constant:"BITRISE",env:"BITRISE_IO",pr:"BITRISE_PULL_REQUEST"},{name:"Buddy",constant:"BUDDY",env:"BUDDY_WORKSPACE_ID",pr:"BUDDY_EXECUTION_PULL_REQUEST_ID"},{name:"Buildkite",constant:"BUILDKITE",env:"BUILDKITE",pr:{env:"BUILDKITE_PULL_REQUEST",ne:"false"}},{name:"CircleCI",constant:"CIRCLE",env:"CIRCLECI",pr:"CIRCLE_PULL_REQUEST"},{name:"Cirrus CI",constant:"CIRRUS",env:"CIRRUS_CI",pr:"CIRRUS_PR"},{name:"Codefresh",constant:"CODEFRESH",env:"CF_BUILD_ID",pr:{any:["CF_PULL_REQUEST_NUMBER","CF_PULL_REQUEST_ID"]}},{name:"Codemagic",constant:"CODEMAGIC",env:"CM_BUILD_ID",pr:"CM_PULL_REQUEST"},{name:"Codeship",constant:"CODESHIP",env:{CI_NAME:"codeship"}},{name:"Drone",constant:"DRONE",env:"DRONE",pr:{DRONE_BUILD_EVENT:"pull_request"}},{name:"dsari",constant:"DSARI",env:"DSARI"},{name:"Earthly",constant:"EARTHLY",env:"EARTHLY_CI"},{name:"Expo Application Services",constant:"EAS",env:"EAS_BUILD"},{name:"Gerrit",constant:"GERRIT",env:"GERRIT_PROJECT"},{name:"Gitea Actions",constant:"GITEA_ACTIONS",env:"GITEA_ACTIONS"},{name:"GitHub Actions",constant:"GITHUB_ACTIONS",env:"GITHUB_ACTIONS",pr:{GITHUB_EVENT_NAME:"pull_request"}},{name:"GitLab CI",constant:"GITLAB",env:"GITLAB_CI",pr:"CI_MERGE_REQUEST_ID"},{name:"GoCD",constant:"GOCD",env:"GO_PIPELINE_LABEL"},{name:"Google Cloud Build",constant:"GOOGLE_CLOUD_BUILD",env:"BUILDER_OUTPUT"},{name:"Harness CI",constant:"HARNESS",env:"HARNESS_BUILD_ID"},{name:"Heroku",constant:"HEROKU",env:{env:"NODE",includes:"/app/.heroku/node/bin/node"}},{name:"Hudson",constant:"HUDSON",env:"HUDSON_URL"},{name:"Jenkins",constant:"JENKINS",env:["JENKINS_URL","BUILD_ID"],pr:{any:["ghprbPullId","CHANGE_ID"]}},{name:"LayerCI",constant:"LAYERCI",env:"LAYERCI",pr:"LAYERCI_PULL_REQUEST"},{name:"Magnum CI",constant:"MAGNUM",env:"MAGNUM"},{name:"Netlify CI",constant:"NETLIFY",env:"NETLIFY",pr:{env:"PULL_REQUEST",ne:"false"}},{name:"Nevercode",constant:"NEVERCODE",env:"NEVERCODE",pr:{env:"NEVERCODE_PULL_REQUEST",ne:"false"}},{name:"Prow",constant:"PROW",env:"PROW_JOB_ID"},{name:"ReleaseHub",constant:"RELEASEHUB",env:"RELEASE_BUILD_ID"},{name:"Render",constant:"RENDER",env:"RENDER",pr:{IS_PULL_REQUEST:"true"}},{name:"Sail CI",constant:"SAIL",env:"SAILCI",pr:"SAIL_PULL_REQUEST_NUMBER"},{name:"Screwdriver",constant:"SCREWDRIVER",env:"SCREWDRIVER",pr:{env:"SD_PULL_REQUEST",ne:"false"}},{name:"Semaphore",constant:"SEMAPHORE",env:"SEMAPHORE",pr:"PULL_REQUEST_NUMBER"},{name:"Sourcehut",constant:"SOURCEHUT",env:{CI_NAME:"sourcehut"}},{name:"Strider CD",constant:"STRIDER",env:"STRIDER"},{name:"TaskCluster",constant:"TASKCLUSTER",env:["TASK_ID","RUN_ID"]},{name:"TeamCity",constant:"TEAMCITY",env:"TEAMCITY_VERSION"},{name:"Travis CI",constant:"TRAVIS",env:"TRAVIS",pr:{env:"TRAVIS_PULL_REQUEST",ne:"false"}},{name:"Vela",constant:"VELA",env:"VELA",pr:{VELA_PULL_REQUEST:"1"}},{name:"Vercel",constant:"VERCEL",env:{any:["NOW_BUILDER","VERCEL"]},pr:"VERCEL_GIT_PULL_REQUEST_ID"},{name:"Visual Studio App Center",constant:"APPCENTER",env:"APPCENTER_BUILD_ID"},{name:"Woodpecker",constant:"WOODPECKER",env:{CI:"woodpecker"},pr:{CI_BUILD_EVENT:"pull_request"}},{name:"Xcode Cloud",constant:"XCODE_CLOUD",env:"CI_XCODE_PROJECT",pr:"CI_PULL_REQUEST_NUMBER"},{name:"Xcode Server",constant:"XCODE_SERVER",env:"XCS"}]});var Nd=L(_l=>{"use strict";var fne=cne(),bs=process.env;Object.defineProperty(_l,"_vendors",{value:fne.map(function(t){return t.constant})});_l.name=null;_l.isPR=null;fne.forEach(function(t){let r=(Array.isArray(t.env)?t.env:[t.env]).every(function(s){return une(s)});if(_l[t.constant]=r,!!r)switch(_l.name=t.name,typeof t.pr){case"string":_l.isPR=!!bs[t.pr];break;case"object":"env"in t.pr?_l.isPR=t.pr.env in bs&&bs[t.pr.env]!==t.pr.ne:"any"in t.pr?_l.isPR=t.pr.any.some(function(s){return!!bs[s]}):_l.isPR=une(t.pr);break;default:_l.isPR=null}});_l.isCI=!!(bs.CI!=="false"&&(bs.BUILD_ID||bs.BUILD_NUMBER||bs.CI||bs.CI_APP_ID||bs.CI_BUILD_ID||bs.CI_BUILD_NUMBER||bs.CI_NAME||bs.CONTINUOUS_INTEGRATION||bs.RUN_ID||_l.name));function une(t){return typeof t=="string"?!!bs[t]:"env"in t?bs[t.env]&&bs[t.env].includes(t.includes):"any"in t?t.any.some(function(e){return!!bs[e]}):Object.keys(t).every(function(e){return bs[e]===t[e]})}});var ei,En,Od,gU,Hx,Ane,dU,mU,jx=It(()=>{(function(t){t.StartOfInput="\0",t.EndOfInput="",t.EndOfPartialInput=""})(ei||(ei={}));(function(t){t[t.InitialNode=0]="InitialNode",t[t.SuccessNode=1]="SuccessNode",t[t.ErrorNode=2]="ErrorNode",t[t.CustomNode=3]="CustomNode"})(En||(En={}));Od=-1,gU=/^(-h|--help)(?:=([0-9]+))?$/,Hx=/^(--[a-z]+(?:-[a-z]+)*|-[a-zA-Z]+)$/,Ane=/^-[a-zA-Z]{2,}$/,dU=/^([^=]+)=([\s\S]*)$/,mU=process.env.DEBUG_CLI==="1"});var nt,IE,qx,yU,Gx=It(()=>{jx();nt=class extends Error{constructor(e){super(e),this.clipanion={type:"usage"},this.name="UsageError"}},IE=class extends Error{constructor(e,r){if(super(),this.input=e,this.candidates=r,this.clipanion={type:"none"},this.name="UnknownSyntaxError",this.candidates.length===0)this.message="Command not found, but we're not sure what's the alternative.";else if(this.candidates.every(s=>s.reason!==null&&s.reason===r[0].reason)){let[{reason:s}]=this.candidates;this.message=`${s} + +${this.candidates.map(({usage:a})=>`$ ${a}`).join(` +`)}`}else if(this.candidates.length===1){let[{usage:s}]=this.candidates;this.message=`Command not found; did you mean: + +$ ${s} +${yU(e)}`}else this.message=`Command not found; did you mean one of: + +${this.candidates.map(({usage:s},a)=>`${`${a}.`.padStart(4)} ${s}`).join(` +`)} + +${yU(e)}`}},qx=class extends Error{constructor(e,r){super(),this.input=e,this.usages=r,this.clipanion={type:"none"},this.name="AmbiguousSyntaxError",this.message=`Cannot find which to pick amongst the following alternatives: + +${this.usages.map((s,a)=>`${`${a}.`.padStart(4)} ${s}`).join(` +`)} + +${yU(e)}`}},yU=t=>`While running ${t.filter(e=>e!==ei.EndOfInput&&e!==ei.EndOfPartialInput).map(e=>{let r=JSON.stringify(e);return e.match(/\s/)||e.length===0||r!==`"${e}"`?r:e}).join(" ")}`});function aZe(t){let e=t.split(` +`),r=e.filter(a=>a.match(/\S/)),s=r.length>0?r.reduce((a,n)=>Math.min(a,n.length-n.trimStart().length),Number.MAX_VALUE):0;return e.map(a=>a.slice(s).trimRight()).join(` +`)}function qo(t,{format:e,paragraphs:r}){return t=t.replace(/\r\n?/g,` +`),t=aZe(t),t=t.replace(/^\n+|\n+$/g,""),t=t.replace(/^(\s*)-([^\n]*?)\n+/gm,`$1-$2 + +`),t=t.replace(/\n(\n)?\n*/g,(s,a)=>a||" "),r&&(t=t.split(/\n/).map(s=>{let a=s.match(/^\s*[*-][\t ]+(.*)/);if(!a)return s.match(/(.{1,80})(?: |$)/g).join(` +`);let n=s.length-s.trimStart().length;return a[1].match(new RegExp(`(.{1,${78-n}})(?: |$)`,"g")).map((c,f)=>" ".repeat(n)+(f===0?"- ":" ")+c).join(` +`)}).join(` + +`)),t=t.replace(/(`+)((?:.|[\n])*?)\1/g,(s,a,n)=>e.code(a+n+a)),t=t.replace(/(\*\*)((?:.|[\n])*?)\1/g,(s,a,n)=>e.bold(a+n+a)),t?`${t} +`:""}var EU,pne,hne,IU=It(()=>{EU=Array(80).fill("\u2501");for(let t=0;t<=24;++t)EU[EU.length-t]=`\x1B[38;5;${232+t}m\u2501`;pne={header:t=>`\x1B[1m\u2501\u2501\u2501 ${t}${t.length<75?` ${EU.slice(t.length+5).join("")}`:":"}\x1B[0m`,bold:t=>`\x1B[1m${t}\x1B[22m`,error:t=>`\x1B[31m\x1B[1m${t}\x1B[22m\x1B[39m`,code:t=>`\x1B[36m${t}\x1B[39m`},hne={header:t=>t,bold:t=>t,error:t=>t,code:t=>t}});function Ea(t){return{...t,[X2]:!0}}function Gf(t,e){return typeof t>"u"?[t,e]:typeof t=="object"&&t!==null&&!Array.isArray(t)?[void 0,t]:[t,e]}function Wx(t,{mergeName:e=!1}={}){let r=t.match(/^([^:]+): (.*)$/m);if(!r)return"validation failed";let[,s,a]=r;return e&&(a=a[0].toLowerCase()+a.slice(1)),a=s!=="."||!e?`${s.replace(/^\.(\[|$)/,"$1")}: ${a}`:`: ${a}`,a}function $2(t,e){return e.length===1?new nt(`${t}${Wx(e[0],{mergeName:!0})}`):new nt(`${t}: +${e.map(r=>` +- ${Wx(r)}`).join("")}`)}function Ld(t,e,r){if(typeof r>"u")return e;let s=[],a=[],n=f=>{let p=e;return e=f,n.bind(null,p)};if(!r(e,{errors:s,coercions:a,coercion:n}))throw $2(`Invalid value for ${t}`,s);for(let[,f]of a)f();return e}var X2,Bp=It(()=>{Gx();X2=Symbol("clipanion/isOption")});var Ia={};Vt(Ia,{KeyRelationship:()=>Wf,TypeAssertionError:()=>c0,applyCascade:()=>rB,as:()=>DZe,assert:()=>BZe,assertWithErrors:()=>vZe,cascade:()=>Jx,fn:()=>bZe,hasAtLeastOneKey:()=>bU,hasExactLength:()=>Ene,hasForbiddenKeys:()=>YZe,hasKeyRelationship:()=>iB,hasMaxLength:()=>xZe,hasMinLength:()=>PZe,hasMutuallyExclusiveKeys:()=>VZe,hasRequiredKeys:()=>WZe,hasUniqueItems:()=>kZe,isArray:()=>Yx,isAtLeast:()=>SU,isAtMost:()=>RZe,isBase64:()=>HZe,isBoolean:()=>gZe,isDate:()=>mZe,isDict:()=>IZe,isEnum:()=>Ao,isHexColor:()=>UZe,isISO8601:()=>_Ze,isInExclusiveRange:()=>NZe,isInInclusiveRange:()=>FZe,isInstanceOf:()=>wZe,isInteger:()=>DU,isJSON:()=>jZe,isLiteral:()=>dne,isLowerCase:()=>OZe,isMap:()=>EZe,isNegative:()=>QZe,isNullable:()=>GZe,isNumber:()=>BU,isObject:()=>mne,isOneOf:()=>vU,isOptional:()=>qZe,isPartial:()=>CZe,isPayload:()=>dZe,isPositive:()=>TZe,isRecord:()=>Kx,isSet:()=>yZe,isString:()=>wE,isTuple:()=>Vx,isUUID4:()=>MZe,isUnknown:()=>wU,isUpperCase:()=>LZe,makeTrait:()=>yne,makeValidator:()=>Wr,matchesRegExp:()=>tB,softAssert:()=>SZe});function ti(t){return t===null?"null":t===void 0?"undefined":t===""?"an empty string":typeof t=="symbol"?`<${t.toString()}>`:Array.isArray(t)?"an array":JSON.stringify(t)}function CE(t,e){if(t.length===0)return"nothing";if(t.length===1)return ti(t[0]);let r=t.slice(0,-1),s=t[t.length-1],a=t.length>2?`, ${e} `:` ${e} `;return`${r.map(n=>ti(n)).join(", ")}${a}${ti(s)}`}function l0(t,e){var r,s,a;return typeof e=="number"?`${(r=t?.p)!==null&&r!==void 0?r:"."}[${e}]`:lZe.test(e)?`${(s=t?.p)!==null&&s!==void 0?s:""}.${e}`:`${(a=t?.p)!==null&&a!==void 0?a:"."}[${JSON.stringify(e)}]`}function CU(t,e,r){return t===1?e:r}function mr({errors:t,p:e}={},r){return t?.push(`${e??"."}: ${r}`),!1}function pZe(t,e){return r=>{t[e]=r}}function Yf(t,e){return r=>{let s=t[e];return t[e]=r,Yf(t,e).bind(null,s)}}function eB(t,e,r){let s=()=>(t(r()),a),a=()=>(t(e),s);return s}function wU(){return Wr({test:(t,e)=>!0})}function dne(t){return Wr({test:(e,r)=>e!==t?mr(r,`Expected ${ti(t)} (got ${ti(e)})`):!0})}function wE(){return Wr({test:(t,e)=>typeof t!="string"?mr(e,`Expected a string (got ${ti(t)})`):!0})}function Ao(t){let e=Array.isArray(t)?t:Object.values(t),r=e.every(a=>typeof a=="string"||typeof a=="number"),s=new Set(e);return s.size===1?dne([...s][0]):Wr({test:(a,n)=>s.has(a)?!0:r?mr(n,`Expected one of ${CE(e,"or")} (got ${ti(a)})`):mr(n,`Expected a valid enumeration value (got ${ti(a)})`)})}function gZe(){return Wr({test:(t,e)=>{var r;if(typeof t!="boolean"){if(typeof e?.coercions<"u"){if(typeof e?.coercion>"u")return mr(e,"Unbound coercion result");let s=hZe.get(t);if(typeof s<"u")return e.coercions.push([(r=e.p)!==null&&r!==void 0?r:".",e.coercion.bind(null,s)]),!0}return mr(e,`Expected a boolean (got ${ti(t)})`)}return!0}})}function BU(){return Wr({test:(t,e)=>{var r;if(typeof t!="number"){if(typeof e?.coercions<"u"){if(typeof e?.coercion>"u")return mr(e,"Unbound coercion result");let s;if(typeof t=="string"){let a;try{a=JSON.parse(t)}catch{}if(typeof a=="number")if(JSON.stringify(a)===t)s=a;else return mr(e,`Received a number that can't be safely represented by the runtime (${t})`)}if(typeof s<"u")return e.coercions.push([(r=e.p)!==null&&r!==void 0?r:".",e.coercion.bind(null,s)]),!0}return mr(e,`Expected a number (got ${ti(t)})`)}return!0}})}function dZe(t){return Wr({test:(e,r)=>{var s;if(typeof r?.coercions>"u")return mr(r,"The isPayload predicate can only be used with coercion enabled");if(typeof r.coercion>"u")return mr(r,"Unbound coercion result");if(typeof e!="string")return mr(r,`Expected a string (got ${ti(e)})`);let a;try{a=JSON.parse(e)}catch{return mr(r,`Expected a JSON string (got ${ti(e)})`)}let n={value:a};return t(a,Object.assign(Object.assign({},r),{coercion:Yf(n,"value")}))?(r.coercions.push([(s=r.p)!==null&&s!==void 0?s:".",r.coercion.bind(null,n.value)]),!0):!1}})}function mZe(){return Wr({test:(t,e)=>{var r;if(!(t instanceof Date)){if(typeof e?.coercions<"u"){if(typeof e?.coercion>"u")return mr(e,"Unbound coercion result");let s;if(typeof t=="string"&&gne.test(t))s=new Date(t);else{let a;if(typeof t=="string"){let n;try{n=JSON.parse(t)}catch{}typeof n=="number"&&(a=n)}else typeof t=="number"&&(a=t);if(typeof a<"u")if(Number.isSafeInteger(a)||!Number.isSafeInteger(a*1e3))s=new Date(a*1e3);else return mr(e,`Received a timestamp that can't be safely represented by the runtime (${t})`)}if(typeof s<"u")return e.coercions.push([(r=e.p)!==null&&r!==void 0?r:".",e.coercion.bind(null,s)]),!0}return mr(e,`Expected a date (got ${ti(t)})`)}return!0}})}function Yx(t,{delimiter:e}={}){return Wr({test:(r,s)=>{var a;let n=r;if(typeof r=="string"&&typeof e<"u"&&typeof s?.coercions<"u"){if(typeof s?.coercion>"u")return mr(s,"Unbound coercion result");r=r.split(e)}if(!Array.isArray(r))return mr(s,`Expected an array (got ${ti(r)})`);let c=!0;for(let f=0,p=r.length;f{var n,c;if(Object.getPrototypeOf(s).toString()==="[object Set]")if(typeof a?.coercions<"u"){if(typeof a?.coercion>"u")return mr(a,"Unbound coercion result");let f=[...s],p=[...s];if(!r(p,Object.assign(Object.assign({},a),{coercion:void 0})))return!1;let h=()=>p.some((E,C)=>E!==f[C])?new Set(p):s;return a.coercions.push([(n=a.p)!==null&&n!==void 0?n:".",eB(a.coercion,s,h)]),!0}else{let f=!0;for(let p of s)if(f=t(p,Object.assign({},a))&&f,!f&&a?.errors==null)break;return f}if(typeof a?.coercions<"u"){if(typeof a?.coercion>"u")return mr(a,"Unbound coercion result");let f={value:s};return r(s,Object.assign(Object.assign({},a),{coercion:Yf(f,"value")}))?(a.coercions.push([(c=a.p)!==null&&c!==void 0?c:".",eB(a.coercion,s,()=>new Set(f.value))]),!0):!1}return mr(a,`Expected a set (got ${ti(s)})`)}})}function EZe(t,e){let r=Yx(Vx([t,e])),s=Kx(e,{keys:t});return Wr({test:(a,n)=>{var c,f,p;if(Object.getPrototypeOf(a).toString()==="[object Map]")if(typeof n?.coercions<"u"){if(typeof n?.coercion>"u")return mr(n,"Unbound coercion result");let h=[...a],E=[...a];if(!r(E,Object.assign(Object.assign({},n),{coercion:void 0})))return!1;let C=()=>E.some((S,P)=>S[0]!==h[P][0]||S[1]!==h[P][1])?new Map(E):a;return n.coercions.push([(c=n.p)!==null&&c!==void 0?c:".",eB(n.coercion,a,C)]),!0}else{let h=!0;for(let[E,C]of a)if(h=t(E,Object.assign({},n))&&h,!h&&n?.errors==null||(h=e(C,Object.assign(Object.assign({},n),{p:l0(n,E)}))&&h,!h&&n?.errors==null))break;return h}if(typeof n?.coercions<"u"){if(typeof n?.coercion>"u")return mr(n,"Unbound coercion result");let h={value:a};return Array.isArray(a)?r(a,Object.assign(Object.assign({},n),{coercion:void 0}))?(n.coercions.push([(f=n.p)!==null&&f!==void 0?f:".",eB(n.coercion,a,()=>new Map(h.value))]),!0):!1:s(a,Object.assign(Object.assign({},n),{coercion:Yf(h,"value")}))?(n.coercions.push([(p=n.p)!==null&&p!==void 0?p:".",eB(n.coercion,a,()=>new Map(Object.entries(h.value)))]),!0):!1}return mr(n,`Expected a map (got ${ti(a)})`)}})}function Vx(t,{delimiter:e}={}){let r=Ene(t.length);return Wr({test:(s,a)=>{var n;if(typeof s=="string"&&typeof e<"u"&&typeof a?.coercions<"u"){if(typeof a?.coercion>"u")return mr(a,"Unbound coercion result");s=s.split(e),a.coercions.push([(n=a.p)!==null&&n!==void 0?n:".",a.coercion.bind(null,s)])}if(!Array.isArray(s))return mr(a,`Expected a tuple (got ${ti(s)})`);let c=r(s,Object.assign({},a));for(let f=0,p=s.length;f{var n;if(Array.isArray(s)&&typeof a?.coercions<"u")return typeof a?.coercion>"u"?mr(a,"Unbound coercion result"):r(s,Object.assign(Object.assign({},a),{coercion:void 0}))?(s=Object.fromEntries(s),a.coercions.push([(n=a.p)!==null&&n!==void 0?n:".",a.coercion.bind(null,s)]),!0):!1;if(typeof s!="object"||s===null)return mr(a,`Expected an object (got ${ti(s)})`);let c=Object.keys(s),f=!0;for(let p=0,h=c.length;p{if(typeof a!="object"||a===null)return mr(n,`Expected an object (got ${ti(a)})`);let c=new Set([...r,...Object.keys(a)]),f={},p=!0;for(let h of c){if(h==="constructor"||h==="__proto__")p=mr(Object.assign(Object.assign({},n),{p:l0(n,h)}),"Unsafe property name");else{let E=Object.prototype.hasOwnProperty.call(t,h)?t[h]:void 0,C=Object.prototype.hasOwnProperty.call(a,h)?a[h]:void 0;typeof E<"u"?p=E(C,Object.assign(Object.assign({},n),{p:l0(n,h),coercion:Yf(a,h)}))&&p:e===null?p=mr(Object.assign(Object.assign({},n),{p:l0(n,h)}),`Extraneous property (got ${ti(C)})`):Object.defineProperty(f,h,{enumerable:!0,get:()=>C,set:pZe(a,h)})}if(!p&&n?.errors==null)break}return e!==null&&(p||n?.errors!=null)&&(p=e(f,n)&&p),p}});return Object.assign(s,{properties:t})}function CZe(t){return mne(t,{extra:Kx(wU())})}function yne(t){return()=>t}function Wr({test:t}){return yne(t)()}function BZe(t,e){if(!e(t))throw new c0}function vZe(t,e){let r=[];if(!e(t,{errors:r}))throw new c0({errors:r})}function SZe(t,e){}function DZe(t,e,{coerce:r=!1,errors:s,throw:a}={}){let n=s?[]:void 0;if(!r){if(e(t,{errors:n}))return a?t:{value:t,errors:void 0};if(a)throw new c0({errors:n});return{value:void 0,errors:n??!0}}let c={value:t},f=Yf(c,"value"),p=[];if(!e(t,{errors:n,coercion:f,coercions:p})){if(a)throw new c0({errors:n});return{value:void 0,errors:n??!0}}for(let[,h]of p)h();return a?c.value:{value:c.value,errors:void 0}}function bZe(t,e){let r=Vx(t);return(...s)=>{if(!r(s))throw new c0;return e(...s)}}function PZe(t){return Wr({test:(e,r)=>e.length>=t?!0:mr(r,`Expected to have a length of at least ${t} elements (got ${e.length})`)})}function xZe(t){return Wr({test:(e,r)=>e.length<=t?!0:mr(r,`Expected to have a length of at most ${t} elements (got ${e.length})`)})}function Ene(t){return Wr({test:(e,r)=>e.length!==t?mr(r,`Expected to have a length of exactly ${t} elements (got ${e.length})`):!0})}function kZe({map:t}={}){return Wr({test:(e,r)=>{let s=new Set,a=new Set;for(let n=0,c=e.length;nt<=0?!0:mr(e,`Expected to be negative (got ${t})`)})}function TZe(){return Wr({test:(t,e)=>t>=0?!0:mr(e,`Expected to be positive (got ${t})`)})}function SU(t){return Wr({test:(e,r)=>e>=t?!0:mr(r,`Expected to be at least ${t} (got ${e})`)})}function RZe(t){return Wr({test:(e,r)=>e<=t?!0:mr(r,`Expected to be at most ${t} (got ${e})`)})}function FZe(t,e){return Wr({test:(r,s)=>r>=t&&r<=e?!0:mr(s,`Expected to be in the [${t}; ${e}] range (got ${r})`)})}function NZe(t,e){return Wr({test:(r,s)=>r>=t&&re!==Math.round(e)?mr(r,`Expected to be an integer (got ${e})`):!t&&!Number.isSafeInteger(e)?mr(r,`Expected to be a safe integer (got ${e})`):!0})}function tB(t){return Wr({test:(e,r)=>t.test(e)?!0:mr(r,`Expected to match the pattern ${t.toString()} (got ${ti(e)})`)})}function OZe(){return Wr({test:(t,e)=>t!==t.toLowerCase()?mr(e,`Expected to be all-lowercase (got ${t})`):!0})}function LZe(){return Wr({test:(t,e)=>t!==t.toUpperCase()?mr(e,`Expected to be all-uppercase (got ${t})`):!0})}function MZe(){return Wr({test:(t,e)=>AZe.test(t)?!0:mr(e,`Expected to be a valid UUID v4 (got ${ti(t)})`)})}function _Ze(){return Wr({test:(t,e)=>gne.test(t)?!0:mr(e,`Expected to be a valid ISO 8601 date string (got ${ti(t)})`)})}function UZe({alpha:t=!1}){return Wr({test:(e,r)=>(t?cZe.test(e):uZe.test(e))?!0:mr(r,`Expected to be a valid hexadecimal color string (got ${ti(e)})`)})}function HZe(){return Wr({test:(t,e)=>fZe.test(t)?!0:mr(e,`Expected to be a valid base 64 string (got ${ti(t)})`)})}function jZe(t=wU()){return Wr({test:(e,r)=>{let s;try{s=JSON.parse(e)}catch{return mr(r,`Expected to be a valid JSON string (got ${ti(e)})`)}return t(s,r)}})}function Jx(t,...e){let r=Array.isArray(e[0])?e[0]:e;return Wr({test:(s,a)=>{var n,c;let f={value:s},p=typeof a?.coercions<"u"?Yf(f,"value"):void 0,h=typeof a?.coercions<"u"?[]:void 0;if(!t(s,Object.assign(Object.assign({},a),{coercion:p,coercions:h})))return!1;let E=[];if(typeof h<"u")for(let[,C]of h)E.push(C());try{if(typeof a?.coercions<"u"){if(f.value!==s){if(typeof a?.coercion>"u")return mr(a,"Unbound coercion result");a.coercions.push([(n=a.p)!==null&&n!==void 0?n:".",a.coercion.bind(null,f.value)])}(c=a?.coercions)===null||c===void 0||c.push(...h)}return r.every(C=>C(f.value,a))}finally{for(let C of E)C()}}})}function rB(t,...e){let r=Array.isArray(e[0])?e[0]:e;return Jx(t,r)}function qZe(t){return Wr({test:(e,r)=>typeof e>"u"?!0:t(e,r)})}function GZe(t){return Wr({test:(e,r)=>e===null?!0:t(e,r)})}function WZe(t,e){var r;let s=new Set(t),a=nB[(r=e?.missingIf)!==null&&r!==void 0?r:"missing"];return Wr({test:(n,c)=>{let f=new Set(Object.keys(n)),p=[];for(let h of s)a(f,h,n)||p.push(h);return p.length>0?mr(c,`Missing required ${CU(p.length,"property","properties")} ${CE(p,"and")}`):!0}})}function bU(t,e){var r;let s=new Set(t),a=nB[(r=e?.missingIf)!==null&&r!==void 0?r:"missing"];return Wr({test:(n,c)=>Object.keys(n).some(h=>a(s,h,n))?!0:mr(c,`Missing at least one property from ${CE(Array.from(s),"or")}`)})}function YZe(t,e){var r;let s=new Set(t),a=nB[(r=e?.missingIf)!==null&&r!==void 0?r:"missing"];return Wr({test:(n,c)=>{let f=new Set(Object.keys(n)),p=[];for(let h of s)a(f,h,n)&&p.push(h);return p.length>0?mr(c,`Forbidden ${CU(p.length,"property","properties")} ${CE(p,"and")}`):!0}})}function VZe(t,e){var r;let s=new Set(t),a=nB[(r=e?.missingIf)!==null&&r!==void 0?r:"missing"];return Wr({test:(n,c)=>{let f=new Set(Object.keys(n)),p=[];for(let h of s)a(f,h,n)&&p.push(h);return p.length>1?mr(c,`Mutually exclusive properties ${CE(p,"and")}`):!0}})}function iB(t,e,r,s){var a,n;let c=new Set((a=s?.ignore)!==null&&a!==void 0?a:[]),f=nB[(n=s?.missingIf)!==null&&n!==void 0?n:"missing"],p=new Set(r),h=KZe[e],E=e===Wf.Forbids?"or":"and";return Wr({test:(C,S)=>{let P=new Set(Object.keys(C));if(!f(P,t,C)||c.has(C[t]))return!0;let I=[];for(let R of p)(f(P,R,C)&&!c.has(C[R]))!==h.expect&&I.push(R);return I.length>=1?mr(S,`Property "${t}" ${h.message} ${CU(I.length,"property","properties")} ${CE(I,E)}`):!0}})}var lZe,cZe,uZe,fZe,AZe,gne,hZe,wZe,vU,c0,nB,Wf,KZe,Ul=It(()=>{lZe=/^[a-zA-Z_][a-zA-Z0-9_]*$/;cZe=/^#[0-9a-f]{6}$/i,uZe=/^#[0-9a-f]{6}([0-9a-f]{2})?$/i,fZe=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/,AZe=/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$/i,gne=/^(?:[1-9]\d{3}(-?)(?:(?:0[1-9]|1[0-2])\1(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])\1(?:29|30)|(?:0[13578]|1[02])(?:\1)31|00[1-9]|0[1-9]\d|[12]\d{2}|3(?:[0-5]\d|6[0-5]))|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)(?:(-?)02(?:\2)29|-?366))T(?:[01]\d|2[0-3])(:?)[0-5]\d(?:\3[0-5]\d)?(?:Z|[+-][01]\d(?:\3[0-5]\d)?)$/;hZe=new Map([["true",!0],["True",!0],["1",!0],[1,!0],["false",!1],["False",!1],["0",!1],[0,!1]]);wZe=t=>Wr({test:(e,r)=>e instanceof t?!0:mr(r,`Expected an instance of ${t.name} (got ${ti(e)})`)}),vU=(t,{exclusive:e=!1}={})=>Wr({test:(r,s)=>{var a,n,c;let f=[],p=typeof s?.errors<"u"?[]:void 0;for(let h=0,E=t.length;h1?mr(s,`Expected to match exactly a single predicate (matched ${f.join(", ")})`):(c=s?.errors)===null||c===void 0||c.push(...p),!1}});c0=class extends Error{constructor({errors:e}={}){let r="Type mismatch";if(e&&e.length>0){r+=` +`;for(let s of e)r+=` +- ${s}`}super(r)}};nB={missing:(t,e)=>t.has(e),undefined:(t,e,r)=>t.has(e)&&typeof r[e]<"u",nil:(t,e,r)=>t.has(e)&&r[e]!=null,falsy:(t,e,r)=>t.has(e)&&!!r[e]};(function(t){t.Forbids="Forbids",t.Requires="Requires"})(Wf||(Wf={}));KZe={[Wf.Forbids]:{expect:!1,message:"forbids using"},[Wf.Requires]:{expect:!0,message:"requires using"}}});var ot,u0=It(()=>{Bp();ot=class{constructor(){this.help=!1}static Usage(e){return e}async catch(e){throw e}async validateAndExecute(){let r=this.constructor.schema;if(Array.isArray(r)){let{isDict:a,isUnknown:n,applyCascade:c}=await Promise.resolve().then(()=>(Ul(),Ia)),f=c(a(n()),r),p=[],h=[];if(!f(this,{errors:p,coercions:h}))throw $2("Invalid option schema",p);for(let[,C]of h)C()}else if(r!=null)throw new Error("Invalid command schema");let s=await this.execute();return typeof s<"u"?s:0}};ot.isOption=X2;ot.Default=[]});function sl(t){mU&&console.log(t)}function Cne(){let t={nodes:[]};for(let e=0;e{if(e.has(s))return;e.add(s);let a=t.nodes[s];for(let c of Object.values(a.statics))for(let{to:f}of c)r(f);for(let[,{to:c}]of a.dynamics)r(c);for(let{to:c}of a.shortcuts)r(c);let n=new Set(a.shortcuts.map(({to:c})=>c));for(;a.shortcuts.length>0;){let{to:c}=a.shortcuts.shift(),f=t.nodes[c];for(let[p,h]of Object.entries(f.statics)){let E=Object.prototype.hasOwnProperty.call(a.statics,p)?a.statics[p]:a.statics[p]=[];for(let C of h)E.some(({to:S})=>C.to===S)||E.push(C)}for(let[p,h]of f.dynamics)a.dynamics.some(([E,{to:C}])=>p===E&&h.to===C)||a.dynamics.push([p,h]);for(let p of f.shortcuts)n.has(p.to)||(a.shortcuts.push(p),n.add(p.to))}};r(En.InitialNode)}function ZZe(t,{prefix:e=""}={}){if(mU){sl(`${e}Nodes are:`);for(let r=0;rE!==En.ErrorNode).map(({state:E})=>({usage:E.candidateUsage,reason:null})));if(h.every(({node:E})=>E===En.ErrorNode))throw new IE(e,h.map(({state:E})=>({usage:E.candidateUsage,reason:E.errorMessage})));s=eXe(h)}if(s.length>0){sl(" Results:");for(let n of s)sl(` - ${n.node} -> ${JSON.stringify(n.state)}`)}else sl(" No results");return s}function $Ze(t,e,{endToken:r=ei.EndOfInput}={}){let s=XZe(t,[...e,r]);return tXe(e,s.map(({state:a})=>a))}function eXe(t){let e=0;for(let{state:r}of t)r.path.length>e&&(e=r.path.length);return t.filter(({state:r})=>r.path.length===e)}function tXe(t,e){let r=e.filter(S=>S.selectedIndex!==null),s=r.filter(S=>!S.partial);if(s.length>0&&(r=s),r.length===0)throw new Error;let a=r.filter(S=>S.selectedIndex===Od||S.requiredOptions.every(P=>P.some(I=>S.options.find(R=>R.name===I))));if(a.length===0)throw new IE(t,r.map(S=>({usage:S.candidateUsage,reason:null})));let n=0;for(let S of a)S.path.length>n&&(n=S.path.length);let c=a.filter(S=>S.path.length===n),f=S=>S.positionals.filter(({extra:P})=>!P).length+S.options.length,p=c.map(S=>({state:S,positionalCount:f(S)})),h=0;for(let{positionalCount:S}of p)S>h&&(h=S);let E=p.filter(({positionalCount:S})=>S===h).map(({state:S})=>S),C=rXe(E);if(C.length>1)throw new qx(t,C.map(S=>S.candidateUsage));return C[0]}function rXe(t){let e=[],r=[];for(let s of t)s.selectedIndex===Od?r.push(s):e.push(s);return r.length>0&&e.push({...Ine,path:wne(...r.map(s=>s.path)),options:r.reduce((s,a)=>s.concat(a.options),[])}),e}function wne(t,e,...r){return e===void 0?Array.from(t):wne(t.filter((s,a)=>s===e[a]),...r)}function Hl(){return{dynamics:[],shortcuts:[],statics:{}}}function Bne(t){return t===En.SuccessNode||t===En.ErrorNode}function PU(t,e=0){return{to:Bne(t.to)?t.to:t.to>=En.CustomNode?t.to+e-En.CustomNode+1:t.to+e,reducer:t.reducer}}function nXe(t,e=0){let r=Hl();for(let[s,a]of t.dynamics)r.dynamics.push([s,PU(a,e)]);for(let s of t.shortcuts)r.shortcuts.push(PU(s,e));for(let[s,a]of Object.entries(t.statics))r.statics[s]=a.map(n=>PU(n,e));return r}function js(t,e,r,s,a){t.nodes[e].dynamics.push([r,{to:s,reducer:a}])}function BE(t,e,r,s){t.nodes[e].shortcuts.push({to:r,reducer:s})}function Ca(t,e,r,s,a){(Object.prototype.hasOwnProperty.call(t.nodes[e].statics,r)?t.nodes[e].statics[r]:t.nodes[e].statics[r]=[]).push({to:s,reducer:a})}function zx(t,e,r,s,a){if(Array.isArray(e)){let[n,...c]=e;return t[n](r,s,a,...c)}else return t[e](r,s,a)}var Ine,iXe,xU,jl,kU,Zx,Xx=It(()=>{jx();Gx();Ine={candidateUsage:null,requiredOptions:[],errorMessage:null,ignoreOptions:!1,path:[],positionals:[],options:[],remainder:null,selectedIndex:Od,partial:!1,tokens:[]};iXe={always:()=>!0,isOptionLike:(t,e)=>!t.ignoreOptions&&e!=="-"&&e.startsWith("-"),isNotOptionLike:(t,e)=>t.ignoreOptions||e==="-"||!e.startsWith("-"),isOption:(t,e,r,s)=>!t.ignoreOptions&&e===s,isBatchOption:(t,e,r,s)=>!t.ignoreOptions&&Ane.test(e)&&[...e.slice(1)].every(a=>s.has(`-${a}`)),isBoundOption:(t,e,r,s,a)=>{let n=e.match(dU);return!t.ignoreOptions&&!!n&&Hx.test(n[1])&&s.has(n[1])&&a.filter(c=>c.nameSet.includes(n[1])).every(c=>c.allowBinding)},isNegatedOption:(t,e,r,s)=>!t.ignoreOptions&&e===`--no-${s.slice(2)}`,isHelp:(t,e)=>!t.ignoreOptions&&gU.test(e),isUnsupportedOption:(t,e,r,s)=>!t.ignoreOptions&&e.startsWith("-")&&Hx.test(e)&&!s.has(e),isInvalidOption:(t,e)=>!t.ignoreOptions&&e.startsWith("-")&&!Hx.test(e)},xU={setCandidateState:(t,e,r,s)=>({...t,...s}),setSelectedIndex:(t,e,r,s)=>({...t,selectedIndex:s}),setPartialIndex:(t,e,r,s)=>({...t,selectedIndex:s,partial:!0}),pushBatch:(t,e,r,s)=>{let a=t.options.slice(),n=t.tokens.slice();for(let c=1;c{let[,s,a]=e.match(dU),n=t.options.concat({name:s,value:a}),c=t.tokens.concat([{segmentIndex:r,type:"option",slice:[0,s.length],option:s},{segmentIndex:r,type:"assign",slice:[s.length,s.length+1]},{segmentIndex:r,type:"value",slice:[s.length+1,s.length+a.length+1]}]);return{...t,options:n,tokens:c}},pushPath:(t,e,r)=>{let s=t.path.concat(e),a=t.tokens.concat({segmentIndex:r,type:"path"});return{...t,path:s,tokens:a}},pushPositional:(t,e,r)=>{let s=t.positionals.concat({value:e,extra:!1}),a=t.tokens.concat({segmentIndex:r,type:"positional"});return{...t,positionals:s,tokens:a}},pushExtra:(t,e,r)=>{let s=t.positionals.concat({value:e,extra:!0}),a=t.tokens.concat({segmentIndex:r,type:"positional"});return{...t,positionals:s,tokens:a}},pushExtraNoLimits:(t,e,r)=>{let s=t.positionals.concat({value:e,extra:jl}),a=t.tokens.concat({segmentIndex:r,type:"positional"});return{...t,positionals:s,tokens:a}},pushTrue:(t,e,r,s)=>{let a=t.options.concat({name:s,value:!0}),n=t.tokens.concat({segmentIndex:r,type:"option",option:s});return{...t,options:a,tokens:n}},pushFalse:(t,e,r,s)=>{let a=t.options.concat({name:s,value:!1}),n=t.tokens.concat({segmentIndex:r,type:"option",option:s});return{...t,options:a,tokens:n}},pushUndefined:(t,e,r,s)=>{let a=t.options.concat({name:e,value:void 0}),n=t.tokens.concat({segmentIndex:r,type:"option",option:e});return{...t,options:a,tokens:n}},pushStringValue:(t,e,r)=>{var s;let a=t.options[t.options.length-1],n=t.options.slice(),c=t.tokens.concat({segmentIndex:r,type:"value"});return a.value=((s=a.value)!==null&&s!==void 0?s:[]).concat([e]),{...t,options:n,tokens:c}},setStringValue:(t,e,r)=>{let s=t.options[t.options.length-1],a=t.options.slice(),n=t.tokens.concat({segmentIndex:r,type:"value"});return s.value=e,{...t,options:a,tokens:n}},inhibateOptions:t=>({...t,ignoreOptions:!0}),useHelp:(t,e,r,s)=>{let[,,a]=e.match(gU);return typeof a<"u"?{...t,options:[{name:"-c",value:String(s)},{name:"-i",value:a}]}:{...t,options:[{name:"-c",value:String(s)}]}},setError:(t,e,r,s)=>e===ei.EndOfInput||e===ei.EndOfPartialInput?{...t,errorMessage:`${s}.`}:{...t,errorMessage:`${s} ("${e}").`},setOptionArityError:(t,e)=>{let r=t.options[t.options.length-1];return{...t,errorMessage:`Not enough arguments to option ${r.name}.`}}},jl=Symbol(),kU=class{constructor(e,r){this.allOptionNames=new Map,this.arity={leading:[],trailing:[],extra:[],proxy:!1},this.options=[],this.paths=[],this.cliIndex=e,this.cliOpts=r}addPath(e){this.paths.push(e)}setArity({leading:e=this.arity.leading,trailing:r=this.arity.trailing,extra:s=this.arity.extra,proxy:a=this.arity.proxy}){Object.assign(this.arity,{leading:e,trailing:r,extra:s,proxy:a})}addPositional({name:e="arg",required:r=!0}={}){if(!r&&this.arity.extra===jl)throw new Error("Optional parameters cannot be declared when using .rest() or .proxy()");if(!r&&this.arity.trailing.length>0)throw new Error("Optional parameters cannot be declared after the required trailing positional arguments");!r&&this.arity.extra!==jl?this.arity.extra.push(e):this.arity.extra!==jl&&this.arity.extra.length===0?this.arity.leading.push(e):this.arity.trailing.push(e)}addRest({name:e="arg",required:r=0}={}){if(this.arity.extra===jl)throw new Error("Infinite lists cannot be declared multiple times in the same command");if(this.arity.trailing.length>0)throw new Error("Infinite lists cannot be declared after the required trailing positional arguments");for(let s=0;s1)throw new Error("The arity cannot be higher than 1 when the option only supports the --arg=value syntax");if(!Number.isInteger(s))throw new Error(`The arity must be an integer, got ${s}`);if(s<0)throw new Error(`The arity must be positive, got ${s}`);let f=e.reduce((p,h)=>h.length>p.length?h:p,"");for(let p of e)this.allOptionNames.set(p,f);this.options.push({preferredName:f,nameSet:e,description:r,arity:s,hidden:a,required:n,allowBinding:c})}setContext(e){this.context=e}usage({detailed:e=!0,inlineOptions:r=!0}={}){let s=[this.cliOpts.binaryName],a=[];if(this.paths.length>0&&s.push(...this.paths[0]),e){for(let{preferredName:c,nameSet:f,arity:p,hidden:h,description:E,required:C}of this.options){if(h)continue;let S=[];for(let I=0;I`:`[${P}]`)}s.push(...this.arity.leading.map(c=>`<${c}>`)),this.arity.extra===jl?s.push("..."):s.push(...this.arity.extra.map(c=>`[${c}]`)),s.push(...this.arity.trailing.map(c=>`<${c}>`))}return{usage:s.join(" "),options:a}}compile(){if(typeof this.context>"u")throw new Error("Assertion failed: No context attached");let e=Cne(),r=En.InitialNode,s=this.usage().usage,a=this.options.filter(f=>f.required).map(f=>f.nameSet);r=Mu(e,Hl()),Ca(e,En.InitialNode,ei.StartOfInput,r,["setCandidateState",{candidateUsage:s,requiredOptions:a}]);let n=this.arity.proxy?"always":"isNotOptionLike",c=this.paths.length>0?this.paths:[[]];for(let f of c){let p=r;if(f.length>0){let S=Mu(e,Hl());BE(e,p,S),this.registerOptions(e,S),p=S}for(let S=0;S0||!this.arity.proxy){let S=Mu(e,Hl());js(e,p,"isHelp",S,["useHelp",this.cliIndex]),js(e,S,"always",S,"pushExtra"),Ca(e,S,ei.EndOfInput,En.SuccessNode,["setSelectedIndex",Od]),this.registerOptions(e,p)}this.arity.leading.length>0&&(Ca(e,p,ei.EndOfInput,En.ErrorNode,["setError","Not enough positional arguments"]),Ca(e,p,ei.EndOfPartialInput,En.SuccessNode,["setPartialIndex",this.cliIndex]));let h=p;for(let S=0;S0||S+1!==this.arity.leading.length)&&(Ca(e,P,ei.EndOfInput,En.ErrorNode,["setError","Not enough positional arguments"]),Ca(e,P,ei.EndOfPartialInput,En.SuccessNode,["setPartialIndex",this.cliIndex])),js(e,h,"isNotOptionLike",P,"pushPositional"),h=P}let E=h;if(this.arity.extra===jl||this.arity.extra.length>0){let S=Mu(e,Hl());if(BE(e,h,S),this.arity.extra===jl){let P=Mu(e,Hl());this.arity.proxy||this.registerOptions(e,P),js(e,h,n,P,"pushExtraNoLimits"),js(e,P,n,P,"pushExtraNoLimits"),BE(e,P,S)}else for(let P=0;P0)&&this.registerOptions(e,I),js(e,E,n,I,"pushExtra"),BE(e,I,S),E=I}E=S}this.arity.trailing.length>0&&(Ca(e,E,ei.EndOfInput,En.ErrorNode,["setError","Not enough positional arguments"]),Ca(e,E,ei.EndOfPartialInput,En.SuccessNode,["setPartialIndex",this.cliIndex]));let C=E;for(let S=0;S=0&&e{let c=n?ei.EndOfPartialInput:ei.EndOfInput;return $Ze(s,a,{endToken:c})}}}}});function Sne(){return $x.default&&"getColorDepth"in $x.default.WriteStream.prototype?$x.default.WriteStream.prototype.getColorDepth():process.env.FORCE_COLOR==="0"?1:process.env.FORCE_COLOR==="1"||typeof process.stdout<"u"&&process.stdout.isTTY?8:1}function Dne(t){let e=vne;if(typeof e>"u"){if(t.stdout===process.stdout&&t.stderr===process.stderr)return null;let{AsyncLocalStorage:r}=ye("async_hooks");e=vne=new r;let s=process.stdout._write;process.stdout._write=function(n,c,f){let p=e.getStore();return typeof p>"u"?s.call(this,n,c,f):p.stdout.write(n,c,f)};let a=process.stderr._write;process.stderr._write=function(n,c,f){let p=e.getStore();return typeof p>"u"?a.call(this,n,c,f):p.stderr.write(n,c,f)}}return r=>e.run(t,r)}var $x,vne,bne=It(()=>{$x=et(ye("tty"),1)});var ek,Pne=It(()=>{u0();ek=class t extends ot{constructor(e){super(),this.contexts=e,this.commands=[]}static from(e,r){let s=new t(r);s.path=e.path;for(let a of e.options)switch(a.name){case"-c":s.commands.push(Number(a.value));break;case"-i":s.index=Number(a.value);break}return s}async execute(){let e=this.commands;if(typeof this.index<"u"&&this.index>=0&&this.index1){this.context.stdout.write(`Multiple commands match your selection: +`),this.context.stdout.write(` +`);let r=0;for(let s of this.commands)this.context.stdout.write(this.cli.usage(this.contexts[s].commandClass,{prefix:`${r++}. `.padStart(5)}));this.context.stdout.write(` +`),this.context.stdout.write(`Run again with -h= to see the longer details of any of those commands. +`)}}}});async function Qne(...t){let{resolvedOptions:e,resolvedCommandClasses:r,resolvedArgv:s,resolvedContext:a}=Rne(t);return wa.from(r,e).runExit(s,a)}async function Tne(...t){let{resolvedOptions:e,resolvedCommandClasses:r,resolvedArgv:s,resolvedContext:a}=Rne(t);return wa.from(r,e).run(s,a)}function Rne(t){let e,r,s,a;switch(typeof process<"u"&&typeof process.argv<"u"&&(s=process.argv.slice(2)),t.length){case 1:r=t[0];break;case 2:t[0]&&t[0].prototype instanceof ot||Array.isArray(t[0])?(r=t[0],Array.isArray(t[1])?s=t[1]:a=t[1]):(e=t[0],r=t[1]);break;case 3:Array.isArray(t[2])?(e=t[0],r=t[1],s=t[2]):t[0]&&t[0].prototype instanceof ot||Array.isArray(t[0])?(r=t[0],s=t[1],a=t[2]):(e=t[0],r=t[1],a=t[2]);break;default:e=t[0],r=t[1],s=t[2],a=t[3];break}if(typeof s>"u")throw new Error("The argv parameter must be provided when running Clipanion outside of a Node context");return{resolvedOptions:e,resolvedCommandClasses:r,resolvedArgv:s,resolvedContext:a}}function kne(t){return t()}var xne,wa,Fne=It(()=>{jx();Xx();IU();bne();u0();Pne();xne=Symbol("clipanion/errorCommand");wa=class t{constructor({binaryLabel:e,binaryName:r="...",binaryVersion:s,enableCapture:a=!1,enableColors:n}={}){this.registrations=new Map,this.builder=new Zx({binaryName:r}),this.binaryLabel=e,this.binaryName=r,this.binaryVersion=s,this.enableCapture=a,this.enableColors=n}static from(e,r={}){let s=new t(r),a=Array.isArray(e)?e:[e];for(let n of a)s.register(n);return s}register(e){var r;let s=new Map,a=new e;for(let p in a){let h=a[p];typeof h=="object"&&h!==null&&h[ot.isOption]&&s.set(p,h)}let n=this.builder.command(),c=n.cliIndex,f=(r=e.paths)!==null&&r!==void 0?r:a.paths;if(typeof f<"u")for(let p of f)n.addPath(p);this.registrations.set(e,{specs:s,builder:n,index:c});for(let[p,{definition:h}]of s.entries())h(n,p);n.setContext({commandClass:e})}process(e,r){let{input:s,context:a,partial:n}=typeof e=="object"&&Array.isArray(e)?{input:e,context:r}:e,{contexts:c,process:f}=this.builder.compile(),p=f(s,{partial:n}),h={...t.defaultContext,...a};switch(p.selectedIndex){case Od:{let E=ek.from(p,c);return E.context=h,E.tokens=p.tokens,E}default:{let{commandClass:E}=c[p.selectedIndex],C=this.registrations.get(E);if(typeof C>"u")throw new Error("Assertion failed: Expected the command class to have been registered.");let S=new E;S.context=h,S.tokens=p.tokens,S.path=p.path;try{for(let[P,{transformer:I}]of C.specs.entries())S[P]=I(C.builder,P,p,h);return S}catch(P){throw P[xne]=S,P}}break}}async run(e,r){var s,a;let n,c={...t.defaultContext,...r},f=(s=this.enableColors)!==null&&s!==void 0?s:c.colorDepth>1;if(!Array.isArray(e))n=e;else try{n=this.process(e,c)}catch(E){return c.stdout.write(this.error(E,{colored:f})),1}if(n.help)return c.stdout.write(this.usage(n,{colored:f,detailed:!0})),0;n.context=c,n.cli={binaryLabel:this.binaryLabel,binaryName:this.binaryName,binaryVersion:this.binaryVersion,enableCapture:this.enableCapture,enableColors:this.enableColors,definitions:()=>this.definitions(),definition:E=>this.definition(E),error:(E,C)=>this.error(E,C),format:E=>this.format(E),process:(E,C)=>this.process(E,{...c,...C}),run:(E,C)=>this.run(E,{...c,...C}),usage:(E,C)=>this.usage(E,C)};let p=this.enableCapture&&(a=Dne(c))!==null&&a!==void 0?a:kne,h;try{h=await p(()=>n.validateAndExecute().catch(E=>n.catch(E).then(()=>0)))}catch(E){return c.stdout.write(this.error(E,{colored:f,command:n})),1}return h}async runExit(e,r){process.exitCode=await this.run(e,r)}definition(e,{colored:r=!1}={}){if(!e.usage)return null;let{usage:s}=this.getUsageByRegistration(e,{detailed:!1}),{usage:a,options:n}=this.getUsageByRegistration(e,{detailed:!0,inlineOptions:!1}),c=typeof e.usage.category<"u"?qo(e.usage.category,{format:this.format(r),paragraphs:!1}):void 0,f=typeof e.usage.description<"u"?qo(e.usage.description,{format:this.format(r),paragraphs:!1}):void 0,p=typeof e.usage.details<"u"?qo(e.usage.details,{format:this.format(r),paragraphs:!0}):void 0,h=typeof e.usage.examples<"u"?e.usage.examples.map(([E,C])=>[qo(E,{format:this.format(r),paragraphs:!1}),C.replace(/\$0/g,this.binaryName)]):void 0;return{path:s,usage:a,category:c,description:f,details:p,examples:h,options:n}}definitions({colored:e=!1}={}){let r=[];for(let s of this.registrations.keys()){let a=this.definition(s,{colored:e});a&&r.push(a)}return r}usage(e=null,{colored:r,detailed:s=!1,prefix:a="$ "}={}){var n;if(e===null){for(let p of this.registrations.keys()){let h=p.paths,E=typeof p.usage<"u";if(!h||h.length===0||h.length===1&&h[0].length===0||((n=h?.some(P=>P.length===0))!==null&&n!==void 0?n:!1))if(e){e=null;break}else e=p;else if(E){e=null;continue}}e&&(s=!0)}let c=e!==null&&e instanceof ot?e.constructor:e,f="";if(c)if(s){let{description:p="",details:h="",examples:E=[]}=c.usage||{};p!==""&&(f+=qo(p,{format:this.format(r),paragraphs:!1}).replace(/^./,P=>P.toUpperCase()),f+=` +`),(h!==""||E.length>0)&&(f+=`${this.format(r).header("Usage")} +`,f+=` +`);let{usage:C,options:S}=this.getUsageByRegistration(c,{inlineOptions:!1});if(f+=`${this.format(r).bold(a)}${C} +`,S.length>0){f+=` +`,f+=`${this.format(r).header("Options")} +`;let P=S.reduce((I,R)=>Math.max(I,R.definition.length),0);f+=` +`;for(let{definition:I,description:R}of S)f+=` ${this.format(r).bold(I.padEnd(P))} ${qo(R,{format:this.format(r),paragraphs:!1})}`}if(h!==""&&(f+=` +`,f+=`${this.format(r).header("Details")} +`,f+=` +`,f+=qo(h,{format:this.format(r),paragraphs:!0})),E.length>0){f+=` +`,f+=`${this.format(r).header("Examples")} +`;for(let[P,I]of E)f+=` +`,f+=qo(P,{format:this.format(r),paragraphs:!1}),f+=`${I.replace(/^/m,` ${this.format(r).bold(a)}`).replace(/\$0/g,this.binaryName)} +`}}else{let{usage:p}=this.getUsageByRegistration(c);f+=`${this.format(r).bold(a)}${p} +`}else{let p=new Map;for(let[S,{index:P}]of this.registrations.entries()){if(typeof S.usage>"u")continue;let I=typeof S.usage.category<"u"?qo(S.usage.category,{format:this.format(r),paragraphs:!1}):null,R=p.get(I);typeof R>"u"&&p.set(I,R=[]);let{usage:N}=this.getUsageByIndex(P);R.push({commandClass:S,usage:N})}let h=Array.from(p.keys()).sort((S,P)=>S===null?-1:P===null?1:S.localeCompare(P,"en",{usage:"sort",caseFirst:"upper"})),E=typeof this.binaryLabel<"u",C=typeof this.binaryVersion<"u";E||C?(E&&C?f+=`${this.format(r).header(`${this.binaryLabel} - ${this.binaryVersion}`)} + +`:E?f+=`${this.format(r).header(`${this.binaryLabel}`)} +`:f+=`${this.format(r).header(`${this.binaryVersion}`)} +`,f+=` ${this.format(r).bold(a)}${this.binaryName} +`):f+=`${this.format(r).bold(a)}${this.binaryName} +`;for(let S of h){let P=p.get(S).slice().sort((R,N)=>R.usage.localeCompare(N.usage,"en",{usage:"sort",caseFirst:"upper"})),I=S!==null?S.trim():"General commands";f+=` +`,f+=`${this.format(r).header(`${I}`)} +`;for(let{commandClass:R,usage:N}of P){let U=R.usage.description||"undocumented";f+=` +`,f+=` ${this.format(r).bold(N)} +`,f+=` ${qo(U,{format:this.format(r),paragraphs:!1})}`}}f+=` +`,f+=qo("You can also print more details about any of these commands by calling them with the `-h,--help` flag right after the command name.",{format:this.format(r),paragraphs:!0})}return f}error(e,r){var s,{colored:a,command:n=(s=e[xne])!==null&&s!==void 0?s:null}=r===void 0?{}:r;(!e||typeof e!="object"||!("stack"in e))&&(e=new Error(`Execution failed with a non-error rejection (rejected value: ${JSON.stringify(e)})`));let c="",f=e.name.replace(/([a-z])([A-Z])/g,"$1 $2");f==="Error"&&(f="Internal Error"),c+=`${this.format(a).error(f)}: ${e.message} +`;let p=e.clipanion;return typeof p<"u"?p.type==="usage"&&(c+=` +`,c+=this.usage(n)):e.stack&&(c+=`${e.stack.replace(/^.*\n/,"")} +`),c}format(e){var r;return((r=e??this.enableColors)!==null&&r!==void 0?r:t.defaultContext.colorDepth>1)?pne:hne}getUsageByRegistration(e,r){let s=this.registrations.get(e);if(typeof s>"u")throw new Error("Assertion failed: Unregistered command");return this.getUsageByIndex(s.index,r)}getUsageByIndex(e,r){return this.builder.getBuilderByIndex(e).usage(r)}};wa.defaultContext={env:process.env,stdin:process.stdin,stdout:process.stdout,stderr:process.stderr,colorDepth:Sne()}});var sB,Nne=It(()=>{u0();sB=class extends ot{async execute(){this.context.stdout.write(`${JSON.stringify(this.cli.definitions(),null,2)} +`)}};sB.paths=[["--clipanion=definitions"]]});var oB,One=It(()=>{u0();oB=class extends ot{async execute(){this.context.stdout.write(this.cli.usage())}};oB.paths=[["-h"],["--help"]]});function tk(t={}){return Ea({definition(e,r){var s;e.addProxy({name:(s=t.name)!==null&&s!==void 0?s:r,required:t.required})},transformer(e,r,s){return s.positionals.map(({value:a})=>a)}})}var QU=It(()=>{Bp()});var aB,Lne=It(()=>{u0();QU();aB=class extends ot{constructor(){super(...arguments),this.args=tk()}async execute(){this.context.stdout.write(`${JSON.stringify(this.cli.process(this.args).tokens,null,2)} +`)}};aB.paths=[["--clipanion=tokens"]]});var lB,Mne=It(()=>{u0();lB=class extends ot{async execute(){var e;this.context.stdout.write(`${(e=this.cli.binaryVersion)!==null&&e!==void 0?e:""} +`)}};lB.paths=[["-v"],["--version"]]});var TU={};Vt(TU,{DefinitionsCommand:()=>sB,HelpCommand:()=>oB,TokensCommand:()=>aB,VersionCommand:()=>lB});var _ne=It(()=>{Nne();One();Lne();Mne()});function Une(t,e,r){let[s,a]=Gf(e,r??{}),{arity:n=1}=a,c=t.split(","),f=new Set(c);return Ea({definition(p){p.addOption({names:c,arity:n,hidden:a?.hidden,description:a?.description,required:a.required})},transformer(p,h,E){let C,S=typeof s<"u"?[...s]:void 0;for(let{name:P,value:I}of E.options)f.has(P)&&(C=P,S=S??[],S.push(I));return typeof S<"u"?Ld(C??h,S,a.validator):S}})}var Hne=It(()=>{Bp()});function jne(t,e,r){let[s,a]=Gf(e,r??{}),n=t.split(","),c=new Set(n);return Ea({definition(f){f.addOption({names:n,allowBinding:!1,arity:0,hidden:a.hidden,description:a.description,required:a.required})},transformer(f,p,h){let E=s;for(let{name:C,value:S}of h.options)c.has(C)&&(E=S);return E}})}var qne=It(()=>{Bp()});function Gne(t,e,r){let[s,a]=Gf(e,r??{}),n=t.split(","),c=new Set(n);return Ea({definition(f){f.addOption({names:n,allowBinding:!1,arity:0,hidden:a.hidden,description:a.description,required:a.required})},transformer(f,p,h){let E=s;for(let{name:C,value:S}of h.options)c.has(C)&&(E??(E=0),S?E+=1:E=0);return E}})}var Wne=It(()=>{Bp()});function Yne(t={}){return Ea({definition(e,r){var s;e.addRest({name:(s=t.name)!==null&&s!==void 0?s:r,required:t.required})},transformer(e,r,s){let a=c=>{let f=s.positionals[c];return f.extra===jl||f.extra===!1&&cc)}})}var Vne=It(()=>{Xx();Bp()});function sXe(t,e,r){let[s,a]=Gf(e,r??{}),{arity:n=1}=a,c=t.split(","),f=new Set(c);return Ea({definition(p){p.addOption({names:c,arity:a.tolerateBoolean?0:n,hidden:a.hidden,description:a.description,required:a.required})},transformer(p,h,E,C){let S,P=s;typeof a.env<"u"&&C.env[a.env]&&(S=a.env,P=C.env[a.env]);for(let{name:I,value:R}of E.options)f.has(I)&&(S=I,P=R);return typeof P=="string"?Ld(S??h,P,a.validator):P}})}function oXe(t={}){let{required:e=!0}=t;return Ea({definition(r,s){var a;r.addPositional({name:(a=t.name)!==null&&a!==void 0?a:s,required:t.required})},transformer(r,s,a){var n;for(let c=0;c{Xx();Bp()});var ge={};Vt(ge,{Array:()=>Une,Boolean:()=>jne,Counter:()=>Gne,Proxy:()=>tk,Rest:()=>Yne,String:()=>Kne,applyValidator:()=>Ld,cleanValidationError:()=>Wx,formatError:()=>$2,isOptionSymbol:()=>X2,makeCommandOption:()=>Ea,rerouteArguments:()=>Gf});var zne=It(()=>{Bp();QU();Hne();qne();Wne();Vne();Jne()});var cB={};Vt(cB,{Builtins:()=>TU,Cli:()=>wa,Command:()=>ot,Option:()=>ge,UsageError:()=>nt,formatMarkdownish:()=>qo,run:()=>Tne,runExit:()=>Qne});var Wt=It(()=>{Gx();IU();u0();Fne();_ne();zne()});var Zne=L((RWt,aXe)=>{aXe.exports={name:"dotenv",version:"16.3.1",description:"Loads environment variables from .env file",main:"lib/main.js",types:"lib/main.d.ts",exports:{".":{types:"./lib/main.d.ts",require:"./lib/main.js",default:"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},scripts:{"dts-check":"tsc --project tests/types/tsconfig.json",lint:"standard","lint-readme":"standard-markdown",pretest:"npm run lint && npm run dts-check",test:"tap tests/*.js --100 -Rspec",prerelease:"npm test",release:"standard-version"},repository:{type:"git",url:"git://github.com/motdotla/dotenv.git"},funding:"https://github.com/motdotla/dotenv?sponsor=1",keywords:["dotenv","env",".env","environment","variables","config","settings"],readmeFilename:"README.md",license:"BSD-2-Clause",devDependencies:{"@definitelytyped/dtslint":"^0.0.133","@types/node":"^18.11.3",decache:"^4.6.1",sinon:"^14.0.1",standard:"^17.0.0","standard-markdown":"^7.1.0","standard-version":"^9.5.0",tap:"^16.3.0",tar:"^6.1.11",typescript:"^4.8.4"},engines:{node:">=12"},browser:{fs:!1}}});var tie=L((FWt,vp)=>{var Xne=ye("fs"),FU=ye("path"),lXe=ye("os"),cXe=ye("crypto"),uXe=Zne(),NU=uXe.version,fXe=/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;function AXe(t){let e={},r=t.toString();r=r.replace(/\r\n?/mg,` +`);let s;for(;(s=fXe.exec(r))!=null;){let a=s[1],n=s[2]||"";n=n.trim();let c=n[0];n=n.replace(/^(['"`])([\s\S]*)\1$/mg,"$2"),c==='"'&&(n=n.replace(/\\n/g,` +`),n=n.replace(/\\r/g,"\r")),e[a]=n}return e}function pXe(t){let e=eie(t),r=qs.configDotenv({path:e});if(!r.parsed)throw new Error(`MISSING_DATA: Cannot parse ${e} for an unknown reason`);let s=$ne(t).split(","),a=s.length,n;for(let c=0;c=a)throw f}return qs.parse(n)}function hXe(t){console.log(`[dotenv@${NU}][INFO] ${t}`)}function gXe(t){console.log(`[dotenv@${NU}][WARN] ${t}`)}function RU(t){console.log(`[dotenv@${NU}][DEBUG] ${t}`)}function $ne(t){return t&&t.DOTENV_KEY&&t.DOTENV_KEY.length>0?t.DOTENV_KEY:process.env.DOTENV_KEY&&process.env.DOTENV_KEY.length>0?process.env.DOTENV_KEY:""}function dXe(t,e){let r;try{r=new URL(e)}catch(f){throw f.code==="ERR_INVALID_URL"?new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=development"):f}let s=r.password;if(!s)throw new Error("INVALID_DOTENV_KEY: Missing key part");let a=r.searchParams.get("environment");if(!a)throw new Error("INVALID_DOTENV_KEY: Missing environment part");let n=`DOTENV_VAULT_${a.toUpperCase()}`,c=t.parsed[n];if(!c)throw new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${n} in your .env.vault file.`);return{ciphertext:c,key:s}}function eie(t){let e=FU.resolve(process.cwd(),".env");return t&&t.path&&t.path.length>0&&(e=t.path),e.endsWith(".vault")?e:`${e}.vault`}function mXe(t){return t[0]==="~"?FU.join(lXe.homedir(),t.slice(1)):t}function yXe(t){hXe("Loading env from encrypted .env.vault");let e=qs._parseVault(t),r=process.env;return t&&t.processEnv!=null&&(r=t.processEnv),qs.populate(r,e,t),{parsed:e}}function EXe(t){let e=FU.resolve(process.cwd(),".env"),r="utf8",s=!!(t&&t.debug);t&&(t.path!=null&&(e=mXe(t.path)),t.encoding!=null&&(r=t.encoding));try{let a=qs.parse(Xne.readFileSync(e,{encoding:r})),n=process.env;return t&&t.processEnv!=null&&(n=t.processEnv),qs.populate(n,a,t),{parsed:a}}catch(a){return s&&RU(`Failed to load ${e} ${a.message}`),{error:a}}}function IXe(t){let e=eie(t);return $ne(t).length===0?qs.configDotenv(t):Xne.existsSync(e)?qs._configVault(t):(gXe(`You set DOTENV_KEY but you are missing a .env.vault file at ${e}. Did you forget to build it?`),qs.configDotenv(t))}function CXe(t,e){let r=Buffer.from(e.slice(-64),"hex"),s=Buffer.from(t,"base64"),a=s.slice(0,12),n=s.slice(-16);s=s.slice(12,-16);try{let c=cXe.createDecipheriv("aes-256-gcm",r,a);return c.setAuthTag(n),`${c.update(s)}${c.final()}`}catch(c){let f=c instanceof RangeError,p=c.message==="Invalid key length",h=c.message==="Unsupported state or unable to authenticate data";if(f||p){let E="INVALID_DOTENV_KEY: It must be 64 characters long (or more)";throw new Error(E)}else if(h){let E="DECRYPTION_FAILED: Please check your DOTENV_KEY";throw new Error(E)}else throw console.error("Error: ",c.code),console.error("Error: ",c.message),c}}function wXe(t,e,r={}){let s=!!(r&&r.debug),a=!!(r&&r.override);if(typeof e!="object")throw new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");for(let n of Object.keys(e))Object.prototype.hasOwnProperty.call(t,n)?(a===!0&&(t[n]=e[n]),s&&RU(a===!0?`"${n}" is already defined and WAS overwritten`:`"${n}" is already defined and was NOT overwritten`)):t[n]=e[n]}var qs={configDotenv:EXe,_configVault:yXe,_parseVault:pXe,config:IXe,decrypt:CXe,parse:AXe,populate:wXe};vp.exports.configDotenv=qs.configDotenv;vp.exports._configVault=qs._configVault;vp.exports._parseVault=qs._parseVault;vp.exports.config=qs.config;vp.exports.decrypt=qs.decrypt;vp.exports.parse=qs.parse;vp.exports.populate=qs.populate;vp.exports=qs});var nie=L((NWt,rie)=>{"use strict";rie.exports=(t,...e)=>new Promise(r=>{r(t(...e))})});var Md=L((OWt,OU)=>{"use strict";var BXe=nie(),iie=t=>{if(t<1)throw new TypeError("Expected `concurrency` to be a number from 1 and up");let e=[],r=0,s=()=>{r--,e.length>0&&e.shift()()},a=(f,p,...h)=>{r++;let E=BXe(f,...h);p(E),E.then(s,s)},n=(f,p,...h)=>{rnew Promise(h=>n(f,h,...p));return Object.defineProperties(c,{activeCount:{get:()=>r},pendingCount:{get:()=>e.length}}),c};OU.exports=iie;OU.exports.default=iie});function Vf(t){return`YN${t.toString(10).padStart(4,"0")}`}function rk(t){let e=Number(t.slice(2));if(typeof Dr[e]>"u")throw new Error(`Unknown message name: "${t}"`);return e}var Dr,nk=It(()=>{Dr=(Me=>(Me[Me.UNNAMED=0]="UNNAMED",Me[Me.EXCEPTION=1]="EXCEPTION",Me[Me.MISSING_PEER_DEPENDENCY=2]="MISSING_PEER_DEPENDENCY",Me[Me.CYCLIC_DEPENDENCIES=3]="CYCLIC_DEPENDENCIES",Me[Me.DISABLED_BUILD_SCRIPTS=4]="DISABLED_BUILD_SCRIPTS",Me[Me.BUILD_DISABLED=5]="BUILD_DISABLED",Me[Me.SOFT_LINK_BUILD=6]="SOFT_LINK_BUILD",Me[Me.MUST_BUILD=7]="MUST_BUILD",Me[Me.MUST_REBUILD=8]="MUST_REBUILD",Me[Me.BUILD_FAILED=9]="BUILD_FAILED",Me[Me.RESOLVER_NOT_FOUND=10]="RESOLVER_NOT_FOUND",Me[Me.FETCHER_NOT_FOUND=11]="FETCHER_NOT_FOUND",Me[Me.LINKER_NOT_FOUND=12]="LINKER_NOT_FOUND",Me[Me.FETCH_NOT_CACHED=13]="FETCH_NOT_CACHED",Me[Me.YARN_IMPORT_FAILED=14]="YARN_IMPORT_FAILED",Me[Me.REMOTE_INVALID=15]="REMOTE_INVALID",Me[Me.REMOTE_NOT_FOUND=16]="REMOTE_NOT_FOUND",Me[Me.RESOLUTION_PACK=17]="RESOLUTION_PACK",Me[Me.CACHE_CHECKSUM_MISMATCH=18]="CACHE_CHECKSUM_MISMATCH",Me[Me.UNUSED_CACHE_ENTRY=19]="UNUSED_CACHE_ENTRY",Me[Me.MISSING_LOCKFILE_ENTRY=20]="MISSING_LOCKFILE_ENTRY",Me[Me.WORKSPACE_NOT_FOUND=21]="WORKSPACE_NOT_FOUND",Me[Me.TOO_MANY_MATCHING_WORKSPACES=22]="TOO_MANY_MATCHING_WORKSPACES",Me[Me.CONSTRAINTS_MISSING_DEPENDENCY=23]="CONSTRAINTS_MISSING_DEPENDENCY",Me[Me.CONSTRAINTS_INCOMPATIBLE_DEPENDENCY=24]="CONSTRAINTS_INCOMPATIBLE_DEPENDENCY",Me[Me.CONSTRAINTS_EXTRANEOUS_DEPENDENCY=25]="CONSTRAINTS_EXTRANEOUS_DEPENDENCY",Me[Me.CONSTRAINTS_INVALID_DEPENDENCY=26]="CONSTRAINTS_INVALID_DEPENDENCY",Me[Me.CANT_SUGGEST_RESOLUTIONS=27]="CANT_SUGGEST_RESOLUTIONS",Me[Me.FROZEN_LOCKFILE_EXCEPTION=28]="FROZEN_LOCKFILE_EXCEPTION",Me[Me.CROSS_DRIVE_VIRTUAL_LOCAL=29]="CROSS_DRIVE_VIRTUAL_LOCAL",Me[Me.FETCH_FAILED=30]="FETCH_FAILED",Me[Me.DANGEROUS_NODE_MODULES=31]="DANGEROUS_NODE_MODULES",Me[Me.NODE_GYP_INJECTED=32]="NODE_GYP_INJECTED",Me[Me.AUTHENTICATION_NOT_FOUND=33]="AUTHENTICATION_NOT_FOUND",Me[Me.INVALID_CONFIGURATION_KEY=34]="INVALID_CONFIGURATION_KEY",Me[Me.NETWORK_ERROR=35]="NETWORK_ERROR",Me[Me.LIFECYCLE_SCRIPT=36]="LIFECYCLE_SCRIPT",Me[Me.CONSTRAINTS_MISSING_FIELD=37]="CONSTRAINTS_MISSING_FIELD",Me[Me.CONSTRAINTS_INCOMPATIBLE_FIELD=38]="CONSTRAINTS_INCOMPATIBLE_FIELD",Me[Me.CONSTRAINTS_EXTRANEOUS_FIELD=39]="CONSTRAINTS_EXTRANEOUS_FIELD",Me[Me.CONSTRAINTS_INVALID_FIELD=40]="CONSTRAINTS_INVALID_FIELD",Me[Me.AUTHENTICATION_INVALID=41]="AUTHENTICATION_INVALID",Me[Me.PROLOG_UNKNOWN_ERROR=42]="PROLOG_UNKNOWN_ERROR",Me[Me.PROLOG_SYNTAX_ERROR=43]="PROLOG_SYNTAX_ERROR",Me[Me.PROLOG_EXISTENCE_ERROR=44]="PROLOG_EXISTENCE_ERROR",Me[Me.STACK_OVERFLOW_RESOLUTION=45]="STACK_OVERFLOW_RESOLUTION",Me[Me.AUTOMERGE_FAILED_TO_PARSE=46]="AUTOMERGE_FAILED_TO_PARSE",Me[Me.AUTOMERGE_IMMUTABLE=47]="AUTOMERGE_IMMUTABLE",Me[Me.AUTOMERGE_SUCCESS=48]="AUTOMERGE_SUCCESS",Me[Me.AUTOMERGE_REQUIRED=49]="AUTOMERGE_REQUIRED",Me[Me.DEPRECATED_CLI_SETTINGS=50]="DEPRECATED_CLI_SETTINGS",Me[Me.PLUGIN_NAME_NOT_FOUND=51]="PLUGIN_NAME_NOT_FOUND",Me[Me.INVALID_PLUGIN_REFERENCE=52]="INVALID_PLUGIN_REFERENCE",Me[Me.CONSTRAINTS_AMBIGUITY=53]="CONSTRAINTS_AMBIGUITY",Me[Me.CACHE_OUTSIDE_PROJECT=54]="CACHE_OUTSIDE_PROJECT",Me[Me.IMMUTABLE_INSTALL=55]="IMMUTABLE_INSTALL",Me[Me.IMMUTABLE_CACHE=56]="IMMUTABLE_CACHE",Me[Me.INVALID_MANIFEST=57]="INVALID_MANIFEST",Me[Me.PACKAGE_PREPARATION_FAILED=58]="PACKAGE_PREPARATION_FAILED",Me[Me.INVALID_RANGE_PEER_DEPENDENCY=59]="INVALID_RANGE_PEER_DEPENDENCY",Me[Me.INCOMPATIBLE_PEER_DEPENDENCY=60]="INCOMPATIBLE_PEER_DEPENDENCY",Me[Me.DEPRECATED_PACKAGE=61]="DEPRECATED_PACKAGE",Me[Me.INCOMPATIBLE_OS=62]="INCOMPATIBLE_OS",Me[Me.INCOMPATIBLE_CPU=63]="INCOMPATIBLE_CPU",Me[Me.FROZEN_ARTIFACT_EXCEPTION=64]="FROZEN_ARTIFACT_EXCEPTION",Me[Me.TELEMETRY_NOTICE=65]="TELEMETRY_NOTICE",Me[Me.PATCH_HUNK_FAILED=66]="PATCH_HUNK_FAILED",Me[Me.INVALID_CONFIGURATION_VALUE=67]="INVALID_CONFIGURATION_VALUE",Me[Me.UNUSED_PACKAGE_EXTENSION=68]="UNUSED_PACKAGE_EXTENSION",Me[Me.REDUNDANT_PACKAGE_EXTENSION=69]="REDUNDANT_PACKAGE_EXTENSION",Me[Me.AUTO_NM_SUCCESS=70]="AUTO_NM_SUCCESS",Me[Me.NM_CANT_INSTALL_EXTERNAL_SOFT_LINK=71]="NM_CANT_INSTALL_EXTERNAL_SOFT_LINK",Me[Me.NM_PRESERVE_SYMLINKS_REQUIRED=72]="NM_PRESERVE_SYMLINKS_REQUIRED",Me[Me.UPDATE_LOCKFILE_ONLY_SKIP_LINK=73]="UPDATE_LOCKFILE_ONLY_SKIP_LINK",Me[Me.NM_HARDLINKS_MODE_DOWNGRADED=74]="NM_HARDLINKS_MODE_DOWNGRADED",Me[Me.PROLOG_INSTANTIATION_ERROR=75]="PROLOG_INSTANTIATION_ERROR",Me[Me.INCOMPATIBLE_ARCHITECTURE=76]="INCOMPATIBLE_ARCHITECTURE",Me[Me.GHOST_ARCHITECTURE=77]="GHOST_ARCHITECTURE",Me[Me.RESOLUTION_MISMATCH=78]="RESOLUTION_MISMATCH",Me[Me.PROLOG_LIMIT_EXCEEDED=79]="PROLOG_LIMIT_EXCEEDED",Me[Me.NETWORK_DISABLED=80]="NETWORK_DISABLED",Me[Me.NETWORK_UNSAFE_HTTP=81]="NETWORK_UNSAFE_HTTP",Me[Me.RESOLUTION_FAILED=82]="RESOLUTION_FAILED",Me[Me.AUTOMERGE_GIT_ERROR=83]="AUTOMERGE_GIT_ERROR",Me[Me.CONSTRAINTS_CHECK_FAILED=84]="CONSTRAINTS_CHECK_FAILED",Me[Me.UPDATED_RESOLUTION_RECORD=85]="UPDATED_RESOLUTION_RECORD",Me[Me.EXPLAIN_PEER_DEPENDENCIES_CTA=86]="EXPLAIN_PEER_DEPENDENCIES_CTA",Me[Me.MIGRATION_SUCCESS=87]="MIGRATION_SUCCESS",Me[Me.VERSION_NOTICE=88]="VERSION_NOTICE",Me[Me.TIPS_NOTICE=89]="TIPS_NOTICE",Me[Me.OFFLINE_MODE_ENABLED=90]="OFFLINE_MODE_ENABLED",Me[Me.INVALID_PROVENANCE_ENVIRONMENT=91]="INVALID_PROVENANCE_ENVIRONMENT",Me))(Dr||{})});var uB=L((MWt,sie)=>{var vXe="2.0.0",SXe=Number.MAX_SAFE_INTEGER||9007199254740991,DXe=16,bXe=250,PXe=["major","premajor","minor","preminor","patch","prepatch","prerelease"];sie.exports={MAX_LENGTH:256,MAX_SAFE_COMPONENT_LENGTH:DXe,MAX_SAFE_BUILD_LENGTH:bXe,MAX_SAFE_INTEGER:SXe,RELEASE_TYPES:PXe,SEMVER_SPEC_VERSION:vXe,FLAG_INCLUDE_PRERELEASE:1,FLAG_LOOSE:2}});var fB=L((_Wt,oie)=>{var xXe=typeof process=="object"&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?(...t)=>console.error("SEMVER",...t):()=>{};oie.exports=xXe});var vE=L((Sp,aie)=>{var{MAX_SAFE_COMPONENT_LENGTH:LU,MAX_SAFE_BUILD_LENGTH:kXe,MAX_LENGTH:QXe}=uB(),TXe=fB();Sp=aie.exports={};var RXe=Sp.re=[],FXe=Sp.safeRe=[],rr=Sp.src=[],nr=Sp.t={},NXe=0,MU="[a-zA-Z0-9-]",OXe=[["\\s",1],["\\d",QXe],[MU,kXe]],LXe=t=>{for(let[e,r]of OXe)t=t.split(`${e}*`).join(`${e}{0,${r}}`).split(`${e}+`).join(`${e}{1,${r}}`);return t},Kr=(t,e,r)=>{let s=LXe(e),a=NXe++;TXe(t,a,e),nr[t]=a,rr[a]=e,RXe[a]=new RegExp(e,r?"g":void 0),FXe[a]=new RegExp(s,r?"g":void 0)};Kr("NUMERICIDENTIFIER","0|[1-9]\\d*");Kr("NUMERICIDENTIFIERLOOSE","\\d+");Kr("NONNUMERICIDENTIFIER",`\\d*[a-zA-Z-]${MU}*`);Kr("MAINVERSION",`(${rr[nr.NUMERICIDENTIFIER]})\\.(${rr[nr.NUMERICIDENTIFIER]})\\.(${rr[nr.NUMERICIDENTIFIER]})`);Kr("MAINVERSIONLOOSE",`(${rr[nr.NUMERICIDENTIFIERLOOSE]})\\.(${rr[nr.NUMERICIDENTIFIERLOOSE]})\\.(${rr[nr.NUMERICIDENTIFIERLOOSE]})`);Kr("PRERELEASEIDENTIFIER",`(?:${rr[nr.NUMERICIDENTIFIER]}|${rr[nr.NONNUMERICIDENTIFIER]})`);Kr("PRERELEASEIDENTIFIERLOOSE",`(?:${rr[nr.NUMERICIDENTIFIERLOOSE]}|${rr[nr.NONNUMERICIDENTIFIER]})`);Kr("PRERELEASE",`(?:-(${rr[nr.PRERELEASEIDENTIFIER]}(?:\\.${rr[nr.PRERELEASEIDENTIFIER]})*))`);Kr("PRERELEASELOOSE",`(?:-?(${rr[nr.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${rr[nr.PRERELEASEIDENTIFIERLOOSE]})*))`);Kr("BUILDIDENTIFIER",`${MU}+`);Kr("BUILD",`(?:\\+(${rr[nr.BUILDIDENTIFIER]}(?:\\.${rr[nr.BUILDIDENTIFIER]})*))`);Kr("FULLPLAIN",`v?${rr[nr.MAINVERSION]}${rr[nr.PRERELEASE]}?${rr[nr.BUILD]}?`);Kr("FULL",`^${rr[nr.FULLPLAIN]}$`);Kr("LOOSEPLAIN",`[v=\\s]*${rr[nr.MAINVERSIONLOOSE]}${rr[nr.PRERELEASELOOSE]}?${rr[nr.BUILD]}?`);Kr("LOOSE",`^${rr[nr.LOOSEPLAIN]}$`);Kr("GTLT","((?:<|>)?=?)");Kr("XRANGEIDENTIFIERLOOSE",`${rr[nr.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);Kr("XRANGEIDENTIFIER",`${rr[nr.NUMERICIDENTIFIER]}|x|X|\\*`);Kr("XRANGEPLAIN",`[v=\\s]*(${rr[nr.XRANGEIDENTIFIER]})(?:\\.(${rr[nr.XRANGEIDENTIFIER]})(?:\\.(${rr[nr.XRANGEIDENTIFIER]})(?:${rr[nr.PRERELEASE]})?${rr[nr.BUILD]}?)?)?`);Kr("XRANGEPLAINLOOSE",`[v=\\s]*(${rr[nr.XRANGEIDENTIFIERLOOSE]})(?:\\.(${rr[nr.XRANGEIDENTIFIERLOOSE]})(?:\\.(${rr[nr.XRANGEIDENTIFIERLOOSE]})(?:${rr[nr.PRERELEASELOOSE]})?${rr[nr.BUILD]}?)?)?`);Kr("XRANGE",`^${rr[nr.GTLT]}\\s*${rr[nr.XRANGEPLAIN]}$`);Kr("XRANGELOOSE",`^${rr[nr.GTLT]}\\s*${rr[nr.XRANGEPLAINLOOSE]}$`);Kr("COERCEPLAIN",`(^|[^\\d])(\\d{1,${LU}})(?:\\.(\\d{1,${LU}}))?(?:\\.(\\d{1,${LU}}))?`);Kr("COERCE",`${rr[nr.COERCEPLAIN]}(?:$|[^\\d])`);Kr("COERCEFULL",rr[nr.COERCEPLAIN]+`(?:${rr[nr.PRERELEASE]})?(?:${rr[nr.BUILD]})?(?:$|[^\\d])`);Kr("COERCERTL",rr[nr.COERCE],!0);Kr("COERCERTLFULL",rr[nr.COERCEFULL],!0);Kr("LONETILDE","(?:~>?)");Kr("TILDETRIM",`(\\s*)${rr[nr.LONETILDE]}\\s+`,!0);Sp.tildeTrimReplace="$1~";Kr("TILDE",`^${rr[nr.LONETILDE]}${rr[nr.XRANGEPLAIN]}$`);Kr("TILDELOOSE",`^${rr[nr.LONETILDE]}${rr[nr.XRANGEPLAINLOOSE]}$`);Kr("LONECARET","(?:\\^)");Kr("CARETTRIM",`(\\s*)${rr[nr.LONECARET]}\\s+`,!0);Sp.caretTrimReplace="$1^";Kr("CARET",`^${rr[nr.LONECARET]}${rr[nr.XRANGEPLAIN]}$`);Kr("CARETLOOSE",`^${rr[nr.LONECARET]}${rr[nr.XRANGEPLAINLOOSE]}$`);Kr("COMPARATORLOOSE",`^${rr[nr.GTLT]}\\s*(${rr[nr.LOOSEPLAIN]})$|^$`);Kr("COMPARATOR",`^${rr[nr.GTLT]}\\s*(${rr[nr.FULLPLAIN]})$|^$`);Kr("COMPARATORTRIM",`(\\s*)${rr[nr.GTLT]}\\s*(${rr[nr.LOOSEPLAIN]}|${rr[nr.XRANGEPLAIN]})`,!0);Sp.comparatorTrimReplace="$1$2$3";Kr("HYPHENRANGE",`^\\s*(${rr[nr.XRANGEPLAIN]})\\s+-\\s+(${rr[nr.XRANGEPLAIN]})\\s*$`);Kr("HYPHENRANGELOOSE",`^\\s*(${rr[nr.XRANGEPLAINLOOSE]})\\s+-\\s+(${rr[nr.XRANGEPLAINLOOSE]})\\s*$`);Kr("STAR","(<|>)?=?\\s*\\*");Kr("GTE0","^\\s*>=\\s*0\\.0\\.0\\s*$");Kr("GTE0PRE","^\\s*>=\\s*0\\.0\\.0-0\\s*$")});var ik=L((UWt,lie)=>{var MXe=Object.freeze({loose:!0}),_Xe=Object.freeze({}),UXe=t=>t?typeof t!="object"?MXe:t:_Xe;lie.exports=UXe});var _U=L((HWt,fie)=>{var cie=/^[0-9]+$/,uie=(t,e)=>{let r=cie.test(t),s=cie.test(e);return r&&s&&(t=+t,e=+e),t===e?0:r&&!s?-1:s&&!r?1:tuie(e,t);fie.exports={compareIdentifiers:uie,rcompareIdentifiers:HXe}});var Go=L((jWt,gie)=>{var sk=fB(),{MAX_LENGTH:Aie,MAX_SAFE_INTEGER:ok}=uB(),{safeRe:pie,t:hie}=vE(),jXe=ik(),{compareIdentifiers:SE}=_U(),UU=class t{constructor(e,r){if(r=jXe(r),e instanceof t){if(e.loose===!!r.loose&&e.includePrerelease===!!r.includePrerelease)return e;e=e.version}else if(typeof e!="string")throw new TypeError(`Invalid version. Must be a string. Got type "${typeof e}".`);if(e.length>Aie)throw new TypeError(`version is longer than ${Aie} characters`);sk("SemVer",e,r),this.options=r,this.loose=!!r.loose,this.includePrerelease=!!r.includePrerelease;let s=e.trim().match(r.loose?pie[hie.LOOSE]:pie[hie.FULL]);if(!s)throw new TypeError(`Invalid Version: ${e}`);if(this.raw=e,this.major=+s[1],this.minor=+s[2],this.patch=+s[3],this.major>ok||this.major<0)throw new TypeError("Invalid major version");if(this.minor>ok||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>ok||this.patch<0)throw new TypeError("Invalid patch version");s[4]?this.prerelease=s[4].split(".").map(a=>{if(/^[0-9]+$/.test(a)){let n=+a;if(n>=0&&n=0;)typeof this.prerelease[n]=="number"&&(this.prerelease[n]++,n=-2);if(n===-1){if(r===this.prerelease.join(".")&&s===!1)throw new Error("invalid increment argument: identifier already exists");this.prerelease.push(a)}}if(r){let n=[r,a];s===!1&&(n=[r]),SE(this.prerelease[0],r)===0?isNaN(this.prerelease[1])&&(this.prerelease=n):this.prerelease=n}break}default:throw new Error(`invalid increment argument: ${e}`)}return this.raw=this.format(),this.build.length&&(this.raw+=`+${this.build.join(".")}`),this}};gie.exports=UU});var _d=L((qWt,mie)=>{var die=Go(),qXe=(t,e,r=!1)=>{if(t instanceof die)return t;try{return new die(t,e)}catch(s){if(!r)return null;throw s}};mie.exports=qXe});var Eie=L((GWt,yie)=>{var GXe=_d(),WXe=(t,e)=>{let r=GXe(t,e);return r?r.version:null};yie.exports=WXe});var Cie=L((WWt,Iie)=>{var YXe=_d(),VXe=(t,e)=>{let r=YXe(t.trim().replace(/^[=v]+/,""),e);return r?r.version:null};Iie.exports=VXe});var vie=L((YWt,Bie)=>{var wie=Go(),KXe=(t,e,r,s,a)=>{typeof r=="string"&&(a=s,s=r,r=void 0);try{return new wie(t instanceof wie?t.version:t,r).inc(e,s,a).version}catch{return null}};Bie.exports=KXe});var bie=L((VWt,Die)=>{var Sie=_d(),JXe=(t,e)=>{let r=Sie(t,null,!0),s=Sie(e,null,!0),a=r.compare(s);if(a===0)return null;let n=a>0,c=n?r:s,f=n?s:r,p=!!c.prerelease.length;if(!!f.prerelease.length&&!p)return!f.patch&&!f.minor?"major":c.patch?"patch":c.minor?"minor":"major";let E=p?"pre":"";return r.major!==s.major?E+"major":r.minor!==s.minor?E+"minor":r.patch!==s.patch?E+"patch":"prerelease"};Die.exports=JXe});var xie=L((KWt,Pie)=>{var zXe=Go(),ZXe=(t,e)=>new zXe(t,e).major;Pie.exports=ZXe});var Qie=L((JWt,kie)=>{var XXe=Go(),$Xe=(t,e)=>new XXe(t,e).minor;kie.exports=$Xe});var Rie=L((zWt,Tie)=>{var e$e=Go(),t$e=(t,e)=>new e$e(t,e).patch;Tie.exports=t$e});var Nie=L((ZWt,Fie)=>{var r$e=_d(),n$e=(t,e)=>{let r=r$e(t,e);return r&&r.prerelease.length?r.prerelease:null};Fie.exports=n$e});var vc=L((XWt,Lie)=>{var Oie=Go(),i$e=(t,e,r)=>new Oie(t,r).compare(new Oie(e,r));Lie.exports=i$e});var _ie=L(($Wt,Mie)=>{var s$e=vc(),o$e=(t,e,r)=>s$e(e,t,r);Mie.exports=o$e});var Hie=L((eYt,Uie)=>{var a$e=vc(),l$e=(t,e)=>a$e(t,e,!0);Uie.exports=l$e});var ak=L((tYt,qie)=>{var jie=Go(),c$e=(t,e,r)=>{let s=new jie(t,r),a=new jie(e,r);return s.compare(a)||s.compareBuild(a)};qie.exports=c$e});var Wie=L((rYt,Gie)=>{var u$e=ak(),f$e=(t,e)=>t.sort((r,s)=>u$e(r,s,e));Gie.exports=f$e});var Vie=L((nYt,Yie)=>{var A$e=ak(),p$e=(t,e)=>t.sort((r,s)=>A$e(s,r,e));Yie.exports=p$e});var AB=L((iYt,Kie)=>{var h$e=vc(),g$e=(t,e,r)=>h$e(t,e,r)>0;Kie.exports=g$e});var lk=L((sYt,Jie)=>{var d$e=vc(),m$e=(t,e,r)=>d$e(t,e,r)<0;Jie.exports=m$e});var HU=L((oYt,zie)=>{var y$e=vc(),E$e=(t,e,r)=>y$e(t,e,r)===0;zie.exports=E$e});var jU=L((aYt,Zie)=>{var I$e=vc(),C$e=(t,e,r)=>I$e(t,e,r)!==0;Zie.exports=C$e});var ck=L((lYt,Xie)=>{var w$e=vc(),B$e=(t,e,r)=>w$e(t,e,r)>=0;Xie.exports=B$e});var uk=L((cYt,$ie)=>{var v$e=vc(),S$e=(t,e,r)=>v$e(t,e,r)<=0;$ie.exports=S$e});var qU=L((uYt,ese)=>{var D$e=HU(),b$e=jU(),P$e=AB(),x$e=ck(),k$e=lk(),Q$e=uk(),T$e=(t,e,r,s)=>{switch(e){case"===":return typeof t=="object"&&(t=t.version),typeof r=="object"&&(r=r.version),t===r;case"!==":return typeof t=="object"&&(t=t.version),typeof r=="object"&&(r=r.version),t!==r;case"":case"=":case"==":return D$e(t,r,s);case"!=":return b$e(t,r,s);case">":return P$e(t,r,s);case">=":return x$e(t,r,s);case"<":return k$e(t,r,s);case"<=":return Q$e(t,r,s);default:throw new TypeError(`Invalid operator: ${e}`)}};ese.exports=T$e});var rse=L((fYt,tse)=>{var R$e=Go(),F$e=_d(),{safeRe:fk,t:Ak}=vE(),N$e=(t,e)=>{if(t instanceof R$e)return t;if(typeof t=="number"&&(t=String(t)),typeof t!="string")return null;e=e||{};let r=null;if(!e.rtl)r=t.match(e.includePrerelease?fk[Ak.COERCEFULL]:fk[Ak.COERCE]);else{let p=e.includePrerelease?fk[Ak.COERCERTLFULL]:fk[Ak.COERCERTL],h;for(;(h=p.exec(t))&&(!r||r.index+r[0].length!==t.length);)(!r||h.index+h[0].length!==r.index+r[0].length)&&(r=h),p.lastIndex=h.index+h[1].length+h[2].length;p.lastIndex=-1}if(r===null)return null;let s=r[2],a=r[3]||"0",n=r[4]||"0",c=e.includePrerelease&&r[5]?`-${r[5]}`:"",f=e.includePrerelease&&r[6]?`+${r[6]}`:"";return F$e(`${s}.${a}.${n}${c}${f}`,e)};tse.exports=N$e});var ise=L((AYt,nse)=>{"use strict";nse.exports=function(t){t.prototype[Symbol.iterator]=function*(){for(let e=this.head;e;e=e.next)yield e.value}}});var pk=L((pYt,sse)=>{"use strict";sse.exports=Fn;Fn.Node=Ud;Fn.create=Fn;function Fn(t){var e=this;if(e instanceof Fn||(e=new Fn),e.tail=null,e.head=null,e.length=0,t&&typeof t.forEach=="function")t.forEach(function(a){e.push(a)});else if(arguments.length>0)for(var r=0,s=arguments.length;r1)r=e;else if(this.head)s=this.head.next,r=this.head.value;else throw new TypeError("Reduce of empty list with no initial value");for(var a=0;s!==null;a++)r=t(r,s.value,a),s=s.next;return r};Fn.prototype.reduceReverse=function(t,e){var r,s=this.tail;if(arguments.length>1)r=e;else if(this.tail)s=this.tail.prev,r=this.tail.value;else throw new TypeError("Reduce of empty list with no initial value");for(var a=this.length-1;s!==null;a--)r=t(r,s.value,a),s=s.prev;return r};Fn.prototype.toArray=function(){for(var t=new Array(this.length),e=0,r=this.head;r!==null;e++)t[e]=r.value,r=r.next;return t};Fn.prototype.toArrayReverse=function(){for(var t=new Array(this.length),e=0,r=this.tail;r!==null;e++)t[e]=r.value,r=r.prev;return t};Fn.prototype.slice=function(t,e){e=e||this.length,e<0&&(e+=this.length),t=t||0,t<0&&(t+=this.length);var r=new Fn;if(ethis.length&&(e=this.length);for(var s=0,a=this.head;a!==null&&sthis.length&&(e=this.length);for(var s=this.length,a=this.tail;a!==null&&s>e;s--)a=a.prev;for(;a!==null&&s>t;s--,a=a.prev)r.push(a.value);return r};Fn.prototype.splice=function(t,e,...r){t>this.length&&(t=this.length-1),t<0&&(t=this.length+t);for(var s=0,a=this.head;a!==null&&s{"use strict";var _$e=pk(),Hd=Symbol("max"),bp=Symbol("length"),DE=Symbol("lengthCalculator"),hB=Symbol("allowStale"),jd=Symbol("maxAge"),Dp=Symbol("dispose"),ose=Symbol("noDisposeOnSet"),Gs=Symbol("lruList"),_u=Symbol("cache"),lse=Symbol("updateAgeOnGet"),GU=()=>1,YU=class{constructor(e){if(typeof e=="number"&&(e={max:e}),e||(e={}),e.max&&(typeof e.max!="number"||e.max<0))throw new TypeError("max must be a non-negative number");let r=this[Hd]=e.max||1/0,s=e.length||GU;if(this[DE]=typeof s!="function"?GU:s,this[hB]=e.stale||!1,e.maxAge&&typeof e.maxAge!="number")throw new TypeError("maxAge must be a number");this[jd]=e.maxAge||0,this[Dp]=e.dispose,this[ose]=e.noDisposeOnSet||!1,this[lse]=e.updateAgeOnGet||!1,this.reset()}set max(e){if(typeof e!="number"||e<0)throw new TypeError("max must be a non-negative number");this[Hd]=e||1/0,pB(this)}get max(){return this[Hd]}set allowStale(e){this[hB]=!!e}get allowStale(){return this[hB]}set maxAge(e){if(typeof e!="number")throw new TypeError("maxAge must be a non-negative number");this[jd]=e,pB(this)}get maxAge(){return this[jd]}set lengthCalculator(e){typeof e!="function"&&(e=GU),e!==this[DE]&&(this[DE]=e,this[bp]=0,this[Gs].forEach(r=>{r.length=this[DE](r.value,r.key),this[bp]+=r.length})),pB(this)}get lengthCalculator(){return this[DE]}get length(){return this[bp]}get itemCount(){return this[Gs].length}rforEach(e,r){r=r||this;for(let s=this[Gs].tail;s!==null;){let a=s.prev;ase(this,e,s,r),s=a}}forEach(e,r){r=r||this;for(let s=this[Gs].head;s!==null;){let a=s.next;ase(this,e,s,r),s=a}}keys(){return this[Gs].toArray().map(e=>e.key)}values(){return this[Gs].toArray().map(e=>e.value)}reset(){this[Dp]&&this[Gs]&&this[Gs].length&&this[Gs].forEach(e=>this[Dp](e.key,e.value)),this[_u]=new Map,this[Gs]=new _$e,this[bp]=0}dump(){return this[Gs].map(e=>hk(this,e)?!1:{k:e.key,v:e.value,e:e.now+(e.maxAge||0)}).toArray().filter(e=>e)}dumpLru(){return this[Gs]}set(e,r,s){if(s=s||this[jd],s&&typeof s!="number")throw new TypeError("maxAge must be a number");let a=s?Date.now():0,n=this[DE](r,e);if(this[_u].has(e)){if(n>this[Hd])return bE(this,this[_u].get(e)),!1;let p=this[_u].get(e).value;return this[Dp]&&(this[ose]||this[Dp](e,p.value)),p.now=a,p.maxAge=s,p.value=r,this[bp]+=n-p.length,p.length=n,this.get(e),pB(this),!0}let c=new VU(e,r,n,a,s);return c.length>this[Hd]?(this[Dp]&&this[Dp](e,r),!1):(this[bp]+=c.length,this[Gs].unshift(c),this[_u].set(e,this[Gs].head),pB(this),!0)}has(e){if(!this[_u].has(e))return!1;let r=this[_u].get(e).value;return!hk(this,r)}get(e){return WU(this,e,!0)}peek(e){return WU(this,e,!1)}pop(){let e=this[Gs].tail;return e?(bE(this,e),e.value):null}del(e){bE(this,this[_u].get(e))}load(e){this.reset();let r=Date.now();for(let s=e.length-1;s>=0;s--){let a=e[s],n=a.e||0;if(n===0)this.set(a.k,a.v);else{let c=n-r;c>0&&this.set(a.k,a.v,c)}}}prune(){this[_u].forEach((e,r)=>WU(this,r,!1))}},WU=(t,e,r)=>{let s=t[_u].get(e);if(s){let a=s.value;if(hk(t,a)){if(bE(t,s),!t[hB])return}else r&&(t[lse]&&(s.value.now=Date.now()),t[Gs].unshiftNode(s));return a.value}},hk=(t,e)=>{if(!e||!e.maxAge&&!t[jd])return!1;let r=Date.now()-e.now;return e.maxAge?r>e.maxAge:t[jd]&&r>t[jd]},pB=t=>{if(t[bp]>t[Hd])for(let e=t[Gs].tail;t[bp]>t[Hd]&&e!==null;){let r=e.prev;bE(t,e),e=r}},bE=(t,e)=>{if(e){let r=e.value;t[Dp]&&t[Dp](r.key,r.value),t[bp]-=r.length,t[_u].delete(r.key),t[Gs].removeNode(e)}},VU=class{constructor(e,r,s,a,n){this.key=e,this.value=r,this.length=s,this.now=a,this.maxAge=n||0}},ase=(t,e,r,s)=>{let a=r.value;hk(t,a)&&(bE(t,r),t[hB]||(a=void 0)),a&&e.call(s,a.value,a.key,t)};cse.exports=YU});var Sc=L((gYt,hse)=>{var KU=class t{constructor(e,r){if(r=H$e(r),e instanceof t)return e.loose===!!r.loose&&e.includePrerelease===!!r.includePrerelease?e:new t(e.raw,r);if(e instanceof JU)return this.raw=e.value,this.set=[[e]],this.format(),this;if(this.options=r,this.loose=!!r.loose,this.includePrerelease=!!r.includePrerelease,this.raw=e.trim().split(/\s+/).join(" "),this.set=this.raw.split("||").map(s=>this.parseRange(s.trim())).filter(s=>s.length),!this.set.length)throw new TypeError(`Invalid SemVer Range: ${this.raw}`);if(this.set.length>1){let s=this.set[0];if(this.set=this.set.filter(a=>!Ase(a[0])),this.set.length===0)this.set=[s];else if(this.set.length>1){for(let a of this.set)if(a.length===1&&K$e(a[0])){this.set=[a];break}}}this.format()}format(){return this.range=this.set.map(e=>e.join(" ").trim()).join("||").trim(),this.range}toString(){return this.range}parseRange(e){let s=((this.options.includePrerelease&&Y$e)|(this.options.loose&&V$e))+":"+e,a=fse.get(s);if(a)return a;let n=this.options.loose,c=n?ol[Ba.HYPHENRANGELOOSE]:ol[Ba.HYPHENRANGE];e=e.replace(c,iet(this.options.includePrerelease)),vi("hyphen replace",e),e=e.replace(ol[Ba.COMPARATORTRIM],q$e),vi("comparator trim",e),e=e.replace(ol[Ba.TILDETRIM],G$e),vi("tilde trim",e),e=e.replace(ol[Ba.CARETTRIM],W$e),vi("caret trim",e);let f=e.split(" ").map(C=>J$e(C,this.options)).join(" ").split(/\s+/).map(C=>net(C,this.options));n&&(f=f.filter(C=>(vi("loose invalid filter",C,this.options),!!C.match(ol[Ba.COMPARATORLOOSE])))),vi("range list",f);let p=new Map,h=f.map(C=>new JU(C,this.options));for(let C of h){if(Ase(C))return[C];p.set(C.value,C)}p.size>1&&p.has("")&&p.delete("");let E=[...p.values()];return fse.set(s,E),E}intersects(e,r){if(!(e instanceof t))throw new TypeError("a Range is required");return this.set.some(s=>pse(s,r)&&e.set.some(a=>pse(a,r)&&s.every(n=>a.every(c=>n.intersects(c,r)))))}test(e){if(!e)return!1;if(typeof e=="string")try{e=new j$e(e,this.options)}catch{return!1}for(let r=0;rt.value==="<0.0.0-0",K$e=t=>t.value==="",pse=(t,e)=>{let r=!0,s=t.slice(),a=s.pop();for(;r&&s.length;)r=s.every(n=>a.intersects(n,e)),a=s.pop();return r},J$e=(t,e)=>(vi("comp",t,e),t=X$e(t,e),vi("caret",t),t=z$e(t,e),vi("tildes",t),t=eet(t,e),vi("xrange",t),t=ret(t,e),vi("stars",t),t),va=t=>!t||t.toLowerCase()==="x"||t==="*",z$e=(t,e)=>t.trim().split(/\s+/).map(r=>Z$e(r,e)).join(" "),Z$e=(t,e)=>{let r=e.loose?ol[Ba.TILDELOOSE]:ol[Ba.TILDE];return t.replace(r,(s,a,n,c,f)=>{vi("tilde",t,s,a,n,c,f);let p;return va(a)?p="":va(n)?p=`>=${a}.0.0 <${+a+1}.0.0-0`:va(c)?p=`>=${a}.${n}.0 <${a}.${+n+1}.0-0`:f?(vi("replaceTilde pr",f),p=`>=${a}.${n}.${c}-${f} <${a}.${+n+1}.0-0`):p=`>=${a}.${n}.${c} <${a}.${+n+1}.0-0`,vi("tilde return",p),p})},X$e=(t,e)=>t.trim().split(/\s+/).map(r=>$$e(r,e)).join(" "),$$e=(t,e)=>{vi("caret",t,e);let r=e.loose?ol[Ba.CARETLOOSE]:ol[Ba.CARET],s=e.includePrerelease?"-0":"";return t.replace(r,(a,n,c,f,p)=>{vi("caret",t,a,n,c,f,p);let h;return va(n)?h="":va(c)?h=`>=${n}.0.0${s} <${+n+1}.0.0-0`:va(f)?n==="0"?h=`>=${n}.${c}.0${s} <${n}.${+c+1}.0-0`:h=`>=${n}.${c}.0${s} <${+n+1}.0.0-0`:p?(vi("replaceCaret pr",p),n==="0"?c==="0"?h=`>=${n}.${c}.${f}-${p} <${n}.${c}.${+f+1}-0`:h=`>=${n}.${c}.${f}-${p} <${n}.${+c+1}.0-0`:h=`>=${n}.${c}.${f}-${p} <${+n+1}.0.0-0`):(vi("no pr"),n==="0"?c==="0"?h=`>=${n}.${c}.${f}${s} <${n}.${c}.${+f+1}-0`:h=`>=${n}.${c}.${f}${s} <${n}.${+c+1}.0-0`:h=`>=${n}.${c}.${f} <${+n+1}.0.0-0`),vi("caret return",h),h})},eet=(t,e)=>(vi("replaceXRanges",t,e),t.split(/\s+/).map(r=>tet(r,e)).join(" ")),tet=(t,e)=>{t=t.trim();let r=e.loose?ol[Ba.XRANGELOOSE]:ol[Ba.XRANGE];return t.replace(r,(s,a,n,c,f,p)=>{vi("xRange",t,s,a,n,c,f,p);let h=va(n),E=h||va(c),C=E||va(f),S=C;return a==="="&&S&&(a=""),p=e.includePrerelease?"-0":"",h?a===">"||a==="<"?s="<0.0.0-0":s="*":a&&S?(E&&(c=0),f=0,a===">"?(a=">=",E?(n=+n+1,c=0,f=0):(c=+c+1,f=0)):a==="<="&&(a="<",E?n=+n+1:c=+c+1),a==="<"&&(p="-0"),s=`${a+n}.${c}.${f}${p}`):E?s=`>=${n}.0.0${p} <${+n+1}.0.0-0`:C&&(s=`>=${n}.${c}.0${p} <${n}.${+c+1}.0-0`),vi("xRange return",s),s})},ret=(t,e)=>(vi("replaceStars",t,e),t.trim().replace(ol[Ba.STAR],"")),net=(t,e)=>(vi("replaceGTE0",t,e),t.trim().replace(ol[e.includePrerelease?Ba.GTE0PRE:Ba.GTE0],"")),iet=t=>(e,r,s,a,n,c,f,p,h,E,C,S,P)=>(va(s)?r="":va(a)?r=`>=${s}.0.0${t?"-0":""}`:va(n)?r=`>=${s}.${a}.0${t?"-0":""}`:c?r=`>=${r}`:r=`>=${r}${t?"-0":""}`,va(h)?p="":va(E)?p=`<${+h+1}.0.0-0`:va(C)?p=`<${h}.${+E+1}.0-0`:S?p=`<=${h}.${E}.${C}-${S}`:t?p=`<${h}.${E}.${+C+1}-0`:p=`<=${p}`,`${r} ${p}`.trim()),set=(t,e,r)=>{for(let s=0;s0){let a=t[s].semver;if(a.major===e.major&&a.minor===e.minor&&a.patch===e.patch)return!0}return!1}return!0}});var gB=L((dYt,Ise)=>{var dB=Symbol("SemVer ANY"),XU=class t{static get ANY(){return dB}constructor(e,r){if(r=gse(r),e instanceof t){if(e.loose===!!r.loose)return e;e=e.value}e=e.trim().split(/\s+/).join(" "),ZU("comparator",e,r),this.options=r,this.loose=!!r.loose,this.parse(e),this.semver===dB?this.value="":this.value=this.operator+this.semver.version,ZU("comp",this)}parse(e){let r=this.options.loose?dse[mse.COMPARATORLOOSE]:dse[mse.COMPARATOR],s=e.match(r);if(!s)throw new TypeError(`Invalid comparator: ${e}`);this.operator=s[1]!==void 0?s[1]:"",this.operator==="="&&(this.operator=""),s[2]?this.semver=new yse(s[2],this.options.loose):this.semver=dB}toString(){return this.value}test(e){if(ZU("Comparator.test",e,this.options.loose),this.semver===dB||e===dB)return!0;if(typeof e=="string")try{e=new yse(e,this.options)}catch{return!1}return zU(e,this.operator,this.semver,this.options)}intersects(e,r){if(!(e instanceof t))throw new TypeError("a Comparator is required");return this.operator===""?this.value===""?!0:new Ese(e.value,r).test(this.value):e.operator===""?e.value===""?!0:new Ese(this.value,r).test(e.semver):(r=gse(r),r.includePrerelease&&(this.value==="<0.0.0-0"||e.value==="<0.0.0-0")||!r.includePrerelease&&(this.value.startsWith("<0.0.0")||e.value.startsWith("<0.0.0"))?!1:!!(this.operator.startsWith(">")&&e.operator.startsWith(">")||this.operator.startsWith("<")&&e.operator.startsWith("<")||this.semver.version===e.semver.version&&this.operator.includes("=")&&e.operator.includes("=")||zU(this.semver,"<",e.semver,r)&&this.operator.startsWith(">")&&e.operator.startsWith("<")||zU(this.semver,">",e.semver,r)&&this.operator.startsWith("<")&&e.operator.startsWith(">")))}};Ise.exports=XU;var gse=ik(),{safeRe:dse,t:mse}=vE(),zU=qU(),ZU=fB(),yse=Go(),Ese=Sc()});var mB=L((mYt,Cse)=>{var oet=Sc(),aet=(t,e,r)=>{try{e=new oet(e,r)}catch{return!1}return e.test(t)};Cse.exports=aet});var Bse=L((yYt,wse)=>{var cet=Sc(),uet=(t,e)=>new cet(t,e).set.map(r=>r.map(s=>s.value).join(" ").trim().split(" "));wse.exports=uet});var Sse=L((EYt,vse)=>{var fet=Go(),Aet=Sc(),pet=(t,e,r)=>{let s=null,a=null,n=null;try{n=new Aet(e,r)}catch{return null}return t.forEach(c=>{n.test(c)&&(!s||a.compare(c)===-1)&&(s=c,a=new fet(s,r))}),s};vse.exports=pet});var bse=L((IYt,Dse)=>{var het=Go(),get=Sc(),det=(t,e,r)=>{let s=null,a=null,n=null;try{n=new get(e,r)}catch{return null}return t.forEach(c=>{n.test(c)&&(!s||a.compare(c)===1)&&(s=c,a=new het(s,r))}),s};Dse.exports=det});var kse=L((CYt,xse)=>{var $U=Go(),met=Sc(),Pse=AB(),yet=(t,e)=>{t=new met(t,e);let r=new $U("0.0.0");if(t.test(r)||(r=new $U("0.0.0-0"),t.test(r)))return r;r=null;for(let s=0;s{let f=new $U(c.semver.version);switch(c.operator){case">":f.prerelease.length===0?f.patch++:f.prerelease.push(0),f.raw=f.format();case"":case">=":(!n||Pse(f,n))&&(n=f);break;case"<":case"<=":break;default:throw new Error(`Unexpected operation: ${c.operator}`)}}),n&&(!r||Pse(r,n))&&(r=n)}return r&&t.test(r)?r:null};xse.exports=yet});var Tse=L((wYt,Qse)=>{var Eet=Sc(),Iet=(t,e)=>{try{return new Eet(t,e).range||"*"}catch{return null}};Qse.exports=Iet});var gk=L((BYt,Ose)=>{var Cet=Go(),Nse=gB(),{ANY:wet}=Nse,Bet=Sc(),vet=mB(),Rse=AB(),Fse=lk(),Det=uk(),bet=ck(),Pet=(t,e,r,s)=>{t=new Cet(t,s),e=new Bet(e,s);let a,n,c,f,p;switch(r){case">":a=Rse,n=Det,c=Fse,f=">",p=">=";break;case"<":a=Fse,n=bet,c=Rse,f="<",p="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(vet(t,e,s))return!1;for(let h=0;h{P.semver===wet&&(P=new Nse(">=0.0.0")),C=C||P,S=S||P,a(P.semver,C.semver,s)?C=P:c(P.semver,S.semver,s)&&(S=P)}),C.operator===f||C.operator===p||(!S.operator||S.operator===f)&&n(t,S.semver))return!1;if(S.operator===p&&c(t,S.semver))return!1}return!0};Ose.exports=Pet});var Mse=L((vYt,Lse)=>{var xet=gk(),ket=(t,e,r)=>xet(t,e,">",r);Lse.exports=ket});var Use=L((SYt,_se)=>{var Qet=gk(),Tet=(t,e,r)=>Qet(t,e,"<",r);_se.exports=Tet});var qse=L((DYt,jse)=>{var Hse=Sc(),Ret=(t,e,r)=>(t=new Hse(t,r),e=new Hse(e,r),t.intersects(e,r));jse.exports=Ret});var Wse=L((bYt,Gse)=>{var Fet=mB(),Net=vc();Gse.exports=(t,e,r)=>{let s=[],a=null,n=null,c=t.sort((E,C)=>Net(E,C,r));for(let E of c)Fet(E,e,r)?(n=E,a||(a=E)):(n&&s.push([a,n]),n=null,a=null);a&&s.push([a,null]);let f=[];for(let[E,C]of s)E===C?f.push(E):!C&&E===c[0]?f.push("*"):C?E===c[0]?f.push(`<=${C}`):f.push(`${E} - ${C}`):f.push(`>=${E}`);let p=f.join(" || "),h=typeof e.raw=="string"?e.raw:String(e);return p.length{var Yse=Sc(),t4=gB(),{ANY:e4}=t4,yB=mB(),r4=vc(),Oet=(t,e,r={})=>{if(t===e)return!0;t=new Yse(t,r),e=new Yse(e,r);let s=!1;e:for(let a of t.set){for(let n of e.set){let c=Met(a,n,r);if(s=s||c!==null,c)continue e}if(s)return!1}return!0},Let=[new t4(">=0.0.0-0")],Vse=[new t4(">=0.0.0")],Met=(t,e,r)=>{if(t===e)return!0;if(t.length===1&&t[0].semver===e4){if(e.length===1&&e[0].semver===e4)return!0;r.includePrerelease?t=Let:t=Vse}if(e.length===1&&e[0].semver===e4){if(r.includePrerelease)return!0;e=Vse}let s=new Set,a,n;for(let P of t)P.operator===">"||P.operator===">="?a=Kse(a,P,r):P.operator==="<"||P.operator==="<="?n=Jse(n,P,r):s.add(P.semver);if(s.size>1)return null;let c;if(a&&n){if(c=r4(a.semver,n.semver,r),c>0)return null;if(c===0&&(a.operator!==">="||n.operator!=="<="))return null}for(let P of s){if(a&&!yB(P,String(a),r)||n&&!yB(P,String(n),r))return null;for(let I of e)if(!yB(P,String(I),r))return!1;return!0}let f,p,h,E,C=n&&!r.includePrerelease&&n.semver.prerelease.length?n.semver:!1,S=a&&!r.includePrerelease&&a.semver.prerelease.length?a.semver:!1;C&&C.prerelease.length===1&&n.operator==="<"&&C.prerelease[0]===0&&(C=!1);for(let P of e){if(E=E||P.operator===">"||P.operator===">=",h=h||P.operator==="<"||P.operator==="<=",a){if(S&&P.semver.prerelease&&P.semver.prerelease.length&&P.semver.major===S.major&&P.semver.minor===S.minor&&P.semver.patch===S.patch&&(S=!1),P.operator===">"||P.operator===">="){if(f=Kse(a,P,r),f===P&&f!==a)return!1}else if(a.operator===">="&&!yB(a.semver,String(P),r))return!1}if(n){if(C&&P.semver.prerelease&&P.semver.prerelease.length&&P.semver.major===C.major&&P.semver.minor===C.minor&&P.semver.patch===C.patch&&(C=!1),P.operator==="<"||P.operator==="<="){if(p=Jse(n,P,r),p===P&&p!==n)return!1}else if(n.operator==="<="&&!yB(n.semver,String(P),r))return!1}if(!P.operator&&(n||a)&&c!==0)return!1}return!(a&&h&&!n&&c!==0||n&&E&&!a&&c!==0||S||C)},Kse=(t,e,r)=>{if(!t)return e;let s=r4(t.semver,e.semver,r);return s>0?t:s<0||e.operator===">"&&t.operator===">="?e:t},Jse=(t,e,r)=>{if(!t)return e;let s=r4(t.semver,e.semver,r);return s<0?t:s>0||e.operator==="<"&&t.operator==="<="?e:t};zse.exports=Oet});var fi=L((xYt,eoe)=>{var n4=vE(),Xse=uB(),_et=Go(),$se=_U(),Uet=_d(),Het=Eie(),jet=Cie(),qet=vie(),Get=bie(),Wet=xie(),Yet=Qie(),Vet=Rie(),Ket=Nie(),Jet=vc(),zet=_ie(),Zet=Hie(),Xet=ak(),$et=Wie(),ett=Vie(),ttt=AB(),rtt=lk(),ntt=HU(),itt=jU(),stt=ck(),ott=uk(),att=qU(),ltt=rse(),ctt=gB(),utt=Sc(),ftt=mB(),Att=Bse(),ptt=Sse(),htt=bse(),gtt=kse(),dtt=Tse(),mtt=gk(),ytt=Mse(),Ett=Use(),Itt=qse(),Ctt=Wse(),wtt=Zse();eoe.exports={parse:Uet,valid:Het,clean:jet,inc:qet,diff:Get,major:Wet,minor:Yet,patch:Vet,prerelease:Ket,compare:Jet,rcompare:zet,compareLoose:Zet,compareBuild:Xet,sort:$et,rsort:ett,gt:ttt,lt:rtt,eq:ntt,neq:itt,gte:stt,lte:ott,cmp:att,coerce:ltt,Comparator:ctt,Range:utt,satisfies:ftt,toComparators:Att,maxSatisfying:ptt,minSatisfying:htt,minVersion:gtt,validRange:dtt,outside:mtt,gtr:ytt,ltr:Ett,intersects:Itt,simplifyRange:Ctt,subset:wtt,SemVer:_et,re:n4.re,src:n4.src,tokens:n4.t,SEMVER_SPEC_VERSION:Xse.SEMVER_SPEC_VERSION,RELEASE_TYPES:Xse.RELEASE_TYPES,compareIdentifiers:$se.compareIdentifiers,rcompareIdentifiers:$se.rcompareIdentifiers}});var roe=L((kYt,toe)=>{"use strict";function Btt(t,e){function r(){this.constructor=t}r.prototype=e.prototype,t.prototype=new r}function qd(t,e,r,s){this.message=t,this.expected=e,this.found=r,this.location=s,this.name="SyntaxError",typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,qd)}Btt(qd,Error);qd.buildMessage=function(t,e){var r={literal:function(h){return'"'+a(h.text)+'"'},class:function(h){var E="",C;for(C=0;C0){for(C=1,S=1;C{switch(Te[1]){case"|":return xe|Te[3];case"&":return xe&Te[3];case"^":return xe^Te[3]}},$)},S="!",P=Fe("!",!1),I=function($){return!$},R="(",N=Fe("(",!1),U=")",W=Fe(")",!1),te=function($){return $},ie=/^[^ \t\n\r()!|&\^]/,Ae=Ne([" "," ",` +`,"\r","(",")","!","|","&","^"],!0,!1),ce=function($){return e.queryPattern.test($)},me=function($){return e.checkFn($)},pe=ke("whitespace"),Be=/^[ \t\n\r]/,Ce=Ne([" "," ",` +`,"\r"],!1,!1),g=0,we=0,Ee=[{line:1,column:1}],fe=0,se=[],X=0,De;if("startRule"in e){if(!(e.startRule in s))throw new Error(`Can't start parsing from rule "`+e.startRule+'".');a=s[e.startRule]}function Re(){return t.substring(we,g)}function gt(){return _e(we,g)}function j($,oe){throw oe=oe!==void 0?oe:_e(we,g),b([ke($)],t.substring(we,g),oe)}function rt($,oe){throw oe=oe!==void 0?oe:_e(we,g),w($,oe)}function Fe($,oe){return{type:"literal",text:$,ignoreCase:oe}}function Ne($,oe,xe){return{type:"class",parts:$,inverted:oe,ignoreCase:xe}}function Pe(){return{type:"any"}}function Ye(){return{type:"end"}}function ke($){return{type:"other",description:$}}function it($){var oe=Ee[$],xe;if(oe)return oe;for(xe=$-1;!Ee[xe];)xe--;for(oe=Ee[xe],oe={line:oe.line,column:oe.column};xe<$;)t.charCodeAt(xe)===10?(oe.line++,oe.column=1):oe.column++,xe++;return Ee[$]=oe,oe}function _e($,oe){var xe=it($),Te=it(oe);return{start:{offset:$,line:xe.line,column:xe.column},end:{offset:oe,line:Te.line,column:Te.column}}}function x($){gfe&&(fe=g,se=[]),se.push($))}function w($,oe){return new qd($,null,null,oe)}function b($,oe,xe){return new qd(qd.buildMessage($,oe),$,oe,xe)}function y(){var $,oe,xe,Te,lt,Et,qt,ir;if($=g,oe=F(),oe!==r){for(xe=[],Te=g,lt=Z(),lt!==r?(t.charCodeAt(g)===124?(Et=n,g++):(Et=r,X===0&&x(c)),Et===r&&(t.charCodeAt(g)===38?(Et=f,g++):(Et=r,X===0&&x(p)),Et===r&&(t.charCodeAt(g)===94?(Et=h,g++):(Et=r,X===0&&x(E)))),Et!==r?(qt=Z(),qt!==r?(ir=F(),ir!==r?(lt=[lt,Et,qt,ir],Te=lt):(g=Te,Te=r)):(g=Te,Te=r)):(g=Te,Te=r)):(g=Te,Te=r);Te!==r;)xe.push(Te),Te=g,lt=Z(),lt!==r?(t.charCodeAt(g)===124?(Et=n,g++):(Et=r,X===0&&x(c)),Et===r&&(t.charCodeAt(g)===38?(Et=f,g++):(Et=r,X===0&&x(p)),Et===r&&(t.charCodeAt(g)===94?(Et=h,g++):(Et=r,X===0&&x(E)))),Et!==r?(qt=Z(),qt!==r?(ir=F(),ir!==r?(lt=[lt,Et,qt,ir],Te=lt):(g=Te,Te=r)):(g=Te,Te=r)):(g=Te,Te=r)):(g=Te,Te=r);xe!==r?(we=$,oe=C(oe,xe),$=oe):(g=$,$=r)}else g=$,$=r;return $}function F(){var $,oe,xe,Te,lt,Et;return $=g,t.charCodeAt(g)===33?(oe=S,g++):(oe=r,X===0&&x(P)),oe!==r?(xe=F(),xe!==r?(we=$,oe=I(xe),$=oe):(g=$,$=r)):(g=$,$=r),$===r&&($=g,t.charCodeAt(g)===40?(oe=R,g++):(oe=r,X===0&&x(N)),oe!==r?(xe=Z(),xe!==r?(Te=y(),Te!==r?(lt=Z(),lt!==r?(t.charCodeAt(g)===41?(Et=U,g++):(Et=r,X===0&&x(W)),Et!==r?(we=$,oe=te(Te),$=oe):(g=$,$=r)):(g=$,$=r)):(g=$,$=r)):(g=$,$=r)):(g=$,$=r),$===r&&($=z())),$}function z(){var $,oe,xe,Te,lt;if($=g,oe=Z(),oe!==r){if(xe=g,Te=[],ie.test(t.charAt(g))?(lt=t.charAt(g),g++):(lt=r,X===0&&x(Ae)),lt!==r)for(;lt!==r;)Te.push(lt),ie.test(t.charAt(g))?(lt=t.charAt(g),g++):(lt=r,X===0&&x(Ae));else Te=r;Te!==r?xe=t.substring(xe,g):xe=Te,xe!==r?(we=g,Te=ce(xe),Te?Te=void 0:Te=r,Te!==r?(we=$,oe=me(xe),$=oe):(g=$,$=r)):(g=$,$=r)}else g=$,$=r;return $}function Z(){var $,oe;for(X++,$=[],Be.test(t.charAt(g))?(oe=t.charAt(g),g++):(oe=r,X===0&&x(Ce));oe!==r;)$.push(oe),Be.test(t.charAt(g))?(oe=t.charAt(g),g++):(oe=r,X===0&&x(Ce));return X--,$===r&&(oe=r,X===0&&x(pe)),$}if(De=a(),De!==r&&g===t.length)return De;throw De!==r&&g{var{parse:Stt}=roe();dk.makeParser=(t=/[a-z]+/)=>(e,r)=>Stt(e,{queryPattern:t,checkFn:r});dk.parse=dk.makeParser()});var soe=L((TYt,ioe)=>{"use strict";ioe.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}});var i4=L((RYt,aoe)=>{var EB=soe(),ooe={};for(let t of Object.keys(EB))ooe[EB[t]]=t;var hr={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};aoe.exports=hr;for(let t of Object.keys(hr)){if(!("channels"in hr[t]))throw new Error("missing channels property: "+t);if(!("labels"in hr[t]))throw new Error("missing channel labels property: "+t);if(hr[t].labels.length!==hr[t].channels)throw new Error("channel and label counts mismatch: "+t);let{channels:e,labels:r}=hr[t];delete hr[t].channels,delete hr[t].labels,Object.defineProperty(hr[t],"channels",{value:e}),Object.defineProperty(hr[t],"labels",{value:r})}hr.rgb.hsl=function(t){let e=t[0]/255,r=t[1]/255,s=t[2]/255,a=Math.min(e,r,s),n=Math.max(e,r,s),c=n-a,f,p;n===a?f=0:e===n?f=(r-s)/c:r===n?f=2+(s-e)/c:s===n&&(f=4+(e-r)/c),f=Math.min(f*60,360),f<0&&(f+=360);let h=(a+n)/2;return n===a?p=0:h<=.5?p=c/(n+a):p=c/(2-n-a),[f,p*100,h*100]};hr.rgb.hsv=function(t){let e,r,s,a,n,c=t[0]/255,f=t[1]/255,p=t[2]/255,h=Math.max(c,f,p),E=h-Math.min(c,f,p),C=function(S){return(h-S)/6/E+1/2};return E===0?(a=0,n=0):(n=E/h,e=C(c),r=C(f),s=C(p),c===h?a=s-r:f===h?a=1/3+e-s:p===h&&(a=2/3+r-e),a<0?a+=1:a>1&&(a-=1)),[a*360,n*100,h*100]};hr.rgb.hwb=function(t){let e=t[0],r=t[1],s=t[2],a=hr.rgb.hsl(t)[0],n=1/255*Math.min(e,Math.min(r,s));return s=1-1/255*Math.max(e,Math.max(r,s)),[a,n*100,s*100]};hr.rgb.cmyk=function(t){let e=t[0]/255,r=t[1]/255,s=t[2]/255,a=Math.min(1-e,1-r,1-s),n=(1-e-a)/(1-a)||0,c=(1-r-a)/(1-a)||0,f=(1-s-a)/(1-a)||0;return[n*100,c*100,f*100,a*100]};function Dtt(t,e){return(t[0]-e[0])**2+(t[1]-e[1])**2+(t[2]-e[2])**2}hr.rgb.keyword=function(t){let e=ooe[t];if(e)return e;let r=1/0,s;for(let a of Object.keys(EB)){let n=EB[a],c=Dtt(t,n);c.04045?((e+.055)/1.055)**2.4:e/12.92,r=r>.04045?((r+.055)/1.055)**2.4:r/12.92,s=s>.04045?((s+.055)/1.055)**2.4:s/12.92;let a=e*.4124+r*.3576+s*.1805,n=e*.2126+r*.7152+s*.0722,c=e*.0193+r*.1192+s*.9505;return[a*100,n*100,c*100]};hr.rgb.lab=function(t){let e=hr.rgb.xyz(t),r=e[0],s=e[1],a=e[2];r/=95.047,s/=100,a/=108.883,r=r>.008856?r**(1/3):7.787*r+16/116,s=s>.008856?s**(1/3):7.787*s+16/116,a=a>.008856?a**(1/3):7.787*a+16/116;let n=116*s-16,c=500*(r-s),f=200*(s-a);return[n,c,f]};hr.hsl.rgb=function(t){let e=t[0]/360,r=t[1]/100,s=t[2]/100,a,n,c;if(r===0)return c=s*255,[c,c,c];s<.5?a=s*(1+r):a=s+r-s*r;let f=2*s-a,p=[0,0,0];for(let h=0;h<3;h++)n=e+1/3*-(h-1),n<0&&n++,n>1&&n--,6*n<1?c=f+(a-f)*6*n:2*n<1?c=a:3*n<2?c=f+(a-f)*(2/3-n)*6:c=f,p[h]=c*255;return p};hr.hsl.hsv=function(t){let e=t[0],r=t[1]/100,s=t[2]/100,a=r,n=Math.max(s,.01);s*=2,r*=s<=1?s:2-s,a*=n<=1?n:2-n;let c=(s+r)/2,f=s===0?2*a/(n+a):2*r/(s+r);return[e,f*100,c*100]};hr.hsv.rgb=function(t){let e=t[0]/60,r=t[1]/100,s=t[2]/100,a=Math.floor(e)%6,n=e-Math.floor(e),c=255*s*(1-r),f=255*s*(1-r*n),p=255*s*(1-r*(1-n));switch(s*=255,a){case 0:return[s,p,c];case 1:return[f,s,c];case 2:return[c,s,p];case 3:return[c,f,s];case 4:return[p,c,s];case 5:return[s,c,f]}};hr.hsv.hsl=function(t){let e=t[0],r=t[1]/100,s=t[2]/100,a=Math.max(s,.01),n,c;c=(2-r)*s;let f=(2-r)*a;return n=r*a,n/=f<=1?f:2-f,n=n||0,c/=2,[e,n*100,c*100]};hr.hwb.rgb=function(t){let e=t[0]/360,r=t[1]/100,s=t[2]/100,a=r+s,n;a>1&&(r/=a,s/=a);let c=Math.floor(6*e),f=1-s;n=6*e-c,c&1&&(n=1-n);let p=r+n*(f-r),h,E,C;switch(c){default:case 6:case 0:h=f,E=p,C=r;break;case 1:h=p,E=f,C=r;break;case 2:h=r,E=f,C=p;break;case 3:h=r,E=p,C=f;break;case 4:h=p,E=r,C=f;break;case 5:h=f,E=r,C=p;break}return[h*255,E*255,C*255]};hr.cmyk.rgb=function(t){let e=t[0]/100,r=t[1]/100,s=t[2]/100,a=t[3]/100,n=1-Math.min(1,e*(1-a)+a),c=1-Math.min(1,r*(1-a)+a),f=1-Math.min(1,s*(1-a)+a);return[n*255,c*255,f*255]};hr.xyz.rgb=function(t){let e=t[0]/100,r=t[1]/100,s=t[2]/100,a,n,c;return a=e*3.2406+r*-1.5372+s*-.4986,n=e*-.9689+r*1.8758+s*.0415,c=e*.0557+r*-.204+s*1.057,a=a>.0031308?1.055*a**(1/2.4)-.055:a*12.92,n=n>.0031308?1.055*n**(1/2.4)-.055:n*12.92,c=c>.0031308?1.055*c**(1/2.4)-.055:c*12.92,a=Math.min(Math.max(0,a),1),n=Math.min(Math.max(0,n),1),c=Math.min(Math.max(0,c),1),[a*255,n*255,c*255]};hr.xyz.lab=function(t){let e=t[0],r=t[1],s=t[2];e/=95.047,r/=100,s/=108.883,e=e>.008856?e**(1/3):7.787*e+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,s=s>.008856?s**(1/3):7.787*s+16/116;let a=116*r-16,n=500*(e-r),c=200*(r-s);return[a,n,c]};hr.lab.xyz=function(t){let e=t[0],r=t[1],s=t[2],a,n,c;n=(e+16)/116,a=r/500+n,c=n-s/200;let f=n**3,p=a**3,h=c**3;return n=f>.008856?f:(n-16/116)/7.787,a=p>.008856?p:(a-16/116)/7.787,c=h>.008856?h:(c-16/116)/7.787,a*=95.047,n*=100,c*=108.883,[a,n,c]};hr.lab.lch=function(t){let e=t[0],r=t[1],s=t[2],a;a=Math.atan2(s,r)*360/2/Math.PI,a<0&&(a+=360);let c=Math.sqrt(r*r+s*s);return[e,c,a]};hr.lch.lab=function(t){let e=t[0],r=t[1],a=t[2]/360*2*Math.PI,n=r*Math.cos(a),c=r*Math.sin(a);return[e,n,c]};hr.rgb.ansi16=function(t,e=null){let[r,s,a]=t,n=e===null?hr.rgb.hsv(t)[2]:e;if(n=Math.round(n/50),n===0)return 30;let c=30+(Math.round(a/255)<<2|Math.round(s/255)<<1|Math.round(r/255));return n===2&&(c+=60),c};hr.hsv.ansi16=function(t){return hr.rgb.ansi16(hr.hsv.rgb(t),t[2])};hr.rgb.ansi256=function(t){let e=t[0],r=t[1],s=t[2];return e===r&&r===s?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(r/255*5)+Math.round(s/255*5)};hr.ansi16.rgb=function(t){let e=t%10;if(e===0||e===7)return t>50&&(e+=3.5),e=e/10.5*255,[e,e,e];let r=(~~(t>50)+1)*.5,s=(e&1)*r*255,a=(e>>1&1)*r*255,n=(e>>2&1)*r*255;return[s,a,n]};hr.ansi256.rgb=function(t){if(t>=232){let n=(t-232)*10+8;return[n,n,n]}t-=16;let e,r=Math.floor(t/36)/5*255,s=Math.floor((e=t%36)/6)/5*255,a=e%6/5*255;return[r,s,a]};hr.rgb.hex=function(t){let r=(((Math.round(t[0])&255)<<16)+((Math.round(t[1])&255)<<8)+(Math.round(t[2])&255)).toString(16).toUpperCase();return"000000".substring(r.length)+r};hr.hex.rgb=function(t){let e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];let r=e[0];e[0].length===3&&(r=r.split("").map(f=>f+f).join(""));let s=parseInt(r,16),a=s>>16&255,n=s>>8&255,c=s&255;return[a,n,c]};hr.rgb.hcg=function(t){let e=t[0]/255,r=t[1]/255,s=t[2]/255,a=Math.max(Math.max(e,r),s),n=Math.min(Math.min(e,r),s),c=a-n,f,p;return c<1?f=n/(1-c):f=0,c<=0?p=0:a===e?p=(r-s)/c%6:a===r?p=2+(s-e)/c:p=4+(e-r)/c,p/=6,p%=1,[p*360,c*100,f*100]};hr.hsl.hcg=function(t){let e=t[1]/100,r=t[2]/100,s=r<.5?2*e*r:2*e*(1-r),a=0;return s<1&&(a=(r-.5*s)/(1-s)),[t[0],s*100,a*100]};hr.hsv.hcg=function(t){let e=t[1]/100,r=t[2]/100,s=e*r,a=0;return s<1&&(a=(r-s)/(1-s)),[t[0],s*100,a*100]};hr.hcg.rgb=function(t){let e=t[0]/360,r=t[1]/100,s=t[2]/100;if(r===0)return[s*255,s*255,s*255];let a=[0,0,0],n=e%1*6,c=n%1,f=1-c,p=0;switch(Math.floor(n)){case 0:a[0]=1,a[1]=c,a[2]=0;break;case 1:a[0]=f,a[1]=1,a[2]=0;break;case 2:a[0]=0,a[1]=1,a[2]=c;break;case 3:a[0]=0,a[1]=f,a[2]=1;break;case 4:a[0]=c,a[1]=0,a[2]=1;break;default:a[0]=1,a[1]=0,a[2]=f}return p=(1-r)*s,[(r*a[0]+p)*255,(r*a[1]+p)*255,(r*a[2]+p)*255]};hr.hcg.hsv=function(t){let e=t[1]/100,r=t[2]/100,s=e+r*(1-e),a=0;return s>0&&(a=e/s),[t[0],a*100,s*100]};hr.hcg.hsl=function(t){let e=t[1]/100,s=t[2]/100*(1-e)+.5*e,a=0;return s>0&&s<.5?a=e/(2*s):s>=.5&&s<1&&(a=e/(2*(1-s))),[t[0],a*100,s*100]};hr.hcg.hwb=function(t){let e=t[1]/100,r=t[2]/100,s=e+r*(1-e);return[t[0],(s-e)*100,(1-s)*100]};hr.hwb.hcg=function(t){let e=t[1]/100,s=1-t[2]/100,a=s-e,n=0;return a<1&&(n=(s-a)/(1-a)),[t[0],a*100,n*100]};hr.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]};hr.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]};hr.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]};hr.gray.hsl=function(t){return[0,0,t[0]]};hr.gray.hsv=hr.gray.hsl;hr.gray.hwb=function(t){return[0,100,t[0]]};hr.gray.cmyk=function(t){return[0,0,0,t[0]]};hr.gray.lab=function(t){return[t[0],0,0]};hr.gray.hex=function(t){let e=Math.round(t[0]/100*255)&255,s=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(s.length)+s};hr.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}});var coe=L((FYt,loe)=>{var mk=i4();function btt(){let t={},e=Object.keys(mk);for(let r=e.length,s=0;s{var s4=i4(),Qtt=coe(),PE={},Ttt=Object.keys(s4);function Rtt(t){let e=function(...r){let s=r[0];return s==null?s:(s.length>1&&(r=s),t(r))};return"conversion"in t&&(e.conversion=t.conversion),e}function Ftt(t){let e=function(...r){let s=r[0];if(s==null)return s;s.length>1&&(r=s);let a=t(r);if(typeof a=="object")for(let n=a.length,c=0;c{PE[t]={},Object.defineProperty(PE[t],"channels",{value:s4[t].channels}),Object.defineProperty(PE[t],"labels",{value:s4[t].labels});let e=Qtt(t);Object.keys(e).forEach(s=>{let a=e[s];PE[t][s]=Ftt(a),PE[t][s].raw=Rtt(a)})});uoe.exports=PE});var IB=L((OYt,doe)=>{"use strict";var Aoe=(t,e)=>(...r)=>`\x1B[${t(...r)+e}m`,poe=(t,e)=>(...r)=>{let s=t(...r);return`\x1B[${38+e};5;${s}m`},hoe=(t,e)=>(...r)=>{let s=t(...r);return`\x1B[${38+e};2;${s[0]};${s[1]};${s[2]}m`},yk=t=>t,goe=(t,e,r)=>[t,e,r],xE=(t,e,r)=>{Object.defineProperty(t,e,{get:()=>{let s=r();return Object.defineProperty(t,e,{value:s,enumerable:!0,configurable:!0}),s},enumerable:!0,configurable:!0})},o4,kE=(t,e,r,s)=>{o4===void 0&&(o4=foe());let a=s?10:0,n={};for(let[c,f]of Object.entries(o4)){let p=c==="ansi16"?"ansi":c;c===e?n[p]=t(r,a):typeof f=="object"&&(n[p]=t(f[e],a))}return n};function Ntt(){let t=new Map,e={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};e.color.gray=e.color.blackBright,e.bgColor.bgGray=e.bgColor.bgBlackBright,e.color.grey=e.color.blackBright,e.bgColor.bgGrey=e.bgColor.bgBlackBright;for(let[r,s]of Object.entries(e)){for(let[a,n]of Object.entries(s))e[a]={open:`\x1B[${n[0]}m`,close:`\x1B[${n[1]}m`},s[a]=e[a],t.set(n[0],n[1]);Object.defineProperty(e,r,{value:s,enumerable:!1})}return Object.defineProperty(e,"codes",{value:t,enumerable:!1}),e.color.close="\x1B[39m",e.bgColor.close="\x1B[49m",xE(e.color,"ansi",()=>kE(Aoe,"ansi16",yk,!1)),xE(e.color,"ansi256",()=>kE(poe,"ansi256",yk,!1)),xE(e.color,"ansi16m",()=>kE(hoe,"rgb",goe,!1)),xE(e.bgColor,"ansi",()=>kE(Aoe,"ansi16",yk,!0)),xE(e.bgColor,"ansi256",()=>kE(poe,"ansi256",yk,!0)),xE(e.bgColor,"ansi16m",()=>kE(hoe,"rgb",goe,!0)),e}Object.defineProperty(doe,"exports",{enumerable:!0,get:Ntt})});var yoe=L((LYt,moe)=>{"use strict";moe.exports=(t,e=process.argv)=>{let r=t.startsWith("-")?"":t.length===1?"-":"--",s=e.indexOf(r+t),a=e.indexOf("--");return s!==-1&&(a===-1||s{"use strict";var Ott=ye("os"),Eoe=ye("tty"),Dc=yoe(),{env:Ps}=process,f0;Dc("no-color")||Dc("no-colors")||Dc("color=false")||Dc("color=never")?f0=0:(Dc("color")||Dc("colors")||Dc("color=true")||Dc("color=always"))&&(f0=1);"FORCE_COLOR"in Ps&&(Ps.FORCE_COLOR==="true"?f0=1:Ps.FORCE_COLOR==="false"?f0=0:f0=Ps.FORCE_COLOR.length===0?1:Math.min(parseInt(Ps.FORCE_COLOR,10),3));function a4(t){return t===0?!1:{level:t,hasBasic:!0,has256:t>=2,has16m:t>=3}}function l4(t,e){if(f0===0)return 0;if(Dc("color=16m")||Dc("color=full")||Dc("color=truecolor"))return 3;if(Dc("color=256"))return 2;if(t&&!e&&f0===void 0)return 0;let r=f0||0;if(Ps.TERM==="dumb")return r;if(process.platform==="win32"){let s=Ott.release().split(".");return Number(s[0])>=10&&Number(s[2])>=10586?Number(s[2])>=14931?3:2:1}if("CI"in Ps)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some(s=>s in Ps)||Ps.CI_NAME==="codeship"?1:r;if("TEAMCITY_VERSION"in Ps)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(Ps.TEAMCITY_VERSION)?1:0;if("GITHUB_ACTIONS"in Ps)return 1;if(Ps.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in Ps){let s=parseInt((Ps.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(Ps.TERM_PROGRAM){case"iTerm.app":return s>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(Ps.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(Ps.TERM)||"COLORTERM"in Ps?1:r}function Ltt(t){let e=l4(t,t&&t.isTTY);return a4(e)}Ioe.exports={supportsColor:Ltt,stdout:a4(l4(!0,Eoe.isatty(1))),stderr:a4(l4(!0,Eoe.isatty(2)))}});var woe=L((_Yt,Coe)=>{"use strict";var Mtt=(t,e,r)=>{let s=t.indexOf(e);if(s===-1)return t;let a=e.length,n=0,c="";do c+=t.substr(n,s-n)+e+r,n=s+a,s=t.indexOf(e,n);while(s!==-1);return c+=t.substr(n),c},_tt=(t,e,r,s)=>{let a=0,n="";do{let c=t[s-1]==="\r";n+=t.substr(a,(c?s-1:s)-a)+e+(c?`\r +`:` +`)+r,a=s+1,s=t.indexOf(` +`,a)}while(s!==-1);return n+=t.substr(a),n};Coe.exports={stringReplaceAll:Mtt,stringEncaseCRLFWithFirstIndex:_tt}});var boe=L((UYt,Doe)=>{"use strict";var Utt=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,Boe=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,Htt=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,jtt=/\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.)|([^\\])/gi,qtt=new Map([["n",` +`],["r","\r"],["t"," "],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e","\x1B"],["a","\x07"]]);function Soe(t){let e=t[0]==="u",r=t[1]==="{";return e&&!r&&t.length===5||t[0]==="x"&&t.length===3?String.fromCharCode(parseInt(t.slice(1),16)):e&&r?String.fromCodePoint(parseInt(t.slice(2,-1),16)):qtt.get(t)||t}function Gtt(t,e){let r=[],s=e.trim().split(/\s*,\s*/g),a;for(let n of s){let c=Number(n);if(!Number.isNaN(c))r.push(c);else if(a=n.match(Htt))r.push(a[2].replace(jtt,(f,p,h)=>p?Soe(p):h));else throw new Error(`Invalid Chalk template style argument: ${n} (in style '${t}')`)}return r}function Wtt(t){Boe.lastIndex=0;let e=[],r;for(;(r=Boe.exec(t))!==null;){let s=r[1];if(r[2]){let a=Gtt(s,r[2]);e.push([s].concat(a))}else e.push([s])}return e}function voe(t,e){let r={};for(let a of e)for(let n of a.styles)r[n[0]]=a.inverse?null:n.slice(1);let s=t;for(let[a,n]of Object.entries(r))if(Array.isArray(n)){if(!(a in s))throw new Error(`Unknown Chalk style: ${a}`);s=n.length>0?s[a](...n):s[a]}return s}Doe.exports=(t,e)=>{let r=[],s=[],a=[];if(e.replace(Utt,(n,c,f,p,h,E)=>{if(c)a.push(Soe(c));else if(p){let C=a.join("");a=[],s.push(r.length===0?C:voe(t,r)(C)),r.push({inverse:f,styles:Wtt(p)})}else if(h){if(r.length===0)throw new Error("Found extraneous } in Chalk template literal");s.push(voe(t,r)(a.join(""))),a=[],r.pop()}else a.push(E)}),s.push(a.join("")),r.length>0){let n=`Chalk template literal is missing ${r.length} closing bracket${r.length===1?"":"s"} (\`}\`)`;throw new Error(n)}return s.join("")}});var g4=L((HYt,Qoe)=>{"use strict";var CB=IB(),{stdout:f4,stderr:A4}=c4(),{stringReplaceAll:Ytt,stringEncaseCRLFWithFirstIndex:Vtt}=woe(),Poe=["ansi","ansi","ansi256","ansi16m"],QE=Object.create(null),Ktt=(t,e={})=>{if(e.level>3||e.level<0)throw new Error("The `level` option should be an integer from 0 to 3");let r=f4?f4.level:0;t.level=e.level===void 0?r:e.level},p4=class{constructor(e){return xoe(e)}},xoe=t=>{let e={};return Ktt(e,t),e.template=(...r)=>Ztt(e.template,...r),Object.setPrototypeOf(e,Ek.prototype),Object.setPrototypeOf(e.template,e),e.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},e.template.Instance=p4,e.template};function Ek(t){return xoe(t)}for(let[t,e]of Object.entries(CB))QE[t]={get(){let r=Ik(this,h4(e.open,e.close,this._styler),this._isEmpty);return Object.defineProperty(this,t,{value:r}),r}};QE.visible={get(){let t=Ik(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:t}),t}};var koe=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(let t of koe)QE[t]={get(){let{level:e}=this;return function(...r){let s=h4(CB.color[Poe[e]][t](...r),CB.color.close,this._styler);return Ik(this,s,this._isEmpty)}}};for(let t of koe){let e="bg"+t[0].toUpperCase()+t.slice(1);QE[e]={get(){let{level:r}=this;return function(...s){let a=h4(CB.bgColor[Poe[r]][t](...s),CB.bgColor.close,this._styler);return Ik(this,a,this._isEmpty)}}}}var Jtt=Object.defineProperties(()=>{},{...QE,level:{enumerable:!0,get(){return this._generator.level},set(t){this._generator.level=t}}}),h4=(t,e,r)=>{let s,a;return r===void 0?(s=t,a=e):(s=r.openAll+t,a=e+r.closeAll),{open:t,close:e,openAll:s,closeAll:a,parent:r}},Ik=(t,e,r)=>{let s=(...a)=>ztt(s,a.length===1?""+a[0]:a.join(" "));return s.__proto__=Jtt,s._generator=t,s._styler=e,s._isEmpty=r,s},ztt=(t,e)=>{if(t.level<=0||!e)return t._isEmpty?"":e;let r=t._styler;if(r===void 0)return e;let{openAll:s,closeAll:a}=r;if(e.indexOf("\x1B")!==-1)for(;r!==void 0;)e=Ytt(e,r.close,r.open),r=r.parent;let n=e.indexOf(` +`);return n!==-1&&(e=Vtt(e,a,s,n)),s+e+a},u4,Ztt=(t,...e)=>{let[r]=e;if(!Array.isArray(r))return e.join(" ");let s=e.slice(1),a=[r.raw[0]];for(let n=1;n{"use strict";bc.isInteger=t=>typeof t=="number"?Number.isInteger(t):typeof t=="string"&&t.trim()!==""?Number.isInteger(Number(t)):!1;bc.find=(t,e)=>t.nodes.find(r=>r.type===e);bc.exceedsLimit=(t,e,r=1,s)=>s===!1||!bc.isInteger(t)||!bc.isInteger(e)?!1:(Number(e)-Number(t))/Number(r)>=s;bc.escapeNode=(t,e=0,r)=>{let s=t.nodes[e];s&&(r&&s.type===r||s.type==="open"||s.type==="close")&&s.escaped!==!0&&(s.value="\\"+s.value,s.escaped=!0)};bc.encloseBrace=t=>t.type!=="brace"||t.commas>>0+t.ranges>>0?!1:(t.invalid=!0,!0);bc.isInvalidBrace=t=>t.type!=="brace"?!1:t.invalid===!0||t.dollar?!0:!(t.commas>>0+t.ranges>>0)||t.open!==!0||t.close!==!0?(t.invalid=!0,!0):!1;bc.isOpenOrClose=t=>t.type==="open"||t.type==="close"?!0:t.open===!0||t.close===!0;bc.reduce=t=>t.reduce((e,r)=>(r.type==="text"&&e.push(r.value),r.type==="range"&&(r.type="text"),e),[]);bc.flatten=(...t)=>{let e=[],r=s=>{for(let a=0;a{"use strict";var Toe=Ck();Roe.exports=(t,e={})=>{let r=(s,a={})=>{let n=e.escapeInvalid&&Toe.isInvalidBrace(a),c=s.invalid===!0&&e.escapeInvalid===!0,f="";if(s.value)return(n||c)&&Toe.isOpenOrClose(s)?"\\"+s.value:s.value;if(s.value)return s.value;if(s.nodes)for(let p of s.nodes)f+=r(p);return f};return r(t)}});var Noe=L((GYt,Foe)=>{"use strict";Foe.exports=function(t){return typeof t=="number"?t-t===0:typeof t=="string"&&t.trim()!==""?Number.isFinite?Number.isFinite(+t):isFinite(+t):!1}});var Goe=L((WYt,qoe)=>{"use strict";var Ooe=Noe(),Gd=(t,e,r)=>{if(Ooe(t)===!1)throw new TypeError("toRegexRange: expected the first argument to be a number");if(e===void 0||t===e)return String(t);if(Ooe(e)===!1)throw new TypeError("toRegexRange: expected the second argument to be a number.");let s={relaxZeros:!0,...r};typeof s.strictZeros=="boolean"&&(s.relaxZeros=s.strictZeros===!1);let a=String(s.relaxZeros),n=String(s.shorthand),c=String(s.capture),f=String(s.wrap),p=t+":"+e+"="+a+n+c+f;if(Gd.cache.hasOwnProperty(p))return Gd.cache[p].result;let h=Math.min(t,e),E=Math.max(t,e);if(Math.abs(h-E)===1){let R=t+"|"+e;return s.capture?`(${R})`:s.wrap===!1?R:`(?:${R})`}let C=joe(t)||joe(e),S={min:t,max:e,a:h,b:E},P=[],I=[];if(C&&(S.isPadded=C,S.maxLen=String(S.max).length),h<0){let R=E<0?Math.abs(E):1;I=Loe(R,Math.abs(h),S,s),h=S.a=0}return E>=0&&(P=Loe(h,E,S,s)),S.negatives=I,S.positives=P,S.result=Xtt(I,P,s),s.capture===!0?S.result=`(${S.result})`:s.wrap!==!1&&P.length+I.length>1&&(S.result=`(?:${S.result})`),Gd.cache[p]=S,S.result};function Xtt(t,e,r){let s=d4(t,e,"-",!1,r)||[],a=d4(e,t,"",!1,r)||[],n=d4(t,e,"-?",!0,r)||[];return s.concat(n).concat(a).join("|")}function $tt(t,e){let r=1,s=1,a=_oe(t,r),n=new Set([e]);for(;t<=a&&a<=e;)n.add(a),r+=1,a=_oe(t,r);for(a=Uoe(e+1,s)-1;t1&&f.count.pop(),f.count.push(E.count[0]),f.string=f.pattern+Hoe(f.count),c=h+1;continue}r.isPadded&&(C=irt(h,r,s)),E.string=C+E.pattern+Hoe(E.count),n.push(E),c=h+1,f=E}return n}function d4(t,e,r,s,a){let n=[];for(let c of t){let{string:f}=c;!s&&!Moe(e,"string",f)&&n.push(r+f),s&&Moe(e,"string",f)&&n.push(r+f)}return n}function trt(t,e){let r=[];for(let s=0;se?1:e>t?-1:0}function Moe(t,e,r){return t.some(s=>s[e]===r)}function _oe(t,e){return Number(String(t).slice(0,-e)+"9".repeat(e))}function Uoe(t,e){return t-t%Math.pow(10,e)}function Hoe(t){let[e=0,r=""]=t;return r||e>1?`{${e+(r?","+r:"")}}`:""}function nrt(t,e,r){return`[${t}${e-t===1?"":"-"}${e}]`}function joe(t){return/^-?(0+)\d/.test(t)}function irt(t,e,r){if(!e.isPadded)return t;let s=Math.abs(e.maxLen-String(t).length),a=r.relaxZeros!==!1;switch(s){case 0:return"";case 1:return a?"0?":"0";case 2:return a?"0{0,2}":"00";default:return a?`0{0,${s}}`:`0{${s}}`}}Gd.cache={};Gd.clearCache=()=>Gd.cache={};qoe.exports=Gd});var E4=L((YYt,Xoe)=>{"use strict";var srt=ye("util"),Voe=Goe(),Woe=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),ort=t=>e=>t===!0?Number(e):String(e),m4=t=>typeof t=="number"||typeof t=="string"&&t!=="",BB=t=>Number.isInteger(+t),y4=t=>{let e=`${t}`,r=-1;if(e[0]==="-"&&(e=e.slice(1)),e==="0")return!1;for(;e[++r]==="0";);return r>0},art=(t,e,r)=>typeof t=="string"||typeof e=="string"?!0:r.stringify===!0,lrt=(t,e,r)=>{if(e>0){let s=t[0]==="-"?"-":"";s&&(t=t.slice(1)),t=s+t.padStart(s?e-1:e,"0")}return r===!1?String(t):t},Yoe=(t,e)=>{let r=t[0]==="-"?"-":"";for(r&&(t=t.slice(1),e--);t.length{t.negatives.sort((c,f)=>cf?1:0),t.positives.sort((c,f)=>cf?1:0);let r=e.capture?"":"?:",s="",a="",n;return t.positives.length&&(s=t.positives.join("|")),t.negatives.length&&(a=`-(${r}${t.negatives.join("|")})`),s&&a?n=`${s}|${a}`:n=s||a,e.wrap?`(${r}${n})`:n},Koe=(t,e,r,s)=>{if(r)return Voe(t,e,{wrap:!1,...s});let a=String.fromCharCode(t);if(t===e)return a;let n=String.fromCharCode(e);return`[${a}-${n}]`},Joe=(t,e,r)=>{if(Array.isArray(t)){let s=r.wrap===!0,a=r.capture?"":"?:";return s?`(${a}${t.join("|")})`:t.join("|")}return Voe(t,e,r)},zoe=(...t)=>new RangeError("Invalid range arguments: "+srt.inspect(...t)),Zoe=(t,e,r)=>{if(r.strictRanges===!0)throw zoe([t,e]);return[]},urt=(t,e)=>{if(e.strictRanges===!0)throw new TypeError(`Expected step "${t}" to be a number`);return[]},frt=(t,e,r=1,s={})=>{let a=Number(t),n=Number(e);if(!Number.isInteger(a)||!Number.isInteger(n)){if(s.strictRanges===!0)throw zoe([t,e]);return[]}a===0&&(a=0),n===0&&(n=0);let c=a>n,f=String(t),p=String(e),h=String(r);r=Math.max(Math.abs(r),1);let E=y4(f)||y4(p)||y4(h),C=E?Math.max(f.length,p.length,h.length):0,S=E===!1&&art(t,e,s)===!1,P=s.transform||ort(S);if(s.toRegex&&r===1)return Koe(Yoe(t,C),Yoe(e,C),!0,s);let I={negatives:[],positives:[]},R=W=>I[W<0?"negatives":"positives"].push(Math.abs(W)),N=[],U=0;for(;c?a>=n:a<=n;)s.toRegex===!0&&r>1?R(a):N.push(lrt(P(a,U),C,S)),a=c?a-r:a+r,U++;return s.toRegex===!0?r>1?crt(I,s):Joe(N,null,{wrap:!1,...s}):N},Art=(t,e,r=1,s={})=>{if(!BB(t)&&t.length>1||!BB(e)&&e.length>1)return Zoe(t,e,s);let a=s.transform||(S=>String.fromCharCode(S)),n=`${t}`.charCodeAt(0),c=`${e}`.charCodeAt(0),f=n>c,p=Math.min(n,c),h=Math.max(n,c);if(s.toRegex&&r===1)return Koe(p,h,!1,s);let E=[],C=0;for(;f?n>=c:n<=c;)E.push(a(n,C)),n=f?n-r:n+r,C++;return s.toRegex===!0?Joe(E,null,{wrap:!1,options:s}):E},Bk=(t,e,r,s={})=>{if(e==null&&m4(t))return[t];if(!m4(t)||!m4(e))return Zoe(t,e,s);if(typeof r=="function")return Bk(t,e,1,{transform:r});if(Woe(r))return Bk(t,e,0,r);let a={...s};return a.capture===!0&&(a.wrap=!0),r=r||a.step||1,BB(r)?BB(t)&&BB(e)?frt(t,e,r,a):Art(t,e,Math.max(Math.abs(r),1),a):r!=null&&!Woe(r)?urt(r,a):Bk(t,e,1,r)};Xoe.exports=Bk});var tae=L((VYt,eae)=>{"use strict";var prt=E4(),$oe=Ck(),hrt=(t,e={})=>{let r=(s,a={})=>{let n=$oe.isInvalidBrace(a),c=s.invalid===!0&&e.escapeInvalid===!0,f=n===!0||c===!0,p=e.escapeInvalid===!0?"\\":"",h="";if(s.isOpen===!0||s.isClose===!0)return p+s.value;if(s.type==="open")return f?p+s.value:"(";if(s.type==="close")return f?p+s.value:")";if(s.type==="comma")return s.prev.type==="comma"?"":f?s.value:"|";if(s.value)return s.value;if(s.nodes&&s.ranges>0){let E=$oe.reduce(s.nodes),C=prt(...E,{...e,wrap:!1,toRegex:!0});if(C.length!==0)return E.length>1&&C.length>1?`(${C})`:C}if(s.nodes)for(let E of s.nodes)h+=r(E,s);return h};return r(t)};eae.exports=hrt});var iae=L((KYt,nae)=>{"use strict";var grt=E4(),rae=wk(),TE=Ck(),Wd=(t="",e="",r=!1)=>{let s=[];if(t=[].concat(t),e=[].concat(e),!e.length)return t;if(!t.length)return r?TE.flatten(e).map(a=>`{${a}}`):e;for(let a of t)if(Array.isArray(a))for(let n of a)s.push(Wd(n,e,r));else for(let n of e)r===!0&&typeof n=="string"&&(n=`{${n}}`),s.push(Array.isArray(n)?Wd(a,n,r):a+n);return TE.flatten(s)},drt=(t,e={})=>{let r=e.rangeLimit===void 0?1e3:e.rangeLimit,s=(a,n={})=>{a.queue=[];let c=n,f=n.queue;for(;c.type!=="brace"&&c.type!=="root"&&c.parent;)c=c.parent,f=c.queue;if(a.invalid||a.dollar){f.push(Wd(f.pop(),rae(a,e)));return}if(a.type==="brace"&&a.invalid!==!0&&a.nodes.length===2){f.push(Wd(f.pop(),["{}"]));return}if(a.nodes&&a.ranges>0){let C=TE.reduce(a.nodes);if(TE.exceedsLimit(...C,e.step,r))throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.");let S=grt(...C,e);S.length===0&&(S=rae(a,e)),f.push(Wd(f.pop(),S)),a.nodes=[];return}let p=TE.encloseBrace(a),h=a.queue,E=a;for(;E.type!=="brace"&&E.type!=="root"&&E.parent;)E=E.parent,h=E.queue;for(let C=0;C{"use strict";sae.exports={MAX_LENGTH:1024*64,CHAR_0:"0",CHAR_9:"9",CHAR_UPPERCASE_A:"A",CHAR_LOWERCASE_A:"a",CHAR_UPPERCASE_Z:"Z",CHAR_LOWERCASE_Z:"z",CHAR_LEFT_PARENTHESES:"(",CHAR_RIGHT_PARENTHESES:")",CHAR_ASTERISK:"*",CHAR_AMPERSAND:"&",CHAR_AT:"@",CHAR_BACKSLASH:"\\",CHAR_BACKTICK:"`",CHAR_CARRIAGE_RETURN:"\r",CHAR_CIRCUMFLEX_ACCENT:"^",CHAR_COLON:":",CHAR_COMMA:",",CHAR_DOLLAR:"$",CHAR_DOT:".",CHAR_DOUBLE_QUOTE:'"',CHAR_EQUAL:"=",CHAR_EXCLAMATION_MARK:"!",CHAR_FORM_FEED:"\f",CHAR_FORWARD_SLASH:"/",CHAR_HASH:"#",CHAR_HYPHEN_MINUS:"-",CHAR_LEFT_ANGLE_BRACKET:"<",CHAR_LEFT_CURLY_BRACE:"{",CHAR_LEFT_SQUARE_BRACKET:"[",CHAR_LINE_FEED:` +`,CHAR_NO_BREAK_SPACE:"\xA0",CHAR_PERCENT:"%",CHAR_PLUS:"+",CHAR_QUESTION_MARK:"?",CHAR_RIGHT_ANGLE_BRACKET:">",CHAR_RIGHT_CURLY_BRACE:"}",CHAR_RIGHT_SQUARE_BRACKET:"]",CHAR_SEMICOLON:";",CHAR_SINGLE_QUOTE:"'",CHAR_SPACE:" ",CHAR_TAB:" ",CHAR_UNDERSCORE:"_",CHAR_VERTICAL_LINE:"|",CHAR_ZERO_WIDTH_NOBREAK_SPACE:"\uFEFF"}});var fae=L((zYt,uae)=>{"use strict";var mrt=wk(),{MAX_LENGTH:aae,CHAR_BACKSLASH:I4,CHAR_BACKTICK:yrt,CHAR_COMMA:Ert,CHAR_DOT:Irt,CHAR_LEFT_PARENTHESES:Crt,CHAR_RIGHT_PARENTHESES:wrt,CHAR_LEFT_CURLY_BRACE:Brt,CHAR_RIGHT_CURLY_BRACE:vrt,CHAR_LEFT_SQUARE_BRACKET:lae,CHAR_RIGHT_SQUARE_BRACKET:cae,CHAR_DOUBLE_QUOTE:Srt,CHAR_SINGLE_QUOTE:Drt,CHAR_NO_BREAK_SPACE:brt,CHAR_ZERO_WIDTH_NOBREAK_SPACE:Prt}=oae(),xrt=(t,e={})=>{if(typeof t!="string")throw new TypeError("Expected a string");let r=e||{},s=typeof r.maxLength=="number"?Math.min(aae,r.maxLength):aae;if(t.length>s)throw new SyntaxError(`Input length (${t.length}), exceeds max characters (${s})`);let a={type:"root",input:t,nodes:[]},n=[a],c=a,f=a,p=0,h=t.length,E=0,C=0,S,P={},I=()=>t[E++],R=N=>{if(N.type==="text"&&f.type==="dot"&&(f.type="text"),f&&f.type==="text"&&N.type==="text"){f.value+=N.value;return}return c.nodes.push(N),N.parent=c,N.prev=f,f=N,N};for(R({type:"bos"});E0){if(c.ranges>0){c.ranges=0;let N=c.nodes.shift();c.nodes=[N,{type:"text",value:mrt(c)}]}R({type:"comma",value:S}),c.commas++;continue}if(S===Irt&&C>0&&c.commas===0){let N=c.nodes;if(C===0||N.length===0){R({type:"text",value:S});continue}if(f.type==="dot"){if(c.range=[],f.value+=S,f.type="range",c.nodes.length!==3&&c.nodes.length!==5){c.invalid=!0,c.ranges=0,f.type="text";continue}c.ranges++,c.args=[];continue}if(f.type==="range"){N.pop();let U=N[N.length-1];U.value+=f.value+S,f=U,c.ranges--;continue}R({type:"dot",value:S});continue}R({type:"text",value:S})}do if(c=n.pop(),c.type!=="root"){c.nodes.forEach(W=>{W.nodes||(W.type==="open"&&(W.isOpen=!0),W.type==="close"&&(W.isClose=!0),W.nodes||(W.type="text"),W.invalid=!0)});let N=n[n.length-1],U=N.nodes.indexOf(c);N.nodes.splice(U,1,...c.nodes)}while(n.length>0);return R({type:"eos"}),a};uae.exports=xrt});var hae=L((ZYt,pae)=>{"use strict";var Aae=wk(),krt=tae(),Qrt=iae(),Trt=fae(),ql=(t,e={})=>{let r=[];if(Array.isArray(t))for(let s of t){let a=ql.create(s,e);Array.isArray(a)?r.push(...a):r.push(a)}else r=[].concat(ql.create(t,e));return e&&e.expand===!0&&e.nodupes===!0&&(r=[...new Set(r)]),r};ql.parse=(t,e={})=>Trt(t,e);ql.stringify=(t,e={})=>Aae(typeof t=="string"?ql.parse(t,e):t,e);ql.compile=(t,e={})=>(typeof t=="string"&&(t=ql.parse(t,e)),krt(t,e));ql.expand=(t,e={})=>{typeof t=="string"&&(t=ql.parse(t,e));let r=Qrt(t,e);return e.noempty===!0&&(r=r.filter(Boolean)),e.nodupes===!0&&(r=[...new Set(r)]),r};ql.create=(t,e={})=>t===""||t.length<3?[t]:e.expand!==!0?ql.compile(t,e):ql.expand(t,e);pae.exports=ql});var vB=L((XYt,Eae)=>{"use strict";var Rrt=ye("path"),Kf="\\\\/",gae=`[^${Kf}]`,Pp="\\.",Frt="\\+",Nrt="\\?",vk="\\/",Ort="(?=.)",dae="[^/]",C4=`(?:${vk}|$)`,mae=`(?:^|${vk})`,w4=`${Pp}{1,2}${C4}`,Lrt=`(?!${Pp})`,Mrt=`(?!${mae}${w4})`,_rt=`(?!${Pp}{0,1}${C4})`,Urt=`(?!${w4})`,Hrt=`[^.${vk}]`,jrt=`${dae}*?`,yae={DOT_LITERAL:Pp,PLUS_LITERAL:Frt,QMARK_LITERAL:Nrt,SLASH_LITERAL:vk,ONE_CHAR:Ort,QMARK:dae,END_ANCHOR:C4,DOTS_SLASH:w4,NO_DOT:Lrt,NO_DOTS:Mrt,NO_DOT_SLASH:_rt,NO_DOTS_SLASH:Urt,QMARK_NO_DOT:Hrt,STAR:jrt,START_ANCHOR:mae},qrt={...yae,SLASH_LITERAL:`[${Kf}]`,QMARK:gae,STAR:`${gae}*?`,DOTS_SLASH:`${Pp}{1,2}(?:[${Kf}]|$)`,NO_DOT:`(?!${Pp})`,NO_DOTS:`(?!(?:^|[${Kf}])${Pp}{1,2}(?:[${Kf}]|$))`,NO_DOT_SLASH:`(?!${Pp}{0,1}(?:[${Kf}]|$))`,NO_DOTS_SLASH:`(?!${Pp}{1,2}(?:[${Kf}]|$))`,QMARK_NO_DOT:`[^.${Kf}]`,START_ANCHOR:`(?:^|[${Kf}])`,END_ANCHOR:`(?:[${Kf}]|$)`},Grt={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};Eae.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:Grt,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,SEP:Rrt.sep,extglobChars(t){return{"!":{type:"negate",open:"(?:(?!(?:",close:`))${t.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(t){return t===!0?qrt:yae}}});var SB=L(al=>{"use strict";var Wrt=ye("path"),Yrt=process.platform==="win32",{REGEX_BACKSLASH:Vrt,REGEX_REMOVE_BACKSLASH:Krt,REGEX_SPECIAL_CHARS:Jrt,REGEX_SPECIAL_CHARS_GLOBAL:zrt}=vB();al.isObject=t=>t!==null&&typeof t=="object"&&!Array.isArray(t);al.hasRegexChars=t=>Jrt.test(t);al.isRegexChar=t=>t.length===1&&al.hasRegexChars(t);al.escapeRegex=t=>t.replace(zrt,"\\$1");al.toPosixSlashes=t=>t.replace(Vrt,"/");al.removeBackslashes=t=>t.replace(Krt,e=>e==="\\"?"":e);al.supportsLookbehinds=()=>{let t=process.version.slice(1).split(".").map(Number);return t.length===3&&t[0]>=9||t[0]===8&&t[1]>=10};al.isWindows=t=>t&&typeof t.windows=="boolean"?t.windows:Yrt===!0||Wrt.sep==="\\";al.escapeLast=(t,e,r)=>{let s=t.lastIndexOf(e,r);return s===-1?t:t[s-1]==="\\"?al.escapeLast(t,e,s-1):`${t.slice(0,s)}\\${t.slice(s)}`};al.removePrefix=(t,e={})=>{let r=t;return r.startsWith("./")&&(r=r.slice(2),e.prefix="./"),r};al.wrapOutput=(t,e={},r={})=>{let s=r.contains?"":"^",a=r.contains?"":"$",n=`${s}(?:${t})${a}`;return e.negated===!0&&(n=`(?:^(?!${n}).*$)`),n}});var bae=L((eVt,Dae)=>{"use strict";var Iae=SB(),{CHAR_ASTERISK:B4,CHAR_AT:Zrt,CHAR_BACKWARD_SLASH:DB,CHAR_COMMA:Xrt,CHAR_DOT:v4,CHAR_EXCLAMATION_MARK:S4,CHAR_FORWARD_SLASH:Sae,CHAR_LEFT_CURLY_BRACE:D4,CHAR_LEFT_PARENTHESES:b4,CHAR_LEFT_SQUARE_BRACKET:$rt,CHAR_PLUS:ent,CHAR_QUESTION_MARK:Cae,CHAR_RIGHT_CURLY_BRACE:tnt,CHAR_RIGHT_PARENTHESES:wae,CHAR_RIGHT_SQUARE_BRACKET:rnt}=vB(),Bae=t=>t===Sae||t===DB,vae=t=>{t.isPrefix!==!0&&(t.depth=t.isGlobstar?1/0:1)},nnt=(t,e)=>{let r=e||{},s=t.length-1,a=r.parts===!0||r.scanToEnd===!0,n=[],c=[],f=[],p=t,h=-1,E=0,C=0,S=!1,P=!1,I=!1,R=!1,N=!1,U=!1,W=!1,te=!1,ie=!1,Ae=!1,ce=0,me,pe,Be={value:"",depth:0,isGlob:!1},Ce=()=>h>=s,g=()=>p.charCodeAt(h+1),we=()=>(me=pe,p.charCodeAt(++h));for(;h0&&(fe=p.slice(0,E),p=p.slice(E),C-=E),Ee&&I===!0&&C>0?(Ee=p.slice(0,C),se=p.slice(C)):I===!0?(Ee="",se=p):Ee=p,Ee&&Ee!==""&&Ee!=="/"&&Ee!==p&&Bae(Ee.charCodeAt(Ee.length-1))&&(Ee=Ee.slice(0,-1)),r.unescape===!0&&(se&&(se=Iae.removeBackslashes(se)),Ee&&W===!0&&(Ee=Iae.removeBackslashes(Ee)));let X={prefix:fe,input:t,start:E,base:Ee,glob:se,isBrace:S,isBracket:P,isGlob:I,isExtglob:R,isGlobstar:N,negated:te,negatedExtglob:ie};if(r.tokens===!0&&(X.maxDepth=0,Bae(pe)||c.push(Be),X.tokens=c),r.parts===!0||r.tokens===!0){let De;for(let Re=0;Re{"use strict";var Sk=vB(),Gl=SB(),{MAX_LENGTH:Dk,POSIX_REGEX_SOURCE:int,REGEX_NON_SPECIAL_CHARS:snt,REGEX_SPECIAL_CHARS_BACKREF:ont,REPLACEMENTS:Pae}=Sk,ant=(t,e)=>{if(typeof e.expandRange=="function")return e.expandRange(...t,e);t.sort();let r=`[${t.join("-")}]`;try{new RegExp(r)}catch{return t.map(a=>Gl.escapeRegex(a)).join("..")}return r},RE=(t,e)=>`Missing ${t}: "${e}" - use "\\\\${e}" to match literal characters`,P4=(t,e)=>{if(typeof t!="string")throw new TypeError("Expected a string");t=Pae[t]||t;let r={...e},s=typeof r.maxLength=="number"?Math.min(Dk,r.maxLength):Dk,a=t.length;if(a>s)throw new SyntaxError(`Input length: ${a}, exceeds maximum allowed length: ${s}`);let n={type:"bos",value:"",output:r.prepend||""},c=[n],f=r.capture?"":"?:",p=Gl.isWindows(e),h=Sk.globChars(p),E=Sk.extglobChars(h),{DOT_LITERAL:C,PLUS_LITERAL:S,SLASH_LITERAL:P,ONE_CHAR:I,DOTS_SLASH:R,NO_DOT:N,NO_DOT_SLASH:U,NO_DOTS_SLASH:W,QMARK:te,QMARK_NO_DOT:ie,STAR:Ae,START_ANCHOR:ce}=h,me=x=>`(${f}(?:(?!${ce}${x.dot?R:C}).)*?)`,pe=r.dot?"":N,Be=r.dot?te:ie,Ce=r.bash===!0?me(r):Ae;r.capture&&(Ce=`(${Ce})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let g={input:t,index:-1,start:0,dot:r.dot===!0,consumed:"",output:"",prefix:"",backtrack:!1,negated:!1,brackets:0,braces:0,parens:0,quotes:0,globstar:!1,tokens:c};t=Gl.removePrefix(t,g),a=t.length;let we=[],Ee=[],fe=[],se=n,X,De=()=>g.index===a-1,Re=g.peek=(x=1)=>t[g.index+x],gt=g.advance=()=>t[++g.index]||"",j=()=>t.slice(g.index+1),rt=(x="",w=0)=>{g.consumed+=x,g.index+=w},Fe=x=>{g.output+=x.output!=null?x.output:x.value,rt(x.value)},Ne=()=>{let x=1;for(;Re()==="!"&&(Re(2)!=="("||Re(3)==="?");)gt(),g.start++,x++;return x%2===0?!1:(g.negated=!0,g.start++,!0)},Pe=x=>{g[x]++,fe.push(x)},Ye=x=>{g[x]--,fe.pop()},ke=x=>{if(se.type==="globstar"){let w=g.braces>0&&(x.type==="comma"||x.type==="brace"),b=x.extglob===!0||we.length&&(x.type==="pipe"||x.type==="paren");x.type!=="slash"&&x.type!=="paren"&&!w&&!b&&(g.output=g.output.slice(0,-se.output.length),se.type="star",se.value="*",se.output=Ce,g.output+=se.output)}if(we.length&&x.type!=="paren"&&(we[we.length-1].inner+=x.value),(x.value||x.output)&&Fe(x),se&&se.type==="text"&&x.type==="text"){se.value+=x.value,se.output=(se.output||"")+x.value;return}x.prev=se,c.push(x),se=x},it=(x,w)=>{let b={...E[w],conditions:1,inner:""};b.prev=se,b.parens=g.parens,b.output=g.output;let y=(r.capture?"(":"")+b.open;Pe("parens"),ke({type:x,value:w,output:g.output?"":I}),ke({type:"paren",extglob:!0,value:gt(),output:y}),we.push(b)},_e=x=>{let w=x.close+(r.capture?")":""),b;if(x.type==="negate"){let y=Ce;if(x.inner&&x.inner.length>1&&x.inner.includes("/")&&(y=me(r)),(y!==Ce||De()||/^\)+$/.test(j()))&&(w=x.close=`)$))${y}`),x.inner.includes("*")&&(b=j())&&/^\.[^\\/.]+$/.test(b)){let F=P4(b,{...e,fastpaths:!1}).output;w=x.close=`)${F})${y})`}x.prev.type==="bos"&&(g.negatedExtglob=!0)}ke({type:"paren",extglob:!0,value:X,output:w}),Ye("parens")};if(r.fastpaths!==!1&&!/(^[*!]|[/()[\]{}"])/.test(t)){let x=!1,w=t.replace(ont,(b,y,F,z,Z,$)=>z==="\\"?(x=!0,b):z==="?"?y?y+z+(Z?te.repeat(Z.length):""):$===0?Be+(Z?te.repeat(Z.length):""):te.repeat(F.length):z==="."?C.repeat(F.length):z==="*"?y?y+z+(Z?Ce:""):Ce:y?b:`\\${b}`);return x===!0&&(r.unescape===!0?w=w.replace(/\\/g,""):w=w.replace(/\\+/g,b=>b.length%2===0?"\\\\":b?"\\":"")),w===t&&r.contains===!0?(g.output=t,g):(g.output=Gl.wrapOutput(w,g,e),g)}for(;!De();){if(X=gt(),X==="\0")continue;if(X==="\\"){let b=Re();if(b==="/"&&r.bash!==!0||b==="."||b===";")continue;if(!b){X+="\\",ke({type:"text",value:X});continue}let y=/^\\+/.exec(j()),F=0;if(y&&y[0].length>2&&(F=y[0].length,g.index+=F,F%2!==0&&(X+="\\")),r.unescape===!0?X=gt():X+=gt(),g.brackets===0){ke({type:"text",value:X});continue}}if(g.brackets>0&&(X!=="]"||se.value==="["||se.value==="[^")){if(r.posix!==!1&&X===":"){let b=se.value.slice(1);if(b.includes("[")&&(se.posix=!0,b.includes(":"))){let y=se.value.lastIndexOf("["),F=se.value.slice(0,y),z=se.value.slice(y+2),Z=int[z];if(Z){se.value=F+Z,g.backtrack=!0,gt(),!n.output&&c.indexOf(se)===1&&(n.output=I);continue}}}(X==="["&&Re()!==":"||X==="-"&&Re()==="]")&&(X=`\\${X}`),X==="]"&&(se.value==="["||se.value==="[^")&&(X=`\\${X}`),r.posix===!0&&X==="!"&&se.value==="["&&(X="^"),se.value+=X,Fe({value:X});continue}if(g.quotes===1&&X!=='"'){X=Gl.escapeRegex(X),se.value+=X,Fe({value:X});continue}if(X==='"'){g.quotes=g.quotes===1?0:1,r.keepQuotes===!0&&ke({type:"text",value:X});continue}if(X==="("){Pe("parens"),ke({type:"paren",value:X});continue}if(X===")"){if(g.parens===0&&r.strictBrackets===!0)throw new SyntaxError(RE("opening","("));let b=we[we.length-1];if(b&&g.parens===b.parens+1){_e(we.pop());continue}ke({type:"paren",value:X,output:g.parens?")":"\\)"}),Ye("parens");continue}if(X==="["){if(r.nobracket===!0||!j().includes("]")){if(r.nobracket!==!0&&r.strictBrackets===!0)throw new SyntaxError(RE("closing","]"));X=`\\${X}`}else Pe("brackets");ke({type:"bracket",value:X});continue}if(X==="]"){if(r.nobracket===!0||se&&se.type==="bracket"&&se.value.length===1){ke({type:"text",value:X,output:`\\${X}`});continue}if(g.brackets===0){if(r.strictBrackets===!0)throw new SyntaxError(RE("opening","["));ke({type:"text",value:X,output:`\\${X}`});continue}Ye("brackets");let b=se.value.slice(1);if(se.posix!==!0&&b[0]==="^"&&!b.includes("/")&&(X=`/${X}`),se.value+=X,Fe({value:X}),r.literalBrackets===!1||Gl.hasRegexChars(b))continue;let y=Gl.escapeRegex(se.value);if(g.output=g.output.slice(0,-se.value.length),r.literalBrackets===!0){g.output+=y,se.value=y;continue}se.value=`(${f}${y}|${se.value})`,g.output+=se.value;continue}if(X==="{"&&r.nobrace!==!0){Pe("braces");let b={type:"brace",value:X,output:"(",outputIndex:g.output.length,tokensIndex:g.tokens.length};Ee.push(b),ke(b);continue}if(X==="}"){let b=Ee[Ee.length-1];if(r.nobrace===!0||!b){ke({type:"text",value:X,output:X});continue}let y=")";if(b.dots===!0){let F=c.slice(),z=[];for(let Z=F.length-1;Z>=0&&(c.pop(),F[Z].type!=="brace");Z--)F[Z].type!=="dots"&&z.unshift(F[Z].value);y=ant(z,r),g.backtrack=!0}if(b.comma!==!0&&b.dots!==!0){let F=g.output.slice(0,b.outputIndex),z=g.tokens.slice(b.tokensIndex);b.value=b.output="\\{",X=y="\\}",g.output=F;for(let Z of z)g.output+=Z.output||Z.value}ke({type:"brace",value:X,output:y}),Ye("braces"),Ee.pop();continue}if(X==="|"){we.length>0&&we[we.length-1].conditions++,ke({type:"text",value:X});continue}if(X===","){let b=X,y=Ee[Ee.length-1];y&&fe[fe.length-1]==="braces"&&(y.comma=!0,b="|"),ke({type:"comma",value:X,output:b});continue}if(X==="/"){if(se.type==="dot"&&g.index===g.start+1){g.start=g.index+1,g.consumed="",g.output="",c.pop(),se=n;continue}ke({type:"slash",value:X,output:P});continue}if(X==="."){if(g.braces>0&&se.type==="dot"){se.value==="."&&(se.output=C);let b=Ee[Ee.length-1];se.type="dots",se.output+=X,se.value+=X,b.dots=!0;continue}if(g.braces+g.parens===0&&se.type!=="bos"&&se.type!=="slash"){ke({type:"text",value:X,output:C});continue}ke({type:"dot",value:X,output:C});continue}if(X==="?"){if(!(se&&se.value==="(")&&r.noextglob!==!0&&Re()==="("&&Re(2)!=="?"){it("qmark",X);continue}if(se&&se.type==="paren"){let y=Re(),F=X;if(y==="<"&&!Gl.supportsLookbehinds())throw new Error("Node.js v10 or higher is required for regex lookbehinds");(se.value==="("&&!/[!=<:]/.test(y)||y==="<"&&!/<([!=]|\w+>)/.test(j()))&&(F=`\\${X}`),ke({type:"text",value:X,output:F});continue}if(r.dot!==!0&&(se.type==="slash"||se.type==="bos")){ke({type:"qmark",value:X,output:ie});continue}ke({type:"qmark",value:X,output:te});continue}if(X==="!"){if(r.noextglob!==!0&&Re()==="("&&(Re(2)!=="?"||!/[!=<:]/.test(Re(3)))){it("negate",X);continue}if(r.nonegate!==!0&&g.index===0){Ne();continue}}if(X==="+"){if(r.noextglob!==!0&&Re()==="("&&Re(2)!=="?"){it("plus",X);continue}if(se&&se.value==="("||r.regex===!1){ke({type:"plus",value:X,output:S});continue}if(se&&(se.type==="bracket"||se.type==="paren"||se.type==="brace")||g.parens>0){ke({type:"plus",value:X});continue}ke({type:"plus",value:S});continue}if(X==="@"){if(r.noextglob!==!0&&Re()==="("&&Re(2)!=="?"){ke({type:"at",extglob:!0,value:X,output:""});continue}ke({type:"text",value:X});continue}if(X!=="*"){(X==="$"||X==="^")&&(X=`\\${X}`);let b=snt.exec(j());b&&(X+=b[0],g.index+=b[0].length),ke({type:"text",value:X});continue}if(se&&(se.type==="globstar"||se.star===!0)){se.type="star",se.star=!0,se.value+=X,se.output=Ce,g.backtrack=!0,g.globstar=!0,rt(X);continue}let x=j();if(r.noextglob!==!0&&/^\([^?]/.test(x)){it("star",X);continue}if(se.type==="star"){if(r.noglobstar===!0){rt(X);continue}let b=se.prev,y=b.prev,F=b.type==="slash"||b.type==="bos",z=y&&(y.type==="star"||y.type==="globstar");if(r.bash===!0&&(!F||x[0]&&x[0]!=="/")){ke({type:"star",value:X,output:""});continue}let Z=g.braces>0&&(b.type==="comma"||b.type==="brace"),$=we.length&&(b.type==="pipe"||b.type==="paren");if(!F&&b.type!=="paren"&&!Z&&!$){ke({type:"star",value:X,output:""});continue}for(;x.slice(0,3)==="/**";){let oe=t[g.index+4];if(oe&&oe!=="/")break;x=x.slice(3),rt("/**",3)}if(b.type==="bos"&&De()){se.type="globstar",se.value+=X,se.output=me(r),g.output=se.output,g.globstar=!0,rt(X);continue}if(b.type==="slash"&&b.prev.type!=="bos"&&!z&&De()){g.output=g.output.slice(0,-(b.output+se.output).length),b.output=`(?:${b.output}`,se.type="globstar",se.output=me(r)+(r.strictSlashes?")":"|$)"),se.value+=X,g.globstar=!0,g.output+=b.output+se.output,rt(X);continue}if(b.type==="slash"&&b.prev.type!=="bos"&&x[0]==="/"){let oe=x[1]!==void 0?"|$":"";g.output=g.output.slice(0,-(b.output+se.output).length),b.output=`(?:${b.output}`,se.type="globstar",se.output=`${me(r)}${P}|${P}${oe})`,se.value+=X,g.output+=b.output+se.output,g.globstar=!0,rt(X+gt()),ke({type:"slash",value:"/",output:""});continue}if(b.type==="bos"&&x[0]==="/"){se.type="globstar",se.value+=X,se.output=`(?:^|${P}|${me(r)}${P})`,g.output=se.output,g.globstar=!0,rt(X+gt()),ke({type:"slash",value:"/",output:""});continue}g.output=g.output.slice(0,-se.output.length),se.type="globstar",se.output=me(r),se.value+=X,g.output+=se.output,g.globstar=!0,rt(X);continue}let w={type:"star",value:X,output:Ce};if(r.bash===!0){w.output=".*?",(se.type==="bos"||se.type==="slash")&&(w.output=pe+w.output),ke(w);continue}if(se&&(se.type==="bracket"||se.type==="paren")&&r.regex===!0){w.output=X,ke(w);continue}(g.index===g.start||se.type==="slash"||se.type==="dot")&&(se.type==="dot"?(g.output+=U,se.output+=U):r.dot===!0?(g.output+=W,se.output+=W):(g.output+=pe,se.output+=pe),Re()!=="*"&&(g.output+=I,se.output+=I)),ke(w)}for(;g.brackets>0;){if(r.strictBrackets===!0)throw new SyntaxError(RE("closing","]"));g.output=Gl.escapeLast(g.output,"["),Ye("brackets")}for(;g.parens>0;){if(r.strictBrackets===!0)throw new SyntaxError(RE("closing",")"));g.output=Gl.escapeLast(g.output,"("),Ye("parens")}for(;g.braces>0;){if(r.strictBrackets===!0)throw new SyntaxError(RE("closing","}"));g.output=Gl.escapeLast(g.output,"{"),Ye("braces")}if(r.strictSlashes!==!0&&(se.type==="star"||se.type==="bracket")&&ke({type:"maybe_slash",value:"",output:`${P}?`}),g.backtrack===!0){g.output="";for(let x of g.tokens)g.output+=x.output!=null?x.output:x.value,x.suffix&&(g.output+=x.suffix)}return g};P4.fastpaths=(t,e)=>{let r={...e},s=typeof r.maxLength=="number"?Math.min(Dk,r.maxLength):Dk,a=t.length;if(a>s)throw new SyntaxError(`Input length: ${a}, exceeds maximum allowed length: ${s}`);t=Pae[t]||t;let n=Gl.isWindows(e),{DOT_LITERAL:c,SLASH_LITERAL:f,ONE_CHAR:p,DOTS_SLASH:h,NO_DOT:E,NO_DOTS:C,NO_DOTS_SLASH:S,STAR:P,START_ANCHOR:I}=Sk.globChars(n),R=r.dot?C:E,N=r.dot?S:E,U=r.capture?"":"?:",W={negated:!1,prefix:""},te=r.bash===!0?".*?":P;r.capture&&(te=`(${te})`);let ie=pe=>pe.noglobstar===!0?te:`(${U}(?:(?!${I}${pe.dot?h:c}).)*?)`,Ae=pe=>{switch(pe){case"*":return`${R}${p}${te}`;case".*":return`${c}${p}${te}`;case"*.*":return`${R}${te}${c}${p}${te}`;case"*/*":return`${R}${te}${f}${p}${N}${te}`;case"**":return R+ie(r);case"**/*":return`(?:${R}${ie(r)}${f})?${N}${p}${te}`;case"**/*.*":return`(?:${R}${ie(r)}${f})?${N}${te}${c}${p}${te}`;case"**/.*":return`(?:${R}${ie(r)}${f})?${c}${p}${te}`;default:{let Be=/^(.*?)\.(\w+)$/.exec(pe);if(!Be)return;let Ce=Ae(Be[1]);return Ce?Ce+c+Be[2]:void 0}}},ce=Gl.removePrefix(t,W),me=Ae(ce);return me&&r.strictSlashes!==!0&&(me+=`${f}?`),me};xae.exports=P4});var Tae=L((rVt,Qae)=>{"use strict";var lnt=ye("path"),cnt=bae(),x4=kae(),k4=SB(),unt=vB(),fnt=t=>t&&typeof t=="object"&&!Array.isArray(t),Xi=(t,e,r=!1)=>{if(Array.isArray(t)){let E=t.map(S=>Xi(S,e,r));return S=>{for(let P of E){let I=P(S);if(I)return I}return!1}}let s=fnt(t)&&t.tokens&&t.input;if(t===""||typeof t!="string"&&!s)throw new TypeError("Expected pattern to be a non-empty string");let a=e||{},n=k4.isWindows(e),c=s?Xi.compileRe(t,e):Xi.makeRe(t,e,!1,!0),f=c.state;delete c.state;let p=()=>!1;if(a.ignore){let E={...e,ignore:null,onMatch:null,onResult:null};p=Xi(a.ignore,E,r)}let h=(E,C=!1)=>{let{isMatch:S,match:P,output:I}=Xi.test(E,c,e,{glob:t,posix:n}),R={glob:t,state:f,regex:c,posix:n,input:E,output:I,match:P,isMatch:S};return typeof a.onResult=="function"&&a.onResult(R),S===!1?(R.isMatch=!1,C?R:!1):p(E)?(typeof a.onIgnore=="function"&&a.onIgnore(R),R.isMatch=!1,C?R:!1):(typeof a.onMatch=="function"&&a.onMatch(R),C?R:!0)};return r&&(h.state=f),h};Xi.test=(t,e,r,{glob:s,posix:a}={})=>{if(typeof t!="string")throw new TypeError("Expected input to be a string");if(t==="")return{isMatch:!1,output:""};let n=r||{},c=n.format||(a?k4.toPosixSlashes:null),f=t===s,p=f&&c?c(t):t;return f===!1&&(p=c?c(t):t,f=p===s),(f===!1||n.capture===!0)&&(n.matchBase===!0||n.basename===!0?f=Xi.matchBase(t,e,r,a):f=e.exec(p)),{isMatch:!!f,match:f,output:p}};Xi.matchBase=(t,e,r,s=k4.isWindows(r))=>(e instanceof RegExp?e:Xi.makeRe(e,r)).test(lnt.basename(t));Xi.isMatch=(t,e,r)=>Xi(e,r)(t);Xi.parse=(t,e)=>Array.isArray(t)?t.map(r=>Xi.parse(r,e)):x4(t,{...e,fastpaths:!1});Xi.scan=(t,e)=>cnt(t,e);Xi.compileRe=(t,e,r=!1,s=!1)=>{if(r===!0)return t.output;let a=e||{},n=a.contains?"":"^",c=a.contains?"":"$",f=`${n}(?:${t.output})${c}`;t&&t.negated===!0&&(f=`^(?!${f}).*$`);let p=Xi.toRegex(f,e);return s===!0&&(p.state=t),p};Xi.makeRe=(t,e={},r=!1,s=!1)=>{if(!t||typeof t!="string")throw new TypeError("Expected a non-empty string");let a={negated:!1,fastpaths:!0};return e.fastpaths!==!1&&(t[0]==="."||t[0]==="*")&&(a.output=x4.fastpaths(t,e)),a.output||(a=x4(t,e)),Xi.compileRe(a,e,r,s)};Xi.toRegex=(t,e)=>{try{let r=e||{};return new RegExp(t,r.flags||(r.nocase?"i":""))}catch(r){if(e&&e.debug===!0)throw r;return/$^/}};Xi.constants=unt;Qae.exports=Xi});var Fae=L((nVt,Rae)=>{"use strict";Rae.exports=Tae()});var Sa=L((iVt,Mae)=>{"use strict";var Oae=ye("util"),Lae=hae(),Jf=Fae(),Q4=SB(),Nae=t=>t===""||t==="./",ki=(t,e,r)=>{e=[].concat(e),t=[].concat(t);let s=new Set,a=new Set,n=new Set,c=0,f=E=>{n.add(E.output),r&&r.onResult&&r.onResult(E)};for(let E=0;E!s.has(E));if(r&&h.length===0){if(r.failglob===!0)throw new Error(`No matches found for "${e.join(", ")}"`);if(r.nonull===!0||r.nullglob===!0)return r.unescape?e.map(E=>E.replace(/\\/g,"")):e}return h};ki.match=ki;ki.matcher=(t,e)=>Jf(t,e);ki.isMatch=(t,e,r)=>Jf(e,r)(t);ki.any=ki.isMatch;ki.not=(t,e,r={})=>{e=[].concat(e).map(String);let s=new Set,a=[],n=f=>{r.onResult&&r.onResult(f),a.push(f.output)},c=new Set(ki(t,e,{...r,onResult:n}));for(let f of a)c.has(f)||s.add(f);return[...s]};ki.contains=(t,e,r)=>{if(typeof t!="string")throw new TypeError(`Expected a string: "${Oae.inspect(t)}"`);if(Array.isArray(e))return e.some(s=>ki.contains(t,s,r));if(typeof e=="string"){if(Nae(t)||Nae(e))return!1;if(t.includes(e)||t.startsWith("./")&&t.slice(2).includes(e))return!0}return ki.isMatch(t,e,{...r,contains:!0})};ki.matchKeys=(t,e,r)=>{if(!Q4.isObject(t))throw new TypeError("Expected the first argument to be an object");let s=ki(Object.keys(t),e,r),a={};for(let n of s)a[n]=t[n];return a};ki.some=(t,e,r)=>{let s=[].concat(t);for(let a of[].concat(e)){let n=Jf(String(a),r);if(s.some(c=>n(c)))return!0}return!1};ki.every=(t,e,r)=>{let s=[].concat(t);for(let a of[].concat(e)){let n=Jf(String(a),r);if(!s.every(c=>n(c)))return!1}return!0};ki.all=(t,e,r)=>{if(typeof t!="string")throw new TypeError(`Expected a string: "${Oae.inspect(t)}"`);return[].concat(e).every(s=>Jf(s,r)(t))};ki.capture=(t,e,r)=>{let s=Q4.isWindows(r),n=Jf.makeRe(String(t),{...r,capture:!0}).exec(s?Q4.toPosixSlashes(e):e);if(n)return n.slice(1).map(c=>c===void 0?"":c)};ki.makeRe=(...t)=>Jf.makeRe(...t);ki.scan=(...t)=>Jf.scan(...t);ki.parse=(t,e)=>{let r=[];for(let s of[].concat(t||[]))for(let a of Lae(String(s),e))r.push(Jf.parse(a,e));return r};ki.braces=(t,e)=>{if(typeof t!="string")throw new TypeError("Expected a string");return e&&e.nobrace===!0||!/\{.*\}/.test(t)?[t]:Lae(t,e)};ki.braceExpand=(t,e)=>{if(typeof t!="string")throw new TypeError("Expected a string");return ki.braces(t,{...e,expand:!0})};Mae.exports=ki});var Uae=L((sVt,_ae)=>{"use strict";_ae.exports=({onlyFirst:t=!1}={})=>{let e=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(e,t?void 0:"g")}});var bk=L((oVt,Hae)=>{"use strict";var Ant=Uae();Hae.exports=t=>typeof t=="string"?t.replace(Ant(),""):t});var qae=L((aVt,jae)=>{function pnt(){this.__data__=[],this.size=0}jae.exports=pnt});var FE=L((lVt,Gae)=>{function hnt(t,e){return t===e||t!==t&&e!==e}Gae.exports=hnt});var bB=L((cVt,Wae)=>{var gnt=FE();function dnt(t,e){for(var r=t.length;r--;)if(gnt(t[r][0],e))return r;return-1}Wae.exports=dnt});var Vae=L((uVt,Yae)=>{var mnt=bB(),ynt=Array.prototype,Ent=ynt.splice;function Int(t){var e=this.__data__,r=mnt(e,t);if(r<0)return!1;var s=e.length-1;return r==s?e.pop():Ent.call(e,r,1),--this.size,!0}Yae.exports=Int});var Jae=L((fVt,Kae)=>{var Cnt=bB();function wnt(t){var e=this.__data__,r=Cnt(e,t);return r<0?void 0:e[r][1]}Kae.exports=wnt});var Zae=L((AVt,zae)=>{var Bnt=bB();function vnt(t){return Bnt(this.__data__,t)>-1}zae.exports=vnt});var $ae=L((pVt,Xae)=>{var Snt=bB();function Dnt(t,e){var r=this.__data__,s=Snt(r,t);return s<0?(++this.size,r.push([t,e])):r[s][1]=e,this}Xae.exports=Dnt});var PB=L((hVt,ele)=>{var bnt=qae(),Pnt=Vae(),xnt=Jae(),knt=Zae(),Qnt=$ae();function NE(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e{var Tnt=PB();function Rnt(){this.__data__=new Tnt,this.size=0}tle.exports=Rnt});var ile=L((dVt,nle)=>{function Fnt(t){var e=this.__data__,r=e.delete(t);return this.size=e.size,r}nle.exports=Fnt});var ole=L((mVt,sle)=>{function Nnt(t){return this.__data__.get(t)}sle.exports=Nnt});var lle=L((yVt,ale)=>{function Ont(t){return this.__data__.has(t)}ale.exports=Ont});var T4=L((EVt,cle)=>{var Lnt=typeof global=="object"&&global&&global.Object===Object&&global;cle.exports=Lnt});var Pc=L((IVt,ule)=>{var Mnt=T4(),_nt=typeof self=="object"&&self&&self.Object===Object&&self,Unt=Mnt||_nt||Function("return this")();ule.exports=Unt});var Yd=L((CVt,fle)=>{var Hnt=Pc(),jnt=Hnt.Symbol;fle.exports=jnt});var gle=L((wVt,hle)=>{var Ale=Yd(),ple=Object.prototype,qnt=ple.hasOwnProperty,Gnt=ple.toString,xB=Ale?Ale.toStringTag:void 0;function Wnt(t){var e=qnt.call(t,xB),r=t[xB];try{t[xB]=void 0;var s=!0}catch{}var a=Gnt.call(t);return s&&(e?t[xB]=r:delete t[xB]),a}hle.exports=Wnt});var mle=L((BVt,dle)=>{var Ynt=Object.prototype,Vnt=Ynt.toString;function Knt(t){return Vnt.call(t)}dle.exports=Knt});var Vd=L((vVt,Ile)=>{var yle=Yd(),Jnt=gle(),znt=mle(),Znt="[object Null]",Xnt="[object Undefined]",Ele=yle?yle.toStringTag:void 0;function $nt(t){return t==null?t===void 0?Xnt:Znt:Ele&&Ele in Object(t)?Jnt(t):znt(t)}Ile.exports=$nt});var Wl=L((SVt,Cle)=>{function eit(t){var e=typeof t;return t!=null&&(e=="object"||e=="function")}Cle.exports=eit});var Pk=L((DVt,wle)=>{var tit=Vd(),rit=Wl(),nit="[object AsyncFunction]",iit="[object Function]",sit="[object GeneratorFunction]",oit="[object Proxy]";function ait(t){if(!rit(t))return!1;var e=tit(t);return e==iit||e==sit||e==nit||e==oit}wle.exports=ait});var vle=L((bVt,Ble)=>{var lit=Pc(),cit=lit["__core-js_shared__"];Ble.exports=cit});var ble=L((PVt,Dle)=>{var R4=vle(),Sle=function(){var t=/[^.]+$/.exec(R4&&R4.keys&&R4.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}();function uit(t){return!!Sle&&Sle in t}Dle.exports=uit});var F4=L((xVt,Ple)=>{var fit=Function.prototype,Ait=fit.toString;function pit(t){if(t!=null){try{return Ait.call(t)}catch{}try{return t+""}catch{}}return""}Ple.exports=pit});var kle=L((kVt,xle)=>{var hit=Pk(),git=ble(),dit=Wl(),mit=F4(),yit=/[\\^$.*+?()[\]{}|]/g,Eit=/^\[object .+?Constructor\]$/,Iit=Function.prototype,Cit=Object.prototype,wit=Iit.toString,Bit=Cit.hasOwnProperty,vit=RegExp("^"+wit.call(Bit).replace(yit,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function Sit(t){if(!dit(t)||git(t))return!1;var e=hit(t)?vit:Eit;return e.test(mit(t))}xle.exports=Sit});var Tle=L((QVt,Qle)=>{function Dit(t,e){return t?.[e]}Qle.exports=Dit});var A0=L((TVt,Rle)=>{var bit=kle(),Pit=Tle();function xit(t,e){var r=Pit(t,e);return bit(r)?r:void 0}Rle.exports=xit});var xk=L((RVt,Fle)=>{var kit=A0(),Qit=Pc(),Tit=kit(Qit,"Map");Fle.exports=Tit});var kB=L((FVt,Nle)=>{var Rit=A0(),Fit=Rit(Object,"create");Nle.exports=Fit});var Mle=L((NVt,Lle)=>{var Ole=kB();function Nit(){this.__data__=Ole?Ole(null):{},this.size=0}Lle.exports=Nit});var Ule=L((OVt,_le)=>{function Oit(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}_le.exports=Oit});var jle=L((LVt,Hle)=>{var Lit=kB(),Mit="__lodash_hash_undefined__",_it=Object.prototype,Uit=_it.hasOwnProperty;function Hit(t){var e=this.__data__;if(Lit){var r=e[t];return r===Mit?void 0:r}return Uit.call(e,t)?e[t]:void 0}Hle.exports=Hit});var Gle=L((MVt,qle)=>{var jit=kB(),qit=Object.prototype,Git=qit.hasOwnProperty;function Wit(t){var e=this.__data__;return jit?e[t]!==void 0:Git.call(e,t)}qle.exports=Wit});var Yle=L((_Vt,Wle)=>{var Yit=kB(),Vit="__lodash_hash_undefined__";function Kit(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=Yit&&e===void 0?Vit:e,this}Wle.exports=Kit});var Kle=L((UVt,Vle)=>{var Jit=Mle(),zit=Ule(),Zit=jle(),Xit=Gle(),$it=Yle();function OE(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e{var Jle=Kle(),est=PB(),tst=xk();function rst(){this.size=0,this.__data__={hash:new Jle,map:new(tst||est),string:new Jle}}zle.exports=rst});var $le=L((jVt,Xle)=>{function nst(t){var e=typeof t;return e=="string"||e=="number"||e=="symbol"||e=="boolean"?t!=="__proto__":t===null}Xle.exports=nst});var QB=L((qVt,ece)=>{var ist=$le();function sst(t,e){var r=t.__data__;return ist(e)?r[typeof e=="string"?"string":"hash"]:r.map}ece.exports=sst});var rce=L((GVt,tce)=>{var ost=QB();function ast(t){var e=ost(this,t).delete(t);return this.size-=e?1:0,e}tce.exports=ast});var ice=L((WVt,nce)=>{var lst=QB();function cst(t){return lst(this,t).get(t)}nce.exports=cst});var oce=L((YVt,sce)=>{var ust=QB();function fst(t){return ust(this,t).has(t)}sce.exports=fst});var lce=L((VVt,ace)=>{var Ast=QB();function pst(t,e){var r=Ast(this,t),s=r.size;return r.set(t,e),this.size+=r.size==s?0:1,this}ace.exports=pst});var kk=L((KVt,cce)=>{var hst=Zle(),gst=rce(),dst=ice(),mst=oce(),yst=lce();function LE(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e{var Est=PB(),Ist=xk(),Cst=kk(),wst=200;function Bst(t,e){var r=this.__data__;if(r instanceof Est){var s=r.__data__;if(!Ist||s.length{var vst=PB(),Sst=rle(),Dst=ile(),bst=ole(),Pst=lle(),xst=fce();function ME(t){var e=this.__data__=new vst(t);this.size=e.size}ME.prototype.clear=Sst;ME.prototype.delete=Dst;ME.prototype.get=bst;ME.prototype.has=Pst;ME.prototype.set=xst;Ace.exports=ME});var hce=L((ZVt,pce)=>{var kst="__lodash_hash_undefined__";function Qst(t){return this.__data__.set(t,kst),this}pce.exports=Qst});var dce=L((XVt,gce)=>{function Tst(t){return this.__data__.has(t)}gce.exports=Tst});var yce=L(($Vt,mce)=>{var Rst=kk(),Fst=hce(),Nst=dce();function Tk(t){var e=-1,r=t==null?0:t.length;for(this.__data__=new Rst;++e{function Ost(t,e){for(var r=-1,s=t==null?0:t.length;++r{function Lst(t,e){return t.has(e)}Cce.exports=Lst});var N4=L((r7t,Bce)=>{var Mst=yce(),_st=Ice(),Ust=wce(),Hst=1,jst=2;function qst(t,e,r,s,a,n){var c=r&Hst,f=t.length,p=e.length;if(f!=p&&!(c&&p>f))return!1;var h=n.get(t),E=n.get(e);if(h&&E)return h==e&&E==t;var C=-1,S=!0,P=r&jst?new Mst:void 0;for(n.set(t,e),n.set(e,t);++C{var Gst=Pc(),Wst=Gst.Uint8Array;vce.exports=Wst});var Dce=L((i7t,Sce)=>{function Yst(t){var e=-1,r=Array(t.size);return t.forEach(function(s,a){r[++e]=[a,s]}),r}Sce.exports=Yst});var Pce=L((s7t,bce)=>{function Vst(t){var e=-1,r=Array(t.size);return t.forEach(function(s){r[++e]=s}),r}bce.exports=Vst});var Rce=L((o7t,Tce)=>{var xce=Yd(),kce=O4(),Kst=FE(),Jst=N4(),zst=Dce(),Zst=Pce(),Xst=1,$st=2,eot="[object Boolean]",tot="[object Date]",rot="[object Error]",not="[object Map]",iot="[object Number]",sot="[object RegExp]",oot="[object Set]",aot="[object String]",lot="[object Symbol]",cot="[object ArrayBuffer]",uot="[object DataView]",Qce=xce?xce.prototype:void 0,L4=Qce?Qce.valueOf:void 0;function fot(t,e,r,s,a,n,c){switch(r){case uot:if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case cot:return!(t.byteLength!=e.byteLength||!n(new kce(t),new kce(e)));case eot:case tot:case iot:return Kst(+t,+e);case rot:return t.name==e.name&&t.message==e.message;case sot:case aot:return t==e+"";case not:var f=zst;case oot:var p=s&Xst;if(f||(f=Zst),t.size!=e.size&&!p)return!1;var h=c.get(t);if(h)return h==e;s|=$st,c.set(t,e);var E=Jst(f(t),f(e),s,a,n,c);return c.delete(t),E;case lot:if(L4)return L4.call(t)==L4.call(e)}return!1}Tce.exports=fot});var Rk=L((a7t,Fce)=>{function Aot(t,e){for(var r=-1,s=e.length,a=t.length;++r{var pot=Array.isArray;Nce.exports=pot});var M4=L((c7t,Oce)=>{var hot=Rk(),got=xc();function dot(t,e,r){var s=e(t);return got(t)?s:hot(s,r(t))}Oce.exports=dot});var Mce=L((u7t,Lce)=>{function mot(t,e){for(var r=-1,s=t==null?0:t.length,a=0,n=[];++r{function yot(){return[]}_ce.exports=yot});var Fk=L((A7t,Hce)=>{var Eot=Mce(),Iot=_4(),Cot=Object.prototype,wot=Cot.propertyIsEnumerable,Uce=Object.getOwnPropertySymbols,Bot=Uce?function(t){return t==null?[]:(t=Object(t),Eot(Uce(t),function(e){return wot.call(t,e)}))}:Iot;Hce.exports=Bot});var qce=L((p7t,jce)=>{function vot(t,e){for(var r=-1,s=Array(t);++r{function Sot(t){return t!=null&&typeof t=="object"}Gce.exports=Sot});var Yce=L((g7t,Wce)=>{var Dot=Vd(),bot=zf(),Pot="[object Arguments]";function xot(t){return bot(t)&&Dot(t)==Pot}Wce.exports=xot});var TB=L((d7t,Jce)=>{var Vce=Yce(),kot=zf(),Kce=Object.prototype,Qot=Kce.hasOwnProperty,Tot=Kce.propertyIsEnumerable,Rot=Vce(function(){return arguments}())?Vce:function(t){return kot(t)&&Qot.call(t,"callee")&&!Tot.call(t,"callee")};Jce.exports=Rot});var Zce=L((m7t,zce)=>{function Fot(){return!1}zce.exports=Fot});var FB=L((RB,_E)=>{var Not=Pc(),Oot=Zce(),eue=typeof RB=="object"&&RB&&!RB.nodeType&&RB,Xce=eue&&typeof _E=="object"&&_E&&!_E.nodeType&&_E,Lot=Xce&&Xce.exports===eue,$ce=Lot?Not.Buffer:void 0,Mot=$ce?$ce.isBuffer:void 0,_ot=Mot||Oot;_E.exports=_ot});var NB=L((y7t,tue)=>{var Uot=9007199254740991,Hot=/^(?:0|[1-9]\d*)$/;function jot(t,e){var r=typeof t;return e=e??Uot,!!e&&(r=="number"||r!="symbol"&&Hot.test(t))&&t>-1&&t%1==0&&t{var qot=9007199254740991;function Got(t){return typeof t=="number"&&t>-1&&t%1==0&&t<=qot}rue.exports=Got});var iue=L((I7t,nue)=>{var Wot=Vd(),Yot=Nk(),Vot=zf(),Kot="[object Arguments]",Jot="[object Array]",zot="[object Boolean]",Zot="[object Date]",Xot="[object Error]",$ot="[object Function]",eat="[object Map]",tat="[object Number]",rat="[object Object]",nat="[object RegExp]",iat="[object Set]",sat="[object String]",oat="[object WeakMap]",aat="[object ArrayBuffer]",lat="[object DataView]",cat="[object Float32Array]",uat="[object Float64Array]",fat="[object Int8Array]",Aat="[object Int16Array]",pat="[object Int32Array]",hat="[object Uint8Array]",gat="[object Uint8ClampedArray]",dat="[object Uint16Array]",mat="[object Uint32Array]",Si={};Si[cat]=Si[uat]=Si[fat]=Si[Aat]=Si[pat]=Si[hat]=Si[gat]=Si[dat]=Si[mat]=!0;Si[Kot]=Si[Jot]=Si[aat]=Si[zot]=Si[lat]=Si[Zot]=Si[Xot]=Si[$ot]=Si[eat]=Si[tat]=Si[rat]=Si[nat]=Si[iat]=Si[sat]=Si[oat]=!1;function yat(t){return Vot(t)&&Yot(t.length)&&!!Si[Wot(t)]}nue.exports=yat});var Ok=L((C7t,sue)=>{function Eat(t){return function(e){return t(e)}}sue.exports=Eat});var Lk=L((OB,UE)=>{var Iat=T4(),oue=typeof OB=="object"&&OB&&!OB.nodeType&&OB,LB=oue&&typeof UE=="object"&&UE&&!UE.nodeType&&UE,Cat=LB&&LB.exports===oue,U4=Cat&&Iat.process,wat=function(){try{var t=LB&&LB.require&&LB.require("util").types;return t||U4&&U4.binding&&U4.binding("util")}catch{}}();UE.exports=wat});var Mk=L((w7t,cue)=>{var Bat=iue(),vat=Ok(),aue=Lk(),lue=aue&&aue.isTypedArray,Sat=lue?vat(lue):Bat;cue.exports=Sat});var H4=L((B7t,uue)=>{var Dat=qce(),bat=TB(),Pat=xc(),xat=FB(),kat=NB(),Qat=Mk(),Tat=Object.prototype,Rat=Tat.hasOwnProperty;function Fat(t,e){var r=Pat(t),s=!r&&bat(t),a=!r&&!s&&xat(t),n=!r&&!s&&!a&&Qat(t),c=r||s||a||n,f=c?Dat(t.length,String):[],p=f.length;for(var h in t)(e||Rat.call(t,h))&&!(c&&(h=="length"||a&&(h=="offset"||h=="parent")||n&&(h=="buffer"||h=="byteLength"||h=="byteOffset")||kat(h,p)))&&f.push(h);return f}uue.exports=Fat});var _k=L((v7t,fue)=>{var Nat=Object.prototype;function Oat(t){var e=t&&t.constructor,r=typeof e=="function"&&e.prototype||Nat;return t===r}fue.exports=Oat});var j4=L((S7t,Aue)=>{function Lat(t,e){return function(r){return t(e(r))}}Aue.exports=Lat});var hue=L((D7t,pue)=>{var Mat=j4(),_at=Mat(Object.keys,Object);pue.exports=_at});var due=L((b7t,gue)=>{var Uat=_k(),Hat=hue(),jat=Object.prototype,qat=jat.hasOwnProperty;function Gat(t){if(!Uat(t))return Hat(t);var e=[];for(var r in Object(t))qat.call(t,r)&&r!="constructor"&&e.push(r);return e}gue.exports=Gat});var MB=L((P7t,mue)=>{var Wat=Pk(),Yat=Nk();function Vat(t){return t!=null&&Yat(t.length)&&!Wat(t)}mue.exports=Vat});var Uk=L((x7t,yue)=>{var Kat=H4(),Jat=due(),zat=MB();function Zat(t){return zat(t)?Kat(t):Jat(t)}yue.exports=Zat});var q4=L((k7t,Eue)=>{var Xat=M4(),$at=Fk(),elt=Uk();function tlt(t){return Xat(t,elt,$at)}Eue.exports=tlt});var wue=L((Q7t,Cue)=>{var Iue=q4(),rlt=1,nlt=Object.prototype,ilt=nlt.hasOwnProperty;function slt(t,e,r,s,a,n){var c=r&rlt,f=Iue(t),p=f.length,h=Iue(e),E=h.length;if(p!=E&&!c)return!1;for(var C=p;C--;){var S=f[C];if(!(c?S in e:ilt.call(e,S)))return!1}var P=n.get(t),I=n.get(e);if(P&&I)return P==e&&I==t;var R=!0;n.set(t,e),n.set(e,t);for(var N=c;++C{var olt=A0(),alt=Pc(),llt=olt(alt,"DataView");Bue.exports=llt});var Due=L((R7t,Sue)=>{var clt=A0(),ult=Pc(),flt=clt(ult,"Promise");Sue.exports=flt});var Pue=L((F7t,bue)=>{var Alt=A0(),plt=Pc(),hlt=Alt(plt,"Set");bue.exports=hlt});var kue=L((N7t,xue)=>{var glt=A0(),dlt=Pc(),mlt=glt(dlt,"WeakMap");xue.exports=mlt});var _B=L((O7t,Lue)=>{var G4=vue(),W4=xk(),Y4=Due(),V4=Pue(),K4=kue(),Oue=Vd(),HE=F4(),Que="[object Map]",ylt="[object Object]",Tue="[object Promise]",Rue="[object Set]",Fue="[object WeakMap]",Nue="[object DataView]",Elt=HE(G4),Ilt=HE(W4),Clt=HE(Y4),wlt=HE(V4),Blt=HE(K4),Kd=Oue;(G4&&Kd(new G4(new ArrayBuffer(1)))!=Nue||W4&&Kd(new W4)!=Que||Y4&&Kd(Y4.resolve())!=Tue||V4&&Kd(new V4)!=Rue||K4&&Kd(new K4)!=Fue)&&(Kd=function(t){var e=Oue(t),r=e==ylt?t.constructor:void 0,s=r?HE(r):"";if(s)switch(s){case Elt:return Nue;case Ilt:return Que;case Clt:return Tue;case wlt:return Rue;case Blt:return Fue}return e});Lue.exports=Kd});var Wue=L((L7t,Gue)=>{var J4=Qk(),vlt=N4(),Slt=Rce(),Dlt=wue(),Mue=_B(),_ue=xc(),Uue=FB(),blt=Mk(),Plt=1,Hue="[object Arguments]",jue="[object Array]",Hk="[object Object]",xlt=Object.prototype,que=xlt.hasOwnProperty;function klt(t,e,r,s,a,n){var c=_ue(t),f=_ue(e),p=c?jue:Mue(t),h=f?jue:Mue(e);p=p==Hue?Hk:p,h=h==Hue?Hk:h;var E=p==Hk,C=h==Hk,S=p==h;if(S&&Uue(t)){if(!Uue(e))return!1;c=!0,E=!1}if(S&&!E)return n||(n=new J4),c||blt(t)?vlt(t,e,r,s,a,n):Slt(t,e,p,r,s,a,n);if(!(r&Plt)){var P=E&&que.call(t,"__wrapped__"),I=C&&que.call(e,"__wrapped__");if(P||I){var R=P?t.value():t,N=I?e.value():e;return n||(n=new J4),a(R,N,r,s,n)}}return S?(n||(n=new J4),Dlt(t,e,r,s,a,n)):!1}Gue.exports=klt});var Jue=L((M7t,Kue)=>{var Qlt=Wue(),Yue=zf();function Vue(t,e,r,s,a){return t===e?!0:t==null||e==null||!Yue(t)&&!Yue(e)?t!==t&&e!==e:Qlt(t,e,r,s,Vue,a)}Kue.exports=Vue});var Zue=L((_7t,zue)=>{var Tlt=Jue();function Rlt(t,e){return Tlt(t,e)}zue.exports=Rlt});var z4=L((U7t,Xue)=>{var Flt=A0(),Nlt=function(){try{var t=Flt(Object,"defineProperty");return t({},"",{}),t}catch{}}();Xue.exports=Nlt});var jk=L((H7t,efe)=>{var $ue=z4();function Olt(t,e,r){e=="__proto__"&&$ue?$ue(t,e,{configurable:!0,enumerable:!0,value:r,writable:!0}):t[e]=r}efe.exports=Olt});var Z4=L((j7t,tfe)=>{var Llt=jk(),Mlt=FE();function _lt(t,e,r){(r!==void 0&&!Mlt(t[e],r)||r===void 0&&!(e in t))&&Llt(t,e,r)}tfe.exports=_lt});var nfe=L((q7t,rfe)=>{function Ult(t){return function(e,r,s){for(var a=-1,n=Object(e),c=s(e),f=c.length;f--;){var p=c[t?f:++a];if(r(n[p],p,n)===!1)break}return e}}rfe.exports=Ult});var sfe=L((G7t,ife)=>{var Hlt=nfe(),jlt=Hlt();ife.exports=jlt});var X4=L((UB,jE)=>{var qlt=Pc(),cfe=typeof UB=="object"&&UB&&!UB.nodeType&&UB,ofe=cfe&&typeof jE=="object"&&jE&&!jE.nodeType&&jE,Glt=ofe&&ofe.exports===cfe,afe=Glt?qlt.Buffer:void 0,lfe=afe?afe.allocUnsafe:void 0;function Wlt(t,e){if(e)return t.slice();var r=t.length,s=lfe?lfe(r):new t.constructor(r);return t.copy(s),s}jE.exports=Wlt});var qk=L((W7t,ffe)=>{var ufe=O4();function Ylt(t){var e=new t.constructor(t.byteLength);return new ufe(e).set(new ufe(t)),e}ffe.exports=Ylt});var $4=L((Y7t,Afe)=>{var Vlt=qk();function Klt(t,e){var r=e?Vlt(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.length)}Afe.exports=Klt});var Gk=L((V7t,pfe)=>{function Jlt(t,e){var r=-1,s=t.length;for(e||(e=Array(s));++r{var zlt=Wl(),hfe=Object.create,Zlt=function(){function t(){}return function(e){if(!zlt(e))return{};if(hfe)return hfe(e);t.prototype=e;var r=new t;return t.prototype=void 0,r}}();gfe.exports=Zlt});var Wk=L((J7t,mfe)=>{var Xlt=j4(),$lt=Xlt(Object.getPrototypeOf,Object);mfe.exports=$lt});var e3=L((z7t,yfe)=>{var ect=dfe(),tct=Wk(),rct=_k();function nct(t){return typeof t.constructor=="function"&&!rct(t)?ect(tct(t)):{}}yfe.exports=nct});var Ife=L((Z7t,Efe)=>{var ict=MB(),sct=zf();function oct(t){return sct(t)&&ict(t)}Efe.exports=oct});var t3=L((X7t,wfe)=>{var act=Vd(),lct=Wk(),cct=zf(),uct="[object Object]",fct=Function.prototype,Act=Object.prototype,Cfe=fct.toString,pct=Act.hasOwnProperty,hct=Cfe.call(Object);function gct(t){if(!cct(t)||act(t)!=uct)return!1;var e=lct(t);if(e===null)return!0;var r=pct.call(e,"constructor")&&e.constructor;return typeof r=="function"&&r instanceof r&&Cfe.call(r)==hct}wfe.exports=gct});var r3=L(($7t,Bfe)=>{function dct(t,e){if(!(e==="constructor"&&typeof t[e]=="function")&&e!="__proto__")return t[e]}Bfe.exports=dct});var Yk=L((eKt,vfe)=>{var mct=jk(),yct=FE(),Ect=Object.prototype,Ict=Ect.hasOwnProperty;function Cct(t,e,r){var s=t[e];(!(Ict.call(t,e)&&yct(s,r))||r===void 0&&!(e in t))&&mct(t,e,r)}vfe.exports=Cct});var Jd=L((tKt,Sfe)=>{var wct=Yk(),Bct=jk();function vct(t,e,r,s){var a=!r;r||(r={});for(var n=-1,c=e.length;++n{function Sct(t){var e=[];if(t!=null)for(var r in Object(t))e.push(r);return e}Dfe.exports=Sct});var xfe=L((nKt,Pfe)=>{var Dct=Wl(),bct=_k(),Pct=bfe(),xct=Object.prototype,kct=xct.hasOwnProperty;function Qct(t){if(!Dct(t))return Pct(t);var e=bct(t),r=[];for(var s in t)s=="constructor"&&(e||!kct.call(t,s))||r.push(s);return r}Pfe.exports=Qct});var qE=L((iKt,kfe)=>{var Tct=H4(),Rct=xfe(),Fct=MB();function Nct(t){return Fct(t)?Tct(t,!0):Rct(t)}kfe.exports=Nct});var Tfe=L((sKt,Qfe)=>{var Oct=Jd(),Lct=qE();function Mct(t){return Oct(t,Lct(t))}Qfe.exports=Mct});var Mfe=L((oKt,Lfe)=>{var Rfe=Z4(),_ct=X4(),Uct=$4(),Hct=Gk(),jct=e3(),Ffe=TB(),Nfe=xc(),qct=Ife(),Gct=FB(),Wct=Pk(),Yct=Wl(),Vct=t3(),Kct=Mk(),Ofe=r3(),Jct=Tfe();function zct(t,e,r,s,a,n,c){var f=Ofe(t,r),p=Ofe(e,r),h=c.get(p);if(h){Rfe(t,r,h);return}var E=n?n(f,p,r+"",t,e,c):void 0,C=E===void 0;if(C){var S=Nfe(p),P=!S&&Gct(p),I=!S&&!P&&Kct(p);E=p,S||P||I?Nfe(f)?E=f:qct(f)?E=Hct(f):P?(C=!1,E=_ct(p,!0)):I?(C=!1,E=Uct(p,!0)):E=[]:Vct(p)||Ffe(p)?(E=f,Ffe(f)?E=Jct(f):(!Yct(f)||Wct(f))&&(E=jct(p))):C=!1}C&&(c.set(p,E),a(E,p,s,n,c),c.delete(p)),Rfe(t,r,E)}Lfe.exports=zct});var Hfe=L((aKt,Ufe)=>{var Zct=Qk(),Xct=Z4(),$ct=sfe(),eut=Mfe(),tut=Wl(),rut=qE(),nut=r3();function _fe(t,e,r,s,a){t!==e&&$ct(e,function(n,c){if(a||(a=new Zct),tut(n))eut(t,e,c,r,_fe,s,a);else{var f=s?s(nut(t,c),n,c+"",t,e,a):void 0;f===void 0&&(f=n),Xct(t,c,f)}},rut)}Ufe.exports=_fe});var n3=L((lKt,jfe)=>{function iut(t){return t}jfe.exports=iut});var Gfe=L((cKt,qfe)=>{function sut(t,e,r){switch(r.length){case 0:return t.call(e);case 1:return t.call(e,r[0]);case 2:return t.call(e,r[0],r[1]);case 3:return t.call(e,r[0],r[1],r[2])}return t.apply(e,r)}qfe.exports=sut});var i3=L((uKt,Yfe)=>{var out=Gfe(),Wfe=Math.max;function aut(t,e,r){return e=Wfe(e===void 0?t.length-1:e,0),function(){for(var s=arguments,a=-1,n=Wfe(s.length-e,0),c=Array(n);++a{function lut(t){return function(){return t}}Vfe.exports=lut});var Zfe=L((AKt,zfe)=>{var cut=Kfe(),Jfe=z4(),uut=n3(),fut=Jfe?function(t,e){return Jfe(t,"toString",{configurable:!0,enumerable:!1,value:cut(e),writable:!0})}:uut;zfe.exports=fut});var $fe=L((pKt,Xfe)=>{var Aut=800,put=16,hut=Date.now;function gut(t){var e=0,r=0;return function(){var s=hut(),a=put-(s-r);if(r=s,a>0){if(++e>=Aut)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}Xfe.exports=gut});var s3=L((hKt,eAe)=>{var dut=Zfe(),mut=$fe(),yut=mut(dut);eAe.exports=yut});var rAe=L((gKt,tAe)=>{var Eut=n3(),Iut=i3(),Cut=s3();function wut(t,e){return Cut(Iut(t,e,Eut),t+"")}tAe.exports=wut});var iAe=L((dKt,nAe)=>{var But=FE(),vut=MB(),Sut=NB(),Dut=Wl();function but(t,e,r){if(!Dut(r))return!1;var s=typeof e;return(s=="number"?vut(r)&&Sut(e,r.length):s=="string"&&e in r)?But(r[e],t):!1}nAe.exports=but});var oAe=L((mKt,sAe)=>{var Put=rAe(),xut=iAe();function kut(t){return Put(function(e,r){var s=-1,a=r.length,n=a>1?r[a-1]:void 0,c=a>2?r[2]:void 0;for(n=t.length>3&&typeof n=="function"?(a--,n):void 0,c&&xut(r[0],r[1],c)&&(n=a<3?void 0:n,a=1),e=Object(e);++s{var Qut=Hfe(),Tut=oAe(),Rut=Tut(function(t,e,r,s){Qut(t,e,r,s)});aAe.exports=Rut});var je={};Vt(je,{AsyncActions:()=>l3,BufferStream:()=>a3,CachingStrategy:()=>IAe,DefaultStream:()=>c3,allSettledSafe:()=>Uu,assertNever:()=>f3,bufferStream:()=>WE,buildIgnorePattern:()=>Uut,convertMapsToIndexableObjects:()=>Kk,dynamicRequire:()=>kp,escapeRegExp:()=>Nut,getArrayWithDefault:()=>jB,getFactoryWithDefault:()=>Vl,getMapWithDefault:()=>A3,getSetWithDefault:()=>xp,groupBy:()=>qut,isIndexableObject:()=>o3,isPathLike:()=>Hut,isTaggedYarnVersion:()=>Fut,makeDeferred:()=>mAe,mapAndFilter:()=>Yl,mapAndFind:()=>p0,mergeIntoTarget:()=>wAe,overrideType:()=>Out,parseBoolean:()=>qB,parseInt:()=>YE,parseOptionalBoolean:()=>CAe,plural:()=>Vk,prettifyAsyncErrors:()=>GE,prettifySyncErrors:()=>p3,releaseAfterUseAsync:()=>Mut,replaceEnvVariables:()=>Jk,sortMap:()=>Ws,toMerged:()=>jut,tryParseOptionalBoolean:()=>h3,validateEnum:()=>Lut});function Fut(t){return!!(hAe.default.valid(t)&&t.match(/^[^-]+(-rc\.[0-9]+)?$/))}function Vk(t,{one:e,more:r,zero:s=r}){return t===0?s:t===1?e:r}function Nut(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Out(t){}function f3(t){throw new Error(`Assertion failed: Unexpected object '${t}'`)}function Lut(t,e){let r=Object.values(t);if(!r.includes(e))throw new nt(`Invalid value for enumeration: ${JSON.stringify(e)} (expected one of ${r.map(s=>JSON.stringify(s)).join(", ")})`);return e}function Yl(t,e){let r=[];for(let s of t){let a=e(s);a!==gAe&&r.push(a)}return r}function p0(t,e){for(let r of t){let s=e(r);if(s!==dAe)return s}}function o3(t){return typeof t=="object"&&t!==null}async function Uu(t){let e=await Promise.allSettled(t),r=[];for(let s of e){if(s.status==="rejected")throw s.reason;r.push(s.value)}return r}function Kk(t){if(t instanceof Map&&(t=Object.fromEntries(t)),o3(t))for(let e of Object.keys(t)){let r=t[e];o3(r)&&(t[e]=Kk(r))}return t}function Vl(t,e,r){let s=t.get(e);return typeof s>"u"&&t.set(e,s=r()),s}function jB(t,e){let r=t.get(e);return typeof r>"u"&&t.set(e,r=[]),r}function xp(t,e){let r=t.get(e);return typeof r>"u"&&t.set(e,r=new Set),r}function A3(t,e){let r=t.get(e);return typeof r>"u"&&t.set(e,r=new Map),r}async function Mut(t,e){if(e==null)return await t();try{return await t()}finally{await e()}}async function GE(t,e){try{return await t()}catch(r){throw r.message=e(r.message),r}}function p3(t,e){try{return t()}catch(r){throw r.message=e(r.message),r}}async function WE(t){return await new Promise((e,r)=>{let s=[];t.on("error",a=>{r(a)}),t.on("data",a=>{s.push(a)}),t.on("end",()=>{e(Buffer.concat(s))})})}function mAe(){let t,e;return{promise:new Promise((s,a)=>{t=s,e=a}),resolve:t,reject:e}}function yAe(t){return HB(ue.fromPortablePath(t))}function EAe(path){let physicalPath=ue.fromPortablePath(path),currentCacheEntry=HB.cache[physicalPath];delete HB.cache[physicalPath];let result;try{result=yAe(physicalPath);let freshCacheEntry=HB.cache[physicalPath],dynamicModule=eval("module"),freshCacheIndex=dynamicModule.children.indexOf(freshCacheEntry);freshCacheIndex!==-1&&dynamicModule.children.splice(freshCacheIndex,1)}finally{HB.cache[physicalPath]=currentCacheEntry}return result}function _ut(t){let e=cAe.get(t),r=le.statSync(t);if(e?.mtime===r.mtimeMs)return e.instance;let s=EAe(t);return cAe.set(t,{mtime:r.mtimeMs,instance:s}),s}function kp(t,{cachingStrategy:e=2}={}){switch(e){case 0:return EAe(t);case 1:return _ut(t);case 2:return yAe(t);default:throw new Error("Unsupported caching strategy")}}function Ws(t,e){let r=Array.from(t);Array.isArray(e)||(e=[e]);let s=[];for(let n of e)s.push(r.map(c=>n(c)));let a=r.map((n,c)=>c);return a.sort((n,c)=>{for(let f of s){let p=f[n]f[c]?1:0;if(p!==0)return p}return 0}),a.map(n=>r[n])}function Uut(t){return t.length===0?null:t.map(e=>`(${AAe.default.makeRe(e,{windows:!1,dot:!0}).source})`).join("|")}function Jk(t,{env:e}){let r=/\${(?[\d\w_]+)(?:)?(?:-(?[^}]*))?}/g;return t.replace(r,(...s)=>{let{variableName:a,colon:n,fallback:c}=s[s.length-1],f=Object.hasOwn(e,a),p=e[a];if(p||f&&!n)return p;if(c!=null)return c;throw new nt(`Environment variable not found (${a})`)})}function qB(t){switch(t){case"true":case"1":case 1:case!0:return!0;case"false":case"0":case 0:case!1:return!1;default:throw new Error(`Couldn't parse "${t}" as a boolean`)}}function CAe(t){return typeof t>"u"?t:qB(t)}function h3(t){try{return CAe(t)}catch{return null}}function Hut(t){return!!(ue.isAbsolute(t)||t.match(/^(\.{1,2}|~)\//))}function wAe(t,...e){let r=c=>({value:c}),s=r(t),a=e.map(c=>r(c)),{value:n}=(0,fAe.default)(s,...a,(c,f)=>{if(Array.isArray(c)&&Array.isArray(f)){for(let p of f)c.find(h=>(0,uAe.default)(h,p))||c.push(p);return c}});return n}function jut(...t){return wAe({},...t)}function qut(t,e){let r=Object.create(null);for(let s of t){let a=s[e];r[a]??=[],r[a].push(s)}return r}function YE(t){return typeof t=="string"?Number.parseInt(t,10):t}var uAe,fAe,AAe,pAe,hAe,u3,gAe,dAe,a3,l3,c3,HB,cAe,IAe,kc=It(()=>{bt();Wt();uAe=et(Zue()),fAe=et(lAe()),AAe=et(Sa()),pAe=et(Md()),hAe=et(fi()),u3=ye("stream");gAe=Symbol();Yl.skip=gAe;dAe=Symbol();p0.skip=dAe;a3=class extends u3.Transform{constructor(){super(...arguments);this.chunks=[]}_transform(r,s,a){if(s!=="buffer"||!Buffer.isBuffer(r))throw new Error("Assertion failed: BufferStream only accept buffers");this.chunks.push(r),a(null,null)}_flush(r){r(null,Buffer.concat(this.chunks))}};l3=class{constructor(e){this.deferred=new Map;this.promises=new Map;this.limit=(0,pAe.default)(e)}set(e,r){let s=this.deferred.get(e);typeof s>"u"&&this.deferred.set(e,s=mAe());let a=this.limit(()=>r());return this.promises.set(e,a),a.then(()=>{this.promises.get(e)===a&&s.resolve()},n=>{this.promises.get(e)===a&&s.reject(n)}),s.promise}reduce(e,r){let s=this.promises.get(e)??Promise.resolve();this.set(e,()=>r(s))}async wait(){await Promise.all(this.promises.values())}},c3=class extends u3.Transform{constructor(r=Buffer.alloc(0)){super();this.active=!0;this.ifEmpty=r}_transform(r,s,a){if(s!=="buffer"||!Buffer.isBuffer(r))throw new Error("Assertion failed: DefaultStream only accept buffers");this.active=!1,a(null,r)}_flush(r){this.active&&this.ifEmpty.length>0?r(null,this.ifEmpty):r(null)}},HB=eval("require");cAe=new Map;IAe=(s=>(s[s.NoCache=0]="NoCache",s[s.FsTime=1]="FsTime",s[s.Node=2]="Node",s))(IAe||{})});var VE,g3,d3,BAe=It(()=>{VE=(r=>(r.HARD="HARD",r.SOFT="SOFT",r))(VE||{}),g3=(s=>(s.Dependency="Dependency",s.PeerDependency="PeerDependency",s.PeerDependencyMeta="PeerDependencyMeta",s))(g3||{}),d3=(s=>(s.Inactive="inactive",s.Redundant="redundant",s.Active="active",s))(d3||{})});var he={};Vt(he,{LogLevel:()=>eQ,Style:()=>Zk,Type:()=>Ct,addLogFilterSupport:()=>YB,applyColor:()=>po,applyHyperlink:()=>JE,applyStyle:()=>zd,json:()=>Zd,jsonOrPretty:()=>Yut,mark:()=>C3,pretty:()=>Ut,prettyField:()=>Zf,prettyList:()=>I3,prettyTruncatedLocatorList:()=>$k,stripAnsi:()=>KE.default,supportsColor:()=>Xk,supportsHyperlinks:()=>E3,tuple:()=>Hu});function vAe(t){let e=["KiB","MiB","GiB","TiB"],r=e.length;for(;r>1&&t<1024**r;)r-=1;let s=1024**r;return`${Math.floor(t*100/s)/100} ${e[r-1]}`}function Hu(t,e){return[e,t]}function zd(t,e,r){return t.get("enableColors")&&r&2&&(e=WB.default.bold(e)),e}function po(t,e,r){if(!t.get("enableColors"))return e;let s=Gut.get(r);if(s===null)return e;let a=typeof s>"u"?r:y3.level>=3?s[0]:s[1],n=typeof a=="number"?m3.ansi256(a):a.startsWith("#")?m3.hex(a):m3[a];if(typeof n!="function")throw new Error(`Invalid format type ${a}`);return n(e)}function JE(t,e,r){return t.get("enableHyperlinks")?Wut?`\x1B]8;;${r}\x1B\\${e}\x1B]8;;\x1B\\`:`\x1B]8;;${r}\x07${e}\x1B]8;;\x07`:e}function Ut(t,e,r){if(e===null)return po(t,"null",Ct.NULL);if(Object.hasOwn(zk,r))return zk[r].pretty(t,e);if(typeof e!="string")throw new Error(`Assertion failed: Expected the value to be a string, got ${typeof e}`);return po(t,e,r)}function I3(t,e,r,{separator:s=", "}={}){return[...e].map(a=>Ut(t,a,r)).join(s)}function Zd(t,e){if(t===null)return null;if(Object.hasOwn(zk,e))return zk[e].json(t);if(typeof t!="string")throw new Error(`Assertion failed: Expected the value to be a string, got ${typeof t}`);return t}function Yut(t,e,[r,s]){return t?Zd(r,s):Ut(e,r,s)}function C3(t){return{Check:po(t,"\u2713","green"),Cross:po(t,"\u2718","red"),Question:po(t,"?","cyan")}}function Zf(t,{label:e,value:[r,s]}){return`${Ut(t,e,Ct.CODE)}: ${Ut(t,r,s)}`}function $k(t,e,r){let s=[],a=[...e],n=r;for(;a.length>0;){let h=a[0],E=`${Yr(t,h)}, `,C=w3(h).length+2;if(s.length>0&&nh).join("").slice(0,-2);let c="X".repeat(a.length.toString().length),f=`and ${c} more.`,p=a.length;for(;s.length>1&&nh).join(""),f.replace(c,Ut(t,p,Ct.NUMBER))].join("")}function YB(t,{configuration:e}){let r=e.get("logFilters"),s=new Map,a=new Map,n=[];for(let C of r){let S=C.get("level");if(typeof S>"u")continue;let P=C.get("code");typeof P<"u"&&s.set(P,S);let I=C.get("text");typeof I<"u"&&a.set(I,S);let R=C.get("pattern");typeof R<"u"&&n.push([SAe.default.matcher(R,{contains:!0}),S])}n.reverse();let c=(C,S,P)=>{if(C===null||C===0)return P;let I=a.size>0||n.length>0?(0,KE.default)(S):S;if(a.size>0){let R=a.get(I);if(typeof R<"u")return R??P}if(n.length>0){for(let[R,N]of n)if(R(I))return N??P}if(s.size>0){let R=s.get(Vf(C));if(typeof R<"u")return R??P}return P},f=t.reportInfo,p=t.reportWarning,h=t.reportError,E=function(C,S,P,I){switch(c(S,P,I)){case"info":f.call(C,S,P);break;case"warning":p.call(C,S??0,P);break;case"error":h.call(C,S??0,P);break}};t.reportInfo=function(...C){return E(this,...C,"info")},t.reportWarning=function(...C){return E(this,...C,"warning")},t.reportError=function(...C){return E(this,...C,"error")}}var WB,GB,SAe,KE,DAe,Ct,Zk,y3,Xk,E3,m3,Gut,Wo,zk,Wut,eQ,Qc=It(()=>{bt();WB=et(g4()),GB=et(Nd());Wt();SAe=et(Sa()),KE=et(bk()),DAe=ye("util");nk();Yo();Ct={NO_HINT:"NO_HINT",ID:"ID",NULL:"NULL",SCOPE:"SCOPE",NAME:"NAME",RANGE:"RANGE",REFERENCE:"REFERENCE",NUMBER:"NUMBER",PATH:"PATH",URL:"URL",ADDED:"ADDED",REMOVED:"REMOVED",CODE:"CODE",INSPECT:"INSPECT",DURATION:"DURATION",SIZE:"SIZE",SIZE_DIFF:"SIZE_DIFF",IDENT:"IDENT",DESCRIPTOR:"DESCRIPTOR",LOCATOR:"LOCATOR",RESOLUTION:"RESOLUTION",DEPENDENT:"DEPENDENT",PACKAGE_EXTENSION:"PACKAGE_EXTENSION",SETTING:"SETTING",MARKDOWN:"MARKDOWN",MARKDOWN_INLINE:"MARKDOWN_INLINE"},Zk=(e=>(e[e.BOLD=2]="BOLD",e))(Zk||{}),y3=GB.default.GITHUB_ACTIONS?{level:2}:WB.default.supportsColor?{level:WB.default.supportsColor.level}:{level:0},Xk=y3.level!==0,E3=Xk&&!GB.default.GITHUB_ACTIONS&&!GB.default.CIRCLE&&!GB.default.GITLAB,m3=new WB.default.Instance(y3),Gut=new Map([[Ct.NO_HINT,null],[Ct.NULL,["#a853b5",129]],[Ct.SCOPE,["#d75f00",166]],[Ct.NAME,["#d7875f",173]],[Ct.RANGE,["#00afaf",37]],[Ct.REFERENCE,["#87afff",111]],[Ct.NUMBER,["#ffd700",220]],[Ct.PATH,["#d75fd7",170]],[Ct.URL,["#d75fd7",170]],[Ct.ADDED,["#5faf00",70]],[Ct.REMOVED,["#ff3131",160]],[Ct.CODE,["#87afff",111]],[Ct.SIZE,["#ffd700",220]]]),Wo=t=>t;zk={[Ct.ID]:Wo({pretty:(t,e)=>typeof e=="number"?po(t,`${e}`,Ct.NUMBER):po(t,e,Ct.CODE),json:t=>t}),[Ct.INSPECT]:Wo({pretty:(t,e)=>(0,DAe.inspect)(e,{depth:1/0,colors:t.get("enableColors"),compact:!0,breakLength:1/0}),json:t=>t}),[Ct.NUMBER]:Wo({pretty:(t,e)=>po(t,`${e}`,Ct.NUMBER),json:t=>t}),[Ct.IDENT]:Wo({pretty:(t,e)=>$i(t,e),json:t=>cn(t)}),[Ct.LOCATOR]:Wo({pretty:(t,e)=>Yr(t,e),json:t=>cl(t)}),[Ct.DESCRIPTOR]:Wo({pretty:(t,e)=>ri(t,e),json:t=>ll(t)}),[Ct.RESOLUTION]:Wo({pretty:(t,{descriptor:e,locator:r})=>VB(t,e,r),json:({descriptor:t,locator:e})=>({descriptor:ll(t),locator:e!==null?cl(e):null})}),[Ct.DEPENDENT]:Wo({pretty:(t,{locator:e,descriptor:r})=>B3(t,e,r),json:({locator:t,descriptor:e})=>({locator:cl(t),descriptor:ll(e)})}),[Ct.PACKAGE_EXTENSION]:Wo({pretty:(t,e)=>{switch(e.type){case"Dependency":return`${$i(t,e.parentDescriptor)} \u27A4 ${po(t,"dependencies",Ct.CODE)} \u27A4 ${$i(t,e.descriptor)}`;case"PeerDependency":return`${$i(t,e.parentDescriptor)} \u27A4 ${po(t,"peerDependencies",Ct.CODE)} \u27A4 ${$i(t,e.descriptor)}`;case"PeerDependencyMeta":return`${$i(t,e.parentDescriptor)} \u27A4 ${po(t,"peerDependenciesMeta",Ct.CODE)} \u27A4 ${$i(t,Da(e.selector))} \u27A4 ${po(t,e.key,Ct.CODE)}`;default:throw new Error(`Assertion failed: Unsupported package extension type: ${e.type}`)}},json:t=>{switch(t.type){case"Dependency":return`${cn(t.parentDescriptor)} > ${cn(t.descriptor)}`;case"PeerDependency":return`${cn(t.parentDescriptor)} >> ${cn(t.descriptor)}`;case"PeerDependencyMeta":return`${cn(t.parentDescriptor)} >> ${t.selector} / ${t.key}`;default:throw new Error(`Assertion failed: Unsupported package extension type: ${t.type}`)}}}),[Ct.SETTING]:Wo({pretty:(t,e)=>(t.get(e),JE(t,po(t,e,Ct.CODE),`https://yarnpkg.com/configuration/yarnrc#${e}`)),json:t=>t}),[Ct.DURATION]:Wo({pretty:(t,e)=>{if(e>1e3*60){let r=Math.floor(e/1e3/60),s=Math.ceil((e-r*60*1e3)/1e3);return s===0?`${r}m`:`${r}m ${s}s`}else{let r=Math.floor(e/1e3),s=e-r*1e3;return s===0?`${r}s`:`${r}s ${s}ms`}},json:t=>t}),[Ct.SIZE]:Wo({pretty:(t,e)=>po(t,vAe(e),Ct.NUMBER),json:t=>t}),[Ct.SIZE_DIFF]:Wo({pretty:(t,e)=>{let r=e>=0?"+":"-",s=r==="+"?Ct.REMOVED:Ct.ADDED;return po(t,`${r} ${vAe(Math.max(Math.abs(e),1))}`,s)},json:t=>t}),[Ct.PATH]:Wo({pretty:(t,e)=>po(t,ue.fromPortablePath(e),Ct.PATH),json:t=>ue.fromPortablePath(t)}),[Ct.MARKDOWN]:Wo({pretty:(t,{text:e,format:r,paragraphs:s})=>qo(e,{format:r,paragraphs:s}),json:({text:t})=>t}),[Ct.MARKDOWN_INLINE]:Wo({pretty:(t,e)=>(e=e.replace(/(`+)((?:.|[\n])*?)\1/g,(r,s,a)=>Ut(t,s+a+s,Ct.CODE)),e=e.replace(/(\*\*)((?:.|[\n])*?)\1/g,(r,s,a)=>zd(t,a,2)),e),json:t=>t})};Wut=!!process.env.KONSOLE_VERSION;eQ=(a=>(a.Error="error",a.Warning="warning",a.Info="info",a.Discard="discard",a))(eQ||{})});var bAe=L(zE=>{"use strict";Object.defineProperty(zE,"__esModule",{value:!0});zE.splitWhen=zE.flatten=void 0;function Vut(t){return t.reduce((e,r)=>[].concat(e,r),[])}zE.flatten=Vut;function Kut(t,e){let r=[[]],s=0;for(let a of t)e(a)?(s++,r[s]=[]):r[s].push(a);return r}zE.splitWhen=Kut});var PAe=L(tQ=>{"use strict";Object.defineProperty(tQ,"__esModule",{value:!0});tQ.isEnoentCodeError=void 0;function Jut(t){return t.code==="ENOENT"}tQ.isEnoentCodeError=Jut});var xAe=L(rQ=>{"use strict";Object.defineProperty(rQ,"__esModule",{value:!0});rQ.createDirentFromStats=void 0;var v3=class{constructor(e,r){this.name=e,this.isBlockDevice=r.isBlockDevice.bind(r),this.isCharacterDevice=r.isCharacterDevice.bind(r),this.isDirectory=r.isDirectory.bind(r),this.isFIFO=r.isFIFO.bind(r),this.isFile=r.isFile.bind(r),this.isSocket=r.isSocket.bind(r),this.isSymbolicLink=r.isSymbolicLink.bind(r)}};function zut(t,e){return new v3(t,e)}rQ.createDirentFromStats=zut});var RAe=L(cs=>{"use strict";Object.defineProperty(cs,"__esModule",{value:!0});cs.convertPosixPathToPattern=cs.convertWindowsPathToPattern=cs.convertPathToPattern=cs.escapePosixPath=cs.escapeWindowsPath=cs.escape=cs.removeLeadingDotSegment=cs.makeAbsolute=cs.unixify=void 0;var Zut=ye("os"),Xut=ye("path"),kAe=Zut.platform()==="win32",$ut=2,eft=/(\\?)([()*?[\]{|}]|^!|[!+@](?=\()|\\(?![!()*+?@[\]{|}]))/g,tft=/(\\?)([()[\]{}]|^!|[!+@](?=\())/g,rft=/^\\\\([.?])/,nft=/\\(?![!()+@[\]{}])/g;function ift(t){return t.replace(/\\/g,"/")}cs.unixify=ift;function sft(t,e){return Xut.resolve(t,e)}cs.makeAbsolute=sft;function oft(t){if(t.charAt(0)==="."){let e=t.charAt(1);if(e==="/"||e==="\\")return t.slice($ut)}return t}cs.removeLeadingDotSegment=oft;cs.escape=kAe?S3:D3;function S3(t){return t.replace(tft,"\\$2")}cs.escapeWindowsPath=S3;function D3(t){return t.replace(eft,"\\$2")}cs.escapePosixPath=D3;cs.convertPathToPattern=kAe?QAe:TAe;function QAe(t){return S3(t).replace(rft,"//$1").replace(nft,"/")}cs.convertWindowsPathToPattern=QAe;function TAe(t){return D3(t)}cs.convertPosixPathToPattern=TAe});var NAe=L((RKt,FAe)=>{FAe.exports=function(e){if(typeof e!="string"||e==="")return!1;for(var r;r=/(\\).|([@?!+*]\(.*\))/g.exec(e);){if(r[2])return!0;e=e.slice(r.index+r[0].length)}return!1}});var MAe=L((FKt,LAe)=>{var aft=NAe(),OAe={"{":"}","(":")","[":"]"},lft=function(t){if(t[0]==="!")return!0;for(var e=0,r=-2,s=-2,a=-2,n=-2,c=-2;ee&&(c===-1||c>s||(c=t.indexOf("\\",e),c===-1||c>s)))||a!==-1&&t[e]==="{"&&t[e+1]!=="}"&&(a=t.indexOf("}",e),a>e&&(c=t.indexOf("\\",e),c===-1||c>a))||n!==-1&&t[e]==="("&&t[e+1]==="?"&&/[:!=]/.test(t[e+2])&&t[e+3]!==")"&&(n=t.indexOf(")",e),n>e&&(c=t.indexOf("\\",e),c===-1||c>n))||r!==-1&&t[e]==="("&&t[e+1]!=="|"&&(rr&&(c=t.indexOf("\\",r),c===-1||c>n))))return!0;if(t[e]==="\\"){var f=t[e+1];e+=2;var p=OAe[f];if(p){var h=t.indexOf(p,e);h!==-1&&(e=h+1)}if(t[e]==="!")return!0}else e++}return!1},cft=function(t){if(t[0]==="!")return!0;for(var e=0;e{"use strict";var uft=MAe(),fft=ye("path").posix.dirname,Aft=ye("os").platform()==="win32",b3="/",pft=/\\/g,hft=/[\{\[].*[\}\]]$/,gft=/(^|[^\\])([\{\[]|\([^\)]+$)/,dft=/\\([\!\*\?\|\[\]\(\)\{\}])/g;_Ae.exports=function(e,r){var s=Object.assign({flipBackslashes:!0},r);s.flipBackslashes&&Aft&&e.indexOf(b3)<0&&(e=e.replace(pft,b3)),hft.test(e)&&(e+=b3),e+="a";do e=fft(e);while(uft(e)||gft.test(e));return e.replace(dft,"$1")}});var KAe=L(jr=>{"use strict";Object.defineProperty(jr,"__esModule",{value:!0});jr.removeDuplicateSlashes=jr.matchAny=jr.convertPatternsToRe=jr.makeRe=jr.getPatternParts=jr.expandBraceExpansion=jr.expandPatternsWithBraceExpansion=jr.isAffectDepthOfReadingPattern=jr.endsWithSlashGlobStar=jr.hasGlobStar=jr.getBaseDirectory=jr.isPatternRelatedToParentDirectory=jr.getPatternsOutsideCurrentDirectory=jr.getPatternsInsideCurrentDirectory=jr.getPositivePatterns=jr.getNegativePatterns=jr.isPositivePattern=jr.isNegativePattern=jr.convertToNegativePattern=jr.convertToPositivePattern=jr.isDynamicPattern=jr.isStaticPattern=void 0;var mft=ye("path"),yft=UAe(),P3=Sa(),HAe="**",Eft="\\",Ift=/[*?]|^!/,Cft=/\[[^[]*]/,wft=/(?:^|[^!*+?@])\([^(]*\|[^|]*\)/,Bft=/[!*+?@]\([^(]*\)/,vft=/,|\.\./,Sft=/(?!^)\/{2,}/g;function jAe(t,e={}){return!qAe(t,e)}jr.isStaticPattern=jAe;function qAe(t,e={}){return t===""?!1:!!(e.caseSensitiveMatch===!1||t.includes(Eft)||Ift.test(t)||Cft.test(t)||wft.test(t)||e.extglob!==!1&&Bft.test(t)||e.braceExpansion!==!1&&Dft(t))}jr.isDynamicPattern=qAe;function Dft(t){let e=t.indexOf("{");if(e===-1)return!1;let r=t.indexOf("}",e+1);if(r===-1)return!1;let s=t.slice(e,r);return vft.test(s)}function bft(t){return nQ(t)?t.slice(1):t}jr.convertToPositivePattern=bft;function Pft(t){return"!"+t}jr.convertToNegativePattern=Pft;function nQ(t){return t.startsWith("!")&&t[1]!=="("}jr.isNegativePattern=nQ;function GAe(t){return!nQ(t)}jr.isPositivePattern=GAe;function xft(t){return t.filter(nQ)}jr.getNegativePatterns=xft;function kft(t){return t.filter(GAe)}jr.getPositivePatterns=kft;function Qft(t){return t.filter(e=>!x3(e))}jr.getPatternsInsideCurrentDirectory=Qft;function Tft(t){return t.filter(x3)}jr.getPatternsOutsideCurrentDirectory=Tft;function x3(t){return t.startsWith("..")||t.startsWith("./..")}jr.isPatternRelatedToParentDirectory=x3;function Rft(t){return yft(t,{flipBackslashes:!1})}jr.getBaseDirectory=Rft;function Fft(t){return t.includes(HAe)}jr.hasGlobStar=Fft;function WAe(t){return t.endsWith("/"+HAe)}jr.endsWithSlashGlobStar=WAe;function Nft(t){let e=mft.basename(t);return WAe(t)||jAe(e)}jr.isAffectDepthOfReadingPattern=Nft;function Oft(t){return t.reduce((e,r)=>e.concat(YAe(r)),[])}jr.expandPatternsWithBraceExpansion=Oft;function YAe(t){let e=P3.braces(t,{expand:!0,nodupes:!0,keepEscaping:!0});return e.sort((r,s)=>r.length-s.length),e.filter(r=>r!=="")}jr.expandBraceExpansion=YAe;function Lft(t,e){let{parts:r}=P3.scan(t,Object.assign(Object.assign({},e),{parts:!0}));return r.length===0&&(r=[t]),r[0].startsWith("/")&&(r[0]=r[0].slice(1),r.unshift("")),r}jr.getPatternParts=Lft;function VAe(t,e){return P3.makeRe(t,e)}jr.makeRe=VAe;function Mft(t,e){return t.map(r=>VAe(r,e))}jr.convertPatternsToRe=Mft;function _ft(t,e){return e.some(r=>r.test(t))}jr.matchAny=_ft;function Uft(t){return t.replace(Sft,"/")}jr.removeDuplicateSlashes=Uft});var XAe=L((LKt,ZAe)=>{"use strict";var Hft=ye("stream"),JAe=Hft.PassThrough,jft=Array.prototype.slice;ZAe.exports=qft;function qft(){let t=[],e=jft.call(arguments),r=!1,s=e[e.length-1];s&&!Array.isArray(s)&&s.pipe==null?e.pop():s={};let a=s.end!==!1,n=s.pipeError===!0;s.objectMode==null&&(s.objectMode=!0),s.highWaterMark==null&&(s.highWaterMark=64*1024);let c=JAe(s);function f(){for(let E=0,C=arguments.length;E0||(r=!1,p())}function P(I){function R(){I.removeListener("merge2UnpipeEnd",R),I.removeListener("end",R),n&&I.removeListener("error",N),S()}function N(U){c.emit("error",U)}if(I._readableState.endEmitted)return S();I.on("merge2UnpipeEnd",R),I.on("end",R),n&&I.on("error",N),I.pipe(c,{end:!1}),I.resume()}for(let I=0;I{"use strict";Object.defineProperty(iQ,"__esModule",{value:!0});iQ.merge=void 0;var Gft=XAe();function Wft(t){let e=Gft(t);return t.forEach(r=>{r.once("error",s=>e.emit("error",s))}),e.once("close",()=>$Ae(t)),e.once("end",()=>$Ae(t)),e}iQ.merge=Wft;function $Ae(t){t.forEach(e=>e.emit("close"))}});var tpe=L(ZE=>{"use strict";Object.defineProperty(ZE,"__esModule",{value:!0});ZE.isEmpty=ZE.isString=void 0;function Yft(t){return typeof t=="string"}ZE.isString=Yft;function Vft(t){return t===""}ZE.isEmpty=Vft});var Qp=L(Vo=>{"use strict";Object.defineProperty(Vo,"__esModule",{value:!0});Vo.string=Vo.stream=Vo.pattern=Vo.path=Vo.fs=Vo.errno=Vo.array=void 0;var Kft=bAe();Vo.array=Kft;var Jft=PAe();Vo.errno=Jft;var zft=xAe();Vo.fs=zft;var Zft=RAe();Vo.path=Zft;var Xft=KAe();Vo.pattern=Xft;var $ft=epe();Vo.stream=$ft;var eAt=tpe();Vo.string=eAt});var spe=L(Ko=>{"use strict";Object.defineProperty(Ko,"__esModule",{value:!0});Ko.convertPatternGroupToTask=Ko.convertPatternGroupsToTasks=Ko.groupPatternsByBaseDirectory=Ko.getNegativePatternsAsPositive=Ko.getPositivePatterns=Ko.convertPatternsToTasks=Ko.generate=void 0;var ju=Qp();function tAt(t,e){let r=rpe(t,e),s=rpe(e.ignore,e),a=npe(r),n=ipe(r,s),c=a.filter(E=>ju.pattern.isStaticPattern(E,e)),f=a.filter(E=>ju.pattern.isDynamicPattern(E,e)),p=k3(c,n,!1),h=k3(f,n,!0);return p.concat(h)}Ko.generate=tAt;function rpe(t,e){let r=t;return e.braceExpansion&&(r=ju.pattern.expandPatternsWithBraceExpansion(r)),e.baseNameMatch&&(r=r.map(s=>s.includes("/")?s:`**/${s}`)),r.map(s=>ju.pattern.removeDuplicateSlashes(s))}function k3(t,e,r){let s=[],a=ju.pattern.getPatternsOutsideCurrentDirectory(t),n=ju.pattern.getPatternsInsideCurrentDirectory(t),c=Q3(a),f=Q3(n);return s.push(...T3(c,e,r)),"."in f?s.push(R3(".",n,e,r)):s.push(...T3(f,e,r)),s}Ko.convertPatternsToTasks=k3;function npe(t){return ju.pattern.getPositivePatterns(t)}Ko.getPositivePatterns=npe;function ipe(t,e){return ju.pattern.getNegativePatterns(t).concat(e).map(ju.pattern.convertToPositivePattern)}Ko.getNegativePatternsAsPositive=ipe;function Q3(t){let e={};return t.reduce((r,s)=>{let a=ju.pattern.getBaseDirectory(s);return a in r?r[a].push(s):r[a]=[s],r},e)}Ko.groupPatternsByBaseDirectory=Q3;function T3(t,e,r){return Object.keys(t).map(s=>R3(s,t[s],e,r))}Ko.convertPatternGroupsToTasks=T3;function R3(t,e,r,s){return{dynamic:s,positive:e,negative:r,base:t,patterns:[].concat(e,r.map(ju.pattern.convertToNegativePattern))}}Ko.convertPatternGroupToTask=R3});var ape=L(sQ=>{"use strict";Object.defineProperty(sQ,"__esModule",{value:!0});sQ.read=void 0;function rAt(t,e,r){e.fs.lstat(t,(s,a)=>{if(s!==null){ope(r,s);return}if(!a.isSymbolicLink()||!e.followSymbolicLink){F3(r,a);return}e.fs.stat(t,(n,c)=>{if(n!==null){if(e.throwErrorOnBrokenSymbolicLink){ope(r,n);return}F3(r,a);return}e.markSymbolicLink&&(c.isSymbolicLink=()=>!0),F3(r,c)})})}sQ.read=rAt;function ope(t,e){t(e)}function F3(t,e){t(null,e)}});var lpe=L(oQ=>{"use strict";Object.defineProperty(oQ,"__esModule",{value:!0});oQ.read=void 0;function nAt(t,e){let r=e.fs.lstatSync(t);if(!r.isSymbolicLink()||!e.followSymbolicLink)return r;try{let s=e.fs.statSync(t);return e.markSymbolicLink&&(s.isSymbolicLink=()=>!0),s}catch(s){if(!e.throwErrorOnBrokenSymbolicLink)return r;throw s}}oQ.read=nAt});var cpe=L(h0=>{"use strict";Object.defineProperty(h0,"__esModule",{value:!0});h0.createFileSystemAdapter=h0.FILE_SYSTEM_ADAPTER=void 0;var aQ=ye("fs");h0.FILE_SYSTEM_ADAPTER={lstat:aQ.lstat,stat:aQ.stat,lstatSync:aQ.lstatSync,statSync:aQ.statSync};function iAt(t){return t===void 0?h0.FILE_SYSTEM_ADAPTER:Object.assign(Object.assign({},h0.FILE_SYSTEM_ADAPTER),t)}h0.createFileSystemAdapter=iAt});var upe=L(O3=>{"use strict";Object.defineProperty(O3,"__esModule",{value:!0});var sAt=cpe(),N3=class{constructor(e={}){this._options=e,this.followSymbolicLink=this._getValue(this._options.followSymbolicLink,!0),this.fs=sAt.createFileSystemAdapter(this._options.fs),this.markSymbolicLink=this._getValue(this._options.markSymbolicLink,!1),this.throwErrorOnBrokenSymbolicLink=this._getValue(this._options.throwErrorOnBrokenSymbolicLink,!0)}_getValue(e,r){return e??r}};O3.default=N3});var Xd=L(g0=>{"use strict";Object.defineProperty(g0,"__esModule",{value:!0});g0.statSync=g0.stat=g0.Settings=void 0;var fpe=ape(),oAt=lpe(),L3=upe();g0.Settings=L3.default;function aAt(t,e,r){if(typeof e=="function"){fpe.read(t,M3(),e);return}fpe.read(t,M3(e),r)}g0.stat=aAt;function lAt(t,e){let r=M3(e);return oAt.read(t,r)}g0.statSync=lAt;function M3(t={}){return t instanceof L3.default?t:new L3.default(t)}});var hpe=L((VKt,ppe)=>{var Ape;ppe.exports=typeof queueMicrotask=="function"?queueMicrotask.bind(typeof window<"u"?window:global):t=>(Ape||(Ape=Promise.resolve())).then(t).catch(e=>setTimeout(()=>{throw e},0))});var dpe=L((KKt,gpe)=>{gpe.exports=uAt;var cAt=hpe();function uAt(t,e){let r,s,a,n=!0;Array.isArray(t)?(r=[],s=t.length):(a=Object.keys(t),r={},s=a.length);function c(p){function h(){e&&e(p,r),e=null}n?cAt(h):h()}function f(p,h,E){r[p]=E,(--s===0||h)&&c(h)}s?a?a.forEach(function(p){t[p](function(h,E){f(p,h,E)})}):t.forEach(function(p,h){p(function(E,C){f(h,E,C)})}):c(null),n=!1}});var _3=L(cQ=>{"use strict";Object.defineProperty(cQ,"__esModule",{value:!0});cQ.IS_SUPPORT_READDIR_WITH_FILE_TYPES=void 0;var lQ=process.versions.node.split(".");if(lQ[0]===void 0||lQ[1]===void 0)throw new Error(`Unexpected behavior. The 'process.versions.node' variable has invalid value: ${process.versions.node}`);var mpe=Number.parseInt(lQ[0],10),fAt=Number.parseInt(lQ[1],10),ype=10,AAt=10,pAt=mpe>ype,hAt=mpe===ype&&fAt>=AAt;cQ.IS_SUPPORT_READDIR_WITH_FILE_TYPES=pAt||hAt});var Epe=L(uQ=>{"use strict";Object.defineProperty(uQ,"__esModule",{value:!0});uQ.createDirentFromStats=void 0;var U3=class{constructor(e,r){this.name=e,this.isBlockDevice=r.isBlockDevice.bind(r),this.isCharacterDevice=r.isCharacterDevice.bind(r),this.isDirectory=r.isDirectory.bind(r),this.isFIFO=r.isFIFO.bind(r),this.isFile=r.isFile.bind(r),this.isSocket=r.isSocket.bind(r),this.isSymbolicLink=r.isSymbolicLink.bind(r)}};function gAt(t,e){return new U3(t,e)}uQ.createDirentFromStats=gAt});var H3=L(fQ=>{"use strict";Object.defineProperty(fQ,"__esModule",{value:!0});fQ.fs=void 0;var dAt=Epe();fQ.fs=dAt});var j3=L(AQ=>{"use strict";Object.defineProperty(AQ,"__esModule",{value:!0});AQ.joinPathSegments=void 0;function mAt(t,e,r){return t.endsWith(r)?t+e:t+r+e}AQ.joinPathSegments=mAt});var Spe=L(d0=>{"use strict";Object.defineProperty(d0,"__esModule",{value:!0});d0.readdir=d0.readdirWithFileTypes=d0.read=void 0;var yAt=Xd(),Ipe=dpe(),EAt=_3(),Cpe=H3(),wpe=j3();function IAt(t,e,r){if(!e.stats&&EAt.IS_SUPPORT_READDIR_WITH_FILE_TYPES){Bpe(t,e,r);return}vpe(t,e,r)}d0.read=IAt;function Bpe(t,e,r){e.fs.readdir(t,{withFileTypes:!0},(s,a)=>{if(s!==null){pQ(r,s);return}let n=a.map(f=>({dirent:f,name:f.name,path:wpe.joinPathSegments(t,f.name,e.pathSegmentSeparator)}));if(!e.followSymbolicLinks){q3(r,n);return}let c=n.map(f=>CAt(f,e));Ipe(c,(f,p)=>{if(f!==null){pQ(r,f);return}q3(r,p)})})}d0.readdirWithFileTypes=Bpe;function CAt(t,e){return r=>{if(!t.dirent.isSymbolicLink()){r(null,t);return}e.fs.stat(t.path,(s,a)=>{if(s!==null){if(e.throwErrorOnBrokenSymbolicLink){r(s);return}r(null,t);return}t.dirent=Cpe.fs.createDirentFromStats(t.name,a),r(null,t)})}}function vpe(t,e,r){e.fs.readdir(t,(s,a)=>{if(s!==null){pQ(r,s);return}let n=a.map(c=>{let f=wpe.joinPathSegments(t,c,e.pathSegmentSeparator);return p=>{yAt.stat(f,e.fsStatSettings,(h,E)=>{if(h!==null){p(h);return}let C={name:c,path:f,dirent:Cpe.fs.createDirentFromStats(c,E)};e.stats&&(C.stats=E),p(null,C)})}});Ipe(n,(c,f)=>{if(c!==null){pQ(r,c);return}q3(r,f)})})}d0.readdir=vpe;function pQ(t,e){t(e)}function q3(t,e){t(null,e)}});var kpe=L(m0=>{"use strict";Object.defineProperty(m0,"__esModule",{value:!0});m0.readdir=m0.readdirWithFileTypes=m0.read=void 0;var wAt=Xd(),BAt=_3(),Dpe=H3(),bpe=j3();function vAt(t,e){return!e.stats&&BAt.IS_SUPPORT_READDIR_WITH_FILE_TYPES?Ppe(t,e):xpe(t,e)}m0.read=vAt;function Ppe(t,e){return e.fs.readdirSync(t,{withFileTypes:!0}).map(s=>{let a={dirent:s,name:s.name,path:bpe.joinPathSegments(t,s.name,e.pathSegmentSeparator)};if(a.dirent.isSymbolicLink()&&e.followSymbolicLinks)try{let n=e.fs.statSync(a.path);a.dirent=Dpe.fs.createDirentFromStats(a.name,n)}catch(n){if(e.throwErrorOnBrokenSymbolicLink)throw n}return a})}m0.readdirWithFileTypes=Ppe;function xpe(t,e){return e.fs.readdirSync(t).map(s=>{let a=bpe.joinPathSegments(t,s,e.pathSegmentSeparator),n=wAt.statSync(a,e.fsStatSettings),c={name:s,path:a,dirent:Dpe.fs.createDirentFromStats(s,n)};return e.stats&&(c.stats=n),c})}m0.readdir=xpe});var Qpe=L(y0=>{"use strict";Object.defineProperty(y0,"__esModule",{value:!0});y0.createFileSystemAdapter=y0.FILE_SYSTEM_ADAPTER=void 0;var XE=ye("fs");y0.FILE_SYSTEM_ADAPTER={lstat:XE.lstat,stat:XE.stat,lstatSync:XE.lstatSync,statSync:XE.statSync,readdir:XE.readdir,readdirSync:XE.readdirSync};function SAt(t){return t===void 0?y0.FILE_SYSTEM_ADAPTER:Object.assign(Object.assign({},y0.FILE_SYSTEM_ADAPTER),t)}y0.createFileSystemAdapter=SAt});var Tpe=L(W3=>{"use strict";Object.defineProperty(W3,"__esModule",{value:!0});var DAt=ye("path"),bAt=Xd(),PAt=Qpe(),G3=class{constructor(e={}){this._options=e,this.followSymbolicLinks=this._getValue(this._options.followSymbolicLinks,!1),this.fs=PAt.createFileSystemAdapter(this._options.fs),this.pathSegmentSeparator=this._getValue(this._options.pathSegmentSeparator,DAt.sep),this.stats=this._getValue(this._options.stats,!1),this.throwErrorOnBrokenSymbolicLink=this._getValue(this._options.throwErrorOnBrokenSymbolicLink,!0),this.fsStatSettings=new bAt.Settings({followSymbolicLink:this.followSymbolicLinks,fs:this.fs,throwErrorOnBrokenSymbolicLink:this.throwErrorOnBrokenSymbolicLink})}_getValue(e,r){return e??r}};W3.default=G3});var hQ=L(E0=>{"use strict";Object.defineProperty(E0,"__esModule",{value:!0});E0.Settings=E0.scandirSync=E0.scandir=void 0;var Rpe=Spe(),xAt=kpe(),Y3=Tpe();E0.Settings=Y3.default;function kAt(t,e,r){if(typeof e=="function"){Rpe.read(t,V3(),e);return}Rpe.read(t,V3(e),r)}E0.scandir=kAt;function QAt(t,e){let r=V3(e);return xAt.read(t,r)}E0.scandirSync=QAt;function V3(t={}){return t instanceof Y3.default?t:new Y3.default(t)}});var Npe=L((iJt,Fpe)=>{"use strict";function TAt(t){var e=new t,r=e;function s(){var n=e;return n.next?e=n.next:(e=new t,r=e),n.next=null,n}function a(n){r.next=n,r=n}return{get:s,release:a}}Fpe.exports=TAt});var Lpe=L((sJt,K3)=>{"use strict";var RAt=Npe();function Ope(t,e,r){if(typeof t=="function"&&(r=e,e=t,t=null),!(r>=1))throw new Error("fastqueue concurrency must be equal to or greater than 1");var s=RAt(FAt),a=null,n=null,c=0,f=null,p={push:R,drain:Tc,saturated:Tc,pause:E,paused:!1,get concurrency(){return r},set concurrency(Ae){if(!(Ae>=1))throw new Error("fastqueue concurrency must be equal to or greater than 1");if(r=Ae,!p.paused)for(;a&&c=r||p.paused?n?(n.next=me,n=me):(a=me,n=me,p.saturated()):(c++,e.call(t,me.value,me.worked))}function N(Ae,ce){var me=s.get();me.context=t,me.release=U,me.value=Ae,me.callback=ce||Tc,me.errorHandler=f,c>=r||p.paused?a?(me.next=a,a=me):(a=me,n=me,p.saturated()):(c++,e.call(t,me.value,me.worked))}function U(Ae){Ae&&s.release(Ae);var ce=a;ce&&c<=r?p.paused?c--:(n===a&&(n=null),a=ce.next,ce.next=null,e.call(t,ce.value,ce.worked),n===null&&p.empty()):--c===0&&p.drain()}function W(){a=null,n=null,p.drain=Tc}function te(){a=null,n=null,p.drain(),p.drain=Tc}function ie(Ae){f=Ae}}function Tc(){}function FAt(){this.value=null,this.callback=Tc,this.next=null,this.release=Tc,this.context=null,this.errorHandler=null;var t=this;this.worked=function(r,s){var a=t.callback,n=t.errorHandler,c=t.value;t.value=null,t.callback=Tc,t.errorHandler&&n(r,c),a.call(t.context,r,s),t.release(t)}}function NAt(t,e,r){typeof t=="function"&&(r=e,e=t,t=null);function s(E,C){e.call(this,E).then(function(S){C(null,S)},C)}var a=Ope(t,s,r),n=a.push,c=a.unshift;return a.push=f,a.unshift=p,a.drained=h,a;function f(E){var C=new Promise(function(S,P){n(E,function(I,R){if(I){P(I);return}S(R)})});return C.catch(Tc),C}function p(E){var C=new Promise(function(S,P){c(E,function(I,R){if(I){P(I);return}S(R)})});return C.catch(Tc),C}function h(){if(a.idle())return new Promise(function(S){S()});var E=a.drain,C=new Promise(function(S){a.drain=function(){E(),S()}});return C}}K3.exports=Ope;K3.exports.promise=NAt});var gQ=L(Xf=>{"use strict";Object.defineProperty(Xf,"__esModule",{value:!0});Xf.joinPathSegments=Xf.replacePathSegmentSeparator=Xf.isAppliedFilter=Xf.isFatalError=void 0;function OAt(t,e){return t.errorFilter===null?!0:!t.errorFilter(e)}Xf.isFatalError=OAt;function LAt(t,e){return t===null||t(e)}Xf.isAppliedFilter=LAt;function MAt(t,e){return t.split(/[/\\]/).join(e)}Xf.replacePathSegmentSeparator=MAt;function _At(t,e,r){return t===""?e:t.endsWith(r)?t+e:t+r+e}Xf.joinPathSegments=_At});var Z3=L(z3=>{"use strict";Object.defineProperty(z3,"__esModule",{value:!0});var UAt=gQ(),J3=class{constructor(e,r){this._root=e,this._settings=r,this._root=UAt.replacePathSegmentSeparator(e,r.pathSegmentSeparator)}};z3.default=J3});var e8=L($3=>{"use strict";Object.defineProperty($3,"__esModule",{value:!0});var HAt=ye("events"),jAt=hQ(),qAt=Lpe(),dQ=gQ(),GAt=Z3(),X3=class extends GAt.default{constructor(e,r){super(e,r),this._settings=r,this._scandir=jAt.scandir,this._emitter=new HAt.EventEmitter,this._queue=qAt(this._worker.bind(this),this._settings.concurrency),this._isFatalError=!1,this._isDestroyed=!1,this._queue.drain=()=>{this._isFatalError||this._emitter.emit("end")}}read(){return this._isFatalError=!1,this._isDestroyed=!1,setImmediate(()=>{this._pushToQueue(this._root,this._settings.basePath)}),this._emitter}get isDestroyed(){return this._isDestroyed}destroy(){if(this._isDestroyed)throw new Error("The reader is already destroyed");this._isDestroyed=!0,this._queue.killAndDrain()}onEntry(e){this._emitter.on("entry",e)}onError(e){this._emitter.once("error",e)}onEnd(e){this._emitter.once("end",e)}_pushToQueue(e,r){let s={directory:e,base:r};this._queue.push(s,a=>{a!==null&&this._handleError(a)})}_worker(e,r){this._scandir(e.directory,this._settings.fsScandirSettings,(s,a)=>{if(s!==null){r(s,void 0);return}for(let n of a)this._handleEntry(n,e.base);r(null,void 0)})}_handleError(e){this._isDestroyed||!dQ.isFatalError(this._settings,e)||(this._isFatalError=!0,this._isDestroyed=!0,this._emitter.emit("error",e))}_handleEntry(e,r){if(this._isDestroyed||this._isFatalError)return;let s=e.path;r!==void 0&&(e.path=dQ.joinPathSegments(r,e.name,this._settings.pathSegmentSeparator)),dQ.isAppliedFilter(this._settings.entryFilter,e)&&this._emitEntry(e),e.dirent.isDirectory()&&dQ.isAppliedFilter(this._settings.deepFilter,e)&&this._pushToQueue(s,r===void 0?void 0:e.path)}_emitEntry(e){this._emitter.emit("entry",e)}};$3.default=X3});var Mpe=L(r8=>{"use strict";Object.defineProperty(r8,"__esModule",{value:!0});var WAt=e8(),t8=class{constructor(e,r){this._root=e,this._settings=r,this._reader=new WAt.default(this._root,this._settings),this._storage=[]}read(e){this._reader.onError(r=>{YAt(e,r)}),this._reader.onEntry(r=>{this._storage.push(r)}),this._reader.onEnd(()=>{VAt(e,this._storage)}),this._reader.read()}};r8.default=t8;function YAt(t,e){t(e)}function VAt(t,e){t(null,e)}});var _pe=L(i8=>{"use strict";Object.defineProperty(i8,"__esModule",{value:!0});var KAt=ye("stream"),JAt=e8(),n8=class{constructor(e,r){this._root=e,this._settings=r,this._reader=new JAt.default(this._root,this._settings),this._stream=new KAt.Readable({objectMode:!0,read:()=>{},destroy:()=>{this._reader.isDestroyed||this._reader.destroy()}})}read(){return this._reader.onError(e=>{this._stream.emit("error",e)}),this._reader.onEntry(e=>{this._stream.push(e)}),this._reader.onEnd(()=>{this._stream.push(null)}),this._reader.read(),this._stream}};i8.default=n8});var Upe=L(o8=>{"use strict";Object.defineProperty(o8,"__esModule",{value:!0});var zAt=hQ(),mQ=gQ(),ZAt=Z3(),s8=class extends ZAt.default{constructor(){super(...arguments),this._scandir=zAt.scandirSync,this._storage=[],this._queue=new Set}read(){return this._pushToQueue(this._root,this._settings.basePath),this._handleQueue(),this._storage}_pushToQueue(e,r){this._queue.add({directory:e,base:r})}_handleQueue(){for(let e of this._queue.values())this._handleDirectory(e.directory,e.base)}_handleDirectory(e,r){try{let s=this._scandir(e,this._settings.fsScandirSettings);for(let a of s)this._handleEntry(a,r)}catch(s){this._handleError(s)}}_handleError(e){if(mQ.isFatalError(this._settings,e))throw e}_handleEntry(e,r){let s=e.path;r!==void 0&&(e.path=mQ.joinPathSegments(r,e.name,this._settings.pathSegmentSeparator)),mQ.isAppliedFilter(this._settings.entryFilter,e)&&this._pushToStorage(e),e.dirent.isDirectory()&&mQ.isAppliedFilter(this._settings.deepFilter,e)&&this._pushToQueue(s,r===void 0?void 0:e.path)}_pushToStorage(e){this._storage.push(e)}};o8.default=s8});var Hpe=L(l8=>{"use strict";Object.defineProperty(l8,"__esModule",{value:!0});var XAt=Upe(),a8=class{constructor(e,r){this._root=e,this._settings=r,this._reader=new XAt.default(this._root,this._settings)}read(){return this._reader.read()}};l8.default=a8});var jpe=L(u8=>{"use strict";Object.defineProperty(u8,"__esModule",{value:!0});var $At=ye("path"),ept=hQ(),c8=class{constructor(e={}){this._options=e,this.basePath=this._getValue(this._options.basePath,void 0),this.concurrency=this._getValue(this._options.concurrency,Number.POSITIVE_INFINITY),this.deepFilter=this._getValue(this._options.deepFilter,null),this.entryFilter=this._getValue(this._options.entryFilter,null),this.errorFilter=this._getValue(this._options.errorFilter,null),this.pathSegmentSeparator=this._getValue(this._options.pathSegmentSeparator,$At.sep),this.fsScandirSettings=new ept.Settings({followSymbolicLinks:this._options.followSymbolicLinks,fs:this._options.fs,pathSegmentSeparator:this._options.pathSegmentSeparator,stats:this._options.stats,throwErrorOnBrokenSymbolicLink:this._options.throwErrorOnBrokenSymbolicLink})}_getValue(e,r){return e??r}};u8.default=c8});var EQ=L($f=>{"use strict";Object.defineProperty($f,"__esModule",{value:!0});$f.Settings=$f.walkStream=$f.walkSync=$f.walk=void 0;var qpe=Mpe(),tpt=_pe(),rpt=Hpe(),f8=jpe();$f.Settings=f8.default;function npt(t,e,r){if(typeof e=="function"){new qpe.default(t,yQ()).read(e);return}new qpe.default(t,yQ(e)).read(r)}$f.walk=npt;function ipt(t,e){let r=yQ(e);return new rpt.default(t,r).read()}$f.walkSync=ipt;function spt(t,e){let r=yQ(e);return new tpt.default(t,r).read()}$f.walkStream=spt;function yQ(t={}){return t instanceof f8.default?t:new f8.default(t)}});var IQ=L(p8=>{"use strict";Object.defineProperty(p8,"__esModule",{value:!0});var opt=ye("path"),apt=Xd(),Gpe=Qp(),A8=class{constructor(e){this._settings=e,this._fsStatSettings=new apt.Settings({followSymbolicLink:this._settings.followSymbolicLinks,fs:this._settings.fs,throwErrorOnBrokenSymbolicLink:this._settings.followSymbolicLinks})}_getFullEntryPath(e){return opt.resolve(this._settings.cwd,e)}_makeEntry(e,r){let s={name:r,path:r,dirent:Gpe.fs.createDirentFromStats(r,e)};return this._settings.stats&&(s.stats=e),s}_isFatalError(e){return!Gpe.errno.isEnoentCodeError(e)&&!this._settings.suppressErrors}};p8.default=A8});var d8=L(g8=>{"use strict";Object.defineProperty(g8,"__esModule",{value:!0});var lpt=ye("stream"),cpt=Xd(),upt=EQ(),fpt=IQ(),h8=class extends fpt.default{constructor(){super(...arguments),this._walkStream=upt.walkStream,this._stat=cpt.stat}dynamic(e,r){return this._walkStream(e,r)}static(e,r){let s=e.map(this._getFullEntryPath,this),a=new lpt.PassThrough({objectMode:!0});a._write=(n,c,f)=>this._getEntry(s[n],e[n],r).then(p=>{p!==null&&r.entryFilter(p)&&a.push(p),n===s.length-1&&a.end(),f()}).catch(f);for(let n=0;nthis._makeEntry(a,r)).catch(a=>{if(s.errorFilter(a))return null;throw a})}_getStat(e){return new Promise((r,s)=>{this._stat(e,this._fsStatSettings,(a,n)=>a===null?r(n):s(a))})}};g8.default=h8});var Wpe=L(y8=>{"use strict";Object.defineProperty(y8,"__esModule",{value:!0});var Apt=EQ(),ppt=IQ(),hpt=d8(),m8=class extends ppt.default{constructor(){super(...arguments),this._walkAsync=Apt.walk,this._readerStream=new hpt.default(this._settings)}dynamic(e,r){return new Promise((s,a)=>{this._walkAsync(e,r,(n,c)=>{n===null?s(c):a(n)})})}async static(e,r){let s=[],a=this._readerStream.static(e,r);return new Promise((n,c)=>{a.once("error",c),a.on("data",f=>s.push(f)),a.once("end",()=>n(s))})}};y8.default=m8});var Ype=L(I8=>{"use strict";Object.defineProperty(I8,"__esModule",{value:!0});var KB=Qp(),E8=class{constructor(e,r,s){this._patterns=e,this._settings=r,this._micromatchOptions=s,this._storage=[],this._fillStorage()}_fillStorage(){for(let e of this._patterns){let r=this._getPatternSegments(e),s=this._splitSegmentsIntoSections(r);this._storage.push({complete:s.length<=1,pattern:e,segments:r,sections:s})}}_getPatternSegments(e){return KB.pattern.getPatternParts(e,this._micromatchOptions).map(s=>KB.pattern.isDynamicPattern(s,this._settings)?{dynamic:!0,pattern:s,patternRe:KB.pattern.makeRe(s,this._micromatchOptions)}:{dynamic:!1,pattern:s})}_splitSegmentsIntoSections(e){return KB.array.splitWhen(e,r=>r.dynamic&&KB.pattern.hasGlobStar(r.pattern))}};I8.default=E8});var Vpe=L(w8=>{"use strict";Object.defineProperty(w8,"__esModule",{value:!0});var gpt=Ype(),C8=class extends gpt.default{match(e){let r=e.split("/"),s=r.length,a=this._storage.filter(n=>!n.complete||n.segments.length>s);for(let n of a){let c=n.sections[0];if(!n.complete&&s>c.length||r.every((p,h)=>{let E=n.segments[h];return!!(E.dynamic&&E.patternRe.test(p)||!E.dynamic&&E.pattern===p)}))return!0}return!1}};w8.default=C8});var Kpe=L(v8=>{"use strict";Object.defineProperty(v8,"__esModule",{value:!0});var CQ=Qp(),dpt=Vpe(),B8=class{constructor(e,r){this._settings=e,this._micromatchOptions=r}getFilter(e,r,s){let a=this._getMatcher(r),n=this._getNegativePatternsRe(s);return c=>this._filter(e,c,a,n)}_getMatcher(e){return new dpt.default(e,this._settings,this._micromatchOptions)}_getNegativePatternsRe(e){let r=e.filter(CQ.pattern.isAffectDepthOfReadingPattern);return CQ.pattern.convertPatternsToRe(r,this._micromatchOptions)}_filter(e,r,s,a){if(this._isSkippedByDeep(e,r.path)||this._isSkippedSymbolicLink(r))return!1;let n=CQ.path.removeLeadingDotSegment(r.path);return this._isSkippedByPositivePatterns(n,s)?!1:this._isSkippedByNegativePatterns(n,a)}_isSkippedByDeep(e,r){return this._settings.deep===1/0?!1:this._getEntryLevel(e,r)>=this._settings.deep}_getEntryLevel(e,r){let s=r.split("/").length;if(e==="")return s;let a=e.split("/").length;return s-a}_isSkippedSymbolicLink(e){return!this._settings.followSymbolicLinks&&e.dirent.isSymbolicLink()}_isSkippedByPositivePatterns(e,r){return!this._settings.baseNameMatch&&!r.match(e)}_isSkippedByNegativePatterns(e,r){return!CQ.pattern.matchAny(e,r)}};v8.default=B8});var Jpe=L(D8=>{"use strict";Object.defineProperty(D8,"__esModule",{value:!0});var $d=Qp(),S8=class{constructor(e,r){this._settings=e,this._micromatchOptions=r,this.index=new Map}getFilter(e,r){let s=$d.pattern.convertPatternsToRe(e,this._micromatchOptions),a=$d.pattern.convertPatternsToRe(r,Object.assign(Object.assign({},this._micromatchOptions),{dot:!0}));return n=>this._filter(n,s,a)}_filter(e,r,s){let a=$d.path.removeLeadingDotSegment(e.path);if(this._settings.unique&&this._isDuplicateEntry(a)||this._onlyFileFilter(e)||this._onlyDirectoryFilter(e)||this._isSkippedByAbsoluteNegativePatterns(a,s))return!1;let n=e.dirent.isDirectory(),c=this._isMatchToPatterns(a,r,n)&&!this._isMatchToPatterns(a,s,n);return this._settings.unique&&c&&this._createIndexRecord(a),c}_isDuplicateEntry(e){return this.index.has(e)}_createIndexRecord(e){this.index.set(e,void 0)}_onlyFileFilter(e){return this._settings.onlyFiles&&!e.dirent.isFile()}_onlyDirectoryFilter(e){return this._settings.onlyDirectories&&!e.dirent.isDirectory()}_isSkippedByAbsoluteNegativePatterns(e,r){if(!this._settings.absolute)return!1;let s=$d.path.makeAbsolute(this._settings.cwd,e);return $d.pattern.matchAny(s,r)}_isMatchToPatterns(e,r,s){let a=$d.pattern.matchAny(e,r);return!a&&s?$d.pattern.matchAny(e+"/",r):a}};D8.default=S8});var zpe=L(P8=>{"use strict";Object.defineProperty(P8,"__esModule",{value:!0});var mpt=Qp(),b8=class{constructor(e){this._settings=e}getFilter(){return e=>this._isNonFatalError(e)}_isNonFatalError(e){return mpt.errno.isEnoentCodeError(e)||this._settings.suppressErrors}};P8.default=b8});var Xpe=L(k8=>{"use strict";Object.defineProperty(k8,"__esModule",{value:!0});var Zpe=Qp(),x8=class{constructor(e){this._settings=e}getTransformer(){return e=>this._transform(e)}_transform(e){let r=e.path;return this._settings.absolute&&(r=Zpe.path.makeAbsolute(this._settings.cwd,r),r=Zpe.path.unixify(r)),this._settings.markDirectories&&e.dirent.isDirectory()&&(r+="/"),this._settings.objectMode?Object.assign(Object.assign({},e),{path:r}):r}};k8.default=x8});var wQ=L(T8=>{"use strict";Object.defineProperty(T8,"__esModule",{value:!0});var ypt=ye("path"),Ept=Kpe(),Ipt=Jpe(),Cpt=zpe(),wpt=Xpe(),Q8=class{constructor(e){this._settings=e,this.errorFilter=new Cpt.default(this._settings),this.entryFilter=new Ipt.default(this._settings,this._getMicromatchOptions()),this.deepFilter=new Ept.default(this._settings,this._getMicromatchOptions()),this.entryTransformer=new wpt.default(this._settings)}_getRootDirectory(e){return ypt.resolve(this._settings.cwd,e.base)}_getReaderOptions(e){let r=e.base==="."?"":e.base;return{basePath:r,pathSegmentSeparator:"/",concurrency:this._settings.concurrency,deepFilter:this.deepFilter.getFilter(r,e.positive,e.negative),entryFilter:this.entryFilter.getFilter(e.positive,e.negative),errorFilter:this.errorFilter.getFilter(),followSymbolicLinks:this._settings.followSymbolicLinks,fs:this._settings.fs,stats:this._settings.stats,throwErrorOnBrokenSymbolicLink:this._settings.throwErrorOnBrokenSymbolicLink,transform:this.entryTransformer.getTransformer()}}_getMicromatchOptions(){return{dot:this._settings.dot,matchBase:this._settings.baseNameMatch,nobrace:!this._settings.braceExpansion,nocase:!this._settings.caseSensitiveMatch,noext:!this._settings.extglob,noglobstar:!this._settings.globstar,posix:!0,strictSlashes:!1}}};T8.default=Q8});var $pe=L(F8=>{"use strict";Object.defineProperty(F8,"__esModule",{value:!0});var Bpt=Wpe(),vpt=wQ(),R8=class extends vpt.default{constructor(){super(...arguments),this._reader=new Bpt.default(this._settings)}async read(e){let r=this._getRootDirectory(e),s=this._getReaderOptions(e);return(await this.api(r,e,s)).map(n=>s.transform(n))}api(e,r,s){return r.dynamic?this._reader.dynamic(e,s):this._reader.static(r.patterns,s)}};F8.default=R8});var ehe=L(O8=>{"use strict";Object.defineProperty(O8,"__esModule",{value:!0});var Spt=ye("stream"),Dpt=d8(),bpt=wQ(),N8=class extends bpt.default{constructor(){super(...arguments),this._reader=new Dpt.default(this._settings)}read(e){let r=this._getRootDirectory(e),s=this._getReaderOptions(e),a=this.api(r,e,s),n=new Spt.Readable({objectMode:!0,read:()=>{}});return a.once("error",c=>n.emit("error",c)).on("data",c=>n.emit("data",s.transform(c))).once("end",()=>n.emit("end")),n.once("close",()=>a.destroy()),n}api(e,r,s){return r.dynamic?this._reader.dynamic(e,s):this._reader.static(r.patterns,s)}};O8.default=N8});var the=L(M8=>{"use strict";Object.defineProperty(M8,"__esModule",{value:!0});var Ppt=Xd(),xpt=EQ(),kpt=IQ(),L8=class extends kpt.default{constructor(){super(...arguments),this._walkSync=xpt.walkSync,this._statSync=Ppt.statSync}dynamic(e,r){return this._walkSync(e,r)}static(e,r){let s=[];for(let a of e){let n=this._getFullEntryPath(a),c=this._getEntry(n,a,r);c===null||!r.entryFilter(c)||s.push(c)}return s}_getEntry(e,r,s){try{let a=this._getStat(e);return this._makeEntry(a,r)}catch(a){if(s.errorFilter(a))return null;throw a}}_getStat(e){return this._statSync(e,this._fsStatSettings)}};M8.default=L8});var rhe=L(U8=>{"use strict";Object.defineProperty(U8,"__esModule",{value:!0});var Qpt=the(),Tpt=wQ(),_8=class extends Tpt.default{constructor(){super(...arguments),this._reader=new Qpt.default(this._settings)}read(e){let r=this._getRootDirectory(e),s=this._getReaderOptions(e);return this.api(r,e,s).map(s.transform)}api(e,r,s){return r.dynamic?this._reader.dynamic(e,s):this._reader.static(r.patterns,s)}};U8.default=_8});var nhe=L(eI=>{"use strict";Object.defineProperty(eI,"__esModule",{value:!0});eI.DEFAULT_FILE_SYSTEM_ADAPTER=void 0;var $E=ye("fs"),Rpt=ye("os"),Fpt=Math.max(Rpt.cpus().length,1);eI.DEFAULT_FILE_SYSTEM_ADAPTER={lstat:$E.lstat,lstatSync:$E.lstatSync,stat:$E.stat,statSync:$E.statSync,readdir:$E.readdir,readdirSync:$E.readdirSync};var H8=class{constructor(e={}){this._options=e,this.absolute=this._getValue(this._options.absolute,!1),this.baseNameMatch=this._getValue(this._options.baseNameMatch,!1),this.braceExpansion=this._getValue(this._options.braceExpansion,!0),this.caseSensitiveMatch=this._getValue(this._options.caseSensitiveMatch,!0),this.concurrency=this._getValue(this._options.concurrency,Fpt),this.cwd=this._getValue(this._options.cwd,process.cwd()),this.deep=this._getValue(this._options.deep,1/0),this.dot=this._getValue(this._options.dot,!1),this.extglob=this._getValue(this._options.extglob,!0),this.followSymbolicLinks=this._getValue(this._options.followSymbolicLinks,!0),this.fs=this._getFileSystemMethods(this._options.fs),this.globstar=this._getValue(this._options.globstar,!0),this.ignore=this._getValue(this._options.ignore,[]),this.markDirectories=this._getValue(this._options.markDirectories,!1),this.objectMode=this._getValue(this._options.objectMode,!1),this.onlyDirectories=this._getValue(this._options.onlyDirectories,!1),this.onlyFiles=this._getValue(this._options.onlyFiles,!0),this.stats=this._getValue(this._options.stats,!1),this.suppressErrors=this._getValue(this._options.suppressErrors,!1),this.throwErrorOnBrokenSymbolicLink=this._getValue(this._options.throwErrorOnBrokenSymbolicLink,!1),this.unique=this._getValue(this._options.unique,!0),this.onlyDirectories&&(this.onlyFiles=!1),this.stats&&(this.objectMode=!0),this.ignore=[].concat(this.ignore)}_getValue(e,r){return e===void 0?r:e}_getFileSystemMethods(e={}){return Object.assign(Object.assign({},eI.DEFAULT_FILE_SYSTEM_ADAPTER),e)}};eI.default=H8});var BQ=L((kJt,she)=>{"use strict";var ihe=spe(),Npt=$pe(),Opt=ehe(),Lpt=rhe(),j8=nhe(),Rc=Qp();async function q8(t,e){qu(t);let r=G8(t,Npt.default,e),s=await Promise.all(r);return Rc.array.flatten(s)}(function(t){t.glob=t,t.globSync=e,t.globStream=r,t.async=t;function e(h,E){qu(h);let C=G8(h,Lpt.default,E);return Rc.array.flatten(C)}t.sync=e;function r(h,E){qu(h);let C=G8(h,Opt.default,E);return Rc.stream.merge(C)}t.stream=r;function s(h,E){qu(h);let C=[].concat(h),S=new j8.default(E);return ihe.generate(C,S)}t.generateTasks=s;function a(h,E){qu(h);let C=new j8.default(E);return Rc.pattern.isDynamicPattern(h,C)}t.isDynamicPattern=a;function n(h){return qu(h),Rc.path.escape(h)}t.escapePath=n;function c(h){return qu(h),Rc.path.convertPathToPattern(h)}t.convertPathToPattern=c;let f;(function(h){function E(S){return qu(S),Rc.path.escapePosixPath(S)}h.escapePath=E;function C(S){return qu(S),Rc.path.convertPosixPathToPattern(S)}h.convertPathToPattern=C})(f=t.posix||(t.posix={}));let p;(function(h){function E(S){return qu(S),Rc.path.escapeWindowsPath(S)}h.escapePath=E;function C(S){return qu(S),Rc.path.convertWindowsPathToPattern(S)}h.convertPathToPattern=C})(p=t.win32||(t.win32={}))})(q8||(q8={}));function G8(t,e,r){let s=[].concat(t),a=new j8.default(r),n=ihe.generate(s,a),c=new e(a);return n.map(c.read,c)}function qu(t){if(![].concat(t).every(s=>Rc.string.isString(s)&&!Rc.string.isEmpty(s)))throw new TypeError("Patterns must be a string (non empty) or an array of strings")}she.exports=q8});var Nn={};Vt(Nn,{checksumFile:()=>SQ,checksumPattern:()=>DQ,makeHash:()=>us});function us(...t){let e=(0,vQ.createHash)("sha512"),r="";for(let s of t)typeof s=="string"?r+=s:s&&(r&&(e.update(r),r=""),e.update(s));return r&&e.update(r),e.digest("hex")}async function SQ(t,{baseFs:e,algorithm:r}={baseFs:le,algorithm:"sha512"}){let s=await e.openPromise(t,"r");try{let n=Buffer.allocUnsafeSlow(65536),c=(0,vQ.createHash)(r),f=0;for(;(f=await e.readPromise(s,n,0,65536))!==0;)c.update(f===65536?n:n.slice(0,f));return c.digest("hex")}finally{await e.closePromise(s)}}async function DQ(t,{cwd:e}){let s=(await(0,W8.default)(t,{cwd:ue.fromPortablePath(e),onlyDirectories:!0})).map(f=>`${f}/**/*`),a=await(0,W8.default)([t,...s],{cwd:ue.fromPortablePath(e),onlyFiles:!1});a.sort();let n=await Promise.all(a.map(async f=>{let p=[Buffer.from(f)],h=K.join(e,ue.toPortablePath(f)),E=await le.lstatPromise(h);return E.isSymbolicLink()?p.push(Buffer.from(await le.readlinkPromise(h))):E.isFile()&&p.push(await le.readFilePromise(h)),p.join("\0")})),c=(0,vQ.createHash)("sha512");for(let f of n)c.update(f);return c.digest("hex")}var vQ,W8,I0=It(()=>{bt();vQ=ye("crypto"),W8=et(BQ())});var q={};Vt(q,{allPeerRequests:()=>nv,areDescriptorsEqual:()=>uhe,areIdentsEqual:()=>XB,areLocatorsEqual:()=>$B,areVirtualPackagesEquivalent:()=>Ypt,bindDescriptor:()=>Gpt,bindLocator:()=>Wpt,convertDescriptorToLocator:()=>bQ,convertLocatorToDescriptor:()=>V8,convertPackageToLocator:()=>Hpt,convertToIdent:()=>Upt,convertToManifestRange:()=>rht,copyPackage:()=>zB,devirtualizeDescriptor:()=>ZB,devirtualizeLocator:()=>rI,ensureDevirtualizedDescriptor:()=>jpt,ensureDevirtualizedLocator:()=>qpt,getIdentVendorPath:()=>Z8,isPackageCompatible:()=>TQ,isVirtualDescriptor:()=>Tp,isVirtualLocator:()=>Gu,makeDescriptor:()=>On,makeIdent:()=>ba,makeLocator:()=>Ys,makeRange:()=>kQ,parseDescriptor:()=>C0,parseFileStyleRange:()=>eht,parseIdent:()=>Da,parseLocator:()=>Rp,parseRange:()=>em,prettyDependent:()=>B3,prettyDescriptor:()=>ri,prettyIdent:()=>$i,prettyLocator:()=>Yr,prettyLocatorNoColors:()=>w3,prettyRange:()=>iI,prettyReference:()=>tv,prettyResolution:()=>VB,prettyWorkspace:()=>rv,renamePackage:()=>K8,slugifyIdent:()=>Y8,slugifyLocator:()=>nI,sortDescriptors:()=>sI,stringifyDescriptor:()=>ll,stringifyIdent:()=>cn,stringifyLocator:()=>cl,tryParseDescriptor:()=>ev,tryParseIdent:()=>fhe,tryParseLocator:()=>xQ,tryParseRange:()=>$pt,unwrapIdentFromScope:()=>iht,virtualizeDescriptor:()=>J8,virtualizePackage:()=>z8,wrapIdentIntoScope:()=>nht});function ba(t,e){if(t?.startsWith("@"))throw new Error("Invalid scope: don't prefix it with '@'");return{identHash:us(t,e),scope:t,name:e}}function On(t,e){return{identHash:t.identHash,scope:t.scope,name:t.name,descriptorHash:us(t.identHash,e),range:e}}function Ys(t,e){return{identHash:t.identHash,scope:t.scope,name:t.name,locatorHash:us(t.identHash,e),reference:e}}function Upt(t){return{identHash:t.identHash,scope:t.scope,name:t.name}}function bQ(t){return{identHash:t.identHash,scope:t.scope,name:t.name,locatorHash:t.descriptorHash,reference:t.range}}function V8(t){return{identHash:t.identHash,scope:t.scope,name:t.name,descriptorHash:t.locatorHash,range:t.reference}}function Hpt(t){return{identHash:t.identHash,scope:t.scope,name:t.name,locatorHash:t.locatorHash,reference:t.reference}}function K8(t,e){return{identHash:e.identHash,scope:e.scope,name:e.name,locatorHash:e.locatorHash,reference:e.reference,version:t.version,languageName:t.languageName,linkType:t.linkType,conditions:t.conditions,dependencies:new Map(t.dependencies),peerDependencies:new Map(t.peerDependencies),dependenciesMeta:new Map(t.dependenciesMeta),peerDependenciesMeta:new Map(t.peerDependenciesMeta),bin:new Map(t.bin)}}function zB(t){return K8(t,t)}function J8(t,e){if(e.includes("#"))throw new Error("Invalid entropy");return On(t,`virtual:${e}#${t.range}`)}function z8(t,e){if(e.includes("#"))throw new Error("Invalid entropy");return K8(t,Ys(t,`virtual:${e}#${t.reference}`))}function Tp(t){return t.range.startsWith(JB)}function Gu(t){return t.reference.startsWith(JB)}function ZB(t){if(!Tp(t))throw new Error("Not a virtual descriptor");return On(t,t.range.replace(PQ,""))}function rI(t){if(!Gu(t))throw new Error("Not a virtual descriptor");return Ys(t,t.reference.replace(PQ,""))}function jpt(t){return Tp(t)?On(t,t.range.replace(PQ,"")):t}function qpt(t){return Gu(t)?Ys(t,t.reference.replace(PQ,"")):t}function Gpt(t,e){return t.range.includes("::")?t:On(t,`${t.range}::${tI.default.stringify(e)}`)}function Wpt(t,e){return t.reference.includes("::")?t:Ys(t,`${t.reference}::${tI.default.stringify(e)}`)}function XB(t,e){return t.identHash===e.identHash}function uhe(t,e){return t.descriptorHash===e.descriptorHash}function $B(t,e){return t.locatorHash===e.locatorHash}function Ypt(t,e){if(!Gu(t))throw new Error("Invalid package type");if(!Gu(e))throw new Error("Invalid package type");if(!XB(t,e)||t.dependencies.size!==e.dependencies.size)return!1;for(let r of t.dependencies.values()){let s=e.dependencies.get(r.identHash);if(!s||!uhe(r,s))return!1}return!0}function Da(t){let e=fhe(t);if(!e)throw new Error(`Invalid ident (${t})`);return e}function fhe(t){let e=t.match(Vpt);if(!e)return null;let[,r,s]=e;return ba(typeof r<"u"?r:null,s)}function C0(t,e=!1){let r=ev(t,e);if(!r)throw new Error(`Invalid descriptor (${t})`);return r}function ev(t,e=!1){let r=e?t.match(Kpt):t.match(Jpt);if(!r)return null;let[,s,a,n]=r;if(n==="unknown")throw new Error(`Invalid range (${t})`);let c=typeof s<"u"?s:null,f=typeof n<"u"?n:"unknown";return On(ba(c,a),f)}function Rp(t,e=!1){let r=xQ(t,e);if(!r)throw new Error(`Invalid locator (${t})`);return r}function xQ(t,e=!1){let r=e?t.match(zpt):t.match(Zpt);if(!r)return null;let[,s,a,n]=r;if(n==="unknown")throw new Error(`Invalid reference (${t})`);let c=typeof s<"u"?s:null,f=typeof n<"u"?n:"unknown";return Ys(ba(c,a),f)}function em(t,e){let r=t.match(Xpt);if(r===null)throw new Error(`Invalid range (${t})`);let s=typeof r[1]<"u"?r[1]:null;if(typeof e?.requireProtocol=="string"&&s!==e.requireProtocol)throw new Error(`Invalid protocol (${s})`);if(e?.requireProtocol&&s===null)throw new Error(`Missing protocol (${s})`);let a=typeof r[3]<"u"?decodeURIComponent(r[2]):null;if(e?.requireSource&&a===null)throw new Error(`Missing source (${t})`);let n=typeof r[3]<"u"?decodeURIComponent(r[3]):decodeURIComponent(r[2]),c=e?.parseSelector?tI.default.parse(n):n,f=typeof r[4]<"u"?tI.default.parse(r[4]):null;return{protocol:s,source:a,selector:c,params:f}}function $pt(t,e){try{return em(t,e)}catch{return null}}function eht(t,{protocol:e}){let{selector:r,params:s}=em(t,{requireProtocol:e,requireBindings:!0});if(typeof s.locator!="string")throw new Error(`Assertion failed: Invalid bindings for ${t}`);return{parentLocator:Rp(s.locator,!0),path:r}}function ohe(t){return t=t.replaceAll("%","%25"),t=t.replaceAll(":","%3A"),t=t.replaceAll("#","%23"),t}function tht(t){return t===null?!1:Object.entries(t).length>0}function kQ({protocol:t,source:e,selector:r,params:s}){let a="";return t!==null&&(a+=`${t}`),e!==null&&(a+=`${ohe(e)}#`),a+=ohe(r),tht(s)&&(a+=`::${tI.default.stringify(s)}`),a}function rht(t){let{params:e,protocol:r,source:s,selector:a}=em(t);for(let n in e)n.startsWith("__")&&delete e[n];return kQ({protocol:r,source:s,params:e,selector:a})}function cn(t){return t.scope?`@${t.scope}/${t.name}`:`${t.name}`}function nht(t,e){return t.scope?ba(e,`${t.scope}__${t.name}`):ba(e,t.name)}function iht(t,e){if(t.scope!==e)return t;let r=t.name.indexOf("__");if(r===-1)return ba(null,t.name);let s=t.name.slice(0,r),a=t.name.slice(r+2);return ba(s,a)}function ll(t){return t.scope?`@${t.scope}/${t.name}@${t.range}`:`${t.name}@${t.range}`}function cl(t){return t.scope?`@${t.scope}/${t.name}@${t.reference}`:`${t.name}@${t.reference}`}function Y8(t){return t.scope!==null?`@${t.scope}-${t.name}`:t.name}function nI(t){let{protocol:e,selector:r}=em(t.reference),s=e!==null?e.replace(sht,""):"exotic",a=ahe.default.valid(r),n=a!==null?`${s}-${a}`:`${s}`,c=10;return t.scope?`${Y8(t)}-${n}-${t.locatorHash.slice(0,c)}`:`${Y8(t)}-${n}-${t.locatorHash.slice(0,c)}`}function $i(t,e){return e.scope?`${Ut(t,`@${e.scope}/`,Ct.SCOPE)}${Ut(t,e.name,Ct.NAME)}`:`${Ut(t,e.name,Ct.NAME)}`}function QQ(t){if(t.startsWith(JB)){let e=QQ(t.substring(t.indexOf("#")+1)),r=t.substring(JB.length,JB.length+Mpt);return`${e} [${r}]`}else return t.replace(oht,"?[...]")}function iI(t,e){return`${Ut(t,QQ(e),Ct.RANGE)}`}function ri(t,e){return`${$i(t,e)}${Ut(t,"@",Ct.RANGE)}${iI(t,e.range)}`}function tv(t,e){return`${Ut(t,QQ(e),Ct.REFERENCE)}`}function Yr(t,e){return`${$i(t,e)}${Ut(t,"@",Ct.REFERENCE)}${tv(t,e.reference)}`}function w3(t){return`${cn(t)}@${QQ(t.reference)}`}function sI(t){return Ws(t,[e=>cn(e),e=>e.range])}function rv(t,e){return $i(t,e.anchoredLocator)}function VB(t,e,r){let s=Tp(e)?ZB(e):e;return r===null?`${ri(t,s)} \u2192 ${C3(t).Cross}`:s.identHash===r.identHash?`${ri(t,s)} \u2192 ${tv(t,r.reference)}`:`${ri(t,s)} \u2192 ${Yr(t,r)}`}function B3(t,e,r){return r===null?`${Yr(t,e)}`:`${Yr(t,e)} (via ${iI(t,r.range)})`}function Z8(t){return`node_modules/${cn(t)}`}function TQ(t,e){return t.conditions?_pt(t.conditions,r=>{let[,s,a]=r.match(che),n=e[s];return n?n.includes(a):!0}):!0}function nv(t){let e=new Set;if("children"in t)e.add(t);else for(let r of t.requests.values())e.add(r);for(let r of e)for(let s of r.children.values())e.add(s);return e}var tI,ahe,lhe,JB,Mpt,che,_pt,PQ,Vpt,Kpt,Jpt,zpt,Zpt,Xpt,sht,oht,Yo=It(()=>{tI=et(ye("querystring")),ahe=et(fi()),lhe=et(noe());Qc();I0();kc();Yo();JB="virtual:",Mpt=5,che=/(os|cpu|libc)=([a-z0-9_-]+)/,_pt=(0,lhe.makeParser)(che);PQ=/^[^#]*#/;Vpt=/^(?:@([^/]+?)\/)?([^@/]+)$/;Kpt=/^(?:@([^/]+?)\/)?([^@/]+?)(?:@(.+))$/,Jpt=/^(?:@([^/]+?)\/)?([^@/]+?)(?:@(.+))?$/;zpt=/^(?:@([^/]+?)\/)?([^@/]+?)(?:@(.+))$/,Zpt=/^(?:@([^/]+?)\/)?([^@/]+?)(?:@(.+))?$/;Xpt=/^([^#:]*:)?((?:(?!::)[^#])*)(?:#((?:(?!::).)*))?(?:::(.*))?$/;sht=/:$/;oht=/\?.*/});var Ahe,phe=It(()=>{Yo();Ahe={hooks:{reduceDependency:(t,e,r,s,{resolver:a,resolveOptions:n})=>{for(let{pattern:c,reference:f}of e.topLevelWorkspace.manifest.resolutions){if(c.from&&(c.from.fullName!==cn(r)||e.configuration.normalizeLocator(Ys(Da(c.from.fullName),c.from.description??r.reference)).locatorHash!==r.locatorHash)||c.descriptor.fullName!==cn(t)||e.configuration.normalizeDependency(On(Rp(c.descriptor.fullName),c.descriptor.description??t.range)).descriptorHash!==t.descriptorHash)continue;return a.bindDescriptor(e.configuration.normalizeDependency(On(t,f)),e.topLevelWorkspace.anchoredLocator,n)}return t},validateProject:async(t,e)=>{for(let r of t.workspaces){let s=rv(t.configuration,r);await t.configuration.triggerHook(a=>a.validateWorkspace,r,{reportWarning:(a,n)=>e.reportWarning(a,`${s}: ${n}`),reportError:(a,n)=>e.reportError(a,`${s}: ${n}`)})}},validateWorkspace:async(t,e)=>{let{manifest:r}=t;r.resolutions.length&&t.cwd!==t.project.cwd&&r.errors.push(new Error("Resolutions field will be ignored"));for(let s of r.errors)e.reportWarning(57,s.message)}}}});var yi,tm=It(()=>{yi=class t{static{this.protocol="workspace:"}supportsDescriptor(e,r){return!!(e.range.startsWith(t.protocol)||r.project.tryWorkspaceByDescriptor(e)!==null)}supportsLocator(e,r){return!!e.reference.startsWith(t.protocol)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){return[s.project.getWorkspaceByDescriptor(e).anchoredLocator]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){let s=r.project.getWorkspaceByCwd(e.reference.slice(t.protocol.length));return{...e,version:s.manifest.version||"0.0.0",languageName:"unknown",linkType:"SOFT",conditions:null,dependencies:r.project.configuration.normalizeDependencyMap(new Map([...s.manifest.dependencies,...s.manifest.devDependencies])),peerDependencies:new Map([...s.manifest.peerDependencies]),dependenciesMeta:s.manifest.dependenciesMeta,peerDependenciesMeta:s.manifest.peerDependenciesMeta,bin:s.manifest.bin}}}});var Or={};Vt(Or,{SemVer:()=>yhe.SemVer,clean:()=>lht,getComparator:()=>dhe,mergeComparators:()=>X8,satisfiesWithPrereleases:()=>eA,simplifyRanges:()=>$8,stringifyComparator:()=>mhe,validRange:()=>ul});function eA(t,e,r=!1){if(!t)return!1;let s=`${e}${r}`,a=hhe.get(s);if(typeof a>"u")try{a=new Fp.default.Range(e,{includePrerelease:!0,loose:r})}catch{return!1}finally{hhe.set(s,a||null)}else if(a===null)return!1;let n;try{n=new Fp.default.SemVer(t,a)}catch{return!1}return a.test(n)?!0:(n.prerelease&&(n.prerelease=[]),a.set.some(c=>{for(let f of c)f.semver.prerelease&&(f.semver.prerelease=[]);return c.every(f=>f.test(n))}))}function ul(t){if(t.indexOf(":")!==-1)return null;let e=ghe.get(t);if(typeof e<"u")return e;try{e=new Fp.default.Range(t)}catch{e=null}return ghe.set(t,e),e}function lht(t){let e=aht.exec(t);return e?e[1]:null}function dhe(t){if(t.semver===Fp.default.Comparator.ANY)return{gt:null,lt:null};switch(t.operator){case"":return{gt:[">=",t.semver],lt:["<=",t.semver]};case">":case">=":return{gt:[t.operator,t.semver],lt:null};case"<":case"<=":return{gt:null,lt:[t.operator,t.semver]};default:throw new Error(`Assertion failed: Unexpected comparator operator (${t.operator})`)}}function X8(t){if(t.length===0)return null;let e=null,r=null;for(let s of t){if(s.gt){let a=e!==null?Fp.default.compare(s.gt[1],e[1]):null;(a===null||a>0||a===0&&s.gt[0]===">")&&(e=s.gt)}if(s.lt){let a=r!==null?Fp.default.compare(s.lt[1],r[1]):null;(a===null||a<0||a===0&&s.lt[0]==="<")&&(r=s.lt)}}if(e&&r){let s=Fp.default.compare(e[1],r[1]);if(s===0&&(e[0]===">"||r[0]==="<")||s>0)return null}return{gt:e,lt:r}}function mhe(t){if(t.gt&&t.lt){if(t.gt[0]===">="&&t.lt[0]==="<="&&t.gt[1].version===t.lt[1].version)return t.gt[1].version;if(t.gt[0]===">="&&t.lt[0]==="<"){if(t.lt[1].version===`${t.gt[1].major+1}.0.0-0`)return`^${t.gt[1].version}`;if(t.lt[1].version===`${t.gt[1].major}.${t.gt[1].minor+1}.0-0`)return`~${t.gt[1].version}`}}let e=[];return t.gt&&e.push(t.gt[0]+t.gt[1].version),t.lt&&e.push(t.lt[0]+t.lt[1].version),e.length?e.join(" "):"*"}function $8(t){let e=t.map(cht).map(s=>ul(s).set.map(a=>a.map(n=>dhe(n)))),r=e.shift().map(s=>X8(s)).filter(s=>s!==null);for(let s of e){let a=[];for(let n of r)for(let c of s){let f=X8([n,...c]);f!==null&&a.push(f)}r=a}return r.length===0?null:r.map(s=>mhe(s)).join(" || ")}function cht(t){let e=t.split("||");if(e.length>1){let r=new Set;for(let s of e)e.some(a=>a!==s&&Fp.default.subset(s,a))||r.add(s);if(r.size{Fp=et(fi()),yhe=et(fi()),hhe=new Map;ghe=new Map;aht=/^(?:[\sv=]*?)((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\s*)$/});function Ehe(t){let e=t.match(/^[ \t]+/m);return e?e[0]:" "}function Ihe(t){return t.charCodeAt(0)===65279?t.slice(1):t}function Pa(t){return t.replace(/\\/g,"/")}function RQ(t,{yamlCompatibilityMode:e}){return e?h3(t):typeof t>"u"||typeof t=="boolean"?t:null}function Che(t,e){let r=e.search(/[^!]/);if(r===-1)return"invalid";let s=r%2===0?"":"!",a=e.slice(r);return`${s}${t}=${a}`}function eH(t,e){return e.length===1?Che(t,e[0]):`(${e.map(r=>Che(t,r)).join(" | ")})`}var whe,Ht,oI=It(()=>{bt();Bc();whe=et(fi());tm();kc();Np();Yo();Ht=class t{constructor(){this.indent=" ";this.name=null;this.version=null;this.os=null;this.cpu=null;this.libc=null;this.type=null;this.packageManager=null;this.private=!1;this.license=null;this.main=null;this.module=null;this.browser=null;this.languageName=null;this.bin=new Map;this.scripts=new Map;this.dependencies=new Map;this.devDependencies=new Map;this.peerDependencies=new Map;this.workspaceDefinitions=[];this.dependenciesMeta=new Map;this.peerDependenciesMeta=new Map;this.resolutions=[];this.files=null;this.publishConfig=null;this.installConfig=null;this.preferUnplugged=null;this.raw={};this.errors=[]}static{this.fileName="package.json"}static{this.allDependencies=["dependencies","devDependencies","peerDependencies"]}static{this.hardDependencies=["dependencies","devDependencies"]}static async tryFind(e,{baseFs:r=new Yn}={}){let s=K.join(e,"package.json");try{return await t.fromFile(s,{baseFs:r})}catch(a){if(a.code==="ENOENT")return null;throw a}}static async find(e,{baseFs:r}={}){let s=await t.tryFind(e,{baseFs:r});if(s===null)throw new Error("Manifest not found");return s}static async fromFile(e,{baseFs:r=new Yn}={}){let s=new t;return await s.loadFile(e,{baseFs:r}),s}static fromText(e){let r=new t;return r.loadFromText(e),r}loadFromText(e){let r;try{r=JSON.parse(Ihe(e)||"{}")}catch(s){throw s.message+=` (when parsing ${e})`,s}this.load(r),this.indent=Ehe(e)}async loadFile(e,{baseFs:r=new Yn}){let s=await r.readFilePromise(e,"utf8"),a;try{a=JSON.parse(Ihe(s)||"{}")}catch(n){throw n.message+=` (when parsing ${e})`,n}this.load(a),this.indent=Ehe(s)}load(e,{yamlCompatibilityMode:r=!1}={}){if(typeof e!="object"||e===null)throw new Error(`Utterly invalid manifest data (${e})`);this.raw=e;let s=[];if(this.name=null,typeof e.name=="string")try{this.name=Da(e.name)}catch{s.push(new Error("Parsing failed for the 'name' field"))}if(typeof e.version=="string"?this.version=e.version:this.version=null,Array.isArray(e.os)){let n=[];this.os=n;for(let c of e.os)typeof c!="string"?s.push(new Error("Parsing failed for the 'os' field")):n.push(c)}else this.os=null;if(Array.isArray(e.cpu)){let n=[];this.cpu=n;for(let c of e.cpu)typeof c!="string"?s.push(new Error("Parsing failed for the 'cpu' field")):n.push(c)}else this.cpu=null;if(Array.isArray(e.libc)){let n=[];this.libc=n;for(let c of e.libc)typeof c!="string"?s.push(new Error("Parsing failed for the 'libc' field")):n.push(c)}else this.libc=null;if(typeof e.type=="string"?this.type=e.type:this.type=null,typeof e.packageManager=="string"?this.packageManager=e.packageManager:this.packageManager=null,typeof e.private=="boolean"?this.private=e.private:this.private=!1,typeof e.license=="string"?this.license=e.license:this.license=null,typeof e.languageName=="string"?this.languageName=e.languageName:this.languageName=null,typeof e.main=="string"?this.main=Pa(e.main):this.main=null,typeof e.module=="string"?this.module=Pa(e.module):this.module=null,e.browser!=null)if(typeof e.browser=="string")this.browser=Pa(e.browser);else{this.browser=new Map;for(let[n,c]of Object.entries(e.browser))this.browser.set(Pa(n),typeof c=="string"?Pa(c):c)}else this.browser=null;if(this.bin=new Map,typeof e.bin=="string")e.bin.trim()===""?s.push(new Error("Invalid bin field")):this.name!==null?this.bin.set(this.name.name,Pa(e.bin)):s.push(new Error("String bin field, but no attached package name"));else if(typeof e.bin=="object"&&e.bin!==null)for(let[n,c]of Object.entries(e.bin)){if(typeof c!="string"||c.trim()===""){s.push(new Error(`Invalid bin definition for '${n}'`));continue}let f=Da(n);this.bin.set(f.name,Pa(c))}if(this.scripts=new Map,typeof e.scripts=="object"&&e.scripts!==null)for(let[n,c]of Object.entries(e.scripts)){if(typeof c!="string"){s.push(new Error(`Invalid script definition for '${n}'`));continue}this.scripts.set(n,c)}if(this.dependencies=new Map,typeof e.dependencies=="object"&&e.dependencies!==null)for(let[n,c]of Object.entries(e.dependencies)){if(typeof c!="string"){s.push(new Error(`Invalid dependency range for '${n}'`));continue}let f;try{f=Da(n)}catch{s.push(new Error(`Parsing failed for the dependency name '${n}'`));continue}let p=On(f,c);this.dependencies.set(p.identHash,p)}if(this.devDependencies=new Map,typeof e.devDependencies=="object"&&e.devDependencies!==null)for(let[n,c]of Object.entries(e.devDependencies)){if(typeof c!="string"){s.push(new Error(`Invalid dependency range for '${n}'`));continue}let f;try{f=Da(n)}catch{s.push(new Error(`Parsing failed for the dependency name '${n}'`));continue}let p=On(f,c);this.devDependencies.set(p.identHash,p)}if(this.peerDependencies=new Map,typeof e.peerDependencies=="object"&&e.peerDependencies!==null)for(let[n,c]of Object.entries(e.peerDependencies)){let f;try{f=Da(n)}catch{s.push(new Error(`Parsing failed for the dependency name '${n}'`));continue}(typeof c!="string"||!c.startsWith(yi.protocol)&&!ul(c))&&(s.push(new Error(`Invalid dependency range for '${n}'`)),c="*");let p=On(f,c);this.peerDependencies.set(p.identHash,p)}typeof e.workspaces=="object"&&e.workspaces!==null&&e.workspaces.nohoist&&s.push(new Error("'nohoist' is deprecated, please use 'installConfig.hoistingLimits' instead"));let a=Array.isArray(e.workspaces)?e.workspaces:typeof e.workspaces=="object"&&e.workspaces!==null&&Array.isArray(e.workspaces.packages)?e.workspaces.packages:[];this.workspaceDefinitions=[];for(let n of a){if(typeof n!="string"){s.push(new Error(`Invalid workspace definition for '${n}'`));continue}this.workspaceDefinitions.push({pattern:n})}if(this.dependenciesMeta=new Map,typeof e.dependenciesMeta=="object"&&e.dependenciesMeta!==null)for(let[n,c]of Object.entries(e.dependenciesMeta)){if(typeof c!="object"||c===null){s.push(new Error(`Invalid meta field for '${n}`));continue}let f=C0(n),p=this.ensureDependencyMeta(f),h=RQ(c.built,{yamlCompatibilityMode:r});if(h===null){s.push(new Error(`Invalid built meta field for '${n}'`));continue}let E=RQ(c.optional,{yamlCompatibilityMode:r});if(E===null){s.push(new Error(`Invalid optional meta field for '${n}'`));continue}let C=RQ(c.unplugged,{yamlCompatibilityMode:r});if(C===null){s.push(new Error(`Invalid unplugged meta field for '${n}'`));continue}Object.assign(p,{built:h,optional:E,unplugged:C})}if(this.peerDependenciesMeta=new Map,typeof e.peerDependenciesMeta=="object"&&e.peerDependenciesMeta!==null)for(let[n,c]of Object.entries(e.peerDependenciesMeta)){if(typeof c!="object"||c===null){s.push(new Error(`Invalid meta field for '${n}'`));continue}let f=C0(n),p=this.ensurePeerDependencyMeta(f),h=RQ(c.optional,{yamlCompatibilityMode:r});if(h===null){s.push(new Error(`Invalid optional meta field for '${n}'`));continue}Object.assign(p,{optional:h})}if(this.resolutions=[],typeof e.resolutions=="object"&&e.resolutions!==null)for(let[n,c]of Object.entries(e.resolutions)){if(typeof c!="string"){s.push(new Error(`Invalid resolution entry for '${n}'`));continue}try{this.resolutions.push({pattern:bx(n),reference:c})}catch(f){s.push(f);continue}}if(Array.isArray(e.files)){this.files=new Set;for(let n of e.files){if(typeof n!="string"){s.push(new Error(`Invalid files entry for '${n}'`));continue}this.files.add(n)}}else this.files=null;if(typeof e.publishConfig=="object"&&e.publishConfig!==null){if(this.publishConfig={},typeof e.publishConfig.access=="string"&&(this.publishConfig.access=e.publishConfig.access),typeof e.publishConfig.main=="string"&&(this.publishConfig.main=Pa(e.publishConfig.main)),typeof e.publishConfig.module=="string"&&(this.publishConfig.module=Pa(e.publishConfig.module)),e.publishConfig.browser!=null)if(typeof e.publishConfig.browser=="string")this.publishConfig.browser=Pa(e.publishConfig.browser);else{this.publishConfig.browser=new Map;for(let[n,c]of Object.entries(e.publishConfig.browser))this.publishConfig.browser.set(Pa(n),typeof c=="string"?Pa(c):c)}if(typeof e.publishConfig.registry=="string"&&(this.publishConfig.registry=e.publishConfig.registry),typeof e.publishConfig.bin=="string")this.name!==null?this.publishConfig.bin=new Map([[this.name.name,Pa(e.publishConfig.bin)]]):s.push(new Error("String bin field, but no attached package name"));else if(typeof e.publishConfig.bin=="object"&&e.publishConfig.bin!==null){this.publishConfig.bin=new Map;for(let[n,c]of Object.entries(e.publishConfig.bin)){if(typeof c!="string"){s.push(new Error(`Invalid bin definition for '${n}'`));continue}this.publishConfig.bin.set(n,Pa(c))}}if(Array.isArray(e.publishConfig.executableFiles)){this.publishConfig.executableFiles=new Set;for(let n of e.publishConfig.executableFiles){if(typeof n!="string"){s.push(new Error("Invalid executable file definition"));continue}this.publishConfig.executableFiles.add(Pa(n))}}}else this.publishConfig=null;if(typeof e.installConfig=="object"&&e.installConfig!==null){this.installConfig={};for(let n of Object.keys(e.installConfig))n==="hoistingLimits"?typeof e.installConfig.hoistingLimits=="string"?this.installConfig.hoistingLimits=e.installConfig.hoistingLimits:s.push(new Error("Invalid hoisting limits definition")):n=="selfReferences"?typeof e.installConfig.selfReferences=="boolean"?this.installConfig.selfReferences=e.installConfig.selfReferences:s.push(new Error("Invalid selfReferences definition, must be a boolean value")):s.push(new Error(`Unrecognized installConfig key: ${n}`))}else this.installConfig=null;if(typeof e.optionalDependencies=="object"&&e.optionalDependencies!==null)for(let[n,c]of Object.entries(e.optionalDependencies)){if(typeof c!="string"){s.push(new Error(`Invalid dependency range for '${n}'`));continue}let f;try{f=Da(n)}catch{s.push(new Error(`Parsing failed for the dependency name '${n}'`));continue}let p=On(f,c);this.dependencies.set(p.identHash,p);let h=On(f,"unknown"),E=this.ensureDependencyMeta(h);Object.assign(E,{optional:!0})}typeof e.preferUnplugged=="boolean"?this.preferUnplugged=e.preferUnplugged:this.preferUnplugged=null,this.errors=s}getForScope(e){switch(e){case"dependencies":return this.dependencies;case"devDependencies":return this.devDependencies;case"peerDependencies":return this.peerDependencies;default:throw new Error(`Unsupported value ("${e}")`)}}hasConsumerDependency(e){return!!(this.dependencies.has(e.identHash)||this.peerDependencies.has(e.identHash))}hasHardDependency(e){return!!(this.dependencies.has(e.identHash)||this.devDependencies.has(e.identHash))}hasSoftDependency(e){return!!this.peerDependencies.has(e.identHash)}hasDependency(e){return!!(this.hasHardDependency(e)||this.hasSoftDependency(e))}getConditions(){let e=[];return this.os&&this.os.length>0&&e.push(eH("os",this.os)),this.cpu&&this.cpu.length>0&&e.push(eH("cpu",this.cpu)),this.libc&&this.libc.length>0&&e.push(eH("libc",this.libc)),e.length>0?e.join(" & "):null}ensureDependencyMeta(e){if(e.range!=="unknown"&&!whe.default.valid(e.range))throw new Error(`Invalid meta field range for '${ll(e)}'`);let r=cn(e),s=e.range!=="unknown"?e.range:null,a=this.dependenciesMeta.get(r);a||this.dependenciesMeta.set(r,a=new Map);let n=a.get(s);return n||a.set(s,n={}),n}ensurePeerDependencyMeta(e){if(e.range!=="unknown")throw new Error(`Invalid meta field range for '${ll(e)}'`);let r=cn(e),s=this.peerDependenciesMeta.get(r);return s||this.peerDependenciesMeta.set(r,s={}),s}setRawField(e,r,{after:s=[]}={}){let a=new Set(s.filter(n=>Object.hasOwn(this.raw,n)));if(a.size===0||Object.hasOwn(this.raw,e))this.raw[e]=r;else{let n=this.raw,c=this.raw={},f=!1;for(let p of Object.keys(n))c[p]=n[p],f||(a.delete(p),a.size===0&&(c[e]=r,f=!0))}}exportTo(e,{compatibilityMode:r=!0}={}){if(Object.assign(e,this.raw),this.name!==null?e.name=cn(this.name):delete e.name,this.version!==null?e.version=this.version:delete e.version,this.os!==null?e.os=this.os:delete e.os,this.cpu!==null?e.cpu=this.cpu:delete e.cpu,this.type!==null?e.type=this.type:delete e.type,this.packageManager!==null?e.packageManager=this.packageManager:delete e.packageManager,this.private?e.private=!0:delete e.private,this.license!==null?e.license=this.license:delete e.license,this.languageName!==null?e.languageName=this.languageName:delete e.languageName,this.main!==null?e.main=this.main:delete e.main,this.module!==null?e.module=this.module:delete e.module,this.browser!==null){let n=this.browser;typeof n=="string"?e.browser=n:n instanceof Map&&(e.browser=Object.assign({},...Array.from(n.keys()).sort().map(c=>({[c]:n.get(c)}))))}else delete e.browser;this.bin.size===1&&this.name!==null&&this.bin.has(this.name.name)?e.bin=this.bin.get(this.name.name):this.bin.size>0?e.bin=Object.assign({},...Array.from(this.bin.keys()).sort().map(n=>({[n]:this.bin.get(n)}))):delete e.bin,this.workspaceDefinitions.length>0?this.raw.workspaces&&!Array.isArray(this.raw.workspaces)?e.workspaces={...this.raw.workspaces,packages:this.workspaceDefinitions.map(({pattern:n})=>n)}:e.workspaces=this.workspaceDefinitions.map(({pattern:n})=>n):this.raw.workspaces&&!Array.isArray(this.raw.workspaces)&&Object.keys(this.raw.workspaces).length>0?e.workspaces=this.raw.workspaces:delete e.workspaces;let s=[],a=[];for(let n of this.dependencies.values()){let c=this.dependenciesMeta.get(cn(n)),f=!1;if(r&&c){let p=c.get(null);p&&p.optional&&(f=!0)}f?a.push(n):s.push(n)}s.length>0?e.dependencies=Object.assign({},...sI(s).map(n=>({[cn(n)]:n.range}))):delete e.dependencies,a.length>0?e.optionalDependencies=Object.assign({},...sI(a).map(n=>({[cn(n)]:n.range}))):delete e.optionalDependencies,this.devDependencies.size>0?e.devDependencies=Object.assign({},...sI(this.devDependencies.values()).map(n=>({[cn(n)]:n.range}))):delete e.devDependencies,this.peerDependencies.size>0?e.peerDependencies=Object.assign({},...sI(this.peerDependencies.values()).map(n=>({[cn(n)]:n.range}))):delete e.peerDependencies,e.dependenciesMeta={};for(let[n,c]of Ws(this.dependenciesMeta.entries(),([f,p])=>f))for(let[f,p]of Ws(c.entries(),([h,E])=>h!==null?`0${h}`:"1")){let h=f!==null?ll(On(Da(n),f)):n,E={...p};r&&f===null&&delete E.optional,Object.keys(E).length!==0&&(e.dependenciesMeta[h]=E)}if(Object.keys(e.dependenciesMeta).length===0&&delete e.dependenciesMeta,this.peerDependenciesMeta.size>0?e.peerDependenciesMeta=Object.assign({},...Ws(this.peerDependenciesMeta.entries(),([n,c])=>n).map(([n,c])=>({[n]:c}))):delete e.peerDependenciesMeta,this.resolutions.length>0?e.resolutions=Object.assign({},...this.resolutions.map(({pattern:n,reference:c})=>({[Px(n)]:c}))):delete e.resolutions,this.files!==null?e.files=Array.from(this.files):delete e.files,this.preferUnplugged!==null?e.preferUnplugged=this.preferUnplugged:delete e.preferUnplugged,this.scripts!==null&&this.scripts.size>0){e.scripts??={};for(let n of Object.keys(e.scripts))this.scripts.has(n)||delete e.scripts[n];for(let[n,c]of this.scripts.entries())e.scripts[n]=c}else delete e.scripts;return e}}});var vhe=L((WJt,Bhe)=>{var uht=Pc(),fht=function(){return uht.Date.now()};Bhe.exports=fht});var Dhe=L((YJt,She)=>{var Aht=/\s/;function pht(t){for(var e=t.length;e--&&Aht.test(t.charAt(e)););return e}She.exports=pht});var Phe=L((VJt,bhe)=>{var hht=Dhe(),ght=/^\s+/;function dht(t){return t&&t.slice(0,hht(t)+1).replace(ght,"")}bhe.exports=dht});var aI=L((KJt,xhe)=>{var mht=Vd(),yht=zf(),Eht="[object Symbol]";function Iht(t){return typeof t=="symbol"||yht(t)&&mht(t)==Eht}xhe.exports=Iht});var Rhe=L((JJt,The)=>{var Cht=Phe(),khe=Wl(),wht=aI(),Qhe=NaN,Bht=/^[-+]0x[0-9a-f]+$/i,vht=/^0b[01]+$/i,Sht=/^0o[0-7]+$/i,Dht=parseInt;function bht(t){if(typeof t=="number")return t;if(wht(t))return Qhe;if(khe(t)){var e=typeof t.valueOf=="function"?t.valueOf():t;t=khe(e)?e+"":e}if(typeof t!="string")return t===0?t:+t;t=Cht(t);var r=vht.test(t);return r||Sht.test(t)?Dht(t.slice(2),r?2:8):Bht.test(t)?Qhe:+t}The.exports=bht});var Ohe=L((zJt,Nhe)=>{var Pht=Wl(),tH=vhe(),Fhe=Rhe(),xht="Expected a function",kht=Math.max,Qht=Math.min;function Tht(t,e,r){var s,a,n,c,f,p,h=0,E=!1,C=!1,S=!0;if(typeof t!="function")throw new TypeError(xht);e=Fhe(e)||0,Pht(r)&&(E=!!r.leading,C="maxWait"in r,n=C?kht(Fhe(r.maxWait)||0,e):n,S="trailing"in r?!!r.trailing:S);function P(ce){var me=s,pe=a;return s=a=void 0,h=ce,c=t.apply(pe,me),c}function I(ce){return h=ce,f=setTimeout(U,e),E?P(ce):c}function R(ce){var me=ce-p,pe=ce-h,Be=e-me;return C?Qht(Be,n-pe):Be}function N(ce){var me=ce-p,pe=ce-h;return p===void 0||me>=e||me<0||C&&pe>=n}function U(){var ce=tH();if(N(ce))return W(ce);f=setTimeout(U,R(ce))}function W(ce){return f=void 0,S&&s?P(ce):(s=a=void 0,c)}function te(){f!==void 0&&clearTimeout(f),h=0,s=p=a=f=void 0}function ie(){return f===void 0?c:W(tH())}function Ae(){var ce=tH(),me=N(ce);if(s=arguments,a=this,p=ce,me){if(f===void 0)return I(p);if(C)return clearTimeout(f),f=setTimeout(U,e),P(p)}return f===void 0&&(f=setTimeout(U,e)),c}return Ae.cancel=te,Ae.flush=ie,Ae}Nhe.exports=Tht});var rH=L((ZJt,Lhe)=>{var Rht=Ohe(),Fht=Wl(),Nht="Expected a function";function Oht(t,e,r){var s=!0,a=!0;if(typeof t!="function")throw new TypeError(Nht);return Fht(r)&&(s="leading"in r?!!r.leading:s,a="trailing"in r?!!r.trailing:a),Rht(t,e,{leading:s,maxWait:e,trailing:a})}Lhe.exports=Oht});function Mht(t){return typeof t.reportCode<"u"}var Mhe,_he,Uhe,Lht,Yt,ho,Fc=It(()=>{Mhe=et(rH()),_he=ye("stream"),Uhe=ye("string_decoder"),Lht=15,Yt=class extends Error{constructor(r,s,a){super(s);this.reportExtra=a;this.reportCode=r}};ho=class{constructor(){this.cacheHits=new Set;this.cacheMisses=new Set;this.reportedInfos=new Set;this.reportedWarnings=new Set;this.reportedErrors=new Set}getRecommendedLength(){return 180}reportCacheHit(e){this.cacheHits.add(e.locatorHash)}reportCacheMiss(e,r){this.cacheMisses.add(e.locatorHash)}static progressViaCounter(e){let r=0,s,a=new Promise(p=>{s=p}),n=p=>{let h=s;a=new Promise(E=>{s=E}),r=p,h()},c=(p=0)=>{n(r+1)},f=async function*(){for(;r{r=c}),a=(0,Mhe.default)(c=>{let f=r;s=new Promise(p=>{r=p}),e=c,f()},1e3/Lht),n=async function*(){for(;;)await s,yield{title:e}}();return{[Symbol.asyncIterator](){return n},hasProgress:!1,hasTitle:!0,setTitle:a}}async startProgressPromise(e,r){let s=this.reportProgress(e);try{return await r(e)}finally{s.stop()}}startProgressSync(e,r){let s=this.reportProgress(e);try{return r(e)}finally{s.stop()}}reportInfoOnce(e,r,s){let a=s&&s.key?s.key:r;this.reportedInfos.has(a)||(this.reportedInfos.add(a),this.reportInfo(e,r),s?.reportExtra?.(this))}reportWarningOnce(e,r,s){let a=s&&s.key?s.key:r;this.reportedWarnings.has(a)||(this.reportedWarnings.add(a),this.reportWarning(e,r),s?.reportExtra?.(this))}reportErrorOnce(e,r,s){let a=s&&s.key?s.key:r;this.reportedErrors.has(a)||(this.reportedErrors.add(a),this.reportError(e,r),s?.reportExtra?.(this))}reportExceptionOnce(e){Mht(e)?this.reportErrorOnce(e.reportCode,e.message,{key:e,reportExtra:e.reportExtra}):this.reportErrorOnce(1,e.stack||e.message,{key:e})}createStreamReporter(e=null){let r=new _he.PassThrough,s=new Uhe.StringDecoder,a="";return r.on("data",n=>{let c=s.write(n),f;do if(f=c.indexOf(` +`),f!==-1){let p=a+c.substring(0,f);c=c.substring(f+1),a="",e!==null?this.reportInfo(null,`${e} ${p}`):this.reportInfo(null,p)}while(f!==-1);a+=c}),r.on("end",()=>{let n=s.end();n!==""&&(e!==null?this.reportInfo(null,`${e} ${n}`):this.reportInfo(null,n))}),r}}});var lI,nH=It(()=>{Fc();Yo();lI=class{constructor(e){this.fetchers=e}supports(e,r){return!!this.tryFetcher(e,r)}getLocalPath(e,r){return this.getFetcher(e,r).getLocalPath(e,r)}async fetch(e,r){return await this.getFetcher(e,r).fetch(e,r)}tryFetcher(e,r){let s=this.fetchers.find(a=>a.supports(e,r));return s||null}getFetcher(e,r){let s=this.fetchers.find(a=>a.supports(e,r));if(!s)throw new Yt(11,`${Yr(r.project.configuration,e)} isn't supported by any available fetcher`);return s}}});var rm,iH=It(()=>{Yo();rm=class{constructor(e){this.resolvers=e.filter(r=>r)}supportsDescriptor(e,r){return!!this.tryResolverByDescriptor(e,r)}supportsLocator(e,r){return!!this.tryResolverByLocator(e,r)}shouldPersistResolution(e,r){return this.getResolverByLocator(e,r).shouldPersistResolution(e,r)}bindDescriptor(e,r,s){return this.getResolverByDescriptor(e,s).bindDescriptor(e,r,s)}getResolutionDependencies(e,r){return this.getResolverByDescriptor(e,r).getResolutionDependencies(e,r)}async getCandidates(e,r,s){return await this.getResolverByDescriptor(e,s).getCandidates(e,r,s)}async getSatisfying(e,r,s,a){return this.getResolverByDescriptor(e,a).getSatisfying(e,r,s,a)}async resolve(e,r){return await this.getResolverByLocator(e,r).resolve(e,r)}tryResolverByDescriptor(e,r){let s=this.resolvers.find(a=>a.supportsDescriptor(e,r));return s||null}getResolverByDescriptor(e,r){let s=this.resolvers.find(a=>a.supportsDescriptor(e,r));if(!s)throw new Error(`${ri(r.project.configuration,e)} isn't supported by any available resolver`);return s}tryResolverByLocator(e,r){let s=this.resolvers.find(a=>a.supportsLocator(e,r));return s||null}getResolverByLocator(e,r){let s=this.resolvers.find(a=>a.supportsLocator(e,r));if(!s)throw new Error(`${Yr(r.project.configuration,e)} isn't supported by any available resolver`);return s}}});var cI,sH=It(()=>{bt();Yo();cI=class{supports(e){return!!e.reference.startsWith("virtual:")}getLocalPath(e,r){let s=e.reference.indexOf("#");if(s===-1)throw new Error("Invalid virtual package reference");let a=e.reference.slice(s+1),n=Ys(e,a);return r.fetcher.getLocalPath(n,r)}async fetch(e,r){let s=e.reference.indexOf("#");if(s===-1)throw new Error("Invalid virtual package reference");let a=e.reference.slice(s+1),n=Ys(e,a),c=await r.fetcher.fetch(n,r);return await this.ensureVirtualLink(e,c,r)}getLocatorFilename(e){return nI(e)}async ensureVirtualLink(e,r,s){let a=r.packageFs.getRealPath(),n=s.project.configuration.get("virtualFolder"),c=this.getLocatorFilename(e),f=fo.makeVirtualPath(n,c,a),p=new Hf(f,{baseFs:r.packageFs,pathUtils:K});return{...r,packageFs:p}}}});var FQ,Hhe=It(()=>{FQ=class t{static{this.protocol="virtual:"}static isVirtualDescriptor(e){return!!e.range.startsWith(t.protocol)}static isVirtualLocator(e){return!!e.reference.startsWith(t.protocol)}supportsDescriptor(e,r){return t.isVirtualDescriptor(e)}supportsLocator(e,r){return t.isVirtualLocator(e)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){throw new Error('Assertion failed: calling "bindDescriptor" on a virtual descriptor is unsupported')}getResolutionDependencies(e,r){throw new Error('Assertion failed: calling "getResolutionDependencies" on a virtual descriptor is unsupported')}async getCandidates(e,r,s){throw new Error('Assertion failed: calling "getCandidates" on a virtual descriptor is unsupported')}async getSatisfying(e,r,s,a){throw new Error('Assertion failed: calling "getSatisfying" on a virtual descriptor is unsupported')}async resolve(e,r){throw new Error('Assertion failed: calling "resolve" on a virtual locator is unsupported')}}});var uI,oH=It(()=>{bt();tm();uI=class{supports(e){return!!e.reference.startsWith(yi.protocol)}getLocalPath(e,r){return this.getWorkspace(e,r).cwd}async fetch(e,r){let s=this.getWorkspace(e,r).cwd;return{packageFs:new Sn(s),prefixPath:vt.dot,localPath:s}}getWorkspace(e,r){return r.project.getWorkspaceByCwd(e.reference.slice(yi.protocol.length))}}});function iv(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function jhe(t){return typeof t>"u"?3:iv(t)?0:Array.isArray(t)?1:2}function cH(t,e){return Object.hasOwn(t,e)}function Uht(t){return iv(t)&&cH(t,"onConflict")&&typeof t.onConflict=="string"}function Hht(t){if(typeof t>"u")return{onConflict:"default",value:t};if(!Uht(t))return{onConflict:"default",value:t};if(cH(t,"value"))return t;let{onConflict:e,...r}=t;return{onConflict:e,value:r}}function qhe(t,e){let r=iv(t)&&cH(t,e)?t[e]:void 0;return Hht(r)}function fI(t,e){return[t,e,Ghe]}function uH(t){return Array.isArray(t)?t[2]===Ghe:!1}function aH(t,e){if(iv(t)){let r={};for(let s of Object.keys(t))r[s]=aH(t[s],e);return fI(e,r)}return Array.isArray(t)?fI(e,t.map(r=>aH(r,e))):fI(e,t)}function lH(t,e,r,s,a){let n,c=[],f=a,p=0;for(let E=a-1;E>=s;--E){let[C,S]=t[E],{onConflict:P,value:I}=qhe(S,r),R=jhe(I);if(R!==3){if(n??=R,R!==n||P==="hardReset"){p=f;break}if(R===2)return fI(C,I);if(c.unshift([C,I]),P==="reset"){p=E;break}P==="extend"&&E===s&&(s=0),f=E}}if(typeof n>"u")return null;let h=c.map(([E])=>E).join(", ");switch(n){case 1:return fI(h,new Array().concat(...c.map(([E,C])=>C.map(S=>aH(S,E)))));case 0:{let E=Object.assign({},...c.map(([,R])=>R)),C=Object.keys(E),S={},P=t.map(([R,N])=>[R,qhe(N,r).value]),I=_ht(P,([R,N])=>{let U=jhe(N);return U!==0&&U!==3});if(I!==-1){let R=P.slice(I+1);for(let N of C)S[N]=lH(R,e,N,0,R.length)}else for(let R of C)S[R]=lH(P,e,R,p,P.length);return fI(h,S)}default:throw new Error("Assertion failed: Non-extendable value type")}}function Whe(t){return lH(t.map(([e,r])=>[e,{".":r}]),[],".",0,t.length)}function sv(t){return uH(t)?t[1]:t}function NQ(t){let e=uH(t)?t[1]:t;if(Array.isArray(e))return e.map(r=>NQ(r));if(iv(e)){let r={};for(let[s,a]of Object.entries(e))r[s]=NQ(a);return r}return e}function fH(t){return uH(t)?t[0]:null}var _ht,Ghe,Yhe=It(()=>{_ht=(t,e,r)=>{let s=[...t];return s.reverse(),s.findIndex(e,r)};Ghe=Symbol()});var OQ={};Vt(OQ,{getDefaultGlobalFolder:()=>pH,getHomeFolder:()=>AI,isFolderInside:()=>hH});function pH(){if(process.platform==="win32"){let t=ue.toPortablePath(process.env.LOCALAPPDATA||ue.join((0,AH.homedir)(),"AppData","Local"));return K.resolve(t,"Yarn/Berry")}if(process.env.XDG_DATA_HOME){let t=ue.toPortablePath(process.env.XDG_DATA_HOME);return K.resolve(t,"yarn/berry")}return K.resolve(AI(),".yarn/berry")}function AI(){return ue.toPortablePath((0,AH.homedir)()||"/usr/local/share")}function hH(t,e){let r=K.relative(e,t);return r&&!r.startsWith("..")&&!K.isAbsolute(r)}var AH,LQ=It(()=>{bt();AH=ye("os")});var zhe=L(pI=>{"use strict";var uzt=ye("net"),qht=ye("tls"),gH=ye("http"),Vhe=ye("https"),Ght=ye("events"),fzt=ye("assert"),Wht=ye("util");pI.httpOverHttp=Yht;pI.httpsOverHttp=Vht;pI.httpOverHttps=Kht;pI.httpsOverHttps=Jht;function Yht(t){var e=new Op(t);return e.request=gH.request,e}function Vht(t){var e=new Op(t);return e.request=gH.request,e.createSocket=Khe,e.defaultPort=443,e}function Kht(t){var e=new Op(t);return e.request=Vhe.request,e}function Jht(t){var e=new Op(t);return e.request=Vhe.request,e.createSocket=Khe,e.defaultPort=443,e}function Op(t){var e=this;e.options=t||{},e.proxyOptions=e.options.proxy||{},e.maxSockets=e.options.maxSockets||gH.Agent.defaultMaxSockets,e.requests=[],e.sockets=[],e.on("free",function(s,a,n,c){for(var f=Jhe(a,n,c),p=0,h=e.requests.length;p=this.maxSockets){n.requests.push(c);return}n.createSocket(c,function(f){f.on("free",p),f.on("close",h),f.on("agentRemove",h),e.onSocket(f);function p(){n.emit("free",f,c)}function h(E){n.removeSocket(f),f.removeListener("free",p),f.removeListener("close",h),f.removeListener("agentRemove",h)}})};Op.prototype.createSocket=function(e,r){var s=this,a={};s.sockets.push(a);var n=dH({},s.proxyOptions,{method:"CONNECT",path:e.host+":"+e.port,agent:!1,headers:{host:e.host+":"+e.port}});e.localAddress&&(n.localAddress=e.localAddress),n.proxyAuth&&(n.headers=n.headers||{},n.headers["Proxy-Authorization"]="Basic "+new Buffer(n.proxyAuth).toString("base64")),w0("making CONNECT request");var c=s.request(n);c.useChunkedEncodingByDefault=!1,c.once("response",f),c.once("upgrade",p),c.once("connect",h),c.once("error",E),c.end();function f(C){C.upgrade=!0}function p(C,S,P){process.nextTick(function(){h(C,S,P)})}function h(C,S,P){if(c.removeAllListeners(),S.removeAllListeners(),C.statusCode!==200){w0("tunneling socket could not be established, statusCode=%d",C.statusCode),S.destroy();var I=new Error("tunneling socket could not be established, statusCode="+C.statusCode);I.code="ECONNRESET",e.request.emit("error",I),s.removeSocket(a);return}if(P.length>0){w0("got illegal response body from proxy"),S.destroy();var I=new Error("got illegal response body from proxy");I.code="ECONNRESET",e.request.emit("error",I),s.removeSocket(a);return}return w0("tunneling connection has established"),s.sockets[s.sockets.indexOf(a)]=S,r(S)}function E(C){c.removeAllListeners(),w0(`tunneling socket could not be established, cause=%s +`,C.message,C.stack);var S=new Error("tunneling socket could not be established, cause="+C.message);S.code="ECONNRESET",e.request.emit("error",S),s.removeSocket(a)}};Op.prototype.removeSocket=function(e){var r=this.sockets.indexOf(e);if(r!==-1){this.sockets.splice(r,1);var s=this.requests.shift();s&&this.createSocket(s,function(a){s.request.onSocket(a)})}};function Khe(t,e){var r=this;Op.prototype.createSocket.call(r,t,function(s){var a=t.request.getHeader("host"),n=dH({},r.options,{socket:s,servername:a?a.replace(/:.*$/,""):t.host}),c=qht.connect(0,n);r.sockets[r.sockets.indexOf(s)]=c,e(c)})}function Jhe(t,e,r){return typeof t=="string"?{host:t,port:e,localAddress:r}:t}function dH(t){for(var e=1,r=arguments.length;e{Zhe.exports=zhe()});var Mp=L((Lp,MQ)=>{"use strict";Object.defineProperty(Lp,"__esModule",{value:!0});var $he=["Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Uint16Array","Int32Array","Uint32Array","Float32Array","Float64Array","BigInt64Array","BigUint64Array"];function zht(t){return $he.includes(t)}var Zht=["Function","Generator","AsyncGenerator","GeneratorFunction","AsyncGeneratorFunction","AsyncFunction","Observable","Array","Buffer","Blob","Object","RegExp","Date","Error","Map","Set","WeakMap","WeakSet","ArrayBuffer","SharedArrayBuffer","DataView","Promise","URL","FormData","URLSearchParams","HTMLElement",...$he];function Xht(t){return Zht.includes(t)}var $ht=["null","undefined","string","number","bigint","boolean","symbol"];function e0t(t){return $ht.includes(t)}function hI(t){return e=>typeof e===t}var{toString:e0e}=Object.prototype,ov=t=>{let e=e0e.call(t).slice(8,-1);if(/HTML\w+Element/.test(e)&&be.domElement(t))return"HTMLElement";if(Xht(e))return e},Ai=t=>e=>ov(e)===t;function be(t){if(t===null)return"null";switch(typeof t){case"undefined":return"undefined";case"string":return"string";case"number":return"number";case"boolean":return"boolean";case"function":return"Function";case"bigint":return"bigint";case"symbol":return"symbol";default:}if(be.observable(t))return"Observable";if(be.array(t))return"Array";if(be.buffer(t))return"Buffer";let e=ov(t);if(e)return e;if(t instanceof String||t instanceof Boolean||t instanceof Number)throw new TypeError("Please don't use object wrappers for primitive types");return"Object"}be.undefined=hI("undefined");be.string=hI("string");var t0t=hI("number");be.number=t=>t0t(t)&&!be.nan(t);be.bigint=hI("bigint");be.function_=hI("function");be.null_=t=>t===null;be.class_=t=>be.function_(t)&&t.toString().startsWith("class ");be.boolean=t=>t===!0||t===!1;be.symbol=hI("symbol");be.numericString=t=>be.string(t)&&!be.emptyStringOrWhitespace(t)&&!Number.isNaN(Number(t));be.array=(t,e)=>Array.isArray(t)?be.function_(e)?t.every(e):!0:!1;be.buffer=t=>{var e,r,s,a;return(a=(s=(r=(e=t)===null||e===void 0?void 0:e.constructor)===null||r===void 0?void 0:r.isBuffer)===null||s===void 0?void 0:s.call(r,t))!==null&&a!==void 0?a:!1};be.blob=t=>Ai("Blob")(t);be.nullOrUndefined=t=>be.null_(t)||be.undefined(t);be.object=t=>!be.null_(t)&&(typeof t=="object"||be.function_(t));be.iterable=t=>{var e;return be.function_((e=t)===null||e===void 0?void 0:e[Symbol.iterator])};be.asyncIterable=t=>{var e;return be.function_((e=t)===null||e===void 0?void 0:e[Symbol.asyncIterator])};be.generator=t=>{var e,r;return be.iterable(t)&&be.function_((e=t)===null||e===void 0?void 0:e.next)&&be.function_((r=t)===null||r===void 0?void 0:r.throw)};be.asyncGenerator=t=>be.asyncIterable(t)&&be.function_(t.next)&&be.function_(t.throw);be.nativePromise=t=>Ai("Promise")(t);var r0t=t=>{var e,r;return be.function_((e=t)===null||e===void 0?void 0:e.then)&&be.function_((r=t)===null||r===void 0?void 0:r.catch)};be.promise=t=>be.nativePromise(t)||r0t(t);be.generatorFunction=Ai("GeneratorFunction");be.asyncGeneratorFunction=t=>ov(t)==="AsyncGeneratorFunction";be.asyncFunction=t=>ov(t)==="AsyncFunction";be.boundFunction=t=>be.function_(t)&&!t.hasOwnProperty("prototype");be.regExp=Ai("RegExp");be.date=Ai("Date");be.error=Ai("Error");be.map=t=>Ai("Map")(t);be.set=t=>Ai("Set")(t);be.weakMap=t=>Ai("WeakMap")(t);be.weakSet=t=>Ai("WeakSet")(t);be.int8Array=Ai("Int8Array");be.uint8Array=Ai("Uint8Array");be.uint8ClampedArray=Ai("Uint8ClampedArray");be.int16Array=Ai("Int16Array");be.uint16Array=Ai("Uint16Array");be.int32Array=Ai("Int32Array");be.uint32Array=Ai("Uint32Array");be.float32Array=Ai("Float32Array");be.float64Array=Ai("Float64Array");be.bigInt64Array=Ai("BigInt64Array");be.bigUint64Array=Ai("BigUint64Array");be.arrayBuffer=Ai("ArrayBuffer");be.sharedArrayBuffer=Ai("SharedArrayBuffer");be.dataView=Ai("DataView");be.enumCase=(t,e)=>Object.values(e).includes(t);be.directInstanceOf=(t,e)=>Object.getPrototypeOf(t)===e.prototype;be.urlInstance=t=>Ai("URL")(t);be.urlString=t=>{if(!be.string(t))return!1;try{return new URL(t),!0}catch{return!1}};be.truthy=t=>!!t;be.falsy=t=>!t;be.nan=t=>Number.isNaN(t);be.primitive=t=>be.null_(t)||e0t(typeof t);be.integer=t=>Number.isInteger(t);be.safeInteger=t=>Number.isSafeInteger(t);be.plainObject=t=>{if(e0e.call(t)!=="[object Object]")return!1;let e=Object.getPrototypeOf(t);return e===null||e===Object.getPrototypeOf({})};be.typedArray=t=>zht(ov(t));var n0t=t=>be.safeInteger(t)&&t>=0;be.arrayLike=t=>!be.nullOrUndefined(t)&&!be.function_(t)&&n0t(t.length);be.inRange=(t,e)=>{if(be.number(e))return t>=Math.min(0,e)&&t<=Math.max(e,0);if(be.array(e)&&e.length===2)return t>=Math.min(...e)&&t<=Math.max(...e);throw new TypeError(`Invalid range: ${JSON.stringify(e)}`)};var i0t=1,s0t=["innerHTML","ownerDocument","style","attributes","nodeValue"];be.domElement=t=>be.object(t)&&t.nodeType===i0t&&be.string(t.nodeName)&&!be.plainObject(t)&&s0t.every(e=>e in t);be.observable=t=>{var e,r,s,a;return t?t===((r=(e=t)[Symbol.observable])===null||r===void 0?void 0:r.call(e))||t===((a=(s=t)["@@observable"])===null||a===void 0?void 0:a.call(s)):!1};be.nodeStream=t=>be.object(t)&&be.function_(t.pipe)&&!be.observable(t);be.infinite=t=>t===1/0||t===-1/0;var t0e=t=>e=>be.integer(e)&&Math.abs(e%2)===t;be.evenInteger=t0e(0);be.oddInteger=t0e(1);be.emptyArray=t=>be.array(t)&&t.length===0;be.nonEmptyArray=t=>be.array(t)&&t.length>0;be.emptyString=t=>be.string(t)&&t.length===0;var o0t=t=>be.string(t)&&!/\S/.test(t);be.emptyStringOrWhitespace=t=>be.emptyString(t)||o0t(t);be.nonEmptyString=t=>be.string(t)&&t.length>0;be.nonEmptyStringAndNotWhitespace=t=>be.string(t)&&!be.emptyStringOrWhitespace(t);be.emptyObject=t=>be.object(t)&&!be.map(t)&&!be.set(t)&&Object.keys(t).length===0;be.nonEmptyObject=t=>be.object(t)&&!be.map(t)&&!be.set(t)&&Object.keys(t).length>0;be.emptySet=t=>be.set(t)&&t.size===0;be.nonEmptySet=t=>be.set(t)&&t.size>0;be.emptyMap=t=>be.map(t)&&t.size===0;be.nonEmptyMap=t=>be.map(t)&&t.size>0;be.propertyKey=t=>be.any([be.string,be.number,be.symbol],t);be.formData=t=>Ai("FormData")(t);be.urlSearchParams=t=>Ai("URLSearchParams")(t);var r0e=(t,e,r)=>{if(!be.function_(e))throw new TypeError(`Invalid predicate: ${JSON.stringify(e)}`);if(r.length===0)throw new TypeError("Invalid number of values");return t.call(r,e)};be.any=(t,...e)=>(be.array(t)?t:[t]).some(s=>r0e(Array.prototype.some,s,e));be.all=(t,...e)=>r0e(Array.prototype.every,t,e);var _t=(t,e,r,s={})=>{if(!t){let{multipleValues:a}=s,n=a?`received values of types ${[...new Set(r.map(c=>`\`${be(c)}\``))].join(", ")}`:`received value of type \`${be(r)}\``;throw new TypeError(`Expected value which is \`${e}\`, ${n}.`)}};Lp.assert={undefined:t=>_t(be.undefined(t),"undefined",t),string:t=>_t(be.string(t),"string",t),number:t=>_t(be.number(t),"number",t),bigint:t=>_t(be.bigint(t),"bigint",t),function_:t=>_t(be.function_(t),"Function",t),null_:t=>_t(be.null_(t),"null",t),class_:t=>_t(be.class_(t),"Class",t),boolean:t=>_t(be.boolean(t),"boolean",t),symbol:t=>_t(be.symbol(t),"symbol",t),numericString:t=>_t(be.numericString(t),"string with a number",t),array:(t,e)=>{_t(be.array(t),"Array",t),e&&t.forEach(e)},buffer:t=>_t(be.buffer(t),"Buffer",t),blob:t=>_t(be.blob(t),"Blob",t),nullOrUndefined:t=>_t(be.nullOrUndefined(t),"null or undefined",t),object:t=>_t(be.object(t),"Object",t),iterable:t=>_t(be.iterable(t),"Iterable",t),asyncIterable:t=>_t(be.asyncIterable(t),"AsyncIterable",t),generator:t=>_t(be.generator(t),"Generator",t),asyncGenerator:t=>_t(be.asyncGenerator(t),"AsyncGenerator",t),nativePromise:t=>_t(be.nativePromise(t),"native Promise",t),promise:t=>_t(be.promise(t),"Promise",t),generatorFunction:t=>_t(be.generatorFunction(t),"GeneratorFunction",t),asyncGeneratorFunction:t=>_t(be.asyncGeneratorFunction(t),"AsyncGeneratorFunction",t),asyncFunction:t=>_t(be.asyncFunction(t),"AsyncFunction",t),boundFunction:t=>_t(be.boundFunction(t),"Function",t),regExp:t=>_t(be.regExp(t),"RegExp",t),date:t=>_t(be.date(t),"Date",t),error:t=>_t(be.error(t),"Error",t),map:t=>_t(be.map(t),"Map",t),set:t=>_t(be.set(t),"Set",t),weakMap:t=>_t(be.weakMap(t),"WeakMap",t),weakSet:t=>_t(be.weakSet(t),"WeakSet",t),int8Array:t=>_t(be.int8Array(t),"Int8Array",t),uint8Array:t=>_t(be.uint8Array(t),"Uint8Array",t),uint8ClampedArray:t=>_t(be.uint8ClampedArray(t),"Uint8ClampedArray",t),int16Array:t=>_t(be.int16Array(t),"Int16Array",t),uint16Array:t=>_t(be.uint16Array(t),"Uint16Array",t),int32Array:t=>_t(be.int32Array(t),"Int32Array",t),uint32Array:t=>_t(be.uint32Array(t),"Uint32Array",t),float32Array:t=>_t(be.float32Array(t),"Float32Array",t),float64Array:t=>_t(be.float64Array(t),"Float64Array",t),bigInt64Array:t=>_t(be.bigInt64Array(t),"BigInt64Array",t),bigUint64Array:t=>_t(be.bigUint64Array(t),"BigUint64Array",t),arrayBuffer:t=>_t(be.arrayBuffer(t),"ArrayBuffer",t),sharedArrayBuffer:t=>_t(be.sharedArrayBuffer(t),"SharedArrayBuffer",t),dataView:t=>_t(be.dataView(t),"DataView",t),enumCase:(t,e)=>_t(be.enumCase(t,e),"EnumCase",t),urlInstance:t=>_t(be.urlInstance(t),"URL",t),urlString:t=>_t(be.urlString(t),"string with a URL",t),truthy:t=>_t(be.truthy(t),"truthy",t),falsy:t=>_t(be.falsy(t),"falsy",t),nan:t=>_t(be.nan(t),"NaN",t),primitive:t=>_t(be.primitive(t),"primitive",t),integer:t=>_t(be.integer(t),"integer",t),safeInteger:t=>_t(be.safeInteger(t),"integer",t),plainObject:t=>_t(be.plainObject(t),"plain object",t),typedArray:t=>_t(be.typedArray(t),"TypedArray",t),arrayLike:t=>_t(be.arrayLike(t),"array-like",t),domElement:t=>_t(be.domElement(t),"HTMLElement",t),observable:t=>_t(be.observable(t),"Observable",t),nodeStream:t=>_t(be.nodeStream(t),"Node.js Stream",t),infinite:t=>_t(be.infinite(t),"infinite number",t),emptyArray:t=>_t(be.emptyArray(t),"empty array",t),nonEmptyArray:t=>_t(be.nonEmptyArray(t),"non-empty array",t),emptyString:t=>_t(be.emptyString(t),"empty string",t),emptyStringOrWhitespace:t=>_t(be.emptyStringOrWhitespace(t),"empty string or whitespace",t),nonEmptyString:t=>_t(be.nonEmptyString(t),"non-empty string",t),nonEmptyStringAndNotWhitespace:t=>_t(be.nonEmptyStringAndNotWhitespace(t),"non-empty string and not whitespace",t),emptyObject:t=>_t(be.emptyObject(t),"empty object",t),nonEmptyObject:t=>_t(be.nonEmptyObject(t),"non-empty object",t),emptySet:t=>_t(be.emptySet(t),"empty set",t),nonEmptySet:t=>_t(be.nonEmptySet(t),"non-empty set",t),emptyMap:t=>_t(be.emptyMap(t),"empty map",t),nonEmptyMap:t=>_t(be.nonEmptyMap(t),"non-empty map",t),propertyKey:t=>_t(be.propertyKey(t),"PropertyKey",t),formData:t=>_t(be.formData(t),"FormData",t),urlSearchParams:t=>_t(be.urlSearchParams(t),"URLSearchParams",t),evenInteger:t=>_t(be.evenInteger(t),"even integer",t),oddInteger:t=>_t(be.oddInteger(t),"odd integer",t),directInstanceOf:(t,e)=>_t(be.directInstanceOf(t,e),"T",t),inRange:(t,e)=>_t(be.inRange(t,e),"in range",t),any:(t,...e)=>_t(be.any(t,...e),"predicate returns truthy for any value",e,{multipleValues:!0}),all:(t,...e)=>_t(be.all(t,...e),"predicate returns truthy for all values",e,{multipleValues:!0})};Object.defineProperties(be,{class:{value:be.class_},function:{value:be.function_},null:{value:be.null_}});Object.defineProperties(Lp.assert,{class:{value:Lp.assert.class_},function:{value:Lp.assert.function_},null:{value:Lp.assert.null_}});Lp.default=be;MQ.exports=be;MQ.exports.default=be;MQ.exports.assert=Lp.assert});var n0e=L((hzt,mH)=>{"use strict";var _Q=class extends Error{constructor(e){super(e||"Promise was canceled"),this.name="CancelError"}get isCanceled(){return!0}},UQ=class t{static fn(e){return(...r)=>new t((s,a,n)=>{r.push(n),e(...r).then(s,a)})}constructor(e){this._cancelHandlers=[],this._isPending=!0,this._isCanceled=!1,this._rejectOnCancel=!0,this._promise=new Promise((r,s)=>{this._reject=s;let a=f=>{this._isPending=!1,r(f)},n=f=>{this._isPending=!1,s(f)},c=f=>{if(!this._isPending)throw new Error("The `onCancel` handler was attached after the promise settled.");this._cancelHandlers.push(f)};return Object.defineProperties(c,{shouldReject:{get:()=>this._rejectOnCancel,set:f=>{this._rejectOnCancel=f}}}),e(a,n,c)})}then(e,r){return this._promise.then(e,r)}catch(e){return this._promise.catch(e)}finally(e){return this._promise.finally(e)}cancel(e){if(!(!this._isPending||this._isCanceled)){if(this._cancelHandlers.length>0)try{for(let r of this._cancelHandlers)r()}catch(r){this._reject(r)}this._isCanceled=!0,this._rejectOnCancel&&this._reject(new _Q(e))}}get isCanceled(){return this._isCanceled}};Object.setPrototypeOf(UQ.prototype,Promise.prototype);mH.exports=UQ;mH.exports.CancelError=_Q});var i0e=L((EH,IH)=>{"use strict";Object.defineProperty(EH,"__esModule",{value:!0});function a0t(t){return t.encrypted}var yH=(t,e)=>{let r;typeof e=="function"?r={connect:e}:r=e;let s=typeof r.connect=="function",a=typeof r.secureConnect=="function",n=typeof r.close=="function",c=()=>{s&&r.connect(),a0t(t)&&a&&(t.authorized?r.secureConnect():t.authorizationError||t.once("secureConnect",r.secureConnect)),n&&t.once("close",r.close)};t.writable&&!t.connecting?c():t.connecting?t.once("connect",c):t.destroyed&&n&&r.close(t._hadError)};EH.default=yH;IH.exports=yH;IH.exports.default=yH});var s0e=L((wH,BH)=>{"use strict";Object.defineProperty(wH,"__esModule",{value:!0});var l0t=i0e(),c0t=Number(process.versions.node.split(".")[0]),CH=t=>{let e={start:Date.now(),socket:void 0,lookup:void 0,connect:void 0,secureConnect:void 0,upload:void 0,response:void 0,end:void 0,error:void 0,abort:void 0,phases:{wait:void 0,dns:void 0,tcp:void 0,tls:void 0,request:void 0,firstByte:void 0,download:void 0,total:void 0}};t.timings=e;let r=c=>{let f=c.emit.bind(c);c.emit=(p,...h)=>(p==="error"&&(e.error=Date.now(),e.phases.total=e.error-e.start,c.emit=f),f(p,...h))};r(t),t.prependOnceListener("abort",()=>{e.abort=Date.now(),(!e.response||c0t>=13)&&(e.phases.total=Date.now()-e.start)});let s=c=>{e.socket=Date.now(),e.phases.wait=e.socket-e.start;let f=()=>{e.lookup=Date.now(),e.phases.dns=e.lookup-e.socket};c.prependOnceListener("lookup",f),l0t.default(c,{connect:()=>{e.connect=Date.now(),e.lookup===void 0&&(c.removeListener("lookup",f),e.lookup=e.connect,e.phases.dns=e.lookup-e.socket),e.phases.tcp=e.connect-e.lookup},secureConnect:()=>{e.secureConnect=Date.now(),e.phases.tls=e.secureConnect-e.connect}})};t.socket?s(t.socket):t.prependOnceListener("socket",s);let a=()=>{var c;e.upload=Date.now(),e.phases.request=e.upload-(c=e.secureConnect,c??e.connect)};return(typeof t.writableFinished=="boolean"?t.writableFinished:t.finished&&t.outputSize===0&&(!t.socket||t.socket.writableLength===0))?a():t.prependOnceListener("finish",a),t.prependOnceListener("response",c=>{e.response=Date.now(),e.phases.firstByte=e.response-e.upload,c.timings=e,r(c),c.prependOnceListener("end",()=>{e.end=Date.now(),e.phases.download=e.end-e.response,e.phases.total=e.end-e.start})}),e};wH.default=CH;BH.exports=CH;BH.exports.default=CH});var A0e=L((gzt,DH)=>{"use strict";var{V4MAPPED:u0t,ADDRCONFIG:f0t,ALL:f0e,promises:{Resolver:o0e},lookup:A0t}=ye("dns"),{promisify:vH}=ye("util"),p0t=ye("os"),gI=Symbol("cacheableLookupCreateConnection"),SH=Symbol("cacheableLookupInstance"),a0e=Symbol("expires"),h0t=typeof f0e=="number",l0e=t=>{if(!(t&&typeof t.createConnection=="function"))throw new Error("Expected an Agent instance as the first argument")},g0t=t=>{for(let e of t)e.family!==6&&(e.address=`::ffff:${e.address}`,e.family=6)},c0e=()=>{let t=!1,e=!1;for(let r of Object.values(p0t.networkInterfaces()))for(let s of r)if(!s.internal&&(s.family==="IPv6"?e=!0:t=!0,t&&e))return{has4:t,has6:e};return{has4:t,has6:e}},d0t=t=>Symbol.iterator in t,u0e={ttl:!0},m0t={all:!0},HQ=class{constructor({cache:e=new Map,maxTtl:r=1/0,fallbackDuration:s=3600,errorTtl:a=.15,resolver:n=new o0e,lookup:c=A0t}={}){if(this.maxTtl=r,this.errorTtl=a,this._cache=e,this._resolver=n,this._dnsLookup=vH(c),this._resolver instanceof o0e?(this._resolve4=this._resolver.resolve4.bind(this._resolver),this._resolve6=this._resolver.resolve6.bind(this._resolver)):(this._resolve4=vH(this._resolver.resolve4.bind(this._resolver)),this._resolve6=vH(this._resolver.resolve6.bind(this._resolver))),this._iface=c0e(),this._pending={},this._nextRemovalTime=!1,this._hostnamesToFallback=new Set,s<1)this._fallback=!1;else{this._fallback=!0;let f=setInterval(()=>{this._hostnamesToFallback.clear()},s*1e3);f.unref&&f.unref()}this.lookup=this.lookup.bind(this),this.lookupAsync=this.lookupAsync.bind(this)}set servers(e){this.clear(),this._resolver.setServers(e)}get servers(){return this._resolver.getServers()}lookup(e,r,s){if(typeof r=="function"?(s=r,r={}):typeof r=="number"&&(r={family:r}),!s)throw new Error("Callback must be a function.");this.lookupAsync(e,r).then(a=>{r.all?s(null,a):s(null,a.address,a.family,a.expires,a.ttl)},s)}async lookupAsync(e,r={}){typeof r=="number"&&(r={family:r});let s=await this.query(e);if(r.family===6){let a=s.filter(n=>n.family===6);r.hints&u0t&&(h0t&&r.hints&f0e||a.length===0)?g0t(s):s=a}else r.family===4&&(s=s.filter(a=>a.family===4));if(r.hints&f0t){let{_iface:a}=this;s=s.filter(n=>n.family===6?a.has6:a.has4)}if(s.length===0){let a=new Error(`cacheableLookup ENOTFOUND ${e}`);throw a.code="ENOTFOUND",a.hostname=e,a}return r.all?s:s[0]}async query(e){let r=await this._cache.get(e);if(!r){let s=this._pending[e];if(s)r=await s;else{let a=this.queryAndCache(e);this._pending[e]=a,r=await a}}return r=r.map(s=>({...s})),r}async _resolve(e){let r=async h=>{try{return await h}catch(E){if(E.code==="ENODATA"||E.code==="ENOTFOUND")return[];throw E}},[s,a]=await Promise.all([this._resolve4(e,u0e),this._resolve6(e,u0e)].map(h=>r(h))),n=0,c=0,f=0,p=Date.now();for(let h of s)h.family=4,h.expires=p+h.ttl*1e3,n=Math.max(n,h.ttl);for(let h of a)h.family=6,h.expires=p+h.ttl*1e3,c=Math.max(c,h.ttl);return s.length>0?a.length>0?f=Math.min(n,c):f=n:f=c,{entries:[...s,...a],cacheTtl:f}}async _lookup(e){try{return{entries:await this._dnsLookup(e,{all:!0}),cacheTtl:0}}catch{return{entries:[],cacheTtl:0}}}async _set(e,r,s){if(this.maxTtl>0&&s>0){s=Math.min(s,this.maxTtl)*1e3,r[a0e]=Date.now()+s;try{await this._cache.set(e,r,s)}catch(a){this.lookupAsync=async()=>{let n=new Error("Cache Error. Please recreate the CacheableLookup instance.");throw n.cause=a,n}}d0t(this._cache)&&this._tick(s)}}async queryAndCache(e){if(this._hostnamesToFallback.has(e))return this._dnsLookup(e,m0t);try{let r=await this._resolve(e);r.entries.length===0&&this._fallback&&(r=await this._lookup(e),r.entries.length!==0&&this._hostnamesToFallback.add(e));let s=r.entries.length===0?this.errorTtl:r.cacheTtl;return await this._set(e,r.entries,s),delete this._pending[e],r.entries}catch(r){throw delete this._pending[e],r}}_tick(e){let r=this._nextRemovalTime;(!r||e{this._nextRemovalTime=!1;let s=1/0,a=Date.now();for(let[n,c]of this._cache){let f=c[a0e];a>=f?this._cache.delete(n):f("lookup"in r||(r.lookup=this.lookup),e[gI](r,s))}uninstall(e){if(l0e(e),e[gI]){if(e[SH]!==this)throw new Error("The agent is not owned by this CacheableLookup instance");e.createConnection=e[gI],delete e[gI],delete e[SH]}}updateInterfaceInfo(){let{_iface:e}=this;this._iface=c0e(),(e.has4&&!this._iface.has4||e.has6&&!this._iface.has6)&&this._cache.clear()}clear(e){if(e){this._cache.delete(e);return}this._cache.clear()}};DH.exports=HQ;DH.exports.default=HQ});var g0e=L((dzt,bH)=>{"use strict";var y0t=typeof URL>"u"?ye("url").URL:URL,E0t="text/plain",I0t="us-ascii",p0e=(t,e)=>e.some(r=>r instanceof RegExp?r.test(t):r===t),C0t=(t,{stripHash:e})=>{let r=t.match(/^data:([^,]*?),([^#]*?)(?:#(.*))?$/);if(!r)throw new Error(`Invalid URL: ${t}`);let s=r[1].split(";"),a=r[2],n=e?"":r[3],c=!1;s[s.length-1]==="base64"&&(s.pop(),c=!0);let f=(s.shift()||"").toLowerCase(),h=[...s.map(E=>{let[C,S=""]=E.split("=").map(P=>P.trim());return C==="charset"&&(S=S.toLowerCase(),S===I0t)?"":`${C}${S?`=${S}`:""}`}).filter(Boolean)];return c&&h.push("base64"),(h.length!==0||f&&f!==E0t)&&h.unshift(f),`data:${h.join(";")},${c?a.trim():a}${n?`#${n}`:""}`},h0e=(t,e)=>{if(e={defaultProtocol:"http:",normalizeProtocol:!0,forceHttp:!1,forceHttps:!1,stripAuthentication:!0,stripHash:!1,stripWWW:!0,removeQueryParameters:[/^utm_\w+/i],removeTrailingSlash:!0,removeDirectoryIndex:!1,sortQueryParameters:!0,...e},Reflect.has(e,"normalizeHttps"))throw new Error("options.normalizeHttps is renamed to options.forceHttp");if(Reflect.has(e,"normalizeHttp"))throw new Error("options.normalizeHttp is renamed to options.forceHttps");if(Reflect.has(e,"stripFragment"))throw new Error("options.stripFragment is renamed to options.stripHash");if(t=t.trim(),/^data:/i.test(t))return C0t(t,e);let r=t.startsWith("//");!r&&/^\.*\//.test(t)||(t=t.replace(/^(?!(?:\w+:)?\/\/)|^\/\//,e.defaultProtocol));let a=new y0t(t);if(e.forceHttp&&e.forceHttps)throw new Error("The `forceHttp` and `forceHttps` options cannot be used together");if(e.forceHttp&&a.protocol==="https:"&&(a.protocol="http:"),e.forceHttps&&a.protocol==="http:"&&(a.protocol="https:"),e.stripAuthentication&&(a.username="",a.password=""),e.stripHash&&(a.hash=""),a.pathname&&(a.pathname=a.pathname.replace(/((?!:).|^)\/{2,}/g,(n,c)=>/^(?!\/)/g.test(c)?`${c}/`:"/")),a.pathname&&(a.pathname=decodeURI(a.pathname)),e.removeDirectoryIndex===!0&&(e.removeDirectoryIndex=[/^index\.[a-z]+$/]),Array.isArray(e.removeDirectoryIndex)&&e.removeDirectoryIndex.length>0){let n=a.pathname.split("/"),c=n[n.length-1];p0e(c,e.removeDirectoryIndex)&&(n=n.slice(0,n.length-1),a.pathname=n.slice(1).join("/")+"/")}if(a.hostname&&(a.hostname=a.hostname.replace(/\.$/,""),e.stripWWW&&/^www\.([a-z\-\d]{2,63})\.([a-z.]{2,5})$/.test(a.hostname)&&(a.hostname=a.hostname.replace(/^www\./,""))),Array.isArray(e.removeQueryParameters))for(let n of[...a.searchParams.keys()])p0e(n,e.removeQueryParameters)&&a.searchParams.delete(n);return e.sortQueryParameters&&a.searchParams.sort(),e.removeTrailingSlash&&(a.pathname=a.pathname.replace(/\/$/,"")),t=a.toString(),(e.removeTrailingSlash||a.pathname==="/")&&a.hash===""&&(t=t.replace(/\/$/,"")),r&&!e.normalizeProtocol&&(t=t.replace(/^http:\/\//,"//")),e.stripProtocol&&(t=t.replace(/^(?:https?:)?\/\//,"")),t};bH.exports=h0e;bH.exports.default=h0e});var y0e=L((mzt,m0e)=>{m0e.exports=d0e;function d0e(t,e){if(t&&e)return d0e(t)(e);if(typeof t!="function")throw new TypeError("need wrapper function");return Object.keys(t).forEach(function(s){r[s]=t[s]}),r;function r(){for(var s=new Array(arguments.length),a=0;a{var E0e=y0e();PH.exports=E0e(jQ);PH.exports.strict=E0e(I0e);jQ.proto=jQ(function(){Object.defineProperty(Function.prototype,"once",{value:function(){return jQ(this)},configurable:!0}),Object.defineProperty(Function.prototype,"onceStrict",{value:function(){return I0e(this)},configurable:!0})});function jQ(t){var e=function(){return e.called?e.value:(e.called=!0,e.value=t.apply(this,arguments))};return e.called=!1,e}function I0e(t){var e=function(){if(e.called)throw new Error(e.onceError);return e.called=!0,e.value=t.apply(this,arguments)},r=t.name||"Function wrapped with `once`";return e.onceError=r+" shouldn't be called more than once",e.called=!1,e}});var kH=L((Ezt,w0e)=>{var w0t=xH(),B0t=function(){},v0t=function(t){return t.setHeader&&typeof t.abort=="function"},S0t=function(t){return t.stdio&&Array.isArray(t.stdio)&&t.stdio.length===3},C0e=function(t,e,r){if(typeof e=="function")return C0e(t,null,e);e||(e={}),r=w0t(r||B0t);var s=t._writableState,a=t._readableState,n=e.readable||e.readable!==!1&&t.readable,c=e.writable||e.writable!==!1&&t.writable,f=function(){t.writable||p()},p=function(){c=!1,n||r.call(t)},h=function(){n=!1,c||r.call(t)},E=function(I){r.call(t,I?new Error("exited with error code: "+I):null)},C=function(I){r.call(t,I)},S=function(){if(n&&!(a&&a.ended))return r.call(t,new Error("premature close"));if(c&&!(s&&s.ended))return r.call(t,new Error("premature close"))},P=function(){t.req.on("finish",p)};return v0t(t)?(t.on("complete",p),t.on("abort",S),t.req?P():t.on("request",P)):c&&!s&&(t.on("end",f),t.on("close",f)),S0t(t)&&t.on("exit",E),t.on("end",h),t.on("finish",p),e.error!==!1&&t.on("error",C),t.on("close",S),function(){t.removeListener("complete",p),t.removeListener("abort",S),t.removeListener("request",P),t.req&&t.req.removeListener("finish",p),t.removeListener("end",f),t.removeListener("close",f),t.removeListener("finish",p),t.removeListener("exit",E),t.removeListener("end",h),t.removeListener("error",C),t.removeListener("close",S)}};w0e.exports=C0e});var S0e=L((Izt,v0e)=>{var D0t=xH(),b0t=kH(),QH=ye("fs"),av=function(){},P0t=/^v?\.0/.test(process.version),qQ=function(t){return typeof t=="function"},x0t=function(t){return!P0t||!QH?!1:(t instanceof(QH.ReadStream||av)||t instanceof(QH.WriteStream||av))&&qQ(t.close)},k0t=function(t){return t.setHeader&&qQ(t.abort)},Q0t=function(t,e,r,s){s=D0t(s);var a=!1;t.on("close",function(){a=!0}),b0t(t,{readable:e,writable:r},function(c){if(c)return s(c);a=!0,s()});var n=!1;return function(c){if(!a&&!n){if(n=!0,x0t(t))return t.close(av);if(k0t(t))return t.abort();if(qQ(t.destroy))return t.destroy();s(c||new Error("stream was destroyed"))}}},B0e=function(t){t()},T0t=function(t,e){return t.pipe(e)},R0t=function(){var t=Array.prototype.slice.call(arguments),e=qQ(t[t.length-1]||av)&&t.pop()||av;if(Array.isArray(t[0])&&(t=t[0]),t.length<2)throw new Error("pump requires two streams per minimum");var r,s=t.map(function(a,n){var c=n0;return Q0t(a,c,f,function(p){r||(r=p),p&&s.forEach(B0e),!c&&(s.forEach(B0e),e(r))})});return t.reduce(T0t)};v0e.exports=R0t});var b0e=L((Czt,D0e)=>{"use strict";var{PassThrough:F0t}=ye("stream");D0e.exports=t=>{t={...t};let{array:e}=t,{encoding:r}=t,s=r==="buffer",a=!1;e?a=!(r||s):r=r||"utf8",s&&(r=null);let n=new F0t({objectMode:a});r&&n.setEncoding(r);let c=0,f=[];return n.on("data",p=>{f.push(p),a?c=f.length:c+=p.length}),n.getBufferedValue=()=>e?f:s?Buffer.concat(f,c):f.join(""),n.getBufferedLength=()=>c,n}});var P0e=L((wzt,dI)=>{"use strict";var N0t=S0e(),O0t=b0e(),GQ=class extends Error{constructor(){super("maxBuffer exceeded"),this.name="MaxBufferError"}};async function WQ(t,e){if(!t)return Promise.reject(new Error("Expected a stream"));e={maxBuffer:1/0,...e};let{maxBuffer:r}=e,s;return await new Promise((a,n)=>{let c=f=>{f&&(f.bufferedData=s.getBufferedValue()),n(f)};s=N0t(t,O0t(e),f=>{if(f){c(f);return}a()}),s.on("data",()=>{s.getBufferedLength()>r&&c(new GQ)})}),s.getBufferedValue()}dI.exports=WQ;dI.exports.default=WQ;dI.exports.buffer=(t,e)=>WQ(t,{...e,encoding:"buffer"});dI.exports.array=(t,e)=>WQ(t,{...e,array:!0});dI.exports.MaxBufferError=GQ});var k0e=L((vzt,x0e)=>{"use strict";var L0t=new Set([200,203,204,206,300,301,308,404,405,410,414,501]),M0t=new Set([200,203,204,300,301,302,303,307,308,404,405,410,414,501]),_0t=new Set([500,502,503,504]),U0t={date:!0,connection:!0,"keep-alive":!0,"proxy-authenticate":!0,"proxy-authorization":!0,te:!0,trailer:!0,"transfer-encoding":!0,upgrade:!0},H0t={"content-length":!0,"content-encoding":!0,"transfer-encoding":!0,"content-range":!0};function nm(t){let e=parseInt(t,10);return isFinite(e)?e:0}function j0t(t){return t?_0t.has(t.status):!0}function TH(t){let e={};if(!t)return e;let r=t.trim().split(/,/);for(let s of r){let[a,n]=s.split(/=/,2);e[a.trim()]=n===void 0?!0:n.trim().replace(/^"|"$/g,"")}return e}function q0t(t){let e=[];for(let r in t){let s=t[r];e.push(s===!0?r:r+"="+s)}if(e.length)return e.join(", ")}x0e.exports=class{constructor(e,r,{shared:s,cacheHeuristic:a,immutableMinTimeToLive:n,ignoreCargoCult:c,_fromObject:f}={}){if(f){this._fromObject(f);return}if(!r||!r.headers)throw Error("Response headers missing");this._assertRequestHasHeaders(e),this._responseTime=this.now(),this._isShared=s!==!1,this._cacheHeuristic=a!==void 0?a:.1,this._immutableMinTtl=n!==void 0?n:24*3600*1e3,this._status="status"in r?r.status:200,this._resHeaders=r.headers,this._rescc=TH(r.headers["cache-control"]),this._method="method"in e?e.method:"GET",this._url=e.url,this._host=e.headers.host,this._noAuthorization=!e.headers.authorization,this._reqHeaders=r.headers.vary?e.headers:null,this._reqcc=TH(e.headers["cache-control"]),c&&"pre-check"in this._rescc&&"post-check"in this._rescc&&(delete this._rescc["pre-check"],delete this._rescc["post-check"],delete this._rescc["no-cache"],delete this._rescc["no-store"],delete this._rescc["must-revalidate"],this._resHeaders=Object.assign({},this._resHeaders,{"cache-control":q0t(this._rescc)}),delete this._resHeaders.expires,delete this._resHeaders.pragma),r.headers["cache-control"]==null&&/no-cache/.test(r.headers.pragma)&&(this._rescc["no-cache"]=!0)}now(){return Date.now()}storable(){return!!(!this._reqcc["no-store"]&&(this._method==="GET"||this._method==="HEAD"||this._method==="POST"&&this._hasExplicitExpiration())&&M0t.has(this._status)&&!this._rescc["no-store"]&&(!this._isShared||!this._rescc.private)&&(!this._isShared||this._noAuthorization||this._allowsStoringAuthenticated())&&(this._resHeaders.expires||this._rescc["max-age"]||this._isShared&&this._rescc["s-maxage"]||this._rescc.public||L0t.has(this._status)))}_hasExplicitExpiration(){return this._isShared&&this._rescc["s-maxage"]||this._rescc["max-age"]||this._resHeaders.expires}_assertRequestHasHeaders(e){if(!e||!e.headers)throw Error("Request headers missing")}satisfiesWithoutRevalidation(e){this._assertRequestHasHeaders(e);let r=TH(e.headers["cache-control"]);return r["no-cache"]||/no-cache/.test(e.headers.pragma)||r["max-age"]&&this.age()>r["max-age"]||r["min-fresh"]&&this.timeToLive()<1e3*r["min-fresh"]||this.stale()&&!(r["max-stale"]&&!this._rescc["must-revalidate"]&&(r["max-stale"]===!0||r["max-stale"]>this.age()-this.maxAge()))?!1:this._requestMatches(e,!1)}_requestMatches(e,r){return(!this._url||this._url===e.url)&&this._host===e.headers.host&&(!e.method||this._method===e.method||r&&e.method==="HEAD")&&this._varyMatches(e)}_allowsStoringAuthenticated(){return this._rescc["must-revalidate"]||this._rescc.public||this._rescc["s-maxage"]}_varyMatches(e){if(!this._resHeaders.vary)return!0;if(this._resHeaders.vary==="*")return!1;let r=this._resHeaders.vary.trim().toLowerCase().split(/\s*,\s*/);for(let s of r)if(e.headers[s]!==this._reqHeaders[s])return!1;return!0}_copyWithoutHopByHopHeaders(e){let r={};for(let s in e)U0t[s]||(r[s]=e[s]);if(e.connection){let s=e.connection.trim().split(/\s*,\s*/);for(let a of s)delete r[a]}if(r.warning){let s=r.warning.split(/,/).filter(a=>!/^\s*1[0-9][0-9]/.test(a));s.length?r.warning=s.join(",").trim():delete r.warning}return r}responseHeaders(){let e=this._copyWithoutHopByHopHeaders(this._resHeaders),r=this.age();return r>3600*24&&!this._hasExplicitExpiration()&&this.maxAge()>3600*24&&(e.warning=(e.warning?`${e.warning}, `:"")+'113 - "rfc7234 5.5.4"'),e.age=`${Math.round(r)}`,e.date=new Date(this.now()).toUTCString(),e}date(){let e=Date.parse(this._resHeaders.date);return isFinite(e)?e:this._responseTime}age(){let e=this._ageValue(),r=(this.now()-this._responseTime)/1e3;return e+r}_ageValue(){return nm(this._resHeaders.age)}maxAge(){if(!this.storable()||this._rescc["no-cache"]||this._isShared&&this._resHeaders["set-cookie"]&&!this._rescc.public&&!this._rescc.immutable||this._resHeaders.vary==="*")return 0;if(this._isShared){if(this._rescc["proxy-revalidate"])return 0;if(this._rescc["s-maxage"])return nm(this._rescc["s-maxage"])}if(this._rescc["max-age"])return nm(this._rescc["max-age"]);let e=this._rescc.immutable?this._immutableMinTtl:0,r=this.date();if(this._resHeaders.expires){let s=Date.parse(this._resHeaders.expires);return Number.isNaN(s)||ss)return Math.max(e,(r-s)/1e3*this._cacheHeuristic)}return e}timeToLive(){let e=this.maxAge()-this.age(),r=e+nm(this._rescc["stale-if-error"]),s=e+nm(this._rescc["stale-while-revalidate"]);return Math.max(0,e,r,s)*1e3}stale(){return this.maxAge()<=this.age()}_useStaleIfError(){return this.maxAge()+nm(this._rescc["stale-if-error"])>this.age()}useStaleWhileRevalidate(){return this.maxAge()+nm(this._rescc["stale-while-revalidate"])>this.age()}static fromObject(e){return new this(void 0,void 0,{_fromObject:e})}_fromObject(e){if(this._responseTime)throw Error("Reinitialized");if(!e||e.v!==1)throw Error("Invalid serialization");this._responseTime=e.t,this._isShared=e.sh,this._cacheHeuristic=e.ch,this._immutableMinTtl=e.imm!==void 0?e.imm:24*3600*1e3,this._status=e.st,this._resHeaders=e.resh,this._rescc=e.rescc,this._method=e.m,this._url=e.u,this._host=e.h,this._noAuthorization=e.a,this._reqHeaders=e.reqh,this._reqcc=e.reqcc}toObject(){return{v:1,t:this._responseTime,sh:this._isShared,ch:this._cacheHeuristic,imm:this._immutableMinTtl,st:this._status,resh:this._resHeaders,rescc:this._rescc,m:this._method,u:this._url,h:this._host,a:this._noAuthorization,reqh:this._reqHeaders,reqcc:this._reqcc}}revalidationHeaders(e){this._assertRequestHasHeaders(e);let r=this._copyWithoutHopByHopHeaders(e.headers);if(delete r["if-range"],!this._requestMatches(e,!0)||!this.storable())return delete r["if-none-match"],delete r["if-modified-since"],r;if(this._resHeaders.etag&&(r["if-none-match"]=r["if-none-match"]?`${r["if-none-match"]}, ${this._resHeaders.etag}`:this._resHeaders.etag),r["accept-ranges"]||r["if-match"]||r["if-unmodified-since"]||this._method&&this._method!="GET"){if(delete r["if-modified-since"],r["if-none-match"]){let a=r["if-none-match"].split(/,/).filter(n=>!/^\s*W\//.test(n));a.length?r["if-none-match"]=a.join(",").trim():delete r["if-none-match"]}}else this._resHeaders["last-modified"]&&!r["if-modified-since"]&&(r["if-modified-since"]=this._resHeaders["last-modified"]);return r}revalidatedPolicy(e,r){if(this._assertRequestHasHeaders(e),this._useStaleIfError()&&j0t(r))return{modified:!1,matches:!1,policy:this};if(!r||!r.headers)throw Error("Response headers missing");let s=!1;if(r.status!==void 0&&r.status!=304?s=!1:r.headers.etag&&!/^\s*W\//.test(r.headers.etag)?s=this._resHeaders.etag&&this._resHeaders.etag.replace(/^\s*W\//,"")===r.headers.etag:this._resHeaders.etag&&r.headers.etag?s=this._resHeaders.etag.replace(/^\s*W\//,"")===r.headers.etag.replace(/^\s*W\//,""):this._resHeaders["last-modified"]?s=this._resHeaders["last-modified"]===r.headers["last-modified"]:!this._resHeaders.etag&&!this._resHeaders["last-modified"]&&!r.headers.etag&&!r.headers["last-modified"]&&(s=!0),!s)return{policy:new this.constructor(e,r),modified:r.status!=304,matches:!1};let a={};for(let c in this._resHeaders)a[c]=c in r.headers&&!H0t[c]?r.headers[c]:this._resHeaders[c];let n=Object.assign({},r,{status:this._status,method:this._method,headers:a});return{policy:new this.constructor(e,n,{shared:this._isShared,cacheHeuristic:this._cacheHeuristic,immutableMinTimeToLive:this._immutableMinTtl}),modified:!1,matches:!0}}}});var YQ=L((Szt,Q0e)=>{"use strict";Q0e.exports=t=>{let e={};for(let[r,s]of Object.entries(t))e[r.toLowerCase()]=s;return e}});var R0e=L((Dzt,T0e)=>{"use strict";var G0t=ye("stream").Readable,W0t=YQ(),RH=class extends G0t{constructor(e,r,s,a){if(typeof e!="number")throw new TypeError("Argument `statusCode` should be a number");if(typeof r!="object")throw new TypeError("Argument `headers` should be an object");if(!(s instanceof Buffer))throw new TypeError("Argument `body` should be a buffer");if(typeof a!="string")throw new TypeError("Argument `url` should be a string");super(),this.statusCode=e,this.headers=W0t(r),this.body=s,this.url=a}_read(){this.push(this.body),this.push(null)}};T0e.exports=RH});var N0e=L((bzt,F0e)=>{"use strict";var Y0t=["destroy","setTimeout","socket","headers","trailers","rawHeaders","statusCode","httpVersion","httpVersionMinor","httpVersionMajor","rawTrailers","statusMessage"];F0e.exports=(t,e)=>{let r=new Set(Object.keys(t).concat(Y0t));for(let s of r)s in e||(e[s]=typeof t[s]=="function"?t[s].bind(t):t[s])}});var L0e=L((Pzt,O0e)=>{"use strict";var V0t=ye("stream").PassThrough,K0t=N0e(),J0t=t=>{if(!(t&&t.pipe))throw new TypeError("Parameter `response` must be a response stream.");let e=new V0t;return K0t(t,e),t.pipe(e)};O0e.exports=J0t});var M0e=L(FH=>{FH.stringify=function t(e){if(typeof e>"u")return e;if(e&&Buffer.isBuffer(e))return JSON.stringify(":base64:"+e.toString("base64"));if(e&&e.toJSON&&(e=e.toJSON()),e&&typeof e=="object"){var r="",s=Array.isArray(e);r=s?"[":"{";var a=!0;for(var n in e){var c=typeof e[n]=="function"||!s&&typeof e[n]>"u";Object.hasOwnProperty.call(e,n)&&!c&&(a||(r+=","),a=!1,s?e[n]==null?r+="null":r+=t(e[n]):e[n]!==void 0&&(r+=t(n)+":"+t(e[n])))}return r+=s?"]":"}",r}else return typeof e=="string"?JSON.stringify(/^:/.test(e)?":"+e:e):typeof e>"u"?"null":JSON.stringify(e)};FH.parse=function(t){return JSON.parse(t,function(e,r){return typeof r=="string"?/^:base64:/.test(r)?Buffer.from(r.substring(8),"base64"):/^:/.test(r)?r.substring(1):r:r})}});var j0e=L((kzt,H0e)=>{"use strict";var z0t=ye("events"),_0e=M0e(),Z0t=t=>{let e={redis:"@keyv/redis",rediss:"@keyv/redis",mongodb:"@keyv/mongo",mongo:"@keyv/mongo",sqlite:"@keyv/sqlite",postgresql:"@keyv/postgres",postgres:"@keyv/postgres",mysql:"@keyv/mysql",etcd:"@keyv/etcd",offline:"@keyv/offline",tiered:"@keyv/tiered"};if(t.adapter||t.uri){let r=t.adapter||/^[^:+]*/.exec(t.uri)[0];return new(ye(e[r]))(t)}return new Map},U0e=["sqlite","postgres","mysql","mongo","redis","tiered"],NH=class extends z0t{constructor(e,{emitErrors:r=!0,...s}={}){if(super(),this.opts={namespace:"keyv",serialize:_0e.stringify,deserialize:_0e.parse,...typeof e=="string"?{uri:e}:e,...s},!this.opts.store){let n={...this.opts};this.opts.store=Z0t(n)}if(this.opts.compression){let n=this.opts.compression;this.opts.serialize=n.serialize.bind(n),this.opts.deserialize=n.deserialize.bind(n)}typeof this.opts.store.on=="function"&&r&&this.opts.store.on("error",n=>this.emit("error",n)),this.opts.store.namespace=this.opts.namespace;let a=n=>async function*(){for await(let[c,f]of typeof n=="function"?n(this.opts.store.namespace):n){let p=await this.opts.deserialize(f);if(!(this.opts.store.namespace&&!c.includes(this.opts.store.namespace))){if(typeof p.expires=="number"&&Date.now()>p.expires){this.delete(c);continue}yield[this._getKeyUnprefix(c),p.value]}}};typeof this.opts.store[Symbol.iterator]=="function"&&this.opts.store instanceof Map?this.iterator=a(this.opts.store):typeof this.opts.store.iterator=="function"&&this.opts.store.opts&&this._checkIterableAdaptar()&&(this.iterator=a(this.opts.store.iterator.bind(this.opts.store)))}_checkIterableAdaptar(){return U0e.includes(this.opts.store.opts.dialect)||U0e.findIndex(e=>this.opts.store.opts.url.includes(e))>=0}_getKeyPrefix(e){return`${this.opts.namespace}:${e}`}_getKeyPrefixArray(e){return e.map(r=>`${this.opts.namespace}:${r}`)}_getKeyUnprefix(e){return e.split(":").splice(1).join(":")}get(e,r){let{store:s}=this.opts,a=Array.isArray(e),n=a?this._getKeyPrefixArray(e):this._getKeyPrefix(e);if(a&&s.getMany===void 0){let c=[];for(let f of n)c.push(Promise.resolve().then(()=>s.get(f)).then(p=>typeof p=="string"?this.opts.deserialize(p):this.opts.compression?this.opts.deserialize(p):p).then(p=>{if(p!=null)return typeof p.expires=="number"&&Date.now()>p.expires?this.delete(f).then(()=>{}):r&&r.raw?p:p.value}));return Promise.allSettled(c).then(f=>{let p=[];for(let h of f)p.push(h.value);return p})}return Promise.resolve().then(()=>a?s.getMany(n):s.get(n)).then(c=>typeof c=="string"?this.opts.deserialize(c):this.opts.compression?this.opts.deserialize(c):c).then(c=>{if(c!=null)return a?c.map((f,p)=>{if(typeof f=="string"&&(f=this.opts.deserialize(f)),f!=null){if(typeof f.expires=="number"&&Date.now()>f.expires){this.delete(e[p]).then(()=>{});return}return r&&r.raw?f:f.value}}):typeof c.expires=="number"&&Date.now()>c.expires?this.delete(e).then(()=>{}):r&&r.raw?c:c.value})}set(e,r,s){let a=this._getKeyPrefix(e);typeof s>"u"&&(s=this.opts.ttl),s===0&&(s=void 0);let{store:n}=this.opts;return Promise.resolve().then(()=>{let c=typeof s=="number"?Date.now()+s:null;return typeof r=="symbol"&&this.emit("error","symbol cannot be serialized"),r={value:r,expires:c},this.opts.serialize(r)}).then(c=>n.set(a,c,s)).then(()=>!0)}delete(e){let{store:r}=this.opts;if(Array.isArray(e)){let a=this._getKeyPrefixArray(e);if(r.deleteMany===void 0){let n=[];for(let c of a)n.push(r.delete(c));return Promise.allSettled(n).then(c=>c.every(f=>f.value===!0))}return Promise.resolve().then(()=>r.deleteMany(a))}let s=this._getKeyPrefix(e);return Promise.resolve().then(()=>r.delete(s))}clear(){let{store:e}=this.opts;return Promise.resolve().then(()=>e.clear())}has(e){let r=this._getKeyPrefix(e),{store:s}=this.opts;return Promise.resolve().then(async()=>typeof s.has=="function"?s.has(r):await s.get(r)!==void 0)}disconnect(){let{store:e}=this.opts;if(typeof e.disconnect=="function")return e.disconnect()}};H0e.exports=NH});var W0e=L((Tzt,G0e)=>{"use strict";var X0t=ye("events"),VQ=ye("url"),$0t=g0e(),egt=P0e(),OH=k0e(),q0e=R0e(),tgt=YQ(),rgt=L0e(),ngt=j0e(),lv=class t{constructor(e,r){if(typeof e!="function")throw new TypeError("Parameter `request` must be a function");return this.cache=new ngt({uri:typeof r=="string"&&r,store:typeof r!="string"&&r,namespace:"cacheable-request"}),this.createCacheableRequest(e)}createCacheableRequest(e){return(r,s)=>{let a;if(typeof r=="string")a=LH(VQ.parse(r)),r={};else if(r instanceof VQ.URL)a=LH(VQ.parse(r.toString())),r={};else{let[C,...S]=(r.path||"").split("?"),P=S.length>0?`?${S.join("?")}`:"";a=LH({...r,pathname:C,search:P})}r={headers:{},method:"GET",cache:!0,strictTtl:!1,automaticFailover:!1,...r,...igt(a)},r.headers=tgt(r.headers);let n=new X0t,c=$0t(VQ.format(a),{stripWWW:!1,removeTrailingSlash:!1,stripAuthentication:!1}),f=`${r.method}:${c}`,p=!1,h=!1,E=C=>{h=!0;let S=!1,P,I=new Promise(N=>{P=()=>{S||(S=!0,N())}}),R=N=>{if(p&&!C.forceRefresh){N.status=N.statusCode;let W=OH.fromObject(p.cachePolicy).revalidatedPolicy(C,N);if(!W.modified){let te=W.policy.responseHeaders();N=new q0e(p.statusCode,te,p.body,p.url),N.cachePolicy=W.policy,N.fromCache=!0}}N.fromCache||(N.cachePolicy=new OH(C,N,C),N.fromCache=!1);let U;C.cache&&N.cachePolicy.storable()?(U=rgt(N),(async()=>{try{let W=egt.buffer(N);if(await Promise.race([I,new Promise(ce=>N.once("end",ce))]),S)return;let te=await W,ie={cachePolicy:N.cachePolicy.toObject(),url:N.url,statusCode:N.fromCache?p.statusCode:N.statusCode,body:te},Ae=C.strictTtl?N.cachePolicy.timeToLive():void 0;C.maxTtl&&(Ae=Ae?Math.min(Ae,C.maxTtl):C.maxTtl),await this.cache.set(f,ie,Ae)}catch(W){n.emit("error",new t.CacheError(W))}})()):C.cache&&p&&(async()=>{try{await this.cache.delete(f)}catch(W){n.emit("error",new t.CacheError(W))}})(),n.emit("response",U||N),typeof s=="function"&&s(U||N)};try{let N=e(C,R);N.once("error",P),N.once("abort",P),n.emit("request",N)}catch(N){n.emit("error",new t.RequestError(N))}};return(async()=>{let C=async P=>{await Promise.resolve();let I=P.cache?await this.cache.get(f):void 0;if(typeof I>"u")return E(P);let R=OH.fromObject(I.cachePolicy);if(R.satisfiesWithoutRevalidation(P)&&!P.forceRefresh){let N=R.responseHeaders(),U=new q0e(I.statusCode,N,I.body,I.url);U.cachePolicy=R,U.fromCache=!0,n.emit("response",U),typeof s=="function"&&s(U)}else p=I,P.headers=R.revalidationHeaders(P),E(P)},S=P=>n.emit("error",new t.CacheError(P));this.cache.once("error",S),n.on("response",()=>this.cache.removeListener("error",S));try{await C(r)}catch(P){r.automaticFailover&&!h&&E(r),n.emit("error",new t.CacheError(P))}})(),n}}};function igt(t){let e={...t};return e.path=`${t.pathname||"/"}${t.search||""}`,delete e.pathname,delete e.search,e}function LH(t){return{protocol:t.protocol,auth:t.auth,hostname:t.hostname||t.host||"localhost",port:t.port,pathname:t.pathname,search:t.search}}lv.RequestError=class extends Error{constructor(t){super(t.message),this.name="RequestError",Object.assign(this,t)}};lv.CacheError=class extends Error{constructor(t){super(t.message),this.name="CacheError",Object.assign(this,t)}};G0e.exports=lv});var V0e=L((Nzt,Y0e)=>{"use strict";var sgt=["aborted","complete","headers","httpVersion","httpVersionMinor","httpVersionMajor","method","rawHeaders","rawTrailers","setTimeout","socket","statusCode","statusMessage","trailers","url"];Y0e.exports=(t,e)=>{if(e._readableState.autoDestroy)throw new Error("The second stream must have the `autoDestroy` option set to `false`");let r=new Set(Object.keys(t).concat(sgt)),s={};for(let a of r)a in e||(s[a]={get(){let n=t[a];return typeof n=="function"?n.bind(t):n},set(n){t[a]=n},enumerable:!0,configurable:!1});return Object.defineProperties(e,s),t.once("aborted",()=>{e.destroy(),e.emit("aborted")}),t.once("close",()=>{t.complete&&e.readable?e.once("end",()=>{e.emit("close")}):e.emit("close")}),e}});var J0e=L((Ozt,K0e)=>{"use strict";var{Transform:ogt,PassThrough:agt}=ye("stream"),MH=ye("zlib"),lgt=V0e();K0e.exports=t=>{let e=(t.headers["content-encoding"]||"").toLowerCase();if(!["gzip","deflate","br"].includes(e))return t;let r=e==="br";if(r&&typeof MH.createBrotliDecompress!="function")return t.destroy(new Error("Brotli is not supported on Node.js < 12")),t;let s=!0,a=new ogt({transform(f,p,h){s=!1,h(null,f)},flush(f){f()}}),n=new agt({autoDestroy:!1,destroy(f,p){t.destroy(),p(f)}}),c=r?MH.createBrotliDecompress():MH.createUnzip();return c.once("error",f=>{if(s&&!t.readable){n.end();return}n.destroy(f)}),lgt(t,n),t.pipe(a).pipe(c).pipe(n),n}});var UH=L((Lzt,z0e)=>{"use strict";var _H=class{constructor(e={}){if(!(e.maxSize&&e.maxSize>0))throw new TypeError("`maxSize` must be a number greater than 0");this.maxSize=e.maxSize,this.onEviction=e.onEviction,this.cache=new Map,this.oldCache=new Map,this._size=0}_set(e,r){if(this.cache.set(e,r),this._size++,this._size>=this.maxSize){if(this._size=0,typeof this.onEviction=="function")for(let[s,a]of this.oldCache.entries())this.onEviction(s,a);this.oldCache=this.cache,this.cache=new Map}}get(e){if(this.cache.has(e))return this.cache.get(e);if(this.oldCache.has(e)){let r=this.oldCache.get(e);return this.oldCache.delete(e),this._set(e,r),r}}set(e,r){return this.cache.has(e)?this.cache.set(e,r):this._set(e,r),this}has(e){return this.cache.has(e)||this.oldCache.has(e)}peek(e){if(this.cache.has(e))return this.cache.get(e);if(this.oldCache.has(e))return this.oldCache.get(e)}delete(e){let r=this.cache.delete(e);return r&&this._size--,this.oldCache.delete(e)||r}clear(){this.cache.clear(),this.oldCache.clear(),this._size=0}*keys(){for(let[e]of this)yield e}*values(){for(let[,e]of this)yield e}*[Symbol.iterator](){for(let e of this.cache)yield e;for(let e of this.oldCache){let[r]=e;this.cache.has(r)||(yield e)}}get size(){let e=0;for(let r of this.oldCache.keys())this.cache.has(r)||e++;return Math.min(this._size+e,this.maxSize)}};z0e.exports=_H});var jH=L((Mzt,ege)=>{"use strict";var cgt=ye("events"),ugt=ye("tls"),fgt=ye("http2"),Agt=UH(),xa=Symbol("currentStreamsCount"),Z0e=Symbol("request"),Nc=Symbol("cachedOriginSet"),mI=Symbol("gracefullyClosing"),pgt=["maxDeflateDynamicTableSize","maxSessionMemory","maxHeaderListPairs","maxOutstandingPings","maxReservedRemoteStreams","maxSendHeaderBlockLength","paddingStrategy","localAddress","path","rejectUnauthorized","minDHSize","ca","cert","clientCertEngine","ciphers","key","pfx","servername","minVersion","maxVersion","secureProtocol","crl","honorCipherOrder","ecdhCurve","dhparam","secureOptions","sessionIdContext"],hgt=(t,e,r)=>{let s=0,a=t.length;for(;s>>1;r(t[n],e)?s=n+1:a=n}return s},ggt=(t,e)=>t.remoteSettings.maxConcurrentStreams>e.remoteSettings.maxConcurrentStreams,HH=(t,e)=>{for(let r of t)r[Nc].lengthe[Nc].includes(s))&&r[xa]+e[xa]<=e.remoteSettings.maxConcurrentStreams&&$0e(r)},dgt=(t,e)=>{for(let r of t)e[Nc].lengthr[Nc].includes(s))&&e[xa]+r[xa]<=r.remoteSettings.maxConcurrentStreams&&$0e(e)},X0e=({agent:t,isFree:e})=>{let r={};for(let s in t.sessions){let n=t.sessions[s].filter(c=>{let f=c[im.kCurrentStreamsCount]{t[mI]=!0,t[xa]===0&&t.close()},im=class t extends cgt{constructor({timeout:e=6e4,maxSessions:r=1/0,maxFreeSessions:s=10,maxCachedTlsSessions:a=100}={}){super(),this.sessions={},this.queue={},this.timeout=e,this.maxSessions=r,this.maxFreeSessions=s,this._freeSessionsCount=0,this._sessionsCount=0,this.settings={enablePush:!1},this.tlsSessionCache=new Agt({maxSize:a})}static normalizeOrigin(e,r){return typeof e=="string"&&(e=new URL(e)),r&&e.hostname!==r&&(e.hostname=r),e.origin}normalizeOptions(e){let r="";if(e)for(let s of pgt)e[s]&&(r+=`:${e[s]}`);return r}_tryToCreateNewSession(e,r){if(!(e in this.queue)||!(r in this.queue[e]))return;let s=this.queue[e][r];this._sessionsCount{Array.isArray(s)?(s=[...s],a()):s=[{resolve:a,reject:n}];let c=this.normalizeOptions(r),f=t.normalizeOrigin(e,r&&r.servername);if(f===void 0){for(let{reject:E}of s)E(new TypeError("The `origin` argument needs to be a string or an URL object"));return}if(c in this.sessions){let E=this.sessions[c],C=-1,S=-1,P;for(let I of E){let R=I.remoteSettings.maxConcurrentStreams;if(R=R||I[mI]||I.destroyed)continue;P||(C=R),N>S&&(P=I,S=N)}}if(P){if(s.length!==1){for(let{reject:I}of s){let R=new Error(`Expected the length of listeners to be 1, got ${s.length}. +Please report this to https://github.com/szmarczak/http2-wrapper/`);I(R)}return}s[0].resolve(P);return}}if(c in this.queue){if(f in this.queue[c]){this.queue[c][f].listeners.push(...s),this._tryToCreateNewSession(c,f);return}}else this.queue[c]={};let p=()=>{c in this.queue&&this.queue[c][f]===h&&(delete this.queue[c][f],Object.keys(this.queue[c]).length===0&&delete this.queue[c])},h=()=>{let E=`${f}:${c}`,C=!1;try{let S=fgt.connect(e,{createConnection:this.createConnection,settings:this.settings,session:this.tlsSessionCache.get(E),...r});S[xa]=0,S[mI]=!1;let P=()=>S[xa]{this.tlsSessionCache.set(E,N)}),S.once("error",N=>{for(let{reject:U}of s)U(N);this.tlsSessionCache.delete(E)}),S.setTimeout(this.timeout,()=>{S.destroy()}),S.once("close",()=>{if(C){I&&this._freeSessionsCount--,this._sessionsCount--;let N=this.sessions[c];N.splice(N.indexOf(S),1),N.length===0&&delete this.sessions[c]}else{let N=new Error("Session closed without receiving a SETTINGS frame");N.code="HTTP2WRAPPER_NOSETTINGS";for(let{reject:U}of s)U(N);p()}this._tryToCreateNewSession(c,f)});let R=()=>{if(!(!(c in this.queue)||!P())){for(let N of S[Nc])if(N in this.queue[c]){let{listeners:U}=this.queue[c][N];for(;U.length!==0&&P();)U.shift().resolve(S);let W=this.queue[c];if(W[N].listeners.length===0&&(delete W[N],Object.keys(W).length===0)){delete this.queue[c];break}if(!P())break}}};S.on("origin",()=>{S[Nc]=S.originSet,P()&&(R(),HH(this.sessions[c],S))}),S.once("remoteSettings",()=>{if(S.ref(),S.unref(),this._sessionsCount++,h.destroyed){let N=new Error("Agent has been destroyed");for(let U of s)U.reject(N);S.destroy();return}S[Nc]=S.originSet;{let N=this.sessions;if(c in N){let U=N[c];U.splice(hgt(U,S,ggt),0,S)}else N[c]=[S]}this._freeSessionsCount+=1,C=!0,this.emit("session",S),R(),p(),S[xa]===0&&this._freeSessionsCount>this.maxFreeSessions&&S.close(),s.length!==0&&(this.getSession(f,r,s),s.length=0),S.on("remoteSettings",()=>{R(),HH(this.sessions[c],S)})}),S[Z0e]=S.request,S.request=(N,U)=>{if(S[mI])throw new Error("The session is gracefully closing. No new streams are allowed.");let W=S[Z0e](N,U);return S.ref(),++S[xa],S[xa]===S.remoteSettings.maxConcurrentStreams&&this._freeSessionsCount--,W.once("close",()=>{if(I=P(),--S[xa],!S.destroyed&&!S.closed&&(dgt(this.sessions[c],S),P()&&!S.closed)){I||(this._freeSessionsCount++,I=!0);let te=S[xa]===0;te&&S.unref(),te&&(this._freeSessionsCount>this.maxFreeSessions||S[mI])?S.close():(HH(this.sessions[c],S),R())}}),W}}catch(S){for(let P of s)P.reject(S);p()}};h.listeners=s,h.completed=!1,h.destroyed=!1,this.queue[c][f]=h,this._tryToCreateNewSession(c,f)})}request(e,r,s,a){return new Promise((n,c)=>{this.getSession(e,r,[{reject:c,resolve:f=>{try{n(f.request(s,a))}catch(p){c(p)}}}])})}createConnection(e,r){return t.connect(e,r)}static connect(e,r){r.ALPNProtocols=["h2"];let s=e.port||443,a=e.hostname||e.host;return typeof r.servername>"u"&&(r.servername=a),ugt.connect(s,a,r)}closeFreeSessions(){for(let e of Object.values(this.sessions))for(let r of e)r[xa]===0&&r.close()}destroy(e){for(let r of Object.values(this.sessions))for(let s of r)s.destroy(e);for(let r of Object.values(this.queue))for(let s of Object.values(r))s.destroyed=!0;this.queue={}}get freeSessions(){return X0e({agent:this,isFree:!0})}get busySessions(){return X0e({agent:this,isFree:!1})}};im.kCurrentStreamsCount=xa;im.kGracefullyClosing=mI;ege.exports={Agent:im,globalAgent:new im}});var GH=L((_zt,tge)=>{"use strict";var{Readable:mgt}=ye("stream"),qH=class extends mgt{constructor(e,r){super({highWaterMark:r,autoDestroy:!1}),this.statusCode=null,this.statusMessage="",this.httpVersion="2.0",this.httpVersionMajor=2,this.httpVersionMinor=0,this.headers={},this.trailers={},this.req=null,this.aborted=!1,this.complete=!1,this.upgrade=null,this.rawHeaders=[],this.rawTrailers=[],this.socket=e,this.connection=e,this._dumped=!1}_destroy(e){this.req._request.destroy(e)}setTimeout(e,r){return this.req.setTimeout(e,r),this}_dump(){this._dumped||(this._dumped=!0,this.removeAllListeners("data"),this.resume())}_read(){this.req&&this.req._request.resume()}};tge.exports=qH});var WH=L((Uzt,rge)=>{"use strict";rge.exports=t=>{let e={protocol:t.protocol,hostname:typeof t.hostname=="string"&&t.hostname.startsWith("[")?t.hostname.slice(1,-1):t.hostname,host:t.host,hash:t.hash,search:t.search,pathname:t.pathname,href:t.href,path:`${t.pathname||""}${t.search||""}`};return typeof t.port=="string"&&t.port.length!==0&&(e.port=Number(t.port)),(t.username||t.password)&&(e.auth=`${t.username||""}:${t.password||""}`),e}});var ige=L((Hzt,nge)=>{"use strict";nge.exports=(t,e,r)=>{for(let s of r)t.on(s,(...a)=>e.emit(s,...a))}});var oge=L((jzt,sge)=>{"use strict";sge.exports=t=>{switch(t){case":method":case":scheme":case":authority":case":path":return!0;default:return!1}}});var lge=L((Gzt,age)=>{"use strict";var yI=(t,e,r)=>{age.exports[e]=class extends t{constructor(...a){super(typeof r=="string"?r:r(a)),this.name=`${super.name} [${e}]`,this.code=e}}};yI(TypeError,"ERR_INVALID_ARG_TYPE",t=>{let e=t[0].includes(".")?"property":"argument",r=t[1],s=Array.isArray(r);return s&&(r=`${r.slice(0,-1).join(", ")} or ${r.slice(-1)}`),`The "${t[0]}" ${e} must be ${s?"one of":"of"} type ${r}. Received ${typeof t[2]}`});yI(TypeError,"ERR_INVALID_PROTOCOL",t=>`Protocol "${t[0]}" not supported. Expected "${t[1]}"`);yI(Error,"ERR_HTTP_HEADERS_SENT",t=>`Cannot ${t[0]} headers after they are sent to the client`);yI(TypeError,"ERR_INVALID_HTTP_TOKEN",t=>`${t[0]} must be a valid HTTP token [${t[1]}]`);yI(TypeError,"ERR_HTTP_INVALID_HEADER_VALUE",t=>`Invalid value "${t[0]} for header "${t[1]}"`);yI(TypeError,"ERR_INVALID_CHAR",t=>`Invalid character in ${t[0]} [${t[1]}]`)});var zH=L((Wzt,gge)=>{"use strict";var ygt=ye("http2"),{Writable:Egt}=ye("stream"),{Agent:cge,globalAgent:Igt}=jH(),Cgt=GH(),wgt=WH(),Bgt=ige(),vgt=oge(),{ERR_INVALID_ARG_TYPE:YH,ERR_INVALID_PROTOCOL:Sgt,ERR_HTTP_HEADERS_SENT:uge,ERR_INVALID_HTTP_TOKEN:Dgt,ERR_HTTP_INVALID_HEADER_VALUE:bgt,ERR_INVALID_CHAR:Pgt}=lge(),{HTTP2_HEADER_STATUS:fge,HTTP2_HEADER_METHOD:Age,HTTP2_HEADER_PATH:pge,HTTP2_METHOD_CONNECT:xgt}=ygt.constants,Jo=Symbol("headers"),VH=Symbol("origin"),KH=Symbol("session"),hge=Symbol("options"),KQ=Symbol("flushedHeaders"),cv=Symbol("jobs"),kgt=/^[\^`\-\w!#$%&*+.|~]+$/,Qgt=/[^\t\u0020-\u007E\u0080-\u00FF]/,JH=class extends Egt{constructor(e,r,s){super({autoDestroy:!1});let a=typeof e=="string"||e instanceof URL;if(a&&(e=wgt(e instanceof URL?e:new URL(e))),typeof r=="function"||r===void 0?(s=r,r=a?e:{...e}):r={...e,...r},r.h2session)this[KH]=r.h2session;else if(r.agent===!1)this.agent=new cge({maxFreeSessions:0});else if(typeof r.agent>"u"||r.agent===null)typeof r.createConnection=="function"?(this.agent=new cge({maxFreeSessions:0}),this.agent.createConnection=r.createConnection):this.agent=Igt;else if(typeof r.agent.request=="function")this.agent=r.agent;else throw new YH("options.agent",["Agent-like Object","undefined","false"],r.agent);if(r.protocol&&r.protocol!=="https:")throw new Sgt(r.protocol,"https:");let n=r.port||r.defaultPort||this.agent&&this.agent.defaultPort||443,c=r.hostname||r.host||"localhost";delete r.hostname,delete r.host,delete r.port;let{timeout:f}=r;if(r.timeout=void 0,this[Jo]=Object.create(null),this[cv]=[],this.socket=null,this.connection=null,this.method=r.method||"GET",this.path=r.path,this.res=null,this.aborted=!1,this.reusedSocket=!1,r.headers)for(let[p,h]of Object.entries(r.headers))this.setHeader(p,h);r.auth&&!("authorization"in this[Jo])&&(this[Jo].authorization="Basic "+Buffer.from(r.auth).toString("base64")),r.session=r.tlsSession,r.path=r.socketPath,this[hge]=r,n===443?(this[VH]=`https://${c}`,":authority"in this[Jo]||(this[Jo][":authority"]=c)):(this[VH]=`https://${c}:${n}`,":authority"in this[Jo]||(this[Jo][":authority"]=`${c}:${n}`)),f&&this.setTimeout(f),s&&this.once("response",s),this[KQ]=!1}get method(){return this[Jo][Age]}set method(e){e&&(this[Jo][Age]=e.toUpperCase())}get path(){return this[Jo][pge]}set path(e){e&&(this[Jo][pge]=e)}get _mustNotHaveABody(){return this.method==="GET"||this.method==="HEAD"||this.method==="DELETE"}_write(e,r,s){if(this._mustNotHaveABody){s(new Error("The GET, HEAD and DELETE methods must NOT have a body"));return}this.flushHeaders();let a=()=>this._request.write(e,r,s);this._request?a():this[cv].push(a)}_final(e){if(this.destroyed)return;this.flushHeaders();let r=()=>{if(this._mustNotHaveABody){e();return}this._request.end(e)};this._request?r():this[cv].push(r)}abort(){this.res&&this.res.complete||(this.aborted||process.nextTick(()=>this.emit("abort")),this.aborted=!0,this.destroy())}_destroy(e,r){this.res&&this.res._dump(),this._request&&this._request.destroy(),r(e)}async flushHeaders(){if(this[KQ]||this.destroyed)return;this[KQ]=!0;let e=this.method===xgt,r=s=>{if(this._request=s,this.destroyed){s.destroy();return}e||Bgt(s,this,["timeout","continue","close","error"]);let a=c=>(...f)=>{!this.writable&&!this.destroyed?c(...f):this.once("finish",()=>{c(...f)})};s.once("response",a((c,f,p)=>{let h=new Cgt(this.socket,s.readableHighWaterMark);this.res=h,h.req=this,h.statusCode=c[fge],h.headers=c,h.rawHeaders=p,h.once("end",()=>{this.aborted?(h.aborted=!0,h.emit("aborted")):(h.complete=!0,h.socket=null,h.connection=null)}),e?(h.upgrade=!0,this.emit("connect",h,s,Buffer.alloc(0))?this.emit("close"):s.destroy()):(s.on("data",E=>{!h._dumped&&!h.push(E)&&s.pause()}),s.once("end",()=>{h.push(null)}),this.emit("response",h)||h._dump())})),s.once("headers",a(c=>this.emit("information",{statusCode:c[fge]}))),s.once("trailers",a((c,f,p)=>{let{res:h}=this;h.trailers=c,h.rawTrailers=p}));let{socket:n}=s.session;this.socket=n,this.connection=n;for(let c of this[cv])c();this.emit("socket",this.socket)};if(this[KH])try{r(this[KH].request(this[Jo]))}catch(s){this.emit("error",s)}else{this.reusedSocket=!0;try{r(await this.agent.request(this[VH],this[hge],this[Jo]))}catch(s){this.emit("error",s)}}}getHeader(e){if(typeof e!="string")throw new YH("name","string",e);return this[Jo][e.toLowerCase()]}get headersSent(){return this[KQ]}removeHeader(e){if(typeof e!="string")throw new YH("name","string",e);if(this.headersSent)throw new uge("remove");delete this[Jo][e.toLowerCase()]}setHeader(e,r){if(this.headersSent)throw new uge("set");if(typeof e!="string"||!kgt.test(e)&&!vgt(e))throw new Dgt("Header name",e);if(typeof r>"u")throw new bgt(r,e);if(Qgt.test(r))throw new Pgt("header content",e);this[Jo][e.toLowerCase()]=r}setNoDelay(){}setSocketKeepAlive(){}setTimeout(e,r){let s=()=>this._request.setTimeout(e,r);return this._request?s():this[cv].push(s),this}get maxHeadersCount(){if(!this.destroyed&&this._request)return this._request.session.localSettings.maxHeaderListSize}set maxHeadersCount(e){}};gge.exports=JH});var mge=L((Yzt,dge)=>{"use strict";var Tgt=ye("tls");dge.exports=(t={},e=Tgt.connect)=>new Promise((r,s)=>{let a=!1,n,c=async()=>{await p,n.off("timeout",f),n.off("error",s),t.resolveSocket?(r({alpnProtocol:n.alpnProtocol,socket:n,timeout:a}),a&&(await Promise.resolve(),n.emit("timeout"))):(n.destroy(),r({alpnProtocol:n.alpnProtocol,timeout:a}))},f=async()=>{a=!0,c()},p=(async()=>{try{n=await e(t,c),n.on("error",s),n.once("timeout",f)}catch(h){s(h)}})()})});var Ege=L((Vzt,yge)=>{"use strict";var Rgt=ye("net");yge.exports=t=>{let e=t.host,r=t.headers&&t.headers.host;return r&&(r.startsWith("[")?r.indexOf("]")===-1?e=r:e=r.slice(1,-1):e=r.split(":",1)[0]),Rgt.isIP(e)?"":e}});var wge=L((Kzt,XH)=>{"use strict";var Ige=ye("http"),ZH=ye("https"),Fgt=mge(),Ngt=UH(),Ogt=zH(),Lgt=Ege(),Mgt=WH(),JQ=new Ngt({maxSize:100}),uv=new Map,Cge=(t,e,r)=>{e._httpMessage={shouldKeepAlive:!0};let s=()=>{t.emit("free",e,r)};e.on("free",s);let a=()=>{t.removeSocket(e,r)};e.on("close",a);let n=()=>{t.removeSocket(e,r),e.off("close",a),e.off("free",s),e.off("agentRemove",n)};e.on("agentRemove",n),t.emit("free",e,r)},_gt=async t=>{let e=`${t.host}:${t.port}:${t.ALPNProtocols.sort()}`;if(!JQ.has(e)){if(uv.has(e))return(await uv.get(e)).alpnProtocol;let{path:r,agent:s}=t;t.path=t.socketPath;let a=Fgt(t);uv.set(e,a);try{let{socket:n,alpnProtocol:c}=await a;if(JQ.set(e,c),t.path=r,c==="h2")n.destroy();else{let{globalAgent:f}=ZH,p=ZH.Agent.prototype.createConnection;s?s.createConnection===p?Cge(s,n,t):n.destroy():f.createConnection===p?Cge(f,n,t):n.destroy()}return uv.delete(e),c}catch(n){throw uv.delete(e),n}}return JQ.get(e)};XH.exports=async(t,e,r)=>{if((typeof t=="string"||t instanceof URL)&&(t=Mgt(new URL(t))),typeof e=="function"&&(r=e,e=void 0),e={ALPNProtocols:["h2","http/1.1"],...t,...e,resolveSocket:!0},!Array.isArray(e.ALPNProtocols)||e.ALPNProtocols.length===0)throw new Error("The `ALPNProtocols` option must be an Array with at least one entry");e.protocol=e.protocol||"https:";let s=e.protocol==="https:";e.host=e.hostname||e.host||"localhost",e.session=e.tlsSession,e.servername=e.servername||Lgt(e),e.port=e.port||(s?443:80),e._defaultAgent=s?ZH.globalAgent:Ige.globalAgent;let a=e.agent;if(a){if(a.addRequest)throw new Error("The `options.agent` object can contain only `http`, `https` or `http2` properties");e.agent=a[s?"https":"http"]}return s&&await _gt(e)==="h2"?(a&&(e.agent=a.http2),new Ogt(e,r)):Ige.request(e,r)};XH.exports.protocolCache=JQ});var vge=L((Jzt,Bge)=>{"use strict";var Ugt=ye("http2"),Hgt=jH(),$H=zH(),jgt=GH(),qgt=wge(),Ggt=(t,e,r)=>new $H(t,e,r),Wgt=(t,e,r)=>{let s=new $H(t,e,r);return s.end(),s};Bge.exports={...Ugt,ClientRequest:$H,IncomingMessage:jgt,...Hgt,request:Ggt,get:Wgt,auto:qgt}});var tj=L(ej=>{"use strict";Object.defineProperty(ej,"__esModule",{value:!0});var Sge=Mp();ej.default=t=>Sge.default.nodeStream(t)&&Sge.default.function_(t.getBoundary)});var xge=L(rj=>{"use strict";Object.defineProperty(rj,"__esModule",{value:!0});var bge=ye("fs"),Pge=ye("util"),Dge=Mp(),Ygt=tj(),Vgt=Pge.promisify(bge.stat);rj.default=async(t,e)=>{if(e&&"content-length"in e)return Number(e["content-length"]);if(!t)return 0;if(Dge.default.string(t))return Buffer.byteLength(t);if(Dge.default.buffer(t))return t.length;if(Ygt.default(t))return Pge.promisify(t.getLength.bind(t))();if(t instanceof bge.ReadStream){let{size:r}=await Vgt(t.path);return r===0?void 0:r}}});var ij=L(nj=>{"use strict";Object.defineProperty(nj,"__esModule",{value:!0});function Kgt(t,e,r){let s={};for(let a of r)s[a]=(...n)=>{e.emit(a,...n)},t.on(a,s[a]);return()=>{for(let a of r)t.off(a,s[a])}}nj.default=Kgt});var kge=L(sj=>{"use strict";Object.defineProperty(sj,"__esModule",{value:!0});sj.default=()=>{let t=[];return{once(e,r,s){e.once(r,s),t.push({origin:e,event:r,fn:s})},unhandleAll(){for(let e of t){let{origin:r,event:s,fn:a}=e;r.removeListener(s,a)}t.length=0}}}});var Tge=L(fv=>{"use strict";Object.defineProperty(fv,"__esModule",{value:!0});fv.TimeoutError=void 0;var Jgt=ye("net"),zgt=kge(),Qge=Symbol("reentry"),Zgt=()=>{},zQ=class extends Error{constructor(e,r){super(`Timeout awaiting '${r}' for ${e}ms`),this.event=r,this.name="TimeoutError",this.code="ETIMEDOUT"}};fv.TimeoutError=zQ;fv.default=(t,e,r)=>{if(Qge in t)return Zgt;t[Qge]=!0;let s=[],{once:a,unhandleAll:n}=zgt.default(),c=(C,S,P)=>{var I;let R=setTimeout(S,C,C,P);(I=R.unref)===null||I===void 0||I.call(R);let N=()=>{clearTimeout(R)};return s.push(N),N},{host:f,hostname:p}=r,h=(C,S)=>{t.destroy(new zQ(C,S))},E=()=>{for(let C of s)C();n()};if(t.once("error",C=>{if(E(),t.listenerCount("error")===0)throw C}),t.once("close",E),a(t,"response",C=>{a(C,"end",E)}),typeof e.request<"u"&&c(e.request,h,"request"),typeof e.socket<"u"){let C=()=>{h(e.socket,"socket")};t.setTimeout(e.socket,C),s.push(()=>{t.removeListener("timeout",C)})}return a(t,"socket",C=>{var S;let{socketPath:P}=t;if(C.connecting){let I=!!(P??Jgt.isIP((S=p??f)!==null&&S!==void 0?S:"")!==0);if(typeof e.lookup<"u"&&!I&&typeof C.address().address>"u"){let R=c(e.lookup,h,"lookup");a(C,"lookup",R)}if(typeof e.connect<"u"){let R=()=>c(e.connect,h,"connect");I?a(C,"connect",R()):a(C,"lookup",N=>{N===null&&a(C,"connect",R())})}typeof e.secureConnect<"u"&&r.protocol==="https:"&&a(C,"connect",()=>{let R=c(e.secureConnect,h,"secureConnect");a(C,"secureConnect",R)})}if(typeof e.send<"u"){let I=()=>c(e.send,h,"send");C.connecting?a(C,"connect",()=>{a(t,"upload-complete",I())}):a(t,"upload-complete",I())}}),typeof e.response<"u"&&a(t,"upload-complete",()=>{let C=c(e.response,h,"response");a(t,"response",C)}),E}});var Fge=L(oj=>{"use strict";Object.defineProperty(oj,"__esModule",{value:!0});var Rge=Mp();oj.default=t=>{t=t;let e={protocol:t.protocol,hostname:Rge.default.string(t.hostname)&&t.hostname.startsWith("[")?t.hostname.slice(1,-1):t.hostname,host:t.host,hash:t.hash,search:t.search,pathname:t.pathname,href:t.href,path:`${t.pathname||""}${t.search||""}`};return Rge.default.string(t.port)&&t.port.length>0&&(e.port=Number(t.port)),(t.username||t.password)&&(e.auth=`${t.username||""}:${t.password||""}`),e}});var Nge=L(aj=>{"use strict";Object.defineProperty(aj,"__esModule",{value:!0});var Xgt=ye("url"),$gt=["protocol","host","hostname","port","pathname","search"];aj.default=(t,e)=>{var r,s;if(e.path){if(e.pathname)throw new TypeError("Parameters `path` and `pathname` are mutually exclusive.");if(e.search)throw new TypeError("Parameters `path` and `search` are mutually exclusive.");if(e.searchParams)throw new TypeError("Parameters `path` and `searchParams` are mutually exclusive.")}if(e.search&&e.searchParams)throw new TypeError("Parameters `search` and `searchParams` are mutually exclusive.");if(!t){if(!e.protocol)throw new TypeError("No URL protocol specified");t=`${e.protocol}//${(s=(r=e.hostname)!==null&&r!==void 0?r:e.host)!==null&&s!==void 0?s:""}`}let a=new Xgt.URL(t);if(e.path){let n=e.path.indexOf("?");n===-1?e.pathname=e.path:(e.pathname=e.path.slice(0,n),e.search=e.path.slice(n+1)),delete e.path}for(let n of $gt)e[n]&&(a[n]=e[n].toString());return a}});var Oge=L(cj=>{"use strict";Object.defineProperty(cj,"__esModule",{value:!0});var lj=class{constructor(){this.weakMap=new WeakMap,this.map=new Map}set(e,r){typeof e=="object"?this.weakMap.set(e,r):this.map.set(e,r)}get(e){return typeof e=="object"?this.weakMap.get(e):this.map.get(e)}has(e){return typeof e=="object"?this.weakMap.has(e):this.map.has(e)}};cj.default=lj});var fj=L(uj=>{"use strict";Object.defineProperty(uj,"__esModule",{value:!0});var edt=async t=>{let e=[],r=0;for await(let s of t)e.push(s),r+=Buffer.byteLength(s);return Buffer.isBuffer(e[0])?Buffer.concat(e,r):Buffer.from(e.join(""))};uj.default=edt});var Mge=L(sm=>{"use strict";Object.defineProperty(sm,"__esModule",{value:!0});sm.dnsLookupIpVersionToFamily=sm.isDnsLookupIpVersion=void 0;var Lge={auto:0,ipv4:4,ipv6:6};sm.isDnsLookupIpVersion=t=>t in Lge;sm.dnsLookupIpVersionToFamily=t=>{if(sm.isDnsLookupIpVersion(t))return Lge[t];throw new Error("Invalid DNS lookup IP version")}});var Aj=L(ZQ=>{"use strict";Object.defineProperty(ZQ,"__esModule",{value:!0});ZQ.isResponseOk=void 0;ZQ.isResponseOk=t=>{let{statusCode:e}=t,r=t.request.options.followRedirect?299:399;return e>=200&&e<=r||e===304}});var Uge=L(pj=>{"use strict";Object.defineProperty(pj,"__esModule",{value:!0});var _ge=new Set;pj.default=t=>{_ge.has(t)||(_ge.add(t),process.emitWarning(`Got: ${t}`,{type:"DeprecationWarning"}))}});var Hge=L(hj=>{"use strict";Object.defineProperty(hj,"__esModule",{value:!0});var Di=Mp(),tdt=(t,e)=>{if(Di.default.null_(t.encoding))throw new TypeError("To get a Buffer, set `options.responseType` to `buffer` instead");Di.assert.any([Di.default.string,Di.default.undefined],t.encoding),Di.assert.any([Di.default.boolean,Di.default.undefined],t.resolveBodyOnly),Di.assert.any([Di.default.boolean,Di.default.undefined],t.methodRewriting),Di.assert.any([Di.default.boolean,Di.default.undefined],t.isStream),Di.assert.any([Di.default.string,Di.default.undefined],t.responseType),t.responseType===void 0&&(t.responseType="text");let{retry:r}=t;if(e?t.retry={...e.retry}:t.retry={calculateDelay:s=>s.computedValue,limit:0,methods:[],statusCodes:[],errorCodes:[],maxRetryAfter:void 0},Di.default.object(r)?(t.retry={...t.retry,...r},t.retry.methods=[...new Set(t.retry.methods.map(s=>s.toUpperCase()))],t.retry.statusCodes=[...new Set(t.retry.statusCodes)],t.retry.errorCodes=[...new Set(t.retry.errorCodes)]):Di.default.number(r)&&(t.retry.limit=r),Di.default.undefined(t.retry.maxRetryAfter)&&(t.retry.maxRetryAfter=Math.min(...[t.timeout.request,t.timeout.connect].filter(Di.default.number))),Di.default.object(t.pagination)){e&&(t.pagination={...e.pagination,...t.pagination});let{pagination:s}=t;if(!Di.default.function_(s.transform))throw new Error("`options.pagination.transform` must be implemented");if(!Di.default.function_(s.shouldContinue))throw new Error("`options.pagination.shouldContinue` must be implemented");if(!Di.default.function_(s.filter))throw new TypeError("`options.pagination.filter` must be implemented");if(!Di.default.function_(s.paginate))throw new Error("`options.pagination.paginate` must be implemented")}return t.responseType==="json"&&t.headers.accept===void 0&&(t.headers.accept="application/json"),t};hj.default=tdt});var jge=L(Av=>{"use strict";Object.defineProperty(Av,"__esModule",{value:!0});Av.retryAfterStatusCodes=void 0;Av.retryAfterStatusCodes=new Set([413,429,503]);var rdt=({attemptCount:t,retryOptions:e,error:r,retryAfter:s})=>{if(t>e.limit)return 0;let a=e.methods.includes(r.options.method),n=e.errorCodes.includes(r.code),c=r.response&&e.statusCodes.includes(r.response.statusCode);if(!a||!n&&!c)return 0;if(r.response){if(s)return e.maxRetryAfter===void 0||s>e.maxRetryAfter?0:s;if(r.response.statusCode===413)return 0}let f=Math.random()*100;return 2**(t-1)*1e3+f};Av.default=rdt});var gv=L(Ln=>{"use strict";Object.defineProperty(Ln,"__esModule",{value:!0});Ln.UnsupportedProtocolError=Ln.ReadError=Ln.TimeoutError=Ln.UploadError=Ln.CacheError=Ln.HTTPError=Ln.MaxRedirectsError=Ln.RequestError=Ln.setNonEnumerableProperties=Ln.knownHookEvents=Ln.withoutBody=Ln.kIsNormalizedAlready=void 0;var qge=ye("util"),Gge=ye("stream"),ndt=ye("fs"),B0=ye("url"),Wge=ye("http"),gj=ye("http"),idt=ye("https"),sdt=s0e(),odt=A0e(),Yge=W0e(),adt=J0e(),ldt=vge(),cdt=YQ(),at=Mp(),udt=xge(),Vge=tj(),fdt=ij(),Kge=Tge(),Adt=Fge(),Jge=Nge(),pdt=Oge(),hdt=fj(),zge=Mge(),gdt=Aj(),v0=Uge(),ddt=Hge(),mdt=jge(),dj,go=Symbol("request"),eT=Symbol("response"),EI=Symbol("responseSize"),II=Symbol("downloadedSize"),CI=Symbol("bodySize"),wI=Symbol("uploadedSize"),XQ=Symbol("serverResponsesPiped"),Zge=Symbol("unproxyEvents"),Xge=Symbol("isFromCache"),mj=Symbol("cancelTimeouts"),$ge=Symbol("startedReading"),BI=Symbol("stopReading"),$Q=Symbol("triggerRead"),S0=Symbol("body"),pv=Symbol("jobs"),ede=Symbol("originalResponse"),tde=Symbol("retryTimeout");Ln.kIsNormalizedAlready=Symbol("isNormalizedAlready");var ydt=at.default.string(process.versions.brotli);Ln.withoutBody=new Set(["GET","HEAD"]);Ln.knownHookEvents=["init","beforeRequest","beforeRedirect","beforeError","beforeRetry","afterResponse"];function Edt(t){for(let e in t){let r=t[e];if(!at.default.string(r)&&!at.default.number(r)&&!at.default.boolean(r)&&!at.default.null_(r)&&!at.default.undefined(r))throw new TypeError(`The \`searchParams\` value '${String(r)}' must be a string, number, boolean or null`)}}function Idt(t){return at.default.object(t)&&!("statusCode"in t)}var yj=new pdt.default,Cdt=async t=>new Promise((e,r)=>{let s=a=>{r(a)};t.pending||e(),t.once("error",s),t.once("ready",()=>{t.off("error",s),e()})}),wdt=new Set([300,301,302,303,304,307,308]),Bdt=["context","body","json","form"];Ln.setNonEnumerableProperties=(t,e)=>{let r={};for(let s of t)if(s)for(let a of Bdt)a in s&&(r[a]={writable:!0,configurable:!0,enumerable:!1,value:s[a]});Object.defineProperties(e,r)};var fs=class extends Error{constructor(e,r,s){var a;if(super(e),Error.captureStackTrace(this,this.constructor),this.name="RequestError",this.code=r.code,s instanceof aT?(Object.defineProperty(this,"request",{enumerable:!1,value:s}),Object.defineProperty(this,"response",{enumerable:!1,value:s[eT]}),Object.defineProperty(this,"options",{enumerable:!1,value:s.options})):Object.defineProperty(this,"options",{enumerable:!1,value:s}),this.timings=(a=this.request)===null||a===void 0?void 0:a.timings,at.default.string(r.stack)&&at.default.string(this.stack)){let n=this.stack.indexOf(this.message)+this.message.length,c=this.stack.slice(n).split(` +`).reverse(),f=r.stack.slice(r.stack.indexOf(r.message)+r.message.length).split(` +`).reverse();for(;f.length!==0&&f[0]===c[0];)c.shift();this.stack=`${this.stack.slice(0,n)}${c.reverse().join(` +`)}${f.reverse().join(` +`)}`}}};Ln.RequestError=fs;var tT=class extends fs{constructor(e){super(`Redirected ${e.options.maxRedirects} times. Aborting.`,{},e),this.name="MaxRedirectsError"}};Ln.MaxRedirectsError=tT;var rT=class extends fs{constructor(e){super(`Response code ${e.statusCode} (${e.statusMessage})`,{},e.request),this.name="HTTPError"}};Ln.HTTPError=rT;var nT=class extends fs{constructor(e,r){super(e.message,e,r),this.name="CacheError"}};Ln.CacheError=nT;var iT=class extends fs{constructor(e,r){super(e.message,e,r),this.name="UploadError"}};Ln.UploadError=iT;var sT=class extends fs{constructor(e,r,s){super(e.message,e,s),this.name="TimeoutError",this.event=e.event,this.timings=r}};Ln.TimeoutError=sT;var hv=class extends fs{constructor(e,r){super(e.message,e,r),this.name="ReadError"}};Ln.ReadError=hv;var oT=class extends fs{constructor(e){super(`Unsupported protocol "${e.url.protocol}"`,{},e),this.name="UnsupportedProtocolError"}};Ln.UnsupportedProtocolError=oT;var vdt=["socket","connect","continue","information","upgrade","timeout"],aT=class extends Gge.Duplex{constructor(e,r={},s){super({autoDestroy:!1,highWaterMark:0}),this[II]=0,this[wI]=0,this.requestInitialized=!1,this[XQ]=new Set,this.redirects=[],this[BI]=!1,this[$Q]=!1,this[pv]=[],this.retryCount=0,this._progressCallbacks=[];let a=()=>this._unlockWrite(),n=()=>this._lockWrite();this.on("pipe",h=>{h.prependListener("data",a),h.on("data",n),h.prependListener("end",a),h.on("end",n)}),this.on("unpipe",h=>{h.off("data",a),h.off("data",n),h.off("end",a),h.off("end",n)}),this.on("pipe",h=>{h instanceof gj.IncomingMessage&&(this.options.headers={...h.headers,...this.options.headers})});let{json:c,body:f,form:p}=r;if((c||f||p)&&this._lockWrite(),Ln.kIsNormalizedAlready in r)this.options=r;else try{this.options=this.constructor.normalizeArguments(e,r,s)}catch(h){at.default.nodeStream(r.body)&&r.body.destroy(),this.destroy(h);return}(async()=>{var h;try{this.options.body instanceof ndt.ReadStream&&await Cdt(this.options.body);let{url:E}=this.options;if(!E)throw new TypeError("Missing `url` property");if(this.requestUrl=E.toString(),decodeURI(this.requestUrl),await this._finalizeBody(),await this._makeRequest(),this.destroyed){(h=this[go])===null||h===void 0||h.destroy();return}for(let C of this[pv])C();this[pv].length=0,this.requestInitialized=!0}catch(E){if(E instanceof fs){this._beforeError(E);return}this.destroyed||this.destroy(E)}})()}static normalizeArguments(e,r,s){var a,n,c,f,p;let h=r;if(at.default.object(e)&&!at.default.urlInstance(e))r={...s,...e,...r};else{if(e&&r&&r.url!==void 0)throw new TypeError("The `url` option is mutually exclusive with the `input` argument");r={...s,...r},e!==void 0&&(r.url=e),at.default.urlInstance(r.url)&&(r.url=new B0.URL(r.url.toString()))}if(r.cache===!1&&(r.cache=void 0),r.dnsCache===!1&&(r.dnsCache=void 0),at.assert.any([at.default.string,at.default.undefined],r.method),at.assert.any([at.default.object,at.default.undefined],r.headers),at.assert.any([at.default.string,at.default.urlInstance,at.default.undefined],r.prefixUrl),at.assert.any([at.default.object,at.default.undefined],r.cookieJar),at.assert.any([at.default.object,at.default.string,at.default.undefined],r.searchParams),at.assert.any([at.default.object,at.default.string,at.default.undefined],r.cache),at.assert.any([at.default.object,at.default.number,at.default.undefined],r.timeout),at.assert.any([at.default.object,at.default.undefined],r.context),at.assert.any([at.default.object,at.default.undefined],r.hooks),at.assert.any([at.default.boolean,at.default.undefined],r.decompress),at.assert.any([at.default.boolean,at.default.undefined],r.ignoreInvalidCookies),at.assert.any([at.default.boolean,at.default.undefined],r.followRedirect),at.assert.any([at.default.number,at.default.undefined],r.maxRedirects),at.assert.any([at.default.boolean,at.default.undefined],r.throwHttpErrors),at.assert.any([at.default.boolean,at.default.undefined],r.http2),at.assert.any([at.default.boolean,at.default.undefined],r.allowGetBody),at.assert.any([at.default.string,at.default.undefined],r.localAddress),at.assert.any([zge.isDnsLookupIpVersion,at.default.undefined],r.dnsLookupIpVersion),at.assert.any([at.default.object,at.default.undefined],r.https),at.assert.any([at.default.boolean,at.default.undefined],r.rejectUnauthorized),r.https&&(at.assert.any([at.default.boolean,at.default.undefined],r.https.rejectUnauthorized),at.assert.any([at.default.function_,at.default.undefined],r.https.checkServerIdentity),at.assert.any([at.default.string,at.default.object,at.default.array,at.default.undefined],r.https.certificateAuthority),at.assert.any([at.default.string,at.default.object,at.default.array,at.default.undefined],r.https.key),at.assert.any([at.default.string,at.default.object,at.default.array,at.default.undefined],r.https.certificate),at.assert.any([at.default.string,at.default.undefined],r.https.passphrase),at.assert.any([at.default.string,at.default.buffer,at.default.array,at.default.undefined],r.https.pfx)),at.assert.any([at.default.object,at.default.undefined],r.cacheOptions),at.default.string(r.method)?r.method=r.method.toUpperCase():r.method="GET",r.headers===s?.headers?r.headers={...r.headers}:r.headers=cdt({...s?.headers,...r.headers}),"slashes"in r)throw new TypeError("The legacy `url.Url` has been deprecated. Use `URL` instead.");if("auth"in r)throw new TypeError("Parameter `auth` is deprecated. Use `username` / `password` instead.");if("searchParams"in r&&r.searchParams&&r.searchParams!==s?.searchParams){let P;if(at.default.string(r.searchParams)||r.searchParams instanceof B0.URLSearchParams)P=new B0.URLSearchParams(r.searchParams);else{Edt(r.searchParams),P=new B0.URLSearchParams;for(let I in r.searchParams){let R=r.searchParams[I];R===null?P.append(I,""):R!==void 0&&P.append(I,R)}}(a=s?.searchParams)===null||a===void 0||a.forEach((I,R)=>{P.has(R)||P.append(R,I)}),r.searchParams=P}if(r.username=(n=r.username)!==null&&n!==void 0?n:"",r.password=(c=r.password)!==null&&c!==void 0?c:"",at.default.undefined(r.prefixUrl)?r.prefixUrl=(f=s?.prefixUrl)!==null&&f!==void 0?f:"":(r.prefixUrl=r.prefixUrl.toString(),r.prefixUrl!==""&&!r.prefixUrl.endsWith("/")&&(r.prefixUrl+="/")),at.default.string(r.url)){if(r.url.startsWith("/"))throw new Error("`input` must not start with a slash when using `prefixUrl`");r.url=Jge.default(r.prefixUrl+r.url,r)}else(at.default.undefined(r.url)&&r.prefixUrl!==""||r.protocol)&&(r.url=Jge.default(r.prefixUrl,r));if(r.url){"port"in r&&delete r.port;let{prefixUrl:P}=r;Object.defineProperty(r,"prefixUrl",{set:R=>{let N=r.url;if(!N.href.startsWith(R))throw new Error(`Cannot change \`prefixUrl\` from ${P} to ${R}: ${N.href}`);r.url=new B0.URL(R+N.href.slice(P.length)),P=R},get:()=>P});let{protocol:I}=r.url;if(I==="unix:"&&(I="http:",r.url=new B0.URL(`http://unix${r.url.pathname}${r.url.search}`)),r.searchParams&&(r.url.search=r.searchParams.toString()),I!=="http:"&&I!=="https:")throw new oT(r);r.username===""?r.username=r.url.username:r.url.username=r.username,r.password===""?r.password=r.url.password:r.url.password=r.password}let{cookieJar:E}=r;if(E){let{setCookie:P,getCookieString:I}=E;at.assert.function_(P),at.assert.function_(I),P.length===4&&I.length===0&&(P=qge.promisify(P.bind(r.cookieJar)),I=qge.promisify(I.bind(r.cookieJar)),r.cookieJar={setCookie:P,getCookieString:I})}let{cache:C}=r;if(C&&(yj.has(C)||yj.set(C,new Yge((P,I)=>{let R=P[go](P,I);return at.default.promise(R)&&(R.once=(N,U)=>{if(N==="error")R.catch(U);else if(N==="abort")(async()=>{try{(await R).once("abort",U)}catch{}})();else throw new Error(`Unknown HTTP2 promise event: ${N}`);return R}),R},C))),r.cacheOptions={...r.cacheOptions},r.dnsCache===!0)dj||(dj=new odt.default),r.dnsCache=dj;else if(!at.default.undefined(r.dnsCache)&&!r.dnsCache.lookup)throw new TypeError(`Parameter \`dnsCache\` must be a CacheableLookup instance or a boolean, got ${at.default(r.dnsCache)}`);at.default.number(r.timeout)?r.timeout={request:r.timeout}:s&&r.timeout!==s.timeout?r.timeout={...s.timeout,...r.timeout}:r.timeout={...r.timeout},r.context||(r.context={});let S=r.hooks===s?.hooks;r.hooks={...r.hooks};for(let P of Ln.knownHookEvents)if(P in r.hooks)if(at.default.array(r.hooks[P]))r.hooks[P]=[...r.hooks[P]];else throw new TypeError(`Parameter \`${P}\` must be an Array, got ${at.default(r.hooks[P])}`);else r.hooks[P]=[];if(s&&!S)for(let P of Ln.knownHookEvents)s.hooks[P].length>0&&(r.hooks[P]=[...s.hooks[P],...r.hooks[P]]);if("family"in r&&v0.default('"options.family" was never documented, please use "options.dnsLookupIpVersion"'),s?.https&&(r.https={...s.https,...r.https}),"rejectUnauthorized"in r&&v0.default('"options.rejectUnauthorized" is now deprecated, please use "options.https.rejectUnauthorized"'),"checkServerIdentity"in r&&v0.default('"options.checkServerIdentity" was never documented, please use "options.https.checkServerIdentity"'),"ca"in r&&v0.default('"options.ca" was never documented, please use "options.https.certificateAuthority"'),"key"in r&&v0.default('"options.key" was never documented, please use "options.https.key"'),"cert"in r&&v0.default('"options.cert" was never documented, please use "options.https.certificate"'),"passphrase"in r&&v0.default('"options.passphrase" was never documented, please use "options.https.passphrase"'),"pfx"in r&&v0.default('"options.pfx" was never documented, please use "options.https.pfx"'),"followRedirects"in r)throw new TypeError("The `followRedirects` option does not exist. Use `followRedirect` instead.");if(r.agent){for(let P in r.agent)if(P!=="http"&&P!=="https"&&P!=="http2")throw new TypeError(`Expected the \`options.agent\` properties to be \`http\`, \`https\` or \`http2\`, got \`${P}\``)}return r.maxRedirects=(p=r.maxRedirects)!==null&&p!==void 0?p:0,Ln.setNonEnumerableProperties([s,h],r),ddt.default(r,s)}_lockWrite(){let e=()=>{throw new TypeError("The payload has been already provided")};this.write=e,this.end=e}_unlockWrite(){this.write=super.write,this.end=super.end}async _finalizeBody(){let{options:e}=this,{headers:r}=e,s=!at.default.undefined(e.form),a=!at.default.undefined(e.json),n=!at.default.undefined(e.body),c=s||a||n,f=Ln.withoutBody.has(e.method)&&!(e.method==="GET"&&e.allowGetBody);if(this._cannotHaveBody=f,c){if(f)throw new TypeError(`The \`${e.method}\` method cannot be used with a body`);if([n,s,a].filter(p=>p).length>1)throw new TypeError("The `body`, `json` and `form` options are mutually exclusive");if(n&&!(e.body instanceof Gge.Readable)&&!at.default.string(e.body)&&!at.default.buffer(e.body)&&!Vge.default(e.body))throw new TypeError("The `body` option must be a stream.Readable, string or Buffer");if(s&&!at.default.object(e.form))throw new TypeError("The `form` option must be an Object");{let p=!at.default.string(r["content-type"]);n?(Vge.default(e.body)&&p&&(r["content-type"]=`multipart/form-data; boundary=${e.body.getBoundary()}`),this[S0]=e.body):s?(p&&(r["content-type"]="application/x-www-form-urlencoded"),this[S0]=new B0.URLSearchParams(e.form).toString()):(p&&(r["content-type"]="application/json"),this[S0]=e.stringifyJson(e.json));let h=await udt.default(this[S0],e.headers);at.default.undefined(r["content-length"])&&at.default.undefined(r["transfer-encoding"])&&!f&&!at.default.undefined(h)&&(r["content-length"]=String(h))}}else f?this._lockWrite():this._unlockWrite();this[CI]=Number(r["content-length"])||void 0}async _onResponseBase(e){let{options:r}=this,{url:s}=r;this[ede]=e,r.decompress&&(e=adt(e));let a=e.statusCode,n=e;n.statusMessage=n.statusMessage?n.statusMessage:Wge.STATUS_CODES[a],n.url=r.url.toString(),n.requestUrl=this.requestUrl,n.redirectUrls=this.redirects,n.request=this,n.isFromCache=e.fromCache||!1,n.ip=this.ip,n.retryCount=this.retryCount,this[Xge]=n.isFromCache,this[EI]=Number(e.headers["content-length"])||void 0,this[eT]=e,e.once("end",()=>{this[EI]=this[II],this.emit("downloadProgress",this.downloadProgress)}),e.once("error",f=>{e.destroy(),this._beforeError(new hv(f,this))}),e.once("aborted",()=>{this._beforeError(new hv({name:"Error",message:"The server aborted pending request",code:"ECONNRESET"},this))}),this.emit("downloadProgress",this.downloadProgress);let c=e.headers["set-cookie"];if(at.default.object(r.cookieJar)&&c){let f=c.map(async p=>r.cookieJar.setCookie(p,s.toString()));r.ignoreInvalidCookies&&(f=f.map(async p=>p.catch(()=>{})));try{await Promise.all(f)}catch(p){this._beforeError(p);return}}if(r.followRedirect&&e.headers.location&&wdt.has(a)){if(e.resume(),this[go]&&(this[mj](),delete this[go],this[Zge]()),(a===303&&r.method!=="GET"&&r.method!=="HEAD"||!r.methodRewriting)&&(r.method="GET","body"in r&&delete r.body,"json"in r&&delete r.json,"form"in r&&delete r.form,this[S0]=void 0,delete r.headers["content-length"]),this.redirects.length>=r.maxRedirects){this._beforeError(new tT(this));return}try{let p=Buffer.from(e.headers.location,"binary").toString(),h=new B0.URL(p,s),E=h.toString();decodeURI(E),h.hostname!==s.hostname||h.port!==s.port?("host"in r.headers&&delete r.headers.host,"cookie"in r.headers&&delete r.headers.cookie,"authorization"in r.headers&&delete r.headers.authorization,(r.username||r.password)&&(r.username="",r.password="")):(h.username=r.username,h.password=r.password),this.redirects.push(E),r.url=h;for(let C of r.hooks.beforeRedirect)await C(r,n);this.emit("redirect",n,r),await this._makeRequest()}catch(p){this._beforeError(p);return}return}if(r.isStream&&r.throwHttpErrors&&!gdt.isResponseOk(n)){this._beforeError(new rT(n));return}e.on("readable",()=>{this[$Q]&&this._read()}),this.on("resume",()=>{e.resume()}),this.on("pause",()=>{e.pause()}),e.once("end",()=>{this.push(null)}),this.emit("response",e);for(let f of this[XQ])if(!f.headersSent){for(let p in e.headers){let h=r.decompress?p!=="content-encoding":!0,E=e.headers[p];h&&f.setHeader(p,E)}f.statusCode=a}}async _onResponse(e){try{await this._onResponseBase(e)}catch(r){this._beforeError(r)}}_onRequest(e){let{options:r}=this,{timeout:s,url:a}=r;sdt.default(e),this[mj]=Kge.default(e,s,a);let n=r.cache?"cacheableResponse":"response";e.once(n,p=>{this._onResponse(p)}),e.once("error",p=>{var h;e.destroy(),(h=e.res)===null||h===void 0||h.removeAllListeners("end"),p=p instanceof Kge.TimeoutError?new sT(p,this.timings,this):new fs(p.message,p,this),this._beforeError(p)}),this[Zge]=fdt.default(e,this,vdt),this[go]=e,this.emit("uploadProgress",this.uploadProgress);let c=this[S0],f=this.redirects.length===0?this:e;at.default.nodeStream(c)?(c.pipe(f),c.once("error",p=>{this._beforeError(new iT(p,this))})):(this._unlockWrite(),at.default.undefined(c)?(this._cannotHaveBody||this._noPipe)&&(f.end(),this._lockWrite()):(this._writeRequest(c,void 0,()=>{}),f.end(),this._lockWrite())),this.emit("request",e)}async _createCacheableRequest(e,r){return new Promise((s,a)=>{Object.assign(r,Adt.default(e)),delete r.url;let n,c=yj.get(r.cache)(r,async f=>{f._readableState.autoDestroy=!1,n&&(await n).emit("cacheableResponse",f),s(f)});r.url=e,c.once("error",a),c.once("request",async f=>{n=f,s(n)})})}async _makeRequest(){var e,r,s,a,n;let{options:c}=this,{headers:f}=c;for(let U in f)if(at.default.undefined(f[U]))delete f[U];else if(at.default.null_(f[U]))throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${U}\` header`);if(c.decompress&&at.default.undefined(f["accept-encoding"])&&(f["accept-encoding"]=ydt?"gzip, deflate, br":"gzip, deflate"),c.cookieJar){let U=await c.cookieJar.getCookieString(c.url.toString());at.default.nonEmptyString(U)&&(c.headers.cookie=U)}for(let U of c.hooks.beforeRequest){let W=await U(c);if(!at.default.undefined(W)){c.request=()=>W;break}}c.body&&this[S0]!==c.body&&(this[S0]=c.body);let{agent:p,request:h,timeout:E,url:C}=c;if(c.dnsCache&&!("lookup"in c)&&(c.lookup=c.dnsCache.lookup),C.hostname==="unix"){let U=/(?.+?):(?.+)/.exec(`${C.pathname}${C.search}`);if(U?.groups){let{socketPath:W,path:te}=U.groups;Object.assign(c,{socketPath:W,path:te,host:""})}}let S=C.protocol==="https:",P;c.http2?P=ldt.auto:P=S?idt.request:Wge.request;let I=(e=c.request)!==null&&e!==void 0?e:P,R=c.cache?this._createCacheableRequest:I;p&&!c.http2&&(c.agent=p[S?"https":"http"]),c[go]=I,delete c.request,delete c.timeout;let N=c;if(N.shared=(r=c.cacheOptions)===null||r===void 0?void 0:r.shared,N.cacheHeuristic=(s=c.cacheOptions)===null||s===void 0?void 0:s.cacheHeuristic,N.immutableMinTimeToLive=(a=c.cacheOptions)===null||a===void 0?void 0:a.immutableMinTimeToLive,N.ignoreCargoCult=(n=c.cacheOptions)===null||n===void 0?void 0:n.ignoreCargoCult,c.dnsLookupIpVersion!==void 0)try{N.family=zge.dnsLookupIpVersionToFamily(c.dnsLookupIpVersion)}catch{throw new Error("Invalid `dnsLookupIpVersion` option value")}c.https&&("rejectUnauthorized"in c.https&&(N.rejectUnauthorized=c.https.rejectUnauthorized),c.https.checkServerIdentity&&(N.checkServerIdentity=c.https.checkServerIdentity),c.https.certificateAuthority&&(N.ca=c.https.certificateAuthority),c.https.certificate&&(N.cert=c.https.certificate),c.https.key&&(N.key=c.https.key),c.https.passphrase&&(N.passphrase=c.https.passphrase),c.https.pfx&&(N.pfx=c.https.pfx));try{let U=await R(C,N);at.default.undefined(U)&&(U=P(C,N)),c.request=h,c.timeout=E,c.agent=p,c.https&&("rejectUnauthorized"in c.https&&delete N.rejectUnauthorized,c.https.checkServerIdentity&&delete N.checkServerIdentity,c.https.certificateAuthority&&delete N.ca,c.https.certificate&&delete N.cert,c.https.key&&delete N.key,c.https.passphrase&&delete N.passphrase,c.https.pfx&&delete N.pfx),Idt(U)?this._onRequest(U):this.writable?(this.once("finish",()=>{this._onResponse(U)}),this._unlockWrite(),this.end(),this._lockWrite()):this._onResponse(U)}catch(U){throw U instanceof Yge.CacheError?new nT(U,this):new fs(U.message,U,this)}}async _error(e){try{for(let r of this.options.hooks.beforeError)e=await r(e)}catch(r){e=new fs(r.message,r,this)}this.destroy(e)}_beforeError(e){if(this[BI])return;let{options:r}=this,s=this.retryCount+1;this[BI]=!0,e instanceof fs||(e=new fs(e.message,e,this));let a=e,{response:n}=a;(async()=>{if(n&&!n.body){n.setEncoding(this._readableState.encoding);try{n.rawBody=await hdt.default(n),n.body=n.rawBody.toString()}catch{}}if(this.listenerCount("retry")!==0){let c;try{let f;n&&"retry-after"in n.headers&&(f=Number(n.headers["retry-after"]),Number.isNaN(f)?(f=Date.parse(n.headers["retry-after"])-Date.now(),f<=0&&(f=1)):f*=1e3),c=await r.retry.calculateDelay({attemptCount:s,retryOptions:r.retry,error:a,retryAfter:f,computedValue:mdt.default({attemptCount:s,retryOptions:r.retry,error:a,retryAfter:f,computedValue:0})})}catch(f){this._error(new fs(f.message,f,this));return}if(c){let f=async()=>{try{for(let p of this.options.hooks.beforeRetry)await p(this.options,a,s)}catch(p){this._error(new fs(p.message,e,this));return}this.destroyed||(this.destroy(),this.emit("retry",s,e))};this[tde]=setTimeout(f,c);return}}this._error(a)})()}_read(){this[$Q]=!0;let e=this[eT];if(e&&!this[BI]){e.readableLength&&(this[$Q]=!1);let r;for(;(r=e.read())!==null;){this[II]+=r.length,this[$ge]=!0;let s=this.downloadProgress;s.percent<1&&this.emit("downloadProgress",s),this.push(r)}}}_write(e,r,s){let a=()=>{this._writeRequest(e,r,s)};this.requestInitialized?a():this[pv].push(a)}_writeRequest(e,r,s){this[go].destroyed||(this._progressCallbacks.push(()=>{this[wI]+=Buffer.byteLength(e,r);let a=this.uploadProgress;a.percent<1&&this.emit("uploadProgress",a)}),this[go].write(e,r,a=>{!a&&this._progressCallbacks.length>0&&this._progressCallbacks.shift()(),s(a)}))}_final(e){let r=()=>{for(;this._progressCallbacks.length!==0;)this._progressCallbacks.shift()();if(!(go in this)){e();return}if(this[go].destroyed){e();return}this[go].end(s=>{s||(this[CI]=this[wI],this.emit("uploadProgress",this.uploadProgress),this[go].emit("upload-complete")),e(s)})};this.requestInitialized?r():this[pv].push(r)}_destroy(e,r){var s;this[BI]=!0,clearTimeout(this[tde]),go in this&&(this[mj](),!((s=this[eT])===null||s===void 0)&&s.complete||this[go].destroy()),e!==null&&!at.default.undefined(e)&&!(e instanceof fs)&&(e=new fs(e.message,e,this)),r(e)}get _isAboutToError(){return this[BI]}get ip(){var e;return(e=this.socket)===null||e===void 0?void 0:e.remoteAddress}get aborted(){var e,r,s;return((r=(e=this[go])===null||e===void 0?void 0:e.destroyed)!==null&&r!==void 0?r:this.destroyed)&&!(!((s=this[ede])===null||s===void 0)&&s.complete)}get socket(){var e,r;return(r=(e=this[go])===null||e===void 0?void 0:e.socket)!==null&&r!==void 0?r:void 0}get downloadProgress(){let e;return this[EI]?e=this[II]/this[EI]:this[EI]===this[II]?e=1:e=0,{percent:e,transferred:this[II],total:this[EI]}}get uploadProgress(){let e;return this[CI]?e=this[wI]/this[CI]:this[CI]===this[wI]?e=1:e=0,{percent:e,transferred:this[wI],total:this[CI]}}get timings(){var e;return(e=this[go])===null||e===void 0?void 0:e.timings}get isFromCache(){return this[Xge]}pipe(e,r){if(this[$ge])throw new Error("Failed to pipe. The response has been emitted already.");return e instanceof gj.ServerResponse&&this[XQ].add(e),super.pipe(e,r)}unpipe(e){return e instanceof gj.ServerResponse&&this[XQ].delete(e),super.unpipe(e),this}};Ln.default=aT});var dv=L(Wu=>{"use strict";var Sdt=Wu&&Wu.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),Ddt=Wu&&Wu.__exportStar||function(t,e){for(var r in t)r!=="default"&&!Object.prototype.hasOwnProperty.call(e,r)&&Sdt(e,t,r)};Object.defineProperty(Wu,"__esModule",{value:!0});Wu.CancelError=Wu.ParseError=void 0;var rde=gv(),Ej=class extends rde.RequestError{constructor(e,r){let{options:s}=r.request;super(`${e.message} in "${s.url.toString()}"`,e,r.request),this.name="ParseError"}};Wu.ParseError=Ej;var Ij=class extends rde.RequestError{constructor(e){super("Promise was canceled",{},e),this.name="CancelError"}get isCanceled(){return!0}};Wu.CancelError=Ij;Ddt(gv(),Wu)});var ide=L(Cj=>{"use strict";Object.defineProperty(Cj,"__esModule",{value:!0});var nde=dv(),bdt=(t,e,r,s)=>{let{rawBody:a}=t;try{if(e==="text")return a.toString(s);if(e==="json")return a.length===0?"":r(a.toString());if(e==="buffer")return a;throw new nde.ParseError({message:`Unknown body type '${e}'`,name:"Error"},t)}catch(n){throw new nde.ParseError(n,t)}};Cj.default=bdt});var wj=L(D0=>{"use strict";var Pdt=D0&&D0.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),xdt=D0&&D0.__exportStar||function(t,e){for(var r in t)r!=="default"&&!Object.prototype.hasOwnProperty.call(e,r)&&Pdt(e,t,r)};Object.defineProperty(D0,"__esModule",{value:!0});var kdt=ye("events"),Qdt=Mp(),Tdt=n0e(),lT=dv(),sde=ide(),ode=gv(),Rdt=ij(),Fdt=fj(),ade=Aj(),Ndt=["request","response","redirect","uploadProgress","downloadProgress"];function lde(t){let e,r,s=new kdt.EventEmitter,a=new Tdt((c,f,p)=>{let h=E=>{let C=new ode.default(void 0,t);C.retryCount=E,C._noPipe=!0,p(()=>C.destroy()),p.shouldReject=!1,p(()=>f(new lT.CancelError(C))),e=C,C.once("response",async I=>{var R;if(I.retryCount=E,I.request.aborted)return;let N;try{N=await Fdt.default(C),I.rawBody=N}catch{return}if(C._isAboutToError)return;let U=((R=I.headers["content-encoding"])!==null&&R!==void 0?R:"").toLowerCase(),W=["gzip","deflate","br"].includes(U),{options:te}=C;if(W&&!te.decompress)I.body=N;else try{I.body=sde.default(I,te.responseType,te.parseJson,te.encoding)}catch(ie){if(I.body=N.toString(),ade.isResponseOk(I)){C._beforeError(ie);return}}try{for(let[ie,Ae]of te.hooks.afterResponse.entries())I=await Ae(I,async ce=>{let me=ode.default.normalizeArguments(void 0,{...ce,retry:{calculateDelay:()=>0},throwHttpErrors:!1,resolveBodyOnly:!1},te);me.hooks.afterResponse=me.hooks.afterResponse.slice(0,ie);for(let Be of me.hooks.beforeRetry)await Be(me);let pe=lde(me);return p(()=>{pe.catch(()=>{}),pe.cancel()}),pe})}catch(ie){C._beforeError(new lT.RequestError(ie.message,ie,C));return}if(!ade.isResponseOk(I)){C._beforeError(new lT.HTTPError(I));return}r=I,c(C.options.resolveBodyOnly?I.body:I)});let S=I=>{if(a.isCanceled)return;let{options:R}=C;if(I instanceof lT.HTTPError&&!R.throwHttpErrors){let{response:N}=I;c(C.options.resolveBodyOnly?N.body:N);return}f(I)};C.once("error",S);let P=C.options.body;C.once("retry",(I,R)=>{var N,U;if(P===((N=R.request)===null||N===void 0?void 0:N.options.body)&&Qdt.default.nodeStream((U=R.request)===null||U===void 0?void 0:U.options.body)){S(R);return}h(I)}),Rdt.default(C,s,Ndt)};h(0)});a.on=(c,f)=>(s.on(c,f),a);let n=c=>{let f=(async()=>{await a;let{options:p}=r.request;return sde.default(r,c,p.parseJson,p.encoding)})();return Object.defineProperties(f,Object.getOwnPropertyDescriptors(a)),f};return a.json=()=>{let{headers:c}=e.options;return!e.writableFinished&&c.accept===void 0&&(c.accept="application/json"),n("json")},a.buffer=()=>n("buffer"),a.text=()=>n("text"),a}D0.default=lde;xdt(dv(),D0)});var cde=L(Bj=>{"use strict";Object.defineProperty(Bj,"__esModule",{value:!0});var Odt=dv();function Ldt(t,...e){let r=(async()=>{if(t instanceof Odt.RequestError)try{for(let a of e)if(a)for(let n of a)t=await n(t)}catch(a){t=a}throw t})(),s=()=>r;return r.json=s,r.text=s,r.buffer=s,r.on=s,r}Bj.default=Ldt});var Ade=L(vj=>{"use strict";Object.defineProperty(vj,"__esModule",{value:!0});var ude=Mp();function fde(t){for(let e of Object.values(t))(ude.default.plainObject(e)||ude.default.array(e))&&fde(e);return Object.freeze(t)}vj.default=fde});var hde=L(pde=>{"use strict";Object.defineProperty(pde,"__esModule",{value:!0})});var Sj=L(Lc=>{"use strict";var Mdt=Lc&&Lc.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),_dt=Lc&&Lc.__exportStar||function(t,e){for(var r in t)r!=="default"&&!Object.prototype.hasOwnProperty.call(e,r)&&Mdt(e,t,r)};Object.defineProperty(Lc,"__esModule",{value:!0});Lc.defaultHandler=void 0;var gde=Mp(),Oc=wj(),Udt=cde(),uT=gv(),Hdt=Ade(),jdt={RequestError:Oc.RequestError,CacheError:Oc.CacheError,ReadError:Oc.ReadError,HTTPError:Oc.HTTPError,MaxRedirectsError:Oc.MaxRedirectsError,TimeoutError:Oc.TimeoutError,ParseError:Oc.ParseError,CancelError:Oc.CancelError,UnsupportedProtocolError:Oc.UnsupportedProtocolError,UploadError:Oc.UploadError},qdt=async t=>new Promise(e=>{setTimeout(e,t)}),{normalizeArguments:cT}=uT.default,dde=(...t)=>{let e;for(let r of t)e=cT(void 0,r,e);return e},Gdt=t=>t.isStream?new uT.default(void 0,t):Oc.default(t),Wdt=t=>"defaults"in t&&"options"in t.defaults,Ydt=["get","post","put","patch","head","delete"];Lc.defaultHandler=(t,e)=>e(t);var mde=(t,e)=>{if(t)for(let r of t)r(e)},yde=t=>{t._rawHandlers=t.handlers,t.handlers=t.handlers.map(s=>(a,n)=>{let c,f=s(a,p=>(c=n(p),c));if(f!==c&&!a.isStream&&c){let p=f,{then:h,catch:E,finally:C}=p;Object.setPrototypeOf(p,Object.getPrototypeOf(c)),Object.defineProperties(p,Object.getOwnPropertyDescriptors(c)),p.then=h,p.catch=E,p.finally=C}return f});let e=(s,a={},n)=>{var c,f;let p=0,h=E=>t.handlers[p++](E,p===t.handlers.length?Gdt:h);if(gde.default.plainObject(s)){let E={...s,...a};uT.setNonEnumerableProperties([s,a],E),a=E,s=void 0}try{let E;try{mde(t.options.hooks.init,a),mde((c=a.hooks)===null||c===void 0?void 0:c.init,a)}catch(S){E=S}let C=cT(s,a,n??t.options);if(C[uT.kIsNormalizedAlready]=!0,E)throw new Oc.RequestError(E.message,E,C);return h(C)}catch(E){if(a.isStream)throw E;return Udt.default(E,t.options.hooks.beforeError,(f=a.hooks)===null||f===void 0?void 0:f.beforeError)}};e.extend=(...s)=>{let a=[t.options],n=[...t._rawHandlers],c;for(let f of s)Wdt(f)?(a.push(f.defaults.options),n.push(...f.defaults._rawHandlers),c=f.defaults.mutableDefaults):(a.push(f),"handlers"in f&&n.push(...f.handlers),c=f.mutableDefaults);return n=n.filter(f=>f!==Lc.defaultHandler),n.length===0&&n.push(Lc.defaultHandler),yde({options:dde(...a),handlers:n,mutableDefaults:!!c})};let r=async function*(s,a){let n=cT(s,a,t.options);n.resolveBodyOnly=!1;let c=n.pagination;if(!gde.default.object(c))throw new TypeError("`options.pagination` must be implemented");let f=[],{countLimit:p}=c,h=0;for(;h{let n=[];for await(let c of r(s,a))n.push(c);return n},e.paginate.each=r,e.stream=(s,a)=>e(s,{...a,isStream:!0});for(let s of Ydt)e[s]=(a,n)=>e(a,{...n,method:s}),e.stream[s]=(a,n)=>e(a,{...n,method:s,isStream:!0});return Object.assign(e,jdt),Object.defineProperty(e,"defaults",{value:t.mutableDefaults?t:Hdt.default(t),writable:t.mutableDefaults,configurable:t.mutableDefaults,enumerable:!0}),e.mergeOptions=dde,e};Lc.default=yde;_dt(hde(),Lc)});var Cde=L((_p,fT)=>{"use strict";var Vdt=_p&&_p.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),Ede=_p&&_p.__exportStar||function(t,e){for(var r in t)r!=="default"&&!Object.prototype.hasOwnProperty.call(e,r)&&Vdt(e,t,r)};Object.defineProperty(_p,"__esModule",{value:!0});var Kdt=ye("url"),Ide=Sj(),Jdt={options:{method:"GET",retry:{limit:2,methods:["GET","PUT","HEAD","DELETE","OPTIONS","TRACE"],statusCodes:[408,413,429,500,502,503,504,521,522,524],errorCodes:["ETIMEDOUT","ECONNRESET","EADDRINUSE","ECONNREFUSED","EPIPE","ENOTFOUND","ENETUNREACH","EAI_AGAIN"],maxRetryAfter:void 0,calculateDelay:({computedValue:t})=>t},timeout:{},headers:{"user-agent":"got (https://github.com/sindresorhus/got)"},hooks:{init:[],beforeRequest:[],beforeRedirect:[],beforeRetry:[],beforeError:[],afterResponse:[]},cache:void 0,dnsCache:void 0,decompress:!0,throwHttpErrors:!0,followRedirect:!0,isStream:!1,responseType:"text",resolveBodyOnly:!1,maxRedirects:10,prefixUrl:"",methodRewriting:!0,ignoreInvalidCookies:!1,context:{},http2:!1,allowGetBody:!1,https:void 0,pagination:{transform:t=>t.request.options.responseType==="json"?t.body:JSON.parse(t.body),paginate:t=>{if(!Reflect.has(t.headers,"link"))return!1;let e=t.headers.link.split(","),r;for(let s of e){let a=s.split(";");if(a[1].includes("next")){r=a[0].trimStart().trim(),r=r.slice(1,-1);break}}return r?{url:new Kdt.URL(r)}:!1},filter:()=>!0,shouldContinue:()=>!0,countLimit:1/0,backoff:0,requestLimit:1e4,stackAllItems:!0},parseJson:t=>JSON.parse(t),stringifyJson:t=>JSON.stringify(t),cacheOptions:{}},handlers:[Ide.defaultHandler],mutableDefaults:!1},Dj=Ide.default(Jdt);_p.default=Dj;fT.exports=Dj;fT.exports.default=Dj;fT.exports.__esModule=!0;Ede(Sj(),_p);Ede(wj(),_p)});var An={};Vt(An,{Method:()=>Pde,del:()=>emt,get:()=>kj,getNetworkSettings:()=>bde,post:()=>Qj,put:()=>$dt,request:()=>mv});function vde(t){let e=new URL(t),r={host:e.hostname,headers:{}};return e.port&&(r.port=Number(e.port)),e.username&&e.password&&(r.proxyAuth=`${e.username}:${e.password}`),{proxy:r}}async function bj(t){return Vl(Bde,t,()=>le.readFilePromise(t).then(e=>(Bde.set(t,e),e)))}function Xdt({statusCode:t,statusMessage:e},r){let s=Ut(r,t,Ct.NUMBER),a=`https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/${t}`;return JE(r,`${s}${e?` (${e})`:""}`,a)}async function AT(t,{configuration:e,customErrorMessage:r}){try{return await t}catch(s){if(s.name!=="HTTPError")throw s;let a=r?.(s,e)??s.response.body?.error;a==null&&(s.message.startsWith("Response code")?a="The remote server failed to provide the requested resource":a=s.message),s.code==="ETIMEDOUT"&&s.event==="socket"&&(a+=`(can be increased via ${Ut(e,"httpTimeout",Ct.SETTING)})`);let n=new Yt(35,a,c=>{s.response&&c.reportError(35,` ${Zf(e,{label:"Response Code",value:Hu(Ct.NO_HINT,Xdt(s.response,e))})}`),s.request&&(c.reportError(35,` ${Zf(e,{label:"Request Method",value:Hu(Ct.NO_HINT,s.request.options.method)})}`),c.reportError(35,` ${Zf(e,{label:"Request URL",value:Hu(Ct.URL,s.request.requestUrl)})}`)),s.request.redirects.length>0&&c.reportError(35,` ${Zf(e,{label:"Request Redirects",value:Hu(Ct.NO_HINT,I3(e,s.request.redirects,Ct.URL))})}`),s.request.retryCount===s.request.options.retry.limit&&c.reportError(35,` ${Zf(e,{label:"Request Retry Count",value:Hu(Ct.NO_HINT,`${Ut(e,s.request.retryCount,Ct.NUMBER)} (can be increased via ${Ut(e,"httpRetry",Ct.SETTING)})`)})}`)});throw n.originalError=s,n}}function bde(t,e){let r=[...e.configuration.get("networkSettings")].sort(([c],[f])=>f.length-c.length),s={enableNetwork:void 0,httpsCaFilePath:void 0,httpProxy:void 0,httpsProxy:void 0,httpsKeyFilePath:void 0,httpsCertFilePath:void 0},a=Object.keys(s),n=typeof t=="string"?new URL(t):t;for(let[c,f]of r)if(xj.default.isMatch(n.hostname,c))for(let p of a){let h=f.get(p);h!==null&&typeof s[p]>"u"&&(s[p]=h)}for(let c of a)typeof s[c]>"u"&&(s[c]=e.configuration.get(c));return s}async function mv(t,e,{configuration:r,headers:s,jsonRequest:a,jsonResponse:n,method:c="GET",wrapNetworkRequest:f}){let p={target:t,body:e,configuration:r,headers:s,jsonRequest:a,jsonResponse:n,method:c},h=async()=>await tmt(t,e,p),E=typeof f<"u"?await f(h,p):h;return await(await r.reduceHook(S=>S.wrapNetworkRequest,E,p))()}async function kj(t,{configuration:e,jsonResponse:r,customErrorMessage:s,wrapNetworkRequest:a,...n}){let c=()=>AT(mv(t,null,{configuration:e,wrapNetworkRequest:a,...n}),{configuration:e,customErrorMessage:s}).then(p=>p.body),f=await(typeof a<"u"?c():Vl(wde,t,()=>c().then(p=>(wde.set(t,p),p))));return r?JSON.parse(f.toString()):f}async function $dt(t,e,{customErrorMessage:r,...s}){return(await AT(mv(t,e,{...s,method:"PUT"}),{customErrorMessage:r,configuration:s.configuration})).body}async function Qj(t,e,{customErrorMessage:r,...s}){return(await AT(mv(t,e,{...s,method:"POST"}),{customErrorMessage:r,configuration:s.configuration})).body}async function emt(t,{customErrorMessage:e,...r}){return(await AT(mv(t,null,{...r,method:"DELETE"}),{customErrorMessage:e,configuration:r.configuration})).body}async function tmt(t,e,{configuration:r,headers:s,jsonRequest:a,jsonResponse:n,method:c="GET"}){let f=typeof t=="string"?new URL(t):t,p=bde(f,{configuration:r});if(p.enableNetwork===!1)throw new Yt(80,`Request to '${f.href}' has been blocked because of your configuration settings`);if(f.protocol==="http:"&&!xj.default.isMatch(f.hostname,r.get("unsafeHttpWhitelist")))throw new Yt(81,`Unsafe http requests must be explicitly whitelisted in your configuration (${f.hostname})`);let E={agent:{http:p.httpProxy?Pj.default.httpOverHttp(vde(p.httpProxy)):zdt,https:p.httpsProxy?Pj.default.httpsOverHttp(vde(p.httpsProxy)):Zdt},headers:s,method:c};E.responseType=n?"json":"buffer",e!==null&&(Buffer.isBuffer(e)||!a&&typeof e=="string"?E.body=e:E.json=e);let C=r.get("httpTimeout"),S=r.get("httpRetry"),P=r.get("enableStrictSsl"),I=p.httpsCaFilePath,R=p.httpsCertFilePath,N=p.httpsKeyFilePath,{default:U}=await Promise.resolve().then(()=>et(Cde())),W=I?await bj(I):void 0,te=R?await bj(R):void 0,ie=N?await bj(N):void 0,Ae=U.extend({timeout:{socket:C},retry:S,https:{rejectUnauthorized:P,certificateAuthority:W,certificate:te,key:ie},...E});return r.getLimit("networkConcurrency")(()=>Ae(f))}var Sde,Dde,xj,Pj,wde,Bde,zdt,Zdt,Pde,pT=It(()=>{bt();Sde=ye("https"),Dde=ye("http"),xj=et(Sa()),Pj=et(Xhe());Fc();Qc();kc();wde=new Map,Bde=new Map,zdt=new Dde.Agent({keepAlive:!0}),Zdt=new Sde.Agent({keepAlive:!0});Pde=(a=>(a.GET="GET",a.PUT="PUT",a.POST="POST",a.DELETE="DELETE",a))(Pde||{})});var As={};Vt(As,{availableParallelism:()=>Rj,getArchitecture:()=>yv,getArchitectureName:()=>omt,getArchitectureSet:()=>Tj,getCaller:()=>umt,major:()=>rmt,openUrl:()=>nmt});function smt(){if(process.platform==="darwin"||process.platform==="win32")return null;let t;try{t=le.readFileSync(imt)}catch{}if(typeof t<"u"){if(t&&(t.includes("GLIBC")||t.includes("libc")))return"glibc";if(t&&t.includes("musl"))return"musl"}let r=(process.report?.getReport()??{}).sharedObjects??[],s=/\/(?:(ld-linux-|[^/]+-linux-gnu\/)|(libc.musl-|ld-musl-))/;return p0(r,a=>{let n=a.match(s);if(!n)return p0.skip;if(n[1])return"glibc";if(n[2])return"musl";throw new Error("Assertion failed: Expected the libc variant to have been detected")})??null}function yv(){return kde=kde??{os:process.platform,cpu:process.arch,libc:smt()}}function omt(t=yv()){return t.libc?`${t.os}-${t.cpu}-${t.libc}`:`${t.os}-${t.cpu}`}function Tj(){let t=yv();return Qde=Qde??{os:[t.os],cpu:[t.cpu],libc:t.libc?[t.libc]:[]}}function cmt(t){let e=amt.exec(t);if(!e)return null;let r=e[2]&&e[2].indexOf("native")===0,s=e[2]&&e[2].indexOf("eval")===0,a=lmt.exec(e[2]);return s&&a!=null&&(e[2]=a[1],e[3]=a[2],e[4]=a[3]),{file:r?null:e[2],methodName:e[1]||"",arguments:r?[e[2]]:[],line:e[3]?+e[3]:null,column:e[4]?+e[4]:null}}function umt(){let e=new Error().stack.split(` +`)[3];return cmt(e)}function Rj(){return typeof hT.default.availableParallelism<"u"?hT.default.availableParallelism():Math.max(1,hT.default.cpus().length)}var hT,rmt,xde,nmt,imt,kde,Qde,amt,lmt,gT=It(()=>{bt();hT=et(ye("os"));dT();kc();rmt=Number(process.versions.node.split(".")[0]),xde=new Map([["darwin","open"],["linux","xdg-open"],["win32","explorer.exe"]]).get(process.platform),nmt=typeof xde<"u"?async t=>{try{return await Fj(xde,[t],{cwd:K.cwd()}),!0}catch{return!1}}:void 0,imt="/usr/bin/ldd";amt=/^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/|[a-z]:\\|\\\\).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,lmt=/\((\S*)(?::(\d+))(?::(\d+))\)/});function _j(t,e,r,s,a){let n=sv(r);if(s.isArray||s.type==="ANY"&&Array.isArray(n))return Array.isArray(n)?n.map((c,f)=>Nj(t,`${e}[${f}]`,c,s,a)):String(n).split(/,/).map(c=>Nj(t,e,c,s,a));if(Array.isArray(n))throw new Error(`Non-array configuration settings "${e}" cannot be an array`);return Nj(t,e,r,s,a)}function Nj(t,e,r,s,a){let n=sv(r);switch(s.type){case"ANY":return NQ(n);case"SHAPE":return hmt(t,e,r,s,a);case"MAP":return gmt(t,e,r,s,a)}if(n===null&&!s.isNullable&&s.default!==null)throw new Error(`Non-nullable configuration settings "${e}" cannot be set to null`);if(s.values?.includes(n))return n;let f=(()=>{if(s.type==="BOOLEAN"&&typeof n!="string")return qB(n);if(typeof n!="string")throw new Error(`Expected configuration setting "${e}" to be a string, got ${typeof n}`);let p=Jk(n,{env:t.env});switch(s.type){case"ABSOLUTE_PATH":{let h=a,E=fH(r);return E&&E[0]!=="<"&&(h=K.dirname(E)),K.resolve(h,ue.toPortablePath(p))}case"LOCATOR_LOOSE":return Rp(p,!1);case"NUMBER":return parseInt(p);case"LOCATOR":return Rp(p);case"BOOLEAN":return qB(p);default:return p}})();if(s.values&&!s.values.includes(f))throw new Error(`Invalid value, expected one of ${s.values.join(", ")}`);return f}function hmt(t,e,r,s,a){let n=sv(r);if(typeof n!="object"||Array.isArray(n))throw new nt(`Object configuration settings "${e}" must be an object`);let c=Uj(t,s,{ignoreArrays:!0});if(n===null)return c;for(let[f,p]of Object.entries(n)){let h=`${e}.${f}`;if(!s.properties[f])throw new nt(`Unrecognized configuration settings found: ${e}.${f} - run "yarn config -v" to see the list of settings supported in Yarn`);c.set(f,_j(t,h,p,s.properties[f],a))}return c}function gmt(t,e,r,s,a){let n=sv(r),c=new Map;if(typeof n!="object"||Array.isArray(n))throw new nt(`Map configuration settings "${e}" must be an object`);if(n===null)return c;for(let[f,p]of Object.entries(n)){let h=s.normalizeKeys?s.normalizeKeys(f):f,E=`${e}['${h}']`,C=s.valueDefinition;c.set(h,_j(t,E,p,C,a))}return c}function Uj(t,e,{ignoreArrays:r=!1}={}){switch(e.type){case"SHAPE":{if(e.isArray&&!r)return[];let s=new Map;for(let[a,n]of Object.entries(e.properties))s.set(a,Uj(t,n));return s}case"MAP":return e.isArray&&!r?[]:new Map;case"ABSOLUTE_PATH":return e.default===null?null:t.projectCwd===null?Array.isArray(e.default)?e.default.map(s=>K.normalize(s)):K.isAbsolute(e.default)?K.normalize(e.default):e.isNullable?null:void 0:Array.isArray(e.default)?e.default.map(s=>K.resolve(t.projectCwd,s)):K.resolve(t.projectCwd,e.default);default:return e.default}}function yT(t,e,r){if(e.type==="SECRET"&&typeof t=="string"&&r.hideSecrets)return pmt;if(e.type==="ABSOLUTE_PATH"&&typeof t=="string"&&r.getNativePaths)return ue.fromPortablePath(t);if(e.isArray&&Array.isArray(t)){let s=[];for(let a of t)s.push(yT(a,e,r));return s}if(e.type==="MAP"&&t instanceof Map){if(t.size===0)return;let s=new Map;for(let[a,n]of t.entries()){let c=yT(n,e.valueDefinition,r);typeof c<"u"&&s.set(a,c)}return s}if(e.type==="SHAPE"&&t instanceof Map){if(t.size===0)return;let s=new Map;for(let[a,n]of t.entries()){let c=e.properties[a],f=yT(n,c,r);typeof f<"u"&&s.set(a,f)}return s}return t}function dmt(){let t={};for(let[e,r]of Object.entries(process.env))e=e.toLowerCase(),e.startsWith(ET)&&(e=(0,Rde.default)(e.slice(ET.length)),t[e]=r);return t}function Lj(){let t=`${ET}rc_filename`;for(let[e,r]of Object.entries(process.env))if(e.toLowerCase()===t&&typeof r=="string")return r;return Mj}async function Tde(t){try{return await le.readFilePromise(t)}catch{return Buffer.of()}}async function mmt(t,e){return Buffer.compare(...await Promise.all([Tde(t),Tde(e)]))===0}async function ymt(t,e){let[r,s]=await Promise.all([le.statPromise(t),le.statPromise(e)]);return r.dev===s.dev&&r.ino===s.ino}async function Imt({configuration:t,selfPath:e}){let r=t.get("yarnPath");return t.get("ignorePath")||r===null||r===e||await Emt(r,e)?null:r}var Rde,Up,Fde,Nde,Ode,Oj,fmt,Ev,Amt,Hp,ET,Mj,pmt,Iv,Lde,IT,mT,Emt,ze,Cv=It(()=>{bt();Bc();Rde=et(lne()),Up=et(Nd());Wt();Fde=et(tie()),Nde=ye("module"),Ode=et(Md()),Oj=ye("stream");phe();oI();nH();iH();sH();Hhe();oH();tm();Yhe();LQ();Qc();I0();pT();kc();gT();Np();Yo();fmt=function(){if(!Up.GITHUB_ACTIONS||!process.env.GITHUB_EVENT_PATH)return!1;let t=ue.toPortablePath(process.env.GITHUB_EVENT_PATH),e;try{e=le.readJsonSync(t)}catch{return!1}return!(!("repository"in e)||!e.repository||(e.repository.private??!0))}(),Ev=new Set(["@yarnpkg/plugin-constraints","@yarnpkg/plugin-exec","@yarnpkg/plugin-interactive-tools","@yarnpkg/plugin-stage","@yarnpkg/plugin-typescript","@yarnpkg/plugin-version","@yarnpkg/plugin-workspace-tools"]),Amt=new Set(["isTestEnv","injectNpmUser","injectNpmPassword","injectNpm2FaToken","zipDataEpilogue","cacheCheckpointOverride","cacheVersionOverride","lockfileVersionOverride","binFolder","version","flags","profile","gpg","ignoreNode","wrapOutput","home","confDir","registry","ignoreCwd"]),Hp=/^(?!v)[a-z0-9._-]+$/i,ET="yarn_",Mj=".yarnrc.yml",pmt="********",Iv=(E=>(E.ANY="ANY",E.BOOLEAN="BOOLEAN",E.ABSOLUTE_PATH="ABSOLUTE_PATH",E.LOCATOR="LOCATOR",E.LOCATOR_LOOSE="LOCATOR_LOOSE",E.NUMBER="NUMBER",E.STRING="STRING",E.SECRET="SECRET",E.SHAPE="SHAPE",E.MAP="MAP",E))(Iv||{}),Lde=Ct,IT=(r=>(r.JUNCTIONS="junctions",r.SYMLINKS="symlinks",r))(IT||{}),mT={lastUpdateCheck:{description:"Last timestamp we checked whether new Yarn versions were available",type:"STRING",default:null},yarnPath:{description:"Path to the local executable that must be used over the global one",type:"ABSOLUTE_PATH",default:null},ignorePath:{description:"If true, the local executable will be ignored when using the global one",type:"BOOLEAN",default:!1},globalFolder:{description:"Folder where all system-global files are stored",type:"ABSOLUTE_PATH",default:pH()},cacheFolder:{description:"Folder where the cache files must be written",type:"ABSOLUTE_PATH",default:"./.yarn/cache"},compressionLevel:{description:"Zip files compression level, from 0 to 9 or mixed (a variant of 9, which stores some files uncompressed, when compression doesn't yield good results)",type:"NUMBER",values:["mixed",0,1,2,3,4,5,6,7,8,9],default:0},virtualFolder:{description:"Folder where the virtual packages (cf doc) will be mapped on the disk (must be named __virtual__)",type:"ABSOLUTE_PATH",default:"./.yarn/__virtual__"},installStatePath:{description:"Path of the file where the install state will be persisted",type:"ABSOLUTE_PATH",default:"./.yarn/install-state.gz"},immutablePatterns:{description:"Array of glob patterns; files matching them won't be allowed to change during immutable installs",type:"STRING",default:[],isArray:!0},rcFilename:{description:"Name of the files where the configuration can be found",type:"STRING",default:Lj()},enableGlobalCache:{description:"If true, the system-wide cache folder will be used regardless of `cache-folder`",type:"BOOLEAN",default:!0},cacheMigrationMode:{description:"Defines the conditions under which Yarn upgrades should cause the cache archives to be regenerated.",type:"STRING",values:["always","match-spec","required-only"],default:"always"},enableColors:{description:"If true, the CLI is allowed to use colors in its output",type:"BOOLEAN",default:Xk,defaultText:""},enableHyperlinks:{description:"If true, the CLI is allowed to use hyperlinks in its output",type:"BOOLEAN",default:E3,defaultText:""},enableInlineBuilds:{description:"If true, the CLI will print the build output on the command line",type:"BOOLEAN",default:Up.isCI,defaultText:""},enableMessageNames:{description:"If true, the CLI will prefix most messages with codes suitable for search engines",type:"BOOLEAN",default:!0},enableProgressBars:{description:"If true, the CLI is allowed to show a progress bar for long-running events",type:"BOOLEAN",default:!Up.isCI,defaultText:""},enableTimers:{description:"If true, the CLI is allowed to print the time spent executing commands",type:"BOOLEAN",default:!0},enableTips:{description:"If true, installs will print a helpful message every day of the week",type:"BOOLEAN",default:!Up.isCI,defaultText:""},preferInteractive:{description:"If true, the CLI will automatically use the interactive mode when called from a TTY",type:"BOOLEAN",default:!1},preferTruncatedLines:{description:"If true, the CLI will truncate lines that would go beyond the size of the terminal",type:"BOOLEAN",default:!1},progressBarStyle:{description:"Which style of progress bar should be used (only when progress bars are enabled)",type:"STRING",default:void 0,defaultText:""},defaultLanguageName:{description:"Default language mode that should be used when a package doesn't offer any insight",type:"STRING",default:"node"},defaultProtocol:{description:"Default resolution protocol used when resolving pure semver and tag ranges",type:"STRING",default:"npm:"},enableTransparentWorkspaces:{description:"If false, Yarn won't automatically resolve workspace dependencies unless they use the `workspace:` protocol",type:"BOOLEAN",default:!0},supportedArchitectures:{description:"Architectures that Yarn will fetch and inject into the resolver",type:"SHAPE",properties:{os:{description:"Array of supported process.platform strings, or null to target them all",type:"STRING",isArray:!0,isNullable:!0,default:["current"]},cpu:{description:"Array of supported process.arch strings, or null to target them all",type:"STRING",isArray:!0,isNullable:!0,default:["current"]},libc:{description:"Array of supported libc libraries, or null to target them all",type:"STRING",isArray:!0,isNullable:!0,default:["current"]}}},enableMirror:{description:"If true, the downloaded packages will be retrieved and stored in both the local and global folders",type:"BOOLEAN",default:!0},enableNetwork:{description:"If false, Yarn will refuse to use the network if required to",type:"BOOLEAN",default:!0},enableOfflineMode:{description:"If true, Yarn will attempt to retrieve files and metadata from the global cache rather than the network",type:"BOOLEAN",default:!1},httpProxy:{description:"URL of the http proxy that must be used for outgoing http requests",type:"STRING",default:null},httpsProxy:{description:"URL of the http proxy that must be used for outgoing https requests",type:"STRING",default:null},unsafeHttpWhitelist:{description:"List of the hostnames for which http queries are allowed (glob patterns are supported)",type:"STRING",default:[],isArray:!0},httpTimeout:{description:"Timeout of each http request in milliseconds",type:"NUMBER",default:6e4},httpRetry:{description:"Retry times on http failure",type:"NUMBER",default:3},networkConcurrency:{description:"Maximal number of concurrent requests",type:"NUMBER",default:50},taskPoolConcurrency:{description:"Maximal amount of concurrent heavy task processing",type:"NUMBER",default:Rj()},taskPoolMode:{description:"Execution strategy for heavy tasks",type:"STRING",values:["async","workers"],default:"workers"},networkSettings:{description:"Network settings per hostname (glob patterns are supported)",type:"MAP",valueDefinition:{description:"",type:"SHAPE",properties:{httpsCaFilePath:{description:"Path to file containing one or multiple Certificate Authority signing certificates",type:"ABSOLUTE_PATH",default:null},enableNetwork:{description:"If false, the package manager will refuse to use the network if required to",type:"BOOLEAN",default:null},httpProxy:{description:"URL of the http proxy that must be used for outgoing http requests",type:"STRING",default:null},httpsProxy:{description:"URL of the http proxy that must be used for outgoing https requests",type:"STRING",default:null},httpsKeyFilePath:{description:"Path to file containing private key in PEM format",type:"ABSOLUTE_PATH",default:null},httpsCertFilePath:{description:"Path to file containing certificate chain in PEM format",type:"ABSOLUTE_PATH",default:null}}}},httpsCaFilePath:{description:"A path to a file containing one or multiple Certificate Authority signing certificates",type:"ABSOLUTE_PATH",default:null},httpsKeyFilePath:{description:"Path to file containing private key in PEM format",type:"ABSOLUTE_PATH",default:null},httpsCertFilePath:{description:"Path to file containing certificate chain in PEM format",type:"ABSOLUTE_PATH",default:null},enableStrictSsl:{description:"If false, SSL certificate errors will be ignored",type:"BOOLEAN",default:!0},logFilters:{description:"Overrides for log levels",type:"SHAPE",isArray:!0,concatenateValues:!0,properties:{code:{description:"Code of the messages covered by this override",type:"STRING",default:void 0},text:{description:"Code of the texts covered by this override",type:"STRING",default:void 0},pattern:{description:"Code of the patterns covered by this override",type:"STRING",default:void 0},level:{description:"Log level override, set to null to remove override",type:"STRING",values:Object.values(eQ),isNullable:!0,default:void 0}}},enableTelemetry:{description:"If true, telemetry will be periodically sent, following the rules in https://yarnpkg.com/advanced/telemetry",type:"BOOLEAN",default:!0},telemetryInterval:{description:"Minimal amount of time between two telemetry uploads, in days",type:"NUMBER",default:7},telemetryUserId:{description:"If you desire to tell us which project you are, you can set this field. Completely optional and opt-in.",type:"STRING",default:null},enableHardenedMode:{description:"If true, automatically enable --check-resolutions --refresh-lockfile on installs",type:"BOOLEAN",default:Up.isPR&&fmt,defaultText:""},enableScripts:{description:"If true, packages are allowed to have install scripts by default",type:"BOOLEAN",default:!0},enableStrictSettings:{description:"If true, unknown settings will cause Yarn to abort",type:"BOOLEAN",default:!0},enableImmutableCache:{description:"If true, the cache is reputed immutable and actions that would modify it will throw",type:"BOOLEAN",default:!1},enableCacheClean:{description:"If false, disallows the `cache clean` command",type:"BOOLEAN",default:!0},checksumBehavior:{description:"Enumeration defining what to do when a checksum doesn't match expectations",type:"STRING",default:"throw"},injectEnvironmentFiles:{description:"List of all the environment files that Yarn should inject inside the process when it starts",type:"ABSOLUTE_PATH",default:[".env.yarn?"],isArray:!0},packageExtensions:{description:"Map of package corrections to apply on the dependency tree",type:"MAP",valueDefinition:{description:"The extension that will be applied to any package whose version matches the specified range",type:"SHAPE",properties:{dependencies:{description:"The set of dependencies that must be made available to the current package in order for it to work properly",type:"MAP",valueDefinition:{description:"A range",type:"STRING"}},peerDependencies:{description:"Inherited dependencies - the consumer of the package will be tasked to provide them",type:"MAP",valueDefinition:{description:"A semver range",type:"STRING"}},peerDependenciesMeta:{description:"Extra information related to the dependencies listed in the peerDependencies field",type:"MAP",valueDefinition:{description:"The peerDependency meta",type:"SHAPE",properties:{optional:{description:"If true, the selected peer dependency will be marked as optional by the package manager and the consumer omitting it won't be reported as an error",type:"BOOLEAN",default:!1}}}}}}}};Emt=process.platform==="win32"?mmt:ymt;ze=class t{constructor(e){this.isCI=Up.isCI;this.projectCwd=null;this.plugins=new Map;this.settings=new Map;this.values=new Map;this.sources=new Map;this.invalid=new Map;this.env={};this.limits=new Map;this.packageExtensions=null;this.startingCwd=e}static{this.deleteProperty=Symbol()}static{this.telemetry=null}static create(e,r,s){let a=new t(e);typeof r<"u"&&!(r instanceof Map)&&(a.projectCwd=r),a.importSettings(mT);let n=typeof s<"u"?s:r instanceof Map?r:new Map;for(let[c,f]of n)a.activatePlugin(c,f);return a}static async find(e,r,{strict:s=!0,usePathCheck:a=null,useRc:n=!0}={}){let c=dmt();delete c.rcFilename;let f=new t(e),p=await t.findRcFiles(e),h=await t.findFolderRcFile(AI());h&&(p.find(me=>me.path===h.path)||p.unshift(h));let E=Whe(p.map(ce=>[ce.path,ce.data])),C=vt.dot,S=new Set(Object.keys(mT)),P=({yarnPath:ce,ignorePath:me,injectEnvironmentFiles:pe})=>({yarnPath:ce,ignorePath:me,injectEnvironmentFiles:pe}),I=({yarnPath:ce,ignorePath:me,injectEnvironmentFiles:pe,...Be})=>{let Ce={};for(let[g,we]of Object.entries(Be))S.has(g)&&(Ce[g]=we);return Ce},R=({yarnPath:ce,ignorePath:me,...pe})=>{let Be={};for(let[Ce,g]of Object.entries(pe))S.has(Ce)||(Be[Ce]=g);return Be};if(f.importSettings(P(mT)),f.useWithSource("",P(c),e,{strict:!1}),E){let[ce,me]=E;f.useWithSource(ce,P(me),C,{strict:!1})}if(a){if(await Imt({configuration:f,selfPath:a})!==null)return f;f.useWithSource("",{ignorePath:!0},e,{strict:!1,overwrite:!0})}let N=await t.findProjectCwd(e);f.startingCwd=e,f.projectCwd=N;let U=Object.assign(Object.create(null),process.env);f.env=U;let W=await Promise.all(f.get("injectEnvironmentFiles").map(async ce=>{let me=ce.endsWith("?")?await le.readFilePromise(ce.slice(0,-1),"utf8").catch(()=>""):await le.readFilePromise(ce,"utf8");return(0,Fde.parse)(me)}));for(let ce of W)for(let[me,pe]of Object.entries(ce))f.env[me]=Jk(pe,{env:U});if(f.importSettings(I(mT)),f.useWithSource("",I(c),e,{strict:s}),E){let[ce,me]=E;f.useWithSource(ce,I(me),C,{strict:s})}let te=ce=>"default"in ce?ce.default:ce,ie=new Map([["@@core",Ahe]]);if(r!==null)for(let ce of r.plugins.keys())ie.set(ce,te(r.modules.get(ce)));for(let[ce,me]of ie)f.activatePlugin(ce,me);let Ae=new Map([]);if(r!==null){let ce=new Map;for(let[Be,Ce]of r.modules)ce.set(Be,()=>Ce);let me=new Set,pe=async(Be,Ce)=>{let{factory:g,name:we}=kp(Be);if(!g||me.has(we))return;let Ee=new Map(ce),fe=X=>{if((0,Nde.isBuiltin)(X))return kp(X);if(Ee.has(X))return Ee.get(X)();throw new nt(`This plugin cannot access the package referenced via ${X} which is neither a builtin, nor an exposed entry`)},se=await GE(async()=>te(await g(fe)),X=>`${X} (when initializing ${we}, defined in ${Ce})`);ce.set(we,()=>se),me.add(we),Ae.set(we,se)};if(c.plugins)for(let Be of c.plugins.split(";")){let Ce=K.resolve(e,ue.toPortablePath(Be));await pe(Ce,"")}for(let{path:Be,cwd:Ce,data:g}of p)if(n&&Array.isArray(g.plugins))for(let we of g.plugins){let Ee=typeof we!="string"?we.path:we,fe=we?.spec??"",se=we?.checksum??"";if(Ev.has(fe))continue;let X=K.resolve(Ce,ue.toPortablePath(Ee));if(!await le.existsPromise(X)){if(!fe){let gt=Ut(f,K.basename(X,".cjs"),Ct.NAME),j=Ut(f,".gitignore",Ct.NAME),rt=Ut(f,f.values.get("rcFilename"),Ct.NAME),Fe=Ut(f,"https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored",Ct.URL);throw new nt(`Missing source for the ${gt} plugin - please try to remove the plugin from ${rt} then reinstall it manually. This error usually occurs because ${j} is incorrect, check ${Fe} to make sure your plugin folder isn't gitignored.`)}if(!fe.match(/^https?:/)){let gt=Ut(f,K.basename(X,".cjs"),Ct.NAME),j=Ut(f,f.values.get("rcFilename"),Ct.NAME);throw new nt(`Failed to recognize the source for the ${gt} plugin - please try to delete the plugin from ${j} then reinstall it manually.`)}let De=await kj(fe,{configuration:f}),Re=us(De);if(se&&se!==Re){let gt=Ut(f,K.basename(X,".cjs"),Ct.NAME),j=Ut(f,f.values.get("rcFilename"),Ct.NAME),rt=Ut(f,`yarn plugin import ${fe}`,Ct.CODE);throw new nt(`Failed to fetch the ${gt} plugin from its remote location: its checksum seems to have changed. If this is expected, please remove the plugin from ${j} then run ${rt} to reimport it.`)}await le.mkdirPromise(K.dirname(X),{recursive:!0}),await le.writeFilePromise(X,De)}await pe(X,Be)}}for(let[ce,me]of Ae)f.activatePlugin(ce,me);if(f.useWithSource("",R(c),e,{strict:s}),E){let[ce,me]=E;f.useWithSource(ce,R(me),C,{strict:s})}return f.get("enableGlobalCache")&&(f.values.set("cacheFolder",`${f.get("globalFolder")}/cache`),f.sources.set("cacheFolder","")),f}static async findRcFiles(e){let r=Lj(),s=[],a=e,n=null;for(;a!==n;){n=a;let c=K.join(n,r);if(le.existsSync(c)){let f,p;try{p=await le.readFilePromise(c,"utf8"),f=ls(p)}catch{let h="";throw p?.match(/^\s+(?!-)[^:]+\s+\S+/m)&&(h=" (in particular, make sure you list the colons after each key name)"),new nt(`Parse error when loading ${c}; please check it's proper Yaml${h}`)}s.unshift({path:c,cwd:n,data:f})}a=K.dirname(n)}return s}static async findFolderRcFile(e){let r=K.join(e,Er.rc),s;try{s=await le.readFilePromise(r,"utf8")}catch(n){if(n.code==="ENOENT")return null;throw n}let a=ls(s);return{path:r,cwd:e,data:a}}static async findProjectCwd(e){let r=null,s=e,a=null;for(;s!==a;){if(a=s,le.existsSync(K.join(a,Er.lockfile)))return a;le.existsSync(K.join(a,Er.manifest))&&(r=a),s=K.dirname(a)}return r}static async updateConfiguration(e,r,s={}){let a=Lj(),n=K.join(e,a),c=le.existsSync(n)?ls(await le.readFilePromise(n,"utf8")):{},f=!1,p;if(typeof r=="function"){try{p=r(c)}catch{p=r({})}if(p===c)return!1}else{p=c;for(let h of Object.keys(r)){let E=c[h],C=r[h],S;if(typeof C=="function")try{S=C(E)}catch{S=C(void 0)}else S=C;E!==S&&(S===t.deleteProperty?delete p[h]:p[h]=S,f=!0)}if(!f)return!1}return await le.changeFilePromise(n,il(p),{automaticNewlines:!0}),!0}static async addPlugin(e,r){r.length!==0&&await t.updateConfiguration(e,s=>{let a=s.plugins??[];if(a.length===0)return{...s,plugins:r};let n=[],c=[...r];for(let f of a){let p=typeof f!="string"?f.path:f,h=c.find(E=>E.path===p);h?(n.push(h),c=c.filter(E=>E!==h)):n.push(f)}return n.push(...c),{...s,plugins:n}})}static async updateHomeConfiguration(e){let r=AI();return await t.updateConfiguration(r,e)}activatePlugin(e,r){this.plugins.set(e,r),typeof r.configuration<"u"&&this.importSettings(r.configuration)}importSettings(e){for(let[r,s]of Object.entries(e))if(s!=null){if(this.settings.has(r))throw new Error(`Cannot redefine settings "${r}"`);this.settings.set(r,s),this.values.set(r,Uj(this,s))}}useWithSource(e,r,s,a){try{this.use(e,r,s,a)}catch(n){throw n.message+=` (in ${Ut(this,e,Ct.PATH)})`,n}}use(e,r,s,{strict:a=!0,overwrite:n=!1}={}){a=a&&this.get("enableStrictSettings");for(let c of["enableStrictSettings",...Object.keys(r)]){let f=r[c],p=fH(f);if(p&&(e=p),typeof f>"u"||c==="plugins"||e===""&&Amt.has(c))continue;if(c==="rcFilename")throw new nt(`The rcFilename settings can only be set via ${`${ET}RC_FILENAME`.toUpperCase()}, not via a rc file`);let h=this.settings.get(c);if(!h){let C=AI(),S=e[0]!=="<"?K.dirname(e):null;if(a&&!(S!==null?C===S:!1))throw new nt(`Unrecognized or legacy configuration settings found: ${c} - run "yarn config -v" to see the list of settings supported in Yarn`);this.invalid.set(c,e);continue}if(this.sources.has(c)&&!(n||h.type==="MAP"||h.isArray&&h.concatenateValues))continue;let E;try{E=_j(this,c,f,h,s)}catch(C){throw C.message+=` in ${Ut(this,e,Ct.PATH)}`,C}if(c==="enableStrictSettings"&&e!==""){a=E;continue}if(h.type==="MAP"){let C=this.values.get(c);this.values.set(c,new Map(n?[...C,...E]:[...E,...C])),this.sources.set(c,`${this.sources.get(c)}, ${e}`)}else if(h.isArray&&h.concatenateValues){let C=this.values.get(c);this.values.set(c,n?[...C,...E]:[...E,...C]),this.sources.set(c,`${this.sources.get(c)}, ${e}`)}else this.values.set(c,E),this.sources.set(c,e)}}get(e){if(!this.values.has(e))throw new Error(`Invalid configuration key "${e}"`);return this.values.get(e)}getSpecial(e,{hideSecrets:r=!1,getNativePaths:s=!1}){let a=this.get(e),n=this.settings.get(e);if(typeof n>"u")throw new nt(`Couldn't find a configuration settings named "${e}"`);return yT(a,n,{hideSecrets:r,getNativePaths:s})}getSubprocessStreams(e,{header:r,prefix:s,report:a}){let n,c,f=le.createWriteStream(e);if(this.get("enableInlineBuilds")){let p=a.createStreamReporter(`${s} ${Ut(this,"STDOUT","green")}`),h=a.createStreamReporter(`${s} ${Ut(this,"STDERR","red")}`);n=new Oj.PassThrough,n.pipe(p),n.pipe(f),c=new Oj.PassThrough,c.pipe(h),c.pipe(f)}else n=f,c=f,typeof r<"u"&&n.write(`${r} +`);return{stdout:n,stderr:c}}makeResolver(){let e=[];for(let r of this.plugins.values())for(let s of r.resolvers||[])e.push(new s);return new rm([new FQ,new yi,...e])}makeFetcher(){let e=[];for(let r of this.plugins.values())for(let s of r.fetchers||[])e.push(new s);return new lI([new cI,new uI,...e])}getLinkers(){let e=[];for(let r of this.plugins.values())for(let s of r.linkers||[])e.push(new s);return e}getSupportedArchitectures(){let e=yv(),r=this.get("supportedArchitectures"),s=r.get("os");s!==null&&(s=s.map(c=>c==="current"?e.os:c));let a=r.get("cpu");a!==null&&(a=a.map(c=>c==="current"?e.cpu:c));let n=r.get("libc");return n!==null&&(n=Yl(n,c=>c==="current"?e.libc??Yl.skip:c)),{os:s,cpu:a,libc:n}}isInteractive({interactive:e,stdout:r}){return r.isTTY?e??this.get("preferInteractive"):!1}async getPackageExtensions(){if(this.packageExtensions!==null)return this.packageExtensions;this.packageExtensions=new Map;let e=this.packageExtensions,r=(s,a,{userProvided:n=!1}={})=>{if(!ul(s.range))throw new Error("Only semver ranges are allowed as keys for the packageExtensions setting");let c=new Ht;c.load(a,{yamlCompatibilityMode:!0});let f=jB(e,s.identHash),p=[];f.push([s.range,p]);let h={status:"inactive",userProvided:n,parentDescriptor:s};for(let E of c.dependencies.values())p.push({...h,type:"Dependency",descriptor:E});for(let E of c.peerDependencies.values())p.push({...h,type:"PeerDependency",descriptor:E});for(let[E,C]of c.peerDependenciesMeta)for(let[S,P]of Object.entries(C))p.push({...h,type:"PeerDependencyMeta",selector:E,key:S,value:P})};await this.triggerHook(s=>s.registerPackageExtensions,this,r);for(let[s,a]of this.get("packageExtensions"))r(C0(s,!0),Kk(a),{userProvided:!0});return e}normalizeLocator(e){return ul(e.reference)?Ys(e,`${this.get("defaultProtocol")}${e.reference}`):Hp.test(e.reference)?Ys(e,`${this.get("defaultProtocol")}${e.reference}`):e}normalizeDependency(e){return ul(e.range)?On(e,`${this.get("defaultProtocol")}${e.range}`):Hp.test(e.range)?On(e,`${this.get("defaultProtocol")}${e.range}`):e}normalizeDependencyMap(e){return new Map([...e].map(([r,s])=>[r,this.normalizeDependency(s)]))}normalizePackage(e,{packageExtensions:r}){let s=zB(e),a=r.get(e.identHash);if(typeof a<"u"){let c=e.version;if(c!==null){for(let[f,p]of a)if(eA(c,f))for(let h of p)switch(h.status==="inactive"&&(h.status="redundant"),h.type){case"Dependency":typeof s.dependencies.get(h.descriptor.identHash)>"u"&&(h.status="active",s.dependencies.set(h.descriptor.identHash,this.normalizeDependency(h.descriptor)));break;case"PeerDependency":typeof s.peerDependencies.get(h.descriptor.identHash)>"u"&&(h.status="active",s.peerDependencies.set(h.descriptor.identHash,h.descriptor));break;case"PeerDependencyMeta":{let E=s.peerDependenciesMeta.get(h.selector);(typeof E>"u"||!Object.hasOwn(E,h.key)||E[h.key]!==h.value)&&(h.status="active",Vl(s.peerDependenciesMeta,h.selector,()=>({}))[h.key]=h.value)}break;default:f3(h)}}}let n=c=>c.scope?`${c.scope}__${c.name}`:`${c.name}`;for(let c of s.peerDependenciesMeta.keys()){let f=Da(c);s.peerDependencies.has(f.identHash)||s.peerDependencies.set(f.identHash,On(f,"*"))}for(let c of s.peerDependencies.values()){if(c.scope==="types")continue;let f=n(c),p=ba("types",f),h=cn(p);s.peerDependencies.has(p.identHash)||s.peerDependenciesMeta.has(h)||(s.peerDependencies.set(p.identHash,On(p,"*")),s.peerDependenciesMeta.set(h,{optional:!0}))}return s.dependencies=new Map(Ws(s.dependencies,([,c])=>ll(c))),s.peerDependencies=new Map(Ws(s.peerDependencies,([,c])=>ll(c))),s}getLimit(e){return Vl(this.limits,e,()=>(0,Ode.default)(this.get(e)))}async triggerHook(e,...r){for(let s of this.plugins.values()){let a=s.hooks;if(!a)continue;let n=e(a);n&&await n(...r)}}async triggerMultipleHooks(e,r){for(let s of r)await this.triggerHook(e,...s)}async reduceHook(e,r,...s){let a=r;for(let n of this.plugins.values()){let c=n.hooks;if(!c)continue;let f=e(c);f&&(a=await f(a,...s))}return a}async firstHook(e,...r){for(let s of this.plugins.values()){let a=s.hooks;if(!a)continue;let n=e(a);if(!n)continue;let c=await n(...r);if(typeof c<"u")return c}return null}}});var Gr={};Vt(Gr,{EndStrategy:()=>Gj,ExecError:()=>CT,PipeError:()=>wv,execvp:()=>Fj,pipevp:()=>Yu});function om(t){return t!==null&&typeof t.fd=="number"}function Hj(){}function jj(){for(let t of am)t.kill()}async function Yu(t,e,{cwd:r,env:s=process.env,strict:a=!1,stdin:n=null,stdout:c,stderr:f,end:p=2}){let h=["pipe","pipe","pipe"];n===null?h[0]="ignore":om(n)&&(h[0]=n),om(c)&&(h[1]=c),om(f)&&(h[2]=f);let E=(0,qj.default)(t,e,{cwd:ue.fromPortablePath(r),env:{...s,PWD:ue.fromPortablePath(r)},stdio:h});am.add(E),am.size===1&&(process.on("SIGINT",Hj),process.on("SIGTERM",jj)),!om(n)&&n!==null&&n.pipe(E.stdin),om(c)||E.stdout.pipe(c,{end:!1}),om(f)||E.stderr.pipe(f,{end:!1});let C=()=>{for(let S of new Set([c,f]))om(S)||S.end()};return new Promise((S,P)=>{E.on("error",I=>{am.delete(E),am.size===0&&(process.off("SIGINT",Hj),process.off("SIGTERM",jj)),(p===2||p===1)&&C(),P(I)}),E.on("close",(I,R)=>{am.delete(E),am.size===0&&(process.off("SIGINT",Hj),process.off("SIGTERM",jj)),(p===2||p===1&&I!==0)&&C(),I===0||!a?S({code:Wj(I,R)}):P(new wv({fileName:t,code:I,signal:R}))})})}async function Fj(t,e,{cwd:r,env:s=process.env,encoding:a="utf8",strict:n=!1}){let c=["ignore","pipe","pipe"],f=[],p=[],h=ue.fromPortablePath(r);typeof s.PWD<"u"&&(s={...s,PWD:h});let E=(0,qj.default)(t,e,{cwd:h,env:s,stdio:c});return E.stdout.on("data",C=>{f.push(C)}),E.stderr.on("data",C=>{p.push(C)}),await new Promise((C,S)=>{E.on("error",P=>{let I=ze.create(r),R=Ut(I,t,Ct.PATH);S(new Yt(1,`Process ${R} failed to spawn`,N=>{N.reportError(1,` ${Zf(I,{label:"Thrown Error",value:Hu(Ct.NO_HINT,P.message)})}`)}))}),E.on("close",(P,I)=>{let R=a==="buffer"?Buffer.concat(f):Buffer.concat(f).toString(a),N=a==="buffer"?Buffer.concat(p):Buffer.concat(p).toString(a);P===0||!n?C({code:Wj(P,I),stdout:R,stderr:N}):S(new CT({fileName:t,code:P,signal:I,stdout:R,stderr:N}))})})}function Wj(t,e){let r=Cmt.get(e);return typeof r<"u"?128+r:t??1}function wmt(t,e,{configuration:r,report:s}){s.reportError(1,` ${Zf(r,t!==null?{label:"Exit Code",value:Hu(Ct.NUMBER,t)}:{label:"Exit Signal",value:Hu(Ct.CODE,e)})}`)}var qj,Gj,wv,CT,am,Cmt,dT=It(()=>{bt();qj=et(J_());Cv();Fc();Qc();Gj=(s=>(s[s.Never=0]="Never",s[s.ErrorCode=1]="ErrorCode",s[s.Always=2]="Always",s))(Gj||{}),wv=class extends Yt{constructor({fileName:e,code:r,signal:s}){let a=ze.create(K.cwd()),n=Ut(a,e,Ct.PATH);super(1,`Child ${n} reported an error`,c=>{wmt(r,s,{configuration:a,report:c})}),this.code=Wj(r,s)}},CT=class extends wv{constructor({fileName:e,code:r,signal:s,stdout:a,stderr:n}){super({fileName:e,code:r,signal:s}),this.stdout=a,this.stderr=n}};am=new Set;Cmt=new Map([["SIGINT",2],["SIGQUIT",3],["SIGKILL",9],["SIGTERM",15]])});function _de(t){Mde=t}function Bv(){return typeof Yj>"u"&&(Yj=Mde()),Yj}var Yj,Mde,Vj=It(()=>{Mde=()=>{throw new Error("Assertion failed: No libzip instance is available, and no factory was configured")}});var Ude=L((wT,Jj)=>{var Bmt=Object.assign({},ye("fs")),Kj=function(){var t=typeof document<"u"&&document.currentScript?document.currentScript.src:void 0;return typeof __filename<"u"&&(t=t||__filename),function(e){e=e||{};var r=typeof e<"u"?e:{},s,a;r.ready=new Promise(function(Je,st){s=Je,a=st});var n={},c;for(c in r)r.hasOwnProperty(c)&&(n[c]=r[c]);var f=[],p="./this.program",h=function(Je,st){throw st},E=!1,C=!0,S="";function P(Je){return r.locateFile?r.locateFile(Je,S):S+Je}var I,R,N,U;C&&(E?S=ye("path").dirname(S)+"/":S=__dirname+"/",I=function(st,St){var lr=Me(st);return lr?St?lr:lr.toString():(N||(N=Bmt),U||(U=ye("path")),st=U.normalize(st),N.readFileSync(st,St?null:"utf8"))},R=function(st){var St=I(st,!0);return St.buffer||(St=new Uint8Array(St)),we(St.buffer),St},process.argv.length>1&&(p=process.argv[1].replace(/\\/g,"/")),f=process.argv.slice(2),h=function(Je){process.exit(Je)},r.inspect=function(){return"[Emscripten Module object]"});var W=r.print||console.log.bind(console),te=r.printErr||console.warn.bind(console);for(c in n)n.hasOwnProperty(c)&&(r[c]=n[c]);n=null,r.arguments&&(f=r.arguments),r.thisProgram&&(p=r.thisProgram),r.quit&&(h=r.quit);var ie=0,Ae=function(Je){ie=Je},ce;r.wasmBinary&&(ce=r.wasmBinary);var me=r.noExitRuntime||!0;typeof WebAssembly!="object"&&rs("no native wasm support detected");function pe(Je,st,St){switch(st=st||"i8",st.charAt(st.length-1)==="*"&&(st="i32"),st){case"i1":return Ye[Je>>0];case"i8":return Ye[Je>>0];case"i16":return Ih((Je>>1)*2);case"i32":return ro((Je>>2)*4);case"i64":return ro((Je>>2)*4);case"float":return pf((Je>>2)*4);case"double":return Eh((Je>>3)*8);default:rs("invalid type for getValue: "+st)}return null}var Be,Ce=!1,g;function we(Je,st){Je||rs("Assertion failed: "+st)}function Ee(Je){var st=r["_"+Je];return we(st,"Cannot call unknown function "+Je+", make sure it is exported"),st}function fe(Je,st,St,lr,ee){var Ie={string:function(qi){var Tn=0;if(qi!=null&&qi!==0){var Ga=(qi.length<<2)+1;Tn=wi(Ga),gt(qi,Tn,Ga)}return Tn},array:function(qi){var Tn=wi(qi.length);return Fe(qi,Tn),Tn}};function Oe(qi){return st==="string"?De(qi):st==="boolean"?!!qi:qi}var ht=Ee(Je),mt=[],Dt=0;if(lr)for(var tr=0;tr=St)&&ke[lr];)++lr;return X.decode(ke.subarray(Je,lr))}function Re(Je,st,St,lr){if(!(lr>0))return 0;for(var ee=St,Ie=St+lr-1,Oe=0;Oe=55296&&ht<=57343){var mt=Je.charCodeAt(++Oe);ht=65536+((ht&1023)<<10)|mt&1023}if(ht<=127){if(St>=Ie)break;st[St++]=ht}else if(ht<=2047){if(St+1>=Ie)break;st[St++]=192|ht>>6,st[St++]=128|ht&63}else if(ht<=65535){if(St+2>=Ie)break;st[St++]=224|ht>>12,st[St++]=128|ht>>6&63,st[St++]=128|ht&63}else{if(St+3>=Ie)break;st[St++]=240|ht>>18,st[St++]=128|ht>>12&63,st[St++]=128|ht>>6&63,st[St++]=128|ht&63}}return st[St]=0,St-ee}function gt(Je,st,St){return Re(Je,ke,st,St)}function j(Je){for(var st=0,St=0;St=55296&&lr<=57343&&(lr=65536+((lr&1023)<<10)|Je.charCodeAt(++St)&1023),lr<=127?++st:lr<=2047?st+=2:lr<=65535?st+=3:st+=4}return st}function rt(Je){var st=j(Je)+1,St=Ma(st);return St&&Re(Je,Ye,St,st),St}function Fe(Je,st){Ye.set(Je,st)}function Ne(Je,st){return Je%st>0&&(Je+=st-Je%st),Je}var Pe,Ye,ke,it,_e,x,w,b,y,F;function z(Je){Pe=Je,r.HEAP_DATA_VIEW=F=new DataView(Je),r.HEAP8=Ye=new Int8Array(Je),r.HEAP16=it=new Int16Array(Je),r.HEAP32=x=new Int32Array(Je),r.HEAPU8=ke=new Uint8Array(Je),r.HEAPU16=_e=new Uint16Array(Je),r.HEAPU32=w=new Uint32Array(Je),r.HEAPF32=b=new Float32Array(Je),r.HEAPF64=y=new Float64Array(Je)}var Z=r.INITIAL_MEMORY||16777216,$,oe=[],xe=[],Te=[],lt=!1;function Et(){if(r.preRun)for(typeof r.preRun=="function"&&(r.preRun=[r.preRun]);r.preRun.length;)Pt(r.preRun.shift());Rs(oe)}function qt(){lt=!0,Rs(xe)}function ir(){if(r.postRun)for(typeof r.postRun=="function"&&(r.postRun=[r.postRun]);r.postRun.length;)Pr(r.postRun.shift());Rs(Te)}function Pt(Je){oe.unshift(Je)}function gn(Je){xe.unshift(Je)}function Pr(Je){Te.unshift(Je)}var Ir=0,Nr=null,nn=null;function oi(Je){Ir++,r.monitorRunDependencies&&r.monitorRunDependencies(Ir)}function wo(Je){if(Ir--,r.monitorRunDependencies&&r.monitorRunDependencies(Ir),Ir==0&&(Nr!==null&&(clearInterval(Nr),Nr=null),nn)){var st=nn;nn=null,st()}}r.preloadedImages={},r.preloadedAudios={};function rs(Je){r.onAbort&&r.onAbort(Je),Je+="",te(Je),Ce=!0,g=1,Je="abort("+Je+"). Build with -s ASSERTIONS=1 for more info.";var st=new WebAssembly.RuntimeError(Je);throw a(st),st}var eo="data:application/octet-stream;base64,";function Bo(Je){return Je.startsWith(eo)}var Hi="data:application/octet-stream;base64,AGFzbQEAAAAB/wEkYAN/f38Bf2ABfwF/YAJ/fwF/YAF/AGAEf39/fwF/YAN/f38AYAV/f39/fwF/YAJ/fwBgBH9/f38AYAABf2AFf39/fn8BfmAEf35/fwF/YAR/f35/AX5gAn9+AX9gA398fwBgA39/fgF/YAF/AX5gBn9/f39/fwF/YAN/fn8Bf2AEf39/fwF+YAV/f35/fwF/YAR/f35/AX9gA39/fgF+YAJ/fgBgAn9/AX5gBX9/f39/AGADf35/AX5gBX5+f35/AX5gA39/fwF+YAZ/fH9/f38Bf2AAAGAHf35/f39+fwF/YAV/fn9/fwF/YAV/f39/fwF+YAJ+fwF/YAJ/fAACJQYBYQFhAAMBYQFiAAEBYQFjAAABYQFkAAEBYQFlAAIBYQFmAAED5wHlAQMAAwEDAwEHDAgDFgcNEgEDDRcFAQ8DEAUQAwIBAhgECxkEAQMBBQsFAwMDARACBAMAAggLBwEAAwADGgQDGwYGABwBBgMTFBEHBwcVCx4ABAgHBAICAgAfAQICAgIGFSAAIQAiAAIBBgIHAg0LEw0FAQUCACMDAQAUAAAGBQECBQUDCwsSAgEDBQIHAQEICAACCQQEAQABCAEBCQoBAwkBAQEBBgEGBgYABAIEBAQGEQQEAAARAAEDCQEJAQAJCQkBAQECCgoAAAMPAQEBAwACAgICBQIABwAKBgwHAAADAgICBQEEBQFwAT8/BQcBAYACgIACBgkBfwFBgInBAgsH+gEzAWcCAAFoAFQBaQDqAQFqALsBAWsAwQEBbACpAQFtAKgBAW4ApwEBbwClAQFwAKMBAXEAoAEBcgCbAQFzAMABAXQAugEBdQC5AQF2AEsBdwDiAQF4AMgBAXkAxwEBegDCAQFBAMkBAUIAuAEBQwAGAUQACQFFAKYBAUYAtwEBRwC2AQFIALUBAUkAtAEBSgCzAQFLALIBAUwAsQEBTQCwAQFOAK8BAU8AvAEBUACuAQFRAK0BAVIArAEBUwAaAVQACwFVAKQBAVYAMgFXAQABWACrAQFZAKoBAVoAxgEBXwDFAQEkAMQBAmFhAL8BAmJhAL4BAmNhAL0BCXgBAEEBCz6iAeMBjgGQAVpbjwFYnwGdAVeeAV1coQFZVlWcAZoBmQGYAZcBlgGVAZQBkwGSAZEB6QHoAecB5gHlAeQB4QHfAeAB3gHdAdwB2gHbAYUB2QHYAdcB1gHVAdQB0wHSAdEB0AHPAc4BzQHMAcsBygE4wwEK1N8G5QHMDAEHfwJAIABFDQAgAEEIayIDIABBBGsoAgAiAUF4cSIAaiEFAkAgAUEBcQ0AIAFBA3FFDQEgAyADKAIAIgFrIgNBxIQBKAIASQ0BIAAgAWohACADQciEASgCAEcEQCABQf8BTQRAIAMoAggiAiABQQN2IgRBA3RB3IQBakYaIAIgAygCDCIBRgRAQbSEAUG0hAEoAgBBfiAEd3E2AgAMAwsgAiABNgIMIAEgAjYCCAwCCyADKAIYIQYCQCADIAMoAgwiAUcEQCADKAIIIgIgATYCDCABIAI2AggMAQsCQCADQRRqIgIoAgAiBA0AIANBEGoiAigCACIEDQBBACEBDAELA0AgAiEHIAQiAUEUaiICKAIAIgQNACABQRBqIQIgASgCECIEDQALIAdBADYCAAsgBkUNAQJAIAMgAygCHCICQQJ0QeSGAWoiBCgCAEYEQCAEIAE2AgAgAQ0BQbiEAUG4hAEoAgBBfiACd3E2AgAMAwsgBkEQQRQgBigCECADRhtqIAE2AgAgAUUNAgsgASAGNgIYIAMoAhAiAgRAIAEgAjYCECACIAE2AhgLIAMoAhQiAkUNASABIAI2AhQgAiABNgIYDAELIAUoAgQiAUEDcUEDRw0AQbyEASAANgIAIAUgAUF+cTYCBCADIABBAXI2AgQgACADaiAANgIADwsgAyAFTw0AIAUoAgQiAUEBcUUNAAJAIAFBAnFFBEAgBUHMhAEoAgBGBEBBzIQBIAM2AgBBwIQBQcCEASgCACAAaiIANgIAIAMgAEEBcjYCBCADQciEASgCAEcNA0G8hAFBADYCAEHIhAFBADYCAA8LIAVByIQBKAIARgRAQciEASADNgIAQbyEAUG8hAEoAgAgAGoiADYCACADIABBAXI2AgQgACADaiAANgIADwsgAUF4cSAAaiEAAkAgAUH/AU0EQCAFKAIIIgIgAUEDdiIEQQN0QdyEAWpGGiACIAUoAgwiAUYEQEG0hAFBtIQBKAIAQX4gBHdxNgIADAILIAIgATYCDCABIAI2AggMAQsgBSgCGCEGAkAgBSAFKAIMIgFHBEAgBSgCCCICQcSEASgCAEkaIAIgATYCDCABIAI2AggMAQsCQCAFQRRqIgIoAgAiBA0AIAVBEGoiAigCACIEDQBBACEBDAELA0AgAiEHIAQiAUEUaiICKAIAIgQNACABQRBqIQIgASgCECIEDQALIAdBADYCAAsgBkUNAAJAIAUgBSgCHCICQQJ0QeSGAWoiBCgCAEYEQCAEIAE2AgAgAQ0BQbiEAUG4hAEoAgBBfiACd3E2AgAMAgsgBkEQQRQgBigCECAFRhtqIAE2AgAgAUUNAQsgASAGNgIYIAUoAhAiAgRAIAEgAjYCECACIAE2AhgLIAUoAhQiAkUNACABIAI2AhQgAiABNgIYCyADIABBAXI2AgQgACADaiAANgIAIANByIQBKAIARw0BQbyEASAANgIADwsgBSABQX5xNgIEIAMgAEEBcjYCBCAAIANqIAA2AgALIABB/wFNBEAgAEEDdiIBQQN0QdyEAWohAAJ/QbSEASgCACICQQEgAXQiAXFFBEBBtIQBIAEgAnI2AgAgAAwBCyAAKAIICyECIAAgAzYCCCACIAM2AgwgAyAANgIMIAMgAjYCCA8LQR8hAiADQgA3AhAgAEH///8HTQRAIABBCHYiASABQYD+P2pBEHZBCHEiAXQiAiACQYDgH2pBEHZBBHEiAnQiBCAEQYCAD2pBEHZBAnEiBHRBD3YgASACciAEcmsiAUEBdCAAIAFBFWp2QQFxckEcaiECCyADIAI2AhwgAkECdEHkhgFqIQECQAJAAkBBuIQBKAIAIgRBASACdCIHcUUEQEG4hAEgBCAHcjYCACABIAM2AgAgAyABNgIYDAELIABBAEEZIAJBAXZrIAJBH0YbdCECIAEoAgAhAQNAIAEiBCgCBEF4cSAARg0CIAJBHXYhASACQQF0IQIgBCABQQRxaiIHQRBqKAIAIgENAAsgByADNgIQIAMgBDYCGAsgAyADNgIMIAMgAzYCCAwBCyAEKAIIIgAgAzYCDCAEIAM2AgggA0EANgIYIAMgBDYCDCADIAA2AggLQdSEAUHUhAEoAgBBAWsiAEF/IAAbNgIACwuDBAEDfyACQYAETwRAIAAgASACEAIaIAAPCyAAIAJqIQMCQCAAIAFzQQNxRQRAAkAgAEEDcUUEQCAAIQIMAQsgAkEBSARAIAAhAgwBCyAAIQIDQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAkEDcUUNASACIANJDQALCwJAIANBfHEiBEHAAEkNACACIARBQGoiBUsNAANAIAIgASgCADYCACACIAEoAgQ2AgQgAiABKAIINgIIIAIgASgCDDYCDCACIAEoAhA2AhAgAiABKAIUNgIUIAIgASgCGDYCGCACIAEoAhw2AhwgAiABKAIgNgIgIAIgASgCJDYCJCACIAEoAig2AiggAiABKAIsNgIsIAIgASgCMDYCMCACIAEoAjQ2AjQgAiABKAI4NgI4IAIgASgCPDYCPCABQUBrIQEgAkFAayICIAVNDQALCyACIARPDQEDQCACIAEoAgA2AgAgAUEEaiEBIAJBBGoiAiAESQ0ACwwBCyADQQRJBEAgACECDAELIAAgA0EEayIESwRAIAAhAgwBCyAAIQIDQCACIAEtAAA6AAAgAiABLQABOgABIAIgAS0AAjoAAiACIAEtAAM6AAMgAUEEaiEBIAJBBGoiAiAETQ0ACwsgAiADSQRAA0AgAiABLQAAOgAAIAFBAWohASACQQFqIgIgA0cNAAsLIAALGgAgAARAIAAtAAEEQCAAKAIEEAYLIAAQBgsLoi4BDH8jAEEQayIMJAACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAEH0AU0EQEG0hAEoAgAiBUEQIABBC2pBeHEgAEELSRsiCEEDdiICdiIBQQNxBEAgAUF/c0EBcSACaiIDQQN0IgFB5IQBaigCACIEQQhqIQACQCAEKAIIIgIgAUHchAFqIgFGBEBBtIQBIAVBfiADd3E2AgAMAQsgAiABNgIMIAEgAjYCCAsgBCADQQN0IgFBA3I2AgQgASAEaiIBIAEoAgRBAXI2AgQMDQsgCEG8hAEoAgAiCk0NASABBEACQEECIAJ0IgBBACAAa3IgASACdHEiAEEAIABrcUEBayIAIABBDHZBEHEiAnYiAUEFdkEIcSIAIAJyIAEgAHYiAUECdkEEcSIAciABIAB2IgFBAXZBAnEiAHIgASAAdiIBQQF2QQFxIgByIAEgAHZqIgNBA3QiAEHkhAFqKAIAIgQoAggiASAAQdyEAWoiAEYEQEG0hAEgBUF+IAN3cSIFNgIADAELIAEgADYCDCAAIAE2AggLIARBCGohACAEIAhBA3I2AgQgBCAIaiICIANBA3QiASAIayIDQQFyNgIEIAEgBGogAzYCACAKBEAgCkEDdiIBQQN0QdyEAWohB0HIhAEoAgAhBAJ/IAVBASABdCIBcUUEQEG0hAEgASAFcjYCACAHDAELIAcoAggLIQEgByAENgIIIAEgBDYCDCAEIAc2AgwgBCABNgIIC0HIhAEgAjYCAEG8hAEgAzYCAAwNC0G4hAEoAgAiBkUNASAGQQAgBmtxQQFrIgAgAEEMdkEQcSICdiIBQQV2QQhxIgAgAnIgASAAdiIBQQJ2QQRxIgByIAEgAHYiAUEBdkECcSIAciABIAB2IgFBAXZBAXEiAHIgASAAdmpBAnRB5IYBaigCACIBKAIEQXhxIAhrIQMgASECA0ACQCACKAIQIgBFBEAgAigCFCIARQ0BCyAAKAIEQXhxIAhrIgIgAyACIANJIgIbIQMgACABIAIbIQEgACECDAELCyABIAhqIgkgAU0NAiABKAIYIQsgASABKAIMIgRHBEAgASgCCCIAQcSEASgCAEkaIAAgBDYCDCAEIAA2AggMDAsgAUEUaiICKAIAIgBFBEAgASgCECIARQ0EIAFBEGohAgsDQCACIQcgACIEQRRqIgIoAgAiAA0AIARBEGohAiAEKAIQIgANAAsgB0EANgIADAsLQX8hCCAAQb9/Sw0AIABBC2oiAEF4cSEIQbiEASgCACIJRQ0AQQAgCGshAwJAAkACQAJ/QQAgCEGAAkkNABpBHyAIQf///wdLDQAaIABBCHYiACAAQYD+P2pBEHZBCHEiAnQiACAAQYDgH2pBEHZBBHEiAXQiACAAQYCAD2pBEHZBAnEiAHRBD3YgASACciAAcmsiAEEBdCAIIABBFWp2QQFxckEcagsiBUECdEHkhgFqKAIAIgJFBEBBACEADAELQQAhACAIQQBBGSAFQQF2ayAFQR9GG3QhAQNAAkAgAigCBEF4cSAIayIHIANPDQAgAiEEIAciAw0AQQAhAyACIQAMAwsgACACKAIUIgcgByACIAFBHXZBBHFqKAIQIgJGGyAAIAcbIQAgAUEBdCEBIAINAAsLIAAgBHJFBEBBAiAFdCIAQQAgAGtyIAlxIgBFDQMgAEEAIABrcUEBayIAIABBDHZBEHEiAnYiAUEFdkEIcSIAIAJyIAEgAHYiAUECdkEEcSIAciABIAB2IgFBAXZBAnEiAHIgASAAdiIBQQF2QQFxIgByIAEgAHZqQQJ0QeSGAWooAgAhAAsgAEUNAQsDQCAAKAIEQXhxIAhrIgEgA0khAiABIAMgAhshAyAAIAQgAhshBCAAKAIQIgEEfyABBSAAKAIUCyIADQALCyAERQ0AIANBvIQBKAIAIAhrTw0AIAQgCGoiBiAETQ0BIAQoAhghBSAEIAQoAgwiAUcEQCAEKAIIIgBBxIQBKAIASRogACABNgIMIAEgADYCCAwKCyAEQRRqIgIoAgAiAEUEQCAEKAIQIgBFDQQgBEEQaiECCwNAIAIhByAAIgFBFGoiAigCACIADQAgAUEQaiECIAEoAhAiAA0ACyAHQQA2AgAMCQsgCEG8hAEoAgAiAk0EQEHIhAEoAgAhAwJAIAIgCGsiAUEQTwRAQbyEASABNgIAQciEASADIAhqIgA2AgAgACABQQFyNgIEIAIgA2ogATYCACADIAhBA3I2AgQMAQtByIQBQQA2AgBBvIQBQQA2AgAgAyACQQNyNgIEIAIgA2oiACAAKAIEQQFyNgIECyADQQhqIQAMCwsgCEHAhAEoAgAiBkkEQEHAhAEgBiAIayIBNgIAQcyEAUHMhAEoAgAiAiAIaiIANgIAIAAgAUEBcjYCBCACIAhBA3I2AgQgAkEIaiEADAsLQQAhACAIQS9qIgkCf0GMiAEoAgAEQEGUiAEoAgAMAQtBmIgBQn83AgBBkIgBQoCggICAgAQ3AgBBjIgBIAxBDGpBcHFB2KrVqgVzNgIAQaCIAUEANgIAQfCHAUEANgIAQYAgCyIBaiIFQQAgAWsiB3EiAiAITQ0KQeyHASgCACIEBEBB5IcBKAIAIgMgAmoiASADTQ0LIAEgBEsNCwtB8IcBLQAAQQRxDQUCQAJAQcyEASgCACIDBEBB9IcBIQADQCADIAAoAgAiAU8EQCABIAAoAgRqIANLDQMLIAAoAggiAA0ACwtBABApIgFBf0YNBiACIQVBkIgBKAIAIgNBAWsiACABcQRAIAIgAWsgACABakEAIANrcWohBQsgBSAITQ0GIAVB/v///wdLDQZB7IcBKAIAIgQEQEHkhwEoAgAiAyAFaiIAIANNDQcgACAESw0HCyAFECkiACABRw0BDAgLIAUgBmsgB3EiBUH+////B0sNBSAFECkiASAAKAIAIAAoAgRqRg0EIAEhAAsCQCAAQX9GDQAgCEEwaiAFTQ0AQZSIASgCACIBIAkgBWtqQQAgAWtxIgFB/v///wdLBEAgACEBDAgLIAEQKUF/RwRAIAEgBWohBSAAIQEMCAtBACAFaxApGgwFCyAAIgFBf0cNBgwECwALQQAhBAwHC0EAIQEMBQsgAUF/Rw0CC0HwhwFB8IcBKAIAQQRyNgIACyACQf7///8HSw0BIAIQKSEBQQAQKSEAIAFBf0YNASAAQX9GDQEgACABTQ0BIAAgAWsiBSAIQShqTQ0BC0HkhwFB5IcBKAIAIAVqIgA2AgBB6IcBKAIAIABJBEBB6IcBIAA2AgALAkACQAJAQcyEASgCACIHBEBB9IcBIQADQCABIAAoAgAiAyAAKAIEIgJqRg0CIAAoAggiAA0ACwwCC0HEhAEoAgAiAEEAIAAgAU0bRQRAQcSEASABNgIAC0EAIQBB+IcBIAU2AgBB9IcBIAE2AgBB1IQBQX82AgBB2IQBQYyIASgCADYCAEGAiAFBADYCAANAIABBA3QiA0HkhAFqIANB3IQBaiICNgIAIANB6IQBaiACNgIAIABBAWoiAEEgRw0AC0HAhAEgBUEoayIDQXggAWtBB3FBACABQQhqQQdxGyIAayICNgIAQcyEASAAIAFqIgA2AgAgACACQQFyNgIEIAEgA2pBKDYCBEHQhAFBnIgBKAIANgIADAILIAAtAAxBCHENACADIAdLDQAgASAHTQ0AIAAgAiAFajYCBEHMhAEgB0F4IAdrQQdxQQAgB0EIakEHcRsiAGoiAjYCAEHAhAFBwIQBKAIAIAVqIgEgAGsiADYCACACIABBAXI2AgQgASAHakEoNgIEQdCEAUGciAEoAgA2AgAMAQtBxIQBKAIAIAFLBEBBxIQBIAE2AgALIAEgBWohAkH0hwEhAAJAAkACQAJAAkACQANAIAIgACgCAEcEQCAAKAIIIgANAQwCCwsgAC0ADEEIcUUNAQtB9IcBIQADQCAHIAAoAgAiAk8EQCACIAAoAgRqIgQgB0sNAwsgACgCCCEADAALAAsgACABNgIAIAAgACgCBCAFajYCBCABQXggAWtBB3FBACABQQhqQQdxG2oiCSAIQQNyNgIEIAJBeCACa0EHcUEAIAJBCGpBB3EbaiIFIAggCWoiBmshAiAFIAdGBEBBzIQBIAY2AgBBwIQBQcCEASgCACACaiIANgIAIAYgAEEBcjYCBAwDCyAFQciEASgCAEYEQEHIhAEgBjYCAEG8hAFBvIQBKAIAIAJqIgA2AgAgBiAAQQFyNgIEIAAgBmogADYCAAwDCyAFKAIEIgBBA3FBAUYEQCAAQXhxIQcCQCAAQf8BTQRAIAUoAggiAyAAQQN2IgBBA3RB3IQBakYaIAMgBSgCDCIBRgRAQbSEAUG0hAEoAgBBfiAAd3E2AgAMAgsgAyABNgIMIAEgAzYCCAwBCyAFKAIYIQgCQCAFIAUoAgwiAUcEQCAFKAIIIgAgATYCDCABIAA2AggMAQsCQCAFQRRqIgAoAgAiAw0AIAVBEGoiACgCACIDDQBBACEBDAELA0AgACEEIAMiAUEUaiIAKAIAIgMNACABQRBqIQAgASgCECIDDQALIARBADYCAAsgCEUNAAJAIAUgBSgCHCIDQQJ0QeSGAWoiACgCAEYEQCAAIAE2AgAgAQ0BQbiEAUG4hAEoAgBBfiADd3E2AgAMAgsgCEEQQRQgCCgCECAFRhtqIAE2AgAgAUUNAQsgASAINgIYIAUoAhAiAARAIAEgADYCECAAIAE2AhgLIAUoAhQiAEUNACABIAA2AhQgACABNgIYCyAFIAdqIQUgAiAHaiECCyAFIAUoAgRBfnE2AgQgBiACQQFyNgIEIAIgBmogAjYCACACQf8BTQRAIAJBA3YiAEEDdEHchAFqIQICf0G0hAEoAgAiAUEBIAB0IgBxRQRAQbSEASAAIAFyNgIAIAIMAQsgAigCCAshACACIAY2AgggACAGNgIMIAYgAjYCDCAGIAA2AggMAwtBHyEAIAJB////B00EQCACQQh2IgAgAEGA/j9qQRB2QQhxIgN0IgAgAEGA4B9qQRB2QQRxIgF0IgAgAEGAgA9qQRB2QQJxIgB0QQ92IAEgA3IgAHJrIgBBAXQgAiAAQRVqdkEBcXJBHGohAAsgBiAANgIcIAZCADcCECAAQQJ0QeSGAWohBAJAQbiEASgCACIDQQEgAHQiAXFFBEBBuIQBIAEgA3I2AgAgBCAGNgIAIAYgBDYCGAwBCyACQQBBGSAAQQF2ayAAQR9GG3QhACAEKAIAIQEDQCABIgMoAgRBeHEgAkYNAyAAQR12IQEgAEEBdCEAIAMgAUEEcWoiBCgCECIBDQALIAQgBjYCECAGIAM2AhgLIAYgBjYCDCAGIAY2AggMAgtBwIQBIAVBKGsiA0F4IAFrQQdxQQAgAUEIakEHcRsiAGsiAjYCAEHMhAEgACABaiIANgIAIAAgAkEBcjYCBCABIANqQSg2AgRB0IQBQZyIASgCADYCACAHIARBJyAEa0EHcUEAIARBJ2tBB3EbakEvayIAIAAgB0EQakkbIgJBGzYCBCACQfyHASkCADcCECACQfSHASkCADcCCEH8hwEgAkEIajYCAEH4hwEgBTYCAEH0hwEgATYCAEGAiAFBADYCACACQRhqIQADQCAAQQc2AgQgAEEIaiEBIABBBGohACABIARJDQALIAIgB0YNAyACIAIoAgRBfnE2AgQgByACIAdrIgRBAXI2AgQgAiAENgIAIARB/wFNBEAgBEEDdiIAQQN0QdyEAWohAgJ/QbSEASgCACIBQQEgAHQiAHFFBEBBtIQBIAAgAXI2AgAgAgwBCyACKAIICyEAIAIgBzYCCCAAIAc2AgwgByACNgIMIAcgADYCCAwEC0EfIQAgB0IANwIQIARB////B00EQCAEQQh2IgAgAEGA/j9qQRB2QQhxIgJ0IgAgAEGA4B9qQRB2QQRxIgF0IgAgAEGAgA9qQRB2QQJxIgB0QQ92IAEgAnIgAHJrIgBBAXQgBCAAQRVqdkEBcXJBHGohAAsgByAANgIcIABBAnRB5IYBaiEDAkBBuIQBKAIAIgJBASAAdCIBcUUEQEG4hAEgASACcjYCACADIAc2AgAgByADNgIYDAELIARBAEEZIABBAXZrIABBH0YbdCEAIAMoAgAhAQNAIAEiAigCBEF4cSAERg0EIABBHXYhASAAQQF0IQAgAiABQQRxaiIDKAIQIgENAAsgAyAHNgIQIAcgAjYCGAsgByAHNgIMIAcgBzYCCAwDCyADKAIIIgAgBjYCDCADIAY2AgggBkEANgIYIAYgAzYCDCAGIAA2AggLIAlBCGohAAwFCyACKAIIIgAgBzYCDCACIAc2AgggB0EANgIYIAcgAjYCDCAHIAA2AggLQcCEASgCACIAIAhNDQBBwIQBIAAgCGsiATYCAEHMhAFBzIQBKAIAIgIgCGoiADYCACAAIAFBAXI2AgQgAiAIQQNyNgIEIAJBCGohAAwDC0GEhAFBMDYCAEEAIQAMAgsCQCAFRQ0AAkAgBCgCHCICQQJ0QeSGAWoiACgCACAERgRAIAAgATYCACABDQFBuIQBIAlBfiACd3EiCTYCAAwCCyAFQRBBFCAFKAIQIARGG2ogATYCACABRQ0BCyABIAU2AhggBCgCECIABEAgASAANgIQIAAgATYCGAsgBCgCFCIARQ0AIAEgADYCFCAAIAE2AhgLAkAgA0EPTQRAIAQgAyAIaiIAQQNyNgIEIAAgBGoiACAAKAIEQQFyNgIEDAELIAQgCEEDcjYCBCAGIANBAXI2AgQgAyAGaiADNgIAIANB/wFNBEAgA0EDdiIAQQN0QdyEAWohAgJ/QbSEASgCACIBQQEgAHQiAHFFBEBBtIQBIAAgAXI2AgAgAgwBCyACKAIICyEAIAIgBjYCCCAAIAY2AgwgBiACNgIMIAYgADYCCAwBC0EfIQAgA0H///8HTQRAIANBCHYiACAAQYD+P2pBEHZBCHEiAnQiACAAQYDgH2pBEHZBBHEiAXQiACAAQYCAD2pBEHZBAnEiAHRBD3YgASACciAAcmsiAEEBdCADIABBFWp2QQFxckEcaiEACyAGIAA2AhwgBkIANwIQIABBAnRB5IYBaiECAkACQCAJQQEgAHQiAXFFBEBBuIQBIAEgCXI2AgAgAiAGNgIAIAYgAjYCGAwBCyADQQBBGSAAQQF2ayAAQR9GG3QhACACKAIAIQgDQCAIIgEoAgRBeHEgA0YNAiAAQR12IQIgAEEBdCEAIAEgAkEEcWoiAigCECIIDQALIAIgBjYCECAGIAE2AhgLIAYgBjYCDCAGIAY2AggMAQsgASgCCCIAIAY2AgwgASAGNgIIIAZBADYCGCAGIAE2AgwgBiAANgIICyAEQQhqIQAMAQsCQCALRQ0AAkAgASgCHCICQQJ0QeSGAWoiACgCACABRgRAIAAgBDYCACAEDQFBuIQBIAZBfiACd3E2AgAMAgsgC0EQQRQgCygCECABRhtqIAQ2AgAgBEUNAQsgBCALNgIYIAEoAhAiAARAIAQgADYCECAAIAQ2AhgLIAEoAhQiAEUNACAEIAA2AhQgACAENgIYCwJAIANBD00EQCABIAMgCGoiAEEDcjYCBCAAIAFqIgAgACgCBEEBcjYCBAwBCyABIAhBA3I2AgQgCSADQQFyNgIEIAMgCWogAzYCACAKBEAgCkEDdiIAQQN0QdyEAWohBEHIhAEoAgAhAgJ/QQEgAHQiACAFcUUEQEG0hAEgACAFcjYCACAEDAELIAQoAggLIQAgBCACNgIIIAAgAjYCDCACIAQ2AgwgAiAANgIIC0HIhAEgCTYCAEG8hAEgAzYCAAsgAUEIaiEACyAMQRBqJAAgAAuJAQEDfyAAKAIcIgEQMAJAIAAoAhAiAiABKAIQIgMgAiADSRsiAkUNACAAKAIMIAEoAgggAhAHGiAAIAAoAgwgAmo2AgwgASABKAIIIAJqNgIIIAAgACgCFCACajYCFCAAIAAoAhAgAms2AhAgASABKAIQIAJrIgA2AhAgAA0AIAEgASgCBDYCCAsLzgEBBX8CQCAARQ0AIAAoAjAiAQRAIAAgAUEBayIBNgIwIAENAQsgACgCIARAIABBATYCICAAEBoaCyAAKAIkQQFGBEAgABBDCwJAIAAoAiwiAUUNACAALQAoDQACQCABKAJEIgNFDQAgASgCTCEEA0AgACAEIAJBAnRqIgUoAgBHBEAgAyACQQFqIgJHDQEMAgsLIAUgBCADQQFrIgJBAnRqKAIANgIAIAEgAjYCRAsLIABBAEIAQQUQDhogACgCACIBBEAgARALCyAAEAYLC1oCAn4BfwJ/AkACQCAALQAARQ0AIAApAxAiAUJ9Vg0AIAFCAnwiAiAAKQMIWA0BCyAAQQA6AABBAAwBC0EAIAAoAgQiA0UNABogACACNwMQIAMgAadqLwAACwthAgJ+AX8CQAJAIAAtAABFDQAgACkDECICQn1WDQAgAkICfCIDIAApAwhYDQELIABBADoAAA8LIAAoAgQiBEUEQA8LIAAgAzcDECAEIAKnaiIAIAFBCHY6AAEgACABOgAAC8wCAQJ/IwBBEGsiBCQAAkAgACkDGCADrYinQQFxRQRAIABBDGoiAARAIABBADYCBCAAQRw2AgALQn8hAgwBCwJ+IAAoAgAiBUUEQCAAKAIIIAEgAiADIAAoAgQRDAAMAQsgBSAAKAIIIAEgAiADIAAoAgQRCgALIgJCf1UNAAJAIANBBGsOCwEAAAAAAAAAAAABAAsCQAJAIAAtABhBEHFFBEAgAEEMaiIBBEAgAUEANgIEIAFBHDYCAAsMAQsCfiAAKAIAIgFFBEAgACgCCCAEQQhqQghBBCAAKAIEEQwADAELIAEgACgCCCAEQQhqQghBBCAAKAIEEQoAC0J/VQ0BCyAAQQxqIgAEQCAAQQA2AgQgAEEUNgIACwwBCyAEKAIIIQEgBCgCDCEDIABBDGoiAARAIAAgAzYCBCAAIAE2AgALCyAEQRBqJAAgAguTFQIOfwN+AkACQAJAAkACQAJAAkACQAJAAkACQCAAKALwLQRAIAAoAogBQQFIDQEgACgCACIEKAIsQQJHDQQgAC8B5AENAyAALwHoAQ0DIAAvAewBDQMgAC8B8AENAyAALwH0AQ0DIAAvAfgBDQMgAC8B/AENAyAALwGcAg0DIAAvAaACDQMgAC8BpAINAyAALwGoAg0DIAAvAawCDQMgAC8BsAINAyAALwG0Ag0DIAAvAbgCDQMgAC8BvAINAyAALwHAAg0DIAAvAcQCDQMgAC8ByAINAyAALwHUAg0DIAAvAdgCDQMgAC8B3AINAyAALwHgAg0DIAAvAYgCDQIgAC8BjAINAiAALwGYAg0CQSAhBgNAIAAgBkECdCIFai8B5AENAyAAIAVBBHJqLwHkAQ0DIAAgBUEIcmovAeQBDQMgACAFQQxyai8B5AENAyAGQQRqIgZBgAJHDQALDAMLIABBBzYC/C0gAkF8Rw0FIAFFDQUMBgsgAkEFaiIEIQcMAwtBASEHCyAEIAc2AiwLIAAgAEHoFmoQUSAAIABB9BZqEFEgAC8B5gEhBCAAIABB7BZqKAIAIgxBAnRqQf//AzsB6gEgAEGQFmohECAAQZQWaiERIABBjBZqIQdBACEGIAxBAE4EQEEHQYoBIAQbIQ1BBEEDIAQbIQpBfyEJA0AgBCEIIAAgCyIOQQFqIgtBAnRqLwHmASEEAkACQCAGQQFqIgVB//8DcSIPIA1B//8DcU8NACAEIAhHDQAgBSEGDAELAn8gACAIQQJ0akHMFWogCkH//wNxIA9LDQAaIAgEQEEBIQUgByAIIAlGDQEaIAAgCEECdGpBzBVqIgYgBi8BAEEBajsBACAHDAELQQEhBSAQIBEgBkH//wNxQQpJGwsiBiAGLwEAIAVqOwEAQQAhBgJ/IARFBEBBAyEKQYoBDAELQQNBBCAEIAhGIgUbIQpBBkEHIAUbCyENIAghCQsgDCAORw0ACwsgAEHaE2ovAQAhBCAAIABB+BZqKAIAIgxBAnRqQd4TakH//wM7AQBBACEGIAxBAE4EQEEHQYoBIAQbIQ1BBEEDIAQbIQpBfyEJQQAhCwNAIAQhCCAAIAsiDkEBaiILQQJ0akHaE2ovAQAhBAJAAkAgBkEBaiIFQf//A3EiDyANQf//A3FPDQAgBCAIRw0AIAUhBgwBCwJ/IAAgCEECdGpBzBVqIApB//8DcSAPSw0AGiAIBEBBASEFIAcgCCAJRg0BGiAAIAhBAnRqQcwVaiIGIAYvAQBBAWo7AQAgBwwBC0EBIQUgECARIAZB//8DcUEKSRsLIgYgBi8BACAFajsBAEEAIQYCfyAERQRAQQMhCkGKAQwBC0EDQQQgBCAIRiIFGyEKQQZBByAFGwshDSAIIQkLIAwgDkcNAAsLIAAgAEGAF2oQUSAAIAAoAvgtAn9BEiAAQYoWai8BAA0AGkERIABB0hVqLwEADQAaQRAgAEGGFmovAQANABpBDyAAQdYVai8BAA0AGkEOIABBghZqLwEADQAaQQ0gAEHaFWovAQANABpBDCAAQf4Vai8BAA0AGkELIABB3hVqLwEADQAaQQogAEH6FWovAQANABpBCSAAQeIVai8BAA0AGkEIIABB9hVqLwEADQAaQQcgAEHmFWovAQANABpBBiAAQfIVai8BAA0AGkEFIABB6hVqLwEADQAaQQQgAEHuFWovAQANABpBA0ECIABBzhVqLwEAGwsiBkEDbGoiBEERajYC+C0gACgC/C1BCmpBA3YiByAEQRtqQQN2IgRNBEAgByEEDAELIAAoAowBQQRHDQAgByEECyAEIAJBBGpPQQAgARsNASAEIAdHDQQLIANBAmqtIRIgACkDmC4hFCAAKAKgLiIBQQNqIgdBP0sNASASIAGthiAUhCESDAILIAAgASACIAMQOQwDCyABQcAARgRAIAAoAgQgACgCEGogFDcAACAAIAAoAhBBCGo2AhBBAyEHDAELIAAoAgQgACgCEGogEiABrYYgFIQ3AAAgACAAKAIQQQhqNgIQIAFBPWshByASQcAAIAFrrYghEgsgACASNwOYLiAAIAc2AqAuIABBgMEAQYDKABCHAQwBCyADQQRqrSESIAApA5guIRQCQCAAKAKgLiIBQQNqIgRBP00EQCASIAGthiAUhCESDAELIAFBwABGBEAgACgCBCAAKAIQaiAUNwAAIAAgACgCEEEIajYCEEEDIQQMAQsgACgCBCAAKAIQaiASIAGthiAUhDcAACAAIAAoAhBBCGo2AhAgAUE9ayEEIBJBwAAgAWutiCESCyAAIBI3A5guIAAgBDYCoC4gAEHsFmooAgAiC6xCgAJ9IRMgAEH4FmooAgAhCQJAAkACfwJ+AkACfwJ/IARBOk0EQCATIASthiAShCETIARBBWoMAQsgBEHAAEYEQCAAKAIEIAAoAhBqIBI3AAAgACAAKAIQQQhqNgIQIAmsIRJCBSEUQQoMAgsgACgCBCAAKAIQaiATIASthiAShDcAACAAIAAoAhBBCGo2AhAgE0HAACAEa62IIRMgBEE7awshBSAJrCESIAVBOksNASAFrSEUIAVBBWoLIQcgEiAUhiAThAwBCyAFQcAARgRAIAAoAgQgACgCEGogEzcAACAAIAAoAhBBCGo2AhAgBq1CA30hE0IFIRRBCQwCCyAAKAIEIAAoAhBqIBIgBa2GIBOENwAAIAAgACgCEEEIajYCECAFQTtrIQcgEkHAACAFa62ICyESIAatQgN9IRMgB0E7Sw0BIAetIRQgB0EEagshBCATIBSGIBKEIRMMAQsgB0HAAEYEQCAAKAIEIAAoAhBqIBI3AAAgACAAKAIQQQhqNgIQQQQhBAwBCyAAKAIEIAAoAhBqIBMgB62GIBKENwAAIAAgACgCEEEIajYCECAHQTxrIQQgE0HAACAHa62IIRMLQQAhBQNAIAAgBSIBQZDWAGotAABBAnRqQc4VajMBACEUAn8gBEE8TQRAIBQgBK2GIBOEIRMgBEEDagwBCyAEQcAARgRAIAAoAgQgACgCEGogEzcAACAAIAAoAhBBCGo2AhAgFCETQQMMAQsgACgCBCAAKAIQaiAUIASthiAThDcAACAAIAAoAhBBCGo2AhAgFEHAACAEa62IIRMgBEE9awshBCABQQFqIQUgASAGRw0ACyAAIAQ2AqAuIAAgEzcDmC4gACAAQeQBaiICIAsQhgEgACAAQdgTaiIBIAkQhgEgACACIAEQhwELIAAQiAEgAwRAAkAgACgCoC4iBEE5TgRAIAAoAgQgACgCEGogACkDmC43AAAgACAAKAIQQQhqNgIQDAELIARBGU4EQCAAKAIEIAAoAhBqIAApA5guPgAAIAAgAEGcLmo1AgA3A5guIAAgACgCEEEEajYCECAAIAAoAqAuQSBrIgQ2AqAuCyAEQQlOBH8gACgCBCAAKAIQaiAAKQOYLj0AACAAIAAoAhBBAmo2AhAgACAAKQOYLkIQiDcDmC4gACgCoC5BEGsFIAQLQQFIDQAgACAAKAIQIgFBAWo2AhAgASAAKAIEaiAAKQOYLjwAAAsgAEEANgKgLiAAQgA3A5guCwsZACAABEAgACgCABAGIAAoAgwQBiAAEAYLC6wBAQJ+Qn8hAwJAIAAtACgNAAJAAkAgACgCIEUNACACQgBTDQAgAlANASABDQELIABBDGoiAARAIABBADYCBCAAQRI2AgALQn8PCyAALQA1DQBCACEDIAAtADQNACACUA0AA0AgACABIAOnaiACIAN9QQEQDiIEQn9XBEAgAEEBOgA1Qn8gAyADUBsPCyAEUEUEQCADIAR8IgMgAloNAgwBCwsgAEEBOgA0CyADC3UCAn4BfwJAAkAgAC0AAEUNACAAKQMQIgJCe1YNACACQgR8IgMgACkDCFgNAQsgAEEAOgAADwsgACgCBCIERQRADwsgACADNwMQIAQgAqdqIgAgAUEYdjoAAyAAIAFBEHY6AAIgACABQQh2OgABIAAgAToAAAtUAgF+AX8CQAJAIAAtAABFDQAgASAAKQMQIgF8IgIgAVQNACACIAApAwhYDQELIABBADoAAEEADwsgACgCBCIDRQRAQQAPCyAAIAI3AxAgAyABp2oLdwECfyMAQRBrIgMkAEF/IQQCQCAALQAoDQAgACgCIEEAIAJBA0kbRQRAIABBDGoiAARAIABBADYCBCAAQRI2AgALDAELIAMgAjYCCCADIAE3AwAgACADQhBBBhAOQgBTDQBBACEEIABBADoANAsgA0EQaiQAIAQLVwICfgF/AkACQCAALQAARQ0AIAApAxAiAUJ7Vg0AIAFCBHwiAiAAKQMIWA0BCyAAQQA6AABBAA8LIAAoAgQiA0UEQEEADwsgACACNwMQIAMgAadqKAAAC1UCAX4BfyAABEACQCAAKQMIUA0AQgEhAQNAIAAoAgAgAkEEdGoQPiABIAApAwhaDQEgAachAiABQgF8IQEMAAsACyAAKAIAEAYgACgCKBAQIAAQBgsLZAECfwJAAkACQCAARQRAIAGnEAkiA0UNAkEYEAkiAkUNAQwDCyAAIQNBGBAJIgINAkEADwsgAxAGC0EADwsgAkIANwMQIAIgATcDCCACIAM2AgQgAkEBOgAAIAIgAEU6AAEgAgudAQICfgF/AkACQCAALQAARQ0AIAApAxAiAkJ3Vg0AIAJCCHwiAyAAKQMIWA0BCyAAQQA6AAAPCyAAKAIEIgRFBEAPCyAAIAM3AxAgBCACp2oiACABQjiIPAAHIAAgAUIwiDwABiAAIAFCKIg8AAUgACABQiCIPAAEIAAgAUIYiDwAAyAAIAFCEIg8AAIgACABQgiIPAABIAAgATwAAAvwAgICfwF+AkAgAkUNACAAIAJqIgNBAWsgAToAACAAIAE6AAAgAkEDSQ0AIANBAmsgAToAACAAIAE6AAEgA0EDayABOgAAIAAgAToAAiACQQdJDQAgA0EEayABOgAAIAAgAToAAyACQQlJDQAgAEEAIABrQQNxIgRqIgMgAUH/AXFBgYKECGwiADYCACADIAIgBGtBfHEiAmoiAUEEayAANgIAIAJBCUkNACADIAA2AgggAyAANgIEIAFBCGsgADYCACABQQxrIAA2AgAgAkEZSQ0AIAMgADYCGCADIAA2AhQgAyAANgIQIAMgADYCDCABQRBrIAA2AgAgAUEUayAANgIAIAFBGGsgADYCACABQRxrIAA2AgAgAiADQQRxQRhyIgFrIgJBIEkNACAArUKBgICAEH4hBSABIANqIQEDQCABIAU3AxggASAFNwMQIAEgBTcDCCABIAU3AwAgAUEgaiEBIAJBIGsiAkEfSw0ACwsLbwEDfyAAQQxqIQICQAJ/IAAoAiAiAUUEQEF/IQFBEgwBCyAAIAFBAWsiAzYCIEEAIQEgAw0BIABBAEIAQQIQDhogACgCACIARQ0BIAAQGkF/Sg0BQRQLIQAgAgRAIAJBADYCBCACIAA2AgALCyABC58BAgF/AX4CfwJAAn4gACgCACIDKAIkQQFGQQAgAkJ/VRtFBEAgA0EMaiIBBEAgAUEANgIEIAFBEjYCAAtCfwwBCyADIAEgAkELEA4LIgRCf1cEQCAAKAIAIQEgAEEIaiIABEAgACABKAIMNgIAIAAgASgCEDYCBAsMAQtBACACIARRDQEaIABBCGoEQCAAQRs2AgwgAEEGNgIICwtBfwsLJAEBfyAABEADQCAAKAIAIQEgACgCDBAGIAAQBiABIgANAAsLC5gBAgJ+AX8CQAJAIAAtAABFDQAgACkDECIBQndWDQAgAUIIfCICIAApAwhYDQELIABBADoAAEIADwsgACgCBCIDRQRAQgAPCyAAIAI3AxAgAyABp2oiADEABkIwhiAAMQAHQjiGhCAAMQAFQiiGhCAAMQAEQiCGhCAAMQADQhiGhCAAMQACQhCGhCAAMQABQgiGhCAAMQAAfAsjACAAQShGBEAgAhAGDwsgAgRAIAEgAkEEaygCACAAEQcACwsyACAAKAIkQQFHBEAgAEEMaiIABEAgAEEANgIEIABBEjYCAAtCfw8LIABBAEIAQQ0QDgsPACAABEAgABA2IAAQBgsLgAEBAX8gAC0AKAR/QX8FIAFFBEAgAEEMagRAIABBADYCECAAQRI2AgwLQX8PCyABECoCQCAAKAIAIgJFDQAgAiABECFBf0oNACAAKAIAIQEgAEEMaiIABEAgACABKAIMNgIAIAAgASgCEDYCBAtBfw8LIAAgAUI4QQMQDkI/h6cLC38BA38gACEBAkAgAEEDcQRAA0AgAS0AAEUNAiABQQFqIgFBA3ENAAsLA0AgASICQQRqIQEgAigCACIDQX9zIANBgYKECGtxQYCBgoR4cUUNAAsgA0H/AXFFBEAgAiAAaw8LA0AgAi0AASEDIAJBAWoiASECIAMNAAsLIAEgAGsL3wIBCH8gAEUEQEEBDwsCQCAAKAIIIgINAEEBIQQgAC8BBCIHRQRAQQEhAgwBCyAAKAIAIQgDQAJAIAMgCGoiBS0AACICQSBPBEAgAkEYdEEYdUF/Sg0BCyACQQ1NQQBBASACdEGAzABxGw0AAn8CfyACQeABcUHAAUYEQEEBIQYgA0EBagwBCyACQfABcUHgAUYEQCADQQJqIQNBACEGQQEMAgsgAkH4AXFB8AFHBEBBBCECDAULQQAhBiADQQNqCyEDQQALIQlBBCECIAMgB08NAiAFLQABQcABcUGAAUcNAkEDIQQgBg0AIAUtAAJBwAFxQYABRw0CIAkNACAFLQADQcABcUGAAUcNAgsgBCECIANBAWoiAyAHSQ0ACwsgACACNgIIAn8CQCABRQ0AAkAgAUECRw0AIAJBA0cNAEECIQIgAEECNgIICyABIAJGDQBBBSACQQFHDQEaCyACCwtIAgJ+An8jAEEQayIEIAE2AgxCASAArYYhAgNAIAQgAUEEaiIANgIMIAIiA0IBIAEoAgAiBa2GhCECIAAhASAFQX9KDQALIAMLhwUBB38CQAJAIABFBEBBxRQhAiABRQ0BIAFBADYCAEHFFA8LIAJBwABxDQEgACgCCEUEQCAAQQAQIxoLIAAoAgghBAJAIAJBgAFxBEAgBEEBa0ECTw0BDAMLIARBBEcNAgsCQCAAKAIMIgINACAAAn8gACgCACEIIABBEGohCUEAIQICQAJAAkACQCAALwEEIgUEQEEBIQQgBUEBcSEHIAVBAUcNAQwCCyAJRQ0CIAlBADYCAEEADAQLIAVBfnEhBgNAIARBAUECQQMgAiAIai0AAEEBdEHQFGovAQAiCkGAEEkbIApBgAFJG2pBAUECQQMgCCACQQFyai0AAEEBdEHQFGovAQAiBEGAEEkbIARBgAFJG2ohBCACQQJqIQIgBkECayIGDQALCwJ/IAcEQCAEQQFBAkEDIAIgCGotAABBAXRB0BRqLwEAIgJBgBBJGyACQYABSRtqIQQLIAQLEAkiB0UNASAFQQEgBUEBSxshCkEAIQVBACEGA0AgBSAHaiEDAn8gBiAIai0AAEEBdEHQFGovAQAiAkH/AE0EQCADIAI6AAAgBUEBagwBCyACQf8PTQRAIAMgAkE/cUGAAXI6AAEgAyACQQZ2QcABcjoAACAFQQJqDAELIAMgAkE/cUGAAXI6AAIgAyACQQx2QeABcjoAACADIAJBBnZBP3FBgAFyOgABIAVBA2oLIQUgBkEBaiIGIApHDQALIAcgBEEBayICakEAOgAAIAlFDQAgCSACNgIACyAHDAELIAMEQCADQQA2AgQgA0EONgIAC0EACyICNgIMIAINAEEADwsgAUUNACABIAAoAhA2AgALIAIPCyABBEAgASAALwEENgIACyAAKAIAC4MBAQR/QRIhBQJAAkAgACkDMCABWA0AIAGnIQYgACgCQCEEIAJBCHEiB0UEQCAEIAZBBHRqKAIEIgINAgsgBCAGQQR0aiIEKAIAIgJFDQAgBC0ADEUNAUEXIQUgBw0BC0EAIQIgAyAAQQhqIAMbIgAEQCAAQQA2AgQgACAFNgIACwsgAgtuAQF/IwBBgAJrIgUkAAJAIARBgMAEcQ0AIAIgA0wNACAFIAFB/wFxIAIgA2siAkGAAiACQYACSSIBGxAZIAFFBEADQCAAIAVBgAIQLiACQYACayICQf8BSw0ACwsgACAFIAIQLgsgBUGAAmokAAuBAQEBfyMAQRBrIgQkACACIANsIQICQCAAQSdGBEAgBEEMaiACEIwBIQBBACAEKAIMIAAbIQAMAQsgAUEBIAJBxABqIAARAAAiAUUEQEEAIQAMAQtBwAAgAUE/cWsiACABakHAAEEAIABBBEkbaiIAQQRrIAE2AAALIARBEGokACAAC1IBAn9BhIEBKAIAIgEgAEEDakF8cSICaiEAAkAgAkEAIAAgAU0bDQAgAD8AQRB0SwRAIAAQA0UNAQtBhIEBIAA2AgAgAQ8LQYSEAUEwNgIAQX8LNwAgAEJ/NwMQIABBADYCCCAAQgA3AwAgAEEANgIwIABC/////w83AyggAEIANwMYIABCADcDIAulAQEBf0HYABAJIgFFBEBBAA8LAkAgAARAIAEgAEHYABAHGgwBCyABQgA3AyAgAUEANgIYIAFC/////w83AxAgAUEAOwEMIAFBv4YoNgIIIAFBAToABiABQQA6AAQgAUIANwNIIAFBgIDYjXg2AkQgAUIANwMoIAFCADcDMCABQgA3AzggAUFAa0EAOwEAIAFCADcDUAsgAUEBOgAFIAFBADYCACABC1gCAn4BfwJAAkAgAC0AAEUNACAAKQMQIgMgAq18IgQgA1QNACAEIAApAwhYDQELIABBADoAAA8LIAAoAgQiBUUEQA8LIAAgBDcDECAFIAOnaiABIAIQBxoLlgEBAn8CQAJAIAJFBEAgAacQCSIFRQ0BQRgQCSIEDQIgBRAGDAELIAIhBUEYEAkiBA0BCyADBEAgA0EANgIEIANBDjYCAAtBAA8LIARCADcDECAEIAE3AwggBCAFNgIEIARBAToAACAEIAJFOgABIAAgBSABIAMQZUEASAR/IAQtAAEEQCAEKAIEEAYLIAQQBkEABSAECwubAgEDfyAALQAAQSBxRQRAAkAgASEDAkAgAiAAIgEoAhAiAAR/IAAFAn8gASABLQBKIgBBAWsgAHI6AEogASgCACIAQQhxBEAgASAAQSByNgIAQX8MAQsgAUIANwIEIAEgASgCLCIANgIcIAEgADYCFCABIAAgASgCMGo2AhBBAAsNASABKAIQCyABKAIUIgVrSwRAIAEgAyACIAEoAiQRAAAaDAILAn8gASwAS0F/SgRAIAIhAANAIAIgACIERQ0CGiADIARBAWsiAGotAABBCkcNAAsgASADIAQgASgCJBEAACAESQ0CIAMgBGohAyABKAIUIQUgAiAEawwBCyACCyEAIAUgAyAAEAcaIAEgASgCFCAAajYCFAsLCwvNBQEGfyAAKAIwIgNBhgJrIQYgACgCPCECIAMhAQNAIAAoAkQgAiAAKAJoIgRqayECIAEgBmogBE0EQCAAKAJIIgEgASADaiADEAcaAkAgAyAAKAJsIgFNBEAgACABIANrNgJsDAELIABCADcCbAsgACAAKAJoIANrIgE2AmggACAAKAJYIANrNgJYIAEgACgChC5JBEAgACABNgKELgsgAEH8gAEoAgARAwAgAiADaiECCwJAIAAoAgAiASgCBCIERQ0AIAAoAjwhBSAAIAIgBCACIARJGyICBH8gACgCSCAAKAJoaiAFaiEFIAEgBCACazYCBAJAAkACQAJAIAEoAhwiBCgCFEEBaw4CAQACCyAEQaABaiAFIAEoAgAgAkHcgAEoAgARCAAMAgsgASABKAIwIAUgASgCACACQcSAASgCABEEADYCMAwBCyAFIAEoAgAgAhAHGgsgASABKAIAIAJqNgIAIAEgASgCCCACajYCCCAAKAI8BSAFCyACaiICNgI8AkAgACgChC4iASACakEDSQ0AIAAoAmggAWshAQJAIAAoAnRBgQhPBEAgACAAIAAoAkggAWoiAi0AACACLQABIAAoAnwRAAA2AlQMAQsgAUUNACAAIAFBAWsgACgChAERAgAaCyAAKAKELiAAKAI8IgJBAUZrIgRFDQAgACABIAQgACgCgAERBQAgACAAKAKELiAEazYChC4gACgCPCECCyACQYUCSw0AIAAoAgAoAgRFDQAgACgCMCEBDAELCwJAIAAoAkQiAiAAKAJAIgNNDQAgAAJ/IAAoAjwgACgCaGoiASADSwRAIAAoAkggAWpBACACIAFrIgNBggIgA0GCAkkbIgMQGSABIANqDAELIAFBggJqIgEgA00NASAAKAJIIANqQQAgAiADayICIAEgA2siAyACIANJGyIDEBkgACgCQCADags2AkALC50CAQF/AkAgAAJ/IAAoAqAuIgFBwABGBEAgACgCBCAAKAIQaiAAKQOYLjcAACAAQgA3A5guIAAgACgCEEEIajYCEEEADAELIAFBIE4EQCAAKAIEIAAoAhBqIAApA5guPgAAIAAgAEGcLmo1AgA3A5guIAAgACgCEEEEajYCECAAIAAoAqAuQSBrIgE2AqAuCyABQRBOBEAgACgCBCAAKAIQaiAAKQOYLj0AACAAIAAoAhBBAmo2AhAgACAAKQOYLkIQiDcDmC4gACAAKAKgLkEQayIBNgKgLgsgAUEISA0BIAAgACgCECIBQQFqNgIQ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KAJUIgEEfyABQQAgARAiEBkgACgCVAVBAAsQBiAAQQA2AlQLC9wMAgl/AX4jAEFAaiIGJAACQAJAAkACQAJAIAEoAjBBABAjIgVBAkZBACABKAI4QQAQIyIEQQFGGw0AIAVBAUZBACAEQQJGGw0AIAVBAkciAw0BIARBAkcNAQsgASABLwEMQYAQcjsBDEEAIQMMAQsgASABLwEMQf/vA3E7AQxBACEFIANFBEBB9eABIAEoAjAgAEEIahBpIgVFDQILIAJBgAJxBEAgBSEDDAELIARBAkcEQCAFIQMMAQtB9cYBIAEoAjggAEEIahBpIgNFBEAgBRAcDAILIAMgBTYCAAsgASABLwEMQf7/A3EgAS8BUiIFQQBHcjsBDAJAAkACQAJAAn8CQAJAIAEpAyhC/v///w9WDQAgASkDIEL+////D1YNACACQYAEcUUNASABKQNIQv////8PVA0BCyAFQYECa0H//wNxQQNJIQdBAQwBCyAFQYECa0H//wNxIQQgAkGACnFBgApHDQEgBEEDSSEHQQALIQkgBkIcEBciBEUEQCAAQQhqIgAEQCAAQQA2AgQgAEEONgIACyADEBwMBQsgAkGACHEhBQJAAkAgAkGAAnEEQAJAIAUNACABKQMgQv////8PVg0AIAEpAyhCgICAgBBUDQMLIAQgASkDKBAYIAEpAyAhDAwBCwJAAkACQCAFDQAgASkDIEL/////D1YNACABKQMoIgxC/////w9WDQEgASkDSEKAgICAEFQNBAsgASkDKCIMQv////8PVA0BCyAEIAwQGAsgASkDICIMQv////8PWgRAIAQgDBAYCyABKQNIIgxC/////w9UDQELIAQgDBAYCyAELQAARQRAIABBCGoiAARAIABBADYCBCAAQRQ2AgALIAQQCCADEBwMBQtBASEKQQEgBC0AAAR+IAQpAxAFQgALp0H//wNxIAYQRyEFIAQQCCAFIAM2AgAgBw0BDAILIAMhBSAEQQJLDQELIAZCBxAXIgRFBEAgAEEIaiIABEAgAEEANgIEIABBDjYCAAsgBRAcDAMLIARBAhANIARBhxJBAhAsIAQgAS0AUhBwIAQgAS8BEBANIAQtAABFBEAgAEEIaiIABEAgAEEANgIEIABBFDYCAAsgBBAIDAILQYGyAkEHIAYQRyEDIAQQCCADIAU2AgBBASEL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NgIEIABB7A42AhggGiEdDAMLIARB/wFxIgJBwABxRQRAIAggDSADLwECQQJ0aiAdp0F/IAJ0QX9zcUECdGoiAy0AASICayEIIB0gAq2IIR0gAy0AACIERQ0HDAELCyAEQSBxBEAgB0G//gA2AgQgASECDAgLIAdB0f4ANgIEIABB0A42AhggASECDAcLIB1BfyAGdEF/c62DIBt8IhunIQUgCCAEQQ9xIgNrIQggGiAErUIPg4ghHSABIBdrIgYgAjMBAiAaQX8gA3RBf3Otg3ynIgRPDQIgBCAGayIGIBlNDQEgBygCjEdFDQEgB0HR/gA2AgQgAEG5DDYCGAsgASECIAshBQwFCwJAIA5FBEAgDCARIAZraiEDDAELIAYgDk0EQCAMIA4gBmtqIQMMAQsgDCARIAYgDmsiBmtqIQMgBSAGTQ0AIAUgBmshBQJAAkAgASADTSABIA8gAWusIhogBq0iGyAaIBtUGyIapyIGaiICIANLcQ0AIAMgBmogAUsgASADT3ENACABIAMgBhAHGiACIQEMAQsgASADIAMgAWsiASABQR91IgFqIAFzIgIQByACaiEBIBogAq0iHn0iHFANACACIANqIQIDQAJAIBwgHiAcIB5UGyIbQiBUBEAgGyEaDAELIBsiGkIgfSIgQgWIQgF8QgODIh9QRQRAA0AgASACKQAANwAAIAEgAikAGDcAGCABIAIpABA3ABAgASACKQAINwAIIBpCIH0hGiACQSBqIQIgAUEgaiEBIB9CAX0iH0IAUg0ACwsgIELgAFQNAANAIAEgAikAADcAACABIAIpABg3ABggASACKQAQNwAQIAEgAikACDcACCABIAIpADg3ADggASACKQAwNwAwIAEgAikAKDcAKCABIAIpACA3ACAgASACKQBYNwBYIAEgAikAUDcAUCABIAIpAEg3AEggASACKQBANwBAIAEgAikAYDcAYCABIAIpAGg3AGggASACKQBwNwBwIAEgAikAeDcAeCACQYABaiECIAFBgAFqIQEgGkKAAX0iGkIfVg0ACwsgGkIQWgRAIAEgAikAADcAACABIAIpAAg3AAggGkIQfSEaIAJBEGohAiABQRBqIQELIBpCCFoEQCABIAIpAAA3AAAgGkIIfSEaIAJBCGohAiABQQhqIQELIBpCBFoEQCABIAIoAAA2AAAgGkIEfSEaIAJBBGohAiABQQRqIQELIBpCAloEQCABIAIvAAA7AAAgGkICfSEaIAJBAmohAiABQQJqIQELIBwgG30hHCAaUEUEQCABIAItAAA6AAAgAkEBaiECIAFBAWohAQsgHEIAUg0ACwsgDiEGIAwhAwsgBSAGSwRAAkACQCABIANNIAEgDyABa6wiGiAGrSIbIBogG1QbIhqnIglqIgIgA0txDQAgAyAJaiABSyABIANPcQ0AIAEgAyAJEAcaDAELIAEgAyADIAFrIgEgAUEfdSIBaiABcyIBEAcgAWohAiAaIAGtIh59IhxQDQAgASADaiEBA0ACQCAcIB4gHCAeVBsiG0IgVARAIBshGgwBCyAbIhpCIH0iIEIFiEIBfEIDgyIfUEUEQANAIAIgASkAADcAACACIAEpABg3ABggAiABKQAQNwAQIAIgASkACDcACCAaQiB9IRogAUEgaiEBIAJBIGohAiAfQgF9Ih9CAFINAAsLICBC4ABUDQADQCACIAEpAAA3AAAgAiABKQAYNwAYIAIgASkAEDcAECACIAEpAAg3AAggAiABKQA4NwA4IAIgASkAMDcAMCACIAEpACg3ACggAiABKQAgNwAgIAIgASkAWDcAWCACIAEpAFA3AFAgAiABKQBINwBIIAIgASkAQDcAQCACIAEpAGA3AGAgAiABKQBoNwBoIAIgASkAcDcAcCACIAEpAHg3AHggAUGAAWohASACQYABaiECIBpCgAF9IhpCH1YNAAsLIBpCEFoEQCACIAEpAAA3AAAgAiABKQAINwAIIBpCEH0hGiACQRBqIQIgAUEQaiEBCyAaQghaBEAgAiABKQAANwAAIBpCCH0hGiACQQhqIQIgAUEIaiEBCyAaQgRaBEAgAiABKAAANgAAIBpCBH0hGiACQQRqIQIgAUEEaiEBCyAaQgJaBEAgAiABLwAAOwAAIBpCAn0hGiACQQJqIQIgAUECaiEBCyAcIBt9IRwgGlBFBEAgAiABLQAAOgAAIAJBAWohAiABQQFqIQELIBxCAFINAAsLIAUgBmshAUEAIARrIQUCQCAEQQdLBEAgBCEDDAELIAEgBE0EQCAEIQMMAQsgAiAEayEFA0ACQCACIAUpAAA3AAAgBEEBdCEDIAEgBGshASACIARqIQIgBEEDSw0AIAMhBCABIANLDQELC0EAIANrIQULIAIgBWohBAJAIAUgDyACa6wiGiABrSIbIBogG1QbIhqnIgFIIAVBf0pxDQAgBUEBSCABIARqIAJLcQ0AIAIgBCABEAcgAWohAgwDCyACIAQgAyADQR91IgFqIAFzIgEQByABaiECIBogAa0iHn0iHFANAiABIARqIQEDQAJAIBwgHiAcIB5UGyIbQiBUBEAgGyEaDAELIBsiGkIgfSIgQgWIQgF8QgODIh9QRQRAA0AgAiABKQAANwAAIAIgASkAGDcAGCACIAEpABA3ABAgAiABKQAINwAIIBpCIH0hGiABQSBqIQEgAkEgaiECIB9CAX0iH0IAUg0ACwsgIELgAFQNAANAIAIgASkAADcAACACIAEpABg3ABggAiABKQAQNwAQIAIgASkACDcACCACIAEpADg3ADggAiABKQAwNwAwIAIgASkAKDcAKCACIAEpACA3ACAgAiABKQBYNwBYIAIgASkAUDcAUCACIAEpAEg3AEggAiABKQBANwBAIAIgASkAYDcAYCACIAEpAGg3AGggAiABKQBwNwBwIAIgASkAeDcAeCABQYABaiEBIAJBgAFqIQIgGkKAAX0iGkIfVg0ACwsgGkIQWgRAIAIgASkAADcAACACIAEpAAg3AAggGkIQfSEaIAJBEGohAiABQRBqIQELIBpCCFoEQCACIAEpAAA3AAAgGkIIfSEaIAJBCGohAiABQQhqIQELIBpCBFoEQCACIAEoAAA2AAAgGkIEfSEaIAJBBGohAiABQQRqIQELIBpCAloEQCACIAEvAAA7AAAgGkICfSEaIAJBAmohAiABQQJqIQELIBwgG30hHCAaUEUEQCACIAEtAAA6AAAgAkEBaiECIAFBAWohAQsgHFBFDQALDAILAkAgASADTSABIA8gAWusIhogBa0iGyAaIBtUGyIapyIEaiICIANLcQ0AIAMgBGogAUsgASADT3ENACABIAMgBBAHGgwCCyABIAMgAyABayIBIAFBH3UiAWogAXMiARAHIAFqIQIgGiABrSIefSIcUA0BIAEgA2ohAQNAAkAgHCAeIBwgHlQbIhtCIFQEQCAbIRoMAQsgGyIaQiB9IiBCBYhCAXxCA4MiH1BFBEADQCACIAEpAAA3AAAgAiABKQAYNwAYIAIgASkAEDcAECACIAEpAAg3AAggGkIgfSEaIAFBIGohASACQSBqIQIgH0IBfSIfQgBSDQALCyAgQuAAVA0AA0AgAiABKQAANwAAIAIgASkAGDcAGCACIAEpABA3ABAgAiABKQAINwAIIAIgASkAODcAOCACIAEpADA3ADAgAiABKQAoNwAoIAIgASkAIDcAICACIAEpAFg3AFggAiABKQBQNwBQIAIgASkASDcASCACIAEpAEA3AEAgAiABKQBgNwBgIAIgASkAaDcAaCACIAEpAHA3AHAgAiABKQB4NwB4IAFBgAFqIQEgAkGAAWohAiAaQoABfSIaQh9WDQALCyAaQhBaBEAgAiABKQAANwAAIAIgASkACDcACCAaQhB9IRogAkEQaiECIAFBEGohAQsgGkIIWgRAIAIgASkAADcAACAaQgh9IRogAkEIaiECIAFBCGohAQsgGkIEWgRAIAIgASgAADYAACAaQgR9IRogAkEEaiECIAFBBGohAQsgGkICWgRAIAIgAS8AADsAACAaQgJ9IRogAkECaiECIAFBAmohAQsgHCAbfSEcIBpQRQRAIAIgAS0AADoAACACQQFqIQIgAUEBaiEBCyAcUEUNAAsMAQsCQAJAIBYEQAJAIAQgBUkEQCAHKAKYRyAESw0BCyABIARrIQMCQEEAIARrIgVBf0ogDyABa6wiGiAbIBogG1QbIhqnIgIgBUpxDQAgBUEBSCACIANqIAFLcQ0AIAEgAyACEAcgAmohAgwFCyABIAMgBCAEQR91IgFqIAFzIgEQByABaiECIBogAa0iHn0iHFANBCABIANqIQEDQAJAIBwgHiAcIB5UGyIbQiBUBEAgGyEaDAELIBsiGkIgfSIgQgWIQgF8QgODIh9QRQRAA0AgAiABKQAANwAAIAIgASkAGDcAGCACIAEpABA3ABAgAiABKQAINwAIIBpCIH0hGiABQSBqIQEgAkEgaiECIB9CAX0iH0IAUg0ACwsgIELgAFQNAANAIAIgASkAADcAACACIAEpABg3ABggAiABKQAQNwAQIAIgASkACDcACCACIAEpADg3ADggAiABKQAwNwAwIAIgASkAKDcAKCACIAEpACA3ACAgAiABKQBYNwBYIAIgASkAUDcAUCACIAEpAEg3AEggAiABKQBANwBAIAIgASkAYDcAYCACIAEpAGg3AGggAiABKQBwNwBwIAIgASkAeDcAeCABQYABaiEBIAJBgAFqIQIgGkKAAX0iGkIfVg0ACwsgGkIQWgRAIAIgASkAADcAACACIAEpAAg3AAggGkIQfSEaIAJBEGohAiABQRBqIQELIBpCCFoEQCACIAEpAAA3AAAgGkIIfSEaIAJBCGohAiABQQhqIQELIBpCBFoEQCACIAEoAAA2AAAgGkIEfSEaIAJBBGohAiABQQRqIQELIBpCAloEQCACIAEvAAA7AAAgGkICfSEaIAJBAmohAiABQQJqIQELIBwgG30hHCAaUEUEQCACIAEtAAA6AAAgAkEBaiECIAFBAWohAQsgHFBFDQALDAQLIBAgAWsiCUEBaiIGIAUgBSAGSxshAyABIARrIQIgAUEHcUUNAiADRQ0CIAEgAi0AADoAACACQQFqIQIgAUEBaiIGQQdxQQAgA0EBayIFGw0BIAYhASAFIQMgCSEGDAILAkAgBCAFSQRAIAcoAphHIARLDQELIAEgASAEayIGKQAANwAAIAEgBUEBa0EHcUEBaiIDaiECIAUgA2siBEUNAyADIAZqIQEDQCACIAEpAAA3AAAgAUEIaiEBIAJBCGohAiAEQQhrIgQNAAsMAwsgASAEIAUQPyECDAILIAEgAi0AADoAASAJQQFrIQYgA0ECayEFIAJBAWohAgJAIAFBAmoiCkEHcUUNACAFRQ0AIAEgAi0AADoAAiAJQQJrIQYgA0EDayEFIAJBAWohAgJAIAFBA2oiCkEHcUUNACAFRQ0AIAEgAi0AADoAAyAJQQNrIQYgA0EEayEFIAJBAWohAgJAIAFBBGoiCkEHcUUNACAFRQ0AIAEgAi0AADoABCAJQQRrIQYgA0EFayEFIAJBAWohAgJAIAFBBWoiCkEHcUUNACAFRQ0AIAEgAi0AADoABSAJQQVrIQYgA0EGayEFIAJBAWohAgJAIAFBBmoiCkEHcUUNACAFRQ0AIAEgAi0AADoABiAJQQZrIQYgA0EHayEFIAJBAWohAgJAIAFBB2oiCkEHcUUNACAFRQ0AIAEgAi0AADoAByAJQQdrIQYgA0EIayEDIAFBCGohASACQQFqIQIMBgsgCiEBIAUhAwwFCyAKIQEgBSEDDAQLIAohASAFIQMMAwsgCiEBIAUhAwwCCyAKIQEgBSEDDAELIAohASAFIQMLAkACQCAGQRdNBEAgA0UNASADQQFrIQUgA0EHcSIEBEADQCABIAItAAA6AAAgA0EBayEDIAFBAWohASACQQFqIQIgBEEBayIEDQALCyAFQQdJDQEDQCABIAItAAA6AAAgASACLQABOgABIAEgAi0AAjoAAiABIAItAAM6AAMgASACLQAEOgAEIAEgAi0ABToABSABIAItAAY6AAYgASACLQAHOgAHIAFBCGohASACQQhqIQIgA0EIayIDDQALDAELIAMNAQsgASECDAELIAEgBCADED8hAgsgCyEFDAELIAEgAy0AAjoAACABQQFqIQILIAUgFE8NACACIBNJDQELCyAAIAI2AgwgACAFIAhBA3ZrIgE2AgAgACATIAJrQYMCajYCECAAIBQgAWtBDmo2AgQgByAIQQdxIgA2AogBIAcgHUJ/IACthkJ/hYM+AoQBC+cFAQR/IAMgAiACIANLGyEEIAAgAWshAgJAIABBB3FFDQAgBEUNACAAIAItAAA6AAAgA0EBayEGIAJBAWohAiAAQQFqIgdBB3FBACAEQQFrIgUbRQRAIAchACAFIQQgBiEDDAELIAAgAi0AADoAASADQQJrIQYgBEECayEFIAJBAWohAgJAIABBAmoiB0EHcUUNACAFRQ0AIAAgAi0AADoAAiADQQNrIQYgBEEDayEFIAJBAWohAgJAIABBA2oiB0EHcUUNACAFRQ0AIAAgAi0AADoAAyADQQRrIQYgBEEEayEFIAJBAWohAgJAIABBBGoiB0EHcUUNACAFRQ0AIAAgAi0AADoABCADQQVrIQYgBEEFayEFIAJBAWohAgJAIABBBWoiB0EHcUUNACAFRQ0AIAAgAi0AADoABSADQQZrIQYgBEEGayEFIAJBAWohAgJAIABBBmoiB0EHcUUNACAFRQ0AIAAgAi0AADoABiADQQdrIQYgBEEHayEFIAJBAWohAgJAIABBB2oiB0EHcUUNACAFRQ0AIAAgAi0AADoAByADQQhrIQMgBEEIayEEIABBCGohACACQQFqIQIMBgsgByEAIAUhBCAGIQMMBQsgByEAIAUhBCAGIQMMBAsgByEAIAUhBCAGIQMMAwsgByEAIAUhBCAGIQMMAgsgByEAIAUhBCAGIQMMAQsgByEAIAUhBCAGIQMLAkAgA0EXTQRAIARFDQEgBEEBayEBIARBB3EiAwRAA0AgACACLQAAOgAAIARBAWshBCAAQQFqIQAgAkEBaiECIANBAWsiAw0ACwsgAUEHSQ0BA0AgACACLQAAOgAAIAAgAi0AAToAASAAIAItAAI6AAIgACACLQADOgADIAAgAi0ABDoABCAAIAItAAU6AAUgACACLQAGOgAGIAAgAi0ABzoAByAAQQhqIQAgAkEIaiECIARBCGsiBA0ACwwBCyAERQ0AIAAgASAEED8hAAsgAAvyCAEXfyAAKAJoIgwgACgCMEGGAmsiBWtBACAFIAxJGyENIAAoAnQhAiAAKAKQASEPIAAoAkgiDiAMaiIJIAAoAnAiBUECIAUbIgVBAWsiBmoiAy0AASESIAMtAAAhEyAGIA5qIQZBAyEDIAAoApQBIRYgACgCPCEUIAAoAkwhECAAKAI4IRECQAJ/IAVBA0kEQCANIQggDgwBCyAAIABBACAJLQABIAAoAnwRAAAgCS0AAiAAKAJ8EQAAIQoDQCAAIAogAyAJai0AACAAKAJ8EQAAIQogACgCUCAKQQF0ai8BACIIIAEgCCABQf//A3FJIggbIQEgA0ECayAHIAgbIQcgA0EBaiIDIAVNDQALIAFB//8DcSAHIA1qIghB//8DcU0NASAGIAdB//8DcSIDayEGIA4gA2sLIQMCQAJAIAwgAUH//wNxTQ0AIAIgAkECdiAFIA9JGyEKIA1B//8DcSEVIAlBAmohDyAJQQRrIRcDQAJAAkAgBiABQf//A3EiC2otAAAgE0cNACAGIAtBAWoiAWotAAAgEkcNACADIAtqIgItAAAgCS0AAEcNACABIANqLQAAIAktAAFGDQELIApBAWsiCkUNAiAQIAsgEXFBAXRqLwEAIgEgCEH//wNxSw0BDAILIAJBAmohAUEAIQQgDyECAkADQCACLQAAIAEtAABHDQEgAi0AASABLQABRwRAIARBAXIhBAwCCyACLQACIAEtAAJHBEAgBEECciEEDAILIAItAAMgAS0AA0cEQCAEQQNyIQQMAgsgAi0ABCABLQAERwRAIARBBHIhBAwCCyACLQAFIAEtAAVHBEAgBEEFciEEDAILIAItAAYgAS0ABkcEQCAEQQZyIQQMAgsgAi0AByABLQAHRwRAIARBB3IhBAwCCyABQQhqIQEgAkEIaiECIARB+AFJIRggBEEIaiEEIBgNAAtBgAIhBAsCQAJAIAUgBEECaiICSQRAIAAgCyAHQf//A3FrIgY2AmwgAiAUSwRAIBQPCyACIBZPBEAgAg8LIAkgBEEBaiIFaiIBLQABIRIgAS0AACETAkAgAkEESQ0AIAIgBmogDE8NACAGQf//A3EhCCAEQQFrIQtBACEDQQAhBwNAIBAgAyAIaiARcUEBdGovAQAiASAGQf//A3FJBEAgAyAVaiABTw0IIAMhByABIQYLIANBAWoiAyALTQ0ACyAAIAAgAEEAIAIgF2oiAS0AACAAKAJ8EQAAIAEtAAEgACgCfBEAACABLQACIAAoAnwRAAAhASAAKAJQIAFBAXRqLwEAIgEgBkH//wNxTwRAIAdB//8DcSEDIAYhAQwDCyAEQQJrIgdB//8DcSIDIBVqIAFPDQYMAgsgAyAFaiEGIAIhBQsgCkEBayIKRQ0DIBAgCyARcUEBdGovAQAiASAIQf//A3FNDQMMAQsgByANaiEIIA4gA2siAyAFaiEGIAIhBQsgDCABQf//A3FLDQALCyAFDwsgAiEFCyAFIAAoAjwiACAAIAVLGwuGBQETfyAAKAJ0IgMgA0ECdiAAKAJwIgNBAiADGyIDIAAoApABSRshByAAKAJoIgogACgCMEGGAmsiBWtB//8DcUEAIAUgCkkbIQwgACgCSCIIIApqIgkgA0EBayICaiIFLQABIQ0gBS0AACEOIAlBAmohBSACIAhqIQsgACgClAEhEiAAKAI8IQ8gACgCTCEQIAAoAjghESAAKAKIAUEFSCETA0ACQCAKIAFB//8DcU0NAANAAkACQCALIAFB//8DcSIGai0AACAORw0AIAsgBkEBaiIBai0AACANRw0AIAYgCGoiAi0AACAJLQAARw0AIAEgCGotAAAgCS0AAUYNAQsgB0EBayIHRQ0CIAwgECAGIBFxQQF0ai8BACIBSQ0BDAILCyACQQJqIQRBACECIAUhAQJAA0AgAS0AACAELQAARw0BIAEtAAEgBC0AAUcEQCACQQFyIQIMAgsgAS0AAiAELQACRwRAIAJBAnIhAgwCCyABLQADIAQtAANHBEAgAkEDciECDAILIAEtAAQgBC0ABEcEQCACQQRyIQIMAgsgAS0ABSAELQAFRwRAIAJBBXIhAgwCCyABLQAGIAQtAAZHBEAgAkEGciECDAILIAEtAAcgBC0AB0cEQCACQQdyIQIMAgsgBEEIaiEEIAFBCGohASACQfgBSSEUIAJBCGohAiAUDQALQYACIQILAkAgAyACQQJqIgFJBEAgACAGNgJsIAEgD0sEQCAPDwsgASASTwRAIAEPCyAIIAJBAWoiA2ohCyADIAlqIgMtAAEhDSADLQAAIQ4gASEDDAELIBMNAQsgB0EBayIHRQ0AIAwgECAGIBFxQQF0ai8BACIBSQ0BCwsgAwvLAQECfwJAA0AgAC0AACABLQAARw0BIAAtAAEgAS0AAUcEQCACQQFyDwsgAC0AAiABLQACRwRAIAJBAnIPCyAALQADIAEtAANHBEAgAkEDcg8LIAAtAAQgAS0ABEcEQCACQQRyDwsgAC0ABSABLQAFRwRAIAJBBXIPCyAALQAGIAEtAAZHBEAgAkEGcg8LIAAtAAcgAS0AB0cEQCACQQdyDwsgAUEIaiEBIABBCGohACACQfgBSSEDIAJBCGohAiADDQALQYACIQILIAIL5wwBB38gAEF/cyEAIAJBF08EQAJAIAFBA3FFDQAgAS0AACAAQf8BcXNBAnRB0BhqKAIAIABBCHZzIQAgAkEBayIEQQAgAUEBaiIDQQNxG0UEQCAEIQIgAyEBDAELIAEtAAEgAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBAmohAwJAIAJBAmsiBEUNACADQQNxRQ0AIAEtAAIgAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBA2ohAwJAIAJBA2siBEUNACADQQNxRQ0AIAEtAAMgAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBBGohASACQQRrIQIMAgsgBCECIAMhAQwBCyAEIQIgAyEBCyACQRRuIgNBbGwhCQJAIANBAWsiCEUEQEEAIQQMAQsgA0EUbCABakEUayEDQQAhBANAIAEoAhAgB3MiB0EWdkH8B3FB0DhqKAIAIAdBDnZB/AdxQdAwaigCACAHQQZ2QfwHcUHQKGooAgAgB0H/AXFBAnRB0CBqKAIAc3NzIQcgASgCDCAGcyIGQRZ2QfwHcUHQOGooAgAgBkEOdkH8B3FB0DBqKAIAIAZBBnZB/AdxQdAoaigCACAGQf8BcUECdEHQIGooAgBzc3MhBiABKAIIIAVzIgVBFnZB/AdxQdA4aigCACAFQQ52QfwHcUHQMGooAgAgBUEGdkH8B3FB0ChqKAIAIAVB/wFxQQJ0QdAgaigCAHNzcyEFIAEoAgQgBHMiBEEWdkH8B3FB0DhqKAIAIARBDnZB/AdxQdAwaigCACAEQQZ2QfwHcUHQKGooAgAgBEH/AXFBAnRB0CBqKAIAc3NzIQQgASgCACAAcyIAQRZ2QfwHcUHQOGooAgAgAEEOdkH8B3FB0DBqKAIAIABBBnZB/AdxQdAoaigCACAAQf8BcUECdEHQIGooAgBzc3MhACABQRRqIQEgCEEBayIIDQALIAMhAQsgAiAJaiECIAEoAhAgASgCDCABKAIIIAEoAgQgASgCACAAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQf8BcUECdEHQGGooAgAgBHNzIABBCHZzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBB/wFxQQJ0QdAYaigCACAFc3MgAEEIdnMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEH/AXFBAnRB0BhqKAIAIAZzcyAAQQh2cyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQf8BcUECdEHQGGooAgAgB3NzIABBCHZzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyEAIAFBFGohAQsgAkEHSwRAA0AgAS0AByABLQAGIAEtAAUgAS0ABCABLQADIAEtAAIgAS0AASABLQAAIABB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyIAQf8BcXNBAnRB0BhqKAIAIABBCHZzIgBB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyIAQf8BcXNBAnRB0BhqKAIAIABBCHZzIgBB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBCGohASACQQhrIgJBB0sNAAsLAkAgAkUNACACQQFxBH8gAS0AACAAQf8BcXNBAnRB0BhqKAIAIABBCHZzIQAgAUEBaiEBIAJBAWsFIAILIQMgAkEBRg0AA0AgAS0AASABLQAAIABB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBAmohASADQQJrIgMNAAsLIABBf3MLwgIBA38jAEEQayIIJAACfwJAIAAEQCAEDQEgBVANAQsgBgRAIAZBADYCBCAGQRI2AgALQQAMAQtBgAEQCSIHRQRAIAYEQCAGQQA2AgQgBkEONgIAC0EADAELIAcgATcDCCAHQgA3AwAgB0EoaiIJECogByAFNwMYIAcgBDYCECAHIAM6AGAgB0EANgJsIAdCADcCZCAAKQMYIQEgCEF/NgIIIAhCjoCAgPAANwMAIAdBECAIECQgAUL/gQGDhCIBNwNwIAcgAadBBnZBAXE6AHgCQCACRQ0AIAkgAhBgQX9KDQAgBxAGQQAMAQsgBhBfIgIEQCAAIAAoAjBBAWo2AjAgAiAHNgIIIAJBATYCBCACIAA2AgAgAkI/IAAgB0EAQgBBDkEBEQoAIgEgAUIAUxs3AxgLIAILIQAgCEEQaiQAIAALYgEBf0E4EAkiAUUEQCAABEAgAEEANgIEIABBDjYCAAtBAA8LIAFBADYCCCABQgA3AwAgAUIANwMgIAFCgICAgBA3AiwgAUEAOgAoIAFBADYCFCABQgA3AgwgAUEAOwE0IAELuwEBAX4gASkDACICQgKDUEUEQCAAIAEpAxA3AxALIAJCBINQRQRAIAAgASkDGDcDGAsgAkIIg1BFBEAgACABKQMgNwMgCyACQhCDUEUEQCAAIAEoAig2AigLIAJCIINQRQRAIAAgASgCLDYCLAsgAkLAAINQRQRAIAAgAS8BMDsBMAsgAkKAAYNQRQRAIAAgAS8BMjsBMgsgAkKAAoNQRQRAIAAgASgCNDYCNAsgACAAKQMAIAKENwMAQQALGQAgAUUEQEEADwsgACABKAIAIAEzAQQQGws3AQJ/IABBACABG0UEQCAAIAFGDwsgAC8BBCIDIAEvAQRGBH8gACgCACABKAIAIAMQPQVBAQtFCyIBAX8gAUUEQEEADwsgARAJIgJFBEBBAA8LIAIgACABEAcLKQAgACABIAIgAyAEEEUiAEUEQEEADwsgACACQQAgBBA1IQEgABAGIAELcQEBfgJ/AkAgAkJ/VwRAIAMEQCADQQA2AgQgA0EUNgIACwwBCyAAIAEgAhARIgRCf1cEQCADBEAgAyAAKAIMNgIAIAMgACgCEDYCBAsMAQtBACACIARXDQEaIAMEQCADQQA2AgQgA0ERNgIACwtBfwsLNQAgACABIAJBABAmIgBFBEBBfw8LIAMEQCADIAAtAAk6AAALIAQEQCAEIAAoAkQ2AgALQQAL/AECAn8BfiMAQRBrIgMkAAJAIAAgA0EOaiABQYAGQQAQRiIARQRAIAIhAAwBCyADLwEOIgFBBUkEQCACIQAMAQsgAC0AAEEBRwRAIAIhAAwBCyAAIAGtQv//A4MQFyIBRQRAIAIhAAwBCyABEH0aAkAgARAVIAIEfwJ/IAIvAQQhAEEAIAIoAgAiBEUNABpBACAEIABB1IABKAIAEQAACwVBAAtHBEAgAiEADAELIAEgAS0AAAR+IAEpAwggASkDEH0FQgALIgVC//8DgxATIAWnQf//A3FBgBBBABA1IgBFBEAgAiEADAELIAIQEAsgARAICyADQRBqJAAgAAvmDwIIfwJ+IwBB4ABrIgckAEEeQS4gAxshCwJAAkAgAgRAIAIiBSIGLQAABH4gBikDCCAGKQMQfQVCAAsgC61aDQEgBARAIARBADYCBCAEQRM2AgALQn8hDQwCCyABIAutIAcgBBAtIgUNAEJ/IQ0MAQsgBUIEEBMoAABBoxJBqBIgAxsoAABHBEAgBARAIARBADYCBCAEQRM2AgALQn8hDSACDQEgBRAIDAELIABCADcDICAAQQA2AhggAEL/////DzcDECAAQQA7AQwgAEG/hig2AgggAEEBOgAGIABBADsBBCAAQQA2AgAgAEIANwNIIABBgIDYjXg2AkQgAEIANwMoIABCADcDMCAAQgA3AzggAEFAa0EAOwEAIABCADcDUCAAIAMEf0EABSAFEAwLOwEIIAAgBRAMOwEKIAAgBRAMOwEMIAAgBRAMNgIQIAUQDCEGIAUQDCEJIAdBADYCWCAHQgA3A1AgB0IANwNIIAcgCUEfcTYCPCAHIAZBC3Y2AjggByAGQQV2QT9xNgI0IAcgBkEBdEE+cTYCMCAHIAlBCXZB0ABqNgJEIAcgCUEFdkEPcUEBazYCQCAAIAdBMGoQBTYCFCAAIAUQFTYCGCAAIAUQFa03AyAgACAFEBWtNwMoIAUQDCEIIAUQDCEGIAACfiADBEBBACEJIABBADYCRCAAQQA7AUAgAEEANgI8QgAMAQsgBRAMIQkgACAFEAw2AjwgACAFEAw7AUAgACAFEBU2AkQgBRAVrQs3A0ggBS0AAEUEQCAEBEAgBEEANgIEIARBFDYCAAtCfyENIAINASAFEAgMAQsCQCAALwEMIgpBAXEEQCAKQcAAcQRAIABB//8DOwFSDAILIABBATsBUgwBCyAAQQA7AVILIABBADYCOCAAQgA3AzAgBiAIaiAJaiEKAkAgAgRAIAUtAAAEfiAFKQMIIAUpAxB9BUIACyAKrVoNASAEBEAgBEEANgIEIARBFTYCAAtCfyENDAILIAUQCCABIAqtQQAgBBAtIgUNAEJ/IQ0MAQsCQCAIRQ0AIAAgBSABIAhBASAEEGQiCDYCMCAIRQRAIAQoAgBBEUYEQCAEBEAgBEEANgIEIARBFTYCAAsLQn8hDSACDQIgBRAIDAILIAAtAA1BCHFFDQAgCEECECNBBUcNACAEBEAgBEEANgIEIARBFTYCAAtCfyENIAINASAFEAgMAQsgAEE0aiEIAkAgBkUNACAFIAEgBkEAIAQQRSIMRQRAQn8hDSACDQIgBRAIDAILIAwgBkGAAkGABCADGyAIIAQQbiEGIAwQBiAGRQRAQn8hDSACDQIgBRAIDAILIANFDQAgAEEBOgAECwJAIAlFDQAgACAFIAEgCUEAIAQQZCIBNgI4IAFFBEBCfyENIAINAiAFEAgMAgsgAC0ADUEIcUUNACABQQIQI0EFRw0AIAQEQCAEQQA2AgQgBEEVNgIAC0J/IQ0gAg0BIAUQCAwBCyAAIAAoAjRB9eABIAAoAjAQZzYCMCAAIAAoAjRB9cYBIAAoAjgQZzYCOAJAAkAgACkDKEL/////D1ENACAAKQMgQv////8PUQ0AIAApA0hC/////w9SDQELAkACQAJAIAgoAgAgB0EwakEBQYACQYAEIAMbIAQQRiIBRQRAIAJFDQEMAgsgASAHMwEwEBciAUUEQCAEBEAgBEEANgIEIARBDjYCAAsgAkUNAQwCCwJAIAApAyhC/////w9RBEAgACABEB03AygMAQsgA0UNAEEAIQYCQCABKQMQIg5CCHwiDSAOVA0AIAEpAwggDVQNACABIA03AxBBASEGCyABIAY6AAALIAApAyBC/////w9RBEAgACABEB03AyALAkAgAw0AIAApA0hC/////w9RBEAgACABEB03A0gLIAAoAjxB//8DRw0AIAAgARAVNgI8CyABLQAABH8gASkDECABKQMIUQVBAAsNAiAEBEAgBEEANgIEIARBFTYCAAsgARAIIAINAQsgBRAIC0J/IQ0MAgsgARAICyAFLQAARQRAIAQEQCAEQQA2AgQgBEEUNgIAC0J/IQ0gAg0BIAUQCAwBCyACRQRAIAUQCAtCfyENIAApA0hCf1cEQCAEBEAgBEEWNgIEIARBBDYCAAsMAQsjAEEQayIDJABBASEBAkAgACgCEEHjAEcNAEEAIQECQCAAKAI0IANBDmpBgbICQYAGQQAQRiICBEAgAy8BDiIFQQZLDQELIAQEQCAEQQA2AgQgBEEVNgIACwwBCyACIAWtQv//A4MQFyICRQRAIAQEQCAEQQA2AgQgBEEUNgIACwwBC0EBIQECQAJAAkAgAhAMQQFrDgICAQALQQAhASAEBEAgBEEANgIEIARBGDYCAAsgAhAIDAILIAApAyhCE1YhAQsgAkICEBMvAABBwYoBRwRAQQAhASAEBEAgBEEANgIEIARBGDYCAAsgAhAIDAELIAIQfUEBayIFQf8BcUEDTwRAQQAhASAEBEAgBEEANgIEIARBGDYCAAsgAhAIDAELIAMvAQ5BB0cEQEEAIQEgBARAIARBADYCBCAEQRU2AgALIAIQCAwBCyAAIAE6AAYgACAFQf8BcUGBAmo7AVIgACACEAw2AhAgAhAIQQEhAQsgA0EQaiQAIAFFDQAgCCAIKAIAEG02AgAgCiALaq0hDQsgB0HgAGokACANC4ECAQR/IwBBEGsiBCQAAkAgASAEQQxqQcAAQQAQJSIGRQ0AIAQoAgxBBWoiA0GAgARPBEAgAgRAIAJBADYCBCACQRI2AgALDAELQQAgA60QFyIDRQRAIAIEQCACQQA2AgQgAkEONgIACwwBCyADQQEQcCADIAEEfwJ/IAEvAQQhBUEAIAEoAgAiAUUNABpBACABIAVB1IABKAIAEQAACwVBAAsQEiADIAYgBCgCDBAsAn8gAy0AAEUEQCACBEAgAkEANgIEIAJBFDYCAAtBAAwBCyAAIAMtAAAEfiADKQMQBUIAC6dB//8DcSADKAIEEEcLIQUgAxAICyAEQRBqJAAgBQvgAQICfwF+QTAQCSICRQRAIAEEQCABQQA2AgQgAUEONgIAC0EADwsgAkIANwMIIAJBADYCACACQgA3AxAgAkIANwMYIAJCADcDICACQgA3ACUgAFAEQCACDwsCQCAAQv////8AVg0AIACnQQR0EAkiA0UNACACIAM2AgBBACEBQgEhBANAIAMgAUEEdGoiAUIANwIAIAFCADcABSAAIARSBEAgBKchASAEQgF8IQQMAQsLIAIgADcDCCACIAA3AxAgAg8LIAEEQCABQQA2AgQgAUEONgIAC0EAEBAgAhAGQQAL7gECA38BfiMAQRBrIgQkAAJAIARBDGpCBBAXIgNFBEBBfyECDAELAkAgAQRAIAJBgAZxIQUDQAJAIAUgASgCBHFFDQACQCADKQMIQgBUBEAgA0EAOgAADAELIANCADcDECADQQE6AAALIAMgAS8BCBANIAMgAS8BChANIAMtAABFBEAgAEEIaiIABEAgAEEANgIEIABBFDYCAAtBfyECDAQLQX8hAiAAIARBDGpCBBAbQQBIDQMgATMBCiIGUA0AIAAgASgCDCAGEBtBAEgNAwsgASgCACIBDQALC0EAIQILIAMQCAsgBEEQaiQAIAILPAEBfyAABEAgAUGABnEhAQNAIAEgACgCBHEEQCACIAAvAQpqQQRqIQILIAAoAgAiAA0ACwsgAkH//wNxC5wBAQN/IABFBEBBAA8LIAAhAwNAAn8CQAJAIAAvAQgiAUH04AFNBEAgAUEBRg0BIAFB9cYBRg0BDAILIAFBgbICRg0AIAFB9eABRw0BCyAAKAIAIQEgAEEANgIAIAAoAgwQBiAAEAYgASADIAAgA0YbIQMCQCACRQRAQQAhAgwBCyACIAE2AgALIAEMAQsgACICKAIACyIADQALIAMLsgQCBX8BfgJAAkACQCAAIAGtEBciAQRAIAEtAAANAUEAIQAMAgsgBARAIARBADYCBCAEQQ42AgALQQAPC0EAIQADQCABLQAABH4gASkDCCABKQMQfQVCAAtCBFQNASABEAwhByABIAEQDCIGrRATIghFBEBBACECIAQEQCAEQQA2AgQgBEEVNgIACyABEAggAEUNAwNAIAAoAgAhASAAKAIMEAYg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DCAAQRg2AggLDAMLIAAgBiAFLwFqQQAgBCABEQYAIQEgBhALIAFFDQILAkAgDUUEQCABIQMMAQsgACABIAUvAWgQgQEhAyABEAsgA0UNAQsCQCAJRSAMckUEQCADIQEMAQsgACADQQEQgAEhASADEAsgAUUNAQsgASEDDAELQQAhAwsgBUHwAGokACADC4UBAQF/IAFFBEAgAEEIaiIABEAgAEEANgIEIABBEjYCAAtBAA8LQTgQCSIDRQRAIABBCGoiAARAIABBADYCBCAAQQ42AgALQQAPCyADQQA2AhAgA0IANwIIIANCADcDKCADQQA2AgQgAyACNgIAIANCADcDGCADQQA2AjAgACABQTsgAxBCCw8AIAAgASACQQBBABCCAQusAgECfyABRQRAIABBCGoiAARAIABBADYCBCAAQRI2AgALQQAPCwJAIAJBfUsNACACQf//A3FBCEYNACAAQQhqIgAEQCAAQQA2AgQgAEEQNgIAC0EADwsCQEGwwAAQCSIFBEAgBUEANgIIIAVCADcCACAFQYiBAUGogQEgAxs2AqhAIAUgAjYCFCAFIAM6ABAgBUEAOgAPIAVBADsBDCAFIAMgAkF9SyIGcToADiAFQQggAiAGG0H//wNxIAQgBUGIgQFBqIEBIAMbKAIAEQAAIgI2AqxAIAINASAFEDEgBRAGCyAAQQhqIgAEQCAAQQA2AgQgAEEONgIAC0EADwsgACABQTogBRBCIgAEfyAABSAFKAKsQCAFKAKoQCgCBBEDACAFEDEgBRAGQQALC6ABAQF/IAIgACgCBCIDIAIgA0kbIgIEQCAAIAMgAms2AgQCQAJAAkACQCAAKAIcIgMoAhRBAWsOAgEAAgsgA0GgAWogASAAKAIAIAJB3IABKAIAEQgADAILIAAgACgCMCABIAAoAgAgAkHEgAEoAgARBAA2AjAMAQsgASAAKAIAIAIQBxoLIAAgACgCACACajYCACAAIAAoAgggAmo2AggLC7cCAQR/QX4hAgJAIABFDQAgACgCIEUNACAAKAIkIgRFDQAgACgCHCIBRQ0AIAEoAgAgAEcNAAJAAkAgASgCICIDQTlrDjkBAgICAgICAgICAgIBAgICAQICAgICAgICAgICAgICAgICAQICAgICAgICAgICAQICAgICAgICAgEACyADQZoFRg0AIANBKkcNAQsCfwJ/An8gASgCBCICBEAgBCAAKAIoIAIQHiAAKAIcIQELIAEoAlAiAgsEQCAAKAIkIAAoAiggAhAeIAAoAhwhAQsgASgCTCICCwRAIAAoAiQgACgCKCACEB4gACgCHCEBCyABKAJIIgILBEAgACgCJCAAKAIoIAIQHiAAKAIcIQELIAAoAiQgACgCKCABEB4gAEEANgIcQX1BACADQfEARhshAgsgAgvrCQEIfyAAKAIwIgMgACgCDEEFayICIAIgA0sbIQggACgCACIEKAIEIQkgAUEERiEHAkADQCAEKAIQIgMgACgCoC5BKmpBA3UiAkkEQEEBIQYMAgsgCCADIAJrIgMgACgCaCAAKAJYayICIAQoAgRqIgVB//8DIAVB//8DSRsiBiADIAZJGyIDSwRAQQEhBiADQQBHIAdyRQ0CIAFFDQIgAyAFRw0CCyAAQQBBACAHIAMgBUZxIgUQOSAAIAAoAhBBBGsiBDYCECAAKAIEIARqIAM7AAAgACAAKAIQQQJqIgQ2AhAgACgCBCAEaiADQX9zOwAAIAAgACgCEEECajYCECAAKAIAEAoCfyACBEAgACgCACgCDCAAKAJIIAAoAlhqIAMgAiACIANLGyICEAcaIAAoAgAiBCAEKAIMIAJqNgIMIAQgBCgCECACazYCECAEIAQoAhQgAmo2AhQgACAAKAJYIAJqNgJYIAMgAmshAwsgAwsEQCAAKAIAIgIgAigCDCADEIMBIAAoAgAiAiACKAIMIANqNgIMIAIgAigCECADazYCECACIAIoAhQgA2o2AhQLIAAoAgAhBCAFRQ0AC0EAIQYLAkAgCSAEKAIEayICRQRAIAAoAmghAwwBCwJAIAAoAjAiAyACTQRAIABBAjYCgC4gACgCSCAEKAIAIANrIAMQBxogACAAKAIwIgM2AoQuIAAgAzYCaAwBCyACIAAoAkQgACgCaCIFa08EQCAAIAUgA2siBDYCaCAAKAJIIgUgAyAFaiAEEAcaIAAoAoAuIgNBAU0EQCAAIANBAWo2AoAuCyAAIAAoAmgiBSAAKAKELiIDIAMgBUsbNgKELiAAKAIAIQQLIAAoAkggBWogBCgCACACayACEAcaIAAgACgCaCACaiIDNgJoIAAgACgCMCAAKAKELiIEayIFIAIgAiAFSxsgBGo2AoQuCyAAIAM2AlgLIAAgAyAAKAJAIgIgAiADSRs2AkBBAyECAkAgBkUNACAAKAIAIgUoAgQhAgJAAkAgAUF7cUUNACACDQBBASECIAMgACgCWEYNAiAAKAJEIANrIQRBACECDAELIAIgACgCRCADayIETQ0AIAAoAlgiByAAKAIwIgZIDQAgACADIAZrIgM2AmggACAHIAZrNgJYIAAoAkgiAiACIAZqIAMQBxogACgCgC4iA0EBTQRAIAAgA0EBajYCgC4LIAAgACgCaCIDIAAoAoQuIgIgAiADSxs2AoQuIAAoAjAgBGohBCAAKAIAIgUoAgQhAgsCQCACIAQgAiAESRsiAkUEQCAAKAIwIQUMAQsgBSAAKAJIIANqIAIQgwEgACAAKAJoIAJqIgM2AmggACAAKAIwIgUgACgChC4iBGsiBiACIAIgBksbIARqNgKELgsgACADIAAoAkAiAiACIANJGzYCQCADIAAoAlgiBmsiAyAFIAAoAgwgACgCoC5BKmpBA3VrIgJB//8DIAJB//8DSRsiBCAEIAVLG0kEQEEAIQIgAUEERiADQQBHckUNASABRQ0BIAAoAgAoAgQNASADIARLDQELQQAhAiABQQRGBEAgACgCACgCBEUgAyAETXEhAgsgACAAKAJIIAZqIAQgAyADIARLGyIBIAIQOSAAIAAoAlggAWo2AlggACgCABAKQQJBACACGw8LIAIL/woCCn8DfiAAKQOYLiENIAAoAqAuIQQgAkEATgRAQQRBAyABLwECIggbIQlBB0GKASAIGyEFQX8hCgNAIAghByABIAsiDEEBaiILQQJ0ai8BAiEIAkACQCAGQQFqIgMgBU4NACAHIAhHDQAgAyEGDAELAkAgAyAJSARAIAAgB0ECdGoiBkHOFWohCSAGQcwVaiEKA0AgCjMBACEPAn8gBCAJLwEAIgZqIgVBP00EQCAPIASthiANhCENIAUMAQsgBEHAAEYEQCAAKAIEIAAoAhBqIA03AAAgACAAKAIQQQhqNgIQIA8hDSAGDAELIAAoAgQgACgCEGogDyAErYYgDYQ3AAAgACAAKAIQQQhqNgIQIA9BwAAgBGutiCENIAVBQGoLIQQgA0EBayIDDQALDAELIAcEQAJAIAcgCkYEQCANIQ8gBCEFIAMhBgwBCyAAIAdBAnRqIgNBzBVqMwEAIQ8gBCADQc4Vai8BACIDaiIFQT9NBEAgDyAErYYgDYQhDwwBCyAEQcAARgRAIAAoAgQgACgCEGogDTcAACAAIAAoAhBBCGo2AhAgAyEFDAELIAAoAgQgACgCEGogDyAErYYgDYQ3AAAgACAAKAIQQQhqNgIQIAVBQGohBSAPQcAAIARrrYghDwsgADMBjBYhDgJAIAUgAC8BjhYiBGoiA0E/TQRAIA4gBa2GIA+EIQ4MAQsgBUHAAEYEQCAAKAIEIAAoAhBqIA83AAAgACAAKAIQQQhqNgIQIAQhAwwBCyAAKAIEIAAoAhBqIA4gBa2GIA+ENwAAIAAgACgCEEEIajYCECADQUBqIQMgDkHAACAFa62IIQ4LIAasQgN9IQ0gA0E9TQRAIANBAmohBCANIAOthiAOhCENDAILIANBwABGBEAgACgCBCAAKAIQaiAONwAAIAAgACgCEEEIajYCEEECIQQMAgsgACgCBCAAKAIQaiANIAOthiAOhDcAACAAIAAoAhBBCGo2AhAgA0E+ayEEIA1BwAAgA2utiCENDAELIAZBCUwEQCAAMwGQFiEOAkAgBCAALwGSFiIFaiIDQT9NBEAgDiAErYYgDYQhDgwBCyAEQcAARgRAIAAoAgQgACgCEGogDTcAACAAIAAoAhBBCGo2AhAgBSEDDAELIAAoAgQgACgCEGogDiAErYYgDYQ3AAAgACAAKAIQQQhqNgIQIANBQGohAyAOQcAAIARrrYghDgsgBqxCAn0hDSADQTxNBEAgA0EDaiEEIA0gA62GIA6EIQ0MAgsgA0HAAEYEQCAAKAIEIAAoAhBqIA43AAAgACAAKAIQQQhqNgIQQQMhBAwCCyAAKAIEIAAoAhBqIA0gA62GIA6ENwAAIAAgACgCEEEIajYCECADQT1rIQQgDUHAACADa62IIQ0MAQsgADMBlBYhDgJAIAQgAC8BlhYiBWoiA0E/TQRAIA4gBK2GIA2EIQ4MAQsgBEHAAEYEQCAAKAIEIAAoAhBqIA03AAAgACAAKAIQQQhqNgIQIAUhAwwBCyAAKAIEIAAoAhBqIA4gBK2GIA2ENwAAIAAgACgCEEEIajYCECADQUBqIQMgDkHAACAEa62IIQ4LIAatQgp9IQ0gA0E4TQRAIANBB2ohBCANIAOthiAOhCENDAELIANBwABGBEAgACgCBCAAKAIQaiAONwAAIAAgACgCEEEIajYCEEEHIQQMAQsgACgCBCAAKAIQaiANIAOthiAOhDcAACAAIAAoAhBBCGo2AhAgA0E5ayEEIA1BwAAgA2utiCENC0EAIQYCfyAIRQRAQYoBIQVBAwwBC0EGQQcgByAIRiIDGyEFQQNBBCADGwshCSAHIQoLIAIgDEcNAAsLIAAgBDYCoC4gACANNwOYLgv5BQIIfwJ+AkAgACgC8C1FBEAgACkDmC4hCyAAKAKgLiEDDAELA0AgCSIDQQNqIQkgAyAAKALsLWoiAy0AAiEFIAApA5guIQwgACgCoC4hBAJAIAMvAAAiB0UEQCABIAVBAnRqIgMzAQAhCyAEIAMvAQIiBWoiA0E/TQRAIAsgBK2GIAyEIQsMAgsgBEHAAEYEQCAAKAIEIAAoAhBqIAw3AAAgACAAKAIQQQhqNgIQIAUhAwwCCyAAKAIEIAAoAhBqIAsgBK2GIAyENwAAIAAgACgCEEEIajYCECADQUBqIQMgC0HAACAEa62IIQsMAQsgBUGAzwBqLQAAIghBAnQiBiABaiIDQYQIajMBACELIANBhghqLwEAIQMgCEEIa0ETTQRAIAUgBkGA0QBqKAIAa60gA62GIAuEIQsgBkHA0wBqKAIAIANqIQMLIAMgAiAHQQFrIgcgB0EHdkGAAmogB0GAAkkbQYDLAGotAAAiBUECdCIIaiIKLwECaiEGIAozAQAgA62GIAuEIQsgBCAFQQRJBH8gBgUgByAIQYDSAGooAgBrrSAGrYYgC4QhCyAIQcDUAGooAgAgBmoLIgVqIgNBP00EQCALIASthiAMhCELDAELIARBwABGBEAgACgCBCAAKAIQaiAMNwAAIAAgACgCEEEIajYCECAFIQMMAQsgACgCBCAAKAIQaiALIASthiAMhDcAACAAIAAoAhBBCGo2AhAgA0FAaiEDIAtBwAAgBGutiCELCyAAIAs3A5guIAAgAzYCoC4gCSAAKALwLUkNAAsLIAFBgAhqMwEAIQwCQCADIAFBgghqLwEAIgJqIgFBP00EQCAMIAOthiALhCEMDAELIANBwABGBEAgACgCBCAAKAIQaiALNwAAIAAgACgCEEEIajYCECACIQEMAQsgACgCBCAAKAIQaiAMIAOthiALhDcAACAAIAAoAhBBCGo2AhAgAUFAaiEBIAxBwAAgA2utiCEMCyAAIAw3A5guIAAgATYCoC4L8AQBA38gAEHkAWohAgNAIAIgAUECdCIDakEAOwEAIAIgA0EEcmpBADsBACABQQJqIgFBngJHDQALIABBADsBzBUgAEEAOwHYEyAAQZQWakEAOwEAIABBkBZqQQA7AQAgAEGMFmpBADsBACAAQYgWakEAOwEAIABBhBZqQQA7AQAgAEGAFmpBADsBACAAQfwVakEAOwEAIABB+BVqQQA7AQAgAEH0FWpBADsBACAAQfAVakEAOwEAIABB7BVqQQA7AQAgAEHoFWpBADsBACAAQeQVakEAOwEAIABB4BVqQQA7AQAgAEHcFWpBADsBACAAQdgVakEAOwEAIABB1BVqQQA7AQAgAEHQFWpBADsBACAAQcwUakEAOwEAIABByBRqQQA7AQAgAEHEFGpBADsBACAAQcAUakEAOwEAIABBvBRqQQA7AQAgAEG4FGpBADsBACAAQbQUakEAOwEAIABBsBRqQQA7AQAgAEGsFGpBADsBACAAQagUakEAOwEAIABBpBRqQQA7AQAgAEGgFGpBADsBACAAQZwUakEAOwEAIABBmBRqQQA7AQAgAEGUFGpBADsBACAAQZAUakEAOwEAIABBjBRqQQA7AQAgAEGIFGpBADsBACAAQYQUakEAOwEAIABBgBRqQQA7AQAgAEH8E2pBADsBACAAQfgTakEAOwEAIABB9BNqQQA7AQAgAEHwE2pBADsBACAAQewTakEAOwEAIABB6BNqQQA7AQAgAEHkE2pBADsBACAAQeATakEAOwEAIABB3BNqQQA7AQAgAEIANwL8LSAAQeQJakEBOwEAIABBADYC+C0gAEEANgLwLQuKAwIGfwR+QcgAEAkiBEUEQEEADwsgBEIANwMAIARCADcDMCAEQQA2AiggBEIANwMgIARCADcDGCAEQgA3AxAgBEIANwMIIARCADcDOCABUARAIARBCBAJIgA2AgQgAEUEQCAEEAYgAwRAIANBADYCBCADQQ42AgALQQAPCyAAQgA3AwAgBA8LAkAgAaciBUEEdBAJIgZFDQAgBCAGNgIAIAVBA3RBCGoQCSIFRQ0AIAQgATcDECAEIAU2AgQDQCAAIAynIghBBHRqIgcpAwgiDVBFBEAgBygCACIHRQRAIAMEQCADQQA2AgQgA0ESNgIACyAGEAYgBRAGIAQQBkEADwsgBiAKp0EEdGoiCSANNwMIIAkgBzYCACAFIAhBA3RqIAs3AwAgCyANfCELIApCAXwhCgsgDEIBfCIMIAFSDQALIAQgCjcDCCAEQgAgCiACGzcDGCAFIAqnQQN0aiALNwMAIAQgCzcDMCAEDwsgAwRAIANBADYCBCADQQ42AgALIAYQBiAEEAZBAAvlAQIDfwF+QX8hBQJAIAAgASACQQAQJiIERQ0AIAAgASACEIsBIgZFDQACfgJAIAJBCHENACAAKAJAIAGnQQR0aigCCCICRQ0AIAIgAxAhQQBOBEAgAykDAAwCCyAAQQhqIgAEQCAAQQA2AgQgAEEPNgIAC0F/DwsgAxAqIAMgBCgCGDYCLCADIAQpAyg3AxggAyAEKAIUNgIoIAMgBCkDIDcDICADIAQoAhA7ATAgAyAELwFSOwEyQvwBQtwBIAQtAAYbCyEHIAMgBjYCCCADIAE3AxAgAyAHQgOENwMAQQAhBQsgBQspAQF/IAAgASACIABBCGoiABAmIgNFBEBBAA8LIAMoAjBBACACIAAQJQuAAwEGfwJ/An9BMCABQYB/Sw0BGgJ/IAFBgH9PBEBBhIQBQTA2AgBBAAwBC0EAQRAgAUELakF4cSABQQtJGyIFQcwAahAJIgFFDQAaIAFBCGshAgJAIAFBP3FFBEAgAiEBDAELIAFBBGsiBigCACIHQXhxIAFBP2pBQHFBCGsiASABQUBrIAEgAmtBD0sbIgEgAmsiA2shBCAHQQNxRQRAIAIoAgAhAiABIAQ2AgQgASACIANqNgIADAELIAEgBCABKAIEQQFxckECcjYCBCABIARqIgQgBCgCBEEBcjYCBCAGIAMgBigCAEEBcXJBAnI2AgAgAiADaiIEIAQoAgRBAXI2AgQgAiADEDsLAkAgASgCBCICQQNxRQ0AIAJBeHEiAyAFQRBqTQ0AIAEgBSACQQFxckECcjYCBCABIAVqIgIgAyAFayIFQQNyNgIEIAEgA2oiAyADKAIEQQFyNgIEIAIgBRA7CyABQQhqCyIBRQsEQEEwDwsgACABNgIAQQALCwoAIABBiIQBEAQL6AIBBX8gACgCUCEBIAAvATAhBEEEIQUDQCABQQAgAS8BACICIARrIgMgAiADSRs7AQAgAUEAIAEvAQIiAiAEayIDIAIgA0kbOwECIAFBACABLwEEIgIgBGsiAyACIANJGzsBBCABQQAgAS8BBiICIARrIgMgAiADSRs7AQYgBUGAgARGRQRAIAFBCGohASAFQQRqIQUMAQsLAkAgBEUNACAEQQNxIQUgACgCTCEBIARBAWtBA08EQCAEIAVrIQADQCABQQAgAS8BACICIARrIgMgAiADSRs7AQAgAUEAIAEvAQIiAiAEayIDIAIgA0kbOwECIAFBACABLwEEIgIgBGsiAyACIANJGzsBBCABQQAgAS8BBiICIARrIgMgAiADSRs7AQYgAUEIaiEBIABBBGsiAA0ACwsgBUUNAANAIAFBACABLwEAIgAgBGsiAiAAIAJJGzsBACABQQJqIQEgBUEBayIFDQALCwuDAQEEfyACQQFOBEAgAiAAKAJIIAFqIgJqIQMgACgCUCEEA0AgBCACKAAAQbHz3fF5bEEPdkH+/wdxaiIFLwEAIgYgAUH//wNxRwRAIAAoAkwgASAAKAI4cUH//wNxQQF0aiAGOwEAIAUgATsBAAsgAUEBaiEBIAJBAWoiAiADSQ0ACwsLUAECfyABIAAoAlAgACgCSCABaigAAEGx893xeWxBD3ZB/v8HcWoiAy8BACICRwRAIAAoAkwgACgCOCABcUEBdGogAjsBACADIAE7AQALIAILugEBAX8jAEEQayICJAAgAkEAOgAIQYCBAUECNgIAQfyAAUEDNgIAQfiAAUEENgIAQfSAAUEFNgIAQfCAAUEGNgIAQeyAAUEHNgIAQeiAAUEINgIAQeSAAUEJNgIAQeCAAUEKNgIAQdyAAUELNgIAQdiAAUEMNgIAQdSAAUENNgIAQdCAAUEONgIAQcyAAUEPNgIAQciAAUEQNgIAQcSAAUERNgIAQcCAAUESNgIAIAAgARBYIAJBEGokAAu9AQEBfyMAQRBrIgEkACABQQA6AAhBgIEBQQI2AgBB/IABQQM2AgBB+IABQQQ2AgBB9IABQQU2AgBB8IABQQY2AgBB7IABQQc2AgBB6IABQQg2AgBB5IABQQk2AgBB4IABQQo2AgBB3IABQQs2AgBB2IABQQw2AgBB1IABQQ02AgBB0IABQQ42AgBBzIABQQ82AgBByIABQRA2AgBBxIABQRE2AgBBwIABQRI2AgAgAEEANgJAIAFBEGokAEEAC70BAQF/IwBBEGsiASQAIAFBADoACEGAgQFBAjYCAEH8gAFBAzYCAEH4gAFBBDYCAEH0gAFBBTYCAEHwgAFBBjYCAEHsgAFBBzYCAEHogAFBCDYCAEHkgAFBCTYCAEHggAFBCjYCAEHcgAFBCzYCAEHYgAFBDDYCAEHUgAFBDTYCAEHQgAFBDjYCAEHMgAFBDzYCAEHIgAFBEDYCAEHEgAFBETYCAEHAgAFBEjYCACAAKAJAIQAgAUEQaiQAIAALvgEBAX8jAEEQayIEJAAgBEEAOgAIQYCBAUECNgIAQfyAAUEDNgIAQfiAAUEENgIAQfSAAUEFNgIAQfCAAUEGNgIAQeyAAUEHNgIAQeiAAUEINgIAQeSAAUEJNgIAQeCAAUEKNgIAQdyAAUELNgIAQdiAAUEMNgIAQdSAAUENNgIAQdCAAUEONgIAQcyAAUEPNgIAQciAAUEQNgIAQcSAAUERNgIAQcCAAUESNgIAIAAgASACIAMQVyAEQRBqJAALygEAIwBBEGsiAyQAIANBADoACEGAgQFBAjYCAEH8gAFBAzYCAEH4gAFBBDYCAEH0gAFBBTYCAEHwgAFBBjYCAEHsgAFBBzYCAEHogAFBCDYCAEHkgAFBCTYCAEHggAFBCjYCAEHcgAFBCzYCAEHYgAFBDDYCAEHUgAFBDTYCAEHQgAFBDjYCAEHMgAFBDzYCAEHIgAFBEDYCAEHEgAFBETYCAEHAgAFBEjYCACAAIAAoAkAgASACQdSAASgCABEAADYCQCADQRBqJAALwAEBAX8jAEEQayIDJAAgA0EAOgAIQYCBAUECNgIAQfyAAUEDNgIAQfiAAUEENgIAQfSAAUEFNgIAQfCAAUEGNgIAQeyAAUEHNgIAQeiAAUEINgIAQeSAAUEJNgIAQeCAAUEKNgIAQdyAAUELNgIAQdiAAUEMNgIAQdSAAUENNgIAQdCAAUEONgIAQcyAAUEPNgIAQciAAUEQNgIAQcSAAUERNgIAQcCAAUESNgIAIAAgASACEF0hACADQRBqJAAgAAu+AQEBfyMAQRBrIgIkACACQQA6AAhBgIEBQQI2AgBB/IABQQM2AgBB+IABQQQ2AgBB9IABQQU2AgBB8IABQQY2AgBB7IABQQc2AgBB6IABQQg2AgBB5IABQQk2AgBB4IABQQo2AgBB3IABQQs2AgBB2IABQQw2AgBB1IABQQ02AgBB0IABQQ42AgBBzIABQQ82AgBByIABQRA2AgBBxIABQRE2AgBBwIABQRI2AgAgACABEFwhACACQRBqJAAgAAu2AQEBfyMAQRBrIgAkACAAQQA6AAhBgIEBQQI2AgBB/IABQQM2AgBB+IABQQQ2AgBB9IABQQU2AgBB8IABQQY2AgBB7IABQQc2AgBB6IABQQg2AgBB5IABQQk2AgBB4IABQQo2AgBB3IABQQs2AgBB2IABQQw2AgBB1IABQQ02AgBB0IABQQ42AgBBzIABQQ82AgBByIABQRA2AgBBxIABQRE2AgBBwIABQRI2AgAgAEEQaiQAQQgLwgEBAX8jAEEQayIEJAAgBEEAOgAIQYCBAUECNgIAQfyAAUEDNgIAQfiAAUEENgIAQfSAAUEFNgIAQfCAAUEGNgIAQeyAAUEHNgIAQeiAAUEINgIAQeSAAUEJNgIAQeCAAUEKNgIAQdyAAUELNgIAQdiAAUEMNgIAQdSAAUENNgIAQdCAAUEONgIAQcyAAUEPNgIAQciAAUEQNgIAQcSAAUERNgIAQcCAAUESNgIAIAAgASACIAMQWSEAIARBEGokACAAC8IBAQF/IwBBEGsiBCQAIARBADoACEGAgQFBAjYCAEH8gAFBAzYCAEH4gAFBBDYCAEH0gAFBBTYCAEHwgAFBBjYCAEHsgAFBBzYCAEHogAFBCDYCAEHkgAFBCTYCAEHggAFBCjYCAEHcgAFBCzYCAEHYgAFBDDYCAEHUgAFBDTYCAEHQgAFBDjYCAEHMgAFBDzYCAEHIgAFBEDYCAEHEgAFBETYCAEHAgAFBEjYCACAAIAEgAiADEFYhACAEQRBqJAAgAAsHACAALwEwC8ABAQF/IwBBEGsiAyQAIANBADoACEGAgQFBAjYCAEH8gAFBAzYCAEH4gAFBBDYCAEH0gAFBBTYCAEHwgAFBBjYCAEHsgAFBBzYCAEHogAFBCDYCAEHkgAFBCTYCAEHggAFBCjYCAEHcgAFBCzYCAEHYgAFBDDYCAEHUgAFBDTYCAEHQgAFBDjYCAEHMgAFBDzYCAEHIgAFBEDYCAEHEgAFBETYCAEHAgAFBEjYCACAAIAEgAhBVIQAgA0EQaiQAIAALBwAgACgCQAsaACAAIAAoAkAgASACQdSAASgCABEAADYCQAsLACAAQQA2AkBBAAsHACAAKAIgCwQAQQgLzgUCA34BfyMAQYBAaiIIJAACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAEDhECAwwFAAEECAkJCQkJCQcJBgkLIANCCFoEfiACIAEoAmQ2AgAgAiABKAJoNgIEQggFQn8LIQYMCwsgARAGDAoLIAEoAhAiAgRAIAIgASkDGCABQeQAaiICEEEiA1ANCCABKQMIIgVCf4UgA1QEQCACBEAgAkEANgIEIAJBFTYCAAsMCQsgAUEANgIQIAEgAyAFfDcDCCABIAEpAwAgA3w3AwALIAEtAHgEQCABKQMAIQUMCQtCACEDIAEpAwAiBVAEQCABQgA3AyAMCgsDQCAAIAggBSADfSIFQoDAACAFQoDAAFQbEBEiB0J/VwRAIAFB5ABqIgEEQCABIAAoAgw2AgAgASAAKAIQNgIECwwJCyAHUEUEQCABKQMAIgUgAyAHfCIDWA0KDAELCyABQeQAagRAIAFBADYCaCABQRE2AmQLDAcLIAEpAwggASkDICIFfSIHIAMgAyAHVhsiA1ANCAJAIAEtAHhFDQAgACAFQQAQFEF/Sg0AIAFB5ABqIgEEQCABIAAoAgw2AgAgASAAKAIQNgIECwwHCyAAIAIgAxARIgZCf1cEQCABQeQAagRAIAFBADYCaCABQRE2AmQLDAcLIAEgASkDICAGfCIDNwMgIAZCAFINCEIAIQYgAyABKQMIWg0IIAFB5ABqBEAgAUEANgJoIAFBETYCZAsMBgsgASkDICABKQMAIgV9IAEpAwggBX0gAiADIAFB5ABqEEQiA0IAUw0FIAEgASkDACADfDcDIAwHCyACIAFBKGoQYEEfdawhBgwGCyABMABgIQYMBQsgASkDcCEGDAQLIAEpAyAgASkDAH0hBgwDCyABQeQAagRAIAFBADYCaCABQRw2AmQLC0J/IQYMAQsgASAFNwMgCyAIQYBAayQAIAYLBwAgACgCAAsPACAAIAAoAjBBAWo2AjALGABB+IMBQgA3AgBBgIQBQQA2AgBB+IMBCwcAIABBDGoLBwAgACgCLAsHACAAKAIoCwcAIAAoAhgLFQAgACABrSACrUIghoQgAyAEEIoBCxMBAX4gABAzIgFCIIinEAAgAacLbwEBfiABrSACrUIghoQhBSMAQRBrIgEkAAJ/IABFBEAgBVBFBEAgBARAIARBADYCBCAEQRI2AgALQQAMAgtBAEIAIAMgBBA6DAELIAEgBTcDCCABIAA2AgAgAUIBIAMgBBA6CyEAIAFBEGokACAACxQAIAAgASACrSADrUIghoQgBBBSC9oCAgJ/AX4CfyABrSACrUIghoQiByAAKQMwVEEAIARBCkkbRQRAIABBCGoEQCAAQQA2AgwgAEESNgIIC0F/DAELIAAtABhBAnEEQCAAQQhqBEAgAEEANgIMIABBGTYCCAtBfwwBCyADBH8gA0H//wNxQQhGIANBfUtyBUEBC0UEQCAAQQhqBEAgAEEANgIMIABBEDYCCAtBfwwBCyAAKAJAIgEgB6ciBUEEdGooAgAiAgR/IAIoAhAgA0YFIANBf0YLIQYgASAFQQR0aiIBIQUgASgCBCEBAkAgBgRAIAFFDQEgAUEAOwFQIAEgASgCAEF+cSIANgIAIAANASABECAgBUEANgIEQQAMAgsCQCABDQAgBSACECsiATYCBCABDQAgAEEIagRAIABBADYCDCAAQQ42AggLQX8MAgsgASAEOwFQIAEgAzYCECABIAEoAgBBAXI2AgALQQALCxwBAX4gACABIAIgAEEIahBMIgNCIIinEAAgA6cLHwEBfiAAIAEgAq0gA61CIIaEEBEiBEIgiKcQACAEpwteAQF+An5CfyAARQ0AGiAAKQMwIgIgAUEIcUUNABpCACACUA0AGiAAKAJAIQADQCACIAKnQQR0IABqQRBrKAIADQEaIAJCAX0iAkIAUg0AC0IACyICQiCIpxAAIAKnCxMAIAAgAa0gAq1CIIaEIAMQiwELnwEBAn4CfiACrSADrUIghoQhBUJ/IQQCQCAARQ0AIAAoAgQNACAAQQRqIQIgBUJ/VwRAIAIEQCACQQA2AgQgAkESNgIAC0J/DAILQgAhBCAALQAQDQAgBVANACAAKAIUIAEgBRARIgRCf1UNACAAKAIUIQAgAgRAIAIgACgCDDYCACACIAAoAhA2AgQLQn8hBAsgBAsiBEIgiKcQACAEpwueAQEBfwJ/IAAgACABrSACrUIghoQgAyAAKAIcEH8iAQRAIAEQMkF/TARAIABBCGoEQCAAIAEoAgw2AgggACABKAIQNgIMCyABEAtBAAwCC0EYEAkiBEUEQCAAQQhqBEAgAEEANgIMIABBDjYCCAsgARALQQAMAgsgBCAANgIAIARBADYCDCAEQgA3AgQgBCABNgIUIARBADoAEAsgBAsLsQICAX8BfgJ/QX8hBAJAIAAgAa0gAq1CIIaEIgZBAEEAECZFDQAgAC0AGEECcQRAIABBCGoEQCAAQQA2AgwgAEEZNgIIC0F/DAILIAAoAkAiASAGpyICQQR0aiIEKAIIIgUEQEEAIQQgBSADEHFBf0oNASAAQQhqBEAgAEEANgIMIABBDzYCCAtBfwwCCwJAIAQoAgAiBQRAIAUoAhQgA0YNAQsCQCABIAJBBHRqIgEoAgQiBA0AIAEgBRArIgQ2AgQgBA0AIABBCGoEQCAAQQA2AgwgAEEONgIIC0F/DAMLIAQgAzYCFCAEIAQoAgBBIHI2AgBBAAwCC0EAIQQgASACQQR0aiIBKAIEIgBFDQAgACAAKAIAQV9xIgI2AgAgAg0AIAAQICABQQA2AgQLIAQLCxQAIAAgAa0gAq1CIIaEIAQgBRBzCxIAIAAgAa0gAq1CIIaEIAMQFAtBAQF+An4gAUEAIAIbRQRAIABBCGoEQCAAQQA2AgwgAEESNgIIC0J/DAELIAAgASACIAMQdAsiBEIgiKcQACAEpwvGAwIFfwF+An4CQAJAIAAiBC0AGEECcQRAIARBCGoEQCAEQQA2AgwgBEEZNgIICwwBCyABRQRAIARBCGoEQCAEQQA2AgwgBEESNgIICwwBCyABECIiByABakEBay0AAEEvRwRAIAdBAmoQCSIARQRAIARBCGoEQCAEQQA2AgwgBEEONgIICwwCCwJAAkAgACIGIAEiBXNBA3ENACAFQQNxBEADQCAGIAUtAAAiAzoAACADRQ0DIAZBAWohBiAFQQFqIgVBA3ENAAsLIAUoAgAiA0F/cyADQYGChAhrcUGAgYKEeHENAANAIAYgAzYCACAFKAIEIQMgBkEEaiEGIAVBBGohBSADQYGChAhrIANBf3NxQYCBgoR4cUUNAAsLIAYgBS0AACIDOgAAIANFDQADQCAGIAUtAAEiAzoAASAGQQFqIQYgBUEBaiEFIAMNAAsLIAcgACIDakEvOwAACyAEQQBCAEEAEFIiAEUEQCADEAYMAQsgBCADIAEgAxsgACACEHQhCCADEAYgCEJ/VwRAIAAQCyAIDAMLIAQgCEEDQYCA/I8EEHNBf0oNASAEIAgQchoLQn8hCAsgCAsiCEIgiKcQACAIpwsQACAAIAGtIAKtQiCGhBByCxYAIAAgAa0gAq1CIIaEIAMgBCAFEGYL3iMDD38IfgF8IwBB8ABrIgkkAAJAIAFBAE5BACAAG0UEQCACBEAgAkEANgIEIAJBEjYCAAsMAQsgACkDGCISAn5BsIMBKQMAIhNCf1EEQCAJQoOAgIBwNwMwIAlChoCAgPAANwMoIAlCgYCAgCA3AyBBsIMBQQAgCUEgahAkNwMAIAlCj4CAgHA3AxAgCUKJgICAoAE3AwAgCUKMgICA0AE3AwhBuIMBQQggCRAkNwMAQbCDASkDACETCyATC4MgE1IEQCACBEAgAkEANgIEIAJBHDYCAAsMAQsgASABQRByQbiDASkDACITIBKDIBNRGyIKQRhxQRhGBEAgAgRAIAJBADYCBCACQRk2AgALDAELIAlBOGoQKgJAIAAgCUE4ahAhBEACQCAAKAIMQQVGBEAgACgCEEEsRg0BCyACBEAgAiAAKAIMNgIAIAIgACgCEDYCBAsMAgsgCkEBcUUEQCACBEAgAkEANgIEIAJBCTYCAAsMAwsgAhBJIgVFDQEgBSAKNgIEIAUgADYCACAKQRBxRQ0CIAUgBSgCFEECcjYCFCAFIAUoAhhBAnI2AhgMAgsgCkECcQRAIAIEQCACQQA2AgQgAkEKNgIACwwCCyAAEDJBf0wEQCACBEAgAiAAKAIMNgIAIAIgACgCEDYCBAsMAQsCfyAKQQhxBEACQCACEEkiAUUNACABIAo2AgQgASAANgIAIApBEHFFDQAgASABKAIUQQJyNgIUIAEgASgCGEECcjYCGAsgAQwBCyMAQUBqIg4kACAOQQhqECoCQCAAIA5BCGoQIUF/TARAIAIEQCACIAAoAgw2AgAgAiAAKAIQNgIECwwBCyAOLQAIQQRxRQRAIAIEQCACQYoBNgIEIAJBBDYCAAsMAQsgDikDICETIAIQSSIFRQRAQQAhBQwBCyAFIAo2AgQgBSAANgIAIApBEHEEQCAFIAUoAhRBAnI2AhQgBSAFKAIYQQJyNgIYCwJAAkACQCATUARAAn8gACEBAkADQCABKQMYQoCAEINCAFINASABKAIAIgENAAtBAQwBCyABQQBCAEESEA6nCw0EIAVBCGoEQCAFQQA2AgwgBUETNgIICwwBCyMAQdAAayIBJAACQCATQhVYBEAgBUEIagRAIAVBADYCDCAFQRM2AggLDAELAkACQCAFKAIAQgAgE0KqgAQgE0KqgARUGyISfUECEBRBf0oNACAFKAIAIgMoAgxBBEYEQCADKAIQQRZGDQELIAVBCGoEQCAFIAMoAgw2AgggBSADKAIQNgIMCwwBCyAFKAIAEDMiE0J/VwRAIAUoAgAhAyAFQQhqIggEQCAIIAMoAgw2AgAgCCADKAIQNgIECwwBCyAFKAIAIBJBACAFQQhqIg8QLSIERQ0BIBJCqoAEWgRAAkAgBCkDCEIUVARAIARBADoAAAwBCyAEQhQ3AxAgBEEBOgAACwsgAQRAIAFBADYCBCABQRM2AgALIARCABATIQwCQCAELQAABH4gBCkDCCAEKQMQfQVCAAunIgdBEmtBA0sEQEJ/IRcDQCAMQQFrIQMgByAMakEVayEGAkADQCADQQFqIgNB0AAgBiADaxB6IgNFDQEgA0EBaiIMQZ8SQQMQPQ0ACwJAIAMgBCgCBGusIhIgBCkDCFYEQCAEQQA6AAAMAQsgBCASNwMQIARBAToAAAsgBC0AAAR+IAQpAxAFQgALIRICQCAELQAABH4gBCkDCCAEKQMQfQVCAAtCFVgEQCABBEAgAUEANgIEIAFBEzYCAAsMAQsgBEIEEBMoAABB0JaVMEcEQCABBEAgAUEANgIEIAFBEzYCAAsMAQsCQAJAAkAgEkIUVA0AIAQoAgQgEqdqQRRrKAAAQdCWmThHDQACQCASQhR9IhQgBCIDKQMIVgRAIANBADoAAAwBCyADIBQ3AxAgA0EBOgAACyAFKAIUIRAgBSgCACEGIAMtAAAEfiAEKQMQBUIACyEWIARCBBATGiAEEAwhCyAEEAwhDSAEEB0iFEJ/VwRAIAEEQCABQRY2AgQgAUEENgIACwwECyAUQjh8IhUgEyAWfCIWVgRAIAEEQCABQQA2AgQgAUEVNgIACwwECwJAAkAgEyAUVg0AIBUgEyAEKQMIfFYNAAJAIBQgE30iFSAEKQMIVgRAIANBADoAAAwBCyADIBU3AxAgA0EBOgAAC0EAIQcMAQsgBiAUQQAQFEF/TARAIAEEQCABIAYoAgw2AgAgASAGKAIQNgIECwwFC0EBIQcgBkI4IAFBEGogARAtIgNFDQQLIANCBBATKAAAQdCWmTBHBEAgAQRAIAFBADYCBCABQRU2AgALIAdFDQQgAxAIDAQLIAMQHSEVAkAgEEEEcSIGRQ0AIBQgFXxCDHwgFlENACABBEAgAUEANgIEIAFBFTYCAAsgB0UNBCADEAgMBAsgA0IEEBMaIAMQFSIQIAsgC0H//wNGGyELIAMQFSIRIA0gDUH//wNGGyENAkAgBkUNACANIBFGQQAgCyAQRhsNACABBEAgAUEANgIEIAFBFTYCAAsgB0UNBCADEAgMBAsgCyANcgRAIAEEQCABQQA2AgQgAUEBNgIACyAHRQ0EIAMQCAwECyADEB0iGCADEB1SBEAgAQRAIAFBADYCBCABQQE2AgALIAdFDQQgAxAIDAQLIAMQHSEVIAMQHSEWIAMtAABFBEAgAQRAIAFBADYCBCABQRQ2AgALIAdFDQQgAxAIDAQLIAcEQCADEAgLAkAgFkIAWQRAIBUgFnwiGSAWWg0BCyABBEAgAUEWNgIEIAFBBDYCAAsMBAsgEyAUfCIUIBlUBEAgAQRAIAFBADYCBCABQRU2AgALDAQLAkAgBkUNACAUIBlRDQAgAQRAIAFBADYCBCABQRU2AgALDAQLIBggFUIugFgNASABBEAgAUEANgIEIAFBFTYCAAsMAwsCQCASIAQpAwhWBEAgBEEAOgAADAELIAQgEjcDECAEQQE6AAALIAUoAhQhAyAELQAABH4gBCkDCCAEKQMQfQVCAAtCFVgEQCABBEAgAUEANgIEIAFBFTYCAAsMAwsgBC0AAAR+IAQpAxAFQgALIRQgBEIEEBMaIAQQFQRAIAEEQCABQQA2AgQgAUEBNgIACwwDCyAEEAwgBBAMIgZHBEAgAQRAIAFBADYCBCABQRM2AgALDAMLIAQQFSEHIAQQFa0iFiAHrSIVfCIYIBMgFHwiFFYEQCABBEAgAUEANgIEIAFBFTYCAAsMAwsCQCADQQRxRQ0AIBQgGFENACABBEAgAUEANgIEIAFBFTYCAAsMAwsgBq0gARBqIgNFDQIgAyAWNwMgIAMgFTcDGCADQQA6ACwMAQsgGCABEGoiA0UNASADIBY3AyAgAyAVNwMYIANBAToALAsCQCASQhR8IhQgBCkDCFYEQCAEQQA6AAAMAQsgBCAUNwMQIARBAToAAAsgBBAMIQYCQCADKQMYIAMpAyB8IBIgE3xWDQACQCAGRQRAIAUtAARBBHFFDQELAkAgEkIWfCISIAQpAwhWBEAgBEEAOgAADAELIAQgEjcDECAEQQE6AAALIAQtAAAEfiAEKQMIIAQpAxB9BUIACyIUIAatIhJUDQEgBS0ABEEEcUEAIBIgFFIbDQEgBkUNACADIAQgEhATIAZBACABEDUiBjYCKCAGDQAgAxAWDAILAkAgEyADKQMgIhJYBEACQCASIBN9IhIgBCkDCFYEQCAEQQA6AAAMAQsgBCASNwMQIARBAToAAAsgBCADKQMYEBMiBkUNAiAGIAMpAxgQFyIHDQEgAQRAIAFBADYCBCABQQ42AgALIAMQFgwDCyAFKAIAIBJBABAUIQcgBSgCACEGIAdBf0wEQCABBEAgASAGKAIMNgIAIAEgBigCEDYCBAsgAxAWDAMLQQAhByAGEDMgAykDIFENACABBEAgAUEANgIEIAFBEzYCAAsgAxAWDAILQgAhFAJAAkAgAykDGCIWUEUEQANAIBQgAykDCFIiC0UEQCADLQAsDQMgFkIuVA0DAn8CQCADKQMQIhVCgIAEfCISIBVaQQAgEkKAgICAAVQbRQ0AIAMoAgAgEqdBBHQQNCIGRQ0AIAMgBjYCAAJAIAMpAwgiFSASWg0AIAYgFadBBHRqIgZCADcCACAGQgA3AAUgFUIBfCIVIBJRDQADQCADKAIAIBWnQQR0aiIGQgA3AgAgBkIANwAFIBVCAXwiFSASUg0ACwsgAyASNwMIIAMgEjcDEEEBDAELIAEEQCABQQA2AgQgAUEONgIAC0EAC0UNBAtB2AAQCSIGBH8gBkIANwMgIAZBADYCGCAGQv////8PNwMQIAZBADsBDCAGQb+GKDYCCCAGQQE6AAYgBkEAOwEEIAZBADYCACAGQgA3A0ggBkGAgNiNeDYCRCAGQgA3AyggBkIANwMwIAZCADcDOCAGQUBrQQA7AQAgBkIANwNQIAYFQQALIQYgAygCACAUp0EEdGogBjYCAAJAIAYEQCAGIAUoAgAgB0EAIAEQaCISQn9VDQELIAsNBCABKAIAQRNHDQQgAQRAIAFBADYCBCABQRU2AgALDAQLIBRCAXwhFCAWIBJ9IhZCAFINAAsLIBQgAykDCFINAAJAIAUtAARBBHFFDQAgBwRAIActAAAEfyAHKQMQIAcpAwhRBUEAC0UNAgwBCyAFKAIAEDMiEkJ/VwRAIAUoAgAhBiABBEAgASAGKAIMNgIAIAEgBigCEDYCBAsgAxAWDAULIBIgAykDGCADKQMgfFINAQsgBxAIAn4gCARAAn8gF0IAVwRAIAUgCCABEEghFwsgBSADIAEQSCISIBdVCwRAIAgQFiASDAILIAMQFgwFC0IAIAUtAARBBHFFDQAaIAUgAyABEEgLIRcgAyEIDAMLIAEEQCABQQA2AgQgAUEVNgIACyAHEAggAxAWDAILIAMQFiAHEAgMAQsgAQRAIAFBADYCBCABQRU2AgALIAMQFgsCQCAMIAQoAgRrrCISIAQpAwhWBEAgBEEAOgAADAELIAQgEjcDECAEQQE6AAALIAQtAAAEfiAEKQMIIAQpAxB9BUIAC6ciB0ESa0EDSw0BCwsgBBAIIBdCf1UNAwwBCyAEEAgLIA8iAwRAIAMgASgCADYCACADIAEoAgQ2AgQLIAgQFgtBACEICyABQdAAaiQAIAgNAQsgAgRAIAIgBSgCCDYCACACIAUoAgw2AgQLDAELIAUgCCgCADYCQCAFIAgpAwg3AzAgBSAIKQMQNwM4IAUgCCgCKDYCICAIEAYgBSgCUCEIIAVBCGoiBCEBQQAhBwJAIAUpAzAiE1ANAEGAgICAeCEGAn8gE7pEAAAAAAAA6D+jRAAA4P///+9BpCIaRAAAAAAAAPBBYyAaRAAAAAAAAAAAZnEEQCAaqwwBC0EACyIDQYCAgIB4TQRAIANBAWsiA0EBdiADciIDQQJ2IANyIgNBBHYgA3IiA0EIdiADciIDQRB2IANyQQFqIQYLIAYgCCgCACIMTQ0AIAYQPCILRQRAIAEEQCABQQA2AgQgAUEONgIACwwBCwJAIAgpAwhCACAMG1AEQCAIKAIQIQ8MAQsgCCgCECEPA0AgDyAHQQJ0aigCACIBBEADQCABKAIYIQMgASALIAEoAhwgBnBBAnRqIg0oAgA2AhggDSABNgIAIAMiAQ0ACwsgB0EBaiIHIAxHDQALCyAPEAYgCCAGNgIAIAggCzYCEAsCQCAFKQMwUA0AQgAhEwJAIApBBHFFBEADQCAFKAJAIBOnQQR0aigCACgCMEEAQQAgAhAlIgFFDQQgBSgCUCABIBNBCCAEEE1FBEAgBCgCAEEKRw0DCyATQgF8IhMgBSkDMFQNAAwDCwALA0AgBSgCQCATp0EEdGooAgAoAjBBAEEAIAIQJSIBRQ0DIAUoAlAgASATQQggBBBNRQ0BIBNCAXwiEyAFKQMwVA0ACwwBCyACBEAgAiAEKAIANgIAIAIgBCgCBDYCBAsMAQsgBSAFKAIUNgIYDAELIAAgACgCMEEBajYCMCAFEEtBACEFCyAOQUBrJAAgBQsiBQ0BIAAQGhoLQQAhBQsgCUHwAGokACAFCxAAIwAgAGtBcHEiACQAIAALBgAgACQACwQAIwAL4CoDEX8IfgN8IwBBwMAAayIHJABBfyECAkAgAEUNAAJ/IAAtAChFBEBBACAAKAIYIAAoAhRGDQEaC0EBCyEBAkACQCAAKQMwIhRQRQRAIAAoAkAhCgNAIAogEqdBBHRqIgMtAAwhCwJAAkAgAygCCA0AIAsNACADKAIEIgNFDQEgAygCAEUNAQtBASEBCyAXIAtBAXOtQv8Bg3whFyASQgF8IhIgFFINAAsgF0IAUg0BCyAAKAIEQQhxIAFyRQ0BAn8gACgCACIDKAIkIgFBA0cEQCADKAIgBH9BfyADEBpBAEgNAhogAygCJAUgAQsEQCADEEMLQX8gA0EAQgBBDxAOQgBTDQEaIANBAzYCJAtBAAtBf0oNASAAKAIAKAIMQRZGBEAgACgCACgCEEEsRg0CCyAAKAIAIQEgAEEIagRAIAAgASgCDDYCCCAAIAEoAhA2AgwLDAILIAFFDQAgFCAXVARAIABBCGoEQCAAQQA2AgwgAEEUNgIICwwCCyAXp0EDdBAJIgtFDQFCfyEWQgAhEgNAAkAgCiASp0EEdGoiBigCACIDRQ0AAkAgBigCCA0AIAYtAAwNACAGKAIEIgFFDQEgASgCAEUNAQsgFiADKQNIIhMgEyAWVhshFgsgBi0ADEUEQCAXIBlYBEAgCxAGIABBCGoEQCAAQQA2AgwgAEEUNgIICwwECyALIBmnQQN0aiASNwMAIBlCAXwhGQsgEkIBfCISIBRSDQALIBcgGVYEQCALEAYgAEEIagRAIABBADYCDCAAQRQ2AggLDAILAkACQCAAKAIAKQMYQoCACINQDQACQAJAIBZCf1INACAAKQMwIhNQDQIgE0IBgyEVIAAoAkAhAwJAIBNCAVEEQEJ/IRRCACESQgAhFgwBCyATQn6DIRlCfyEUQgAhEkIAIRYDQCADIBKnQQR0aigCACIBBEAgFiABKQNIIhMgEyAWVCIBGyEWIBQgEiABGyEUCyADIBJCAYQiGKdBBHRqKAIAIgEEQCAWIAEpA0giEyATIBZUIgEbIRYgFCAYIAEbIRQLIBJCAnwhEiAZQgJ9IhlQRQ0ACwsCQCAVUA0AIAMgEqdBBHRqKAIAIgFFDQAgFiABKQNIIhMgEyAWVCIBGyEWIBQgEiABGyEUCyAUQn9RDQBCACETIwBBEGsiBiQAAkAgACAUIABBCGoiCBBBIhVQDQAgFSAAKAJAIBSnQQR0aigCACIKKQMgIhh8IhQgGFpBACAUQn9VG0UEQCAIBEAgCEEWNgIEIAhBBDYCAAsMAQsgCi0ADEEIcUUEQCAUIRMMAQsgACgCACAUQQAQFCEBIAAoAgAhAyABQX9MBEAgCARAIAggAygCDDYCACAIIAMoAhA2AgQLDAELIAMgBkEMakIEEBFCBFIEQCAAKAIAIQEgCARAIAggASgCDDYCACAIIAEoAhA2AgQLDAELIBRCBHwgFCAGKAAMQdCWncAARhtCFEIMAn9BASEBAkAgCikDKEL+////D1YNACAKKQMgQv7///8PVg0AQQAhAQsgAQsbfCIUQn9XBEAgCARAIAhBFjYCBCAIQQQ2AgALDAELIBQhEwsgBkEQaiQAIBMiFkIAUg0BIAsQBgwFCyAWUA0BCwJ/IAAoAgAiASgCJEEBRgRAIAFBDGoEQCABQQA2AhAgAUESNgIMC0F/DAELQX8gAUEAIBZBERAOQgBTDQAaIAFBATYCJEEAC0F/Sg0BC0IAIRYCfyAAKAIAIgEoAiRBAUYEQCABQQxqBEAgAUEANgIQIAFBEjYCDAtBfwwBC0F/IAFBAEIAQQgQDkIAUw0AGiABQQE2AiRBAAtBf0oNACAAKAIAIQEgAEEIagRAIAAgASgCDDYCCCAAIAEoAhA2AgwLIAsQBgwCCyAAKAJUIgIEQCACQgA3AxggAigCAEQAAAAAAAAAACACKAIMIAIoAgQRDgALIABBCGohBCAXuiEcQgAhFAJAAkACQANAIBcgFCITUgRAIBO6IByjIRsgE0IBfCIUuiAcoyEaAkAgACgCVCICRQ0AIAIgGjkDKCACIBs5AyAgAisDECAaIBuhRAAAAAAAAAAAoiAboCIaIAIrAxihY0UNACACKAIAIBogAigCDCACKAIEEQ4AIAIgGjkDGAsCfwJAIAAoAkAgCyATp0EDdGopAwAiE6dBBHRqIg0oAgAiAQRAIAEpA0ggFlQNAQsgDSgCBCEFAkACfwJAIA0oAggiAkUEQCAFRQ0BQQEgBSgCACICQQFxDQIaIAJBwABxQQZ2DAILQQEgBQ0BGgsgDSABECsiBTYCBCAFRQ0BIAJBAEcLIQZBACEJIwBBEGsiDCQAAkAgEyAAKQMwWgRAIABBCGoEQCAAQQA2AgwgAEESNgIIC0F/IQkMAQsgACgCQCIKIBOnIgNBBHRqIg8oAgAiAkUNACACLQAEDQACQCACKQNIQhp8IhhCf1cEQCAAQQhqBEAgAEEWNgIMIABBBDYCCAsMAQtBfyEJIAAoAgAgGEEAEBRBf0wEQCAAKAIAIQIgAEEIagRAIAAgAigCDDYCCCAAIAIoAhA2AgwLDAILIAAoAgBCBCAMQQxqIABBCGoiDhAtIhBFDQEgEBAMIQEgEBAMIQggEC0AAAR/IBApAxAgECkDCFEFQQALIQIgEBAIIAJFBEAgDgRAIA5BADYCBCAOQRQ2AgALDAILAkAgCEUNACAAKAIAIAGtQQEQFEF/TARAQYSEASgCACECIA4EQCAOIAI2AgQgDkEENgIACwwDC0EAIAAoAgAgCEEAIA4QRSIBRQ0BIAEgCEGAAiAMQQhqIA4QbiECIAEQBiACRQ0BIAwoAggiAkUNACAMIAIQbSICNgIIIA8oAgAoAjQgAhBvIQIgDygCACACNgI0CyAPKAIAIgJBAToABEEAIQkgCiADQQR0aigCBCIBRQ0BIAEtAAQNASACKAI0IQIgAUEBOgAEIAEgAjYCNAwBC0F/IQkLIAxBEGokACAJQQBIDQUgACgCABAfIhhCAFMNBSAFIBg3A0ggBgRAQQAhDCANKAIIIg0hASANRQRAIAAgACATQQhBABB/IgwhASAMRQ0HCwJAAkAgASAHQQhqECFBf0wEQCAEBEAgBCABKAIMNgIAIAQgASgCEDYCBAsMAQsgBykDCCISQsAAg1AEQCAHQQA7ATggByASQsAAhCISNwMICwJAAkAgBSgCECICQX5PBEAgBy8BOCIDRQ0BIAUgAzYCECADIQIMAgsgAg0AIBJCBINQDQAgByAHKQMgNwMoIAcgEkIIhCISNwMIQQAhAgwBCyAHIBJC9////w+DIhI3AwgLIBJCgAGDUARAIAdBADsBOiAHIBJCgAGEIhI3AwgLAn8gEkIEg1AEQEJ/IRVBgAoMAQsgBSAHKQMgIhU3AyggEkIIg1AEQAJAAkACQAJAQQggAiACQX1LG0H//wNxDg0CAwMDAwMDAwEDAwMAAwtBgApBgAIgFUKUwuTzD1YbDAQLQYAKQYACIBVCg4Ow/w9WGwwDC0GACkGAAiAVQv////8PVhsMAgtBgApBgAIgFUIAUhsMAQsgBSAHKQMoNwMgQYACCyEPIAAoAgAQHyITQn9XBEAgACgCACECIAQEQCAEIAIoAgw2AgAgBCACKAIQNgIECwwBCyAFIAUvAQxB9/8DcTsBDCAAIAUgDxA3IgpBAEgNACAHLwE4IghBCCAFKAIQIgMgA0F9SxtB//8DcSICRyEGAkACQAJAAkACQAJAAkAgAiAIRwRAIANBAEchAwwBC0EAIQMgBS0AAEGAAXFFDQELIAUvAVIhCSAHLwE6IQIMAQsgBS8BUiIJIAcvAToiAkYNAQsgASABKAIwQQFqNgIwIAJB//8DcQ0BIAEhAgwCCyABIAEoAjBBAWo2AjBBACEJDAILQSZBACAHLwE6QQFGGyICRQRAIAQEQCAEQQA2AgQgBEEYNgIACyABEAsMAwsgACABIAcvATpBACAAKAIcIAIRBgAhAiABEAsgAkUNAgsgCUEARyEJIAhBAEcgBnFFBEAgAiEBDAELIAAgAiAHLwE4EIEBIQEgAhALIAFFDQELAkAgCEUgBnJFBEAgASECDAELIAAgAUEAEIABIQIgARALIAJFDQELAkAgA0UEQCACIQMMAQsgACACIAUoAhBBASAFLwFQEIIBIQMgAhALIANFDQELAkAgCUUEQCADIQEMAQsgBSgCVCIBRQRAIAAoAhwhAQsCfyAFLwFSGkEBCwRAIAQEQCAEQQA2AgQgBEEYNgIACyADEAsMAgsgACADIAUvAVJBASABQQARBgAhASADEAsgAUUNAQsgACgCABAfIhhCf1cEQCAAKAIAIQIgBARAIAQgAigCDDYCACAEIAIoAhA2AgQLDAELAkAgARAyQQBOBEACfwJAAkAgASAHQUBrQoDAABARIhJCAVMNAEIAIRkgFUIAVQRAIBW5IRoDQCAAIAdBQGsgEhAbQQBIDQMCQCASQoDAAFINACAAKAJUIgJFDQAgAiAZQoBAfSIZuSAaoxB7CyABIAdBQGtCgMAAEBEiEkIAVQ0ACwwBCwNAIAAgB0FAayASEBtBAEgNAiABIAdBQGtCgMAAEBEiEkIAVQ0ACwtBACASQn9VDQEaIAQEQCAEIAEoAgw2AgAgBCABKAIQNgIECwtBfwshAiABEBoaDAELIAQEQCAEIAEoAgw2AgAgBCABKAIQNgIEC0F/IQILIAEgB0EIahAhQX9MBEAgBARAIAQgASgCDDYCACAEIAEoAhA2AgQLQX8hAgsCf0EAIQkCQCABIgNFDQADQCADLQAaQQFxBEBB/wEhCSADQQBCAEEQEA4iFUIAUw0CIBVCBFkEQCADQQxqBEAgA0EANgIQIANBFDYCDAsMAwsgFachCQwCCyADKAIAIgMNAAsLIAlBGHRBGHUiA0F/TAsEQCAEBEAgBCABKAIMNgIAIAQgASgCEDYCBAsgARALDAELIAEQCyACQQBIDQAgACgCABAfIRUgACgCACECIBVCf1cEQCAEBEAgBCACKAIMNgIAIAQgAigCEDYCBAsMAQsgAiATEHVBf0wEQCAAKAIAIQIgBARAIAQgAigCDDYCACAEIAIoAhA2AgQLDAELIAcpAwgiE0LkAINC5ABSBEAgBARAIARBADYCBCAEQRQ2AgALDAELAkAgBS0AAEEgcQ0AIBNCEINQRQRAIAUgBygCMDYCFAwBCyAFQRRqEAEaCyAFIAcvATg2AhAgBSAHKAI0NgIYIAcpAyAhEyAFIBUgGH03AyAgBSATNwMoIAUgBS8BDEH5/wNxIANB/wFxQQF0cjsBDCAPQQp2IQNBPyEBAkACQAJAAkAgBSgCECICQQxrDgMAAQIBCyAFQS47AQoMAgtBLSEBIAMNACAFKQMoQv7///8PVg0AIAUpAyBC/v///w9WDQBBFCEBIAJBCEYNACAFLwFSQQFGDQAgBSgCMCICBH8gAi8BBAVBAAtB//8DcSICBEAgAiAFKAIwKAIAakEBay0AAEEvRg0BC0EKIQELIAUgATsBCgsgACAFIA8QNyICQQBIDQAgAiAKRwRAIAQEQCAEQQA2AgQgBEEUNgIACwwBCyAAKAIAIBUQdUF/Sg0BIAAoAgAhAiAEBEAgBCACKAIMNgIAIAQgAigCEDYCBAsLIA0NByAMEAsMBwsgDQ0CIAwQCwwCCyAFIAUvAQxB9/8DcTsBDCAAIAVBgAIQN0EASA0FIAAgEyAEEEEiE1ANBSAAKAIAIBNBABAUQX9MBEAgACgCACECIAQEQCAEIAIoAgw2AgAgBCACKAIQNgIECwwGCyAFKQMgIRIjAEGAQGoiAyQAAkAgElBFBEAgAEEIaiECIBK6IRoDQEF/IQEgACgCACADIBJCgMAAIBJCgMAAVBsiEyACEGVBAEgNAiAAIAMgExAbQQBIDQIgACgCVCAaIBIgE30iErqhIBqjEHsgEkIAUg0ACwtBACEBCyADQYBAayQAIAFBf0oNAUEBIREgAUEcdkEIcUEIRgwCCyAEBEAgBEEANgIEIARBDjYCAAsMBAtBAAtFDQELCyARDQBBfyECAkAgACgCABAfQgBTDQAgFyEUQQAhCkIAIRcjAEHwAGsiESQAAkAgACgCABAfIhVCAFkEQCAUUEUEQANAIAAgACgCQCALIBenQQN0aigCAEEEdGoiAygCBCIBBH8gAQUgAygCAAtBgAQQNyIBQQBIBEBCfyEXDAQLIAFBAEcgCnIhCiAXQgF8IhcgFFINAAsLQn8hFyAAKAIAEB8iGEJ/VwRAIAAoAgAhASAAQQhqBEAgACABKAIMNgIIIAAgASgCEDYCDAsMAgsgEULiABAXIgZFBEAgAEEIagRAIABBADYCDCAAQQ42AggLDAILIBggFX0hEyAVQv////8PViAUQv//A1ZyIApyQQFxBEAgBkGZEkEEECwgBkIsEBggBkEtEA0gBkEtEA0gBkEAEBIgBkEAEBIgBiAUEBggBiAUEBggBiATEBggBiAVEBggBkGUEkEEECwgBkEAEBIgBiAYEBggBkEBEBILIAZBnhJBBBAsIAZBABASIAYgFEL//wMgFEL//wNUG6dB//8DcSIBEA0gBiABEA0gBkF/IBOnIBNC/v///w9WGxASIAZBfyAVpyAVQv7///8PVhsQEiAGIABBJEEgIAAtACgbaigCACIDBH8gAy8BBAVBAAtB//8DcRANIAYtAABFBEAgAEEIagRAIABBADYCDCAAQRQ2AggLIAYQCAwCCyAAIAYoAgQgBi0AAAR+IAYpAxAFQgALEBshASAGEAggAUEASA0BIAMEQCAAIAMoAgAgAzMBBBAbQQBIDQILIBMhFwwBCyAAKAIAIQEgAEEIagRAIAAgASgCDDYCCCAAIAEoAhA2AgwLQn8hFwsgEUHwAGokACAXQgBTDQAgACgCABAfQj+HpyECCyALEAYgAkEASA0BAn8gACgCACIBKAIkQQFHBEAgAUEMagRAIAFBADYCECABQRI2AgwLQX8MAQsgASgCICICQQJPBEAgAUEMagRAIAFBADYCECABQR02AgwLQX8MAQsCQCACQQFHDQAgARAaQQBODQBBfwwBCyABQQBCAEEJEA5Cf1cEQCABQQI2AiRBfwwBCyABQQA2AiRBAAtFDQIgACgCACECIAQEQCAEIAIoAgw2AgAgBCACKAIQNgIECwwBCyALEAYLIAAoAlQQfCAAKAIAEENBfyECDAILIAAoAlQQfAsgABBLQQAhAgsgB0HAwABqJAAgAgtFAEHwgwFCADcDAEHogwFCADcDAEHggwFCADcDAEHYgwFCADcDAEHQgwFCADcDAEHIgwFCADcDAEHAgwFCADcDAEHAgwELoQMBCH8jAEGgAWsiAiQAIAAQMQJAAn8CQCAAKAIAIgFBAE4EQCABQbATKAIASA0BCyACIAE2AhAgAkEgakH2ESACQRBqEHZBASEGIAJBIGohBCACQSBqECIhA0EADAELIAFBAnQiAUGwEmooAgAhBQJ/AkACQCABQcATaigCAEEBaw4CAAEECyAAKAIEIQNB9IIBKAIAIQdBACEBAkACQANAIAMgAUHQ8QBqLQAARwRAQdcAIQQgAUEBaiIBQdcARw0BDAILCyABIgQNAEGw8gAhAwwBC0Gw8gAhAQNAIAEtAAAhCCABQQFqIgMhASAIDQAgAyEBIARBAWsiBA0ACwsgBygCFBogAwwBC0EAIAAoAgRrQQJ0QdjAAGooAgALIgRFDQEgBBAiIQMgBUUEQEEAIQVBASEGQQAMAQsgBRAiQQJqCyEBIAEgA2pBAWoQCSIBRQRAQegSKAIAIQUMAQsgAiAENgIIIAJBrBJBkRIgBhs2AgQgAkGsEiAFIAYbNgIAIAFBqwogAhB2IAAgATYCCCABIQULIAJBoAFqJAAgBQszAQF/IAAoAhQiAyABIAIgACgCECADayIBIAEgAksbIgEQBxogACAAKAIUIAFqNgIUIAILBgBBsIgBCwYAQayIAQsGAEGkiAELBwAgAEEEagsHACAAQQhqCyYBAX8gACgCFCIBBEAgARALCyAAKAIEIQEgAEEEahAxIAAQBiABC6kBAQN/AkAgAC0AACICRQ0AA0AgAS0AACIERQRAIAIhAwwCCwJAIAIgBEYNACACQSByIAIgAkHBAGtBGkkbIAEtAAAiAkEgciACIAJBwQBrQRpJG0YNACAALQAAIQMMAgsgAUEBaiEBIAAtAAEhAiAAQQFqIQAgAg0ACwsgA0H/AXEiAEEgciAAIABBwQBrQRpJGyABLQAAIgBBIHIgACAAQcEAa0EaSRtrC8sGAgJ+An8jAEHgAGsiByQAAkACQAJAAkACQAJAAkACQAJAAkACQCAEDg8AAQoCAwQGBwgICAgICAUICyABQgA3AyAMCQsgACACIAMQESIFQn9XBEAgAUEIaiIBBEAgASAAKAIMNgIAIAEgACgCEDYCBAsMCAsCQCAFUARAIAEpAygiAyABKQMgUg0BIAEgAzcDGCABQQE2AgQgASgCAEUNASAAIAdBKGoQIUF/TARAIAFBCGoiAQRAIAEgACgCDDYCACABIAAoAhA2AgQLDAoLAkAgBykDKCIDQiCDUA0AIAcoAlQgASgCMEYNACABQQhqBEAgAUEANgIMIAFBBzYCCAsMCgsgA0IEg1ANASAHKQNAIAEpAxhRDQEgAUEIagRAIAFBADYCDCABQRU2AggLDAkLIAEoAgQNACABKQMoIgMgASkDICIGVA0AIAUgAyAGfSIDWA0AIAEoAjAhBANAIAECfyAFIAN9IgZC/////w8gBkL/////D1QbIganIQBBACACIAOnaiIIRQ0AGiAEIAggAEHUgAEoAgARAAALIgQ2AjAgASABKQMoIAZ8NwMoIAUgAyAGfCIDVg0ACwsgASABKQMgIAV8NwMgDAgLIAEoAgRFDQcgAiABKQMYIgM3AxggASgCMCEAIAJBADYCMCACIAM3AyAgAiAANgIsIAIgAikDAELsAYQ3AwAMBwsgA0IIWgR+IAIgASgCCDYCACACIAEoAgw2AgRCCAVCfwshBQwGCyABEAYMBQtCfyEFIAApAxgiA0J/VwRAIAFBCGoiAQRAIAEgACgCDDYCACABIAAoAhA2AgQLDAULIAdBfzYCGCAHQo+AgICAAjcDECAHQoyAgIDQATcDCCAHQomAgICgATcDACADQQggBxAkQn+FgyEFDAQLIANCD1gEQCABQQhqBEAgAUEANgIMIAFBEjYCCAsMAwsgAkUNAgJAIAAgAikDACACKAIIEBRBAE4EQCAAEDMiA0J/VQ0BCyABQQhqIgEEQCABIAAoAgw2AgAgASAAKAIQNgIECwwDCyABIAM3AyAMAwsgASkDICEFDAILIAFBCGoEQCABQQA2AgwgAUEcNgIICwtCfyEFCyAHQeAAaiQAIAULjAcCAn4CfyMAQRBrIgckAAJAAkACQAJAAkACQAJAAkACQAJAIAQOEQABAgMFBggICAgICAgIBwgECAsgAUJ/NwMgIAFBADoADyABQQA7AQwgAUIANwMYIAEoAqxAIAEoAqhAKAIMEQEArUIBfSEFDAgLQn8hBSABKAIADQdCACEFIANQDQcgAS0ADQ0HIAFBKGohBAJAA0ACQCAHIAMgBX03AwggASgCrEAgAiAFp2ogB0EIaiABKAKoQCgCHBEAACEIQgAgBykDCCAIQQJGGyAFfCEFAkACQAJAIAhBAWsOAwADAQILIAFBAToADSABKQMgIgNCf1cEQCABBEAgAUEANgIEIAFBFDYCAAsMBQsgAS0ADkUNBCADIAVWDQQgASADNwMYIAFBAToADyACIAQgA6cQBxogASkDGCEFDAwLIAEtAAwNAyAAIARCgMAAEBEiBkJ/VwRAIAEEQCABIAAoAgw2AgAgASAAKAIQNgIECwwECyAGUARAIAFBAToADCABKAKsQCABKAKoQCgCGBEDACABKQMgQn9VDQEgAUIANwMgDAELAkAgASkDIEIAWQRAIAFBADoADgwBCyABIAY3AyALIAEoAqxAIAQgBiABKAKoQCgCFBEPABoLIAMgBVYNAQwCCwsgASgCAA0AIAEEQCABQQA2AgQgAUEUNgIACwsgBVBFBEAgAUEAOgAOIAEgASkDGCAFfDcDGAwIC0J/QgAgASgCABshBQwHCyABKAKsQCABKAKoQCgCEBEBAK1CAX0hBQwGCyABLQAQBEAgAS0ADQRAIAIgAS0ADwR/QQAFQQggASgCFCIAIABBfUsbCzsBMCACIAEpAxg3AyAgAiACKQMAQsgAhDcDAAwHCyACIAIpAwBCt////w+DNwMADAYLIAJBADsBMCACKQMAIQMgAS0ADQRAIAEpAxghBSACIANCxACENwMAIAIgBTcDGEIAIQUMBgsgAiADQrv///8Pg0LAAIQ3AwAMBQsgAS0ADw0EIAEoAqxAIAEoAqhAKAIIEQEArCEFDAQLIANCCFoEfiACIAEoAgA2AgAgAiABKAIENgIEQggFQn8LIQUMAwsgAUUNAiABKAKsQCABKAKoQCgCBBEDACABEDEgARAGDAILIAdBfzYCAEEQIAcQJEI/hCEFDAELIAEEQCABQQA2AgQgAUEUNgIAC0J/IQULIAdBEGokACAFC2MAQcgAEAkiAEUEQEGEhAEoAgAhASACBEAgAiABNgIEIAJBATYCAAsgAA8LIABBADoADCAAQQA6AAQgACACNgIAIABBADYCOCAAQgA3AzAgACABQQkgAUEBa0EJSRs2AgggAAu3fAIefwZ+IAIpAwAhIiAAIAE2AhwgACAiQv////8PICJC/////w9UGz4CICAAQRBqIQECfyAALQAEBEACfyAALQAMQQJ0IQpBfiEEAkACQAJAIAEiBUUNACAFKAIgRQ0AIAUoAiRFDQAgBSgCHCIDRQ0AIAMoAgAgBUcNAAJAAkAgAygCICIGQTlrDjkBAgICAgICAgICAgIBAgICAQICAgICAgICAgICAgICAgICAQICAgICAgICAgICAQICAgICAgICAgEACyAGQZoFRg0AIAZBKkcNAQsgCkEFSw0AAkACQCAFKAIMRQ0AIAUoAgQiAQRAIAUoAgBFDQELIAZBmgVHDQEgCkEERg0BCyAFQeDAACgCADYCGEF+DAQLIAUoAhBFDQEgAygCJCEEIAMgCjYCJAJAIAMoAhAEQCADEDACQCAFKAIQIgYgAygCECIIIAYgCEkbIgFFDQAgBSgCDCADKAIIIAEQBxogBSAFKAIMIAFqNgIMIAMgAygCCCABajYCCCAFIAUoAhQgAWo2AhQgBSAFKAIQIAFrIgY2AhAgAyADKAIQIAFrIgg2AhAgCA0AIAMgAygCBDYCCEEAIQgLIAYEQCADKAIgIQYMAgsMBAsgAQ0AIApBAXRBd0EAIApBBEsbaiAEQQF0QXdBACAEQQRKG2pKDQAgCkEERg0ADAILAkACQAJAAkACQCAGQSpHBEAgBkGaBUcNASAFKAIERQ0DDAcLIAMoAhRFBEAgA0HxADYCIAwCCyADKAI0QQx0QYDwAWshBAJAIAMoAowBQQJODQAgAygCiAEiAUEBTA0AIAFBBUwEQCAEQcAAciEEDAELQYABQcABIAFBBkYbIARyIQQLIAMoAgQgCGogBEEgciAEIAMoAmgbIgFBH3AgAXJBH3NBCHQgAUGA/gNxQQh2cjsAACADIAMoAhBBAmoiATYCECADKAJoBEAgAygCBCABaiAFKAIwIgFBGHQgAUEIdEGAgPwHcXIgAUEIdkGA/gNxIAFBGHZycjYAACADIAMoAhBBBGo2AhALIAVBATYCMCADQfEANgIgIAUQCiADKAIQDQcgAygCICEGCwJAAkACQAJAIAZBOUYEfyADQaABakHkgAEoAgARAQAaIAMgAygCECIBQQFqNgIQIAEgAygCBGpBHzoAACADIAMoAhAiAUEBajYCECABIAMoAgRqQYsBOgAAIAMgAygCECIBQQFqNgIQIAEgAygCBGpBCDoAAAJAIAMoAhwiAUUEQCADKAIEIAMoAhBqQQA2AAAgAyADKAIQIgFBBWo2AhAgASADKAIEakEAOgAEQQIhBCADKAKIASIBQQlHBEBBBCABQQJIQQJ0IAMoAowBQQFKGyEECyADIAMoAhAiAUEBajYCECABIAMoAgRqIAQ6AAAgAyADKAIQIgFBAWo2AhAgASADKAIEakEDOgAAIANB8QA2AiAgBRAKIAMoAhBFDQEMDQsgASgCJCELIAEoAhwhCSABKAIQIQggASgCLCENIAEoAgAhBiADIAMoAhAiAUEBajYCEEECIQQgASADKAIEaiANQQBHQQF0IAZBAEdyIAhBAEdBAnRyIAlBAEdBA3RyIAtBAEdBBHRyOgAAIAMoAgQgAygCEGogAygCHCgCBDYAACADIAMoAhAiDUEEaiIGNgIQIAMoAogBIgFBCUcEQEEEIAFBAkhBAnQgAygCjAFBAUobIQQLIAMgDUEFajYCECADKAIEIAZqIAQ6AAAgAygCHCgCDCEEIAMgAygCECIBQQFqNgIQIAEgAygCBGogBDoAACADKAIcIgEoAhAEfyADKAIEIAMoAhBqIAEoAhQ7AAAgAyADKAIQQQJqNgIQIAMoAhwFIAELKAIsBEAgBQJ/IAUoAjAhBiADKAIQIQRBACADKAIEIgFFDQAaIAYgASAEQdSAASgCABEAAAs2AjALIANBxQA2AiAgA0EANgIYDAILIAMoAiAFIAYLQcUAaw4jAAQEBAEEBAQEBAQEBAQEBAQEBAQEBAIEBAQEBAQEBAQEBAMECyADKAIcIgEoAhAiBgRAIAMoAgwiCCADKAIQIgQgAS8BFCADKAIYIg1rIglqSQRAA0AgAygCBCAEaiAGIA1qIAggBGsiCBAHGiADIAMoAgwiDTYCEAJAIAMoAhwoAixFDQAgBCANTw0AIAUCfyAFKAIwIQZBACADKAIEIARqIgFFDQAaIAYgASANIARrQdSAASgCABEAAAs2AjALIAMgAygCGCAIajYCGCAFKAIcIgYQMAJAIAUoAhAiBCAGKAIQIgEgASAESxsiAUUNACAFKAIMIAYoAgggARAHGiAFIAUoAgwgAWo2AgwgBiAGKAIIIAFqNgIIIAUgBSgCFCABajYCFCAFIAUoAhAgAWs2AhAgBiAGKAIQIAFrIgE2AhAgAQ0AIAYgBigCBDYCCAsgAygCEA0MIAMoAhghDSADKAIcKAIQIQZBACEEIAkgCGsiCSADKAIMIghLDQALCyADKAIEIARqIAYgDWogCRAHGiADIAMoAhAgCWoiDTYCEAJAIAMoAhwoAixFDQAgBCANTw0AIAUCfyAFKAIwIQZBACADKAIEIARqIgFFDQAaIAYgASANIARrQdSAASgCABEAAAs2AjALIANBADYCGAsgA0HJADYCIAsgAygCHCgCHARAIAMoAhAiBCEJA0ACQCAEIAMoAgxHDQACQCADKAIcKAIsRQ0AIAQgCU0NACAFAn8gBSgCMCEGQQAgAygCBCAJaiIBRQ0AGiAGIAEgBCAJa0HUgAEoAgARAAALNgIwCyAFKAIcIgYQMAJAIAUoAhAiBCAGKAIQIgEgASAESxsiAUUNACAFKAIMIAYoAgggARAHGiAFIAUoAgwgAWo2AgwgBiAGKAIIIAFqNgIIIAUgBSgCFCABajYCFCAFIAUoAhAgAWs2AhAgBiAGKAIQIAFrIgE2AhAgAQ0AIAYgBigCBDYCCAtBACEEQQAhCSADKAIQRQ0ADAsLIAMoAhwoAhwhBiADIAMoAhgiAUEBajYCGCABIAZqLQAAIQEgAyAEQQFqNgIQIAMoAgQgBGogAToAACABBEAgAygCECEEDAELCwJAIAMoAhwoAixFDQAgAygCECIGIAlNDQAgBQJ/IAUoAjAhBEEAIAMoAgQgCWoiAUUNABogBCABIAYgCWtB1IABKAIAEQAACzYCMAsgA0EANgIYCyADQdsANgIgCwJAIAMoAhwoAiRFDQAgAygCECIEIQkDQAJAIAQgAygCDEcNAAJAIAMoAhwoAixFDQAgBCAJTQ0AIAUCfyAFKAIwIQZBACADKAIEIAlqIgFFDQAaIAYgASAEIAlrQdSAASgCABEAAAs2AjALIAUoAhwiBhAwAkAgBSgCECIEIAYoAhAiASABIARLGyIBRQ0AIAUoAgwgBigCCCABEAcaIAUgBSgCDCABajYCDCAGIAYoAgggAWo2AgggBSAFKAIUIAFqNgIUIAUgBSgCECABazYCECAGIAYoAhAgAWsiATYCECABDQAgBiAGKAIENgIIC0EAIQRBACEJIAMoAhBFDQAMCgsgAygCHCgCJCEGIAMgAygCGCIBQQFqNgIYIAEgBmotAAAhASADIARBAWo2AhAgAygCBCAEaiABOgAAIAEEQCADKAIQIQQMAQsLIAMoAhwoAixFDQAgAygCECIGIAlNDQAgBQJ/IAUoAjAhBEEAIAMoAgQgCWoiAUUNABogBCABIAYgCWtB1IABKAIAEQAACzYCMAsgA0HnADYCIAsCQCADKAIcKAIsBEAgAygCDCADKAIQIgFBAmpJBH8gBRAKIAMoAhANAkEABSABCyADKAIEaiAFKAIwOwAAIAMgAygCEEECajYCECADQaABakHkgAEoAgARAQAaCyADQfEANgIgIAUQCiADKAIQRQ0BDAcLDAYLIAUoAgQNAQsgAygCPA0AIApFDQEgAygCIEGaBUYNAQsCfyADKAKIASIBRQRAIAMgChCFAQwBCwJAAkACQCADKAKMAUECaw4CAAECCwJ/AkADQAJAAkAgAygCPA0AIAMQLyADKAI8DQAgCg0BQQAMBAsgAygCSCADKAJoai0AACEEIAMgAygC8C0iAUEBajYC8C0gASADKALsLWpBADoAACADIAMoAvAtIgFBAWo2AvAtIAEgAygC7C1qQQA6AAAgAyADKALwLSIBQQFqNgLwLSABIAMoAuwtaiAEOgAAIAMgBEECdGoiASABLwHkAUEBajsB5AEgAyADKAI8QQFrNgI8IAMgAygCaEEBaiIBNgJoIAMoAvAtIAMoAvQtRw0BQQAhBCADIAMoAlgiBkEATgR/IAMoAkggBmoFQQALIAEgBmtBABAPIAMgAygCaDYCWCADKAIAEAogAygCACgCEA0BDAILCyADQQA2AoQuIApBBEYEQCADIAMoAlgiAUEATgR/IAMoAkggAWoFQQALIAMoAmggAWtBARAPIAMgAygCaDYCWCADKAIAEApBA0ECIAMoAgAoAhAbDAILIAMoAvAtBEBBACEEIAMgAygCWCIBQQBOBH8gAygCSCABagVBAAsgAygCaCABa0EAEA8gAyADKAJoNgJYIAMoAgAQCiADKAIAKAIQRQ0BC0EBIQQLIAQL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bGl0ZXJhbC9sZW5ndGhzIHNldABpbnZhbGlkIGNvZGUgbGVuZ3RocyBzZXQAdW5rbm93biBoZWFkZXIgZmxhZ3Mgc2V0AGludmFsaWQgZGlzdGFuY2VzIHNldABpbnZhbGlkIGJpdCBsZW5ndGggcmVwZWF0AEZpbGUgYWxyZWFkeSBleGlzdHMAdG9vIG1hbnkgbGVuZ3RoIG9yIGRpc3RhbmNlIHN5bWJvbHMAaW52YWxpZCBzdG9yZWQgYmxvY2sgbGVuZ3RocwAlcyVzJXMAYnVmZmVyIGVycm9yAE5vIGVycm9yAHN0cmVhbSBlcnJvcgBUZWxsIGVycm9yAEludGVybmFsIGVycm9yAFNlZWsgZXJyb3IAV3JpdGUgZXJyb3IAZmlsZSBlcnJvcgBSZWFkIGVycm9yAFpsaWIgZXJyb3IAZGF0YSBlcnJvcgBDUkMgZXJyb3IAaW5jb21wYXRpYmxlIHZlcnNpb24AaW52YWxpZCBjb2RlIC0tIG1pc3NpbmcgZW5kLW9mLWJsb2NrAGluY29ycmVjdCBoZWFkZXIgY2hlY2sAaW5jb3JyZWN0IGxlbmd0aCBjaGVjawBpbmNvcnJlY3QgZGF0YSBjaGVjawBpbnZhbGlkIGRpc3RhbmNlIHRvbyBmYXIgYmFjawBoZWFkZXIgY3JjIG1pc21hdGNoADEuMi4xMy56bGliLW5nAGludmFsaWQgd2luZG93IHNpemUAUmVhZC1vbmx5IGFyY2hpdmUATm90IGEgemlwIGFyY2hpdmUAUmVzb3VyY2Ugc3RpbGwgaW4gdXNlAE1hbGxvYyBmYWlsdXJlAGludmFsaWQgYmxvY2sgdHlwZQBGYWlsdXJlIHRvIGNyZWF0ZSB0ZW1wb3JhcnkgZmlsZQBDYW4ndCBvcGVuIGZpbGUATm8gc3VjaCBmaWxlAFByZW1hdHVyZSBlbmQgb2YgZmlsZQBDYW4ndCByZW1vdmUgZmlsZQBpbnZhbGlkIGxpdGVyYWwvbGVuZ3RoIGNvZGUAaW52YWxpZCBkaXN0YW5jZSBjb2RlAHVua25vd24gY29tcHJlc3Npb24gbWV0aG9kAHN0cmVhbSBlbmQAQ29tcHJlc3NlZCBkYXRhIGludmFsaWQATXVsdGktZGlzayB6aXAgYXJjaGl2ZXMgbm90IHN1cHBvcnRlZABPcGVyYXRpb24gbm90IHN1cHBvcnRlZABFbmNyeXB0aW9uIG1ldGhvZCBub3Qgc3VwcG9ydGVkAENvbXByZXNzaW9uIG1ldGhvZCBub3Qgc3VwcG9ydGVkAEVudHJ5IGhhcyBiZWVuIGRlbGV0ZWQAQ29udGFpbmluZyB6aXAgYXJjaGl2ZSB3YXMgY2xvc2VkAENsb3NpbmcgemlwIGFyY2hpdmUgZmFpbGVkAFJlbmFtaW5nIHRlbXBvcmFyeSBmaWxlIGZhaWxlZABFbnRyeSBoYXMgYmVlbiBjaGFuZ2VkAE5vIHBhc3N3b3JkIHByb3ZpZGVkAFdyb25nIHBhc3N3b3JkIHByb3ZpZGVkAFVua25vd24gZXJyb3IgJWQAQUUAKG51bGwpADogAFBLBgcAUEsGBgBQSwUGAFBLAwQAUEsBAgAAAAA/BQAAwAcAAJMIAAB4CAAAbwUAAJEFAAB6BQAAsgUAAFYIAAAbBwAA1gQAAAsHAADqBgAAnAUAAMgGAACyCAAAHggAACgHAABHBAAAoAYAAGAFAAAuBAAAPgcAAD8IAAD+BwAAjgYAAMkIAADeCAAA5gcAALIGAABVBQAAqAcAACAAQcgTCxEBAAAAAQAAAAEAAAABAAAAAQBB7BMLCQEAAAABAAAAAgBBmBQLAQEAQbgUCwEBAEHSFAukLDomOyZlJmYmYyZgJiIg2CXLJdklQiZAJmomayY8JrolxCWVITwgtgCnAKwlqCGRIZMhkiGQIR8ilCGyJbwlIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgAzADQANQA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBIAEkASgBLAEwATQBOAE8AUABRAFIAUwBUAFUAVgBXAFgAWQBaAFsAXABdAF4AXwBgAGEAYgBjAGQAZQBmAGcAaABpAGoAawBsAG0AbgBvAHAAcQByAHMAdAB1AHYAdwB4AHkAegB7AHwAfQB+AAIjxwD8AOkA4gDkAOAA5QDnAOoA6wDoAO8A7gDsAMQAxQDJAOYAxgD0APYA8gD7APkA/wDWANwAogCjAKUApyCSAeEA7QDzAPoA8QDRAKoAugC/ABAjrAC9ALwAoQCrALsAkSWSJZMlAiUkJWElYiVWJVUlYyVRJVclXSVcJVslECUUJTQlLCUcJQAlPCVeJV8lWiVUJWklZiVgJVAlbCVnJWglZCVlJVklWCVSJVMlayVqJRglDCWIJYQljCWQJYAlsQPfAJMDwAOjA8MDtQDEA6YDmAOpA7QDHiLGA7UDKSJhIrEAZSJkIiAjISP3AEgisAAZIrcAGiJ/ILIAoCWgAAAAAACWMAd3LGEO7rpRCZkZxG0Hj/RqcDWlY+mjlWSeMojbDqS43Hke6dXgiNnSlytMtgm9fLF+By2455Edv5BkELcd8iCwakhxufPeQb6EfdTaGuvk3W1RtdT0x4XTg1aYbBPAqGtkevli/ezJZYpPXAEU2WwGY2M9D/r1DQiNyCBuO14QaUzkQWDVcnFnotHkAzxH1ARL/YUN0mu1CqX6qLU1bJiyQtbJu9tA+bys42zYMnVc30XPDdbcWT3Rq6ww2SY6AN5RgFHXyBZh0L+19LQhI8SzVpmVus8Ppb24nrgCKAiIBV+y2QzGJOkLsYd8by8RTGhYqx1hwT0tZraQQdx2BnHbAbwg0pgqENXviYWxcR+1tgal5L+fM9S46KLJB3g0+QAPjqgJlhiYDuG7DWp/LT1tCJdsZJEBXGPm9FFra2JhbBzYMGWFTgBi8u2VBmx7pQEbwfQIglfED/XG2bBlUOm3Euq4vot8iLn83x3dYkkt2hXzfNOMZUzU+1hhsk3OUbU6dAC8o+Iwu9RBpd9K15XYPW3E0aT79NbTaulpQ/zZbjRGiGet0Lhg2nMtBETlHQMzX0wKqsl8Dd08cQVQqkECJxAQC76GIAzJJbVoV7OFbyAJ1Ga5n+Rhzg753l6YydkpIpjQsLSo18cXPbNZgQ20LjtcvbetbLrAIIO47bazv5oM4rYDmtKxdDlH1eqvd9KdFSbbBIMW3HMSC2PjhDtklD5qbQ2oWmp6C88O5J3/CZMnrgAKsZ4HfUSTD/DSowiHaPIBHv7CBmldV2L3y2dlgHE2bBnnBmtudhvU/uAr04laetoQzErdZ2/fufn5776OQ763F9WOsGDoo9bWfpPRocTC2DhS8t9P8We70WdXvKbdBrU/SzaySNorDdhMGwqv9koDNmB6BEHD72DfVd9nqO+ObjF5vmlGjLNhyxqDZryg0m8lNuJoUpV3DMwDRwu7uRYCIi8mBVW+O7rFKAu9spJatCsEarNcp//XwjHP0LWLntksHa7eW7DCZJsm8mPsnKNqdQqTbQKpBgmcPzYO64VnB3ITVwAFgkq/lRR6uOKuK7F7OBu2DJuO0pINvtXlt+/cfCHf2wvU0tOGQuLU8fiz3Whug9ofzRa+gVsmufbhd7Bvd0e3GOZaCIhwag//yjsGZlwLARH/nmWPaa5i+NP/a2FFz2wWeOIKoO7SDddUgwROwrMDOWEmZ6f3FmDQTUdpSdt3bj5KatGu3FrW2WYL30DwO9g3U668qcWeu95/z7JH6f+1MBzyvb2KwrrKMJOzU6ajtCQFNtC6kwbXzSlX3lS/Z9kjLnpms7hKYcQCG2hdlCtvKje+C7ShjgzDG98FWo3vAi0AAAAARjtnZYx2zsrKTamvWevtTh/QiivVnSOEk6ZE4bLW25307bz4PqAVV3ibcjLrPTbTrQZRtmdL+BkhcJ98JavG4GOQoYWp3Qgq7+ZvT3xAK646e0zL8DblZLYNggGXfR190UZ6GBsL07ddMLTSzpbwM4itl1ZC4D75BNtZnAtQ/BpNa5t/hyYy0MEdVbVSuxFUFIB2Md7N356Y9rj7uYYnh/+9QOI18OlNc8uOKOBtysmmVq2sbBsEAyogY2Yu+zr6aMBdn6KN9DDktpNVdxDXtDErsNH7Zhl+vV1+G5wt4WfaFoYCEFsvrVZgSMjFxgwpg/1rTEmwwuMPi6WGFqD4NVCbn1Ca1jb/3O1Rmk9LFXsJcHIewz3bsYUGvNSkdiOo4k1EzSgA7WJuO4oH/Z3O5rumqYNx6wAsN9BnSTMLPtV1MFmwv33wH/lGl3pq4NObLNu0/uaWHVGgrXo0gd3lSMfmgi0NqyuCS5BM59g2CAaeDW9jVEDGzBJ7oakd8AQvW8tjSpGGyuXXva2ARBvpYQIgjgTIbSerjlZAzq8m37LpHbjXI1AReGVrdh32zTL8sPZVmXq7/DY8gJtTOFvCz35gpaq0LQwF8hZrYGGwL4Eni0jk7cbhS6v9hi6KjRlSzLZ+Nwb715hAwLD902b0HJVdk3lfEDrWGStdsyxA8Wtqe5YOoDY/oeYNWMR1qxwlM5B7QPnd0u+/5rWKnpYq9titTZMS4OQ8VNuDWcd9x7iBRqDdSwsJcg0wbhcJ6zeLT9BQ7oWd+UHDpp4kUADaxRY7vaDcdhQPmk1zars97Bb9BotzN0si3HFwRbni1gFYpO1mPW6gz5Iom6j3JxANcWErahSrZsO77V2k3n774D84wIda8o0u9bS2SZCVxtbs0/2xiRmwGCZfi39DzC07oooWXMdAW/VoBmCSDQK7y5FEgKz0js0FW8j2Yj5bUCbfHWtButcm6BWRHY9wsG0QDPZWd2k8G97GeiC5o+mG/UKvvZonZfAziCPLVO064AlefNtuO7aWx5TwraDxYwvkECUwg3XvfSraqUZNv4g20sPODbWmBEAcCUJ7e2zR3T+Nl+ZY6F2r8UcbkJYiH0vPvllwqNuTPQF01QZmEUagIvAAm0WVytbsOozti1+tnRQj66ZzRiHr2uln0L2M9Hb5bbJNngh4ADenPjtQwjGw9UR3i5IhvcY7jvv9XOtoWxgKLmB/b+Qt1sCiFrGlg2Yu2cVdSbwPEOATSSuHdtqNw5ectqTyVvsNXRDAajgUGzOkUiBUwZht/W7eVpoLTfDe6gvLuY/BhhAgh713RabN6Dng9o9cKrsm82yAQZb/JgV3uR1iEnNQy701a6zYAAAAAFiA4tfxBrR0qYZWo+INaOm6jYo+EwvcnUuLPkqFHaEJ3Z1D3nQbFX0sm/eqZxDJ4D+QKzeWFn2UzpafQwo7QhNSu6DE+z32Z6O9FLDoNir6sLbILRkwno5BsHxZjybjGtemAc1+IFduJqC1uW0ri/M1q2kknC0/h8St3VAUdoQmTPZm8eVwMFK98NKF9nvsz677DhgHfVi7X/26bJFrJS/J68f4YG2RWzjtc4xzZk3GK+avEYJg+bLa4BtlHk3GNUbNJOLvS3JBt8uQlvxArtykwEwLDUYaqFXG+H+bUGc8w9CF62pW00gy1jGfeV0P1SHd7QKIW7uh0NtZdijsCE1wbOqa2eq8OYFqXu7K4WCkkmGCczvn1NBjZzYHrfGpRPVxS5Nc9x0wBHf/50/8wa0XfCN6vvp12eZ6lw4i10peeleoidPR/iqLURz9wNoit5hawGAx3JbDaVx0FKfK61f/SgmAVsxfIw5MvfRFx4O+HUdhabTBN8rsQdUdPJqMa2QabrzNnDgflRzayN6X5IKGFwZVL5FQ9ncRsiG5hy1i4QfPtUiBmRYQAXvBW4pFiwMKp1yqjPH/8gwTKDahznhuISyvx6d6DJ8nmNvUrKaRjCxERiWqEuV9KvAys7xvces8jaZCutsFGjo50lGxB5gJMeVPoLez7Pg3UTtQ2BGaCFjzTaHepe75Xkc5stV5c+pVm6RD080HG1Mv0NXFsJONRVJEJMME53xD5jA3yNh6b0g6rcbObA6eTo7ZWuNTiQJjsV6r5ef982UFKrjuO2Dgbtm3SeiPFBFobcPf/vKAh34QVy74RvR2eKQjPfOaaWVzeL7M9S4dlHXMykSulbwcLndrtaghyO0owx+mo/1V/iMfglelSSEPJav2wbM0tZkz1mIwtYDBaDViFiO+XFx7Pr6L0rjoKIo4Cv9OldevFhU1eL+TY9vnE4EMrJi/RvQYXZFdngsyBR7p5cuIdqaTCJRxOo7C0mIOIAUphR5PcQX8mNiDqjuAA0jseDQZ1yC0+wCJMq2j0bJPdJo5cT7CuZPpaz/FSjO/J539KbjepalaCQwvDKpUr+59HyTQN0ekMuDuImRDtqKGlHIPW8Qqj7kTgwnvsNuJDWeQAjMtyILR+mEEh1k5hGWO9xL6za+SGBoGFE65XpSsbhUfkiRNn3Dz5BkmULyZxIdsQp3xNMJ/Jp1EKYXFxMtSjk/1GNbPF89/SUFsJ8mju+lfPPix394vGFmIjEDZalsLUlQRU9K2xvpU4GWi1AKyZnnf4j75PTWXf2uWz/+JQYR0twvc9FXcdXIDfy3y4ajjZH7ru+ScPBJiyp9K4ihIAWkWAlnp9NXwb6J2qO9AoQAAAADhtlLvg2vUBWLdhuoG16gL52H65IW8fA5kCi7hDK5RF+0YA/iPxYUSbnPX/Qp5+Rzrz6vziRItGWikf/YYXKMu+erxwZs3dyt6gSXEHosLJf89Wcqd4N8gfFaNzxTy8jn1RKDWl5kmPHYvdNMSJVoy85MI3ZFOjjdw+NzYMLhGXdEOFLKz05JYUmXAtzZv7lbX2by5tQQ6U1SyaLw8FhdK3aBFpb99w09ey5GgOsG/Qdt37a65qmtEWBw5qyjk5XPJUrecq48xdko5Y5kuM014z4Ufl61YmX1M7suSJEq0ZMX85ounIWBhRpcyjiKdHG/DK06AofbIakBAmoVgcI26gcbfVeMbWb8CrQtQZqclsYcRd17lzPG0BHqjW2ze3K2NaI5C77UIqA4DWkdqCXSmi78mSelioKMI1PJMeCwulJmafHv7R/qRGvGofn77hp+fTdRw/ZBSmhwmAHV0gn+DlTQtbPfpq4YWX/lpclXXiJPjhWfxPgONEIhRYlDIy+exfpkI06Mf4jIVTQ1WH2Pst6kxA9V0t+k0wuUGXGaa8L3QyB/fDU71PrscGlqxMvu7B2AU2drm/jhstBFIlGjJqSI6Jsv/vMwqSe4jTkPAwq/1ki3NKBTHLJ5GKEQ6Od6ljGsxx1Ht2ybnvzRC7ZHVo1vDOsGGRdAgMBc/geZrrmBQOUECjb+r4zvtRIcxw6Vmh5FKBFoXoOXsRU+NSDq5bP5oVg4j7rzvlbxTi5+SsmopwF0I9Ea36UIUWJm6yIB4DJpvGtEchftnTmqfbWCLftsyZBwGtI79sOZhlRSZl3Siy3gWf02S98kffZPDMZxydWNzEKjlmfEet3axXi3zUOh/HDI1+fbTg6sZt4mF+FY/1xc04lH91VQDEr3wfORcRi4LPpuo4d8t+g67J9TvWpGGADhMAOrZ+lIFqQKO3Ui03DIqaVrYy98IN6/VJtZOY3Q5LL7y080IoDylrN/KRBqNJSbHC8/HcVkgo3t3wULNJS4gEKPEwabxK+GW5hQAILT7Yv0yEYNLYP7nQU4fBvcc8GQqmhqFnMj17Ti3AwyO5exuU2MGj+Ux6evvHwgKWU3naITLDYkymeL5ykU6GHwX1XqhkT+bF8PQ/x3tMR6rv958djk0ncBr2/VkFC0U0kbCdg/AKJe5ksfzs7wmEgXuyXDYaCORbjrM0S6gSTCY8qZSRXRMs/Mmo9f5CEI2T1qtVJLcR7UkjqjdgPFePDajsV7rJVu/XXe021dZVTrhC7pYPI1QuYrfv8lyA2coxFGIShnXYquvhY3PpatsLhP5g0zOf2mteC2GxdxScCRqAJ9Gt4Z1pwHUmsML+nsivaiUQGAufqHWfJEAAAAAQ8umh8eQPNSEW5pTzycIc4zsrvQItzSnS3ySIJ5PEObdhLZhWd8sMhoUirVRaBiVEqO+Epb4JEHVM4LGfZlRFz5S95C6CW3D+cLLRLK+WWTxdf/jdS5lsDblwzfj1kHxoB3ndiRGfSVnjduiLPFJgm867wXrYXVWqKrT0foyoy65+QWpPaKf+n5pOX01Fatddt4N2vKFl4mxTjEOZH2zyCe2FU+j7Y8c4CYpm6tau7vokR08bMqHby8BIeiHq/I5xGBUvkA7zu0D8GhqSIz6SgtHXM2PHMaezNdgGRnk4t9aL0RY3nTeC52/eIzWw+qslQhMKxFT1nhSmHD/9GVGXbeu4Noz9XqJcD7cDjtCTi54ieip/NJy+r8Z1H1qKla7KeHwPK26am/ucczopQ1eyObG+E9inWIcIVbEm4n8F0rKN7HNTmwrng2njRlG2x85BRC5voFLI+3CgIVqF7MHrFR4oSvQIzt4k+id/9iUD9+bX6lYHwQzC1zPlYwOV+VzTZxD9MnH2aeKDH8gwXDtAIK7S4cG4NHURSt3U5AY9ZXT01MSV4jJQRRDb8ZfP/3mHPRbYZivwTLbZGe1c860ZDAFEuO0Xoiw95UuN7zpvBf/IhqQe3mAwziyJkTtgaSCrkoCBSoRmFZp2j7RIqas8WFtCnblNpAlpv02oujLjLqrACo9L1uwbmyQFukn7ITJZCciTuB8uB2jtx6adoScXDVPOtuxFKCI8t8GD7mjlC/6aDKofjOo+z34DnyVUt2t1pl7KlLC4XkRCUf+WnXV3hm+c1md5ekK3i5PjQsdzUtI1mvMzI3xn49GVxjEOsU4h/FjvwOq+exAYV9rEvkvlFEyiRPVaRNAlqK1x93eJ+eeFYFgGk4bM1mFvbSMtj9yz32Z9UsmA6YI7aUhQ5E3AQBakYaEAQvVx8qtUm9gfoMsq9gEqPBCV+s75NCgR3bw44zQd2fXSiQkHOyj8S9uZbLkyOI2v1KxdXT0Nj4IZhZ9w8CR+ZhawrpT/EUcrsrnX2VsYNs+9jOY9VC004nClJBCZBMUGf5AV9JYx4Lh2gHBKnyGRXHm1Qa6QFJNxtJyDg109YpW7qbJnUghYTeb8CL8PXemp6ck5WwBo64Qk4Pt2zUEaYCvVypLCdD/eIsWvLMtkTjot8J7IxFFMF+DZXOUJeL3z7+xtAQZNuacacmlV89OIQxVHWLH85opu2G6anDHPe4rXW6t4PvpeNN5LzsY36i/Q0X7/IjjfLf0cVz0P9fbcGRNiDOv6w+bBTje2M6eWVyVBAofXqKNVCIwrRfpliqTsgx50Hmq/gVKKDhGgY6/wtoU7IERsmvKbSBLiaaGzA39HJ9ONroYFAQAAJ0HAAAsCQAAhgUAAEgFAACnBQAAAAQAADIFAAC8BQAALAkAQYDBAAv3CQwACACMAAgATAAIAMwACAAsAAgArAAIAGwACADsAAgAHAAIAJwACABcAAgA3AAIADwACAC8AAgAfAAIAPwACAACAAgAggAIAEIACADCAAgAIgAIAKIACABiAAgA4gAIABIACACSAAgAUgAIANIACAAyAAgAsgAIAHIACADyAAgACgAIAIoACABKAAgAygAIACoACACqAAgAagAIAOoACAAaAAgAmgAIAFoACADaAAgAOgAIALoACAB6AAgA+gAIAAYACACGAAgARgAIAMYACAAmAAgApgAIAGYACADmAAgAFgAIAJYACABWAAgA1gAIADYACAC2AAgAdgAIAPYACAAOAAgAjgAIAE4ACADOAAgALgAIAK4ACABuAAgA7gAIAB4ACACeAAgAXgAIAN4ACAA+AAgAvgAIAH4ACAD+AAgAAQAIAIEACABBAAgAwQAIACEACAChAAgAYQAIAOEACAARAAgAkQAIAFEACADRAAgAMQAIALEACABxAAgA8QAIAAkACACJAAgASQAIAMkACAApAAgAqQAIAGkACADpAAgAGQAIAJkACABZAAgA2QAIADkACAC5AAgAeQAIAPkACAAFAAgAhQAIAEUACADFAAgAJQAIAKUACABlAAgA5QAIABUACACVAAgAVQAIANUACAA1AAgAtQAIAHUACAD1AAgADQAIAI0ACABNAAgAzQAIAC0ACACtAAgAbQAIAO0ACAAdAAgAnQAIAF0ACADdAAgAPQAIAL0ACAB9AAgA/QAIABMACQATAQkAkwAJAJMBCQBTAAkAUwEJANMACQDTAQkAMwAJADMBCQCzAAkAswEJAHMACQBzAQkA8wAJAPMBCQALAAkACwEJAIsACQCLAQkASwAJAEsBCQDLAAkAywEJACsACQArAQkAqwAJAKsBCQBrAAkAawEJAOsACQDrAQkAGwAJABsBCQCbAAkAmwEJAFsACQBbAQkA2wAJANsBCQA7AAkAOwEJALsACQC7AQkAewAJAHsBCQD7AAkA+wEJAAcACQAHAQkAhwAJAIcBCQBHAAkARwEJAMcACQDHAQkAJwAJACcBCQCnAAkApwEJAGcACQBnAQkA5wAJAOcBCQAXAAkAFwEJAJcACQCXAQkAVwAJAFcBCQDXAAkA1wEJADcACQA3AQkAtwAJALcBCQB3AAkAdwEJAPcACQD3AQkADwAJAA8BCQCPAAkAjwEJAE8ACQBPAQkAzwAJAM8BCQAvAAkALwEJAK8ACQCvAQkAbwAJAG8BCQDvAAkA7wEJAB8ACQAfAQkAnwAJAJ8BCQBfAAkAXwEJAN8ACQDfAQkAPwAJAD8BCQC/AAkA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SXMgYSBkaXJlY3RvcnkAVGV4dCBmaWxlIGJ1c3kARXhlYyBmb3JtYXQgZXJyb3IASW52YWxpZCBhcmd1bWVudABBcmd1bWVudCBsaXN0IHRvbyBsb25nAFN5bWJvbGljIGxpbmsgbG9vcABGaWxlbmFtZSB0b28gbG9uZwBUb28gbWFueSBvcGVuIGZpbGVzIGluIHN5c3RlbQBObyBmaWxlIGRlc2NyaXB0b3JzIGF2YWlsYWJsZQBCYWQgZmlsZSBkZXNjcmlwdG9yAE5vIGNoaWxkIHByb2Nlc3MAQmFkIGFkZHJlc3MARmlsZSB0b28gbGFyZ2UAVG9vIG1hbnkgbGlua3MATm8gbG9ja3MgYXZhaWxhYmxlAFJlc291cmNlIGRlYWRsb2NrIHdvdWxkIG9jY3VyAFN0YXRlIG5vdCByZWNvdmVyYWJsZQBQcmV2aW91cyBvd25lciBkaWVkAE9wZXJhdGlvbiBjYW5jZWxlZABGdW5jdGlvbiBub3QgaW1wbGVtZW50ZWQATm8gbWVzc2FnZSBvZiBkZXNpcmVkIHR5cGUASWRlbnRpZmllciByZW1vdmVkAERldmljZSBub3QgYSBzdHJlYW0ATm8gZGF0YSBhdmFpbGFibGUARGV2aWNlIHRpbWVvdXQAT3V0IG9mIHN0cmVhbXMgcmVzb3VyY2VzAExpbmsgaGFzIGJlZW4gc2V2ZXJlZABQcm90b2NvbCBlcnJvcgBCYWQgbWVzc2FnZQBGaWxlIGRlc2NyaXB0b3IgaW4gYmFkIHN0YXRlAE5vdCBhIHNvY2tldABEZXN0aW5hdGlvbiBhZGRyZXNzIHJlcXVpcmVkAE1lc3NhZ2UgdG9vIGxhcmdlAFByb3RvY29sIHdyb25nIHR5cGUgZm9yIHNvY2tldABQcm90b2NvbCBub3QgYXZhaWxhYmxlAFByb3RvY29sIG5vdCBzdXBwb3J0ZWQAU29ja2V0IHR5cGUgbm90IHN1cHBvcnRlZABOb3Qgc3VwcG9ydGVkAFByb3RvY29sIGZhbWlseSBub3Qgc3VwcG9ydGVkAEFkZHJlc3MgZmFtaWx5IG5vdCBzdXBwb3J0ZWQgYnkgcHJvdG9jb2wAQWRkcmVzcyBub3QgYXZhaWxhYmxlAE5ldHdvcmsgaXMgZG93bgBOZXR3b3JrIHVucmVhY2hhYmxlAENvbm5lY3Rpb24gcmVzZXQgYnkgbmV0d29yawBDb25uZWN0aW9uIGFib3J0ZWQATm8gYnVmZmVyIHNwYWNlIGF2YWlsYWJsZQBTb2NrZXQgaXMgY29ubmVjdGVkAFNvY2tldCBub3QgY29ubmVjdGVkAENhbm5vdCBzZW5kIGFmdGVyIHNvY2tldCBzaHV0ZG93bgBPcGVyYXRpb24gYWxyZWFkeSBpbiBwcm9ncmVzcwBPcGVyYXRpb24gaW4gcHJvZ3Jlc3MAU3RhbGUgZmlsZSBoYW5kbGUAUmVtb3RlIEkvTyBlcnJvcgBRdW90YSBleGNlZWRlZABObyBtZWRpdW0gZm91bmQAV3JvbmcgbWVkaXVtIHR5cGUATm8gZXJyb3IgaW5mb3JtYXRpb24AQcCAAQuFARMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAgAAAAIQAAACIAAAAjAAAAgERQADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADcAAAA4AAAAOQAAADIAAAAzAAAANAAAADUAAAA2AAAANwAAADgAQfSCAQsCXEQAQbCDAQsQ/////////////////////w==";Bo(Hi)||(Hi=P(Hi));function to(Je){try{if(Je==Hi&&ce)return new Uint8Array(ce);var st=Me(Je);if(st)return st;if(R)return R(Je);throw"sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS)"}catch(St){rs(St)}}function vo(Je,st){var St,lr,ee;try{ee=to(Je),lr=new WebAssembly.Module(ee),St=new WebAssembly.Instance(lr,st)}catch(Oe){var Ie=Oe.toString();throw te("failed to compile wasm module: "+Ie),(Ie.includes("imported Memory")||Ie.includes("memory import"))&&te("Memory size incompatibility issues may be due to changing INITIAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set INITIAL_MEMORY at runtime to something smaller than it was at compile time)."),Oe}return[St,lr]}function RA(){var Je={a:fu};function st(ee,Ie){var Oe=ee.exports;r.asm=Oe,Be=r.asm.g,z(Be.buffer),$=r.asm.W,gn(r.asm.h),wo("wasm-instantiate")}if(oi("wasm-instantiate"),r.instantiateWasm)try{var St=r.instantiateWasm(Je,st);return St}catch(ee){return te("Module.instantiateWasm callback failed with error: "+ee),!1}var lr=vo(Hi,Je);return st(lr[0]),r.asm}function pf(Je){return F.getFloat32(Je,!0)}function Eh(Je){return F.getFloat64(Je,!0)}function Ih(Je){return F.getInt16(Je,!0)}function ro(Je){return F.getInt32(Je,!0)}function jn(Je,st){F.setInt32(Je,st,!0)}function Rs(Je){for(;Je.length>0;){var st=Je.shift();if(typeof st=="function"){st(r);continue}var St=st.func;typeof St=="number"?st.arg===void 0?$.get(St)():$.get(St)(st.arg):St(st.arg===void 0?null:st.arg)}}function no(Je,st){var St=new Date(ro((Je>>2)*4)*1e3);jn((st>>2)*4,St.getUTCSeconds()),jn((st+4>>2)*4,St.getUTCMinutes()),jn((st+8>>2)*4,St.getUTCHours()),jn((st+12>>2)*4,St.getUTCDate()),jn((st+16>>2)*4,St.getUTCMonth()),jn((st+20>>2)*4,St.getUTCFullYear()-1900),jn((st+24>>2)*4,St.getUTCDay()),jn((st+36>>2)*4,0),jn((st+32>>2)*4,0);var lr=Date.UTC(St.getUTCFullYear(),0,1,0,0,0,0),ee=(St.getTime()-lr)/(1e3*60*60*24)|0;return jn((st+28>>2)*4,ee),no.GMTString||(no.GMTString=rt("GMT")),jn((st+40>>2)*4,no.GMTString),st}function lu(Je,st){return no(Je,st)}function cu(Je,st,St){ke.copyWithin(Je,st,st+St)}function uu(Je){try{return Be.grow(Je-Pe.byteLength+65535>>>16),z(Be.buffer),1}catch{}}function FA(Je){var st=ke.length;Je=Je>>>0;var St=2147483648;if(Je>St)return!1;for(var lr=1;lr<=4;lr*=2){var ee=st*(1+.2/lr);ee=Math.min(ee,Je+100663296);var Ie=Math.min(St,Ne(Math.max(Je,ee),65536)),Oe=uu(Ie);if(Oe)return!0}return!1}function NA(Je){Ae(Je)}function aa(Je){var st=Date.now()/1e3|0;return Je&&jn((Je>>2)*4,st),st}function la(){if(la.called)return;la.called=!0;var Je=new Date().getFullYear(),st=new Date(Je,0,1),St=new Date(Je,6,1),lr=st.getTimezoneOffset(),ee=St.getTimezoneOffset(),Ie=Math.max(lr,ee);jn((Sl()>>2)*4,Ie*60),jn((Cs()>>2)*4,+(lr!=ee));function Oe(fn){var ai=fn.toTimeString().match(/\(([A-Za-z ]+)\)$/);return ai?ai[1]:"GMT"}var ht=Oe(st),mt=Oe(St),Dt=rt(ht),tr=rt(mt);ee>2)*4,Dt),jn((Li()+4>>2)*4,tr)):(jn((Li()>>2)*4,tr),jn((Li()+4>>2)*4,Dt))}function OA(Je){la();var st=Date.UTC(ro((Je+20>>2)*4)+1900,ro((Je+16>>2)*4),ro((Je+12>>2)*4),ro((Je+8>>2)*4),ro((Je+4>>2)*4),ro((Je>>2)*4),0),St=new Date(st);jn((Je+24>>2)*4,St.getUTCDay());var lr=Date.UTC(St.getUTCFullYear(),0,1,0,0,0,0),ee=(St.getTime()-lr)/(1e3*60*60*24)|0;return jn((Je+28>>2)*4,ee),St.getTime()/1e3|0}var gr=typeof atob=="function"?atob:function(Je){var st="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",St="",lr,ee,Ie,Oe,ht,mt,Dt,tr=0;Je=Je.replace(/[^A-Za-z0-9\+\/\=]/g,"");do Oe=st.indexOf(Je.charAt(tr++)),ht=st.indexOf(Je.charAt(tr++)),mt=st.indexOf(Je.charAt(tr++)),Dt=st.indexOf(Je.charAt(tr++)),lr=Oe<<2|ht>>4,ee=(ht&15)<<4|mt>>2,Ie=(mt&3)<<6|Dt,St=St+String.fromCharCode(lr),mt!==64&&(St=St+String.fromCharCode(ee)),Dt!==64&&(St=St+String.fromCharCode(Ie));while(tr0||(Et(),Ir>0))return;function st(){Qn||(Qn=!0,r.calledRun=!0,!Ce&&(qt(),s(r),r.onRuntimeInitialized&&r.onRuntimeInitialized(),ir()))}r.setStatus?(r.setStatus("Running..."),setTimeout(function(){setTimeout(function(){r.setStatus("")},1),st()},1)):st()}if(r.run=pc,r.preInit)for(typeof r.preInit=="function"&&(r.preInit=[r.preInit]);r.preInit.length>0;)r.preInit.pop()();return pc(),e}}();typeof wT=="object"&&typeof Jj=="object"?Jj.exports=Kj:typeof define=="function"&&define.amd?define([],function(){return Kj}):typeof wT=="object"&&(wT.createModule=Kj)});var jp,Hde,jde,qde=It(()=>{jp=["number","number"],Hde=(X=>(X[X.ZIP_ER_OK=0]="ZIP_ER_OK",X[X.ZIP_ER_MULTIDISK=1]="ZIP_ER_MULTIDISK",X[X.ZIP_ER_RENAME=2]="ZIP_ER_RENAME",X[X.ZIP_ER_CLOSE=3]="ZIP_ER_CLOSE",X[X.ZIP_ER_SEEK=4]="ZIP_ER_SEEK",X[X.ZIP_ER_READ=5]="ZIP_ER_READ",X[X.ZIP_ER_WRITE=6]="ZIP_ER_WRITE",X[X.ZIP_ER_CRC=7]="ZIP_ER_CRC",X[X.ZIP_ER_ZIPCLOSED=8]="ZIP_ER_ZIPCLOSED",X[X.ZIP_ER_NOENT=9]="ZIP_ER_NOENT",X[X.ZIP_ER_EXISTS=10]="ZIP_ER_EXISTS",X[X.ZIP_ER_OPEN=11]="ZIP_ER_OPEN",X[X.ZIP_ER_TMPOPEN=12]="ZIP_ER_TMPOPEN",X[X.ZIP_ER_ZLIB=13]="ZIP_ER_ZLIB",X[X.ZIP_ER_MEMORY=14]="ZIP_ER_MEMORY",X[X.ZIP_ER_CHANGED=15]="ZIP_ER_CHANGED",X[X.ZIP_ER_COMPNOTSUPP=16]="ZIP_ER_COMPNOTSUPP",X[X.ZIP_ER_EOF=17]="ZIP_ER_EOF",X[X.ZIP_ER_INVAL=18]="ZIP_ER_INVAL",X[X.ZIP_ER_NOZIP=19]="ZIP_ER_NOZIP",X[X.ZIP_ER_INTERNAL=20]="ZIP_ER_INTERNAL",X[X.ZIP_ER_INCONS=21]="ZIP_ER_INCONS",X[X.ZIP_ER_REMOVE=22]="ZIP_ER_REMOVE",X[X.ZIP_ER_DELETED=23]="ZIP_ER_DELETED",X[X.ZIP_ER_ENCRNOTSUPP=24]="ZIP_ER_ENCRNOTSUPP",X[X.ZIP_ER_RDONLY=25]="ZIP_ER_RDONLY",X[X.ZIP_ER_NOPASSWD=26]="ZIP_ER_NOPASSWD",X[X.ZIP_ER_WRONGPASSWD=27]="ZIP_ER_WRONGPASSWD",X[X.ZIP_ER_OPNOTSUPP=28]="ZIP_ER_OPNOTSUPP",X[X.ZIP_ER_INUSE=29]="ZIP_ER_INUSE",X[X.ZIP_ER_TELL=30]="ZIP_ER_TELL",X[X.ZIP_ER_COMPRESSED_DATA=31]="ZIP_ER_COMPRESSED_DATA",X))(Hde||{}),jde=t=>({get HEAPU8(){return t.HEAPU8},errors:Hde,SEEK_SET:0,SEEK_CUR:1,SEEK_END:2,ZIP_CHECKCONS:4,ZIP_EXCL:2,ZIP_RDONLY:16,ZIP_FL_OVERWRITE:8192,ZIP_FL_COMPRESSED:4,ZIP_OPSYS_DOS:0,ZIP_OPSYS_AMIGA:1,ZIP_OPSYS_OPENVMS:2,ZIP_OPSYS_UNIX:3,ZIP_OPSYS_VM_CMS:4,ZIP_OPSYS_ATARI_ST:5,ZIP_OPSYS_OS_2:6,ZIP_OPSYS_MACINTOSH:7,ZIP_OPSYS_Z_SYSTEM:8,ZIP_OPSYS_CPM:9,ZIP_OPSYS_WINDOWS_NTFS:10,ZIP_OPSYS_MVS:11,ZIP_OPSYS_VSE:12,ZIP_OPSYS_ACORN_RISC:13,ZIP_OPSYS_VFAT:14,ZIP_OPSYS_ALTERNATE_MVS:15,ZIP_OPSYS_BEOS:16,ZIP_OPSYS_TANDEM:17,ZIP_OPSYS_OS_400:18,ZIP_OPSYS_OS_X:19,ZIP_CM_DEFAULT:-1,ZIP_CM_STORE:0,ZIP_CM_DEFLATE:8,uint08S:t._malloc(1),uint32S:t._malloc(4),malloc:t._malloc,free:t._free,getValue:t.getValue,openFromSource:t.cwrap("zip_open_from_source","number",["number","number","number"]),close:t.cwrap("zip_close","number",["number"]),discard:t.cwrap("zip_discard",null,["number"]),getError:t.cwrap("zip_get_error","number",["number"]),getName:t.cwrap("zip_get_name","string",["number","number","number"]),getNumEntries:t.cwrap("zip_get_num_entries","number",["number","number"]),delete:t.cwrap("zip_delete","number",["number","number"]),statIndex:t.cwrap("zip_stat_index","number",["number",...jp,"number","number"]),fopenIndex:t.cwrap("zip_fopen_index","number",["number",...jp,"number"]),fread:t.cwrap("zip_fread","number",["number","number","number","number"]),fclose:t.cwrap("zip_fclose","number",["number"]),dir:{add:t.cwrap("zip_dir_add","number",["number","string"])},file:{add:t.cwrap("zip_file_add","number",["number","string","number","number"]),getError:t.cwrap("zip_file_get_error","number",["number"]),getExternalAttributes:t.cwrap("zip_file_get_external_attributes","number",["number",...jp,"number","number","number"]),setExternalAttributes:t.cwrap("zip_file_set_external_attributes","number",["number",...jp,"number","number","number"]),setMtime:t.cwrap("zip_file_set_mtime","number",["number",...jp,"number","number"]),setCompression:t.cwrap("zip_set_file_compression","number",["number",...jp,"number","number"])},ext:{countSymlinks:t.cwrap("zip_ext_count_symlinks","number",["number"])},error:{initWithCode:t.cwrap("zip_error_init_with_code",null,["number","number"]),strerror:t.cwrap("zip_error_strerror","string",["number"])},name:{locate:t.cwrap("zip_name_locate","number",["number","string","number"])},source:{fromUnattachedBuffer:t.cwrap("zip_source_buffer_create","number",["number",...jp,"number","number"]),fromBuffer:t.cwrap("zip_source_buffer","number",["number","number",...jp,"number"]),free:t.cwrap("zip_source_free",null,["number"]),keep:t.cwrap("zip_source_keep",null,["number"]),open:t.cwrap("zip_source_open","number",["number"]),close:t.cwrap("zip_source_close","number",["number"]),seek:t.cwrap("zip_source_seek","number",["number",...jp,"number"]),tell:t.cwrap("zip_source_tell","number",["number"]),read:t.cwrap("zip_source_read","number",["number","number","number"]),error:t.cwrap("zip_source_error","number",["number"])},struct:{statS:t.cwrap("zipstruct_statS","number",[]),statSize:t.cwrap("zipstruct_stat_size","number",["number"]),statCompSize:t.cwrap("zipstruct_stat_comp_size","number",["number"]),statCompMethod:t.cwrap("zipstruct_stat_comp_method","number",["number"]),statMtime:t.cwrap("zipstruct_stat_mtime","number",["number"]),statCrc:t.cwrap("zipstruct_stat_crc","number",["number"]),errorS:t.cwrap("zipstruct_errorS","number",[]),errorCodeZip:t.cwrap("zipstruct_error_code_zip","number",["number"])}})});function zj(t,e){let r=t.indexOf(e);if(r<=0)return null;let s=r;for(;r>=0&&(s=r+e.length,t[s]!==K.sep);){if(t[r-1]===K.sep)return null;r=t.indexOf(e,s)}return t.length>s&&t[s]!==K.sep?null:t.slice(0,s)}var tA,Gde=It(()=>{bt();bt();rA();tA=class t extends n0{static async openPromise(e,r){let s=new t(r);try{return await e(s)}finally{s.saveAndClose()}}constructor(e={}){let r=e.fileExtensions,s=e.readOnlyArchives,a=typeof r>"u"?f=>zj(f,".zip"):f=>{for(let p of r){let h=zj(f,p);if(h)return h}return null},n=(f,p)=>new ps(p,{baseFs:f,readOnly:s,stats:f.statSync(p),customZipImplementation:e.customZipImplementation}),c=async(f,p)=>{let h={baseFs:f,readOnly:s,stats:await f.statPromise(p),customZipImplementation:e.customZipImplementation};return()=>new ps(p,h)};super({...e,factorySync:n,factoryPromise:c,getMountPoint:a})}}});var Zj,vI,Xj=It(()=>{Vj();Zj=class extends Error{constructor(e,r){super(e),this.name="Libzip Error",this.code=r}},vI=class{constructor(e){this.filesShouldBeCached=!0;let r="buffer"in e?e.buffer:e.baseFs.readFileSync(e.path);this.libzip=Bv();let s=this.libzip.malloc(4);try{let c=0;e.readOnly&&(c|=this.libzip.ZIP_RDONLY);let f=this.allocateUnattachedSource(r);try{this.zip=this.libzip.openFromSource(f,c,s),this.lzSource=f}catch(p){throw this.libzip.source.free(f),p}if(this.zip===0){let p=this.libzip.struct.errorS();throw this.libzip.error.initWithCode(p,this.libzip.getValue(s,"i32")),this.makeLibzipError(p)}}finally{this.libzip.free(s)}let a=this.libzip.getNumEntries(this.zip,0),n=new Array(a);for(let c=0;c>>0,n=this.libzip.struct.statMtime(r)>>>0,c=this.libzip.struct.statCrc(r)>>>0;return{size:a,mtime:n,crc:c}}makeLibzipError(e){let r=this.libzip.struct.errorCodeZip(e),s=this.libzip.error.strerror(e),a=new Zj(s,this.libzip.errors[r]);if(r===this.libzip.errors.ZIP_ER_CHANGED)throw new Error(`Assertion failed: Unexpected libzip error: ${a.message}`);return a}setFileSource(e,r,s){let a=this.allocateSource(s);try{let n=this.libzip.file.add(this.zip,e,a,this.libzip.ZIP_FL_OVERWRITE);if(n===-1)throw this.makeLibzipError(this.libzip.getError(this.zip));if(r!==null&&this.libzip.file.setCompression(this.zip,n,0,r[0],r[1])===-1)throw this.makeLibzipError(this.libzip.getError(this.zip));return n}catch(n){throw this.libzip.source.free(a),n}}setMtime(e,r){if(this.libzip.file.setMtime(this.zip,e,0,r,0)===-1)throw this.makeLibzipError(this.libzip.getError(this.zip))}getExternalAttributes(e){if(this.libzip.file.getExternalAttributes(this.zip,e,0,0,this.libzip.uint08S,this.libzip.uint32S)===-1)throw this.makeLibzipError(this.libzip.getError(this.zip));let s=this.libzip.getValue(this.libzip.uint08S,"i8")>>>0,a=this.libzip.getValue(this.libzip.uint32S,"i32")>>>0;return[s,a]}setExternalAttributes(e,r,s){if(this.libzip.file.setExternalAttributes(this.zip,e,0,0,r,s)===-1)throw this.makeLibzipError(this.libzip.getError(this.zip))}locate(e){return this.libzip.name.locate(this.zip,e,0)}getFileSource(e){let r=this.libzip.struct.statS();if(this.libzip.statIndex(this.zip,e,0,0,r)===-1)throw this.makeLibzipError(this.libzip.getError(this.zip));let a=this.libzip.struct.statCompSize(r),n=this.libzip.struct.statCompMethod(r),c=this.libzip.malloc(a);try{let f=this.libzip.fopenIndex(this.zip,e,0,this.libzip.ZIP_FL_COMPRESSED);if(f===0)throw this.makeLibzipError(this.libzip.getError(this.zip));try{let p=this.libzip.fread(f,c,a,0);if(p===-1)throw this.makeLibzipError(this.libzip.file.getError(f));if(pa)throw new Error("Overread");let h=this.libzip.HEAPU8.subarray(c,c+a);return{data:Buffer.from(h),compressionMethod:n}}finally{this.libzip.fclose(f)}}finally{this.libzip.free(c)}}deleteEntry(e){if(this.libzip.delete(this.zip,e)===-1)throw this.makeLibzipError(this.libzip.getError(this.zip))}addDirectory(e){let r=this.libzip.dir.add(this.zip,e);if(r===-1)throw this.makeLibzipError(this.libzip.getError(this.zip));return r}getBufferAndClose(){try{if(this.libzip.source.keep(this.lzSource),this.libzip.close(this.zip)===-1)throw this.makeLibzipError(this.libzip.getError(this.zip));if(this.libzip.source.open(this.lzSource)===-1)throw this.makeLibzipError(this.libzip.source.error(this.lzSource));if(this.libzip.source.seek(this.lzSource,0,0,this.libzip.SEEK_END)===-1)throw this.makeLibzipError(this.libzip.source.error(this.lzSource));let e=this.libzip.source.tell(this.lzSource);if(e===-1)throw this.makeLibzipError(this.libzip.source.error(this.lzSource));if(this.libzip.source.seek(this.lzSource,0,0,this.libzip.SEEK_SET)===-1)throw this.makeLibzipError(this.libzip.source.error(this.lzSource));let r=this.libzip.malloc(e);if(!r)throw new Error("Couldn't allocate enough memory");try{let s=this.libzip.source.read(this.lzSource,r,e);if(s===-1)throw this.makeLibzipError(this.libzip.source.error(this.lzSource));if(se)throw new Error("Overread");let a=Buffer.from(this.libzip.HEAPU8.subarray(r,r+e));return process.env.YARN_IS_TEST_ENV&&process.env.YARN_ZIP_DATA_EPILOGUE&&(a=Buffer.concat([a,Buffer.from(process.env.YARN_ZIP_DATA_EPILOGUE)])),a}finally{this.libzip.free(r)}}finally{this.libzip.source.close(this.lzSource),this.libzip.source.free(this.lzSource)}}allocateBuffer(e){Buffer.isBuffer(e)||(e=Buffer.from(e));let r=this.libzip.malloc(e.byteLength);if(!r)throw new Error("Couldn't allocate enough memory");return new Uint8Array(this.libzip.HEAPU8.buffer,r,e.byteLength).set(e),{buffer:r,byteLength:e.byteLength}}allocateUnattachedSource(e){let r=this.libzip.struct.errorS(),{buffer:s,byteLength:a}=this.allocateBuffer(e),n=this.libzip.source.fromUnattachedBuffer(s,a,0,1,r);if(n===0)throw this.libzip.free(r),this.makeLibzipError(r);return n}allocateSource(e){let{buffer:r,byteLength:s}=this.allocateBuffer(e),a=this.libzip.source.fromBuffer(this.zip,r,s,0,1);if(a===0)throw this.libzip.free(r),this.makeLibzipError(this.libzip.getError(this.zip));return a}discard(){this.libzip.discard(this.zip)}}});function vmt(t){if(typeof t=="string"&&String(+t)===t)return+t;if(typeof t=="number"&&Number.isFinite(t))return t<0?Date.now()/1e3:t;if(Wde.types.isDate(t))return t.getTime()/1e3;throw new Error("Invalid time")}function BT(){return Buffer.from([80,75,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])}var ka,$j,Wde,e6,lm,t6,r6,Yde,ps,vT=It(()=>{bt();bt();bt();bt();bt();bt();ka=ye("fs"),$j=ye("stream"),Wde=ye("util"),e6=et(ye("zlib"));Xj();lm=3,t6=0,r6=8,Yde="mixed";ps=class extends Uf{constructor(r,s={}){super();this.listings=new Map;this.entries=new Map;this.fileSources=new Map;this.fds=new Map;this.nextFd=0;this.ready=!1;this.readOnly=!1;s.readOnly&&(this.readOnly=!0);let a=s;this.level=typeof a.level<"u"?a.level:Yde;let n=s.customZipImplementation??vI;if(typeof r=="string"){let{baseFs:f=new Yn}=a;this.baseFs=f,this.path=r}else this.path=null,this.baseFs=null;if(s.stats)this.stats=s.stats;else if(typeof r=="string")try{this.stats=this.baseFs.statSync(r)}catch(f){if(f.code==="ENOENT"&&a.create)this.stats=el.makeDefaultStats();else throw f}else this.stats=el.makeDefaultStats();typeof r=="string"?s.create?this.zipImpl=new n({buffer:BT(),readOnly:this.readOnly}):this.zipImpl=new n({path:r,baseFs:this.baseFs,readOnly:this.readOnly,size:this.stats.size}):this.zipImpl=new n({buffer:r??BT(),readOnly:this.readOnly}),this.listings.set(vt.root,new Set);let c=this.zipImpl.getListings();for(let f=0;f{this.closeSync(f)}})}async readPromise(r,s,a,n,c){return this.readSync(r,s,a,n,c)}readSync(r,s,a=0,n=s.byteLength,c=-1){let f=this.fds.get(r);if(typeof f>"u")throw or.EBADF("read");let p=c===-1||c===null?f.cursor:c,h=this.readFileSync(f.p);h.copy(s,a,p,p+n);let E=Math.max(0,Math.min(h.length-p,n));return(c===-1||c===null)&&(f.cursor+=E),E}async writePromise(r,s,a,n,c){return typeof s=="string"?this.writeSync(r,s,c):this.writeSync(r,s,a,n,c)}writeSync(r,s,a,n,c){throw typeof this.fds.get(r)>"u"?or.EBADF("read"):new Error("Unimplemented")}async closePromise(r){return this.closeSync(r)}closeSync(r){if(typeof this.fds.get(r)>"u")throw or.EBADF("read");this.fds.delete(r)}createReadStream(r,{encoding:s}={}){if(r===null)throw new Error("Unimplemented");let a=this.openSync(r,"r"),n=Object.assign(new $j.PassThrough({emitClose:!0,autoDestroy:!0,destroy:(f,p)=>{clearImmediate(c),this.closeSync(a),p(f)}}),{close(){n.destroy()},bytesRead:0,path:r,pending:!1}),c=setImmediate(async()=>{try{let f=await this.readFilePromise(r,s);n.bytesRead=f.length,n.end(f)}catch(f){n.destroy(f)}});return n}createWriteStream(r,{encoding:s}={}){if(this.readOnly)throw or.EROFS(`open '${r}'`);if(r===null)throw new Error("Unimplemented");let a=[],n=this.openSync(r,"w"),c=Object.assign(new $j.PassThrough({autoDestroy:!0,emitClose:!0,destroy:(f,p)=>{try{f?p(f):(this.writeFileSync(r,Buffer.concat(a),s),p(null))}catch(h){p(h)}finally{this.closeSync(n)}}}),{close(){c.destroy()},bytesWritten:0,path:r,pending:!1});return c.on("data",f=>{let p=Buffer.from(f);c.bytesWritten+=p.length,a.push(p)}),c}async realpathPromise(r){return this.realpathSync(r)}realpathSync(r){let s=this.resolveFilename(`lstat '${r}'`,r);if(!this.entries.has(s)&&!this.listings.has(s))throw or.ENOENT(`lstat '${r}'`);return s}async existsPromise(r){return this.existsSync(r)}existsSync(r){if(!this.ready)throw or.EBUSY(`archive closed, existsSync '${r}'`);if(this.symlinkCount===0){let a=K.resolve(vt.root,r);return this.entries.has(a)||this.listings.has(a)}let s;try{s=this.resolveFilename(`stat '${r}'`,r,void 0,!1)}catch{return!1}return s===void 0?!1:this.entries.has(s)||this.listings.has(s)}async accessPromise(r,s){return this.accessSync(r,s)}accessSync(r,s=ka.constants.F_OK){let a=this.resolveFilename(`access '${r}'`,r);if(!this.entries.has(a)&&!this.listings.has(a))throw or.ENOENT(`access '${r}'`);if(this.readOnly&&s&ka.constants.W_OK)throw or.EROFS(`access '${r}'`)}async statPromise(r,s={bigint:!1}){return s.bigint?this.statSync(r,{bigint:!0}):this.statSync(r)}statSync(r,s={bigint:!1,throwIfNoEntry:!0}){let a=this.resolveFilename(`stat '${r}'`,r,void 0,s.throwIfNoEntry);if(a!==void 0){if(!this.entries.has(a)&&!this.listings.has(a)){if(s.throwIfNoEntry===!1)return;throw or.ENOENT(`stat '${r}'`)}if(r[r.length-1]==="/"&&!this.listings.has(a))throw or.ENOTDIR(`stat '${r}'`);return this.statImpl(`stat '${r}'`,a,s)}}async fstatPromise(r,s){return this.fstatSync(r,s)}fstatSync(r,s){let a=this.fds.get(r);if(typeof a>"u")throw or.EBADF("fstatSync");let{p:n}=a,c=this.resolveFilename(`stat '${n}'`,n);if(!this.entries.has(c)&&!this.listings.has(c))throw or.ENOENT(`stat '${n}'`);if(n[n.length-1]==="/"&&!this.listings.has(c))throw or.ENOTDIR(`stat '${n}'`);return this.statImpl(`fstat '${n}'`,c,s)}async lstatPromise(r,s={bigint:!1}){return s.bigint?this.lstatSync(r,{bigint:!0}):this.lstatSync(r)}lstatSync(r,s={bigint:!1,throwIfNoEntry:!0}){let a=this.resolveFilename(`lstat '${r}'`,r,!1,s.throwIfNoEntry);if(a!==void 0){if(!this.entries.has(a)&&!this.listings.has(a)){if(s.throwIfNoEntry===!1)return;throw or.ENOENT(`lstat '${r}'`)}if(r[r.length-1]==="/"&&!this.listings.has(a))throw or.ENOTDIR(`lstat '${r}'`);return this.statImpl(`lstat '${r}'`,a,s)}}statImpl(r,s,a={}){let n=this.entries.get(s);if(typeof n<"u"){let c=this.zipImpl.stat(n),f=c.crc,p=c.size,h=c.mtime*1e3,E=this.stats.uid,C=this.stats.gid,S=512,P=Math.ceil(c.size/S),I=h,R=h,N=h,U=new Date(I),W=new Date(R),te=new Date(N),ie=new Date(h),Ae=this.listings.has(s)?ka.constants.S_IFDIR:this.isSymbolicLink(n)?ka.constants.S_IFLNK:ka.constants.S_IFREG,ce=Ae===ka.constants.S_IFDIR?493:420,me=Ae|this.getUnixMode(n,ce)&511,pe=Object.assign(new el.StatEntry,{uid:E,gid:C,size:p,blksize:S,blocks:P,atime:U,birthtime:W,ctime:te,mtime:ie,atimeMs:I,birthtimeMs:R,ctimeMs:N,mtimeMs:h,mode:me,crc:f});return a.bigint===!0?el.convertToBigIntStats(pe):pe}if(this.listings.has(s)){let c=this.stats.uid,f=this.stats.gid,p=0,h=512,E=0,C=this.stats.mtimeMs,S=this.stats.mtimeMs,P=this.stats.mtimeMs,I=this.stats.mtimeMs,R=new Date(C),N=new Date(S),U=new Date(P),W=new Date(I),te=ka.constants.S_IFDIR|493,Ae=Object.assign(new el.StatEntry,{uid:c,gid:f,size:p,blksize:h,blocks:E,atime:R,birthtime:N,ctime:U,mtime:W,atimeMs:C,birthtimeMs:S,ctimeMs:P,mtimeMs:I,mode:te,crc:0});return a.bigint===!0?el.convertToBigIntStats(Ae):Ae}throw new Error("Unreachable")}getUnixMode(r,s){let[a,n]=this.zipImpl.getExternalAttributes(r);return a!==lm?s:n>>>16}registerListing(r){let s=this.listings.get(r);if(s)return s;this.registerListing(K.dirname(r)).add(K.basename(r));let n=new Set;return this.listings.set(r,n),n}registerEntry(r,s){this.registerListing(K.dirname(r)).add(K.basename(r)),this.entries.set(r,s)}unregisterListing(r){this.listings.delete(r),this.listings.get(K.dirname(r))?.delete(K.basename(r))}unregisterEntry(r){this.unregisterListing(r);let s=this.entries.get(r);this.entries.delete(r),!(typeof s>"u")&&(this.fileSources.delete(s),this.isSymbolicLink(s)&&this.symlinkCount--)}deleteEntry(r,s){this.unregisterEntry(r),this.zipImpl.deleteEntry(s)}resolveFilename(r,s,a=!0,n=!0){if(!this.ready)throw or.EBUSY(`archive closed, ${r}`);let c=K.resolve(vt.root,s);if(c==="/")return vt.root;let f=this.entries.get(c);if(a&&f!==void 0)if(this.symlinkCount!==0&&this.isSymbolicLink(f)){let p=this.getFileSource(f).toString();return this.resolveFilename(r,K.resolve(K.dirname(c),p),!0,n)}else return c;for(;;){let p=this.resolveFilename(r,K.dirname(c),!0,n);if(p===void 0)return p;let h=this.listings.has(p),E=this.entries.has(p);if(!h&&!E){if(n===!1)return;throw or.ENOENT(r)}if(!h)throw or.ENOTDIR(r);if(c=K.resolve(p,K.basename(c)),!a||this.symlinkCount===0)break;let C=this.zipImpl.locate(c.slice(1));if(C===-1)break;if(this.isSymbolicLink(C)){let S=this.getFileSource(C).toString();c=K.resolve(K.dirname(c),S)}else break}return c}setFileSource(r,s){let a=Buffer.isBuffer(s)?s:Buffer.from(s),n=K.relative(vt.root,r),c=null;this.level!=="mixed"&&(c=[this.level===0?t6:r6,this.level]);let f=this.zipImpl.setFileSource(n,c,a);return this.fileSources.set(f,a),f}isSymbolicLink(r){if(this.symlinkCount===0)return!1;let[s,a]=this.zipImpl.getExternalAttributes(r);return s!==lm?!1:(a>>>16&ka.constants.S_IFMT)===ka.constants.S_IFLNK}getFileSource(r,s={asyncDecompress:!1}){let a=this.fileSources.get(r);if(typeof a<"u")return a;let{data:n,compressionMethod:c}=this.zipImpl.getFileSource(r);if(c===t6)return this.zipImpl.filesShouldBeCached&&this.fileSources.set(r,n),n;if(c===r6){if(s.asyncDecompress)return new Promise((f,p)=>{e6.default.inflateRaw(n,(h,E)=>{h?p(h):(this.zipImpl.filesShouldBeCached&&this.fileSources.set(r,E),f(E))})});{let f=e6.default.inflateRawSync(n);return this.zipImpl.filesShouldBeCached&&this.fileSources.set(r,f),f}}else throw new Error(`Unsupported compression method: ${c}`)}async fchmodPromise(r,s){return this.chmodPromise(this.fdToPath(r,"fchmod"),s)}fchmodSync(r,s){return this.chmodSync(this.fdToPath(r,"fchmodSync"),s)}async chmodPromise(r,s){return this.chmodSync(r,s)}chmodSync(r,s){if(this.readOnly)throw or.EROFS(`chmod '${r}'`);s&=493;let a=this.resolveFilename(`chmod '${r}'`,r,!1),n=this.entries.get(a);if(typeof n>"u")throw new Error(`Assertion failed: The entry should have been registered (${a})`);let f=this.getUnixMode(n,ka.constants.S_IFREG|0)&-512|s;this.zipImpl.setExternalAttributes(n,lm,f<<16)}async fchownPromise(r,s,a){return this.chownPromise(this.fdToPath(r,"fchown"),s,a)}fchownSync(r,s,a){return this.chownSync(this.fdToPath(r,"fchownSync"),s,a)}async chownPromise(r,s,a){return this.chownSync(r,s,a)}chownSync(r,s,a){throw new Error("Unimplemented")}async renamePromise(r,s){return this.renameSync(r,s)}renameSync(r,s){throw new Error("Unimplemented")}async copyFilePromise(r,s,a){let{indexSource:n,indexDest:c,resolvedDestP:f}=this.prepareCopyFile(r,s,a),p=await this.getFileSource(n,{asyncDecompress:!0}),h=this.setFileSource(f,p);h!==c&&this.registerEntry(f,h)}copyFileSync(r,s,a=0){let{indexSource:n,indexDest:c,resolvedDestP:f}=this.prepareCopyFile(r,s,a),p=this.getFileSource(n),h=this.setFileSource(f,p);h!==c&&this.registerEntry(f,h)}prepareCopyFile(r,s,a=0){if(this.readOnly)throw or.EROFS(`copyfile '${r} -> '${s}'`);if(a&ka.constants.COPYFILE_FICLONE_FORCE)throw or.ENOSYS("unsupported clone operation",`copyfile '${r}' -> ${s}'`);let n=this.resolveFilename(`copyfile '${r} -> ${s}'`,r),c=this.entries.get(n);if(typeof c>"u")throw or.EINVAL(`copyfile '${r}' -> '${s}'`);let f=this.resolveFilename(`copyfile '${r}' -> ${s}'`,s),p=this.entries.get(f);if(a&(ka.constants.COPYFILE_EXCL|ka.constants.COPYFILE_FICLONE_FORCE)&&typeof p<"u")throw or.EEXIST(`copyfile '${r}' -> '${s}'`);return{indexSource:c,resolvedDestP:f,indexDest:p}}async appendFilePromise(r,s,a){if(this.readOnly)throw or.EROFS(`open '${r}'`);return typeof a>"u"?a={flag:"a"}:typeof a=="string"?a={flag:"a",encoding:a}:typeof a.flag>"u"&&(a={flag:"a",...a}),this.writeFilePromise(r,s,a)}appendFileSync(r,s,a={}){if(this.readOnly)throw or.EROFS(`open '${r}'`);return typeof a>"u"?a={flag:"a"}:typeof a=="string"?a={flag:"a",encoding:a}:typeof a.flag>"u"&&(a={flag:"a",...a}),this.writeFileSync(r,s,a)}fdToPath(r,s){let a=this.fds.get(r)?.p;if(typeof a>"u")throw or.EBADF(s);return a}async writeFilePromise(r,s,a){let{encoding:n,mode:c,index:f,resolvedP:p}=this.prepareWriteFile(r,a);f!==void 0&&typeof a=="object"&&a.flag&&a.flag.includes("a")&&(s=Buffer.concat([await this.getFileSource(f,{asyncDecompress:!0}),Buffer.from(s)])),n!==null&&(s=s.toString(n));let h=this.setFileSource(p,s);h!==f&&this.registerEntry(p,h),c!==null&&await this.chmodPromise(p,c)}writeFileSync(r,s,a){let{encoding:n,mode:c,index:f,resolvedP:p}=this.prepareWriteFile(r,a);f!==void 0&&typeof a=="object"&&a.flag&&a.flag.includes("a")&&(s=Buffer.concat([this.getFileSource(f),Buffer.from(s)])),n!==null&&(s=s.toString(n));let h=this.setFileSource(p,s);h!==f&&this.registerEntry(p,h),c!==null&&this.chmodSync(p,c)}prepareWriteFile(r,s){if(typeof r=="number"&&(r=this.fdToPath(r,"read")),this.readOnly)throw or.EROFS(`open '${r}'`);let a=this.resolveFilename(`open '${r}'`,r);if(this.listings.has(a))throw or.EISDIR(`open '${r}'`);let n=null,c=null;typeof s=="string"?n=s:typeof s=="object"&&({encoding:n=null,mode:c=null}=s);let f=this.entries.get(a);return{encoding:n,mode:c,resolvedP:a,index:f}}async unlinkPromise(r){return this.unlinkSync(r)}unlinkSync(r){if(this.readOnly)throw or.EROFS(`unlink '${r}'`);let s=this.resolveFilename(`unlink '${r}'`,r);if(this.listings.has(s))throw or.EISDIR(`unlink '${r}'`);let a=this.entries.get(s);if(typeof a>"u")throw or.EINVAL(`unlink '${r}'`);this.deleteEntry(s,a)}async utimesPromise(r,s,a){return this.utimesSync(r,s,a)}utimesSync(r,s,a){if(this.readOnly)throw or.EROFS(`utimes '${r}'`);let n=this.resolveFilename(`utimes '${r}'`,r);this.utimesImpl(n,a)}async lutimesPromise(r,s,a){return this.lutimesSync(r,s,a)}lutimesSync(r,s,a){if(this.readOnly)throw or.EROFS(`lutimes '${r}'`);let n=this.resolveFilename(`utimes '${r}'`,r,!1);this.utimesImpl(n,a)}utimesImpl(r,s){this.listings.has(r)&&(this.entries.has(r)||this.hydrateDirectory(r));let a=this.entries.get(r);if(a===void 0)throw new Error("Unreachable");this.zipImpl.setMtime(a,vmt(s))}async mkdirPromise(r,s){return this.mkdirSync(r,s)}mkdirSync(r,{mode:s=493,recursive:a=!1}={}){if(a)return this.mkdirpSync(r,{chmod:s});if(this.readOnly)throw or.EROFS(`mkdir '${r}'`);let n=this.resolveFilename(`mkdir '${r}'`,r);if(this.entries.has(n)||this.listings.has(n))throw or.EEXIST(`mkdir '${r}'`);this.hydrateDirectory(n),this.chmodSync(n,s)}async rmdirPromise(r,s){return this.rmdirSync(r,s)}rmdirSync(r,{recursive:s=!1}={}){if(this.readOnly)throw or.EROFS(`rmdir '${r}'`);if(s){this.removeSync(r);return}let a=this.resolveFilename(`rmdir '${r}'`,r),n=this.listings.get(a);if(!n)throw or.ENOTDIR(`rmdir '${r}'`);if(n.size>0)throw or.ENOTEMPTY(`rmdir '${r}'`);let c=this.entries.get(a);if(typeof c>"u")throw or.EINVAL(`rmdir '${r}'`);this.deleteEntry(r,c)}async rmPromise(r,s){return this.rmSync(r,s)}rmSync(r,{recursive:s=!1}={}){if(this.readOnly)throw or.EROFS(`rm '${r}'`);if(s){this.removeSync(r);return}let a=this.resolveFilename(`rm '${r}'`,r),n=this.listings.get(a);if(!n)throw or.ENOTDIR(`rm '${r}'`);if(n.size>0)throw or.ENOTEMPTY(`rm '${r}'`);let c=this.entries.get(a);if(typeof c>"u")throw or.EINVAL(`rm '${r}'`);this.deleteEntry(r,c)}hydrateDirectory(r){let s=this.zipImpl.addDirectory(K.relative(vt.root,r));return this.registerListing(r),this.registerEntry(r,s),s}async linkPromise(r,s){return this.linkSync(r,s)}linkSync(r,s){throw or.EOPNOTSUPP(`link '${r}' -> '${s}'`)}async symlinkPromise(r,s){return this.symlinkSync(r,s)}symlinkSync(r,s){if(this.readOnly)throw or.EROFS(`symlink '${r}' -> '${s}'`);let a=this.resolveFilename(`symlink '${r}' -> '${s}'`,s);if(this.listings.has(a))throw or.EISDIR(`symlink '${r}' -> '${s}'`);if(this.entries.has(a))throw or.EEXIST(`symlink '${r}' -> '${s}'`);let n=this.setFileSource(a,r);this.registerEntry(a,n),this.zipImpl.setExternalAttributes(n,lm,(ka.constants.S_IFLNK|511)<<16),this.symlinkCount+=1}async readFilePromise(r,s){typeof s=="object"&&(s=s?s.encoding:void 0);let a=await this.readFileBuffer(r,{asyncDecompress:!0});return s?a.toString(s):a}readFileSync(r,s){typeof s=="object"&&(s=s?s.encoding:void 0);let a=this.readFileBuffer(r);return s?a.toString(s):a}readFileBuffer(r,s={asyncDecompress:!1}){typeof r=="number"&&(r=this.fdToPath(r,"read"));let a=this.resolveFilename(`open '${r}'`,r);if(!this.entries.has(a)&&!this.listings.has(a))throw or.ENOENT(`open '${r}'`);if(r[r.length-1]==="/"&&!this.listings.has(a))throw or.ENOTDIR(`open '${r}'`);if(this.listings.has(a))throw or.EISDIR("read");let n=this.entries.get(a);if(n===void 0)throw new Error("Unreachable");return this.getFileSource(n,s)}async readdirPromise(r,s){return this.readdirSync(r,s)}readdirSync(r,s){let a=this.resolveFilename(`scandir '${r}'`,r);if(!this.entries.has(a)&&!this.listings.has(a))throw or.ENOENT(`scandir '${r}'`);let n=this.listings.get(a);if(!n)throw or.ENOTDIR(`scandir '${r}'`);if(s?.recursive)if(s?.withFileTypes){let c=Array.from(n,f=>Object.assign(this.statImpl("lstat",K.join(r,f)),{name:f,path:vt.dot}));for(let f of c){if(!f.isDirectory())continue;let p=K.join(f.path,f.name),h=this.listings.get(K.join(a,p));for(let E of h)c.push(Object.assign(this.statImpl("lstat",K.join(r,p,E)),{name:E,path:p}))}return c}else{let c=[...n];for(let f of c){let p=this.listings.get(K.join(a,f));if(!(typeof p>"u"))for(let h of p)c.push(K.join(f,h))}return c}else return s?.withFileTypes?Array.from(n,c=>Object.assign(this.statImpl("lstat",K.join(r,c)),{name:c,path:void 0})):[...n]}async readlinkPromise(r){let s=this.prepareReadlink(r);return(await this.getFileSource(s,{asyncDecompress:!0})).toString()}readlinkSync(r){let s=this.prepareReadlink(r);return this.getFileSource(s).toString()}prepareReadlink(r){let s=this.resolveFilename(`readlink '${r}'`,r,!1);if(!this.entries.has(s)&&!this.listings.has(s))throw or.ENOENT(`readlink '${r}'`);if(r[r.length-1]==="/"&&!this.listings.has(s))throw or.ENOTDIR(`open '${r}'`);if(this.listings.has(s))throw or.EINVAL(`readlink '${r}'`);let a=this.entries.get(s);if(a===void 0)throw new Error("Unreachable");if(!this.isSymbolicLink(a))throw or.EINVAL(`readlink '${r}'`);return a}async truncatePromise(r,s=0){let a=this.resolveFilename(`open '${r}'`,r),n=this.entries.get(a);if(typeof n>"u")throw or.EINVAL(`open '${r}'`);let c=await this.getFileSource(n,{asyncDecompress:!0}),f=Buffer.alloc(s,0);return c.copy(f),await this.writeFilePromise(r,f)}truncateSync(r,s=0){let a=this.resolveFilename(`open '${r}'`,r),n=this.entries.get(a);if(typeof n>"u")throw or.EINVAL(`open '${r}'`);let c=this.getFileSource(n),f=Buffer.alloc(s,0);return c.copy(f),this.writeFileSync(r,f)}async ftruncatePromise(r,s){return this.truncatePromise(this.fdToPath(r,"ftruncate"),s)}ftruncateSync(r,s){return this.truncateSync(this.fdToPath(r,"ftruncateSync"),s)}watch(r,s,a){let n;switch(typeof s){case"function":case"string":case"undefined":n=!0;break;default:({persistent:n=!0}=s);break}if(!n)return{on:()=>{},close:()=>{}};let c=setInterval(()=>{},24*60*60*1e3);return{on:()=>{},close:()=>{clearInterval(c)}}}watchFile(r,s,a){let n=K.resolve(vt.root,r);return sE(this,n,s,a)}unwatchFile(r,s){let a=K.resolve(vt.root,r);return yd(this,a,s)}}});function Kde(t,e,r=Buffer.alloc(0),s){let a=new ps(r),n=C=>C===e||C.startsWith(`${e}/`)?C.slice(0,e.length):null,c=async(C,S)=>()=>a,f=(C,S)=>a,p={...t},h=new Yn(p),E=new n0({baseFs:h,getMountPoint:n,factoryPromise:c,factorySync:f,magicByte:21,maxAge:1/0,typeCheck:s?.typeCheck});return j2(Vde.default,new i0(E)),a}var Vde,Jde=It(()=>{bt();Vde=et(ye("fs"));vT()});var zde=It(()=>{Gde();vT();Jde()});var n6,vv,ST,Zde=It(()=>{bt();vT();n6={CENTRAL_DIRECTORY:33639248,END_OF_CENTRAL_DIRECTORY:101010256},vv=22,ST=class t{constructor(e){this.filesShouldBeCached=!1;if("buffer"in e)throw new Error("Buffer based zip archives are not supported");if(!e.readOnly)throw new Error("Writable zip archives are not supported");this.baseFs=e.baseFs,this.fd=this.baseFs.openSync(e.path,"r");try{this.entries=t.readZipSync(this.fd,this.baseFs,e.size)}catch(r){throw this.baseFs.closeSync(this.fd),this.fd="closed",r}}static readZipSync(e,r,s){if(s=0;N--)if(n.readUInt32LE(N)===n6.END_OF_CENTRAL_DIRECTORY){a=N;break}if(a===-1)throw new Error("Not a zip archive")}let c=n.readUInt16LE(a+10),f=n.readUInt32LE(a+12),p=n.readUInt32LE(a+16),h=n.readUInt16LE(a+20);if(a+h+vv>n.length)throw new Error("Zip archive inconsistent");if(c==65535||f==4294967295||p==4294967295)throw new Error("Zip 64 is not supported");if(f>s)throw new Error("Zip archive inconsistent");if(c>f/46)throw new Error("Zip archive inconsistent");let E=Buffer.alloc(f);if(r.readSync(e,E,0,E.length,p)!==E.length)throw new Error("Zip archive inconsistent");let C=[],S=0,P=0,I=0;for(;PE.length)throw new Error("Zip archive inconsistent");if(E.readUInt32LE(S)!==n6.CENTRAL_DIRECTORY)throw new Error("Zip archive inconsistent");let N=E.readUInt16LE(S+4)>>>8;if(E.readUInt16LE(S+8)&1)throw new Error("Encrypted zip files are not supported");let W=E.readUInt16LE(S+10),te=E.readUInt32LE(S+16),ie=E.readUInt16LE(S+28),Ae=E.readUInt16LE(S+30),ce=E.readUInt16LE(S+32),me=E.readUInt32LE(S+42),pe=E.toString("utf8",S+46,S+46+ie).replaceAll("\0"," ");if(pe.includes("\0"))throw new Error("Invalid ZIP file");let Be=E.readUInt32LE(S+20),Ce=E.readUInt32LE(S+38);C.push({name:pe,os:N,mtime:ui.SAFE_TIME,crc:te,compressionMethod:W,isSymbolicLink:N===lm&&(Ce>>>16&ui.S_IFMT)===ui.S_IFLNK,size:E.readUInt32LE(S+24),compressedSize:Be,externalAttributes:Ce,localHeaderOffset:me}),I+=Be,P+=1,S+=46+ie+Ae+ce}if(I>s)throw new Error("Zip archive inconsistent");if(S!==E.length)throw new Error("Zip archive inconsistent");return C}getExternalAttributes(e){let r=this.entries[e];return[r.os,r.externalAttributes]}getListings(){return this.entries.map(e=>e.name)}getSymlinkCount(){let e=0;for(let r of this.entries)r.isSymbolicLink&&(e+=1);return e}stat(e){let r=this.entries[e];return{crc:r.crc,mtime:r.mtime,size:r.size}}locate(e){for(let r=0;rYde,DEFLATE:()=>r6,JsZipImpl:()=>ST,LibZipImpl:()=>vI,STORE:()=>t6,ZIP_UNIX:()=>lm,ZipFS:()=>ps,ZipOpenFS:()=>tA,getArchivePart:()=>zj,getLibzipPromise:()=>Dmt,getLibzipSync:()=>Smt,makeEmptyArchive:()=>BT,mountMemoryDrive:()=>Kde});function Smt(){return Bv()}async function Dmt(){return Bv()}var Xde,rA=It(()=>{Vj();Xde=et(Ude());qde();zde();Zde();Xj();_de(()=>{let t=(0,Xde.default)();return jde(t)})});var Dv,$de=It(()=>{bt();Wt();bv();Dv=class extends ot{constructor(){super(...arguments);this.cwd=ge.String("--cwd",process.cwd(),{description:"The directory to run the command in"});this.commandName=ge.String();this.args=ge.Proxy()}static{this.usage={description:"run a command using yarn's portable shell",details:` + This command will run a command using Yarn's portable shell. + + Make sure to escape glob patterns, redirections, and other features that might be expanded by your own shell. + + Note: To escape something from Yarn's shell, you might have to escape it twice, the first time from your own shell. + + Note: Don't use this command in Yarn scripts, as Yarn's shell is automatically used. + + For a list of features, visit: https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-shell/README.md. + `,examples:[["Run a simple command","$0 echo Hello"],["Run a command with a glob pattern","$0 echo '*.js'"],["Run a command with a redirection","$0 echo Hello World '>' hello.txt"],["Run a command with an escaped glob pattern (The double escape is needed in Unix shells)",`$0 echo '"*.js"'`],["Run a command with a variable (Double quotes are needed in Unix shells, to prevent them from expanding the variable)",'$0 "GREETING=Hello echo $GREETING World"']]}}async execute(){let r=this.args.length>0?`${this.commandName} ${this.args.join(" ")}`:this.commandName;return await SI(r,[],{cwd:ue.toPortablePath(this.cwd),stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr})}}});var Kl,eme=It(()=>{Kl=class extends Error{constructor(e){super(e),this.name="ShellError"}}});var PT={};Vt(PT,{fastGlobOptions:()=>nme,isBraceExpansion:()=>i6,isGlobPattern:()=>bmt,match:()=>Pmt,micromatchOptions:()=>bT});function bmt(t){if(!DT.default.scan(t,bT).isGlob)return!1;try{DT.default.parse(t,bT)}catch{return!1}return!0}function Pmt(t,{cwd:e,baseFs:r}){return(0,tme.default)(t,{...nme,cwd:ue.fromPortablePath(e),fs:Cx(rme.default,new i0(r))})}function i6(t){return DT.default.scan(t,bT).isBrace}var tme,rme,DT,bT,nme,ime=It(()=>{bt();tme=et(BQ()),rme=et(ye("fs")),DT=et(Sa()),bT={strictBrackets:!0},nme={onlyDirectories:!1,onlyFiles:!1}});function s6(){}function o6(){for(let t of cm)t.kill()}function lme(t,e,r,s){return a=>{let n=a[0]instanceof nA.Transform?"pipe":a[0],c=a[1]instanceof nA.Transform?"pipe":a[1],f=a[2]instanceof nA.Transform?"pipe":a[2],p=(0,ome.default)(t,e,{...s,stdio:[n,c,f]});return cm.add(p),cm.size===1&&(process.on("SIGINT",s6),process.on("SIGTERM",o6)),a[0]instanceof nA.Transform&&a[0].pipe(p.stdin),a[1]instanceof nA.Transform&&p.stdout.pipe(a[1],{end:!1}),a[2]instanceof nA.Transform&&p.stderr.pipe(a[2],{end:!1}),{stdin:p.stdin,promise:new Promise(h=>{p.on("error",E=>{switch(cm.delete(p),cm.size===0&&(process.off("SIGINT",s6),process.off("SIGTERM",o6)),E.code){case"ENOENT":a[2].write(`command not found: ${t} +`),h(127);break;case"EACCES":a[2].write(`permission denied: ${t} +`),h(128);break;default:a[2].write(`uncaught error: ${E.message} +`),h(1);break}}),p.on("close",E=>{cm.delete(p),cm.size===0&&(process.off("SIGINT",s6),process.off("SIGTERM",o6)),h(E!==null?E:129)})})}}}function cme(t){return e=>{let r=e[0]==="pipe"?new nA.PassThrough:e[0];return{stdin:r,promise:Promise.resolve().then(()=>t({stdin:r,stdout:e[1],stderr:e[2]}))}}}function xT(t,e){return l6.start(t,e)}function sme(t,e=null){let r=new nA.PassThrough,s=new ame.StringDecoder,a="";return r.on("data",n=>{let c=s.write(n),f;do if(f=c.indexOf(` +`),f!==-1){let p=a+c.substring(0,f);c=c.substring(f+1),a="",t(e!==null?`${e} ${p}`:p)}while(f!==-1);a+=c}),r.on("end",()=>{let n=s.end();n!==""&&t(e!==null?`${e} ${n}`:n)}),r}function ume(t,{prefix:e}){return{stdout:sme(r=>t.stdout.write(`${r} +`),t.stdout.isTTY?e:null),stderr:sme(r=>t.stderr.write(`${r} +`),t.stderr.isTTY?e:null)}}var ome,nA,ame,cm,Mc,a6,l6,c6=It(()=>{ome=et(J_()),nA=ye("stream"),ame=ye("string_decoder"),cm=new Set;Mc=class{constructor(e){this.stream=e}close(){}get(){return this.stream}},a6=class{constructor(){this.stream=null}close(){if(this.stream===null)throw new Error("Assertion failed: No stream attached");this.stream.end()}attach(e){this.stream=e}get(){if(this.stream===null)throw new Error("Assertion failed: No stream attached");return this.stream}},l6=class t{constructor(e,r){this.stdin=null;this.stdout=null;this.stderr=null;this.pipe=null;this.ancestor=e,this.implementation=r}static start(e,{stdin:r,stdout:s,stderr:a}){let n=new t(null,e);return n.stdin=r,n.stdout=s,n.stderr=a,n}pipeTo(e,r=1){let s=new t(this,e),a=new a6;return s.pipe=a,s.stdout=this.stdout,s.stderr=this.stderr,(r&1)===1?this.stdout=a:this.ancestor!==null&&(this.stderr=this.ancestor.stdout),(r&2)===2?this.stderr=a:this.ancestor!==null&&(this.stderr=this.ancestor.stderr),s}async exec(){let e=["ignore","ignore","ignore"];if(this.pipe)e[0]="pipe";else{if(this.stdin===null)throw new Error("Assertion failed: No input stream registered");e[0]=this.stdin.get()}let r;if(this.stdout===null)throw new Error("Assertion failed: No output stream registered");r=this.stdout,e[1]=r.get();let s;if(this.stderr===null)throw new Error("Assertion failed: No error stream registered");s=this.stderr,e[2]=s.get();let a=this.implementation(e);return this.pipe&&this.pipe.attach(a.stdin),await a.promise.then(n=>(r.close(),s.close(),n))}async run(){let e=[];for(let s=this;s;s=s.ancestor)e.push(s.exec());return(await Promise.all(e))[0]}}});var Qv={};Vt(Qv,{EntryCommand:()=>Dv,ShellError:()=>Kl,execute:()=>SI,globUtils:()=>PT});function fme(t,e,r){let s=new Jl.PassThrough({autoDestroy:!0});switch(t){case 0:(e&1)===1&&r.stdin.pipe(s,{end:!1}),(e&2)===2&&r.stdin instanceof Jl.Writable&&s.pipe(r.stdin,{end:!1});break;case 1:(e&1)===1&&r.stdout.pipe(s,{end:!1}),(e&2)===2&&s.pipe(r.stdout,{end:!1});break;case 2:(e&1)===1&&r.stderr.pipe(s,{end:!1}),(e&2)===2&&s.pipe(r.stderr,{end:!1});break;default:throw new Kl(`Bad file descriptor: "${t}"`)}return s}function QT(t,e={}){let r={...t,...e};return r.environment={...t.environment,...e.environment},r.variables={...t.variables,...e.variables},r}async function kmt(t,e,r){let s=[],a=new Jl.PassThrough;return a.on("data",n=>s.push(n)),await TT(t,e,QT(r,{stdout:a})),Buffer.concat(s).toString().replace(/[\r\n]+$/,"")}async function Ame(t,e,r){let s=t.map(async n=>{let c=await um(n.args,e,r);return{name:n.name,value:c.join(" ")}});return(await Promise.all(s)).reduce((n,c)=>(n[c.name]=c.value,n),{})}function kT(t){return t.match(/[^ \r\n\t]+/g)||[]}async function yme(t,e,r,s,a=s){switch(t.name){case"$":s(String(process.pid));break;case"#":s(String(e.args.length));break;case"@":if(t.quoted)for(let n of e.args)a(n);else for(let n of e.args){let c=kT(n);for(let f=0;f=0&&n"u"&&(t.defaultValue?c=(await um(t.defaultValue,e,r)).join(" "):t.alternativeValue&&(c="")),typeof c>"u")throw f?new Kl(`Unbound argument #${n}`):new Kl(`Unbound variable "${t.name}"`);if(t.quoted)s(c);else{let p=kT(c);for(let E=0;Es.push(n));let a=Number(s.join(" "));return Number.isNaN(a)?Pv({type:"variable",name:s.join(" ")},e,r):Pv({type:"number",value:a},e,r)}else return Qmt[t.type](await Pv(t.left,e,r),await Pv(t.right,e,r))}async function um(t,e,r){let s=new Map,a=[],n=[],c=E=>{n.push(E)},f=()=>{n.length>0&&a.push(n.join("")),n=[]},p=E=>{c(E),f()},h=(E,C,S)=>{let P=JSON.stringify({type:E,fd:C}),I=s.get(P);typeof I>"u"&&s.set(P,I=[]),I.push(S)};for(let E of t){let C=!1;switch(E.type){case"redirection":{let S=await um(E.args,e,r);for(let P of S)h(E.subtype,E.fd,P)}break;case"argument":for(let S of E.segments)switch(S.type){case"text":c(S.text);break;case"glob":c(S.pattern),C=!0;break;case"shell":{let P=await kmt(S.shell,e,r);if(S.quoted)c(P);else{let I=kT(P);for(let R=0;R"u")throw new Error("Assertion failed: Expected a glob pattern to have been set");let P=await e.glob.match(S,{cwd:r.cwd,baseFs:e.baseFs});if(P.length===0){let I=i6(S)?". Note: Brace expansion of arbitrary strings isn't currently supported. For more details, please read this issue: https://github.com/yarnpkg/berry/issues/22":"";throw new Kl(`No matches found: "${S}"${I}`)}for(let I of P.sort())p(I)}}if(s.size>0){let E=[];for(let[C,S]of s.entries())E.splice(E.length,0,C,String(S.length),...S);a.splice(0,0,"__ysh_set_redirects",...E,"--")}return a}function xv(t,e,r){e.builtins.has(t[0])||(t=["command",...t]);let s=ue.fromPortablePath(r.cwd),a=r.environment;typeof a.PWD<"u"&&(a={...a,PWD:s});let[n,...c]=t;if(n==="command")return lme(c[0],c.slice(1),e,{cwd:s,env:a});let f=e.builtins.get(n);if(typeof f>"u")throw new Error(`Assertion failed: A builtin should exist for "${n}"`);return cme(async({stdin:p,stdout:h,stderr:E})=>{let{stdin:C,stdout:S,stderr:P}=r;r.stdin=p,r.stdout=h,r.stderr=E;try{return await f(c,e,r)}finally{r.stdin=C,r.stdout=S,r.stderr=P}})}function Tmt(t,e,r){return s=>{let a=new Jl.PassThrough,n=TT(t,e,QT(r,{stdin:a}));return{stdin:a,promise:n}}}function Rmt(t,e,r){return s=>{let a=new Jl.PassThrough,n=TT(t,e,r);return{stdin:a,promise:n}}}function pme(t,e,r,s){if(e.length===0)return t;{let a;do a=String(Math.random());while(Object.hasOwn(s.procedures,a));return s.procedures={...s.procedures},s.procedures[a]=t,xv([...e,"__ysh_run_procedure",a],r,s)}}async function hme(t,e,r){let s=t,a=null,n=null;for(;s;){let c=s.then?{...r}:r,f;switch(s.type){case"command":{let p=await um(s.args,e,r),h=await Ame(s.envs,e,r);f=s.envs.length?xv(p,e,QT(c,{environment:h})):xv(p,e,c)}break;case"subshell":{let p=await um(s.args,e,r),h=Tmt(s.subshell,e,c);f=pme(h,p,e,c)}break;case"group":{let p=await um(s.args,e,r),h=Rmt(s.group,e,c);f=pme(h,p,e,c)}break;case"envs":{let p=await Ame(s.envs,e,r);c.environment={...c.environment,...p},f=xv(["true"],e,c)}break}if(typeof f>"u")throw new Error("Assertion failed: An action should have been generated");if(a===null)n=xT(f,{stdin:new Mc(c.stdin),stdout:new Mc(c.stdout),stderr:new Mc(c.stderr)});else{if(n===null)throw new Error("Assertion failed: The execution pipeline should have been setup");switch(a){case"|":n=n.pipeTo(f,1);break;case"|&":n=n.pipeTo(f,3);break}}s.then?(a=s.then.type,s=s.then.chain):s=null}if(n===null)throw new Error("Assertion failed: The execution pipeline should have been setup");return await n.run()}async function Fmt(t,e,r,{background:s=!1}={}){function a(n){let c=["#2E86AB","#A23B72","#F18F01","#C73E1D","#CCE2A3"],f=c[n%c.length];return gme.default.hex(f)}if(s){let n=r.nextBackgroundJobIndex++,c=a(n),f=`[${n}]`,p=c(f),{stdout:h,stderr:E}=ume(r,{prefix:p});return r.backgroundJobs.push(hme(t,e,QT(r,{stdout:h,stderr:E})).catch(C=>E.write(`${C.message} +`)).finally(()=>{r.stdout.isTTY&&r.stdout.write(`Job ${p}, '${c(AE(t))}' has ended +`)})),0}return await hme(t,e,r)}async function Nmt(t,e,r,{background:s=!1}={}){let a,n=f=>{a=f,r.variables["?"]=String(f)},c=async f=>{try{return await Fmt(f.chain,e,r,{background:s&&typeof f.then>"u"})}catch(p){if(!(p instanceof Kl))throw p;return r.stderr.write(`${p.message} +`),1}};for(n(await c(t));t.then;){if(r.exitCode!==null)return r.exitCode;switch(t.then.type){case"&&":a===0&&n(await c(t.then.line));break;case"||":a!==0&&n(await c(t.then.line));break;default:throw new Error(`Assertion failed: Unsupported command type: "${t.then.type}"`)}t=t.then.line}return a}async function TT(t,e,r){let s=r.backgroundJobs;r.backgroundJobs=[];let a=0;for(let{command:n,type:c}of t){if(a=await Nmt(n,e,r,{background:c==="&"}),r.exitCode!==null)return r.exitCode;r.variables["?"]=String(a)}return await Promise.all(r.backgroundJobs),r.backgroundJobs=s,a}function Eme(t){switch(t.type){case"variable":return t.name==="@"||t.name==="#"||t.name==="*"||Number.isFinite(parseInt(t.name,10))||"defaultValue"in t&&!!t.defaultValue&&t.defaultValue.some(e=>kv(e))||"alternativeValue"in t&&!!t.alternativeValue&&t.alternativeValue.some(e=>kv(e));case"arithmetic":return u6(t.arithmetic);case"shell":return f6(t.shell);default:return!1}}function kv(t){switch(t.type){case"redirection":return t.args.some(e=>kv(e));case"argument":return t.segments.some(e=>Eme(e));default:throw new Error(`Assertion failed: Unsupported argument type: "${t.type}"`)}}function u6(t){switch(t.type){case"variable":return Eme(t);case"number":return!1;default:return u6(t.left)||u6(t.right)}}function f6(t){return t.some(({command:e})=>{for(;e;){let r=e.chain;for(;r;){let s;switch(r.type){case"subshell":s=f6(r.subshell);break;case"command":s=r.envs.some(a=>a.args.some(n=>kv(n)))||r.args.some(a=>kv(a));break}if(s)return!0;if(!r.then)break;r=r.then.chain}if(!e.then)break;e=e.then.line}return!1})}async function SI(t,e=[],{baseFs:r=new Yn,builtins:s={},cwd:a=ue.toPortablePath(process.cwd()),env:n=process.env,stdin:c=process.stdin,stdout:f=process.stdout,stderr:p=process.stderr,variables:h={},glob:E=PT}={}){let C={};for(let[I,R]of Object.entries(n))typeof R<"u"&&(C[I]=R);let S=new Map(xmt);for(let[I,R]of Object.entries(s))S.set(I,R);c===null&&(c=new Jl.PassThrough,c.end());let P=vx(t,E);if(!f6(P)&&P.length>0&&e.length>0){let{command:I}=P[P.length-1];for(;I.then;)I=I.then.line;let R=I.chain;for(;R.then;)R=R.then.chain;R.type==="command"&&(R.args=R.args.concat(e.map(N=>({type:"argument",segments:[{type:"text",text:N}]}))))}return await TT(P,{args:e,baseFs:r,builtins:S,initialStdin:c,initialStdout:f,initialStderr:p,glob:E},{cwd:a,environment:C,exitCode:null,procedures:{},stdin:c,stdout:f,stderr:p,variables:Object.assign({},h,{"?":0}),nextBackgroundJobIndex:1,backgroundJobs:[]})}var gme,dme,Jl,mme,xmt,Qmt,bv=It(()=>{bt();Bc();gme=et(g4()),dme=ye("os"),Jl=ye("stream"),mme=ye("timers/promises");$de();eme();ime();c6();c6();xmt=new Map([["cd",async([t=(0,dme.homedir)(),...e],r,s)=>{let a=K.resolve(s.cwd,ue.toPortablePath(t));if(!(await r.baseFs.statPromise(a).catch(c=>{throw c.code==="ENOENT"?new Kl(`cd: no such file or directory: ${t}`):c})).isDirectory())throw new Kl(`cd: not a directory: ${t}`);return s.cwd=a,0}],["pwd",async(t,e,r)=>(r.stdout.write(`${ue.fromPortablePath(r.cwd)} +`),0)],[":",async(t,e,r)=>0],["true",async(t,e,r)=>0],["false",async(t,e,r)=>1],["exit",async([t,...e],r,s)=>s.exitCode=parseInt(t??s.variables["?"],10)],["echo",async(t,e,r)=>(r.stdout.write(`${t.join(" ")} +`),0)],["sleep",async([t],e,r)=>{if(typeof t>"u")throw new Kl("sleep: missing operand");let s=Number(t);if(Number.isNaN(s))throw new Kl(`sleep: invalid time interval '${t}'`);return await(0,mme.setTimeout)(1e3*s,0)}],["unset",async(t,e,r)=>{for(let s of t)delete r.environment[s],delete r.variables[s];return 0}],["__ysh_run_procedure",async(t,e,r)=>{let s=r.procedures[t[0]];return await xT(s,{stdin:new Mc(r.stdin),stdout:new Mc(r.stdout),stderr:new Mc(r.stderr)}).run()}],["__ysh_set_redirects",async(t,e,r)=>{let s=r.stdin,a=r.stdout,n=r.stderr,c=[],f=[],p=[],h=0;for(;t[h]!=="--";){let C=t[h++],{type:S,fd:P}=JSON.parse(C),I=W=>{switch(P){case null:case 0:c.push(W);break;default:throw new Error(`Unsupported file descriptor: "${P}"`)}},R=W=>{switch(P){case null:case 1:f.push(W);break;case 2:p.push(W);break;default:throw new Error(`Unsupported file descriptor: "${P}"`)}},N=Number(t[h++]),U=h+N;for(let W=h;We.baseFs.createReadStream(K.resolve(r.cwd,ue.toPortablePath(t[W]))));break;case"<<<":I(()=>{let te=new Jl.PassThrough;return process.nextTick(()=>{te.write(`${t[W]} +`),te.end()}),te});break;case"<&":I(()=>fme(Number(t[W]),1,r));break;case">":case">>":{let te=K.resolve(r.cwd,ue.toPortablePath(t[W]));R(te==="/dev/null"?new Jl.Writable({autoDestroy:!0,emitClose:!0,write(ie,Ae,ce){setImmediate(ce)}}):e.baseFs.createWriteStream(te,S===">>"?{flags:"a"}:void 0))}break;case">&":R(fme(Number(t[W]),2,r));break;default:throw new Error(`Assertion failed: Unsupported redirection type: "${S}"`)}}if(c.length>0){let C=new Jl.PassThrough;s=C;let S=P=>{if(P===c.length)C.end();else{let I=c[P]();I.pipe(C,{end:!1}),I.on("end",()=>{S(P+1)})}};S(0)}if(f.length>0){let C=new Jl.PassThrough;a=C;for(let S of f)C.pipe(S)}if(p.length>0){let C=new Jl.PassThrough;n=C;for(let S of p)C.pipe(S)}let E=await xT(xv(t.slice(h+1),e,r),{stdin:new Mc(s),stdout:new Mc(a),stderr:new Mc(n)}).run();return await Promise.all(f.map(C=>new Promise((S,P)=>{C.on("error",I=>{P(I)}),C.on("close",()=>{S()}),C.end()}))),await Promise.all(p.map(C=>new Promise((S,P)=>{C.on("error",I=>{P(I)}),C.on("close",()=>{S()}),C.end()}))),E}]]);Qmt={addition:(t,e)=>t+e,subtraction:(t,e)=>t-e,multiplication:(t,e)=>t*e,division:(t,e)=>Math.trunc(t/e)}});var RT=L((y$t,Ime)=>{function Omt(t,e){for(var r=-1,s=t==null?0:t.length,a=Array(s);++r{var Cme=Yd(),Lmt=RT(),Mmt=xc(),_mt=aI(),Umt=1/0,wme=Cme?Cme.prototype:void 0,Bme=wme?wme.toString:void 0;function vme(t){if(typeof t=="string")return t;if(Mmt(t))return Lmt(t,vme)+"";if(_mt(t))return Bme?Bme.call(t):"";var e=t+"";return e=="0"&&1/t==-Umt?"-0":e}Sme.exports=vme});var Tv=L((I$t,bme)=>{var Hmt=Dme();function jmt(t){return t==null?"":Hmt(t)}bme.exports=jmt});var A6=L((C$t,Pme)=>{function qmt(t,e,r){var s=-1,a=t.length;e<0&&(e=-e>a?0:a+e),r=r>a?a:r,r<0&&(r+=a),a=e>r?0:r-e>>>0,e>>>=0;for(var n=Array(a);++s{var Gmt=A6();function Wmt(t,e,r){var s=t.length;return r=r===void 0?s:r,!e&&r>=s?t:Gmt(t,e,r)}xme.exports=Wmt});var p6=L((B$t,Qme)=>{var Ymt="\\ud800-\\udfff",Vmt="\\u0300-\\u036f",Kmt="\\ufe20-\\ufe2f",Jmt="\\u20d0-\\u20ff",zmt=Vmt+Kmt+Jmt,Zmt="\\ufe0e\\ufe0f",Xmt="\\u200d",$mt=RegExp("["+Xmt+Ymt+zmt+Zmt+"]");function eyt(t){return $mt.test(t)}Qme.exports=eyt});var Rme=L((v$t,Tme)=>{function tyt(t){return t.split("")}Tme.exports=tyt});var Hme=L((S$t,Ume)=>{var Fme="\\ud800-\\udfff",ryt="\\u0300-\\u036f",nyt="\\ufe20-\\ufe2f",iyt="\\u20d0-\\u20ff",syt=ryt+nyt+iyt,oyt="\\ufe0e\\ufe0f",ayt="["+Fme+"]",h6="["+syt+"]",g6="\\ud83c[\\udffb-\\udfff]",lyt="(?:"+h6+"|"+g6+")",Nme="[^"+Fme+"]",Ome="(?:\\ud83c[\\udde6-\\uddff]){2}",Lme="[\\ud800-\\udbff][\\udc00-\\udfff]",cyt="\\u200d",Mme=lyt+"?",_me="["+oyt+"]?",uyt="(?:"+cyt+"(?:"+[Nme,Ome,Lme].join("|")+")"+_me+Mme+")*",fyt=_me+Mme+uyt,Ayt="(?:"+[Nme+h6+"?",h6,Ome,Lme,ayt].join("|")+")",pyt=RegExp(g6+"(?="+g6+")|"+Ayt+fyt,"g");function hyt(t){return t.match(pyt)||[]}Ume.exports=hyt});var qme=L((D$t,jme)=>{var gyt=Rme(),dyt=p6(),myt=Hme();function yyt(t){return dyt(t)?myt(t):gyt(t)}jme.exports=yyt});var Wme=L((b$t,Gme)=>{var Eyt=kme(),Iyt=p6(),Cyt=qme(),wyt=Tv();function Byt(t){return function(e){e=wyt(e);var r=Iyt(e)?Cyt(e):void 0,s=r?r[0]:e.charAt(0),a=r?Eyt(r,1).join(""):e.slice(1);return s[t]()+a}}Gme.exports=Byt});var Vme=L((P$t,Yme)=>{var vyt=Wme(),Syt=vyt("toUpperCase");Yme.exports=Syt});var d6=L((x$t,Kme)=>{var Dyt=Tv(),byt=Vme();function Pyt(t){return byt(Dyt(t).toLowerCase())}Kme.exports=Pyt});var Jme=L((k$t,FT)=>{function xyt(){var t=0,e=1,r=2,s=3,a=4,n=5,c=6,f=7,p=8,h=9,E=10,C=11,S=12,P=13,I=14,R=15,N=16,U=17,W=0,te=1,ie=2,Ae=3,ce=4;function me(g,we){return 55296<=g.charCodeAt(we)&&g.charCodeAt(we)<=56319&&56320<=g.charCodeAt(we+1)&&g.charCodeAt(we+1)<=57343}function pe(g,we){we===void 0&&(we=0);var Ee=g.charCodeAt(we);if(55296<=Ee&&Ee<=56319&&we=1){var fe=g.charCodeAt(we-1),se=Ee;return 55296<=fe&&fe<=56319?(fe-55296)*1024+(se-56320)+65536:se}return Ee}function Be(g,we,Ee){var fe=[g].concat(we).concat([Ee]),se=fe[fe.length-2],X=Ee,De=fe.lastIndexOf(I);if(De>1&&fe.slice(1,De).every(function(j){return j==s})&&[s,P,U].indexOf(g)==-1)return ie;var Re=fe.lastIndexOf(a);if(Re>0&&fe.slice(1,Re).every(function(j){return j==a})&&[S,a].indexOf(se)==-1)return fe.filter(function(j){return j==a}).length%2==1?Ae:ce;if(se==t&&X==e)return W;if(se==r||se==t||se==e)return X==I&&we.every(function(j){return j==s})?ie:te;if(X==r||X==t||X==e)return te;if(se==c&&(X==c||X==f||X==h||X==E))return W;if((se==h||se==f)&&(X==f||X==p))return W;if((se==E||se==p)&&X==p)return W;if(X==s||X==R)return W;if(X==n)return W;if(se==S)return W;var gt=fe.indexOf(s)!=-1?fe.lastIndexOf(s)-1:fe.length-2;return[P,U].indexOf(fe[gt])!=-1&&fe.slice(gt+1,-1).every(function(j){return j==s})&&X==I||se==R&&[N,U].indexOf(X)!=-1?W:we.indexOf(a)!=-1?ie:se==a&&X==a?W:te}this.nextBreak=function(g,we){if(we===void 0&&(we=0),we<0)return 0;if(we>=g.length-1)return g.length;for(var Ee=Ce(pe(g,we)),fe=[],se=we+1;se{var kyt=/^(.*?)(\x1b\[[^m]+m|\x1b\]8;;.*?(\x1b\\|\u0007))/,NT;function Qyt(){if(NT)return NT;if(typeof Intl.Segmenter<"u"){let t=new Intl.Segmenter("en",{granularity:"grapheme"});return NT=e=>Array.from(t.segment(e),({segment:r})=>r)}else{let t=Jme(),e=new t;return NT=r=>e.splitGraphemes(r)}}zme.exports=(t,e=0,r=t.length)=>{if(e<0||r<0)throw new RangeError("Negative indices aren't supported by this implementation");let s=r-e,a="",n=0,c=0;for(;t.length>0;){let f=t.match(kyt)||[t,t,void 0],p=Qyt()(f[1]),h=Math.min(e-n,p.length);p=p.slice(h);let E=Math.min(s-c,p.length);a+=p.slice(0,E).join(""),n+=h,c+=E,typeof f[2]<"u"&&(a+=f[2]),t=t.slice(f[0].length)}return a}});var un,Rv=It(()=>{un=process.env.YARN_IS_TEST_ENV?"0.0.0":"4.9.1"});function nye(t,{configuration:e,json:r}){if(!e.get("enableMessageNames"))return"";let a=Vf(t===null?0:t);return!r&&t===null?Ut(e,a,"grey"):a}function m6(t,{configuration:e,json:r}){let s=nye(t,{configuration:e,json:r});if(!s||t===null||t===0)return s;let a=Dr[t],n=`https://yarnpkg.com/advanced/error-codes#${s}---${a}`.toLowerCase();return JE(e,s,n)}async function DI({configuration:t,stdout:e,forceError:r},s){let a=await Ot.start({configuration:t,stdout:e,includeFooter:!1},async n=>{let c=!1,f=!1;for(let p of s)typeof p.option<"u"&&(p.error||r?(f=!0,n.reportError(50,p.message)):(c=!0,n.reportWarning(50,p.message)),p.callback?.());c&&!f&&n.reportSeparator()});return a.hasErrors()?a.exitCode():null}var tye,OT,Tyt,Xme,$me,b0,rye,eye,Ryt,Fyt,LT,Nyt,Ot,Fv=It(()=>{tye=et(Zme()),OT=et(Nd());nk();Fc();Rv();Qc();Tyt="\xB7",Xme=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],$me=80,b0=OT.default.GITHUB_ACTIONS?{start:t=>`::group::${t} +`,end:t=>`::endgroup:: +`}:OT.default.TRAVIS?{start:t=>`travis_fold:start:${t} +`,end:t=>`travis_fold:end:${t} +`}:OT.default.GITLAB?{start:t=>`section_start:${Math.floor(Date.now()/1e3)}:${t.toLowerCase().replace(/\W+/g,"_")}[collapsed=true]\r\x1B[0K${t} +`,end:t=>`section_end:${Math.floor(Date.now()/1e3)}:${t.toLowerCase().replace(/\W+/g,"_")}\r\x1B[0K`}:null,rye=b0!==null,eye=new Date,Ryt=["iTerm.app","Apple_Terminal","WarpTerminal","vscode"].includes(process.env.TERM_PROGRAM)||!!process.env.WT_SESSION,Fyt=t=>t,LT=Fyt({patrick:{date:[17,3],chars:["\u{1F340}","\u{1F331}"],size:40},simba:{date:[19,7],chars:["\u{1F981}","\u{1F334}"],size:40},jack:{date:[31,10],chars:["\u{1F383}","\u{1F987}"],size:40},hogsfather:{date:[31,12],chars:["\u{1F389}","\u{1F384}"],size:40},default:{chars:["=","-"],size:80}}),Nyt=Ryt&&Object.keys(LT).find(t=>{let e=LT[t];return!(e.date&&(e.date[0]!==eye.getDate()||e.date[1]!==eye.getMonth()+1))})||"default";Ot=class extends ho{constructor({configuration:r,stdout:s,json:a=!1,forceSectionAlignment:n=!1,includeNames:c=!0,includePrefix:f=!0,includeFooter:p=!0,includeLogs:h=!a,includeInfos:E=h,includeWarnings:C=h}){super();this.uncommitted=new Set;this.warningCount=0;this.errorCount=0;this.timerFooter=[];this.startTime=Date.now();this.indent=0;this.level=0;this.progress=new Map;this.progressTime=0;this.progressFrame=0;this.progressTimeout=null;this.progressStyle=null;this.progressMaxScaledSize=null;if(YB(this,{configuration:r}),this.configuration=r,this.forceSectionAlignment=n,this.includeNames=c,this.includePrefix=f,this.includeFooter=p,this.includeInfos=E,this.includeWarnings=C,this.json=a,this.stdout=s,r.get("enableProgressBars")&&!a&&s.isTTY&&s.columns>22){let S=r.get("progressBarStyle")||Nyt;if(!Object.hasOwn(LT,S))throw new Error("Assertion failed: Invalid progress bar style");this.progressStyle=LT[S];let P=Math.min(this.getRecommendedLength(),80);this.progressMaxScaledSize=Math.floor(this.progressStyle.size*P/80)}}static async start(r,s){let a=new this(r),n=process.emitWarning;process.emitWarning=(c,f)=>{if(typeof c!="string"){let h=c;c=h.message,f=f??h.name}let p=typeof f<"u"?`${f}: ${c}`:c;a.reportWarning(0,p)},r.includeVersion&&a.reportInfo(0,zd(r.configuration,`Yarn ${un}`,2));try{await s(a)}catch(c){a.reportExceptionOnce(c)}finally{await a.finalize(),process.emitWarning=n}return a}hasErrors(){return this.errorCount>0}exitCode(){return this.hasErrors()?1:0}getRecommendedLength(){let s=this.progressStyle!==null?this.stdout.columns-1:super.getRecommendedLength();return Math.max(40,s-12-this.indent*2)}startSectionSync({reportHeader:r,reportFooter:s,skipIfEmpty:a},n){let c={committed:!1,action:()=>{r?.()}};a?this.uncommitted.add(c):(c.action(),c.committed=!0);let f=Date.now();try{return n()}catch(p){throw this.reportExceptionOnce(p),p}finally{let p=Date.now();this.uncommitted.delete(c),c.committed&&s?.(p-f)}}async startSectionPromise({reportHeader:r,reportFooter:s,skipIfEmpty:a},n){let c={committed:!1,action:()=>{r?.()}};a?this.uncommitted.add(c):(c.action(),c.committed=!0);let f=Date.now();try{return await n()}catch(p){throw this.reportExceptionOnce(p),p}finally{let p=Date.now();this.uncommitted.delete(c),c.committed&&s?.(p-f)}}startTimerImpl(r,s,a){return{cb:typeof s=="function"?s:a,reportHeader:()=>{this.level+=1,this.reportInfo(null,`\u250C ${r}`),this.indent+=1,b0!==null&&!this.json&&this.includeInfos&&this.stdout.write(b0.start(r))},reportFooter:f=>{if(this.indent-=1,b0!==null&&!this.json&&this.includeInfos){this.stdout.write(b0.end(r));for(let p of this.timerFooter)p()}this.configuration.get("enableTimers")&&f>200?this.reportInfo(null,`\u2514 Completed in ${Ut(this.configuration,f,Ct.DURATION)}`):this.reportInfo(null,"\u2514 Completed"),this.level-=1},skipIfEmpty:(typeof s=="function"?{}:s).skipIfEmpty}}startTimerSync(r,s,a){let{cb:n,...c}=this.startTimerImpl(r,s,a);return this.startSectionSync(c,n)}async startTimerPromise(r,s,a){let{cb:n,...c}=this.startTimerImpl(r,s,a);return this.startSectionPromise(c,n)}reportSeparator(){this.indent===0?this.writeLine(""):this.reportInfo(null,"")}reportInfo(r,s){if(!this.includeInfos)return;this.commit();let a=this.formatNameWithHyperlink(r),n=a?`${a}: `:"",c=`${this.formatPrefix(n,"blueBright")}${s}`;this.json?this.reportJson({type:"info",name:r,displayName:this.formatName(r),indent:this.formatIndent(),data:s}):this.writeLine(c)}reportWarning(r,s){if(this.warningCount+=1,!this.includeWarnings)return;this.commit();let a=this.formatNameWithHyperlink(r),n=a?`${a}: `:"";this.json?this.reportJson({type:"warning",name:r,displayName:this.formatName(r),indent:this.formatIndent(),data:s}):this.writeLine(`${this.formatPrefix(n,"yellowBright")}${s}`)}reportError(r,s){this.errorCount+=1,this.timerFooter.push(()=>this.reportErrorImpl(r,s)),this.reportErrorImpl(r,s)}reportErrorImpl(r,s){this.commit();let a=this.formatNameWithHyperlink(r),n=a?`${a}: `:"";this.json?this.reportJson({type:"error",name:r,displayName:this.formatName(r),indent:this.formatIndent(),data:s}):this.writeLine(`${this.formatPrefix(n,"redBright")}${s}`,{truncate:!1})}reportFold(r,s){if(!b0)return;let a=`${b0.start(r)}${s}${b0.end(r)}`;this.timerFooter.push(()=>this.stdout.write(a))}reportProgress(r){if(this.progressStyle===null)return{...Promise.resolve(),stop:()=>{}};if(r.hasProgress&&r.hasTitle)throw new Error("Unimplemented: Progress bars can't have both progress and titles.");let s=!1,a=Promise.resolve().then(async()=>{let c={progress:r.hasProgress?0:void 0,title:r.hasTitle?"":void 0};this.progress.set(r,{definition:c,lastScaledSize:r.hasProgress?-1:void 0,lastTitle:void 0}),this.refreshProgress({delta:-1});for await(let{progress:f,title:p}of r)s||c.progress===f&&c.title===p||(c.progress=f,c.title=p,this.refreshProgress());n()}),n=()=>{s||(s=!0,this.progress.delete(r),this.refreshProgress({delta:1}))};return{...a,stop:n}}reportJson(r){this.json&&this.writeLine(`${JSON.stringify(r)}`)}async finalize(){if(!this.includeFooter)return;let r="";this.errorCount>0?r="Failed with errors":this.warningCount>0?r="Done with warnings":r="Done";let s=Ut(this.configuration,Date.now()-this.startTime,Ct.DURATION),a=this.configuration.get("enableTimers")?`${r} in ${s}`:r;this.errorCount>0?this.reportError(0,a):this.warningCount>0?this.reportWarning(0,a):this.reportInfo(0,a)}writeLine(r,{truncate:s}={}){this.clearProgress({clear:!0}),this.stdout.write(`${this.truncate(r,{truncate:s})} +`),this.writeProgress()}writeLines(r,{truncate:s}={}){this.clearProgress({delta:r.length});for(let a of r)this.stdout.write(`${this.truncate(a,{truncate:s})} +`);this.writeProgress()}commit(){let r=this.uncommitted;this.uncommitted=new Set;for(let s of r)s.committed=!0,s.action()}clearProgress({delta:r=0,clear:s=!1}){this.progressStyle!==null&&this.progress.size+r>0&&(this.stdout.write(`\x1B[${this.progress.size+r}A`),(r>0||s)&&this.stdout.write("\x1B[0J"))}writeProgress(){if(this.progressStyle===null||(this.progressTimeout!==null&&clearTimeout(this.progressTimeout),this.progressTimeout=null,this.progress.size===0))return;let r=Date.now();r-this.progressTime>$me&&(this.progressFrame=(this.progressFrame+1)%Xme.length,this.progressTime=r);let s=Xme[this.progressFrame];for(let a of this.progress.values()){let n="";if(typeof a.lastScaledSize<"u"){let h=this.progressStyle.chars[0].repeat(a.lastScaledSize),E=this.progressStyle.chars[1].repeat(this.progressMaxScaledSize-a.lastScaledSize);n=` ${h}${E}`}let c=this.formatName(null),f=c?`${c}: `:"",p=a.definition.title?` ${a.definition.title}`:"";this.stdout.write(`${Ut(this.configuration,"\u27A4","blueBright")} ${f}${s}${n}${p} +`)}this.progressTimeout=setTimeout(()=>{this.refreshProgress({force:!0})},$me)}refreshProgress({delta:r=0,force:s=!1}={}){let a=!1,n=!1;if(s||this.progress.size===0)a=!0;else for(let c of this.progress.values()){let f=typeof c.definition.progress<"u"?Math.trunc(this.progressMaxScaledSize*c.definition.progress):void 0,p=c.lastScaledSize;c.lastScaledSize=f;let h=c.lastTitle;if(c.lastTitle=c.definition.title,f!==p||(n=h!==c.definition.title)){a=!0;break}}a&&(this.clearProgress({delta:r,clear:n}),this.writeProgress())}truncate(r,{truncate:s}={}){return this.progressStyle===null&&(s=!1),typeof s>"u"&&(s=this.configuration.get("preferTruncatedLines")),s&&(r=(0,tye.default)(r,0,this.stdout.columns-1)),r}formatName(r){return this.includeNames?nye(r,{configuration:this.configuration,json:this.json}):""}formatPrefix(r,s){return this.includePrefix?`${Ut(this.configuration,"\u27A4",s)} ${r}${this.formatIndent()}`:""}formatNameWithHyperlink(r){return this.includeNames?m6(r,{configuration:this.configuration,json:this.json}):""}formatIndent(){return this.level>0||!this.forceSectionAlignment?"\u2502 ".repeat(this.indent):`${Tyt} `}}});var In={};Vt(In,{PackageManager:()=>oye,detectPackageManager:()=>aye,executePackageAccessibleBinary:()=>Aye,executePackageScript:()=>MT,executePackageShellcode:()=>y6,executeWorkspaceAccessibleBinary:()=>jyt,executeWorkspaceLifecycleScript:()=>uye,executeWorkspaceScript:()=>cye,getPackageAccessibleBinaries:()=>_T,getWorkspaceAccessibleBinaries:()=>fye,hasPackageScript:()=>_yt,hasWorkspaceScript:()=>E6,isNodeScript:()=>I6,makeScriptEnv:()=>Nv,maybeExecuteWorkspaceLifecycleScript:()=>Hyt,prepareExternalProject:()=>Myt});async function P0(t,e,r,s=[]){if(process.platform==="win32"){let a=`@goto #_undefined_# 2>NUL || @title %COMSPEC% & @setlocal & @"${r}" ${s.map(n=>`"${n.replace('"','""')}"`).join(" ")} %*`;await le.writeFilePromise(K.format({dir:t,name:e,ext:".cmd"}),a)}await le.writeFilePromise(K.join(t,e),`#!/bin/sh +exec "${r}" ${s.map(a=>`'${a.replace(/'/g,`'"'"'`)}'`).join(" ")} "$@" +`,{mode:493})}async function aye(t){let e=await Ht.tryFind(t);if(e?.packageManager){let s=xQ(e.packageManager);if(s?.name){let a=`found ${JSON.stringify({packageManager:e.packageManager})} in manifest`,[n]=s.reference.split(".");switch(s.name){case"yarn":return{packageManagerField:!0,packageManager:Number(n)===1?"Yarn Classic":"Yarn",reason:a};case"npm":return{packageManagerField:!0,packageManager:"npm",reason:a};case"pnpm":return{packageManagerField:!0,packageManager:"pnpm",reason:a}}}}let r;try{r=await le.readFilePromise(K.join(t,Er.lockfile),"utf8")}catch{}return r!==void 0?r.match(/^__metadata:$/m)?{packageManager:"Yarn",reason:'"__metadata" key found in yarn.lock'}:{packageManager:"Yarn Classic",reason:'"__metadata" key not found in yarn.lock, must be a Yarn classic lockfile'}:le.existsSync(K.join(t,"package-lock.json"))?{packageManager:"npm",reason:`found npm's "package-lock.json" lockfile`}:le.existsSync(K.join(t,"pnpm-lock.yaml"))?{packageManager:"pnpm",reason:`found pnpm's "pnpm-lock.yaml" lockfile`}:null}async function Nv({project:t,locator:e,binFolder:r,ignoreCorepack:s,lifecycleScript:a,baseEnv:n=t?.configuration.env??process.env}){let c={};for(let[E,C]of Object.entries(n))typeof C<"u"&&(c[E.toLowerCase()!=="path"?E:"PATH"]=C);let f=ue.fromPortablePath(r);c.BERRY_BIN_FOLDER=ue.fromPortablePath(f);let p=process.env.COREPACK_ROOT&&!s?ue.join(process.env.COREPACK_ROOT,"dist/yarn.js"):process.argv[1];if(await Promise.all([P0(r,"node",process.execPath),...un!==null?[P0(r,"run",process.execPath,[p,"run"]),P0(r,"yarn",process.execPath,[p]),P0(r,"yarnpkg",process.execPath,[p]),P0(r,"node-gyp",process.execPath,[p,"run","--top-level","node-gyp"])]:[]]),t&&(c.INIT_CWD=ue.fromPortablePath(t.configuration.startingCwd),c.PROJECT_CWD=ue.fromPortablePath(t.cwd)),c.PATH=c.PATH?`${f}${ue.delimiter}${c.PATH}`:`${f}`,c.npm_execpath=`${f}${ue.sep}yarn`,c.npm_node_execpath=`${f}${ue.sep}node`,e){if(!t)throw new Error("Assertion failed: Missing project");let E=t.tryWorkspaceByLocator(e),C=E?E.manifest.version??"":t.storedPackages.get(e.locatorHash).version??"";c.npm_package_name=cn(e),c.npm_package_version=C;let S;if(E)S=E.cwd;else{let P=t.storedPackages.get(e.locatorHash);if(!P)throw new Error(`Package for ${Yr(t.configuration,e)} not found in the project`);let I=t.configuration.getLinkers(),R={project:t,report:new Ot({stdout:new x0.PassThrough,configuration:t.configuration})},N=I.find(U=>U.supportsPackage(P,R));if(!N)throw new Error(`The package ${Yr(t.configuration,P)} isn't supported by any of the available linkers`);S=await N.findPackageLocation(P,R)}c.npm_package_json=ue.fromPortablePath(K.join(S,Er.manifest))}let h=un!==null?`yarn/${un}`:`yarn/${kp("@yarnpkg/core").version}-core`;return c.npm_config_user_agent=`${h} npm/? node/${process.version} ${process.platform} ${process.arch}`,a&&(c.npm_lifecycle_event=a),t&&await t.configuration.triggerHook(E=>E.setupScriptEnvironment,t,c,async(E,C,S)=>await P0(r,E,C,S)),c}async function Myt(t,e,{configuration:r,report:s,workspace:a=null,locator:n=null}){await Lyt(async()=>{await le.mktempPromise(async c=>{let f=K.join(c,"pack.log"),p=null,{stdout:h,stderr:E}=r.getSubprocessStreams(f,{prefix:ue.fromPortablePath(t),report:s}),C=n&&Gu(n)?rI(n):n,S=C?cl(C):"an external project";h.write(`Packing ${S} from sources +`);let P=await aye(t),I;P!==null?(h.write(`Using ${P.packageManager} for bootstrap. Reason: ${P.reason} + +`),I=P.packageManager):(h.write(`No package manager configuration detected; defaulting to Yarn + +`),I="Yarn");let R=I==="Yarn"&&!P?.packageManagerField;await le.mktempPromise(async N=>{let U=await Nv({binFolder:N,ignoreCorepack:R,baseEnv:{...process.env,COREPACK_ENABLE_AUTO_PIN:"0"}}),te=new Map([["Yarn Classic",async()=>{let Ae=a!==null?["workspace",a]:[],ce=K.join(t,Er.manifest),me=await le.readFilePromise(ce),pe=await Yu(process.execPath,[process.argv[1],"set","version","classic","--only-if-needed","--yarn-path"],{cwd:t,env:U,stdin:p,stdout:h,stderr:E,end:1});if(pe.code!==0)return pe.code;await le.writeFilePromise(ce,me),await le.appendFilePromise(K.join(t,".npmignore"),`/.yarn +`),h.write(` +`),delete U.NODE_ENV;let Be=await Yu("yarn",["install"],{cwd:t,env:U,stdin:p,stdout:h,stderr:E,end:1});if(Be.code!==0)return Be.code;h.write(` +`);let Ce=await Yu("yarn",[...Ae,"pack","--filename",ue.fromPortablePath(e)],{cwd:t,env:U,stdin:p,stdout:h,stderr:E});return Ce.code!==0?Ce.code:0}],["Yarn",async()=>{let Ae=a!==null?["workspace",a]:[];U.YARN_ENABLE_INLINE_BUILDS="1";let ce=K.join(t,Er.lockfile);await le.existsPromise(ce)||await le.writeFilePromise(ce,"");let me=await Yu("yarn",[...Ae,"pack","--install-if-needed","--filename",ue.fromPortablePath(e)],{cwd:t,env:U,stdin:p,stdout:h,stderr:E});return me.code!==0?me.code:0}],["npm",async()=>{if(a!==null){let we=new x0.PassThrough,Ee=WE(we);we.pipe(h,{end:!1});let fe=await Yu("npm",["--version"],{cwd:t,env:U,stdin:p,stdout:we,stderr:E,end:0});if(we.end(),fe.code!==0)return h.end(),E.end(),fe.code;let se=(await Ee).toString().trim();if(!eA(se,">=7.x")){let X=ba(null,"npm"),De=On(X,se),Re=On(X,">=7.x");throw new Error(`Workspaces aren't supported by ${ri(r,De)}; please upgrade to ${ri(r,Re)} (npm has been detected as the primary package manager for ${Ut(r,t,Ct.PATH)})`)}}let Ae=a!==null?["--workspace",a]:[];delete U.npm_config_user_agent,delete U.npm_config_production,delete U.NPM_CONFIG_PRODUCTION,delete U.NODE_ENV;let ce=await Yu("npm",["install","--legacy-peer-deps"],{cwd:t,env:U,stdin:p,stdout:h,stderr:E,end:1});if(ce.code!==0)return ce.code;let me=new x0.PassThrough,pe=WE(me);me.pipe(h);let Be=await Yu("npm",["pack","--silent",...Ae],{cwd:t,env:U,stdin:p,stdout:me,stderr:E});if(Be.code!==0)return Be.code;let Ce=(await pe).toString().trim().replace(/^.*\n/s,""),g=K.resolve(t,ue.toPortablePath(Ce));return await le.renamePromise(g,e),0}]]).get(I);if(typeof te>"u")throw new Error("Assertion failed: Unsupported workflow");let ie=await te();if(!(ie===0||typeof ie>"u"))throw le.detachTemp(c),new Yt(58,`Packing the package failed (exit code ${ie}, logs can be found here: ${Ut(r,f,Ct.PATH)})`)})})})}async function _yt(t,e,{project:r}){let s=r.tryWorkspaceByLocator(t);if(s!==null)return E6(s,e);let a=r.storedPackages.get(t.locatorHash);if(!a)throw new Error(`Package for ${Yr(r.configuration,t)} not found in the project`);return await tA.openPromise(async n=>{let c=r.configuration,f=r.configuration.getLinkers(),p={project:r,report:new Ot({stdout:new x0.PassThrough,configuration:c})},h=f.find(P=>P.supportsPackage(a,p));if(!h)throw new Error(`The package ${Yr(r.configuration,a)} isn't supported by any of the available linkers`);let E=await h.findPackageLocation(a,p),C=new Sn(E,{baseFs:n});return(await Ht.find(vt.dot,{baseFs:C})).scripts.has(e)})}async function MT(t,e,r,{cwd:s,project:a,stdin:n,stdout:c,stderr:f}){return await le.mktempPromise(async p=>{let{manifest:h,env:E,cwd:C}=await lye(t,{project:a,binFolder:p,cwd:s,lifecycleScript:e}),S=h.scripts.get(e);if(typeof S>"u")return 1;let P=async()=>await SI(S,r,{cwd:C,env:E,stdin:n,stdout:c,stderr:f});return await(await a.configuration.reduceHook(R=>R.wrapScriptExecution,P,a,t,e,{script:S,args:r,cwd:C,env:E,stdin:n,stdout:c,stderr:f}))()})}async function y6(t,e,r,{cwd:s,project:a,stdin:n,stdout:c,stderr:f}){return await le.mktempPromise(async p=>{let{env:h,cwd:E}=await lye(t,{project:a,binFolder:p,cwd:s});return await SI(e,r,{cwd:E,env:h,stdin:n,stdout:c,stderr:f})})}async function Uyt(t,{binFolder:e,cwd:r,lifecycleScript:s}){let a=await Nv({project:t.project,locator:t.anchoredLocator,binFolder:e,lifecycleScript:s});return await C6(e,await fye(t)),typeof r>"u"&&(r=K.dirname(await le.realpathPromise(K.join(t.cwd,"package.json")))),{manifest:t.manifest,binFolder:e,env:a,cwd:r}}async function lye(t,{project:e,binFolder:r,cwd:s,lifecycleScript:a}){let n=e.tryWorkspaceByLocator(t);if(n!==null)return Uyt(n,{binFolder:r,cwd:s,lifecycleScript:a});let c=e.storedPackages.get(t.locatorHash);if(!c)throw new Error(`Package for ${Yr(e.configuration,t)} not found in the project`);return await tA.openPromise(async f=>{let p=e.configuration,h=e.configuration.getLinkers(),E={project:e,report:new Ot({stdout:new x0.PassThrough,configuration:p})},C=h.find(N=>N.supportsPackage(c,E));if(!C)throw new Error(`The package ${Yr(e.configuration,c)} isn't supported by any of the available linkers`);let S=await Nv({project:e,locator:t,binFolder:r,lifecycleScript:a});await C6(r,await _T(t,{project:e}));let P=await C.findPackageLocation(c,E),I=new Sn(P,{baseFs:f}),R=await Ht.find(vt.dot,{baseFs:I});return typeof s>"u"&&(s=P),{manifest:R,binFolder:r,env:S,cwd:s}})}async function cye(t,e,r,{cwd:s,stdin:a,stdout:n,stderr:c}){return await MT(t.anchoredLocator,e,r,{cwd:s,project:t.project,stdin:a,stdout:n,stderr:c})}function E6(t,e){return t.manifest.scripts.has(e)}async function uye(t,e,{cwd:r,report:s}){let{configuration:a}=t.project,n=null;await le.mktempPromise(async c=>{let f=K.join(c,`${e}.log`),p=`# This file contains the result of Yarn calling the "${e}" lifecycle script inside a workspace ("${ue.fromPortablePath(t.cwd)}") +`,{stdout:h,stderr:E}=a.getSubprocessStreams(f,{report:s,prefix:Yr(a,t.anchoredLocator),header:p});s.reportInfo(36,`Calling the "${e}" lifecycle script`);let C=await cye(t,e,[],{cwd:r,stdin:n,stdout:h,stderr:E});if(h.end(),E.end(),C!==0)throw le.detachTemp(c),new Yt(36,`${(0,iye.default)(e)} script failed (exit code ${Ut(a,C,Ct.NUMBER)}, logs can be found here: ${Ut(a,f,Ct.PATH)}); run ${Ut(a,`yarn ${e}`,Ct.CODE)} to investigate`)})}async function Hyt(t,e,r){E6(t,e)&&await uye(t,e,r)}function I6(t){let e=K.extname(t);if(e.match(/\.[cm]?[jt]sx?$/))return!0;if(e===".exe"||e===".bin")return!1;let r=Buffer.alloc(4),s;try{s=le.openSync(t,"r")}catch{return!0}try{le.readSync(s,r,0,r.length,0)}finally{le.closeSync(s)}let a=r.readUint32BE();return!(a===3405691582||a===3489328638||a===2135247942||(a&4294901760)===1297743872)}async function _T(t,{project:e}){let r=e.configuration,s=new Map,a=e.storedPackages.get(t.locatorHash);if(!a)throw new Error(`Package for ${Yr(r,t)} not found in the project`);let n=new x0.Writable,c=r.getLinkers(),f={project:e,report:new Ot({configuration:r,stdout:n})},p=new Set([t.locatorHash]);for(let E of a.dependencies.values()){let C=e.storedResolutions.get(E.descriptorHash);if(!C)throw new Error(`Assertion failed: The resolution (${ri(r,E)}) should have been registered`);p.add(C)}let h=await Promise.all(Array.from(p,async E=>{let C=e.storedPackages.get(E);if(!C)throw new Error(`Assertion failed: The package (${E}) should have been registered`);if(C.bin.size===0)return Yl.skip;let S=c.find(I=>I.supportsPackage(C,f));if(!S)return Yl.skip;let P=null;try{P=await S.findPackageLocation(C,f)}catch(I){if(I.code==="LOCATOR_NOT_INSTALLED")return Yl.skip;throw I}return{dependency:C,packageLocation:P}}));for(let E of h){if(E===Yl.skip)continue;let{dependency:C,packageLocation:S}=E;for(let[P,I]of C.bin){let R=K.resolve(S,I);s.set(P,[C,ue.fromPortablePath(R),I6(R)])}}return s}async function fye(t){return await _T(t.anchoredLocator,{project:t.project})}async function C6(t,e){await Promise.all(Array.from(e,([r,[,s,a]])=>a?P0(t,r,process.execPath,[s]):P0(t,r,s,[])))}async function Aye(t,e,r,{cwd:s,project:a,stdin:n,stdout:c,stderr:f,nodeArgs:p=[],packageAccessibleBinaries:h}){h??=await _T(t,{project:a});let E=h.get(e);if(!E)throw new Error(`Binary not found (${e}) for ${Yr(a.configuration,t)}`);return await le.mktempPromise(async C=>{let[,S]=E,P=await Nv({project:a,locator:t,binFolder:C});await C6(P.BERRY_BIN_FOLDER,h);let I=I6(ue.toPortablePath(S))?Yu(process.execPath,[...p,S,...r],{cwd:s,env:P,stdin:n,stdout:c,stderr:f}):Yu(S,r,{cwd:s,env:P,stdin:n,stdout:c,stderr:f}),R;try{R=await I}finally{await le.removePromise(P.BERRY_BIN_FOLDER)}return R.code})}async function jyt(t,e,r,{cwd:s,stdin:a,stdout:n,stderr:c,packageAccessibleBinaries:f}){return await Aye(t.anchoredLocator,e,r,{project:t.project,cwd:s,stdin:a,stdout:n,stderr:c,packageAccessibleBinaries:f})}var iye,sye,x0,oye,Oyt,Lyt,w6=It(()=>{bt();bt();rA();bv();iye=et(d6()),sye=et(Md()),x0=ye("stream");oI();Fc();Fv();Rv();dT();Qc();kc();Np();Yo();oye=(a=>(a.Yarn1="Yarn Classic",a.Yarn2="Yarn",a.Npm="npm",a.Pnpm="pnpm",a))(oye||{});Oyt=2,Lyt=(0,sye.default)(Oyt)});var bI=L((X$t,hye)=>{"use strict";var pye=new Map([["C","cwd"],["f","file"],["z","gzip"],["P","preservePaths"],["U","unlink"],["strip-components","strip"],["stripComponents","strip"],["keep-newer","newer"],["keepNewer","newer"],["keep-newer-files","newer"],["keepNewerFiles","newer"],["k","keep"],["keep-existing","keep"],["keepExisting","keep"],["m","noMtime"],["no-mtime","noMtime"],["p","preserveOwner"],["L","follow"],["h","follow"]]);hye.exports=t=>t?Object.keys(t).map(e=>[pye.has(e)?pye.get(e):e,t[e]]).reduce((e,r)=>(e[r[0]]=r[1],e),Object.create(null)):{}});var xI=L(($$t,Bye)=>{"use strict";var gye=typeof process=="object"&&process?process:{stdout:null,stderr:null},qyt=ye("events"),dye=ye("stream"),mye=ye("string_decoder").StringDecoder,qp=Symbol("EOF"),Gp=Symbol("maybeEmitEnd"),k0=Symbol("emittedEnd"),UT=Symbol("emittingEnd"),Ov=Symbol("emittedError"),HT=Symbol("closed"),yye=Symbol("read"),jT=Symbol("flush"),Eye=Symbol("flushChunk"),fl=Symbol("encoding"),Wp=Symbol("decoder"),qT=Symbol("flowing"),Lv=Symbol("paused"),PI=Symbol("resume"),Vs=Symbol("bufferLength"),B6=Symbol("bufferPush"),v6=Symbol("bufferShift"),zo=Symbol("objectMode"),Zo=Symbol("destroyed"),S6=Symbol("emitData"),Iye=Symbol("emitEnd"),D6=Symbol("emitEnd2"),Yp=Symbol("async"),Mv=t=>Promise.resolve().then(t),Cye=global._MP_NO_ITERATOR_SYMBOLS_!=="1",Gyt=Cye&&Symbol.asyncIterator||Symbol("asyncIterator not implemented"),Wyt=Cye&&Symbol.iterator||Symbol("iterator not implemented"),Yyt=t=>t==="end"||t==="finish"||t==="prefinish",Vyt=t=>t instanceof ArrayBuffer||typeof t=="object"&&t.constructor&&t.constructor.name==="ArrayBuffer"&&t.byteLength>=0,Kyt=t=>!Buffer.isBuffer(t)&&ArrayBuffer.isView(t),GT=class{constructor(e,r,s){this.src=e,this.dest=r,this.opts=s,this.ondrain=()=>e[PI](),r.on("drain",this.ondrain)}unpipe(){this.dest.removeListener("drain",this.ondrain)}proxyErrors(){}end(){this.unpipe(),this.opts.end&&this.dest.end()}},b6=class extends GT{unpipe(){this.src.removeListener("error",this.proxyErrors),super.unpipe()}constructor(e,r,s){super(e,r,s),this.proxyErrors=a=>r.emit("error",a),e.on("error",this.proxyErrors)}};Bye.exports=class wye extends dye{constructor(e){super(),this[qT]=!1,this[Lv]=!1,this.pipes=[],this.buffer=[],this[zo]=e&&e.objectMode||!1,this[zo]?this[fl]=null:this[fl]=e&&e.encoding||null,this[fl]==="buffer"&&(this[fl]=null),this[Yp]=e&&!!e.async||!1,this[Wp]=this[fl]?new mye(this[fl]):null,this[qp]=!1,this[k0]=!1,this[UT]=!1,this[HT]=!1,this[Ov]=null,this.writable=!0,this.readable=!0,this[Vs]=0,this[Zo]=!1}get bufferLength(){return this[Vs]}get encoding(){return this[fl]}set encoding(e){if(this[zo])throw new Error("cannot set encoding in objectMode");if(this[fl]&&e!==this[fl]&&(this[Wp]&&this[Wp].lastNeed||this[Vs]))throw new Error("cannot change encoding");this[fl]!==e&&(this[Wp]=e?new mye(e):null,this.buffer.length&&(this.buffer=this.buffer.map(r=>this[Wp].write(r)))),this[fl]=e}setEncoding(e){this.encoding=e}get objectMode(){return this[zo]}set objectMode(e){this[zo]=this[zo]||!!e}get async(){return this[Yp]}set async(e){this[Yp]=this[Yp]||!!e}write(e,r,s){if(this[qp])throw new Error("write after end");if(this[Zo])return this.emit("error",Object.assign(new Error("Cannot call write after a stream was destroyed"),{code:"ERR_STREAM_DESTROYED"})),!0;typeof r=="function"&&(s=r,r="utf8"),r||(r="utf8");let a=this[Yp]?Mv:n=>n();return!this[zo]&&!Buffer.isBuffer(e)&&(Kyt(e)?e=Buffer.from(e.buffer,e.byteOffset,e.byteLength):Vyt(e)?e=Buffer.from(e):typeof e!="string"&&(this.objectMode=!0)),this[zo]?(this.flowing&&this[Vs]!==0&&this[jT](!0),this.flowing?this.emit("data",e):this[B6](e),this[Vs]!==0&&this.emit("readable"),s&&a(s),this.flowing):e.length?(typeof e=="string"&&!(r===this[fl]&&!this[Wp].lastNeed)&&(e=Buffer.from(e,r)),Buffer.isBuffer(e)&&this[fl]&&(e=this[Wp].write(e)),this.flowing&&this[Vs]!==0&&this[jT](!0),this.flowing?this.emit("data",e):this[B6](e),this[Vs]!==0&&this.emit("readable"),s&&a(s),this.flowing):(this[Vs]!==0&&this.emit("readable"),s&&a(s),this.flowing)}read(e){if(this[Zo])return null;if(this[Vs]===0||e===0||e>this[Vs])return this[Gp](),null;this[zo]&&(e=null),this.buffer.length>1&&!this[zo]&&(this.encoding?this.buffer=[this.buffer.join("")]:this.buffer=[Buffer.concat(this.buffer,this[Vs])]);let r=this[yye](e||null,this.buffer[0]);return this[Gp](),r}[yye](e,r){return e===r.length||e===null?this[v6]():(this.buffer[0]=r.slice(e),r=r.slice(0,e),this[Vs]-=e),this.emit("data",r),!this.buffer.length&&!this[qp]&&this.emit("drain"),r}end(e,r,s){return typeof e=="function"&&(s=e,e=null),typeof r=="function"&&(s=r,r="utf8"),e&&this.write(e,r),s&&this.once("end",s),this[qp]=!0,this.writable=!1,(this.flowing||!this[Lv])&&this[Gp](),this}[PI](){this[Zo]||(this[Lv]=!1,this[qT]=!0,this.emit("resume"),this.buffer.length?this[jT]():this[qp]?this[Gp]():this.emit("drain"))}resume(){return this[PI]()}pause(){this[qT]=!1,this[Lv]=!0}get destroyed(){return this[Zo]}get flowing(){return this[qT]}get paused(){return this[Lv]}[B6](e){this[zo]?this[Vs]+=1:this[Vs]+=e.length,this.buffer.push(e)}[v6](){return this.buffer.length&&(this[zo]?this[Vs]-=1:this[Vs]-=this.buffer[0].length),this.buffer.shift()}[jT](e){do;while(this[Eye](this[v6]()));!e&&!this.buffer.length&&!this[qp]&&this.emit("drain")}[Eye](e){return e?(this.emit("data",e),this.flowing):!1}pipe(e,r){if(this[Zo])return;let s=this[k0];return r=r||{},e===gye.stdout||e===gye.stderr?r.end=!1:r.end=r.end!==!1,r.proxyErrors=!!r.proxyErrors,s?r.end&&e.end():(this.pipes.push(r.proxyErrors?new b6(this,e,r):new GT(this,e,r)),this[Yp]?Mv(()=>this[PI]()):this[PI]()),e}unpipe(e){let r=this.pipes.find(s=>s.dest===e);r&&(this.pipes.splice(this.pipes.indexOf(r),1),r.unpipe())}addListener(e,r){return this.on(e,r)}on(e,r){let s=super.on(e,r);return e==="data"&&!this.pipes.length&&!this.flowing?this[PI]():e==="readable"&&this[Vs]!==0?super.emit("readable"):Yyt(e)&&this[k0]?(super.emit(e),this.removeAllListeners(e)):e==="error"&&this[Ov]&&(this[Yp]?Mv(()=>r.call(this,this[Ov])):r.call(this,this[Ov])),s}get emittedEnd(){return this[k0]}[Gp](){!this[UT]&&!this[k0]&&!this[Zo]&&this.buffer.length===0&&this[qp]&&(this[UT]=!0,this.emit("end"),this.emit("prefinish"),this.emit("finish"),this[HT]&&this.emit("close"),this[UT]=!1)}emit(e,r,...s){if(e!=="error"&&e!=="close"&&e!==Zo&&this[Zo])return;if(e==="data")return r?this[Yp]?Mv(()=>this[S6](r)):this[S6](r):!1;if(e==="end")return this[Iye]();if(e==="close"){if(this[HT]=!0,!this[k0]&&!this[Zo])return;let n=super.emit("close");return this.removeAllListeners("close"),n}else if(e==="error"){this[Ov]=r;let n=super.emit("error",r);return this[Gp](),n}else if(e==="resume"){let n=super.emit("resume");return this[Gp](),n}else if(e==="finish"||e==="prefinish"){let n=super.emit(e);return this.removeAllListeners(e),n}let a=super.emit(e,r,...s);return this[Gp](),a}[S6](e){for(let s of this.pipes)s.dest.write(e)===!1&&this.pause();let r=super.emit("data",e);return this[Gp](),r}[Iye](){this[k0]||(this[k0]=!0,this.readable=!1,this[Yp]?Mv(()=>this[D6]()):this[D6]())}[D6](){if(this[Wp]){let r=this[Wp].end();if(r){for(let s of this.pipes)s.dest.write(r);super.emit("data",r)}}for(let r of this.pipes)r.end();let e=super.emit("end");return this.removeAllListeners("end"),e}collect(){let e=[];this[zo]||(e.dataLength=0);let r=this.promise();return this.on("data",s=>{e.push(s),this[zo]||(e.dataLength+=s.length)}),r.then(()=>e)}concat(){return this[zo]?Promise.reject(new Error("cannot concat in objectMode")):this.collect().then(e=>this[zo]?Promise.reject(new Error("cannot concat in objectMode")):this[fl]?e.join(""):Buffer.concat(e,e.dataLength))}promise(){return new Promise((e,r)=>{this.on(Zo,()=>r(new Error("stream destroyed"))),this.on("error",s=>r(s)),this.on("end",()=>e())})}[Gyt](){return{next:()=>{let r=this.read();if(r!==null)return Promise.resolve({done:!1,value:r});if(this[qp])return Promise.resolve({done:!0});let s=null,a=null,n=h=>{this.removeListener("data",c),this.removeListener("end",f),a(h)},c=h=>{this.removeListener("error",n),this.removeListener("end",f),this.pause(),s({value:h,done:!!this[qp]})},f=()=>{this.removeListener("error",n),this.removeListener("data",c),s({done:!0})},p=()=>n(new Error("stream destroyed"));return new Promise((h,E)=>{a=E,s=h,this.once(Zo,p),this.once("error",n),this.once("end",f),this.once("data",c)})}}}[Wyt](){return{next:()=>{let r=this.read();return{value:r,done:r===null}}}}destroy(e){return this[Zo]?(e?this.emit("error",e):this.emit(Zo),this):(this[Zo]=!0,this.buffer.length=0,this[Vs]=0,typeof this.close=="function"&&!this[HT]&&this.close(),e?this.emit("error",e):this.emit(Zo),this)}static isStream(e){return!!e&&(e instanceof wye||e instanceof dye||e instanceof qyt&&(typeof e.pipe=="function"||typeof e.write=="function"&&typeof e.end=="function"))}}});var Sye=L((eer,vye)=>{var Jyt=ye("zlib").constants||{ZLIB_VERNUM:4736};vye.exports=Object.freeze(Object.assign(Object.create(null),{Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_MEM_ERROR:-4,Z_BUF_ERROR:-5,Z_VERSION_ERROR:-6,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,DEFLATE:1,INFLATE:2,GZIP:3,GUNZIP:4,DEFLATERAW:5,INFLATERAW:6,UNZIP:7,BROTLI_DECODE:8,BROTLI_ENCODE:9,Z_MIN_WINDOWBITS:8,Z_MAX_WINDOWBITS:15,Z_DEFAULT_WINDOWBITS:15,Z_MIN_CHUNK:64,Z_MAX_CHUNK:1/0,Z_DEFAULT_CHUNK:16384,Z_MIN_MEMLEVEL:1,Z_MAX_MEMLEVEL:9,Z_DEFAULT_MEMLEVEL:8,Z_MIN_LEVEL:-1,Z_MAX_LEVEL:9,Z_DEFAULT_LEVEL:-1,BROTLI_OPERATION_PROCESS:0,BROTLI_OPERATION_FLUSH:1,BROTLI_OPERATION_FINISH:2,BROTLI_OPERATION_EMIT_METADATA:3,BROTLI_MODE_GENERIC:0,BROTLI_MODE_TEXT:1,BROTLI_MODE_FONT:2,BROTLI_DEFAULT_MODE:0,BROTLI_MIN_QUALITY:0,BROTLI_MAX_QUALITY:11,BROTLI_DEFAULT_QUALITY:11,BROTLI_MIN_WINDOW_BITS:10,BROTLI_MAX_WINDOW_BITS:24,BROTLI_LARGE_MAX_WINDOW_BITS:30,BROTLI_DEFAULT_WINDOW:22,BROTLI_MIN_INPUT_BLOCK_BITS:16,BROTLI_MAX_INPUT_BLOCK_BITS:24,BROTLI_PARAM_MODE:0,BROTLI_PARAM_QUALITY:1,BROTLI_PARAM_LGWIN:2,BROTLI_PARAM_LGBLOCK:3,BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING:4,BROTLI_PARAM_SIZE_HINT:5,BROTLI_PARAM_LARGE_WINDOW:6,BROTLI_PARAM_NPOSTFIX:7,BROTLI_PARAM_NDIRECT:8,BROTLI_DECODER_RESULT_ERROR:0,BROTLI_DECODER_RESULT_SUCCESS:1,BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:2,BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:3,BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION:0,BROTLI_DECODER_PARAM_LARGE_WINDOW:1,BROTLI_DECODER_NO_ERROR:0,BROTLI_DECODER_SUCCESS:1,BROTLI_DECODER_NEEDS_MORE_INPUT:2,BROTLI_DECODER_NEEDS_MORE_OUTPUT:3,BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:-1,BROTLI_DECODER_ERROR_FORMAT_RESERVED:-2,BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:-3,BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:-4,BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:-5,BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:-6,BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:-7,BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:-8,BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:-9,BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:-10,BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:-11,BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:-12,BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:-13,BROTLI_DECODER_ERROR_FORMAT_PADDING_1:-14,BROTLI_DECODER_ERROR_FORMAT_PADDING_2:-15,BROTLI_DECODER_ERROR_FORMAT_DISTANCE:-16,BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:-19,BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:-20,BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:-21,BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:-22,BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:-25,BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:-26,BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:-27,BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:-30,BROTLI_DECODER_ERROR_UNREACHABLE:-31},Jyt))});var q6=L(zl=>{"use strict";var T6=ye("assert"),Q0=ye("buffer").Buffer,Pye=ye("zlib"),fm=zl.constants=Sye(),zyt=xI(),Dye=Q0.concat,Am=Symbol("_superWrite"),QI=class extends Error{constructor(e){super("zlib: "+e.message),this.code=e.code,this.errno=e.errno,this.code||(this.code="ZLIB_ERROR"),this.message="zlib: "+e.message,Error.captureStackTrace(this,this.constructor)}get name(){return"ZlibError"}},Zyt=Symbol("opts"),_v=Symbol("flushFlag"),bye=Symbol("finishFlushFlag"),j6=Symbol("fullFlushFlag"),Ei=Symbol("handle"),WT=Symbol("onError"),kI=Symbol("sawError"),P6=Symbol("level"),x6=Symbol("strategy"),k6=Symbol("ended"),ter=Symbol("_defaultFullFlush"),YT=class extends zyt{constructor(e,r){if(!e||typeof e!="object")throw new TypeError("invalid options for ZlibBase constructor");super(e),this[kI]=!1,this[k6]=!1,this[Zyt]=e,this[_v]=e.flush,this[bye]=e.finishFlush;try{this[Ei]=new Pye[r](e)}catch(s){throw new QI(s)}this[WT]=s=>{this[kI]||(this[kI]=!0,this.close(),this.emit("error",s))},this[Ei].on("error",s=>this[WT](new QI(s))),this.once("end",()=>this.close)}close(){this[Ei]&&(this[Ei].close(),this[Ei]=null,this.emit("close"))}reset(){if(!this[kI])return T6(this[Ei],"zlib binding closed"),this[Ei].reset()}flush(e){this.ended||(typeof e!="number"&&(e=this[j6]),this.write(Object.assign(Q0.alloc(0),{[_v]:e})))}end(e,r,s){return e&&this.write(e,r),this.flush(this[bye]),this[k6]=!0,super.end(null,null,s)}get ended(){return this[k6]}write(e,r,s){if(typeof r=="function"&&(s=r,r="utf8"),typeof e=="string"&&(e=Q0.from(e,r)),this[kI])return;T6(this[Ei],"zlib binding closed");let a=this[Ei]._handle,n=a.close;a.close=()=>{};let c=this[Ei].close;this[Ei].close=()=>{},Q0.concat=h=>h;let f;try{let h=typeof e[_v]=="number"?e[_v]:this[_v];f=this[Ei]._processChunk(e,h),Q0.concat=Dye}catch(h){Q0.concat=Dye,this[WT](new QI(h))}finally{this[Ei]&&(this[Ei]._handle=a,a.close=n,this[Ei].close=c,this[Ei].removeAllListeners("error"))}this[Ei]&&this[Ei].on("error",h=>this[WT](new QI(h)));let p;if(f)if(Array.isArray(f)&&f.length>0){p=this[Am](Q0.from(f[0]));for(let h=1;h{this.flush(a),n()};try{this[Ei].params(e,r)}finally{this[Ei].flush=s}this[Ei]&&(this[P6]=e,this[x6]=r)}}}},R6=class extends Vp{constructor(e){super(e,"Deflate")}},F6=class extends Vp{constructor(e){super(e,"Inflate")}},Q6=Symbol("_portable"),N6=class extends Vp{constructor(e){super(e,"Gzip"),this[Q6]=e&&!!e.portable}[Am](e){return this[Q6]?(this[Q6]=!1,e[9]=255,super[Am](e)):super[Am](e)}},O6=class extends Vp{constructor(e){super(e,"Gunzip")}},L6=class extends Vp{constructor(e){super(e,"DeflateRaw")}},M6=class extends Vp{constructor(e){super(e,"InflateRaw")}},_6=class extends Vp{constructor(e){super(e,"Unzip")}},VT=class extends YT{constructor(e,r){e=e||{},e.flush=e.flush||fm.BROTLI_OPERATION_PROCESS,e.finishFlush=e.finishFlush||fm.BROTLI_OPERATION_FINISH,super(e,r),this[j6]=fm.BROTLI_OPERATION_FLUSH}},U6=class extends VT{constructor(e){super(e,"BrotliCompress")}},H6=class extends VT{constructor(e){super(e,"BrotliDecompress")}};zl.Deflate=R6;zl.Inflate=F6;zl.Gzip=N6;zl.Gunzip=O6;zl.DeflateRaw=L6;zl.InflateRaw=M6;zl.Unzip=_6;typeof Pye.BrotliCompress=="function"?(zl.BrotliCompress=U6,zl.BrotliDecompress=H6):zl.BrotliCompress=zl.BrotliDecompress=class{constructor(){throw new Error("Brotli is not supported in this version of Node.js")}}});var TI=L((ier,xye)=>{var Xyt=process.env.TESTING_TAR_FAKE_PLATFORM||process.platform;xye.exports=Xyt!=="win32"?t=>t:t=>t&&t.replace(/\\/g,"/")});var KT=L((oer,kye)=>{"use strict";var $yt=xI(),G6=TI(),W6=Symbol("slurp");kye.exports=class extends $yt{constructor(e,r,s){switch(super(),this.pause(),this.extended=r,this.globalExtended=s,this.header=e,this.startBlockSize=512*Math.ceil(e.size/512),this.blockRemain=this.startBlockSize,this.remain=e.size,this.type=e.type,this.meta=!1,this.ignore=!1,this.type){case"File":case"OldFile":case"Link":case"SymbolicLink":case"CharacterDevice":case"BlockDevice":case"Directory":case"FIFO":case"ContiguousFile":case"GNUDumpDir":break;case"NextFileHasLongLinkpath":case"NextFileHasLongPath":case"OldGnuLongPath":case"GlobalExtendedHeader":case"ExtendedHeader":case"OldExtendedHeader":this.meta=!0;break;default:this.ignore=!0}this.path=G6(e.path),this.mode=e.mode,this.mode&&(this.mode=this.mode&4095),this.uid=e.uid,this.gid=e.gid,this.uname=e.uname,this.gname=e.gname,this.size=e.size,this.mtime=e.mtime,this.atime=e.atime,this.ctime=e.ctime,this.linkpath=G6(e.linkpath),this.uname=e.uname,this.gname=e.gname,r&&this[W6](r),s&&this[W6](s,!0)}write(e){let r=e.length;if(r>this.blockRemain)throw new Error("writing more to entry than is appropriate");let s=this.remain,a=this.blockRemain;return this.remain=Math.max(0,s-r),this.blockRemain=Math.max(0,a-r),this.ignore?!0:s>=r?super.write(e):super.write(e.slice(0,s))}[W6](e,r){for(let s in e)e[s]!==null&&e[s]!==void 0&&!(r&&s==="path")&&(this[s]=s==="path"||s==="linkpath"?G6(e[s]):e[s])}}});var Y6=L(JT=>{"use strict";JT.name=new Map([["0","File"],["","OldFile"],["1","Link"],["2","SymbolicLink"],["3","CharacterDevice"],["4","BlockDevice"],["5","Directory"],["6","FIFO"],["7","ContiguousFile"],["g","GlobalExtendedHeader"],["x","ExtendedHeader"],["A","SolarisACL"],["D","GNUDumpDir"],["I","Inode"],["K","NextFileHasLongLinkpath"],["L","NextFileHasLongPath"],["M","ContinuationFile"],["N","OldGnuLongPath"],["S","SparseFile"],["V","TapeVolumeHeader"],["X","OldExtendedHeader"]]);JT.code=new Map(Array.from(JT.name).map(t=>[t[1],t[0]]))});var Fye=L((ler,Rye)=>{"use strict";var eEt=(t,e)=>{if(Number.isSafeInteger(t))t<0?rEt(t,e):tEt(t,e);else throw Error("cannot encode number outside of javascript safe integer range");return e},tEt=(t,e)=>{e[0]=128;for(var r=e.length;r>1;r--)e[r-1]=t&255,t=Math.floor(t/256)},rEt=(t,e)=>{e[0]=255;var r=!1;t=t*-1;for(var s=e.length;s>1;s--){var a=t&255;t=Math.floor(t/256),r?e[s-1]=Qye(a):a===0?e[s-1]=0:(r=!0,e[s-1]=Tye(a))}},nEt=t=>{let e=t[0],r=e===128?sEt(t.slice(1,t.length)):e===255?iEt(t):null;if(r===null)throw Error("invalid base256 encoding");if(!Number.isSafeInteger(r))throw Error("parsed number outside of javascript safe integer range");return r},iEt=t=>{for(var e=t.length,r=0,s=!1,a=e-1;a>-1;a--){var n=t[a],c;s?c=Qye(n):n===0?c=n:(s=!0,c=Tye(n)),c!==0&&(r-=c*Math.pow(256,e-a-1))}return r},sEt=t=>{for(var e=t.length,r=0,s=e-1;s>-1;s--){var a=t[s];a!==0&&(r+=a*Math.pow(256,e-s-1))}return r},Qye=t=>(255^t)&255,Tye=t=>(255^t)+1&255;Rye.exports={encode:eEt,parse:nEt}});var FI=L((cer,Oye)=>{"use strict";var V6=Y6(),RI=ye("path").posix,Nye=Fye(),K6=Symbol("slurp"),Zl=Symbol("type"),Z6=class{constructor(e,r,s,a){this.cksumValid=!1,this.needPax=!1,this.nullBlock=!1,this.block=null,this.path=null,this.mode=null,this.uid=null,this.gid=null,this.size=null,this.mtime=null,this.cksum=null,this[Zl]="0",this.linkpath=null,this.uname=null,this.gname=null,this.devmaj=0,this.devmin=0,this.atime=null,this.ctime=null,Buffer.isBuffer(e)?this.decode(e,r||0,s,a):e&&this.set(e)}decode(e,r,s,a){if(r||(r=0),!e||!(e.length>=r+512))throw new Error("need 512 bytes for header");if(this.path=pm(e,r,100),this.mode=T0(e,r+100,8),this.uid=T0(e,r+108,8),this.gid=T0(e,r+116,8),this.size=T0(e,r+124,12),this.mtime=J6(e,r+136,12),this.cksum=T0(e,r+148,12),this[K6](s),this[K6](a,!0),this[Zl]=pm(e,r+156,1),this[Zl]===""&&(this[Zl]="0"),this[Zl]==="0"&&this.path.substr(-1)==="/"&&(this[Zl]="5"),this[Zl]==="5"&&(this.size=0),this.linkpath=pm(e,r+157,100),e.slice(r+257,r+265).toString()==="ustar\x0000")if(this.uname=pm(e,r+265,32),this.gname=pm(e,r+297,32),this.devmaj=T0(e,r+329,8),this.devmin=T0(e,r+337,8),e[r+475]!==0){let c=pm(e,r+345,155);this.path=c+"/"+this.path}else{let c=pm(e,r+345,130);c&&(this.path=c+"/"+this.path),this.atime=J6(e,r+476,12),this.ctime=J6(e,r+488,12)}let n=8*32;for(let c=r;c=r+512))throw new Error("need 512 bytes for header");let s=this.ctime||this.atime?130:155,a=oEt(this.path||"",s),n=a[0],c=a[1];this.needPax=a[2],this.needPax=hm(e,r,100,n)||this.needPax,this.needPax=R0(e,r+100,8,this.mode)||this.needPax,this.needPax=R0(e,r+108,8,this.uid)||this.needPax,this.needPax=R0(e,r+116,8,this.gid)||this.needPax,this.needPax=R0(e,r+124,12,this.size)||this.needPax,this.needPax=z6(e,r+136,12,this.mtime)||this.needPax,e[r+156]=this[Zl].charCodeAt(0),this.needPax=hm(e,r+157,100,this.linkpath)||this.needPax,e.write("ustar\x0000",r+257,8),this.needPax=hm(e,r+265,32,this.uname)||this.needPax,this.needPax=hm(e,r+297,32,this.gname)||this.needPax,this.needPax=R0(e,r+329,8,this.devmaj)||this.needPax,this.needPax=R0(e,r+337,8,this.devmin)||this.needPax,this.needPax=hm(e,r+345,s,c)||this.needPax,e[r+475]!==0?this.needPax=hm(e,r+345,155,c)||this.needPax:(this.needPax=hm(e,r+345,130,c)||this.needPax,this.needPax=z6(e,r+476,12,this.atime)||this.needPax,this.needPax=z6(e,r+488,12,this.ctime)||this.needPax);let f=8*32;for(let p=r;p{let s=t,a="",n,c=RI.parse(t).root||".";if(Buffer.byteLength(s)<100)n=[s,a,!1];else{a=RI.dirname(s),s=RI.basename(s);do Buffer.byteLength(s)<=100&&Buffer.byteLength(a)<=e?n=[s,a,!1]:Buffer.byteLength(s)>100&&Buffer.byteLength(a)<=e?n=[s.substr(0,99),a,!0]:(s=RI.join(RI.basename(a),s),a=RI.dirname(a));while(a!==c&&!n);n||(n=[t.substr(0,99),"",!0])}return n},pm=(t,e,r)=>t.slice(e,e+r).toString("utf8").replace(/\0.*/,""),J6=(t,e,r)=>aEt(T0(t,e,r)),aEt=t=>t===null?null:new Date(t*1e3),T0=(t,e,r)=>t[e]&128?Nye.parse(t.slice(e,e+r)):cEt(t,e,r),lEt=t=>isNaN(t)?null:t,cEt=(t,e,r)=>lEt(parseInt(t.slice(e,e+r).toString("utf8").replace(/\0.*$/,"").trim(),8)),uEt={12:8589934591,8:2097151},R0=(t,e,r,s)=>s===null?!1:s>uEt[r]||s<0?(Nye.encode(s,t.slice(e,e+r)),!0):(fEt(t,e,r,s),!1),fEt=(t,e,r,s)=>t.write(AEt(s,r),e,r,"ascii"),AEt=(t,e)=>pEt(Math.floor(t).toString(8),e),pEt=(t,e)=>(t.length===e-1?t:new Array(e-t.length-1).join("0")+t+" ")+"\0",z6=(t,e,r,s)=>s===null?!1:R0(t,e,r,s.getTime()/1e3),hEt=new Array(156).join("\0"),hm=(t,e,r,s)=>s===null?!1:(t.write(s+hEt,e,r,"utf8"),s.length!==Buffer.byteLength(s)||s.length>r);Oye.exports=Z6});var zT=L((uer,Lye)=>{"use strict";var gEt=FI(),dEt=ye("path"),Uv=class{constructor(e,r){this.atime=e.atime||null,this.charset=e.charset||null,this.comment=e.comment||null,this.ctime=e.ctime||null,this.gid=e.gid||null,this.gname=e.gname||null,this.linkpath=e.linkpath||null,this.mtime=e.mtime||null,this.path=e.path||null,this.size=e.size||null,this.uid=e.uid||null,this.uname=e.uname||null,this.dev=e.dev||null,this.ino=e.ino||null,this.nlink=e.nlink||null,this.global=r||!1}encode(){let e=this.encodeBody();if(e==="")return null;let r=Buffer.byteLength(e),s=512*Math.ceil(1+r/512),a=Buffer.allocUnsafe(s);for(let n=0;n<512;n++)a[n]=0;new gEt({path:("PaxHeader/"+dEt.basename(this.path)).slice(0,99),mode:this.mode||420,uid:this.uid||null,gid:this.gid||null,size:r,mtime:this.mtime||null,type:this.global?"GlobalExtendedHeader":"ExtendedHeader",linkpath:"",uname:this.uname||"",gname:this.gname||"",devmaj:0,devmin:0,atime:this.atime||null,ctime:this.ctime||null}).encode(a),a.write(e,512,r,"utf8");for(let n=r+512;n=Math.pow(10,n)&&(n+=1),n+a+s}};Uv.parse=(t,e,r)=>new Uv(mEt(yEt(t),e),r);var mEt=(t,e)=>e?Object.keys(t).reduce((r,s)=>(r[s]=t[s],r),e):t,yEt=t=>t.replace(/\n$/,"").split(` +`).reduce(EEt,Object.create(null)),EEt=(t,e)=>{let r=parseInt(e,10);if(r!==Buffer.byteLength(e)+1)return t;e=e.substr((r+" ").length);let s=e.split("="),a=s.shift().replace(/^SCHILY\.(dev|ino|nlink)/,"$1");if(!a)return t;let n=s.join("=");return t[a]=/^([A-Z]+\.)?([mac]|birth|creation)time$/.test(a)?new Date(n*1e3):/^[0-9]+$/.test(n)?+n:n,t};Lye.exports=Uv});var NI=L((fer,Mye)=>{Mye.exports=t=>{let e=t.length-1,r=-1;for(;e>-1&&t.charAt(e)==="/";)r=e,e--;return r===-1?t:t.slice(0,r)}});var ZT=L((Aer,_ye)=>{"use strict";_ye.exports=t=>class extends t{warn(e,r,s={}){this.file&&(s.file=this.file),this.cwd&&(s.cwd=this.cwd),s.code=r instanceof Error&&r.code||e,s.tarCode=e,!this.strict&&s.recoverable!==!1?(r instanceof Error&&(s=Object.assign(r,s),r=r.message),this.emit("warn",s.tarCode,r,s)):r instanceof Error?this.emit("error",Object.assign(r,s)):this.emit("error",Object.assign(new Error(`${e}: ${r}`),s))}}});var $6=L((her,Uye)=>{"use strict";var XT=["|","<",">","?",":"],X6=XT.map(t=>String.fromCharCode(61440+t.charCodeAt(0))),IEt=new Map(XT.map((t,e)=>[t,X6[e]])),CEt=new Map(X6.map((t,e)=>[t,XT[e]]));Uye.exports={encode:t=>XT.reduce((e,r)=>e.split(r).join(IEt.get(r)),t),decode:t=>X6.reduce((e,r)=>e.split(r).join(CEt.get(r)),t)}});var eq=L((ger,jye)=>{var{isAbsolute:wEt,parse:Hye}=ye("path").win32;jye.exports=t=>{let e="",r=Hye(t);for(;wEt(t)||r.root;){let s=t.charAt(0)==="/"&&t.slice(0,4)!=="//?/"?"/":r.root;t=t.substr(s.length),e+=s,r=Hye(t)}return[e,t]}});var Gye=L((der,qye)=>{"use strict";qye.exports=(t,e,r)=>(t&=4095,r&&(t=(t|384)&-19),e&&(t&256&&(t|=64),t&32&&(t|=8),t&4&&(t|=1)),t)});var uq=L((Eer,iEe)=>{"use strict";var Zye=xI(),Xye=zT(),$ye=FI(),sA=ye("fs"),Wye=ye("path"),iA=TI(),BEt=NI(),eEe=(t,e)=>e?(t=iA(t).replace(/^\.(\/|$)/,""),BEt(e)+"/"+t):iA(t),vEt=16*1024*1024,Yye=Symbol("process"),Vye=Symbol("file"),Kye=Symbol("directory"),rq=Symbol("symlink"),Jye=Symbol("hardlink"),Hv=Symbol("header"),$T=Symbol("read"),nq=Symbol("lstat"),eR=Symbol("onlstat"),iq=Symbol("onread"),sq=Symbol("onreadlink"),oq=Symbol("openfile"),aq=Symbol("onopenfile"),F0=Symbol("close"),tR=Symbol("mode"),lq=Symbol("awaitDrain"),tq=Symbol("ondrain"),oA=Symbol("prefix"),zye=Symbol("hadError"),tEe=ZT(),SEt=$6(),rEe=eq(),nEe=Gye(),rR=tEe(class extends Zye{constructor(e,r){if(r=r||{},super(r),typeof e!="string")throw new TypeError("path is required");this.path=iA(e),this.portable=!!r.portable,this.myuid=process.getuid&&process.getuid()||0,this.myuser=process.env.USER||"",this.maxReadSize=r.maxReadSize||vEt,this.linkCache=r.linkCache||new Map,this.statCache=r.statCache||new Map,this.preservePaths=!!r.preservePaths,this.cwd=iA(r.cwd||process.cwd()),this.strict=!!r.strict,this.noPax=!!r.noPax,this.noMtime=!!r.noMtime,this.mtime=r.mtime||null,this.prefix=r.prefix?iA(r.prefix):null,this.fd=null,this.blockLen=null,this.blockRemain=null,this.buf=null,this.offset=null,this.length=null,this.pos=null,this.remain=null,typeof r.onwarn=="function"&&this.on("warn",r.onwarn);let s=!1;if(!this.preservePaths){let[a,n]=rEe(this.path);a&&(this.path=n,s=a)}this.win32=!!r.win32||process.platform==="win32",this.win32&&(this.path=SEt.decode(this.path.replace(/\\/g,"/")),e=e.replace(/\\/g,"/")),this.absolute=iA(r.absolute||Wye.resolve(this.cwd,e)),this.path===""&&(this.path="./"),s&&this.warn("TAR_ENTRY_INFO",`stripping ${s} from absolute path`,{entry:this,path:s+this.path}),this.statCache.has(this.absolute)?this[eR](this.statCache.get(this.absolute)):this[nq]()}emit(e,...r){return e==="error"&&(this[zye]=!0),super.emit(e,...r)}[nq](){sA.lstat(this.absolute,(e,r)=>{if(e)return this.emit("error",e);this[eR](r)})}[eR](e){this.statCache.set(this.absolute,e),this.stat=e,e.isFile()||(e.size=0),this.type=bEt(e),this.emit("stat",e),this[Yye]()}[Yye](){switch(this.type){case"File":return this[Vye]();case"Directory":return this[Kye]();case"SymbolicLink":return this[rq]();default:return this.end()}}[tR](e){return nEe(e,this.type==="Directory",this.portable)}[oA](e){return eEe(e,this.prefix)}[Hv](){this.type==="Directory"&&this.portable&&(this.noMtime=!0),this.header=new $ye({path:this[oA](this.path),linkpath:this.type==="Link"?this[oA](this.linkpath):this.linkpath,mode:this[tR](this.stat.mode),uid:this.portable?null:this.stat.uid,gid:this.portable?null:this.stat.gid,size:this.stat.size,mtime:this.noMtime?null:this.mtime||this.stat.mtime,type:this.type,uname:this.portable?null:this.stat.uid===this.myuid?this.myuser:"",atime:this.portable?null:this.stat.atime,ctime:this.portable?null:this.stat.ctime}),this.header.encode()&&!this.noPax&&super.write(new Xye({atime:this.portable?null:this.header.atime,ctime:this.portable?null:this.header.ctime,gid:this.portable?null:this.header.gid,mtime:this.noMtime?null:this.mtime||this.header.mtime,path:this[oA](this.path),linkpath:this.type==="Link"?this[oA](this.linkpath):this.linkpath,size:this.header.size,uid:this.portable?null:this.header.uid,uname:this.portable?null:this.header.uname,dev:this.portable?null:this.stat.dev,ino:this.portable?null:this.stat.ino,nlink:this.portable?null:this.stat.nlink}).encode()),super.write(this.header.block)}[Kye](){this.path.substr(-1)!=="/"&&(this.path+="/"),this.stat.size=0,this[Hv](),this.end()}[rq](){sA.readlink(this.absolute,(e,r)=>{if(e)return this.emit("error",e);this[sq](r)})}[sq](e){this.linkpath=iA(e),this[Hv](),this.end()}[Jye](e){this.type="Link",this.linkpath=iA(Wye.relative(this.cwd,e)),this.stat.size=0,this[Hv](),this.end()}[Vye](){if(this.stat.nlink>1){let e=this.stat.dev+":"+this.stat.ino;if(this.linkCache.has(e)){let r=this.linkCache.get(e);if(r.indexOf(this.cwd)===0)return this[Jye](r)}this.linkCache.set(e,this.absolute)}if(this[Hv](),this.stat.size===0)return this.end();this[oq]()}[oq](){sA.open(this.absolute,"r",(e,r)=>{if(e)return this.emit("error",e);this[aq](r)})}[aq](e){if(this.fd=e,this[zye])return this[F0]();this.blockLen=512*Math.ceil(this.stat.size/512),this.blockRemain=this.blockLen;let r=Math.min(this.blockLen,this.maxReadSize);this.buf=Buffer.allocUnsafe(r),this.offset=0,this.pos=0,this.remain=this.stat.size,this.length=this.buf.length,this[$T]()}[$T](){let{fd:e,buf:r,offset:s,length:a,pos:n}=this;sA.read(e,r,s,a,n,(c,f)=>{if(c)return this[F0](()=>this.emit("error",c));this[iq](f)})}[F0](e){sA.close(this.fd,e)}[iq](e){if(e<=0&&this.remain>0){let a=new Error("encountered unexpected EOF");return a.path=this.absolute,a.syscall="read",a.code="EOF",this[F0](()=>this.emit("error",a))}if(e>this.remain){let a=new Error("did not encounter expected EOF");return a.path=this.absolute,a.syscall="read",a.code="EOF",this[F0](()=>this.emit("error",a))}if(e===this.remain)for(let a=e;athis[tq]())}[lq](e){this.once("drain",e)}write(e){if(this.blockRemaine?this.emit("error",e):this.end());this.offset>=this.length&&(this.buf=Buffer.allocUnsafe(Math.min(this.blockRemain,this.buf.length)),this.offset=0),this.length=this.buf.length-this.offset,this[$T]()}}),cq=class extends rR{[nq](){this[eR](sA.lstatSync(this.absolute))}[rq](){this[sq](sA.readlinkSync(this.absolute))}[oq](){this[aq](sA.openSync(this.absolute,"r"))}[$T](){let e=!0;try{let{fd:r,buf:s,offset:a,length:n,pos:c}=this,f=sA.readSync(r,s,a,n,c);this[iq](f),e=!1}finally{if(e)try{this[F0](()=>{})}catch{}}}[lq](e){e()}[F0](e){sA.closeSync(this.fd),e()}},DEt=tEe(class extends Zye{constructor(e,r){r=r||{},super(r),this.preservePaths=!!r.preservePaths,this.portable=!!r.portable,this.strict=!!r.strict,this.noPax=!!r.noPax,this.noMtime=!!r.noMtime,this.readEntry=e,this.type=e.type,this.type==="Directory"&&this.portable&&(this.noMtime=!0),this.prefix=r.prefix||null,this.path=iA(e.path),this.mode=this[tR](e.mode),this.uid=this.portable?null:e.uid,this.gid=this.portable?null:e.gid,this.uname=this.portable?null:e.uname,this.gname=this.portable?null:e.gname,this.size=e.size,this.mtime=this.noMtime?null:r.mtime||e.mtime,this.atime=this.portable?null:e.atime,this.ctime=this.portable?null:e.ctime,this.linkpath=iA(e.linkpath),typeof r.onwarn=="function"&&this.on("warn",r.onwarn);let s=!1;if(!this.preservePaths){let[a,n]=rEe(this.path);a&&(this.path=n,s=a)}this.remain=e.size,this.blockRemain=e.startBlockSize,this.header=new $ye({path:this[oA](this.path),linkpath:this.type==="Link"?this[oA](this.linkpath):this.linkpath,mode:this.mode,uid:this.portable?null:this.uid,gid:this.portable?null:this.gid,size:this.size,mtime:this.noMtime?null:this.mtime,type:this.type,uname:this.portable?null:this.uname,atime:this.portable?null:this.atime,ctime:this.portable?null:this.ctime}),s&&this.warn("TAR_ENTRY_INFO",`stripping ${s} from absolute path`,{entry:this,path:s+this.path}),this.header.encode()&&!this.noPax&&super.write(new Xye({atime:this.portable?null:this.atime,ctime:this.portable?null:this.ctime,gid:this.portable?null:this.gid,mtime:this.noMtime?null:this.mtime,path:this[oA](this.path),linkpath:this.type==="Link"?this[oA](this.linkpath):this.linkpath,size:this.size,uid:this.portable?null:this.uid,uname:this.portable?null:this.uname,dev:this.portable?null:this.readEntry.dev,ino:this.portable?null:this.readEntry.ino,nlink:this.portable?null:this.readEntry.nlink}).encode()),super.write(this.header.block),e.pipe(this)}[oA](e){return eEe(e,this.prefix)}[tR](e){return nEe(e,this.type==="Directory",this.portable)}write(e){let r=e.length;if(r>this.blockRemain)throw new Error("writing more to entry than is appropriate");return this.blockRemain-=r,super.write(e)}end(){return this.blockRemain&&super.write(Buffer.alloc(this.blockRemain)),super.end()}});rR.Sync=cq;rR.Tar=DEt;var bEt=t=>t.isFile()?"File":t.isDirectory()?"Directory":t.isSymbolicLink()?"SymbolicLink":"Unsupported";iEe.exports=rR});var fR=L((Cer,fEe)=>{"use strict";var cR=class{constructor(e,r){this.path=e||"./",this.absolute=r,this.entry=null,this.stat=null,this.readdir=null,this.pending=!1,this.ignore=!1,this.piped=!1}},PEt=xI(),xEt=q6(),kEt=KT(),Eq=uq(),QEt=Eq.Sync,TEt=Eq.Tar,REt=pk(),sEe=Buffer.alloc(1024),sR=Symbol("onStat"),nR=Symbol("ended"),aA=Symbol("queue"),OI=Symbol("current"),gm=Symbol("process"),iR=Symbol("processing"),oEe=Symbol("processJob"),lA=Symbol("jobs"),fq=Symbol("jobDone"),oR=Symbol("addFSEntry"),aEe=Symbol("addTarEntry"),gq=Symbol("stat"),dq=Symbol("readdir"),aR=Symbol("onreaddir"),lR=Symbol("pipe"),lEe=Symbol("entry"),Aq=Symbol("entryOpt"),mq=Symbol("writeEntryClass"),uEe=Symbol("write"),pq=Symbol("ondrain"),uR=ye("fs"),cEe=ye("path"),FEt=ZT(),hq=TI(),Iq=FEt(class extends PEt{constructor(e){super(e),e=e||Object.create(null),this.opt=e,this.file=e.file||"",this.cwd=e.cwd||process.cwd(),this.maxReadSize=e.maxReadSize,this.preservePaths=!!e.preservePaths,this.strict=!!e.strict,this.noPax=!!e.noPax,this.prefix=hq(e.prefix||""),this.linkCache=e.linkCache||new Map,this.statCache=e.statCache||new Map,this.readdirCache=e.readdirCache||new Map,this[mq]=Eq,typeof e.onwarn=="function"&&this.on("warn",e.onwarn),this.portable=!!e.portable,this.zip=null,e.gzip?(typeof e.gzip!="object"&&(e.gzip={}),this.portable&&(e.gzip.portable=!0),this.zip=new xEt.Gzip(e.gzip),this.zip.on("data",r=>super.write(r)),this.zip.on("end",r=>super.end()),this.zip.on("drain",r=>this[pq]()),this.on("resume",r=>this.zip.resume())):this.on("drain",this[pq]),this.noDirRecurse=!!e.noDirRecurse,this.follow=!!e.follow,this.noMtime=!!e.noMtime,this.mtime=e.mtime||null,this.filter=typeof e.filter=="function"?e.filter:r=>!0,this[aA]=new REt,this[lA]=0,this.jobs=+e.jobs||4,this[iR]=!1,this[nR]=!1}[uEe](e){return super.write(e)}add(e){return this.write(e),this}end(e){return e&&this.write(e),this[nR]=!0,this[gm](),this}write(e){if(this[nR])throw new Error("write after end");return e instanceof kEt?this[aEe](e):this[oR](e),this.flowing}[aEe](e){let r=hq(cEe.resolve(this.cwd,e.path));if(!this.filter(e.path,e))e.resume();else{let s=new cR(e.path,r,!1);s.entry=new TEt(e,this[Aq](s)),s.entry.on("end",a=>this[fq](s)),this[lA]+=1,this[aA].push(s)}this[gm]()}[oR](e){let r=hq(cEe.resolve(this.cwd,e));this[aA].push(new cR(e,r)),this[gm]()}[gq](e){e.pending=!0,this[lA]+=1;let r=this.follow?"stat":"lstat";uR[r](e.absolute,(s,a)=>{e.pending=!1,this[lA]-=1,s?this.emit("error",s):this[sR](e,a)})}[sR](e,r){this.statCache.set(e.absolute,r),e.stat=r,this.filter(e.path,r)||(e.ignore=!0),this[gm]()}[dq](e){e.pending=!0,this[lA]+=1,uR.readdir(e.absolute,(r,s)=>{if(e.pending=!1,this[lA]-=1,r)return this.emit("error",r);this[aR](e,s)})}[aR](e,r){this.readdirCache.set(e.absolute,r),e.readdir=r,this[gm]()}[gm](){if(!this[iR]){this[iR]=!0;for(let e=this[aA].head;e!==null&&this[lA]this.warn(r,s,a),noPax:this.noPax,cwd:this.cwd,absolute:e.absolute,preservePaths:this.preservePaths,maxReadSize:this.maxReadSize,strict:this.strict,portable:this.portable,linkCache:this.linkCache,statCache:this.statCache,noMtime:this.noMtime,mtime:this.mtime,prefix:this.prefix}}[lEe](e){this[lA]+=1;try{return new this[mq](e.path,this[Aq](e)).on("end",()=>this[fq](e)).on("error",r=>this.emit("error",r))}catch(r){this.emit("error",r)}}[pq](){this[OI]&&this[OI].entry&&this[OI].entry.resume()}[lR](e){e.piped=!0,e.readdir&&e.readdir.forEach(a=>{let n=e.path,c=n==="./"?"":n.replace(/\/*$/,"/");this[oR](c+a)});let r=e.entry,s=this.zip;s?r.on("data",a=>{s.write(a)||r.pause()}):r.on("data",a=>{super.write(a)||r.pause()})}pause(){return this.zip&&this.zip.pause(),super.pause()}}),yq=class extends Iq{constructor(e){super(e),this[mq]=QEt}pause(){}resume(){}[gq](e){let r=this.follow?"statSync":"lstatSync";this[sR](e,uR[r](e.absolute))}[dq](e,r){this[aR](e,uR.readdirSync(e.absolute))}[lR](e){let r=e.entry,s=this.zip;e.readdir&&e.readdir.forEach(a=>{let n=e.path,c=n==="./"?"":n.replace(/\/*$/,"/");this[oR](c+a)}),s?r.on("data",a=>{s.write(a)}):r.on("data",a=>{super[uEe](a)})}};Iq.Sync=yq;fEe.exports=Iq});var GI=L(qv=>{"use strict";var NEt=xI(),OEt=ye("events").EventEmitter,Al=ye("fs"),Bq=Al.writev;if(!Bq){let t=process.binding("fs"),e=t.FSReqWrap||t.FSReqCallback;Bq=(r,s,a,n)=>{let c=(p,h)=>n(p,h,s),f=new e;f.oncomplete=c,t.writeBuffers(r,s,a,f)}}var jI=Symbol("_autoClose"),Vu=Symbol("_close"),jv=Symbol("_ended"),ni=Symbol("_fd"),AEe=Symbol("_finished"),O0=Symbol("_flags"),Cq=Symbol("_flush"),vq=Symbol("_handleChunk"),Sq=Symbol("_makeBuf"),dR=Symbol("_mode"),AR=Symbol("_needDrain"),UI=Symbol("_onerror"),qI=Symbol("_onopen"),wq=Symbol("_onread"),MI=Symbol("_onwrite"),L0=Symbol("_open"),Kp=Symbol("_path"),dm=Symbol("_pos"),cA=Symbol("_queue"),_I=Symbol("_read"),pEe=Symbol("_readSize"),N0=Symbol("_reading"),pR=Symbol("_remain"),hEe=Symbol("_size"),hR=Symbol("_write"),LI=Symbol("_writing"),gR=Symbol("_defaultFlag"),HI=Symbol("_errored"),mR=class extends NEt{constructor(e,r){if(r=r||{},super(r),this.readable=!0,this.writable=!1,typeof e!="string")throw new TypeError("path must be a string");this[HI]=!1,this[ni]=typeof r.fd=="number"?r.fd:null,this[Kp]=e,this[pEe]=r.readSize||16*1024*1024,this[N0]=!1,this[hEe]=typeof r.size=="number"?r.size:1/0,this[pR]=this[hEe],this[jI]=typeof r.autoClose=="boolean"?r.autoClose:!0,typeof this[ni]=="number"?this[_I]():this[L0]()}get fd(){return this[ni]}get path(){return this[Kp]}write(){throw new TypeError("this is a readable stream")}end(){throw new TypeError("this is a readable stream")}[L0](){Al.open(this[Kp],"r",(e,r)=>this[qI](e,r))}[qI](e,r){e?this[UI](e):(this[ni]=r,this.emit("open",r),this[_I]())}[Sq](){return Buffer.allocUnsafe(Math.min(this[pEe],this[pR]))}[_I](){if(!this[N0]){this[N0]=!0;let e=this[Sq]();if(e.length===0)return process.nextTick(()=>this[wq](null,0,e));Al.read(this[ni],e,0,e.length,null,(r,s,a)=>this[wq](r,s,a))}}[wq](e,r,s){this[N0]=!1,e?this[UI](e):this[vq](r,s)&&this[_I]()}[Vu](){if(this[jI]&&typeof this[ni]=="number"){let e=this[ni];this[ni]=null,Al.close(e,r=>r?this.emit("error",r):this.emit("close"))}}[UI](e){this[N0]=!0,this[Vu](),this.emit("error",e)}[vq](e,r){let s=!1;return this[pR]-=e,e>0&&(s=super.write(ethis[qI](e,r))}[qI](e,r){this[gR]&&this[O0]==="r+"&&e&&e.code==="ENOENT"?(this[O0]="w",this[L0]()):e?this[UI](e):(this[ni]=r,this.emit("open",r),this[Cq]())}end(e,r){return e&&this.write(e,r),this[jv]=!0,!this[LI]&&!this[cA].length&&typeof this[ni]=="number"&&this[MI](null,0),this}write(e,r){return typeof e=="string"&&(e=Buffer.from(e,r)),this[jv]?(this.emit("error",new Error("write() after end()")),!1):this[ni]===null||this[LI]||this[cA].length?(this[cA].push(e),this[AR]=!0,!1):(this[LI]=!0,this[hR](e),!0)}[hR](e){Al.write(this[ni],e,0,e.length,this[dm],(r,s)=>this[MI](r,s))}[MI](e,r){e?this[UI](e):(this[dm]!==null&&(this[dm]+=r),this[cA].length?this[Cq]():(this[LI]=!1,this[jv]&&!this[AEe]?(this[AEe]=!0,this[Vu](),this.emit("finish")):this[AR]&&(this[AR]=!1,this.emit("drain"))))}[Cq](){if(this[cA].length===0)this[jv]&&this[MI](null,0);else if(this[cA].length===1)this[hR](this[cA].pop());else{let e=this[cA];this[cA]=[],Bq(this[ni],e,this[dm],(r,s)=>this[MI](r,s))}}[Vu](){if(this[jI]&&typeof this[ni]=="number"){let e=this[ni];this[ni]=null,Al.close(e,r=>r?this.emit("error",r):this.emit("close"))}}},bq=class extends yR{[L0](){let e;if(this[gR]&&this[O0]==="r+")try{e=Al.openSync(this[Kp],this[O0],this[dR])}catch(r){if(r.code==="ENOENT")return this[O0]="w",this[L0]();throw r}else e=Al.openSync(this[Kp],this[O0],this[dR]);this[qI](null,e)}[Vu](){if(this[jI]&&typeof this[ni]=="number"){let e=this[ni];this[ni]=null,Al.closeSync(e),this.emit("close")}}[hR](e){let r=!0;try{this[MI](null,Al.writeSync(this[ni],e,0,e.length,this[dm])),r=!1}finally{if(r)try{this[Vu]()}catch{}}}};qv.ReadStream=mR;qv.ReadStreamSync=Dq;qv.WriteStream=yR;qv.WriteStreamSync=bq});var SR=L((ver,CEe)=>{"use strict";var LEt=ZT(),MEt=FI(),_Et=ye("events"),UEt=pk(),HEt=1024*1024,jEt=KT(),gEe=zT(),qEt=q6(),Pq=Buffer.from([31,139]),_c=Symbol("state"),mm=Symbol("writeEntry"),Jp=Symbol("readEntry"),xq=Symbol("nextEntry"),dEe=Symbol("processEntry"),Uc=Symbol("extendedHeader"),Gv=Symbol("globalExtendedHeader"),M0=Symbol("meta"),mEe=Symbol("emitMeta"),bi=Symbol("buffer"),zp=Symbol("queue"),ym=Symbol("ended"),yEe=Symbol("emittedEnd"),Em=Symbol("emit"),pl=Symbol("unzip"),ER=Symbol("consumeChunk"),IR=Symbol("consumeChunkSub"),kq=Symbol("consumeBody"),EEe=Symbol("consumeMeta"),IEe=Symbol("consumeHeader"),CR=Symbol("consuming"),Qq=Symbol("bufferConcat"),Tq=Symbol("maybeEnd"),Wv=Symbol("writing"),_0=Symbol("aborted"),wR=Symbol("onDone"),Im=Symbol("sawValidEntry"),BR=Symbol("sawNullBlock"),vR=Symbol("sawEOF"),GEt=t=>!0;CEe.exports=LEt(class extends _Et{constructor(e){e=e||{},super(e),this.file=e.file||"",this[Im]=null,this.on(wR,r=>{(this[_c]==="begin"||this[Im]===!1)&&this.warn("TAR_BAD_ARCHIVE","Unrecognized archive format")}),e.ondone?this.on(wR,e.ondone):this.on(wR,r=>{this.emit("prefinish"),this.emit("finish"),this.emit("end"),this.emit("close")}),this.strict=!!e.strict,this.maxMetaEntrySize=e.maxMetaEntrySize||HEt,this.filter=typeof e.filter=="function"?e.filter:GEt,this.writable=!0,this.readable=!1,this[zp]=new UEt,this[bi]=null,this[Jp]=null,this[mm]=null,this[_c]="begin",this[M0]="",this[Uc]=null,this[Gv]=null,this[ym]=!1,this[pl]=null,this[_0]=!1,this[BR]=!1,this[vR]=!1,typeof e.onwarn=="function"&&this.on("warn",e.onwarn),typeof e.onentry=="function"&&this.on("entry",e.onentry)}[IEe](e,r){this[Im]===null&&(this[Im]=!1);let s;try{s=new MEt(e,r,this[Uc],this[Gv])}catch(a){return this.warn("TAR_ENTRY_INVALID",a)}if(s.nullBlock)this[BR]?(this[vR]=!0,this[_c]==="begin"&&(this[_c]="header"),this[Em]("eof")):(this[BR]=!0,this[Em]("nullBlock"));else if(this[BR]=!1,!s.cksumValid)this.warn("TAR_ENTRY_INVALID","checksum failure",{header:s});else if(!s.path)this.warn("TAR_ENTRY_INVALID","path is required",{header:s});else{let a=s.type;if(/^(Symbolic)?Link$/.test(a)&&!s.linkpath)this.warn("TAR_ENTRY_INVALID","linkpath required",{header:s});else if(!/^(Symbolic)?Link$/.test(a)&&s.linkpath)this.warn("TAR_ENTRY_INVALID","linkpath forbidden",{header:s});else{let n=this[mm]=new jEt(s,this[Uc],this[Gv]);if(!this[Im])if(n.remain){let c=()=>{n.invalid||(this[Im]=!0)};n.on("end",c)}else this[Im]=!0;n.meta?n.size>this.maxMetaEntrySize?(n.ignore=!0,this[Em]("ignoredEntry",n),this[_c]="ignore",n.resume()):n.size>0&&(this[M0]="",n.on("data",c=>this[M0]+=c),this[_c]="meta"):(this[Uc]=null,n.ignore=n.ignore||!this.filter(n.path,n),n.ignore?(this[Em]("ignoredEntry",n),this[_c]=n.remain?"ignore":"header",n.resume()):(n.remain?this[_c]="body":(this[_c]="header",n.end()),this[Jp]?this[zp].push(n):(this[zp].push(n),this[xq]())))}}}[dEe](e){let r=!0;return e?Array.isArray(e)?this.emit.apply(this,e):(this[Jp]=e,this.emit("entry",e),e.emittedEnd||(e.on("end",s=>this[xq]()),r=!1)):(this[Jp]=null,r=!1),r}[xq](){do;while(this[dEe](this[zp].shift()));if(!this[zp].length){let e=this[Jp];!e||e.flowing||e.size===e.remain?this[Wv]||this.emit("drain"):e.once("drain",s=>this.emit("drain"))}}[kq](e,r){let s=this[mm],a=s.blockRemain,n=a>=e.length&&r===0?e:e.slice(r,r+a);return s.write(n),s.blockRemain||(this[_c]="header",this[mm]=null,s.end()),n.length}[EEe](e,r){let s=this[mm],a=this[kq](e,r);return this[mm]||this[mEe](s),a}[Em](e,r,s){!this[zp].length&&!this[Jp]?this.emit(e,r,s):this[zp].push([e,r,s])}[mEe](e){switch(this[Em]("meta",this[M0]),e.type){case"ExtendedHeader":case"OldExtendedHeader":this[Uc]=gEe.parse(this[M0],this[Uc],!1);break;case"GlobalExtendedHeader":this[Gv]=gEe.parse(this[M0],this[Gv],!0);break;case"NextFileHasLongPath":case"OldGnuLongPath":this[Uc]=this[Uc]||Object.create(null),this[Uc].path=this[M0].replace(/\0.*/,"");break;case"NextFileHasLongLinkpath":this[Uc]=this[Uc]||Object.create(null),this[Uc].linkpath=this[M0].replace(/\0.*/,"");break;default:throw new Error("unknown meta: "+e.type)}}abort(e){this[_0]=!0,this.emit("abort",e),this.warn("TAR_ABORT",e,{recoverable:!1})}write(e){if(this[_0])return;if(this[pl]===null&&e){if(this[bi]&&(e=Buffer.concat([this[bi],e]),this[bi]=null),e.lengththis[ER](n)),this[pl].on("error",n=>this.abort(n)),this[pl].on("end",n=>{this[ym]=!0,this[ER]()}),this[Wv]=!0;let a=this[pl][s?"end":"write"](e);return this[Wv]=!1,a}}this[Wv]=!0,this[pl]?this[pl].write(e):this[ER](e),this[Wv]=!1;let r=this[zp].length?!1:this[Jp]?this[Jp].flowing:!0;return!r&&!this[zp].length&&this[Jp].once("drain",s=>this.emit("drain")),r}[Qq](e){e&&!this[_0]&&(this[bi]=this[bi]?Buffer.concat([this[bi],e]):e)}[Tq](){if(this[ym]&&!this[yEe]&&!this[_0]&&!this[CR]){this[yEe]=!0;let e=this[mm];if(e&&e.blockRemain){let r=this[bi]?this[bi].length:0;this.warn("TAR_BAD_ARCHIVE",`Truncated input (needed ${e.blockRemain} more bytes, only ${r} available)`,{entry:e}),this[bi]&&e.write(this[bi]),e.end()}this[Em](wR)}}[ER](e){if(this[CR])this[Qq](e);else if(!e&&!this[bi])this[Tq]();else{if(this[CR]=!0,this[bi]){this[Qq](e);let r=this[bi];this[bi]=null,this[IR](r)}else this[IR](e);for(;this[bi]&&this[bi].length>=512&&!this[_0]&&!this[vR];){let r=this[bi];this[bi]=null,this[IR](r)}this[CR]=!1}(!this[bi]||this[ym])&&this[Tq]()}[IR](e){let r=0,s=e.length;for(;r+512<=s&&!this[_0]&&!this[vR];)switch(this[_c]){case"begin":case"header":this[IEe](e,r),r+=512;break;case"ignore":case"body":r+=this[kq](e,r);break;case"meta":r+=this[EEe](e,r);break;default:throw new Error("invalid state: "+this[_c])}r{"use strict";var WEt=bI(),BEe=SR(),WI=ye("fs"),YEt=GI(),wEe=ye("path"),Rq=NI();SEe.exports=(t,e,r)=>{typeof t=="function"?(r=t,e=null,t={}):Array.isArray(t)&&(e=t,t={}),typeof e=="function"&&(r=e,e=null),e?e=Array.from(e):e=[];let s=WEt(t);if(s.sync&&typeof r=="function")throw new TypeError("callback not supported for sync tar functions");if(!s.file&&typeof r=="function")throw new TypeError("callback only supported with file option");return e.length&&KEt(s,e),s.noResume||VEt(s),s.file&&s.sync?JEt(s):s.file?zEt(s,r):vEe(s)};var VEt=t=>{let e=t.onentry;t.onentry=e?r=>{e(r),r.resume()}:r=>r.resume()},KEt=(t,e)=>{let r=new Map(e.map(n=>[Rq(n),!0])),s=t.filter,a=(n,c)=>{let f=c||wEe.parse(n).root||".",p=n===f?!1:r.has(n)?r.get(n):a(wEe.dirname(n),f);return r.set(n,p),p};t.filter=s?(n,c)=>s(n,c)&&a(Rq(n)):n=>a(Rq(n))},JEt=t=>{let e=vEe(t),r=t.file,s=!0,a;try{let n=WI.statSync(r),c=t.maxReadSize||16*1024*1024;if(n.size{let r=new BEe(t),s=t.maxReadSize||16*1024*1024,a=t.file,n=new Promise((c,f)=>{r.on("error",f),r.on("end",c),WI.stat(a,(p,h)=>{if(p)f(p);else{let E=new YEt.ReadStream(a,{readSize:s,size:h.size});E.on("error",f),E.pipe(r)}})});return e?n.then(e,e):n},vEe=t=>new BEe(t)});var QEe=L((Der,kEe)=>{"use strict";var ZEt=bI(),bR=fR(),DEe=GI(),bEe=DR(),PEe=ye("path");kEe.exports=(t,e,r)=>{if(typeof e=="function"&&(r=e),Array.isArray(t)&&(e=t,t={}),!e||!Array.isArray(e)||!e.length)throw new TypeError("no files or directories specified");e=Array.from(e);let s=ZEt(t);if(s.sync&&typeof r=="function")throw new TypeError("callback not supported for sync tar functions");if(!s.file&&typeof r=="function")throw new TypeError("callback only supported with file option");return s.file&&s.sync?XEt(s,e):s.file?$Et(s,e,r):s.sync?eIt(s,e):tIt(s,e)};var XEt=(t,e)=>{let r=new bR.Sync(t),s=new DEe.WriteStreamSync(t.file,{mode:t.mode||438});r.pipe(s),xEe(r,e)},$Et=(t,e,r)=>{let s=new bR(t),a=new DEe.WriteStream(t.file,{mode:t.mode||438});s.pipe(a);let n=new Promise((c,f)=>{a.on("error",f),a.on("close",c),s.on("error",f)});return Fq(s,e),r?n.then(r,r):n},xEe=(t,e)=>{e.forEach(r=>{r.charAt(0)==="@"?bEe({file:PEe.resolve(t.cwd,r.substr(1)),sync:!0,noResume:!0,onentry:s=>t.add(s)}):t.add(r)}),t.end()},Fq=(t,e)=>{for(;e.length;){let r=e.shift();if(r.charAt(0)==="@")return bEe({file:PEe.resolve(t.cwd,r.substr(1)),noResume:!0,onentry:s=>t.add(s)}).then(s=>Fq(t,e));t.add(r)}t.end()},eIt=(t,e)=>{let r=new bR.Sync(t);return xEe(r,e),r},tIt=(t,e)=>{let r=new bR(t);return Fq(r,e),r}});var Nq=L((ber,MEe)=>{"use strict";var rIt=bI(),TEe=fR(),Xl=ye("fs"),REe=GI(),FEe=DR(),NEe=ye("path"),OEe=FI();MEe.exports=(t,e,r)=>{let s=rIt(t);if(!s.file)throw new TypeError("file is required");if(s.gzip)throw new TypeError("cannot append to compressed archives");if(!e||!Array.isArray(e)||!e.length)throw new TypeError("no files or directories specified");return e=Array.from(e),s.sync?nIt(s,e):sIt(s,e,r)};var nIt=(t,e)=>{let r=new TEe.Sync(t),s=!0,a,n;try{try{a=Xl.openSync(t.file,"r+")}catch(p){if(p.code==="ENOENT")a=Xl.openSync(t.file,"w+");else throw p}let c=Xl.fstatSync(a),f=Buffer.alloc(512);e:for(n=0;nc.size)break;n+=h,t.mtimeCache&&t.mtimeCache.set(p.path,p.mtime)}s=!1,iIt(t,r,n,a,e)}finally{if(s)try{Xl.closeSync(a)}catch{}}},iIt=(t,e,r,s,a)=>{let n=new REe.WriteStreamSync(t.file,{fd:s,start:r});e.pipe(n),oIt(e,a)},sIt=(t,e,r)=>{e=Array.from(e);let s=new TEe(t),a=(c,f,p)=>{let h=(I,R)=>{I?Xl.close(c,N=>p(I)):p(null,R)},E=0;if(f===0)return h(null,0);let C=0,S=Buffer.alloc(512),P=(I,R)=>{if(I)return h(I);if(C+=R,C<512&&R)return Xl.read(c,S,C,S.length-C,E+C,P);if(E===0&&S[0]===31&&S[1]===139)return h(new Error("cannot append to compressed archives"));if(C<512)return h(null,E);let N=new OEe(S);if(!N.cksumValid)return h(null,E);let U=512*Math.ceil(N.size/512);if(E+U+512>f||(E+=U+512,E>=f))return h(null,E);t.mtimeCache&&t.mtimeCache.set(N.path,N.mtime),C=0,Xl.read(c,S,0,512,E,P)};Xl.read(c,S,0,512,E,P)},n=new Promise((c,f)=>{s.on("error",f);let p="r+",h=(E,C)=>{if(E&&E.code==="ENOENT"&&p==="r+")return p="w+",Xl.open(t.file,p,h);if(E)return f(E);Xl.fstat(C,(S,P)=>{if(S)return Xl.close(C,()=>f(S));a(C,P.size,(I,R)=>{if(I)return f(I);let N=new REe.WriteStream(t.file,{fd:C,start:R});s.pipe(N),N.on("error",f),N.on("close",c),LEe(s,e)})})};Xl.open(t.file,p,h)});return r?n.then(r,r):n},oIt=(t,e)=>{e.forEach(r=>{r.charAt(0)==="@"?FEe({file:NEe.resolve(t.cwd,r.substr(1)),sync:!0,noResume:!0,onentry:s=>t.add(s)}):t.add(r)}),t.end()},LEe=(t,e)=>{for(;e.length;){let r=e.shift();if(r.charAt(0)==="@")return FEe({file:NEe.resolve(t.cwd,r.substr(1)),noResume:!0,onentry:s=>t.add(s)}).then(s=>LEe(t,e));t.add(r)}t.end()}});var UEe=L((Per,_Ee)=>{"use strict";var aIt=bI(),lIt=Nq();_Ee.exports=(t,e,r)=>{let s=aIt(t);if(!s.file)throw new TypeError("file is required");if(s.gzip)throw new TypeError("cannot append to compressed archives");if(!e||!Array.isArray(e)||!e.length)throw new TypeError("no files or directories specified");return e=Array.from(e),cIt(s),lIt(s,e,r)};var cIt=t=>{let e=t.filter;t.mtimeCache||(t.mtimeCache=new Map),t.filter=e?(r,s)=>e(r,s)&&!(t.mtimeCache.get(r)>s.mtime):(r,s)=>!(t.mtimeCache.get(r)>s.mtime)}});var qEe=L((xer,jEe)=>{var{promisify:HEe}=ye("util"),U0=ye("fs"),uIt=t=>{if(!t)t={mode:511,fs:U0};else if(typeof t=="object")t={mode:511,fs:U0,...t};else if(typeof t=="number")t={mode:t,fs:U0};else if(typeof t=="string")t={mode:parseInt(t,8),fs:U0};else throw new TypeError("invalid options argument");return t.mkdir=t.mkdir||t.fs.mkdir||U0.mkdir,t.mkdirAsync=HEe(t.mkdir),t.stat=t.stat||t.fs.stat||U0.stat,t.statAsync=HEe(t.stat),t.statSync=t.statSync||t.fs.statSync||U0.statSync,t.mkdirSync=t.mkdirSync||t.fs.mkdirSync||U0.mkdirSync,t};jEe.exports=uIt});var WEe=L((ker,GEe)=>{var fIt=process.platform,{resolve:AIt,parse:pIt}=ye("path"),hIt=t=>{if(/\0/.test(t))throw Object.assign(new TypeError("path must be a string without null bytes"),{path:t,code:"ERR_INVALID_ARG_VALUE"});if(t=AIt(t),fIt==="win32"){let e=/[*|"<>?:]/,{root:r}=pIt(t);if(e.test(t.substr(r.length)))throw Object.assign(new Error("Illegal characters in path."),{path:t,code:"EINVAL"})}return t};GEe.exports=hIt});var zEe=L((Qer,JEe)=>{var{dirname:YEe}=ye("path"),VEe=(t,e,r=void 0)=>r===e?Promise.resolve():t.statAsync(e).then(s=>s.isDirectory()?r:void 0,s=>s.code==="ENOENT"?VEe(t,YEe(e),e):void 0),KEe=(t,e,r=void 0)=>{if(r!==e)try{return t.statSync(e).isDirectory()?r:void 0}catch(s){return s.code==="ENOENT"?KEe(t,YEe(e),e):void 0}};JEe.exports={findMade:VEe,findMadeSync:KEe}});var Mq=L((Ter,XEe)=>{var{dirname:ZEe}=ye("path"),Oq=(t,e,r)=>{e.recursive=!1;let s=ZEe(t);return s===t?e.mkdirAsync(t,e).catch(a=>{if(a.code!=="EISDIR")throw a}):e.mkdirAsync(t,e).then(()=>r||t,a=>{if(a.code==="ENOENT")return Oq(s,e).then(n=>Oq(t,e,n));if(a.code!=="EEXIST"&&a.code!=="EROFS")throw a;return e.statAsync(t).then(n=>{if(n.isDirectory())return r;throw a},()=>{throw a})})},Lq=(t,e,r)=>{let s=ZEe(t);if(e.recursive=!1,s===t)try{return e.mkdirSync(t,e)}catch(a){if(a.code!=="EISDIR")throw a;return}try{return e.mkdirSync(t,e),r||t}catch(a){if(a.code==="ENOENT")return Lq(t,e,Lq(s,e,r));if(a.code!=="EEXIST"&&a.code!=="EROFS")throw a;try{if(!e.statSync(t).isDirectory())throw a}catch{throw a}}};XEe.exports={mkdirpManual:Oq,mkdirpManualSync:Lq}});var tIe=L((Rer,eIe)=>{var{dirname:$Ee}=ye("path"),{findMade:gIt,findMadeSync:dIt}=zEe(),{mkdirpManual:mIt,mkdirpManualSync:yIt}=Mq(),EIt=(t,e)=>(e.recursive=!0,$Ee(t)===t?e.mkdirAsync(t,e):gIt(e,t).then(s=>e.mkdirAsync(t,e).then(()=>s).catch(a=>{if(a.code==="ENOENT")return mIt(t,e);throw a}))),IIt=(t,e)=>{if(e.recursive=!0,$Ee(t)===t)return e.mkdirSync(t,e);let s=dIt(e,t);try{return e.mkdirSync(t,e),s}catch(a){if(a.code==="ENOENT")return yIt(t,e);throw a}};eIe.exports={mkdirpNative:EIt,mkdirpNativeSync:IIt}});var sIe=L((Fer,iIe)=>{var rIe=ye("fs"),CIt=process.version,_q=CIt.replace(/^v/,"").split("."),nIe=+_q[0]>10||+_q[0]==10&&+_q[1]>=12,wIt=nIe?t=>t.mkdir===rIe.mkdir:()=>!1,BIt=nIe?t=>t.mkdirSync===rIe.mkdirSync:()=>!1;iIe.exports={useNative:wIt,useNativeSync:BIt}});var fIe=L((Ner,uIe)=>{var YI=qEe(),VI=WEe(),{mkdirpNative:oIe,mkdirpNativeSync:aIe}=tIe(),{mkdirpManual:lIe,mkdirpManualSync:cIe}=Mq(),{useNative:vIt,useNativeSync:SIt}=sIe(),KI=(t,e)=>(t=VI(t),e=YI(e),vIt(e)?oIe(t,e):lIe(t,e)),DIt=(t,e)=>(t=VI(t),e=YI(e),SIt(e)?aIe(t,e):cIe(t,e));KI.sync=DIt;KI.native=(t,e)=>oIe(VI(t),YI(e));KI.manual=(t,e)=>lIe(VI(t),YI(e));KI.nativeSync=(t,e)=>aIe(VI(t),YI(e));KI.manualSync=(t,e)=>cIe(VI(t),YI(e));uIe.exports=KI});var yIe=L((Oer,mIe)=>{"use strict";var Hc=ye("fs"),Cm=ye("path"),bIt=Hc.lchown?"lchown":"chown",PIt=Hc.lchownSync?"lchownSync":"chownSync",pIe=Hc.lchown&&!process.version.match(/v1[1-9]+\./)&&!process.version.match(/v10\.[6-9]/),AIe=(t,e,r)=>{try{return Hc[PIt](t,e,r)}catch(s){if(s.code!=="ENOENT")throw s}},xIt=(t,e,r)=>{try{return Hc.chownSync(t,e,r)}catch(s){if(s.code!=="ENOENT")throw s}},kIt=pIe?(t,e,r,s)=>a=>{!a||a.code!=="EISDIR"?s(a):Hc.chown(t,e,r,s)}:(t,e,r,s)=>s,Uq=pIe?(t,e,r)=>{try{return AIe(t,e,r)}catch(s){if(s.code!=="EISDIR")throw s;xIt(t,e,r)}}:(t,e,r)=>AIe(t,e,r),QIt=process.version,hIe=(t,e,r)=>Hc.readdir(t,e,r),TIt=(t,e)=>Hc.readdirSync(t,e);/^v4\./.test(QIt)&&(hIe=(t,e,r)=>Hc.readdir(t,r));var PR=(t,e,r,s)=>{Hc[bIt](t,e,r,kIt(t,e,r,a=>{s(a&&a.code!=="ENOENT"?a:null)}))},gIe=(t,e,r,s,a)=>{if(typeof e=="string")return Hc.lstat(Cm.resolve(t,e),(n,c)=>{if(n)return a(n.code!=="ENOENT"?n:null);c.name=e,gIe(t,c,r,s,a)});if(e.isDirectory())Hq(Cm.resolve(t,e.name),r,s,n=>{if(n)return a(n);let c=Cm.resolve(t,e.name);PR(c,r,s,a)});else{let n=Cm.resolve(t,e.name);PR(n,r,s,a)}},Hq=(t,e,r,s)=>{hIe(t,{withFileTypes:!0},(a,n)=>{if(a){if(a.code==="ENOENT")return s();if(a.code!=="ENOTDIR"&&a.code!=="ENOTSUP")return s(a)}if(a||!n.length)return PR(t,e,r,s);let c=n.length,f=null,p=h=>{if(!f){if(h)return s(f=h);if(--c===0)return PR(t,e,r,s)}};n.forEach(h=>gIe(t,h,e,r,p))})},RIt=(t,e,r,s)=>{if(typeof e=="string")try{let a=Hc.lstatSync(Cm.resolve(t,e));a.name=e,e=a}catch(a){if(a.code==="ENOENT")return;throw a}e.isDirectory()&&dIe(Cm.resolve(t,e.name),r,s),Uq(Cm.resolve(t,e.name),r,s)},dIe=(t,e,r)=>{let s;try{s=TIt(t,{withFileTypes:!0})}catch(a){if(a.code==="ENOENT")return;if(a.code==="ENOTDIR"||a.code==="ENOTSUP")return Uq(t,e,r);throw a}return s&&s.length&&s.forEach(a=>RIt(t,a,e,r)),Uq(t,e,r)};mIe.exports=Hq;Hq.sync=dIe});var wIe=L((Ler,jq)=>{"use strict";var EIe=fIe(),jc=ye("fs"),xR=ye("path"),IIe=yIe(),Ku=TI(),kR=class extends Error{constructor(e,r){super("Cannot extract through symbolic link"),this.path=r,this.symlink=e}get name(){return"SylinkError"}},QR=class extends Error{constructor(e,r){super(r+": Cannot cd into '"+e+"'"),this.path=e,this.code=r}get name(){return"CwdError"}},TR=(t,e)=>t.get(Ku(e)),Yv=(t,e,r)=>t.set(Ku(e),r),FIt=(t,e)=>{jc.stat(t,(r,s)=>{(r||!s.isDirectory())&&(r=new QR(t,r&&r.code||"ENOTDIR")),e(r)})};jq.exports=(t,e,r)=>{t=Ku(t);let s=e.umask,a=e.mode|448,n=(a&s)!==0,c=e.uid,f=e.gid,p=typeof c=="number"&&typeof f=="number"&&(c!==e.processUid||f!==e.processGid),h=e.preserve,E=e.unlink,C=e.cache,S=Ku(e.cwd),P=(N,U)=>{N?r(N):(Yv(C,t,!0),U&&p?IIe(U,c,f,W=>P(W)):n?jc.chmod(t,a,r):r())};if(C&&TR(C,t)===!0)return P();if(t===S)return FIt(t,P);if(h)return EIe(t,{mode:a}).then(N=>P(null,N),P);let R=Ku(xR.relative(S,t)).split("/");RR(S,R,a,C,E,S,null,P)};var RR=(t,e,r,s,a,n,c,f)=>{if(!e.length)return f(null,c);let p=e.shift(),h=Ku(xR.resolve(t+"/"+p));if(TR(s,h))return RR(h,e,r,s,a,n,c,f);jc.mkdir(h,r,CIe(h,e,r,s,a,n,c,f))},CIe=(t,e,r,s,a,n,c,f)=>p=>{p?jc.lstat(t,(h,E)=>{if(h)h.path=h.path&&Ku(h.path),f(h);else if(E.isDirectory())RR(t,e,r,s,a,n,c,f);else if(a)jc.unlink(t,C=>{if(C)return f(C);jc.mkdir(t,r,CIe(t,e,r,s,a,n,c,f))});else{if(E.isSymbolicLink())return f(new kR(t,t+"/"+e.join("/")));f(p)}}):(c=c||t,RR(t,e,r,s,a,n,c,f))},NIt=t=>{let e=!1,r="ENOTDIR";try{e=jc.statSync(t).isDirectory()}catch(s){r=s.code}finally{if(!e)throw new QR(t,r)}};jq.exports.sync=(t,e)=>{t=Ku(t);let r=e.umask,s=e.mode|448,a=(s&r)!==0,n=e.uid,c=e.gid,f=typeof n=="number"&&typeof c=="number"&&(n!==e.processUid||c!==e.processGid),p=e.preserve,h=e.unlink,E=e.cache,C=Ku(e.cwd),S=N=>{Yv(E,t,!0),N&&f&&IIe.sync(N,n,c),a&&jc.chmodSync(t,s)};if(E&&TR(E,t)===!0)return S();if(t===C)return NIt(C),S();if(p)return S(EIe.sync(t,s));let I=Ku(xR.relative(C,t)).split("/"),R=null;for(let N=I.shift(),U=C;N&&(U+="/"+N);N=I.shift())if(U=Ku(xR.resolve(U)),!TR(E,U))try{jc.mkdirSync(U,s),R=R||U,Yv(E,U,!0)}catch{let te=jc.lstatSync(U);if(te.isDirectory()){Yv(E,U,!0);continue}else if(h){jc.unlinkSync(U),jc.mkdirSync(U,s),R=R||U,Yv(E,U,!0);continue}else if(te.isSymbolicLink())return new kR(U,U+"/"+I.join("/"))}return S(R)}});var Gq=L((Mer,BIe)=>{var qq=Object.create(null),{hasOwnProperty:OIt}=Object.prototype;BIe.exports=t=>(OIt.call(qq,t)||(qq[t]=t.normalize("NFKD")),qq[t])});var bIe=L((_er,DIe)=>{var vIe=ye("assert"),LIt=Gq(),MIt=NI(),{join:SIe}=ye("path"),_It=process.env.TESTING_TAR_FAKE_PLATFORM||process.platform,UIt=_It==="win32";DIe.exports=()=>{let t=new Map,e=new Map,r=h=>h.split("/").slice(0,-1).reduce((C,S)=>(C.length&&(S=SIe(C[C.length-1],S)),C.push(S||"/"),C),[]),s=new Set,a=h=>{let E=e.get(h);if(!E)throw new Error("function does not have any path reservations");return{paths:E.paths.map(C=>t.get(C)),dirs:[...E.dirs].map(C=>t.get(C))}},n=h=>{let{paths:E,dirs:C}=a(h);return E.every(S=>S[0]===h)&&C.every(S=>S[0]instanceof Set&&S[0].has(h))},c=h=>s.has(h)||!n(h)?!1:(s.add(h),h(()=>f(h)),!0),f=h=>{if(!s.has(h))return!1;let{paths:E,dirs:C}=e.get(h),S=new Set;return E.forEach(P=>{let I=t.get(P);vIe.equal(I[0],h),I.length===1?t.delete(P):(I.shift(),typeof I[0]=="function"?S.add(I[0]):I[0].forEach(R=>S.add(R)))}),C.forEach(P=>{let I=t.get(P);vIe(I[0]instanceof Set),I[0].size===1&&I.length===1?t.delete(P):I[0].size===1?(I.shift(),S.add(I[0])):I[0].delete(h)}),s.delete(h),S.forEach(P=>c(P)),!0};return{check:n,reserve:(h,E)=>{h=UIt?["win32 parallelization disabled"]:h.map(S=>LIt(MIt(SIe(S))).toLowerCase());let C=new Set(h.map(S=>r(S)).reduce((S,P)=>S.concat(P)));return e.set(E,{dirs:C,paths:h}),h.forEach(S=>{let P=t.get(S);P?P.push(E):t.set(S,[E])}),C.forEach(S=>{let P=t.get(S);P?P[P.length-1]instanceof Set?P[P.length-1].add(E):P.push(new Set([E])):t.set(S,[new Set([E])])}),c(E)}}}});var kIe=L((Uer,xIe)=>{var HIt=process.platform,jIt=HIt==="win32",qIt=global.__FAKE_TESTING_FS__||ye("fs"),{O_CREAT:GIt,O_TRUNC:WIt,O_WRONLY:YIt,UV_FS_O_FILEMAP:PIe=0}=qIt.constants,VIt=jIt&&!!PIe,KIt=512*1024,JIt=PIe|WIt|GIt|YIt;xIe.exports=VIt?t=>t"w"});var $q=L((Her,GIe)=>{"use strict";var zIt=ye("assert"),ZIt=SR(),Mn=ye("fs"),XIt=GI(),Zp=ye("path"),HIe=wIe(),QIe=$6(),$It=bIe(),eCt=eq(),$l=TI(),tCt=NI(),rCt=Gq(),TIe=Symbol("onEntry"),Vq=Symbol("checkFs"),RIe=Symbol("checkFs2"),OR=Symbol("pruneCache"),Kq=Symbol("isReusable"),qc=Symbol("makeFs"),Jq=Symbol("file"),zq=Symbol("directory"),LR=Symbol("link"),FIe=Symbol("symlink"),NIe=Symbol("hardlink"),OIe=Symbol("unsupported"),LIe=Symbol("checkPath"),H0=Symbol("mkdir"),Xo=Symbol("onError"),FR=Symbol("pending"),MIe=Symbol("pend"),JI=Symbol("unpend"),Wq=Symbol("ended"),Yq=Symbol("maybeClose"),Zq=Symbol("skip"),Vv=Symbol("doChown"),Kv=Symbol("uid"),Jv=Symbol("gid"),zv=Symbol("checkedCwd"),jIe=ye("crypto"),qIe=kIe(),nCt=process.env.TESTING_TAR_FAKE_PLATFORM||process.platform,Zv=nCt==="win32",iCt=(t,e)=>{if(!Zv)return Mn.unlink(t,e);let r=t+".DELETE."+jIe.randomBytes(16).toString("hex");Mn.rename(t,r,s=>{if(s)return e(s);Mn.unlink(r,e)})},sCt=t=>{if(!Zv)return Mn.unlinkSync(t);let e=t+".DELETE."+jIe.randomBytes(16).toString("hex");Mn.renameSync(t,e),Mn.unlinkSync(e)},_Ie=(t,e,r)=>t===t>>>0?t:e===e>>>0?e:r,UIe=t=>rCt(tCt($l(t))).toLowerCase(),oCt=(t,e)=>{e=UIe(e);for(let r of t.keys()){let s=UIe(r);(s===e||s.indexOf(e+"/")===0)&&t.delete(r)}},aCt=t=>{for(let e of t.keys())t.delete(e)},Xv=class extends ZIt{constructor(e){if(e||(e={}),e.ondone=r=>{this[Wq]=!0,this[Yq]()},super(e),this[zv]=!1,this.reservations=$It(),this.transform=typeof e.transform=="function"?e.transform:null,this.writable=!0,this.readable=!1,this[FR]=0,this[Wq]=!1,this.dirCache=e.dirCache||new Map,typeof e.uid=="number"||typeof e.gid=="number"){if(typeof e.uid!="number"||typeof e.gid!="number")throw new TypeError("cannot set owner without number uid and gid");if(e.preserveOwner)throw new TypeError("cannot preserve owner in archive and also set owner explicitly");this.uid=e.uid,this.gid=e.gid,this.setOwner=!0}else this.uid=null,this.gid=null,this.setOwner=!1;e.preserveOwner===void 0&&typeof e.uid!="number"?this.preserveOwner=process.getuid&&process.getuid()===0:this.preserveOwner=!!e.preserveOwner,this.processUid=(this.preserveOwner||this.setOwner)&&process.getuid?process.getuid():null,this.processGid=(this.preserveOwner||this.setOwner)&&process.getgid?process.getgid():null,this.forceChown=e.forceChown===!0,this.win32=!!e.win32||Zv,this.newer=!!e.newer,this.keep=!!e.keep,this.noMtime=!!e.noMtime,this.preservePaths=!!e.preservePaths,this.unlink=!!e.unlink,this.cwd=$l(Zp.resolve(e.cwd||process.cwd())),this.strip=+e.strip||0,this.processUmask=e.noChmod?0:process.umask(),this.umask=typeof e.umask=="number"?e.umask:this.processUmask,this.dmode=e.dmode||511&~this.umask,this.fmode=e.fmode||438&~this.umask,this.on("entry",r=>this[TIe](r))}warn(e,r,s={}){return(e==="TAR_BAD_ARCHIVE"||e==="TAR_ABORT")&&(s.recoverable=!1),super.warn(e,r,s)}[Yq](){this[Wq]&&this[FR]===0&&(this.emit("prefinish"),this.emit("finish"),this.emit("end"),this.emit("close"))}[LIe](e){if(this.strip){let r=$l(e.path).split("/");if(r.length=this.strip)e.linkpath=s.slice(this.strip).join("/");else return!1}}if(!this.preservePaths){let r=$l(e.path),s=r.split("/");if(s.includes("..")||Zv&&/^[a-z]:\.\.$/i.test(s[0]))return this.warn("TAR_ENTRY_ERROR","path contains '..'",{entry:e,path:r}),!1;let[a,n]=eCt(r);a&&(e.path=n,this.warn("TAR_ENTRY_INFO",`stripping ${a} from absolute path`,{entry:e,path:r}))}if(Zp.isAbsolute(e.path)?e.absolute=$l(Zp.resolve(e.path)):e.absolute=$l(Zp.resolve(this.cwd,e.path)),!this.preservePaths&&e.absolute.indexOf(this.cwd+"/")!==0&&e.absolute!==this.cwd)return this.warn("TAR_ENTRY_ERROR","path escaped extraction target",{entry:e,path:$l(e.path),resolvedPath:e.absolute,cwd:this.cwd}),!1;if(e.absolute===this.cwd&&e.type!=="Directory"&&e.type!=="GNUDumpDir")return!1;if(this.win32){let{root:r}=Zp.win32.parse(e.absolute);e.absolute=r+QIe.encode(e.absolute.substr(r.length));let{root:s}=Zp.win32.parse(e.path);e.path=s+QIe.encode(e.path.substr(s.length))}return!0}[TIe](e){if(!this[LIe](e))return e.resume();switch(zIt.equal(typeof e.absolute,"string"),e.type){case"Directory":case"GNUDumpDir":e.mode&&(e.mode=e.mode|448);case"File":case"OldFile":case"ContiguousFile":case"Link":case"SymbolicLink":return this[Vq](e);case"CharacterDevice":case"BlockDevice":case"FIFO":default:return this[OIe](e)}}[Xo](e,r){e.name==="CwdError"?this.emit("error",e):(this.warn("TAR_ENTRY_ERROR",e,{entry:r}),this[JI](),r.resume())}[H0](e,r,s){HIe($l(e),{uid:this.uid,gid:this.gid,processUid:this.processUid,processGid:this.processGid,umask:this.processUmask,preserve:this.preservePaths,unlink:this.unlink,cache:this.dirCache,cwd:this.cwd,mode:r,noChmod:this.noChmod},s)}[Vv](e){return this.forceChown||this.preserveOwner&&(typeof e.uid=="number"&&e.uid!==this.processUid||typeof e.gid=="number"&&e.gid!==this.processGid)||typeof this.uid=="number"&&this.uid!==this.processUid||typeof this.gid=="number"&&this.gid!==this.processGid}[Kv](e){return _Ie(this.uid,e.uid,this.processUid)}[Jv](e){return _Ie(this.gid,e.gid,this.processGid)}[Jq](e,r){let s=e.mode&4095||this.fmode,a=new XIt.WriteStream(e.absolute,{flags:qIe(e.size),mode:s,autoClose:!1});a.on("error",p=>{a.fd&&Mn.close(a.fd,()=>{}),a.write=()=>!0,this[Xo](p,e),r()});let n=1,c=p=>{if(p){a.fd&&Mn.close(a.fd,()=>{}),this[Xo](p,e),r();return}--n===0&&Mn.close(a.fd,h=>{h?this[Xo](h,e):this[JI](),r()})};a.on("finish",p=>{let h=e.absolute,E=a.fd;if(e.mtime&&!this.noMtime){n++;let C=e.atime||new Date,S=e.mtime;Mn.futimes(E,C,S,P=>P?Mn.utimes(h,C,S,I=>c(I&&P)):c())}if(this[Vv](e)){n++;let C=this[Kv](e),S=this[Jv](e);Mn.fchown(E,C,S,P=>P?Mn.chown(h,C,S,I=>c(I&&P)):c())}c()});let f=this.transform&&this.transform(e)||e;f!==e&&(f.on("error",p=>{this[Xo](p,e),r()}),e.pipe(f)),f.pipe(a)}[zq](e,r){let s=e.mode&4095||this.dmode;this[H0](e.absolute,s,a=>{if(a){this[Xo](a,e),r();return}let n=1,c=f=>{--n===0&&(r(),this[JI](),e.resume())};e.mtime&&!this.noMtime&&(n++,Mn.utimes(e.absolute,e.atime||new Date,e.mtime,c)),this[Vv](e)&&(n++,Mn.chown(e.absolute,this[Kv](e),this[Jv](e),c)),c()})}[OIe](e){e.unsupported=!0,this.warn("TAR_ENTRY_UNSUPPORTED",`unsupported entry type: ${e.type}`,{entry:e}),e.resume()}[FIe](e,r){this[LR](e,e.linkpath,"symlink",r)}[NIe](e,r){let s=$l(Zp.resolve(this.cwd,e.linkpath));this[LR](e,s,"link",r)}[MIe](){this[FR]++}[JI](){this[FR]--,this[Yq]()}[Zq](e){this[JI](),e.resume()}[Kq](e,r){return e.type==="File"&&!this.unlink&&r.isFile()&&r.nlink<=1&&!Zv}[Vq](e){this[MIe]();let r=[e.path];e.linkpath&&r.push(e.linkpath),this.reservations.reserve(r,s=>this[RIe](e,s))}[OR](e){e.type==="SymbolicLink"?aCt(this.dirCache):e.type!=="Directory"&&oCt(this.dirCache,e.absolute)}[RIe](e,r){this[OR](e);let s=f=>{this[OR](e),r(f)},a=()=>{this[H0](this.cwd,this.dmode,f=>{if(f){this[Xo](f,e),s();return}this[zv]=!0,n()})},n=()=>{if(e.absolute!==this.cwd){let f=$l(Zp.dirname(e.absolute));if(f!==this.cwd)return this[H0](f,this.dmode,p=>{if(p){this[Xo](p,e),s();return}c()})}c()},c=()=>{Mn.lstat(e.absolute,(f,p)=>{if(p&&(this.keep||this.newer&&p.mtime>e.mtime)){this[Zq](e),s();return}if(f||this[Kq](e,p))return this[qc](null,e,s);if(p.isDirectory()){if(e.type==="Directory"){let h=!this.noChmod&&e.mode&&(p.mode&4095)!==e.mode,E=C=>this[qc](C,e,s);return h?Mn.chmod(e.absolute,e.mode,E):E()}if(e.absolute!==this.cwd)return Mn.rmdir(e.absolute,h=>this[qc](h,e,s))}if(e.absolute===this.cwd)return this[qc](null,e,s);iCt(e.absolute,h=>this[qc](h,e,s))})};this[zv]?n():a()}[qc](e,r,s){if(e){this[Xo](e,r),s();return}switch(r.type){case"File":case"OldFile":case"ContiguousFile":return this[Jq](r,s);case"Link":return this[NIe](r,s);case"SymbolicLink":return this[FIe](r,s);case"Directory":case"GNUDumpDir":return this[zq](r,s)}}[LR](e,r,s,a){Mn[s](r,e.absolute,n=>{n?this[Xo](n,e):(this[JI](),e.resume()),a()})}},NR=t=>{try{return[null,t()]}catch(e){return[e,null]}},Xq=class extends Xv{[qc](e,r){return super[qc](e,r,()=>{})}[Vq](e){if(this[OR](e),!this[zv]){let n=this[H0](this.cwd,this.dmode);if(n)return this[Xo](n,e);this[zv]=!0}if(e.absolute!==this.cwd){let n=$l(Zp.dirname(e.absolute));if(n!==this.cwd){let c=this[H0](n,this.dmode);if(c)return this[Xo](c,e)}}let[r,s]=NR(()=>Mn.lstatSync(e.absolute));if(s&&(this.keep||this.newer&&s.mtime>e.mtime))return this[Zq](e);if(r||this[Kq](e,s))return this[qc](null,e);if(s.isDirectory()){if(e.type==="Directory"){let c=!this.noChmod&&e.mode&&(s.mode&4095)!==e.mode,[f]=c?NR(()=>{Mn.chmodSync(e.absolute,e.mode)}):[];return this[qc](f,e)}let[n]=NR(()=>Mn.rmdirSync(e.absolute));this[qc](n,e)}let[a]=e.absolute===this.cwd?[]:NR(()=>sCt(e.absolute));this[qc](a,e)}[Jq](e,r){let s=e.mode&4095||this.fmode,a=f=>{let p;try{Mn.closeSync(n)}catch(h){p=h}(f||p)&&this[Xo](f||p,e),r()},n;try{n=Mn.openSync(e.absolute,qIe(e.size),s)}catch(f){return a(f)}let c=this.transform&&this.transform(e)||e;c!==e&&(c.on("error",f=>this[Xo](f,e)),e.pipe(c)),c.on("data",f=>{try{Mn.writeSync(n,f,0,f.length)}catch(p){a(p)}}),c.on("end",f=>{let p=null;if(e.mtime&&!this.noMtime){let h=e.atime||new Date,E=e.mtime;try{Mn.futimesSync(n,h,E)}catch(C){try{Mn.utimesSync(e.absolute,h,E)}catch{p=C}}}if(this[Vv](e)){let h=this[Kv](e),E=this[Jv](e);try{Mn.fchownSync(n,h,E)}catch(C){try{Mn.chownSync(e.absolute,h,E)}catch{p=p||C}}}a(p)})}[zq](e,r){let s=e.mode&4095||this.dmode,a=this[H0](e.absolute,s);if(a){this[Xo](a,e),r();return}if(e.mtime&&!this.noMtime)try{Mn.utimesSync(e.absolute,e.atime||new Date,e.mtime)}catch{}if(this[Vv](e))try{Mn.chownSync(e.absolute,this[Kv](e),this[Jv](e))}catch{}r(),e.resume()}[H0](e,r){try{return HIe.sync($l(e),{uid:this.uid,gid:this.gid,processUid:this.processUid,processGid:this.processGid,umask:this.processUmask,preserve:this.preservePaths,unlink:this.unlink,cache:this.dirCache,cwd:this.cwd,mode:r})}catch(s){return s}}[LR](e,r,s,a){try{Mn[s+"Sync"](r,e.absolute),a(),e.resume()}catch(n){return this[Xo](n,e)}}};Xv.Sync=Xq;GIe.exports=Xv});var JIe=L((jer,KIe)=>{"use strict";var lCt=bI(),MR=$q(),YIe=ye("fs"),VIe=GI(),WIe=ye("path"),eG=NI();KIe.exports=(t,e,r)=>{typeof t=="function"?(r=t,e=null,t={}):Array.isArray(t)&&(e=t,t={}),typeof e=="function"&&(r=e,e=null),e?e=Array.from(e):e=[];let s=lCt(t);if(s.sync&&typeof r=="function")throw new TypeError("callback not supported for sync tar functions");if(!s.file&&typeof r=="function")throw new TypeError("callback only supported with file option");return e.length&&cCt(s,e),s.file&&s.sync?uCt(s):s.file?fCt(s,r):s.sync?ACt(s):pCt(s)};var cCt=(t,e)=>{let r=new Map(e.map(n=>[eG(n),!0])),s=t.filter,a=(n,c)=>{let f=c||WIe.parse(n).root||".",p=n===f?!1:r.has(n)?r.get(n):a(WIe.dirname(n),f);return r.set(n,p),p};t.filter=s?(n,c)=>s(n,c)&&a(eG(n)):n=>a(eG(n))},uCt=t=>{let e=new MR.Sync(t),r=t.file,s=YIe.statSync(r),a=t.maxReadSize||16*1024*1024;new VIe.ReadStreamSync(r,{readSize:a,size:s.size}).pipe(e)},fCt=(t,e)=>{let r=new MR(t),s=t.maxReadSize||16*1024*1024,a=t.file,n=new Promise((c,f)=>{r.on("error",f),r.on("close",c),YIe.stat(a,(p,h)=>{if(p)f(p);else{let E=new VIe.ReadStream(a,{readSize:s,size:h.size});E.on("error",f),E.pipe(r)}})});return e?n.then(e,e):n},ACt=t=>new MR.Sync(t),pCt=t=>new MR(t)});var zIe=L(xs=>{"use strict";xs.c=xs.create=QEe();xs.r=xs.replace=Nq();xs.t=xs.list=DR();xs.u=xs.update=UEe();xs.x=xs.extract=JIe();xs.Pack=fR();xs.Unpack=$q();xs.Parse=SR();xs.ReadEntry=KT();xs.WriteEntry=uq();xs.Header=FI();xs.Pax=zT();xs.types=Y6()});var tG,ZIe,j0,$v,eS,XIe=It(()=>{tG=et(Md()),ZIe=ye("worker_threads"),j0=Symbol("kTaskInfo"),$v=class{constructor(e,r){this.fn=e;this.limit=(0,tG.default)(r.poolSize)}run(e){return this.limit(()=>this.fn(e))}},eS=class{constructor(e,r){this.source=e;this.workers=[];this.limit=(0,tG.default)(r.poolSize),this.cleanupInterval=setInterval(()=>{if(this.limit.pendingCount===0&&this.limit.activeCount===0){let s=this.workers.pop();s?s.terminate():clearInterval(this.cleanupInterval)}},5e3).unref()}createWorker(){this.cleanupInterval.refresh();let e=new ZIe.Worker(this.source,{eval:!0,execArgv:[...process.execArgv,"--unhandled-rejections=strict"]});return e.on("message",r=>{if(!e[j0])throw new Error("Assertion failed: Worker sent a result without having a task assigned");e[j0].resolve(r),e[j0]=null,e.unref(),this.workers.push(e)}),e.on("error",r=>{e[j0]?.reject(r),e[j0]=null}),e.on("exit",r=>{r!==0&&e[j0]?.reject(new Error(`Worker exited with code ${r}`)),e[j0]=null}),e}run(e){return this.limit(()=>{let r=this.workers.pop()??this.createWorker();return r.ref(),new Promise((s,a)=>{r[j0]={resolve:s,reject:a},r.postMessage(e)})})}}});var eCe=L((Yer,$Ie)=>{var rG;$Ie.exports.getContent=()=>(typeof rG>"u"&&(rG=ye("zlib").brotliDecompressSync(Buffer.from("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","base64")).toString()),rG)});var hs={};Vt(hs,{convertToZip:()=>mCt,convertToZipWorker:()=>sG,extractArchiveTo:()=>sCe,getDefaultTaskPool:()=>nCe,getTaskPoolForConfiguration:()=>iCe,makeArchiveFromDirectory:()=>dCt});function hCt(t,e){switch(t){case"async":return new $v(sG,{poolSize:e});case"workers":return new eS((0,iG.getContent)(),{poolSize:e});default:throw new Error(`Assertion failed: Unknown value ${t} for taskPoolMode`)}}function nCe(){return typeof nG>"u"&&(nG=hCt("workers",As.availableParallelism())),nG}function iCe(t){return typeof t>"u"?nCe():Vl(gCt,t,()=>{let e=t.get("taskPoolMode"),r=t.get("taskPoolConcurrency");switch(e){case"async":return new $v(sG,{poolSize:r});case"workers":return new eS((0,iG.getContent)(),{poolSize:r});default:throw new Error(`Assertion failed: Unknown value ${e} for taskPoolMode`)}})}async function sG(t){let{tmpFile:e,tgz:r,compressionLevel:s,extractBufferOpts:a}=t,n=new ps(e,{create:!0,level:s,stats:el.makeDefaultStats()}),c=Buffer.from(r.buffer,r.byteOffset,r.byteLength);return await sCe(c,n,a),n.saveAndClose(),e}async function dCt(t,{baseFs:e=new Yn,prefixPath:r=vt.root,compressionLevel:s,inMemory:a=!1}={}){let n;if(a)n=new ps(null,{level:s});else{let f=await le.mktempPromise(),p=K.join(f,"archive.zip");n=new ps(p,{create:!0,level:s})}let c=K.resolve(vt.root,r);return await n.copyPromise(c,t,{baseFs:e,stableTime:!0,stableSort:!0}),n}async function mCt(t,e={}){let r=await le.mktempPromise(),s=K.join(r,"archive.zip"),a=e.compressionLevel??e.configuration?.get("compressionLevel")??"mixed",n={prefixPath:e.prefixPath,stripComponents:e.stripComponents};return await(e.taskPool??iCe(e.configuration)).run({tmpFile:s,tgz:t,compressionLevel:a,extractBufferOpts:n}),new ps(s,{level:e.compressionLevel})}async function*yCt(t){let e=new rCe.default.Parse,r=new tCe.PassThrough({objectMode:!0,autoDestroy:!0,emitClose:!0});e.on("entry",s=>{r.write(s)}),e.on("error",s=>{r.destroy(s)}),e.on("close",()=>{r.destroyed||r.end()}),e.end(t);for await(let s of r){let a=s;yield a,a.resume()}}async function sCe(t,e,{stripComponents:r=0,prefixPath:s=vt.dot}={}){function a(n){if(n.path[0]==="/")return!0;let c=n.path.split(/\//g);return!!(c.some(f=>f==="..")||c.length<=r)}for await(let n of yCt(t)){if(a(n))continue;let c=K.normalize(ue.toPortablePath(n.path)).replace(/\/$/,"").split(/\//g);if(c.length<=r)continue;let f=c.slice(r).join("/"),p=K.join(s,f),h=420;switch((n.type==="Directory"||(n.mode??0)&73)&&(h|=73),n.type){case"Directory":e.mkdirpSync(K.dirname(p),{chmod:493,utimes:[ui.SAFE_TIME,ui.SAFE_TIME]}),e.mkdirSync(p,{mode:h}),e.utimesSync(p,ui.SAFE_TIME,ui.SAFE_TIME);break;case"OldFile":case"File":e.mkdirpSync(K.dirname(p),{chmod:493,utimes:[ui.SAFE_TIME,ui.SAFE_TIME]}),e.writeFileSync(p,await WE(n),{mode:h}),e.utimesSync(p,ui.SAFE_TIME,ui.SAFE_TIME);break;case"SymbolicLink":e.mkdirpSync(K.dirname(p),{chmod:493,utimes:[ui.SAFE_TIME,ui.SAFE_TIME]}),e.symlinkSync(n.linkpath,p),e.lutimesSync(p,ui.SAFE_TIME,ui.SAFE_TIME);break}}return e}var tCe,rCe,iG,nG,gCt,oCe=It(()=>{Ve();bt();rA();tCe=ye("stream"),rCe=et(zIe());XIe();kc();iG=et(eCe());gCt=new WeakMap});var lCe=L((oG,aCe)=>{(function(t,e){typeof oG=="object"?aCe.exports=e():typeof define=="function"&&define.amd?define(e):t.treeify=e()})(oG,function(){function t(a,n){var c=n?"\u2514":"\u251C";return a?c+="\u2500 ":c+="\u2500\u2500\u2510",c}function e(a,n){var c=[];for(var f in a)a.hasOwnProperty(f)&&(n&&typeof a[f]=="function"||c.push(f));return c}function r(a,n,c,f,p,h,E){var C="",S=0,P,I,R=f.slice(0);if(R.push([n,c])&&f.length>0&&(f.forEach(function(U,W){W>0&&(C+=(U[1]?" ":"\u2502")+" "),!I&&U[0]===n&&(I=!0)}),C+=t(a,c)+a,p&&(typeof n!="object"||n instanceof Date)&&(C+=": "+n),I&&(C+=" (circular ref.)"),E(C)),!I&&typeof n=="object"){var N=e(n,h);N.forEach(function(U){P=++S===N.length,r(U,n[U],P,R,p,h,E)})}}var s={};return s.asLines=function(a,n,c,f){var p=typeof c!="function"?c:!1;r(".",a,!1,[],n,p,f||c)},s.asTree=function(a,n,c){var f="";return r(".",a,!1,[],n,c,function(p){f+=p+` +`}),f},s})});var ks={};Vt(ks,{emitList:()=>ECt,emitTree:()=>ACe,treeNodeToJson:()=>fCe,treeNodeToTreeify:()=>uCe});function uCe(t,{configuration:e}){let r={},s=0,a=(n,c)=>{let f=Array.isArray(n)?n.entries():Object.entries(n);for(let[p,h]of f){if(!h)continue;let{label:E,value:C,children:S}=h,P=[];typeof E<"u"&&P.push(zd(e,E,2)),typeof C<"u"&&P.push(Ut(e,C[0],C[1])),P.length===0&&P.push(zd(e,`${p}`,2));let I=P.join(": ").trim(),R=`\0${s++}\0`,N=c[`${R}${I}`]={};typeof S<"u"&&a(S,N)}};if(typeof t.children>"u")throw new Error("The root node must only contain children");return a(t.children,r),r}function fCe(t){let e=r=>{if(typeof r.children>"u"){if(typeof r.value>"u")throw new Error("Assertion failed: Expected a value to be set if the children are missing");return Zd(r.value[0],r.value[1])}let s=Array.isArray(r.children)?r.children.entries():Object.entries(r.children??{}),a=Array.isArray(r.children)?[]:{};for(let[n,c]of s)c&&(a[ICt(n)]=e(c));return typeof r.value>"u"?a:{value:Zd(r.value[0],r.value[1]),children:a}};return e(t)}function ECt(t,{configuration:e,stdout:r,json:s}){let a=t.map(n=>({value:n}));ACe({children:a},{configuration:e,stdout:r,json:s})}function ACe(t,{configuration:e,stdout:r,json:s,separators:a=0}){if(s){let c=Array.isArray(t.children)?t.children.values():Object.values(t.children??{});for(let f of c)f&&r.write(`${JSON.stringify(fCe(f))} +`);return}let n=(0,cCe.asTree)(uCe(t,{configuration:e}),!1,!1);if(n=n.replace(/\0[0-9]+\0/g,""),a>=1&&(n=n.replace(/^([├└]─)/gm,`\u2502 +$1`).replace(/^│\n/,"")),a>=2)for(let c=0;c<2;++c)n=n.replace(/^([│ ].{2}[├│ ].{2}[^\n]+\n)(([│ ]).{2}[├└].{2}[^\n]*\n[│ ].{2}[│ ].{2}[├└]─)/gm,`$1$3 \u2502 +$2`).replace(/^│\n/,"");if(a>=3)throw new Error("Only the first two levels are accepted by treeUtils.emitTree");r.write(n)}function ICt(t){return typeof t=="string"?t.replace(/^\0[0-9]+\0/,""):t}var cCe,pCe=It(()=>{cCe=et(lCe());Qc()});var _R,hCe=It(()=>{_R=class{constructor(e){this.releaseFunction=e;this.map=new Map}addOrCreate(e,r){let s=this.map.get(e);if(typeof s<"u"){if(s.refCount<=0)throw new Error(`Race condition in RefCountedMap. While adding a new key the refCount is: ${s.refCount} for ${JSON.stringify(e)}`);return s.refCount++,{value:s.value,release:()=>this.release(e)}}else{let a=r();return this.map.set(e,{refCount:1,value:a}),{value:a,release:()=>this.release(e)}}}release(e){let r=this.map.get(e);if(!r)throw new Error(`Unbalanced calls to release. No known instances of: ${JSON.stringify(e)}`);let s=r.refCount;if(s<=0)throw new Error(`Unbalanced calls to release. Too many release vs alloc refcount would become: ${s-1} of ${JSON.stringify(e)}`);s==1?(this.map.delete(e),this.releaseFunction(r.value)):r.refCount--}}});function tS(t){let e=t.match(CCt);if(!e?.groups)throw new Error("Assertion failed: Expected the checksum to match the requested pattern");let r=e.groups.cacheVersion?parseInt(e.groups.cacheVersion):null;return{cacheKey:e.groups.cacheKey??null,cacheVersion:r,cacheSpec:e.groups.cacheSpec??null,hash:e.groups.hash}}var gCe,aG,lG,UR,Jr,CCt,cG=It(()=>{Ve();bt();bt();rA();gCe=ye("crypto"),aG=et(ye("fs"));hCe();Fc();I0();kc();Yo();lG=YE(process.env.YARN_CACHE_CHECKPOINT_OVERRIDE??process.env.YARN_CACHE_VERSION_OVERRIDE??9),UR=YE(process.env.YARN_CACHE_VERSION_OVERRIDE??10),Jr=class t{constructor(e,{configuration:r,immutable:s=r.get("enableImmutableCache"),check:a=!1}){this.markedFiles=new Set;this.mutexes=new Map;this.refCountedZipFsCache=new _R(e=>{e.discardAndClose()});this.cacheId=`-${(0,gCe.randomBytes)(8).toString("hex")}.tmp`;this.configuration=r,this.cwd=e,this.immutable=s,this.check=a;let{cacheSpec:n,cacheKey:c}=t.getCacheKey(r);this.cacheSpec=n,this.cacheKey=c}static async find(e,{immutable:r,check:s}={}){let a=new t(e.get("cacheFolder"),{configuration:e,immutable:r,check:s});return await a.setup(),a}static getCacheKey(e){let r=e.get("compressionLevel"),s=r!=="mixed"?`c${r}`:"";return{cacheKey:[UR,s].join(""),cacheSpec:s}}get mirrorCwd(){if(!this.configuration.get("enableMirror"))return null;let e=`${this.configuration.get("globalFolder")}/cache`;return e!==this.cwd?e:null}getVersionFilename(e){return`${nI(e)}-${this.cacheKey}.zip`}getChecksumFilename(e,r){let a=tS(r).hash.slice(0,10);return`${nI(e)}-${a}.zip`}isChecksumCompatible(e){if(e===null)return!1;let{cacheVersion:r,cacheSpec:s}=tS(e);if(r===null||r{let pe=new ps,Be=K.join(vt.root,Z8(e));return pe.mkdirSync(Be,{recursive:!0}),pe.writeJsonSync(K.join(Be,Er.manifest),{name:cn(e),mocked:!0}),pe},E=async(pe,{isColdHit:Be,controlPath:Ce=null})=>{if(Ce===null&&c.unstablePackages?.has(e.locatorHash))return{isValid:!0,hash:null};let g=r&&!Be?tS(r).cacheKey:this.cacheKey,we=!c.skipIntegrityCheck||!r?`${g}/${await SQ(pe)}`:r;if(Ce!==null){let fe=!c.skipIntegrityCheck||!r?`${this.cacheKey}/${await SQ(Ce)}`:r;if(we!==fe)throw new Yt(18,"The remote archive doesn't match the local checksum - has the local cache been corrupted?")}let Ee=null;switch(r!==null&&we!==r&&(this.check?Ee="throw":tS(r).cacheKey!==tS(we).cacheKey?Ee="update":Ee=this.configuration.get("checksumBehavior")),Ee){case null:case"update":return{isValid:!0,hash:we};case"ignore":return{isValid:!0,hash:r};case"reset":return{isValid:!1,hash:r};default:case"throw":throw new Yt(18,"The remote archive doesn't match the expected checksum")}},C=async pe=>{if(!n)throw new Error(`Cache check required but no loader configured for ${Yr(this.configuration,e)}`);let Be=await n(),Ce=Be.getRealPath();Be.saveAndClose(),await le.chmodPromise(Ce,420);let g=await E(pe,{controlPath:Ce,isColdHit:!1});if(!g.isValid)throw new Error("Assertion failed: Expected a valid checksum");return g.hash},S=async()=>{if(f===null||!await le.existsPromise(f)){let pe=await n(),Be=pe.getRealPath();return pe.saveAndClose(),{source:"loader",path:Be}}return{source:"mirror",path:f}},P=async()=>{if(!n)throw new Error(`Cache entry required but missing for ${Yr(this.configuration,e)}`);if(this.immutable)throw new Yt(56,`Cache entry required but missing for ${Yr(this.configuration,e)}`);let{path:pe,source:Be}=await S(),{hash:Ce}=await E(pe,{isColdHit:!0}),g=this.getLocatorPath(e,Ce),we=[];Be!=="mirror"&&f!==null&&we.push(async()=>{let fe=`${f}${this.cacheId}`;await le.copyFilePromise(pe,fe,aG.default.constants.COPYFILE_FICLONE),await le.chmodPromise(fe,420),await le.renamePromise(fe,f)}),(!c.mirrorWriteOnly||f===null)&&we.push(async()=>{let fe=`${g}${this.cacheId}`;await le.copyFilePromise(pe,fe,aG.default.constants.COPYFILE_FICLONE),await le.chmodPromise(fe,420),await le.renamePromise(fe,g)});let Ee=c.mirrorWriteOnly?f??g:g;return await Promise.all(we.map(fe=>fe())),[!1,Ee,Ce]},I=async()=>{let Be=(async()=>{let Ce=c.unstablePackages?.has(e.locatorHash),g=Ce||!r||this.isChecksumCompatible(r)?this.getLocatorPath(e,r):null,we=g!==null?this.markedFiles.has(g)||await p.existsPromise(g):!1,Ee=!!c.mockedPackages?.has(e.locatorHash)&&(!this.check||!we),fe=Ee||we,se=fe?s:a;if(se&&se(),fe){let X=null,De=g;if(!Ee)if(this.check)X=await C(De);else{let Re=await E(De,{isColdHit:!1});if(Re.isValid)X=Re.hash;else return P()}return[Ee,De,X]}else{if(this.immutable&&Ce)throw new Yt(56,`Cache entry required but missing for ${Yr(this.configuration,e)}; consider defining ${he.pretty(this.configuration,"supportedArchitectures",he.Type.CODE)} to cache packages for multiple systems`);return P()}})();this.mutexes.set(e.locatorHash,Be);try{return await Be}finally{this.mutexes.delete(e.locatorHash)}};for(let pe;pe=this.mutexes.get(e.locatorHash);)await pe;let[R,N,U]=await I();R||this.markedFiles.add(N);let W=()=>this.refCountedZipFsCache.addOrCreate(N,()=>R?h():new ps(N,{baseFs:p,readOnly:!0})),te,ie=new oE(()=>p3(()=>(te=W(),te.value),pe=>`Failed to open the cache entry for ${Yr(this.configuration,e)}: ${pe}`),K),Ae=new Hf(N,{baseFs:ie,pathUtils:K}),ce=()=>{te?.release()},me=c.unstablePackages?.has(e.locatorHash)?null:U;return[Ae,ce,me]}},CCt=/^(?:(?(?[0-9]+)(?.*))\/)?(?.*)$/});var HR,dCe=It(()=>{HR=(r=>(r[r.SCRIPT=0]="SCRIPT",r[r.SHELLCODE=1]="SHELLCODE",r))(HR||{})});var wCt,zI,uG=It(()=>{bt();Bc();Np();Yo();wCt=[[/^(git(?:\+(?:https|ssh))?:\/\/.*(?:\.git)?)#(.*)$/,(t,e,r,s)=>`${r}#commit=${s}`],[/^https:\/\/((?:[^/]+?)@)?codeload\.github\.com\/([^/]+\/[^/]+)\/tar\.gz\/([0-9a-f]+)$/,(t,e,r="",s,a)=>`https://${r}github.com/${s}.git#commit=${a}`],[/^https:\/\/((?:[^/]+?)@)?github\.com\/([^/]+\/[^/]+?)(?:\.git)?#([0-9a-f]+)$/,(t,e,r="",s,a)=>`https://${r}github.com/${s}.git#commit=${a}`],[/^https?:\/\/[^/]+\/(?:[^/]+\/)*(?:@.+(?:\/|(?:%2f)))?([^/]+)\/(?:-|download)\/\1-[^/]+\.tgz(?:#|$)/,t=>`npm:${t}`],[/^https:\/\/npm\.pkg\.github\.com\/download\/(?:@[^/]+)\/(?:[^/]+)\/(?:[^/]+)\/(?:[0-9a-f]+)(?:#|$)/,t=>`npm:${t}`],[/^https:\/\/npm\.fontawesome\.com\/(?:@[^/]+)\/([^/]+)\/-\/([^/]+)\/\1-\2.tgz(?:#|$)/,t=>`npm:${t}`],[/^https?:\/\/[^/]+\/.*\/(@[^/]+)\/([^/]+)\/-\/\1\/\2-(?:[.\d\w-]+)\.tgz(?:#|$)/,(t,e)=>kQ({protocol:"npm:",source:null,selector:t,params:{__archiveUrl:e}})],[/^[^/]+\.tgz#[0-9a-f]+$/,t=>`npm:${t}`]],zI=class{constructor(e){this.resolver=e;this.resolutions=null}async setup(e,{report:r}){let s=K.join(e.cwd,Er.lockfile);if(!le.existsSync(s))return;let a=await le.readFilePromise(s,"utf8"),n=ls(a);if(Object.hasOwn(n,"__metadata"))return;let c=this.resolutions=new Map;for(let f of Object.keys(n)){let p=ev(f);if(!p){r.reportWarning(14,`Failed to parse the string "${f}" into a proper descriptor`);continue}let h=ul(p.range)?On(p,`npm:${p.range}`):p,{version:E,resolved:C}=n[f];if(!C)continue;let S;for(let[I,R]of wCt){let N=C.match(I);if(N){S=R(E,...N);break}}if(!S){r.reportWarning(14,`${ri(e.configuration,h)}: Only some patterns can be imported from legacy lockfiles (not "${C}")`);continue}let P=h;try{let I=em(h.range),R=ev(I.selector,!0);R&&(P=R)}catch{}c.set(h.descriptorHash,Ys(P,S))}}supportsDescriptor(e,r){return this.resolutions?this.resolutions.has(e.descriptorHash):!1}supportsLocator(e,r){return!1}shouldPersistResolution(e,r){throw new Error("Assertion failed: This resolver doesn't support resolving locators to packages")}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){if(!this.resolutions)throw new Error("Assertion failed: The resolution store should have been setup");let a=this.resolutions.get(e.descriptorHash);if(!a)throw new Error("Assertion failed: The resolution should have been registered");let n=V8(a),c=s.project.configuration.normalizeDependency(n);return await this.resolver.getCandidates(c,r,s)}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){throw new Error("Assertion failed: This resolver doesn't support resolving locators to packages")}}});var uA,mCe=It(()=>{Fc();Fv();Qc();uA=class extends ho{constructor({configuration:r,stdout:s,suggestInstall:a=!0}){super();this.errorCount=0;YB(this,{configuration:r}),this.configuration=r,this.stdout=s,this.suggestInstall=a}static async start(r,s){let a=new this(r);try{await s(a)}catch(n){a.reportExceptionOnce(n)}finally{await a.finalize()}return a}hasErrors(){return this.errorCount>0}exitCode(){return this.hasErrors()?1:0}reportCacheHit(r){}reportCacheMiss(r){}startSectionSync(r,s){return s()}async startSectionPromise(r,s){return await s()}startTimerSync(r,s,a){return(typeof s=="function"?s:a)()}async startTimerPromise(r,s,a){return await(typeof s=="function"?s:a)()}reportSeparator(){}reportInfo(r,s){}reportWarning(r,s){}reportError(r,s){this.errorCount+=1,this.stdout.write(`${Ut(this.configuration,"\u27A4","redBright")} ${this.formatNameWithHyperlink(r)}: ${s} +`)}reportProgress(r){return{...Promise.resolve().then(async()=>{for await(let{}of r);}),stop:()=>{}}}reportJson(r){}reportFold(r,s){}async finalize(){this.errorCount>0&&(this.stdout.write(` +`),this.stdout.write(`${Ut(this.configuration,"\u27A4","redBright")} Errors happened when preparing the environment required to run this command. +`),this.suggestInstall&&this.stdout.write(`${Ut(this.configuration,"\u27A4","redBright")} This might be caused by packages being missing from the lockfile, in which case running "yarn install" might help. +`))}formatNameWithHyperlink(r){return m6(r,{configuration:this.configuration,json:!1})}}});var ZI,fG=It(()=>{Yo();ZI=class{constructor(e){this.resolver=e}supportsDescriptor(e,r){return!!(r.project.storedResolutions.get(e.descriptorHash)||r.project.originalPackages.has(bQ(e).locatorHash))}supportsLocator(e,r){return!!(r.project.originalPackages.has(e.locatorHash)&&!r.project.lockfileNeedsRefresh)}shouldPersistResolution(e,r){throw new Error("The shouldPersistResolution method shouldn't be called on the lockfile resolver, which would always answer yes")}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return this.resolver.getResolutionDependencies(e,r)}async getCandidates(e,r,s){let a=s.project.storedResolutions.get(e.descriptorHash);if(a){let c=s.project.originalPackages.get(a);if(c)return[c]}let n=s.project.originalPackages.get(bQ(e).locatorHash);if(n)return[n];throw new Error("Resolution expected from the lockfile data")}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){let s=r.project.originalPackages.get(e.locatorHash);if(!s)throw new Error("The lockfile resolver isn't meant to resolve packages - they should already have been stored into a cache");return s}}});function Xp(){}function BCt(t,e,r,s,a){for(var n=0,c=e.length,f=0,p=0;nP.length?R:P}),h.value=t.join(E)}else h.value=t.join(r.slice(f,f+h.count));f+=h.count,h.added||(p+=h.count)}}var S=e[c-1];return c>1&&typeof S.value=="string"&&(S.added||S.removed)&&t.equals("",S.value)&&(e[c-2].value+=S.value,e.pop()),e}function vCt(t){return{newPos:t.newPos,components:t.components.slice(0)}}function SCt(t,e){if(typeof t=="function")e.callback=t;else if(t)for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}function ICe(t,e,r){return r=SCt(r,{ignoreWhitespace:!0}),dG.diff(t,e,r)}function DCt(t,e,r){return mG.diff(t,e,r)}function jR(t){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?jR=function(e){return typeof e}:jR=function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},jR(t)}function AG(t){return xCt(t)||kCt(t)||QCt(t)||TCt()}function xCt(t){if(Array.isArray(t))return pG(t)}function kCt(t){if(typeof Symbol<"u"&&Symbol.iterator in Object(t))return Array.from(t)}function QCt(t,e){if(t){if(typeof t=="string")return pG(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);if(r==="Object"&&t.constructor&&(r=t.constructor.name),r==="Map"||r==="Set")return Array.from(t);if(r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return pG(t,e)}}function pG(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,s=new Array(e);r"u"&&(c.context=4);var f=DCt(r,s,c);if(!f)return;f.push({value:"",lines:[]});function p(U){return U.map(function(W){return" "+W})}for(var h=[],E=0,C=0,S=[],P=1,I=1,R=function(W){var te=f[W],ie=te.lines||te.value.replace(/\n$/,"").split(` +`);if(te.lines=ie,te.added||te.removed){var Ae;if(!E){var ce=f[W-1];E=P,C=I,ce&&(S=c.context>0?p(ce.lines.slice(-c.context)):[],E-=S.length,C-=S.length)}(Ae=S).push.apply(Ae,AG(ie.map(function(fe){return(te.added?"+":"-")+fe}))),te.added?I+=ie.length:P+=ie.length}else{if(E)if(ie.length<=c.context*2&&W=f.length-2&&ie.length<=c.context){var g=/\n$/.test(r),we=/\n$/.test(s),Ee=ie.length==0&&S.length>Ce.oldLines;!g&&Ee&&r.length>0&&S.splice(Ce.oldLines,0,"\\ No newline at end of file"),(!g&&!Ee||!we)&&S.push("\\ No newline at end of file")}h.push(Ce),E=0,C=0,S=[]}P+=ie.length,I+=ie.length}},N=0;N{Xp.prototype={diff:function(e,r){var s=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},a=s.callback;typeof s=="function"&&(a=s,s={}),this.options=s;var n=this;function c(R){return a?(setTimeout(function(){a(void 0,R)},0),!0):R}e=this.castInput(e),r=this.castInput(r),e=this.removeEmpty(this.tokenize(e)),r=this.removeEmpty(this.tokenize(r));var f=r.length,p=e.length,h=1,E=f+p;s.maxEditLength&&(E=Math.min(E,s.maxEditLength));var C=[{newPos:-1,components:[]}],S=this.extractCommon(C[0],r,e,0);if(C[0].newPos+1>=f&&S+1>=p)return c([{value:this.join(r),count:r.length}]);function P(){for(var R=-1*h;R<=h;R+=2){var N=void 0,U=C[R-1],W=C[R+1],te=(W?W.newPos:0)-R;U&&(C[R-1]=void 0);var ie=U&&U.newPos+1=f&&te+1>=p)return c(BCt(n,N.components,r,e,n.useLongestToken));C[R]=N}h++}if(a)(function R(){setTimeout(function(){if(h>E)return a();P()||R()},0)})();else for(;h<=E;){var I=P();if(I)return I}},pushComponent:function(e,r,s){var a=e[e.length-1];a&&a.added===r&&a.removed===s?e[e.length-1]={count:a.count+1,added:r,removed:s}:e.push({count:1,added:r,removed:s})},extractCommon:function(e,r,s,a){for(var n=r.length,c=s.length,f=e.newPos,p=f-a,h=0;f+1"u"?r:c}:s;return typeof t=="string"?t:JSON.stringify(hG(t,null,null,a),a," ")};rS.equals=function(t,e){return Xp.prototype.equals.call(rS,t.replace(/,([\r\n])/g,"$1"),e.replace(/,([\r\n])/g,"$1"))};gG=new Xp;gG.tokenize=function(t){return t.slice()};gG.join=gG.removeEmpty=function(t){return t}});var BCe=L((Str,wCe)=>{var FCt=xc(),NCt=aI(),OCt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,LCt=/^\w*$/;function MCt(t,e){if(FCt(t))return!1;var r=typeof t;return r=="number"||r=="symbol"||r=="boolean"||t==null||NCt(t)?!0:LCt.test(t)||!OCt.test(t)||e!=null&&t in Object(e)}wCe.exports=MCt});var DCe=L((Dtr,SCe)=>{var vCe=kk(),_Ct="Expected a function";function EG(t,e){if(typeof t!="function"||e!=null&&typeof e!="function")throw new TypeError(_Ct);var r=function(){var s=arguments,a=e?e.apply(this,s):s[0],n=r.cache;if(n.has(a))return n.get(a);var c=t.apply(this,s);return r.cache=n.set(a,c)||n,c};return r.cache=new(EG.Cache||vCe),r}EG.Cache=vCe;SCe.exports=EG});var PCe=L((btr,bCe)=>{var UCt=DCe(),HCt=500;function jCt(t){var e=UCt(t,function(s){return r.size===HCt&&r.clear(),s}),r=e.cache;return e}bCe.exports=jCt});var IG=L((Ptr,xCe)=>{var qCt=PCe(),GCt=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,WCt=/\\(\\)?/g,YCt=qCt(function(t){var e=[];return t.charCodeAt(0)===46&&e.push(""),t.replace(GCt,function(r,s,a,n){e.push(a?n.replace(WCt,"$1"):s||r)}),e});xCe.exports=YCt});var wm=L((xtr,kCe)=>{var VCt=xc(),KCt=BCe(),JCt=IG(),zCt=Tv();function ZCt(t,e){return VCt(t)?t:KCt(t,e)?[t]:JCt(zCt(t))}kCe.exports=ZCt});var XI=L((ktr,QCe)=>{var XCt=aI(),$Ct=1/0;function ewt(t){if(typeof t=="string"||XCt(t))return t;var e=t+"";return e=="0"&&1/t==-$Ct?"-0":e}QCe.exports=ewt});var qR=L((Qtr,TCe)=>{var twt=wm(),rwt=XI();function nwt(t,e){e=twt(e,t);for(var r=0,s=e.length;t!=null&&r{var iwt=Yk(),swt=wm(),owt=NB(),RCe=Wl(),awt=XI();function lwt(t,e,r,s){if(!RCe(t))return t;e=swt(e,t);for(var a=-1,n=e.length,c=n-1,f=t;f!=null&&++a{var cwt=qR(),uwt=CG(),fwt=wm();function Awt(t,e,r){for(var s=-1,a=e.length,n={};++s{function pwt(t,e){return t!=null&&e in Object(t)}LCe.exports=pwt});var wG=L((Ntr,_Ce)=>{var hwt=wm(),gwt=TB(),dwt=xc(),mwt=NB(),ywt=Nk(),Ewt=XI();function Iwt(t,e,r){e=hwt(e,t);for(var s=-1,a=e.length,n=!1;++s{var Cwt=MCe(),wwt=wG();function Bwt(t,e){return t!=null&&wwt(t,e,Cwt)}UCe.exports=Bwt});var qCe=L((Ltr,jCe)=>{var vwt=OCe(),Swt=HCe();function Dwt(t,e){return vwt(t,e,function(r,s){return Swt(t,s)})}jCe.exports=Dwt});var VCe=L((Mtr,YCe)=>{var GCe=Yd(),bwt=TB(),Pwt=xc(),WCe=GCe?GCe.isConcatSpreadable:void 0;function xwt(t){return Pwt(t)||bwt(t)||!!(WCe&&t&&t[WCe])}YCe.exports=xwt});var zCe=L((_tr,JCe)=>{var kwt=Rk(),Qwt=VCe();function KCe(t,e,r,s,a){var n=-1,c=t.length;for(r||(r=Qwt),a||(a=[]);++n0&&r(f)?e>1?KCe(f,e-1,r,s,a):kwt(a,f):s||(a[a.length]=f)}return a}JCe.exports=KCe});var XCe=L((Utr,ZCe)=>{var Twt=zCe();function Rwt(t){var e=t==null?0:t.length;return e?Twt(t,1):[]}ZCe.exports=Rwt});var BG=L((Htr,$Ce)=>{var Fwt=XCe(),Nwt=i3(),Owt=s3();function Lwt(t){return Owt(Nwt(t,void 0,Fwt),t+"")}$Ce.exports=Lwt});var vG=L((jtr,ewe)=>{var Mwt=qCe(),_wt=BG(),Uwt=_wt(function(t,e){return t==null?{}:Mwt(t,e)});ewe.exports=Uwt});var GR,twe=It(()=>{Fc();GR=class{constructor(e){this.resolver=e}supportsDescriptor(e,r){return this.resolver.supportsDescriptor(e,r)}supportsLocator(e,r){return this.resolver.supportsLocator(e,r)}shouldPersistResolution(e,r){return this.resolver.shouldPersistResolution(e,r)}bindDescriptor(e,r,s){return this.resolver.bindDescriptor(e,r,s)}getResolutionDependencies(e,r){return this.resolver.getResolutionDependencies(e,r)}async getCandidates(e,r,s){throw new Yt(20,`This package doesn't seem to be present in your lockfile; run "yarn install" to update the lockfile`)}async getSatisfying(e,r,s,a){throw new Yt(20,`This package doesn't seem to be present in your lockfile; run "yarn install" to update the lockfile`)}async resolve(e,r){throw new Yt(20,`This package doesn't seem to be present in your lockfile; run "yarn install" to update the lockfile`)}}});var Wi,SG=It(()=>{Fc();Wi=class extends ho{reportCacheHit(e){}reportCacheMiss(e){}startSectionSync(e,r){return r()}async startSectionPromise(e,r){return await r()}startTimerSync(e,r,s){return(typeof r=="function"?r:s)()}async startTimerPromise(e,r,s){return await(typeof r=="function"?r:s)()}reportSeparator(){}reportInfo(e,r){}reportWarning(e,r){}reportError(e,r){}reportProgress(e){return{...Promise.resolve().then(async()=>{for await(let{}of e);}),stop:()=>{}}}reportJson(e){}reportFold(e,r){}async finalize(){}}});var rwe,$I,DG=It(()=>{bt();rwe=et(BQ());oI();tm();Qc();I0();Np();Yo();$I=class{constructor(e,{project:r}){this.workspacesCwds=new Set;this.project=r,this.cwd=e}async setup(){this.manifest=await Ht.tryFind(this.cwd)??new Ht,this.relativeCwd=K.relative(this.project.cwd,this.cwd)||vt.dot;let e=this.manifest.name?this.manifest.name:ba(null,`${this.computeCandidateName()}-${us(this.relativeCwd).substring(0,6)}`);this.anchoredDescriptor=On(e,`${yi.protocol}${this.relativeCwd}`),this.anchoredLocator=Ys(e,`${yi.protocol}${this.relativeCwd}`);let r=this.manifest.workspaceDefinitions.map(({pattern:a})=>a);if(r.length===0)return;let s=await(0,rwe.default)(r,{cwd:ue.fromPortablePath(this.cwd),onlyDirectories:!0,ignore:["**/node_modules","**/.git","**/.yarn"]});s.sort(),await s.reduce(async(a,n)=>{let c=K.resolve(this.cwd,ue.toPortablePath(n)),f=await le.existsPromise(K.join(c,"package.json"));await a,f&&this.workspacesCwds.add(c)},Promise.resolve())}get anchoredPackage(){let e=this.project.storedPackages.get(this.anchoredLocator.locatorHash);if(!e)throw new Error(`Assertion failed: Expected workspace ${rv(this.project.configuration,this)} (${Ut(this.project.configuration,K.join(this.cwd,Er.manifest),Ct.PATH)}) to have been resolved. Run "yarn install" to update the lockfile`);return e}accepts(e){let r=e.indexOf(":"),s=r!==-1?e.slice(0,r+1):null,a=r!==-1?e.slice(r+1):e;if(s===yi.protocol&&K.normalize(a)===this.relativeCwd||s===yi.protocol&&(a==="*"||a==="^"||a==="~"))return!0;let n=ul(a);return n?s===yi.protocol?n.test(this.manifest.version??"0.0.0"):this.project.configuration.get("enableTransparentWorkspaces")&&this.manifest.version!==null?n.test(this.manifest.version):!1:!1}computeCandidateName(){return this.cwd===this.project.cwd?"root-workspace":`${K.basename(this.cwd)}`||"unnamed-workspace"}getRecursiveWorkspaceDependencies({dependencies:e=Ht.hardDependencies}={}){let r=new Set,s=a=>{for(let n of e)for(let c of a.manifest[n].values()){let f=this.project.tryWorkspaceByDescriptor(c);f===null||r.has(f)||(r.add(f),s(f))}};return s(this),r}getRecursiveWorkspaceDependents({dependencies:e=Ht.hardDependencies}={}){let r=new Set,s=a=>{for(let n of this.project.workspaces)e.some(f=>[...n.manifest[f].values()].some(p=>{let h=this.project.tryWorkspaceByDescriptor(p);return h!==null&&$B(h.anchoredLocator,a.anchoredLocator)}))&&!r.has(n)&&(r.add(n),s(n))};return s(this),r}getRecursiveWorkspaceChildren(){let e=new Set([this]);for(let r of e)for(let s of r.workspacesCwds){let a=this.project.workspacesByCwd.get(s);a&&e.add(a)}return e.delete(this),Array.from(e)}async persistManifest(){let e={};this.manifest.exportTo(e);let r=K.join(this.cwd,Ht.fileName),s=`${JSON.stringify(e,null,this.manifest.indent)} +`;await le.changeFilePromise(r,s,{automaticNewlines:!0}),this.manifest.raw=e}}});function Ywt({project:t,allDescriptors:e,allResolutions:r,allPackages:s,accessibleLocators:a=new Set,optionalBuilds:n=new Set,peerRequirements:c=new Map,peerWarnings:f=[],peerRequirementNodes:p=new Map,volatileDescriptors:h=new Set}){let E=new Map,C=[],S=new Map,P=new Map,I=new Map,R=new Map,N=new Map,U=new Map(t.workspaces.map(ce=>{let me=ce.anchoredLocator.locatorHash,pe=s.get(me);if(typeof pe>"u")throw new Error("Assertion failed: The workspace should have an associated package");return[me,zB(pe)]})),W=()=>{let ce=le.mktempSync(),me=K.join(ce,"stacktrace.log"),pe=String(C.length+1).length,Be=C.map((Ce,g)=>`${`${g+1}.`.padStart(pe," ")} ${cl(Ce)} +`).join("");throw le.writeFileSync(me,Be),le.detachTemp(ce),new Yt(45,`Encountered a stack overflow when resolving peer dependencies; cf ${ue.fromPortablePath(me)}`)},te=ce=>{let me=r.get(ce.descriptorHash);if(typeof me>"u")throw new Error("Assertion failed: The resolution should have been registered");let pe=s.get(me);if(!pe)throw new Error("Assertion failed: The package could not be found");return pe},ie=(ce,me,pe,{top:Be,optional:Ce})=>{C.length>1e3&&W(),C.push(me);let g=Ae(ce,me,pe,{top:Be,optional:Ce});return C.pop(),g},Ae=(ce,me,pe,{top:Be,optional:Ce})=>{if(Ce||n.delete(me.locatorHash),a.has(me.locatorHash))return;a.add(me.locatorHash);let g=s.get(me.locatorHash);if(!g)throw new Error(`Assertion failed: The package (${Yr(t.configuration,me)}) should have been registered`);let we=new Set,Ee=new Map,fe=[],se=[],X=[],De=[];for(let Re of Array.from(g.dependencies.values())){if(g.peerDependencies.has(Re.identHash)&&g.locatorHash!==Be)continue;if(Tp(Re))throw new Error("Assertion failed: Virtual packages shouldn't be encountered when virtualizing a branch");h.delete(Re.descriptorHash);let gt=Ce;if(!gt){let ke=g.dependenciesMeta.get(cn(Re));if(typeof ke<"u"){let it=ke.get(null);typeof it<"u"&&it.optional&&(gt=!0)}}let j=r.get(Re.descriptorHash);if(!j)throw new Error(`Assertion failed: The resolution (${ri(t.configuration,Re)}) should have been registered`);let rt=U.get(j)||s.get(j);if(!rt)throw new Error(`Assertion failed: The package (${j}, resolved from ${ri(t.configuration,Re)}) should have been registered`);if(rt.peerDependencies.size===0){ie(Re,rt,new Map,{top:Be,optional:gt});continue}let Fe,Ne,Pe=new Set,Ye=new Map;fe.push(()=>{Fe=J8(Re,me.locatorHash),Ne=z8(rt,me.locatorHash),g.dependencies.set(Re.identHash,Fe),r.set(Fe.descriptorHash,Ne.locatorHash),e.set(Fe.descriptorHash,Fe),s.set(Ne.locatorHash,Ne),xp(R,Ne.locatorHash).add(Fe.descriptorHash),we.add(Ne.locatorHash)}),se.push(()=>{N.set(Ne.locatorHash,Ye);for(let ke of Ne.peerDependencies.values()){let _e=Vl(Ee,ke.identHash,()=>{let x=pe.get(ke.identHash)??null,w=g.dependencies.get(ke.identHash);return!w&&XB(me,ke)&&(ce.identHash===me.identHash?w=ce:(w=On(me,ce.range),e.set(w.descriptorHash,w),r.set(w.descriptorHash,me.locatorHash),h.delete(w.descriptorHash),x=null)),w||(w=On(ke,"missing:")),{subject:me,ident:ke,provided:w,root:!x,requests:new Map,hash:`p${us(me.locatorHash,ke.identHash).slice(0,5)}`}}).provided;if(_e.range==="missing:"&&Ne.dependencies.has(ke.identHash)){Ne.peerDependencies.delete(ke.identHash);continue}if(Ye.set(ke.identHash,{requester:Ne,descriptor:ke,meta:Ne.peerDependenciesMeta.get(cn(ke)),children:new Map}),Ne.dependencies.set(ke.identHash,_e),Tp(_e)){let x=r.get(_e.descriptorHash);xp(I,x).add(Ne.locatorHash)}S.set(_e.identHash,_e),_e.range==="missing:"&&Pe.add(_e.identHash)}Ne.dependencies=new Map(Ws(Ne.dependencies,([ke,it])=>cn(it)))}),X.push(()=>{if(!s.has(Ne.locatorHash))return;let ke=E.get(rt.locatorHash);typeof ke=="number"&&ke>=2&&W();let it=E.get(rt.locatorHash),_e=typeof it<"u"?it+1:1;E.set(rt.locatorHash,_e),ie(Fe,Ne,Ye,{top:Be,optional:gt}),E.set(rt.locatorHash,_e-1)}),De.push(()=>{let ke=r.get(Fe.descriptorHash);if(typeof ke>"u")throw new Error("Assertion failed: Expected the descriptor to be registered");let it=N.get(ke);if(typeof it>"u")throw new Error("Assertion failed: Expected the peer requests to be registered");for(let _e of Ee.values()){let x=it.get(_e.ident.identHash);x&&(_e.requests.set(Fe.descriptorHash,x),p.set(_e.hash,_e),_e.root||pe.get(_e.ident.identHash)?.children.set(Fe.descriptorHash,x))}if(s.has(Ne.locatorHash))for(let _e of Pe)Ne.dependencies.delete(_e)})}for(let Re of[...fe,...se])Re();for(let Re of we){we.delete(Re);let gt=s.get(Re),j=us(rI(gt).locatorHash,...Array.from(gt.dependencies.values(),Pe=>{let Ye=Pe.range!=="missing:"?r.get(Pe.descriptorHash):"missing:";if(typeof Ye>"u")throw new Error(`Assertion failed: Expected the resolution for ${ri(t.configuration,Pe)} to have been registered`);return Ye===Be?`${Ye} (top)`:Ye})),rt=P.get(j);if(typeof rt>"u"){P.set(j,gt);continue}let Fe=xp(R,rt.locatorHash);for(let Pe of R.get(gt.locatorHash)??[])r.set(Pe,rt.locatorHash),Fe.add(Pe);s.delete(gt.locatorHash),a.delete(gt.locatorHash),we.delete(gt.locatorHash);let Ne=I.get(gt.locatorHash);if(Ne!==void 0){let Pe=xp(I,rt.locatorHash);for(let Ye of Ne)Pe.add(Ye),we.add(Ye)}}for(let Re of[...X,...De])Re()};for(let ce of t.workspaces){let me=ce.anchoredLocator;h.delete(ce.anchoredDescriptor.descriptorHash),ie(ce.anchoredDescriptor,me,new Map,{top:me.locatorHash,optional:!1})}for(let ce of p.values()){if(!ce.root)continue;let me=s.get(ce.subject.locatorHash);if(typeof me>"u")continue;for(let Be of ce.requests.values()){let Ce=`p${us(ce.subject.locatorHash,cn(ce.ident),Be.requester.locatorHash).slice(0,5)}`;c.set(Ce,{subject:ce.subject.locatorHash,requested:ce.ident,rootRequester:Be.requester.locatorHash,allRequesters:Array.from(nv(Be),g=>g.requester.locatorHash)})}let pe=[...nv(ce)];if(ce.provided.range!=="missing:"){let Be=te(ce.provided),Ce=Be.version??"0.0.0",g=Ee=>{if(Ee.startsWith(yi.protocol)){if(!t.tryWorkspaceByLocator(Be))return null;Ee=Ee.slice(yi.protocol.length),(Ee==="^"||Ee==="~")&&(Ee="*")}return Ee},we=!0;for(let Ee of pe){let fe=g(Ee.descriptor.range);if(fe===null){we=!1;continue}if(!eA(Ce,fe)){we=!1;let se=`p${us(ce.subject.locatorHash,cn(ce.ident),Ee.requester.locatorHash).slice(0,5)}`;f.push({type:1,subject:me,requested:ce.ident,requester:Ee.requester,version:Ce,hash:se,requirementCount:pe.length})}}if(!we){let Ee=pe.map(fe=>g(fe.descriptor.range));f.push({type:3,node:ce,range:Ee.includes(null)?null:$8(Ee),hash:ce.hash})}}else{let Be=!0;for(let Ce of pe)if(!Ce.meta?.optional){Be=!1;let g=`p${us(ce.subject.locatorHash,cn(ce.ident),Ce.requester.locatorHash).slice(0,5)}`;f.push({type:0,subject:me,requested:ce.ident,requester:Ce.requester,hash:g})}Be||f.push({type:2,node:ce,hash:ce.hash})}}}function*Vwt(t){let e=new Map;if("children"in t)e.set(t,t);else for(let r of t.requests.values())e.set(r,r);for(let[r,s]of e){yield{request:r,root:s};for(let a of r.children.values())e.has(a)||e.set(a,s)}}function Kwt(t,e){let r=[],s=[],a=!1;for(let n of t.peerWarnings)if(!(n.type===1||n.type===0)){if(!t.tryWorkspaceByLocator(n.node.subject)){a=!0;continue}if(n.type===3){let c=t.storedResolutions.get(n.node.provided.descriptorHash);if(typeof c>"u")throw new Error("Assertion failed: Expected the descriptor to be registered");let f=t.storedPackages.get(c);if(typeof f>"u")throw new Error("Assertion failed: Expected the package to be registered");let p=p0(Vwt(n.node),({request:C,root:S})=>eA(f.version??"0.0.0",C.descriptor.range)?p0.skip:C===S?$i(t.configuration,C.requester):`${$i(t.configuration,C.requester)} (via ${$i(t.configuration,S.requester)})`),h=[...nv(n.node)].length>1?"and other dependencies request":"requests",E=n.range?iI(t.configuration,n.range):Ut(t.configuration,"but they have non-overlapping ranges!","redBright");r.push(`${$i(t.configuration,n.node.ident)} is listed by your project with version ${tv(t.configuration,f.version??"0.0.0")} (${Ut(t.configuration,n.hash,Ct.CODE)}), which doesn't satisfy what ${p} ${h} (${E}).`)}if(n.type===2){let c=n.node.requests.size>1?" and other dependencies":"";s.push(`${Yr(t.configuration,n.node.subject)} doesn't provide ${$i(t.configuration,n.node.ident)} (${Ut(t.configuration,n.hash,Ct.CODE)}), requested by ${$i(t.configuration,n.node.requests.values().next().value.requester)}${c}.`)}}e.startSectionSync({reportFooter:()=>{e.reportWarning(86,`Some peer dependencies are incorrectly met by your project; run ${Ut(t.configuration,"yarn explain peer-requirements ",Ct.CODE)} for details, where ${Ut(t.configuration,"",Ct.CODE)} is the six-letter p-prefixed code.`)},skipIfEmpty:!0},()=>{for(let n of Ws(r,c=>KE.default(c)))e.reportWarning(60,n);for(let n of Ws(s,c=>KE.default(c)))e.reportWarning(2,n)}),a&&e.reportWarning(86,`Some peer dependencies are incorrectly met by dependencies; run ${Ut(t.configuration,"yarn explain peer-requirements",Ct.CODE)} for details.`)}var WR,YR,VR,swe,xG,PG,kG,KR,Hwt,jwt,nwe,qwt,Gwt,Wwt,ec,bG,JR,iwe,Tt,owe=It(()=>{bt();bt();Bc();Wt();WR=ye("crypto");yG();YR=et(vG()),VR=et(Md()),swe=et(fi()),xG=ye("util"),PG=et(ye("v8")),kG=et(ye("zlib"));cG();Cv();uG();fG();oI();iH();Fc();twe();Fv();SG();tm();DG();LQ();Qc();I0();kc();gT();w6();Np();Yo();KR=YE(process.env.YARN_LOCKFILE_VERSION_OVERRIDE??8),Hwt=3,jwt=/ *, */g,nwe=/\/$/,qwt=32,Gwt=(0,xG.promisify)(kG.default.gzip),Wwt=(0,xG.promisify)(kG.default.gunzip),ec=(r=>(r.UpdateLockfile="update-lockfile",r.SkipBuild="skip-build",r))(ec||{}),bG={restoreLinkersCustomData:["linkersCustomData"],restoreResolutions:["accessibleLocators","conditionalLocators","disabledLocators","optionalBuilds","storedDescriptors","storedResolutions","storedPackages","lockFileChecksum"],restoreBuildState:["skippedBuilds","storedBuildState"]},JR=(a=>(a[a.NotProvided=0]="NotProvided",a[a.NotCompatible=1]="NotCompatible",a[a.NodeNotProvided=2]="NodeNotProvided",a[a.NodeNotCompatible=3]="NodeNotCompatible",a))(JR||{}),iwe=t=>us(`${Hwt}`,t),Tt=class t{constructor(e,{configuration:r}){this.resolutionAliases=new Map;this.workspaces=[];this.workspacesByCwd=new Map;this.workspacesByIdent=new Map;this.storedResolutions=new Map;this.storedDescriptors=new Map;this.storedPackages=new Map;this.storedChecksums=new Map;this.storedBuildState=new Map;this.accessibleLocators=new Set;this.conditionalLocators=new Set;this.disabledLocators=new Set;this.originalPackages=new Map;this.optionalBuilds=new Set;this.skippedBuilds=new Set;this.lockfileLastVersion=null;this.lockfileNeedsRefresh=!1;this.peerRequirements=new Map;this.peerWarnings=[];this.peerRequirementNodes=new Map;this.linkersCustomData=new Map;this.lockFileChecksum=null;this.installStateChecksum=null;this.configuration=r,this.cwd=e}static async find(e,r){if(!e.projectCwd)throw new nt(`No project found in ${r}`);let s=e.projectCwd,a=r,n=null;for(;n!==e.projectCwd;){if(n=a,le.existsSync(K.join(n,Er.manifest))){s=n;break}a=K.dirname(n)}let c=new t(e.projectCwd,{configuration:e});ze.telemetry?.reportProject(c.cwd),await c.setupResolutions(),await c.setupWorkspaces(),ze.telemetry?.reportWorkspaceCount(c.workspaces.length),ze.telemetry?.reportDependencyCount(c.workspaces.reduce((I,R)=>I+R.manifest.dependencies.size+R.manifest.devDependencies.size,0));let f=c.tryWorkspaceByCwd(s);if(f)return{project:c,workspace:f,locator:f.anchoredLocator};let p=await c.findLocatorForLocation(`${s}/`,{strict:!0});if(p)return{project:c,locator:p,workspace:null};let h=Ut(e,c.cwd,Ct.PATH),E=Ut(e,K.relative(c.cwd,s),Ct.PATH),C=`- If ${h} isn't intended to be a project, remove any yarn.lock and/or package.json file there.`,S=`- If ${h} is intended to be a project, it might be that you forgot to list ${E} in its workspace configuration.`,P=`- Finally, if ${h} is fine and you intend ${E} to be treated as a completely separate project (not even a workspace), create an empty yarn.lock file in it.`;throw new nt(`The nearest package directory (${Ut(e,s,Ct.PATH)}) doesn't seem to be part of the project declared in ${Ut(e,c.cwd,Ct.PATH)}. + +${[C,S,P].join(` +`)}`)}async setupResolutions(){this.storedResolutions=new Map,this.storedDescriptors=new Map,this.storedPackages=new Map,this.lockFileChecksum=null;let e=K.join(this.cwd,Er.lockfile),r=this.configuration.get("defaultLanguageName");if(le.existsSync(e)){let s=await le.readFilePromise(e,"utf8");this.lockFileChecksum=iwe(s);let a=ls(s);if(a.__metadata){let n=a.__metadata.version,c=a.__metadata.cacheKey;this.lockfileLastVersion=n,this.lockfileNeedsRefresh=n"u")throw new Error(`Assertion failed: Expected the lockfile entry to have a resolution field (${f})`);let h=Rp(p.resolution,!0),E=new Ht;E.load(p,{yamlCompatibilityMode:!0});let C=E.version,S=E.languageName||r,P=p.linkType.toUpperCase(),I=p.conditions??null,R=E.dependencies,N=E.peerDependencies,U=E.dependenciesMeta,W=E.peerDependenciesMeta,te=E.bin;if(p.checksum!=null){let Ae=typeof c<"u"&&!p.checksum.includes("/")?`${c}/${p.checksum}`:p.checksum;this.storedChecksums.set(h.locatorHash,Ae)}let ie={...h,version:C,languageName:S,linkType:P,conditions:I,dependencies:R,peerDependencies:N,dependenciesMeta:U,peerDependenciesMeta:W,bin:te};this.originalPackages.set(ie.locatorHash,ie);for(let Ae of f.split(jwt)){let ce=C0(Ae);n<=6&&(ce=this.configuration.normalizeDependency(ce),ce=On(ce,ce.range.replace(/^patch:[^@]+@(?!npm(:|%3A))/,"$1npm%3A"))),this.storedDescriptors.set(ce.descriptorHash,ce),this.storedResolutions.set(ce.descriptorHash,h.locatorHash)}}}else s.includes("yarn lockfile v1")&&(this.lockfileLastVersion=-1)}}async setupWorkspaces(){this.workspaces=[],this.workspacesByCwd=new Map,this.workspacesByIdent=new Map;let e=new Set,r=(0,VR.default)(4),s=async(a,n)=>{if(e.has(n))return a;e.add(n);let c=new $I(n,{project:this});await r(()=>c.setup());let f=a.then(()=>{this.addWorkspace(c)});return Array.from(c.workspacesCwds).reduce(s,f)};await s(Promise.resolve(),this.cwd)}addWorkspace(e){let r=this.workspacesByIdent.get(e.anchoredLocator.identHash);if(typeof r<"u")throw new Error(`Duplicate workspace name ${$i(this.configuration,e.anchoredLocator)}: ${ue.fromPortablePath(e.cwd)} conflicts with ${ue.fromPortablePath(r.cwd)}`);this.workspaces.push(e),this.workspacesByCwd.set(e.cwd,e),this.workspacesByIdent.set(e.anchoredLocator.identHash,e)}get topLevelWorkspace(){return this.getWorkspaceByCwd(this.cwd)}tryWorkspaceByCwd(e){K.isAbsolute(e)||(e=K.resolve(this.cwd,e)),e=K.normalize(e).replace(/\/+$/,"");let r=this.workspacesByCwd.get(e);return r||null}getWorkspaceByCwd(e){let r=this.tryWorkspaceByCwd(e);if(!r)throw new Error(`Workspace not found (${e})`);return r}tryWorkspaceByFilePath(e){let r=null;for(let s of this.workspaces)K.relative(s.cwd,e).startsWith("../")||r&&r.cwd.length>=s.cwd.length||(r=s);return r||null}getWorkspaceByFilePath(e){let r=this.tryWorkspaceByFilePath(e);if(!r)throw new Error(`Workspace not found (${e})`);return r}tryWorkspaceByIdent(e){let r=this.workspacesByIdent.get(e.identHash);return typeof r>"u"?null:r}getWorkspaceByIdent(e){let r=this.tryWorkspaceByIdent(e);if(!r)throw new Error(`Workspace not found (${$i(this.configuration,e)})`);return r}tryWorkspaceByDescriptor(e){if(e.range.startsWith(yi.protocol)){let s=e.range.slice(yi.protocol.length);if(s!=="^"&&s!=="~"&&s!=="*"&&!ul(s))return this.tryWorkspaceByCwd(s)}let r=this.tryWorkspaceByIdent(e);return r===null||(Tp(e)&&(e=ZB(e)),!r.accepts(e.range))?null:r}getWorkspaceByDescriptor(e){let r=this.tryWorkspaceByDescriptor(e);if(r===null)throw new Error(`Workspace not found (${ri(this.configuration,e)})`);return r}tryWorkspaceByLocator(e){let r=this.tryWorkspaceByIdent(e);return r===null||(Gu(e)&&(e=rI(e)),r.anchoredLocator.locatorHash!==e.locatorHash)?null:r}getWorkspaceByLocator(e){let r=this.tryWorkspaceByLocator(e);if(!r)throw new Error(`Workspace not found (${Yr(this.configuration,e)})`);return r}deleteDescriptor(e){this.storedResolutions.delete(e),this.storedDescriptors.delete(e)}deleteLocator(e){this.originalPackages.delete(e),this.storedPackages.delete(e),this.accessibleLocators.delete(e)}forgetResolution(e){if("descriptorHash"in e){let r=this.storedResolutions.get(e.descriptorHash);this.deleteDescriptor(e.descriptorHash);let s=new Set(this.storedResolutions.values());typeof r<"u"&&!s.has(r)&&this.deleteLocator(r)}if("locatorHash"in e){this.deleteLocator(e.locatorHash);for(let[r,s]of this.storedResolutions)s===e.locatorHash&&this.deleteDescriptor(r)}}forgetTransientResolutions(){let e=this.configuration.makeResolver(),r=new Map;for(let[s,a]of this.storedResolutions.entries()){let n=r.get(a);n||r.set(a,n=new Set),n.add(s)}for(let s of this.originalPackages.values()){let a;try{a=e.shouldPersistResolution(s,{project:this,resolver:e})}catch{a=!1}if(!a){this.deleteLocator(s.locatorHash);let n=r.get(s.locatorHash);if(n){r.delete(s.locatorHash);for(let c of n)this.deleteDescriptor(c)}}}}forgetVirtualResolutions(){for(let e of this.storedPackages.values())for(let[r,s]of e.dependencies)Tp(s)&&e.dependencies.set(r,ZB(s))}getDependencyMeta(e,r){let s={},n=this.topLevelWorkspace.manifest.dependenciesMeta.get(cn(e));if(!n)return s;let c=n.get(null);if(c&&Object.assign(s,c),r===null||!swe.default.valid(r))return s;for(let[f,p]of n)f!==null&&f===r&&Object.assign(s,p);return s}async findLocatorForLocation(e,{strict:r=!1}={}){let s=new Wi,a=this.configuration.getLinkers(),n={project:this,report:s};for(let c of a){let f=await c.findPackageLocator(e,n);if(f){if(r&&(await c.findPackageLocation(f,n)).replace(nwe,"")!==e.replace(nwe,""))continue;return f}}return null}async loadUserConfig(){let e=K.join(this.cwd,".pnp.cjs");await le.existsPromise(e)&&kp(e).setup();let r=K.join(this.cwd,"yarn.config.cjs");return await le.existsPromise(r)?kp(r):null}async preparePackage(e,{resolver:r,resolveOptions:s}){let a=await this.configuration.getPackageExtensions(),n=this.configuration.normalizePackage(e,{packageExtensions:a});for(let[c,f]of n.dependencies){let p=await this.configuration.reduceHook(E=>E.reduceDependency,f,this,n,f,{resolver:r,resolveOptions:s});if(!XB(f,p))throw new Error("Assertion failed: The descriptor ident cannot be changed through aliases");let h=r.bindDescriptor(p,n,s);n.dependencies.set(c,h)}return n}async resolveEverything(e){if(!this.workspacesByCwd||!this.workspacesByIdent)throw new Error("Workspaces must have been setup before calling this function");this.forgetVirtualResolutions();let r=new Map(this.originalPackages),s=[];e.lockfileOnly||this.forgetTransientResolutions();let a=e.resolver||this.configuration.makeResolver(),n=new zI(a);await n.setup(this,{report:e.report});let c=e.lockfileOnly?[new GR(a)]:[n,a],f=new rm([new ZI(a),...c]),p=new rm([...c]),h=this.configuration.makeFetcher(),E=e.lockfileOnly?{project:this,report:e.report,resolver:f}:{project:this,report:e.report,resolver:f,fetchOptions:{project:this,cache:e.cache,checksums:this.storedChecksums,report:e.report,fetcher:h,cacheOptions:{mirrorWriteOnly:!0}}},C=new Map,S=new Map,P=new Map,I=new Map,R=new Map,N=new Map,U=this.topLevelWorkspace.anchoredLocator,W=new Set,te=[],ie=Tj(),Ae=this.configuration.getSupportedArchitectures();await e.report.startProgressPromise(ho.progressViaTitle(),async se=>{let X=async rt=>{let Fe=await GE(async()=>await f.resolve(rt,E),ke=>`${Yr(this.configuration,rt)}: ${ke}`);if(!$B(rt,Fe))throw new Error(`Assertion failed: The locator cannot be changed by the resolver (went from ${Yr(this.configuration,rt)} to ${Yr(this.configuration,Fe)})`);I.set(Fe.locatorHash,Fe),!r.delete(Fe.locatorHash)&&!this.tryWorkspaceByLocator(Fe)&&s.push(Fe);let Pe=await this.preparePackage(Fe,{resolver:f,resolveOptions:E}),Ye=Uu([...Pe.dependencies.values()].map(ke=>j(ke)));return te.push(Ye),Ye.catch(()=>{}),S.set(Pe.locatorHash,Pe),Pe},De=async rt=>{let Fe=R.get(rt.locatorHash);if(typeof Fe<"u")return Fe;let Ne=Promise.resolve().then(()=>X(rt));return R.set(rt.locatorHash,Ne),Ne},Re=async(rt,Fe)=>{let Ne=await j(Fe);return C.set(rt.descriptorHash,rt),P.set(rt.descriptorHash,Ne.locatorHash),Ne},gt=async rt=>{se.setTitle(ri(this.configuration,rt));let Fe=this.resolutionAliases.get(rt.descriptorHash);if(typeof Fe<"u")return Re(rt,this.storedDescriptors.get(Fe));let Ne=f.getResolutionDependencies(rt,E),Pe=Object.fromEntries(await Uu(Object.entries(Ne).map(async([it,_e])=>{let x=f.bindDescriptor(_e,U,E),w=await j(x);return W.add(w.locatorHash),[it,w]}))),ke=(await GE(async()=>await f.getCandidates(rt,Pe,E),it=>`${ri(this.configuration,rt)}: ${it}`))[0];if(typeof ke>"u")throw new Yt(82,`${ri(this.configuration,rt)}: No candidates found`);if(e.checkResolutions){let{locators:it}=await p.getSatisfying(rt,Pe,[ke],{...E,resolver:p});if(!it.find(_e=>_e.locatorHash===ke.locatorHash))throw new Yt(78,`Invalid resolution ${VB(this.configuration,rt,ke)}`)}return C.set(rt.descriptorHash,rt),P.set(rt.descriptorHash,ke.locatorHash),De(ke)},j=rt=>{let Fe=N.get(rt.descriptorHash);if(typeof Fe<"u")return Fe;C.set(rt.descriptorHash,rt);let Ne=Promise.resolve().then(()=>gt(rt));return N.set(rt.descriptorHash,Ne),Ne};for(let rt of this.workspaces){let Fe=rt.anchoredDescriptor;te.push(j(Fe))}for(;te.length>0;){let rt=[...te];te.length=0,await Uu(rt)}});let ce=Yl(r.values(),se=>this.tryWorkspaceByLocator(se)?Yl.skip:se);if(s.length>0||ce.length>0){let se=new Set(this.workspaces.flatMap(rt=>{let Fe=S.get(rt.anchoredLocator.locatorHash);if(!Fe)throw new Error("Assertion failed: The workspace should have been resolved");return Array.from(Fe.dependencies.values(),Ne=>{let Pe=P.get(Ne.descriptorHash);if(!Pe)throw new Error("Assertion failed: The resolution should have been registered");return Pe})})),X=rt=>se.has(rt.locatorHash)?"0":"1",De=rt=>cl(rt),Re=Ws(s,[X,De]),gt=Ws(ce,[X,De]),j=e.report.getRecommendedLength();Re.length>0&&e.report.reportInfo(85,`${Ut(this.configuration,"+",Ct.ADDED)} ${$k(this.configuration,Re,j)}`),gt.length>0&&e.report.reportInfo(85,`${Ut(this.configuration,"-",Ct.REMOVED)} ${$k(this.configuration,gt,j)}`)}let me=new Set(this.resolutionAliases.values()),pe=new Set(S.keys()),Be=new Set,Ce=new Map,g=[],we=new Map;Ywt({project:this,accessibleLocators:Be,volatileDescriptors:me,optionalBuilds:pe,peerRequirements:Ce,peerWarnings:g,peerRequirementNodes:we,allDescriptors:C,allResolutions:P,allPackages:S});for(let se of W)pe.delete(se);for(let se of me)C.delete(se),P.delete(se);let Ee=new Set,fe=new Set;for(let se of S.values())se.conditions!=null&&pe.has(se.locatorHash)&&(TQ(se,Ae)||(TQ(se,ie)&&e.report.reportWarningOnce(77,`${Yr(this.configuration,se)}: Your current architecture (${process.platform}-${process.arch}) is supported by this package, but is missing from the ${Ut(this.configuration,"supportedArchitectures",Ct.SETTING)} setting`),fe.add(se.locatorHash)),Ee.add(se.locatorHash));this.storedResolutions=P,this.storedDescriptors=C,this.storedPackages=S,this.accessibleLocators=Be,this.conditionalLocators=Ee,this.disabledLocators=fe,this.originalPackages=I,this.optionalBuilds=pe,this.peerRequirements=Ce,this.peerWarnings=g,this.peerRequirementNodes=we}async fetchEverything({cache:e,report:r,fetcher:s,mode:a,persistProject:n=!0}){let c={mockedPackages:this.disabledLocators,unstablePackages:this.conditionalLocators},f=s||this.configuration.makeFetcher(),p={checksums:this.storedChecksums,project:this,cache:e,fetcher:f,report:r,cacheOptions:c},h=Array.from(new Set(Ws(this.storedResolutions.values(),[I=>{let R=this.storedPackages.get(I);if(!R)throw new Error("Assertion failed: The locator should have been registered");return cl(R)}])));a==="update-lockfile"&&(h=h.filter(I=>!this.storedChecksums.has(I)));let E=!1,C=ho.progressViaCounter(h.length);await r.reportProgress(C);let S=(0,VR.default)(qwt);if(await Uu(h.map(I=>S(async()=>{let R=this.storedPackages.get(I);if(!R)throw new Error("Assertion failed: The locator should have been registered");if(Gu(R))return;let N;try{N=await f.fetch(R,p)}catch(U){U.message=`${Yr(this.configuration,R)}: ${U.message}`,r.reportExceptionOnce(U),E=U;return}N.checksum!=null?this.storedChecksums.set(R.locatorHash,N.checksum):this.storedChecksums.delete(R.locatorHash),N.releaseFs&&N.releaseFs()}).finally(()=>{C.tick()}))),E)throw E;let P=n&&a!=="update-lockfile"?await this.cacheCleanup({cache:e,report:r}):null;if(r.cacheMisses.size>0||P){let R=(await Promise.all([...r.cacheMisses].map(async ce=>{let me=this.storedPackages.get(ce),pe=this.storedChecksums.get(ce)??null,Be=e.getLocatorPath(me,pe);return(await le.statPromise(Be)).size}))).reduce((ce,me)=>ce+me,0)-(P?.size??0),N=r.cacheMisses.size,U=P?.count??0,W=`${Vk(N,{zero:"No new packages",one:"A package was",more:`${Ut(this.configuration,N,Ct.NUMBER)} packages were`})} added to the project`,te=`${Vk(U,{zero:"none were",one:"one was",more:`${Ut(this.configuration,U,Ct.NUMBER)} were`})} removed`,ie=R!==0?` (${Ut(this.configuration,R,Ct.SIZE_DIFF)})`:"",Ae=U>0?N>0?`${W}, and ${te}${ie}.`:`${W}, but ${te}${ie}.`:`${W}${ie}.`;r.reportInfo(13,Ae)}}async linkEverything({cache:e,report:r,fetcher:s,mode:a}){let n={mockedPackages:this.disabledLocators,unstablePackages:this.conditionalLocators,skipIntegrityCheck:!0},c=s||this.configuration.makeFetcher(),f={checksums:this.storedChecksums,project:this,cache:e,fetcher:c,report:r,cacheOptions:n},p=this.configuration.getLinkers(),h={project:this,report:r},E=new Map(p.map(Ee=>{let fe=Ee.makeInstaller(h),se=Ee.getCustomDataKey(),X=this.linkersCustomData.get(se);return typeof X<"u"&&fe.attachCustomData(X),[Ee,fe]})),C=new Map,S=new Map,P=new Map,I=new Map(await Uu([...this.accessibleLocators].map(async Ee=>{let fe=this.storedPackages.get(Ee);if(!fe)throw new Error("Assertion failed: The locator should have been registered");return[Ee,await c.fetch(fe,f)]}))),R=[],N=new Set,U=[];for(let Ee of this.accessibleLocators){let fe=this.storedPackages.get(Ee);if(typeof fe>"u")throw new Error("Assertion failed: The locator should have been registered");let se=I.get(fe.locatorHash);if(typeof se>"u")throw new Error("Assertion failed: The fetch result should have been registered");let X=[],De=gt=>{X.push(gt)},Re=this.tryWorkspaceByLocator(fe);if(Re!==null){let gt=[],{scripts:j}=Re.manifest;for(let Fe of["preinstall","install","postinstall"])j.has(Fe)&>.push({type:0,script:Fe});try{for(let[Fe,Ne]of E)if(Fe.supportsPackage(fe,h)&&(await Ne.installPackage(fe,se,{holdFetchResult:De})).buildRequest!==null)throw new Error("Assertion failed: Linkers can't return build directives for workspaces; this responsibility befalls to the Yarn core")}finally{X.length===0?se.releaseFs?.():R.push(Uu(X).catch(()=>{}).then(()=>{se.releaseFs?.()}))}let rt=K.join(se.packageFs.getRealPath(),se.prefixPath);S.set(fe.locatorHash,rt),!Gu(fe)&>.length>0&&P.set(fe.locatorHash,{buildDirectives:gt,buildLocations:[rt]})}else{let gt=p.find(Fe=>Fe.supportsPackage(fe,h));if(!gt)throw new Yt(12,`${Yr(this.configuration,fe)} isn't supported by any available linker`);let j=E.get(gt);if(!j)throw new Error("Assertion failed: The installer should have been registered");let rt;try{rt=await j.installPackage(fe,se,{holdFetchResult:De})}finally{X.length===0?se.releaseFs?.():R.push(Uu(X).then(()=>{}).then(()=>{se.releaseFs?.()}))}C.set(fe.locatorHash,gt),S.set(fe.locatorHash,rt.packageLocation),rt.buildRequest&&rt.packageLocation&&(rt.buildRequest.skipped?(N.add(fe.locatorHash),this.skippedBuilds.has(fe.locatorHash)||U.push([fe,rt.buildRequest.explain])):P.set(fe.locatorHash,{buildDirectives:rt.buildRequest.directives,buildLocations:[rt.packageLocation]}))}}let W=new Map;for(let Ee of this.accessibleLocators){let fe=this.storedPackages.get(Ee);if(!fe)throw new Error("Assertion failed: The locator should have been registered");let se=this.tryWorkspaceByLocator(fe)!==null,X=async(De,Re)=>{let gt=S.get(fe.locatorHash);if(typeof gt>"u")throw new Error(`Assertion failed: The package (${Yr(this.configuration,fe)}) should have been registered`);let j=[];for(let rt of fe.dependencies.values()){let Fe=this.storedResolutions.get(rt.descriptorHash);if(typeof Fe>"u")throw new Error(`Assertion failed: The resolution (${ri(this.configuration,rt)}, from ${Yr(this.configuration,fe)})should have been registered`);let Ne=this.storedPackages.get(Fe);if(typeof Ne>"u")throw new Error(`Assertion failed: The package (${Fe}, resolved from ${ri(this.configuration,rt)}) should have been registered`);let Pe=this.tryWorkspaceByLocator(Ne)===null?C.get(Fe):null;if(typeof Pe>"u")throw new Error(`Assertion failed: The package (${Fe}, resolved from ${ri(this.configuration,rt)}) should have been registered`);Pe===De||Pe===null?S.get(Ne.locatorHash)!==null&&j.push([rt,Ne]):!se&>!==null&&jB(W,Fe).push(gt)}gt!==null&&await Re.attachInternalDependencies(fe,j)};if(se)for(let[De,Re]of E)De.supportsPackage(fe,h)&&await X(De,Re);else{let De=C.get(fe.locatorHash);if(!De)throw new Error("Assertion failed: The linker should have been found");let Re=E.get(De);if(!Re)throw new Error("Assertion failed: The installer should have been registered");await X(De,Re)}}for(let[Ee,fe]of W){let se=this.storedPackages.get(Ee);if(!se)throw new Error("Assertion failed: The package should have been registered");let X=C.get(se.locatorHash);if(!X)throw new Error("Assertion failed: The linker should have been found");let De=E.get(X);if(!De)throw new Error("Assertion failed: The installer should have been registered");await De.attachExternalDependents(se,fe)}let te=new Map;for(let[Ee,fe]of E){let se=await fe.finalizeInstall();for(let X of se?.records??[])X.buildRequest.skipped?(N.add(X.locator.locatorHash),this.skippedBuilds.has(X.locator.locatorHash)||U.push([X.locator,X.buildRequest.explain])):P.set(X.locator.locatorHash,{buildDirectives:X.buildRequest.directives,buildLocations:X.buildLocations});typeof se?.customData<"u"&&te.set(Ee.getCustomDataKey(),se.customData)}if(this.linkersCustomData=te,await Uu(R),a==="skip-build")return;for(let[,Ee]of Ws(U,([fe])=>cl(fe)))Ee(r);let ie=new Set(P.keys()),Ae=(0,WR.createHash)("sha512");Ae.update(process.versions.node),await this.configuration.triggerHook(Ee=>Ee.globalHashGeneration,this,Ee=>{Ae.update("\0"),Ae.update(Ee)});let ce=Ae.digest("hex"),me=new Map,pe=Ee=>{let fe=me.get(Ee.locatorHash);if(typeof fe<"u")return fe;let se=this.storedPackages.get(Ee.locatorHash);if(typeof se>"u")throw new Error("Assertion failed: The package should have been registered");let X=(0,WR.createHash)("sha512");X.update(Ee.locatorHash),me.set(Ee.locatorHash,"");for(let De of se.dependencies.values()){let Re=this.storedResolutions.get(De.descriptorHash);if(typeof Re>"u")throw new Error(`Assertion failed: The resolution (${ri(this.configuration,De)}) should have been registered`);let gt=this.storedPackages.get(Re);if(typeof gt>"u")throw new Error("Assertion failed: The package should have been registered");X.update(pe(gt))}return fe=X.digest("hex"),me.set(Ee.locatorHash,fe),fe},Be=(Ee,fe)=>{let se=(0,WR.createHash)("sha512");se.update(ce),se.update(pe(Ee));for(let X of fe)se.update(X);return se.digest("hex")},Ce=new Map,g=!1,we=Ee=>{let fe=new Set([Ee.locatorHash]);for(let se of fe){let X=this.storedPackages.get(se);if(!X)throw new Error("Assertion failed: The package should have been registered");for(let De of X.dependencies.values()){let Re=this.storedResolutions.get(De.descriptorHash);if(!Re)throw new Error(`Assertion failed: The resolution (${ri(this.configuration,De)}) should have been registered`);if(Re!==Ee.locatorHash&&ie.has(Re))return!1;let gt=this.storedPackages.get(Re);if(!gt)throw new Error("Assertion failed: The package should have been registered");let j=this.tryWorkspaceByLocator(gt);if(j){if(j.anchoredLocator.locatorHash!==Ee.locatorHash&&ie.has(j.anchoredLocator.locatorHash))return!1;fe.add(j.anchoredLocator.locatorHash)}fe.add(Re)}}return!0};for(;ie.size>0;){let Ee=ie.size,fe=[];for(let se of ie){let X=this.storedPackages.get(se);if(!X)throw new Error("Assertion failed: The package should have been registered");if(!we(X))continue;let De=P.get(X.locatorHash);if(!De)throw new Error("Assertion failed: The build directive should have been registered");let Re=Be(X,De.buildLocations);if(this.storedBuildState.get(X.locatorHash)===Re){Ce.set(X.locatorHash,Re),ie.delete(se);continue}g||(await this.persistInstallStateFile(),g=!0),this.storedBuildState.has(X.locatorHash)?r.reportInfo(8,`${Yr(this.configuration,X)} must be rebuilt because its dependency tree changed`):r.reportInfo(7,`${Yr(this.configuration,X)} must be built because it never has been before or the last one failed`);let gt=De.buildLocations.map(async j=>{if(!K.isAbsolute(j))throw new Error(`Assertion failed: Expected the build location to be absolute (not ${j})`);for(let rt of De.buildDirectives){let Fe=`# This file contains the result of Yarn building a package (${cl(X)}) +`;switch(rt.type){case 0:Fe+=`# Script name: ${rt.script} +`;break;case 1:Fe+=`# Script code: ${rt.script} +`;break}let Ne=null;if(!await le.mktempPromise(async Ye=>{let ke=K.join(Ye,"build.log"),{stdout:it,stderr:_e}=this.configuration.getSubprocessStreams(ke,{header:Fe,prefix:Yr(this.configuration,X),report:r}),x;try{switch(rt.type){case 0:x=await MT(X,rt.script,[],{cwd:j,project:this,stdin:Ne,stdout:it,stderr:_e});break;case 1:x=await y6(X,rt.script,[],{cwd:j,project:this,stdin:Ne,stdout:it,stderr:_e});break}}catch(y){_e.write(y.stack),x=1}if(it.end(),_e.end(),x===0)return!0;le.detachTemp(Ye);let w=`${Yr(this.configuration,X)} couldn't be built successfully (exit code ${Ut(this.configuration,x,Ct.NUMBER)}, logs can be found here: ${Ut(this.configuration,ke,Ct.PATH)})`,b=this.optionalBuilds.has(X.locatorHash);return b?r.reportInfo(9,w):r.reportError(9,w),rye&&r.reportFold(ue.fromPortablePath(ke),le.readFileSync(ke,"utf8")),b}))return!1}return!0});fe.push(...gt,Promise.allSettled(gt).then(j=>{ie.delete(se),j.every(rt=>rt.status==="fulfilled"&&rt.value===!0)&&Ce.set(X.locatorHash,Re)}))}if(await Uu(fe),Ee===ie.size){let se=Array.from(ie).map(X=>{let De=this.storedPackages.get(X);if(!De)throw new Error("Assertion failed: The package should have been registered");return Yr(this.configuration,De)}).join(", ");r.reportError(3,`Some packages have circular dependencies that make their build order unsatisfiable - as a result they won't be built (affected packages are: ${se})`);break}}this.storedBuildState=Ce,this.skippedBuilds=N}async installWithNewReport(e,r){return(await Ot.start({configuration:this.configuration,json:e.json,stdout:e.stdout,forceSectionAlignment:!0,includeLogs:!e.json&&!e.quiet,includeVersion:!0},async a=>{await this.install({...r,report:a})})).exitCode()}async install(e){let r=this.configuration.get("nodeLinker");ze.telemetry?.reportInstall(r);let s=!1;if(await e.report.startTimerPromise("Project validation",{skipIfEmpty:!0},async()=>{this.configuration.get("enableOfflineMode")&&e.report.reportWarning(90,"Offline work is enabled; Yarn won't fetch packages from the remote registry if it can avoid it"),await this.configuration.triggerHook(E=>E.validateProject,this,{reportWarning:(E,C)=>{e.report.reportWarning(E,C)},reportError:(E,C)=>{e.report.reportError(E,C),s=!0}})}),s)return;let a=await this.configuration.getPackageExtensions();for(let E of a.values())for(let[,C]of E)for(let S of C)S.status="inactive";let n=K.join(this.cwd,Er.lockfile),c=null;if(e.immutable)try{c=await le.readFilePromise(n,"utf8")}catch(E){throw E.code==="ENOENT"?new Yt(28,"The lockfile would have been created by this install, which is explicitly forbidden."):E}await e.report.startTimerPromise("Resolution step",async()=>{await this.resolveEverything(e)}),await e.report.startTimerPromise("Post-resolution validation",{skipIfEmpty:!0},async()=>{Kwt(this,e.report);for(let[,E]of a)for(let[,C]of E)for(let S of C)if(S.userProvided){let P=Ut(this.configuration,S,Ct.PACKAGE_EXTENSION);switch(S.status){case"inactive":e.report.reportWarning(68,`${P}: No matching package in the dependency tree; you may not need this rule anymore.`);break;case"redundant":e.report.reportWarning(69,`${P}: This rule seems redundant when applied on the original package; the extension may have been applied upstream.`);break}}if(c!==null){let E=Id(c,this.generateLockfile());if(E!==c){let C=CCe(n,n,c,E,void 0,void 0,{maxEditLength:100});if(C){e.report.reportSeparator();for(let S of C.hunks){e.report.reportInfo(null,`@@ -${S.oldStart},${S.oldLines} +${S.newStart},${S.newLines} @@`);for(let P of S.lines)P.startsWith("+")?e.report.reportError(28,Ut(this.configuration,P,Ct.ADDED)):P.startsWith("-")?e.report.reportError(28,Ut(this.configuration,P,Ct.REMOVED)):e.report.reportInfo(null,Ut(this.configuration,P,"grey"))}e.report.reportSeparator()}throw new Yt(28,"The lockfile would have been modified by this install, which is explicitly forbidden.")}}});for(let E of a.values())for(let[,C]of E)for(let S of C)S.userProvided&&S.status==="active"&&ze.telemetry?.reportPackageExtension(Zd(S,Ct.PACKAGE_EXTENSION));await e.report.startTimerPromise("Fetch step",async()=>{await this.fetchEverything(e)});let f=e.immutable?[...new Set(this.configuration.get("immutablePatterns"))].sort():[],p=await Promise.all(f.map(async E=>DQ(E,{cwd:this.cwd})));(typeof e.persistProject>"u"||e.persistProject)&&await this.persist(),await e.report.startTimerPromise("Link step",async()=>{if(e.mode==="update-lockfile"){e.report.reportWarning(73,`Skipped due to ${Ut(this.configuration,"mode=update-lockfile",Ct.CODE)}`);return}await this.linkEverything(e);let E=await Promise.all(f.map(async C=>DQ(C,{cwd:this.cwd})));for(let C=0;C{await this.configuration.triggerHook(E=>E.validateProjectAfterInstall,this,{reportWarning:(E,C)=>{e.report.reportWarning(E,C)},reportError:(E,C)=>{e.report.reportError(E,C),h=!0}})}),!h&&await this.configuration.triggerHook(E=>E.afterAllInstalled,this,e)}generateLockfile(){let e=new Map;for(let[n,c]of this.storedResolutions.entries()){let f=e.get(c);f||e.set(c,f=new Set),f.add(n)}let r={},{cacheKey:s}=Jr.getCacheKey(this.configuration);r.__metadata={version:KR,cacheKey:s};for(let[n,c]of e.entries()){let f=this.originalPackages.get(n);if(!f)continue;let p=[];for(let C of c){let S=this.storedDescriptors.get(C);if(!S)throw new Error("Assertion failed: The descriptor should have been registered");p.push(S)}let h=p.map(C=>ll(C)).sort().join(", "),E=new Ht;E.version=f.linkType==="HARD"?f.version:"0.0.0-use.local",E.languageName=f.languageName,E.dependencies=new Map(f.dependencies),E.peerDependencies=new Map(f.peerDependencies),E.dependenciesMeta=new Map(f.dependenciesMeta),E.peerDependenciesMeta=new Map(f.peerDependenciesMeta),E.bin=new Map(f.bin),r[h]={...E.exportTo({},{compatibilityMode:!1}),linkType:f.linkType.toLowerCase(),resolution:cl(f),checksum:this.storedChecksums.get(f.locatorHash),conditions:f.conditions||void 0}}return`${[`# This file is generated by running "yarn install" inside your project. +`,`# Manual changes might be lost - proceed with caution! +`].join("")} +`+il(r)}async persistLockfile(){let e=K.join(this.cwd,Er.lockfile),r="";try{r=await le.readFilePromise(e,"utf8")}catch{}let s=this.generateLockfile(),a=Id(r,s);a!==r&&(await le.writeFilePromise(e,a),this.lockFileChecksum=iwe(a),this.lockfileNeedsRefresh=!1)}async persistInstallStateFile(){let e=[];for(let c of Object.values(bG))e.push(...c);let r=(0,YR.default)(this,e),s=PG.default.serialize(r),a=us(s);if(this.installStateChecksum===a)return;let n=this.configuration.get("installStatePath");await le.mkdirPromise(K.dirname(n),{recursive:!0}),await le.writeFilePromise(n,await Gwt(s)),this.installStateChecksum=a}async restoreInstallState({restoreLinkersCustomData:e=!0,restoreResolutions:r=!0,restoreBuildState:s=!0}={}){let a=this.configuration.get("installStatePath"),n;try{let c=await Wwt(await le.readFilePromise(a));n=PG.default.deserialize(c),this.installStateChecksum=us(c)}catch{r&&await this.applyLightResolution();return}e&&typeof n.linkersCustomData<"u"&&(this.linkersCustomData=n.linkersCustomData),s&&Object.assign(this,(0,YR.default)(n,bG.restoreBuildState)),r&&(n.lockFileChecksum===this.lockFileChecksum?Object.assign(this,(0,YR.default)(n,bG.restoreResolutions)):await this.applyLightResolution())}async applyLightResolution(){await this.resolveEverything({lockfileOnly:!0,report:new Wi}),await this.persistInstallStateFile()}async persist(){let e=(0,VR.default)(4);await Promise.all([this.persistLockfile(),...this.workspaces.map(r=>e(()=>r.persistManifest()))])}async cacheCleanup({cache:e,report:r}){if(this.configuration.get("enableGlobalCache"))return null;let s=new Set([".gitignore"]);if(!hH(e.cwd,this.cwd)||!await le.existsPromise(e.cwd))return null;let a=[];for(let c of await le.readdirPromise(e.cwd)){if(s.has(c))continue;let f=K.resolve(e.cwd,c);e.markedFiles.has(f)||(e.immutable?r.reportError(56,`${Ut(this.configuration,K.basename(f),"magenta")} appears to be unused and would be marked for deletion, but the cache is immutable`):a.push(le.lstatPromise(f).then(async p=>(await le.removePromise(f),p.size))))}if(a.length===0)return null;let n=await Promise.all(a);return{count:a.length,size:n.reduce((c,f)=>c+f,0)}}}});function Jwt(t){let s=Math.floor(t.timeNow/864e5),a=t.updateInterval*864e5,n=t.state.lastUpdate??t.timeNow+a+Math.floor(a*t.randomInitialInterval),c=n+a,f=t.state.lastTips??s*864e5,p=f+864e5+8*36e5-t.timeZone,h=c<=t.timeNow,E=p<=t.timeNow,C=null;return(h||E||!t.state.lastUpdate||!t.state.lastTips)&&(C={},C.lastUpdate=h?t.timeNow:n,C.lastTips=f,C.blocks=h?{}:t.state.blocks,C.displayedTips=t.state.displayedTips),{nextState:C,triggerUpdate:h,triggerTips:E,nextTips:E?s*864e5:f}}var eC,awe=It(()=>{bt();Rv();I0();pT();kc();Np();eC=class{constructor(e,r){this.values=new Map;this.hits=new Map;this.enumerators=new Map;this.nextTips=0;this.displayedTips=[];this.shouldCommitTips=!1;this.configuration=e;let s=this.getRegistryPath();this.isNew=!le.existsSync(s),this.shouldShowTips=!1,this.sendReport(r),this.startBuffer()}commitTips(){this.shouldShowTips&&(this.shouldCommitTips=!0)}selectTip(e){let r=new Set(this.displayedTips),s=f=>f&&un?eA(un,f):!1,a=e.map((f,p)=>p).filter(f=>e[f]&&s(e[f]?.selector));if(a.length===0)return null;let n=a.filter(f=>!r.has(f));if(n.length===0){let f=Math.floor(a.length*.2);this.displayedTips=f>0?this.displayedTips.slice(-f):[],n=a.filter(p=>!r.has(p))}let c=n[Math.floor(Math.random()*n.length)];return this.displayedTips.push(c),this.commitTips(),e[c]}reportVersion(e){this.reportValue("version",e.replace(/-git\..*/,"-git"))}reportCommandName(e){this.reportValue("commandName",e||"")}reportPluginName(e){this.reportValue("pluginName",e)}reportProject(e){this.reportEnumerator("projectCount",e)}reportInstall(e){this.reportHit("installCount",e)}reportPackageExtension(e){this.reportValue("packageExtension",e)}reportWorkspaceCount(e){this.reportValue("workspaceCount",String(e))}reportDependencyCount(e){this.reportValue("dependencyCount",String(e))}reportValue(e,r){xp(this.values,e).add(r)}reportEnumerator(e,r){xp(this.enumerators,e).add(us(r))}reportHit(e,r="*"){let s=A3(this.hits,e),a=Vl(s,r,()=>0);s.set(r,a+1)}getRegistryPath(){let e=this.configuration.get("globalFolder");return K.join(e,"telemetry.json")}sendReport(e){let r=this.getRegistryPath(),s;try{s=le.readJsonSync(r)}catch{s={}}let{nextState:a,triggerUpdate:n,triggerTips:c,nextTips:f}=Jwt({state:s,timeNow:Date.now(),timeZone:new Date().getTimezoneOffset()*60*1e3,randomInitialInterval:Math.random(),updateInterval:this.configuration.get("telemetryInterval")});if(this.nextTips=f,this.displayedTips=s.displayedTips??[],a!==null)try{le.mkdirSync(K.dirname(r),{recursive:!0}),le.writeJsonSync(r,a)}catch{return!1}if(c&&this.configuration.get("enableTips")&&(this.shouldShowTips=!0),n){let p=s.blocks??{};if(Object.keys(p).length===0){let h=`https://browser-http-intake.logs.datadoghq.eu/v1/input/${e}?ddsource=yarn`,E=C=>Qj(h,C,{configuration:this.configuration}).catch(()=>{});for(let[C,S]of Object.entries(s.blocks??{})){if(Object.keys(S).length===0)continue;let P=S;P.userId=C,P.reportType="primary";for(let N of Object.keys(P.enumerators??{}))P.enumerators[N]=P.enumerators[N].length;E(P);let I=new Map,R=20;for(let[N,U]of Object.entries(P.values))U.length>0&&I.set(N,U.slice(0,R));for(;I.size>0;){let N={};N.userId=C,N.reportType="secondary",N.metrics={};for(let[U,W]of I)N.metrics[U]=W.shift(),W.length===0&&I.delete(U);E(N)}}}}return!0}applyChanges(){let e=this.getRegistryPath(),r;try{r=le.readJsonSync(e)}catch{r={}}let s=this.configuration.get("telemetryUserId")??"*",a=r.blocks=r.blocks??{},n=a[s]=a[s]??{};for(let c of this.hits.keys()){let f=n.hits=n.hits??{},p=f[c]=f[c]??{};for(let[h,E]of this.hits.get(c))p[h]=(p[h]??0)+E}for(let c of["values","enumerators"])for(let f of this[c].keys()){let p=n[c]=n[c]??{};p[f]=[...new Set([...p[f]??[],...this[c].get(f)??[]])]}this.shouldCommitTips&&(r.lastTips=this.nextTips,r.displayedTips=this.displayedTips),le.mkdirSync(K.dirname(e),{recursive:!0}),le.writeJsonSync(e,r)}startBuffer(){process.on("exit",()=>{try{this.applyChanges()}catch{}})}}});var nS={};Vt(nS,{BuildDirectiveType:()=>HR,CACHE_CHECKPOINT:()=>lG,CACHE_VERSION:()=>UR,Cache:()=>Jr,Configuration:()=>ze,DEFAULT_RC_FILENAME:()=>Mj,FormatType:()=>Lde,InstallMode:()=>ec,LEGACY_PLUGINS:()=>Ev,LOCKFILE_VERSION:()=>KR,LegacyMigrationResolver:()=>zI,LightReport:()=>uA,LinkType:()=>VE,LockfileResolver:()=>ZI,Manifest:()=>Ht,MessageName:()=>Dr,MultiFetcher:()=>lI,PackageExtensionStatus:()=>d3,PackageExtensionType:()=>g3,PeerWarningType:()=>JR,Project:()=>Tt,Report:()=>ho,ReportError:()=>Yt,SettingsType:()=>Iv,StreamReport:()=>Ot,TAG_REGEXP:()=>Hp,TelemetryManager:()=>eC,ThrowReport:()=>Wi,VirtualFetcher:()=>cI,WindowsLinkType:()=>IT,Workspace:()=>$I,WorkspaceFetcher:()=>uI,WorkspaceResolver:()=>yi,YarnVersion:()=>un,execUtils:()=>Gr,folderUtils:()=>OQ,formatUtils:()=>he,hashUtils:()=>Nn,httpUtils:()=>An,miscUtils:()=>je,nodeUtils:()=>As,parseMessageName:()=>rk,reportOptionDeprecations:()=>DI,scriptUtils:()=>In,semverUtils:()=>Or,stringifyMessageName:()=>Vf,structUtils:()=>q,tgzUtils:()=>hs,treeUtils:()=>ks});var Ve=It(()=>{dT();LQ();Qc();I0();pT();kc();gT();w6();Np();Yo();oCe();pCe();cG();Cv();Cv();dCe();uG();mCe();fG();oI();nk();nH();owe();Fc();Fv();awe();SG();sH();oH();tm();DG();Rv();BAe()});var pwe=L((hnr,sS)=>{"use strict";var Zwt=process.env.TERM_PROGRAM==="Hyper",Xwt=process.platform==="win32",uwe=process.platform==="linux",QG={ballotDisabled:"\u2612",ballotOff:"\u2610",ballotOn:"\u2611",bullet:"\u2022",bulletWhite:"\u25E6",fullBlock:"\u2588",heart:"\u2764",identicalTo:"\u2261",line:"\u2500",mark:"\u203B",middot:"\xB7",minus:"\uFF0D",multiplication:"\xD7",obelus:"\xF7",pencilDownRight:"\u270E",pencilRight:"\u270F",pencilUpRight:"\u2710",percent:"%",pilcrow2:"\u2761",pilcrow:"\xB6",plusMinus:"\xB1",section:"\xA7",starsOff:"\u2606",starsOn:"\u2605",upDownArrow:"\u2195"},fwe=Object.assign({},QG,{check:"\u221A",cross:"\xD7",ellipsisLarge:"...",ellipsis:"...",info:"i",question:"?",questionSmall:"?",pointer:">",pointerSmall:"\xBB",radioOff:"( )",radioOn:"(*)",warning:"\u203C"}),Awe=Object.assign({},QG,{ballotCross:"\u2718",check:"\u2714",cross:"\u2716",ellipsisLarge:"\u22EF",ellipsis:"\u2026",info:"\u2139",question:"?",questionFull:"\uFF1F",questionSmall:"\uFE56",pointer:uwe?"\u25B8":"\u276F",pointerSmall:uwe?"\u2023":"\u203A",radioOff:"\u25EF",radioOn:"\u25C9",warning:"\u26A0"});sS.exports=Xwt&&!Zwt?fwe:Awe;Reflect.defineProperty(sS.exports,"common",{enumerable:!1,value:QG});Reflect.defineProperty(sS.exports,"windows",{enumerable:!1,value:fwe});Reflect.defineProperty(sS.exports,"other",{enumerable:!1,value:Awe})});var Ju=L((gnr,TG)=>{"use strict";var $wt=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),e1t=/[\u001b\u009b][[\]#;?()]*(?:(?:(?:[^\W_]*;?[^\W_]*)\u0007)|(?:(?:[0-9]{1,4}(;[0-9]{0,4})*)?[~0-9=<>cf-nqrtyA-PRZ]))/g,hwe=()=>{let t={enabled:!0,visible:!0,styles:{},keys:{}};"FORCE_COLOR"in process.env&&(t.enabled=process.env.FORCE_COLOR!=="0");let e=n=>{let c=n.open=`\x1B[${n.codes[0]}m`,f=n.close=`\x1B[${n.codes[1]}m`,p=n.regex=new RegExp(`\\u001b\\[${n.codes[1]}m`,"g");return n.wrap=(h,E)=>{h.includes(f)&&(h=h.replace(p,f+c));let C=c+h+f;return E?C.replace(/\r*\n/g,`${f}$&${c}`):C},n},r=(n,c,f)=>typeof n=="function"?n(c):n.wrap(c,f),s=(n,c)=>{if(n===""||n==null)return"";if(t.enabled===!1)return n;if(t.visible===!1)return"";let f=""+n,p=f.includes(` +`),h=c.length;for(h>0&&c.includes("unstyle")&&(c=[...new Set(["unstyle",...c])].reverse());h-- >0;)f=r(t.styles[c[h]],f,p);return f},a=(n,c,f)=>{t.styles[n]=e({name:n,codes:c}),(t.keys[f]||(t.keys[f]=[])).push(n),Reflect.defineProperty(t,n,{configurable:!0,enumerable:!0,set(h){t.alias(n,h)},get(){let h=E=>s(E,h.stack);return Reflect.setPrototypeOf(h,t),h.stack=this.stack?this.stack.concat(n):[n],h}})};return a("reset",[0,0],"modifier"),a("bold",[1,22],"modifier"),a("dim",[2,22],"modifier"),a("italic",[3,23],"modifier"),a("underline",[4,24],"modifier"),a("inverse",[7,27],"modifier"),a("hidden",[8,28],"modifier"),a("strikethrough",[9,29],"modifier"),a("black",[30,39],"color"),a("red",[31,39],"color"),a("green",[32,39],"color"),a("yellow",[33,39],"color"),a("blue",[34,39],"color"),a("magenta",[35,39],"color"),a("cyan",[36,39],"color"),a("white",[37,39],"color"),a("gray",[90,39],"color"),a("grey",[90,39],"color"),a("bgBlack",[40,49],"bg"),a("bgRed",[41,49],"bg"),a("bgGreen",[42,49],"bg"),a("bgYellow",[43,49],"bg"),a("bgBlue",[44,49],"bg"),a("bgMagenta",[45,49],"bg"),a("bgCyan",[46,49],"bg"),a("bgWhite",[47,49],"bg"),a("blackBright",[90,39],"bright"),a("redBright",[91,39],"bright"),a("greenBright",[92,39],"bright"),a("yellowBright",[93,39],"bright"),a("blueBright",[94,39],"bright"),a("magentaBright",[95,39],"bright"),a("cyanBright",[96,39],"bright"),a("whiteBright",[97,39],"bright"),a("bgBlackBright",[100,49],"bgBright"),a("bgRedBright",[101,49],"bgBright"),a("bgGreenBright",[102,49],"bgBright"),a("bgYellowBright",[103,49],"bgBright"),a("bgBlueBright",[104,49],"bgBright"),a("bgMagentaBright",[105,49],"bgBright"),a("bgCyanBright",[106,49],"bgBright"),a("bgWhiteBright",[107,49],"bgBright"),t.ansiRegex=e1t,t.hasColor=t.hasAnsi=n=>(t.ansiRegex.lastIndex=0,typeof n=="string"&&n!==""&&t.ansiRegex.test(n)),t.alias=(n,c)=>{let f=typeof c=="string"?t[c]:c;if(typeof f!="function")throw new TypeError("Expected alias to be the name of an existing color (string) or a function");f.stack||(Reflect.defineProperty(f,"name",{value:n}),t.styles[n]=f,f.stack=[n]),Reflect.defineProperty(t,n,{configurable:!0,enumerable:!0,set(p){t.alias(n,p)},get(){let p=h=>s(h,p.stack);return Reflect.setPrototypeOf(p,t),p.stack=this.stack?this.stack.concat(f.stack):f.stack,p}})},t.theme=n=>{if(!$wt(n))throw new TypeError("Expected theme to be an object");for(let c of Object.keys(n))t.alias(c,n[c]);return t},t.alias("unstyle",n=>typeof n=="string"&&n!==""?(t.ansiRegex.lastIndex=0,n.replace(t.ansiRegex,"")):""),t.alias("noop",n=>n),t.none=t.clear=t.noop,t.stripColor=t.unstyle,t.symbols=pwe(),t.define=a,t};TG.exports=hwe();TG.exports.create=hwe});var $o=L(pn=>{"use strict";var t1t=Object.prototype.toString,Gc=Ju(),gwe=!1,RG=[],dwe={yellow:"blue",cyan:"red",green:"magenta",black:"white",blue:"yellow",red:"cyan",magenta:"green",white:"black"};pn.longest=(t,e)=>t.reduce((r,s)=>Math.max(r,e?s[e].length:s.length),0);pn.hasColor=t=>!!t&&Gc.hasColor(t);var ZR=pn.isObject=t=>t!==null&&typeof t=="object"&&!Array.isArray(t);pn.nativeType=t=>t1t.call(t).slice(8,-1).toLowerCase().replace(/\s/g,"");pn.isAsyncFn=t=>pn.nativeType(t)==="asyncfunction";pn.isPrimitive=t=>t!=null&&typeof t!="object"&&typeof t!="function";pn.resolve=(t,e,...r)=>typeof e=="function"?e.call(t,...r):e;pn.scrollDown=(t=[])=>[...t.slice(1),t[0]];pn.scrollUp=(t=[])=>[t.pop(),...t];pn.reorder=(t=[])=>{let e=t.slice();return e.sort((r,s)=>r.index>s.index?1:r.index{let s=t.length,a=r===s?0:r<0?s-1:r,n=t[e];t[e]=t[a],t[a]=n};pn.width=(t,e=80)=>{let r=t&&t.columns?t.columns:e;return t&&typeof t.getWindowSize=="function"&&(r=t.getWindowSize()[0]),process.platform==="win32"?r-1:r};pn.height=(t,e=20)=>{let r=t&&t.rows?t.rows:e;return t&&typeof t.getWindowSize=="function"&&(r=t.getWindowSize()[1]),r};pn.wordWrap=(t,e={})=>{if(!t)return t;typeof e=="number"&&(e={width:e});let{indent:r="",newline:s=` +`+r,width:a=80}=e,n=(s+r).match(/[^\S\n]/g)||[];a-=n.length;let c=`.{1,${a}}([\\s\\u200B]+|$)|[^\\s\\u200B]+?([\\s\\u200B]+|$)`,f=t.trim(),p=new RegExp(c,"g"),h=f.match(p)||[];return h=h.map(E=>E.replace(/\n$/,"")),e.padEnd&&(h=h.map(E=>E.padEnd(a," "))),e.padStart&&(h=h.map(E=>E.padStart(a," "))),r+h.join(s)};pn.unmute=t=>{let e=t.stack.find(s=>Gc.keys.color.includes(s));return e?Gc[e]:t.stack.find(s=>s.slice(2)==="bg")?Gc[e.slice(2)]:s=>s};pn.pascal=t=>t?t[0].toUpperCase()+t.slice(1):"";pn.inverse=t=>{if(!t||!t.stack)return t;let e=t.stack.find(s=>Gc.keys.color.includes(s));if(e){let s=Gc["bg"+pn.pascal(e)];return s?s.black:t}let r=t.stack.find(s=>s.slice(0,2)==="bg");return r?Gc[r.slice(2).toLowerCase()]||t:Gc.none};pn.complement=t=>{if(!t||!t.stack)return t;let e=t.stack.find(s=>Gc.keys.color.includes(s)),r=t.stack.find(s=>s.slice(0,2)==="bg");if(e&&!r)return Gc[dwe[e]||e];if(r){let s=r.slice(2).toLowerCase(),a=dwe[s];return a&&Gc["bg"+pn.pascal(a)]||t}return Gc.none};pn.meridiem=t=>{let e=t.getHours(),r=t.getMinutes(),s=e>=12?"pm":"am";e=e%12;let a=e===0?12:e,n=r<10?"0"+r:r;return a+":"+n+" "+s};pn.set=(t={},e="",r)=>e.split(".").reduce((s,a,n,c)=>{let f=c.length-1>n?s[a]||{}:r;return!pn.isObject(f)&&n{let s=t[e]==null?e.split(".").reduce((a,n)=>a&&a[n],t):t[e];return s??r};pn.mixin=(t,e)=>{if(!ZR(t))return e;if(!ZR(e))return t;for(let r of Object.keys(e)){let s=Object.getOwnPropertyDescriptor(e,r);if(s.hasOwnProperty("value"))if(t.hasOwnProperty(r)&&ZR(s.value)){let a=Object.getOwnPropertyDescriptor(t,r);ZR(a.value)?t[r]=pn.merge({},t[r],e[r]):Reflect.defineProperty(t,r,s)}else Reflect.defineProperty(t,r,s);else Reflect.defineProperty(t,r,s)}return t};pn.merge=(...t)=>{let e={};for(let r of t)pn.mixin(e,r);return e};pn.mixinEmitter=(t,e)=>{let r=e.constructor.prototype;for(let s of Object.keys(r)){let a=r[s];typeof a=="function"?pn.define(t,s,a.bind(e)):pn.define(t,s,a)}};pn.onExit=t=>{let e=(r,s)=>{gwe||(gwe=!0,RG.forEach(a=>a()),r===!0&&process.exit(128+s))};RG.length===0&&(process.once("SIGTERM",e.bind(null,!0,15)),process.once("SIGINT",e.bind(null,!0,2)),process.once("exit",e)),RG.push(t)};pn.define=(t,e,r)=>{Reflect.defineProperty(t,e,{value:r})};pn.defineExport=(t,e,r)=>{let s;Reflect.defineProperty(t,e,{enumerable:!0,configurable:!0,set(a){s=a},get(){return s?s():r()}})}});var mwe=L(iC=>{"use strict";iC.ctrl={a:"first",b:"backward",c:"cancel",d:"deleteForward",e:"last",f:"forward",g:"reset",i:"tab",k:"cutForward",l:"reset",n:"newItem",m:"cancel",j:"submit",p:"search",r:"remove",s:"save",u:"undo",w:"cutLeft",x:"toggleCursor",v:"paste"};iC.shift={up:"shiftUp",down:"shiftDown",left:"shiftLeft",right:"shiftRight",tab:"prev"};iC.fn={up:"pageUp",down:"pageDown",left:"pageLeft",right:"pageRight",delete:"deleteForward"};iC.option={b:"backward",f:"forward",d:"cutRight",left:"cutLeft",up:"altUp",down:"altDown"};iC.keys={pageup:"pageUp",pagedown:"pageDown",home:"home",end:"end",cancel:"cancel",delete:"deleteForward",backspace:"delete",down:"down",enter:"submit",escape:"cancel",left:"left",space:"space",number:"number",return:"submit",right:"right",tab:"next",up:"up"}});var Iwe=L((ynr,Ewe)=>{"use strict";var ywe=ye("readline"),r1t=mwe(),n1t=/^(?:\x1b)([a-zA-Z0-9])$/,i1t=/^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/,s1t={OP:"f1",OQ:"f2",OR:"f3",OS:"f4","[11~":"f1","[12~":"f2","[13~":"f3","[14~":"f4","[[A":"f1","[[B":"f2","[[C":"f3","[[D":"f4","[[E":"f5","[15~":"f5","[17~":"f6","[18~":"f7","[19~":"f8","[20~":"f9","[21~":"f10","[23~":"f11","[24~":"f12","[A":"up","[B":"down","[C":"right","[D":"left","[E":"clear","[F":"end","[H":"home",OA:"up",OB:"down",OC:"right",OD:"left",OE:"clear",OF:"end",OH:"home","[1~":"home","[2~":"insert","[3~":"delete","[4~":"end","[5~":"pageup","[6~":"pagedown","[[5~":"pageup","[[6~":"pagedown","[7~":"home","[8~":"end","[a":"up","[b":"down","[c":"right","[d":"left","[e":"clear","[2$":"insert","[3$":"delete","[5$":"pageup","[6$":"pagedown","[7$":"home","[8$":"end",Oa:"up",Ob:"down",Oc:"right",Od:"left",Oe:"clear","[2^":"insert","[3^":"delete","[5^":"pageup","[6^":"pagedown","[7^":"home","[8^":"end","[Z":"tab"};function o1t(t){return["[a","[b","[c","[d","[e","[2$","[3$","[5$","[6$","[7$","[8$","[Z"].includes(t)}function a1t(t){return["Oa","Ob","Oc","Od","Oe","[2^","[3^","[5^","[6^","[7^","[8^"].includes(t)}var XR=(t="",e={})=>{let r,s={name:e.name,ctrl:!1,meta:!1,shift:!1,option:!1,sequence:t,raw:t,...e};if(Buffer.isBuffer(t)?t[0]>127&&t[1]===void 0?(t[0]-=128,t="\x1B"+String(t)):t=String(t):t!==void 0&&typeof t!="string"?t=String(t):t||(t=s.sequence||""),s.sequence=s.sequence||t||s.name,t==="\r")s.raw=void 0,s.name="return";else if(t===` +`)s.name="enter";else if(t===" ")s.name="tab";else if(t==="\b"||t==="\x7F"||t==="\x1B\x7F"||t==="\x1B\b")s.name="backspace",s.meta=t.charAt(0)==="\x1B";else if(t==="\x1B"||t==="\x1B\x1B")s.name="escape",s.meta=t.length===2;else if(t===" "||t==="\x1B ")s.name="space",s.meta=t.length===2;else if(t<="")s.name=String.fromCharCode(t.charCodeAt(0)+97-1),s.ctrl=!0;else if(t.length===1&&t>="0"&&t<="9")s.name="number";else if(t.length===1&&t>="a"&&t<="z")s.name=t;else if(t.length===1&&t>="A"&&t<="Z")s.name=t.toLowerCase(),s.shift=!0;else if(r=n1t.exec(t))s.meta=!0,s.shift=/^[A-Z]$/.test(r[1]);else if(r=i1t.exec(t)){let a=[...t];a[0]==="\x1B"&&a[1]==="\x1B"&&(s.option=!0);let n=[r[1],r[2],r[4],r[6]].filter(Boolean).join(""),c=(r[3]||r[5]||1)-1;s.ctrl=!!(c&4),s.meta=!!(c&10),s.shift=!!(c&1),s.code=n,s.name=s1t[n],s.shift=o1t(n)||s.shift,s.ctrl=a1t(n)||s.ctrl}return s};XR.listen=(t={},e)=>{let{stdin:r}=t;if(!r||r!==process.stdin&&!r.isTTY)throw new Error("Invalid stream passed");let s=ywe.createInterface({terminal:!0,input:r});ywe.emitKeypressEvents(r,s);let a=(f,p)=>e(f,XR(f,p),s),n=r.isRaw;return r.isTTY&&r.setRawMode(!0),r.on("keypress",a),s.resume(),()=>{r.isTTY&&r.setRawMode(n),r.removeListener("keypress",a),s.pause(),s.close()}};XR.action=(t,e,r)=>{let s={...r1t,...r};return e.ctrl?(e.action=s.ctrl[e.name],e):e.option&&s.option?(e.action=s.option[e.name],e):e.shift?(e.action=s.shift[e.name],e):(e.action=s.keys[e.name],e)};Ewe.exports=XR});var wwe=L((Enr,Cwe)=>{"use strict";Cwe.exports=t=>{t.timers=t.timers||{};let e=t.options.timers;if(e)for(let r of Object.keys(e)){let s=e[r];typeof s=="number"&&(s={interval:s}),l1t(t,r,s)}};function l1t(t,e,r={}){let s=t.timers[e]={name:e,start:Date.now(),ms:0,tick:0},a=r.interval||120;s.frames=r.frames||[],s.loading=!0;let n=setInterval(()=>{s.ms=Date.now()-s.start,s.tick++,t.render()},a);return s.stop=()=>{s.loading=!1,clearInterval(n)},Reflect.defineProperty(s,"interval",{value:n}),t.once("close",()=>s.stop()),s.stop}});var vwe=L((Inr,Bwe)=>{"use strict";var{define:c1t,width:u1t}=$o(),FG=class{constructor(e){let r=e.options;c1t(this,"_prompt",e),this.type=e.type,this.name=e.name,this.message="",this.header="",this.footer="",this.error="",this.hint="",this.input="",this.cursor=0,this.index=0,this.lines=0,this.tick=0,this.prompt="",this.buffer="",this.width=u1t(r.stdout||process.stdout),Object.assign(this,r),this.name=this.name||this.message,this.message=this.message||this.name,this.symbols=e.symbols,this.styles=e.styles,this.required=new Set,this.cancelled=!1,this.submitted=!1}clone(){let e={...this};return e.status=this.status,e.buffer=Buffer.from(e.buffer),delete e.clone,e}set color(e){this._color=e}get color(){let e=this.prompt.styles;if(this.cancelled)return e.cancelled;if(this.submitted)return e.submitted;let r=this._color||e[this.status];return typeof r=="function"?r:e.pending}set loading(e){this._loading=e}get loading(){return typeof this._loading=="boolean"?this._loading:this.loadingChoices?"choices":!1}get status(){return this.cancelled?"cancelled":this.submitted?"submitted":"pending"}};Bwe.exports=FG});var Dwe=L((Cnr,Swe)=>{"use strict";var NG=$o(),mo=Ju(),OG={default:mo.noop,noop:mo.noop,set inverse(t){this._inverse=t},get inverse(){return this._inverse||NG.inverse(this.primary)},set complement(t){this._complement=t},get complement(){return this._complement||NG.complement(this.primary)},primary:mo.cyan,success:mo.green,danger:mo.magenta,strong:mo.bold,warning:mo.yellow,muted:mo.dim,disabled:mo.gray,dark:mo.dim.gray,underline:mo.underline,set info(t){this._info=t},get info(){return this._info||this.primary},set em(t){this._em=t},get em(){return this._em||this.primary.underline},set heading(t){this._heading=t},get heading(){return this._heading||this.muted.underline},set pending(t){this._pending=t},get pending(){return this._pending||this.primary},set submitted(t){this._submitted=t},get submitted(){return this._submitted||this.success},set cancelled(t){this._cancelled=t},get cancelled(){return this._cancelled||this.danger},set typing(t){this._typing=t},get typing(){return this._typing||this.dim},set placeholder(t){this._placeholder=t},get placeholder(){return this._placeholder||this.primary.dim},set highlight(t){this._highlight=t},get highlight(){return this._highlight||this.inverse}};OG.merge=(t={})=>{t.styles&&typeof t.styles.enabled=="boolean"&&(mo.enabled=t.styles.enabled),t.styles&&typeof t.styles.visible=="boolean"&&(mo.visible=t.styles.visible);let e=NG.merge({},OG,t.styles);delete e.merge;for(let r of Object.keys(mo))e.hasOwnProperty(r)||Reflect.defineProperty(e,r,{get:()=>mo[r]});for(let r of Object.keys(mo.styles))e.hasOwnProperty(r)||Reflect.defineProperty(e,r,{get:()=>mo[r]});return e};Swe.exports=OG});var Pwe=L((wnr,bwe)=>{"use strict";var LG=process.platform==="win32",$p=Ju(),f1t=$o(),MG={...$p.symbols,upDownDoubleArrow:"\u21D5",upDownDoubleArrow2:"\u2B0D",upDownArrow:"\u2195",asterisk:"*",asterism:"\u2042",bulletWhite:"\u25E6",electricArrow:"\u2301",ellipsisLarge:"\u22EF",ellipsisSmall:"\u2026",fullBlock:"\u2588",identicalTo:"\u2261",indicator:$p.symbols.check,leftAngle:"\u2039",mark:"\u203B",minus:"\u2212",multiplication:"\xD7",obelus:"\xF7",percent:"%",pilcrow:"\xB6",pilcrow2:"\u2761",pencilUpRight:"\u2710",pencilDownRight:"\u270E",pencilRight:"\u270F",plus:"+",plusMinus:"\xB1",pointRight:"\u261E",rightAngle:"\u203A",section:"\xA7",hexagon:{off:"\u2B21",on:"\u2B22",disabled:"\u2B22"},ballot:{on:"\u2611",off:"\u2610",disabled:"\u2612"},stars:{on:"\u2605",off:"\u2606",disabled:"\u2606"},folder:{on:"\u25BC",off:"\u25B6",disabled:"\u25B6"},prefix:{pending:$p.symbols.question,submitted:$p.symbols.check,cancelled:$p.symbols.cross},separator:{pending:$p.symbols.pointerSmall,submitted:$p.symbols.middot,cancelled:$p.symbols.middot},radio:{off:LG?"( )":"\u25EF",on:LG?"(*)":"\u25C9",disabled:LG?"(|)":"\u24BE"},numbers:["\u24EA","\u2460","\u2461","\u2462","\u2463","\u2464","\u2465","\u2466","\u2467","\u2468","\u2469","\u246A","\u246B","\u246C","\u246D","\u246E","\u246F","\u2470","\u2471","\u2472","\u2473","\u3251","\u3252","\u3253","\u3254","\u3255","\u3256","\u3257","\u3258","\u3259","\u325A","\u325B","\u325C","\u325D","\u325E","\u325F","\u32B1","\u32B2","\u32B3","\u32B4","\u32B5","\u32B6","\u32B7","\u32B8","\u32B9","\u32BA","\u32BB","\u32BC","\u32BD","\u32BE","\u32BF"]};MG.merge=t=>{let e=f1t.merge({},$p.symbols,MG,t.symbols);return delete e.merge,e};bwe.exports=MG});var kwe=L((Bnr,xwe)=>{"use strict";var A1t=Dwe(),p1t=Pwe(),h1t=$o();xwe.exports=t=>{t.options=h1t.merge({},t.options.theme,t.options),t.symbols=p1t.merge(t.options),t.styles=A1t.merge(t.options)}});var Nwe=L((Rwe,Fwe)=>{"use strict";var Qwe=process.env.TERM_PROGRAM==="Apple_Terminal",g1t=Ju(),_G=$o(),zu=Fwe.exports=Rwe,Mi="\x1B[",Twe="\x07",UG=!1,q0=zu.code={bell:Twe,beep:Twe,beginning:`${Mi}G`,down:`${Mi}J`,esc:Mi,getPosition:`${Mi}6n`,hide:`${Mi}?25l`,line:`${Mi}2K`,lineEnd:`${Mi}K`,lineStart:`${Mi}1K`,restorePosition:Mi+(Qwe?"8":"u"),savePosition:Mi+(Qwe?"7":"s"),screen:`${Mi}2J`,show:`${Mi}?25h`,up:`${Mi}1J`},Bm=zu.cursor={get hidden(){return UG},hide(){return UG=!0,q0.hide},show(){return UG=!1,q0.show},forward:(t=1)=>`${Mi}${t}C`,backward:(t=1)=>`${Mi}${t}D`,nextLine:(t=1)=>`${Mi}E`.repeat(t),prevLine:(t=1)=>`${Mi}F`.repeat(t),up:(t=1)=>t?`${Mi}${t}A`:"",down:(t=1)=>t?`${Mi}${t}B`:"",right:(t=1)=>t?`${Mi}${t}C`:"",left:(t=1)=>t?`${Mi}${t}D`:"",to(t,e){return e?`${Mi}${e+1};${t+1}H`:`${Mi}${t+1}G`},move(t=0,e=0){let r="";return r+=t<0?Bm.left(-t):t>0?Bm.right(t):"",r+=e<0?Bm.up(-e):e>0?Bm.down(e):"",r},restore(t={}){let{after:e,cursor:r,initial:s,input:a,prompt:n,size:c,value:f}=t;if(s=_G.isPrimitive(s)?String(s):"",a=_G.isPrimitive(a)?String(a):"",f=_G.isPrimitive(f)?String(f):"",c){let p=zu.cursor.up(c)+zu.cursor.to(n.length),h=a.length-r;return h>0&&(p+=zu.cursor.left(h)),p}if(f||e){let p=!a&&s?-s.length:-a.length+r;return e&&(p-=e.length),a===""&&s&&!n.includes(s)&&(p+=s.length),zu.cursor.move(p)}}},HG=zu.erase={screen:q0.screen,up:q0.up,down:q0.down,line:q0.line,lineEnd:q0.lineEnd,lineStart:q0.lineStart,lines(t){let e="";for(let r=0;r{if(!e)return HG.line+Bm.to(0);let r=n=>[...g1t.unstyle(n)].length,s=t.split(/\r?\n/),a=0;for(let n of s)a+=1+Math.floor(Math.max(r(n)-1,0)/e);return(HG.line+Bm.prevLine()).repeat(a-1)+HG.line+Bm.to(0)}});var sC=L((vnr,Lwe)=>{"use strict";var d1t=ye("events"),Owe=Ju(),jG=Iwe(),m1t=wwe(),y1t=vwe(),E1t=kwe(),hl=$o(),vm=Nwe(),qG=class t extends d1t{constructor(e={}){super(),this.name=e.name,this.type=e.type,this.options=e,E1t(this),m1t(this),this.state=new y1t(this),this.initial=[e.initial,e.default].find(r=>r!=null),this.stdout=e.stdout||process.stdout,this.stdin=e.stdin||process.stdin,this.scale=e.scale||1,this.term=this.options.term||process.env.TERM_PROGRAM,this.margin=C1t(this.options.margin),this.setMaxListeners(0),I1t(this)}async keypress(e,r={}){this.keypressed=!0;let s=jG.action(e,jG(e,r),this.options.actions);this.state.keypress=s,this.emit("keypress",e,s),this.emit("state",this.state.clone());let a=this.options[s.action]||this[s.action]||this.dispatch;if(typeof a=="function")return await a.call(this,e,s);this.alert()}alert(){delete this.state.alert,this.options.show===!1?this.emit("alert"):this.stdout.write(vm.code.beep)}cursorHide(){this.stdout.write(vm.cursor.hide()),hl.onExit(()=>this.cursorShow())}cursorShow(){this.stdout.write(vm.cursor.show())}write(e){e&&(this.stdout&&this.state.show!==!1&&this.stdout.write(e),this.state.buffer+=e)}clear(e=0){let r=this.state.buffer;this.state.buffer="",!(!r&&!e||this.options.show===!1)&&this.stdout.write(vm.cursor.down(e)+vm.clear(r,this.width))}restore(){if(this.state.closed||this.options.show===!1)return;let{prompt:e,after:r,rest:s}=this.sections(),{cursor:a,initial:n="",input:c="",value:f=""}=this,p=this.state.size=s.length,h={after:r,cursor:a,initial:n,input:c,prompt:e,size:p,value:f},E=vm.cursor.restore(h);E&&this.stdout.write(E)}sections(){let{buffer:e,input:r,prompt:s}=this.state;s=Owe.unstyle(s);let a=Owe.unstyle(e),n=a.indexOf(s),c=a.slice(0,n),p=a.slice(n).split(` +`),h=p[0],E=p[p.length-1],S=(s+(r?" "+r:"")).length,P=Se.call(this,this.value),this.result=()=>s.call(this,this.value),typeof r.initial=="function"&&(this.initial=await r.initial.call(this,this)),typeof r.onRun=="function"&&await r.onRun.call(this,this),typeof r.onSubmit=="function"){let a=r.onSubmit.bind(this),n=this.submit.bind(this);delete this.options.onSubmit,this.submit=async()=>(await a(this.name,this.value,this),n())}await this.start(),await this.render()}render(){throw new Error("expected prompt to have a custom render method")}run(){return new Promise(async(e,r)=>{if(this.once("submit",e),this.once("cancel",r),await this.skip())return this.render=()=>{},this.submit();await this.initialize(),this.emit("run")})}async element(e,r,s){let{options:a,state:n,symbols:c,timers:f}=this,p=f&&f[e];n.timer=p;let h=a[e]||n[e]||c[e],E=r&&r[e]!=null?r[e]:await h;if(E==="")return E;let C=await this.resolve(E,n,r,s);return!C&&r&&r[e]?this.resolve(h,n,r,s):C}async prefix(){let e=await this.element("prefix")||this.symbols,r=this.timers&&this.timers.prefix,s=this.state;return s.timer=r,hl.isObject(e)&&(e=e[s.status]||e.pending),hl.hasColor(e)?e:(this.styles[s.status]||this.styles.pending)(e)}async message(){let e=await this.element("message");return hl.hasColor(e)?e:this.styles.strong(e)}async separator(){let e=await this.element("separator")||this.symbols,r=this.timers&&this.timers.separator,s=this.state;s.timer=r;let a=e[s.status]||e.pending||s.separator,n=await this.resolve(a,s);return hl.isObject(n)&&(n=n[s.status]||n.pending),hl.hasColor(n)?n:this.styles.muted(n)}async pointer(e,r){let s=await this.element("pointer",e,r);if(typeof s=="string"&&hl.hasColor(s))return s;if(s){let a=this.styles,n=this.index===r,c=n?a.primary:h=>h,f=await this.resolve(s[n?"on":"off"]||s,this.state),p=hl.hasColor(f)?f:c(f);return n?p:" ".repeat(f.length)}}async indicator(e,r){let s=await this.element("indicator",e,r);if(typeof s=="string"&&hl.hasColor(s))return s;if(s){let a=this.styles,n=e.enabled===!0,c=n?a.success:a.dark,f=s[n?"on":"off"]||s;return hl.hasColor(f)?f:c(f)}return""}body(){return null}footer(){if(this.state.status==="pending")return this.element("footer")}header(){if(this.state.status==="pending")return this.element("header")}async hint(){if(this.state.status==="pending"&&!this.isValue(this.state.input)){let e=await this.element("hint");return hl.hasColor(e)?e:this.styles.muted(e)}}error(e){return this.state.submitted?"":e||this.state.error}format(e){return e}result(e){return e}validate(e){return this.options.required===!0?this.isValue(e):!0}isValue(e){return e!=null&&e!==""}resolve(e,...r){return hl.resolve(this,e,...r)}get base(){return t.prototype}get style(){return this.styles[this.state.status]}get height(){return this.options.rows||hl.height(this.stdout,25)}get width(){return this.options.columns||hl.width(this.stdout,80)}get size(){return{width:this.width,height:this.height}}set cursor(e){this.state.cursor=e}get cursor(){return this.state.cursor}set input(e){this.state.input=e}get input(){return this.state.input}set value(e){this.state.value=e}get value(){let{input:e,value:r}=this.state,s=[r,e].find(this.isValue.bind(this));return this.isValue(s)?s:this.initial}static get prompt(){return e=>new this(e).run()}};function I1t(t){let e=a=>t[a]===void 0||typeof t[a]=="function",r=["actions","choices","initial","margin","roles","styles","symbols","theme","timers","value"],s=["body","footer","error","header","hint","indicator","message","prefix","separator","skip"];for(let a of Object.keys(t.options)){if(r.includes(a)||/^on[A-Z]/.test(a))continue;let n=t.options[a];typeof n=="function"&&e(a)?s.includes(a)||(t[a]=n.bind(t)):typeof t[a]!="function"&&(t[a]=n)}}function C1t(t){typeof t=="number"&&(t=[t,t,t,t]);let e=[].concat(t||[]),r=a=>a%2===0?` +`:" ",s=[];for(let a=0;a<4;a++){let n=r(a);e[a]?s.push(n.repeat(e[a])):s.push("")}return s}Lwe.exports=qG});var Uwe=L((Snr,_we)=>{"use strict";var w1t=$o(),Mwe={default(t,e){return e},checkbox(t,e){throw new Error("checkbox role is not implemented yet")},editable(t,e){throw new Error("editable role is not implemented yet")},expandable(t,e){throw new Error("expandable role is not implemented yet")},heading(t,e){return e.disabled="",e.indicator=[e.indicator," "].find(r=>r!=null),e.message=e.message||"",e},input(t,e){throw new Error("input role is not implemented yet")},option(t,e){return Mwe.default(t,e)},radio(t,e){throw new Error("radio role is not implemented yet")},separator(t,e){return e.disabled="",e.indicator=[e.indicator," "].find(r=>r!=null),e.message=e.message||t.symbols.line.repeat(5),e},spacer(t,e){return e}};_we.exports=(t,e={})=>{let r=w1t.merge({},Mwe,e.roles);return r[t]||r.default}});var oS=L((Dnr,qwe)=>{"use strict";var B1t=Ju(),v1t=sC(),S1t=Uwe(),$R=$o(),{reorder:GG,scrollUp:D1t,scrollDown:b1t,isObject:Hwe,swap:P1t}=$R,WG=class extends v1t{constructor(e){super(e),this.cursorHide(),this.maxSelected=e.maxSelected||1/0,this.multiple=e.multiple||!1,this.initial=e.initial||0,this.delay=e.delay||0,this.longest=0,this.num=""}async initialize(){typeof this.options.initial=="function"&&(this.initial=await this.options.initial.call(this)),await this.reset(!0),await super.initialize()}async reset(){let{choices:e,initial:r,autofocus:s,suggest:a}=this.options;if(this.state._choices=[],this.state.choices=[],this.choices=await Promise.all(await this.toChoices(e)),this.choices.forEach(n=>n.enabled=!1),typeof a!="function"&&this.selectable.length===0)throw new Error("At least one choice must be selectable");Hwe(r)&&(r=Object.keys(r)),Array.isArray(r)?(s!=null&&(this.index=this.findIndex(s)),r.forEach(n=>this.enable(this.find(n))),await this.render()):(s!=null&&(r=s),typeof r=="string"&&(r=this.findIndex(r)),typeof r=="number"&&r>-1&&(this.index=Math.max(0,Math.min(r,this.choices.length)),this.enable(this.find(this.index)))),this.isDisabled(this.focused)&&await this.down()}async toChoices(e,r){this.state.loadingChoices=!0;let s=[],a=0,n=async(c,f)=>{typeof c=="function"&&(c=await c.call(this)),c instanceof Promise&&(c=await c);for(let p=0;p(this.state.loadingChoices=!1,c))}async toChoice(e,r,s){if(typeof e=="function"&&(e=await e.call(this,this)),e instanceof Promise&&(e=await e),typeof e=="string"&&(e={name:e}),e.normalized)return e;e.normalized=!0;let a=e.value;if(e=S1t(e.role,this.options)(this,e),typeof e.disabled=="string"&&!e.hint&&(e.hint=e.disabled,e.disabled=!0),e.disabled===!0&&e.hint==null&&(e.hint="(disabled)"),e.index!=null)return e;e.name=e.name||e.key||e.title||e.value||e.message,e.message=e.message||e.name||"",e.value=[e.value,e.name].find(this.isValue.bind(this)),e.input="",e.index=r,e.cursor=0,$R.define(e,"parent",s),e.level=s?s.level+1:1,e.indent==null&&(e.indent=s?s.indent+" ":e.indent||""),e.path=s?s.path+"."+e.name:e.name,e.enabled=!!(this.multiple&&!this.isDisabled(e)&&(e.enabled||this.isSelected(e))),this.isDisabled(e)||(this.longest=Math.max(this.longest,B1t.unstyle(e.message).length));let c={...e};return e.reset=(f=c.input,p=c.value)=>{for(let h of Object.keys(c))e[h]=c[h];e.input=f,e.value=p},a==null&&typeof e.initial=="function"&&(e.input=await e.initial.call(this,this.state,e,r)),e}async onChoice(e,r){this.emit("choice",e,r,this),typeof e.onChoice=="function"&&await e.onChoice.call(this,this.state,e,r)}async addChoice(e,r,s){let a=await this.toChoice(e,r,s);return this.choices.push(a),this.index=this.choices.length-1,this.limit=this.choices.length,a}async newItem(e,r,s){let a={name:"New choice name?",editable:!0,newChoice:!0,...e},n=await this.addChoice(a,r,s);return n.updateChoice=()=>{delete n.newChoice,n.name=n.message=n.input,n.input="",n.cursor=0},this.render()}indent(e){return e.indent==null?e.level>1?" ".repeat(e.level-1):"":e.indent}dispatch(e,r){if(this.multiple&&this[r.name])return this[r.name]();this.alert()}focus(e,r){return typeof r!="boolean"&&(r=e.enabled),r&&!e.enabled&&this.selected.length>=this.maxSelected?this.alert():(this.index=e.index,e.enabled=r&&!this.isDisabled(e),e)}space(){return this.multiple?(this.toggle(this.focused),this.render()):this.alert()}a(){if(this.maxSelectedr.enabled);return this.choices.forEach(r=>r.enabled=!e),this.render()}i(){return this.choices.length-this.selected.length>this.maxSelected?this.alert():(this.choices.forEach(e=>e.enabled=!e.enabled),this.render())}g(e=this.focused){return this.choices.some(r=>!!r.parent)?(this.toggle(e.parent&&!e.choices?e.parent:e),this.render()):this.a()}toggle(e,r){if(!e.enabled&&this.selected.length>=this.maxSelected)return this.alert();typeof r!="boolean"&&(r=!e.enabled),e.enabled=r,e.choices&&e.choices.forEach(a=>this.toggle(a,r));let s=e.parent;for(;s;){let a=s.choices.filter(n=>this.isDisabled(n));s.enabled=a.every(n=>n.enabled===!0),s=s.parent}return jwe(this,this.choices),this.emit("toggle",e,this),e}enable(e){return this.selected.length>=this.maxSelected?this.alert():(e.enabled=!this.isDisabled(e),e.choices&&e.choices.forEach(this.enable.bind(this)),e)}disable(e){return e.enabled=!1,e.choices&&e.choices.forEach(this.disable.bind(this)),e}number(e){this.num+=e;let r=s=>{let a=Number(s);if(a>this.choices.length-1)return this.alert();let n=this.focused,c=this.choices.find(f=>a===f.index);if(!c.enabled&&this.selected.length>=this.maxSelected)return this.alert();if(this.visible.indexOf(c)===-1){let f=GG(this.choices),p=f.indexOf(c);if(n.index>p){let h=f.slice(p,p+this.limit),E=f.filter(C=>!h.includes(C));this.choices=h.concat(E)}else{let h=p-this.limit+1;this.choices=f.slice(h).concat(f.slice(0,h))}}return this.index=this.choices.indexOf(c),this.toggle(this.focused),this.render()};return clearTimeout(this.numberTimeout),new Promise(s=>{let a=this.choices.length,n=this.num,c=(f=!1,p)=>{clearTimeout(this.numberTimeout),f&&(p=r(n)),this.num="",s(p)};if(n==="0"||n.length===1&&+(n+"0")>a)return c(!0);if(Number(n)>a)return c(!1,this.alert());this.numberTimeout=setTimeout(()=>c(!0),this.delay)})}home(){return this.choices=GG(this.choices),this.index=0,this.render()}end(){let e=this.choices.length-this.limit,r=GG(this.choices);return this.choices=r.slice(e).concat(r.slice(0,e)),this.index=this.limit-1,this.render()}first(){return this.index=0,this.render()}last(){return this.index=this.visible.length-1,this.render()}prev(){return this.visible.length<=1?this.alert():this.up()}next(){return this.visible.length<=1?this.alert():this.down()}right(){return this.cursor>=this.input.length?this.alert():(this.cursor++,this.render())}left(){return this.cursor<=0?this.alert():(this.cursor--,this.render())}up(){let e=this.choices.length,r=this.visible.length,s=this.index;return this.options.scroll===!1&&s===0?this.alert():e>r&&s===0?this.scrollUp():(this.index=(s-1%e+e)%e,this.isDisabled()?this.up():this.render())}down(){let e=this.choices.length,r=this.visible.length,s=this.index;return this.options.scroll===!1&&s===r-1?this.alert():e>r&&s===r-1?this.scrollDown():(this.index=(s+1)%e,this.isDisabled()?this.down():this.render())}scrollUp(e=0){return this.choices=D1t(this.choices),this.index=e,this.isDisabled()?this.up():this.render()}scrollDown(e=this.visible.length-1){return this.choices=b1t(this.choices),this.index=e,this.isDisabled()?this.down():this.render()}async shiftUp(){if(this.options.sort===!0){this.sorting=!0,this.swap(this.index-1),await this.up(),this.sorting=!1;return}return this.scrollUp(this.index)}async shiftDown(){if(this.options.sort===!0){this.sorting=!0,this.swap(this.index+1),await this.down(),this.sorting=!1;return}return this.scrollDown(this.index)}pageUp(){return this.visible.length<=1?this.alert():(this.limit=Math.max(this.limit-1,0),this.index=Math.min(this.limit-1,this.index),this._limit=this.limit,this.isDisabled()?this.up():this.render())}pageDown(){return this.visible.length>=this.choices.length?this.alert():(this.index=Math.max(0,this.index),this.limit=Math.min(this.limit+1,this.choices.length),this._limit=this.limit,this.isDisabled()?this.down():this.render())}swap(e){P1t(this.choices,this.index,e)}isDisabled(e=this.focused){return e&&["disabled","collapsed","hidden","completing","readonly"].some(s=>e[s]===!0)?!0:e&&e.role==="heading"}isEnabled(e=this.focused){if(Array.isArray(e))return e.every(r=>this.isEnabled(r));if(e.choices){let r=e.choices.filter(s=>!this.isDisabled(s));return e.enabled&&r.every(s=>this.isEnabled(s))}return e.enabled&&!this.isDisabled(e)}isChoice(e,r){return e.name===r||e.index===Number(r)}isSelected(e){return Array.isArray(this.initial)?this.initial.some(r=>this.isChoice(e,r)):this.isChoice(e,this.initial)}map(e=[],r="value"){return[].concat(e||[]).reduce((s,a)=>(s[a]=this.find(a,r),s),{})}filter(e,r){let a=typeof e=="function"?e:(f,p)=>[f.name,p].includes(e),c=(this.options.multiple?this.state._choices:this.choices).filter(a);return r?c.map(f=>f[r]):c}find(e,r){if(Hwe(e))return r?e[r]:e;let a=typeof e=="function"?e:(c,f)=>[c.name,f].includes(e),n=this.choices.find(a);if(n)return r?n[r]:n}findIndex(e){return this.choices.indexOf(this.find(e))}async submit(){let e=this.focused;if(!e)return this.alert();if(e.newChoice)return e.input?(e.updateChoice(),this.render()):this.alert();if(this.choices.some(c=>c.newChoice))return this.alert();let{reorder:r,sort:s}=this.options,a=this.multiple===!0,n=this.selected;return n===void 0?this.alert():(Array.isArray(n)&&r!==!1&&s!==!0&&(n=$R.reorder(n)),this.value=a?n.map(c=>c.name):n.name,super.submit())}set choices(e=[]){this.state._choices=this.state._choices||[],this.state.choices=e;for(let r of e)this.state._choices.some(s=>s.name===r.name)||this.state._choices.push(r);if(!this._initial&&this.options.initial){this._initial=!0;let r=this.initial;if(typeof r=="string"||typeof r=="number"){let s=this.find(r);s&&(this.initial=s.index,this.focus(s,!0))}}}get choices(){return jwe(this,this.state.choices||[])}set visible(e){this.state.visible=e}get visible(){return(this.state.visible||this.choices).slice(0,this.limit)}set limit(e){this.state.limit=e}get limit(){let{state:e,options:r,choices:s}=this,a=e.limit||this._limit||r.limit||s.length;return Math.min(a,this.height)}set value(e){super.value=e}get value(){return typeof super.value!="string"&&super.value===this.initial?this.input:super.value}set index(e){this.state.index=e}get index(){return Math.max(0,this.state?this.state.index:0)}get enabled(){return this.filter(this.isEnabled.bind(this))}get focused(){let e=this.choices[this.index];return e&&this.state.submitted&&this.multiple!==!0&&(e.enabled=!0),e}get selectable(){return this.choices.filter(e=>!this.isDisabled(e))}get selected(){return this.multiple?this.enabled:this.focused}};function jwe(t,e){if(e instanceof Promise)return e;if(typeof e=="function"){if($R.isAsyncFn(e))return e;e=e.call(t,t)}for(let r of e){if(Array.isArray(r.choices)){let s=r.choices.filter(a=>!t.isDisabled(a));r.enabled=s.every(a=>a.enabled===!0)}t.isDisabled(r)===!0&&delete r.enabled}return e}qwe.exports=WG});var G0=L((bnr,Gwe)=>{"use strict";var x1t=oS(),YG=$o(),VG=class extends x1t{constructor(e){super(e),this.emptyError=this.options.emptyError||"No items were selected"}async dispatch(e,r){if(this.multiple)return this[r.name]?await this[r.name](e,r):await super.dispatch(e,r);this.alert()}separator(){if(this.options.separator)return super.separator();let e=this.styles.muted(this.symbols.ellipsis);return this.state.submitted?super.separator():e}pointer(e,r){return!this.multiple||this.options.pointer?super.pointer(e,r):""}indicator(e,r){return this.multiple?super.indicator(e,r):""}choiceMessage(e,r){let s=this.resolve(e.message,this.state,e,r);return e.role==="heading"&&!YG.hasColor(s)&&(s=this.styles.strong(s)),this.resolve(s,this.state,e,r)}choiceSeparator(){return":"}async renderChoice(e,r){await this.onChoice(e,r);let s=this.index===r,a=await this.pointer(e,r),n=await this.indicator(e,r)+(e.pad||""),c=await this.resolve(e.hint,this.state,e,r);c&&!YG.hasColor(c)&&(c=this.styles.muted(c));let f=this.indent(e),p=await this.choiceMessage(e,r),h=()=>[this.margin[3],f+a+n,p,this.margin[1],c].filter(Boolean).join(" ");return e.role==="heading"?h():e.disabled?(YG.hasColor(p)||(p=this.styles.disabled(p)),h()):(s&&(p=this.styles.em(p)),h())}async renderChoices(){if(this.state.loading==="choices")return this.styles.warning("Loading choices");if(this.state.submitted)return"";let e=this.visible.map(async(n,c)=>await this.renderChoice(n,c)),r=await Promise.all(e);r.length||r.push(this.styles.danger("No matching choices"));let s=this.margin[0]+r.join(` +`),a;return this.options.choicesHeader&&(a=await this.resolve(this.options.choicesHeader,this.state)),[a,s].filter(Boolean).join(` +`)}format(){return!this.state.submitted||this.state.cancelled?"":Array.isArray(this.selected)?this.selected.map(e=>this.styles.primary(e.name)).join(", "):this.styles.primary(this.selected.name)}async render(){let{submitted:e,size:r}=this.state,s="",a=await this.header(),n=await this.prefix(),c=await this.separator(),f=await this.message();this.options.promptLine!==!1&&(s=[n,f,c,""].join(" "),this.state.prompt=s);let p=await this.format(),h=await this.error()||await this.hint(),E=await this.renderChoices(),C=await this.footer();p&&(s+=p),h&&!s.includes(h)&&(s+=" "+h),e&&!p&&!E.trim()&&this.multiple&&this.emptyError!=null&&(s+=this.styles.danger(this.emptyError)),this.clear(r),this.write([a,s,E,C].filter(Boolean).join(` +`)),this.write(this.margin[2]),this.restore()}};Gwe.exports=VG});var Ywe=L((Pnr,Wwe)=>{"use strict";var k1t=G0(),Q1t=(t,e)=>{let r=t.toLowerCase();return s=>{let n=s.toLowerCase().indexOf(r),c=e(s.slice(n,n+r.length));return n>=0?s.slice(0,n)+c+s.slice(n+r.length):s}},KG=class extends k1t{constructor(e){super(e),this.cursorShow()}moveCursor(e){this.state.cursor+=e}dispatch(e){return this.append(e)}space(e){return this.options.multiple?super.space(e):this.append(e)}append(e){let{cursor:r,input:s}=this.state;return this.input=s.slice(0,r)+e+s.slice(r),this.moveCursor(1),this.complete()}delete(){let{cursor:e,input:r}=this.state;return r?(this.input=r.slice(0,e-1)+r.slice(e),this.moveCursor(-1),this.complete()):this.alert()}deleteForward(){let{cursor:e,input:r}=this.state;return r[e]===void 0?this.alert():(this.input=`${r}`.slice(0,e)+`${r}`.slice(e+1),this.complete())}number(e){return this.append(e)}async complete(){this.completing=!0,this.choices=await this.suggest(this.input,this.state._choices),this.state.limit=void 0,this.index=Math.min(Math.max(this.visible.length-1,0),this.index),await this.render(),this.completing=!1}suggest(e=this.input,r=this.state._choices){if(typeof this.options.suggest=="function")return this.options.suggest.call(this,e,r);let s=e.toLowerCase();return r.filter(a=>a.message.toLowerCase().includes(s))}pointer(){return""}format(){if(!this.focused)return this.input;if(this.options.multiple&&this.state.submitted)return this.selected.map(e=>this.styles.primary(e.message)).join(", ");if(this.state.submitted){let e=this.value=this.input=this.focused.value;return this.styles.primary(e)}return this.input}async render(){if(this.state.status!=="pending")return super.render();let e=this.options.highlight?this.options.highlight.bind(this):this.styles.placeholder,r=Q1t(this.input,e),s=this.choices;this.choices=s.map(a=>({...a,message:r(a.message)})),await super.render(),this.choices=s}submit(){return this.options.multiple&&(this.value=this.selected.map(e=>e.name)),super.submit()}};Wwe.exports=KG});var zG=L((xnr,Vwe)=>{"use strict";var JG=$o();Vwe.exports=(t,e={})=>{t.cursorHide();let{input:r="",initial:s="",pos:a,showCursor:n=!0,color:c}=e,f=c||t.styles.placeholder,p=JG.inverse(t.styles.primary),h=R=>p(t.styles.black(R)),E=r,C=" ",S=h(C);if(t.blink&&t.blink.off===!0&&(h=R=>R,S=""),n&&a===0&&s===""&&r==="")return h(C);if(n&&a===0&&(r===s||r===""))return h(s[0])+f(s.slice(1));s=JG.isPrimitive(s)?`${s}`:"",r=JG.isPrimitive(r)?`${r}`:"";let P=s&&s.startsWith(r)&&s!==r,I=P?h(s[r.length]):S;if(a!==r.length&&n===!0&&(E=r.slice(0,a)+h(r[a])+r.slice(a+1),I=""),n===!1&&(I=""),P){let R=t.styles.unstyle(E+I);return E+I+f(s.slice(R.length))}return E+I}});var eF=L((knr,Kwe)=>{"use strict";var T1t=Ju(),R1t=G0(),F1t=zG(),ZG=class extends R1t{constructor(e){super({...e,multiple:!0}),this.type="form",this.initial=this.options.initial,this.align=[this.options.align,"right"].find(r=>r!=null),this.emptyError="",this.values={}}async reset(e){return await super.reset(),e===!0&&(this._index=this.index),this.index=this._index,this.values={},this.choices.forEach(r=>r.reset&&r.reset()),this.render()}dispatch(e){return!!e&&this.append(e)}append(e){let r=this.focused;if(!r)return this.alert();let{cursor:s,input:a}=r;return r.value=r.input=a.slice(0,s)+e+a.slice(s),r.cursor++,this.render()}delete(){let e=this.focused;if(!e||e.cursor<=0)return this.alert();let{cursor:r,input:s}=e;return e.value=e.input=s.slice(0,r-1)+s.slice(r),e.cursor--,this.render()}deleteForward(){let e=this.focused;if(!e)return this.alert();let{cursor:r,input:s}=e;if(s[r]===void 0)return this.alert();let a=`${s}`.slice(0,r)+`${s}`.slice(r+1);return e.value=e.input=a,this.render()}right(){let e=this.focused;return e?e.cursor>=e.input.length?this.alert():(e.cursor++,this.render()):this.alert()}left(){let e=this.focused;return e?e.cursor<=0?this.alert():(e.cursor--,this.render()):this.alert()}space(e,r){return this.dispatch(e,r)}number(e,r){return this.dispatch(e,r)}next(){let e=this.focused;if(!e)return this.alert();let{initial:r,input:s}=e;return r&&r.startsWith(s)&&s!==r?(e.value=e.input=r,e.cursor=e.value.length,this.render()):super.next()}prev(){let e=this.focused;return e?e.cursor===0?super.prev():(e.value=e.input="",e.cursor=0,this.render()):this.alert()}separator(){return""}format(e){return this.state.submitted?"":super.format(e)}pointer(){return""}indicator(e){return e.input?"\u29BF":"\u2299"}async choiceSeparator(e,r){let s=await this.resolve(e.separator,this.state,e,r)||":";return s?" "+this.styles.disabled(s):""}async renderChoice(e,r){await this.onChoice(e,r);let{state:s,styles:a}=this,{cursor:n,initial:c="",name:f,hint:p,input:h=""}=e,{muted:E,submitted:C,primary:S,danger:P}=a,I=p,R=this.index===r,N=e.validate||(()=>!0),U=await this.choiceSeparator(e,r),W=e.message;this.align==="right"&&(W=W.padStart(this.longest+1," ")),this.align==="left"&&(W=W.padEnd(this.longest+1," "));let te=this.values[f]=h||c,ie=h?"success":"dark";await N.call(e,te,this.state)!==!0&&(ie="danger");let Ae=a[ie],ce=Ae(await this.indicator(e,r))+(e.pad||""),me=this.indent(e),pe=()=>[me,ce,W+U,h,I].filter(Boolean).join(" ");if(s.submitted)return W=T1t.unstyle(W),h=C(h),I="",pe();if(e.format)h=await e.format.call(this,h,e,r);else{let Be=this.styles.muted;h=F1t(this,{input:h,initial:c,pos:n,showCursor:R,color:Be})}return this.isValue(h)||(h=this.styles.muted(this.symbols.ellipsis)),e.result&&(this.values[f]=await e.result.call(this,te,e,r)),R&&(W=S(W)),e.error?h+=(h?" ":"")+P(e.error.trim()):e.hint&&(h+=(h?" ":"")+E(e.hint.trim())),pe()}async submit(){return this.value=this.values,super.base.submit.call(this)}};Kwe.exports=ZG});var XG=L((Qnr,zwe)=>{"use strict";var N1t=eF(),O1t=()=>{throw new Error("expected prompt to have a custom authenticate method")},Jwe=(t=O1t)=>{class e extends N1t{constructor(s){super(s)}async submit(){this.value=await t.call(this,this.values,this.state),super.base.submit.call(this)}static create(s){return Jwe(s)}}return e};zwe.exports=Jwe()});var $we=L((Tnr,Xwe)=>{"use strict";var L1t=XG();function M1t(t,e){return t.username===this.options.username&&t.password===this.options.password}var Zwe=(t=M1t)=>{let e=[{name:"username",message:"username"},{name:"password",message:"password",format(s){return this.options.showPassword?s:(this.state.submitted?this.styles.primary:this.styles.muted)(this.symbols.asterisk.repeat(s.length))}}];class r extends L1t.create(t){constructor(a){super({...a,choices:e})}static create(a){return Zwe(a)}}return r};Xwe.exports=Zwe()});var tF=L((Rnr,e1e)=>{"use strict";var _1t=sC(),{isPrimitive:U1t,hasColor:H1t}=$o(),$G=class extends _1t{constructor(e){super(e),this.cursorHide()}async initialize(){let e=await this.resolve(this.initial,this.state);this.input=await this.cast(e),await super.initialize()}dispatch(e){return this.isValue(e)?(this.input=e,this.submit()):this.alert()}format(e){let{styles:r,state:s}=this;return s.submitted?r.success(e):r.primary(e)}cast(e){return this.isTrue(e)}isTrue(e){return/^[ty1]/i.test(e)}isFalse(e){return/^[fn0]/i.test(e)}isValue(e){return U1t(e)&&(this.isTrue(e)||this.isFalse(e))}async hint(){if(this.state.status==="pending"){let e=await this.element("hint");return H1t(e)?e:this.styles.muted(e)}}async render(){let{input:e,size:r}=this.state,s=await this.prefix(),a=await this.separator(),n=await this.message(),c=this.styles.muted(this.default),f=[s,n,c,a].filter(Boolean).join(" ");this.state.prompt=f;let p=await this.header(),h=this.value=this.cast(e),E=await this.format(h),C=await this.error()||await this.hint(),S=await this.footer();C&&!f.includes(C)&&(E+=" "+C),f+=" "+E,this.clear(r),this.write([p,f,S].filter(Boolean).join(` +`)),this.restore()}set value(e){super.value=e}get value(){return this.cast(super.value)}};e1e.exports=$G});var r1e=L((Fnr,t1e)=>{"use strict";var j1t=tF(),e5=class extends j1t{constructor(e){super(e),this.default=this.options.default||(this.initial?"(Y/n)":"(y/N)")}};t1e.exports=e5});var i1e=L((Nnr,n1e)=>{"use strict";var q1t=G0(),G1t=eF(),oC=G1t.prototype,t5=class extends q1t{constructor(e){super({...e,multiple:!0}),this.align=[this.options.align,"left"].find(r=>r!=null),this.emptyError="",this.values={}}dispatch(e,r){let s=this.focused,a=s.parent||{};return!s.editable&&!a.editable&&(e==="a"||e==="i")?super[e]():oC.dispatch.call(this,e,r)}append(e,r){return oC.append.call(this,e,r)}delete(e,r){return oC.delete.call(this,e,r)}space(e){return this.focused.editable?this.append(e):super.space()}number(e){return this.focused.editable?this.append(e):super.number(e)}next(){return this.focused.editable?oC.next.call(this):super.next()}prev(){return this.focused.editable?oC.prev.call(this):super.prev()}async indicator(e,r){let s=e.indicator||"",a=e.editable?s:super.indicator(e,r);return await this.resolve(a,this.state,e,r)||""}indent(e){return e.role==="heading"?"":e.editable?" ":" "}async renderChoice(e,r){return e.indent="",e.editable?oC.renderChoice.call(this,e,r):super.renderChoice(e,r)}error(){return""}footer(){return this.state.error}async validate(){let e=!0;for(let r of this.choices){if(typeof r.validate!="function"||r.role==="heading")continue;let s=r.parent?this.value[r.parent.name]:this.value;if(r.editable?s=r.value===r.name?r.initial||"":r.value:this.isDisabled(r)||(s=r.enabled===!0),e=await r.validate(s,this.state),e!==!0)break}return e!==!0&&(this.state.error=typeof e=="string"?e:"Invalid Input"),e}submit(){if(this.focused.newChoice===!0)return super.submit();if(this.choices.some(e=>e.newChoice))return this.alert();this.value={};for(let e of this.choices){let r=e.parent?this.value[e.parent.name]:this.value;if(e.role==="heading"){this.value[e.name]={};continue}e.editable?r[e.name]=e.value===e.name?e.initial||"":e.value:this.isDisabled(e)||(r[e.name]=e.enabled===!0)}return this.base.submit.call(this)}};n1e.exports=t5});var Sm=L((Onr,s1e)=>{"use strict";var W1t=sC(),Y1t=zG(),{isPrimitive:V1t}=$o(),r5=class extends W1t{constructor(e){super(e),this.initial=V1t(this.initial)?String(this.initial):"",this.initial&&this.cursorHide(),this.state.prevCursor=0,this.state.clipboard=[]}async keypress(e,r={}){let s=this.state.prevKeypress;return this.state.prevKeypress=r,this.options.multiline===!0&&r.name==="return"&&(!s||s.name!=="return")?this.append(` +`,r):super.keypress(e,r)}moveCursor(e){this.cursor+=e}reset(){return this.input=this.value="",this.cursor=0,this.render()}dispatch(e,r){if(!e||r.ctrl||r.code)return this.alert();this.append(e)}append(e){let{cursor:r,input:s}=this.state;this.input=`${s}`.slice(0,r)+e+`${s}`.slice(r),this.moveCursor(String(e).length),this.render()}insert(e){this.append(e)}delete(){let{cursor:e,input:r}=this.state;if(e<=0)return this.alert();this.input=`${r}`.slice(0,e-1)+`${r}`.slice(e),this.moveCursor(-1),this.render()}deleteForward(){let{cursor:e,input:r}=this.state;if(r[e]===void 0)return this.alert();this.input=`${r}`.slice(0,e)+`${r}`.slice(e+1),this.render()}cutForward(){let e=this.cursor;if(this.input.length<=e)return this.alert();this.state.clipboard.push(this.input.slice(e)),this.input=this.input.slice(0,e),this.render()}cutLeft(){let e=this.cursor;if(e===0)return this.alert();let r=this.input.slice(0,e),s=this.input.slice(e),a=r.split(" ");this.state.clipboard.push(a.pop()),this.input=a.join(" "),this.cursor=this.input.length,this.input+=s,this.render()}paste(){if(!this.state.clipboard.length)return this.alert();this.insert(this.state.clipboard.pop()),this.render()}toggleCursor(){this.state.prevCursor?(this.cursor=this.state.prevCursor,this.state.prevCursor=0):(this.state.prevCursor=this.cursor,this.cursor=0),this.render()}first(){this.cursor=0,this.render()}last(){this.cursor=this.input.length-1,this.render()}next(){let e=this.initial!=null?String(this.initial):"";if(!e||!e.startsWith(this.input))return this.alert();this.input=this.initial,this.cursor=this.initial.length,this.render()}prev(){if(!this.input)return this.alert();this.reset()}backward(){return this.left()}forward(){return this.right()}right(){return this.cursor>=this.input.length?this.alert():(this.moveCursor(1),this.render())}left(){return this.cursor<=0?this.alert():(this.moveCursor(-1),this.render())}isValue(e){return!!e}async format(e=this.value){let r=await this.resolve(this.initial,this.state);return this.state.submitted?this.styles.submitted(e||r):Y1t(this,{input:e,initial:r,pos:this.cursor})}async render(){let e=this.state.size,r=await this.prefix(),s=await this.separator(),a=await this.message(),n=[r,a,s].filter(Boolean).join(" ");this.state.prompt=n;let c=await this.header(),f=await this.format(),p=await this.error()||await this.hint(),h=await this.footer();p&&!f.includes(p)&&(f+=" "+p),n+=" "+f,this.clear(e),this.write([c,n,h].filter(Boolean).join(` +`)),this.restore()}};s1e.exports=r5});var a1e=L((Lnr,o1e)=>{"use strict";var K1t=t=>t.filter((e,r)=>t.lastIndexOf(e)===r),rF=t=>K1t(t).filter(Boolean);o1e.exports=(t,e={},r="")=>{let{past:s=[],present:a=""}=e,n,c;switch(t){case"prev":case"undo":return n=s.slice(0,s.length-1),c=s[s.length-1]||"",{past:rF([r,...n]),present:c};case"next":case"redo":return n=s.slice(1),c=s[0]||"",{past:rF([...n,r]),present:c};case"save":return{past:rF([...s,r]),present:""};case"remove":return c=rF(s.filter(f=>f!==r)),a="",c.length&&(a=c.pop()),{past:c,present:a};default:throw new Error(`Invalid action: "${t}"`)}}});var i5=L((Mnr,c1e)=>{"use strict";var J1t=Sm(),l1e=a1e(),n5=class extends J1t{constructor(e){super(e);let r=this.options.history;if(r&&r.store){let s=r.values||this.initial;this.autosave=!!r.autosave,this.store=r.store,this.data=this.store.get("values")||{past:[],present:s},this.initial=this.data.present||this.data.past[this.data.past.length-1]}}completion(e){return this.store?(this.data=l1e(e,this.data,this.input),this.data.present?(this.input=this.data.present,this.cursor=this.input.length,this.render()):this.alert()):this.alert()}altUp(){return this.completion("prev")}altDown(){return this.completion("next")}prev(){return this.save(),super.prev()}save(){this.store&&(this.data=l1e("save",this.data,this.input),this.store.set("values",this.data))}submit(){return this.store&&this.autosave===!0&&this.save(),super.submit()}};c1e.exports=n5});var f1e=L((_nr,u1e)=>{"use strict";var z1t=Sm(),s5=class extends z1t{format(){return""}};u1e.exports=s5});var p1e=L((Unr,A1e)=>{"use strict";var Z1t=Sm(),o5=class extends Z1t{constructor(e={}){super(e),this.sep=this.options.separator||/, */,this.initial=e.initial||""}split(e=this.value){return e?String(e).split(this.sep):[]}format(){let e=this.state.submitted?this.styles.primary:r=>r;return this.list.map(e).join(", ")}async submit(e){let r=this.state.error||await this.validate(this.list,this.state);return r!==!0?(this.state.error=r,super.submit()):(this.value=this.list,super.submit())}get list(){return this.split()}};A1e.exports=o5});var g1e=L((Hnr,h1e)=>{"use strict";var X1t=G0(),a5=class extends X1t{constructor(e){super({...e,multiple:!0})}};h1e.exports=a5});var c5=L((jnr,d1e)=>{"use strict";var $1t=Sm(),l5=class extends $1t{constructor(e={}){super({style:"number",...e}),this.min=this.isValue(e.min)?this.toNumber(e.min):-1/0,this.max=this.isValue(e.max)?this.toNumber(e.max):1/0,this.delay=e.delay!=null?e.delay:1e3,this.float=e.float!==!1,this.round=e.round===!0||e.float===!1,this.major=e.major||10,this.minor=e.minor||1,this.initial=e.initial!=null?e.initial:"",this.input=String(this.initial),this.cursor=this.input.length,this.cursorShow()}append(e){return!/[-+.]/.test(e)||e==="."&&this.input.includes(".")?this.alert("invalid number"):super.append(e)}number(e){return super.append(e)}next(){return this.input&&this.input!==this.initial?this.alert():this.isValue(this.initial)?(this.input=this.initial,this.cursor=String(this.initial).length,this.render()):this.alert()}up(e){let r=e||this.minor,s=this.toNumber(this.input);return s>this.max+r?this.alert():(this.input=`${s+r}`,this.render())}down(e){let r=e||this.minor,s=this.toNumber(this.input);return sthis.isValue(r));return this.value=this.toNumber(e||0),super.submit()}};d1e.exports=l5});var y1e=L((qnr,m1e)=>{m1e.exports=c5()});var I1e=L((Gnr,E1e)=>{"use strict";var e2t=Sm(),u5=class extends e2t{constructor(e){super(e),this.cursorShow()}format(e=this.input){return this.keypressed?(this.state.submitted?this.styles.primary:this.styles.muted)(this.symbols.asterisk.repeat(e.length)):""}};E1e.exports=u5});var B1e=L((Wnr,w1e)=>{"use strict";var t2t=Ju(),r2t=oS(),C1e=$o(),f5=class extends r2t{constructor(e={}){super(e),this.widths=[].concat(e.messageWidth||50),this.align=[].concat(e.align||"left"),this.linebreak=e.linebreak||!1,this.edgeLength=e.edgeLength||3,this.newline=e.newline||` + `;let r=e.startNumber||1;typeof this.scale=="number"&&(this.scaleKey=!1,this.scale=Array(this.scale).fill(0).map((s,a)=>({name:a+r})))}async reset(){return this.tableized=!1,await super.reset(),this.render()}tableize(){if(this.tableized===!0)return;this.tableized=!0;let e=0;for(let r of this.choices){e=Math.max(e,r.message.length),r.scaleIndex=r.initial||2,r.scale=[];for(let s=0;s=this.scale.length-1?this.alert():(e.scaleIndex++,this.render())}left(){let e=this.focused;return e.scaleIndex<=0?this.alert():(e.scaleIndex--,this.render())}indent(){return""}format(){return this.state.submitted?this.choices.map(r=>this.styles.info(r.index)).join(", "):""}pointer(){return""}renderScaleKey(){return this.scaleKey===!1||this.state.submitted?"":["",...this.scale.map(s=>` ${s.name} - ${s.message}`)].map(s=>this.styles.muted(s)).join(` +`)}renderScaleHeading(e){let r=this.scale.map(p=>p.name);typeof this.options.renderScaleHeading=="function"&&(r=this.options.renderScaleHeading.call(this,e));let s=this.scaleLength-r.join("").length,a=Math.round(s/(r.length-1)),c=r.map(p=>this.styles.strong(p)).join(" ".repeat(a)),f=" ".repeat(this.widths[0]);return this.margin[3]+f+this.margin[1]+c}scaleIndicator(e,r,s){if(typeof this.options.scaleIndicator=="function")return this.options.scaleIndicator.call(this,e,r,s);let a=e.scaleIndex===r.index;return r.disabled?this.styles.hint(this.symbols.radio.disabled):a?this.styles.success(this.symbols.radio.on):this.symbols.radio.off}renderScale(e,r){let s=e.scale.map(n=>this.scaleIndicator(e,n,r)),a=this.term==="Hyper"?"":" ";return s.join(a+this.symbols.line.repeat(this.edgeLength))}async renderChoice(e,r){await this.onChoice(e,r);let s=this.index===r,a=await this.pointer(e,r),n=await e.hint;n&&!C1e.hasColor(n)&&(n=this.styles.muted(n));let c=I=>this.margin[3]+I.replace(/\s+$/,"").padEnd(this.widths[0]," "),f=this.newline,p=this.indent(e),h=await this.resolve(e.message,this.state,e,r),E=await this.renderScale(e,r),C=this.margin[1]+this.margin[3];this.scaleLength=t2t.unstyle(E).length,this.widths[0]=Math.min(this.widths[0],this.width-this.scaleLength-C.length);let P=C1e.wordWrap(h,{width:this.widths[0],newline:f}).split(` +`).map(I=>c(I)+this.margin[1]);return s&&(E=this.styles.info(E),P=P.map(I=>this.styles.info(I))),P[0]+=E,this.linebreak&&P.push(""),[p+a,P.join(` +`)].filter(Boolean)}async renderChoices(){if(this.state.submitted)return"";this.tableize();let e=this.visible.map(async(a,n)=>await this.renderChoice(a,n)),r=await Promise.all(e),s=await this.renderScaleHeading();return this.margin[0]+[s,...r.map(a=>a.join(" "))].join(` +`)}async render(){let{submitted:e,size:r}=this.state,s=await this.prefix(),a=await this.separator(),n=await this.message(),c="";this.options.promptLine!==!1&&(c=[s,n,a,""].join(" "),this.state.prompt=c);let f=await this.header(),p=await this.format(),h=await this.renderScaleKey(),E=await this.error()||await this.hint(),C=await this.renderChoices(),S=await this.footer(),P=this.emptyError;p&&(c+=p),E&&!c.includes(E)&&(c+=" "+E),e&&!p&&!C.trim()&&this.multiple&&P!=null&&(c+=this.styles.danger(P)),this.clear(r),this.write([f,c,h,C,S].filter(Boolean).join(` +`)),this.state.submitted||this.write(this.margin[2]),this.restore()}submit(){this.value={};for(let e of this.choices)this.value[e.name]=e.scaleIndex;return this.base.submit.call(this)}};w1e.exports=f5});var D1e=L((Ynr,S1e)=>{"use strict";var v1e=Ju(),n2t=(t="")=>typeof t=="string"?t.replace(/^['"]|['"]$/g,""):"",p5=class{constructor(e){this.name=e.key,this.field=e.field||{},this.value=n2t(e.initial||this.field.initial||""),this.message=e.message||this.name,this.cursor=0,this.input="",this.lines=[]}},i2t=async(t={},e={},r=s=>s)=>{let s=new Set,a=t.fields||[],n=t.template,c=[],f=[],p=[],h=1;typeof n=="function"&&(n=await n());let E=-1,C=()=>n[++E],S=()=>n[E+1],P=I=>{I.line=h,c.push(I)};for(P({type:"bos",value:""});Eie.name===U.key);U.field=a.find(ie=>ie.name===U.key),te||(te=new p5(U),f.push(te)),te.lines.push(U.line-1);continue}let R=c[c.length-1];R.type==="text"&&R.line===h?R.value+=I:P({type:"text",value:I})}return P({type:"eos",value:""}),{input:n,tabstops:c,unique:s,keys:p,items:f}};S1e.exports=async t=>{let e=t.options,r=new Set(e.required===!0?[]:e.required||[]),s={...e.values,...e.initial},{tabstops:a,items:n,keys:c}=await i2t(e,s),f=A5("result",t,e),p=A5("format",t,e),h=A5("validate",t,e,!0),E=t.isValue.bind(t);return async(C={},S=!1)=>{let P=0;C.required=r,C.items=n,C.keys=c,C.output="";let I=async(W,te,ie,Ae)=>{let ce=await h(W,te,ie,Ae);return ce===!1?"Invalid field "+ie.name:ce};for(let W of a){let te=W.value,ie=W.key;if(W.type!=="template"){te&&(C.output+=te);continue}if(W.type==="template"){let Ae=n.find(Ce=>Ce.name===ie);e.required===!0&&C.required.add(Ae.name);let ce=[Ae.input,C.values[Ae.value],Ae.value,te].find(E),pe=(Ae.field||{}).message||W.inner;if(S){let Ce=await I(C.values[ie],C,Ae,P);if(Ce&&typeof Ce=="string"||Ce===!1){C.invalid.set(ie,Ce);continue}C.invalid.delete(ie);let g=await f(C.values[ie],C,Ae,P);C.output+=v1e.unstyle(g);continue}Ae.placeholder=!1;let Be=te;te=await p(te,C,Ae,P),ce!==te?(C.values[ie]=ce,te=t.styles.typing(ce),C.missing.delete(pe)):(C.values[ie]=void 0,ce=`<${pe}>`,te=t.styles.primary(ce),Ae.placeholder=!0,C.required.has(ie)&&C.missing.add(pe)),C.missing.has(pe)&&C.validating&&(te=t.styles.warning(ce)),C.invalid.has(ie)&&C.validating&&(te=t.styles.danger(ce)),P===C.index&&(Be!==te?te=t.styles.underline(te):te=t.styles.heading(v1e.unstyle(te))),P++}te&&(C.output+=te)}let R=C.output.split(` +`).map(W=>" "+W),N=n.length,U=0;for(let W of n)C.invalid.has(W.name)&&W.lines.forEach(te=>{R[te][0]===" "&&(R[te]=C.styles.danger(C.symbols.bullet)+R[te].slice(1))}),t.isValue(C.values[W.name])&&U++;return C.completed=(U/N*100).toFixed(0),C.output=R.join(` +`),C.output}};function A5(t,e,r,s){return(a,n,c,f)=>typeof c.field[t]=="function"?c.field[t].call(e,a,n,c,f):[s,a].find(p=>e.isValue(p))}});var P1e=L((Vnr,b1e)=>{"use strict";var s2t=Ju(),o2t=D1e(),a2t=sC(),h5=class extends a2t{constructor(e){super(e),this.cursorHide(),this.reset(!0)}async initialize(){this.interpolate=await o2t(this),await super.initialize()}async reset(e){this.state.keys=[],this.state.invalid=new Map,this.state.missing=new Set,this.state.completed=0,this.state.values={},e!==!0&&(await this.initialize(),await this.render())}moveCursor(e){let r=this.getItem();this.cursor+=e,r.cursor+=e}dispatch(e,r){if(!r.code&&!r.ctrl&&e!=null&&this.getItem()){this.append(e,r);return}this.alert()}append(e,r){let s=this.getItem(),a=s.input.slice(0,this.cursor),n=s.input.slice(this.cursor);this.input=s.input=`${a}${e}${n}`,this.moveCursor(1),this.render()}delete(){let e=this.getItem();if(this.cursor<=0||!e.input)return this.alert();let r=e.input.slice(this.cursor),s=e.input.slice(0,this.cursor-1);this.input=e.input=`${s}${r}`,this.moveCursor(-1),this.render()}increment(e){return e>=this.state.keys.length-1?0:e+1}decrement(e){return e<=0?this.state.keys.length-1:e-1}first(){this.state.index=0,this.render()}last(){this.state.index=this.state.keys.length-1,this.render()}right(){if(this.cursor>=this.input.length)return this.alert();this.moveCursor(1),this.render()}left(){if(this.cursor<=0)return this.alert();this.moveCursor(-1),this.render()}prev(){this.state.index=this.decrement(this.state.index),this.getItem(),this.render()}next(){this.state.index=this.increment(this.state.index),this.getItem(),this.render()}up(){this.prev()}down(){this.next()}format(e){let r=this.state.completed<100?this.styles.warning:this.styles.success;return this.state.submitted===!0&&this.state.completed!==100&&(r=this.styles.danger),r(`${this.state.completed}% completed`)}async render(){let{index:e,keys:r=[],submitted:s,size:a}=this.state,n=[this.options.newline,` +`].find(W=>W!=null),c=await this.prefix(),f=await this.separator(),p=await this.message(),h=[c,p,f].filter(Boolean).join(" ");this.state.prompt=h;let E=await this.header(),C=await this.error()||"",S=await this.hint()||"",P=s?"":await this.interpolate(this.state),I=this.state.key=r[e]||"",R=await this.format(I),N=await this.footer();R&&(h+=" "+R),S&&!R&&this.state.completed===0&&(h+=" "+S),this.clear(a);let U=[E,h,P,N,C.trim()];this.write(U.filter(Boolean).join(n)),this.restore()}getItem(e){let{items:r,keys:s,index:a}=this.state,n=r.find(c=>c.name===s[a]);return n&&n.input!=null&&(this.input=n.input,this.cursor=n.cursor),n}async submit(){typeof this.interpolate!="function"&&await this.initialize(),await this.interpolate(this.state,!0);let{invalid:e,missing:r,output:s,values:a}=this.state;if(e.size){let f="";for(let[p,h]of e)f+=`Invalid ${p}: ${h} +`;return this.state.error=f,super.submit()}if(r.size)return this.state.error="Required: "+[...r.keys()].join(", "),super.submit();let c=s2t.unstyle(s).split(` +`).map(f=>f.slice(1)).join(` +`);return this.value={values:a,result:c},super.submit()}};b1e.exports=h5});var k1e=L((Knr,x1e)=>{"use strict";var l2t="(Use + to sort)",c2t=G0(),g5=class extends c2t{constructor(e){super({...e,reorder:!1,sort:!0,multiple:!0}),this.state.hint=[this.options.hint,l2t].find(this.isValue.bind(this))}indicator(){return""}async renderChoice(e,r){let s=await super.renderChoice(e,r),a=this.symbols.identicalTo+" ",n=this.index===r&&this.sorting?this.styles.muted(a):" ";return this.options.drag===!1&&(n=""),this.options.numbered===!0?n+`${r+1} - `+s:n+s}get selected(){return this.choices}submit(){return this.value=this.choices.map(e=>e.value),super.submit()}};x1e.exports=g5});var T1e=L((Jnr,Q1e)=>{"use strict";var u2t=oS(),d5=class extends u2t{constructor(e={}){if(super(e),this.emptyError=e.emptyError||"No items were selected",this.term=process.env.TERM_PROGRAM,!this.options.header){let r=["","4 - Strongly Agree","3 - Agree","2 - Neutral","1 - Disagree","0 - Strongly Disagree",""];r=r.map(s=>this.styles.muted(s)),this.state.header=r.join(` + `)}}async toChoices(...e){if(this.createdScales)return!1;this.createdScales=!0;let r=await super.toChoices(...e);for(let s of r)s.scale=f2t(5,this.options),s.scaleIdx=2;return r}dispatch(){this.alert()}space(){let e=this.focused,r=e.scale[e.scaleIdx],s=r.selected;return e.scale.forEach(a=>a.selected=!1),r.selected=!s,this.render()}indicator(){return""}pointer(){return""}separator(){return this.styles.muted(this.symbols.ellipsis)}right(){let e=this.focused;return e.scaleIdx>=e.scale.length-1?this.alert():(e.scaleIdx++,this.render())}left(){let e=this.focused;return e.scaleIdx<=0?this.alert():(e.scaleIdx--,this.render())}indent(){return" "}async renderChoice(e,r){await this.onChoice(e,r);let s=this.index===r,a=this.term==="Hyper",n=a?9:8,c=a?"":" ",f=this.symbols.line.repeat(n),p=" ".repeat(n+(a?0:1)),h=te=>(te?this.styles.success("\u25C9"):"\u25EF")+c,E=r+1+".",C=s?this.styles.heading:this.styles.noop,S=await this.resolve(e.message,this.state,e,r),P=this.indent(e),I=P+e.scale.map((te,ie)=>h(ie===e.scaleIdx)).join(f),R=te=>te===e.scaleIdx?C(te):te,N=P+e.scale.map((te,ie)=>R(ie)).join(p),U=()=>[E,S].filter(Boolean).join(" "),W=()=>[U(),I,N," "].filter(Boolean).join(` +`);return s&&(I=this.styles.cyan(I),N=this.styles.cyan(N)),W()}async renderChoices(){if(this.state.submitted)return"";let e=this.visible.map(async(s,a)=>await this.renderChoice(s,a)),r=await Promise.all(e);return r.length||r.push(this.styles.danger("No matching choices")),r.join(` +`)}format(){return this.state.submitted?this.choices.map(r=>this.styles.info(r.scaleIdx)).join(", "):""}async render(){let{submitted:e,size:r}=this.state,s=await this.prefix(),a=await this.separator(),n=await this.message(),c=[s,n,a].filter(Boolean).join(" ");this.state.prompt=c;let f=await this.header(),p=await this.format(),h=await this.error()||await this.hint(),E=await this.renderChoices(),C=await this.footer();(p||!h)&&(c+=" "+p),h&&!c.includes(h)&&(c+=" "+h),e&&!p&&!E&&this.multiple&&this.type!=="form"&&(c+=this.styles.danger(this.emptyError)),this.clear(r),this.write([c,f,E,C].filter(Boolean).join(` +`)),this.restore()}submit(){this.value={};for(let e of this.choices)this.value[e.name]=e.scaleIdx;return this.base.submit.call(this)}};function f2t(t,e={}){if(Array.isArray(e.scale))return e.scale.map(s=>({...s}));let r=[];for(let s=1;s{R1e.exports=i5()});var O1e=L((Znr,N1e)=>{"use strict";var A2t=tF(),m5=class extends A2t{async initialize(){await super.initialize(),this.value=this.initial=!!this.options.initial,this.disabled=this.options.disabled||"no",this.enabled=this.options.enabled||"yes",await this.render()}reset(){this.value=this.initial,this.render()}delete(){this.alert()}toggle(){this.value=!this.value,this.render()}enable(){if(this.value===!0)return this.alert();this.value=!0,this.render()}disable(){if(this.value===!1)return this.alert();this.value=!1,this.render()}up(){this.toggle()}down(){this.toggle()}right(){this.toggle()}left(){this.toggle()}next(){this.toggle()}prev(){this.toggle()}dispatch(e="",r){switch(e.toLowerCase()){case" ":return this.toggle();case"1":case"y":case"t":return this.enable();case"0":case"n":case"f":return this.disable();default:return this.alert()}}format(){let e=s=>this.styles.primary.underline(s);return[this.value?this.disabled:e(this.disabled),this.value?e(this.enabled):this.enabled].join(this.styles.muted(" / "))}async render(){let{size:e}=this.state,r=await this.header(),s=await this.prefix(),a=await this.separator(),n=await this.message(),c=await this.format(),f=await this.error()||await this.hint(),p=await this.footer(),h=[s,n,a,c].join(" ");this.state.prompt=h,f&&!h.includes(f)&&(h+=" "+f),this.clear(e),this.write([r,h,p].filter(Boolean).join(` +`)),this.write(this.margin[2]),this.restore()}};N1e.exports=m5});var M1e=L((Xnr,L1e)=>{"use strict";var p2t=G0(),y5=class extends p2t{constructor(e){if(super(e),typeof this.options.correctChoice!="number"||this.options.correctChoice<0)throw new Error("Please specify the index of the correct answer from the list of choices")}async toChoices(e,r){let s=await super.toChoices(e,r);if(s.length<2)throw new Error("Please give at least two choices to the user");if(this.options.correctChoice>s.length)throw new Error("Please specify the index of the correct answer from the list of choices");return s}check(e){return e.index===this.options.correctChoice}async result(e){return{selectedAnswer:e,correctAnswer:this.options.choices[this.options.correctChoice].value,correct:await this.check(this.state)}}};L1e.exports=y5});var U1e=L(E5=>{"use strict";var _1e=$o(),Qs=(t,e)=>{_1e.defineExport(E5,t,e),_1e.defineExport(E5,t.toLowerCase(),e)};Qs("AutoComplete",()=>Ywe());Qs("BasicAuth",()=>$we());Qs("Confirm",()=>r1e());Qs("Editable",()=>i1e());Qs("Form",()=>eF());Qs("Input",()=>i5());Qs("Invisible",()=>f1e());Qs("List",()=>p1e());Qs("MultiSelect",()=>g1e());Qs("Numeral",()=>y1e());Qs("Password",()=>I1e());Qs("Scale",()=>B1e());Qs("Select",()=>G0());Qs("Snippet",()=>P1e());Qs("Sort",()=>k1e());Qs("Survey",()=>T1e());Qs("Text",()=>F1e());Qs("Toggle",()=>O1e());Qs("Quiz",()=>M1e())});var j1e=L((eir,H1e)=>{H1e.exports={ArrayPrompt:oS(),AuthPrompt:XG(),BooleanPrompt:tF(),NumberPrompt:c5(),StringPrompt:Sm()}});var lS=L((tir,G1e)=>{"use strict";var q1e=ye("assert"),C5=ye("events"),W0=$o(),Zu=class extends C5{constructor(e,r){super(),this.options=W0.merge({},e),this.answers={...r}}register(e,r){if(W0.isObject(e)){for(let a of Object.keys(e))this.register(a,e[a]);return this}q1e.equal(typeof r,"function","expected a function");let s=e.toLowerCase();return r.prototype instanceof this.Prompt?this.prompts[s]=r:this.prompts[s]=r(this.Prompt,this),this}async prompt(e=[]){for(let r of[].concat(e))try{typeof r=="function"&&(r=await r.call(this)),await this.ask(W0.merge({},this.options,r))}catch(s){return Promise.reject(s)}return this.answers}async ask(e){typeof e=="function"&&(e=await e.call(this));let r=W0.merge({},this.options,e),{type:s,name:a}=e,{set:n,get:c}=W0;if(typeof s=="function"&&(s=await s.call(this,e,this.answers)),!s)return this.answers[a];q1e(this.prompts[s],`Prompt "${s}" is not registered`);let f=new this.prompts[s](r),p=c(this.answers,a);f.state.answers=this.answers,f.enquirer=this,a&&f.on("submit",E=>{this.emit("answer",a,E,f),n(this.answers,a,E)});let h=f.emit.bind(f);return f.emit=(...E)=>(this.emit.call(this,...E),h(...E)),this.emit("prompt",f,this),r.autofill&&p!=null?(f.value=f.input=p,r.autofill==="show"&&await f.submit()):p=f.value=await f.run(),p}use(e){return e.call(this,this),this}set Prompt(e){this._Prompt=e}get Prompt(){return this._Prompt||this.constructor.Prompt}get prompts(){return this.constructor.prompts}static set Prompt(e){this._Prompt=e}static get Prompt(){return this._Prompt||sC()}static get prompts(){return U1e()}static get types(){return j1e()}static get prompt(){let e=(r,...s)=>{let a=new this(...s),n=a.emit.bind(a);return a.emit=(...c)=>(e.emit(...c),n(...c)),a.prompt(r)};return W0.mixinEmitter(e,new C5),e}};W0.mixinEmitter(Zu,new C5);var I5=Zu.prompts;for(let t of Object.keys(I5)){let e=t.toLowerCase(),r=s=>new I5[t](s).run();Zu.prompt[e]=r,Zu[e]=r,Zu[t]||Reflect.defineProperty(Zu,t,{get:()=>I5[t]})}var aS=t=>{W0.defineExport(Zu,t,()=>Zu.types[t])};aS("ArrayPrompt");aS("AuthPrompt");aS("BooleanPrompt");aS("NumberPrompt");aS("StringPrompt");G1e.exports=Zu});var AS=L((Lir,Z1e)=>{var I2t=qR();function C2t(t,e,r){var s=t==null?void 0:I2t(t,e);return s===void 0?r:s}Z1e.exports=C2t});var e2e=L((qir,$1e)=>{function w2t(t,e){for(var r=-1,s=t==null?0:t.length;++r{var B2t=Jd(),v2t=Uk();function S2t(t,e){return t&&B2t(e,v2t(e),t)}t2e.exports=S2t});var i2e=L((Wir,n2e)=>{var D2t=Jd(),b2t=qE();function P2t(t,e){return t&&D2t(e,b2t(e),t)}n2e.exports=P2t});var o2e=L((Yir,s2e)=>{var x2t=Jd(),k2t=Fk();function Q2t(t,e){return x2t(t,k2t(t),e)}s2e.exports=Q2t});var b5=L((Vir,a2e)=>{var T2t=Rk(),R2t=Wk(),F2t=Fk(),N2t=_4(),O2t=Object.getOwnPropertySymbols,L2t=O2t?function(t){for(var e=[];t;)T2t(e,F2t(t)),t=R2t(t);return e}:N2t;a2e.exports=L2t});var c2e=L((Kir,l2e)=>{var M2t=Jd(),_2t=b5();function U2t(t,e){return M2t(t,_2t(t),e)}l2e.exports=U2t});var P5=L((Jir,u2e)=>{var H2t=M4(),j2t=b5(),q2t=qE();function G2t(t){return H2t(t,q2t,j2t)}u2e.exports=G2t});var A2e=L((zir,f2e)=>{var W2t=Object.prototype,Y2t=W2t.hasOwnProperty;function V2t(t){var e=t.length,r=new t.constructor(e);return e&&typeof t[0]=="string"&&Y2t.call(t,"index")&&(r.index=t.index,r.input=t.input),r}f2e.exports=V2t});var h2e=L((Zir,p2e)=>{var K2t=qk();function J2t(t,e){var r=e?K2t(t.buffer):t.buffer;return new t.constructor(r,t.byteOffset,t.byteLength)}p2e.exports=J2t});var d2e=L((Xir,g2e)=>{var z2t=/\w*$/;function Z2t(t){var e=new t.constructor(t.source,z2t.exec(t));return e.lastIndex=t.lastIndex,e}g2e.exports=Z2t});var C2e=L(($ir,I2e)=>{var m2e=Yd(),y2e=m2e?m2e.prototype:void 0,E2e=y2e?y2e.valueOf:void 0;function X2t(t){return E2e?Object(E2e.call(t)):{}}I2e.exports=X2t});var B2e=L((esr,w2e)=>{var $2t=qk(),eBt=h2e(),tBt=d2e(),rBt=C2e(),nBt=$4(),iBt="[object Boolean]",sBt="[object Date]",oBt="[object Map]",aBt="[object Number]",lBt="[object RegExp]",cBt="[object Set]",uBt="[object String]",fBt="[object Symbol]",ABt="[object ArrayBuffer]",pBt="[object DataView]",hBt="[object Float32Array]",gBt="[object Float64Array]",dBt="[object Int8Array]",mBt="[object Int16Array]",yBt="[object Int32Array]",EBt="[object Uint8Array]",IBt="[object Uint8ClampedArray]",CBt="[object Uint16Array]",wBt="[object Uint32Array]";function BBt(t,e,r){var s=t.constructor;switch(e){case ABt:return $2t(t);case iBt:case sBt:return new s(+t);case pBt:return eBt(t,r);case hBt:case gBt:case dBt:case mBt:case yBt:case EBt:case IBt:case CBt:case wBt:return nBt(t,r);case oBt:return new s;case aBt:case uBt:return new s(t);case lBt:return tBt(t);case cBt:return new s;case fBt:return rBt(t)}}w2e.exports=BBt});var S2e=L((tsr,v2e)=>{var vBt=_B(),SBt=zf(),DBt="[object Map]";function bBt(t){return SBt(t)&&vBt(t)==DBt}v2e.exports=bBt});var x2e=L((rsr,P2e)=>{var PBt=S2e(),xBt=Ok(),D2e=Lk(),b2e=D2e&&D2e.isMap,kBt=b2e?xBt(b2e):PBt;P2e.exports=kBt});var Q2e=L((nsr,k2e)=>{var QBt=_B(),TBt=zf(),RBt="[object Set]";function FBt(t){return TBt(t)&&QBt(t)==RBt}k2e.exports=FBt});var N2e=L((isr,F2e)=>{var NBt=Q2e(),OBt=Ok(),T2e=Lk(),R2e=T2e&&T2e.isSet,LBt=R2e?OBt(R2e):NBt;F2e.exports=LBt});var x5=L((ssr,_2e)=>{var MBt=Qk(),_Bt=e2e(),UBt=Yk(),HBt=r2e(),jBt=i2e(),qBt=X4(),GBt=Gk(),WBt=o2e(),YBt=c2e(),VBt=q4(),KBt=P5(),JBt=_B(),zBt=A2e(),ZBt=B2e(),XBt=e3(),$Bt=xc(),evt=FB(),tvt=x2e(),rvt=Wl(),nvt=N2e(),ivt=Uk(),svt=qE(),ovt=1,avt=2,lvt=4,O2e="[object Arguments]",cvt="[object Array]",uvt="[object Boolean]",fvt="[object Date]",Avt="[object Error]",L2e="[object Function]",pvt="[object GeneratorFunction]",hvt="[object Map]",gvt="[object Number]",M2e="[object Object]",dvt="[object RegExp]",mvt="[object Set]",yvt="[object String]",Evt="[object Symbol]",Ivt="[object WeakMap]",Cvt="[object ArrayBuffer]",wvt="[object DataView]",Bvt="[object Float32Array]",vvt="[object Float64Array]",Svt="[object Int8Array]",Dvt="[object Int16Array]",bvt="[object Int32Array]",Pvt="[object Uint8Array]",xvt="[object Uint8ClampedArray]",kvt="[object Uint16Array]",Qvt="[object Uint32Array]",Ii={};Ii[O2e]=Ii[cvt]=Ii[Cvt]=Ii[wvt]=Ii[uvt]=Ii[fvt]=Ii[Bvt]=Ii[vvt]=Ii[Svt]=Ii[Dvt]=Ii[bvt]=Ii[hvt]=Ii[gvt]=Ii[M2e]=Ii[dvt]=Ii[mvt]=Ii[yvt]=Ii[Evt]=Ii[Pvt]=Ii[xvt]=Ii[kvt]=Ii[Qvt]=!0;Ii[Avt]=Ii[L2e]=Ii[Ivt]=!1;function iF(t,e,r,s,a,n){var c,f=e&ovt,p=e&avt,h=e&lvt;if(r&&(c=a?r(t,s,a,n):r(t)),c!==void 0)return c;if(!rvt(t))return t;var E=$Bt(t);if(E){if(c=zBt(t),!f)return GBt(t,c)}else{var C=JBt(t),S=C==L2e||C==pvt;if(evt(t))return qBt(t,f);if(C==M2e||C==O2e||S&&!a){if(c=p||S?{}:XBt(t),!f)return p?YBt(t,jBt(c,t)):WBt(t,HBt(c,t))}else{if(!Ii[C])return a?t:{};c=ZBt(t,C,f)}}n||(n=new MBt);var P=n.get(t);if(P)return P;n.set(t,c),nvt(t)?t.forEach(function(N){c.add(iF(N,e,r,N,t,n))}):tvt(t)&&t.forEach(function(N,U){c.set(U,iF(N,e,r,U,t,n))});var I=h?p?KBt:VBt:p?svt:ivt,R=E?void 0:I(t);return _Bt(R||t,function(N,U){R&&(U=N,N=t[U]),UBt(c,U,iF(N,e,r,U,t,n))}),c}_2e.exports=iF});var k5=L((osr,U2e)=>{var Tvt=x5(),Rvt=1,Fvt=4;function Nvt(t){return Tvt(t,Rvt|Fvt)}U2e.exports=Nvt});var Q5=L((asr,H2e)=>{var Ovt=CG();function Lvt(t,e,r){return t==null?t:Ovt(t,e,r)}H2e.exports=Lvt});var Y2e=L((psr,W2e)=>{var Mvt=Object.prototype,_vt=Mvt.hasOwnProperty;function Uvt(t,e){return t!=null&&_vt.call(t,e)}W2e.exports=Uvt});var K2e=L((hsr,V2e)=>{var Hvt=Y2e(),jvt=wG();function qvt(t,e){return t!=null&&jvt(t,e,Hvt)}V2e.exports=qvt});var z2e=L((gsr,J2e)=>{function Gvt(t){var e=t==null?0:t.length;return e?t[e-1]:void 0}J2e.exports=Gvt});var X2e=L((dsr,Z2e)=>{var Wvt=qR(),Yvt=A6();function Vvt(t,e){return e.length<2?t:Wvt(t,Yvt(e,0,-1))}Z2e.exports=Vvt});var R5=L((msr,$2e)=>{var Kvt=wm(),Jvt=z2e(),zvt=X2e(),Zvt=XI();function Xvt(t,e){return e=Kvt(e,t),t=zvt(t,e),t==null||delete t[Zvt(Jvt(e))]}$2e.exports=Xvt});var F5=L((ysr,eBe)=>{var $vt=R5();function eSt(t,e){return t==null?!0:$vt(t,e)}eBe.exports=eSt});var sBe=L((Vsr,nSt)=>{nSt.exports={name:"@yarnpkg/cli",version:"4.9.1",license:"BSD-2-Clause",main:"./sources/index.ts",exports:{".":"./sources/index.ts","./polyfills":"./sources/polyfills.ts","./package.json":"./package.json"},dependencies:{"@yarnpkg/core":"workspace:^","@yarnpkg/fslib":"workspace:^","@yarnpkg/libzip":"workspace:^","@yarnpkg/parsers":"workspace:^","@yarnpkg/plugin-compat":"workspace:^","@yarnpkg/plugin-constraints":"workspace:^","@yarnpkg/plugin-dlx":"workspace:^","@yarnpkg/plugin-essentials":"workspace:^","@yarnpkg/plugin-exec":"workspace:^","@yarnpkg/plugin-file":"workspace:^","@yarnpkg/plugin-git":"workspace:^","@yarnpkg/plugin-github":"workspace:^","@yarnpkg/plugin-http":"workspace:^","@yarnpkg/plugin-init":"workspace:^","@yarnpkg/plugin-interactive-tools":"workspace:^","@yarnpkg/plugin-jsr":"workspace:^","@yarnpkg/plugin-link":"workspace:^","@yarnpkg/plugin-nm":"workspace:^","@yarnpkg/plugin-npm":"workspace:^","@yarnpkg/plugin-npm-cli":"workspace:^","@yarnpkg/plugin-pack":"workspace:^","@yarnpkg/plugin-patch":"workspace:^","@yarnpkg/plugin-pnp":"workspace:^","@yarnpkg/plugin-pnpm":"workspace:^","@yarnpkg/plugin-stage":"workspace:^","@yarnpkg/plugin-typescript":"workspace:^","@yarnpkg/plugin-version":"workspace:^","@yarnpkg/plugin-workspace-tools":"workspace:^","@yarnpkg/shell":"workspace:^","ci-info":"^4.0.0",clipanion:"^4.0.0-rc.2",semver:"^7.1.2",tslib:"^2.4.0",typanion:"^3.14.0"},devDependencies:{"@types/semver":"^7.1.0","@yarnpkg/builder":"workspace:^","@yarnpkg/monorepo":"workspace:^","@yarnpkg/pnpify":"workspace:^"},peerDependencies:{"@yarnpkg/core":"workspace:^"},scripts:{postpack:"rm -rf lib",prepack:'run build:compile "$(pwd)"',"build:cli+hook":"run build:pnp:hook && builder build bundle","build:cli":"builder build bundle","run:cli":"builder run","update-local":"run build:cli --no-git-hash && rsync -a --delete bundles/ bin/"},publishConfig:{main:"./lib/index.js",bin:null,exports:{".":"./lib/index.js","./package.json":"./package.json"}},files:["/lib/**/*","!/lib/pluginConfiguration.*","!/lib/cli.*"],"@yarnpkg/builder":{bundles:{standard:["@yarnpkg/plugin-essentials","@yarnpkg/plugin-compat","@yarnpkg/plugin-constraints","@yarnpkg/plugin-dlx","@yarnpkg/plugin-exec","@yarnpkg/plugin-file","@yarnpkg/plugin-git","@yarnpkg/plugin-github","@yarnpkg/plugin-http","@yarnpkg/plugin-init","@yarnpkg/plugin-interactive-tools","@yarnpkg/plugin-jsr","@yarnpkg/plugin-link","@yarnpkg/plugin-nm","@yarnpkg/plugin-npm","@yarnpkg/plugin-npm-cli","@yarnpkg/plugin-pack","@yarnpkg/plugin-patch","@yarnpkg/plugin-pnp","@yarnpkg/plugin-pnpm","@yarnpkg/plugin-stage","@yarnpkg/plugin-typescript","@yarnpkg/plugin-version","@yarnpkg/plugin-workspace-tools"]}},repository:{type:"git",url:"git+https://github.com/yarnpkg/berry.git",directory:"packages/yarnpkg-cli"},engines:{node:">=18.12.0"}}});var q5=L((Dcr,mBe)=>{"use strict";mBe.exports=function(e,r){r===!0&&(r=0);var s="";if(typeof e=="string")try{s=new URL(e).protocol}catch{}else e&&e.constructor===URL&&(s=e.protocol);var a=s.split(/\:|\+/).filter(Boolean);return typeof r=="number"?a[r]:a}});var EBe=L((bcr,yBe)=>{"use strict";var BSt=q5();function vSt(t){var e={protocols:[],protocol:null,port:null,resource:"",host:"",user:"",password:"",pathname:"",hash:"",search:"",href:t,query:{},parse_failed:!1};try{var r=new URL(t);e.protocols=BSt(r),e.protocol=e.protocols[0],e.port=r.port,e.resource=r.hostname,e.host=r.host,e.user=r.username||"",e.password=r.password||"",e.pathname=r.pathname,e.hash=r.hash.slice(1),e.search=r.search.slice(1),e.href=r.href,e.query=Object.fromEntries(r.searchParams)}catch{e.protocols=["file"],e.protocol=e.protocols[0],e.port="",e.resource="",e.user="",e.pathname="",e.hash="",e.search="",e.href=t,e.query={},e.parse_failed=!0}return e}yBe.exports=vSt});var wBe=L((Pcr,CBe)=>{"use strict";var SSt=EBe();function DSt(t){return t&&typeof t=="object"&&"default"in t?t:{default:t}}var bSt=DSt(SSt),PSt="text/plain",xSt="us-ascii",IBe=(t,e)=>e.some(r=>r instanceof RegExp?r.test(t):r===t),kSt=(t,{stripHash:e})=>{let r=/^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(t);if(!r)throw new Error(`Invalid URL: ${t}`);let{type:s,data:a,hash:n}=r.groups,c=s.split(";");n=e?"":n;let f=!1;c[c.length-1]==="base64"&&(c.pop(),f=!0);let p=(c.shift()||"").toLowerCase(),E=[...c.map(C=>{let[S,P=""]=C.split("=").map(I=>I.trim());return S==="charset"&&(P=P.toLowerCase(),P===xSt)?"":`${S}${P?`=${P}`:""}`}).filter(Boolean)];return f&&E.push("base64"),(E.length>0||p&&p!==PSt)&&E.unshift(p),`data:${E.join(";")},${f?a.trim():a}${n?`#${n}`:""}`};function QSt(t,e){if(e={defaultProtocol:"http:",normalizeProtocol:!0,forceHttp:!1,forceHttps:!1,stripAuthentication:!0,stripHash:!1,stripTextFragment:!0,stripWWW:!0,removeQueryParameters:[/^utm_\w+/i],removeTrailingSlash:!0,removeSingleSlash:!0,removeDirectoryIndex:!1,sortQueryParameters:!0,...e},t=t.trim(),/^data:/i.test(t))return kSt(t,e);if(/^view-source:/i.test(t))throw new Error("`view-source:` is not supported as it is a non-standard protocol");let r=t.startsWith("//");!r&&/^\.*\//.test(t)||(t=t.replace(/^(?!(?:\w+:)?\/\/)|^\/\//,e.defaultProtocol));let a=new URL(t);if(e.forceHttp&&e.forceHttps)throw new Error("The `forceHttp` and `forceHttps` options cannot be used together");if(e.forceHttp&&a.protocol==="https:"&&(a.protocol="http:"),e.forceHttps&&a.protocol==="http:"&&(a.protocol="https:"),e.stripAuthentication&&(a.username="",a.password=""),e.stripHash?a.hash="":e.stripTextFragment&&(a.hash=a.hash.replace(/#?:~:text.*?$/i,"")),a.pathname){let c=/\b[a-z][a-z\d+\-.]{1,50}:\/\//g,f=0,p="";for(;;){let E=c.exec(a.pathname);if(!E)break;let C=E[0],S=E.index,P=a.pathname.slice(f,S);p+=P.replace(/\/{2,}/g,"/"),p+=C,f=S+C.length}let h=a.pathname.slice(f,a.pathname.length);p+=h.replace(/\/{2,}/g,"/"),a.pathname=p}if(a.pathname)try{a.pathname=decodeURI(a.pathname)}catch{}if(e.removeDirectoryIndex===!0&&(e.removeDirectoryIndex=[/^index\.[a-z]+$/]),Array.isArray(e.removeDirectoryIndex)&&e.removeDirectoryIndex.length>0){let c=a.pathname.split("/"),f=c[c.length-1];IBe(f,e.removeDirectoryIndex)&&(c=c.slice(0,-1),a.pathname=c.slice(1).join("/")+"/")}if(a.hostname&&(a.hostname=a.hostname.replace(/\.$/,""),e.stripWWW&&/^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(a.hostname)&&(a.hostname=a.hostname.replace(/^www\./,""))),Array.isArray(e.removeQueryParameters))for(let c of[...a.searchParams.keys()])IBe(c,e.removeQueryParameters)&&a.searchParams.delete(c);if(e.removeQueryParameters===!0&&(a.search=""),e.sortQueryParameters){a.searchParams.sort();try{a.search=decodeURIComponent(a.search)}catch{}}e.removeTrailingSlash&&(a.pathname=a.pathname.replace(/\/$/,""));let n=t;return t=a.toString(),!e.removeSingleSlash&&a.pathname==="/"&&!n.endsWith("/")&&a.hash===""&&(t=t.replace(/\/$/,"")),(e.removeTrailingSlash||a.pathname==="/")&&a.hash===""&&e.removeSingleSlash&&(t=t.replace(/\/$/,"")),r&&!e.normalizeProtocol&&(t=t.replace(/^http:\/\//,"//")),e.stripProtocol&&(t=t.replace(/^(?:https?:)?\/\//,"")),t}var G5=(t,e=!1)=>{let r=/^(?:([a-z_][a-z0-9_-]{0,31})@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/,s=n=>{let c=new Error(n);throw c.subject_url=t,c};(typeof t!="string"||!t.trim())&&s("Invalid url."),t.length>G5.MAX_INPUT_LENGTH&&s("Input exceeds maximum length. If needed, change the value of parseUrl.MAX_INPUT_LENGTH."),e&&(typeof e!="object"&&(e={stripHash:!1}),t=QSt(t,e));let a=bSt.default(t);if(a.parse_failed){let n=a.href.match(r);n?(a.protocols=["ssh"],a.protocol="ssh",a.resource=n[2],a.host=n[2],a.user=n[1],a.pathname=`/${n[3]}`,a.parse_failed=!1):s("URL parsing failed.")}return a};G5.MAX_INPUT_LENGTH=2048;CBe.exports=G5});var SBe=L((xcr,vBe)=>{"use strict";var TSt=q5();function BBe(t){if(Array.isArray(t))return t.indexOf("ssh")!==-1||t.indexOf("rsync")!==-1;if(typeof t!="string")return!1;var e=TSt(t);if(t=t.substring(t.indexOf("://")+3),BBe(e))return!0;var r=new RegExp(".([a-zA-Z\\d]+):(\\d+)/");return!t.match(r)&&t.indexOf("@"){"use strict";var RSt=wBe(),DBe=SBe();function FSt(t){var e=RSt(t);return e.token="",e.password==="x-oauth-basic"?e.token=e.user:e.user==="x-token-auth"&&(e.token=e.password),DBe(e.protocols)||e.protocols.length===0&&DBe(t)?e.protocol="ssh":e.protocols.length?e.protocol=e.protocols[0]:(e.protocol="file",e.protocols=["file"]),e.href=e.href.replace(/\/$/,""),e}bBe.exports=FSt});var kBe=L((Qcr,xBe)=>{"use strict";var NSt=PBe();function W5(t){if(typeof t!="string")throw new Error("The url must be a string.");var e=/^([a-z\d-]{1,39})\/([-\.\w]{1,100})$/i;e.test(t)&&(t="https://github.com/"+t);var r=NSt(t),s=r.resource.split("."),a=null;switch(r.toString=function(N){return W5.stringify(this,N)},r.source=s.length>2?s.slice(1-s.length).join("."):r.source=r.resource,r.git_suffix=/\.git$/.test(r.pathname),r.name=decodeURIComponent((r.pathname||r.href).replace(/(^\/)|(\/$)/g,"").replace(/\.git$/,"")),r.owner=decodeURIComponent(r.user),r.source){case"git.cloudforge.com":r.owner=r.user,r.organization=s[0],r.source="cloudforge.com";break;case"visualstudio.com":if(r.resource==="vs-ssh.visualstudio.com"){a=r.name.split("/"),a.length===4&&(r.organization=a[1],r.owner=a[2],r.name=a[3],r.full_name=a[2]+"/"+a[3]);break}else{a=r.name.split("/"),a.length===2?(r.owner=a[1],r.name=a[1],r.full_name="_git/"+r.name):a.length===3?(r.name=a[2],a[0]==="DefaultCollection"?(r.owner=a[2],r.organization=a[0],r.full_name=r.organization+"/_git/"+r.name):(r.owner=a[0],r.full_name=r.owner+"/_git/"+r.name)):a.length===4&&(r.organization=a[0],r.owner=a[1],r.name=a[3],r.full_name=r.organization+"/"+r.owner+"/_git/"+r.name);break}case"dev.azure.com":case"azure.com":if(r.resource==="ssh.dev.azure.com"){a=r.name.split("/"),a.length===4&&(r.organization=a[1],r.owner=a[2],r.name=a[3]);break}else{a=r.name.split("/"),a.length===5?(r.organization=a[0],r.owner=a[1],r.name=a[4],r.full_name="_git/"+r.name):a.length===3?(r.name=a[2],a[0]==="DefaultCollection"?(r.owner=a[2],r.organization=a[0],r.full_name=r.organization+"/_git/"+r.name):(r.owner=a[0],r.full_name=r.owner+"/_git/"+r.name)):a.length===4&&(r.organization=a[0],r.owner=a[1],r.name=a[3],r.full_name=r.organization+"/"+r.owner+"/_git/"+r.name),r.query&&r.query.path&&(r.filepath=r.query.path.replace(/^\/+/g,"")),r.query&&r.query.version&&(r.ref=r.query.version.replace(/^GB/,""));break}default:a=r.name.split("/");var n=a.length-1;if(a.length>=2){var c=a.indexOf("-",2),f=a.indexOf("blob",2),p=a.indexOf("tree",2),h=a.indexOf("commit",2),E=a.indexOf("src",2),C=a.indexOf("raw",2),S=a.indexOf("edit",2);n=c>0?c-1:f>0?f-1:p>0?p-1:h>0?h-1:E>0?E-1:C>0?C-1:S>0?S-1:n,r.owner=a.slice(0,n).join("/"),r.name=a[n],h&&(r.commit=a[n+2])}r.ref="",r.filepathtype="",r.filepath="";var P=a.length>n&&a[n+1]==="-"?n+1:n;a.length>P+2&&["raw","src","blob","tree","edit"].indexOf(a[P+1])>=0&&(r.filepathtype=a[P+1],r.ref=a[P+2],a.length>P+3&&(r.filepath=a.slice(P+3).join("/"))),r.organization=r.owner;break}r.full_name||(r.full_name=r.owner,r.name&&(r.full_name&&(r.full_name+="/"),r.full_name+=r.name)),r.owner.startsWith("scm/")&&(r.source="bitbucket-server",r.owner=r.owner.replace("scm/",""),r.organization=r.owner,r.full_name=r.owner+"/"+r.name);var I=/(projects|users)\/(.*?)\/repos\/(.*?)((\/.*$)|$)/,R=I.exec(r.pathname);return R!=null&&(r.source="bitbucket-server",R[1]==="users"?r.owner="~"+R[2]:r.owner=R[2],r.organization=r.owner,r.name=R[3],a=R[4].split("/"),a.length>1&&(["raw","browse"].indexOf(a[1])>=0?(r.filepathtype=a[1],a.length>2&&(r.filepath=a.slice(2).join("/"))):a[1]==="commits"&&a.length>2&&(r.commit=a[2])),r.full_name=r.owner+"/"+r.name,r.query.at?r.ref=r.query.at:r.ref=""),r}W5.stringify=function(t,e){e=e||(t.protocols&&t.protocols.length?t.protocols.join("+"):t.protocol);var r=t.port?":"+t.port:"",s=t.user||"git",a=t.git_suffix?".git":"";switch(e){case"ssh":return r?"ssh://"+s+"@"+t.resource+r+"/"+t.full_name+a:s+"@"+t.resource+":"+t.full_name+a;case"git+ssh":case"ssh+git":case"ftp":case"ftps":return e+"://"+s+"@"+t.resource+r+"/"+t.full_name+a;case"http":case"https":var n=t.token?OSt(t):t.user&&(t.protocols.includes("http")||t.protocols.includes("https"))?t.user+"@":"";return e+"://"+n+t.resource+r+"/"+LSt(t)+a;default:return t.href}};function OSt(t){switch(t.source){case"bitbucket.org":return"x-token-auth:"+t.token+"@";default:return t.token+"@"}}function LSt(t){switch(t.source){case"bitbucket-server":return"scm/"+t.full_name;default:return""+t.full_name}}xBe.exports=W5});var YBe=L((ufr,WBe)=>{var KSt=RT(),JSt=Gk(),zSt=xc(),ZSt=aI(),XSt=IG(),$St=XI(),eDt=Tv();function tDt(t){return zSt(t)?KSt(t,$St):ZSt(t)?[t]:JSt(XSt(eDt(t)))}WBe.exports=tDt});function sDt(t,e){return e===1&&iDt.has(t[0])}function ES(t){let e=Array.isArray(t)?t:(0,JBe.default)(t);return e.map((s,a)=>rDt.test(s)?`[${s}]`:nDt.test(s)&&!sDt(e,a)?`.${s}`:`[${JSON.stringify(s)}]`).join("").replace(/^\./,"")}function oDt(t,e){let r=[];if(e.methodName!==null&&r.push(he.pretty(t,e.methodName,he.Type.CODE)),e.file!==null){let s=[];s.push(he.pretty(t,e.file,he.Type.PATH)),e.line!==null&&(s.push(he.pretty(t,e.line,he.Type.NUMBER)),e.column!==null&&s.push(he.pretty(t,e.column,he.Type.NUMBER))),r.push(`(${s.join(he.pretty(t,":","grey"))})`)}return r.join(" ")}function lF(t,{manifestUpdates:e,reportedErrors:r},{fix:s}={}){let a=new Map,n=new Map,c=[...r.keys()].map(f=>[f,new Map]);for(let[f,p]of[...c,...e]){let h=r.get(f)?.map(P=>({text:P,fixable:!1}))??[],E=!1,C=t.getWorkspaceByCwd(f),S=C.manifest.exportTo({});for(let[P,I]of p){if(I.size>1){let R=[...I].map(([N,U])=>{let W=he.pretty(t.configuration,N,he.Type.INSPECT),te=U.size>0?oDt(t.configuration,U.values().next().value):null;return te!==null?` +${W} at ${te}`:` +${W}`}).join("");h.push({text:`Conflict detected in constraint targeting ${he.pretty(t.configuration,P,he.Type.CODE)}; conflicting values are:${R}`,fixable:!1})}else{let[[R]]=I,N=(0,VBe.default)(S,P);if(JSON.stringify(N)===JSON.stringify(R))continue;if(!s){let U=typeof N>"u"?`Missing field ${he.pretty(t.configuration,P,he.Type.CODE)}; expected ${he.pretty(t.configuration,R,he.Type.INSPECT)}`:typeof R>"u"?`Extraneous field ${he.pretty(t.configuration,P,he.Type.CODE)} currently set to ${he.pretty(t.configuration,N,he.Type.INSPECT)}`:`Invalid field ${he.pretty(t.configuration,P,he.Type.CODE)}; expected ${he.pretty(t.configuration,R,he.Type.INSPECT)}, found ${he.pretty(t.configuration,N,he.Type.INSPECT)}`;h.push({text:U,fixable:!0});continue}typeof R>"u"?(0,zBe.default)(S,P):(0,KBe.default)(S,P,R),E=!0}E&&a.set(C,S)}h.length>0&&n.set(C,h)}return{changedWorkspaces:a,remainingErrors:n}}function ZBe(t,{configuration:e}){let r={children:[]};for(let[s,a]of t){let n=[];for(let f of a){let p=f.text.split(/\n/);f.fixable&&(p[0]=`${he.pretty(e,"\u2699","gray")} ${p[0]}`),n.push({value:he.tuple(he.Type.NO_HINT,p[0]),children:p.slice(1).map(h=>({value:he.tuple(he.Type.NO_HINT,h)}))})}let c={value:he.tuple(he.Type.LOCATOR,s.anchoredLocator),children:je.sortMap(n,f=>f.value[1])};r.children.push(c)}return r.children=je.sortMap(r.children,s=>s.value[1]),r}var VBe,KBe,JBe,zBe,VC,rDt,nDt,iDt,IS=It(()=>{Ve();VBe=et(AS()),KBe=et(Q5()),JBe=et(YBe()),zBe=et(F5()),VC=class{constructor(e){this.indexedFields=e;this.items=[];this.indexes={};this.clear()}clear(){this.items=[];for(let e of this.indexedFields)this.indexes[e]=new Map}insert(e){this.items.push(e);for(let r of this.indexedFields){let s=Object.hasOwn(e,r)?e[r]:void 0;if(typeof s>"u")continue;je.getArrayWithDefault(this.indexes[r],s).push(e)}return e}find(e){if(typeof e>"u")return this.items;let r=Object.entries(e);if(r.length===0)return this.items;let s=[],a;for(let[c,f]of r){let p=c,h=Object.hasOwn(this.indexes,p)?this.indexes[p]:void 0;if(typeof h>"u"){s.push([p,f]);continue}let E=new Set(h.get(f)??[]);if(E.size===0)return[];if(typeof a>"u")a=E;else for(let C of a)E.has(C)||a.delete(C);if(a.size===0)break}let n=[...a??[]];return s.length>0&&(n=n.filter(c=>{for(let[f,p]of s)if(!(typeof p<"u"?Object.hasOwn(c,f)&&c[f]===p:Object.hasOwn(c,f)===!1))return!1;return!0})),n}},rDt=/^[0-9]+$/,nDt=/^[a-zA-Z0-9_]+$/,iDt=new Set(["scripts",...Ht.allDependencies])});var XBe=L((wfr,s9)=>{var aDt;(function(t){var e=function(){return{"append/2":[new t.type.Rule(new t.type.Term("append",[new t.type.Var("X"),new t.type.Var("L")]),new t.type.Term("foldl",[new t.type.Term("append",[]),new t.type.Var("X"),new t.type.Term("[]",[]),new t.type.Var("L")]))],"append/3":[new t.type.Rule(new t.type.Term("append",[new t.type.Term("[]",[]),new t.type.Var("X"),new t.type.Var("X")]),null),new t.type.Rule(new t.type.Term("append",[new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("T")]),new t.type.Var("X"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("S")])]),new t.type.Term("append",[new t.type.Var("T"),new t.type.Var("X"),new t.type.Var("S")]))],"member/2":[new t.type.Rule(new t.type.Term("member",[new t.type.Var("X"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("_")])]),null),new t.type.Rule(new t.type.Term("member",[new t.type.Var("X"),new t.type.Term(".",[new t.type.Var("_"),new t.type.Var("Xs")])]),new t.type.Term("member",[new t.type.Var("X"),new t.type.Var("Xs")]))],"permutation/2":[new t.type.Rule(new t.type.Term("permutation",[new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("permutation",[new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("T")]),new t.type.Var("S")]),new t.type.Term(",",[new t.type.Term("permutation",[new t.type.Var("T"),new t.type.Var("P")]),new t.type.Term(",",[new t.type.Term("append",[new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("P")]),new t.type.Term("append",[new t.type.Var("X"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("Y")]),new t.type.Var("S")])])]))],"maplist/2":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("X")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("Xs")])]))],"maplist/3":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("A"),new t.type.Var("As")]),new t.type.Term(".",[new t.type.Var("B"),new t.type.Var("Bs")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("A"),new t.type.Var("B")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("As"),new t.type.Var("Bs")])]))],"maplist/4":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("A"),new t.type.Var("As")]),new t.type.Term(".",[new t.type.Var("B"),new t.type.Var("Bs")]),new t.type.Term(".",[new t.type.Var("C"),new t.type.Var("Cs")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("A"),new t.type.Var("B"),new t.type.Var("C")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("As"),new t.type.Var("Bs"),new t.type.Var("Cs")])]))],"maplist/5":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("A"),new t.type.Var("As")]),new t.type.Term(".",[new t.type.Var("B"),new t.type.Var("Bs")]),new t.type.Term(".",[new t.type.Var("C"),new t.type.Var("Cs")]),new t.type.Term(".",[new t.type.Var("D"),new t.type.Var("Ds")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("A"),new t.type.Var("B"),new t.type.Var("C"),new t.type.Var("D")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("As"),new t.type.Var("Bs"),new t.type.Var("Cs"),new t.type.Var("Ds")])]))],"maplist/6":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("A"),new t.type.Var("As")]),new t.type.Term(".",[new t.type.Var("B"),new t.type.Var("Bs")]),new t.type.Term(".",[new t.type.Var("C"),new t.type.Var("Cs")]),new t.type.Term(".",[new t.type.Var("D"),new t.type.Var("Ds")]),new t.type.Term(".",[new t.type.Var("E"),new t.type.Var("Es")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("A"),new t.type.Var("B"),new t.type.Var("C"),new t.type.Var("D"),new t.type.Var("E")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("As"),new t.type.Var("Bs"),new t.type.Var("Cs"),new t.type.Var("Ds"),new t.type.Var("Es")])]))],"maplist/7":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("A"),new t.type.Var("As")]),new t.type.Term(".",[new t.type.Var("B"),new t.type.Var("Bs")]),new t.type.Term(".",[new t.type.Var("C"),new t.type.Var("Cs")]),new t.type.Term(".",[new t.type.Var("D"),new t.type.Var("Ds")]),new t.type.Term(".",[new t.type.Var("E"),new t.type.Var("Es")]),new t.type.Term(".",[new t.type.Var("F"),new t.type.Var("Fs")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("A"),new t.type.Var("B"),new t.type.Var("C"),new t.type.Var("D"),new t.type.Var("E"),new t.type.Var("F")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("As"),new t.type.Var("Bs"),new t.type.Var("Cs"),new t.type.Var("Ds"),new t.type.Var("Es"),new t.type.Var("Fs")])]))],"maplist/8":[new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("A"),new t.type.Var("As")]),new t.type.Term(".",[new t.type.Var("B"),new t.type.Var("Bs")]),new t.type.Term(".",[new t.type.Var("C"),new t.type.Var("Cs")]),new t.type.Term(".",[new t.type.Var("D"),new t.type.Var("Ds")]),new t.type.Term(".",[new t.type.Var("E"),new t.type.Var("Es")]),new t.type.Term(".",[new t.type.Var("F"),new t.type.Var("Fs")]),new t.type.Term(".",[new t.type.Var("G"),new t.type.Var("Gs")])]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P"),new t.type.Var("A"),new t.type.Var("B"),new t.type.Var("C"),new t.type.Var("D"),new t.type.Var("E"),new t.type.Var("F"),new t.type.Var("G")]),new t.type.Term("maplist",[new t.type.Var("P"),new t.type.Var("As"),new t.type.Var("Bs"),new t.type.Var("Cs"),new t.type.Var("Ds"),new t.type.Var("Es"),new t.type.Var("Fs"),new t.type.Var("Gs")])]))],"include/3":[new t.type.Rule(new t.type.Term("include",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("include",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("T")]),new t.type.Var("L")]),new t.type.Term(",",[new t.type.Term("=..",[new t.type.Var("P"),new t.type.Var("A")]),new t.type.Term(",",[new t.type.Term("append",[new t.type.Var("A"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Term("[]",[])]),new t.type.Var("B")]),new t.type.Term(",",[new t.type.Term("=..",[new t.type.Var("F"),new t.type.Var("B")]),new t.type.Term(",",[new t.type.Term(";",[new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("F")]),new t.type.Term(",",[new t.type.Term("=",[new t.type.Var("L"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("S")])]),new t.type.Term("!",[])])]),new t.type.Term("=",[new t.type.Var("L"),new t.type.Var("S")])]),new t.type.Term("include",[new t.type.Var("P"),new t.type.Var("T"),new t.type.Var("S")])])])])]))],"exclude/3":[new t.type.Rule(new t.type.Term("exclude",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Term("[]",[])]),null),new t.type.Rule(new t.type.Term("exclude",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("T")]),new t.type.Var("S")]),new t.type.Term(",",[new t.type.Term("exclude",[new t.type.Var("P"),new t.type.Var("T"),new t.type.Var("E")]),new t.type.Term(",",[new t.type.Term("=..",[new t.type.Var("P"),new t.type.Var("L")]),new t.type.Term(",",[new t.type.Term("append",[new t.type.Var("L"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Term("[]",[])]),new t.type.Var("Q")]),new t.type.Term(",",[new t.type.Term("=..",[new t.type.Var("R"),new t.type.Var("Q")]),new t.type.Term(";",[new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("R")]),new t.type.Term(",",[new t.type.Term("!",[]),new t.type.Term("=",[new t.type.Var("S"),new t.type.Var("E")])])]),new t.type.Term("=",[new t.type.Var("S"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("E")])])])])])])]))],"foldl/4":[new t.type.Rule(new t.type.Term("foldl",[new t.type.Var("_"),new t.type.Term("[]",[]),new t.type.Var("I"),new t.type.Var("I")]),null),new t.type.Rule(new t.type.Term("foldl",[new t.type.Var("P"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Var("T")]),new t.type.Var("I"),new t.type.Var("R")]),new t.type.Term(",",[new t.type.Term("=..",[new t.type.Var("P"),new t.type.Var("L")]),new t.type.Term(",",[new t.type.Term("append",[new t.type.Var("L"),new t.type.Term(".",[new t.type.Var("I"),new t.type.Term(".",[new t.type.Var("H"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Term("[]",[])])])]),new t.type.Var("L2")]),new t.type.Term(",",[new t.type.Term("=..",[new t.type.Var("P2"),new t.type.Var("L2")]),new t.type.Term(",",[new t.type.Term("call",[new t.type.Var("P2")]),new t.type.Term("foldl",[new t.type.Var("P"),new t.type.Var("T"),new t.type.Var("X"),new t.type.Var("R")])])])])]))],"select/3":[new t.type.Rule(new t.type.Term("select",[new t.type.Var("E"),new t.type.Term(".",[new t.type.Var("E"),new t.type.Var("Xs")]),new t.type.Var("Xs")]),null),new t.type.Rule(new t.type.Term("select",[new t.type.Var("E"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Ys")])]),new t.type.Term("select",[new t.type.Var("E"),new t.type.Var("Xs"),new t.type.Var("Ys")]))],"sum_list/2":[new t.type.Rule(new t.type.Term("sum_list",[new t.type.Term("[]",[]),new t.type.Num(0,!1)]),null),new t.type.Rule(new t.type.Term("sum_list",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Var("S")]),new t.type.Term(",",[new t.type.Term("sum_list",[new t.type.Var("Xs"),new t.type.Var("Y")]),new t.type.Term("is",[new t.type.Var("S"),new t.type.Term("+",[new t.type.Var("X"),new t.type.Var("Y")])])]))],"max_list/2":[new t.type.Rule(new t.type.Term("max_list",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Term("[]",[])]),new t.type.Var("X")]),null),new t.type.Rule(new t.type.Term("max_list",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Var("S")]),new t.type.Term(",",[new t.type.Term("max_list",[new t.type.Var("Xs"),new t.type.Var("Y")]),new t.type.Term(";",[new t.type.Term(",",[new t.type.Term(">=",[new t.type.Var("X"),new t.type.Var("Y")]),new t.type.Term(",",[new t.type.Term("=",[new t.type.Var("S"),new t.type.Var("X")]),new t.type.Term("!",[])])]),new t.type.Term("=",[new t.type.Var("S"),new t.type.Var("Y")])])]))],"min_list/2":[new t.type.Rule(new t.type.Term("min_list",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Term("[]",[])]),new t.type.Var("X")]),null),new t.type.Rule(new t.type.Term("min_list",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Var("S")]),new t.type.Term(",",[new t.type.Term("min_list",[new t.type.Var("Xs"),new t.type.Var("Y")]),new t.type.Term(";",[new t.type.Term(",",[new t.type.Term("=<",[new t.type.Var("X"),new t.type.Var("Y")]),new t.type.Term(",",[new t.type.Term("=",[new t.type.Var("S"),new t.type.Var("X")]),new t.type.Term("!",[])])]),new t.type.Term("=",[new t.type.Var("S"),new t.type.Var("Y")])])]))],"prod_list/2":[new t.type.Rule(new t.type.Term("prod_list",[new t.type.Term("[]",[]),new t.type.Num(1,!1)]),null),new t.type.Rule(new t.type.Term("prod_list",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Var("S")]),new t.type.Term(",",[new t.type.Term("prod_list",[new t.type.Var("Xs"),new t.type.Var("Y")]),new t.type.Term("is",[new t.type.Var("S"),new t.type.Term("*",[new t.type.Var("X"),new t.type.Var("Y")])])]))],"last/2":[new t.type.Rule(new t.type.Term("last",[new t.type.Term(".",[new t.type.Var("X"),new t.type.Term("[]",[])]),new t.type.Var("X")]),null),new t.type.Rule(new t.type.Term("last",[new t.type.Term(".",[new t.type.Var("_"),new t.type.Var("Xs")]),new t.type.Var("X")]),new t.type.Term("last",[new t.type.Var("Xs"),new t.type.Var("X")]))],"prefix/2":[new t.type.Rule(new t.type.Term("prefix",[new t.type.Var("Part"),new t.type.Var("Whole")]),new t.type.Term("append",[new t.type.Var("Part"),new t.type.Var("_"),new t.type.Var("Whole")]))],"nth0/3":[new t.type.Rule(new t.type.Term("nth0",[new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z")]),new t.type.Term(";",[new t.type.Term("->",[new t.type.Term("var",[new t.type.Var("X")]),new t.type.Term("nth",[new t.type.Num(0,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("_")])]),new t.type.Term(",",[new t.type.Term(">=",[new t.type.Var("X"),new t.type.Num(0,!1)]),new t.type.Term(",",[new t.type.Term("nth",[new t.type.Num(0,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("_")]),new t.type.Term("!",[])])])]))],"nth1/3":[new t.type.Rule(new t.type.Term("nth1",[new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z")]),new t.type.Term(";",[new t.type.Term("->",[new t.type.Term("var",[new t.type.Var("X")]),new t.type.Term("nth",[new t.type.Num(1,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("_")])]),new t.type.Term(",",[new t.type.Term(">",[new t.type.Var("X"),new t.type.Num(0,!1)]),new t.type.Term(",",[new t.type.Term("nth",[new t.type.Num(1,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("_")]),new t.type.Term("!",[])])])]))],"nth0/4":[new t.type.Rule(new t.type.Term("nth0",[new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("W")]),new t.type.Term(";",[new t.type.Term("->",[new t.type.Term("var",[new t.type.Var("X")]),new t.type.Term("nth",[new t.type.Num(0,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("W")])]),new t.type.Term(",",[new t.type.Term(">=",[new t.type.Var("X"),new t.type.Num(0,!1)]),new t.type.Term(",",[new t.type.Term("nth",[new t.type.Num(0,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("W")]),new t.type.Term("!",[])])])]))],"nth1/4":[new t.type.Rule(new t.type.Term("nth1",[new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("W")]),new t.type.Term(";",[new t.type.Term("->",[new t.type.Term("var",[new t.type.Var("X")]),new t.type.Term("nth",[new t.type.Num(1,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("W")])]),new t.type.Term(",",[new t.type.Term(">",[new t.type.Var("X"),new t.type.Num(0,!1)]),new t.type.Term(",",[new t.type.Term("nth",[new t.type.Num(1,!1),new t.type.Var("X"),new t.type.Var("Y"),new t.type.Var("Z"),new t.type.Var("W")]),new t.type.Term("!",[])])])]))],"nth/5":[new t.type.Rule(new t.type.Term("nth",[new t.type.Var("N"),new t.type.Var("N"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Var("X"),new t.type.Var("Xs")]),null),new t.type.Rule(new t.type.Term("nth",[new t.type.Var("N"),new t.type.Var("O"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Xs")]),new t.type.Var("Y"),new t.type.Term(".",[new t.type.Var("X"),new t.type.Var("Ys")])]),new t.type.Term(",",[new t.type.Term("is",[new t.type.Var("M"),new t.type.Term("+",[new t.type.Var("N"),new t.type.Num(1,!1)])]),new t.type.Term("nth",[new t.type.Var("M"),new t.type.Var("O"),new t.type.Var("Xs"),new t.type.Var("Y"),new t.type.Var("Ys")])]))],"length/2":function(s,a,n){var c=n.args[0],f=n.args[1];if(!t.type.is_variable(f)&&!t.type.is_integer(f))s.throw_error(t.error.type("integer",f,n.indicator));else if(t.type.is_integer(f)&&f.value<0)s.throw_error(t.error.domain("not_less_than_zero",f,n.indicator));else{var p=new t.type.Term("length",[c,new t.type.Num(0,!1),f]);t.type.is_integer(f)&&(p=new t.type.Term(",",[p,new t.type.Term("!",[])])),s.prepend([new t.type.State(a.goal.replace(p),a.substitution,a)])}},"length/3":[new t.type.Rule(new t.type.Term("length",[new t.type.Term("[]",[]),new t.type.Var("N"),new t.type.Var("N")]),null),new t.type.Rule(new t.type.Term("length",[new t.type.Term(".",[new t.type.Var("_"),new t.type.Var("X")]),new t.type.Var("A"),new t.type.Var("N")]),new t.type.Term(",",[new t.type.Term("succ",[new t.type.Var("A"),new t.type.Var("B")]),new t.type.Term("length",[new t.type.Var("X"),new t.type.Var("B"),new t.type.Var("N")])]))],"replicate/3":function(s,a,n){var c=n.args[0],f=n.args[1],p=n.args[2];if(t.type.is_variable(f))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_integer(f))s.throw_error(t.error.type("integer",f,n.indicator));else if(f.value<0)s.throw_error(t.error.domain("not_less_than_zero",f,n.indicator));else if(!t.type.is_variable(p)&&!t.type.is_list(p))s.throw_error(t.error.type("list",p,n.indicator));else{for(var h=new t.type.Term("[]"),E=0;E0;C--)E[C].equals(E[C-1])&&E.splice(C,1);for(var S=new t.type.Term("[]"),C=E.length-1;C>=0;C--)S=new t.type.Term(".",[E[C],S]);s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[S,f])),a.substitution,a)])}}},"msort/2":function(s,a,n){var c=n.args[0],f=n.args[1];if(t.type.is_variable(c))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_variable(f)&&!t.type.is_fully_list(f))s.throw_error(t.error.type("list",f,n.indicator));else{for(var p=[],h=c;h.indicator==="./2";)p.push(h.args[0]),h=h.args[1];if(t.type.is_variable(h))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_empty_list(h))s.throw_error(t.error.type("list",c,n.indicator));else{for(var E=p.sort(t.compare),C=new t.type.Term("[]"),S=E.length-1;S>=0;S--)C=new t.type.Term(".",[E[S],C]);s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[C,f])),a.substitution,a)])}}},"keysort/2":function(s,a,n){var c=n.args[0],f=n.args[1];if(t.type.is_variable(c))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_variable(f)&&!t.type.is_fully_list(f))s.throw_error(t.error.type("list",f,n.indicator));else{for(var p=[],h,E=c;E.indicator==="./2";){if(h=E.args[0],t.type.is_variable(h)){s.throw_error(t.error.instantiation(n.indicator));return}else if(!t.type.is_term(h)||h.indicator!=="-/2"){s.throw_error(t.error.type("pair",h,n.indicator));return}h.args[0].pair=h.args[1],p.push(h.args[0]),E=E.args[1]}if(t.type.is_variable(E))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_empty_list(E))s.throw_error(t.error.type("list",c,n.indicator));else{for(var C=p.sort(t.compare),S=new t.type.Term("[]"),P=C.length-1;P>=0;P--)S=new t.type.Term(".",[new t.type.Term("-",[C[P],C[P].pair]),S]),delete C[P].pair;s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[S,f])),a.substitution,a)])}}},"take/3":function(s,a,n){var c=n.args[0],f=n.args[1],p=n.args[2];if(t.type.is_variable(f)||t.type.is_variable(c))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_list(f))s.throw_error(t.error.type("list",f,n.indicator));else if(!t.type.is_integer(c))s.throw_error(t.error.type("integer",c,n.indicator));else if(!t.type.is_variable(p)&&!t.type.is_list(p))s.throw_error(t.error.type("list",p,n.indicator));else{for(var h=c.value,E=[],C=f;h>0&&C.indicator==="./2";)E.push(C.args[0]),C=C.args[1],h--;if(h===0){for(var S=new t.type.Term("[]"),h=E.length-1;h>=0;h--)S=new t.type.Term(".",[E[h],S]);s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[S,p])),a.substitution,a)])}}},"drop/3":function(s,a,n){var c=n.args[0],f=n.args[1],p=n.args[2];if(t.type.is_variable(f)||t.type.is_variable(c))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_list(f))s.throw_error(t.error.type("list",f,n.indicator));else if(!t.type.is_integer(c))s.throw_error(t.error.type("integer",c,n.indicator));else if(!t.type.is_variable(p)&&!t.type.is_list(p))s.throw_error(t.error.type("list",p,n.indicator));else{for(var h=c.value,E=[],C=f;h>0&&C.indicator==="./2";)E.push(C.args[0]),C=C.args[1],h--;h===0&&s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[C,p])),a.substitution,a)])}},"reverse/2":function(s,a,n){var c=n.args[0],f=n.args[1],p=t.type.is_instantiated_list(c),h=t.type.is_instantiated_list(f);if(t.type.is_variable(c)&&t.type.is_variable(f))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_variable(c)&&!t.type.is_fully_list(c))s.throw_error(t.error.type("list",c,n.indicator));else if(!t.type.is_variable(f)&&!t.type.is_fully_list(f))s.throw_error(t.error.type("list",f,n.indicator));else if(!p&&!h)s.throw_error(t.error.instantiation(n.indicator));else{for(var E=p?c:f,C=new t.type.Term("[]",[]);E.indicator==="./2";)C=new t.type.Term(".",[E.args[0],C]),E=E.args[1];s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[C,p?f:c])),a.substitution,a)])}},"list_to_set/2":function(s,a,n){var c=n.args[0],f=n.args[1];if(t.type.is_variable(c))s.throw_error(t.error.instantiation(n.indicator));else{for(var p=c,h=[];p.indicator==="./2";)h.push(p.args[0]),p=p.args[1];if(t.type.is_variable(p))s.throw_error(t.error.instantiation(n.indicator));else if(!t.type.is_term(p)||p.indicator!=="[]/0")s.throw_error(t.error.type("list",c,n.indicator));else{for(var E=[],C=new t.type.Term("[]",[]),S,P=0;P=0;P--)C=new t.type.Term(".",[E[P],C]);s.prepend([new t.type.State(a.goal.replace(new t.type.Term("=",[f,C])),a.substitution,a)])}}}}},r=["append/2","append/3","member/2","permutation/2","maplist/2","maplist/3","maplist/4","maplist/5","maplist/6","maplist/7","maplist/8","include/3","exclude/3","foldl/4","sum_list/2","max_list/2","min_list/2","prod_list/2","last/2","prefix/2","nth0/3","nth1/3","nth0/4","nth1/4","length/2","replicate/3","select/3","sort/2","msort/2","keysort/2","take/3","drop/3","reverse/2","list_to_set/2"];typeof s9<"u"?s9.exports=function(s){t=s,new t.type.Module("lists",e(),r)}:new t.type.Module("lists",e(),r)})(aDt)});var pve=L($r=>{"use strict";var Pm=process.platform==="win32",o9="aes-256-cbc",lDt="sha256",tve="The current environment doesn't support interactive reading from TTY.",ii=ye("fs"),$Be=process.binding("tty_wrap").TTY,l9=ye("child_process"),K0=ye("path"),c9={prompt:"> ",hideEchoBack:!1,mask:"*",limit:[],limitMessage:"Input another, please.$<( [)limit(])>",defaultInput:"",trueValue:[],falseValue:[],caseSensitive:!1,keepWhitespace:!1,encoding:"utf8",bufferSize:1024,print:void 0,history:!0,cd:!1,phContent:void 0,preCheck:void 0},eh="none",$u,JC,eve=!1,V0,uF,a9,cDt=0,h9="",bm=[],fF,rve=!1,u9=!1,CS=!1;function nve(t){function e(r){return r.replace(/[^\w\u0080-\uFFFF]/g,function(s){return"#"+s.charCodeAt(0)+";"})}return uF.concat(function(r){var s=[];return Object.keys(r).forEach(function(a){r[a]==="boolean"?t[a]&&s.push("--"+a):r[a]==="string"&&t[a]&&s.push("--"+a,e(t[a]))}),s}({display:"string",displayOnly:"boolean",keyIn:"boolean",hideEchoBack:"boolean",mask:"string",limit:"string",caseSensitive:"boolean"}))}function uDt(t,e){function r(U){var W,te="",ie;for(a9=a9||ye("os").tmpdir();;){W=K0.join(a9,U+te);try{ie=ii.openSync(W,"wx")}catch(Ae){if(Ae.code==="EEXIST"){te++;continue}else throw Ae}ii.closeSync(ie);break}return W}var s,a,n,c={},f,p,h=r("readline-sync.stdout"),E=r("readline-sync.stderr"),C=r("readline-sync.exit"),S=r("readline-sync.done"),P=ye("crypto"),I,R,N;I=P.createHash(lDt),I.update(""+process.pid+cDt+++Math.random()),N=I.digest("hex"),R=P.createDecipher(o9,N),s=nve(t),Pm?(a=process.env.ComSpec||"cmd.exe",process.env.Q='"',n=["/V:ON","/S","/C","(%Q%"+a+"%Q% /V:ON /S /C %Q%%Q%"+V0+"%Q%"+s.map(function(U){return" %Q%"+U+"%Q%"}).join("")+" & (echo !ERRORLEVEL!)>%Q%"+C+"%Q%%Q%) 2>%Q%"+E+"%Q% |%Q%"+process.execPath+"%Q% %Q%"+__dirname+"\\encrypt.js%Q% %Q%"+o9+"%Q% %Q%"+N+"%Q% >%Q%"+h+"%Q% & (echo 1)>%Q%"+S+"%Q%"]):(a="/bin/sh",n=["-c",'("'+V0+'"'+s.map(function(U){return" '"+U.replace(/'/g,"'\\''")+"'"}).join("")+'; echo $?>"'+C+'") 2>"'+E+'" |"'+process.execPath+'" "'+__dirname+'/encrypt.js" "'+o9+'" "'+N+'" >"'+h+'"; echo 1 >"'+S+'"']),CS&&CS("_execFileSync",s);try{l9.spawn(a,n,e)}catch(U){c.error=new Error(U.message),c.error.method="_execFileSync - spawn",c.error.program=a,c.error.args=n}for(;ii.readFileSync(S,{encoding:t.encoding}).trim()!=="1";);return(f=ii.readFileSync(C,{encoding:t.encoding}).trim())==="0"?c.input=R.update(ii.readFileSync(h,{encoding:"binary"}),"hex",t.encoding)+R.final(t.encoding):(p=ii.readFileSync(E,{encoding:t.encoding}).trim(),c.error=new Error(tve+(p?` +`+p:"")),c.error.method="_execFileSync",c.error.program=a,c.error.args=n,c.error.extMessage=p,c.error.exitCode=+f),ii.unlinkSync(h),ii.unlinkSync(E),ii.unlinkSync(C),ii.unlinkSync(S),c}function fDt(t){var e,r={},s,a={env:process.env,encoding:t.encoding};if(V0||(Pm?process.env.PSModulePath?(V0="powershell.exe",uF=["-ExecutionPolicy","Bypass","-File",__dirname+"\\read.ps1"]):(V0="cscript.exe",uF=["//nologo",__dirname+"\\read.cs.js"]):(V0="/bin/sh",uF=[__dirname+"/read.sh"])),Pm&&!process.env.PSModulePath&&(a.stdio=[process.stdin]),l9.execFileSync){e=nve(t),CS&&CS("execFileSync",e);try{r.input=l9.execFileSync(V0,e,a)}catch(n){s=n.stderr?(n.stderr+"").trim():"",r.error=new Error(tve+(s?` +`+s:"")),r.error.method="execFileSync",r.error.program=V0,r.error.args=e,r.error.extMessage=s,r.error.exitCode=n.status,r.error.code=n.code,r.error.signal=n.signal}}else r=uDt(t,a);return r.error||(r.input=r.input.replace(/^\s*'|'\s*$/g,""),t.display=""),r}function f9(t){var e="",r=t.display,s=!t.display&&t.keyIn&&t.hideEchoBack&&!t.mask;function a(){var n=fDt(t);if(n.error)throw n.error;return n.input}return u9&&u9(t),function(){var n,c,f;function p(){return n||(n=process.binding("fs"),c=process.binding("constants")),n}if(typeof eh=="string")if(eh=null,Pm){if(f=function(h){var E=h.replace(/^\D+/,"").split("."),C=0;return(E[0]=+E[0])&&(C+=E[0]*1e4),(E[1]=+E[1])&&(C+=E[1]*100),(E[2]=+E[2])&&(C+=E[2]),C}(process.version),!(f>=20302&&f<40204||f>=5e4&&f<50100||f>=50600&&f<60200)&&process.stdin.isTTY)process.stdin.pause(),eh=process.stdin.fd,JC=process.stdin._handle;else try{eh=p().open("CONIN$",c.O_RDWR,parseInt("0666",8)),JC=new $Be(eh,!0)}catch{}if(process.stdout.isTTY)$u=process.stdout.fd;else{try{$u=ii.openSync("\\\\.\\CON","w")}catch{}if(typeof $u!="number")try{$u=p().open("CONOUT$",c.O_RDWR,parseInt("0666",8))}catch{}}}else{if(process.stdin.isTTY){process.stdin.pause();try{eh=ii.openSync("/dev/tty","r"),JC=process.stdin._handle}catch{}}else try{eh=ii.openSync("/dev/tty","r"),JC=new $Be(eh,!1)}catch{}if(process.stdout.isTTY)$u=process.stdout.fd;else try{$u=ii.openSync("/dev/tty","w")}catch{}}}(),function(){var n,c,f=!t.hideEchoBack&&!t.keyIn,p,h,E,C,S;fF="";function P(I){return I===eve?!0:JC.setRawMode(I)!==0?!1:(eve=I,!0)}if(rve||!JC||typeof $u!="number"&&(t.display||!f)){e=a();return}if(t.display&&(ii.writeSync($u,t.display),t.display=""),!t.displayOnly){if(!P(!f)){e=a();return}for(h=t.keyIn?1:t.bufferSize,p=Buffer.allocUnsafe&&Buffer.alloc?Buffer.alloc(h):new Buffer(h),t.keyIn&&t.limit&&(c=new RegExp("[^"+t.limit+"]","g"+(t.caseSensitive?"":"i")));;){E=0;try{E=ii.readSync(eh,p,0,h)}catch(I){if(I.code!=="EOF"){P(!1),e+=a();return}}if(E>0?(C=p.toString(t.encoding,0,E),fF+=C):(C=` +`,fF+="\0"),C&&typeof(S=(C.match(/^(.*?)[\r\n]/)||[])[1])=="string"&&(C=S,n=!0),C&&(C=C.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"")),C&&c&&(C=C.replace(c,"")),C&&(f||(t.hideEchoBack?t.mask&&ii.writeSync($u,new Array(C.length+1).join(t.mask)):ii.writeSync($u,C)),e+=C),!t.keyIn&&n||t.keyIn&&e.length>=h)break}!f&&!s&&ii.writeSync($u,` +`),P(!1)}}(),t.print&&!s&&t.print(r+(t.displayOnly?"":(t.hideEchoBack?new Array(e.length+1).join(t.mask):e)+` +`),t.encoding),t.displayOnly?"":h9=t.keepWhitespace||t.keyIn?e:e.trim()}function ADt(t,e){var r=[];function s(a){a!=null&&(Array.isArray(a)?a.forEach(s):(!e||e(a))&&r.push(a))}return s(t),r}function g9(t){return t.replace(/[\x00-\x7f]/g,function(e){return"\\x"+("00"+e.charCodeAt().toString(16)).substr(-2)})}function Ks(){var t=Array.prototype.slice.call(arguments),e,r;return t.length&&typeof t[0]=="boolean"&&(r=t.shift(),r&&(e=Object.keys(c9),t.unshift(c9))),t.reduce(function(s,a){return a==null||(a.hasOwnProperty("noEchoBack")&&!a.hasOwnProperty("hideEchoBack")&&(a.hideEchoBack=a.noEchoBack,delete a.noEchoBack),a.hasOwnProperty("noTrim")&&!a.hasOwnProperty("keepWhitespace")&&(a.keepWhitespace=a.noTrim,delete a.noTrim),r||(e=Object.keys(a)),e.forEach(function(n){var c;if(a.hasOwnProperty(n))switch(c=a[n],n){case"mask":case"limitMessage":case"defaultInput":case"encoding":c=c!=null?c+"":"",c&&n!=="limitMessage"&&(c=c.replace(/[\r\n]/g,"")),s[n]=c;break;case"bufferSize":!isNaN(c=parseInt(c,10))&&typeof c=="number"&&(s[n]=c);break;case"displayOnly":case"keyIn":case"hideEchoBack":case"caseSensitive":case"keepWhitespace":case"history":case"cd":s[n]=!!c;break;case"limit":case"trueValue":case"falseValue":s[n]=ADt(c,function(f){var p=typeof f;return p==="string"||p==="number"||p==="function"||f instanceof RegExp}).map(function(f){return typeof f=="string"?f.replace(/[\r\n]/g,""):f});break;case"print":case"phContent":case"preCheck":s[n]=typeof c=="function"?c:void 0;break;case"prompt":case"display":s[n]=c??"";break}})),s},{})}function A9(t,e,r){return e.some(function(s){var a=typeof s;return a==="string"?r?t===s:t.toLowerCase()===s.toLowerCase():a==="number"?parseFloat(t)===s:a==="function"?s(t):s instanceof RegExp?s.test(t):!1})}function d9(t,e){var r=K0.normalize(Pm?(process.env.HOMEDRIVE||"")+(process.env.HOMEPATH||""):process.env.HOME||"").replace(/[\/\\]+$/,"");return t=K0.normalize(t),e?t.replace(/^~(?=\/|\\|$)/,r):t.replace(new RegExp("^"+g9(r)+"(?=\\/|\\\\|$)",Pm?"i":""),"~")}function zC(t,e){var r="(?:\\(([\\s\\S]*?)\\))?(\\w+|.-.)(?:\\(([\\s\\S]*?)\\))?",s=new RegExp("(\\$)?(\\$<"+r+">)","g"),a=new RegExp("(\\$)?(\\$\\{"+r+"\\})","g");function n(c,f,p,h,E,C){var S;return f||typeof(S=e(E))!="string"?p:S?(h||"")+S+(C||""):""}return t.replace(s,n).replace(a,n)}function ive(t,e,r){var s,a=[],n=-1,c=0,f="",p;function h(E,C){return C.length>3?(E.push(C[0]+"..."+C[C.length-1]),p=!0):C.length&&(E=E.concat(C)),E}return s=t.reduce(function(E,C){return E.concat((C+"").split(""))},[]).reduce(function(E,C){var S,P;return e||(C=C.toLowerCase()),S=/^\d$/.test(C)?1:/^[A-Z]$/.test(C)?2:/^[a-z]$/.test(C)?3:0,r&&S===0?f+=C:(P=C.charCodeAt(0),S&&S===n&&P===c+1?a.push(C):(E=h(E,a),a=[C],n=S),c=P),E},[]),s=h(s,a),f&&(s.push(f),p=!0),{values:s,suppressed:p}}function sve(t,e){return t.join(t.length>2?", ":e?" / ":"/")}function ove(t,e){var r,s,a={},n;if(e.phContent&&(r=e.phContent(t,e)),typeof r!="string")switch(t){case"hideEchoBack":case"mask":case"defaultInput":case"caseSensitive":case"keepWhitespace":case"encoding":case"bufferSize":case"history":case"cd":r=e.hasOwnProperty(t)?typeof e[t]=="boolean"?e[t]?"on":"off":e[t]+"":"";break;case"limit":case"trueValue":case"falseValue":s=e[e.hasOwnProperty(t+"Src")?t+"Src":t],e.keyIn?(a=ive(s,e.caseSensitive),s=a.values):s=s.filter(function(c){var f=typeof c;return f==="string"||f==="number"}),r=sve(s,a.suppressed);break;case"limitCount":case"limitCountNotZero":r=e[e.hasOwnProperty("limitSrc")?"limitSrc":"limit"].length,r=r||t!=="limitCountNotZero"?r+"":"";break;case"lastInput":r=h9;break;case"cwd":case"CWD":case"cwdHome":r=process.cwd(),t==="CWD"?r=K0.basename(r):t==="cwdHome"&&(r=d9(r));break;case"date":case"time":case"localeDate":case"localeTime":r=new Date()["to"+t.replace(/^./,function(c){return c.toUpperCase()})+"String"]();break;default:typeof(n=(t.match(/^history_m(\d+)$/)||[])[1])=="string"&&(r=bm[bm.length-n]||"")}return r}function ave(t){var e=/^(.)-(.)$/.exec(t),r="",s,a,n,c;if(!e)return null;for(s=e[1].charCodeAt(0),a=e[2].charCodeAt(0),c=s +And the length must be: $`,trueValue:null,falseValue:null,caseSensitive:!0},e,{history:!1,cd:!1,phContent:function(P){return P==="charlist"?r.text:P==="length"?s+"..."+a:null}}),c,f,p,h,E,C,S;for(e=e||{},c=zC(e.charlist?e.charlist+"":"$",ave),(isNaN(s=parseInt(e.min,10))||typeof s!="number")&&(s=12),(isNaN(a=parseInt(e.max,10))||typeof a!="number")&&(a=24),h=new RegExp("^["+g9(c)+"]{"+s+","+a+"}$"),r=ive([c],n.caseSensitive,!0),r.text=sve(r.values,r.suppressed),f=e.confirmMessage!=null?e.confirmMessage:"Reinput a same one to confirm it: ",p=e.unmatchMessage!=null?e.unmatchMessage:"It differs from first one. Hit only the Enter key if you want to retry from first one.",t==null&&(t="Input new password: "),E=n.limitMessage;!S;)n.limit=h,n.limitMessage=E,C=$r.question(t,n),n.limit=[C,""],n.limitMessage=p,S=$r.question(f,n);return C};function uve(t,e,r){var s;function a(n){return s=r(n),!isNaN(s)&&typeof s=="number"}return $r.question(t,Ks({limitMessage:"Input valid number, please."},e,{limit:a,cd:!1})),s}$r.questionInt=function(t,e){return uve(t,e,function(r){return parseInt(r,10)})};$r.questionFloat=function(t,e){return uve(t,e,parseFloat)};$r.questionPath=function(t,e){var r,s="",a=Ks({hideEchoBack:!1,limitMessage:`$Input valid path, please.$<( Min:)min>$<( Max:)max>`,history:!0,cd:!0},e,{keepWhitespace:!1,limit:function(n){var c,f,p;n=d9(n,!0),s="";function h(E){E.split(/\/|\\/).reduce(function(C,S){var P=K0.resolve(C+=S+K0.sep);if(!ii.existsSync(P))ii.mkdirSync(P);else if(!ii.statSync(P).isDirectory())throw new Error("Non directory already exists: "+P);return C},"")}try{if(c=ii.existsSync(n),r=c?ii.realpathSync(n):K0.resolve(n),!e.hasOwnProperty("exists")&&!c||typeof e.exists=="boolean"&&e.exists!==c)return s=(c?"Already exists":"No such file or directory")+": "+r,!1;if(!c&&e.create&&(e.isDirectory?h(r):(h(K0.dirname(r)),ii.closeSync(ii.openSync(r,"w"))),r=ii.realpathSync(r)),c&&(e.min||e.max||e.isFile||e.isDirectory)){if(f=ii.statSync(r),e.isFile&&!f.isFile())return s="Not file: "+r,!1;if(e.isDirectory&&!f.isDirectory())return s="Not directory: "+r,!1;if(e.min&&f.size<+e.min||e.max&&f.size>+e.max)return s="Size "+f.size+" is out of range: "+r,!1}if(typeof e.validate=="function"&&(p=e.validate(r))!==!0)return typeof p=="string"&&(s=p),!1}catch(E){return s=E+"",!1}return!0},phContent:function(n){return n==="error"?s:n!=="min"&&n!=="max"?null:e.hasOwnProperty(n)?e[n]+"":""}});return e=e||{},t==null&&(t='Input path (you can "cd" and "pwd"): '),$r.question(t,a),r};function fve(t,e){var r={},s={};return typeof t=="object"?(Object.keys(t).forEach(function(a){typeof t[a]=="function"&&(s[e.caseSensitive?a:a.toLowerCase()]=t[a])}),r.preCheck=function(a){var n;return r.args=p9(a),n=r.args[0]||"",e.caseSensitive||(n=n.toLowerCase()),r.hRes=n!=="_"&&s.hasOwnProperty(n)?s[n].apply(a,r.args.slice(1)):s.hasOwnProperty("_")?s._.apply(a,r.args):null,{res:a,forceNext:!1}},s.hasOwnProperty("_")||(r.limit=function(){var a=r.args[0]||"";return e.caseSensitive||(a=a.toLowerCase()),s.hasOwnProperty(a)})):r.preCheck=function(a){return r.args=p9(a),r.hRes=typeof t=="function"?t.apply(a,r.args):!0,{res:a,forceNext:!1}},r}$r.promptCL=function(t,e){var r=Ks({hideEchoBack:!1,limitMessage:"Requested command is not available.",caseSensitive:!1,history:!0},e),s=fve(t,r);return r.limit=s.limit,r.preCheck=s.preCheck,$r.prompt(r),s.args};$r.promptLoop=function(t,e){for(var r=Ks({hideEchoBack:!1,trueValue:null,falseValue:null,caseSensitive:!1,history:!0},e);!t($r.prompt(r)););};$r.promptCLLoop=function(t,e){var r=Ks({hideEchoBack:!1,limitMessage:"Requested command is not available.",caseSensitive:!1,history:!0},e),s=fve(t,r);for(r.limit=s.limit,r.preCheck=s.preCheck;$r.prompt(r),!s.hRes;);};$r.promptSimShell=function(t){return $r.prompt(Ks({hideEchoBack:!1,history:!0},t,{prompt:function(){return Pm?"$>":(process.env.USER||"")+(process.env.HOSTNAME?"@"+process.env.HOSTNAME.replace(/\..*$/,""):"")+":$$ "}()}))};function Ave(t,e,r){var s;return t==null&&(t="Are you sure? "),(!e||e.guide!==!1)&&(t+="")&&(t=t.replace(/\s*:?\s*$/,"")+" [y/n]: "),s=$r.keyIn(t,Ks(e,{hideEchoBack:!1,limit:r,trueValue:"y",falseValue:"n",caseSensitive:!1})),typeof s=="boolean"?s:""}$r.keyInYN=function(t,e){return Ave(t,e)};$r.keyInYNStrict=function(t,e){return Ave(t,e,"yn")};$r.keyInPause=function(t,e){t==null&&(t="Continue..."),(!e||e.guide!==!1)&&(t+="")&&(t=t.replace(/\s+$/,"")+" (Hit any key)"),$r.keyIn(t,Ks({limit:null},e,{hideEchoBack:!0,mask:""}))};$r.keyInSelect=function(t,e,r){var s=Ks({hideEchoBack:!1},r,{trueValue:null,falseValue:null,caseSensitive:!1,phContent:function(p){return p==="itemsCount"?t.length+"":p==="firstItem"?(t[0]+"").trim():p==="lastItem"?(t[t.length-1]+"").trim():null}}),a="",n={},c=49,f=` +`;if(!Array.isArray(t)||!t.length||t.length>35)throw"`items` must be Array (max length: 35).";return t.forEach(function(p,h){var E=String.fromCharCode(c);a+=E,n[E]=h,f+="["+E+"] "+(p+"").trim()+` +`,c=c===57?97:c+1}),(!r||r.cancel!==!1)&&(a+="0",n[0]=-1,f+="[0] "+(r&&r.cancel!=null&&typeof r.cancel!="boolean"?(r.cancel+"").trim():"CANCEL")+` +`),s.limit=a,f+=` +`,e==null&&(e="Choose one from list: "),(e+="")&&((!r||r.guide!==!1)&&(e=e.replace(/\s*:?\s*$/,"")+" [$]: "),f+=e),n[$r.keyIn(f,s).toLowerCase()]};$r.getRawInput=function(){return fF};function wS(t,e){var r;return e.length&&(r={},r[t]=e[0]),$r.setDefaultOptions(r)[t]}$r.setPrint=function(){return wS("print",arguments)};$r.setPrompt=function(){return wS("prompt",arguments)};$r.setEncoding=function(){return wS("encoding",arguments)};$r.setMask=function(){return wS("mask",arguments)};$r.setBufferSize=function(){return wS("bufferSize",arguments)}});var m9=L((vfr,tc)=>{(function(){var t={major:0,minor:2,patch:66,status:"beta"};tau_file_system={files:{},open:function(w,b,y){var F=tau_file_system.files[w];if(!F){if(y==="read")return null;F={path:w,text:"",type:b,get:function(z,Z){return Z===this.text.length||Z>this.text.length?"end_of_file":this.text.substring(Z,Z+z)},put:function(z,Z){return Z==="end_of_file"?(this.text+=z,!0):Z==="past_end_of_file"?null:(this.text=this.text.substring(0,Z)+z+this.text.substring(Z+z.length),!0)},get_byte:function(z){if(z==="end_of_stream")return-1;var Z=Math.floor(z/2);if(this.text.length<=Z)return-1;var $=n(this.text[Math.floor(z/2)],0);return z%2===0?$&255:$/256>>>0},put_byte:function(z,Z){var $=Z==="end_of_stream"?this.text.length:Math.floor(Z/2);if(this.text.length<$)return null;var oe=this.text.length===$?-1:n(this.text[Math.floor(Z/2)],0);return Z%2===0?(oe=oe/256>>>0,oe=(oe&255)<<8|z&255):(oe=oe&255,oe=(z&255)<<8|oe&255),this.text.length===$?this.text+=c(oe):this.text=this.text.substring(0,$)+c(oe)+this.text.substring($+1),!0},flush:function(){return!0},close:function(){var z=tau_file_system.files[this.path];return z?!0:null}},tau_file_system.files[w]=F}return y==="write"&&(F.text=""),F}},tau_user_input={buffer:"",get:function(w,b){for(var y;tau_user_input.buffer.length\?\@\^\~\\]+|'(?:[^']*?(?:\\(?:x?\d+)?\\)*(?:'')*(?:\\')*)*')/,number:/^(?:0o[0-7]+|0x[0-9a-fA-F]+|0b[01]+|0'(?:''|\\[abfnrtv\\'"`]|\\x?\d+\\|[^\\])|\d+(?:\.\d+(?:[eE][+-]?\d+)?)?)/,string:/^(?:"([^"]|""|\\")*"|`([^`]|``|\\`)*`)/,l_brace:/^(?:\[)/,r_brace:/^(?:\])/,l_bracket:/^(?:\{)/,r_bracket:/^(?:\})/,bar:/^(?:\|)/,l_paren:/^(?:\()/,r_paren:/^(?:\))/};function N(w,b){return w.get_flag("char_conversion").id==="on"?b.replace(/./g,function(y){return w.get_char_conversion(y)}):b}function U(w){this.thread=w,this.text="",this.tokens=[]}U.prototype.set_last_tokens=function(w){return this.tokens=w},U.prototype.new_text=function(w){this.text=w,this.tokens=[]},U.prototype.get_tokens=function(w){var b,y=0,F=0,z=0,Z=[],$=!1;if(w){var oe=this.tokens[w-1];y=oe.len,b=N(this.thread,this.text.substr(oe.len)),F=oe.line,z=oe.start}else b=this.text;if(/^\s*$/.test(b))return null;for(;b!=="";){var xe=[],Te=!1;if(/^\n/.exec(b)!==null){F++,z=0,y++,b=b.replace(/\n/,""),$=!0;continue}for(var lt in R)if(R.hasOwnProperty(lt)){var Et=R[lt].exec(b);Et&&xe.push({value:Et[0],name:lt,matches:Et})}if(!xe.length)return this.set_last_tokens([{value:b,matches:[],name:"lexical",line:F,start:z}]);var oe=r(xe,function(Pr,Ir){return Pr.value.length>=Ir.value.length?Pr:Ir});switch(oe.start=z,oe.line=F,b=b.replace(oe.value,""),z+=oe.value.length,y+=oe.value.length,oe.name){case"atom":oe.raw=oe.value,oe.value.charAt(0)==="'"&&(oe.value=S(oe.value.substr(1,oe.value.length-2),"'"),oe.value===null&&(oe.name="lexical",oe.value="unknown escape sequence"));break;case"number":oe.float=oe.value.substring(0,2)!=="0x"&&oe.value.match(/[.eE]/)!==null&&oe.value!=="0'.",oe.value=I(oe.value),oe.blank=Te;break;case"string":var qt=oe.value.charAt(0);oe.value=S(oe.value.substr(1,oe.value.length-2),qt),oe.value===null&&(oe.name="lexical",oe.value="unknown escape sequence");break;case"whitespace":var ir=Z[Z.length-1];ir&&(ir.space=!0),Te=!0;continue;case"r_bracket":Z.length>0&&Z[Z.length-1].name==="l_bracket"&&(oe=Z.pop(),oe.name="atom",oe.value="{}",oe.raw="{}",oe.space=!1);break;case"r_brace":Z.length>0&&Z[Z.length-1].name==="l_brace"&&(oe=Z.pop(),oe.name="atom",oe.value="[]",oe.raw="[]",oe.space=!1);break}oe.len=y,Z.push(oe),Te=!1}var Pt=this.set_last_tokens(Z);return Pt.length===0?null:Pt};function W(w,b,y,F,z){if(!b[y])return{type:f,value:x.error.syntax(b[y-1],"expression expected",!0)};var Z;if(F==="0"){var $=b[y];switch($.name){case"number":return{type:p,len:y+1,value:new x.type.Num($.value,$.float)};case"variable":return{type:p,len:y+1,value:new x.type.Var($.value)};case"string":var oe;switch(w.get_flag("double_quotes").id){case"atom":oe=new j($.value,[]);break;case"codes":oe=new j("[]",[]);for(var xe=$.value.length-1;xe>=0;xe--)oe=new j(".",[new x.type.Num(n($.value,xe),!1),oe]);break;case"chars":oe=new j("[]",[]);for(var xe=$.value.length-1;xe>=0;xe--)oe=new j(".",[new x.type.Term($.value.charAt(xe),[]),oe]);break}return{type:p,len:y+1,value:oe};case"l_paren":var Pt=W(w,b,y+1,w.__get_max_priority(),!0);return Pt.type!==p?Pt:b[Pt.len]&&b[Pt.len].name==="r_paren"?(Pt.len++,Pt):{type:f,derived:!0,value:x.error.syntax(b[Pt.len]?b[Pt.len]:b[Pt.len-1],") or operator expected",!b[Pt.len])};case"l_bracket":var Pt=W(w,b,y+1,w.__get_max_priority(),!0);return Pt.type!==p?Pt:b[Pt.len]&&b[Pt.len].name==="r_bracket"?(Pt.len++,Pt.value=new j("{}",[Pt.value]),Pt):{type:f,derived:!0,value:x.error.syntax(b[Pt.len]?b[Pt.len]:b[Pt.len-1],"} or operator expected",!b[Pt.len])}}var Te=te(w,b,y,z);return Te.type===p||Te.derived||(Te=ie(w,b,y),Te.type===p||Te.derived)?Te:{type:f,derived:!1,value:x.error.syntax(b[y],"unexpected token")}}var lt=w.__get_max_priority(),Et=w.__get_next_priority(F),qt=y;if(b[y].name==="atom"&&b[y+1]&&(b[y].space||b[y+1].name!=="l_paren")){var $=b[y++],ir=w.__lookup_operator_classes(F,$.value);if(ir&&ir.indexOf("fy")>-1){var Pt=W(w,b,y,F,z);if(Pt.type!==f)return $.value==="-"&&!$.space&&x.type.is_number(Pt.value)?{value:new x.type.Num(-Pt.value.value,Pt.value.is_float),len:Pt.len,type:p}:{value:new x.type.Term($.value,[Pt.value]),len:Pt.len,type:p};Z=Pt}else if(ir&&ir.indexOf("fx")>-1){var Pt=W(w,b,y,Et,z);if(Pt.type!==f)return{value:new x.type.Term($.value,[Pt.value]),len:Pt.len,type:p};Z=Pt}}y=qt;var Pt=W(w,b,y,Et,z);if(Pt.type===p){y=Pt.len;var $=b[y];if(b[y]&&(b[y].name==="atom"&&w.__lookup_operator_classes(F,$.value)||b[y].name==="bar"&&w.__lookup_operator_classes(F,"|"))){var gn=Et,Pr=F,ir=w.__lookup_operator_classes(F,$.value);if(ir.indexOf("xf")>-1)return{value:new x.type.Term($.value,[Pt.value]),len:++Pt.len,type:p};if(ir.indexOf("xfx")>-1){var Ir=W(w,b,y+1,gn,z);return Ir.type===p?{value:new x.type.Term($.value,[Pt.value,Ir.value]),len:Ir.len,type:p}:(Ir.derived=!0,Ir)}else if(ir.indexOf("xfy")>-1){var Ir=W(w,b,y+1,Pr,z);return Ir.type===p?{value:new x.type.Term($.value,[Pt.value,Ir.value]),len:Ir.len,type:p}:(Ir.derived=!0,Ir)}else if(Pt.type!==f)for(;;){y=Pt.len;var $=b[y];if($&&$.name==="atom"&&w.__lookup_operator_classes(F,$.value)){var ir=w.__lookup_operator_classes(F,$.value);if(ir.indexOf("yf")>-1)Pt={value:new x.type.Term($.value,[Pt.value]),len:++y,type:p};else if(ir.indexOf("yfx")>-1){var Ir=W(w,b,++y,gn,z);if(Ir.type===f)return Ir.derived=!0,Ir;y=Ir.len,Pt={value:new x.type.Term($.value,[Pt.value,Ir.value]),len:y,type:p}}else break}else break}}else Z={type:f,value:x.error.syntax(b[Pt.len-1],"operator expected")};return Pt}return Pt}function te(w,b,y,F){if(!b[y]||b[y].name==="atom"&&b[y].raw==="."&&!F&&(b[y].space||!b[y+1]||b[y+1].name!=="l_paren"))return{type:f,derived:!1,value:x.error.syntax(b[y-1],"unfounded token")};var z=b[y],Z=[];if(b[y].name==="atom"&&b[y].raw!==","){if(y++,b[y-1].space)return{type:p,len:y,value:new x.type.Term(z.value,Z)};if(b[y]&&b[y].name==="l_paren"){if(b[y+1]&&b[y+1].name==="r_paren")return{type:f,derived:!0,value:x.error.syntax(b[y+1],"argument expected")};var $=W(w,b,++y,"999",!0);if($.type===f)return $.derived?$:{type:f,derived:!0,value:x.error.syntax(b[y]?b[y]:b[y-1],"argument expected",!b[y])};for(Z.push($.value),y=$.len;b[y]&&b[y].name==="atom"&&b[y].value===",";){if($=W(w,b,y+1,"999",!0),$.type===f)return $.derived?$:{type:f,derived:!0,value:x.error.syntax(b[y+1]?b[y+1]:b[y],"argument expected",!b[y+1])};Z.push($.value),y=$.len}if(b[y]&&b[y].name==="r_paren")y++;else return{type:f,derived:!0,value:x.error.syntax(b[y]?b[y]:b[y-1],", or ) expected",!b[y])}}return{type:p,len:y,value:new x.type.Term(z.value,Z)}}return{type:f,derived:!1,value:x.error.syntax(b[y],"term expected")}}function ie(w,b,y){if(!b[y])return{type:f,derived:!1,value:x.error.syntax(b[y-1],"[ expected")};if(b[y]&&b[y].name==="l_brace"){var F=W(w,b,++y,"999",!0),z=[F.value],Z=void 0;if(F.type===f)return b[y]&&b[y].name==="r_brace"?{type:p,len:y+1,value:new x.type.Term("[]",[])}:{type:f,derived:!0,value:x.error.syntax(b[y],"] expected")};for(y=F.len;b[y]&&b[y].name==="atom"&&b[y].value===",";){if(F=W(w,b,y+1,"999",!0),F.type===f)return F.derived?F:{type:f,derived:!0,value:x.error.syntax(b[y+1]?b[y+1]:b[y],"argument expected",!b[y+1])};z.push(F.value),y=F.len}var $=!1;if(b[y]&&b[y].name==="bar"){if($=!0,F=W(w,b,y+1,"999",!0),F.type===f)return F.derived?F:{type:f,derived:!0,value:x.error.syntax(b[y+1]?b[y+1]:b[y],"argument expected",!b[y+1])};Z=F.value,y=F.len}return b[y]&&b[y].name==="r_brace"?{type:p,len:y+1,value:g(z,Z)}:{type:f,derived:!0,value:x.error.syntax(b[y]?b[y]:b[y-1],$?"] expected":", or | or ] expected",!b[y])}}return{type:f,derived:!1,value:x.error.syntax(b[y],"list expected")}}function Ae(w,b,y){var F=b[y].line,z=W(w,b,y,w.__get_max_priority(),!1),Z=null,$;if(z.type!==f)if(y=z.len,b[y]&&b[y].name==="atom"&&b[y].raw===".")if(y++,x.type.is_term(z.value)){if(z.value.indicator===":-/2"?(Z=new x.type.Rule(z.value.args[0],Ce(z.value.args[1])),$={value:Z,len:y,type:p}):z.value.indicator==="-->/2"?(Z=pe(new x.type.Rule(z.value.args[0],z.value.args[1]),w),Z.body=Ce(Z.body),$={value:Z,len:y,type:x.type.is_rule(Z)?p:f}):(Z=new x.type.Rule(z.value,null),$={value:Z,len:y,type:p}),Z){var oe=Z.singleton_variables();oe.length>0&&w.throw_warning(x.warning.singleton(oe,Z.head.indicator,F))}return $}else return{type:f,value:x.error.syntax(b[y],"callable expected")};else return{type:f,value:x.error.syntax(b[y]?b[y]:b[y-1],". or operator expected")};return z}function ce(w,b,y){y=y||{},y.from=y.from?y.from:"$tau-js",y.reconsult=y.reconsult!==void 0?y.reconsult:!0;var F=new U(w),z={},Z;F.new_text(b);var $=0,oe=F.get_tokens($);do{if(oe===null||!oe[$])break;var xe=Ae(w,oe,$);if(xe.type===f)return new j("throw",[xe.value]);if(xe.value.body===null&&xe.value.head.indicator==="?-/1"){var Te=new it(w.session);Te.add_goal(xe.value.head.args[0]),Te.answer(function(Et){x.type.is_error(Et)?w.throw_warning(Et.args[0]):(Et===!1||Et===null)&&w.throw_warning(x.warning.failed_goal(xe.value.head.args[0],xe.len))}),$=xe.len;var lt=!0}else if(xe.value.body===null&&xe.value.head.indicator===":-/1"){var lt=w.run_directive(xe.value.head.args[0]);$=xe.len,xe.value.head.args[0].indicator==="char_conversion/2"&&(oe=F.get_tokens($),$=0)}else{Z=xe.value.head.indicator,y.reconsult!==!1&&z[Z]!==!0&&!w.is_multifile_predicate(Z)&&(w.session.rules[Z]=a(w.session.rules[Z]||[],function(qt){return qt.dynamic}),z[Z]=!0);var lt=w.add_rule(xe.value,y);$=xe.len}if(!lt)return lt}while(!0);return!0}function me(w,b){var y=new U(w);y.new_text(b);var F=0;do{var z=y.get_tokens(F);if(z===null)break;var Z=W(w,z,0,w.__get_max_priority(),!1);if(Z.type!==f){var $=Z.len,oe=$;if(z[$]&&z[$].name==="atom"&&z[$].raw===".")w.add_goal(Ce(Z.value));else{var xe=z[$];return new j("throw",[x.error.syntax(xe||z[$-1],". or operator expected",!xe)])}F=Z.len+1}else return new j("throw",[Z.value])}while(!0);return!0}function pe(w,b){w=w.rename(b);var y=b.next_free_variable(),F=Be(w.body,y,b);return F.error?F.value:(w.body=F.value,w.head.args=w.head.args.concat([y,F.variable]),w.head=new j(w.head.id,w.head.args),w)}function Be(w,b,y){var F;if(x.type.is_term(w)&&w.indicator==="!/0")return{value:w,variable:b,error:!1};if(x.type.is_term(w)&&w.indicator===",/2"){var z=Be(w.args[0],b,y);if(z.error)return z;var Z=Be(w.args[1],z.variable,y);return Z.error?Z:{value:new j(",",[z.value,Z.value]),variable:Z.variable,error:!1}}else{if(x.type.is_term(w)&&w.indicator==="{}/1")return{value:w.args[0],variable:b,error:!1};if(x.type.is_empty_list(w))return{value:new j("true",[]),variable:b,error:!1};if(x.type.is_list(w)){F=y.next_free_variable();for(var $=w,oe;$.indicator==="./2";)oe=$,$=$.args[1];return x.type.is_variable($)?{value:x.error.instantiation("DCG"),variable:b,error:!0}:x.type.is_empty_list($)?(oe.args[1]=F,{value:new j("=",[b,w]),variable:F,error:!1}):{value:x.error.type("list",w,"DCG"),variable:b,error:!0}}else return x.type.is_callable(w)?(F=y.next_free_variable(),w.args=w.args.concat([b,F]),w=new j(w.id,w.args),{value:w,variable:F,error:!1}):{value:x.error.type("callable",w,"DCG"),variable:b,error:!0}}}function Ce(w){return x.type.is_variable(w)?new j("call",[w]):x.type.is_term(w)&&[",/2",";/2","->/2"].indexOf(w.indicator)!==-1?new j(w.id,[Ce(w.args[0]),Ce(w.args[1])]):w}function g(w,b){for(var y=b||new x.type.Term("[]",[]),F=w.length-1;F>=0;F--)y=new x.type.Term(".",[w[F],y]);return y}function we(w,b){for(var y=w.length-1;y>=0;y--)w[y]===b&&w.splice(y,1)}function Ee(w){for(var b={},y=[],F=0;F=0;b--)if(w.charAt(b)==="/")return new j("/",[new j(w.substring(0,b)),new Re(parseInt(w.substring(b+1)),!1)])}function De(w){this.id=w}function Re(w,b){this.is_float=b!==void 0?b:parseInt(w)!==w,this.value=this.is_float?w:parseInt(w)}var gt=0;function j(w,b,y){this.ref=y||++gt,this.id=w,this.args=b||[],this.indicator=w+"/"+this.args.length}var rt=0;function Fe(w,b,y,F,z,Z){this.id=rt++,this.stream=w,this.mode=b,this.alias=y,this.type=F!==void 0?F:"text",this.reposition=z!==void 0?z:!0,this.eof_action=Z!==void 0?Z:"eof_code",this.position=this.mode==="append"?"end_of_stream":0,this.output=this.mode==="write"||this.mode==="append",this.input=this.mode==="read"}function Ne(w){w=w||{},this.links=w}function Pe(w,b,y){b=b||new Ne,y=y||null,this.goal=w,this.substitution=b,this.parent=y}function Ye(w,b,y){this.head=w,this.body=b,this.dynamic=y||!1}function ke(w){w=w===void 0||w<=0?1e3:w,this.rules={},this.src_predicates={},this.rename=0,this.modules=[],this.thread=new it(this),this.total_threads=1,this.renamed_variables={},this.public_predicates={},this.multifile_predicates={},this.limit=w,this.streams={user_input:new Fe(typeof tc<"u"&&tc.exports?nodejs_user_input:tau_user_input,"read","user_input","text",!1,"reset"),user_output:new Fe(typeof tc<"u"&&tc.exports?nodejs_user_output:tau_user_output,"write","user_output","text",!1,"eof_code")},this.file_system=typeof tc<"u"&&tc.exports?nodejs_file_system:tau_file_system,this.standard_input=this.streams.user_input,this.standard_output=this.streams.user_output,this.current_input=this.streams.user_input,this.current_output=this.streams.user_output,this.format_success=function(b){return b.substitution},this.format_error=function(b){return b.goal},this.flag={bounded:x.flag.bounded.value,max_integer:x.flag.max_integer.value,min_integer:x.flag.min_integer.value,integer_rounding_function:x.flag.integer_rounding_function.value,char_conversion:x.flag.char_conversion.value,debug:x.flag.debug.value,max_arity:x.flag.max_arity.value,unknown:x.flag.unknown.value,double_quotes:x.flag.double_quotes.value,occurs_check:x.flag.occurs_check.value,dialect:x.flag.dialect.value,version_data:x.flag.version_data.value,nodejs:x.flag.nodejs.value},this.__loaded_modules=[],this.__char_conversion={},this.__operators={1200:{":-":["fx","xfx"],"-->":["xfx"],"?-":["fx"]},1100:{";":["xfy"]},1050:{"->":["xfy"]},1e3:{",":["xfy"]},900:{"\\+":["fy"]},700:{"=":["xfx"],"\\=":["xfx"],"==":["xfx"],"\\==":["xfx"],"@<":["xfx"],"@=<":["xfx"],"@>":["xfx"],"@>=":["xfx"],"=..":["xfx"],is:["xfx"],"=:=":["xfx"],"=\\=":["xfx"],"<":["xfx"],"=<":["xfx"],">":["xfx"],">=":["xfx"]},600:{":":["xfy"]},500:{"+":["yfx"],"-":["yfx"],"/\\":["yfx"],"\\/":["yfx"]},400:{"*":["yfx"],"/":["yfx"],"//":["yfx"],rem:["yfx"],mod:["yfx"],"<<":["yfx"],">>":["yfx"]},200:{"**":["xfx"],"^":["xfy"],"-":["fy"],"+":["fy"],"\\":["fy"]}}}function it(w){this.epoch=Date.now(),this.session=w,this.session.total_threads++,this.total_steps=0,this.cpu_time=0,this.cpu_time_last=0,this.points=[],this.debugger=!1,this.debugger_states=[],this.level="top_level/0",this.__calls=[],this.current_limit=this.session.limit,this.warnings=[]}function _e(w,b,y){this.id=w,this.rules=b,this.exports=y,x.module[w]=this}_e.prototype.exports_predicate=function(w){return this.exports.indexOf(w)!==-1},De.prototype.unify=function(w,b){if(b&&e(w.variables(),this.id)!==-1&&!x.type.is_variable(w))return null;var y={};return y[this.id]=w,new Ne(y)},Re.prototype.unify=function(w,b){return x.type.is_number(w)&&this.value===w.value&&this.is_float===w.is_float?new Ne:null},j.prototype.unify=function(w,b){if(x.type.is_term(w)&&this.indicator===w.indicator){for(var y=new Ne,F=0;F=0){var F=this.args[0].value,z=Math.floor(F/26),Z=F%26;return"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[Z]+(z!==0?z:"")}switch(this.indicator){case"[]/0":case"{}/0":case"!/0":return this.id;case"{}/1":return"{"+this.args[0].toString(w)+"}";case"./2":for(var $="["+this.args[0].toString(w),oe=this.args[1];oe.indicator==="./2";)$+=", "+oe.args[0].toString(w),oe=oe.args[1];return oe.indicator!=="[]/0"&&($+="|"+oe.toString(w)),$+="]",$;case",/2":return"("+this.args[0].toString(w)+", "+this.args[1].toString(w)+")";default:var xe=this.id,Te=w.session?w.session.lookup_operator(this.id,this.args.length):null;if(w.session===void 0||w.ignore_ops||Te===null)return w.quoted&&!/^(!|,|;|[a-z][0-9a-zA-Z_]*)$/.test(xe)&&xe!=="{}"&&xe!=="[]"&&(xe="'"+P(xe)+"'"),xe+(this.args.length?"("+s(this.args,function(ir){return ir.toString(w)}).join(", ")+")":"");var lt=Te.priority>b.priority||Te.priority===b.priority&&(Te.class==="xfy"&&this.indicator!==b.indicator||Te.class==="yfx"&&this.indicator!==b.indicator||this.indicator===b.indicator&&Te.class==="yfx"&&y==="right"||this.indicator===b.indicator&&Te.class==="xfy"&&y==="left");Te.indicator=this.indicator;var Et=lt?"(":"",qt=lt?")":"";return this.args.length===0?"("+this.id+")":["fy","fx"].indexOf(Te.class)!==-1?Et+xe+" "+this.args[0].toString(w,Te)+qt:["yf","xf"].indexOf(Te.class)!==-1?Et+this.args[0].toString(w,Te)+" "+xe+qt:Et+this.args[0].toString(w,Te,"left")+" "+this.id+" "+this.args[1].toString(w,Te,"right")+qt}},Fe.prototype.toString=function(w){return"("+this.id+")"},Ne.prototype.toString=function(w){var b="{";for(var y in this.links)this.links.hasOwnProperty(y)&&(b!=="{"&&(b+=", "),b+=y+"/"+this.links[y].toString(w));return b+="}",b},Pe.prototype.toString=function(w){return this.goal===null?"<"+this.substitution.toString(w)+">":"<"+this.goal.toString(w)+", "+this.substitution.toString(w)+">"},Ye.prototype.toString=function(w){return this.body?this.head.toString(w)+" :- "+this.body.toString(w)+".":this.head.toString(w)+"."},ke.prototype.toString=function(w){for(var b="",y=0;y=0;z--)F=new j(".",[b[z],F]);return F}return new j(this.id,s(this.args,function(Z){return Z.apply(w)}),this.ref)},Fe.prototype.apply=function(w){return this},Ye.prototype.apply=function(w){return new Ye(this.head.apply(w),this.body!==null?this.body.apply(w):null)},Ne.prototype.apply=function(w){var b,y={};for(b in this.links)this.links.hasOwnProperty(b)&&(y[b]=this.links[b].apply(w));return new Ne(y)},j.prototype.select=function(){for(var w=this;w.indicator===",/2";)w=w.args[0];return w},j.prototype.replace=function(w){return this.indicator===",/2"?this.args[0].indicator===",/2"?new j(",",[this.args[0].replace(w),this.args[1]]):w===null?this.args[1]:new j(",",[w,this.args[1]]):w},j.prototype.search=function(w){if(x.type.is_term(w)&&w.ref!==void 0&&this.ref===w.ref)return!0;for(var b=0;bb&&F0&&(b=this.head_point().substitution.domain());e(b,x.format_variable(this.session.rename))!==-1;)this.session.rename++;if(w.id==="_")return new De(x.format_variable(this.session.rename));this.session.renamed_variables[w.id]=x.format_variable(this.session.rename)}return new De(this.session.renamed_variables[w.id])},ke.prototype.next_free_variable=function(){return this.thread.next_free_variable()},it.prototype.next_free_variable=function(){this.session.rename++;var w=[];for(this.points.length>0&&(w=this.head_point().substitution.domain());e(w,x.format_variable(this.session.rename))!==-1;)this.session.rename++;return new De(x.format_variable(this.session.rename))},ke.prototype.is_public_predicate=function(w){return!this.public_predicates.hasOwnProperty(w)||this.public_predicates[w]===!0},it.prototype.is_public_predicate=function(w){return this.session.is_public_predicate(w)},ke.prototype.is_multifile_predicate=function(w){return this.multifile_predicates.hasOwnProperty(w)&&this.multifile_predicates[w]===!0},it.prototype.is_multifile_predicate=function(w){return this.session.is_multifile_predicate(w)},ke.prototype.prepend=function(w){return this.thread.prepend(w)},it.prototype.prepend=function(w){for(var b=w.length-1;b>=0;b--)this.points.push(w[b])},ke.prototype.success=function(w,b){return this.thread.success(w,b)},it.prototype.success=function(w,y){var y=typeof y>"u"?w:y;this.prepend([new Pe(w.goal.replace(null),w.substitution,y)])},ke.prototype.throw_error=function(w){return this.thread.throw_error(w)},it.prototype.throw_error=function(w){this.prepend([new Pe(new j("throw",[w]),new Ne,null,null)])},ke.prototype.step_rule=function(w,b){return this.thread.step_rule(w,b)},it.prototype.step_rule=function(w,b){var y=b.indicator;if(w==="user"&&(w=null),w===null&&this.session.rules.hasOwnProperty(y))return this.session.rules[y];for(var F=w===null?this.session.modules:e(this.session.modules,w)===-1?[]:[w],z=0;z1)&&this.again()},ke.prototype.answers=function(w,b,y){return this.thread.answers(w,b,y)},it.prototype.answers=function(w,b,y){var F=b||1e3,z=this;if(b<=0){y&&y();return}this.answer(function(Z){w(Z),Z!==!1?setTimeout(function(){z.answers(w,b-1,y)},1):y&&y()})},ke.prototype.again=function(w){return this.thread.again(w)},it.prototype.again=function(w){for(var b,y=Date.now();this.__calls.length>0;){for(this.warnings=[],w!==!1&&(this.current_limit=this.session.limit);this.current_limit>0&&this.points.length>0&&this.head_point().goal!==null&&!x.type.is_error(this.head_point().goal);)if(this.current_limit--,this.step()===!0)return;var F=Date.now();this.cpu_time_last=F-y,this.cpu_time+=this.cpu_time_last;var z=this.__calls.shift();this.current_limit<=0?z(null):this.points.length===0?z(!1):x.type.is_error(this.head_point().goal)?(b=this.session.format_error(this.points.pop()),this.points=[],z(b)):(this.debugger&&this.debugger_states.push(this.head_point()),b=this.session.format_success(this.points.pop()),z(b))}},ke.prototype.unfold=function(w){if(w.body===null)return!1;var b=w.head,y=w.body,F=y.select(),z=new it(this),Z=[];z.add_goal(F),z.step();for(var $=z.points.length-1;$>=0;$--){var oe=z.points[$],xe=b.apply(oe.substitution),Te=y.replace(oe.goal);Te!==null&&(Te=Te.apply(oe.substitution)),Z.push(new Ye(xe,Te))}var lt=this.rules[b.indicator],Et=e(lt,w);return Z.length>0&&Et!==-1?(lt.splice.apply(lt,[Et,1].concat(Z)),!0):!1},it.prototype.unfold=function(w){return this.session.unfold(w)},De.prototype.interpret=function(w){return x.error.instantiation(w.level)},Re.prototype.interpret=function(w){return this},j.prototype.interpret=function(w){return x.type.is_unitary_list(this)?this.args[0].interpret(w):x.operate(w,this)},De.prototype.compare=function(w){return this.idw.id?1:0},Re.prototype.compare=function(w){if(this.value===w.value&&this.is_float===w.is_float)return 0;if(this.valuew.value)return 1},j.prototype.compare=function(w){if(this.args.lengthw.args.length||this.args.length===w.args.length&&this.id>w.id)return 1;for(var b=0;bF)return 1;if(w.constructor===Re){if(w.is_float&&b.is_float)return 0;if(w.is_float)return-1;if(b.is_float)return 1}return 0},is_substitution:function(w){return w instanceof Ne},is_state:function(w){return w instanceof Pe},is_rule:function(w){return w instanceof Ye},is_variable:function(w){return w instanceof De},is_stream:function(w){return w instanceof Fe},is_anonymous_var:function(w){return w instanceof De&&w.id==="_"},is_callable:function(w){return w instanceof j},is_number:function(w){return w instanceof Re},is_integer:function(w){return w instanceof Re&&!w.is_float},is_float:function(w){return w instanceof Re&&w.is_float},is_term:function(w){return w instanceof j},is_atom:function(w){return w instanceof j&&w.args.length===0},is_ground:function(w){if(w instanceof De)return!1;if(w instanceof j){for(var b=0;b0},is_list:function(w){return w instanceof j&&(w.indicator==="[]/0"||w.indicator==="./2")},is_empty_list:function(w){return w instanceof j&&w.indicator==="[]/0"},is_non_empty_list:function(w){return w instanceof j&&w.indicator==="./2"},is_fully_list:function(w){for(;w instanceof j&&w.indicator==="./2";)w=w.args[1];return w instanceof De||w instanceof j&&w.indicator==="[]/0"},is_instantiated_list:function(w){for(;w instanceof j&&w.indicator==="./2";)w=w.args[1];return w instanceof j&&w.indicator==="[]/0"},is_unitary_list:function(w){return w instanceof j&&w.indicator==="./2"&&w.args[1]instanceof j&&w.args[1].indicator==="[]/0"},is_character:function(w){return w instanceof j&&(w.id.length===1||w.id.length>0&&w.id.length<=2&&n(w.id,0)>=65536)},is_character_code:function(w){return w instanceof Re&&!w.is_float&&w.value>=0&&w.value<=1114111},is_byte:function(w){return w instanceof Re&&!w.is_float&&w.value>=0&&w.value<=255},is_operator:function(w){return w instanceof j&&x.arithmetic.evaluation[w.indicator]},is_directive:function(w){return w instanceof j&&x.directive[w.indicator]!==void 0},is_builtin:function(w){return w instanceof j&&x.predicate[w.indicator]!==void 0},is_error:function(w){return w instanceof j&&w.indicator==="throw/1"},is_predicate_indicator:function(w){return w instanceof j&&w.indicator==="//2"&&w.args[0]instanceof j&&w.args[0].args.length===0&&w.args[1]instanceof Re&&w.args[1].is_float===!1},is_flag:function(w){return w instanceof j&&w.args.length===0&&x.flag[w.id]!==void 0},is_value_flag:function(w,b){if(!x.type.is_flag(w))return!1;for(var y in x.flag[w.id].allowed)if(x.flag[w.id].allowed.hasOwnProperty(y)&&x.flag[w.id].allowed[y].equals(b))return!0;return!1},is_io_mode:function(w){return x.type.is_atom(w)&&["read","write","append"].indexOf(w.id)!==-1},is_stream_option:function(w){return x.type.is_term(w)&&(w.indicator==="alias/1"&&x.type.is_atom(w.args[0])||w.indicator==="reposition/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="true"||w.args[0].id==="false")||w.indicator==="type/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="text"||w.args[0].id==="binary")||w.indicator==="eof_action/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="error"||w.args[0].id==="eof_code"||w.args[0].id==="reset"))},is_stream_position:function(w){return x.type.is_integer(w)&&w.value>=0||x.type.is_atom(w)&&(w.id==="end_of_stream"||w.id==="past_end_of_stream")},is_stream_property:function(w){return x.type.is_term(w)&&(w.indicator==="input/0"||w.indicator==="output/0"||w.indicator==="alias/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0]))||w.indicator==="file_name/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0]))||w.indicator==="position/1"&&(x.type.is_variable(w.args[0])||x.type.is_stream_position(w.args[0]))||w.indicator==="reposition/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0])&&(w.args[0].id==="true"||w.args[0].id==="false"))||w.indicator==="type/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0])&&(w.args[0].id==="text"||w.args[0].id==="binary"))||w.indicator==="mode/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0])&&(w.args[0].id==="read"||w.args[0].id==="write"||w.args[0].id==="append"))||w.indicator==="eof_action/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0])&&(w.args[0].id==="error"||w.args[0].id==="eof_code"||w.args[0].id==="reset"))||w.indicator==="end_of_stream/1"&&(x.type.is_variable(w.args[0])||x.type.is_atom(w.args[0])&&(w.args[0].id==="at"||w.args[0].id==="past"||w.args[0].id==="not")))},is_streamable:function(w){return w.__proto__.stream!==void 0},is_read_option:function(w){return x.type.is_term(w)&&["variables/1","variable_names/1","singletons/1"].indexOf(w.indicator)!==-1},is_write_option:function(w){return x.type.is_term(w)&&(w.indicator==="quoted/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="true"||w.args[0].id==="false")||w.indicator==="ignore_ops/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="true"||w.args[0].id==="false")||w.indicator==="numbervars/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="true"||w.args[0].id==="false"))},is_close_option:function(w){return x.type.is_term(w)&&w.indicator==="force/1"&&x.type.is_atom(w.args[0])&&(w.args[0].id==="true"||w.args[0].id==="false")},is_modifiable_flag:function(w){return x.type.is_flag(w)&&x.flag[w.id].changeable},is_module:function(w){return w instanceof j&&w.indicator==="library/1"&&w.args[0]instanceof j&&w.args[0].args.length===0&&x.module[w.args[0].id]!==void 0}},arithmetic:{evaluation:{"e/0":{type_args:null,type_result:!0,fn:function(w){return Math.E}},"pi/0":{type_args:null,type_result:!0,fn:function(w){return Math.PI}},"tau/0":{type_args:null,type_result:!0,fn:function(w){return 2*Math.PI}},"epsilon/0":{type_args:null,type_result:!0,fn:function(w){return Number.EPSILON}},"+/1":{type_args:null,type_result:null,fn:function(w,b){return w}},"-/1":{type_args:null,type_result:null,fn:function(w,b){return-w}},"\\/1":{type_args:!1,type_result:!1,fn:function(w,b){return~w}},"abs/1":{type_args:null,type_result:null,fn:function(w,b){return Math.abs(w)}},"sign/1":{type_args:null,type_result:null,fn:function(w,b){return Math.sign(w)}},"float_integer_part/1":{type_args:!0,type_result:!1,fn:function(w,b){return parseInt(w)}},"float_fractional_part/1":{type_args:!0,type_result:!0,fn:function(w,b){return w-parseInt(w)}},"float/1":{type_args:null,type_result:!0,fn:function(w,b){return parseFloat(w)}},"floor/1":{type_args:!0,type_result:!1,fn:function(w,b){return Math.floor(w)}},"truncate/1":{type_args:!0,type_result:!1,fn:function(w,b){return parseInt(w)}},"round/1":{type_args:!0,type_result:!1,fn:function(w,b){return Math.round(w)}},"ceiling/1":{type_args:!0,type_result:!1,fn:function(w,b){return Math.ceil(w)}},"sin/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.sin(w)}},"cos/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.cos(w)}},"tan/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.tan(w)}},"asin/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.asin(w)}},"acos/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.acos(w)}},"atan/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.atan(w)}},"atan2/2":{type_args:null,type_result:!0,fn:function(w,b,y){return Math.atan2(w,b)}},"exp/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.exp(w)}},"sqrt/1":{type_args:null,type_result:!0,fn:function(w,b){return Math.sqrt(w)}},"log/1":{type_args:null,type_result:!0,fn:function(w,b){return w>0?Math.log(w):x.error.evaluation("undefined",b.__call_indicator)}},"+/2":{type_args:null,type_result:null,fn:function(w,b,y){return w+b}},"-/2":{type_args:null,type_result:null,fn:function(w,b,y){return w-b}},"*/2":{type_args:null,type_result:null,fn:function(w,b,y){return w*b}},"//2":{type_args:null,type_result:!0,fn:function(w,b,y){return b?w/b:x.error.evaluation("zero_division",y.__call_indicator)}},"///2":{type_args:!1,type_result:!1,fn:function(w,b,y){return b?parseInt(w/b):x.error.evaluation("zero_division",y.__call_indicator)}},"**/2":{type_args:null,type_result:!0,fn:function(w,b,y){return Math.pow(w,b)}},"^/2":{type_args:null,type_result:null,fn:function(w,b,y){return Math.pow(w,b)}},"<>/2":{type_args:!1,type_result:!1,fn:function(w,b,y){return w>>b}},"/\\/2":{type_args:!1,type_result:!1,fn:function(w,b,y){return w&b}},"\\//2":{type_args:!1,type_result:!1,fn:function(w,b,y){return w|b}},"xor/2":{type_args:!1,type_result:!1,fn:function(w,b,y){return w^b}},"rem/2":{type_args:!1,type_result:!1,fn:function(w,b,y){return b?w%b:x.error.evaluation("zero_division",y.__call_indicator)}},"mod/2":{type_args:!1,type_result:!1,fn:function(w,b,y){return b?w-parseInt(w/b)*b:x.error.evaluation("zero_division",y.__call_indicator)}},"max/2":{type_args:null,type_result:null,fn:function(w,b,y){return Math.max(w,b)}},"min/2":{type_args:null,type_result:null,fn:function(w,b,y){return Math.min(w,b)}}}},directive:{"dynamic/1":function(w,b){var y=b.args[0];if(x.type.is_variable(y))w.throw_error(x.error.instantiation(b.indicator));else if(!x.type.is_compound(y)||y.indicator!=="//2")w.throw_error(x.error.type("predicate_indicator",y,b.indicator));else if(x.type.is_variable(y.args[0])||x.type.is_variable(y.args[1]))w.throw_error(x.error.instantiation(b.indicator));else if(!x.type.is_atom(y.args[0]))w.throw_error(x.error.type("atom",y.args[0],b.indicator));else if(!x.type.is_integer(y.args[1]))w.throw_error(x.error.type("integer",y.args[1],b.indicator));else{var F=b.args[0].args[0].id+"/"+b.args[0].args[1].value;w.session.public_predicates[F]=!0,w.session.rules[F]||(w.session.rules[F]=[])}},"multifile/1":function(w,b){var y=b.args[0];x.type.is_variable(y)?w.throw_error(x.error.instantiation(b.indicator)):!x.type.is_compound(y)||y.indicator!=="//2"?w.throw_error(x.error.type("predicate_indicator",y,b.indicator)):x.type.is_variable(y.args[0])||x.type.is_variable(y.args[1])?w.throw_error(x.error.instantiation(b.indicator)):x.type.is_atom(y.args[0])?x.type.is_integer(y.args[1])?w.session.multifile_predicates[b.args[0].args[0].id+"/"+b.args[0].args[1].value]=!0:w.throw_error(x.error.type("integer",y.args[1],b.indicator)):w.throw_error(x.error.type("atom",y.args[0],b.indicator))},"set_prolog_flag/2":function(w,b){var y=b.args[0],F=b.args[1];x.type.is_variable(y)||x.type.is_variable(F)?w.throw_error(x.error.instantiation(b.indicator)):x.type.is_atom(y)?x.type.is_flag(y)?x.type.is_value_flag(y,F)?x.type.is_modifiable_flag(y)?w.session.flag[y.id]=F:w.throw_error(x.error.permission("modify","flag",y)):w.throw_error(x.error.domain("flag_value",new j("+",[y,F]),b.indicator)):w.throw_error(x.error.domain("prolog_flag",y,b.indicator)):w.throw_error(x.error.type("atom",y,b.indicator))},"use_module/1":function(w,b){var y=b.args[0];if(x.type.is_variable(y))w.throw_error(x.error.instantiation(b.indicator));else if(!x.type.is_term(y))w.throw_error(x.error.type("term",y,b.indicator));else if(x.type.is_module(y)){var F=y.args[0].id;e(w.session.modules,F)===-1&&w.session.modules.push(F)}},"char_conversion/2":function(w,b){var y=b.args[0],F=b.args[1];x.type.is_variable(y)||x.type.is_variable(F)?w.throw_error(x.error.instantiation(b.indicator)):x.type.is_character(y)?x.type.is_character(F)?y.id===F.id?delete w.session.__char_conversion[y.id]:w.session.__char_conversion[y.id]=F.id:w.throw_error(x.error.type("character",F,b.indicator)):w.throw_error(x.error.type("character",y,b.indicator))},"op/3":function(w,b){var y=b.args[0],F=b.args[1],z=b.args[2];if(x.type.is_variable(y)||x.type.is_variable(F)||x.type.is_variable(z))w.throw_error(x.error.instantiation(b.indicator));else if(!x.type.is_integer(y))w.throw_error(x.error.type("integer",y,b.indicator));else if(!x.type.is_atom(F))w.throw_error(x.error.type("atom",F,b.indicator));else if(!x.type.is_atom(z))w.throw_error(x.error.type("atom",z,b.indicator));else if(y.value<0||y.value>1200)w.throw_error(x.error.domain("operator_priority",y,b.indicator));else if(z.id===",")w.throw_error(x.error.permission("modify","operator",z,b.indicator));else if(z.id==="|"&&(y.value<1001||F.id.length!==3))w.throw_error(x.error.permission("modify","operator",z,b.indicator));else if(["fy","fx","yf","xf","xfx","yfx","xfy"].indexOf(F.id)===-1)w.throw_error(x.error.domain("operator_specifier",F,b.indicator));else{var Z={prefix:null,infix:null,postfix:null};for(var $ in w.session.__operators)if(w.session.__operators.hasOwnProperty($)){var oe=w.session.__operators[$][z.id];oe&&(e(oe,"fx")!==-1&&(Z.prefix={priority:$,type:"fx"}),e(oe,"fy")!==-1&&(Z.prefix={priority:$,type:"fy"}),e(oe,"xf")!==-1&&(Z.postfix={priority:$,type:"xf"}),e(oe,"yf")!==-1&&(Z.postfix={priority:$,type:"yf"}),e(oe,"xfx")!==-1&&(Z.infix={priority:$,type:"xfx"}),e(oe,"xfy")!==-1&&(Z.infix={priority:$,type:"xfy"}),e(oe,"yfx")!==-1&&(Z.infix={priority:$,type:"yfx"}))}var xe;switch(F.id){case"fy":case"fx":xe="prefix";break;case"yf":case"xf":xe="postfix";break;default:xe="infix";break}if(((Z.prefix&&xe==="prefix"||Z.postfix&&xe==="postfix"||Z.infix&&xe==="infix")&&Z[xe].type!==F.id||Z.infix&&xe==="postfix"||Z.postfix&&xe==="infix")&&y.value!==0)w.throw_error(x.error.permission("create","operator",z,b.indicator));else return Z[xe]&&(we(w.session.__operators[Z[xe].priority][z.id],F.id),w.session.__operators[Z[xe].priority][z.id].length===0&&delete w.session.__operators[Z[xe].priority][z.id]),y.value>0&&(w.session.__operators[y.value]||(w.session.__operators[y.value.toString()]={}),w.session.__operators[y.value][z.id]||(w.session.__operators[y.value][z.id]=[]),w.session.__operators[y.value][z.id].push(F.id)),!0}}},predicate:{"op/3":function(w,b,y){x.directive["op/3"](w,y)&&w.success(b)},"current_op/3":function(w,b,y){var F=y.args[0],z=y.args[1],Z=y.args[2],$=[];for(var oe in w.session.__operators)for(var xe in w.session.__operators[oe])for(var Te=0;Te/2"){var F=w.points,z=w.session.format_success,Z=w.session.format_error;w.session.format_success=function(Te){return Te.substitution},w.session.format_error=function(Te){return Te.goal},w.points=[new Pe(y.args[0].args[0],b.substitution,b)];var $=function(Te){w.points=F,w.session.format_success=z,w.session.format_error=Z,Te===!1?w.prepend([new Pe(b.goal.replace(y.args[1]),b.substitution,b)]):x.type.is_error(Te)?w.throw_error(Te.args[0]):Te===null?(w.prepend([b]),w.__calls.shift()(null)):w.prepend([new Pe(b.goal.replace(y.args[0].args[1]).apply(Te),b.substitution.apply(Te),b)])};w.__calls.unshift($)}else{var oe=new Pe(b.goal.replace(y.args[0]),b.substitution,b),xe=new Pe(b.goal.replace(y.args[1]),b.substitution,b);w.prepend([oe,xe])}},"!/0":function(w,b,y){var F,z,Z=[];for(F=b,z=null;F.parent!==null&&F.parent.goal.search(y);)if(z=F,F=F.parent,F.goal!==null){var $=F.goal.select();if($&&$.id==="call"&&$.search(y)){F=z;break}}for(var oe=w.points.length-1;oe>=0;oe--){for(var xe=w.points[oe],Te=xe.parent;Te!==null&&Te!==F.parent;)Te=Te.parent;Te===null&&Te!==F.parent&&Z.push(xe)}w.points=Z.reverse(),w.success(b)},"\\+/1":function(w,b,y){var F=y.args[0];x.type.is_variable(F)?w.throw_error(x.error.instantiation(w.level)):x.type.is_callable(F)?w.prepend([new Pe(b.goal.replace(new j(",",[new j(",",[new j("call",[F]),new j("!",[])]),new j("fail",[])])),b.substitution,b),new Pe(b.goal.replace(null),b.substitution,b)]):w.throw_error(x.error.type("callable",F,w.level))},"->/2":function(w,b,y){var F=b.goal.replace(new j(",",[y.args[0],new j(",",[new j("!"),y.args[1]])]));w.prepend([new Pe(F,b.substitution,b)])},"fail/0":function(w,b,y){},"false/0":function(w,b,y){},"true/0":function(w,b,y){w.success(b)},"call/1":se(1),"call/2":se(2),"call/3":se(3),"call/4":se(4),"call/5":se(5),"call/6":se(6),"call/7":se(7),"call/8":se(8),"once/1":function(w,b,y){var F=y.args[0];w.prepend([new Pe(b.goal.replace(new j(",",[new j("call",[F]),new j("!",[])])),b.substitution,b)])},"forall/2":function(w,b,y){var F=y.args[0],z=y.args[1];w.prepend([new Pe(b.goal.replace(new j("\\+",[new j(",",[new j("call",[F]),new j("\\+",[new j("call",[z])])])])),b.substitution,b)])},"repeat/0":function(w,b,y){w.prepend([new Pe(b.goal.replace(null),b.substitution,b),b])},"throw/1":function(w,b,y){x.type.is_variable(y.args[0])?w.throw_error(x.error.instantiation(w.level)):w.throw_error(y.args[0])},"catch/3":function(w,b,y){var F=w.points;w.points=[],w.prepend([new Pe(y.args[0],b.substitution,b)]);var z=w.session.format_success,Z=w.session.format_error;w.session.format_success=function(oe){return oe.substitution},w.session.format_error=function(oe){return oe.goal};var $=function(oe){var xe=w.points;if(w.points=F,w.session.format_success=z,w.session.format_error=Z,x.type.is_error(oe)){for(var Te=[],lt=w.points.length-1;lt>=0;lt--){for(var ir=w.points[lt],Et=ir.parent;Et!==null&&Et!==b.parent;)Et=Et.parent;Et===null&&Et!==b.parent&&Te.push(ir)}w.points=Te;var qt=w.get_flag("occurs_check").indicator==="true/0",ir=new Pe,Pt=x.unify(oe.args[0],y.args[1],qt);Pt!==null?(ir.substitution=b.substitution.apply(Pt),ir.goal=b.goal.replace(y.args[2]).apply(Pt),ir.parent=b,w.prepend([ir])):w.throw_error(oe.args[0])}else if(oe!==!1){for(var gn=oe===null?[]:[new Pe(b.goal.apply(oe).replace(null),b.substitution.apply(oe),b)],Pr=[],lt=xe.length-1;lt>=0;lt--){Pr.push(xe[lt]);var Ir=xe[lt].goal!==null?xe[lt].goal.select():null;if(x.type.is_term(Ir)&&Ir.indicator==="!/0")break}var Nr=s(Pr,function(nn){return nn.goal===null&&(nn.goal=new j("true",[])),nn=new Pe(b.goal.replace(new j("catch",[nn.goal,y.args[1],y.args[2]])),b.substitution.apply(nn.substitution),nn.parent),nn.exclude=y.args[0].variables(),nn}).reverse();w.prepend(Nr),w.prepend(gn),oe===null&&(this.current_limit=0,w.__calls.shift()(null))}};w.__calls.unshift($)},"=/2":function(w,b,y){var F=w.get_flag("occurs_check").indicator==="true/0",z=new Pe,Z=x.unify(y.args[0],y.args[1],F);Z!==null&&(z.goal=b.goal.apply(Z).replace(null),z.substitution=b.substitution.apply(Z),z.parent=b,w.prepend([z]))},"unify_with_occurs_check/2":function(w,b,y){var F=new Pe,z=x.unify(y.args[0],y.args[1],!0);z!==null&&(F.goal=b.goal.apply(z).replace(null),F.substitution=b.substitution.apply(z),F.parent=b,w.prepend([F]))},"\\=/2":function(w,b,y){var F=w.get_flag("occurs_check").indicator==="true/0",z=x.unify(y.args[0],y.args[1],F);z===null&&w.success(b)},"subsumes_term/2":function(w,b,y){var F=w.get_flag("occurs_check").indicator==="true/0",z=x.unify(y.args[1],y.args[0],F);z!==null&&y.args[1].apply(z).equals(y.args[1])&&w.success(b)},"findall/3":function(w,b,y){var F=y.args[0],z=y.args[1],Z=y.args[2];if(x.type.is_variable(z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(z))w.throw_error(x.error.type("callable",z,y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_list(Z))w.throw_error(x.error.type("list",Z,y.indicator));else{var $=w.next_free_variable(),oe=new j(",",[z,new j("=",[$,F])]),xe=w.points,Te=w.session.limit,lt=w.session.format_success;w.session.format_success=function(ir){return ir.substitution},w.add_goal(oe,!0,b);var Et=[],qt=function(ir){if(ir!==!1&&ir!==null&&!x.type.is_error(ir))w.__calls.unshift(qt),Et.push(ir.links[$.id]),w.session.limit=w.current_limit;else if(w.points=xe,w.session.limit=Te,w.session.format_success=lt,x.type.is_error(ir))w.throw_error(ir.args[0]);else if(w.current_limit>0){for(var Pt=new j("[]"),gn=Et.length-1;gn>=0;gn--)Pt=new j(".",[Et[gn],Pt]);w.prepend([new Pe(b.goal.replace(new j("=",[Z,Pt])),b.substitution,b)])}};w.__calls.unshift(qt)}},"bagof/3":function(w,b,y){var F,z=y.args[0],Z=y.args[1],$=y.args[2];if(x.type.is_variable(Z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(Z))w.throw_error(x.error.type("callable",Z,y.indicator));else if(!x.type.is_variable($)&&!x.type.is_list($))w.throw_error(x.error.type("list",$,y.indicator));else{var oe=w.next_free_variable(),xe;Z.indicator==="^/2"?(xe=Z.args[0].variables(),Z=Z.args[1]):xe=[],xe=xe.concat(z.variables());for(var Te=Z.variables().filter(function(Nr){return e(xe,Nr)===-1}),lt=new j("[]"),Et=Te.length-1;Et>=0;Et--)lt=new j(".",[new De(Te[Et]),lt]);var qt=new j(",",[Z,new j("=",[oe,new j(",",[lt,z])])]),ir=w.points,Pt=w.session.limit,gn=w.session.format_success;w.session.format_success=function(Nr){return Nr.substitution},w.add_goal(qt,!0,b);var Pr=[],Ir=function(Nr){if(Nr!==!1&&Nr!==null&&!x.type.is_error(Nr)){w.__calls.unshift(Ir);var nn=!1,oi=Nr.links[oe.id].args[0],wo=Nr.links[oe.id].args[1];for(var rs in Pr)if(Pr.hasOwnProperty(rs)){var eo=Pr[rs];if(eo.variables.equals(oi)){eo.answers.push(wo),nn=!0;break}}nn||Pr.push({variables:oi,answers:[wo]}),w.session.limit=w.current_limit}else if(w.points=ir,w.session.limit=Pt,w.session.format_success=gn,x.type.is_error(Nr))w.throw_error(Nr.args[0]);else if(w.current_limit>0){for(var Bo=[],Hi=0;Hi=0;vo--)to=new j(".",[Nr[vo],to]);Bo.push(new Pe(b.goal.replace(new j(",",[new j("=",[lt,Pr[Hi].variables]),new j("=",[$,to])])),b.substitution,b))}w.prepend(Bo)}};w.__calls.unshift(Ir)}},"setof/3":function(w,b,y){var F,z=y.args[0],Z=y.args[1],$=y.args[2];if(x.type.is_variable(Z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(Z))w.throw_error(x.error.type("callable",Z,y.indicator));else if(!x.type.is_variable($)&&!x.type.is_list($))w.throw_error(x.error.type("list",$,y.indicator));else{var oe=w.next_free_variable(),xe;Z.indicator==="^/2"?(xe=Z.args[0].variables(),Z=Z.args[1]):xe=[],xe=xe.concat(z.variables());for(var Te=Z.variables().filter(function(Nr){return e(xe,Nr)===-1}),lt=new j("[]"),Et=Te.length-1;Et>=0;Et--)lt=new j(".",[new De(Te[Et]),lt]);var qt=new j(",",[Z,new j("=",[oe,new j(",",[lt,z])])]),ir=w.points,Pt=w.session.limit,gn=w.session.format_success;w.session.format_success=function(Nr){return Nr.substitution},w.add_goal(qt,!0,b);var Pr=[],Ir=function(Nr){if(Nr!==!1&&Nr!==null&&!x.type.is_error(Nr)){w.__calls.unshift(Ir);var nn=!1,oi=Nr.links[oe.id].args[0],wo=Nr.links[oe.id].args[1];for(var rs in Pr)if(Pr.hasOwnProperty(rs)){var eo=Pr[rs];if(eo.variables.equals(oi)){eo.answers.push(wo),nn=!0;break}}nn||Pr.push({variables:oi,answers:[wo]}),w.session.limit=w.current_limit}else if(w.points=ir,w.session.limit=Pt,w.session.format_success=gn,x.type.is_error(Nr))w.throw_error(Nr.args[0]);else if(w.current_limit>0){for(var Bo=[],Hi=0;Hi=0;vo--)to=new j(".",[Nr[vo],to]);Bo.push(new Pe(b.goal.replace(new j(",",[new j("=",[lt,Pr[Hi].variables]),new j("=",[$,to])])),b.substitution,b))}w.prepend(Bo)}};w.__calls.unshift(Ir)}},"functor/3":function(w,b,y){var F,z=y.args[0],Z=y.args[1],$=y.args[2];if(x.type.is_variable(z)&&(x.type.is_variable(Z)||x.type.is_variable($)))w.throw_error(x.error.instantiation("functor/3"));else if(!x.type.is_variable($)&&!x.type.is_integer($))w.throw_error(x.error.type("integer",y.args[2],"functor/3"));else if(!x.type.is_variable(Z)&&!x.type.is_atomic(Z))w.throw_error(x.error.type("atomic",y.args[1],"functor/3"));else if(x.type.is_integer(Z)&&x.type.is_integer($)&&$.value!==0)w.throw_error(x.error.type("atom",y.args[1],"functor/3"));else if(x.type.is_variable(z)){if(y.args[2].value>=0){for(var oe=[],xe=0;xe<$.value;xe++)oe.push(w.next_free_variable());var Te=x.type.is_integer(Z)?Z:new j(Z.id,oe);w.prepend([new Pe(b.goal.replace(new j("=",[z,Te])),b.substitution,b)])}}else{var lt=x.type.is_integer(z)?z:new j(z.id,[]),Et=x.type.is_integer(z)?new Re(0,!1):new Re(z.args.length,!1),qt=new j(",",[new j("=",[lt,Z]),new j("=",[Et,$])]);w.prepend([new Pe(b.goal.replace(qt),b.substitution,b)])}},"arg/3":function(w,b,y){if(x.type.is_variable(y.args[0])||x.type.is_variable(y.args[1]))w.throw_error(x.error.instantiation(y.indicator));else if(y.args[0].value<0)w.throw_error(x.error.domain("not_less_than_zero",y.args[0],y.indicator));else if(!x.type.is_compound(y.args[1]))w.throw_error(x.error.type("compound",y.args[1],y.indicator));else{var F=y.args[0].value;if(F>0&&F<=y.args[1].args.length){var z=new j("=",[y.args[1].args[F-1],y.args[2]]);w.prepend([new Pe(b.goal.replace(z),b.substitution,b)])}}},"=../2":function(w,b,y){var F;if(x.type.is_variable(y.args[0])&&(x.type.is_variable(y.args[1])||x.type.is_non_empty_list(y.args[1])&&x.type.is_variable(y.args[1].args[0])))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_fully_list(y.args[1]))w.throw_error(x.error.type("list",y.args[1],y.indicator));else if(x.type.is_variable(y.args[0])){if(!x.type.is_variable(y.args[1])){var Z=[];for(F=y.args[1].args[1];F.indicator==="./2";)Z.push(F.args[0]),F=F.args[1];x.type.is_variable(y.args[0])&&x.type.is_variable(F)?w.throw_error(x.error.instantiation(y.indicator)):Z.length===0&&x.type.is_compound(y.args[1].args[0])?w.throw_error(x.error.type("atomic",y.args[1].args[0],y.indicator)):Z.length>0&&(x.type.is_compound(y.args[1].args[0])||x.type.is_number(y.args[1].args[0]))?w.throw_error(x.error.type("atom",y.args[1].args[0],y.indicator)):Z.length===0?w.prepend([new Pe(b.goal.replace(new j("=",[y.args[1].args[0],y.args[0]],b)),b.substitution,b)]):w.prepend([new Pe(b.goal.replace(new j("=",[new j(y.args[1].args[0].id,Z),y.args[0]])),b.substitution,b)])}}else{if(x.type.is_atomic(y.args[0]))F=new j(".",[y.args[0],new j("[]")]);else{F=new j("[]");for(var z=y.args[0].args.length-1;z>=0;z--)F=new j(".",[y.args[0].args[z],F]);F=new j(".",[new j(y.args[0].id),F])}w.prepend([new Pe(b.goal.replace(new j("=",[F,y.args[1]])),b.substitution,b)])}},"copy_term/2":function(w,b,y){var F=y.args[0].rename(w);w.prepend([new Pe(b.goal.replace(new j("=",[F,y.args[1]])),b.substitution,b.parent)])},"term_variables/2":function(w,b,y){var F=y.args[0],z=y.args[1];if(!x.type.is_fully_list(z))w.throw_error(x.error.type("list",z,y.indicator));else{var Z=g(s(Ee(F.variables()),function($){return new De($)}));w.prepend([new Pe(b.goal.replace(new j("=",[z,Z])),b.substitution,b)])}},"clause/2":function(w,b,y){if(x.type.is_variable(y.args[0]))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(y.args[0]))w.throw_error(x.error.type("callable",y.args[0],y.indicator));else if(!x.type.is_variable(y.args[1])&&!x.type.is_callable(y.args[1]))w.throw_error(x.error.type("callable",y.args[1],y.indicator));else if(w.session.rules[y.args[0].indicator]!==void 0)if(w.is_public_predicate(y.args[0].indicator)){var F=[];for(var z in w.session.rules[y.args[0].indicator])if(w.session.rules[y.args[0].indicator].hasOwnProperty(z)){var Z=w.session.rules[y.args[0].indicator][z];w.session.renamed_variables={},Z=Z.rename(w),Z.body===null&&(Z.body=new j("true"));var $=new j(",",[new j("=",[Z.head,y.args[0]]),new j("=",[Z.body,y.args[1]])]);F.push(new Pe(b.goal.replace($),b.substitution,b))}w.prepend(F)}else w.throw_error(x.error.permission("access","private_procedure",y.args[0].indicator,y.indicator))},"current_predicate/1":function(w,b,y){var F=y.args[0];if(!x.type.is_variable(F)&&(!x.type.is_compound(F)||F.indicator!=="//2"))w.throw_error(x.error.type("predicate_indicator",F,y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_variable(F.args[0])&&!x.type.is_atom(F.args[0]))w.throw_error(x.error.type("atom",F.args[0],y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_variable(F.args[1])&&!x.type.is_integer(F.args[1]))w.throw_error(x.error.type("integer",F.args[1],y.indicator));else{var z=[];for(var Z in w.session.rules)if(w.session.rules.hasOwnProperty(Z)){var $=Z.lastIndexOf("/"),oe=Z.substr(0,$),xe=parseInt(Z.substr($+1,Z.length-($+1))),Te=new j("/",[new j(oe),new Re(xe,!1)]),lt=new j("=",[Te,F]);z.push(new Pe(b.goal.replace(lt),b.substitution,b))}w.prepend(z)}},"asserta/1":function(w,b,y){if(x.type.is_variable(y.args[0]))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(y.args[0]))w.throw_error(x.error.type("callable",y.args[0],y.indicator));else{var F,z;y.args[0].indicator===":-/2"?(F=y.args[0].args[0],z=Ce(y.args[0].args[1])):(F=y.args[0],z=null),x.type.is_callable(F)?z!==null&&!x.type.is_callable(z)?w.throw_error(x.error.type("callable",z,y.indicator)):w.is_public_predicate(F.indicator)?(w.session.rules[F.indicator]===void 0&&(w.session.rules[F.indicator]=[]),w.session.public_predicates[F.indicator]=!0,w.session.rules[F.indicator]=[new Ye(F,z,!0)].concat(w.session.rules[F.indicator]),w.success(b)):w.throw_error(x.error.permission("modify","static_procedure",F.indicator,y.indicator)):w.throw_error(x.error.type("callable",F,y.indicator))}},"assertz/1":function(w,b,y){if(x.type.is_variable(y.args[0]))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(y.args[0]))w.throw_error(x.error.type("callable",y.args[0],y.indicator));else{var F,z;y.args[0].indicator===":-/2"?(F=y.args[0].args[0],z=Ce(y.args[0].args[1])):(F=y.args[0],z=null),x.type.is_callable(F)?z!==null&&!x.type.is_callable(z)?w.throw_error(x.error.type("callable",z,y.indicator)):w.is_public_predicate(F.indicator)?(w.session.rules[F.indicator]===void 0&&(w.session.rules[F.indicator]=[]),w.session.public_predicates[F.indicator]=!0,w.session.rules[F.indicator].push(new Ye(F,z,!0)),w.success(b)):w.throw_error(x.error.permission("modify","static_procedure",F.indicator,y.indicator)):w.throw_error(x.error.type("callable",F,y.indicator))}},"retract/1":function(w,b,y){if(x.type.is_variable(y.args[0]))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_callable(y.args[0]))w.throw_error(x.error.type("callable",y.args[0],y.indicator));else{var F,z;if(y.args[0].indicator===":-/2"?(F=y.args[0].args[0],z=y.args[0].args[1]):(F=y.args[0],z=new j("true")),typeof b.retract>"u")if(w.is_public_predicate(F.indicator)){if(w.session.rules[F.indicator]!==void 0){for(var Z=[],$=0;$w.get_flag("max_arity").value)w.throw_error(x.error.representation("max_arity",y.indicator));else{var F=y.args[0].args[0].id+"/"+y.args[0].args[1].value;w.is_public_predicate(F)?(delete w.session.rules[F],w.success(b)):w.throw_error(x.error.permission("modify","static_procedure",F,y.indicator))}},"atom_length/2":function(w,b,y){if(x.type.is_variable(y.args[0]))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_atom(y.args[0]))w.throw_error(x.error.type("atom",y.args[0],y.indicator));else if(!x.type.is_variable(y.args[1])&&!x.type.is_integer(y.args[1]))w.throw_error(x.error.type("integer",y.args[1],y.indicator));else if(x.type.is_integer(y.args[1])&&y.args[1].value<0)w.throw_error(x.error.domain("not_less_than_zero",y.args[1],y.indicator));else{var F=new Re(y.args[0].id.length,!1);w.prepend([new Pe(b.goal.replace(new j("=",[F,y.args[1]])),b.substitution,b)])}},"atom_concat/3":function(w,b,y){var F,z,Z=y.args[0],$=y.args[1],oe=y.args[2];if(x.type.is_variable(oe)&&(x.type.is_variable(Z)||x.type.is_variable($)))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_atom(Z))w.throw_error(x.error.type("atom",Z,y.indicator));else if(!x.type.is_variable($)&&!x.type.is_atom($))w.throw_error(x.error.type("atom",$,y.indicator));else if(!x.type.is_variable(oe)&&!x.type.is_atom(oe))w.throw_error(x.error.type("atom",oe,y.indicator));else{var xe=x.type.is_variable(Z),Te=x.type.is_variable($);if(!xe&&!Te)z=new j("=",[oe,new j(Z.id+$.id)]),w.prepend([new Pe(b.goal.replace(z),b.substitution,b)]);else if(xe&&!Te)F=oe.id.substr(0,oe.id.length-$.id.length),F+$.id===oe.id&&(z=new j("=",[Z,new j(F)]),w.prepend([new Pe(b.goal.replace(z),b.substitution,b)]));else if(Te&&!xe)F=oe.id.substr(Z.id.length),Z.id+F===oe.id&&(z=new j("=",[$,new j(F)]),w.prepend([new Pe(b.goal.replace(z),b.substitution,b)]));else{for(var lt=[],Et=0;Et<=oe.id.length;Et++){var qt=new j(oe.id.substr(0,Et)),ir=new j(oe.id.substr(Et));z=new j(",",[new j("=",[qt,Z]),new j("=",[ir,$])]),lt.push(new Pe(b.goal.replace(z),b.substitution,b))}w.prepend(lt)}}},"sub_atom/5":function(w,b,y){var F,z=y.args[0],Z=y.args[1],$=y.args[2],oe=y.args[3],xe=y.args[4];if(x.type.is_variable(z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_integer(Z))w.throw_error(x.error.type("integer",Z,y.indicator));else if(!x.type.is_variable($)&&!x.type.is_integer($))w.throw_error(x.error.type("integer",$,y.indicator));else if(!x.type.is_variable(oe)&&!x.type.is_integer(oe))w.throw_error(x.error.type("integer",oe,y.indicator));else if(x.type.is_integer(Z)&&Z.value<0)w.throw_error(x.error.domain("not_less_than_zero",Z,y.indicator));else if(x.type.is_integer($)&&$.value<0)w.throw_error(x.error.domain("not_less_than_zero",$,y.indicator));else if(x.type.is_integer(oe)&&oe.value<0)w.throw_error(x.error.domain("not_less_than_zero",oe,y.indicator));else{var Te=[],lt=[],Et=[];if(x.type.is_variable(Z))for(F=0;F<=z.id.length;F++)Te.push(F);else Te.push(Z.value);if(x.type.is_variable($))for(F=0;F<=z.id.length;F++)lt.push(F);else lt.push($.value);if(x.type.is_variable(oe))for(F=0;F<=z.id.length;F++)Et.push(F);else Et.push(oe.value);var qt=[];for(var ir in Te)if(Te.hasOwnProperty(ir)){F=Te[ir];for(var Pt in lt)if(lt.hasOwnProperty(Pt)){var gn=lt[Pt],Pr=z.id.length-F-gn;if(e(Et,Pr)!==-1&&F+gn+Pr===z.id.length){var Ir=z.id.substr(F,gn);if(z.id===z.id.substr(0,F)+Ir+z.id.substr(F+gn,Pr)){var Nr=new j("=",[new j(Ir),xe]),nn=new j("=",[Z,new Re(F)]),oi=new j("=",[$,new Re(gn)]),wo=new j("=",[oe,new Re(Pr)]),rs=new j(",",[new j(",",[new j(",",[nn,oi]),wo]),Nr]);qt.push(new Pe(b.goal.replace(rs),b.substitution,b))}}}}w.prepend(qt)}},"atom_chars/2":function(w,b,y){var F=y.args[0],z=y.args[1];if(x.type.is_variable(F)&&x.type.is_variable(z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_atom(F))w.throw_error(x.error.type("atom",F,y.indicator));else if(x.type.is_variable(F)){for(var oe=z,xe=x.type.is_variable(F),Te="";oe.indicator==="./2";){if(x.type.is_character(oe.args[0]))Te+=oe.args[0].id;else if(x.type.is_variable(oe.args[0])&&xe){w.throw_error(x.error.instantiation(y.indicator));return}else if(!x.type.is_variable(oe.args[0])){w.throw_error(x.error.type("character",oe.args[0],y.indicator));return}oe=oe.args[1]}x.type.is_variable(oe)&&xe?w.throw_error(x.error.instantiation(y.indicator)):!x.type.is_empty_list(oe)&&!x.type.is_variable(oe)?w.throw_error(x.error.type("list",z,y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[new j(Te),F])),b.substitution,b)])}else{for(var Z=new j("[]"),$=F.id.length-1;$>=0;$--)Z=new j(".",[new j(F.id.charAt($)),Z]);w.prepend([new Pe(b.goal.replace(new j("=",[z,Z])),b.substitution,b)])}},"atom_codes/2":function(w,b,y){var F=y.args[0],z=y.args[1];if(x.type.is_variable(F)&&x.type.is_variable(z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_atom(F))w.throw_error(x.error.type("atom",F,y.indicator));else if(x.type.is_variable(F)){for(var oe=z,xe=x.type.is_variable(F),Te="";oe.indicator==="./2";){if(x.type.is_character_code(oe.args[0]))Te+=c(oe.args[0].value);else if(x.type.is_variable(oe.args[0])&&xe){w.throw_error(x.error.instantiation(y.indicator));return}else if(!x.type.is_variable(oe.args[0])){w.throw_error(x.error.representation("character_code",y.indicator));return}oe=oe.args[1]}x.type.is_variable(oe)&&xe?w.throw_error(x.error.instantiation(y.indicator)):!x.type.is_empty_list(oe)&&!x.type.is_variable(oe)?w.throw_error(x.error.type("list",z,y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[new j(Te),F])),b.substitution,b)])}else{for(var Z=new j("[]"),$=F.id.length-1;$>=0;$--)Z=new j(".",[new Re(n(F.id,$),!1),Z]);w.prepend([new Pe(b.goal.replace(new j("=",[z,Z])),b.substitution,b)])}},"char_code/2":function(w,b,y){var F=y.args[0],z=y.args[1];if(x.type.is_variable(F)&&x.type.is_variable(z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_character(F))w.throw_error(x.error.type("character",F,y.indicator));else if(!x.type.is_variable(z)&&!x.type.is_integer(z))w.throw_error(x.error.type("integer",z,y.indicator));else if(!x.type.is_variable(z)&&!x.type.is_character_code(z))w.throw_error(x.error.representation("character_code",y.indicator));else if(x.type.is_variable(z)){var Z=new Re(n(F.id,0),!1);w.prepend([new Pe(b.goal.replace(new j("=",[Z,z])),b.substitution,b)])}else{var $=new j(c(z.value));w.prepend([new Pe(b.goal.replace(new j("=",[$,F])),b.substitution,b)])}},"number_chars/2":function(w,b,y){var F,z=y.args[0],Z=y.args[1];if(x.type.is_variable(z)&&x.type.is_variable(Z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(z)&&!x.type.is_number(z))w.throw_error(x.error.type("number",z,y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_list(Z))w.throw_error(x.error.type("list",Z,y.indicator));else{var $=x.type.is_variable(z);if(!x.type.is_variable(Z)){var oe=Z,xe=!0;for(F="";oe.indicator==="./2";){if(x.type.is_character(oe.args[0]))F+=oe.args[0].id;else if(x.type.is_variable(oe.args[0]))xe=!1;else if(!x.type.is_variable(oe.args[0])){w.throw_error(x.error.type("character",oe.args[0],y.indicator));return}oe=oe.args[1]}if(xe=xe&&x.type.is_empty_list(oe),!x.type.is_empty_list(oe)&&!x.type.is_variable(oe)){w.throw_error(x.error.type("list",Z,y.indicator));return}if(!xe&&$){w.throw_error(x.error.instantiation(y.indicator));return}else if(xe)if(x.type.is_variable(oe)&&$){w.throw_error(x.error.instantiation(y.indicator));return}else{var Te=w.parse(F),lt=Te.value;!x.type.is_number(lt)||Te.tokens[Te.tokens.length-1].space?w.throw_error(x.error.syntax_by_predicate("parseable_number",y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[z,lt])),b.substitution,b)]);return}}if(!$){F=z.toString();for(var Et=new j("[]"),qt=F.length-1;qt>=0;qt--)Et=new j(".",[new j(F.charAt(qt)),Et]);w.prepend([new Pe(b.goal.replace(new j("=",[Z,Et])),b.substitution,b)])}}},"number_codes/2":function(w,b,y){var F,z=y.args[0],Z=y.args[1];if(x.type.is_variable(z)&&x.type.is_variable(Z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(z)&&!x.type.is_number(z))w.throw_error(x.error.type("number",z,y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_list(Z))w.throw_error(x.error.type("list",Z,y.indicator));else{var $=x.type.is_variable(z);if(!x.type.is_variable(Z)){var oe=Z,xe=!0;for(F="";oe.indicator==="./2";){if(x.type.is_character_code(oe.args[0]))F+=c(oe.args[0].value);else if(x.type.is_variable(oe.args[0]))xe=!1;else if(!x.type.is_variable(oe.args[0])){w.throw_error(x.error.type("character_code",oe.args[0],y.indicator));return}oe=oe.args[1]}if(xe=xe&&x.type.is_empty_list(oe),!x.type.is_empty_list(oe)&&!x.type.is_variable(oe)){w.throw_error(x.error.type("list",Z,y.indicator));return}if(!xe&&$){w.throw_error(x.error.instantiation(y.indicator));return}else if(xe)if(x.type.is_variable(oe)&&$){w.throw_error(x.error.instantiation(y.indicator));return}else{var Te=w.parse(F),lt=Te.value;!x.type.is_number(lt)||Te.tokens[Te.tokens.length-1].space?w.throw_error(x.error.syntax_by_predicate("parseable_number",y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[z,lt])),b.substitution,b)]);return}}if(!$){F=z.toString();for(var Et=new j("[]"),qt=F.length-1;qt>=0;qt--)Et=new j(".",[new Re(n(F,qt),!1),Et]);w.prepend([new Pe(b.goal.replace(new j("=",[Z,Et])),b.substitution,b)])}}},"upcase_atom/2":function(w,b,y){var F=y.args[0],z=y.args[1];x.type.is_variable(F)?w.throw_error(x.error.instantiation(y.indicator)):x.type.is_atom(F)?!x.type.is_variable(z)&&!x.type.is_atom(z)?w.throw_error(x.error.type("atom",z,y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[z,new j(F.id.toUpperCase(),[])])),b.substitution,b)]):w.throw_error(x.error.type("atom",F,y.indicator))},"downcase_atom/2":function(w,b,y){var F=y.args[0],z=y.args[1];x.type.is_variable(F)?w.throw_error(x.error.instantiation(y.indicator)):x.type.is_atom(F)?!x.type.is_variable(z)&&!x.type.is_atom(z)?w.throw_error(x.error.type("atom",z,y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[z,new j(F.id.toLowerCase(),[])])),b.substitution,b)]):w.throw_error(x.error.type("atom",F,y.indicator))},"atomic_list_concat/2":function(w,b,y){var F=y.args[0],z=y.args[1];w.prepend([new Pe(b.goal.replace(new j("atomic_list_concat",[F,new j("",[]),z])),b.substitution,b)])},"atomic_list_concat/3":function(w,b,y){var F=y.args[0],z=y.args[1],Z=y.args[2];if(x.type.is_variable(z)||x.type.is_variable(F)&&x.type.is_variable(Z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_list(F))w.throw_error(x.error.type("list",F,y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_atom(Z))w.throw_error(x.error.type("atom",Z,y.indicator));else if(x.type.is_variable(Z)){for(var oe="",xe=F;x.type.is_term(xe)&&xe.indicator==="./2";){if(!x.type.is_atom(xe.args[0])&&!x.type.is_number(xe.args[0])){w.throw_error(x.error.type("atomic",xe.args[0],y.indicator));return}oe!==""&&(oe+=z.id),x.type.is_atom(xe.args[0])?oe+=xe.args[0].id:oe+=""+xe.args[0].value,xe=xe.args[1]}oe=new j(oe,[]),x.type.is_variable(xe)?w.throw_error(x.error.instantiation(y.indicator)):!x.type.is_term(xe)||xe.indicator!=="[]/0"?w.throw_error(x.error.type("list",F,y.indicator)):w.prepend([new Pe(b.goal.replace(new j("=",[oe,Z])),b.substitution,b)])}else{var $=g(s(Z.id.split(z.id),function(Te){return new j(Te,[])}));w.prepend([new Pe(b.goal.replace(new j("=",[$,F])),b.substitution,b)])}},"@=/2":function(w,b,y){x.compare(y.args[0],y.args[1])>0&&w.success(b)},"@>=/2":function(w,b,y){x.compare(y.args[0],y.args[1])>=0&&w.success(b)},"compare/3":function(w,b,y){var F=y.args[0],z=y.args[1],Z=y.args[2];if(!x.type.is_variable(F)&&!x.type.is_atom(F))w.throw_error(x.error.type("atom",F,y.indicator));else if(x.type.is_atom(F)&&["<",">","="].indexOf(F.id)===-1)w.throw_error(x.type.domain("order",F,y.indicator));else{var $=x.compare(z,Z);$=$===0?"=":$===-1?"<":">",w.prepend([new Pe(b.goal.replace(new j("=",[F,new j($,[])])),b.substitution,b)])}},"is/2":function(w,b,y){var F=y.args[1].interpret(w);x.type.is_number(F)?w.prepend([new Pe(b.goal.replace(new j("=",[y.args[0],F],w.level)),b.substitution,b)]):w.throw_error(F)},"between/3":function(w,b,y){var F=y.args[0],z=y.args[1],Z=y.args[2];if(x.type.is_variable(F)||x.type.is_variable(z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_integer(F))w.throw_error(x.error.type("integer",F,y.indicator));else if(!x.type.is_integer(z))w.throw_error(x.error.type("integer",z,y.indicator));else if(!x.type.is_variable(Z)&&!x.type.is_integer(Z))w.throw_error(x.error.type("integer",Z,y.indicator));else if(x.type.is_variable(Z)){var $=[new Pe(b.goal.replace(new j("=",[Z,F])),b.substitution,b)];F.value=Z.value&&w.success(b)},"succ/2":function(w,b,y){var F=y.args[0],z=y.args[1];x.type.is_variable(F)&&x.type.is_variable(z)?w.throw_error(x.error.instantiation(y.indicator)):!x.type.is_variable(F)&&!x.type.is_integer(F)?w.throw_error(x.error.type("integer",F,y.indicator)):!x.type.is_variable(z)&&!x.type.is_integer(z)?w.throw_error(x.error.type("integer",z,y.indicator)):!x.type.is_variable(F)&&F.value<0?w.throw_error(x.error.domain("not_less_than_zero",F,y.indicator)):!x.type.is_variable(z)&&z.value<0?w.throw_error(x.error.domain("not_less_than_zero",z,y.indicator)):(x.type.is_variable(z)||z.value>0)&&(x.type.is_variable(F)?w.prepend([new Pe(b.goal.replace(new j("=",[F,new Re(z.value-1,!1)])),b.substitution,b)]):w.prepend([new Pe(b.goal.replace(new j("=",[z,new Re(F.value+1,!1)])),b.substitution,b)]))},"=:=/2":function(w,b,y){var F=x.arithmetic_compare(w,y.args[0],y.args[1]);x.type.is_term(F)?w.throw_error(F):F===0&&w.success(b)},"=\\=/2":function(w,b,y){var F=x.arithmetic_compare(w,y.args[0],y.args[1]);x.type.is_term(F)?w.throw_error(F):F!==0&&w.success(b)},"/2":function(w,b,y){var F=x.arithmetic_compare(w,y.args[0],y.args[1]);x.type.is_term(F)?w.throw_error(F):F>0&&w.success(b)},">=/2":function(w,b,y){var F=x.arithmetic_compare(w,y.args[0],y.args[1]);x.type.is_term(F)?w.throw_error(F):F>=0&&w.success(b)},"var/1":function(w,b,y){x.type.is_variable(y.args[0])&&w.success(b)},"atom/1":function(w,b,y){x.type.is_atom(y.args[0])&&w.success(b)},"atomic/1":function(w,b,y){x.type.is_atomic(y.args[0])&&w.success(b)},"compound/1":function(w,b,y){x.type.is_compound(y.args[0])&&w.success(b)},"integer/1":function(w,b,y){x.type.is_integer(y.args[0])&&w.success(b)},"float/1":function(w,b,y){x.type.is_float(y.args[0])&&w.success(b)},"number/1":function(w,b,y){x.type.is_number(y.args[0])&&w.success(b)},"nonvar/1":function(w,b,y){x.type.is_variable(y.args[0])||w.success(b)},"ground/1":function(w,b,y){y.variables().length===0&&w.success(b)},"acyclic_term/1":function(w,b,y){for(var F=b.substitution.apply(b.substitution),z=y.args[0].variables(),Z=0;Z0?Pt[Pt.length-1]:null,Pt!==null&&(qt=W(w,Pt,0,w.__get_max_priority(),!1))}if(qt.type===p&&qt.len===Pt.length-1&&gn.value==="."){qt=qt.value.rename(w);var Pr=new j("=",[z,qt]);if(oe.variables){var Ir=g(s(Ee(qt.variables()),function(Nr){return new De(Nr)}));Pr=new j(",",[Pr,new j("=",[oe.variables,Ir])])}if(oe.variable_names){var Ir=g(s(Ee(qt.variables()),function(nn){var oi;for(oi in w.session.renamed_variables)if(w.session.renamed_variables.hasOwnProperty(oi)&&w.session.renamed_variables[oi]===nn)break;return new j("=",[new j(oi,[]),new De(nn)])}));Pr=new j(",",[Pr,new j("=",[oe.variable_names,Ir])])}if(oe.singletons){var Ir=g(s(new Ye(qt,null).singleton_variables(),function(nn){var oi;for(oi in w.session.renamed_variables)if(w.session.renamed_variables.hasOwnProperty(oi)&&w.session.renamed_variables[oi]===nn)break;return new j("=",[new j(oi,[]),new De(nn)])}));Pr=new j(",",[Pr,new j("=",[oe.singletons,Ir])])}w.prepend([new Pe(b.goal.replace(Pr),b.substitution,b)])}else qt.type===p?w.throw_error(x.error.syntax(Pt[qt.len],"unexpected token",!1)):w.throw_error(qt.value)}}},"write/1":function(w,b,y){var F=y.args[0];w.prepend([new Pe(b.goal.replace(new j(",",[new j("current_output",[new De("S")]),new j("write",[new De("S"),F])])),b.substitution,b)])},"write/2":function(w,b,y){var F=y.args[0],z=y.args[1];w.prepend([new Pe(b.goal.replace(new j("write_term",[F,z,new j(".",[new j("quoted",[new j("false",[])]),new j(".",[new j("ignore_ops",[new j("false")]),new j(".",[new j("numbervars",[new j("true")]),new j("[]",[])])])])])),b.substitution,b)])},"writeq/1":function(w,b,y){var F=y.args[0];w.prepend([new Pe(b.goal.replace(new j(",",[new j("current_output",[new De("S")]),new j("writeq",[new De("S"),F])])),b.substitution,b)])},"writeq/2":function(w,b,y){var F=y.args[0],z=y.args[1];w.prepend([new Pe(b.goal.replace(new j("write_term",[F,z,new j(".",[new j("quoted",[new j("true",[])]),new j(".",[new j("ignore_ops",[new j("false")]),new j(".",[new j("numbervars",[new j("true")]),new j("[]",[])])])])])),b.substitution,b)])},"write_canonical/1":function(w,b,y){var F=y.args[0];w.prepend([new Pe(b.goal.replace(new j(",",[new j("current_output",[new De("S")]),new j("write_canonical",[new De("S"),F])])),b.substitution,b)])},"write_canonical/2":function(w,b,y){var F=y.args[0],z=y.args[1];w.prepend([new Pe(b.goal.replace(new j("write_term",[F,z,new j(".",[new j("quoted",[new j("true",[])]),new j(".",[new j("ignore_ops",[new j("true")]),new j(".",[new j("numbervars",[new j("false")]),new j("[]",[])])])])])),b.substitution,b)])},"write_term/2":function(w,b,y){var F=y.args[0],z=y.args[1];w.prepend([new Pe(b.goal.replace(new j(",",[new j("current_output",[new De("S")]),new j("write_term",[new De("S"),F,z])])),b.substitution,b)])},"write_term/3":function(w,b,y){var F=y.args[0],z=y.args[1],Z=y.args[2],$=x.type.is_stream(F)?F:w.get_stream_by_alias(F.id);if(x.type.is_variable(F)||x.type.is_variable(Z))w.throw_error(x.error.instantiation(y.indicator));else if(!x.type.is_list(Z))w.throw_error(x.error.type("list",Z,y.indicator));else if(!x.type.is_stream(F)&&!x.type.is_atom(F))w.throw_error(x.error.domain("stream_or_alias",F,y.indicator));else if(!x.type.is_stream($)||$.stream===null)w.throw_error(x.error.existence("stream",F,y.indicator));else if($.input)w.throw_error(x.error.permission("output","stream",F,y.indicator));else if($.type==="binary")w.throw_error(x.error.permission("output","binary_stream",F,y.indicator));else if($.position==="past_end_of_stream"&&$.eof_action==="error")w.throw_error(x.error.permission("output","past_end_of_stream",F,y.indicator));else{for(var oe={},xe=Z,Te;x.type.is_term(xe)&&xe.indicator==="./2";){if(Te=xe.args[0],x.type.is_variable(Te)){w.throw_error(x.error.instantiation(y.indicator));return}else if(!x.type.is_write_option(Te)){w.throw_error(x.error.domain("write_option",Te,y.indicator));return}oe[Te.id]=Te.args[0].id==="true",xe=xe.args[1]}if(xe.indicator!=="[]/0"){x.type.is_variable(xe)?w.throw_error(x.error.instantiation(y.indicator)):w.throw_error(x.error.type("list",Z,y.indicator));return}else{oe.session=w.session;var lt=z.toString(oe);$.stream.put(lt,$.position),typeof $.position=="number"&&($.position+=lt.length),w.success(b)}}},"halt/0":function(w,b,y){w.points=[]},"halt/1":function(w,b,y){var F=y.args[0];x.type.is_variable(F)?w.throw_error(x.error.instantiation(y.indicator)):x.type.is_integer(F)?w.points=[]:w.throw_error(x.error.type("integer",F,y.indicator))},"current_prolog_flag/2":function(w,b,y){var F=y.args[0],z=y.args[1];if(!x.type.is_variable(F)&&!x.type.is_atom(F))w.throw_error(x.error.type("atom",F,y.indicator));else if(!x.type.is_variable(F)&&!x.type.is_flag(F))w.throw_error(x.error.domain("prolog_flag",F,y.indicator));else{var Z=[];for(var $ in x.flag)if(x.flag.hasOwnProperty($)){var oe=new j(",",[new j("=",[new j($),F]),new j("=",[w.get_flag($),z])]);Z.push(new Pe(b.goal.replace(oe),b.substitution,b))}w.prepend(Z)}},"set_prolog_flag/2":function(w,b,y){var F=y.args[0],z=y.args[1];x.type.is_variable(F)||x.type.is_variable(z)?w.throw_error(x.error.instantiation(y.indicator)):x.type.is_atom(F)?x.type.is_flag(F)?x.type.is_value_flag(F,z)?x.type.is_modifiable_flag(F)?(w.session.flag[F.id]=z,w.success(b)):w.throw_error(x.error.permission("modify","flag",F)):w.throw_error(x.error.domain("flag_value",new j("+",[F,z]),y.indicator)):w.throw_error(x.error.domain("prolog_flag",F,y.indicator)):w.throw_error(x.error.type("atom",F,y.indicator))}},flag:{bounded:{allowed:[new j("true"),new j("false")],value:new j("true"),changeable:!1},max_integer:{allowed:[new Re(Number.MAX_SAFE_INTEGER)],value:new Re(Number.MAX_SAFE_INTEGER),changeable:!1},min_integer:{allowed:[new Re(Number.MIN_SAFE_INTEGER)],value:new Re(Number.MIN_SAFE_INTEGER),changeable:!1},integer_rounding_function:{allowed:[new j("down"),new j("toward_zero")],value:new j("toward_zero"),changeable:!1},char_conversion:{allowed:[new j("on"),new j("off")],value:new j("on"),changeable:!0},debug:{allowed:[new j("on"),new j("off")],value:new j("off"),changeable:!0},max_arity:{allowed:[new j("unbounded")],value:new j("unbounded"),changeable:!1},unknown:{allowed:[new j("error"),new j("fail"),new j("warning")],value:new j("error"),changeable:!0},double_quotes:{allowed:[new j("chars"),new j("codes"),new j("atom")],value:new j("codes"),changeable:!0},occurs_check:{allowed:[new j("false"),new j("true")],value:new j("false"),changeable:!0},dialect:{allowed:[new j("tau")],value:new j("tau"),changeable:!1},version_data:{allowed:[new j("tau",[new Re(t.major,!1),new Re(t.minor,!1),new Re(t.patch,!1),new j(t.status)])],value:new j("tau",[new Re(t.major,!1),new Re(t.minor,!1),new Re(t.patch,!1),new j(t.status)]),changeable:!1},nodejs:{allowed:[new j("yes"),new j("no")],value:new j(typeof tc<"u"&&tc.exports?"yes":"no"),changeable:!1}},unify:function(w,b,y){y=y===void 0?!1:y;for(var F=[{left:w,right:b}],z={};F.length!==0;){var Z=F.pop();if(w=Z.left,b=Z.right,x.type.is_term(w)&&x.type.is_term(b)){if(w.indicator!==b.indicator)return null;for(var $=0;$z.value?1:0:z}else return F},operate:function(w,b){if(x.type.is_operator(b)){for(var y=x.type.is_operator(b),F=[],z,Z=!1,$=0;$w.get_flag("max_integer").value||z0?w.start+w.matches[0].length:w.start,z=y?new j("token_not_found"):new j("found",[new j(w.value.toString())]),Z=new j(".",[new j("line",[new Re(w.line+1)]),new j(".",[new j("column",[new Re(F+1)]),new j(".",[z,new j("[]",[])])])]);return new j("error",[new j("syntax_error",[new j(b)]),Z])},syntax_by_predicate:function(w,b){return new j("error",[new j("syntax_error",[new j(w)]),X(b)])}},warning:{singleton:function(w,b,y){for(var F=new j("[]"),z=w.length-1;z>=0;z--)F=new j(".",[new De(w[z]),F]);return new j("warning",[new j("singleton_variables",[F,X(b)]),new j(".",[new j("line",[new Re(y,!1)]),new j("[]")])])},failed_goal:function(w,b){return new j("warning",[new j("failed_goal",[w]),new j(".",[new j("line",[new Re(b,!1)]),new j("[]")])])}},format_variable:function(w){return"_"+w},format_answer:function(w,b,F){b instanceof ke&&(b=b.thread);var F=F||{};if(F.session=b?b.session:void 0,x.type.is_error(w))return"uncaught exception: "+w.args[0].toString();if(w===!1)return"false.";if(w===null)return"limit exceeded ;";var z=0,Z="";if(x.type.is_substitution(w)){var $=w.domain(!0);w=w.filter(function(Te,lt){return!x.type.is_variable(lt)||$.indexOf(lt.id)!==-1&&Te!==lt.id})}for(var oe in w.links)w.links.hasOwnProperty(oe)&&(z++,Z!==""&&(Z+=", "),Z+=oe.toString(F)+" = "+w.links[oe].toString(F));var xe=typeof b>"u"||b.points.length>0?" ;":".";return z===0?"true"+xe:Z+xe},flatten_error:function(w){if(!x.type.is_error(w))return null;w=w.args[0];var b={};return b.type=w.args[0].id,b.thrown=b.type==="syntax_error"?null:w.args[1].id,b.expected=null,b.found=null,b.representation=null,b.existence=null,b.existence_type=null,b.line=null,b.column=null,b.permission_operation=null,b.permission_type=null,b.evaluation_type=null,b.type==="type_error"||b.type==="domain_error"?(b.expected=w.args[0].args[0].id,b.found=w.args[0].args[1].toString()):b.type==="syntax_error"?w.args[1].indicator==="./2"?(b.expected=w.args[0].args[0].id,b.found=w.args[1].args[1].args[1].args[0],b.found=b.found.id==="token_not_found"?b.found.id:b.found.args[0].id,b.line=w.args[1].args[0].args[0].value,b.column=w.args[1].args[1].args[0].args[0].value):b.thrown=w.args[1].id:b.type==="permission_error"?(b.found=w.args[0].args[2].toString(),b.permission_operation=w.args[0].args[0].id,b.permission_type=w.args[0].args[1].id):b.type==="evaluation_error"?b.evaluation_type=w.args[0].args[0].id:b.type==="representation_error"?b.representation=w.args[0].args[0].id:b.type==="existence_error"&&(b.existence=w.args[0].args[1].toString(),b.existence_type=w.args[0].args[0].id),b},create:function(w){return new x.type.Session(w)}};typeof tc<"u"?tc.exports=x:window.pl=x})()});function hve(t,e,r){t.prepend(r.map(s=>new gl.default.type.State(e.goal.replace(s),e.substitution,e)))}function y9(t){let e=dve.get(t.session);if(e==null)throw new Error("Assertion failed: A project should have been registered for the active session");return e}function mve(t,e){dve.set(t,e),t.consult(`:- use_module(library(${gDt.id})).`)}var E9,gl,gve,J0,pDt,hDt,dve,gDt,yve=It(()=>{Ve();E9=et(AS()),gl=et(m9()),gve=et(ye("vm")),{is_atom:J0,is_variable:pDt,is_instantiated_list:hDt}=gl.default.type;dve=new WeakMap;gDt=new gl.default.type.Module("constraints",{"project_workspaces_by_descriptor/3":(t,e,r)=>{let[s,a,n]=r.args;if(!J0(s)||!J0(a)){t.throw_error(gl.default.error.instantiation(r.indicator));return}let c=q.parseIdent(s.id),f=q.makeDescriptor(c,a.id),h=y9(t).tryWorkspaceByDescriptor(f);pDt(n)&&h!==null&&hve(t,e,[new gl.default.type.Term("=",[n,new gl.default.type.Term(String(h.relativeCwd))])]),J0(n)&&h!==null&&h.relativeCwd===n.id&&t.success(e)},"workspace_field/3":(t,e,r)=>{let[s,a,n]=r.args;if(!J0(s)||!J0(a)){t.throw_error(gl.default.error.instantiation(r.indicator));return}let f=y9(t).tryWorkspaceByCwd(s.id);if(f==null)return;let p=(0,E9.default)(f.manifest.raw,a.id);typeof p>"u"||hve(t,e,[new gl.default.type.Term("=",[n,new gl.default.type.Term(typeof p=="object"?JSON.stringify(p):p)])])},"workspace_field_test/3":(t,e,r)=>{let[s,a,n]=r.args;t.prepend([new gl.default.type.State(e.goal.replace(new gl.default.type.Term("workspace_field_test",[s,a,n,new gl.default.type.Term("[]",[])])),e.substitution,e)])},"workspace_field_test/4":(t,e,r)=>{let[s,a,n,c]=r.args;if(!J0(s)||!J0(a)||!J0(n)||!hDt(c)){t.throw_error(gl.default.error.instantiation(r.indicator));return}let p=y9(t).tryWorkspaceByCwd(s.id);if(p==null)return;let h=(0,E9.default)(p.manifest.raw,a.id);if(typeof h>"u")return;let E={$$:h};for(let[S,P]of c.toJavaScript().entries())E[`$${S}`]=P;gve.default.runInNewContext(n.id,E)&&t.success(e)}},["project_workspaces_by_descriptor/3","workspace_field/3","workspace_field_test/3","workspace_field_test/4"])});var BS={};Vt(BS,{Constraints:()=>C9,DependencyType:()=>wve});function yo(t){if(t instanceof ZC.default.type.Num)return t.value;if(t instanceof ZC.default.type.Term)switch(t.indicator){case"throw/1":return yo(t.args[0]);case"error/1":return yo(t.args[0]);case"error/2":if(t.args[0]instanceof ZC.default.type.Term&&t.args[0].indicator==="syntax_error/1")return Object.assign(yo(t.args[0]),...yo(t.args[1]));{let e=yo(t.args[0]);return e.message+=` (in ${yo(t.args[1])})`,e}case"syntax_error/1":return new Yt(43,`Syntax error: ${yo(t.args[0])}`);case"existence_error/2":return new Yt(44,`Existence error: ${yo(t.args[0])} ${yo(t.args[1])} not found`);case"instantiation_error/0":return new Yt(75,"Instantiation error: an argument is variable when an instantiated argument was expected");case"line/1":return{line:yo(t.args[0])};case"column/1":return{column:yo(t.args[0])};case"found/1":return{found:yo(t.args[0])};case"./2":return[yo(t.args[0])].concat(yo(t.args[1]));case"//2":return`${yo(t.args[0])}/${yo(t.args[1])}`;default:return t.id}throw`couldn't pretty print because of unsupported node ${t}`}function Ive(t){let e;try{e=yo(t)}catch(r){throw typeof r=="string"?new Yt(42,`Unknown error: ${t} (note: ${r})`):r}return typeof e.line<"u"&&typeof e.column<"u"&&(e.message+=` at line ${e.line}, column ${e.column}`),e}function xm(t){return t.id==="null"?null:`${t.toJavaScript()}`}function dDt(t){if(t.id==="null")return null;{let e=t.toJavaScript();if(typeof e!="string")return JSON.stringify(e);try{return JSON.stringify(JSON.parse(e))}catch{return JSON.stringify(e)}}}function z0(t){return typeof t=="string"?`'${t}'`:"[]"}var Cve,ZC,wve,Eve,I9,C9,vS=It(()=>{Ve();Ve();bt();Cve=et(XBe()),ZC=et(m9());IS();yve();(0,Cve.default)(ZC.default);wve=(s=>(s.Dependencies="dependencies",s.DevDependencies="devDependencies",s.PeerDependencies="peerDependencies",s))(wve||{}),Eve=["dependencies","devDependencies","peerDependencies"];I9=class{constructor(e,r){let s=1e3*e.workspaces.length;this.session=ZC.default.create(s),mve(this.session,e),this.session.consult(":- use_module(library(lists))."),this.session.consult(r)}fetchNextAnswer(){return new Promise(e=>{this.session.answer(r=>{e(r)})})}async*makeQuery(e){let r=this.session.query(e);if(r!==!0)throw Ive(r);for(;;){let s=await this.fetchNextAnswer();if(s===null)throw new Yt(79,"Resolution limit exceeded");if(!s)break;if(s.id==="throw")throw Ive(s);yield s}}};C9=class t{constructor(e){this.source="";this.project=e;let r=e.configuration.get("constraintsPath");le.existsSync(r)&&(this.source=le.readFileSync(r,"utf8"))}static async find(e){return new t(e)}getProjectDatabase(){let e="";for(let r of Eve)e+=`dependency_type(${r}). +`;for(let r of this.project.workspacesByCwd.values()){let s=r.relativeCwd;e+=`workspace(${z0(s)}). +`,e+=`workspace_ident(${z0(s)}, ${z0(q.stringifyIdent(r.anchoredLocator))}). +`,e+=`workspace_version(${z0(s)}, ${z0(r.manifest.version)}). +`;for(let a of Eve)for(let n of r.manifest[a].values())e+=`workspace_has_dependency(${z0(s)}, ${z0(q.stringifyIdent(n))}, ${z0(n.range)}, ${a}). +`}return e+=`workspace(_) :- false. +`,e+=`workspace_ident(_, _) :- false. +`,e+=`workspace_version(_, _) :- false. +`,e+=`workspace_has_dependency(_, _, _, _) :- false. +`,e}getDeclarations(){let e="";return e+=`gen_enforced_dependency(_, _, _, _) :- false. +`,e+=`gen_enforced_field(_, _, _) :- false. +`,e}get fullSource(){return`${this.getProjectDatabase()} +${this.source} +${this.getDeclarations()}`}createSession(){return new I9(this.project,this.fullSource)}async processClassic(){let e=this.createSession();return{enforcedDependencies:await this.genEnforcedDependencies(e),enforcedFields:await this.genEnforcedFields(e)}}async process(){let{enforcedDependencies:e,enforcedFields:r}=await this.processClassic(),s=new Map;for(let{workspace:a,dependencyIdent:n,dependencyRange:c,dependencyType:f}of e){let p=ES([f,q.stringifyIdent(n)]),h=je.getMapWithDefault(s,a.cwd);je.getMapWithDefault(h,p).set(c??void 0,new Set)}for(let{workspace:a,fieldPath:n,fieldValue:c}of r){let f=ES(n),p=je.getMapWithDefault(s,a.cwd);je.getMapWithDefault(p,f).set(JSON.parse(c)??void 0,new Set)}return{manifestUpdates:s,reportedErrors:new Map}}async genEnforcedDependencies(e){let r=[];for await(let s of e.makeQuery("workspace(WorkspaceCwd), dependency_type(DependencyType), gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType).")){let a=K.resolve(this.project.cwd,xm(s.links.WorkspaceCwd)),n=xm(s.links.DependencyIdent),c=xm(s.links.DependencyRange),f=xm(s.links.DependencyType);if(a===null||n===null)throw new Error("Invalid rule");let p=this.project.getWorkspaceByCwd(a),h=q.parseIdent(n);r.push({workspace:p,dependencyIdent:h,dependencyRange:c,dependencyType:f})}return je.sortMap(r,[({dependencyRange:s})=>s!==null?"0":"1",({workspace:s})=>q.stringifyIdent(s.anchoredLocator),({dependencyIdent:s})=>q.stringifyIdent(s)])}async genEnforcedFields(e){let r=[];for await(let s of e.makeQuery("workspace(WorkspaceCwd), gen_enforced_field(WorkspaceCwd, FieldPath, FieldValue).")){let a=K.resolve(this.project.cwd,xm(s.links.WorkspaceCwd)),n=xm(s.links.FieldPath),c=dDt(s.links.FieldValue);if(a===null||n===null)throw new Error("Invalid rule");let f=this.project.getWorkspaceByCwd(a);r.push({workspace:f,fieldPath:n,fieldValue:c})}return je.sortMap(r,[({workspace:s})=>q.stringifyIdent(s.anchoredLocator),({fieldPath:s})=>s])}async*query(e){let r=this.createSession();for await(let s of r.makeQuery(e)){let a={};for(let[n,c]of Object.entries(s.links))n!=="_"&&(a[n]=xm(c));yield a}}}});var Qve=L(gF=>{"use strict";Object.defineProperty(gF,"__esModule",{value:!0});function US(t){let e=[...t.caches],r=e.shift();return r===void 0?kve():{get(s,a,n={miss:()=>Promise.resolve()}){return r.get(s,a,n).catch(()=>US({caches:e}).get(s,a,n))},set(s,a){return r.set(s,a).catch(()=>US({caches:e}).set(s,a))},delete(s){return r.delete(s).catch(()=>US({caches:e}).delete(s))},clear(){return r.clear().catch(()=>US({caches:e}).clear())}}}function kve(){return{get(t,e,r={miss:()=>Promise.resolve()}){return e().then(a=>Promise.all([a,r.miss(a)])).then(([a])=>a)},set(t,e){return Promise.resolve(e)},delete(t){return Promise.resolve()},clear(){return Promise.resolve()}}}gF.createFallbackableCache=US;gF.createNullCache=kve});var Rve=L((ihr,Tve)=>{Tve.exports=Qve()});var Fve=L(N9=>{"use strict";Object.defineProperty(N9,"__esModule",{value:!0});function NDt(t={serializable:!0}){let e={};return{get(r,s,a={miss:()=>Promise.resolve()}){let n=JSON.stringify(r);if(n in e)return Promise.resolve(t.serializable?JSON.parse(e[n]):e[n]);let c=s(),f=a&&a.miss||(()=>Promise.resolve());return c.then(p=>f(p)).then(()=>c)},set(r,s){return e[JSON.stringify(r)]=t.serializable?JSON.stringify(s):s,Promise.resolve(s)},delete(r){return delete e[JSON.stringify(r)],Promise.resolve()},clear(){return e={},Promise.resolve()}}}N9.createInMemoryCache=NDt});var Ove=L((ohr,Nve)=>{Nve.exports=Fve()});var Mve=L(ef=>{"use strict";Object.defineProperty(ef,"__esModule",{value:!0});function ODt(t,e,r){let s={"x-algolia-api-key":r,"x-algolia-application-id":e};return{headers(){return t===O9.WithinHeaders?s:{}},queryParameters(){return t===O9.WithinQueryParameters?s:{}}}}function LDt(t){let e=0,r=()=>(e++,new Promise(s=>{setTimeout(()=>{s(t(r))},Math.min(100*e,1e3))}));return t(r)}function Lve(t,e=(r,s)=>Promise.resolve()){return Object.assign(t,{wait(r){return Lve(t.then(s=>Promise.all([e(s,r),s])).then(s=>s[1]))}})}function MDt(t){let e=t.length-1;for(e;e>0;e--){let r=Math.floor(Math.random()*(e+1)),s=t[e];t[e]=t[r],t[r]=s}return t}function _Dt(t,e){return e&&Object.keys(e).forEach(r=>{t[r]=e[r](t)}),t}function UDt(t,...e){let r=0;return t.replace(/%s/g,()=>encodeURIComponent(e[r++]))}var HDt="4.22.1",jDt=t=>()=>t.transporter.requester.destroy(),O9={WithinQueryParameters:0,WithinHeaders:1};ef.AuthMode=O9;ef.addMethods=_Dt;ef.createAuth=ODt;ef.createRetryablePromise=LDt;ef.createWaitablePromise=Lve;ef.destroy=jDt;ef.encode=UDt;ef.shuffle=MDt;ef.version=HDt});var HS=L((lhr,_ve)=>{_ve.exports=Mve()});var Uve=L(L9=>{"use strict";Object.defineProperty(L9,"__esModule",{value:!0});var qDt={Delete:"DELETE",Get:"GET",Post:"POST",Put:"PUT"};L9.MethodEnum=qDt});var jS=L((uhr,Hve)=>{Hve.exports=Uve()});var rSe=L(Yi=>{"use strict";Object.defineProperty(Yi,"__esModule",{value:!0});var qve=jS();function M9(t,e){let r=t||{},s=r.data||{};return Object.keys(r).forEach(a=>{["timeout","headers","queryParameters","data","cacheable"].indexOf(a)===-1&&(s[a]=r[a])}),{data:Object.entries(s).length>0?s:void 0,timeout:r.timeout||e,headers:r.headers||{},queryParameters:r.queryParameters||{},cacheable:r.cacheable}}var qS={Read:1,Write:2,Any:3},aw={Up:1,Down:2,Timeouted:3},Gve=2*60*1e3;function U9(t,e=aw.Up){return{...t,status:e,lastUpdate:Date.now()}}function Wve(t){return t.status===aw.Up||Date.now()-t.lastUpdate>Gve}function Yve(t){return t.status===aw.Timeouted&&Date.now()-t.lastUpdate<=Gve}function H9(t){return typeof t=="string"?{protocol:"https",url:t,accept:qS.Any}:{protocol:t.protocol||"https",url:t.url,accept:t.accept||qS.Any}}function GDt(t,e){return Promise.all(e.map(r=>t.get(r,()=>Promise.resolve(U9(r))))).then(r=>{let s=r.filter(f=>Wve(f)),a=r.filter(f=>Yve(f)),n=[...s,...a],c=n.length>0?n.map(f=>H9(f)):e;return{getTimeout(f,p){return(a.length===0&&f===0?1:a.length+3+f)*p},statelessHosts:c}})}var WDt=({isTimedOut:t,status:e})=>!t&&~~e===0,YDt=t=>{let e=t.status;return t.isTimedOut||WDt(t)||~~(e/100)!==2&&~~(e/100)!==4},VDt=({status:t})=>~~(t/100)===2,KDt=(t,e)=>YDt(t)?e.onRetry(t):VDt(t)?e.onSuccess(t):e.onFail(t);function jve(t,e,r,s){let a=[],n=Zve(r,s),c=Xve(t,s),f=r.method,p=r.method!==qve.MethodEnum.Get?{}:{...r.data,...s.data},h={"x-algolia-agent":t.userAgent.value,...t.queryParameters,...p,...s.queryParameters},E=0,C=(S,P)=>{let I=S.pop();if(I===void 0)throw tSe(_9(a));let R={data:n,headers:c,method:f,url:Jve(I,r.path,h),connectTimeout:P(E,t.timeouts.connect),responseTimeout:P(E,s.timeout)},N=W=>{let te={request:R,response:W,host:I,triesLeft:S.length};return a.push(te),te},U={onSuccess:W=>Vve(W),onRetry(W){let te=N(W);return W.isTimedOut&&E++,Promise.all([t.logger.info("Retryable failure",j9(te)),t.hostsCache.set(I,U9(I,W.isTimedOut?aw.Timeouted:aw.Down))]).then(()=>C(S,P))},onFail(W){throw N(W),Kve(W,_9(a))}};return t.requester.send(R).then(W=>KDt(W,U))};return GDt(t.hostsCache,e).then(S=>C([...S.statelessHosts].reverse(),S.getTimeout))}function JDt(t){let{hostsCache:e,logger:r,requester:s,requestsCache:a,responsesCache:n,timeouts:c,userAgent:f,hosts:p,queryParameters:h,headers:E}=t,C={hostsCache:e,logger:r,requester:s,requestsCache:a,responsesCache:n,timeouts:c,userAgent:f,headers:E,queryParameters:h,hosts:p.map(S=>H9(S)),read(S,P){let I=M9(P,C.timeouts.read),R=()=>jve(C,C.hosts.filter(W=>(W.accept&qS.Read)!==0),S,I);if((I.cacheable!==void 0?I.cacheable:S.cacheable)!==!0)return R();let U={request:S,mappedRequestOptions:I,transporter:{queryParameters:C.queryParameters,headers:C.headers}};return C.responsesCache.get(U,()=>C.requestsCache.get(U,()=>C.requestsCache.set(U,R()).then(W=>Promise.all([C.requestsCache.delete(U),W]),W=>Promise.all([C.requestsCache.delete(U),Promise.reject(W)])).then(([W,te])=>te)),{miss:W=>C.responsesCache.set(U,W)})},write(S,P){return jve(C,C.hosts.filter(I=>(I.accept&qS.Write)!==0),S,M9(P,C.timeouts.write))}};return C}function zDt(t){let e={value:`Algolia for JavaScript (${t})`,add(r){let s=`; ${r.segment}${r.version!==void 0?` (${r.version})`:""}`;return e.value.indexOf(s)===-1&&(e.value=`${e.value}${s}`),e}};return e}function Vve(t){try{return JSON.parse(t.content)}catch(e){throw eSe(e.message,t)}}function Kve({content:t,status:e},r){let s=t;try{s=JSON.parse(t).message}catch{}return $ve(s,e,r)}function ZDt(t,...e){let r=0;return t.replace(/%s/g,()=>encodeURIComponent(e[r++]))}function Jve(t,e,r){let s=zve(r),a=`${t.protocol}://${t.url}/${e.charAt(0)==="/"?e.substr(1):e}`;return s.length&&(a+=`?${s}`),a}function zve(t){let e=r=>Object.prototype.toString.call(r)==="[object Object]"||Object.prototype.toString.call(r)==="[object Array]";return Object.keys(t).map(r=>ZDt("%s=%s",r,e(t[r])?JSON.stringify(t[r]):t[r])).join("&")}function Zve(t,e){if(t.method===qve.MethodEnum.Get||t.data===void 0&&e.data===void 0)return;let r=Array.isArray(t.data)?t.data:{...t.data,...e.data};return JSON.stringify(r)}function Xve(t,e){let r={...t.headers,...e.headers},s={};return Object.keys(r).forEach(a=>{let n=r[a];s[a.toLowerCase()]=n}),s}function _9(t){return t.map(e=>j9(e))}function j9(t){let e=t.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return{...t,request:{...t.request,headers:{...t.request.headers,...e}}}}function $ve(t,e,r){return{name:"ApiError",message:t,status:e,transporterStackTrace:r}}function eSe(t,e){return{name:"DeserializationError",message:t,response:e}}function tSe(t){return{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:t}}Yi.CallEnum=qS;Yi.HostStatusEnum=aw;Yi.createApiError=$ve;Yi.createDeserializationError=eSe;Yi.createMappedRequestOptions=M9;Yi.createRetryError=tSe;Yi.createStatefulHost=U9;Yi.createStatelessHost=H9;Yi.createTransporter=JDt;Yi.createUserAgent=zDt;Yi.deserializeFailure=Kve;Yi.deserializeSuccess=Vve;Yi.isStatefulHostTimeouted=Yve;Yi.isStatefulHostUp=Wve;Yi.serializeData=Zve;Yi.serializeHeaders=Xve;Yi.serializeQueryParameters=zve;Yi.serializeUrl=Jve;Yi.stackFrameWithoutCredentials=j9;Yi.stackTraceWithoutCredentials=_9});var GS=L((Ahr,nSe)=>{nSe.exports=rSe()});var iSe=L(X0=>{"use strict";Object.defineProperty(X0,"__esModule",{value:!0});var lw=HS(),XDt=GS(),WS=jS(),$Dt=t=>{let e=t.region||"us",r=lw.createAuth(lw.AuthMode.WithinHeaders,t.appId,t.apiKey),s=XDt.createTransporter({hosts:[{url:`analytics.${e}.algolia.com`}],...t,headers:{...r.headers(),"content-type":"application/json",...t.headers},queryParameters:{...r.queryParameters(),...t.queryParameters}}),a=t.appId;return lw.addMethods({appId:a,transporter:s},t.methods)},ebt=t=>(e,r)=>t.transporter.write({method:WS.MethodEnum.Post,path:"2/abtests",data:e},r),tbt=t=>(e,r)=>t.transporter.write({method:WS.MethodEnum.Delete,path:lw.encode("2/abtests/%s",e)},r),rbt=t=>(e,r)=>t.transporter.read({method:WS.MethodEnum.Get,path:lw.encode("2/abtests/%s",e)},r),nbt=t=>e=>t.transporter.read({method:WS.MethodEnum.Get,path:"2/abtests"},e),ibt=t=>(e,r)=>t.transporter.write({method:WS.MethodEnum.Post,path:lw.encode("2/abtests/%s/stop",e)},r);X0.addABTest=ebt;X0.createAnalyticsClient=$Dt;X0.deleteABTest=tbt;X0.getABTest=rbt;X0.getABTests=nbt;X0.stopABTest=ibt});var oSe=L((hhr,sSe)=>{sSe.exports=iSe()});var lSe=L(YS=>{"use strict";Object.defineProperty(YS,"__esModule",{value:!0});var q9=HS(),sbt=GS(),aSe=jS(),obt=t=>{let e=t.region||"us",r=q9.createAuth(q9.AuthMode.WithinHeaders,t.appId,t.apiKey),s=sbt.createTransporter({hosts:[{url:`personalization.${e}.algolia.com`}],...t,headers:{...r.headers(),"content-type":"application/json",...t.headers},queryParameters:{...r.queryParameters(),...t.queryParameters}});return q9.addMethods({appId:t.appId,transporter:s},t.methods)},abt=t=>e=>t.transporter.read({method:aSe.MethodEnum.Get,path:"1/strategies/personalization"},e),lbt=t=>(e,r)=>t.transporter.write({method:aSe.MethodEnum.Post,path:"1/strategies/personalization",data:e},r);YS.createPersonalizationClient=obt;YS.getPersonalizationStrategy=abt;YS.setPersonalizationStrategy=lbt});var uSe=L((dhr,cSe)=>{cSe.exports=lSe()});var vSe=L(Ft=>{"use strict";Object.defineProperty(Ft,"__esModule",{value:!0});var Kt=HS(),dl=GS(),br=jS(),cbt=ye("crypto");function dF(t){let e=r=>t.request(r).then(s=>{if(t.batch!==void 0&&t.batch(s.hits),!t.shouldStop(s))return s.cursor?e({cursor:s.cursor}):e({page:(r.page||0)+1})});return e({})}var ubt=t=>{let e=t.appId,r=Kt.createAuth(t.authMode!==void 0?t.authMode:Kt.AuthMode.WithinHeaders,e,t.apiKey),s=dl.createTransporter({hosts:[{url:`${e}-dsn.algolia.net`,accept:dl.CallEnum.Read},{url:`${e}.algolia.net`,accept:dl.CallEnum.Write}].concat(Kt.shuffle([{url:`${e}-1.algolianet.com`},{url:`${e}-2.algolianet.com`},{url:`${e}-3.algolianet.com`}])),...t,headers:{...r.headers(),"content-type":"application/x-www-form-urlencoded",...t.headers},queryParameters:{...r.queryParameters(),...t.queryParameters}}),a={transporter:s,appId:e,addAlgoliaAgent(n,c){s.userAgent.add({segment:n,version:c})},clearCache(){return Promise.all([s.requestsCache.clear(),s.responsesCache.clear()]).then(()=>{})}};return Kt.addMethods(a,t.methods)};function fSe(){return{name:"MissingObjectIDError",message:"All objects must have an unique objectID (like a primary key) to be valid. Algolia is also able to generate objectIDs automatically but *it's not recommended*. To do it, use the `{'autoGenerateObjectIDIfNotExist': true}` option."}}function ASe(){return{name:"ObjectNotFoundError",message:"Object not found."}}function pSe(){return{name:"ValidUntilNotFoundError",message:"ValidUntil not found in given secured api key."}}var fbt=t=>(e,r)=>{let{queryParameters:s,...a}=r||{},n={acl:e,...s!==void 0?{queryParameters:s}:{}},c=(f,p)=>Kt.createRetryablePromise(h=>VS(t)(f.key,p).catch(E=>{if(E.status!==404)throw E;return h()}));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:"1/keys",data:n},a),c)},Abt=t=>(e,r,s)=>{let a=dl.createMappedRequestOptions(s);return a.queryParameters["X-Algolia-User-ID"]=e,t.transporter.write({method:br.MethodEnum.Post,path:"1/clusters/mapping",data:{cluster:r}},a)},pbt=t=>(e,r,s)=>t.transporter.write({method:br.MethodEnum.Post,path:"1/clusters/mapping/batch",data:{users:e,cluster:r}},s),hbt=t=>(e,r)=>Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("/1/dictionaries/%s/batch",e),data:{clearExistingDictionaryEntries:!0,requests:{action:"addEntry",body:[]}}},r),(s,a)=>cw(t)(s.taskID,a)),mF=t=>(e,r,s)=>{let a=(n,c)=>KS(t)(e,{methods:{waitTask:gs}}).waitTask(n.taskID,c);return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/operation",e),data:{operation:"copy",destination:r}},s),a)},gbt=t=>(e,r,s)=>mF(t)(e,r,{...s,scope:[EF.Rules]}),dbt=t=>(e,r,s)=>mF(t)(e,r,{...s,scope:[EF.Settings]}),mbt=t=>(e,r,s)=>mF(t)(e,r,{...s,scope:[EF.Synonyms]}),ybt=t=>(e,r)=>e.method===br.MethodEnum.Get?t.transporter.read(e,r):t.transporter.write(e,r),Ebt=t=>(e,r)=>{let s=(a,n)=>Kt.createRetryablePromise(c=>VS(t)(e,n).then(c).catch(f=>{if(f.status!==404)throw f}));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Delete,path:Kt.encode("1/keys/%s",e)},r),s)},Ibt=t=>(e,r,s)=>{let a=r.map(n=>({action:"deleteEntry",body:{objectID:n}}));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("/1/dictionaries/%s/batch",e),data:{clearExistingDictionaryEntries:!1,requests:a}},s),(n,c)=>cw(t)(n.taskID,c))},Cbt=()=>(t,e)=>{let r=dl.serializeQueryParameters(e),s=cbt.createHmac("sha256",t).update(r).digest("hex");return Buffer.from(s+r).toString("base64")},VS=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/keys/%s",e)},r),hSe=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/task/%s",e.toString())},r),wbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"/1/dictionaries/*/settings"},e),Bbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"1/logs"},e),vbt=()=>t=>{let e=Buffer.from(t,"base64").toString("ascii"),r=/validUntil=(\d+)/,s=e.match(r);if(s===null)throw pSe();return parseInt(s[1],10)-Math.round(new Date().getTime()/1e3)},Sbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"1/clusters/mapping/top"},e),Dbt=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/clusters/mapping/%s",e)},r),bbt=t=>e=>{let{retrieveMappings:r,...s}=e||{};return r===!0&&(s.getClusters=!0),t.transporter.read({method:br.MethodEnum.Get,path:"1/clusters/mapping/pending"},s)},KS=t=>(e,r={})=>{let s={transporter:t.transporter,appId:t.appId,indexName:e};return Kt.addMethods(s,r.methods)},Pbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"1/keys"},e),xbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"1/clusters"},e),kbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"1/indexes"},e),Qbt=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:"1/clusters/mapping"},e),Tbt=t=>(e,r,s)=>{let a=(n,c)=>KS(t)(e,{methods:{waitTask:gs}}).waitTask(n.taskID,c);return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/operation",e),data:{operation:"move",destination:r}},s),a)},Rbt=t=>(e,r)=>{let s=(a,n)=>Promise.all(Object.keys(a.taskID).map(c=>KS(t)(c,{methods:{waitTask:gs}}).waitTask(a.taskID[c],n)));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:"1/indexes/*/batch",data:{requests:e}},r),s)},Fbt=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Post,path:"1/indexes/*/objects",data:{requests:e}},r),Nbt=t=>(e,r)=>{let s=e.map(a=>({...a,params:dl.serializeQueryParameters(a.params||{})}));return t.transporter.read({method:br.MethodEnum.Post,path:"1/indexes/*/queries",data:{requests:s},cacheable:!0},r)},Obt=t=>(e,r)=>Promise.all(e.map(s=>{let{facetName:a,facetQuery:n,...c}=s.params;return KS(t)(s.indexName,{methods:{searchForFacetValues:CSe}}).searchForFacetValues(a,n,{...r,...c})})),Lbt=t=>(e,r)=>{let s=dl.createMappedRequestOptions(r);return s.queryParameters["X-Algolia-User-ID"]=e,t.transporter.write({method:br.MethodEnum.Delete,path:"1/clusters/mapping"},s)},Mbt=t=>(e,r,s)=>{let a=r.map(n=>({action:"addEntry",body:n}));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("/1/dictionaries/%s/batch",e),data:{clearExistingDictionaryEntries:!0,requests:a}},s),(n,c)=>cw(t)(n.taskID,c))},_bt=t=>(e,r)=>{let s=(a,n)=>Kt.createRetryablePromise(c=>VS(t)(e,n).catch(f=>{if(f.status!==404)throw f;return c()}));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/keys/%s/restore",e)},r),s)},Ubt=t=>(e,r,s)=>{let a=r.map(n=>({action:"addEntry",body:n}));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("/1/dictionaries/%s/batch",e),data:{clearExistingDictionaryEntries:!1,requests:a}},s),(n,c)=>cw(t)(n.taskID,c))},Hbt=t=>(e,r,s)=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("/1/dictionaries/%s/search",e),data:{query:r},cacheable:!0},s),jbt=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Post,path:"1/clusters/mapping/search",data:{query:e}},r),qbt=t=>(e,r)=>Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Put,path:"/1/dictionaries/*/settings",data:e},r),(s,a)=>cw(t)(s.taskID,a)),Gbt=t=>(e,r)=>{let s=Object.assign({},r),{queryParameters:a,...n}=r||{},c=a?{queryParameters:a}:{},f=["acl","indexes","referers","restrictSources","queryParameters","description","maxQueriesPerIPPerHour","maxHitsPerQuery"],p=E=>Object.keys(s).filter(C=>f.indexOf(C)!==-1).every(C=>{if(Array.isArray(E[C])&&Array.isArray(s[C])){let S=E[C];return S.length===s[C].length&&S.every((P,I)=>P===s[C][I])}else return E[C]===s[C]}),h=(E,C)=>Kt.createRetryablePromise(S=>VS(t)(e,C).then(P=>p(P)?Promise.resolve():S()));return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Put,path:Kt.encode("1/keys/%s",e),data:c},n),h)},cw=t=>(e,r)=>Kt.createRetryablePromise(s=>hSe(t)(e,r).then(a=>a.status!=="published"?s():void 0)),gSe=t=>(e,r)=>{let s=(a,n)=>gs(t)(a.taskID,n);return Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/batch",t.indexName),data:{requests:e}},r),s)},Wbt=t=>e=>dF({shouldStop:r=>r.cursor===void 0,...e,request:r=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/browse",t.indexName),data:r},e)}),Ybt=t=>e=>{let r={hitsPerPage:1e3,...e};return dF({shouldStop:s=>s.hits.length({...a,hits:a.hits.map(n=>(delete n._highlightResult,n))}))}})},Vbt=t=>e=>{let r={hitsPerPage:1e3,...e};return dF({shouldStop:s=>s.hits.length({...a,hits:a.hits.map(n=>(delete n._highlightResult,n))}))}})},yF=t=>(e,r,s)=>{let{batchSize:a,...n}=s||{},c={taskIDs:[],objectIDs:[]},f=(p=0)=>{let h=[],E;for(E=p;E({action:r,body:C})),n).then(C=>(c.objectIDs=c.objectIDs.concat(C.objectIDs),c.taskIDs.push(C.taskID),E++,f(E)))};return Kt.createWaitablePromise(f(),(p,h)=>Promise.all(p.taskIDs.map(E=>gs(t)(E,h))))},Kbt=t=>e=>Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/clear",t.indexName)},e),(r,s)=>gs(t)(r.taskID,s)),Jbt=t=>e=>{let{forwardToReplicas:r,...s}=e||{},a=dl.createMappedRequestOptions(s);return r&&(a.queryParameters.forwardToReplicas=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/rules/clear",t.indexName)},a),(n,c)=>gs(t)(n.taskID,c))},zbt=t=>e=>{let{forwardToReplicas:r,...s}=e||{},a=dl.createMappedRequestOptions(s);return r&&(a.queryParameters.forwardToReplicas=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/synonyms/clear",t.indexName)},a),(n,c)=>gs(t)(n.taskID,c))},Zbt=t=>(e,r)=>Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/deleteByQuery",t.indexName),data:e},r),(s,a)=>gs(t)(s.taskID,a)),Xbt=t=>e=>Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Delete,path:Kt.encode("1/indexes/%s",t.indexName)},e),(r,s)=>gs(t)(r.taskID,s)),$bt=t=>(e,r)=>Kt.createWaitablePromise(dSe(t)([e],r).then(s=>({taskID:s.taskIDs[0]})),(s,a)=>gs(t)(s.taskID,a)),dSe=t=>(e,r)=>{let s=e.map(a=>({objectID:a}));return yF(t)(s,Qm.DeleteObject,r)},ePt=t=>(e,r)=>{let{forwardToReplicas:s,...a}=r||{},n=dl.createMappedRequestOptions(a);return s&&(n.queryParameters.forwardToReplicas=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Delete,path:Kt.encode("1/indexes/%s/rules/%s",t.indexName,e)},n),(c,f)=>gs(t)(c.taskID,f))},tPt=t=>(e,r)=>{let{forwardToReplicas:s,...a}=r||{},n=dl.createMappedRequestOptions(a);return s&&(n.queryParameters.forwardToReplicas=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Delete,path:Kt.encode("1/indexes/%s/synonyms/%s",t.indexName,e)},n),(c,f)=>gs(t)(c.taskID,f))},rPt=t=>e=>mSe(t)(e).then(()=>!0).catch(r=>{if(r.status!==404)throw r;return!1}),nPt=t=>(e,r,s)=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("1/answers/%s/prediction",t.indexName),data:{query:e,queryLanguages:r},cacheable:!0},s),iPt=t=>(e,r)=>{let{query:s,paginate:a,...n}=r||{},c=0,f=()=>ISe(t)(s||"",{...n,page:c}).then(p=>{for(let[h,E]of Object.entries(p.hits))if(e(E))return{object:E,position:parseInt(h,10),page:c};if(c++,a===!1||c>=p.nbPages)throw ASe();return f()});return f()},sPt=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/indexes/%s/%s",t.indexName,e)},r),oPt=()=>(t,e)=>{for(let[r,s]of Object.entries(t.hits))if(s.objectID===e)return parseInt(r,10);return-1},aPt=t=>(e,r)=>{let{attributesToRetrieve:s,...a}=r||{},n=e.map(c=>({indexName:t.indexName,objectID:c,...s?{attributesToRetrieve:s}:{}}));return t.transporter.read({method:br.MethodEnum.Post,path:"1/indexes/*/objects",data:{requests:n}},a)},lPt=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/indexes/%s/rules/%s",t.indexName,e)},r),mSe=t=>e=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/indexes/%s/settings",t.indexName),data:{getVersion:2}},e),cPt=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/indexes/%s/synonyms/%s",t.indexName,e)},r),ySe=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Get,path:Kt.encode("1/indexes/%s/task/%s",t.indexName,e.toString())},r),uPt=t=>(e,r)=>Kt.createWaitablePromise(ESe(t)([e],r).then(s=>({objectID:s.objectIDs[0],taskID:s.taskIDs[0]})),(s,a)=>gs(t)(s.taskID,a)),ESe=t=>(e,r)=>{let{createIfNotExists:s,...a}=r||{},n=s?Qm.PartialUpdateObject:Qm.PartialUpdateObjectNoCreate;return yF(t)(e,n,a)},fPt=t=>(e,r)=>{let{safe:s,autoGenerateObjectIDIfNotExist:a,batchSize:n,...c}=r||{},f=(I,R,N,U)=>Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/operation",I),data:{operation:N,destination:R}},U),(W,te)=>gs(t)(W.taskID,te)),p=Math.random().toString(36).substring(7),h=`${t.indexName}_tmp_${p}`,E=G9({appId:t.appId,transporter:t.transporter,indexName:h}),C=[],S=f(t.indexName,h,"copy",{...c,scope:["settings","synonyms","rules"]});C.push(S);let P=(s?S.wait(c):S).then(()=>{let I=E(e,{...c,autoGenerateObjectIDIfNotExist:a,batchSize:n});return C.push(I),s?I.wait(c):I}).then(()=>{let I=f(h,t.indexName,"move",c);return C.push(I),s?I.wait(c):I}).then(()=>Promise.all(C)).then(([I,R,N])=>({objectIDs:R.objectIDs,taskIDs:[I.taskID,...R.taskIDs,N.taskID]}));return Kt.createWaitablePromise(P,(I,R)=>Promise.all(C.map(N=>N.wait(R))))},APt=t=>(e,r)=>W9(t)(e,{...r,clearExistingRules:!0}),pPt=t=>(e,r)=>Y9(t)(e,{...r,clearExistingSynonyms:!0}),hPt=t=>(e,r)=>Kt.createWaitablePromise(G9(t)([e],r).then(s=>({objectID:s.objectIDs[0],taskID:s.taskIDs[0]})),(s,a)=>gs(t)(s.taskID,a)),G9=t=>(e,r)=>{let{autoGenerateObjectIDIfNotExist:s,...a}=r||{},n=s?Qm.AddObject:Qm.UpdateObject;if(n===Qm.UpdateObject){for(let c of e)if(c.objectID===void 0)return Kt.createWaitablePromise(Promise.reject(fSe()))}return yF(t)(e,n,a)},gPt=t=>(e,r)=>W9(t)([e],r),W9=t=>(e,r)=>{let{forwardToReplicas:s,clearExistingRules:a,...n}=r||{},c=dl.createMappedRequestOptions(n);return s&&(c.queryParameters.forwardToReplicas=1),a&&(c.queryParameters.clearExistingRules=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/rules/batch",t.indexName),data:e},c),(f,p)=>gs(t)(f.taskID,p))},dPt=t=>(e,r)=>Y9(t)([e],r),Y9=t=>(e,r)=>{let{forwardToReplicas:s,clearExistingSynonyms:a,replaceExistingSynonyms:n,...c}=r||{},f=dl.createMappedRequestOptions(c);return s&&(f.queryParameters.forwardToReplicas=1),(n||a)&&(f.queryParameters.replaceExistingSynonyms=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/synonyms/batch",t.indexName),data:e},f),(p,h)=>gs(t)(p.taskID,h))},ISe=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/query",t.indexName),data:{query:e},cacheable:!0},r),CSe=t=>(e,r,s)=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/facets/%s/query",t.indexName,e),data:{facetQuery:r},cacheable:!0},s),wSe=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/rules/search",t.indexName),data:{query:e}},r),BSe=t=>(e,r)=>t.transporter.read({method:br.MethodEnum.Post,path:Kt.encode("1/indexes/%s/synonyms/search",t.indexName),data:{query:e}},r),mPt=t=>(e,r)=>{let{forwardToReplicas:s,...a}=r||{},n=dl.createMappedRequestOptions(a);return s&&(n.queryParameters.forwardToReplicas=1),Kt.createWaitablePromise(t.transporter.write({method:br.MethodEnum.Put,path:Kt.encode("1/indexes/%s/settings",t.indexName),data:e},n),(c,f)=>gs(t)(c.taskID,f))},gs=t=>(e,r)=>Kt.createRetryablePromise(s=>ySe(t)(e,r).then(a=>a.status!=="published"?s():void 0)),yPt={AddObject:"addObject",Analytics:"analytics",Browser:"browse",DeleteIndex:"deleteIndex",DeleteObject:"deleteObject",EditSettings:"editSettings",Inference:"inference",ListIndexes:"listIndexes",Logs:"logs",Personalization:"personalization",Recommendation:"recommendation",Search:"search",SeeUnretrievableAttributes:"seeUnretrievableAttributes",Settings:"settings",Usage:"usage"},Qm={AddObject:"addObject",UpdateObject:"updateObject",PartialUpdateObject:"partialUpdateObject",PartialUpdateObjectNoCreate:"partialUpdateObjectNoCreate",DeleteObject:"deleteObject",DeleteIndex:"delete",ClearIndex:"clear"},EF={Settings:"settings",Synonyms:"synonyms",Rules:"rules"},EPt={None:"none",StopIfEnoughMatches:"stopIfEnoughMatches"},IPt={Synonym:"synonym",OneWaySynonym:"oneWaySynonym",AltCorrection1:"altCorrection1",AltCorrection2:"altCorrection2",Placeholder:"placeholder"};Ft.ApiKeyACLEnum=yPt;Ft.BatchActionEnum=Qm;Ft.ScopeEnum=EF;Ft.StrategyEnum=EPt;Ft.SynonymEnum=IPt;Ft.addApiKey=fbt;Ft.assignUserID=Abt;Ft.assignUserIDs=pbt;Ft.batch=gSe;Ft.browseObjects=Wbt;Ft.browseRules=Ybt;Ft.browseSynonyms=Vbt;Ft.chunkedBatch=yF;Ft.clearDictionaryEntries=hbt;Ft.clearObjects=Kbt;Ft.clearRules=Jbt;Ft.clearSynonyms=zbt;Ft.copyIndex=mF;Ft.copyRules=gbt;Ft.copySettings=dbt;Ft.copySynonyms=mbt;Ft.createBrowsablePromise=dF;Ft.createMissingObjectIDError=fSe;Ft.createObjectNotFoundError=ASe;Ft.createSearchClient=ubt;Ft.createValidUntilNotFoundError=pSe;Ft.customRequest=ybt;Ft.deleteApiKey=Ebt;Ft.deleteBy=Zbt;Ft.deleteDictionaryEntries=Ibt;Ft.deleteIndex=Xbt;Ft.deleteObject=$bt;Ft.deleteObjects=dSe;Ft.deleteRule=ePt;Ft.deleteSynonym=tPt;Ft.exists=rPt;Ft.findAnswers=nPt;Ft.findObject=iPt;Ft.generateSecuredApiKey=Cbt;Ft.getApiKey=VS;Ft.getAppTask=hSe;Ft.getDictionarySettings=wbt;Ft.getLogs=Bbt;Ft.getObject=sPt;Ft.getObjectPosition=oPt;Ft.getObjects=aPt;Ft.getRule=lPt;Ft.getSecuredApiKeyRemainingValidity=vbt;Ft.getSettings=mSe;Ft.getSynonym=cPt;Ft.getTask=ySe;Ft.getTopUserIDs=Sbt;Ft.getUserID=Dbt;Ft.hasPendingMappings=bbt;Ft.initIndex=KS;Ft.listApiKeys=Pbt;Ft.listClusters=xbt;Ft.listIndices=kbt;Ft.listUserIDs=Qbt;Ft.moveIndex=Tbt;Ft.multipleBatch=Rbt;Ft.multipleGetObjects=Fbt;Ft.multipleQueries=Nbt;Ft.multipleSearchForFacetValues=Obt;Ft.partialUpdateObject=uPt;Ft.partialUpdateObjects=ESe;Ft.removeUserID=Lbt;Ft.replaceAllObjects=fPt;Ft.replaceAllRules=APt;Ft.replaceAllSynonyms=pPt;Ft.replaceDictionaryEntries=Mbt;Ft.restoreApiKey=_bt;Ft.saveDictionaryEntries=Ubt;Ft.saveObject=hPt;Ft.saveObjects=G9;Ft.saveRule=gPt;Ft.saveRules=W9;Ft.saveSynonym=dPt;Ft.saveSynonyms=Y9;Ft.search=ISe;Ft.searchDictionaryEntries=Hbt;Ft.searchForFacetValues=CSe;Ft.searchRules=wSe;Ft.searchSynonyms=BSe;Ft.searchUserIDs=jbt;Ft.setDictionarySettings=qbt;Ft.setSettings=mPt;Ft.updateApiKey=Gbt;Ft.waitAppTask=cw;Ft.waitTask=gs});var DSe=L((yhr,SSe)=>{SSe.exports=vSe()});var bSe=L(IF=>{"use strict";Object.defineProperty(IF,"__esModule",{value:!0});function CPt(){return{debug(t,e){return Promise.resolve()},info(t,e){return Promise.resolve()},error(t,e){return Promise.resolve()}}}var wPt={Debug:1,Info:2,Error:3};IF.LogLevelEnum=wPt;IF.createNullLogger=CPt});var xSe=L((Ihr,PSe)=>{PSe.exports=bSe()});var RSe=L(V9=>{"use strict";Object.defineProperty(V9,"__esModule",{value:!0});var kSe=ye("http"),QSe=ye("https"),BPt=ye("url"),TSe={keepAlive:!0},vPt=new kSe.Agent(TSe),SPt=new QSe.Agent(TSe);function DPt({agent:t,httpAgent:e,httpsAgent:r,requesterOptions:s={}}={}){let a=e||t||vPt,n=r||t||SPt;return{send(c){return new Promise(f=>{let p=BPt.parse(c.url),h=p.query===null?p.pathname:`${p.pathname}?${p.query}`,E={...s,agent:p.protocol==="https:"?n:a,hostname:p.hostname,path:h,method:c.method,headers:{...s&&s.headers?s.headers:{},...c.headers},...p.port!==void 0?{port:p.port||""}:{}},C=(p.protocol==="https:"?QSe:kSe).request(E,R=>{let N=[];R.on("data",U=>{N=N.concat(U)}),R.on("end",()=>{clearTimeout(P),clearTimeout(I),f({status:R.statusCode||0,content:Buffer.concat(N).toString(),isTimedOut:!1})})}),S=(R,N)=>setTimeout(()=>{C.abort(),f({status:0,content:N,isTimedOut:!0})},R*1e3),P=S(c.connectTimeout,"Connection timeout"),I;C.on("error",R=>{clearTimeout(P),clearTimeout(I),f({status:0,content:R.message,isTimedOut:!1})}),C.once("response",()=>{clearTimeout(P),I=S(c.responseTimeout,"Socket timeout")}),c.data!==void 0&&C.write(c.data),C.end()})},destroy(){return a.destroy(),n.destroy(),Promise.resolve()}}}V9.createNodeHttpRequester=DPt});var NSe=L((whr,FSe)=>{FSe.exports=RSe()});var _Se=L((Bhr,MSe)=>{"use strict";var OSe=Rve(),bPt=Ove(),uw=oSe(),J9=HS(),K9=uSe(),jt=DSe(),PPt=xSe(),xPt=NSe(),kPt=GS();function LSe(t,e,r){let s={appId:t,apiKey:e,timeouts:{connect:2,read:5,write:30},requester:xPt.createNodeHttpRequester(),logger:PPt.createNullLogger(),responsesCache:OSe.createNullCache(),requestsCache:OSe.createNullCache(),hostsCache:bPt.createInMemoryCache(),userAgent:kPt.createUserAgent(J9.version).add({segment:"Node.js",version:process.versions.node})},a={...s,...r},n=()=>c=>K9.createPersonalizationClient({...s,...c,methods:{getPersonalizationStrategy:K9.getPersonalizationStrategy,setPersonalizationStrategy:K9.setPersonalizationStrategy}});return jt.createSearchClient({...a,methods:{search:jt.multipleQueries,searchForFacetValues:jt.multipleSearchForFacetValues,multipleBatch:jt.multipleBatch,multipleGetObjects:jt.multipleGetObjects,multipleQueries:jt.multipleQueries,copyIndex:jt.copyIndex,copySettings:jt.copySettings,copyRules:jt.copyRules,copySynonyms:jt.copySynonyms,moveIndex:jt.moveIndex,listIndices:jt.listIndices,getLogs:jt.getLogs,listClusters:jt.listClusters,multipleSearchForFacetValues:jt.multipleSearchForFacetValues,getApiKey:jt.getApiKey,addApiKey:jt.addApiKey,listApiKeys:jt.listApiKeys,updateApiKey:jt.updateApiKey,deleteApiKey:jt.deleteApiKey,restoreApiKey:jt.restoreApiKey,assignUserID:jt.assignUserID,assignUserIDs:jt.assignUserIDs,getUserID:jt.getUserID,searchUserIDs:jt.searchUserIDs,listUserIDs:jt.listUserIDs,getTopUserIDs:jt.getTopUserIDs,removeUserID:jt.removeUserID,hasPendingMappings:jt.hasPendingMappings,generateSecuredApiKey:jt.generateSecuredApiKey,getSecuredApiKeyRemainingValidity:jt.getSecuredApiKeyRemainingValidity,destroy:J9.destroy,clearDictionaryEntries:jt.clearDictionaryEntries,deleteDictionaryEntries:jt.deleteDictionaryEntries,getDictionarySettings:jt.getDictionarySettings,getAppTask:jt.getAppTask,replaceDictionaryEntries:jt.replaceDictionaryEntries,saveDictionaryEntries:jt.saveDictionaryEntries,searchDictionaryEntries:jt.searchDictionaryEntries,setDictionarySettings:jt.setDictionarySettings,waitAppTask:jt.waitAppTask,customRequest:jt.customRequest,initIndex:c=>f=>jt.initIndex(c)(f,{methods:{batch:jt.batch,delete:jt.deleteIndex,findAnswers:jt.findAnswers,getObject:jt.getObject,getObjects:jt.getObjects,saveObject:jt.saveObject,saveObjects:jt.saveObjects,search:jt.search,searchForFacetValues:jt.searchForFacetValues,waitTask:jt.waitTask,setSettings:jt.setSettings,getSettings:jt.getSettings,partialUpdateObject:jt.partialUpdateObject,partialUpdateObjects:jt.partialUpdateObjects,deleteObject:jt.deleteObject,deleteObjects:jt.deleteObjects,deleteBy:jt.deleteBy,clearObjects:jt.clearObjects,browseObjects:jt.browseObjects,getObjectPosition:jt.getObjectPosition,findObject:jt.findObject,exists:jt.exists,saveSynonym:jt.saveSynonym,saveSynonyms:jt.saveSynonyms,getSynonym:jt.getSynonym,searchSynonyms:jt.searchSynonyms,browseSynonyms:jt.browseSynonyms,deleteSynonym:jt.deleteSynonym,clearSynonyms:jt.clearSynonyms,replaceAllObjects:jt.replaceAllObjects,replaceAllSynonyms:jt.replaceAllSynonyms,searchRules:jt.searchRules,getRule:jt.getRule,deleteRule:jt.deleteRule,saveRule:jt.saveRule,saveRules:jt.saveRules,replaceAllRules:jt.replaceAllRules,browseRules:jt.browseRules,clearRules:jt.clearRules}}),initAnalytics:()=>c=>uw.createAnalyticsClient({...s,...c,methods:{addABTest:uw.addABTest,getABTest:uw.getABTest,getABTests:uw.getABTests,stopABTest:uw.stopABTest,deleteABTest:uw.deleteABTest}}),initPersonalization:n,initRecommendation:()=>c=>(a.logger.info("The `initRecommendation` method is deprecated. Use `initPersonalization` instead."),n()(c))}})}LSe.version=J9.version;MSe.exports=LSe});var Z9=L((vhr,z9)=>{var USe=_Se();z9.exports=USe;z9.exports.default=USe});var eW=L((Dhr,qSe)=>{"use strict";var jSe=Object.getOwnPropertySymbols,TPt=Object.prototype.hasOwnProperty,RPt=Object.prototype.propertyIsEnumerable;function FPt(t){if(t==null)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}function NPt(){try{if(!Object.assign)return!1;var t=new String("abc");if(t[5]="de",Object.getOwnPropertyNames(t)[0]==="5")return!1;for(var e={},r=0;r<10;r++)e["_"+String.fromCharCode(r)]=r;var s=Object.getOwnPropertyNames(e).map(function(n){return e[n]});if(s.join("")!=="0123456789")return!1;var a={};return"abcdefghijklmnopqrst".split("").forEach(function(n){a[n]=n}),Object.keys(Object.assign({},a)).join("")==="abcdefghijklmnopqrst"}catch{return!1}}qSe.exports=NPt()?Object.assign:function(t,e){for(var r,s=FPt(t),a,n=1;n{"use strict";var rW=eW(),fw=60103,YSe=60106;Dn.Fragment=60107;Dn.StrictMode=60108;Dn.Profiler=60114;var VSe=60109,KSe=60110,JSe=60112;Dn.Suspense=60113;var zSe=60115,ZSe=60116;typeof Symbol=="function"&&Symbol.for&&(Wc=Symbol.for,fw=Wc("react.element"),YSe=Wc("react.portal"),Dn.Fragment=Wc("react.fragment"),Dn.StrictMode=Wc("react.strict_mode"),Dn.Profiler=Wc("react.profiler"),VSe=Wc("react.provider"),KSe=Wc("react.context"),JSe=Wc("react.forward_ref"),Dn.Suspense=Wc("react.suspense"),zSe=Wc("react.memo"),ZSe=Wc("react.lazy"));var Wc,GSe=typeof Symbol=="function"&&Symbol.iterator;function OPt(t){return t===null||typeof t!="object"?null:(t=GSe&&t[GSe]||t["@@iterator"],typeof t=="function"?t:null)}function JS(t){for(var e="https://reactjs.org/docs/error-decoder.html?invariant="+t,r=1;r{"use strict";oDe.exports=sDe()});var lW=L((xhr,aW)=>{"use strict";var Cn=aW.exports;aW.exports.default=Cn;var Zn="\x1B[",zS="\x1B]",pw="\x07",BF=";",aDe=process.env.TERM_PROGRAM==="Apple_Terminal";Cn.cursorTo=(t,e)=>{if(typeof t!="number")throw new TypeError("The `x` argument is required");return typeof e!="number"?Zn+(t+1)+"G":Zn+(e+1)+";"+(t+1)+"H"};Cn.cursorMove=(t,e)=>{if(typeof t!="number")throw new TypeError("The `x` argument is required");let r="";return t<0?r+=Zn+-t+"D":t>0&&(r+=Zn+t+"C"),e<0?r+=Zn+-e+"A":e>0&&(r+=Zn+e+"B"),r};Cn.cursorUp=(t=1)=>Zn+t+"A";Cn.cursorDown=(t=1)=>Zn+t+"B";Cn.cursorForward=(t=1)=>Zn+t+"C";Cn.cursorBackward=(t=1)=>Zn+t+"D";Cn.cursorLeft=Zn+"G";Cn.cursorSavePosition=aDe?"\x1B7":Zn+"s";Cn.cursorRestorePosition=aDe?"\x1B8":Zn+"u";Cn.cursorGetPosition=Zn+"6n";Cn.cursorNextLine=Zn+"E";Cn.cursorPrevLine=Zn+"F";Cn.cursorHide=Zn+"?25l";Cn.cursorShow=Zn+"?25h";Cn.eraseLines=t=>{let e="";for(let r=0;r[zS,"8",BF,BF,e,pw,t,zS,"8",BF,BF,pw].join("");Cn.image=(t,e={})=>{let r=`${zS}1337;File=inline=1`;return e.width&&(r+=`;width=${e.width}`),e.height&&(r+=`;height=${e.height}`),e.preserveAspectRatio===!1&&(r+=";preserveAspectRatio=0"),r+":"+t.toString("base64")+pw};Cn.iTerm={setCwd:(t=process.cwd())=>`${zS}50;CurrentDir=${t}${pw}`,annotation:(t,e={})=>{let r=`${zS}1337;`,s=typeof e.x<"u",a=typeof e.y<"u";if((s||a)&&!(s&&a&&typeof e.length<"u"))throw new Error("`x`, `y` and `length` must be defined when `x` or `y` is defined");return t=t.replace(/\|/g,""),r+=e.isHidden?"AddHiddenAnnotation=":"AddAnnotation=",e.length>0?r+=(s?[t,e.length,e.x,e.y]:[e.length,t]).join("|"):r+=t,r+pw}}});var cDe=L((khr,cW)=>{"use strict";var lDe=(t,e)=>{for(let r of Reflect.ownKeys(e))Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(e,r));return t};cW.exports=lDe;cW.exports.default=lDe});var fDe=L((Qhr,SF)=>{"use strict";var HPt=cDe(),vF=new WeakMap,uDe=(t,e={})=>{if(typeof t!="function")throw new TypeError("Expected a function");let r,s=0,a=t.displayName||t.name||"",n=function(...c){if(vF.set(n,++s),s===1)r=t.apply(this,c),t=null;else if(e.throw===!0)throw new Error(`Function \`${a}\` can only be called once`);return r};return HPt(n,t),vF.set(n,s),n};SF.exports=uDe;SF.exports.default=uDe;SF.exports.callCount=t=>{if(!vF.has(t))throw new Error(`The given function \`${t.name}\` is not wrapped by the \`onetime\` package`);return vF.get(t)}});var ADe=L((Thr,DF)=>{DF.exports=["SIGABRT","SIGALRM","SIGHUP","SIGINT","SIGTERM"];process.platform!=="win32"&&DF.exports.push("SIGVTALRM","SIGXCPU","SIGXFSZ","SIGUSR2","SIGTRAP","SIGSYS","SIGQUIT","SIGIOT");process.platform==="linux"&&DF.exports.push("SIGIO","SIGPOLL","SIGPWR","SIGSTKFLT","SIGUNUSED")});var AW=L((Rhr,dw)=>{var Qi=global.process,Tm=function(t){return t&&typeof t=="object"&&typeof t.removeListener=="function"&&typeof t.emit=="function"&&typeof t.reallyExit=="function"&&typeof t.listeners=="function"&&typeof t.kill=="function"&&typeof t.pid=="number"&&typeof t.on=="function"};Tm(Qi)?(pDe=ye("assert"),hw=ADe(),hDe=/^win/i.test(Qi.platform),ZS=ye("events"),typeof ZS!="function"&&(ZS=ZS.EventEmitter),Qi.__signal_exit_emitter__?Js=Qi.__signal_exit_emitter__:(Js=Qi.__signal_exit_emitter__=new ZS,Js.count=0,Js.emitted={}),Js.infinite||(Js.setMaxListeners(1/0),Js.infinite=!0),dw.exports=function(t,e){if(!Tm(global.process))return function(){};pDe.equal(typeof t,"function","a callback must be provided for exit handler"),gw===!1&&uW();var r="exit";e&&e.alwaysLast&&(r="afterexit");var s=function(){Js.removeListener(r,t),Js.listeners("exit").length===0&&Js.listeners("afterexit").length===0&&bF()};return Js.on(r,t),s},bF=function(){!gw||!Tm(global.process)||(gw=!1,hw.forEach(function(e){try{Qi.removeListener(e,PF[e])}catch{}}),Qi.emit=xF,Qi.reallyExit=fW,Js.count-=1)},dw.exports.unload=bF,Rm=function(e,r,s){Js.emitted[e]||(Js.emitted[e]=!0,Js.emit(e,r,s))},PF={},hw.forEach(function(t){PF[t]=function(){if(Tm(global.process)){var r=Qi.listeners(t);r.length===Js.count&&(bF(),Rm("exit",null,t),Rm("afterexit",null,t),hDe&&t==="SIGHUP"&&(t="SIGINT"),Qi.kill(Qi.pid,t))}}}),dw.exports.signals=function(){return hw},gw=!1,uW=function(){gw||!Tm(global.process)||(gw=!0,Js.count+=1,hw=hw.filter(function(e){try{return Qi.on(e,PF[e]),!0}catch{return!1}}),Qi.emit=dDe,Qi.reallyExit=gDe)},dw.exports.load=uW,fW=Qi.reallyExit,gDe=function(e){Tm(global.process)&&(Qi.exitCode=e||0,Rm("exit",Qi.exitCode,null),Rm("afterexit",Qi.exitCode,null),fW.call(Qi,Qi.exitCode))},xF=Qi.emit,dDe=function(e,r){if(e==="exit"&&Tm(global.process)){r!==void 0&&(Qi.exitCode=r);var s=xF.apply(this,arguments);return Rm("exit",Qi.exitCode,null),Rm("afterexit",Qi.exitCode,null),s}else return xF.apply(this,arguments)}):dw.exports=function(){return function(){}};var pDe,hw,hDe,ZS,Js,bF,Rm,PF,gw,uW,fW,gDe,xF,dDe});var yDe=L((Fhr,mDe)=>{"use strict";var jPt=fDe(),qPt=AW();mDe.exports=jPt(()=>{qPt(()=>{process.stderr.write("\x1B[?25h")},{alwaysLast:!0})})});var pW=L(mw=>{"use strict";var GPt=yDe(),kF=!1;mw.show=(t=process.stderr)=>{t.isTTY&&(kF=!1,t.write("\x1B[?25h"))};mw.hide=(t=process.stderr)=>{t.isTTY&&(GPt(),kF=!0,t.write("\x1B[?25l"))};mw.toggle=(t,e)=>{t!==void 0&&(kF=t),kF?mw.show(e):mw.hide(e)}});var wDe=L(XS=>{"use strict";var CDe=XS&&XS.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(XS,"__esModule",{value:!0});var EDe=CDe(lW()),IDe=CDe(pW()),WPt=(t,{showCursor:e=!1}={})=>{let r=0,s="",a=!1,n=c=>{!e&&!a&&(IDe.default.hide(),a=!0);let f=c+` +`;f!==s&&(s=f,t.write(EDe.default.eraseLines(r)+f),r=f.split(` +`).length)};return n.clear=()=>{t.write(EDe.default.eraseLines(r)),s="",r=0},n.done=()=>{s="",r=0,e||(IDe.default.show(),a=!1)},n};XS.default={create:WPt}});var BDe=L((Lhr,YPt)=>{YPt.exports=[{name:"AppVeyor",constant:"APPVEYOR",env:"APPVEYOR",pr:"APPVEYOR_PULL_REQUEST_NUMBER"},{name:"Azure Pipelines",constant:"AZURE_PIPELINES",env:"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",pr:"SYSTEM_PULLREQUEST_PULLREQUESTID"},{name:"Bamboo",constant:"BAMBOO",env:"bamboo_planKey"},{name:"Bitbucket Pipelines",constant:"BITBUCKET",env:"BITBUCKET_COMMIT",pr:"BITBUCKET_PR_ID"},{name:"Bitrise",constant:"BITRISE",env:"BITRISE_IO",pr:"BITRISE_PULL_REQUEST"},{name:"Buddy",constant:"BUDDY",env:"BUDDY_WORKSPACE_ID",pr:"BUDDY_EXECUTION_PULL_REQUEST_ID"},{name:"Buildkite",constant:"BUILDKITE",env:"BUILDKITE",pr:{env:"BUILDKITE_PULL_REQUEST",ne:"false"}},{name:"CircleCI",constant:"CIRCLE",env:"CIRCLECI",pr:"CIRCLE_PULL_REQUEST"},{name:"Cirrus CI",constant:"CIRRUS",env:"CIRRUS_CI",pr:"CIRRUS_PR"},{name:"AWS CodeBuild",constant:"CODEBUILD",env:"CODEBUILD_BUILD_ARN"},{name:"Codeship",constant:"CODESHIP",env:{CI_NAME:"codeship"}},{name:"Drone",constant:"DRONE",env:"DRONE",pr:{DRONE_BUILD_EVENT:"pull_request"}},{name:"dsari",constant:"DSARI",env:"DSARI"},{name:"GitLab CI",constant:"GITLAB",env:"GITLAB_CI"},{name:"GoCD",constant:"GOCD",env:"GO_PIPELINE_LABEL"},{name:"Hudson",constant:"HUDSON",env:"HUDSON_URL"},{name:"Jenkins",constant:"JENKINS",env:["JENKINS_URL","BUILD_ID"],pr:{any:["ghprbPullId","CHANGE_ID"]}},{name:"Magnum CI",constant:"MAGNUM",env:"MAGNUM"},{name:"Netlify CI",constant:"NETLIFY",env:"NETLIFY_BUILD_BASE",pr:{env:"PULL_REQUEST",ne:"false"}},{name:"Sail CI",constant:"SAIL",env:"SAILCI",pr:"SAIL_PULL_REQUEST_NUMBER"},{name:"Semaphore",constant:"SEMAPHORE",env:"SEMAPHORE",pr:"PULL_REQUEST_NUMBER"},{name:"Shippable",constant:"SHIPPABLE",env:"SHIPPABLE",pr:{IS_PULL_REQUEST:"true"}},{name:"Solano CI",constant:"SOLANO",env:"TDDIUM",pr:"TDDIUM_PR_ID"},{name:"Strider CD",constant:"STRIDER",env:"STRIDER"},{name:"TaskCluster",constant:"TASKCLUSTER",env:["TASK_ID","RUN_ID"]},{name:"TeamCity",constant:"TEAMCITY",env:"TEAMCITY_VERSION"},{name:"Travis CI",constant:"TRAVIS",env:"TRAVIS",pr:{env:"TRAVIS_PULL_REQUEST",ne:"false"}}]});var DDe=L(rc=>{"use strict";var SDe=BDe(),AA=process.env;Object.defineProperty(rc,"_vendors",{value:SDe.map(function(t){return t.constant})});rc.name=null;rc.isPR=null;SDe.forEach(function(t){var e=Array.isArray(t.env)?t.env:[t.env],r=e.every(function(s){return vDe(s)});if(rc[t.constant]=r,r)switch(rc.name=t.name,typeof t.pr){case"string":rc.isPR=!!AA[t.pr];break;case"object":"env"in t.pr?rc.isPR=t.pr.env in AA&&AA[t.pr.env]!==t.pr.ne:"any"in t.pr?rc.isPR=t.pr.any.some(function(s){return!!AA[s]}):rc.isPR=vDe(t.pr);break;default:rc.isPR=null}});rc.isCI=!!(AA.CI||AA.CONTINUOUS_INTEGRATION||AA.BUILD_NUMBER||AA.RUN_ID||rc.name);function vDe(t){return typeof t=="string"?!!AA[t]:Object.keys(t).every(function(e){return AA[e]===t[e]})}});var PDe=L((_hr,bDe)=>{"use strict";bDe.exports=DDe().isCI});var kDe=L((Uhr,xDe)=>{"use strict";var VPt=t=>{let e=new Set;do for(let r of Reflect.ownKeys(t))e.add([t,r]);while((t=Reflect.getPrototypeOf(t))&&t!==Object.prototype);return e};xDe.exports=(t,{include:e,exclude:r}={})=>{let s=a=>{let n=c=>typeof c=="string"?a===c:c.test(a);return e?e.some(n):r?!r.some(n):!0};for(let[a,n]of VPt(t.constructor.prototype)){if(n==="constructor"||!s(n))continue;let c=Reflect.getOwnPropertyDescriptor(a,n);c&&typeof c.value=="function"&&(t[n]=t[n].bind(t))}return t}});var ODe=L(Vn=>{"use strict";var Ew,tD,FF,IW;typeof performance=="object"&&typeof performance.now=="function"?(QDe=performance,Vn.unstable_now=function(){return QDe.now()}):(hW=Date,TDe=hW.now(),Vn.unstable_now=function(){return hW.now()-TDe});var QDe,hW,TDe;typeof window>"u"||typeof MessageChannel!="function"?(yw=null,gW=null,dW=function(){if(yw!==null)try{var t=Vn.unstable_now();yw(!0,t),yw=null}catch(e){throw setTimeout(dW,0),e}},Ew=function(t){yw!==null?setTimeout(Ew,0,t):(yw=t,setTimeout(dW,0))},tD=function(t,e){gW=setTimeout(t,e)},FF=function(){clearTimeout(gW)},Vn.unstable_shouldYield=function(){return!1},IW=Vn.unstable_forceFrameRate=function(){}):(RDe=window.setTimeout,FDe=window.clearTimeout,typeof console<"u"&&(NDe=window.cancelAnimationFrame,typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills"),typeof NDe!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills")),$S=!1,eD=null,QF=-1,mW=5,yW=0,Vn.unstable_shouldYield=function(){return Vn.unstable_now()>=yW},IW=function(){},Vn.unstable_forceFrameRate=function(t){0>t||125>>1,a=t[s];if(a!==void 0&&0RF(c,r))p!==void 0&&0>RF(p,c)?(t[s]=p,t[f]=r,s=f):(t[s]=c,t[n]=r,s=n);else if(p!==void 0&&0>RF(p,r))t[s]=p,t[f]=r,s=f;else break e}}return e}return null}function RF(t,e){var r=t.sortIndex-e.sortIndex;return r!==0?r:t.id-e.id}var pA=[],$0=[],KPt=1,Yc=null,ea=3,OF=!1,Fm=!1,rD=!1;function wW(t){for(var e=tf($0);e!==null;){if(e.callback===null)NF($0);else if(e.startTime<=t)NF($0),e.sortIndex=e.expirationTime,CW(pA,e);else break;e=tf($0)}}function BW(t){if(rD=!1,wW(t),!Fm)if(tf(pA)!==null)Fm=!0,Ew(vW);else{var e=tf($0);e!==null&&tD(BW,e.startTime-t)}}function vW(t,e){Fm=!1,rD&&(rD=!1,FF()),OF=!0;var r=ea;try{for(wW(e),Yc=tf(pA);Yc!==null&&(!(Yc.expirationTime>e)||t&&!Vn.unstable_shouldYield());){var s=Yc.callback;if(typeof s=="function"){Yc.callback=null,ea=Yc.priorityLevel;var a=s(Yc.expirationTime<=e);e=Vn.unstable_now(),typeof a=="function"?Yc.callback=a:Yc===tf(pA)&&NF(pA),wW(e)}else NF(pA);Yc=tf(pA)}if(Yc!==null)var n=!0;else{var c=tf($0);c!==null&&tD(BW,c.startTime-e),n=!1}return n}finally{Yc=null,ea=r,OF=!1}}var JPt=IW;Vn.unstable_IdlePriority=5;Vn.unstable_ImmediatePriority=1;Vn.unstable_LowPriority=4;Vn.unstable_NormalPriority=3;Vn.unstable_Profiling=null;Vn.unstable_UserBlockingPriority=2;Vn.unstable_cancelCallback=function(t){t.callback=null};Vn.unstable_continueExecution=function(){Fm||OF||(Fm=!0,Ew(vW))};Vn.unstable_getCurrentPriorityLevel=function(){return ea};Vn.unstable_getFirstCallbackNode=function(){return tf(pA)};Vn.unstable_next=function(t){switch(ea){case 1:case 2:case 3:var e=3;break;default:e=ea}var r=ea;ea=e;try{return t()}finally{ea=r}};Vn.unstable_pauseExecution=function(){};Vn.unstable_requestPaint=JPt;Vn.unstable_runWithPriority=function(t,e){switch(t){case 1:case 2:case 3:case 4:case 5:break;default:t=3}var r=ea;ea=t;try{return e()}finally{ea=r}};Vn.unstable_scheduleCallback=function(t,e,r){var s=Vn.unstable_now();switch(typeof r=="object"&&r!==null?(r=r.delay,r=typeof r=="number"&&0s?(t.sortIndex=r,CW($0,t),tf(pA)===null&&t===tf($0)&&(rD?FF():rD=!0,tD(BW,r-s))):(t.sortIndex=a,CW(pA,t),Fm||OF||(Fm=!0,Ew(vW))),t};Vn.unstable_wrapCallback=function(t){var e=ea;return function(){var r=ea;ea=e;try{return t.apply(this,arguments)}finally{ea=r}}}});var SW=L((jhr,LDe)=>{"use strict";LDe.exports=ODe()});var MDe=L((qhr,nD)=>{nD.exports=function(e){var r={},s=eW(),a=hn(),n=SW();function c(v){for(var D="https://reactjs.org/docs/error-decoder.html?invariant="+v,Q=1;QUe||V[Se]!==ne[Ue])return` +`+V[Se].replace(" at new "," at ");while(1<=Se&&0<=Ue);break}}}finally{ve=!1,Error.prepareStackTrace=Q}return(v=v?v.displayName||v.name:"")?ac(v):""}var lc=[],Ni=-1;function io(v){return{current:v}}function Rt(v){0>Ni||(v.current=lc[Ni],lc[Ni]=null,Ni--)}function xn(v,D){Ni++,lc[Ni]=v.current,v.current=D}var ca={},ji=io(ca),Oi=io(!1),Oa=ca;function dn(v,D){var Q=v.type.contextTypes;if(!Q)return ca;var H=v.stateNode;if(H&&H.__reactInternalMemoizedUnmaskedChildContext===D)return H.__reactInternalMemoizedMaskedChildContext;var V={},ne;for(ne in Q)V[ne]=D[ne];return H&&(v=v.stateNode,v.__reactInternalMemoizedUnmaskedChildContext=D,v.__reactInternalMemoizedMaskedChildContext=V),V}function Jn(v){return v=v.childContextTypes,v!=null}function hu(){Rt(Oi),Rt(ji)}function Ch(v,D,Q){if(ji.current!==ca)throw Error(c(168));xn(ji,D),xn(Oi,Q)}function La(v,D,Q){var H=v.stateNode;if(v=D.childContextTypes,typeof H.getChildContext!="function")return Q;H=H.getChildContext();for(var V in H)if(!(V in v))throw Error(c(108,g(D)||"Unknown",V));return s({},Q,H)}function Ma(v){return v=(v=v.stateNode)&&v.__reactInternalMemoizedMergedChildContext||ca,Oa=ji.current,xn(ji,v),xn(Oi,Oi.current),!0}function Ua(v,D,Q){var H=v.stateNode;if(!H)throw Error(c(169));Q?(v=La(v,D,Oa),H.__reactInternalMemoizedMergedChildContext=v,Rt(Oi),Rt(ji),xn(ji,v)):Rt(Oi),xn(Oi,Q)}var Xe=null,Ha=null,gf=n.unstable_now;gf();var cc=0,wn=8;function ua(v){if(1&v)return wn=15,1;if(2&v)return wn=14,2;if(4&v)return wn=13,4;var D=24&v;return D!==0?(wn=12,D):v&32?(wn=11,32):(D=192&v,D!==0?(wn=10,D):v&256?(wn=9,256):(D=3584&v,D!==0?(wn=8,D):v&4096?(wn=7,4096):(D=4186112&v,D!==0?(wn=6,D):(D=62914560&v,D!==0?(wn=5,D):v&67108864?(wn=4,67108864):v&134217728?(wn=3,134217728):(D=805306368&v,D!==0?(wn=2,D):1073741824&v?(wn=1,1073741824):(wn=8,v))))))}function _A(v){switch(v){case 99:return 15;case 98:return 10;case 97:case 96:return 8;case 95:return 2;default:return 0}}function UA(v){switch(v){case 15:case 14:return 99;case 13:case 12:case 11:case 10:return 98;case 9:case 8:case 7:case 6:case 4:case 5:return 97;case 3:case 2:case 1:return 95;case 0:return 90;default:throw Error(c(358,v))}}function fa(v,D){var Q=v.pendingLanes;if(Q===0)return wn=0;var H=0,V=0,ne=v.expiredLanes,Se=v.suspendedLanes,Ue=v.pingedLanes;if(ne!==0)H=ne,V=wn=15;else if(ne=Q&134217727,ne!==0){var At=ne&~Se;At!==0?(H=ua(At),V=wn):(Ue&=ne,Ue!==0&&(H=ua(Ue),V=wn))}else ne=Q&~Se,ne!==0?(H=ua(ne),V=wn):Ue!==0&&(H=ua(Ue),V=wn);if(H===0)return 0;if(H=31-ns(H),H=Q&((0>H?0:1<Q;Q++)D.push(v);return D}function ja(v,D,Q){v.pendingLanes|=D;var H=D-1;v.suspendedLanes&=H,v.pingedLanes&=H,v=v.eventTimes,D=31-ns(D),v[D]=Q}var ns=Math.clz32?Math.clz32:fc,uc=Math.log,gu=Math.LN2;function fc(v){return v===0?32:31-(uc(v)/gu|0)|0}var qa=n.unstable_runWithPriority,Li=n.unstable_scheduleCallback,Cs=n.unstable_cancelCallback,Sl=n.unstable_shouldYield,df=n.unstable_requestPaint,Ac=n.unstable_now,wi=n.unstable_getCurrentPriorityLevel,Qn=n.unstable_ImmediatePriority,pc=n.unstable_UserBlockingPriority,Je=n.unstable_NormalPriority,st=n.unstable_LowPriority,St=n.unstable_IdlePriority,lr={},ee=df!==void 0?df:function(){},Ie=null,Oe=null,ht=!1,mt=Ac(),Dt=1e4>mt?Ac:function(){return Ac()-mt};function tr(){switch(wi()){case Qn:return 99;case pc:return 98;case Je:return 97;case st:return 96;case St:return 95;default:throw Error(c(332))}}function fn(v){switch(v){case 99:return Qn;case 98:return pc;case 97:return Je;case 96:return st;case 95:return St;default:throw Error(c(332))}}function ai(v,D){return v=fn(v),qa(v,D)}function qi(v,D,Q){return v=fn(v),Li(v,D,Q)}function Tn(){if(Oe!==null){var v=Oe;Oe=null,Cs(v)}Ga()}function Ga(){if(!ht&&Ie!==null){ht=!0;var v=0;try{var D=Ie;ai(99,function(){for(;vRn?(Un=kr,kr=null):Un=kr.sibling;var zr=Xt($e,kr,pt[Rn],Zt);if(zr===null){kr===null&&(kr=Un);break}v&&kr&&zr.alternate===null&&D($e,kr),qe=ne(zr,qe,Rn),Xn===null?Sr=zr:Xn.sibling=zr,Xn=zr,kr=Un}if(Rn===pt.length)return Q($e,kr),Sr;if(kr===null){for(;RnRn?(Un=kr,kr=null):Un=kr.sibling;var li=Xt($e,kr,zr.value,Zt);if(li===null){kr===null&&(kr=Un);break}v&&kr&&li.alternate===null&&D($e,kr),qe=ne(li,qe,Rn),Xn===null?Sr=li:Xn.sibling=li,Xn=li,kr=Un}if(zr.done)return Q($e,kr),Sr;if(kr===null){for(;!zr.done;Rn++,zr=pt.next())zr=Lr($e,zr.value,Zt),zr!==null&&(qe=ne(zr,qe,Rn),Xn===null?Sr=zr:Xn.sibling=zr,Xn=zr);return Sr}for(kr=H($e,kr);!zr.done;Rn++,zr=pt.next())zr=zn(kr,$e,Rn,zr.value,Zt),zr!==null&&(v&&zr.alternate!==null&&kr.delete(zr.key===null?Rn:zr.key),qe=ne(zr,qe,Rn),Xn===null?Sr=zr:Xn.sibling=zr,Xn=zr);return v&&kr.forEach(function(Pu){return D($e,Pu)}),Sr}return function($e,qe,pt,Zt){var Sr=typeof pt=="object"&&pt!==null&&pt.type===E&&pt.key===null;Sr&&(pt=pt.props.children);var Xn=typeof pt=="object"&&pt!==null;if(Xn)switch(pt.$$typeof){case p:e:{for(Xn=pt.key,Sr=qe;Sr!==null;){if(Sr.key===Xn){switch(Sr.tag){case 7:if(pt.type===E){Q($e,Sr.sibling),qe=V(Sr,pt.props.children),qe.return=$e,$e=qe;break e}break;default:if(Sr.elementType===pt.type){Q($e,Sr.sibling),qe=V(Sr,pt.props),qe.ref=dt($e,Sr,pt),qe.return=$e,$e=qe;break e}}Q($e,Sr);break}else D($e,Sr);Sr=Sr.sibling}pt.type===E?(qe=Qf(pt.props.children,$e.mode,Zt,pt.key),qe.return=$e,$e=qe):(Zt=od(pt.type,pt.key,pt.props,null,$e.mode,Zt),Zt.ref=dt($e,qe,pt),Zt.return=$e,$e=Zt)}return Se($e);case h:e:{for(Sr=pt.key;qe!==null;){if(qe.key===Sr)if(qe.tag===4&&qe.stateNode.containerInfo===pt.containerInfo&&qe.stateNode.implementation===pt.implementation){Q($e,qe.sibling),qe=V(qe,pt.children||[]),qe.return=$e,$e=qe;break e}else{Q($e,qe);break}else D($e,qe);qe=qe.sibling}qe=Ro(pt,$e.mode,Zt),qe.return=$e,$e=qe}return Se($e)}if(typeof pt=="string"||typeof pt=="number")return pt=""+pt,qe!==null&&qe.tag===6?(Q($e,qe.sibling),qe=V(qe,pt),qe.return=$e,$e=qe):(Q($e,qe),qe=k2(pt,$e.mode,Zt),qe.return=$e,$e=qe),Se($e);if(yf(pt))return mi($e,qe,pt,Zt);if(Ce(pt))return Za($e,qe,pt,Zt);if(Xn&&mu($e,pt),typeof pt>"u"&&!Sr)switch($e.tag){case 1:case 22:case 0:case 11:case 15:throw Error(c(152,g($e.type)||"Component"))}return Q($e,qe)}}var _g=By(!0),n2=By(!1),bh={},ur=io(bh),zi=io(bh),Ef=io(bh);function Wa(v){if(v===bh)throw Error(c(174));return v}function Ug(v,D){xn(Ef,D),xn(zi,v),xn(ur,bh),v=gt(D),Rt(ur),xn(ur,v)}function yu(){Rt(ur),Rt(zi),Rt(Ef)}function If(v){var D=Wa(Ef.current),Q=Wa(ur.current);D=j(Q,v.type,D),Q!==D&&(xn(zi,v),xn(ur,D))}function wt(v){zi.current===v&&(Rt(ur),Rt(zi))}var gi=io(0);function WA(v){for(var D=v;D!==null;){if(D.tag===13){var Q=D.memoizedState;if(Q!==null&&(Q=Q.dehydrated,Q===null||gr(Q)||So(Q)))return D}else if(D.tag===19&&D.memoizedProps.revealOrder!==void 0){if(D.flags&64)return D}else if(D.child!==null){D.child.return=D,D=D.child;continue}if(D===v)break;for(;D.sibling===null;){if(D.return===null||D.return===v)return null;D=D.return}D.sibling.return=D.return,D=D.sibling}return null}var Ya=null,pa=null,Va=!1;function Hg(v,D){var Q=za(5,null,null,0);Q.elementType="DELETED",Q.type="DELETED",Q.stateNode=D,Q.return=v,Q.flags=8,v.lastEffect!==null?(v.lastEffect.nextEffect=Q,v.lastEffect=Q):v.firstEffect=v.lastEffect=Q}function Ph(v,D){switch(v.tag){case 5:return D=la(D,v.type,v.pendingProps),D!==null?(v.stateNode=D,!0):!1;case 6:return D=OA(D,v.pendingProps),D!==null?(v.stateNode=D,!0):!1;case 13:return!1;default:return!1}}function jg(v){if(Va){var D=pa;if(D){var Q=D;if(!Ph(v,D)){if(D=Me(Q),!D||!Ph(v,D)){v.flags=v.flags&-1025|2,Va=!1,Ya=v;return}Hg(Ya,Q)}Ya=v,pa=fu(D)}else v.flags=v.flags&-1025|2,Va=!1,Ya=v}}function vy(v){for(v=v.return;v!==null&&v.tag!==5&&v.tag!==3&&v.tag!==13;)v=v.return;Ya=v}function YA(v){if(!Z||v!==Ya)return!1;if(!Va)return vy(v),Va=!0,!1;var D=v.type;if(v.tag!==5||D!=="head"&&D!=="body"&&!it(D,v.memoizedProps))for(D=pa;D;)Hg(v,D),D=Me(D);if(vy(v),v.tag===13){if(!Z)throw Error(c(316));if(v=v.memoizedState,v=v!==null?v.dehydrated:null,!v)throw Error(c(317));pa=LA(v)}else pa=Ya?Me(v.stateNode):null;return!0}function qg(){Z&&(pa=Ya=null,Va=!1)}var Eu=[];function Iu(){for(var v=0;vne))throw Error(c(301));ne+=1,xi=is=null,D.updateQueue=null,Cf.current=re,v=Q(H,V)}while(wf)}if(Cf.current=kt,D=is!==null&&is.next!==null,Cu=0,xi=is=qn=null,VA=!1,D)throw Error(c(300));return v}function ss(){var v={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return xi===null?qn.memoizedState=xi=v:xi=xi.next=v,xi}function xl(){if(is===null){var v=qn.alternate;v=v!==null?v.memoizedState:null}else v=is.next;var D=xi===null?qn.memoizedState:xi.next;if(D!==null)xi=D,is=v;else{if(v===null)throw Error(c(310));is=v,v={memoizedState:is.memoizedState,baseState:is.baseState,baseQueue:is.baseQueue,queue:is.queue,next:null},xi===null?qn.memoizedState=xi=v:xi=xi.next=v}return xi}function ko(v,D){return typeof D=="function"?D(v):D}function Bf(v){var D=xl(),Q=D.queue;if(Q===null)throw Error(c(311));Q.lastRenderedReducer=v;var H=is,V=H.baseQueue,ne=Q.pending;if(ne!==null){if(V!==null){var Se=V.next;V.next=ne.next,ne.next=Se}H.baseQueue=V=ne,Q.pending=null}if(V!==null){V=V.next,H=H.baseState;var Ue=Se=ne=null,At=V;do{var Gt=At.lane;if((Cu&Gt)===Gt)Ue!==null&&(Ue=Ue.next={lane:0,action:At.action,eagerReducer:At.eagerReducer,eagerState:At.eagerState,next:null}),H=At.eagerReducer===v?At.eagerState:v(H,At.action);else{var vr={lane:Gt,action:At.action,eagerReducer:At.eagerReducer,eagerState:At.eagerState,next:null};Ue===null?(Se=Ue=vr,ne=H):Ue=Ue.next=vr,qn.lanes|=Gt,$g|=Gt}At=At.next}while(At!==null&&At!==V);Ue===null?ne=H:Ue.next=Se,Do(H,D.memoizedState)||(Ke=!0),D.memoizedState=H,D.baseState=ne,D.baseQueue=Ue,Q.lastRenderedState=H}return[D.memoizedState,Q.dispatch]}function vf(v){var D=xl(),Q=D.queue;if(Q===null)throw Error(c(311));Q.lastRenderedReducer=v;var H=Q.dispatch,V=Q.pending,ne=D.memoizedState;if(V!==null){Q.pending=null;var Se=V=V.next;do ne=v(ne,Se.action),Se=Se.next;while(Se!==V);Do(ne,D.memoizedState)||(Ke=!0),D.memoizedState=ne,D.baseQueue===null&&(D.baseState=ne),Q.lastRenderedState=ne}return[ne,H]}function kl(v,D,Q){var H=D._getVersion;H=H(D._source);var V=y?D._workInProgressVersionPrimary:D._workInProgressVersionSecondary;if(V!==null?v=V===H:(v=v.mutableReadLanes,(v=(Cu&v)===v)&&(y?D._workInProgressVersionPrimary=H:D._workInProgressVersionSecondary=H,Eu.push(D))),v)return Q(D._source);throw Eu.push(D),Error(c(350))}function yn(v,D,Q,H){var V=oo;if(V===null)throw Error(c(349));var ne=D._getVersion,Se=ne(D._source),Ue=Cf.current,At=Ue.useState(function(){return kl(V,D,Q)}),Gt=At[1],vr=At[0];At=xi;var Lr=v.memoizedState,Xt=Lr.refs,zn=Xt.getSnapshot,mi=Lr.source;Lr=Lr.subscribe;var Za=qn;return v.memoizedState={refs:Xt,source:D,subscribe:H},Ue.useEffect(function(){Xt.getSnapshot=Q,Xt.setSnapshot=Gt;var $e=ne(D._source);if(!Do(Se,$e)){$e=Q(D._source),Do(vr,$e)||(Gt($e),$e=vs(Za),V.mutableReadLanes|=$e&V.pendingLanes),$e=V.mutableReadLanes,V.entangledLanes|=$e;for(var qe=V.entanglements,pt=$e;0Q?98:Q,function(){v(!0)}),ai(97I2&&(D.flags|=64,V=!0,$A(H,!1),D.lanes=33554432)}else{if(!V)if(v=WA(ne),v!==null){if(D.flags|=64,V=!0,v=v.updateQueue,v!==null&&(D.updateQueue=v,D.flags|=4),$A(H,!0),H.tail===null&&H.tailMode==="hidden"&&!ne.alternate&&!Va)return D=D.lastEffect=H.lastEffect,D!==null&&(D.nextEffect=null),null}else 2*Dt()-H.renderingStartTime>I2&&Q!==1073741824&&(D.flags|=64,V=!0,$A(H,!1),D.lanes=33554432);H.isBackwards?(ne.sibling=D.child,D.child=ne):(v=H.last,v!==null?v.sibling=ne:D.child=ne,H.last=ne)}return H.tail!==null?(v=H.tail,H.rendering=v,H.tail=v.sibling,H.lastEffect=D.lastEffect,H.renderingStartTime=Dt(),v.sibling=null,D=gi.current,xn(gi,V?D&1|2:D&1),v):null;case 23:case 24:return D2(),v!==null&&v.memoizedState!==null!=(D.memoizedState!==null)&&H.mode!=="unstable-defer-without-hiding"&&(D.flags|=4),null}throw Error(c(156,D.tag))}function $L(v){switch(v.tag){case 1:Jn(v.type)&&hu();var D=v.flags;return D&4096?(v.flags=D&-4097|64,v):null;case 3:if(yu(),Rt(Oi),Rt(ji),Iu(),D=v.flags,D&64)throw Error(c(285));return v.flags=D&-4097|64,v;case 5:return wt(v),null;case 13:return Rt(gi),D=v.flags,D&4096?(v.flags=D&-4097|64,v):null;case 19:return Rt(gi),null;case 4:return yu(),null;case 10:return Lg(v),null;case 23:case 24:return D2(),null;default:return null}}function Vg(v,D){try{var Q="",H=D;do Q+=r2(H),H=H.return;while(H);var V=Q}catch(ne){V=` +Error generating stack: `+ne.message+` +`+ne.stack}return{value:v,source:D,stack:V}}function Kg(v,D){try{console.error(D.value)}catch(Q){setTimeout(function(){throw Q})}}var eM=typeof WeakMap=="function"?WeakMap:Map;function a2(v,D,Q){Q=bl(-1,Q),Q.tag=3,Q.payload={element:null};var H=D.value;return Q.callback=function(){Uy||(Uy=!0,C2=H),Kg(v,D)},Q}function Jg(v,D,Q){Q=bl(-1,Q),Q.tag=3;var H=v.type.getDerivedStateFromError;if(typeof H=="function"){var V=D.value;Q.payload=function(){return Kg(v,D),H(V)}}var ne=v.stateNode;return ne!==null&&typeof ne.componentDidCatch=="function"&&(Q.callback=function(){typeof H!="function"&&(gc===null?gc=new Set([this]):gc.add(this),Kg(v,D));var Se=D.stack;this.componentDidCatch(D.value,{componentStack:Se!==null?Se:""})}),Q}var tM=typeof WeakSet=="function"?WeakSet:Set;function l2(v){var D=v.ref;if(D!==null)if(typeof D=="function")try{D(null)}catch(Q){kf(v,Q)}else D.current=null}function xy(v,D){switch(D.tag){case 0:case 11:case 15:case 22:return;case 1:if(D.flags&256&&v!==null){var Q=v.memoizedProps,H=v.memoizedState;v=D.stateNode,D=v.getSnapshotBeforeUpdate(D.elementType===D.type?Q:bo(D.type,Q),H),v.__reactInternalSnapshotBeforeUpdate=D}return;case 3:F&&D.flags&256&&Rs(D.stateNode.containerInfo);return;case 5:case 6:case 4:case 17:return}throw Error(c(163))}function Nh(v,D){if(D=D.updateQueue,D=D!==null?D.lastEffect:null,D!==null){var Q=D=D.next;do{if((Q.tag&v)===v){var H=Q.destroy;Q.destroy=void 0,H!==void 0&&H()}Q=Q.next}while(Q!==D)}}function vP(v,D,Q){switch(Q.tag){case 0:case 11:case 15:case 22:if(D=Q.updateQueue,D=D!==null?D.lastEffect:null,D!==null){v=D=D.next;do{if((v.tag&3)===3){var H=v.create;v.destroy=H()}v=v.next}while(v!==D)}if(D=Q.updateQueue,D=D!==null?D.lastEffect:null,D!==null){v=D=D.next;do{var V=v;H=V.next,V=V.tag,V&4&&V&1&&(MP(Q,v),uM(Q,v)),v=H}while(v!==D)}return;case 1:v=Q.stateNode,Q.flags&4&&(D===null?v.componentDidMount():(H=Q.elementType===Q.type?D.memoizedProps:bo(Q.type,D.memoizedProps),v.componentDidUpdate(H,D.memoizedState,v.__reactInternalSnapshotBeforeUpdate))),D=Q.updateQueue,D!==null&&Cy(Q,D,v);return;case 3:if(D=Q.updateQueue,D!==null){if(v=null,Q.child!==null)switch(Q.child.tag){case 5:v=Re(Q.child.stateNode);break;case 1:v=Q.child.stateNode}Cy(Q,D,v)}return;case 5:v=Q.stateNode,D===null&&Q.flags&4&&eo(v,Q.type,Q.memoizedProps,Q);return;case 6:return;case 4:return;case 12:return;case 13:Z&&Q.memoizedState===null&&(Q=Q.alternate,Q!==null&&(Q=Q.memoizedState,Q!==null&&(Q=Q.dehydrated,Q!==null&&Au(Q))));return;case 19:case 17:case 20:case 21:case 23:case 24:return}throw Error(c(163))}function SP(v,D){if(F)for(var Q=v;;){if(Q.tag===5){var H=Q.stateNode;D?Eh(H):ro(Q.stateNode,Q.memoizedProps)}else if(Q.tag===6)H=Q.stateNode,D?Ih(H):jn(H,Q.memoizedProps);else if((Q.tag!==23&&Q.tag!==24||Q.memoizedState===null||Q===v)&&Q.child!==null){Q.child.return=Q,Q=Q.child;continue}if(Q===v)break;for(;Q.sibling===null;){if(Q.return===null||Q.return===v)return;Q=Q.return}Q.sibling.return=Q.return,Q=Q.sibling}}function ky(v,D){if(Ha&&typeof Ha.onCommitFiberUnmount=="function")try{Ha.onCommitFiberUnmount(Xe,D)}catch{}switch(D.tag){case 0:case 11:case 14:case 15:case 22:if(v=D.updateQueue,v!==null&&(v=v.lastEffect,v!==null)){var Q=v=v.next;do{var H=Q,V=H.destroy;if(H=H.tag,V!==void 0)if(H&4)MP(D,Q);else{H=D;try{V()}catch(ne){kf(H,ne)}}Q=Q.next}while(Q!==v)}break;case 1:if(l2(D),v=D.stateNode,typeof v.componentWillUnmount=="function")try{v.props=D.memoizedProps,v.state=D.memoizedState,v.componentWillUnmount()}catch(ne){kf(D,ne)}break;case 5:l2(D);break;case 4:F?xP(v,D):z&&z&&(D=D.stateNode.containerInfo,v=lu(D),FA(D,v))}}function DP(v,D){for(var Q=D;;)if(ky(v,Q),Q.child===null||F&&Q.tag===4){if(Q===D)break;for(;Q.sibling===null;){if(Q.return===null||Q.return===D)return;Q=Q.return}Q.sibling.return=Q.return,Q=Q.sibling}else Q.child.return=Q,Q=Q.child}function Qy(v){v.alternate=null,v.child=null,v.dependencies=null,v.firstEffect=null,v.lastEffect=null,v.memoizedProps=null,v.memoizedState=null,v.pendingProps=null,v.return=null,v.updateQueue=null}function bP(v){return v.tag===5||v.tag===3||v.tag===4}function PP(v){if(F){e:{for(var D=v.return;D!==null;){if(bP(D))break e;D=D.return}throw Error(c(160))}var Q=D;switch(D=Q.stateNode,Q.tag){case 5:var H=!1;break;case 3:D=D.containerInfo,H=!0;break;case 4:D=D.containerInfo,H=!0;break;default:throw Error(c(161))}Q.flags&16&&(pf(D),Q.flags&=-17);e:t:for(Q=v;;){for(;Q.sibling===null;){if(Q.return===null||bP(Q.return)){Q=null;break e}Q=Q.return}for(Q.sibling.return=Q.return,Q=Q.sibling;Q.tag!==5&&Q.tag!==6&&Q.tag!==18;){if(Q.flags&2||Q.child===null||Q.tag===4)continue t;Q.child.return=Q,Q=Q.child}if(!(Q.flags&2)){Q=Q.stateNode;break e}}H?c2(v,Q,D):u2(v,Q,D)}}function c2(v,D,Q){var H=v.tag,V=H===5||H===6;if(V)v=V?v.stateNode:v.stateNode.instance,D?to(Q,v,D):wo(Q,v);else if(H!==4&&(v=v.child,v!==null))for(c2(v,D,Q),v=v.sibling;v!==null;)c2(v,D,Q),v=v.sibling}function u2(v,D,Q){var H=v.tag,V=H===5||H===6;if(V)v=V?v.stateNode:v.stateNode.instance,D?Hi(Q,v,D):oi(Q,v);else if(H!==4&&(v=v.child,v!==null))for(u2(v,D,Q),v=v.sibling;v!==null;)u2(v,D,Q),v=v.sibling}function xP(v,D){for(var Q=D,H=!1,V,ne;;){if(!H){H=Q.return;e:for(;;){if(H===null)throw Error(c(160));switch(V=H.stateNode,H.tag){case 5:ne=!1;break e;case 3:V=V.containerInfo,ne=!0;break e;case 4:V=V.containerInfo,ne=!0;break e}H=H.return}H=!0}if(Q.tag===5||Q.tag===6)DP(v,Q),ne?RA(V,Q.stateNode):vo(V,Q.stateNode);else if(Q.tag===4){if(Q.child!==null){V=Q.stateNode.containerInfo,ne=!0,Q.child.return=Q,Q=Q.child;continue}}else if(ky(v,Q),Q.child!==null){Q.child.return=Q,Q=Q.child;continue}if(Q===D)break;for(;Q.sibling===null;){if(Q.return===null||Q.return===D)return;Q=Q.return,Q.tag===4&&(H=!1)}Q.sibling.return=Q.return,Q=Q.sibling}}function f2(v,D){if(F){switch(D.tag){case 0:case 11:case 14:case 15:case 22:Nh(3,D);return;case 1:return;case 5:var Q=D.stateNode;if(Q!=null){var H=D.memoizedProps;v=v!==null?v.memoizedProps:H;var V=D.type,ne=D.updateQueue;D.updateQueue=null,ne!==null&&Bo(Q,ne,V,v,H,D)}return;case 6:if(D.stateNode===null)throw Error(c(162));Q=D.memoizedProps,rs(D.stateNode,v!==null?v.memoizedProps:Q,Q);return;case 3:Z&&(D=D.stateNode,D.hydrate&&(D.hydrate=!1,MA(D.containerInfo)));return;case 12:return;case 13:kP(D),zg(D);return;case 19:zg(D);return;case 17:return;case 23:case 24:SP(D,D.memoizedState!==null);return}throw Error(c(163))}switch(D.tag){case 0:case 11:case 14:case 15:case 22:Nh(3,D);return;case 12:return;case 13:kP(D),zg(D);return;case 19:zg(D);return;case 3:Z&&(Q=D.stateNode,Q.hydrate&&(Q.hydrate=!1,MA(Q.containerInfo)));break;case 23:case 24:return}e:if(z){switch(D.tag){case 1:case 5:case 6:case 20:break e;case 3:case 4:D=D.stateNode,FA(D.containerInfo,D.pendingChildren);break e}throw Error(c(163))}}function kP(v){v.memoizedState!==null&&(E2=Dt(),F&&SP(v.child,!0))}function zg(v){var D=v.updateQueue;if(D!==null){v.updateQueue=null;var Q=v.stateNode;Q===null&&(Q=v.stateNode=new tM),D.forEach(function(H){var V=AM.bind(null,v,H);Q.has(H)||(Q.add(H),H.then(V,V))})}}function rM(v,D){return v!==null&&(v=v.memoizedState,v===null||v.dehydrated!==null)?(D=D.memoizedState,D!==null&&D.dehydrated===null):!1}var Ty=0,Ry=1,Fy=2,Zg=3,Ny=4;if(typeof Symbol=="function"&&Symbol.for){var Xg=Symbol.for;Ty=Xg("selector.component"),Ry=Xg("selector.has_pseudo_class"),Fy=Xg("selector.role"),Zg=Xg("selector.test_id"),Ny=Xg("selector.text")}function Oy(v){var D=$(v);if(D!=null){if(typeof D.memoizedProps["data-testname"]!="string")throw Error(c(364));return D}if(v=ir(v),v===null)throw Error(c(362));return v.stateNode.current}function Df(v,D){switch(D.$$typeof){case Ty:if(v.type===D.value)return!0;break;case Ry:e:{D=D.value,v=[v,0];for(var Q=0;Q";case Ry:return":has("+(bf(v)||"")+")";case Fy:return'[role="'+v.value+'"]';case Ny:return'"'+v.value+'"';case Zg:return'[data-testname="'+v.value+'"]';default:throw Error(c(365,v))}}function A2(v,D){var Q=[];v=[v,0];for(var H=0;HV&&(V=Se),Q&=~ne}if(Q=V,Q=Dt()-Q,Q=(120>Q?120:480>Q?480:1080>Q?1080:1920>Q?1920:3e3>Q?3e3:4320>Q?4320:1960*iM(Q/1960))-Q,10 component higher in the tree to provide a loading indicator or placeholder to display.`)}Bs!==5&&(Bs=2),At=Vg(At,Ue),Xt=Se;do{switch(Xt.tag){case 3:ne=At,Xt.flags|=4096,D&=-D,Xt.lanes|=D;var Xn=a2(Xt,ne,D);Iy(Xt,Xn);break e;case 1:ne=At;var kr=Xt.type,Rn=Xt.stateNode;if(!(Xt.flags&64)&&(typeof kr.getDerivedStateFromError=="function"||Rn!==null&&typeof Rn.componentDidCatch=="function"&&(gc===null||!gc.has(Rn)))){Xt.flags|=4096,D&=-D,Xt.lanes|=D;var Un=Jg(Xt,ne,D);Iy(Xt,Un);break e}}Xt=Xt.return}while(Xt!==null)}LP(Q)}catch(zr){D=zr,Zi===Q&&Q!==null&&(Zi=Q=Q.return);continue}break}while(!0)}function NP(){var v=My.current;return My.current=kt,v===null?kt:v}function sd(v,D){var Q=xr;xr|=16;var H=NP();oo===v&&Os===D||_h(v,D);do try{oM();break}catch(V){FP(v,V)}while(!0);if(Ng(),xr=Q,My.current=H,Zi!==null)throw Error(c(261));return oo=null,Os=0,Bs}function oM(){for(;Zi!==null;)OP(Zi)}function aM(){for(;Zi!==null&&!Sl();)OP(Zi)}function OP(v){var D=HP(v.alternate,v,ep);v.memoizedProps=v.pendingProps,D===null?LP(v):Zi=D,h2.current=null}function LP(v){var D=v;do{var Q=D.alternate;if(v=D.return,D.flags&2048){if(Q=$L(D),Q!==null){Q.flags&=2047,Zi=Q;return}v!==null&&(v.firstEffect=v.lastEffect=null,v.flags|=2048)}else{if(Q=XL(Q,D,ep),Q!==null){Zi=Q;return}if(Q=D,Q.tag!==24&&Q.tag!==23||Q.memoizedState===null||ep&1073741824||!(Q.mode&4)){for(var H=0,V=Q.child;V!==null;)H|=V.lanes|V.childLanes,V=V.sibling;Q.childLanes=H}v!==null&&!(v.flags&2048)&&(v.firstEffect===null&&(v.firstEffect=D.firstEffect),D.lastEffect!==null&&(v.lastEffect!==null&&(v.lastEffect.nextEffect=D.firstEffect),v.lastEffect=D.lastEffect),1Dt()-E2?_h(v,0):m2|=Q),da(v,D)}function AM(v,D){var Q=v.stateNode;Q!==null&&Q.delete(D),D=0,D===0&&(D=v.mode,D&2?D&4?(Su===0&&(Su=Oh),D=kn(62914560&~Su),D===0&&(D=4194304)):D=tr()===99?1:2:D=1),Q=To(),v=qy(v,D),v!==null&&(ja(v,D,Q),da(v,Q))}var HP;HP=function(v,D,Q){var H=D.lanes;if(v!==null)if(v.memoizedProps!==D.pendingProps||Oi.current)Ke=!0;else if(Q&H)Ke=!!(v.flags&16384);else{switch(Ke=!1,D.tag){case 3:by(D),qg();break;case 5:If(D);break;case 1:Jn(D.type)&&Ma(D);break;case 4:Ug(D,D.stateNode.containerInfo);break;case 10:Og(D,D.memoizedProps.value);break;case 13:if(D.memoizedState!==null)return Q&D.child.childLanes?s2(v,D,Q):(xn(gi,gi.current&1),D=Gn(v,D,Q),D!==null?D.sibling:null);xn(gi,gi.current&1);break;case 19:if(H=(Q&D.childLanes)!==0,v.flags&64){if(H)return BP(v,D,Q);D.flags|=64}var V=D.memoizedState;if(V!==null&&(V.rendering=null,V.tail=null,V.lastEffect=null),xn(gi,gi.current),H)break;return null;case 23:case 24:return D.lanes=0,di(v,D,Q)}return Gn(v,D,Q)}else Ke=!1;switch(D.lanes=0,D.tag){case 2:if(H=D.type,v!==null&&(v.alternate=null,D.alternate=null,D.flags|=2),v=D.pendingProps,V=dn(D,ji.current),mf(D,Q),V=Wg(null,D,H,v,V,Q),D.flags|=1,typeof V=="object"&&V!==null&&typeof V.render=="function"&&V.$$typeof===void 0){if(D.tag=1,D.memoizedState=null,D.updateQueue=null,Jn(H)){var ne=!0;Ma(D)}else ne=!1;D.memoizedState=V.state!==null&&V.state!==void 0?V.state:null,Dh(D);var Se=H.getDerivedStateFromProps;typeof Se=="function"&&jA(D,H,Se,v),V.updater=qA,D.stateNode=V,V._reactInternals=D,xo(D,H,v,Q),D=i2(null,D,H,!0,ne,Q)}else D.tag=0,ft(null,D,V,Q),D=D.child;return D;case 16:V=D.elementType;e:{switch(v!==null&&(v.alternate=null,D.alternate=null,D.flags|=2),v=D.pendingProps,ne=V._init,V=ne(V._payload),D.type=V,ne=D.tag=hM(V),v=bo(V,v),ne){case 0:D=zA(null,D,V,v,Q);break e;case 1:D=wP(null,D,V,v,Q);break e;case 11:D=dr(null,D,V,v,Q);break e;case 14:D=Br(null,D,V,bo(V.type,v),H,Q);break e}throw Error(c(306,V,""))}return D;case 0:return H=D.type,V=D.pendingProps,V=D.elementType===H?V:bo(H,V),zA(v,D,H,V,Q);case 1:return H=D.type,V=D.pendingProps,V=D.elementType===H?V:bo(H,V),wP(v,D,H,V,Q);case 3:if(by(D),H=D.updateQueue,v===null||H===null)throw Error(c(282));if(H=D.pendingProps,V=D.memoizedState,V=V!==null?V.element:null,Mg(v,D),HA(D,H,null,Q),H=D.memoizedState.element,H===V)qg(),D=Gn(v,D,Q);else{if(V=D.stateNode,(ne=V.hydrate)&&(Z?(pa=fu(D.stateNode.containerInfo),Ya=D,ne=Va=!0):ne=!1),ne){if(Z&&(v=V.mutableSourceEagerHydrationData,v!=null))for(V=0;V=Gt&&ne>=Lr&&V<=vr&&Se<=Xt){v.splice(D,1);break}else if(H!==Gt||Q.width!==At.width||XtSe){if(!(ne!==Lr||Q.height!==At.height||vrV)){Gt>H&&(At.width+=Gt-H,At.x=H),vrne&&(At.height+=Lr-ne,At.y=ne),XtQ&&(Q=Se)),Se ")+` + +No matching component was found for: + `)+v.join(" > ")}return null},r.getPublicRootInstance=function(v){if(v=v.current,!v.child)return null;switch(v.child.tag){case 5:return Re(v.child.stateNode);default:return v.child.stateNode}},r.injectIntoDevTools=function(v){if(v={bundleType:v.bundleType,version:v.version,rendererPackageName:v.rendererPackageName,rendererConfig:v.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:f.ReactCurrentDispatcher,findHostInstanceByFiber:dM,findFiberByHostInstance:v.findFiberByHostInstance||mM,findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null},typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u")v=!1;else{var D=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!D.isDisabled&&D.supportsFiber)try{Xe=D.inject(v),Ha=D}catch{}v=!0}return v},r.observeVisibleRects=function(v,D,Q,H){if(!qt)throw Error(c(363));v=p2(v,D);var V=nn(v,Q,H).disconnect;return{disconnect:function(){V()}}},r.registerMutableSourceForHydration=function(v,D){var Q=D._getVersion;Q=Q(D._source),v.mutableSourceEagerHydrationData==null?v.mutableSourceEagerHydrationData=[D,Q]:v.mutableSourceEagerHydrationData.push(D,Q)},r.runWithPriority=function(v,D){var Q=cc;try{return cc=v,D()}finally{cc=Q}},r.shouldSuspend=function(){return!1},r.unbatchedUpdates=function(v,D){var Q=xr;xr&=-2,xr|=8;try{return v(D)}finally{xr=Q,xr===0&&(Pf(),Tn())}},r.updateContainer=function(v,D,Q,H){var V=D.current,ne=To(),Se=vs(V);e:if(Q){Q=Q._reactInternals;t:{if(we(Q)!==Q||Q.tag!==1)throw Error(c(170));var Ue=Q;do{switch(Ue.tag){case 3:Ue=Ue.stateNode.context;break t;case 1:if(Jn(Ue.type)){Ue=Ue.stateNode.__reactInternalMemoizedMergedChildContext;break t}}Ue=Ue.return}while(Ue!==null);throw Error(c(171))}if(Q.tag===1){var At=Q.type;if(Jn(At)){Q=La(Q,At,Ue);break e}}Q=Ue}else Q=ca;return D.context===null?D.context=Q:D.pendingContext=Q,D=bl(ne,Se),D.payload={element:v},H=H===void 0?null:H,H!==null&&(D.callback=H),Pl(V,D),Rl(V,Se,ne),Se},r}});var UDe=L((Ghr,_De)=>{"use strict";_De.exports=MDe()});var jDe=L((Whr,HDe)=>{"use strict";var zPt={ALIGN_COUNT:8,ALIGN_AUTO:0,ALIGN_FLEX_START:1,ALIGN_CENTER:2,ALIGN_FLEX_END:3,ALIGN_STRETCH:4,ALIGN_BASELINE:5,ALIGN_SPACE_BETWEEN:6,ALIGN_SPACE_AROUND:7,DIMENSION_COUNT:2,DIMENSION_WIDTH:0,DIMENSION_HEIGHT:1,DIRECTION_COUNT:3,DIRECTION_INHERIT:0,DIRECTION_LTR:1,DIRECTION_RTL:2,DISPLAY_COUNT:2,DISPLAY_FLEX:0,DISPLAY_NONE:1,EDGE_COUNT:9,EDGE_LEFT:0,EDGE_TOP:1,EDGE_RIGHT:2,EDGE_BOTTOM:3,EDGE_START:4,EDGE_END:5,EDGE_HORIZONTAL:6,EDGE_VERTICAL:7,EDGE_ALL:8,EXPERIMENTAL_FEATURE_COUNT:1,EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS:0,FLEX_DIRECTION_COUNT:4,FLEX_DIRECTION_COLUMN:0,FLEX_DIRECTION_COLUMN_REVERSE:1,FLEX_DIRECTION_ROW:2,FLEX_DIRECTION_ROW_REVERSE:3,JUSTIFY_COUNT:6,JUSTIFY_FLEX_START:0,JUSTIFY_CENTER:1,JUSTIFY_FLEX_END:2,JUSTIFY_SPACE_BETWEEN:3,JUSTIFY_SPACE_AROUND:4,JUSTIFY_SPACE_EVENLY:5,LOG_LEVEL_COUNT:6,LOG_LEVEL_ERROR:0,LOG_LEVEL_WARN:1,LOG_LEVEL_INFO:2,LOG_LEVEL_DEBUG:3,LOG_LEVEL_VERBOSE:4,LOG_LEVEL_FATAL:5,MEASURE_MODE_COUNT:3,MEASURE_MODE_UNDEFINED:0,MEASURE_MODE_EXACTLY:1,MEASURE_MODE_AT_MOST:2,NODE_TYPE_COUNT:2,NODE_TYPE_DEFAULT:0,NODE_TYPE_TEXT:1,OVERFLOW_COUNT:3,OVERFLOW_VISIBLE:0,OVERFLOW_HIDDEN:1,OVERFLOW_SCROLL:2,POSITION_TYPE_COUNT:2,POSITION_TYPE_RELATIVE:0,POSITION_TYPE_ABSOLUTE:1,PRINT_OPTIONS_COUNT:3,PRINT_OPTIONS_LAYOUT:1,PRINT_OPTIONS_STYLE:2,PRINT_OPTIONS_CHILDREN:4,UNIT_COUNT:4,UNIT_UNDEFINED:0,UNIT_POINT:1,UNIT_PERCENT:2,UNIT_AUTO:3,WRAP_COUNT:3,WRAP_NO_WRAP:0,WRAP_WRAP:1,WRAP_WRAP_REVERSE:2};HDe.exports=zPt});var YDe=L((Yhr,WDe)=>{"use strict";var ZPt=Object.assign||function(t){for(var e=1;e"}}]),t}(),qDe=function(){LF(t,null,[{key:"fromJS",value:function(r){var s=r.width,a=r.height;return new t(s,a)}}]);function t(e,r){bW(this,t),this.width=e,this.height=r}return LF(t,[{key:"fromJS",value:function(r){r(this.width,this.height)}},{key:"toString",value:function(){return""}}]),t}(),GDe=function(){function t(e,r){bW(this,t),this.unit=e,this.value=r}return LF(t,[{key:"fromJS",value:function(r){r(this.unit,this.value)}},{key:"toString",value:function(){switch(this.unit){case rf.UNIT_POINT:return String(this.value);case rf.UNIT_PERCENT:return this.value+"%";case rf.UNIT_AUTO:return"auto";default:return this.value+"?"}}},{key:"valueOf",value:function(){return this.value}}]),t}();WDe.exports=function(t,e){function r(c,f,p){var h=c[f];c[f]=function(){for(var E=arguments.length,C=Array(E),S=0;S1?C-1:0),P=1;P1&&arguments[1]!==void 0?arguments[1]:NaN,p=arguments.length>2&&arguments[2]!==void 0?arguments[2]:NaN,h=arguments.length>3&&arguments[3]!==void 0?arguments[3]:rf.DIRECTION_LTR;return c.call(this,f,p,h)}),ZPt({Config:e.Config,Node:e.Node,Layout:t("Layout",XPt),Size:t("Size",qDe),Value:t("Value",GDe),getInstanceCount:function(){return e.getInstanceCount.apply(e,arguments)}},rf)}});var VDe=L((exports,module)=>{(function(t,e){typeof define=="function"&&define.amd?define([],function(){return e}):typeof module=="object"&&module.exports?module.exports=e:(t.nbind=t.nbind||{}).init=e})(exports,function(Module,cb){typeof Module=="function"&&(cb=Module,Module={}),Module.onRuntimeInitialized=function(t,e){return function(){t&&t.apply(this,arguments);try{Module.ccall("nbind_init")}catch(r){e(r);return}e(null,{bind:Module._nbind_value,reflect:Module.NBind.reflect,queryType:Module.NBind.queryType,toggleLightGC:Module.toggleLightGC,lib:Module})}}(Module.onRuntimeInitialized,cb);var Module;Module||(Module=(typeof Module<"u"?Module:null)||{});var moduleOverrides={};for(var key in Module)Module.hasOwnProperty(key)&&(moduleOverrides[key]=Module[key]);var ENVIRONMENT_IS_WEB=!1,ENVIRONMENT_IS_WORKER=!1,ENVIRONMENT_IS_NODE=!1,ENVIRONMENT_IS_SHELL=!1;if(Module.ENVIRONMENT)if(Module.ENVIRONMENT==="WEB")ENVIRONMENT_IS_WEB=!0;else if(Module.ENVIRONMENT==="WORKER")ENVIRONMENT_IS_WORKER=!0;else if(Module.ENVIRONMENT==="NODE")ENVIRONMENT_IS_NODE=!0;else if(Module.ENVIRONMENT==="SHELL")ENVIRONMENT_IS_SHELL=!0;else throw new Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.");else ENVIRONMENT_IS_WEB=typeof window=="object",ENVIRONMENT_IS_WORKER=typeof importScripts=="function",ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof ye=="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER,ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){Module.print||(Module.print=console.log),Module.printErr||(Module.printErr=console.warn);var nodeFS,nodePath;Module.read=function(e,r){nodeFS||(nodeFS={}("")),nodePath||(nodePath={}("")),e=nodePath.normalize(e);var s=nodeFS.readFileSync(e);return r?s:s.toString()},Module.readBinary=function(e){var r=Module.read(e,!0);return r.buffer||(r=new Uint8Array(r)),assert(r.buffer),r},Module.load=function(e){globalEval(read(e))},Module.thisProgram||(process.argv.length>1?Module.thisProgram=process.argv[1].replace(/\\/g,"/"):Module.thisProgram="unknown-program"),Module.arguments=process.argv.slice(2),typeof module<"u"&&(module.exports=Module),Module.inspect=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL)Module.print||(Module.print=print),typeof printErr<"u"&&(Module.printErr=printErr),typeof read<"u"?Module.read=read:Module.read=function(){throw"no read() available"},Module.readBinary=function(e){if(typeof readbuffer=="function")return new Uint8Array(readbuffer(e));var r=read(e,"binary");return assert(typeof r=="object"),r},typeof scriptArgs<"u"?Module.arguments=scriptArgs:typeof arguments<"u"&&(Module.arguments=arguments),typeof quit=="function"&&(Module.quit=function(t,e){quit(t)});else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(Module.read=function(e){var r=new XMLHttpRequest;return r.open("GET",e,!1),r.send(null),r.responseText},ENVIRONMENT_IS_WORKER&&(Module.readBinary=function(e){var r=new XMLHttpRequest;return r.open("GET",e,!1),r.responseType="arraybuffer",r.send(null),new Uint8Array(r.response)}),Module.readAsync=function(e,r,s){var a=new XMLHttpRequest;a.open("GET",e,!0),a.responseType="arraybuffer",a.onload=function(){a.status==200||a.status==0&&a.response?r(a.response):s()},a.onerror=s,a.send(null)},typeof arguments<"u"&&(Module.arguments=arguments),typeof console<"u")Module.print||(Module.print=function(e){console.log(e)}),Module.printErr||(Module.printErr=function(e){console.warn(e)});else{var TRY_USE_DUMP=!1;Module.print||(Module.print=TRY_USE_DUMP&&typeof dump<"u"?function(t){dump(t)}:function(t){})}ENVIRONMENT_IS_WORKER&&(Module.load=importScripts),typeof Module.setWindowTitle>"u"&&(Module.setWindowTitle=function(t){document.title=t})}else throw"Unknown runtime environment. Where are we?";function globalEval(t){eval.call(null,t)}!Module.load&&Module.read&&(Module.load=function(e){globalEval(Module.read(e))}),Module.print||(Module.print=function(){}),Module.printErr||(Module.printErr=Module.print),Module.arguments||(Module.arguments=[]),Module.thisProgram||(Module.thisProgram="./this.program"),Module.quit||(Module.quit=function(t,e){throw e}),Module.print=Module.print,Module.printErr=Module.printErr,Module.preRun=[],Module.postRun=[];for(var key in moduleOverrides)moduleOverrides.hasOwnProperty(key)&&(Module[key]=moduleOverrides[key]);moduleOverrides=void 0;var Runtime={setTempRet0:function(t){return tempRet0=t,t},getTempRet0:function(){return tempRet0},stackSave:function(){return STACKTOP},stackRestore:function(t){STACKTOP=t},getNativeTypeSize:function(t){switch(t){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(t[t.length-1]==="*")return Runtime.QUANTUM_SIZE;if(t[0]==="i"){var e=parseInt(t.substr(1));return assert(e%8===0),e/8}else return 0}}},getNativeFieldSize:function(t){return Math.max(Runtime.getNativeTypeSize(t),Runtime.QUANTUM_SIZE)},STACK_ALIGN:16,prepVararg:function(t,e){return e==="double"||e==="i64"?t&7&&(assert((t&7)===4),t+=4):assert((t&3)===0),t},getAlignSize:function(t,e,r){return!r&&(t=="i64"||t=="double")?8:t?Math.min(e||(t?Runtime.getNativeFieldSize(t):0),Runtime.QUANTUM_SIZE):Math.min(e,8)},dynCall:function(t,e,r){return r&&r.length?Module["dynCall_"+t].apply(null,[e].concat(r)):Module["dynCall_"+t].call(null,e)},functionPointers:[],addFunction:function(t){for(var e=0;e>2],r=(e+t+15|0)&-16;if(HEAP32[DYNAMICTOP_PTR>>2]=r,r>=TOTAL_MEMORY){var s=enlargeMemory();if(!s)return HEAP32[DYNAMICTOP_PTR>>2]=e,0}return e},alignMemory:function(t,e){var r=t=Math.ceil(t/(e||16))*(e||16);return r},makeBigInt:function(t,e,r){var s=r?+(t>>>0)+ +(e>>>0)*4294967296:+(t>>>0)+ +(e|0)*4294967296;return s},GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};Module.Runtime=Runtime;var ABORT=0,EXITSTATUS=0;function assert(t,e){t||abort("Assertion failed: "+e)}function getCFunc(ident){var func=Module["_"+ident];if(!func)try{func=eval("_"+ident)}catch(t){}return assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)"),func}var cwrap,ccall;(function(){var JSfuncs={stackSave:function(){Runtime.stackSave()},stackRestore:function(){Runtime.stackRestore()},arrayToC:function(t){var e=Runtime.stackAlloc(t.length);return writeArrayToMemory(t,e),e},stringToC:function(t){var e=0;if(t!=null&&t!==0){var r=(t.length<<2)+1;e=Runtime.stackAlloc(r),stringToUTF8(t,e,r)}return e}},toC={string:JSfuncs.stringToC,array:JSfuncs.arrayToC};ccall=function(e,r,s,a,n){var c=getCFunc(e),f=[],p=0;if(a)for(var h=0;h>0]=e;break;case"i8":HEAP8[t>>0]=e;break;case"i16":HEAP16[t>>1]=e;break;case"i32":HEAP32[t>>2]=e;break;case"i64":tempI64=[e>>>0,(tempDouble=e,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[t>>2]=tempI64[0],HEAP32[t+4>>2]=tempI64[1];break;case"float":HEAPF32[t>>2]=e;break;case"double":HEAPF64[t>>3]=e;break;default:abort("invalid type for setValue: "+r)}}Module.setValue=setValue;function getValue(t,e,r){switch(e=e||"i8",e.charAt(e.length-1)==="*"&&(e="i32"),e){case"i1":return HEAP8[t>>0];case"i8":return HEAP8[t>>0];case"i16":return HEAP16[t>>1];case"i32":return HEAP32[t>>2];case"i64":return HEAP32[t>>2];case"float":return HEAPF32[t>>2];case"double":return HEAPF64[t>>3];default:abort("invalid type for setValue: "+e)}return null}Module.getValue=getValue;var ALLOC_NORMAL=0,ALLOC_STACK=1,ALLOC_STATIC=2,ALLOC_DYNAMIC=3,ALLOC_NONE=4;Module.ALLOC_NORMAL=ALLOC_NORMAL,Module.ALLOC_STACK=ALLOC_STACK,Module.ALLOC_STATIC=ALLOC_STATIC,Module.ALLOC_DYNAMIC=ALLOC_DYNAMIC,Module.ALLOC_NONE=ALLOC_NONE;function allocate(t,e,r,s){var a,n;typeof t=="number"?(a=!0,n=t):(a=!1,n=t.length);var c=typeof e=="string"?e:null,f;if(r==ALLOC_NONE?f=s:f=[typeof _malloc=="function"?_malloc:Runtime.staticAlloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][r===void 0?ALLOC_STATIC:r](Math.max(n,c?1:e.length)),a){var s=f,p;for(assert((f&3)==0),p=f+(n&-4);s>2]=0;for(p=f+n;s>0]=0;return f}if(c==="i8")return t.subarray||t.slice?HEAPU8.set(t,f):HEAPU8.set(new Uint8Array(t),f),f;for(var h=0,E,C,S;h>0],r|=s,!(s==0&&!e||(a++,e&&a==e)););e||(e=a);var n="";if(r<128){for(var c=1024,f;e>0;)f=String.fromCharCode.apply(String,HEAPU8.subarray(t,t+Math.min(e,c))),n=n?n+f:f,t+=c,e-=c;return n}return Module.UTF8ToString(t)}Module.Pointer_stringify=Pointer_stringify;function AsciiToString(t){for(var e="";;){var r=HEAP8[t++>>0];if(!r)return e;e+=String.fromCharCode(r)}}Module.AsciiToString=AsciiToString;function stringToAscii(t,e){return writeAsciiToMemory(t,e,!1)}Module.stringToAscii=stringToAscii;var UTF8Decoder=typeof TextDecoder<"u"?new TextDecoder("utf8"):void 0;function UTF8ArrayToString(t,e){for(var r=e;t[r];)++r;if(r-e>16&&t.subarray&&UTF8Decoder)return UTF8Decoder.decode(t.subarray(e,r));for(var s,a,n,c,f,p,h="";;){if(s=t[e++],!s)return h;if(!(s&128)){h+=String.fromCharCode(s);continue}if(a=t[e++]&63,(s&224)==192){h+=String.fromCharCode((s&31)<<6|a);continue}if(n=t[e++]&63,(s&240)==224?s=(s&15)<<12|a<<6|n:(c=t[e++]&63,(s&248)==240?s=(s&7)<<18|a<<12|n<<6|c:(f=t[e++]&63,(s&252)==248?s=(s&3)<<24|a<<18|n<<12|c<<6|f:(p=t[e++]&63,s=(s&1)<<30|a<<24|n<<18|c<<12|f<<6|p))),s<65536)h+=String.fromCharCode(s);else{var E=s-65536;h+=String.fromCharCode(55296|E>>10,56320|E&1023)}}}Module.UTF8ArrayToString=UTF8ArrayToString;function UTF8ToString(t){return UTF8ArrayToString(HEAPU8,t)}Module.UTF8ToString=UTF8ToString;function stringToUTF8Array(t,e,r,s){if(!(s>0))return 0;for(var a=r,n=r+s-1,c=0;c=55296&&f<=57343&&(f=65536+((f&1023)<<10)|t.charCodeAt(++c)&1023),f<=127){if(r>=n)break;e[r++]=f}else if(f<=2047){if(r+1>=n)break;e[r++]=192|f>>6,e[r++]=128|f&63}else if(f<=65535){if(r+2>=n)break;e[r++]=224|f>>12,e[r++]=128|f>>6&63,e[r++]=128|f&63}else if(f<=2097151){if(r+3>=n)break;e[r++]=240|f>>18,e[r++]=128|f>>12&63,e[r++]=128|f>>6&63,e[r++]=128|f&63}else if(f<=67108863){if(r+4>=n)break;e[r++]=248|f>>24,e[r++]=128|f>>18&63,e[r++]=128|f>>12&63,e[r++]=128|f>>6&63,e[r++]=128|f&63}else{if(r+5>=n)break;e[r++]=252|f>>30,e[r++]=128|f>>24&63,e[r++]=128|f>>18&63,e[r++]=128|f>>12&63,e[r++]=128|f>>6&63,e[r++]=128|f&63}}return e[r]=0,r-a}Module.stringToUTF8Array=stringToUTF8Array;function stringToUTF8(t,e,r){return stringToUTF8Array(t,HEAPU8,e,r)}Module.stringToUTF8=stringToUTF8;function lengthBytesUTF8(t){for(var e=0,r=0;r=55296&&s<=57343&&(s=65536+((s&1023)<<10)|t.charCodeAt(++r)&1023),s<=127?++e:s<=2047?e+=2:s<=65535?e+=3:s<=2097151?e+=4:s<=67108863?e+=5:e+=6}return e}Module.lengthBytesUTF8=lengthBytesUTF8;var UTF16Decoder=typeof TextDecoder<"u"?new TextDecoder("utf-16le"):void 0;function demangle(t){var e=Module.___cxa_demangle||Module.__cxa_demangle;if(e){try{var r=t.substr(1),s=lengthBytesUTF8(r)+1,a=_malloc(s);stringToUTF8(r,a,s);var n=_malloc(4),c=e(a,0,0,n);if(getValue(n,"i32")===0&&c)return Pointer_stringify(c)}catch{}finally{a&&_free(a),n&&_free(n),c&&_free(c)}return t}return Runtime.warnOnce("warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling"),t}function demangleAll(t){var e=/__Z[\w\d_]+/g;return t.replace(e,function(r){var s=demangle(r);return r===s?r:r+" ["+s+"]"})}function jsStackTrace(){var t=new Error;if(!t.stack){try{throw new Error(0)}catch(e){t=e}if(!t.stack)return"(no stack trace available)"}return t.stack.toString()}function stackTrace(){var t=jsStackTrace();return Module.extraStackTrace&&(t+=` +`+Module.extraStackTrace()),demangleAll(t)}Module.stackTrace=stackTrace;var HEAP,buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferViews(){Module.HEAP8=HEAP8=new Int8Array(buffer),Module.HEAP16=HEAP16=new Int16Array(buffer),Module.HEAP32=HEAP32=new Int32Array(buffer),Module.HEAPU8=HEAPU8=new Uint8Array(buffer),Module.HEAPU16=HEAPU16=new Uint16Array(buffer),Module.HEAPU32=HEAPU32=new Uint32Array(buffer),Module.HEAPF32=HEAPF32=new Float32Array(buffer),Module.HEAPF64=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed,STACK_BASE,STACKTOP,STACK_MAX,DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0,staticSealed=!1;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module.TOTAL_STACK||5242880,TOTAL_MEMORY=Module.TOTAL_MEMORY||134217728;TOTAL_MEMORY0;){var e=t.shift();if(typeof e=="function"){e();continue}var r=e.func;typeof r=="number"?e.arg===void 0?Module.dynCall_v(r):Module.dynCall_vi(r,e.arg):r(e.arg===void 0?null:e.arg)}}var __ATPRERUN__=[],__ATINIT__=[],__ATMAIN__=[],__ATEXIT__=[],__ATPOSTRUN__=[],runtimeInitialized=!1,runtimeExited=!1;function preRun(){if(Module.preRun)for(typeof Module.preRun=="function"&&(Module.preRun=[Module.preRun]);Module.preRun.length;)addOnPreRun(Module.preRun.shift());callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){runtimeInitialized||(runtimeInitialized=!0,callRuntimeCallbacks(__ATINIT__))}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__),runtimeExited=!0}function postRun(){if(Module.postRun)for(typeof Module.postRun=="function"&&(Module.postRun=[Module.postRun]);Module.postRun.length;)addOnPostRun(Module.postRun.shift());callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(t){__ATPRERUN__.unshift(t)}Module.addOnPreRun=addOnPreRun;function addOnInit(t){__ATINIT__.unshift(t)}Module.addOnInit=addOnInit;function addOnPreMain(t){__ATMAIN__.unshift(t)}Module.addOnPreMain=addOnPreMain;function addOnExit(t){__ATEXIT__.unshift(t)}Module.addOnExit=addOnExit;function addOnPostRun(t){__ATPOSTRUN__.unshift(t)}Module.addOnPostRun=addOnPostRun;function intArrayFromString(t,e,r){var s=r>0?r:lengthBytesUTF8(t)+1,a=new Array(s),n=stringToUTF8Array(t,a,0,a.length);return e&&(a.length=n),a}Module.intArrayFromString=intArrayFromString;function intArrayToString(t){for(var e=[],r=0;r255&&(s&=255),e.push(String.fromCharCode(s))}return e.join("")}Module.intArrayToString=intArrayToString;function writeStringToMemory(t,e,r){Runtime.warnOnce("writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!");var s,a;r&&(a=e+lengthBytesUTF8(t),s=HEAP8[a]),stringToUTF8(t,e,1/0),r&&(HEAP8[a]=s)}Module.writeStringToMemory=writeStringToMemory;function writeArrayToMemory(t,e){HEAP8.set(t,e)}Module.writeArrayToMemory=writeArrayToMemory;function writeAsciiToMemory(t,e,r){for(var s=0;s>0]=t.charCodeAt(s);r||(HEAP8[e>>0]=0)}if(Module.writeAsciiToMemory=writeAsciiToMemory,(!Math.imul||Math.imul(4294967295,5)!==-5)&&(Math.imul=function t(e,r){var s=e>>>16,a=e&65535,n=r>>>16,c=r&65535;return a*c+(s*c+a*n<<16)|0}),Math.imul=Math.imul,!Math.fround){var froundBuffer=new Float32Array(1);Math.fround=function(t){return froundBuffer[0]=t,froundBuffer[0]}}Math.fround=Math.fround,Math.clz32||(Math.clz32=function(t){t=t>>>0;for(var e=0;e<32;e++)if(t&1<<31-e)return e;return 32}),Math.clz32=Math.clz32,Math.trunc||(Math.trunc=function(t){return t<0?Math.ceil(t):Math.floor(t)}),Math.trunc=Math.trunc;var Math_abs=Math.abs,Math_cos=Math.cos,Math_sin=Math.sin,Math_tan=Math.tan,Math_acos=Math.acos,Math_asin=Math.asin,Math_atan=Math.atan,Math_atan2=Math.atan2,Math_exp=Math.exp,Math_log=Math.log,Math_sqrt=Math.sqrt,Math_ceil=Math.ceil,Math_floor=Math.floor,Math_pow=Math.pow,Math_imul=Math.imul,Math_fround=Math.fround,Math_round=Math.round,Math_min=Math.min,Math_clz32=Math.clz32,Math_trunc=Math.trunc,runDependencies=0,runDependencyWatcher=null,dependenciesFulfilled=null;function getUniqueRunDependency(t){return t}function addRunDependency(t){runDependencies++,Module.monitorRunDependencies&&Module.monitorRunDependencies(runDependencies)}Module.addRunDependency=addRunDependency;function removeRunDependency(t){if(runDependencies--,Module.monitorRunDependencies&&Module.monitorRunDependencies(runDependencies),runDependencies==0&&(runDependencyWatcher!==null&&(clearInterval(runDependencyWatcher),runDependencyWatcher=null),dependenciesFulfilled)){var e=dependenciesFulfilled;dependenciesFulfilled=null,e()}}Module.removeRunDependency=removeRunDependency,Module.preloadedImages={},Module.preloadedAudios={};var ASM_CONSTS=[function(t,e,r,s,a,n,c,f){return _nbind.callbackSignatureList[t].apply(this,arguments)}];function _emscripten_asm_const_iiiiiiii(t,e,r,s,a,n,c,f){return ASM_CONSTS[t](e,r,s,a,n,c,f)}function _emscripten_asm_const_iiiii(t,e,r,s,a){return ASM_CONSTS[t](e,r,s,a)}function _emscripten_asm_const_iiidddddd(t,e,r,s,a,n,c,f,p){return ASM_CONSTS[t](e,r,s,a,n,c,f,p)}function _emscripten_asm_const_iiididi(t,e,r,s,a,n,c){return ASM_CONSTS[t](e,r,s,a,n,c)}function _emscripten_asm_const_iiii(t,e,r,s){return ASM_CONSTS[t](e,r,s)}function _emscripten_asm_const_iiiid(t,e,r,s,a){return ASM_CONSTS[t](e,r,s,a)}function _emscripten_asm_const_iiiiii(t,e,r,s,a,n){return ASM_CONSTS[t](e,r,s,a,n)}STATIC_BASE=Runtime.GLOBAL_BASE,STATICTOP=STATIC_BASE+12800,__ATINIT__.push({func:function(){__GLOBAL__sub_I_Yoga_cpp()}},{func:function(){__GLOBAL__sub_I_nbind_cc()}},{func:function(){__GLOBAL__sub_I_common_cc()}},{func:function(){__GLOBAL__sub_I_Binding_cc()}}),allocate([0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,192,127,0,0,192,127,0,0,192,127,3,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,3,0,0,0,0,0,192,127,3,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,192,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,192,127,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,0,0,128,191,0,0,128,191,0,0,192,127,0,0,0,0,0,0,0,0,0,0,128,63,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,190,12,0,0,200,12,0,0,208,12,0,0,216,12,0,0,230,12,0,0,242,12,0,0,1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,192,127,3,0,0,0,180,45,0,0,181,45,0,0,182,45,0,0,181,45,0,0,182,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,4,0,0,0,183,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,184,45,0,0,185,45,0,0,181,45,0,0,181,45,0,0,182,45,0,0,186,45,0,0,185,45,0,0,148,4,0,0,3,0,0,0,187,45,0,0,164,4,0,0,188,45,0,0,2,0,0,0,189,45,0,0,164,4,0,0,188,45,0,0,185,45,0,0,164,4,0,0,185,45,0,0,164,4,0,0,188,45,0,0,181,45,0,0,182,45,0,0,181,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,6,0,0,0,1,0,0,0,7,0,0,0,183,45,0,0,182,45,0,0,181,45,0,0,190,45,0,0,190,45,0,0,182,45,0,0,182,45,0,0,185,45,0,0,181,45,0,0,185,45,0,0,182,45,0,0,181,45,0,0,185,45,0,0,182,45,0,0,185,45,0,0,48,5,0,0,3,0,0,0,56,5,0,0,1,0,0,0,189,45,0,0,185,45,0,0,164,4,0,0,76,5,0,0,2,0,0,0,191,45,0,0,186,45,0,0,182,45,0,0,185,45,0,0,192,45,0,0,185,45,0,0,182,45,0,0,186,45,0,0,185,45,0,0,76,5,0,0,76,5,0,0,136,5,0,0,182,45,0,0,181,45,0,0,2,0,0,0,190,45,0,0,136,5,0,0,56,19,0,0,156,5,0,0,2,0,0,0,184,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,8,0,0,0,9,0,0,0,1,0,0,0,10,0,0,0,204,5,0,0,181,45,0,0,181,45,0,0,2,0,0,0,180,45,0,0,204,5,0,0,2,0,0,0,195,45,0,0,236,5,0,0,97,19,0,0,198,45,0,0,211,45,0,0,212,45,0,0,213,45,0,0,214,45,0,0,215,45,0,0,188,45,0,0,182,45,0,0,216,45,0,0,217,45,0,0,218,45,0,0,219,45,0,0,192,45,0,0,181,45,0,0,0,0,0,0,185,45,0,0,110,19,0,0,186,45,0,0,115,19,0,0,221,45,0,0,120,19,0,0,148,4,0,0,132,19,0,0,96,6,0,0,145,19,0,0,222,45,0,0,164,19,0,0,223,45,0,0,173,19,0,0,0,0,0,0,3,0,0,0,104,6,0,0,1,0,0,0,187,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,11,0,0,0,12,0,0,0,1,0,0,0,13,0,0,0,185,45,0,0,224,45,0,0,164,6,0,0,188,45,0,0,172,6,0,0,180,6,0,0,2,0,0,0,188,6,0,0,7,0,0,0,224,45,0,0,7,0,0,0,164,6,0,0,1,0,0,0,213,45,0,0,185,45,0,0,224,45,0,0,172,6,0,0,185,45,0,0,224,45,0,0,164,6,0,0,185,45,0,0,224,45,0,0,211,45,0,0,211,45,0,0,222,45,0,0,211,45,0,0,224,45,0,0,222,45,0,0,211,45,0,0,224,45,0,0,172,6,0,0,222,45,0,0,211,45,0,0,224,45,0,0,188,45,0,0,222,45,0,0,211,45,0,0,40,7,0,0,188,45,0,0,2,0,0,0,224,45,0,0,185,45,0,0,188,45,0,0,188,45,0,0,188,45,0,0,188,45,0,0,222,45,0,0,224,45,0,0,148,4,0,0,185,45,0,0,148,4,0,0,148,4,0,0,148,4,0,0,148,4,0,0,148,4,0,0,185,45,0,0,164,6,0,0,148,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,14,0,0,0,15,0,0,0,1,0,0,0,16,0,0,0,148,7,0,0,2,0,0,0,225,45,0,0,183,45,0,0,188,45,0,0,168,7,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,234,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,9,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,2,0,0,0,242,45,0,0,0,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,111,117,108,100,32,110,111,116,32,97,108,108,111,99,97,116,101,32,109,101,109,111,114,121,32,102,111,114,32,110,111,100,101,0,67,97,110,110,111,116,32,114,101,115,101,116,32,97,32,110,111,100,101,32,119,104,105,99,104,32,115,116,105,108,108,32,104,97,115,32,99,104,105,108,100,114,101,110,32,97,116,116,97,99,104,101,100,0,67,97,110,110,111,116,32,114,101,115,101,116,32,97,32,110,111,100,101,32,115,116,105,108,108,32,97,116,116,97,99,104,101,100,32,116,111,32,97,32,112,97,114,101,110,116,0,67,111,117,108,100,32,110,111,116,32,97,108,108,111,99,97,116,101,32,109,101,109,111,114,121,32,102,111,114,32,99,111,110,102,105,103,0,67,97,110,110,111,116,32,115,101,116,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,58,32,78,111,100,101,115,32,119,105,116,104,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,115,32,99,97,110,110,111,116,32,104,97,118,101,32,99,104,105,108,100,114,101,110,46,0,67,104,105,108,100,32,97,108,114,101,97,100,121,32,104,97,115,32,97,32,112,97,114,101,110,116,44,32,105,116,32,109,117,115,116,32,98,101,32,114,101,109,111,118,101,100,32,102,105,114,115,116,46,0,67,97,110,110,111,116,32,97,100,100,32,99,104,105,108,100,58,32,78,111,100,101,115,32,119,105,116,104,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,115,32,99,97,110,110,111,116,32,104,97,118,101,32,99,104,105,108,100,114,101,110,46,0,79,110,108,121,32,108,101,97,102,32,110,111,100,101,115,32,119,105,116,104,32,99,117,115,116,111,109,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,115,115,104,111,117,108,100,32,109,97,110,117,97,108,108,121,32,109,97,114,107,32,116,104,101,109,115,101,108,118,101,115,32,97,115,32,100,105,114,116,121,0,67,97,110,110,111,116,32,103,101,116,32,108,97,121,111,117,116,32,112,114,111,112,101,114,116,105,101,115,32,111,102,32,109,117,108,116,105,45,101,100,103,101,32,115,104,111,114,116,104,97,110,100,115,0,37,115,37,100,46,123,91,115,107,105,112,112,101,100,93,32,0,119,109,58,32,37,115,44,32,104,109,58,32,37,115,44,32,97,119,58,32,37,102,32,97,104,58,32,37,102,32,61,62,32,100,58,32,40,37,102,44,32,37,102,41,32,37,115,10,0,37,115,37,100,46,123,37,115,0,42,0,119,109,58,32,37,115,44,32,104,109,58,32,37,115,44,32,97,119,58,32,37,102,32,97,104,58,32,37,102,32,37,115,10,0,37,115,37,100,46,125,37,115,0,119,109,58,32,37,115,44,32,104,109,58,32,37,115,44,32,100,58,32,40,37,102,44,32,37,102,41,32,37,115,10,0,79,117,116,32,111,102,32,99,97,99,104,101,32,101,110,116,114,105,101,115,33,10,0,83,99,97,108,101,32,102,97,99,116,111,114,32,115,104,111,117,108,100,32,110,111,116,32,98,101,32,108,101,115,115,32,116,104,97,110,32,122,101,114,111,0,105,110,105,116,105,97,108,0,37,115,10,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0,85,78,68,69,70,73,78,69,68,0,69,88,65,67,84,76,89,0,65,84,95,77,79,83,84,0,76,65,89,95,85,78,68,69,70,73,78,69,68,0,76,65,89,95,69,88,65,67,84,76,89,0,76,65,89,95,65,84,95,77,79,83,84,0,97,118,97,105,108,97,98,108,101,87,105,100,116,104,32,105,115,32,105,110,100,101,102,105,110,105,116,101,32,115,111,32,119,105,100,116,104,77,101,97,115,117,114,101,77,111,100,101,32,109,117,115,116,32,98,101,32,89,71,77,101,97,115,117,114,101,77,111,100,101,85,110,100,101,102,105,110,101,100,0,97,118,97,105,108,97,98,108,101,72,101,105,103,104,116,32,105,115,32,105,110,100,101,102,105,110,105,116,101,32,115,111,32,104,101,105,103,104,116,77,101,97,115,117,114,101,77,111,100,101,32,109,117,115,116,32,98,101,32,89,71,77,101,97,115,117,114,101,77,111,100,101,85,110,100,101,102,105,110,101,100,0,102,108,101,120,0,115,116,114,101,116,99,104,0,109,117,108,116,105,108,105,110,101,45,115,116,114,101,116,99,104,0,69,120,112,101,99,116,101,100,32,110,111,100,101,32,116,111,32,104,97,118,101,32,99,117,115,116,111,109,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,0,109,101,97,115,117,114,101,0,69,120,112,101,99,116,32,99,117,115,116,111,109,32,98,97,115,101,108,105,110,101,32,102,117,110,99,116,105,111,110,32,116,111,32,110,111,116,32,114,101,116,117,114,110,32,78,97,78,0,97,98,115,45,109,101,97,115,117,114,101,0,97,98,115,45,108,97,121,111,117,116,0,78,111,100,101,0,99,114,101,97,116,101,68,101,102,97,117,108,116,0,99,114,101,97,116,101,87,105,116,104,67,111,110,102,105,103,0,100,101,115,116,114,111,121,0,114,101,115,101,116,0,99,111,112,121,83,116,121,108,101,0,115,101,116,80,111,115,105,116,105,111,110,84,121,112,101,0,115,101,116,80,111,115,105,116,105,111,110,0,115,101,116,80,111,115,105,116,105,111,110,80,101,114,99,101,110,116,0,115,101,116,65,108,105,103,110,67,111,110,116,101,110,116,0,115,101,116,65,108,105,103,110,73,116,101,109,115,0,115,101,116,65,108,105,103,110,83,101,108,102,0,115,101,116,70,108,101,120,68,105,114,101,99,116,105,111,110,0,115,101,116,70,108,101,120,87,114,97,112,0,115,101,116,74,117,115,116,105,102,121,67,111,110,116,101,110,116,0,115,101,116,77,97,114,103,105,110,0,115,101,116,77,97,114,103,105,110,80,101,114,99,101,110,116,0,115,101,116,77,97,114,103,105,110,65,117,116,111,0,115,101,116,79,118,101,114,102,108,111,119,0,115,101,116,68,105,115,112,108,97,121,0,115,101,116,70,108,101,120,0,115,101,116,70,108,101,120,66,97,115,105,115,0,115,101,116,70,108,101,120,66,97,115,105,115,80,101,114,99,101,110,116,0,115,101,116,70,108,101,120,71,114,111,119,0,115,101,116,70,108,101,120,83,104,114,105,110,107,0,115,101,116,87,105,100,116,104,0,115,101,116,87,105,100,116,104,80,101,114,99,101,110,116,0,115,101,116,87,105,100,116,104,65,117,116,111,0,115,101,116,72,101,105,103,104,116,0,115,101,116,72,101,105,103,104,116,80,101,114,99,101,110,116,0,115,101,116,72,101,105,103,104,116,65,117,116,111,0,115,101,116,77,105,110,87,105,100,116,104,0,115,101,116,77,105,110,87,105,100,116,104,80,101,114,99,101,110,116,0,115,101,116,77,105,110,72,101,105,103,104,116,0,115,101,116,77,105,110,72,101,105,103,104,116,80,101,114,99,101,110,116,0,115,101,116,77,97,120,87,105,100,116,104,0,115,101,116,77,97,120,87,105,100,116,104,80,101,114,99,101,110,116,0,115,101,116,77,97,120,72,101,105,103,104,116,0,115,101,116,77,97,120,72,101,105,103,104,116,80,101,114,99,101,110,116,0,115,101,116,65,115,112,101,99,116,82,97,116,105,111,0,115,101,116,66,111,114,100,101,114,0,115,101,116,80,97,100,100,105,110,103,0,115,101,116,80,97,100,100,105,110,103,80,101,114,99,101,110,116,0,103,101,116,80,111,115,105,116,105,111,110,84,121,112,101,0,103,101,116,80,111,115,105,116,105,111,110,0,103,101,116,65,108,105,103,110,67,111,110,116,101,110,116,0,103,101,116,65,108,105,103,110,73,116,101,109,115,0,103,101,116,65,108,105,103,110,83,101,108,102,0,103,101,116,70,108,101,120,68,105,114,101,99,116,105,111,110,0,103,101,116,70,108,101,120,87,114,97,112,0,103,101,116,74,117,115,116,105,102,121,67,111,110,116,101,110,116,0,103,101,116,77,97,114,103,105,110,0,103,101,116,70,108,101,120,66,97,115,105,115,0,103,101,116,70,108,101,120,71,114,111,119,0,103,101,116,70,108,101,120,83,104,114,105,110,107,0,103,101,116,87,105,100,116,104,0,103,101,116,72,101,105,103,104,116,0,103,101,116,77,105,110,87,105,100,116,104,0,103,101,116,77,105,110,72,101,105,103,104,116,0,103,101,116,77,97,120,87,105,100,116,104,0,103,101,116,77,97,120,72,101,105,103,104,116,0,103,101,116,65,115,112,101,99,116,82,97,116,105,111,0,103,101,116,66,111,114,100,101,114,0,103,101,116,79,118,101,114,102,108,111,119,0,103,101,116,68,105,115,112,108,97,121,0,103,101,116,80,97,100,100,105,110,103,0,105,110,115,101,114,116,67,104,105,108,100,0,114,101,109,111,118,101,67,104,105,108,100,0,103,101,116,67,104,105,108,100,67,111,117,110,116,0,103,101,116,80,97,114,101,110,116,0,103,101,116,67,104,105,108,100,0,115,101,116,77,101,97,115,117,114,101,70,117,110,99,0,117,110,115,101,116,77,101,97,115,117,114,101,70,117,110,99,0,109,97,114,107,68,105,114,116,121,0,105,115,68,105,114,116,121,0,99,97,108,99,117,108,97,116,101,76,97,121,111,117,116,0,103,101,116,67,111,109,112,117,116,101,100,76,101,102,116,0,103,101,116,67,111,109,112,117,116,101,100,82,105,103,104,116,0,103,101,116,67,111,109,112,117,116,101,100,84,111,112,0,103,101,116,67,111,109,112,117,116,101,100,66,111,116,116,111,109,0,103,101,116,67,111,109,112,117,116,101,100,87,105,100,116,104,0,103,101,116,67,111,109,112,117,116,101,100,72,101,105,103,104,116,0,103,101,116,67,111,109,112,117,116,101,100,76,97,121,111,117,116,0,103,101,116,67,111,109,112,117,116,101,100,77,97,114,103,105,110,0,103,101,116,67,111,109,112,117,116,101,100,66,111,114,100,101,114,0,103,101,116,67,111,109,112,117,116,101,100,80,97,100,100,105,110,103,0,67,111,110,102,105,103,0,99,114,101,97,116,101,0,115,101,116,69,120,112,101,114,105,109,101,110,116,97,108,70,101,97,116,117,114,101,69,110,97,98,108,101,100,0,115,101,116,80,111,105,110,116,83,99,97,108,101,70,97,99,116,111,114,0,105,115,69,120,112,101,114,105,109,101,110,116,97,108,70,101,97,116,117,114,101,69,110,97,98,108,101,100,0,86,97,108,117,101,0,76,97,121,111,117,116,0,83,105,122,101,0,103,101,116,73,110,115,116,97,110,99,101,67,111,117,110,116,0,73,110,116,54,52,0,1,1,1,2,2,4,4,4,4,8,8,4,8,118,111,105,100,0,98,111,111,108,0,115,116,100,58,58,115,116,114,105,110,103,0,99,98,70,117,110,99,116,105,111,110,32,38,0,99,111,110,115,116,32,99,98,70,117,110,99,116,105,111,110,32,38,0,69,120,116,101,114,110,97,108,0,66,117,102,102,101,114,0,78,66,105,110,100,73,68,0,78,66,105,110,100,0,98,105,110,100,95,118,97,108,117,101,0,114,101,102,108,101,99,116,0,113,117,101,114,121,84,121,112,101,0,108,97,108,108,111,99,0,108,114,101,115,101,116,0,123,114,101,116,117,114,110,40,95,110,98,105,110,100,46,99,97,108,108,98,97,99,107,83,105,103,110,97,116,117,114,101,76,105,115,116,91,36,48,93,46,97,112,112,108,121,40,116,104,105,115,44,97,114,103,117,109,101,110,116,115,41,41,59,125,0,95,110,98,105,110,100,95,110,101,119,0,17,0,10,0,17,17,17,0,0,0,0,5,0,0,0,0,0,0,9,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,15,10,17,17,17,3,10,7,0,1,19,9,11,11,0,0,9,6,11,0,0,11,0,6,17,0,0,0,17,17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,10,10,17,17,17,0,10,0,0,2,0,9,11,0,0,0,9,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,4,13,0,0,0,0,9,14,0,0,0,0,0,14,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,15,0,0,0,0,9,16,0,0,0,0,0,16,0,0,16,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,10,0,0,0,0,9,11,0,0,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,45,43,32,32,32,48,88,48,120,0,40,110,117,108,108,41,0,45,48,88,43,48,88,32,48,88,45,48,120,43,48,120,32,48,120,0,105,110,102,0,73,78,70,0,110,97,110,0,78,65,78,0,48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,46,0,84,33,34,25,13,1,2,3,17,75,28,12,16,4,11,29,18,30,39,104,110,111,112,113,98,32,5,6,15,19,20,21,26,8,22,7,40,36,23,24,9,10,14,27,31,37,35,131,130,125,38,42,43,60,61,62,63,67,71,74,77,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,105,106,107,108,114,115,116,121,122,123,124,0,73,108,108,101,103,97,108,32,98,121,116,101,32,115,101,113,117,101,110,99,101,0,68,111,109,97,105,110,32,101,114,114,111,114,0,82,101,115,117,108,116,32,110,111,116,32,114,101,112,114,101,115,101,110,116,97,98,108,101,0,78,111,116,32,97,32,116,116,121,0,80,101,114,109,105,115,115,105,111,110,32,100,101,110,105,101,100,0,79,112,101,114,97,116,105,111,110,32,110,111,116,32,112,101,114,109,105,116,116,101,100,0,78,111,32,115,117,99,104,32,102,105,108,101,32,111,114,32,100,105,114,101,99,116,111,114,121,0,78,111,32,115,117,99,104,32,112,114,111,99,101,115,115,0,70,105,108,101,32,101,120,105,115,116,115,0,86,97,108,117,101,32,116,111,111,32,108,97,114,103,101,32,102,111,114,32,100,97,116,97,32,116,121,112,101,0,78,111,32,115,112,97,99,101,32,108,101,102,116,32,111,110,32,100,101,118,105,99,101,0,79,117,116,32,111,102,32,109,101,109,111,114,121,0,82,101,115,111,117,114,99,101,32,98,117,115,121,0,73,110,116,101,114,114,117,112,116,101,100,32,115,121,115,116,101,109,32,99,97,108,108,0,82,101,115,111,117,114,99,101,32,116,101,109,112,111,114,97,114,105,108,121,32,117,110,97,118,97,105,108,97,98,108,101,0,73,110,118,97,108,105,100,32,115,101,101,107,0,67,114,111,115,115,45,100,101,118,105,99,101,32,108,105,110,107,0,82,101,97,100,45,111,110,108,121,32,102,105,108,101,32,115,121,115,116,101,109,0,68,105,114,101,99,116,111,114,121,32,110,111,116,32,101,109,112,116,121,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,112,101,101,114,0,79,112,101,114,97,116,105,111,110,32,116,105,109,101,100,32,111,117,116,0,67,111,110,110,101,99,116,105,111,110,32,114,101,102,117,115,101,100,0,72,111,115,116,32,105,115,32,100,111,119,110,0,72,111,115,116,32,105,115,32,117,110,114,101,97,99,104,97,98,108,101,0,65,100,100,114,101,115,115,32,105,110,32,117,115,101,0,66,114,111,107,101,110,32,112,105,112,101,0,73,47,79,32,101,114,114,111,114,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,32,111,114,32,97,100,100,114,101,115,115,0,66,108,111,99,107,32,100,101,118,105,99,101,32,114,101,113,117,105,114,101,100,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,0,78,111,116,32,97,32,100,105,114,101,99,116,111,114,121,0,73,115,32,97,32,100,105,114,101,99,116,111,114,121,0,84,101,120,116,32,102,105,108,101,32,98,117,115,121,0,69,120,101,99,32,102,111,114,109,97,116,32,101,114,114,111,114,0,73,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,0,65,114,103,117,109,101,110,116,32,108,105,115,116,32,116,111,111,32,108,111,110,103,0,83,121,109,98,111,108,105,99,32,108,105,110,107,32,108,111,111,112,0,70,105,108,101,110,97,109,101,32,116,111,111,32,108,111,110,103,0,84,111,111,32,109,97,110,121,32,111,112,101,110,32,102,105,108,101,115,32,105,110,32,115,121,115,116,101,109,0,78,111,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,115,32,97,118,97,105,108,97,98,108,101,0,66,97,100,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,0,78,111,32,99,104,105,108,100,32,112,114,111,99,101,115,115,0,66,97,100,32,97,100,100,114,101,115,115,0,70,105,108,101,32,116,111,111,32,108,97,114,103,101,0,84,111,111,32,109,97,110,121,32,108,105,110,107,115,0,78,111,32,108,111,99,107,115,32,97,118,97,105,108,97,98,108,101,0,82,101,115,111,117,114,99,101,32,100,101,97,100,108,111,99,107,32,119,111,117,108,100,32,111,99,99,117,114,0,83,116,97,116,101,32,110,111,116,32,114,101,99,111,118,101,114,97,98,108,101,0,80,114,101,118,105,111,117,115,32,111,119,110,101,114,32,100,105,101,100,0,79,112,101,114,97,116,105,111,110,32,99,97,110,99,101,108,101,100,0,70,117,110,99,116,105,111,110,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,0,78,111,32,109,101,115,115,97,103,101,32,111,102,32,100,101,115,105,114,101,100,32,116,121,112,101,0,73,100,101,110,116,105,102,105,101,114,32,114,101,109,111,118,101,100,0,68,101,118,105,99,101,32,110,111,116,32,97,32,115,116,114,101,97,109,0,78,111,32,100,97,116,97,32,97,118,97,105,108,97,98,108,101,0,68,101,118,105,99,101,32,116,105,109,101,111,117,116,0,79,117,116,32,111,102,32,115,116,114,101,97,109,115,32,114,101,115,111,117,114,99,101,115,0,76,105,110,107,32,104,97,115,32,98,101,101,110,32,115,101,118,101,114,101,100,0,80,114,111,116,111,99,111,108,32,101,114,114,111,114,0,66,97,100,32,109,101,115,115,97,103,101,0,70,105,108,101,32,100,101,115,99,114,105,112,116,111,114,32,105,110,32,98,97,100,32,115,116,97,116,101,0,78,111,116,32,97,32,115,111,99,107,101,116,0,68,101,115,116,105,110,97,116,105,111,110,32,97,100,100,114,101,115,115,32,114,101,113,117,105,114,101,100,0,77,101,115,115,97,103,101,32,116,111,111,32,108,97,114,103,101,0,80,114,111,116,111,99,111,108,32,119,114,111,110,103,32,116,121,112,101,32,102,111,114,32,115,111,99,107,101,116,0,80,114,111,116,111,99,111,108,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,80,114,111,116,111,99,111,108,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,83,111,99,107,101,116,32,116,121,112,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,78,111,116,32,115,117,112,112,111,114,116,101,100,0,80,114,111,116,111,99,111,108,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,65,100,100,114,101,115,115,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,32,98,121,32,112,114,111,116,111,99,111,108,0,65,100,100,114,101,115,115,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,78,101,116,119,111,114,107,32,105,115,32,100,111,119,110,0,78,101,116,119,111,114,107,32,117,110,114,101,97,99,104,97,98,108,101,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,110,101,116,119,111,114,107,0,67,111,110,110,101,99,116,105,111,110,32,97,98,111,114,116,101,100,0,78,111,32,98,117,102,102,101,114,32,115,112,97,99,101,32,97,118,97,105,108,97,98,108,101,0,83,111,99,107,101,116,32,105,115,32,99,111,110,110,101,99,116,101,100,0,83,111,99,107,101,116,32,110,111,116,32,99,111,110,110,101,99,116,101,100,0,67,97,110,110,111,116,32,115,101,110,100,32,97,102,116,101,114,32,115,111,99,107,101,116,32,115,104,117,116,100,111,119,110,0,79,112,101,114,97,116,105,111,110,32,97,108,114,101,97,100,121,32,105,110,32,112,114,111,103,114,101,115,115,0,79,112,101,114,97,116,105,111,110,32,105,110,32,112,114,111,103,114,101,115,115,0,83,116,97,108,101,32,102,105,108,101,32,104,97,110,100,108,101,0,82,101,109,111,116,101,32,73,47,79,32,101,114,114,111,114,0,81,117,111,116,97,32,101,120,99,101,101,100,101,100,0,78,111,32,109,101,100,105,117,109,32,102,111,117,110,100,0,87,114,111,110,103,32,109,101,100,105,117,109,32,116,121,112,101,0,78,111,32,101,114,114,111,114,32,105,110,102,111,114,109,97,116,105,111,110,0,0],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=STATICTOP;STATICTOP+=16;function _atexit(t,e){__ATEXIT__.unshift({func:t,arg:e})}function ___cxa_atexit(){return _atexit.apply(null,arguments)}function _abort(){Module.abort()}function __ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj(){Module.printErr("missing function: _ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj"),abort(-1)}function __decorate(t,e,r,s){var a=arguments.length,n=a<3?e:s===null?s=Object.getOwnPropertyDescriptor(e,r):s,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")n=Reflect.decorate(t,e,r,s);else for(var f=t.length-1;f>=0;f--)(c=t[f])&&(n=(a<3?c(n):a>3?c(e,r,n):c(e,r))||n);return a>3&&n&&Object.defineProperty(e,r,n),n}function _defineHidden(t){return function(e,r){Object.defineProperty(e,r,{configurable:!1,enumerable:!1,value:t,writable:!0})}}var _nbind={};function __nbind_free_external(t){_nbind.externalList[t].dereference(t)}function __nbind_reference_external(t){_nbind.externalList[t].reference()}function _llvm_stackrestore(t){var e=_llvm_stacksave,r=e.LLVM_SAVEDSTACKS[t];e.LLVM_SAVEDSTACKS.splice(t,1),Runtime.stackRestore(r)}function __nbind_register_pool(t,e,r,s){_nbind.Pool.pageSize=t,_nbind.Pool.usedPtr=e/4,_nbind.Pool.rootPtr=r,_nbind.Pool.pagePtr=s/4,HEAP32[e/4]=16909060,HEAP8[e]==1&&(_nbind.bigEndian=!0),HEAP32[e/4]=0,_nbind.makeTypeKindTbl=(n={},n[1024]=_nbind.PrimitiveType,n[64]=_nbind.Int64Type,n[2048]=_nbind.BindClass,n[3072]=_nbind.BindClassPtr,n[4096]=_nbind.SharedClassPtr,n[5120]=_nbind.ArrayType,n[6144]=_nbind.ArrayType,n[7168]=_nbind.CStringType,n[9216]=_nbind.CallbackType,n[10240]=_nbind.BindType,n),_nbind.makeTypeNameTbl={Buffer:_nbind.BufferType,External:_nbind.ExternalType,Int64:_nbind.Int64Type,_nbind_new:_nbind.CreateValueType,bool:_nbind.BooleanType,"cbFunction &":_nbind.CallbackType,"const cbFunction &":_nbind.CallbackType,"const std::string &":_nbind.StringType,"std::string":_nbind.StringType},Module.toggleLightGC=_nbind.toggleLightGC,_nbind.callUpcast=Module.dynCall_ii;var a=_nbind.makeType(_nbind.constructType,{flags:2048,id:0,name:""});a.proto=Module,_nbind.BindClass.list.push(a);var n}function _emscripten_set_main_loop_timing(t,e){if(Browser.mainLoop.timingMode=t,Browser.mainLoop.timingValue=e,!Browser.mainLoop.func)return 1;if(t==0)Browser.mainLoop.scheduler=function(){var c=Math.max(0,Browser.mainLoop.tickStartTime+e-_emscripten_get_now())|0;setTimeout(Browser.mainLoop.runner,c)},Browser.mainLoop.method="timeout";else if(t==1)Browser.mainLoop.scheduler=function(){Browser.requestAnimationFrame(Browser.mainLoop.runner)},Browser.mainLoop.method="rAF";else if(t==2){if(!window.setImmediate){let n=function(c){c.source===window&&c.data===s&&(c.stopPropagation(),r.shift()())};var a=n,r=[],s="setimmediate";window.addEventListener("message",n,!0),window.setImmediate=function(f){r.push(f),ENVIRONMENT_IS_WORKER?(Module.setImmediates===void 0&&(Module.setImmediates=[]),Module.setImmediates.push(f),window.postMessage({target:s})):window.postMessage(s,"*")}}Browser.mainLoop.scheduler=function(){window.setImmediate(Browser.mainLoop.runner)},Browser.mainLoop.method="immediate"}return 0}function _emscripten_get_now(){abort()}function _emscripten_set_main_loop(t,e,r,s,a){Module.noExitRuntime=!0,assert(!Browser.mainLoop.func,"emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters."),Browser.mainLoop.func=t,Browser.mainLoop.arg=s;var n;typeof s<"u"?n=function(){Module.dynCall_vi(t,s)}:n=function(){Module.dynCall_v(t)};var c=Browser.mainLoop.currentlyRunningMainloop;if(Browser.mainLoop.runner=function(){if(!ABORT){if(Browser.mainLoop.queue.length>0){var p=Date.now(),h=Browser.mainLoop.queue.shift();if(h.func(h.arg),Browser.mainLoop.remainingBlockers){var E=Browser.mainLoop.remainingBlockers,C=E%1==0?E-1:Math.floor(E);h.counted?Browser.mainLoop.remainingBlockers=C:(C=C+.5,Browser.mainLoop.remainingBlockers=(8*E+C)/9)}if(console.log('main loop blocker "'+h.name+'" took '+(Date.now()-p)+" ms"),Browser.mainLoop.updateStatus(),c1&&Browser.mainLoop.currentFrameNumber%Browser.mainLoop.timingValue!=0){Browser.mainLoop.scheduler();return}else Browser.mainLoop.timingMode==0&&(Browser.mainLoop.tickStartTime=_emscripten_get_now());Browser.mainLoop.method==="timeout"&&Module.ctx&&(Module.printErr("Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!"),Browser.mainLoop.method=""),Browser.mainLoop.runIter(n),!(c0?_emscripten_set_main_loop_timing(0,1e3/e):_emscripten_set_main_loop_timing(1,1),Browser.mainLoop.scheduler()),r)throw"SimulateInfiniteLoop"}var Browser={mainLoop:{scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function(){Browser.mainLoop.scheduler=null,Browser.mainLoop.currentlyRunningMainloop++},resume:function(){Browser.mainLoop.currentlyRunningMainloop++;var t=Browser.mainLoop.timingMode,e=Browser.mainLoop.timingValue,r=Browser.mainLoop.func;Browser.mainLoop.func=null,_emscripten_set_main_loop(r,0,!1,Browser.mainLoop.arg,!0),_emscripten_set_main_loop_timing(t,e),Browser.mainLoop.scheduler()},updateStatus:function(){if(Module.setStatus){var t=Module.statusMessage||"Please wait...",e=Browser.mainLoop.remainingBlockers,r=Browser.mainLoop.expectedBlockers;e?e"u"&&(console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available."),Module.noImageDecoding=!0);var t={};t.canHandle=function(n){return!Module.noImageDecoding&&/\.(jpg|jpeg|png|bmp)$/i.test(n)},t.handle=function(n,c,f,p){var h=null;if(Browser.hasBlobConstructor)try{h=new Blob([n],{type:Browser.getMimetype(c)}),h.size!==n.length&&(h=new Blob([new Uint8Array(n).buffer],{type:Browser.getMimetype(c)}))}catch(P){Runtime.warnOnce("Blob constructor present but fails: "+P+"; falling back to blob builder")}if(!h){var E=new Browser.BlobBuilder;E.append(new Uint8Array(n).buffer),h=E.getBlob()}var C=Browser.URLObject.createObjectURL(h),S=new Image;S.onload=function(){assert(S.complete,"Image "+c+" could not be decoded");var I=document.createElement("canvas");I.width=S.width,I.height=S.height;var R=I.getContext("2d");R.drawImage(S,0,0),Module.preloadedImages[c]=I,Browser.URLObject.revokeObjectURL(C),f&&f(n)},S.onerror=function(I){console.log("Image "+C+" could not be decoded"),p&&p()},S.src=C},Module.preloadPlugins.push(t);var e={};e.canHandle=function(n){return!Module.noAudioDecoding&&n.substr(-4)in{".ogg":1,".wav":1,".mp3":1}},e.handle=function(n,c,f,p){var h=!1;function E(R){h||(h=!0,Module.preloadedAudios[c]=R,f&&f(n))}function C(){h||(h=!0,Module.preloadedAudios[c]=new Audio,p&&p())}if(Browser.hasBlobConstructor){try{var S=new Blob([n],{type:Browser.getMimetype(c)})}catch{return C()}var P=Browser.URLObject.createObjectURL(S),I=new Audio;I.addEventListener("canplaythrough",function(){E(I)},!1),I.onerror=function(N){if(h)return;console.log("warning: browser could not fully decode audio "+c+", trying slower base64 approach");function U(W){for(var te="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",ie="=",Ae="",ce=0,me=0,pe=0;pe=6;){var Be=ce>>me-6&63;me-=6,Ae+=te[Be]}return me==2?(Ae+=te[(ce&3)<<4],Ae+=ie+ie):me==4&&(Ae+=te[(ce&15)<<2],Ae+=ie),Ae}I.src="data:audio/x-"+c.substr(-3)+";base64,"+U(n),E(I)},I.src=P,Browser.safeSetTimeout(function(){E(I)},1e4)}else return C()},Module.preloadPlugins.push(e);function r(){Browser.pointerLock=document.pointerLockElement===Module.canvas||document.mozPointerLockElement===Module.canvas||document.webkitPointerLockElement===Module.canvas||document.msPointerLockElement===Module.canvas}var s=Module.canvas;s&&(s.requestPointerLock=s.requestPointerLock||s.mozRequestPointerLock||s.webkitRequestPointerLock||s.msRequestPointerLock||function(){},s.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock||document.msExitPointerLock||function(){},s.exitPointerLock=s.exitPointerLock.bind(document),document.addEventListener("pointerlockchange",r,!1),document.addEventListener("mozpointerlockchange",r,!1),document.addEventListener("webkitpointerlockchange",r,!1),document.addEventListener("mspointerlockchange",r,!1),Module.elementPointerLock&&s.addEventListener("click",function(a){!Browser.pointerLock&&Module.canvas.requestPointerLock&&(Module.canvas.requestPointerLock(),a.preventDefault())},!1))},createContext:function(t,e,r,s){if(e&&Module.ctx&&t==Module.canvas)return Module.ctx;var a,n;if(e){var c={antialias:!1,alpha:!1};if(s)for(var f in s)c[f]=s[f];n=GL.createContext(t,c),n&&(a=GL.getContext(n).GLctx)}else a=t.getContext("2d");return a?(r&&(e||assert(typeof GLctx>"u","cannot set in module if GLctx is used, but we are a non-GL context that would replace it"),Module.ctx=a,e&&GL.makeContextCurrent(n),Module.useWebGL=e,Browser.moduleContextCreatedCallbacks.forEach(function(p){p()}),Browser.init()),a):null},destroyContext:function(t,e,r){},fullscreenHandlersInstalled:!1,lockPointer:void 0,resizeCanvas:void 0,requestFullscreen:function(t,e,r){Browser.lockPointer=t,Browser.resizeCanvas=e,Browser.vrDevice=r,typeof Browser.lockPointer>"u"&&(Browser.lockPointer=!0),typeof Browser.resizeCanvas>"u"&&(Browser.resizeCanvas=!1),typeof Browser.vrDevice>"u"&&(Browser.vrDevice=null);var s=Module.canvas;function a(){Browser.isFullscreen=!1;var c=s.parentNode;(document.fullscreenElement||document.mozFullScreenElement||document.msFullscreenElement||document.webkitFullscreenElement||document.webkitCurrentFullScreenElement)===c?(s.exitFullscreen=document.exitFullscreen||document.cancelFullScreen||document.mozCancelFullScreen||document.msExitFullscreen||document.webkitCancelFullScreen||function(){},s.exitFullscreen=s.exitFullscreen.bind(document),Browser.lockPointer&&s.requestPointerLock(),Browser.isFullscreen=!0,Browser.resizeCanvas&&Browser.setFullscreenCanvasSize()):(c.parentNode.insertBefore(s,c),c.parentNode.removeChild(c),Browser.resizeCanvas&&Browser.setWindowedCanvasSize()),Module.onFullScreen&&Module.onFullScreen(Browser.isFullscreen),Module.onFullscreen&&Module.onFullscreen(Browser.isFullscreen),Browser.updateCanvasDimensions(s)}Browser.fullscreenHandlersInstalled||(Browser.fullscreenHandlersInstalled=!0,document.addEventListener("fullscreenchange",a,!1),document.addEventListener("mozfullscreenchange",a,!1),document.addEventListener("webkitfullscreenchange",a,!1),document.addEventListener("MSFullscreenChange",a,!1));var n=document.createElement("div");s.parentNode.insertBefore(n,s),n.appendChild(s),n.requestFullscreen=n.requestFullscreen||n.mozRequestFullScreen||n.msRequestFullscreen||(n.webkitRequestFullscreen?function(){n.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)}:null)||(n.webkitRequestFullScreen?function(){n.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)}:null),r?n.requestFullscreen({vrDisplay:r}):n.requestFullscreen()},requestFullScreen:function(t,e,r){return Module.printErr("Browser.requestFullScreen() is deprecated. Please call Browser.requestFullscreen instead."),Browser.requestFullScreen=function(s,a,n){return Browser.requestFullscreen(s,a,n)},Browser.requestFullscreen(t,e,r)},nextRAF:0,fakeRequestAnimationFrame:function(t){var e=Date.now();if(Browser.nextRAF===0)Browser.nextRAF=e+1e3/60;else for(;e+2>=Browser.nextRAF;)Browser.nextRAF+=1e3/60;var r=Math.max(Browser.nextRAF-e,0);setTimeout(t,r)},requestAnimationFrame:function t(e){typeof window>"u"?Browser.fakeRequestAnimationFrame(e):(window.requestAnimationFrame||(window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame||Browser.fakeRequestAnimationFrame),window.requestAnimationFrame(e))},safeCallback:function(t){return function(){if(!ABORT)return t.apply(null,arguments)}},allowAsyncCallbacks:!0,queuedAsyncCallbacks:[],pauseAsyncCallbacks:function(){Browser.allowAsyncCallbacks=!1},resumeAsyncCallbacks:function(){if(Browser.allowAsyncCallbacks=!0,Browser.queuedAsyncCallbacks.length>0){var t=Browser.queuedAsyncCallbacks;Browser.queuedAsyncCallbacks=[],t.forEach(function(e){e()})}},safeRequestAnimationFrame:function(t){return Browser.requestAnimationFrame(function(){ABORT||(Browser.allowAsyncCallbacks?t():Browser.queuedAsyncCallbacks.push(t))})},safeSetTimeout:function(t,e){return Module.noExitRuntime=!0,setTimeout(function(){ABORT||(Browser.allowAsyncCallbacks?t():Browser.queuedAsyncCallbacks.push(t))},e)},safeSetInterval:function(t,e){return Module.noExitRuntime=!0,setInterval(function(){ABORT||Browser.allowAsyncCallbacks&&t()},e)},getMimetype:function(t){return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",bmp:"image/bmp",ogg:"audio/ogg",wav:"audio/wav",mp3:"audio/mpeg"}[t.substr(t.lastIndexOf(".")+1)]},getUserMedia:function(t){window.getUserMedia||(window.getUserMedia=navigator.getUserMedia||navigator.mozGetUserMedia),window.getUserMedia(t)},getMovementX:function(t){return t.movementX||t.mozMovementX||t.webkitMovementX||0},getMovementY:function(t){return t.movementY||t.mozMovementY||t.webkitMovementY||0},getMouseWheelDelta:function(t){var e=0;switch(t.type){case"DOMMouseScroll":e=t.detail;break;case"mousewheel":e=t.wheelDelta;break;case"wheel":e=t.deltaY;break;default:throw"unrecognized mouse wheel event: "+t.type}return e},mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function(t){if(Browser.pointerLock)t.type!="mousemove"&&"mozMovementX"in t?Browser.mouseMovementX=Browser.mouseMovementY=0:(Browser.mouseMovementX=Browser.getMovementX(t),Browser.mouseMovementY=Browser.getMovementY(t)),typeof SDL<"u"?(Browser.mouseX=SDL.mouseX+Browser.mouseMovementX,Browser.mouseY=SDL.mouseY+Browser.mouseMovementY):(Browser.mouseX+=Browser.mouseMovementX,Browser.mouseY+=Browser.mouseMovementY);else{var e=Module.canvas.getBoundingClientRect(),r=Module.canvas.width,s=Module.canvas.height,a=typeof window.scrollX<"u"?window.scrollX:window.pageXOffset,n=typeof window.scrollY<"u"?window.scrollY:window.pageYOffset;if(t.type==="touchstart"||t.type==="touchend"||t.type==="touchmove"){var c=t.touch;if(c===void 0)return;var f=c.pageX-(a+e.left),p=c.pageY-(n+e.top);f=f*(r/e.width),p=p*(s/e.height);var h={x:f,y:p};if(t.type==="touchstart")Browser.lastTouches[c.identifier]=h,Browser.touches[c.identifier]=h;else if(t.type==="touchend"||t.type==="touchmove"){var E=Browser.touches[c.identifier];E||(E=h),Browser.lastTouches[c.identifier]=E,Browser.touches[c.identifier]=h}return}var C=t.pageX-(a+e.left),S=t.pageY-(n+e.top);C=C*(r/e.width),S=S*(s/e.height),Browser.mouseMovementX=C-Browser.mouseX,Browser.mouseMovementY=S-Browser.mouseY,Browser.mouseX=C,Browser.mouseY=S}},asyncLoad:function(t,e,r,s){var a=s?"":"al "+t;Module.readAsync(t,function(n){assert(n,'Loading data file "'+t+'" failed (no arrayBuffer).'),e(new Uint8Array(n)),a&&removeRunDependency(a)},function(n){if(r)r();else throw'Loading data file "'+t+'" failed.'}),a&&addRunDependency(a)},resizeListeners:[],updateResizeListeners:function(){var t=Module.canvas;Browser.resizeListeners.forEach(function(e){e(t.width,t.height)})},setCanvasSize:function(t,e,r){var s=Module.canvas;Browser.updateCanvasDimensions(s,t,e),r||Browser.updateResizeListeners()},windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function(){if(typeof SDL<"u"){var t=HEAPU32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2];t=t|8388608,HEAP32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2]=t}Browser.updateResizeListeners()},setWindowedCanvasSize:function(){if(typeof SDL<"u"){var t=HEAPU32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2];t=t&-8388609,HEAP32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2]=t}Browser.updateResizeListeners()},updateCanvasDimensions:function(t,e,r){e&&r?(t.widthNative=e,t.heightNative=r):(e=t.widthNative,r=t.heightNative);var s=e,a=r;if(Module.forcedAspectRatio&&Module.forcedAspectRatio>0&&(s/a>2];return e},getStr:function(){var t=Pointer_stringify(SYSCALLS.get());return t},get64:function(){var t=SYSCALLS.get(),e=SYSCALLS.get();return t>=0?assert(e===0):assert(e===-1),t},getZero:function(){assert(SYSCALLS.get()===0)}};function ___syscall6(t,e){SYSCALLS.varargs=e;try{var r=SYSCALLS.getStreamFromFD();return FS.close(r),0}catch(s){return(typeof FS>"u"||!(s instanceof FS.ErrnoError))&&abort(s),-s.errno}}function ___syscall54(t,e){SYSCALLS.varargs=e;try{return 0}catch(r){return(typeof FS>"u"||!(r instanceof FS.ErrnoError))&&abort(r),-r.errno}}function _typeModule(t){var e=[[0,1,"X"],[1,1,"const X"],[128,1,"X *"],[256,1,"X &"],[384,1,"X &&"],[512,1,"std::shared_ptr"],[640,1,"std::unique_ptr"],[5120,1,"std::vector"],[6144,2,"std::array"],[9216,-1,"std::function"]];function r(p,h,E,C,S,P){if(h==1){var I=C&896;(I==128||I==256||I==384)&&(p="X const")}var R;return P?R=E.replace("X",p).replace("Y",S):R=p.replace("X",E).replace("Y",S),R.replace(/([*&]) (?=[*&])/g,"$1")}function s(p,h,E,C,S){throw new Error(p+" type "+E.replace("X",h+"?")+(C?" with flag "+C:"")+" in "+S)}function a(p,h,E,C,S,P,I,R){P===void 0&&(P="X"),R===void 0&&(R=1);var N=E(p);if(N)return N;var U=C(p),W=U.placeholderFlag,te=e[W];I&&te&&(P=r(I[2],I[0],P,te[0],"?",!0));var ie;W==0&&(ie="Unbound"),W>=10&&(ie="Corrupt"),R>20&&(ie="Deeply nested"),ie&&s(ie,p,P,W,S||"?");var Ae=U.paramList[0],ce=a(Ae,h,E,C,S,P,te,R+1),me,pe={flags:te[0],id:p,name:"",paramList:[ce]},Be=[],Ce="?";switch(U.placeholderFlag){case 1:me=ce.spec;break;case 2:if((ce.flags&15360)==1024&&ce.spec.ptrSize==1){pe.flags=7168;break}case 3:case 6:case 5:me=ce.spec,ce.flags&15360;break;case 8:Ce=""+U.paramList[1],pe.paramList.push(U.paramList[1]);break;case 9:for(var g=0,we=U.paramList[1];g>2]=t),t}function _llvm_stacksave(){var t=_llvm_stacksave;return t.LLVM_SAVEDSTACKS||(t.LLVM_SAVEDSTACKS=[]),t.LLVM_SAVEDSTACKS.push(Runtime.stackSave()),t.LLVM_SAVEDSTACKS.length-1}function ___syscall140(t,e){SYSCALLS.varargs=e;try{var r=SYSCALLS.getStreamFromFD(),s=SYSCALLS.get(),a=SYSCALLS.get(),n=SYSCALLS.get(),c=SYSCALLS.get(),f=a;return FS.llseek(r,f,c),HEAP32[n>>2]=r.position,r.getdents&&f===0&&c===0&&(r.getdents=null),0}catch(p){return(typeof FS>"u"||!(p instanceof FS.ErrnoError))&&abort(p),-p.errno}}function ___syscall146(t,e){SYSCALLS.varargs=e;try{var r=SYSCALLS.get(),s=SYSCALLS.get(),a=SYSCALLS.get(),n=0;___syscall146.buffer||(___syscall146.buffers=[null,[],[]],___syscall146.printChar=function(E,C){var S=___syscall146.buffers[E];assert(S),C===0||C===10?((E===1?Module.print:Module.printErr)(UTF8ArrayToString(S,0)),S.length=0):S.push(C)});for(var c=0;c>2],p=HEAP32[s+(c*8+4)>>2],h=0;h"u"||!(E instanceof FS.ErrnoError))&&abort(E),-E.errno}}function __nbind_finish(){for(var t=0,e=_nbind.BindClass.list;tt.pageSize/2||e>t.pageSize-r){var s=_nbind.typeNameTbl.NBind.proto;return s.lalloc(e)}else return HEAPU32[t.usedPtr]=r+e,t.rootPtr+r},t.lreset=function(e,r){var s=HEAPU32[t.pagePtr];if(s){var a=_nbind.typeNameTbl.NBind.proto;a.lreset(e,r)}else HEAPU32[t.usedPtr]=e},t}();_nbind.Pool=Pool;function constructType(t,e){var r=t==10240?_nbind.makeTypeNameTbl[e.name]||_nbind.BindType:_nbind.makeTypeKindTbl[t],s=new r(e);return typeIdTbl[e.id]=s,_nbind.typeNameTbl[e.name]=s,s}_nbind.constructType=constructType;function getType(t){return typeIdTbl[t]}_nbind.getType=getType;function queryType(t){var e=HEAPU8[t],r=_nbind.structureList[e][1];t/=4,r<0&&(++t,r=HEAPU32[t]+1);var s=Array.prototype.slice.call(HEAPU32.subarray(t+1,t+1+r));return e==9&&(s=[s[0],s.slice(1)]),{paramList:s,placeholderFlag:e}}_nbind.queryType=queryType;function getTypes(t,e){return t.map(function(r){return typeof r=="number"?_nbind.getComplexType(r,constructType,getType,queryType,e):_nbind.typeNameTbl[r]})}_nbind.getTypes=getTypes;function readTypeIdList(t,e){return Array.prototype.slice.call(HEAPU32,t/4,t/4+e)}_nbind.readTypeIdList=readTypeIdList;function readAsciiString(t){for(var e=t;HEAPU8[e++];);return String.fromCharCode.apply("",HEAPU8.subarray(t,e-1))}_nbind.readAsciiString=readAsciiString;function readPolicyList(t){var e={};if(t)for(;;){var r=HEAPU32[t/4];if(!r)break;e[readAsciiString(r)]=!0,t+=4}return e}_nbind.readPolicyList=readPolicyList;function getDynCall(t,e){var r={float32_t:"d",float64_t:"d",int64_t:"d",uint64_t:"d",void:"v"},s=t.map(function(n){return r[n.name]||"i"}).join(""),a=Module["dynCall_"+s];if(!a)throw new Error("dynCall_"+s+" not found for "+e+"("+t.map(function(n){return n.name}).join(", ")+")");return a}_nbind.getDynCall=getDynCall;function addMethod(t,e,r,s){var a=t[e];t.hasOwnProperty(e)&&a?((a.arity||a.arity===0)&&(a=_nbind.makeOverloader(a,a.arity),t[e]=a),a.addMethod(r,s)):(r.arity=s,t[e]=r)}_nbind.addMethod=addMethod;function throwError(t){throw new Error(t)}_nbind.throwError=throwError,_nbind.bigEndian=!1,_a=_typeModule(_typeModule),_nbind.Type=_a.Type,_nbind.makeType=_a.makeType,_nbind.getComplexType=_a.getComplexType,_nbind.structureList=_a.structureList;var BindType=function(t){__extends(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.heap=HEAPU32,r.ptrSize=4,r}return e.prototype.needsWireRead=function(r){return!!this.wireRead||!!this.makeWireRead},e.prototype.needsWireWrite=function(r){return!!this.wireWrite||!!this.makeWireWrite},e}(_nbind.Type);_nbind.BindType=BindType;var PrimitiveType=function(t){__extends(e,t);function e(r){var s=t.call(this,r)||this,a=r.flags&32?{32:HEAPF32,64:HEAPF64}:r.flags&8?{8:HEAPU8,16:HEAPU16,32:HEAPU32}:{8:HEAP8,16:HEAP16,32:HEAP32};return s.heap=a[r.ptrSize*8],s.ptrSize=r.ptrSize,s}return e.prototype.needsWireWrite=function(r){return!!r&&!!r.Strict},e.prototype.makeWireWrite=function(r,s){return s&&s.Strict&&function(a){if(typeof a=="number")return a;throw new Error("Type mismatch")}},e}(BindType);_nbind.PrimitiveType=PrimitiveType;function pushCString(t,e){if(t==null){if(e&&e.Nullable)return 0;throw new Error("Type mismatch")}if(e&&e.Strict){if(typeof t!="string")throw new Error("Type mismatch")}else t=t.toString();var r=Module.lengthBytesUTF8(t)+1,s=_nbind.Pool.lalloc(r);return Module.stringToUTF8Array(t,HEAPU8,s,r),s}_nbind.pushCString=pushCString;function popCString(t){return t===0?null:Module.Pointer_stringify(t)}_nbind.popCString=popCString;var CStringType=function(t){__extends(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.wireRead=popCString,r.wireWrite=pushCString,r.readResources=[_nbind.resources.pool],r.writeResources=[_nbind.resources.pool],r}return e.prototype.makeWireWrite=function(r,s){return function(a){return pushCString(a,s)}},e}(BindType);_nbind.CStringType=CStringType;var BooleanType=function(t){__extends(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.wireRead=function(s){return!!s},r}return e.prototype.needsWireWrite=function(r){return!!r&&!!r.Strict},e.prototype.makeWireRead=function(r){return"!!("+r+")"},e.prototype.makeWireWrite=function(r,s){return s&&s.Strict&&function(a){if(typeof a=="boolean")return a;throw new Error("Type mismatch")}||r},e}(BindType);_nbind.BooleanType=BooleanType;var Wrapper=function(){function t(){}return t.prototype.persist=function(){this.__nbindState|=1},t}();_nbind.Wrapper=Wrapper;function makeBound(t,e){var r=function(s){__extends(a,s);function a(n,c,f,p){var h=s.call(this)||this;if(!(h instanceof a))return new(Function.prototype.bind.apply(a,Array.prototype.concat.apply([null],arguments)));var E=c,C=f,S=p;if(n!==_nbind.ptrMarker){var P=h.__nbindConstructor.apply(h,arguments);E=4608,S=HEAPU32[P/4],C=HEAPU32[P/4+1]}var I={configurable:!0,enumerable:!1,value:null,writable:!1},R={__nbindFlags:E,__nbindPtr:C};S&&(R.__nbindShared=S,_nbind.mark(h));for(var N=0,U=Object.keys(R);N>=1;var r=_nbind.valueList[t];return _nbind.valueList[t]=firstFreeValue,firstFreeValue=t,r}else{if(e)return _nbind.popShared(t,e);throw new Error("Invalid value slot "+t)}}_nbind.popValue=popValue;var valueBase=18446744073709552e3;function push64(t){return typeof t=="number"?t:pushValue(t)*4096+valueBase}function pop64(t){return t=3?c=Buffer.from(n):c=new Buffer(n),c.copy(s)}else getBuffer(s).set(n)}}_nbind.commitBuffer=commitBuffer;var dirtyList=[],gcTimer=0;function sweep(){for(var t=0,e=dirtyList;t>2]=DYNAMIC_BASE,staticSealed=!0;function invoke_viiiii(t,e,r,s,a,n){try{Module.dynCall_viiiii(t,e,r,s,a,n)}catch(c){if(typeof c!="number"&&c!=="longjmp")throw c;Module.setThrew(1,0)}}function invoke_vif(t,e,r){try{Module.dynCall_vif(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_vid(t,e,r){try{Module.dynCall_vid(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_fiff(t,e,r,s){try{return Module.dynCall_fiff(t,e,r,s)}catch(a){if(typeof a!="number"&&a!=="longjmp")throw a;Module.setThrew(1,0)}}function invoke_vi(t,e){try{Module.dynCall_vi(t,e)}catch(r){if(typeof r!="number"&&r!=="longjmp")throw r;Module.setThrew(1,0)}}function invoke_vii(t,e,r){try{Module.dynCall_vii(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_ii(t,e){try{return Module.dynCall_ii(t,e)}catch(r){if(typeof r!="number"&&r!=="longjmp")throw r;Module.setThrew(1,0)}}function invoke_viddi(t,e,r,s,a){try{Module.dynCall_viddi(t,e,r,s,a)}catch(n){if(typeof n!="number"&&n!=="longjmp")throw n;Module.setThrew(1,0)}}function invoke_vidd(t,e,r,s){try{Module.dynCall_vidd(t,e,r,s)}catch(a){if(typeof a!="number"&&a!=="longjmp")throw a;Module.setThrew(1,0)}}function invoke_iiii(t,e,r,s){try{return Module.dynCall_iiii(t,e,r,s)}catch(a){if(typeof a!="number"&&a!=="longjmp")throw a;Module.setThrew(1,0)}}function invoke_diii(t,e,r,s){try{return Module.dynCall_diii(t,e,r,s)}catch(a){if(typeof a!="number"&&a!=="longjmp")throw a;Module.setThrew(1,0)}}function invoke_di(t,e){try{return Module.dynCall_di(t,e)}catch(r){if(typeof r!="number"&&r!=="longjmp")throw r;Module.setThrew(1,0)}}function invoke_iid(t,e,r){try{return Module.dynCall_iid(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_iii(t,e,r){try{return Module.dynCall_iii(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_viiddi(t,e,r,s,a,n){try{Module.dynCall_viiddi(t,e,r,s,a,n)}catch(c){if(typeof c!="number"&&c!=="longjmp")throw c;Module.setThrew(1,0)}}function invoke_viiiiii(t,e,r,s,a,n,c){try{Module.dynCall_viiiiii(t,e,r,s,a,n,c)}catch(f){if(typeof f!="number"&&f!=="longjmp")throw f;Module.setThrew(1,0)}}function invoke_dii(t,e,r){try{return Module.dynCall_dii(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_i(t){try{return Module.dynCall_i(t)}catch(e){if(typeof e!="number"&&e!=="longjmp")throw e;Module.setThrew(1,0)}}function invoke_iiiiii(t,e,r,s,a,n){try{return Module.dynCall_iiiiii(t,e,r,s,a,n)}catch(c){if(typeof c!="number"&&c!=="longjmp")throw c;Module.setThrew(1,0)}}function invoke_viiid(t,e,r,s,a){try{Module.dynCall_viiid(t,e,r,s,a)}catch(n){if(typeof n!="number"&&n!=="longjmp")throw n;Module.setThrew(1,0)}}function invoke_viififi(t,e,r,s,a,n,c){try{Module.dynCall_viififi(t,e,r,s,a,n,c)}catch(f){if(typeof f!="number"&&f!=="longjmp")throw f;Module.setThrew(1,0)}}function invoke_viii(t,e,r,s){try{Module.dynCall_viii(t,e,r,s)}catch(a){if(typeof a!="number"&&a!=="longjmp")throw a;Module.setThrew(1,0)}}function invoke_v(t){try{Module.dynCall_v(t)}catch(e){if(typeof e!="number"&&e!=="longjmp")throw e;Module.setThrew(1,0)}}function invoke_viid(t,e,r,s){try{Module.dynCall_viid(t,e,r,s)}catch(a){if(typeof a!="number"&&a!=="longjmp")throw a;Module.setThrew(1,0)}}function invoke_idd(t,e,r){try{return Module.dynCall_idd(t,e,r)}catch(s){if(typeof s!="number"&&s!=="longjmp")throw s;Module.setThrew(1,0)}}function invoke_viiii(t,e,r,s,a){try{Module.dynCall_viiii(t,e,r,s,a)}catch(n){if(typeof n!="number"&&n!=="longjmp")throw n;Module.setThrew(1,0)}}Module.asmGlobalArg={Math,Int8Array,Int16Array,Int32Array,Uint8Array,Uint16Array,Uint32Array,Float32Array,Float64Array,NaN:NaN,Infinity:1/0},Module.asmLibraryArg={abort,assert,enlargeMemory,getTotalMemory,abortOnCannotGrowMemory,invoke_viiiii,invoke_vif,invoke_vid,invoke_fiff,invoke_vi,invoke_vii,invoke_ii,invoke_viddi,invoke_vidd,invoke_iiii,invoke_diii,invoke_di,invoke_iid,invoke_iii,invoke_viiddi,invoke_viiiiii,invoke_dii,invoke_i,invoke_iiiiii,invoke_viiid,invoke_viififi,invoke_viii,invoke_v,invoke_viid,invoke_idd,invoke_viiii,_emscripten_asm_const_iiiii,_emscripten_asm_const_iiidddddd,_emscripten_asm_const_iiiid,__nbind_reference_external,_emscripten_asm_const_iiiiiiii,_removeAccessorPrefix,_typeModule,__nbind_register_pool,__decorate,_llvm_stackrestore,___cxa_atexit,__extends,__nbind_get_value_object,__ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj,_emscripten_set_main_loop_timing,__nbind_register_primitive,__nbind_register_type,_emscripten_memcpy_big,__nbind_register_function,___setErrNo,__nbind_register_class,__nbind_finish,_abort,_nbind_value,_llvm_stacksave,___syscall54,_defineHidden,_emscripten_set_main_loop,_emscripten_get_now,__nbind_register_callback_signature,_emscripten_asm_const_iiiiii,__nbind_free_external,_emscripten_asm_const_iiii,_emscripten_asm_const_iiididi,___syscall6,_atexit,___syscall140,___syscall146,DYNAMICTOP_PTR,tempDoublePtr,ABORT,STACKTOP,STACK_MAX,cttz_i8,___dso_handle};var asm=function(t,e,r){var s=new t.Int8Array(r),a=new t.Int16Array(r),n=new t.Int32Array(r),c=new t.Uint8Array(r),f=new t.Uint16Array(r),p=new t.Uint32Array(r),h=new t.Float32Array(r),E=new t.Float64Array(r),C=e.DYNAMICTOP_PTR|0,S=e.tempDoublePtr|0,P=e.ABORT|0,I=e.STACKTOP|0,R=e.STACK_MAX|0,N=e.cttz_i8|0,U=e.___dso_handle|0,W=0,te=0,ie=0,Ae=0,ce=t.NaN,me=t.Infinity,pe=0,Be=0,Ce=0,g=0,we=0,Ee=0,fe=t.Math.floor,se=t.Math.abs,X=t.Math.sqrt,De=t.Math.pow,Re=t.Math.cos,gt=t.Math.sin,j=t.Math.tan,rt=t.Math.acos,Fe=t.Math.asin,Ne=t.Math.atan,Pe=t.Math.atan2,Ye=t.Math.exp,ke=t.Math.log,it=t.Math.ceil,_e=t.Math.imul,x=t.Math.min,w=t.Math.max,b=t.Math.clz32,y=t.Math.fround,F=e.abort,z=e.assert,Z=e.enlargeMemory,$=e.getTotalMemory,oe=e.abortOnCannotGrowMemory,xe=e.invoke_viiiii,Te=e.invoke_vif,lt=e.invoke_vid,Et=e.invoke_fiff,qt=e.invoke_vi,ir=e.invoke_vii,Pt=e.invoke_ii,gn=e.invoke_viddi,Pr=e.invoke_vidd,Ir=e.invoke_iiii,Nr=e.invoke_diii,nn=e.invoke_di,oi=e.invoke_iid,wo=e.invoke_iii,rs=e.invoke_viiddi,eo=e.invoke_viiiiii,Bo=e.invoke_dii,Hi=e.invoke_i,to=e.invoke_iiiiii,vo=e.invoke_viiid,RA=e.invoke_viififi,pf=e.invoke_viii,Eh=e.invoke_v,Ih=e.invoke_viid,ro=e.invoke_idd,jn=e.invoke_viiii,Rs=e._emscripten_asm_const_iiiii,no=e._emscripten_asm_const_iiidddddd,lu=e._emscripten_asm_const_iiiid,cu=e.__nbind_reference_external,uu=e._emscripten_asm_const_iiiiiiii,FA=e._removeAccessorPrefix,NA=e._typeModule,aa=e.__nbind_register_pool,la=e.__decorate,OA=e._llvm_stackrestore,gr=e.___cxa_atexit,So=e.__extends,Me=e.__nbind_get_value_object,fu=e.__ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj,Cr=e._emscripten_set_main_loop_timing,hf=e.__nbind_register_primitive,LA=e.__nbind_register_type,MA=e._emscripten_memcpy_big,Au=e.__nbind_register_function,pu=e.___setErrNo,ac=e.__nbind_register_class,ve=e.__nbind_finish,Nt=e._abort,lc=e._nbind_value,Ni=e._llvm_stacksave,io=e.___syscall54,Rt=e._defineHidden,xn=e._emscripten_set_main_loop,ca=e._emscripten_get_now,ji=e.__nbind_register_callback_signature,Oi=e._emscripten_asm_const_iiiiii,Oa=e.__nbind_free_external,dn=e._emscripten_asm_const_iiii,Jn=e._emscripten_asm_const_iiididi,hu=e.___syscall6,Ch=e._atexit,La=e.___syscall140,Ma=e.___syscall146,Ua=y(0);let Xe=y(0);function Ha(o){o=o|0;var l=0;return l=I,I=I+o|0,I=I+15&-16,l|0}function gf(){return I|0}function cc(o){o=o|0,I=o}function wn(o,l){o=o|0,l=l|0,I=o,R=l}function ua(o,l){o=o|0,l=l|0,W||(W=o,te=l)}function _A(o){o=o|0,Ee=o}function UA(){return Ee|0}function fa(){var o=0,l=0;Qr(8104,8,400)|0,Qr(8504,408,540)|0,o=9044,l=o+44|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));s[9088]=0,s[9089]=1,n[2273]=0,n[2274]=948,n[2275]=948,gr(17,8104,U|0)|0}function vl(o){o=o|0,ht(o+948|0)}function Mt(o){return o=y(o),((SP(o)|0)&2147483647)>>>0>2139095040|0}function kn(o,l,u){o=o|0,l=l|0,u=u|0;e:do if(n[o+(l<<3)+4>>2]|0)o=o+(l<<3)|0;else{if((l|2|0)==3&&n[o+60>>2]|0){o=o+56|0;break}switch(l|0){case 0:case 2:case 4:case 5:{if(n[o+52>>2]|0){o=o+48|0;break e}break}default:}if(n[o+68>>2]|0){o=o+64|0;break}else{o=(l|1|0)==5?948:u;break}}while(!1);return o|0}function Aa(o){o=o|0;var l=0;return l=ex(1e3)|0,ja(o,(l|0)!=0,2456),n[2276]=(n[2276]|0)+1,Qr(l|0,8104,1e3)|0,s[o+2>>0]|0&&(n[l+4>>2]=2,n[l+12>>2]=4),n[l+976>>2]=o,l|0}function ja(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;d=I,I=I+16|0,A=d,l||(n[A>>2]=u,Yg(o,5,3197,A)),I=d}function ns(){return Aa(956)|0}function uc(o){o=o|0;var l=0;return l=Jt(1e3)|0,gu(l,o),ja(n[o+976>>2]|0,1,2456),n[2276]=(n[2276]|0)+1,n[l+944>>2]=0,l|0}function gu(o,l){o=o|0,l=l|0;var u=0;Qr(o|0,l|0,948)|0,Dy(o+948|0,l+948|0),u=o+960|0,o=l+960|0,l=u+40|0;do n[u>>2]=n[o>>2],u=u+4|0,o=o+4|0;while((u|0)<(l|0))}function fc(o){o=o|0;var l=0,u=0,A=0,d=0;if(l=o+944|0,u=n[l>>2]|0,u|0&&(qa(u+948|0,o)|0,n[l>>2]=0),u=Li(o)|0,u|0){l=0;do n[(Cs(o,l)|0)+944>>2]=0,l=l+1|0;while((l|0)!=(u|0))}u=o+948|0,A=n[u>>2]|0,d=o+952|0,l=n[d>>2]|0,(l|0)!=(A|0)&&(n[d>>2]=l+(~((l+-4-A|0)>>>2)<<2)),Sl(u),tx(o),n[2276]=(n[2276]|0)+-1}function qa(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0;A=n[o>>2]|0,k=o+4|0,u=n[k>>2]|0,m=u;e:do if((A|0)==(u|0))d=A,B=4;else for(o=A;;){if((n[o>>2]|0)==(l|0)){d=o,B=4;break e}if(o=o+4|0,(o|0)==(u|0)){o=0;break}}while(!1);return(B|0)==4&&((d|0)!=(u|0)?(A=d+4|0,o=m-A|0,l=o>>2,l&&(F2(d|0,A|0,o|0)|0,u=n[k>>2]|0),o=d+(l<<2)|0,(u|0)==(o|0)||(n[k>>2]=u+(~((u+-4-o|0)>>>2)<<2)),o=1):o=0),o|0}function Li(o){return o=o|0,(n[o+952>>2]|0)-(n[o+948>>2]|0)>>2|0}function Cs(o,l){o=o|0,l=l|0;var u=0;return u=n[o+948>>2]|0,(n[o+952>>2]|0)-u>>2>>>0>l>>>0?o=n[u+(l<<2)>>2]|0:o=0,o|0}function Sl(o){o=o|0;var l=0,u=0,A=0,d=0;A=I,I=I+32|0,l=A,d=n[o>>2]|0,u=(n[o+4>>2]|0)-d|0,((n[o+8>>2]|0)-d|0)>>>0>u>>>0&&(d=u>>2,ky(l,d,d,o+8|0),DP(o,l),Qy(l)),I=A}function df(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0;_=Li(o)|0;do if(_|0){if((n[(Cs(o,0)|0)+944>>2]|0)==(o|0)){if(!(qa(o+948|0,l)|0))break;Qr(l+400|0,8504,540)|0,n[l+944>>2]=0,Oe(o);break}B=n[(n[o+976>>2]|0)+12>>2]|0,k=o+948|0,T=(B|0)==0,u=0,m=0;do A=n[(n[k>>2]|0)+(m<<2)>>2]|0,(A|0)==(l|0)?Oe(o):(d=uc(A)|0,n[(n[k>>2]|0)+(u<<2)>>2]=d,n[d+944>>2]=o,T||S_[B&15](A,d,o,u),u=u+1|0),m=m+1|0;while((m|0)!=(_|0));if(u>>>0<_>>>0){T=o+948|0,k=o+952|0,B=u,u=n[k>>2]|0;do m=(n[T>>2]|0)+(B<<2)|0,A=m+4|0,d=u-A|0,l=d>>2,l&&(F2(m|0,A|0,d|0)|0,u=n[k>>2]|0),d=u,A=m+(l<<2)|0,(d|0)!=(A|0)&&(u=d+(~((d+-4-A|0)>>>2)<<2)|0,n[k>>2]=u),B=B+1|0;while((B|0)!=(_|0))}}while(!1)}function Ac(o){o=o|0;var l=0,u=0,A=0,d=0;wi(o,(Li(o)|0)==0,2491),wi(o,(n[o+944>>2]|0)==0,2545),l=o+948|0,u=n[l>>2]|0,A=o+952|0,d=n[A>>2]|0,(d|0)!=(u|0)&&(n[A>>2]=d+(~((d+-4-u|0)>>>2)<<2)),Sl(l),l=o+976|0,u=n[l>>2]|0,Qr(o|0,8104,1e3)|0,s[u+2>>0]|0&&(n[o+4>>2]=2,n[o+12>>2]=4),n[l>>2]=u}function wi(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;d=I,I=I+16|0,A=d,l||(n[A>>2]=u,Qo(o,5,3197,A)),I=d}function Qn(){return n[2276]|0}function pc(){var o=0;return o=ex(20)|0,Je((o|0)!=0,2592),n[2277]=(n[2277]|0)+1,n[o>>2]=n[239],n[o+4>>2]=n[240],n[o+8>>2]=n[241],n[o+12>>2]=n[242],n[o+16>>2]=n[243],o|0}function Je(o,l){o=o|0,l=l|0;var u=0,A=0;A=I,I=I+16|0,u=A,o||(n[u>>2]=l,Qo(0,5,3197,u)),I=A}function st(o){o=o|0,tx(o),n[2277]=(n[2277]|0)+-1}function St(o,l){o=o|0,l=l|0;var u=0;l?(wi(o,(Li(o)|0)==0,2629),u=1):(u=0,l=0),n[o+964>>2]=l,n[o+988>>2]=u}function lr(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,m=A+8|0,d=A+4|0,B=A,n[d>>2]=l,wi(o,(n[l+944>>2]|0)==0,2709),wi(o,(n[o+964>>2]|0)==0,2763),ee(o),l=o+948|0,n[B>>2]=(n[l>>2]|0)+(u<<2),n[m>>2]=n[B>>2],Ie(l,m,d)|0,n[(n[d>>2]|0)+944>>2]=o,Oe(o),I=A}function ee(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0;if(u=Li(o)|0,u|0&&(n[(Cs(o,0)|0)+944>>2]|0)!=(o|0)){A=n[(n[o+976>>2]|0)+12>>2]|0,d=o+948|0,m=(A|0)==0,l=0;do B=n[(n[d>>2]|0)+(l<<2)>>2]|0,k=uc(B)|0,n[(n[d>>2]|0)+(l<<2)>>2]=k,n[k+944>>2]=o,m||S_[A&15](B,k,o,l),l=l+1|0;while((l|0)!=(u|0))}}function Ie(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0,tt=0,Ze=0;tt=I,I=I+64|0,G=tt+52|0,k=tt+48|0,ae=tt+28|0,We=tt+24|0,Le=tt+20|0,Qe=tt,A=n[o>>2]|0,m=A,l=A+((n[l>>2]|0)-m>>2<<2)|0,A=o+4|0,d=n[A>>2]|0,B=o+8|0;do if(d>>>0<(n[B>>2]|0)>>>0){if((l|0)==(d|0)){n[l>>2]=n[u>>2],n[A>>2]=(n[A>>2]|0)+4;break}bP(o,l,d,l+4|0),l>>>0<=u>>>0&&(u=(n[A>>2]|0)>>>0>u>>>0?u+4|0:u),n[l>>2]=n[u>>2]}else{A=(d-m>>2)+1|0,d=O(o)|0,d>>>0>>0&&sn(o),M=n[o>>2]|0,_=(n[B>>2]|0)-M|0,m=_>>1,ky(Qe,_>>2>>>0>>1>>>0?m>>>0>>0?A:m:d,l-M>>2,o+8|0),M=Qe+8|0,A=n[M>>2]|0,m=Qe+12|0,_=n[m>>2]|0,B=_,T=A;do if((A|0)==(_|0)){if(_=Qe+4|0,A=n[_>>2]|0,Ze=n[Qe>>2]|0,d=Ze,A>>>0<=Ze>>>0){A=B-d>>1,A=A|0?A:1,ky(ae,A,A>>>2,n[Qe+16>>2]|0),n[We>>2]=n[_>>2],n[Le>>2]=n[M>>2],n[k>>2]=n[We>>2],n[G>>2]=n[Le>>2],c2(ae,k,G),A=n[Qe>>2]|0,n[Qe>>2]=n[ae>>2],n[ae>>2]=A,A=ae+4|0,Ze=n[_>>2]|0,n[_>>2]=n[A>>2],n[A>>2]=Ze,A=ae+8|0,Ze=n[M>>2]|0,n[M>>2]=n[A>>2],n[A>>2]=Ze,A=ae+12|0,Ze=n[m>>2]|0,n[m>>2]=n[A>>2],n[A>>2]=Ze,Qy(ae),A=n[M>>2]|0;break}m=A,B=((m-d>>2)+1|0)/-2|0,k=A+(B<<2)|0,d=T-m|0,m=d>>2,m&&(F2(k|0,A|0,d|0)|0,A=n[_>>2]|0),Ze=k+(m<<2)|0,n[M>>2]=Ze,n[_>>2]=A+(B<<2),A=Ze}while(!1);n[A>>2]=n[u>>2],n[M>>2]=(n[M>>2]|0)+4,l=PP(o,Qe,l)|0,Qy(Qe)}while(!1);return I=tt,l|0}function Oe(o){o=o|0;var l=0;do{if(l=o+984|0,s[l>>0]|0)break;s[l>>0]=1,h[o+504>>2]=y(ce),o=n[o+944>>2]|0}while(o|0)}function ht(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-4-A|0)>>>2)<<2)),yt(u))}function mt(o){return o=o|0,n[o+944>>2]|0}function Dt(o){o=o|0,wi(o,(n[o+964>>2]|0)!=0,2832),Oe(o)}function tr(o){return o=o|0,(s[o+984>>0]|0)!=0|0}function fn(o,l){o=o|0,l=l|0,qYe(o,l,400)|0&&(Qr(o|0,l|0,400)|0,Oe(o))}function ai(o){o=o|0;var l=Xe;return l=y(h[o+44>>2]),o=Mt(l)|0,y(o?y(0):l)}function qi(o){o=o|0;var l=Xe;return l=y(h[o+48>>2]),Mt(l)|0&&(l=s[(n[o+976>>2]|0)+2>>0]|0?y(1):y(0)),y(l)}function Tn(o,l){o=o|0,l=l|0,n[o+980>>2]=l}function Ga(o){return o=o|0,n[o+980>>2]|0}function my(o,l){o=o|0,l=l|0;var u=0;u=o+4|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function t2(o){return o=o|0,n[o+4>>2]|0}function Do(o,l){o=o|0,l=l|0;var u=0;u=o+8|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function yy(o){return o=o|0,n[o+8>>2]|0}function wh(o,l){o=o|0,l=l|0;var u=0;u=o+12|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function r2(o){return o=o|0,n[o+12>>2]|0}function bo(o,l){o=o|0,l=l|0;var u=0;u=o+16|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function Bh(o){return o=o|0,n[o+16>>2]|0}function vh(o,l){o=o|0,l=l|0;var u=0;u=o+20|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function du(o){return o=o|0,n[o+20>>2]|0}function Sh(o,l){o=o|0,l=l|0;var u=0;u=o+24|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function Ng(o){return o=o|0,n[o+24>>2]|0}function Og(o,l){o=o|0,l=l|0;var u=0;u=o+28|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function Lg(o){return o=o|0,n[o+28>>2]|0}function Ey(o,l){o=o|0,l=l|0;var u=0;u=o+32|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function mf(o){return o=o|0,n[o+32>>2]|0}function Po(o,l){o=o|0,l=l|0;var u=0;u=o+36|0,(n[u>>2]|0)!=(l|0)&&(n[u>>2]=l,Oe(o))}function Dl(o){return o=o|0,n[o+36>>2]|0}function Dh(o,l){o=o|0,l=y(l);var u=0;u=o+40|0,y(h[u>>2])!=l&&(h[u>>2]=l,Oe(o))}function Mg(o,l){o=o|0,l=y(l);var u=0;u=o+44|0,y(h[u>>2])!=l&&(h[u>>2]=l,Oe(o))}function bl(o,l){o=o|0,l=y(l);var u=0;u=o+48|0,y(h[u>>2])!=l&&(h[u>>2]=l,Oe(o))}function Pl(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+52|0,d=o+56|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function Iy(o,l){o=o|0,l=y(l);var u=0,A=0;A=o+52|0,u=o+56|0,y(h[A>>2])==l&&(n[u>>2]|0)==2||(h[A>>2]=l,A=Mt(l)|0,n[u>>2]=A?3:2,Oe(o))}function HA(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+52|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function Cy(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=(m^1)&1,d=o+132+(l<<3)|0,l=o+132+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function wy(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=m?0:2,d=o+132+(l<<3)|0,l=o+132+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function jA(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=l+132+(u<<3)|0,l=n[A+4>>2]|0,u=o,n[u>>2]=n[A>>2],n[u+4>>2]=l}function qA(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=(m^1)&1,d=o+60+(l<<3)|0,l=o+60+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function Y(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=m?0:2,d=o+60+(l<<3)|0,l=o+60+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function xt(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=l+60+(u<<3)|0,l=n[A+4>>2]|0,u=o,n[u>>2]=n[A>>2],n[u+4>>2]=l}function GA(o,l){o=o|0,l=l|0;var u=0;u=o+60+(l<<3)+4|0,(n[u>>2]|0)!=3&&(h[o+60+(l<<3)>>2]=y(ce),n[u>>2]=3,Oe(o))}function xo(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=(m^1)&1,d=o+204+(l<<3)|0,l=o+204+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function yf(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=m?0:2,d=o+204+(l<<3)|0,l=o+204+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function dt(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=l+204+(u<<3)|0,l=n[A+4>>2]|0,u=o,n[u>>2]=n[A>>2],n[u+4>>2]=l}function mu(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0,m=0;m=Mt(u)|0,A=(m^1)&1,d=o+276+(l<<3)|0,l=o+276+(l<<3)+4|0,m|y(h[d>>2])==u&&(n[l>>2]|0)==(A|0)||(h[d>>2]=u,n[l>>2]=A,Oe(o))}function By(o,l){return o=o|0,l=l|0,y(h[o+276+(l<<3)>>2])}function _g(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+348|0,d=o+352|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function n2(o,l){o=o|0,l=y(l);var u=0,A=0;A=o+348|0,u=o+352|0,y(h[A>>2])==l&&(n[u>>2]|0)==2||(h[A>>2]=l,A=Mt(l)|0,n[u>>2]=A?3:2,Oe(o))}function bh(o){o=o|0;var l=0;l=o+352|0,(n[l>>2]|0)!=3&&(h[o+348>>2]=y(ce),n[l>>2]=3,Oe(o))}function ur(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+348|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function zi(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+356|0,d=o+360|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function Ef(o,l){o=o|0,l=y(l);var u=0,A=0;A=o+356|0,u=o+360|0,y(h[A>>2])==l&&(n[u>>2]|0)==2||(h[A>>2]=l,A=Mt(l)|0,n[u>>2]=A?3:2,Oe(o))}function Wa(o){o=o|0;var l=0;l=o+360|0,(n[l>>2]|0)!=3&&(h[o+356>>2]=y(ce),n[l>>2]=3,Oe(o))}function Ug(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+356|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function yu(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+364|0,d=o+368|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function If(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=m?0:2,A=o+364|0,d=o+368|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function wt(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+364|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function gi(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+372|0,d=o+376|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function WA(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=m?0:2,A=o+372|0,d=o+376|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function Ya(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+372|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function pa(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+380|0,d=o+384|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function Va(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=m?0:2,A=o+380|0,d=o+384|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function Hg(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+380|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function Ph(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=(m^1)&1,A=o+388|0,d=o+392|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function jg(o,l){o=o|0,l=y(l);var u=0,A=0,d=0,m=0;m=Mt(l)|0,u=m?0:2,A=o+388|0,d=o+392|0,m|y(h[A>>2])==l&&(n[d>>2]|0)==(u|0)||(h[A>>2]=l,n[d>>2]=u,Oe(o))}function vy(o,l){o=o|0,l=l|0;var u=0,A=0;A=l+388|0,u=n[A+4>>2]|0,l=o,n[l>>2]=n[A>>2],n[l+4>>2]=u}function YA(o,l){o=o|0,l=y(l);var u=0;u=o+396|0,y(h[u>>2])!=l&&(h[u>>2]=l,Oe(o))}function qg(o){return o=o|0,y(h[o+396>>2])}function Eu(o){return o=o|0,y(h[o+400>>2])}function Iu(o){return o=o|0,y(h[o+404>>2])}function Cf(o){return o=o|0,y(h[o+408>>2])}function Fs(o){return o=o|0,y(h[o+412>>2])}function Cu(o){return o=o|0,y(h[o+416>>2])}function qn(o){return o=o|0,y(h[o+420>>2])}function is(o,l){switch(o=o|0,l=l|0,wi(o,(l|0)<6,2918),l|0){case 0:{l=(n[o+496>>2]|0)==2?5:4;break}case 2:{l=(n[o+496>>2]|0)==2?4:5;break}default:}return y(h[o+424+(l<<2)>>2])}function xi(o,l){switch(o=o|0,l=l|0,wi(o,(l|0)<6,2918),l|0){case 0:{l=(n[o+496>>2]|0)==2?5:4;break}case 2:{l=(n[o+496>>2]|0)==2?4:5;break}default:}return y(h[o+448+(l<<2)>>2])}function VA(o,l){switch(o=o|0,l=l|0,wi(o,(l|0)<6,2918),l|0){case 0:{l=(n[o+496>>2]|0)==2?5:4;break}case 2:{l=(n[o+496>>2]|0)==2?4:5;break}default:}return y(h[o+472+(l<<2)>>2])}function wf(o,l){o=o|0,l=l|0;var u=0,A=Xe;return u=n[o+4>>2]|0,(u|0)==(n[l+4>>2]|0)?u?(A=y(h[o>>2]),o=y(se(y(A-y(h[l>>2]))))>2]=0,n[A+4>>2]=0,n[A+8>>2]=0,fu(A|0,o|0,l|0,0),Qo(o,3,(s[A+11>>0]|0)<0?n[A>>2]|0:A,u),AVe(A),I=u}function ss(o,l,u,A){o=y(o),l=y(l),u=u|0,A=A|0;var d=Xe;o=y(o*l),d=y(E_(o,y(1)));do if(mn(d,y(0))|0)o=y(o-d);else{if(o=y(o-d),mn(d,y(1))|0){o=y(o+y(1));break}if(u){o=y(o+y(1));break}A||(d>y(.5)?d=y(1):(A=mn(d,y(.5))|0,d=y(A?1:0)),o=y(o+d))}while(!1);return y(o/l)}function xl(o,l,u,A,d,m,B,k,T,_,M,G,ae){o=o|0,l=y(l),u=u|0,A=y(A),d=d|0,m=y(m),B=B|0,k=y(k),T=y(T),_=y(_),M=y(M),G=y(G),ae=ae|0;var We=0,Le=Xe,Qe=Xe,tt=Xe,Ze=Xe,ct=Xe,He=Xe;return T>2]),Le!=y(0))?(tt=y(ss(l,Le,0,0)),Ze=y(ss(A,Le,0,0)),Qe=y(ss(m,Le,0,0)),Le=y(ss(k,Le,0,0))):(Qe=m,tt=l,Le=k,Ze=A),(d|0)==(o|0)?We=mn(Qe,tt)|0:We=0,(B|0)==(u|0)?ae=mn(Le,Ze)|0:ae=0,!We&&(ct=y(l-M),!(ko(o,ct,T)|0))&&!(Bf(o,ct,d,T)|0)?We=vf(o,ct,d,m,T)|0:We=1,!ae&&(He=y(A-G),!(ko(u,He,_)|0))&&!(Bf(u,He,B,_)|0)?ae=vf(u,He,B,k,_)|0:ae=1,ae=We&ae),ae|0}function ko(o,l,u){return o=o|0,l=y(l),u=y(u),(o|0)==1?o=mn(l,u)|0:o=0,o|0}function Bf(o,l,u,A){return o=o|0,l=y(l),u=u|0,A=y(A),(o|0)==2&(u|0)==0?l>=A?o=1:o=mn(l,A)|0:o=0,o|0}function vf(o,l,u,A,d){return o=o|0,l=y(l),u=u|0,A=y(A),d=y(d),(o|0)==2&(u|0)==2&A>l?d<=l?o=1:o=mn(l,d)|0:o=0,o|0}function kl(o,l,u,A,d,m,B,k,T,_,M){o=o|0,l=y(l),u=y(u),A=A|0,d=d|0,m=m|0,B=y(B),k=y(k),T=T|0,_=_|0,M=M|0;var G=0,ae=0,We=0,Le=0,Qe=Xe,tt=Xe,Ze=0,ct=0,He=0,Ge=0,Lt=0,qr=0,fr=0,$t=0,Tr=0,Hr=0,cr=0,Hn=Xe,Fo=Xe,No=Xe,Oo=0,$a=0;cr=I,I=I+160|0,$t=cr+152|0,fr=cr+120|0,qr=cr+104|0,He=cr+72|0,Le=cr+56|0,Lt=cr+8|0,ct=cr,Ge=(n[2279]|0)+1|0,n[2279]=Ge,Tr=o+984|0,s[Tr>>0]|0&&(n[o+512>>2]|0)!=(n[2278]|0)?Ze=4:(n[o+516>>2]|0)==(A|0)?Hr=0:Ze=4,(Ze|0)==4&&(n[o+520>>2]=0,n[o+924>>2]=-1,n[o+928>>2]=-1,h[o+932>>2]=y(-1),h[o+936>>2]=y(-1),Hr=1);e:do if(n[o+964>>2]|0)if(Qe=y(yn(o,2,B)),tt=y(yn(o,0,B)),G=o+916|0,No=y(h[G>>2]),Fo=y(h[o+920>>2]),Hn=y(h[o+932>>2]),xl(d,l,m,u,n[o+924>>2]|0,No,n[o+928>>2]|0,Fo,Hn,y(h[o+936>>2]),Qe,tt,M)|0)Ze=22;else if(We=n[o+520>>2]|0,!We)Ze=21;else for(ae=0;;){if(G=o+524+(ae*24|0)|0,Hn=y(h[G>>2]),Fo=y(h[o+524+(ae*24|0)+4>>2]),No=y(h[o+524+(ae*24|0)+16>>2]),xl(d,l,m,u,n[o+524+(ae*24|0)+8>>2]|0,Hn,n[o+524+(ae*24|0)+12>>2]|0,Fo,No,y(h[o+524+(ae*24|0)+20>>2]),Qe,tt,M)|0){Ze=22;break e}if(ae=ae+1|0,ae>>>0>=We>>>0){Ze=21;break}}else{if(T){if(G=o+916|0,!(mn(y(h[G>>2]),l)|0)){Ze=21;break}if(!(mn(y(h[o+920>>2]),u)|0)){Ze=21;break}if((n[o+924>>2]|0)!=(d|0)){Ze=21;break}G=(n[o+928>>2]|0)==(m|0)?G:0,Ze=22;break}if(We=n[o+520>>2]|0,!We)Ze=21;else for(ae=0;;){if(G=o+524+(ae*24|0)|0,mn(y(h[G>>2]),l)|0&&mn(y(h[o+524+(ae*24|0)+4>>2]),u)|0&&(n[o+524+(ae*24|0)+8>>2]|0)==(d|0)&&(n[o+524+(ae*24|0)+12>>2]|0)==(m|0)){Ze=22;break e}if(ae=ae+1|0,ae>>>0>=We>>>0){Ze=21;break}}}while(!1);do if((Ze|0)==21)s[11697]|0?(G=0,Ze=28):(G=0,Ze=31);else if((Ze|0)==22){if(ae=(s[11697]|0)!=0,!((G|0)!=0&(Hr^1)))if(ae){Ze=28;break}else{Ze=31;break}Le=G+16|0,n[o+908>>2]=n[Le>>2],We=G+20|0,n[o+912>>2]=n[We>>2],(s[11698]|0)==0|ae^1||(n[ct>>2]=wu(Ge)|0,n[ct+4>>2]=Ge,Qo(o,4,2972,ct),ae=n[o+972>>2]|0,ae|0&&op[ae&127](o),d=ha(d,T)|0,m=ha(m,T)|0,$a=+y(h[Le>>2]),Oo=+y(h[We>>2]),n[Lt>>2]=d,n[Lt+4>>2]=m,E[Lt+8>>3]=+l,E[Lt+16>>3]=+u,E[Lt+24>>3]=$a,E[Lt+32>>3]=Oo,n[Lt+40>>2]=_,Qo(o,4,2989,Lt))}while(!1);return(Ze|0)==28&&(ae=wu(Ge)|0,n[Le>>2]=ae,n[Le+4>>2]=Ge,n[Le+8>>2]=Hr?3047:11699,Qo(o,4,3038,Le),ae=n[o+972>>2]|0,ae|0&&op[ae&127](o),Lt=ha(d,T)|0,Ze=ha(m,T)|0,n[He>>2]=Lt,n[He+4>>2]=Ze,E[He+8>>3]=+l,E[He+16>>3]=+u,n[He+24>>2]=_,Qo(o,4,3049,He),Ze=31),(Ze|0)==31&&(Ns(o,l,u,A,d,m,B,k,T,M),s[11697]|0&&(ae=n[2279]|0,Lt=wu(ae)|0,n[qr>>2]=Lt,n[qr+4>>2]=ae,n[qr+8>>2]=Hr?3047:11699,Qo(o,4,3083,qr),ae=n[o+972>>2]|0,ae|0&&op[ae&127](o),Lt=ha(d,T)|0,qr=ha(m,T)|0,Oo=+y(h[o+908>>2]),$a=+y(h[o+912>>2]),n[fr>>2]=Lt,n[fr+4>>2]=qr,E[fr+8>>3]=Oo,E[fr+16>>3]=$a,n[fr+24>>2]=_,Qo(o,4,3092,fr)),n[o+516>>2]=A,G||(ae=o+520|0,G=n[ae>>2]|0,(G|0)==16&&(s[11697]|0&&Qo(o,4,3124,$t),n[ae>>2]=0,G=0),T?G=o+916|0:(n[ae>>2]=G+1,G=o+524+(G*24|0)|0),h[G>>2]=l,h[G+4>>2]=u,n[G+8>>2]=d,n[G+12>>2]=m,n[G+16>>2]=n[o+908>>2],n[G+20>>2]=n[o+912>>2],G=0)),T&&(n[o+416>>2]=n[o+908>>2],n[o+420>>2]=n[o+912>>2],s[o+985>>0]=1,s[Tr>>0]=0),n[2279]=(n[2279]|0)+-1,n[o+512>>2]=n[2278],I=cr,Hr|(G|0)==0|0}function yn(o,l,u){o=o|0,l=l|0,u=y(u);var A=Xe;return A=y(J(o,l,u)),y(A+y(re(o,l,u)))}function Qo(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=I,I=I+16|0,d=m,n[d>>2]=A,o?A=n[o+976>>2]|0:A=0,Qh(A,o,l,u,d),I=m}function wu(o){return o=o|0,(o>>>0>60?3201:3201+(60-o)|0)|0}function ha(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;return d=I,I=I+32|0,u=d+12|0,A=d,n[u>>2]=n[254],n[u+4>>2]=n[255],n[u+8>>2]=n[256],n[A>>2]=n[257],n[A+4>>2]=n[258],n[A+8>>2]=n[259],(o|0)>2?o=11699:o=n[(l?A:u)+(o<<2)>>2]|0,I=d,o|0}function Ns(o,l,u,A,d,m,B,k,T,_){o=o|0,l=y(l),u=y(u),A=A|0,d=d|0,m=m|0,B=y(B),k=y(k),T=T|0,_=_|0;var M=0,G=0,ae=0,We=0,Le=Xe,Qe=Xe,tt=Xe,Ze=Xe,ct=Xe,He=Xe,Ge=Xe,Lt=0,qr=0,fr=0,$t=Xe,Tr=Xe,Hr=0,cr=Xe,Hn=0,Fo=0,No=0,Oo=0,$a=0,Kh=0,Jh=0,dc=0,zh=0,Ff=0,Nf=0,Zh=0,Xh=0,$h=0,on=0,mc=0,e0=0,ku=0,t0=Xe,r0=Xe,Of=Xe,Lf=Xe,Qu=Xe,ao=0,Ml=0,ya=0,yc=0,lp=0,cp=Xe,Mf=Xe,up=Xe,fp=Xe,lo=Xe,_s=Xe,Ec=0,Wn=Xe,Ap=Xe,Lo=Xe,Tu=Xe,Mo=Xe,Ru=Xe,pp=0,hp=0,Fu=Xe,co=Xe,Ic=0,gp=0,dp=0,mp=0,Fr=Xe,ci=0,Us=0,_o=0,uo=0,Mr=0,Ar=0,Cc=0,zt=Xe,yp=0,Bi=0;Cc=I,I=I+16|0,ao=Cc+12|0,Ml=Cc+8|0,ya=Cc+4|0,yc=Cc,wi(o,(d|0)==0|(Mt(l)|0)^1,3326),wi(o,(m|0)==0|(Mt(u)|0)^1,3406),Us=ft(o,A)|0,n[o+496>>2]=Us,Mr=dr(2,Us)|0,Ar=dr(0,Us)|0,h[o+440>>2]=y(J(o,Mr,B)),h[o+444>>2]=y(re(o,Mr,B)),h[o+428>>2]=y(J(o,Ar,B)),h[o+436>>2]=y(re(o,Ar,B)),h[o+464>>2]=y(Br(o,Mr)),h[o+468>>2]=y(_n(o,Mr)),h[o+452>>2]=y(Br(o,Ar)),h[o+460>>2]=y(_n(o,Ar)),h[o+488>>2]=y(di(o,Mr,B)),h[o+492>>2]=y(ws(o,Mr,B)),h[o+476>>2]=y(di(o,Ar,B)),h[o+484>>2]=y(ws(o,Ar,B));do if(n[o+964>>2]|0)zA(o,l,u,d,m,B,k);else{if(_o=o+948|0,uo=(n[o+952>>2]|0)-(n[_o>>2]|0)>>2,!uo){wP(o,l,u,d,m,B,k);break}if(!T&&i2(o,l,u,d,m,B,k)|0)break;ee(o),mc=o+508|0,s[mc>>0]=0,Mr=dr(n[o+4>>2]|0,Us)|0,Ar=by(Mr,Us)|0,ci=de(Mr)|0,e0=n[o+8>>2]|0,gp=o+28|0,ku=(n[gp>>2]|0)!=0,Mo=ci?B:k,Fu=ci?k:B,t0=y(Rh(o,Mr,B)),r0=y(s2(o,Mr,B)),Le=y(Rh(o,Ar,B)),Ru=y(Ka(o,Mr,B)),co=y(Ka(o,Ar,B)),fr=ci?d:m,Ic=ci?m:d,Fr=ci?Ru:co,ct=ci?co:Ru,Tu=y(yn(o,2,B)),Ze=y(yn(o,0,B)),Qe=y(y(Xr(o+364|0,B))-Fr),tt=y(y(Xr(o+380|0,B))-Fr),He=y(y(Xr(o+372|0,k))-ct),Ge=y(y(Xr(o+388|0,k))-ct),Of=ci?Qe:He,Lf=ci?tt:Ge,Tu=y(l-Tu),l=y(Tu-Fr),Mt(l)|0?Fr=l:Fr=y($n(y(hd(l,tt)),Qe)),Ap=y(u-Ze),l=y(Ap-ct),Mt(l)|0?Lo=l:Lo=y($n(y(hd(l,Ge)),He)),Qe=ci?Fr:Lo,Wn=ci?Lo:Fr;e:do if((fr|0)==1)for(A=0,G=0;;){if(M=Cs(o,G)|0,!A)y(ZA(M))>y(0)&&y(Fh(M))>y(0)?A=M:A=0;else if(o2(M)|0){We=0;break e}if(G=G+1|0,G>>>0>=uo>>>0){We=A;break}}else We=0;while(!1);Lt=We+500|0,qr=We+504|0,A=0,M=0,l=y(0),ae=0;do{if(G=n[(n[_o>>2]|0)+(ae<<2)>>2]|0,(n[G+36>>2]|0)==1)Py(G),s[G+985>>0]=1,s[G+984>>0]=0;else{Sf(G),T&&kh(G,ft(G,Us)|0,Qe,Wn,Fr);do if((n[G+24>>2]|0)!=1)if((G|0)==(We|0)){n[Lt>>2]=n[2278],h[qr>>2]=y(0);break}else{BP(o,G,Fr,d,Lo,Fr,Lo,m,Us,_);break}else M|0&&(n[M+960>>2]=G),n[G+960>>2]=0,M=G,A=A|0?A:G;while(!1);_s=y(h[G+504>>2]),l=y(l+y(_s+y(yn(G,Mr,Fr))))}ae=ae+1|0}while((ae|0)!=(uo|0));for(No=l>Qe,Ec=ku&((fr|0)==2&No)?1:fr,Hn=(Ic|0)==1,$a=Hn&(T^1),Kh=(Ec|0)==1,Jh=(Ec|0)==2,dc=976+(Mr<<2)|0,zh=(Ic|2|0)==2,$h=Hn&(ku^1),Ff=1040+(Ar<<2)|0,Nf=1040+(Mr<<2)|0,Zh=976+(Ar<<2)|0,Xh=(Ic|0)!=1,No=ku&((fr|0)!=0&No),Fo=o+976|0,Hn=Hn^1,l=Qe,Hr=0,Oo=0,_s=y(0),Qu=y(0);;){e:do if(Hr>>>0>>0)for(qr=n[_o>>2]|0,ae=0,Ge=y(0),He=y(0),tt=y(0),Qe=y(0),G=0,M=0,We=Hr;;){if(Lt=n[qr+(We<<2)>>2]|0,(n[Lt+36>>2]|0)!=1&&(n[Lt+940>>2]=Oo,(n[Lt+24>>2]|0)!=1)){if(Ze=y(yn(Lt,Mr,Fr)),on=n[dc>>2]|0,u=y(Xr(Lt+380+(on<<3)|0,Mo)),ct=y(h[Lt+504>>2]),u=y(hd(u,ct)),u=y($n(y(Xr(Lt+364+(on<<3)|0,Mo)),u)),ku&(ae|0)!=0&y(Ze+y(He+u))>l){m=ae,Ze=Ge,fr=We;break e}Ze=y(Ze+u),u=y(He+Ze),Ze=y(Ge+Ze),o2(Lt)|0&&(tt=y(tt+y(ZA(Lt))),Qe=y(Qe-y(ct*y(Fh(Lt))))),M|0&&(n[M+960>>2]=Lt),n[Lt+960>>2]=0,ae=ae+1|0,M=Lt,G=G|0?G:Lt}else Ze=Ge,u=He;if(We=We+1|0,We>>>0>>0)Ge=Ze,He=u;else{m=ae,fr=We;break}}else m=0,Ze=y(0),tt=y(0),Qe=y(0),G=0,fr=Hr;while(!1);on=tt>y(0)&tty(0)&QeLf&((Mt(Lf)|0)^1))l=Lf,on=51;else if(s[(n[Fo>>2]|0)+3>>0]|0)on=51;else{if($t!=y(0)&&y(ZA(o))!=y(0)){on=53;break}l=Ze,on=53}while(!1);if((on|0)==51&&(on=0,Mt(l)|0?on=53:(Tr=y(l-Ze),cr=l)),(on|0)==53&&(on=0,Ze>2]|0,We=Try(0),He=y(Tr/$t),tt=y(0),Ze=y(0),l=y(0),M=G;do u=y(Xr(M+380+(ae<<3)|0,Mo)),Qe=y(Xr(M+364+(ae<<3)|0,Mo)),Qe=y(hd(u,y($n(Qe,y(h[M+504>>2]))))),We?(u=y(Qe*y(Fh(M))),u!=y(-0)&&(zt=y(Qe-y(ct*u)),cp=y(Gn(M,Mr,zt,cr,Fr)),zt!=cp)&&(tt=y(tt-y(cp-Qe)),l=y(l+u))):Lt&&(Mf=y(ZA(M)),Mf!=y(0))&&(zt=y(Qe+y(He*Mf)),up=y(Gn(M,Mr,zt,cr,Fr)),zt!=up)&&(tt=y(tt-y(up-Qe)),Ze=y(Ze-Mf)),M=n[M+960>>2]|0;while(M|0);if(l=y(Ge+l),Qe=y(Tr+tt),lp)l=y(0);else{ct=y($t+Ze),We=n[dc>>2]|0,Lt=Qey(0),ct=y(Qe/ct),l=y(0);do{zt=y(Xr(G+380+(We<<3)|0,Mo)),tt=y(Xr(G+364+(We<<3)|0,Mo)),tt=y(hd(zt,y($n(tt,y(h[G+504>>2]))))),Lt?(zt=y(tt*y(Fh(G))),Qe=y(-zt),zt!=y(-0)?(zt=y(He*Qe),Qe=y(Gn(G,Mr,y(tt+(qr?Qe:zt)),cr,Fr))):Qe=tt):ae&&(fp=y(ZA(G)),fp!=y(0))?Qe=y(Gn(G,Mr,y(tt+y(ct*fp)),cr,Fr)):Qe=tt,l=y(l-y(Qe-tt)),Ze=y(yn(G,Mr,Fr)),u=y(yn(G,Ar,Fr)),Qe=y(Qe+Ze),h[Ml>>2]=Qe,n[yc>>2]=1,tt=y(h[G+396>>2]);e:do if(Mt(tt)|0){M=Mt(Wn)|0;do if(!M){if(No|(so(G,Ar,Wn)|0|Hn)||(os(o,G)|0)!=4||(n[(Ql(G,Ar)|0)+4>>2]|0)==3||(n[(Tl(G,Ar)|0)+4>>2]|0)==3)break;h[ao>>2]=Wn,n[ya>>2]=1;break e}while(!1);if(so(G,Ar,Wn)|0){M=n[G+992+(n[Zh>>2]<<2)>>2]|0,zt=y(u+y(Xr(M,Wn))),h[ao>>2]=zt,M=Xh&(n[M+4>>2]|0)==2,n[ya>>2]=((Mt(zt)|0|M)^1)&1;break}else{h[ao>>2]=Wn,n[ya>>2]=M?0:2;break}}else zt=y(Qe-Ze),$t=y(zt/tt),zt=y(tt*zt),n[ya>>2]=1,h[ao>>2]=y(u+(ci?$t:zt));while(!1);Bu(G,Mr,cr,Fr,yc,Ml),Bu(G,Ar,Wn,Fr,ya,ao);do if(!(so(G,Ar,Wn)|0)&&(os(o,G)|0)==4){if((n[(Ql(G,Ar)|0)+4>>2]|0)==3){M=0;break}M=(n[(Tl(G,Ar)|0)+4>>2]|0)!=3}else M=0;while(!1);zt=y(h[Ml>>2]),$t=y(h[ao>>2]),yp=n[yc>>2]|0,Bi=n[ya>>2]|0,kl(G,ci?zt:$t,ci?$t:zt,Us,ci?yp:Bi,ci?Bi:yp,Fr,Lo,T&(M^1),3488,_)|0,s[mc>>0]=s[mc>>0]|s[G+508>>0],G=n[G+960>>2]|0}while(G|0)}}else l=y(0);if(l=y(Tr+l),Bi=l>0]=Bi|c[mc>>0],Jh&l>y(0)?(M=n[dc>>2]|0,n[o+364+(M<<3)+4>>2]|0&&(lo=y(Xr(o+364+(M<<3)|0,Mo)),lo>=y(0))?Qe=y($n(y(0),y(lo-y(cr-l)))):Qe=y(0)):Qe=l,Lt=Hr>>>0>>0,Lt){We=n[_o>>2]|0,ae=Hr,M=0;do G=n[We+(ae<<2)>>2]|0,n[G+24>>2]|0||(M=((n[(Ql(G,Mr)|0)+4>>2]|0)==3&1)+M|0,M=M+((n[(Tl(G,Mr)|0)+4>>2]|0)==3&1)|0),ae=ae+1|0;while((ae|0)!=(fr|0));M?(Ze=y(0),u=y(0)):on=101}else on=101;e:do if((on|0)==101)switch(on=0,e0|0){case 1:{M=0,Ze=y(Qe*y(.5)),u=y(0);break e}case 2:{M=0,Ze=Qe,u=y(0);break e}case 3:{if(m>>>0<=1){M=0,Ze=y(0),u=y(0);break e}u=y((m+-1|0)>>>0),M=0,Ze=y(0),u=y(y($n(Qe,y(0)))/u);break e}case 5:{u=y(Qe/y((m+1|0)>>>0)),M=0,Ze=u;break e}case 4:{u=y(Qe/y(m>>>0)),M=0,Ze=y(u*y(.5));break e}default:{M=0,Ze=y(0),u=y(0);break e}}while(!1);if(l=y(t0+Ze),Lt){tt=y(Qe/y(M|0)),ae=n[_o>>2]|0,G=Hr,Qe=y(0);do{M=n[ae+(G<<2)>>2]|0;e:do if((n[M+36>>2]|0)!=1){switch(n[M+24>>2]|0){case 1:{if(ga(M,Mr)|0){if(!T)break e;zt=y(XA(M,Mr,cr)),zt=y(zt+y(Br(o,Mr))),zt=y(zt+y(J(M,Mr,Fr))),h[M+400+(n[Nf>>2]<<2)>>2]=zt;break e}break}case 0:if(Bi=(n[(Ql(M,Mr)|0)+4>>2]|0)==3,zt=y(tt+l),l=Bi?zt:l,T&&(Bi=M+400+(n[Nf>>2]<<2)|0,h[Bi>>2]=y(l+y(h[Bi>>2]))),Bi=(n[(Tl(M,Mr)|0)+4>>2]|0)==3,zt=y(tt+l),l=Bi?zt:l,$a){zt=y(u+y(yn(M,Mr,Fr))),Qe=Wn,l=y(l+y(zt+y(h[M+504>>2])));break e}else{l=y(l+y(u+y($A(M,Mr,Fr)))),Qe=y($n(Qe,y($A(M,Ar,Fr))));break e}default:}T&&(zt=y(Ze+y(Br(o,Mr))),Bi=M+400+(n[Nf>>2]<<2)|0,h[Bi>>2]=y(zt+y(h[Bi>>2])))}while(!1);G=G+1|0}while((G|0)!=(fr|0))}else Qe=y(0);if(u=y(r0+l),zh?Ze=y(y(Gn(o,Ar,y(co+Qe),Fu,B))-co):Ze=Wn,tt=y(y(Gn(o,Ar,y(co+($h?Wn:Qe)),Fu,B))-co),Lt&T){G=Hr;do{ae=n[(n[_o>>2]|0)+(G<<2)>>2]|0;do if((n[ae+36>>2]|0)!=1){if((n[ae+24>>2]|0)==1){if(ga(ae,Ar)|0){if(zt=y(XA(ae,Ar,Wn)),zt=y(zt+y(Br(o,Ar))),zt=y(zt+y(J(ae,Ar,Fr))),M=n[Ff>>2]|0,h[ae+400+(M<<2)>>2]=zt,!(Mt(zt)|0))break}else M=n[Ff>>2]|0;zt=y(Br(o,Ar)),h[ae+400+(M<<2)>>2]=y(zt+y(J(ae,Ar,Fr)));break}M=os(o,ae)|0;do if((M|0)==4){if((n[(Ql(ae,Ar)|0)+4>>2]|0)==3){on=139;break}if((n[(Tl(ae,Ar)|0)+4>>2]|0)==3){on=139;break}if(so(ae,Ar,Wn)|0){l=Le;break}yp=n[ae+908+(n[dc>>2]<<2)>>2]|0,n[ao>>2]=yp,l=y(h[ae+396>>2]),Bi=Mt(l)|0,Qe=(n[S>>2]=yp,y(h[S>>2])),Bi?l=tt:(Tr=y(yn(ae,Ar,Fr)),zt=y(Qe/l),l=y(l*Qe),l=y(Tr+(ci?zt:l))),h[Ml>>2]=l,h[ao>>2]=y(y(yn(ae,Mr,Fr))+Qe),n[ya>>2]=1,n[yc>>2]=1,Bu(ae,Mr,cr,Fr,ya,ao),Bu(ae,Ar,Wn,Fr,yc,Ml),l=y(h[ao>>2]),Tr=y(h[Ml>>2]),zt=ci?l:Tr,l=ci?Tr:l,Bi=((Mt(zt)|0)^1)&1,kl(ae,zt,l,Us,Bi,((Mt(l)|0)^1)&1,Fr,Lo,1,3493,_)|0,l=Le}else on=139;while(!1);e:do if((on|0)==139){on=0,l=y(Ze-y($A(ae,Ar,Fr)));do if((n[(Ql(ae,Ar)|0)+4>>2]|0)==3){if((n[(Tl(ae,Ar)|0)+4>>2]|0)!=3)break;l=y(Le+y($n(y(0),y(l*y(.5)))));break e}while(!1);if((n[(Tl(ae,Ar)|0)+4>>2]|0)==3){l=Le;break}if((n[(Ql(ae,Ar)|0)+4>>2]|0)==3){l=y(Le+y($n(y(0),l)));break}switch(M|0){case 1:{l=Le;break e}case 2:{l=y(Le+y(l*y(.5)));break e}default:{l=y(Le+l);break e}}}while(!1);zt=y(_s+l),Bi=ae+400+(n[Ff>>2]<<2)|0,h[Bi>>2]=y(zt+y(h[Bi>>2]))}while(!1);G=G+1|0}while((G|0)!=(fr|0))}if(_s=y(_s+tt),Qu=y($n(Qu,u)),m=Oo+1|0,fr>>>0>=uo>>>0)break;l=cr,Hr=fr,Oo=m}do if(T){if(M=m>>>0>1,!M&&!(XL(o)|0))break;if(!(Mt(Wn)|0)){l=y(Wn-_s);e:do switch(n[o+12>>2]|0){case 3:{Le=y(Le+l),He=y(0);break}case 2:{Le=y(Le+y(l*y(.5))),He=y(0);break}case 4:{Wn>_s?He=y(l/y(m>>>0)):He=y(0);break}case 7:if(Wn>_s){Le=y(Le+y(l/y(m<<1>>>0))),He=y(l/y(m>>>0)),He=M?He:y(0);break e}else{Le=y(Le+y(l*y(.5))),He=y(0);break e}case 6:{He=y(l/y(Oo>>>0)),He=Wn>_s&M?He:y(0);break}default:He=y(0)}while(!1);if(m|0)for(Lt=1040+(Ar<<2)|0,qr=976+(Ar<<2)|0,We=0,G=0;;){e:do if(G>>>0>>0)for(Qe=y(0),tt=y(0),l=y(0),ae=G;;){M=n[(n[_o>>2]|0)+(ae<<2)>>2]|0;do if((n[M+36>>2]|0)!=1&&!(n[M+24>>2]|0)){if((n[M+940>>2]|0)!=(We|0))break e;if($L(M,Ar)|0&&(zt=y(h[M+908+(n[qr>>2]<<2)>>2]),l=y($n(l,y(zt+y(yn(M,Ar,Fr)))))),(os(o,M)|0)!=5)break;lo=y(Vg(M)),lo=y(lo+y(J(M,0,Fr))),zt=y(h[M+912>>2]),zt=y(y(zt+y(yn(M,0,Fr)))-lo),lo=y($n(tt,lo)),zt=y($n(Qe,zt)),Qe=zt,tt=lo,l=y($n(l,y(lo+zt)))}while(!1);if(M=ae+1|0,M>>>0>>0)ae=M;else{ae=M;break}}else tt=y(0),l=y(0),ae=G;while(!1);if(ct=y(He+l),u=Le,Le=y(Le+ct),G>>>0>>0){Ze=y(u+tt),M=G;do{G=n[(n[_o>>2]|0)+(M<<2)>>2]|0;e:do if((n[G+36>>2]|0)!=1&&!(n[G+24>>2]|0))switch(os(o,G)|0){case 1:{zt=y(u+y(J(G,Ar,Fr))),h[G+400+(n[Lt>>2]<<2)>>2]=zt;break e}case 3:{zt=y(y(Le-y(re(G,Ar,Fr)))-y(h[G+908+(n[qr>>2]<<2)>>2])),h[G+400+(n[Lt>>2]<<2)>>2]=zt;break e}case 2:{zt=y(u+y(y(ct-y(h[G+908+(n[qr>>2]<<2)>>2]))*y(.5))),h[G+400+(n[Lt>>2]<<2)>>2]=zt;break e}case 4:{if(zt=y(u+y(J(G,Ar,Fr))),h[G+400+(n[Lt>>2]<<2)>>2]=zt,so(G,Ar,Wn)|0||(ci?(Qe=y(h[G+908>>2]),l=y(Qe+y(yn(G,Mr,Fr))),tt=ct):(tt=y(h[G+912>>2]),tt=y(tt+y(yn(G,Ar,Fr))),l=ct,Qe=y(h[G+908>>2])),mn(l,Qe)|0&&mn(tt,y(h[G+912>>2]))|0))break e;kl(G,l,tt,Us,1,1,Fr,Lo,1,3501,_)|0;break e}case 5:{h[G+404>>2]=y(y(Ze-y(Vg(G)))+y(XA(G,0,Wn)));break e}default:break e}while(!1);M=M+1|0}while((M|0)!=(ae|0))}if(We=We+1|0,(We|0)==(m|0))break;G=ae}}}while(!1);if(h[o+908>>2]=y(Gn(o,2,Tu,B,B)),h[o+912>>2]=y(Gn(o,0,Ap,k,B)),Ec|0&&(pp=n[o+32>>2]|0,hp=(Ec|0)==2,!(hp&(pp|0)!=2))?hp&(pp|0)==2&&(l=y(Ru+cr),l=y($n(y(hd(l,y(Kg(o,Mr,Qu,Mo)))),Ru)),on=198):(l=y(Gn(o,Mr,Qu,Mo,B)),on=198),(on|0)==198&&(h[o+908+(n[976+(Mr<<2)>>2]<<2)>>2]=l),Ic|0&&(dp=n[o+32>>2]|0,mp=(Ic|0)==2,!(mp&(dp|0)!=2))?mp&(dp|0)==2&&(l=y(co+Wn),l=y($n(y(hd(l,y(Kg(o,Ar,y(co+_s),Fu)))),co)),on=204):(l=y(Gn(o,Ar,y(co+_s),Fu,B)),on=204),(on|0)==204&&(h[o+908+(n[976+(Ar<<2)>>2]<<2)>>2]=l),T){if((n[gp>>2]|0)==2){G=976+(Ar<<2)|0,ae=1040+(Ar<<2)|0,M=0;do We=Cs(o,M)|0,n[We+24>>2]|0||(yp=n[G>>2]|0,zt=y(h[o+908+(yp<<2)>>2]),Bi=We+400+(n[ae>>2]<<2)|0,zt=y(zt-y(h[Bi>>2])),h[Bi>>2]=y(zt-y(h[We+908+(yp<<2)>>2]))),M=M+1|0;while((M|0)!=(uo|0))}if(A|0){M=ci?Ec:d;do eM(o,A,Fr,M,Lo,Us,_),A=n[A+960>>2]|0;while(A|0)}if(M=(Mr|2|0)==3,G=(Ar|2|0)==3,M|G){A=0;do ae=n[(n[_o>>2]|0)+(A<<2)>>2]|0,(n[ae+36>>2]|0)!=1&&(M&&a2(o,ae,Mr),G&&a2(o,ae,Ar)),A=A+1|0;while((A|0)!=(uo|0))}}}while(!1);I=Cc}function xh(o,l){o=o|0,l=y(l);var u=0;ja(o,l>=y(0),3147),u=l==y(0),h[o+4>>2]=u?y(0):l}function KA(o,l,u,A){o=o|0,l=y(l),u=y(u),A=A|0;var d=Xe,m=Xe,B=0,k=0,T=0;n[2278]=(n[2278]|0)+1,Sf(o),so(o,2,l)|0?(d=y(Xr(n[o+992>>2]|0,l)),T=1,d=y(d+y(yn(o,2,l)))):(d=y(Xr(o+380|0,l)),d>=y(0)?T=2:(T=((Mt(l)|0)^1)&1,d=l)),so(o,0,u)|0?(m=y(Xr(n[o+996>>2]|0,u)),k=1,m=y(m+y(yn(o,0,l)))):(m=y(Xr(o+388|0,u)),m>=y(0)?k=2:(k=((Mt(u)|0)^1)&1,m=u)),B=o+976|0,kl(o,d,m,A,T,k,l,u,1,3189,n[B>>2]|0)|0&&(kh(o,n[o+496>>2]|0,l,u,l),JA(o,y(h[(n[B>>2]|0)+4>>2]),y(0),y(0)),s[11696]|0)&&Gg(o,7)}function Sf(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;k=I,I=I+32|0,B=k+24|0,m=k+16|0,A=k+8|0,d=k,u=0;do l=o+380+(u<<3)|0,n[o+380+(u<<3)+4>>2]|0&&(T=l,_=n[T+4>>2]|0,M=A,n[M>>2]=n[T>>2],n[M+4>>2]=_,M=o+364+(u<<3)|0,_=n[M+4>>2]|0,T=d,n[T>>2]=n[M>>2],n[T+4>>2]=_,n[m>>2]=n[A>>2],n[m+4>>2]=n[A+4>>2],n[B>>2]=n[d>>2],n[B+4>>2]=n[d+4>>2],wf(m,B)|0)||(l=o+348+(u<<3)|0),n[o+992+(u<<2)>>2]=l,u=u+1|0;while((u|0)!=2);I=k}function so(o,l,u){o=o|0,l=l|0,u=y(u);var A=0;switch(o=n[o+992+(n[976+(l<<2)>>2]<<2)>>2]|0,n[o+4>>2]|0){case 0:case 3:{o=0;break}case 1:{y(h[o>>2])>2])>2]|0){case 2:{l=y(y(y(h[o>>2])*l)/y(100));break}case 1:{l=y(h[o>>2]);break}default:l=y(ce)}return y(l)}function kh(o,l,u,A,d){o=o|0,l=l|0,u=y(u),A=y(A),d=y(d);var m=0,B=Xe;l=n[o+944>>2]|0?l:1,m=dr(n[o+4>>2]|0,l)|0,l=by(m,l)|0,u=y(vP(o,m,u)),A=y(vP(o,l,A)),B=y(u+y(J(o,m,d))),h[o+400+(n[1040+(m<<2)>>2]<<2)>>2]=B,u=y(u+y(re(o,m,d))),h[o+400+(n[1e3+(m<<2)>>2]<<2)>>2]=u,u=y(A+y(J(o,l,d))),h[o+400+(n[1040+(l<<2)>>2]<<2)>>2]=u,d=y(A+y(re(o,l,d))),h[o+400+(n[1e3+(l<<2)>>2]<<2)>>2]=d}function JA(o,l,u,A){o=o|0,l=y(l),u=y(u),A=y(A);var d=0,m=0,B=Xe,k=Xe,T=0,_=0,M=Xe,G=0,ae=Xe,We=Xe,Le=Xe,Qe=Xe;if(l!=y(0)&&(d=o+400|0,Qe=y(h[d>>2]),m=o+404|0,Le=y(h[m>>2]),G=o+416|0,We=y(h[G>>2]),_=o+420|0,B=y(h[_>>2]),ae=y(Qe+u),M=y(Le+A),A=y(ae+We),k=y(M+B),T=(n[o+988>>2]|0)==1,h[d>>2]=y(ss(Qe,l,0,T)),h[m>>2]=y(ss(Le,l,0,T)),u=y(E_(y(We*l),y(1))),mn(u,y(0))|0?m=0:m=(mn(u,y(1))|0)^1,u=y(E_(y(B*l),y(1))),mn(u,y(0))|0?d=0:d=(mn(u,y(1))|0)^1,Qe=y(ss(A,l,T&m,T&(m^1))),h[G>>2]=y(Qe-y(ss(ae,l,0,T))),Qe=y(ss(k,l,T&d,T&(d^1))),h[_>>2]=y(Qe-y(ss(M,l,0,T))),m=(n[o+952>>2]|0)-(n[o+948>>2]|0)>>2,m|0)){d=0;do JA(Cs(o,d)|0,l,ae,M),d=d+1|0;while((d|0)!=(m|0))}}function Sy(o,l,u,A,d){switch(o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,u|0){case 5:case 0:{o=n$(n[489]|0,A,d)|0;break}default:o=lVe(A,d)|0}return o|0}function Yg(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;d=I,I=I+16|0,m=d,n[m>>2]=A,Qh(o,0,l,u,m),I=d}function Qh(o,l,u,A,d){if(o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,o=o|0?o:956,v$[n[o+8>>2]&1](o,l,u,A,d)|0,(u|0)==5)Nt();else return}function hc(o,l,u){o=o|0,l=l|0,u=u|0,s[o+l>>0]=u&1}function Dy(o,l){o=o|0,l=l|0;var u=0,A=0;n[o>>2]=0,n[o+4>>2]=0,n[o+8>>2]=0,u=l+4|0,A=(n[u>>2]|0)-(n[l>>2]|0)>>2,A|0&&(Th(o,A),kt(o,n[l>>2]|0,n[u>>2]|0,A))}function Th(o,l){o=o|0,l=l|0;var u=0;if((O(o)|0)>>>0>>0&&sn(o),l>>>0>1073741823)Nt();else{u=Jt(l<<2)|0,n[o+4>>2]=u,n[o>>2]=u,n[o+8>>2]=u+(l<<2);return}}function kt(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,A=o+4|0,o=u-l|0,(o|0)>0&&(Qr(n[A>>2]|0,l|0,o|0)|0,n[A>>2]=(n[A>>2]|0)+(o>>>2<<2))}function O(o){return o=o|0,1073741823}function J(o,l,u){return o=o|0,l=l|0,u=y(u),de(l)|0&&n[o+96>>2]|0?o=o+92|0:o=kn(o+60|0,n[1040+(l<<2)>>2]|0,992)|0,y(Ke(o,u))}function re(o,l,u){return o=o|0,l=l|0,u=y(u),de(l)|0&&n[o+104>>2]|0?o=o+100|0:o=kn(o+60|0,n[1e3+(l<<2)>>2]|0,992)|0,y(Ke(o,u))}function de(o){return o=o|0,(o|1|0)==3|0}function Ke(o,l){return o=o|0,l=y(l),(n[o+4>>2]|0)==3?l=y(0):l=y(Xr(o,l)),y(l)}function ft(o,l){return o=o|0,l=l|0,o=n[o>>2]|0,(o|0?o:(l|0)>1?l:1)|0}function dr(o,l){o=o|0,l=l|0;var u=0;e:do if((l|0)==2){switch(o|0){case 2:{o=3;break e}case 3:break;default:{u=4;break e}}o=2}else u=4;while(!1);return o|0}function Br(o,l){o=o|0,l=l|0;var u=Xe;return de(l)|0&&n[o+312>>2]|0&&(u=y(h[o+308>>2]),u>=y(0))||(u=y($n(y(h[(kn(o+276|0,n[1040+(l<<2)>>2]|0,992)|0)>>2]),y(0)))),y(u)}function _n(o,l){o=o|0,l=l|0;var u=Xe;return de(l)|0&&n[o+320>>2]|0&&(u=y(h[o+316>>2]),u>=y(0))||(u=y($n(y(h[(kn(o+276|0,n[1e3+(l<<2)>>2]|0,992)|0)>>2]),y(0)))),y(u)}function di(o,l,u){o=o|0,l=l|0,u=y(u);var A=Xe;return de(l)|0&&n[o+240>>2]|0&&(A=y(Xr(o+236|0,u)),A>=y(0))||(A=y($n(y(Xr(kn(o+204|0,n[1040+(l<<2)>>2]|0,992)|0,u)),y(0)))),y(A)}function ws(o,l,u){o=o|0,l=l|0,u=y(u);var A=Xe;return de(l)|0&&n[o+248>>2]|0&&(A=y(Xr(o+244|0,u)),A>=y(0))||(A=y($n(y(Xr(kn(o+204|0,n[1e3+(l<<2)>>2]|0,992)|0,u)),y(0)))),y(A)}function zA(o,l,u,A,d,m,B){o=o|0,l=y(l),u=y(u),A=A|0,d=d|0,m=y(m),B=y(B);var k=Xe,T=Xe,_=Xe,M=Xe,G=Xe,ae=Xe,We=0,Le=0,Qe=0;Qe=I,I=I+16|0,We=Qe,Le=o+964|0,wi(o,(n[Le>>2]|0)!=0,3519),k=y(Ka(o,2,l)),T=y(Ka(o,0,l)),_=y(yn(o,2,l)),M=y(yn(o,0,l)),Mt(l)|0?G=l:G=y($n(y(0),y(y(l-_)-k))),Mt(u)|0?ae=u:ae=y($n(y(0),y(y(u-M)-T))),(A|0)==1&(d|0)==1?(h[o+908>>2]=y(Gn(o,2,y(l-_),m,m)),l=y(Gn(o,0,y(u-M),B,m))):(S$[n[Le>>2]&1](We,o,G,A,ae,d),G=y(k+y(h[We>>2])),ae=y(l-_),h[o+908>>2]=y(Gn(o,2,(A|2|0)==2?G:ae,m,m)),ae=y(T+y(h[We+4>>2])),l=y(u-M),l=y(Gn(o,0,(d|2|0)==2?ae:l,B,m))),h[o+912>>2]=l,I=Qe}function wP(o,l,u,A,d,m,B){o=o|0,l=y(l),u=y(u),A=A|0,d=d|0,m=y(m),B=y(B);var k=Xe,T=Xe,_=Xe,M=Xe;_=y(Ka(o,2,m)),k=y(Ka(o,0,m)),M=y(yn(o,2,m)),T=y(yn(o,0,m)),l=y(l-M),h[o+908>>2]=y(Gn(o,2,(A|2|0)==2?_:l,m,m)),u=y(u-T),h[o+912>>2]=y(Gn(o,0,(d|2|0)==2?k:u,B,m))}function i2(o,l,u,A,d,m,B){o=o|0,l=y(l),u=y(u),A=A|0,d=d|0,m=y(m),B=y(B);var k=0,T=Xe,_=Xe;return k=(A|0)==2,!(l<=y(0)&k)&&!(u<=y(0)&(d|0)==2)&&!((A|0)==1&(d|0)==1)?o=0:(T=y(yn(o,0,m)),_=y(yn(o,2,m)),k=l>2]=y(Gn(o,2,k?y(0):l,m,m)),l=y(u-T),k=u>2]=y(Gn(o,0,k?y(0):l,B,m)),o=1),o|0}function by(o,l){return o=o|0,l=l|0,Jg(o)|0?o=dr(2,l)|0:o=0,o|0}function Rh(o,l,u){return o=o|0,l=l|0,u=y(u),u=y(di(o,l,u)),y(u+y(Br(o,l)))}function s2(o,l,u){return o=o|0,l=l|0,u=y(u),u=y(ws(o,l,u)),y(u+y(_n(o,l)))}function Ka(o,l,u){o=o|0,l=l|0,u=y(u);var A=Xe;return A=y(Rh(o,l,u)),y(A+y(s2(o,l,u)))}function o2(o){return o=o|0,n[o+24>>2]|0?o=0:y(ZA(o))!=y(0)?o=1:o=y(Fh(o))!=y(0),o|0}function ZA(o){o=o|0;var l=Xe;if(n[o+944>>2]|0){if(l=y(h[o+44>>2]),Mt(l)|0)return l=y(h[o+40>>2]),o=l>y(0)&((Mt(l)|0)^1),y(o?l:y(0))}else l=y(0);return y(l)}function Fh(o){o=o|0;var l=Xe,u=0,A=Xe;do if(n[o+944>>2]|0){if(l=y(h[o+48>>2]),Mt(l)|0){if(u=s[(n[o+976>>2]|0)+2>>0]|0,!(u<<24>>24)&&(A=y(h[o+40>>2]),A>24?y(1):y(0)}}else l=y(0);while(!1);return y(l)}function Py(o){o=o|0;var l=0,u=0;if(eE(o+400|0,0,540)|0,s[o+985>>0]=1,ee(o),u=Li(o)|0,u|0){l=o+948|0,o=0;do Py(n[(n[l>>2]|0)+(o<<2)>>2]|0),o=o+1|0;while((o|0)!=(u|0))}}function BP(o,l,u,A,d,m,B,k,T,_){o=o|0,l=l|0,u=y(u),A=A|0,d=y(d),m=y(m),B=y(B),k=k|0,T=T|0,_=_|0;var M=0,G=Xe,ae=0,We=0,Le=Xe,Qe=Xe,tt=0,Ze=Xe,ct=0,He=Xe,Ge=0,Lt=0,qr=0,fr=0,$t=0,Tr=0,Hr=0,cr=0,Hn=0,Fo=0;Hn=I,I=I+16|0,qr=Hn+12|0,fr=Hn+8|0,$t=Hn+4|0,Tr=Hn,cr=dr(n[o+4>>2]|0,T)|0,Ge=de(cr)|0,G=y(Xr(tM(l)|0,Ge?m:B)),Lt=so(l,2,m)|0,Hr=so(l,0,B)|0;do if(!(Mt(G)|0)&&!(Mt(Ge?u:d)|0)){if(M=l+504|0,!(Mt(y(h[M>>2]))|0)&&(!(l2(n[l+976>>2]|0,0)|0)||(n[l+500>>2]|0)==(n[2278]|0)))break;h[M>>2]=y($n(G,y(Ka(l,cr,m))))}else ae=7;while(!1);do if((ae|0)==7){if(ct=Ge^1,!(ct|Lt^1)){B=y(Xr(n[l+992>>2]|0,m)),h[l+504>>2]=y($n(B,y(Ka(l,2,m))));break}if(!(Ge|Hr^1)){B=y(Xr(n[l+996>>2]|0,B)),h[l+504>>2]=y($n(B,y(Ka(l,0,m))));break}h[qr>>2]=y(ce),h[fr>>2]=y(ce),n[$t>>2]=0,n[Tr>>2]=0,Ze=y(yn(l,2,m)),He=y(yn(l,0,m)),Lt?(Le=y(Ze+y(Xr(n[l+992>>2]|0,m))),h[qr>>2]=Le,n[$t>>2]=1,We=1):(We=0,Le=y(ce)),Hr?(G=y(He+y(Xr(n[l+996>>2]|0,B))),h[fr>>2]=G,n[Tr>>2]=1,M=1):(M=0,G=y(ce)),ae=n[o+32>>2]|0,Ge&(ae|0)==2?ae=2:Mt(Le)|0&&!(Mt(u)|0)&&(h[qr>>2]=u,n[$t>>2]=2,We=2,Le=u),!((ae|0)==2&ct)&&Mt(G)|0&&!(Mt(d)|0)&&(h[fr>>2]=d,n[Tr>>2]=2,M=2,G=d),Qe=y(h[l+396>>2]),tt=Mt(Qe)|0;do if(tt)ae=We;else{if((We|0)==1&ct){h[fr>>2]=y(y(Le-Ze)/Qe),n[Tr>>2]=1,M=1,ae=1;break}Ge&(M|0)==1?(h[qr>>2]=y(Qe*y(G-He)),n[$t>>2]=1,M=1,ae=1):ae=We}while(!1);Fo=Mt(u)|0,We=(os(o,l)|0)!=4,!(Ge|Lt|((A|0)!=1|Fo)|(We|(ae|0)==1))&&(h[qr>>2]=u,n[$t>>2]=1,!tt)&&(h[fr>>2]=y(y(u-Ze)/Qe),n[Tr>>2]=1,M=1),!(Hr|ct|((k|0)!=1|(Mt(d)|0))|(We|(M|0)==1))&&(h[fr>>2]=d,n[Tr>>2]=1,!tt)&&(h[qr>>2]=y(Qe*y(d-He)),n[$t>>2]=1),Bu(l,2,m,m,$t,qr),Bu(l,0,B,m,Tr,fr),u=y(h[qr>>2]),d=y(h[fr>>2]),kl(l,u,d,T,n[$t>>2]|0,n[Tr>>2]|0,m,B,0,3565,_)|0,B=y(h[l+908+(n[976+(cr<<2)>>2]<<2)>>2]),h[l+504>>2]=y($n(B,y(Ka(l,cr,m))))}while(!1);n[l+500>>2]=n[2278],I=Hn}function Gn(o,l,u,A,d){return o=o|0,l=l|0,u=y(u),A=y(A),d=y(d),A=y(Kg(o,l,u,A)),y($n(A,y(Ka(o,l,d))))}function os(o,l){return o=o|0,l=l|0,l=l+20|0,l=n[(n[l>>2]|0?l:o+16|0)>>2]|0,(l|0)==5&&Jg(n[o+4>>2]|0)|0&&(l=1),l|0}function Ql(o,l){return o=o|0,l=l|0,de(l)|0&&n[o+96>>2]|0?l=4:l=n[1040+(l<<2)>>2]|0,o+60+(l<<3)|0}function Tl(o,l){return o=o|0,l=l|0,de(l)|0&&n[o+104>>2]|0?l=5:l=n[1e3+(l<<2)>>2]|0,o+60+(l<<3)|0}function Bu(o,l,u,A,d,m){switch(o=o|0,l=l|0,u=y(u),A=y(A),d=d|0,m=m|0,u=y(Xr(o+380+(n[976+(l<<2)>>2]<<3)|0,u)),u=y(u+y(yn(o,l,A))),n[d>>2]|0){case 2:case 1:{d=Mt(u)|0,A=y(h[m>>2]),h[m>>2]=d|A>2]=2,h[m>>2]=u);break}default:}}function ga(o,l){return o=o|0,l=l|0,o=o+132|0,de(l)|0&&n[(kn(o,4,948)|0)+4>>2]|0?o=1:o=(n[(kn(o,n[1040+(l<<2)>>2]|0,948)|0)+4>>2]|0)!=0,o|0}function XA(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0;return o=o+132|0,de(l)|0&&(A=kn(o,4,948)|0,(n[A+4>>2]|0)!=0)?d=4:(A=kn(o,n[1040+(l<<2)>>2]|0,948)|0,n[A+4>>2]|0?d=4:u=y(0)),(d|0)==4&&(u=y(Xr(A,u))),y(u)}function $A(o,l,u){o=o|0,l=l|0,u=y(u);var A=Xe;return A=y(h[o+908+(n[976+(l<<2)>>2]<<2)>>2]),A=y(A+y(J(o,l,u))),y(A+y(re(o,l,u)))}function XL(o){o=o|0;var l=0,u=0,A=0;e:do if(Jg(n[o+4>>2]|0)|0)l=0;else if((n[o+16>>2]|0)!=5)if(u=Li(o)|0,!u)l=0;else for(l=0;;){if(A=Cs(o,l)|0,!(n[A+24>>2]|0)&&(n[A+20>>2]|0)==5){l=1;break e}if(l=l+1|0,l>>>0>=u>>>0){l=0;break}}else l=1;while(!1);return l|0}function $L(o,l){o=o|0,l=l|0;var u=Xe;return u=y(h[o+908+(n[976+(l<<2)>>2]<<2)>>2]),u>=y(0)&((Mt(u)|0)^1)|0}function Vg(o){o=o|0;var l=Xe,u=0,A=0,d=0,m=0,B=0,k=0,T=Xe;if(u=n[o+968>>2]|0,u)T=y(h[o+908>>2]),l=y(h[o+912>>2]),l=y(I$[u&0](o,T,l)),wi(o,(Mt(l)|0)^1,3573);else{m=Li(o)|0;do if(m|0){for(u=0,d=0;;){if(A=Cs(o,d)|0,n[A+940>>2]|0){B=8;break}if((n[A+24>>2]|0)!=1)if(k=(os(o,A)|0)==5,k){u=A;break}else u=u|0?u:A;if(d=d+1|0,d>>>0>=m>>>0){B=8;break}}if((B|0)==8&&!u)break;return l=y(Vg(u)),y(l+y(h[u+404>>2]))}while(!1);l=y(h[o+912>>2])}return y(l)}function Kg(o,l,u,A){o=o|0,l=l|0,u=y(u),A=y(A);var d=Xe,m=0;return Jg(l)|0?(l=1,m=3):de(l)|0?(l=0,m=3):(A=y(ce),d=y(ce)),(m|0)==3&&(d=y(Xr(o+364+(l<<3)|0,A)),A=y(Xr(o+380+(l<<3)|0,A))),m=A=y(0)&((Mt(A)|0)^1)),u=m?A:u,m=d>=y(0)&((Mt(d)|0)^1)&u>2]|0,m)|0,Le=by(tt,m)|0,Qe=de(tt)|0,G=y(yn(l,2,u)),ae=y(yn(l,0,u)),so(l,2,u)|0?k=y(G+y(Xr(n[l+992>>2]|0,u))):ga(l,2)|0&&xy(l,2)|0?(k=y(h[o+908>>2]),T=y(Br(o,2)),T=y(k-y(T+y(_n(o,2)))),k=y(XA(l,2,u)),k=y(Gn(l,2,y(T-y(k+y(Nh(l,2,u)))),u,u))):k=y(ce),so(l,0,d)|0?T=y(ae+y(Xr(n[l+996>>2]|0,d))):ga(l,0)|0&&xy(l,0)|0?(T=y(h[o+912>>2]),ct=y(Br(o,0)),ct=y(T-y(ct+y(_n(o,0)))),T=y(XA(l,0,d)),T=y(Gn(l,0,y(ct-y(T+y(Nh(l,0,d)))),d,u))):T=y(ce),_=Mt(k)|0,M=Mt(T)|0;do if(_^M&&(We=y(h[l+396>>2]),!(Mt(We)|0)))if(_){k=y(G+y(y(T-ae)*We));break}else{ct=y(ae+y(y(k-G)/We)),T=M?ct:T;break}while(!1);M=Mt(k)|0,_=Mt(T)|0,M|_&&(He=(M^1)&1,A=u>y(0)&((A|0)!=0&M),k=Qe?k:A?u:k,kl(l,k,T,m,Qe?He:A?2:He,M&(_^1)&1,k,T,0,3623,B)|0,k=y(h[l+908>>2]),k=y(k+y(yn(l,2,u))),T=y(h[l+912>>2]),T=y(T+y(yn(l,0,u)))),kl(l,k,T,m,1,1,k,T,1,3635,B)|0,xy(l,tt)|0&&!(ga(l,tt)|0)?(He=n[976+(tt<<2)>>2]|0,ct=y(h[o+908+(He<<2)>>2]),ct=y(ct-y(h[l+908+(He<<2)>>2])),ct=y(ct-y(_n(o,tt))),ct=y(ct-y(re(l,tt,u))),ct=y(ct-y(Nh(l,tt,Qe?u:d))),h[l+400+(n[1040+(tt<<2)>>2]<<2)>>2]=ct):Ze=21;do if((Ze|0)==21){if(!(ga(l,tt)|0)&&(n[o+8>>2]|0)==1){He=n[976+(tt<<2)>>2]|0,ct=y(h[o+908+(He<<2)>>2]),ct=y(y(ct-y(h[l+908+(He<<2)>>2]))*y(.5)),h[l+400+(n[1040+(tt<<2)>>2]<<2)>>2]=ct;break}!(ga(l,tt)|0)&&(n[o+8>>2]|0)==2&&(He=n[976+(tt<<2)>>2]|0,ct=y(h[o+908+(He<<2)>>2]),ct=y(ct-y(h[l+908+(He<<2)>>2])),h[l+400+(n[1040+(tt<<2)>>2]<<2)>>2]=ct)}while(!1);xy(l,Le)|0&&!(ga(l,Le)|0)?(He=n[976+(Le<<2)>>2]|0,ct=y(h[o+908+(He<<2)>>2]),ct=y(ct-y(h[l+908+(He<<2)>>2])),ct=y(ct-y(_n(o,Le))),ct=y(ct-y(re(l,Le,u))),ct=y(ct-y(Nh(l,Le,Qe?d:u))),h[l+400+(n[1040+(Le<<2)>>2]<<2)>>2]=ct):Ze=30;do if((Ze|0)==30&&!(ga(l,Le)|0)){if((os(o,l)|0)==2){He=n[976+(Le<<2)>>2]|0,ct=y(h[o+908+(He<<2)>>2]),ct=y(y(ct-y(h[l+908+(He<<2)>>2]))*y(.5)),h[l+400+(n[1040+(Le<<2)>>2]<<2)>>2]=ct;break}He=(os(o,l)|0)==3,He^(n[o+28>>2]|0)==2&&(He=n[976+(Le<<2)>>2]|0,ct=y(h[o+908+(He<<2)>>2]),ct=y(ct-y(h[l+908+(He<<2)>>2])),h[l+400+(n[1040+(Le<<2)>>2]<<2)>>2]=ct)}while(!1)}function a2(o,l,u){o=o|0,l=l|0,u=u|0;var A=Xe,d=0;d=n[976+(u<<2)>>2]|0,A=y(h[l+908+(d<<2)>>2]),A=y(y(h[o+908+(d<<2)>>2])-A),A=y(A-y(h[l+400+(n[1040+(u<<2)>>2]<<2)>>2])),h[l+400+(n[1e3+(u<<2)>>2]<<2)>>2]=A}function Jg(o){return o=o|0,(o|1|0)==1|0}function tM(o){o=o|0;var l=Xe;switch(n[o+56>>2]|0){case 0:case 3:{l=y(h[o+40>>2]),l>y(0)&((Mt(l)|0)^1)?o=s[(n[o+976>>2]|0)+2>>0]|0?1056:992:o=1056;break}default:o=o+52|0}return o|0}function l2(o,l){return o=o|0,l=l|0,(s[o+l>>0]|0)!=0|0}function xy(o,l){return o=o|0,l=l|0,o=o+132|0,de(l)|0&&n[(kn(o,5,948)|0)+4>>2]|0?o=1:o=(n[(kn(o,n[1e3+(l<<2)>>2]|0,948)|0)+4>>2]|0)!=0,o|0}function Nh(o,l,u){o=o|0,l=l|0,u=y(u);var A=0,d=0;return o=o+132|0,de(l)|0&&(A=kn(o,5,948)|0,(n[A+4>>2]|0)!=0)?d=4:(A=kn(o,n[1e3+(l<<2)>>2]|0,948)|0,n[A+4>>2]|0?d=4:u=y(0)),(d|0)==4&&(u=y(Xr(A,u))),y(u)}function vP(o,l,u){return o=o|0,l=l|0,u=y(u),ga(o,l)|0?u=y(XA(o,l,u)):u=y(-y(Nh(o,l,u))),y(u)}function SP(o){return o=y(o),h[S>>2]=o,n[S>>2]|0|0}function ky(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>1073741823)Nt();else{d=Jt(l<<2)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<2)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<2)}function DP(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>2)<<2)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function Qy(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-4-l|0)>>>2)<<2)),o=n[o>>2]|0,o|0&&yt(o)}function bP(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;if(B=o+4|0,k=n[B>>2]|0,d=k-A|0,m=d>>2,o=l+(m<<2)|0,o>>>0>>0){A=k;do n[A>>2]=n[o>>2],o=o+4|0,A=(n[B>>2]|0)+4|0,n[B>>2]=A;while(o>>>0>>0)}m|0&&F2(k+(0-m<<2)|0,l|0,d|0)|0}function PP(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0;return k=l+4|0,T=n[k>>2]|0,d=n[o>>2]|0,B=u,m=B-d|0,A=T+(0-(m>>2)<<2)|0,n[k>>2]=A,(m|0)>0&&Qr(A|0,d|0,m|0)|0,d=o+4|0,m=l+8|0,A=(n[d>>2]|0)-B|0,(A|0)>0&&(Qr(n[m>>2]|0,u|0,A|0)|0,n[m>>2]=(n[m>>2]|0)+(A>>>2<<2)),B=n[o>>2]|0,n[o>>2]=n[k>>2],n[k>>2]=B,B=n[d>>2]|0,n[d>>2]=n[m>>2],n[m>>2]=B,B=o+8|0,u=l+12|0,o=n[B>>2]|0,n[B>>2]=n[u>>2],n[u>>2]=o,n[l>>2]=n[k>>2],T|0}function c2(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;if(B=n[l>>2]|0,m=n[u>>2]|0,(B|0)!=(m|0)){d=o+8|0,u=((m+-4-B|0)>>>2)+1|0,o=B,A=n[d>>2]|0;do n[A>>2]=n[o>>2],A=(n[d>>2]|0)+4|0,n[d>>2]=A,o=o+4|0;while((o|0)!=(m|0));n[l>>2]=B+(u<<2)}}function u2(){fa()}function xP(){var o=0;return o=Jt(4)|0,f2(o),o|0}function f2(o){o=o|0,n[o>>2]=pc()|0}function kP(o){o=o|0,o|0&&(zg(o),yt(o))}function zg(o){o=o|0,st(n[o>>2]|0)}function rM(o,l,u){o=o|0,l=l|0,u=u|0,hc(n[o>>2]|0,l,u)}function Ty(o,l){o=o|0,l=y(l),xh(n[o>>2]|0,l)}function Ry(o,l){return o=o|0,l=l|0,l2(n[o>>2]|0,l)|0}function Fy(){var o=0;return o=Jt(8)|0,Zg(o,0),o|0}function Zg(o,l){o=o|0,l=l|0,l?l=Aa(n[l>>2]|0)|0:l=ns()|0,n[o>>2]=l,n[o+4>>2]=0,Tn(l,o)}function Ny(o){o=o|0;var l=0;return l=Jt(8)|0,Zg(l,o),l|0}function Xg(o){o=o|0,o|0&&(Oy(o),yt(o))}function Oy(o){o=o|0;var l=0;fc(n[o>>2]|0),l=o+4|0,o=n[l>>2]|0,n[l>>2]=0,o|0&&(Df(o),yt(o))}function Df(o){o=o|0,bf(o)}function bf(o){o=o|0,o=n[o>>2]|0,o|0&&Oa(o|0)}function A2(o){return o=o|0,Ga(o)|0}function p2(o){o=o|0;var l=0,u=0;u=o+4|0,l=n[u>>2]|0,n[u>>2]=0,l|0&&(Df(l),yt(l)),Ac(n[o>>2]|0)}function Ly(o,l){o=o|0,l=l|0,fn(n[o>>2]|0,n[l>>2]|0)}function nM(o,l){o=o|0,l=l|0,Sh(n[o>>2]|0,l)}function iM(o,l,u){o=o|0,l=l|0,u=+u,Cy(n[o>>2]|0,l,y(u))}function My(o,l,u){o=o|0,l=l|0,u=+u,wy(n[o>>2]|0,l,y(u))}function h2(o,l){o=o|0,l=l|0,wh(n[o>>2]|0,l)}function g2(o,l){o=o|0,l=l|0,bo(n[o>>2]|0,l)}function xr(o,l){o=o|0,l=l|0,vh(n[o>>2]|0,l)}function oo(o,l){o=o|0,l=l|0,my(n[o>>2]|0,l)}function Zi(o,l){o=o|0,l=l|0,Og(n[o>>2]|0,l)}function Os(o,l){o=o|0,l=l|0,Do(n[o>>2]|0,l)}function ep(o,l,u){o=o|0,l=l|0,u=+u,qA(n[o>>2]|0,l,y(u))}function d2(o,l,u){o=o|0,l=l|0,u=+u,Y(n[o>>2]|0,l,y(u))}function Bs(o,l){o=o|0,l=l|0,GA(n[o>>2]|0,l)}function _y(o,l){o=o|0,l=l|0,Ey(n[o>>2]|0,l)}function Oh(o,l){o=o|0,l=l|0,Po(n[o>>2]|0,l)}function $g(o,l){o=o|0,l=+l,Dh(n[o>>2]|0,y(l))}function Lh(o,l){o=o|0,l=+l,Pl(n[o>>2]|0,y(l))}function m2(o,l){o=o|0,l=+l,Iy(n[o>>2]|0,y(l))}function y2(o,l){o=o|0,l=+l,Mg(n[o>>2]|0,y(l))}function E2(o,l){o=o|0,l=+l,bl(n[o>>2]|0,y(l))}function I2(o,l){o=o|0,l=+l,_g(n[o>>2]|0,y(l))}function Pf(o,l){o=o|0,l=+l,n2(n[o>>2]|0,y(l))}function sr(o){o=o|0,bh(n[o>>2]|0)}function Uy(o,l){o=o|0,l=+l,zi(n[o>>2]|0,y(l))}function C2(o,l){o=o|0,l=+l,Ef(n[o>>2]|0,y(l))}function gc(o){o=o|0,Wa(n[o>>2]|0)}function xf(o,l){o=o|0,l=+l,yu(n[o>>2]|0,y(l))}function ed(o,l){o=o|0,l=+l,If(n[o>>2]|0,y(l))}function td(o,l){o=o|0,l=+l,gi(n[o>>2]|0,y(l))}function w2(o,l){o=o|0,l=+l,WA(n[o>>2]|0,y(l))}function B2(o,l){o=o|0,l=+l,pa(n[o>>2]|0,y(l))}function vu(o,l){o=o|0,l=+l,Va(n[o>>2]|0,y(l))}function rd(o,l){o=o|0,l=+l,Ph(n[o>>2]|0,y(l))}function v2(o,l){o=o|0,l=+l,jg(n[o>>2]|0,y(l))}function Hy(o,l){o=o|0,l=+l,YA(n[o>>2]|0,y(l))}function Su(o,l,u){o=o|0,l=l|0,u=+u,mu(n[o>>2]|0,l,y(u))}function jy(o,l,u){o=o|0,l=l|0,u=+u,xo(n[o>>2]|0,l,y(u))}function nd(o,l,u){o=o|0,l=l|0,u=+u,yf(n[o>>2]|0,l,y(u))}function id(o){return o=o|0,Ng(n[o>>2]|0)|0}function To(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;A=I,I=I+16|0,d=A,jA(d,n[l>>2]|0,u),vs(o,d),I=A}function vs(o,l){o=o|0,l=l|0,Rl(o,n[l+4>>2]|0,+y(h[l>>2]))}function Rl(o,l,u){o=o|0,l=l|0,u=+u,n[o>>2]=l,E[o+8>>3]=u}function qy(o){return o=o|0,r2(n[o>>2]|0)|0}function da(o){return o=o|0,Bh(n[o>>2]|0)|0}function QP(o){return o=o|0,du(n[o>>2]|0)|0}function Mh(o){return o=o|0,t2(n[o>>2]|0)|0}function S2(o){return o=o|0,Lg(n[o>>2]|0)|0}function sM(o){return o=o|0,yy(n[o>>2]|0)|0}function TP(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;A=I,I=I+16|0,d=A,xt(d,n[l>>2]|0,u),vs(o,d),I=A}function RP(o){return o=o|0,mf(n[o>>2]|0)|0}function Gy(o){return o=o|0,Dl(n[o>>2]|0)|0}function D2(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,HA(A,n[l>>2]|0),vs(o,A),I=u}function _h(o){return o=o|0,+ +y(ai(n[o>>2]|0))}function FP(o){return o=o|0,+ +y(qi(n[o>>2]|0))}function NP(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,ur(A,n[l>>2]|0),vs(o,A),I=u}function sd(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,Ug(A,n[l>>2]|0),vs(o,A),I=u}function oM(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,wt(A,n[l>>2]|0),vs(o,A),I=u}function aM(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,Ya(A,n[l>>2]|0),vs(o,A),I=u}function OP(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,Hg(A,n[l>>2]|0),vs(o,A),I=u}function LP(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,vy(A,n[l>>2]|0),vs(o,A),I=u}function tp(o){return o=o|0,+ +y(qg(n[o>>2]|0))}function lM(o,l){return o=o|0,l=l|0,+ +y(By(n[o>>2]|0,l))}function cM(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;A=I,I=I+16|0,d=A,dt(d,n[l>>2]|0,u),vs(o,d),I=A}function Du(o,l,u){o=o|0,l=l|0,u=u|0,lr(n[o>>2]|0,n[l>>2]|0,u)}function uM(o,l){o=o|0,l=l|0,df(n[o>>2]|0,n[l>>2]|0)}function MP(o){return o=o|0,Li(n[o>>2]|0)|0}function fM(o){return o=o|0,o=mt(n[o>>2]|0)|0,o?o=A2(o)|0:o=0,o|0}function _P(o,l){return o=o|0,l=l|0,o=Cs(n[o>>2]|0,l)|0,o?o=A2(o)|0:o=0,o|0}function kf(o,l){o=o|0,l=l|0;var u=0,A=0;A=Jt(4)|0,UP(A,l),u=o+4|0,l=n[u>>2]|0,n[u>>2]=A,l|0&&(Df(l),yt(l)),St(n[o>>2]|0,1)}function UP(o,l){o=o|0,l=l|0,gM(o,l)}function AM(o,l,u,A,d,m){o=o|0,l=l|0,u=y(u),A=A|0,d=y(d),m=m|0;var B=0,k=0;B=I,I=I+16|0,k=B,HP(k,Ga(l)|0,+u,A,+d,m),h[o>>2]=y(+E[k>>3]),h[o+4>>2]=y(+E[k+8>>3]),I=B}function HP(o,l,u,A,d,m){o=o|0,l=l|0,u=+u,A=A|0,d=+d,m=m|0;var B=0,k=0,T=0,_=0,M=0;B=I,I=I+32|0,M=B+8|0,_=B+20|0,T=B,k=B+16|0,E[M>>3]=u,n[_>>2]=A,E[T>>3]=d,n[k>>2]=m,Wy(o,n[l+4>>2]|0,M,_,T,k),I=B}function Wy(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0;var B=0,k=0;B=I,I=I+16|0,k=B,Nl(k),l=Ls(l)|0,jP(o,l,+E[u>>3],n[A>>2]|0,+E[d>>3],n[m>>2]|0),Ol(k),I=B}function Ls(o){return o=o|0,n[o>>2]|0}function jP(o,l,u,A,d,m){o=o|0,l=l|0,u=+u,A=A|0,d=+d,m=m|0;var B=0;B=ma(b2()|0)|0,u=+Ja(u),A=Yy(A)|0,d=+Ja(d),pM(o,Jn(0,B|0,l|0,+u,A|0,+d,Yy(m)|0)|0)}function b2(){var o=0;return s[7608]|0||(x2(9120),o=7608,n[o>>2]=1,n[o+4>>2]=0),9120}function ma(o){return o=o|0,n[o+8>>2]|0}function Ja(o){return o=+o,+ +Qf(o)}function Yy(o){return o=o|0,od(o)|0}function pM(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;d=I,I=I+32|0,u=d,A=l,A&1?(za(u,0),Me(A|0,u|0)|0,P2(o,u),hM(u)):(n[o>>2]=n[l>>2],n[o+4>>2]=n[l+4>>2],n[o+8>>2]=n[l+8>>2],n[o+12>>2]=n[l+12>>2]),I=d}function za(o,l){o=o|0,l=l|0,bu(o,l),n[o+8>>2]=0,s[o+24>>0]=0}function P2(o,l){o=o|0,l=l|0,l=l+8|0,n[o>>2]=n[l>>2],n[o+4>>2]=n[l+4>>2],n[o+8>>2]=n[l+8>>2],n[o+12>>2]=n[l+12>>2]}function hM(o){o=o|0,s[o+24>>0]=0}function bu(o,l){o=o|0,l=l|0,n[o>>2]=l}function od(o){return o=o|0,o|0}function Qf(o){return o=+o,+o}function x2(o){o=o|0,Ro(o,k2()|0,4)}function k2(){return 1064}function Ro(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u,n[o+8>>2]=ji(l|0,u+1|0)|0}function gM(o,l){o=o|0,l=l|0,l=n[l>>2]|0,n[o>>2]=l,cu(l|0)}function qP(o){o=o|0;var l=0,u=0;u=o+4|0,l=n[u>>2]|0,n[u>>2]=0,l|0&&(Df(l),yt(l)),St(n[o>>2]|0,0)}function GP(o){o=o|0,Dt(n[o>>2]|0)}function Vy(o){return o=o|0,tr(n[o>>2]|0)|0}function dM(o,l,u,A){o=o|0,l=+l,u=+u,A=A|0,KA(n[o>>2]|0,y(l),y(u),A)}function mM(o){return o=o|0,+ +y(Eu(n[o>>2]|0))}function v(o){return o=o|0,+ +y(Cf(n[o>>2]|0))}function D(o){return o=o|0,+ +y(Iu(n[o>>2]|0))}function Q(o){return o=o|0,+ +y(Fs(n[o>>2]|0))}function H(o){return o=o|0,+ +y(Cu(n[o>>2]|0))}function V(o){return o=o|0,+ +y(qn(n[o>>2]|0))}function ne(o,l){o=o|0,l=l|0,E[o>>3]=+y(Eu(n[l>>2]|0)),E[o+8>>3]=+y(Cf(n[l>>2]|0)),E[o+16>>3]=+y(Iu(n[l>>2]|0)),E[o+24>>3]=+y(Fs(n[l>>2]|0)),E[o+32>>3]=+y(Cu(n[l>>2]|0)),E[o+40>>3]=+y(qn(n[l>>2]|0))}function Se(o,l){return o=o|0,l=l|0,+ +y(is(n[o>>2]|0,l))}function Ue(o,l){return o=o|0,l=l|0,+ +y(xi(n[o>>2]|0,l))}function At(o,l){return o=o|0,l=l|0,+ +y(VA(n[o>>2]|0,l))}function Gt(){return Qn()|0}function vr(){Lr(),Xt(),zn(),mi(),Za(),$e()}function Lr(){Kqe(11713,4938,1)}function Xt(){pqe(10448)}function zn(){K6e(10408)}function mi(){m6e(10324)}function Za(){SHe(10096)}function $e(){qe(9132)}function qe(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0,tt=0,Ze=0,ct=0,He=0,Ge=0,Lt=0,qr=0,fr=0,$t=0,Tr=0,Hr=0,cr=0,Hn=0,Fo=0,No=0,Oo=0,$a=0,Kh=0,Jh=0,dc=0,zh=0,Ff=0,Nf=0,Zh=0,Xh=0,$h=0,on=0,mc=0,e0=0,ku=0,t0=0,r0=0,Of=0,Lf=0,Qu=0,ao=0,Ml=0,ya=0,yc=0,lp=0,cp=0,Mf=0,up=0,fp=0,lo=0,_s=0,Ec=0,Wn=0,Ap=0,Lo=0,Tu=0,Mo=0,Ru=0,pp=0,hp=0,Fu=0,co=0,Ic=0,gp=0,dp=0,mp=0,Fr=0,ci=0,Us=0,_o=0,uo=0,Mr=0,Ar=0,Cc=0;l=I,I=I+672|0,u=l+656|0,Cc=l+648|0,Ar=l+640|0,Mr=l+632|0,uo=l+624|0,_o=l+616|0,Us=l+608|0,ci=l+600|0,Fr=l+592|0,mp=l+584|0,dp=l+576|0,gp=l+568|0,Ic=l+560|0,co=l+552|0,Fu=l+544|0,hp=l+536|0,pp=l+528|0,Ru=l+520|0,Mo=l+512|0,Tu=l+504|0,Lo=l+496|0,Ap=l+488|0,Wn=l+480|0,Ec=l+472|0,_s=l+464|0,lo=l+456|0,fp=l+448|0,up=l+440|0,Mf=l+432|0,cp=l+424|0,lp=l+416|0,yc=l+408|0,ya=l+400|0,Ml=l+392|0,ao=l+384|0,Qu=l+376|0,Lf=l+368|0,Of=l+360|0,r0=l+352|0,t0=l+344|0,ku=l+336|0,e0=l+328|0,mc=l+320|0,on=l+312|0,$h=l+304|0,Xh=l+296|0,Zh=l+288|0,Nf=l+280|0,Ff=l+272|0,zh=l+264|0,dc=l+256|0,Jh=l+248|0,Kh=l+240|0,$a=l+232|0,Oo=l+224|0,No=l+216|0,Fo=l+208|0,Hn=l+200|0,cr=l+192|0,Hr=l+184|0,Tr=l+176|0,$t=l+168|0,fr=l+160|0,qr=l+152|0,Lt=l+144|0,Ge=l+136|0,He=l+128|0,ct=l+120|0,Ze=l+112|0,tt=l+104|0,Qe=l+96|0,Le=l+88|0,We=l+80|0,ae=l+72|0,G=l+64|0,M=l+56|0,_=l+48|0,T=l+40|0,k=l+32|0,B=l+24|0,m=l+16|0,d=l+8|0,A=l,pt(o,3646),Zt(o,3651,2)|0,Sr(o,3665,2)|0,Xn(o,3682,18)|0,n[Cc>>2]=19,n[Cc+4>>2]=0,n[u>>2]=n[Cc>>2],n[u+4>>2]=n[Cc+4>>2],kr(o,3690,u)|0,n[Ar>>2]=1,n[Ar+4>>2]=0,n[u>>2]=n[Ar>>2],n[u+4>>2]=n[Ar+4>>2],Rn(o,3696,u)|0,n[Mr>>2]=2,n[Mr+4>>2]=0,n[u>>2]=n[Mr>>2],n[u+4>>2]=n[Mr+4>>2],Un(o,3706,u)|0,n[uo>>2]=1,n[uo+4>>2]=0,n[u>>2]=n[uo>>2],n[u+4>>2]=n[uo+4>>2],zr(o,3722,u)|0,n[_o>>2]=2,n[_o+4>>2]=0,n[u>>2]=n[_o>>2],n[u+4>>2]=n[_o+4>>2],zr(o,3734,u)|0,n[Us>>2]=3,n[Us+4>>2]=0,n[u>>2]=n[Us>>2],n[u+4>>2]=n[Us+4>>2],Un(o,3753,u)|0,n[ci>>2]=4,n[ci+4>>2]=0,n[u>>2]=n[ci>>2],n[u+4>>2]=n[ci+4>>2],Un(o,3769,u)|0,n[Fr>>2]=5,n[Fr+4>>2]=0,n[u>>2]=n[Fr>>2],n[u+4>>2]=n[Fr+4>>2],Un(o,3783,u)|0,n[mp>>2]=6,n[mp+4>>2]=0,n[u>>2]=n[mp>>2],n[u+4>>2]=n[mp+4>>2],Un(o,3796,u)|0,n[dp>>2]=7,n[dp+4>>2]=0,n[u>>2]=n[dp>>2],n[u+4>>2]=n[dp+4>>2],Un(o,3813,u)|0,n[gp>>2]=8,n[gp+4>>2]=0,n[u>>2]=n[gp>>2],n[u+4>>2]=n[gp+4>>2],Un(o,3825,u)|0,n[Ic>>2]=3,n[Ic+4>>2]=0,n[u>>2]=n[Ic>>2],n[u+4>>2]=n[Ic+4>>2],zr(o,3843,u)|0,n[co>>2]=4,n[co+4>>2]=0,n[u>>2]=n[co>>2],n[u+4>>2]=n[co+4>>2],zr(o,3853,u)|0,n[Fu>>2]=9,n[Fu+4>>2]=0,n[u>>2]=n[Fu>>2],n[u+4>>2]=n[Fu+4>>2],Un(o,3870,u)|0,n[hp>>2]=10,n[hp+4>>2]=0,n[u>>2]=n[hp>>2],n[u+4>>2]=n[hp+4>>2],Un(o,3884,u)|0,n[pp>>2]=11,n[pp+4>>2]=0,n[u>>2]=n[pp>>2],n[u+4>>2]=n[pp+4>>2],Un(o,3896,u)|0,n[Ru>>2]=1,n[Ru+4>>2]=0,n[u>>2]=n[Ru>>2],n[u+4>>2]=n[Ru+4>>2],li(o,3907,u)|0,n[Mo>>2]=2,n[Mo+4>>2]=0,n[u>>2]=n[Mo>>2],n[u+4>>2]=n[Mo+4>>2],li(o,3915,u)|0,n[Tu>>2]=3,n[Tu+4>>2]=0,n[u>>2]=n[Tu>>2],n[u+4>>2]=n[Tu+4>>2],li(o,3928,u)|0,n[Lo>>2]=4,n[Lo+4>>2]=0,n[u>>2]=n[Lo>>2],n[u+4>>2]=n[Lo+4>>2],li(o,3948,u)|0,n[Ap>>2]=5,n[Ap+4>>2]=0,n[u>>2]=n[Ap>>2],n[u+4>>2]=n[Ap+4>>2],li(o,3960,u)|0,n[Wn>>2]=6,n[Wn+4>>2]=0,n[u>>2]=n[Wn>>2],n[u+4>>2]=n[Wn+4>>2],li(o,3974,u)|0,n[Ec>>2]=7,n[Ec+4>>2]=0,n[u>>2]=n[Ec>>2],n[u+4>>2]=n[Ec+4>>2],li(o,3983,u)|0,n[_s>>2]=20,n[_s+4>>2]=0,n[u>>2]=n[_s>>2],n[u+4>>2]=n[_s+4>>2],kr(o,3999,u)|0,n[lo>>2]=8,n[lo+4>>2]=0,n[u>>2]=n[lo>>2],n[u+4>>2]=n[lo+4>>2],li(o,4012,u)|0,n[fp>>2]=9,n[fp+4>>2]=0,n[u>>2]=n[fp>>2],n[u+4>>2]=n[fp+4>>2],li(o,4022,u)|0,n[up>>2]=21,n[up+4>>2]=0,n[u>>2]=n[up>>2],n[u+4>>2]=n[up+4>>2],kr(o,4039,u)|0,n[Mf>>2]=10,n[Mf+4>>2]=0,n[u>>2]=n[Mf>>2],n[u+4>>2]=n[Mf+4>>2],li(o,4053,u)|0,n[cp>>2]=11,n[cp+4>>2]=0,n[u>>2]=n[cp>>2],n[u+4>>2]=n[cp+4>>2],li(o,4065,u)|0,n[lp>>2]=12,n[lp+4>>2]=0,n[u>>2]=n[lp>>2],n[u+4>>2]=n[lp+4>>2],li(o,4084,u)|0,n[yc>>2]=13,n[yc+4>>2]=0,n[u>>2]=n[yc>>2],n[u+4>>2]=n[yc+4>>2],li(o,4097,u)|0,n[ya>>2]=14,n[ya+4>>2]=0,n[u>>2]=n[ya>>2],n[u+4>>2]=n[ya+4>>2],li(o,4117,u)|0,n[Ml>>2]=15,n[Ml+4>>2]=0,n[u>>2]=n[Ml>>2],n[u+4>>2]=n[Ml+4>>2],li(o,4129,u)|0,n[ao>>2]=16,n[ao+4>>2]=0,n[u>>2]=n[ao>>2],n[u+4>>2]=n[ao+4>>2],li(o,4148,u)|0,n[Qu>>2]=17,n[Qu+4>>2]=0,n[u>>2]=n[Qu>>2],n[u+4>>2]=n[Qu+4>>2],li(o,4161,u)|0,n[Lf>>2]=18,n[Lf+4>>2]=0,n[u>>2]=n[Lf>>2],n[u+4>>2]=n[Lf+4>>2],li(o,4181,u)|0,n[Of>>2]=5,n[Of+4>>2]=0,n[u>>2]=n[Of>>2],n[u+4>>2]=n[Of+4>>2],zr(o,4196,u)|0,n[r0>>2]=6,n[r0+4>>2]=0,n[u>>2]=n[r0>>2],n[u+4>>2]=n[r0+4>>2],zr(o,4206,u)|0,n[t0>>2]=7,n[t0+4>>2]=0,n[u>>2]=n[t0>>2],n[u+4>>2]=n[t0+4>>2],zr(o,4217,u)|0,n[ku>>2]=3,n[ku+4>>2]=0,n[u>>2]=n[ku>>2],n[u+4>>2]=n[ku+4>>2],Pu(o,4235,u)|0,n[e0>>2]=1,n[e0+4>>2]=0,n[u>>2]=n[e0>>2],n[u+4>>2]=n[e0+4>>2],yM(o,4251,u)|0,n[mc>>2]=4,n[mc+4>>2]=0,n[u>>2]=n[mc>>2],n[u+4>>2]=n[mc+4>>2],Pu(o,4263,u)|0,n[on>>2]=5,n[on+4>>2]=0,n[u>>2]=n[on>>2],n[u+4>>2]=n[on+4>>2],Pu(o,4279,u)|0,n[$h>>2]=6,n[$h+4>>2]=0,n[u>>2]=n[$h>>2],n[u+4>>2]=n[$h+4>>2],Pu(o,4293,u)|0,n[Xh>>2]=7,n[Xh+4>>2]=0,n[u>>2]=n[Xh>>2],n[u+4>>2]=n[Xh+4>>2],Pu(o,4306,u)|0,n[Zh>>2]=8,n[Zh+4>>2]=0,n[u>>2]=n[Zh>>2],n[u+4>>2]=n[Zh+4>>2],Pu(o,4323,u)|0,n[Nf>>2]=9,n[Nf+4>>2]=0,n[u>>2]=n[Nf>>2],n[u+4>>2]=n[Nf+4>>2],Pu(o,4335,u)|0,n[Ff>>2]=2,n[Ff+4>>2]=0,n[u>>2]=n[Ff>>2],n[u+4>>2]=n[Ff+4>>2],yM(o,4353,u)|0,n[zh>>2]=12,n[zh+4>>2]=0,n[u>>2]=n[zh>>2],n[u+4>>2]=n[zh+4>>2],ad(o,4363,u)|0,n[dc>>2]=1,n[dc+4>>2]=0,n[u>>2]=n[dc>>2],n[u+4>>2]=n[dc+4>>2],rp(o,4376,u)|0,n[Jh>>2]=2,n[Jh+4>>2]=0,n[u>>2]=n[Jh>>2],n[u+4>>2]=n[Jh+4>>2],rp(o,4388,u)|0,n[Kh>>2]=13,n[Kh+4>>2]=0,n[u>>2]=n[Kh>>2],n[u+4>>2]=n[Kh+4>>2],ad(o,4402,u)|0,n[$a>>2]=14,n[$a+4>>2]=0,n[u>>2]=n[$a>>2],n[u+4>>2]=n[$a+4>>2],ad(o,4411,u)|0,n[Oo>>2]=15,n[Oo+4>>2]=0,n[u>>2]=n[Oo>>2],n[u+4>>2]=n[Oo+4>>2],ad(o,4421,u)|0,n[No>>2]=16,n[No+4>>2]=0,n[u>>2]=n[No>>2],n[u+4>>2]=n[No+4>>2],ad(o,4433,u)|0,n[Fo>>2]=17,n[Fo+4>>2]=0,n[u>>2]=n[Fo>>2],n[u+4>>2]=n[Fo+4>>2],ad(o,4446,u)|0,n[Hn>>2]=18,n[Hn+4>>2]=0,n[u>>2]=n[Hn>>2],n[u+4>>2]=n[Hn+4>>2],ad(o,4458,u)|0,n[cr>>2]=3,n[cr+4>>2]=0,n[u>>2]=n[cr>>2],n[u+4>>2]=n[cr+4>>2],rp(o,4471,u)|0,n[Hr>>2]=1,n[Hr+4>>2]=0,n[u>>2]=n[Hr>>2],n[u+4>>2]=n[Hr+4>>2],WP(o,4486,u)|0,n[Tr>>2]=10,n[Tr+4>>2]=0,n[u>>2]=n[Tr>>2],n[u+4>>2]=n[Tr+4>>2],Pu(o,4496,u)|0,n[$t>>2]=11,n[$t+4>>2]=0,n[u>>2]=n[$t>>2],n[u+4>>2]=n[$t+4>>2],Pu(o,4508,u)|0,n[fr>>2]=3,n[fr+4>>2]=0,n[u>>2]=n[fr>>2],n[u+4>>2]=n[fr+4>>2],yM(o,4519,u)|0,n[qr>>2]=4,n[qr+4>>2]=0,n[u>>2]=n[qr>>2],n[u+4>>2]=n[qr+4>>2],rLe(o,4530,u)|0,n[Lt>>2]=19,n[Lt+4>>2]=0,n[u>>2]=n[Lt>>2],n[u+4>>2]=n[Lt+4>>2],nLe(o,4542,u)|0,n[Ge>>2]=12,n[Ge+4>>2]=0,n[u>>2]=n[Ge>>2],n[u+4>>2]=n[Ge+4>>2],iLe(o,4554,u)|0,n[He>>2]=13,n[He+4>>2]=0,n[u>>2]=n[He>>2],n[u+4>>2]=n[He+4>>2],sLe(o,4568,u)|0,n[ct>>2]=2,n[ct+4>>2]=0,n[u>>2]=n[ct>>2],n[u+4>>2]=n[ct+4>>2],oLe(o,4578,u)|0,n[Ze>>2]=20,n[Ze+4>>2]=0,n[u>>2]=n[Ze>>2],n[u+4>>2]=n[Ze+4>>2],aLe(o,4587,u)|0,n[tt>>2]=22,n[tt+4>>2]=0,n[u>>2]=n[tt>>2],n[u+4>>2]=n[tt+4>>2],kr(o,4602,u)|0,n[Qe>>2]=23,n[Qe+4>>2]=0,n[u>>2]=n[Qe>>2],n[u+4>>2]=n[Qe+4>>2],kr(o,4619,u)|0,n[Le>>2]=14,n[Le+4>>2]=0,n[u>>2]=n[Le>>2],n[u+4>>2]=n[Le+4>>2],lLe(o,4629,u)|0,n[We>>2]=1,n[We+4>>2]=0,n[u>>2]=n[We>>2],n[u+4>>2]=n[We+4>>2],cLe(o,4637,u)|0,n[ae>>2]=4,n[ae+4>>2]=0,n[u>>2]=n[ae>>2],n[u+4>>2]=n[ae+4>>2],rp(o,4653,u)|0,n[G>>2]=5,n[G+4>>2]=0,n[u>>2]=n[G>>2],n[u+4>>2]=n[G+4>>2],rp(o,4669,u)|0,n[M>>2]=6,n[M+4>>2]=0,n[u>>2]=n[M>>2],n[u+4>>2]=n[M+4>>2],rp(o,4686,u)|0,n[_>>2]=7,n[_+4>>2]=0,n[u>>2]=n[_>>2],n[u+4>>2]=n[_+4>>2],rp(o,4701,u)|0,n[T>>2]=8,n[T+4>>2]=0,n[u>>2]=n[T>>2],n[u+4>>2]=n[T+4>>2],rp(o,4719,u)|0,n[k>>2]=9,n[k+4>>2]=0,n[u>>2]=n[k>>2],n[u+4>>2]=n[k+4>>2],rp(o,4736,u)|0,n[B>>2]=21,n[B+4>>2]=0,n[u>>2]=n[B>>2],n[u+4>>2]=n[B+4>>2],uLe(o,4754,u)|0,n[m>>2]=2,n[m+4>>2]=0,n[u>>2]=n[m>>2],n[u+4>>2]=n[m+4>>2],WP(o,4772,u)|0,n[d>>2]=3,n[d+4>>2]=0,n[u>>2]=n[d>>2],n[u+4>>2]=n[d+4>>2],WP(o,4790,u)|0,n[A>>2]=4,n[A+4>>2]=0,n[u>>2]=n[A>>2],n[u+4>>2]=n[A+4>>2],WP(o,4808,u)|0,I=l}function pt(o,l){o=o|0,l=l|0;var u=0;u=dHe()|0,n[o>>2]=u,mHe(u,l),Wh(n[o>>2]|0)}function Zt(o,l,u){return o=o|0,l=l|0,u=u|0,rHe(o,Bn(l)|0,u,0),o|0}function Sr(o,l,u){return o=o|0,l=l|0,u=u|0,U8e(o,Bn(l)|0,u,0),o|0}function Xn(o,l,u){return o=o|0,l=l|0,u=u|0,D8e(o,Bn(l)|0,u,0),o|0}function kr(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],u8e(o,l,d),I=A,o|0}function Rn(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],W3e(o,l,d),I=A,o|0}function Un(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],x3e(o,l,d),I=A,o|0}function zr(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],p3e(o,l,d),I=A,o|0}function li(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],Z4e(o,l,d),I=A,o|0}function Pu(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],N4e(o,l,d),I=A,o|0}function yM(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],E4e(o,l,d),I=A,o|0}function ad(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],WUe(o,l,d),I=A,o|0}function rp(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],xUe(o,l,d),I=A,o|0}function WP(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],pUe(o,l,d),I=A,o|0}function rLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],Z_e(o,l,d),I=A,o|0}function nLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],N_e(o,l,d),I=A,o|0}function iLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],I_e(o,l,d),I=A,o|0}function sLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],s_e(o,l,d),I=A,o|0}function oLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],jMe(o,l,d),I=A,o|0}function aLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],SMe(o,l,d),I=A,o|0}function lLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],cMe(o,l,d),I=A,o|0}function cLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],WLe(o,l,d),I=A,o|0}function uLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],fLe(o,l,d),I=A,o|0}function fLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],ALe(o,u,d,1),I=A}function Bn(o){return o=o|0,o|0}function ALe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=EM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=pLe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,hLe(m,A)|0,A),I=d}function EM(){var o=0,l=0;if(s[7616]|0||(eZ(9136),gr(24,9136,U|0)|0,l=7616,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9136)|0)){o=9136,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));eZ(9136)}return 9136}function pLe(o){return o=o|0,0}function hLe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=EM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],$z(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(mLe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function vn(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0;var B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0;B=I,I=I+32|0,ae=B+24|0,G=B+20|0,T=B+16|0,M=B+12|0,_=B+8|0,k=B+4|0,We=B,n[G>>2]=l,n[T>>2]=u,n[M>>2]=A,n[_>>2]=d,n[k>>2]=m,m=o+28|0,n[We>>2]=n[m>>2],n[ae>>2]=n[We>>2],gLe(o+24|0,ae,G,M,_,T,k)|0,n[m>>2]=n[n[m>>2]>>2],I=B}function gLe(o,l,u,A,d,m,B){return o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,B=B|0,o=dLe(l)|0,l=Jt(24)|0,Xz(l+4|0,n[u>>2]|0,n[A>>2]|0,n[d>>2]|0,n[m>>2]|0,n[B>>2]|0),n[l>>2]=n[o>>2],n[o>>2]=l,l|0}function dLe(o){return o=o|0,n[o>>2]|0}function Xz(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,n[o>>2]=l,n[o+4>>2]=u,n[o+8>>2]=A,n[o+12>>2]=d,n[o+16>>2]=m}function yr(o,l){return o=o|0,l=l|0,l|o|0}function $z(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function mLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=yLe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,ELe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],$z(m,A,u),n[T>>2]=(n[T>>2]|0)+12,ILe(o,k),CLe(k),I=_;return}}function yLe(o){return o=o|0,357913941}function ELe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function ILe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function CLe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function eZ(o){o=o|0,vLe(o)}function wLe(o){o=o|0,BLe(o+24|0)}function Ur(o){return o=o|0,n[o>>2]|0}function BLe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function vLe(o){o=o|0;var l=0;l=en()|0,tn(o,2,3,l,SLe()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function en(){return 9228}function SLe(){return 1140}function DLe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;return u=I,I=I+16|0,A=u+8|0,d=u,m=bLe(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],l=PLe(l,A)|0,I=u,l|0}function tn(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,n[o>>2]=l,n[o+4>>2]=u,n[o+8>>2]=A,n[o+12>>2]=d,n[o+16>>2]=m}function bLe(o){return o=o|0,(n[(EM()|0)+24>>2]|0)+(o*12|0)|0}function PLe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;return d=I,I=I+48|0,A=d,u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),ap[u&31](A,o),A=xLe(A)|0,I=d,A|0}function xLe(o){o=o|0;var l=0,u=0,A=0,d=0;return d=I,I=I+32|0,l=d+12|0,u=d,A=IM(tZ()|0)|0,A?(CM(l,A),wM(u,l),kLe(o,u),o=BM(l)|0):o=QLe(o)|0,I=d,o|0}function tZ(){var o=0;return s[7632]|0||(HLe(9184),gr(25,9184,U|0)|0,o=7632,n[o>>2]=1,n[o+4>>2]=0),9184}function IM(o){return o=o|0,n[o+36>>2]|0}function CM(o,l){o=o|0,l=l|0,n[o>>2]=l,n[o+4>>2]=o,n[o+8>>2]=0}function wM(o,l){o=o|0,l=l|0,n[o>>2]=n[l>>2],n[o+4>>2]=n[l+4>>2],n[o+8>>2]=0}function kLe(o,l){o=o|0,l=l|0,NLe(l,o,o+8|0,o+16|0,o+24|0,o+32|0,o+40|0)|0}function BM(o){return o=o|0,n[(n[o+4>>2]|0)+8>>2]|0}function QLe(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0,T=0;T=I,I=I+16|0,u=T+4|0,A=T,d=Fl(8)|0,m=d,B=Jt(48)|0,k=B,l=k+48|0;do n[k>>2]=n[o>>2],k=k+4|0,o=o+4|0;while((k|0)<(l|0));return l=m+4|0,n[l>>2]=B,k=Jt(8)|0,B=n[l>>2]|0,n[A>>2]=0,n[u>>2]=n[A>>2],rZ(k,B,u),n[d>>2]=k,I=T,m|0}function rZ(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,u=Jt(16)|0,n[u+4>>2]=0,n[u+8>>2]=0,n[u>>2]=1092,n[u+12>>2]=l,n[o+4>>2]=u}function TLe(o){o=o|0,$y(o),yt(o)}function RLe(o){o=o|0,o=n[o+12>>2]|0,o|0&&yt(o)}function FLe(o){o=o|0,yt(o)}function NLe(o,l,u,A,d,m,B){return o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,B=B|0,m=OLe(n[o>>2]|0,l,u,A,d,m,B)|0,B=o+4|0,n[(n[B>>2]|0)+8>>2]=m,n[(n[B>>2]|0)+8>>2]|0}function OLe(o,l,u,A,d,m,B){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,B=B|0;var k=0,T=0;return k=I,I=I+16|0,T=k,Nl(T),o=Ls(o)|0,B=LLe(o,+E[l>>3],+E[u>>3],+E[A>>3],+E[d>>3],+E[m>>3],+E[B>>3])|0,Ol(T),I=k,B|0}function LLe(o,l,u,A,d,m,B){o=o|0,l=+l,u=+u,A=+A,d=+d,m=+m,B=+B;var k=0;return k=ma(MLe()|0)|0,l=+Ja(l),u=+Ja(u),A=+Ja(A),d=+Ja(d),m=+Ja(m),no(0,k|0,o|0,+l,+u,+A,+d,+m,+ +Ja(B))|0}function MLe(){var o=0;return s[7624]|0||(_Le(9172),o=7624,n[o>>2]=1,n[o+4>>2]=0),9172}function _Le(o){o=o|0,Ro(o,ULe()|0,6)}function ULe(){return 1112}function HLe(o){o=o|0,Uh(o)}function jLe(o){o=o|0,nZ(o+24|0),iZ(o+16|0)}function nZ(o){o=o|0,GLe(o)}function iZ(o){o=o|0,qLe(o)}function qLe(o){o=o|0;var l=0,u=0;if(l=n[o>>2]|0,l|0)do u=l,l=n[l>>2]|0,yt(u);while(l|0);n[o>>2]=0}function GLe(o){o=o|0;var l=0,u=0;if(l=n[o>>2]|0,l|0)do u=l,l=n[l>>2]|0,yt(u);while(l|0);n[o>>2]=0}function Uh(o){o=o|0;var l=0;n[o+16>>2]=0,n[o+20>>2]=0,l=o+24|0,n[l>>2]=0,n[o+28>>2]=l,n[o+36>>2]=0,s[o+40>>0]=0,s[o+41>>0]=0}function WLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],YLe(o,u,d,0),I=A}function YLe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=vM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=VLe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,KLe(m,A)|0,A),I=d}function vM(){var o=0,l=0;if(s[7640]|0||(oZ(9232),gr(26,9232,U|0)|0,l=7640,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9232)|0)){o=9232,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));oZ(9232)}return 9232}function VLe(o){return o=o|0,0}function KLe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=vM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],sZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(JLe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function sZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function JLe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=zLe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,ZLe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],sZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,XLe(o,k),$Le(k),I=_;return}}function zLe(o){return o=o|0,357913941}function ZLe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function XLe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function $Le(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function oZ(o){o=o|0,rMe(o)}function eMe(o){o=o|0,tMe(o+24|0)}function tMe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function rMe(o){o=o|0;var l=0;l=en()|0,tn(o,2,1,l,nMe()|0,3),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function nMe(){return 1144}function iMe(o,l,u,A,d){o=o|0,l=l|0,u=+u,A=+A,d=d|0;var m=0,B=0,k=0,T=0;m=I,I=I+16|0,B=m+8|0,k=m,T=sMe(o)|0,o=n[T+4>>2]|0,n[k>>2]=n[T>>2],n[k+4>>2]=o,n[B>>2]=n[k>>2],n[B+4>>2]=n[k+4>>2],oMe(l,B,u,A,d),I=m}function sMe(o){return o=o|0,(n[(vM()|0)+24>>2]|0)+(o*12|0)|0}function oMe(o,l,u,A,d){o=o|0,l=l|0,u=+u,A=+A,d=d|0;var m=0,B=0,k=0,T=0,_=0;_=I,I=I+16|0,B=_+2|0,k=_+1|0,T=_,m=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(m=n[(n[o>>2]|0)+m>>2]|0),Tf(B,u),u=+Rf(B,u),Tf(k,A),A=+Rf(k,A),np(T,d),T=ip(T,d)|0,C$[m&1](o,u,A,T),I=_}function Tf(o,l){o=o|0,l=+l}function Rf(o,l){return o=o|0,l=+l,+ +lMe(l)}function np(o,l){o=o|0,l=l|0}function ip(o,l){return o=o|0,l=l|0,aMe(l)|0}function aMe(o){return o=o|0,o|0}function lMe(o){return o=+o,+o}function cMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],uMe(o,u,d,1),I=A}function uMe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=SM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=fMe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,AMe(m,A)|0,A),I=d}function SM(){var o=0,l=0;if(s[7648]|0||(lZ(9268),gr(27,9268,U|0)|0,l=7648,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9268)|0)){o=9268,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));lZ(9268)}return 9268}function fMe(o){return o=o|0,0}function AMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=SM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],aZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(pMe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function aZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function pMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=hMe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,gMe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],aZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,dMe(o,k),mMe(k),I=_;return}}function hMe(o){return o=o|0,357913941}function gMe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function dMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function mMe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function lZ(o){o=o|0,IMe(o)}function yMe(o){o=o|0,EMe(o+24|0)}function EMe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function IMe(o){o=o|0;var l=0;l=en()|0,tn(o,2,4,l,CMe()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function CMe(){return 1160}function wMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;return u=I,I=I+16|0,A=u+8|0,d=u,m=BMe(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],l=vMe(l,A)|0,I=u,l|0}function BMe(o){return o=o|0,(n[(SM()|0)+24>>2]|0)+(o*12|0)|0}function vMe(o,l){o=o|0,l=l|0;var u=0;return u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),cZ(dd[u&31](o)|0)|0}function cZ(o){return o=o|0,o&1|0}function SMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],DMe(o,u,d,0),I=A}function DMe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=DM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=bMe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,PMe(m,A)|0,A),I=d}function DM(){var o=0,l=0;if(s[7656]|0||(fZ(9304),gr(28,9304,U|0)|0,l=7656,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9304)|0)){o=9304,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));fZ(9304)}return 9304}function bMe(o){return o=o|0,0}function PMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=DM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],uZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(xMe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function uZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function xMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=kMe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,QMe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],uZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,TMe(o,k),RMe(k),I=_;return}}function kMe(o){return o=o|0,357913941}function QMe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function TMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function RMe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function fZ(o){o=o|0,OMe(o)}function FMe(o){o=o|0,NMe(o+24|0)}function NMe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function OMe(o){o=o|0;var l=0;l=en()|0,tn(o,2,5,l,LMe()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function LMe(){return 1164}function MMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,d=A+8|0,m=A,B=_Me(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],UMe(l,d,u),I=A}function _Me(o){return o=o|0,(n[(DM()|0)+24>>2]|0)+(o*12|0)|0}function UMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),Hh(d,u),u=jh(d,u)|0,ap[A&31](o,u),qh(d),I=m}function Hh(o,l){o=o|0,l=l|0,HMe(o,l)}function jh(o,l){return o=o|0,l=l|0,o|0}function qh(o){o=o|0,Df(o)}function HMe(o,l){o=o|0,l=l|0,bM(o,l)}function bM(o,l){o=o|0,l=l|0,n[o>>2]=l}function jMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],qMe(o,u,d,0),I=A}function qMe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=PM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=GMe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,WMe(m,A)|0,A),I=d}function PM(){var o=0,l=0;if(s[7664]|0||(pZ(9340),gr(29,9340,U|0)|0,l=7664,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9340)|0)){o=9340,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));pZ(9340)}return 9340}function GMe(o){return o=o|0,0}function WMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=PM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],AZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(YMe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function AZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function YMe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=VMe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,KMe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],AZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,JMe(o,k),zMe(k),I=_;return}}function VMe(o){return o=o|0,357913941}function KMe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function JMe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function zMe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function pZ(o){o=o|0,$Me(o)}function ZMe(o){o=o|0,XMe(o+24|0)}function XMe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function $Me(o){o=o|0;var l=0;l=en()|0,tn(o,2,4,l,e_e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function e_e(){return 1180}function t_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=r_e(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],u=n_e(l,d,u)|0,I=A,u|0}function r_e(o){return o=o|0,(n[(PM()|0)+24>>2]|0)+(o*12|0)|0}function n_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;return m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),ld(d,u),d=cd(d,u)|0,d=YP(v_[A&15](o,d)|0)|0,I=m,d|0}function ld(o,l){o=o|0,l=l|0}function cd(o,l){return o=o|0,l=l|0,i_e(l)|0}function YP(o){return o=o|0,o|0}function i_e(o){return o=o|0,o|0}function s_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],o_e(o,u,d,0),I=A}function o_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=xM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=a_e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,l_e(m,A)|0,A),I=d}function xM(){var o=0,l=0;if(s[7672]|0||(gZ(9376),gr(30,9376,U|0)|0,l=7672,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9376)|0)){o=9376,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));gZ(9376)}return 9376}function a_e(o){return o=o|0,0}function l_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=xM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],hZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(c_e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function hZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function c_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=u_e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,f_e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],hZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,A_e(o,k),p_e(k),I=_;return}}function u_e(o){return o=o|0,357913941}function f_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function A_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function p_e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function gZ(o){o=o|0,d_e(o)}function h_e(o){o=o|0,g_e(o+24|0)}function g_e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function d_e(o){o=o|0;var l=0;l=en()|0,tn(o,2,5,l,dZ()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function dZ(){return 1196}function m_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;return u=I,I=I+16|0,A=u+8|0,d=u,m=y_e(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],l=E_e(l,A)|0,I=u,l|0}function y_e(o){return o=o|0,(n[(xM()|0)+24>>2]|0)+(o*12|0)|0}function E_e(o,l){o=o|0,l=l|0;var u=0;return u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),YP(dd[u&31](o)|0)|0}function I_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],C_e(o,u,d,1),I=A}function C_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=kM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=w_e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,B_e(m,A)|0,A),I=d}function kM(){var o=0,l=0;if(s[7680]|0||(yZ(9412),gr(31,9412,U|0)|0,l=7680,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9412)|0)){o=9412,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));yZ(9412)}return 9412}function w_e(o){return o=o|0,0}function B_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=kM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],mZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(v_e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function mZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function v_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=S_e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,D_e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],mZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,b_e(o,k),P_e(k),I=_;return}}function S_e(o){return o=o|0,357913941}function D_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function b_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function P_e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function yZ(o){o=o|0,Q_e(o)}function x_e(o){o=o|0,k_e(o+24|0)}function k_e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function Q_e(o){o=o|0;var l=0;l=en()|0,tn(o,2,6,l,EZ()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function EZ(){return 1200}function T_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;return u=I,I=I+16|0,A=u+8|0,d=u,m=R_e(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],l=F_e(l,A)|0,I=u,l|0}function R_e(o){return o=o|0,(n[(kM()|0)+24>>2]|0)+(o*12|0)|0}function F_e(o,l){o=o|0,l=l|0;var u=0;return u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),VP(dd[u&31](o)|0)|0}function VP(o){return o=o|0,o|0}function N_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],O_e(o,u,d,0),I=A}function O_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=QM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=L_e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,M_e(m,A)|0,A),I=d}function QM(){var o=0,l=0;if(s[7688]|0||(CZ(9448),gr(32,9448,U|0)|0,l=7688,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9448)|0)){o=9448,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));CZ(9448)}return 9448}function L_e(o){return o=o|0,0}function M_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=QM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],IZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(__e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function IZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function __e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=U_e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,H_e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],IZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,j_e(o,k),q_e(k),I=_;return}}function U_e(o){return o=o|0,357913941}function H_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function j_e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function q_e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function CZ(o){o=o|0,Y_e(o)}function G_e(o){o=o|0,W_e(o+24|0)}function W_e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function Y_e(o){o=o|0;var l=0;l=en()|0,tn(o,2,6,l,wZ()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function wZ(){return 1204}function V_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,d=A+8|0,m=A,B=K_e(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],J_e(l,d,u),I=A}function K_e(o){return o=o|0,(n[(QM()|0)+24>>2]|0)+(o*12|0)|0}function J_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),TM(d,u),d=RM(d,u)|0,ap[A&31](o,d),I=m}function TM(o,l){o=o|0,l=l|0}function RM(o,l){return o=o|0,l=l|0,z_e(l)|0}function z_e(o){return o=o|0,o|0}function Z_e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],X_e(o,u,d,0),I=A}function X_e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=FM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=$_e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,eUe(m,A)|0,A),I=d}function FM(){var o=0,l=0;if(s[7696]|0||(vZ(9484),gr(33,9484,U|0)|0,l=7696,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9484)|0)){o=9484,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));vZ(9484)}return 9484}function $_e(o){return o=o|0,0}function eUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=FM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],BZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(tUe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function BZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function tUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=rUe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,nUe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],BZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,iUe(o,k),sUe(k),I=_;return}}function rUe(o){return o=o|0,357913941}function nUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function iUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function sUe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function vZ(o){o=o|0,lUe(o)}function oUe(o){o=o|0,aUe(o+24|0)}function aUe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function lUe(o){o=o|0;var l=0;l=en()|0,tn(o,2,1,l,cUe()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function cUe(){return 1212}function uUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;d=I,I=I+16|0,m=d+8|0,B=d,k=fUe(o)|0,o=n[k+4>>2]|0,n[B>>2]=n[k>>2],n[B+4>>2]=o,n[m>>2]=n[B>>2],n[m+4>>2]=n[B+4>>2],AUe(l,m,u,A),I=d}function fUe(o){return o=o|0,(n[(FM()|0)+24>>2]|0)+(o*12|0)|0}function AUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;k=I,I=I+16|0,m=k+1|0,B=k,d=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(d=n[(n[o>>2]|0)+d>>2]|0),TM(m,u),m=RM(m,u)|0,ld(B,A),B=cd(B,A)|0,L2[d&15](o,m,B),I=k}function pUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],hUe(o,u,d,1),I=A}function hUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=NM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=gUe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,dUe(m,A)|0,A),I=d}function NM(){var o=0,l=0;if(s[7704]|0||(DZ(9520),gr(34,9520,U|0)|0,l=7704,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9520)|0)){o=9520,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));DZ(9520)}return 9520}function gUe(o){return o=o|0,0}function dUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=NM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],SZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(mUe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function SZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function mUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=yUe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,EUe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],SZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,IUe(o,k),CUe(k),I=_;return}}function yUe(o){return o=o|0,357913941}function EUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function IUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function CUe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function DZ(o){o=o|0,vUe(o)}function wUe(o){o=o|0,BUe(o+24|0)}function BUe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function vUe(o){o=o|0;var l=0;l=en()|0,tn(o,2,1,l,SUe()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function SUe(){return 1224}function DUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;return d=I,I=I+16|0,m=d+8|0,B=d,k=bUe(o)|0,o=n[k+4>>2]|0,n[B>>2]=n[k>>2],n[B+4>>2]=o,n[m>>2]=n[B>>2],n[m+4>>2]=n[B+4>>2],A=+PUe(l,m,u),I=d,+A}function bUe(o){return o=o|0,(n[(NM()|0)+24>>2]|0)+(o*12|0)|0}function PUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),np(d,u),d=ip(d,u)|0,B=+Qf(+B$[A&7](o,d)),I=m,+B}function xUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],kUe(o,u,d,1),I=A}function kUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=OM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=QUe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,TUe(m,A)|0,A),I=d}function OM(){var o=0,l=0;if(s[7712]|0||(PZ(9556),gr(35,9556,U|0)|0,l=7712,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9556)|0)){o=9556,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));PZ(9556)}return 9556}function QUe(o){return o=o|0,0}function TUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=OM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],bZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(RUe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function bZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function RUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=FUe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,NUe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],bZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,OUe(o,k),LUe(k),I=_;return}}function FUe(o){return o=o|0,357913941}function NUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function OUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function LUe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function PZ(o){o=o|0,UUe(o)}function MUe(o){o=o|0,_Ue(o+24|0)}function _Ue(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function UUe(o){o=o|0;var l=0;l=en()|0,tn(o,2,5,l,HUe()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function HUe(){return 1232}function jUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=qUe(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],u=+GUe(l,d),I=A,+u}function qUe(o){return o=o|0,(n[(OM()|0)+24>>2]|0)+(o*12|0)|0}function GUe(o,l){o=o|0,l=l|0;var u=0;return u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),+ +Qf(+w$[u&15](o))}function WUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],YUe(o,u,d,1),I=A}function YUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=LM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=VUe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,KUe(m,A)|0,A),I=d}function LM(){var o=0,l=0;if(s[7720]|0||(kZ(9592),gr(36,9592,U|0)|0,l=7720,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9592)|0)){o=9592,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));kZ(9592)}return 9592}function VUe(o){return o=o|0,0}function KUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=LM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],xZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(JUe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function xZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function JUe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=zUe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,ZUe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],xZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,XUe(o,k),$Ue(k),I=_;return}}function zUe(o){return o=o|0,357913941}function ZUe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function XUe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function $Ue(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function kZ(o){o=o|0,r4e(o)}function e4e(o){o=o|0,t4e(o+24|0)}function t4e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function r4e(o){o=o|0;var l=0;l=en()|0,tn(o,2,7,l,n4e()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function n4e(){return 1276}function i4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;return u=I,I=I+16|0,A=u+8|0,d=u,m=s4e(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],l=o4e(l,A)|0,I=u,l|0}function s4e(o){return o=o|0,(n[(LM()|0)+24>>2]|0)+(o*12|0)|0}function o4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;return d=I,I=I+16|0,A=d,u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),ap[u&31](A,o),A=QZ(A)|0,I=d,A|0}function QZ(o){o=o|0;var l=0,u=0,A=0,d=0;return d=I,I=I+32|0,l=d+12|0,u=d,A=IM(TZ()|0)|0,A?(CM(l,A),wM(u,l),a4e(o,u),o=BM(l)|0):o=l4e(o)|0,I=d,o|0}function TZ(){var o=0;return s[7736]|0||(y4e(9640),gr(25,9640,U|0)|0,o=7736,n[o>>2]=1,n[o+4>>2]=0),9640}function a4e(o,l){o=o|0,l=l|0,A4e(l,o,o+8|0)|0}function l4e(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0;return u=I,I=I+16|0,d=u+4|0,B=u,A=Fl(8)|0,l=A,k=Jt(16)|0,n[k>>2]=n[o>>2],n[k+4>>2]=n[o+4>>2],n[k+8>>2]=n[o+8>>2],n[k+12>>2]=n[o+12>>2],m=l+4|0,n[m>>2]=k,o=Jt(8)|0,m=n[m>>2]|0,n[B>>2]=0,n[d>>2]=n[B>>2],MM(o,m,d),n[A>>2]=o,I=u,l|0}function MM(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,u=Jt(16)|0,n[u+4>>2]=0,n[u+8>>2]=0,n[u>>2]=1244,n[u+12>>2]=l,n[o+4>>2]=u}function c4e(o){o=o|0,$y(o),yt(o)}function u4e(o){o=o|0,o=n[o+12>>2]|0,o|0&&yt(o)}function f4e(o){o=o|0,yt(o)}function A4e(o,l,u){return o=o|0,l=l|0,u=u|0,l=p4e(n[o>>2]|0,l,u)|0,u=o+4|0,n[(n[u>>2]|0)+8>>2]=l,n[(n[u>>2]|0)+8>>2]|0}function p4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;return A=I,I=I+16|0,d=A,Nl(d),o=Ls(o)|0,u=h4e(o,n[l>>2]|0,+E[u>>3])|0,Ol(d),I=A,u|0}function h4e(o,l,u){o=o|0,l=l|0,u=+u;var A=0;return A=ma(g4e()|0)|0,l=Yy(l)|0,lu(0,A|0,o|0,l|0,+ +Ja(u))|0}function g4e(){var o=0;return s[7728]|0||(d4e(9628),o=7728,n[o>>2]=1,n[o+4>>2]=0),9628}function d4e(o){o=o|0,Ro(o,m4e()|0,2)}function m4e(){return 1264}function y4e(o){o=o|0,Uh(o)}function E4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],I4e(o,u,d,1),I=A}function I4e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=_M()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=C4e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,w4e(m,A)|0,A),I=d}function _M(){var o=0,l=0;if(s[7744]|0||(FZ(9684),gr(37,9684,U|0)|0,l=7744,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9684)|0)){o=9684,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));FZ(9684)}return 9684}function C4e(o){return o=o|0,0}function w4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=_M()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],RZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(B4e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function RZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function B4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=v4e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,S4e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],RZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,D4e(o,k),b4e(k),I=_;return}}function v4e(o){return o=o|0,357913941}function S4e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function D4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function b4e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function FZ(o){o=o|0,k4e(o)}function P4e(o){o=o|0,x4e(o+24|0)}function x4e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function k4e(o){o=o|0;var l=0;l=en()|0,tn(o,2,5,l,Q4e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function Q4e(){return 1280}function T4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=R4e(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],u=F4e(l,d,u)|0,I=A,u|0}function R4e(o){return o=o|0,(n[(_M()|0)+24>>2]|0)+(o*12|0)|0}function F4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return B=I,I=I+32|0,d=B,m=B+16|0,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),np(m,u),m=ip(m,u)|0,L2[A&15](d,o,m),m=QZ(d)|0,I=B,m|0}function N4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],O4e(o,u,d,1),I=A}function O4e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=UM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=L4e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,M4e(m,A)|0,A),I=d}function UM(){var o=0,l=0;if(s[7752]|0||(OZ(9720),gr(38,9720,U|0)|0,l=7752,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9720)|0)){o=9720,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));OZ(9720)}return 9720}function L4e(o){return o=o|0,0}function M4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=UM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],NZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(_4e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function NZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function _4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=U4e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,H4e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],NZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,j4e(o,k),q4e(k),I=_;return}}function U4e(o){return o=o|0,357913941}function H4e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function j4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function q4e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function OZ(o){o=o|0,Y4e(o)}function G4e(o){o=o|0,W4e(o+24|0)}function W4e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function Y4e(o){o=o|0;var l=0;l=en()|0,tn(o,2,8,l,V4e()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function V4e(){return 1288}function K4e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;return u=I,I=I+16|0,A=u+8|0,d=u,m=J4e(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],l=z4e(l,A)|0,I=u,l|0}function J4e(o){return o=o|0,(n[(UM()|0)+24>>2]|0)+(o*12|0)|0}function z4e(o,l){o=o|0,l=l|0;var u=0;return u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),od(dd[u&31](o)|0)|0}function Z4e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],X4e(o,u,d,0),I=A}function X4e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=HM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=$4e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,e3e(m,A)|0,A),I=d}function HM(){var o=0,l=0;if(s[7760]|0||(MZ(9756),gr(39,9756,U|0)|0,l=7760,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9756)|0)){o=9756,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));MZ(9756)}return 9756}function $4e(o){return o=o|0,0}function e3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=HM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],LZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(t3e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function LZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function t3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=r3e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,n3e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],LZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,i3e(o,k),s3e(k),I=_;return}}function r3e(o){return o=o|0,357913941}function n3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function i3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function s3e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function MZ(o){o=o|0,l3e(o)}function o3e(o){o=o|0,a3e(o+24|0)}function a3e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function l3e(o){o=o|0;var l=0;l=en()|0,tn(o,2,8,l,c3e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function c3e(){return 1292}function u3e(o,l,u){o=o|0,l=l|0,u=+u;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,d=A+8|0,m=A,B=f3e(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],A3e(l,d,u),I=A}function f3e(o){return o=o|0,(n[(HM()|0)+24>>2]|0)+(o*12|0)|0}function A3e(o,l,u){o=o|0,l=l|0,u=+u;var A=0,d=0,m=0;m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),Tf(d,u),u=+Rf(d,u),E$[A&31](o,u),I=m}function p3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],h3e(o,u,d,0),I=A}function h3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=jM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=g3e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,d3e(m,A)|0,A),I=d}function jM(){var o=0,l=0;if(s[7768]|0||(UZ(9792),gr(40,9792,U|0)|0,l=7768,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9792)|0)){o=9792,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));UZ(9792)}return 9792}function g3e(o){return o=o|0,0}function d3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=jM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],_Z(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(m3e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function _Z(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function m3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=y3e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,E3e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],_Z(m,A,u),n[T>>2]=(n[T>>2]|0)+12,I3e(o,k),C3e(k),I=_;return}}function y3e(o){return o=o|0,357913941}function E3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function I3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function C3e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function UZ(o){o=o|0,v3e(o)}function w3e(o){o=o|0,B3e(o+24|0)}function B3e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function v3e(o){o=o|0;var l=0;l=en()|0,tn(o,2,1,l,S3e()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function S3e(){return 1300}function D3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=+A;var d=0,m=0,B=0,k=0;d=I,I=I+16|0,m=d+8|0,B=d,k=b3e(o)|0,o=n[k+4>>2]|0,n[B>>2]=n[k>>2],n[B+4>>2]=o,n[m>>2]=n[B>>2],n[m+4>>2]=n[B+4>>2],P3e(l,m,u,A),I=d}function b3e(o){return o=o|0,(n[(jM()|0)+24>>2]|0)+(o*12|0)|0}function P3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=+A;var d=0,m=0,B=0,k=0;k=I,I=I+16|0,m=k+1|0,B=k,d=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(d=n[(n[o>>2]|0)+d>>2]|0),np(m,u),m=ip(m,u)|0,Tf(B,A),A=+Rf(B,A),b$[d&15](o,m,A),I=k}function x3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],k3e(o,u,d,0),I=A}function k3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=qM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=Q3e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,T3e(m,A)|0,A),I=d}function qM(){var o=0,l=0;if(s[7776]|0||(jZ(9828),gr(41,9828,U|0)|0,l=7776,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9828)|0)){o=9828,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));jZ(9828)}return 9828}function Q3e(o){return o=o|0,0}function T3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=qM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],HZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(R3e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function HZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function R3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=F3e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,N3e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],HZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,O3e(o,k),L3e(k),I=_;return}}function F3e(o){return o=o|0,357913941}function N3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function O3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function L3e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function jZ(o){o=o|0,U3e(o)}function M3e(o){o=o|0,_3e(o+24|0)}function _3e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function U3e(o){o=o|0;var l=0;l=en()|0,tn(o,2,7,l,H3e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function H3e(){return 1312}function j3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,d=A+8|0,m=A,B=q3e(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],G3e(l,d,u),I=A}function q3e(o){return o=o|0,(n[(qM()|0)+24>>2]|0)+(o*12|0)|0}function G3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),np(d,u),d=ip(d,u)|0,ap[A&31](o,d),I=m}function W3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],Y3e(o,u,d,0),I=A}function Y3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=GM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=V3e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,K3e(m,A)|0,A),I=d}function GM(){var o=0,l=0;if(s[7784]|0||(GZ(9864),gr(42,9864,U|0)|0,l=7784,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9864)|0)){o=9864,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));GZ(9864)}return 9864}function V3e(o){return o=o|0,0}function K3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=GM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],qZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(J3e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function qZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function J3e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=z3e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,Z3e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],qZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,X3e(o,k),$3e(k),I=_;return}}function z3e(o){return o=o|0,357913941}function Z3e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function X3e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function $3e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function GZ(o){o=o|0,r8e(o)}function e8e(o){o=o|0,t8e(o+24|0)}function t8e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function r8e(o){o=o|0;var l=0;l=en()|0,tn(o,2,8,l,n8e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function n8e(){return 1320}function i8e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,d=A+8|0,m=A,B=s8e(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],o8e(l,d,u),I=A}function s8e(o){return o=o|0,(n[(GM()|0)+24>>2]|0)+(o*12|0)|0}function o8e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),a8e(d,u),d=l8e(d,u)|0,ap[A&31](o,d),I=m}function a8e(o,l){o=o|0,l=l|0}function l8e(o,l){return o=o|0,l=l|0,c8e(l)|0}function c8e(o){return o=o|0,o|0}function u8e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],f8e(o,u,d,0),I=A}function f8e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=WM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=A8e(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,p8e(m,A)|0,A),I=d}function WM(){var o=0,l=0;if(s[7792]|0||(YZ(9900),gr(43,9900,U|0)|0,l=7792,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9900)|0)){o=9900,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));YZ(9900)}return 9900}function A8e(o){return o=o|0,0}function p8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=WM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],WZ(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(h8e(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function WZ(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function h8e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=g8e(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,d8e(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],WZ(m,A,u),n[T>>2]=(n[T>>2]|0)+12,m8e(o,k),y8e(k),I=_;return}}function g8e(o){return o=o|0,357913941}function d8e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function m8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function y8e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function YZ(o){o=o|0,C8e(o)}function E8e(o){o=o|0,I8e(o+24|0)}function I8e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function C8e(o){o=o|0;var l=0;l=en()|0,tn(o,2,22,l,w8e()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function w8e(){return 1344}function B8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0;u=I,I=I+16|0,A=u+8|0,d=u,m=v8e(o)|0,o=n[m+4>>2]|0,n[d>>2]=n[m>>2],n[d+4>>2]=o,n[A>>2]=n[d>>2],n[A+4>>2]=n[d+4>>2],S8e(l,A),I=u}function v8e(o){return o=o|0,(n[(WM()|0)+24>>2]|0)+(o*12|0)|0}function S8e(o,l){o=o|0,l=l|0;var u=0;u=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(u=n[(n[o>>2]|0)+u>>2]|0),op[u&127](o)}function D8e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=YM()|0,o=b8e(u)|0,vn(m,l,d,o,P8e(u,A)|0,A)}function YM(){var o=0,l=0;if(s[7800]|0||(KZ(9936),gr(44,9936,U|0)|0,l=7800,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9936)|0)){o=9936,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));KZ(9936)}return 9936}function b8e(o){return o=o|0,o|0}function P8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=YM()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(VZ(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(x8e(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function VZ(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function x8e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=k8e(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,Q8e(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,VZ(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,T8e(o,d),R8e(d),I=k;return}}function k8e(o){return o=o|0,536870911}function Q8e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function T8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function R8e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function KZ(o){o=o|0,O8e(o)}function F8e(o){o=o|0,N8e(o+24|0)}function N8e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function O8e(o){o=o|0;var l=0;l=en()|0,tn(o,1,23,l,wZ()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function L8e(o,l){o=o|0,l=l|0,_8e(n[(M8e(o)|0)>>2]|0,l)}function M8e(o){return o=o|0,(n[(YM()|0)+24>>2]|0)+(o<<3)|0}function _8e(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,TM(A,l),l=RM(A,l)|0,op[o&127](l),I=u}function U8e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=VM()|0,o=H8e(u)|0,vn(m,l,d,o,j8e(u,A)|0,A)}function VM(){var o=0,l=0;if(s[7808]|0||(zZ(9972),gr(45,9972,U|0)|0,l=7808,n[l>>2]=1,n[l+4>>2]=0),!(Ur(9972)|0)){o=9972,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));zZ(9972)}return 9972}function H8e(o){return o=o|0,o|0}function j8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=VM()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(JZ(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(q8e(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function JZ(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function q8e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=G8e(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,W8e(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,JZ(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,Y8e(o,d),V8e(d),I=k;return}}function G8e(o){return o=o|0,536870911}function W8e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function Y8e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function V8e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function zZ(o){o=o|0,z8e(o)}function K8e(o){o=o|0,J8e(o+24|0)}function J8e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function z8e(o){o=o|0;var l=0;l=en()|0,tn(o,1,9,l,Z8e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function Z8e(){return 1348}function X8e(o,l){return o=o|0,l=l|0,eHe(n[($8e(o)|0)>>2]|0,l)|0}function $8e(o){return o=o|0,(n[(VM()|0)+24>>2]|0)+(o<<3)|0}function eHe(o,l){o=o|0,l=l|0;var u=0,A=0;return u=I,I=I+16|0,A=u,ZZ(A,l),l=XZ(A,l)|0,l=YP(dd[o&31](l)|0)|0,I=u,l|0}function ZZ(o,l){o=o|0,l=l|0}function XZ(o,l){return o=o|0,l=l|0,tHe(l)|0}function tHe(o){return o=o|0,o|0}function rHe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=KM()|0,o=nHe(u)|0,vn(m,l,d,o,iHe(u,A)|0,A)}function KM(){var o=0,l=0;if(s[7816]|0||(eX(10008),gr(46,10008,U|0)|0,l=7816,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10008)|0)){o=10008,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));eX(10008)}return 10008}function nHe(o){return o=o|0,o|0}function iHe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=KM()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?($Z(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(sHe(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function $Z(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function sHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=oHe(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,aHe(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,$Z(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,lHe(o,d),cHe(d),I=k;return}}function oHe(o){return o=o|0,536870911}function aHe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function lHe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function cHe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function eX(o){o=o|0,AHe(o)}function uHe(o){o=o|0,fHe(o+24|0)}function fHe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function AHe(o){o=o|0;var l=0;l=en()|0,tn(o,1,15,l,dZ()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function pHe(o){return o=o|0,gHe(n[(hHe(o)|0)>>2]|0)|0}function hHe(o){return o=o|0,(n[(KM()|0)+24>>2]|0)+(o<<3)|0}function gHe(o){return o=o|0,YP(ax[o&7]()|0)|0}function dHe(){var o=0;return s[7832]|0||(vHe(10052),gr(25,10052,U|0)|0,o=7832,n[o>>2]=1,n[o+4>>2]=0),10052}function mHe(o,l){o=o|0,l=l|0,n[o>>2]=yHe()|0,n[o+4>>2]=EHe()|0,n[o+12>>2]=l,n[o+8>>2]=IHe()|0,n[o+32>>2]=2}function yHe(){return 11709}function EHe(){return 1188}function IHe(){return KP()|0}function CHe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(wHe(u),yt(u)):l|0&&(Oy(l),yt(l))}function Gh(o,l){return o=o|0,l=l|0,l&o|0}function wHe(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function KP(){var o=0;return s[7824]|0||(n[2511]=BHe()|0,n[2512]=0,o=7824,n[o>>2]=1,n[o+4>>2]=0),10044}function BHe(){return 0}function vHe(o){o=o|0,Uh(o)}function SHe(o){o=o|0;var l=0,u=0,A=0,d=0,m=0;l=I,I=I+32|0,u=l+24|0,m=l+16|0,d=l+8|0,A=l,DHe(o,4827),bHe(o,4834,3)|0,PHe(o,3682,47)|0,n[m>>2]=9,n[m+4>>2]=0,n[u>>2]=n[m>>2],n[u+4>>2]=n[m+4>>2],xHe(o,4841,u)|0,n[d>>2]=1,n[d+4>>2]=0,n[u>>2]=n[d>>2],n[u+4>>2]=n[d+4>>2],kHe(o,4871,u)|0,n[A>>2]=10,n[A+4>>2]=0,n[u>>2]=n[A>>2],n[u+4>>2]=n[A+4>>2],QHe(o,4891,u)|0,I=l}function DHe(o,l){o=o|0,l=l|0;var u=0;u=c6e()|0,n[o>>2]=u,u6e(u,l),Wh(n[o>>2]|0)}function bHe(o,l,u){return o=o|0,l=l|0,u=u|0,Vje(o,Bn(l)|0,u,0),o|0}function PHe(o,l,u){return o=o|0,l=l|0,u=u|0,Tje(o,Bn(l)|0,u,0),o|0}function xHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],pje(o,l,d),I=A,o|0}function kHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],KHe(o,l,d),I=A,o|0}function QHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=n[u+4>>2]|0,n[m>>2]=n[u>>2],n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],THe(o,l,d),I=A,o|0}function THe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],RHe(o,u,d,1),I=A}function RHe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=JM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=FHe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,NHe(m,A)|0,A),I=d}function JM(){var o=0,l=0;if(s[7840]|0||(rX(10100),gr(48,10100,U|0)|0,l=7840,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10100)|0)){o=10100,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));rX(10100)}return 10100}function FHe(o){return o=o|0,0}function NHe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=JM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],tX(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(OHe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function tX(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function OHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=LHe(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,MHe(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],tX(m,A,u),n[T>>2]=(n[T>>2]|0)+12,_He(o,k),UHe(k),I=_;return}}function LHe(o){return o=o|0,357913941}function MHe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function _He(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function UHe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function rX(o){o=o|0,qHe(o)}function HHe(o){o=o|0,jHe(o+24|0)}function jHe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function qHe(o){o=o|0;var l=0;l=en()|0,tn(o,2,6,l,GHe()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function GHe(){return 1364}function WHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;return A=I,I=I+16|0,d=A+8|0,m=A,B=YHe(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],u=VHe(l,d,u)|0,I=A,u|0}function YHe(o){return o=o|0,(n[(JM()|0)+24>>2]|0)+(o*12|0)|0}function VHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;return m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),np(d,u),d=ip(d,u)|0,d=cZ(v_[A&15](o,d)|0)|0,I=m,d|0}function KHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],JHe(o,u,d,0),I=A}function JHe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=zM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=zHe(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,ZHe(m,A)|0,A),I=d}function zM(){var o=0,l=0;if(s[7848]|0||(iX(10136),gr(49,10136,U|0)|0,l=7848,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10136)|0)){o=10136,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));iX(10136)}return 10136}function zHe(o){return o=o|0,0}function ZHe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=zM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],nX(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(XHe(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function nX(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function XHe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=$He(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,eje(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],nX(m,A,u),n[T>>2]=(n[T>>2]|0)+12,tje(o,k),rje(k),I=_;return}}function $He(o){return o=o|0,357913941}function eje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function tje(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function rje(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function iX(o){o=o|0,sje(o)}function nje(o){o=o|0,ije(o+24|0)}function ije(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function sje(o){o=o|0;var l=0;l=en()|0,tn(o,2,9,l,oje()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function oje(){return 1372}function aje(o,l,u){o=o|0,l=l|0,u=+u;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,d=A+8|0,m=A,B=lje(o)|0,o=n[B+4>>2]|0,n[m>>2]=n[B>>2],n[m+4>>2]=o,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],cje(l,d,u),I=A}function lje(o){return o=o|0,(n[(zM()|0)+24>>2]|0)+(o*12|0)|0}function cje(o,l,u){o=o|0,l=l|0,u=+u;var A=0,d=0,m=0,B=Xe;m=I,I=I+16|0,d=m,A=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(A=n[(n[o>>2]|0)+A>>2]|0),uje(d,u),B=y(fje(d,u)),y$[A&1](o,B),I=m}function uje(o,l){o=o|0,l=+l}function fje(o,l){return o=o|0,l=+l,y(Aje(l))}function Aje(o){return o=+o,y(o)}function pje(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,d=A+8|0,m=A,k=n[u>>2]|0,B=n[u+4>>2]|0,u=Bn(l)|0,n[m>>2]=k,n[m+4>>2]=B,n[d>>2]=n[m>>2],n[d+4>>2]=n[m+4>>2],hje(o,u,d,0),I=A}function hje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0,T=0,_=0,M=0;d=I,I=I+32|0,m=d+16|0,M=d+8|0,k=d,_=n[u>>2]|0,T=n[u+4>>2]|0,B=n[o>>2]|0,o=ZM()|0,n[M>>2]=_,n[M+4>>2]=T,n[m>>2]=n[M>>2],n[m+4>>2]=n[M+4>>2],u=gje(m)|0,n[k>>2]=_,n[k+4>>2]=T,n[m>>2]=n[k>>2],n[m+4>>2]=n[k+4>>2],vn(B,l,o,u,dje(m,A)|0,A),I=d}function ZM(){var o=0,l=0;if(s[7856]|0||(oX(10172),gr(50,10172,U|0)|0,l=7856,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10172)|0)){o=10172,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));oX(10172)}return 10172}function gje(o){return o=o|0,0}function dje(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0;return M=I,I=I+32|0,d=M+24|0,B=M+16|0,k=M,T=M+8|0,m=n[o>>2]|0,A=n[o+4>>2]|0,n[k>>2]=m,n[k+4>>2]=A,G=ZM()|0,_=G+24|0,o=yr(l,4)|0,n[T>>2]=o,l=G+28|0,u=n[l>>2]|0,u>>>0<(n[G+32>>2]|0)>>>0?(n[B>>2]=m,n[B+4>>2]=A,n[d>>2]=n[B>>2],n[d+4>>2]=n[B+4>>2],sX(u,d,o),o=(n[l>>2]|0)+12|0,n[l>>2]=o):(mje(_,k,T),o=n[l>>2]|0),I=M,((o-(n[_>>2]|0)|0)/12|0)+-1|0}function sX(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=n[l+4>>2]|0,n[o>>2]=n[l>>2],n[o+4>>2]=A,n[o+8>>2]=u}function mje(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;if(_=I,I=I+48|0,A=_+32|0,B=_+24|0,k=_,T=o+4|0,d=(((n[T>>2]|0)-(n[o>>2]|0)|0)/12|0)+1|0,m=yje(o)|0,m>>>0>>0)sn(o);else{M=n[o>>2]|0,ae=((n[o+8>>2]|0)-M|0)/12|0,G=ae<<1,Eje(k,ae>>>0>>1>>>0?G>>>0>>0?d:G:m,((n[T>>2]|0)-M|0)/12|0,o+8|0),T=k+8|0,m=n[T>>2]|0,d=n[l+4>>2]|0,u=n[u>>2]|0,n[B>>2]=n[l>>2],n[B+4>>2]=d,n[A>>2]=n[B>>2],n[A+4>>2]=n[B+4>>2],sX(m,A,u),n[T>>2]=(n[T>>2]|0)+12,Ije(o,k),Cje(k),I=_;return}}function yje(o){return o=o|0,357913941}function Eje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>357913941)Nt();else{d=Jt(l*12|0)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u*12|0)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l*12|0)}function Ije(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(((d|0)/-12|0)*12|0)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function Cje(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~(((A+-12-l|0)>>>0)/12|0)*12|0)),o=n[o>>2]|0,o|0&&yt(o)}function oX(o){o=o|0,vje(o)}function wje(o){o=o|0,Bje(o+24|0)}function Bje(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~(((l+-12-A|0)>>>0)/12|0)*12|0)),yt(u))}function vje(o){o=o|0;var l=0;l=en()|0,tn(o,2,3,l,Sje()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function Sje(){return 1380}function Dje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;d=I,I=I+16|0,m=d+8|0,B=d,k=bje(o)|0,o=n[k+4>>2]|0,n[B>>2]=n[k>>2],n[B+4>>2]=o,n[m>>2]=n[B>>2],n[m+4>>2]=n[B+4>>2],Pje(l,m,u,A),I=d}function bje(o){return o=o|0,(n[(ZM()|0)+24>>2]|0)+(o*12|0)|0}function Pje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;k=I,I=I+16|0,m=k+1|0,B=k,d=n[l>>2]|0,l=n[l+4>>2]|0,o=o+(l>>1)|0,l&1&&(d=n[(n[o>>2]|0)+d>>2]|0),np(m,u),m=ip(m,u)|0,xje(B,A),B=kje(B,A)|0,L2[d&15](o,m,B),I=k}function xje(o,l){o=o|0,l=l|0}function kje(o,l){return o=o|0,l=l|0,Qje(l)|0}function Qje(o){return o=o|0,(o|0)!=0|0}function Tje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=XM()|0,o=Rje(u)|0,vn(m,l,d,o,Fje(u,A)|0,A)}function XM(){var o=0,l=0;if(s[7864]|0||(lX(10208),gr(51,10208,U|0)|0,l=7864,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10208)|0)){o=10208,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));lX(10208)}return 10208}function Rje(o){return o=o|0,o|0}function Fje(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=XM()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(aX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(Nje(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function aX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function Nje(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=Oje(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,Lje(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,aX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,Mje(o,d),_je(d),I=k;return}}function Oje(o){return o=o|0,536870911}function Lje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function Mje(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function _je(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function lX(o){o=o|0,jje(o)}function Uje(o){o=o|0,Hje(o+24|0)}function Hje(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function jje(o){o=o|0;var l=0;l=en()|0,tn(o,1,24,l,qje()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function qje(){return 1392}function Gje(o,l){o=o|0,l=l|0,Yje(n[(Wje(o)|0)>>2]|0,l)}function Wje(o){return o=o|0,(n[(XM()|0)+24>>2]|0)+(o<<3)|0}function Yje(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,ZZ(A,l),l=XZ(A,l)|0,op[o&127](l),I=u}function Vje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=$M()|0,o=Kje(u)|0,vn(m,l,d,o,Jje(u,A)|0,A)}function $M(){var o=0,l=0;if(s[7872]|0||(uX(10244),gr(52,10244,U|0)|0,l=7872,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10244)|0)){o=10244,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));uX(10244)}return 10244}function Kje(o){return o=o|0,o|0}function Jje(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=$M()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(cX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(zje(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function cX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function zje(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=Zje(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,Xje(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,cX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,$je(o,d),e6e(d),I=k;return}}function Zje(o){return o=o|0,536870911}function Xje(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function $je(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function e6e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function uX(o){o=o|0,n6e(o)}function t6e(o){o=o|0,r6e(o+24|0)}function r6e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function n6e(o){o=o|0;var l=0;l=en()|0,tn(o,1,16,l,i6e()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function i6e(){return 1400}function s6e(o){return o=o|0,a6e(n[(o6e(o)|0)>>2]|0)|0}function o6e(o){return o=o|0,(n[($M()|0)+24>>2]|0)+(o<<3)|0}function a6e(o){return o=o|0,l6e(ax[o&7]()|0)|0}function l6e(o){return o=o|0,o|0}function c6e(){var o=0;return s[7880]|0||(d6e(10280),gr(25,10280,U|0)|0,o=7880,n[o>>2]=1,n[o+4>>2]=0),10280}function u6e(o,l){o=o|0,l=l|0,n[o>>2]=f6e()|0,n[o+4>>2]=A6e()|0,n[o+12>>2]=l,n[o+8>>2]=p6e()|0,n[o+32>>2]=4}function f6e(){return 11711}function A6e(){return 1356}function p6e(){return KP()|0}function h6e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(g6e(u),yt(u)):l|0&&(zg(l),yt(l))}function g6e(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function d6e(o){o=o|0,Uh(o)}function m6e(o){o=o|0,y6e(o,4920),E6e(o)|0,I6e(o)|0}function y6e(o,l){o=o|0,l=l|0;var u=0;u=TZ()|0,n[o>>2]=u,H6e(u,l),Wh(n[o>>2]|0)}function E6e(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,Q6e()|0),o|0}function I6e(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,C6e()|0),o|0}function C6e(){var o=0;return s[7888]|0||(fX(10328),gr(53,10328,U|0)|0,o=7888,n[o>>2]=1,n[o+4>>2]=0),Ur(10328)|0||fX(10328),10328}function ud(o,l){o=o|0,l=l|0,vn(o,0,l,0,0,0)}function fX(o){o=o|0,v6e(o),fd(o,10)}function w6e(o){o=o|0,B6e(o+24|0)}function B6e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function v6e(o){o=o|0;var l=0;l=en()|0,tn(o,5,1,l,P6e()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function S6e(o,l,u){o=o|0,l=l|0,u=+u,D6e(o,l,u)}function fd(o,l){o=o|0,l=l|0,n[o+20>>2]=l}function D6e(o,l,u){o=o|0,l=l|0,u=+u;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+16|0,m=A+8|0,k=A+13|0,d=A,B=A+12|0,np(k,l),n[m>>2]=ip(k,l)|0,Tf(B,u),E[d>>3]=+Rf(B,u),b6e(o,m,d),I=A}function b6e(o,l,u){o=o|0,l=l|0,u=u|0,Rl(o+8|0,n[l>>2]|0,+E[u>>3]),s[o+24>>0]=1}function P6e(){return 1404}function x6e(o,l){return o=o|0,l=+l,k6e(o,l)|0}function k6e(o,l){o=o|0,l=+l;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return A=I,I=I+16|0,m=A+4|0,B=A+8|0,k=A,d=Fl(8)|0,u=d,T=Jt(16)|0,np(m,o),o=ip(m,o)|0,Tf(B,l),Rl(T,o,+Rf(B,l)),B=u+4|0,n[B>>2]=T,o=Jt(8)|0,B=n[B>>2]|0,n[k>>2]=0,n[m>>2]=n[k>>2],MM(o,B,m),n[d>>2]=o,I=A,u|0}function Q6e(){var o=0;return s[7896]|0||(AX(10364),gr(54,10364,U|0)|0,o=7896,n[o>>2]=1,n[o+4>>2]=0),Ur(10364)|0||AX(10364),10364}function AX(o){o=o|0,F6e(o),fd(o,55)}function T6e(o){o=o|0,R6e(o+24|0)}function R6e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function F6e(o){o=o|0;var l=0;l=en()|0,tn(o,5,4,l,M6e()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function N6e(o){o=o|0,O6e(o)}function O6e(o){o=o|0,L6e(o)}function L6e(o){o=o|0,pX(o+8|0),s[o+24>>0]=1}function pX(o){o=o|0,n[o>>2]=0,E[o+8>>3]=0}function M6e(){return 1424}function _6e(){return U6e()|0}function U6e(){var o=0,l=0,u=0,A=0,d=0,m=0,B=0;return l=I,I=I+16|0,d=l+4|0,B=l,u=Fl(8)|0,o=u,A=Jt(16)|0,pX(A),m=o+4|0,n[m>>2]=A,A=Jt(8)|0,m=n[m>>2]|0,n[B>>2]=0,n[d>>2]=n[B>>2],MM(A,m,d),n[u>>2]=A,I=l,o|0}function H6e(o,l){o=o|0,l=l|0,n[o>>2]=j6e()|0,n[o+4>>2]=q6e()|0,n[o+12>>2]=l,n[o+8>>2]=G6e()|0,n[o+32>>2]=5}function j6e(){return 11710}function q6e(){return 1416}function G6e(){return JP()|0}function W6e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(Y6e(u),yt(u)):l|0&&yt(l)}function Y6e(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function JP(){var o=0;return s[7904]|0||(n[2600]=V6e()|0,n[2601]=0,o=7904,n[o>>2]=1,n[o+4>>2]=0),10400}function V6e(){return n[357]|0}function K6e(o){o=o|0,J6e(o,4926),z6e(o)|0}function J6e(o,l){o=o|0,l=l|0;var u=0;u=tZ()|0,n[o>>2]=u,aqe(u,l),Wh(n[o>>2]|0)}function z6e(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,Z6e()|0),o|0}function Z6e(){var o=0;return s[7912]|0||(hX(10412),gr(56,10412,U|0)|0,o=7912,n[o>>2]=1,n[o+4>>2]=0),Ur(10412)|0||hX(10412),10412}function hX(o){o=o|0,eqe(o),fd(o,57)}function X6e(o){o=o|0,$6e(o+24|0)}function $6e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function eqe(o){o=o|0;var l=0;l=en()|0,tn(o,5,5,l,iqe()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function tqe(o){o=o|0,rqe(o)}function rqe(o){o=o|0,nqe(o)}function nqe(o){o=o|0;var l=0,u=0;l=o+8|0,u=l+48|0;do n[l>>2]=0,l=l+4|0;while((l|0)<(u|0));s[o+56>>0]=1}function iqe(){return 1432}function sqe(){return oqe()|0}function oqe(){var o=0,l=0,u=0,A=0,d=0,m=0,B=0,k=0;B=I,I=I+16|0,o=B+4|0,l=B,u=Fl(8)|0,A=u,d=Jt(48)|0,m=d,k=m+48|0;do n[m>>2]=0,m=m+4|0;while((m|0)<(k|0));return m=A+4|0,n[m>>2]=d,k=Jt(8)|0,m=n[m>>2]|0,n[l>>2]=0,n[o>>2]=n[l>>2],rZ(k,m,o),n[u>>2]=k,I=B,A|0}function aqe(o,l){o=o|0,l=l|0,n[o>>2]=lqe()|0,n[o+4>>2]=cqe()|0,n[o+12>>2]=l,n[o+8>>2]=uqe()|0,n[o+32>>2]=6}function lqe(){return 11704}function cqe(){return 1436}function uqe(){return JP()|0}function fqe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(Aqe(u),yt(u)):l|0&&yt(l)}function Aqe(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function pqe(o){o=o|0,hqe(o,4933),gqe(o)|0,dqe(o)|0}function hqe(o,l){o=o|0,l=l|0;var u=0;u=Uqe()|0,n[o>>2]=u,Hqe(u,l),Wh(n[o>>2]|0)}function gqe(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,kqe()|0),o|0}function dqe(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,mqe()|0),o|0}function mqe(){var o=0;return s[7920]|0||(gX(10452),gr(58,10452,U|0)|0,o=7920,n[o>>2]=1,n[o+4>>2]=0),Ur(10452)|0||gX(10452),10452}function gX(o){o=o|0,Iqe(o),fd(o,1)}function yqe(o){o=o|0,Eqe(o+24|0)}function Eqe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function Iqe(o){o=o|0;var l=0;l=en()|0,tn(o,5,1,l,vqe()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function Cqe(o,l,u){o=o|0,l=+l,u=+u,wqe(o,l,u)}function wqe(o,l,u){o=o|0,l=+l,u=+u;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+32|0,m=A+8|0,k=A+17|0,d=A,B=A+16|0,Tf(k,l),E[m>>3]=+Rf(k,l),Tf(B,u),E[d>>3]=+Rf(B,u),Bqe(o,m,d),I=A}function Bqe(o,l,u){o=o|0,l=l|0,u=u|0,dX(o+8|0,+E[l>>3],+E[u>>3]),s[o+24>>0]=1}function dX(o,l,u){o=o|0,l=+l,u=+u,E[o>>3]=l,E[o+8>>3]=u}function vqe(){return 1472}function Sqe(o,l){return o=+o,l=+l,Dqe(o,l)|0}function Dqe(o,l){o=+o,l=+l;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return A=I,I=I+16|0,B=A+4|0,k=A+8|0,T=A,d=Fl(8)|0,u=d,m=Jt(16)|0,Tf(B,o),o=+Rf(B,o),Tf(k,l),dX(m,o,+Rf(k,l)),k=u+4|0,n[k>>2]=m,m=Jt(8)|0,k=n[k>>2]|0,n[T>>2]=0,n[B>>2]=n[T>>2],mX(m,k,B),n[d>>2]=m,I=A,u|0}function mX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,u=Jt(16)|0,n[u+4>>2]=0,n[u+8>>2]=0,n[u>>2]=1452,n[u+12>>2]=l,n[o+4>>2]=u}function bqe(o){o=o|0,$y(o),yt(o)}function Pqe(o){o=o|0,o=n[o+12>>2]|0,o|0&&yt(o)}function xqe(o){o=o|0,yt(o)}function kqe(){var o=0;return s[7928]|0||(yX(10488),gr(59,10488,U|0)|0,o=7928,n[o>>2]=1,n[o+4>>2]=0),Ur(10488)|0||yX(10488),10488}function yX(o){o=o|0,Rqe(o),fd(o,60)}function Qqe(o){o=o|0,Tqe(o+24|0)}function Tqe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function Rqe(o){o=o|0;var l=0;l=en()|0,tn(o,5,6,l,Lqe()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function Fqe(o){o=o|0,Nqe(o)}function Nqe(o){o=o|0,Oqe(o)}function Oqe(o){o=o|0,EX(o+8|0),s[o+24>>0]=1}function EX(o){o=o|0,n[o>>2]=0,n[o+4>>2]=0,n[o+8>>2]=0,n[o+12>>2]=0}function Lqe(){return 1492}function Mqe(){return _qe()|0}function _qe(){var o=0,l=0,u=0,A=0,d=0,m=0,B=0;return l=I,I=I+16|0,d=l+4|0,B=l,u=Fl(8)|0,o=u,A=Jt(16)|0,EX(A),m=o+4|0,n[m>>2]=A,A=Jt(8)|0,m=n[m>>2]|0,n[B>>2]=0,n[d>>2]=n[B>>2],mX(A,m,d),n[u>>2]=A,I=l,o|0}function Uqe(){var o=0;return s[7936]|0||(Vqe(10524),gr(25,10524,U|0)|0,o=7936,n[o>>2]=1,n[o+4>>2]=0),10524}function Hqe(o,l){o=o|0,l=l|0,n[o>>2]=jqe()|0,n[o+4>>2]=qqe()|0,n[o+12>>2]=l,n[o+8>>2]=Gqe()|0,n[o+32>>2]=7}function jqe(){return 11700}function qqe(){return 1484}function Gqe(){return JP()|0}function Wqe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(Yqe(u),yt(u)):l|0&&yt(l)}function Yqe(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function Vqe(o){o=o|0,Uh(o)}function Kqe(o,l,u){o=o|0,l=l|0,u=u|0,o=Bn(l)|0,l=Jqe(u)|0,u=zqe(u,0)|0,DGe(o,l,u,e_()|0,0)}function Jqe(o){return o=o|0,o|0}function zqe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=e_()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(CX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(nGe(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function e_(){var o=0,l=0;if(s[7944]|0||(IX(10568),gr(61,10568,U|0)|0,l=7944,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10568)|0)){o=10568,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));IX(10568)}return 10568}function IX(o){o=o|0,$qe(o)}function Zqe(o){o=o|0,Xqe(o+24|0)}function Xqe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function $qe(o){o=o|0;var l=0;l=en()|0,tn(o,1,17,l,EZ()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function eGe(o){return o=o|0,rGe(n[(tGe(o)|0)>>2]|0)|0}function tGe(o){return o=o|0,(n[(e_()|0)+24>>2]|0)+(o<<3)|0}function rGe(o){return o=o|0,VP(ax[o&7]()|0)|0}function CX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function nGe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=iGe(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,sGe(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,CX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,oGe(o,d),aGe(d),I=k;return}}function iGe(o){return o=o|0,536870911}function sGe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function oGe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function aGe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function lGe(){cGe()}function cGe(){uGe(10604)}function uGe(o){o=o|0,fGe(o,4955)}function fGe(o,l){o=o|0,l=l|0;var u=0;u=AGe()|0,n[o>>2]=u,pGe(u,l),Wh(n[o>>2]|0)}function AGe(){var o=0;return s[7952]|0||(wGe(10612),gr(25,10612,U|0)|0,o=7952,n[o>>2]=1,n[o+4>>2]=0),10612}function pGe(o,l){o=o|0,l=l|0,n[o>>2]=mGe()|0,n[o+4>>2]=yGe()|0,n[o+12>>2]=l,n[o+8>>2]=EGe()|0,n[o+32>>2]=8}function Wh(o){o=o|0;var l=0,u=0;l=I,I=I+16|0,u=l,Ky()|0,n[u>>2]=o,hGe(10608,u),I=l}function Ky(){return s[11714]|0||(n[2652]=0,gr(62,10608,U|0)|0,s[11714]=1),10608}function hGe(o,l){o=o|0,l=l|0;var u=0;u=Jt(8)|0,n[u+4>>2]=n[l>>2],n[u>>2]=n[o>>2],n[o>>2]=u}function gGe(o){o=o|0,dGe(o)}function dGe(o){o=o|0;var l=0,u=0;if(l=n[o>>2]|0,l|0)do u=l,l=n[l>>2]|0,yt(u);while(l|0);n[o>>2]=0}function mGe(){return 11715}function yGe(){return 1496}function EGe(){return KP()|0}function IGe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(CGe(u),yt(u)):l|0&&yt(l)}function CGe(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function wGe(o){o=o|0,Uh(o)}function BGe(o,l){o=o|0,l=l|0;var u=0,A=0;Ky()|0,u=n[2652]|0;e:do if(u|0){for(;A=n[u+4>>2]|0,!(A|0&&!(r$(t_(A)|0,o)|0));)if(u=n[u>>2]|0,!u)break e;vGe(A,l)}while(!1)}function t_(o){return o=o|0,n[o+12>>2]|0}function vGe(o,l){o=o|0,l=l|0;var u=0;o=o+36|0,u=n[o>>2]|0,u|0&&(Df(u),yt(u)),u=Jt(4)|0,UP(u,l),n[o>>2]=u}function r_(){return s[11716]|0||(n[2664]=0,gr(63,10656,U|0)|0,s[11716]=1),10656}function wX(){var o=0;return s[11717]|0?o=n[2665]|0:(SGe(),n[2665]=1504,s[11717]=1,o=1504),o|0}function SGe(){s[11740]|0||(s[11718]=yr(yr(8,0)|0,0)|0,s[11719]=yr(yr(0,0)|0,0)|0,s[11720]=yr(yr(0,16)|0,0)|0,s[11721]=yr(yr(8,0)|0,0)|0,s[11722]=yr(yr(0,0)|0,0)|0,s[11723]=yr(yr(8,0)|0,0)|0,s[11724]=yr(yr(0,0)|0,0)|0,s[11725]=yr(yr(8,0)|0,0)|0,s[11726]=yr(yr(0,0)|0,0)|0,s[11727]=yr(yr(8,0)|0,0)|0,s[11728]=yr(yr(0,0)|0,0)|0,s[11729]=yr(yr(0,0)|0,32)|0,s[11730]=yr(yr(0,0)|0,32)|0,s[11740]=1)}function BX(){return 1572}function DGe(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0,_=0,M=0;m=I,I=I+32|0,M=m+16|0,_=m+12|0,T=m+8|0,k=m+4|0,B=m,n[M>>2]=o,n[_>>2]=l,n[T>>2]=u,n[k>>2]=A,n[B>>2]=d,r_()|0,bGe(10656,M,_,T,k,B),I=m}function bGe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0;var B=0;B=Jt(24)|0,Xz(B+4|0,n[l>>2]|0,n[u>>2]|0,n[A>>2]|0,n[d>>2]|0,n[m>>2]|0),n[B>>2]=n[o>>2],n[o>>2]=B}function vX(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0,tt=0,Ze=0,ct=0;if(ct=I,I=I+32|0,Le=ct+20|0,Qe=ct+8|0,tt=ct+4|0,Ze=ct,l=n[l>>2]|0,l|0){We=Le+4|0,T=Le+8|0,_=Qe+4|0,M=Qe+8|0,G=Qe+8|0,ae=Le+8|0;do{if(B=l+4|0,k=n_(B)|0,k|0){if(d=Q2(k)|0,n[Le>>2]=0,n[We>>2]=0,n[T>>2]=0,A=(T2(k)|0)+1|0,PGe(Le,A),A|0)for(;A=A+-1|0,xu(Qe,n[d>>2]|0),m=n[We>>2]|0,m>>>0<(n[ae>>2]|0)>>>0?(n[m>>2]=n[Qe>>2],n[We>>2]=(n[We>>2]|0)+4):i_(Le,Qe),A;)d=d+4|0;A=R2(k)|0,n[Qe>>2]=0,n[_>>2]=0,n[M>>2]=0;e:do if(n[A>>2]|0)for(d=0,m=0;;){if((d|0)==(m|0)?xGe(Qe,A):(n[d>>2]=n[A>>2],n[_>>2]=(n[_>>2]|0)+4),A=A+4|0,!(n[A>>2]|0))break e;d=n[_>>2]|0,m=n[G>>2]|0}while(!1);n[tt>>2]=zP(B)|0,n[Ze>>2]=Ur(k)|0,kGe(u,o,tt,Ze,Le,Qe),s_(Qe),sp(Le)}l=n[l>>2]|0}while(l|0)}I=ct}function n_(o){return o=o|0,n[o+12>>2]|0}function Q2(o){return o=o|0,n[o+12>>2]|0}function T2(o){return o=o|0,n[o+16>>2]|0}function PGe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;d=I,I=I+32|0,u=d,A=n[o>>2]|0,(n[o+8>>2]|0)-A>>2>>>0>>0&&(TX(u,l,(n[o+4>>2]|0)-A>>2,o+8|0),RX(o,u),FX(u)),I=d}function i_(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0;if(B=I,I=I+32|0,u=B,A=o+4|0,d=((n[A>>2]|0)-(n[o>>2]|0)>>2)+1|0,m=QX(o)|0,m>>>0>>0)sn(o);else{k=n[o>>2]|0,_=(n[o+8>>2]|0)-k|0,T=_>>1,TX(u,_>>2>>>0>>1>>>0?T>>>0>>0?d:T:m,(n[A>>2]|0)-k>>2,o+8|0),m=u+8|0,n[n[m>>2]>>2]=n[l>>2],n[m>>2]=(n[m>>2]|0)+4,RX(o,u),FX(u),I=B;return}}function R2(o){return o=o|0,n[o+8>>2]|0}function xGe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0;if(B=I,I=I+32|0,u=B,A=o+4|0,d=((n[A>>2]|0)-(n[o>>2]|0)>>2)+1|0,m=kX(o)|0,m>>>0>>0)sn(o);else{k=n[o>>2]|0,_=(n[o+8>>2]|0)-k|0,T=_>>1,JGe(u,_>>2>>>0>>1>>>0?T>>>0>>0?d:T:m,(n[A>>2]|0)-k>>2,o+8|0),m=u+8|0,n[n[m>>2]>>2]=n[l>>2],n[m>>2]=(n[m>>2]|0)+4,zGe(o,u),ZGe(u),I=B;return}}function zP(o){return o=o|0,n[o>>2]|0}function kGe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,QGe(o,l,u,A,d,m)}function s_(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-4-A|0)>>>2)<<2)),yt(u))}function sp(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-4-A|0)>>>2)<<2)),yt(u))}function QGe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0;var B=0,k=0,T=0,_=0,M=0,G=0;B=I,I=I+48|0,M=B+40|0,k=B+32|0,G=B+24|0,T=B+12|0,_=B,Nl(k),o=Ls(o)|0,n[G>>2]=n[l>>2],u=n[u>>2]|0,A=n[A>>2]|0,o_(T,d),TGe(_,m),n[M>>2]=n[G>>2],RGe(o,M,u,A,T,_),s_(_),sp(T),Ol(k),I=B}function o_(o,l){o=o|0,l=l|0;var u=0,A=0;n[o>>2]=0,n[o+4>>2]=0,n[o+8>>2]=0,u=l+4|0,A=(n[u>>2]|0)-(n[l>>2]|0)>>2,A|0&&(VGe(o,A),KGe(o,n[l>>2]|0,n[u>>2]|0,A))}function TGe(o,l){o=o|0,l=l|0;var u=0,A=0;n[o>>2]=0,n[o+4>>2]=0,n[o+8>>2]=0,u=l+4|0,A=(n[u>>2]|0)-(n[l>>2]|0)>>2,A|0&&(WGe(o,A),YGe(o,n[l>>2]|0,n[u>>2]|0,A))}function RGe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0;var B=0,k=0,T=0,_=0,M=0,G=0;B=I,I=I+32|0,M=B+28|0,G=B+24|0,k=B+12|0,T=B,_=ma(FGe()|0)|0,n[G>>2]=n[l>>2],n[M>>2]=n[G>>2],l=Ad(M)|0,u=SX(u)|0,A=a_(A)|0,n[k>>2]=n[d>>2],M=d+4|0,n[k+4>>2]=n[M>>2],G=d+8|0,n[k+8>>2]=n[G>>2],n[G>>2]=0,n[M>>2]=0,n[d>>2]=0,d=l_(k)|0,n[T>>2]=n[m>>2],M=m+4|0,n[T+4>>2]=n[M>>2],G=m+8|0,n[T+8>>2]=n[G>>2],n[G>>2]=0,n[M>>2]=0,n[m>>2]=0,uu(0,_|0,o|0,l|0,u|0,A|0,d|0,NGe(T)|0)|0,s_(T),sp(k),I=B}function FGe(){var o=0;return s[7968]|0||(qGe(10708),o=7968,n[o>>2]=1,n[o+4>>2]=0),10708}function Ad(o){return o=o|0,bX(o)|0}function SX(o){return o=o|0,DX(o)|0}function a_(o){return o=o|0,VP(o)|0}function l_(o){return o=o|0,LGe(o)|0}function NGe(o){return o=o|0,OGe(o)|0}function OGe(o){o=o|0;var l=0,u=0,A=0;if(A=(n[o+4>>2]|0)-(n[o>>2]|0)|0,u=A>>2,A=Fl(A+4|0)|0,n[A>>2]=u,u|0){l=0;do n[A+4+(l<<2)>>2]=DX(n[(n[o>>2]|0)+(l<<2)>>2]|0)|0,l=l+1|0;while((l|0)!=(u|0))}return A|0}function DX(o){return o=o|0,o|0}function LGe(o){o=o|0;var l=0,u=0,A=0;if(A=(n[o+4>>2]|0)-(n[o>>2]|0)|0,u=A>>2,A=Fl(A+4|0)|0,n[A>>2]=u,u|0){l=0;do n[A+4+(l<<2)>>2]=bX((n[o>>2]|0)+(l<<2)|0)|0,l=l+1|0;while((l|0)!=(u|0))}return A|0}function bX(o){o=o|0;var l=0,u=0,A=0,d=0;return d=I,I=I+32|0,l=d+12|0,u=d,A=IM(PX()|0)|0,A?(CM(l,A),wM(u,l),EYe(o,u),o=BM(l)|0):o=MGe(o)|0,I=d,o|0}function PX(){var o=0;return s[7960]|0||(jGe(10664),gr(25,10664,U|0)|0,o=7960,n[o>>2]=1,n[o+4>>2]=0),10664}function MGe(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0;return u=I,I=I+16|0,d=u+4|0,B=u,A=Fl(8)|0,l=A,k=Jt(4)|0,n[k>>2]=n[o>>2],m=l+4|0,n[m>>2]=k,o=Jt(8)|0,m=n[m>>2]|0,n[B>>2]=0,n[d>>2]=n[B>>2],xX(o,m,d),n[A>>2]=o,I=u,l|0}function xX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,u=Jt(16)|0,n[u+4>>2]=0,n[u+8>>2]=0,n[u>>2]=1656,n[u+12>>2]=l,n[o+4>>2]=u}function _Ge(o){o=o|0,$y(o),yt(o)}function UGe(o){o=o|0,o=n[o+12>>2]|0,o|0&&yt(o)}function HGe(o){o=o|0,yt(o)}function jGe(o){o=o|0,Uh(o)}function qGe(o){o=o|0,Ro(o,GGe()|0,5)}function GGe(){return 1676}function WGe(o,l){o=o|0,l=l|0;var u=0;if((kX(o)|0)>>>0>>0&&sn(o),l>>>0>1073741823)Nt();else{u=Jt(l<<2)|0,n[o+4>>2]=u,n[o>>2]=u,n[o+8>>2]=u+(l<<2);return}}function YGe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,A=o+4|0,o=u-l|0,(o|0)>0&&(Qr(n[A>>2]|0,l|0,o|0)|0,n[A>>2]=(n[A>>2]|0)+(o>>>2<<2))}function kX(o){return o=o|0,1073741823}function VGe(o,l){o=o|0,l=l|0;var u=0;if((QX(o)|0)>>>0>>0&&sn(o),l>>>0>1073741823)Nt();else{u=Jt(l<<2)|0,n[o+4>>2]=u,n[o>>2]=u,n[o+8>>2]=u+(l<<2);return}}function KGe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,A=o+4|0,o=u-l|0,(o|0)>0&&(Qr(n[A>>2]|0,l|0,o|0)|0,n[A>>2]=(n[A>>2]|0)+(o>>>2<<2))}function QX(o){return o=o|0,1073741823}function JGe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>1073741823)Nt();else{d=Jt(l<<2)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<2)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<2)}function zGe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>2)<<2)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function ZGe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-4-l|0)>>>2)<<2)),o=n[o>>2]|0,o|0&&yt(o)}function TX(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>1073741823)Nt();else{d=Jt(l<<2)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<2)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<2)}function RX(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>2)<<2)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function FX(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-4-l|0)>>>2)<<2)),o=n[o>>2]|0,o|0&&yt(o)}function XGe(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0;if(Qe=I,I=I+32|0,M=Qe+20|0,G=Qe+12|0,_=Qe+16|0,ae=Qe+4|0,We=Qe,Le=Qe+8|0,k=wX()|0,m=n[k>>2]|0,B=n[m>>2]|0,B|0)for(T=n[k+8>>2]|0,k=n[k+4>>2]|0;xu(M,B),$Ge(o,M,k,T),m=m+4|0,B=n[m>>2]|0,B;)T=T+1|0,k=k+1|0;if(m=BX()|0,B=n[m>>2]|0,B|0)do xu(M,B),n[G>>2]=n[m+4>>2],e5e(l,M,G),m=m+8|0,B=n[m>>2]|0;while(B|0);if(m=n[(Ky()|0)>>2]|0,m|0)do l=n[m+4>>2]|0,xu(M,n[(Jy(l)|0)>>2]|0),n[G>>2]=t_(l)|0,t5e(u,M,G),m=n[m>>2]|0;while(m|0);if(xu(_,0),m=r_()|0,n[M>>2]=n[_>>2],vX(M,m,d),m=n[(Ky()|0)>>2]|0,m|0){o=M+4|0,l=M+8|0,u=M+8|0;do{if(T=n[m+4>>2]|0,xu(G,n[(Jy(T)|0)>>2]|0),r5e(ae,NX(T)|0),B=n[ae>>2]|0,B|0){n[M>>2]=0,n[o>>2]=0,n[l>>2]=0;do xu(We,n[(Jy(n[B+4>>2]|0)|0)>>2]|0),k=n[o>>2]|0,k>>>0<(n[u>>2]|0)>>>0?(n[k>>2]=n[We>>2],n[o>>2]=(n[o>>2]|0)+4):i_(M,We),B=n[B>>2]|0;while(B|0);n5e(A,G,M),sp(M)}n[Le>>2]=n[G>>2],_=OX(T)|0,n[M>>2]=n[Le>>2],vX(M,_,d),iZ(ae),m=n[m>>2]|0}while(m|0)}I=Qe}function $Ge(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,g5e(o,l,u,A)}function e5e(o,l,u){o=o|0,l=l|0,u=u|0,h5e(o,l,u)}function Jy(o){return o=o|0,o|0}function t5e(o,l,u){o=o|0,l=l|0,u=u|0,u5e(o,l,u)}function NX(o){return o=o|0,o+16|0}function r5e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;if(m=I,I=I+16|0,d=m+8|0,u=m,n[o>>2]=0,A=n[l>>2]|0,n[d>>2]=A,n[u>>2]=o,u=c5e(u)|0,A|0){if(A=Jt(12)|0,B=(LX(d)|0)+4|0,o=n[B+4>>2]|0,l=A+4|0,n[l>>2]=n[B>>2],n[l+4>>2]=o,l=n[n[d>>2]>>2]|0,n[d>>2]=l,!l)o=A;else for(l=A;o=Jt(12)|0,T=(LX(d)|0)+4|0,k=n[T+4>>2]|0,B=o+4|0,n[B>>2]=n[T>>2],n[B+4>>2]=k,n[l>>2]=o,B=n[n[d>>2]>>2]|0,n[d>>2]=B,B;)l=o;n[o>>2]=n[u>>2],n[u>>2]=A}I=m}function n5e(o,l,u){o=o|0,l=l|0,u=u|0,i5e(o,l,u)}function OX(o){return o=o|0,o+24|0}function i5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+32|0,B=A+24|0,d=A+16|0,k=A+12|0,m=A,Nl(d),o=Ls(o)|0,n[k>>2]=n[l>>2],o_(m,u),n[B>>2]=n[k>>2],s5e(o,B,m),sp(m),Ol(d),I=A}function s5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=I,I=I+32|0,B=A+16|0,k=A+12|0,d=A,m=ma(o5e()|0)|0,n[k>>2]=n[l>>2],n[B>>2]=n[k>>2],l=Ad(B)|0,n[d>>2]=n[u>>2],B=u+4|0,n[d+4>>2]=n[B>>2],k=u+8|0,n[d+8>>2]=n[k>>2],n[k>>2]=0,n[B>>2]=0,n[u>>2]=0,Rs(0,m|0,o|0,l|0,l_(d)|0)|0,sp(d),I=A}function o5e(){var o=0;return s[7976]|0||(a5e(10720),o=7976,n[o>>2]=1,n[o+4>>2]=0),10720}function a5e(o){o=o|0,Ro(o,l5e()|0,2)}function l5e(){return 1732}function c5e(o){return o=o|0,n[o>>2]|0}function LX(o){return o=o|0,n[o>>2]|0}function u5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+32|0,m=A+16|0,d=A+8|0,B=A,Nl(d),o=Ls(o)|0,n[B>>2]=n[l>>2],u=n[u>>2]|0,n[m>>2]=n[B>>2],MX(o,m,u),Ol(d),I=A}function MX(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+16|0,m=A+4|0,B=A,d=ma(f5e()|0)|0,n[B>>2]=n[l>>2],n[m>>2]=n[B>>2],l=Ad(m)|0,Rs(0,d|0,o|0,l|0,SX(u)|0)|0,I=A}function f5e(){var o=0;return s[7984]|0||(A5e(10732),o=7984,n[o>>2]=1,n[o+4>>2]=0),10732}function A5e(o){o=o|0,Ro(o,p5e()|0,2)}function p5e(){return 1744}function h5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;A=I,I=I+32|0,m=A+16|0,d=A+8|0,B=A,Nl(d),o=Ls(o)|0,n[B>>2]=n[l>>2],u=n[u>>2]|0,n[m>>2]=n[B>>2],MX(o,m,u),Ol(d),I=A}function g5e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;d=I,I=I+32|0,B=d+16|0,m=d+8|0,k=d,Nl(m),o=Ls(o)|0,n[k>>2]=n[l>>2],u=s[u>>0]|0,A=s[A>>0]|0,n[B>>2]=n[k>>2],d5e(o,B,u,A),Ol(m),I=d}function d5e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;d=I,I=I+16|0,B=d+4|0,k=d,m=ma(m5e()|0)|0,n[k>>2]=n[l>>2],n[B>>2]=n[k>>2],l=Ad(B)|0,u=zy(u)|0,Oi(0,m|0,o|0,l|0,u|0,zy(A)|0)|0,I=d}function m5e(){var o=0;return s[7992]|0||(E5e(10744),o=7992,n[o>>2]=1,n[o+4>>2]=0),10744}function zy(o){return o=o|0,y5e(o)|0}function y5e(o){return o=o|0,o&255|0}function E5e(o){o=o|0,Ro(o,I5e()|0,3)}function I5e(){return 1756}function C5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;switch(ae=I,I=I+32|0,k=ae+8|0,T=ae+4|0,_=ae+20|0,M=ae,bM(o,0),A=yYe(l)|0,n[k>>2]=0,G=k+4|0,n[G>>2]=0,n[k+8>>2]=0,A<<24>>24){case 0:{s[_>>0]=0,w5e(T,u,_),ZP(o,T)|0,bf(T);break}case 8:{G=h_(l)|0,s[_>>0]=8,xu(M,n[G+4>>2]|0),B5e(T,u,_,M,G+8|0),ZP(o,T)|0,bf(T);break}case 9:{if(m=h_(l)|0,l=n[m+4>>2]|0,l|0)for(B=k+8|0,d=m+12|0;l=l+-1|0,xu(T,n[d>>2]|0),A=n[G>>2]|0,A>>>0<(n[B>>2]|0)>>>0?(n[A>>2]=n[T>>2],n[G>>2]=(n[G>>2]|0)+4):i_(k,T),l;)d=d+4|0;s[_>>0]=9,xu(M,n[m+8>>2]|0),v5e(T,u,_,M,k),ZP(o,T)|0,bf(T);break}default:G=h_(l)|0,s[_>>0]=A,xu(M,n[G+4>>2]|0),S5e(T,u,_,M),ZP(o,T)|0,bf(T)}sp(k),I=ae}function w5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;A=I,I=I+16|0,d=A,Nl(d),l=Ls(l)|0,M5e(o,l,s[u>>0]|0),Ol(d),I=A}function ZP(o,l){o=o|0,l=l|0;var u=0;return u=n[o>>2]|0,u|0&&Oa(u|0),n[o>>2]=n[l>>2],n[l>>2]=0,o|0}function B5e(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0;m=I,I=I+32|0,k=m+16|0,B=m+8|0,T=m,Nl(B),l=Ls(l)|0,u=s[u>>0]|0,n[T>>2]=n[A>>2],d=n[d>>2]|0,n[k>>2]=n[T>>2],F5e(o,l,u,k,d),Ol(B),I=m}function v5e(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0,_=0;m=I,I=I+32|0,T=m+24|0,B=m+16|0,_=m+12|0,k=m,Nl(B),l=Ls(l)|0,u=s[u>>0]|0,n[_>>2]=n[A>>2],o_(k,d),n[T>>2]=n[_>>2],k5e(o,l,u,T,k),sp(k),Ol(B),I=m}function S5e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;d=I,I=I+32|0,B=d+16|0,m=d+8|0,k=d,Nl(m),l=Ls(l)|0,u=s[u>>0]|0,n[k>>2]=n[A>>2],n[B>>2]=n[k>>2],D5e(o,l,u,B),Ol(m),I=d}function D5e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0,B=0,k=0;d=I,I=I+16|0,m=d+4|0,k=d,B=ma(b5e()|0)|0,u=zy(u)|0,n[k>>2]=n[A>>2],n[m>>2]=n[k>>2],XP(o,Rs(0,B|0,l|0,u|0,Ad(m)|0)|0),I=d}function b5e(){var o=0;return s[8e3]|0||(P5e(10756),o=8e3,n[o>>2]=1,n[o+4>>2]=0),10756}function XP(o,l){o=o|0,l=l|0,bM(o,l)}function P5e(o){o=o|0,Ro(o,x5e()|0,2)}function x5e(){return 1772}function k5e(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0,_=0;m=I,I=I+32|0,T=m+16|0,_=m+12|0,B=m,k=ma(Q5e()|0)|0,u=zy(u)|0,n[_>>2]=n[A>>2],n[T>>2]=n[_>>2],A=Ad(T)|0,n[B>>2]=n[d>>2],T=d+4|0,n[B+4>>2]=n[T>>2],_=d+8|0,n[B+8>>2]=n[_>>2],n[_>>2]=0,n[T>>2]=0,n[d>>2]=0,XP(o,Oi(0,k|0,l|0,u|0,A|0,l_(B)|0)|0),sp(B),I=m}function Q5e(){var o=0;return s[8008]|0||(T5e(10768),o=8008,n[o>>2]=1,n[o+4>>2]=0),10768}function T5e(o){o=o|0,Ro(o,R5e()|0,3)}function R5e(){return 1784}function F5e(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0;m=I,I=I+16|0,k=m+4|0,T=m,B=ma(N5e()|0)|0,u=zy(u)|0,n[T>>2]=n[A>>2],n[k>>2]=n[T>>2],A=Ad(k)|0,XP(o,Oi(0,B|0,l|0,u|0,A|0,a_(d)|0)|0),I=m}function N5e(){var o=0;return s[8016]|0||(O5e(10780),o=8016,n[o>>2]=1,n[o+4>>2]=0),10780}function O5e(o){o=o|0,Ro(o,L5e()|0,3)}function L5e(){return 1800}function M5e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;A=ma(_5e()|0)|0,XP(o,dn(0,A|0,l|0,zy(u)|0)|0)}function _5e(){var o=0;return s[8024]|0||(U5e(10792),o=8024,n[o>>2]=1,n[o+4>>2]=0),10792}function U5e(o){o=o|0,Ro(o,H5e()|0,1)}function H5e(){return 1816}function j5e(){q5e(),G5e(),W5e()}function q5e(){n[2702]=A$(65536)|0}function G5e(){f9e(10856)}function W5e(){Y5e(10816)}function Y5e(o){o=o|0,V5e(o,5044),K5e(o)|0}function V5e(o,l){o=o|0,l=l|0;var u=0;u=PX()|0,n[o>>2]=u,s9e(u,l),Wh(n[o>>2]|0)}function K5e(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,J5e()|0),o|0}function J5e(){var o=0;return s[8032]|0||(_X(10820),gr(64,10820,U|0)|0,o=8032,n[o>>2]=1,n[o+4>>2]=0),Ur(10820)|0||_X(10820),10820}function _X(o){o=o|0,X5e(o),fd(o,25)}function z5e(o){o=o|0,Z5e(o+24|0)}function Z5e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function X5e(o){o=o|0;var l=0;l=en()|0,tn(o,5,18,l,r9e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function $5e(o,l){o=o|0,l=l|0,e9e(o,l)}function e9e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;u=I,I=I+16|0,A=u,d=u+4|0,ld(d,l),n[A>>2]=cd(d,l)|0,t9e(o,A),I=u}function t9e(o,l){o=o|0,l=l|0,UX(o+4|0,n[l>>2]|0),s[o+8>>0]=1}function UX(o,l){o=o|0,l=l|0,n[o>>2]=l}function r9e(){return 1824}function n9e(o){return o=o|0,i9e(o)|0}function i9e(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0;return u=I,I=I+16|0,d=u+4|0,B=u,A=Fl(8)|0,l=A,k=Jt(4)|0,ld(d,o),UX(k,cd(d,o)|0),m=l+4|0,n[m>>2]=k,o=Jt(8)|0,m=n[m>>2]|0,n[B>>2]=0,n[d>>2]=n[B>>2],xX(o,m,d),n[A>>2]=o,I=u,l|0}function Fl(o){o=o|0;var l=0,u=0;return o=o+7&-8,o>>>0<=32768&&(l=n[2701]|0,o>>>0<=(65536-l|0)>>>0)?(u=(n[2702]|0)+l|0,n[2701]=l+o,o=u):(o=A$(o+8|0)|0,n[o>>2]=n[2703],n[2703]=o,o=o+8|0),o|0}function s9e(o,l){o=o|0,l=l|0,n[o>>2]=o9e()|0,n[o+4>>2]=a9e()|0,n[o+12>>2]=l,n[o+8>>2]=l9e()|0,n[o+32>>2]=9}function o9e(){return 11744}function a9e(){return 1832}function l9e(){return JP()|0}function c9e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(u9e(u),yt(u)):l|0&&yt(l)}function u9e(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function f9e(o){o=o|0,A9e(o,5052),p9e(o)|0,h9e(o,5058,26)|0,g9e(o,5069,1)|0,d9e(o,5077,10)|0,m9e(o,5087,19)|0,y9e(o,5094,27)|0}function A9e(o,l){o=o|0,l=l|0;var u=0;u=uYe()|0,n[o>>2]=u,fYe(u,l),Wh(n[o>>2]|0)}function p9e(o){o=o|0;var l=0;return l=n[o>>2]|0,ud(l,zWe()|0),o|0}function h9e(o,l,u){return o=o|0,l=l|0,u=u|0,TWe(o,Bn(l)|0,u,0),o|0}function g9e(o,l,u){return o=o|0,l=l|0,u=u|0,mWe(o,Bn(l)|0,u,0),o|0}function d9e(o,l,u){return o=o|0,l=l|0,u=u|0,J9e(o,Bn(l)|0,u,0),o|0}function m9e(o,l,u){return o=o|0,l=l|0,u=u|0,F9e(o,Bn(l)|0,u,0),o|0}function HX(o,l){o=o|0,l=l|0;var u=0,A=0;e:for(;;){for(u=n[2703]|0;;){if((u|0)==(l|0))break e;if(A=n[u>>2]|0,n[2703]=A,!u)u=A;else break}yt(u)}n[2701]=o}function y9e(o,l,u){return o=o|0,l=l|0,u=u|0,E9e(o,Bn(l)|0,u,0),o|0}function E9e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=c_()|0,o=I9e(u)|0,vn(m,l,d,o,C9e(u,A)|0,A)}function c_(){var o=0,l=0;if(s[8040]|0||(qX(10860),gr(65,10860,U|0)|0,l=8040,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10860)|0)){o=10860,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));qX(10860)}return 10860}function I9e(o){return o=o|0,o|0}function C9e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=c_()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(jX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(w9e(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function jX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function w9e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=B9e(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,v9e(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,jX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,S9e(o,d),D9e(d),I=k;return}}function B9e(o){return o=o|0,536870911}function v9e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function S9e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function D9e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function qX(o){o=o|0,x9e(o)}function b9e(o){o=o|0,P9e(o+24|0)}function P9e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function x9e(o){o=o|0;var l=0;l=en()|0,tn(o,1,11,l,k9e()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function k9e(){return 1840}function Q9e(o,l,u){o=o|0,l=l|0,u=u|0,R9e(n[(T9e(o)|0)>>2]|0,l,u)}function T9e(o){return o=o|0,(n[(c_()|0)+24>>2]|0)+(o<<3)|0}function R9e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;A=I,I=I+16|0,m=A+1|0,d=A,ld(m,l),l=cd(m,l)|0,ld(d,u),u=cd(d,u)|0,ap[o&31](l,u),I=A}function F9e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=u_()|0,o=N9e(u)|0,vn(m,l,d,o,O9e(u,A)|0,A)}function u_(){var o=0,l=0;if(s[8048]|0||(WX(10896),gr(66,10896,U|0)|0,l=8048,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10896)|0)){o=10896,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));WX(10896)}return 10896}function N9e(o){return o=o|0,o|0}function O9e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=u_()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(GX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(L9e(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function GX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function L9e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=M9e(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,_9e(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,GX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,U9e(o,d),H9e(d),I=k;return}}function M9e(o){return o=o|0,536870911}function _9e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function U9e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function H9e(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function WX(o){o=o|0,G9e(o)}function j9e(o){o=o|0,q9e(o+24|0)}function q9e(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function G9e(o){o=o|0;var l=0;l=en()|0,tn(o,1,11,l,W9e()|0,1),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function W9e(){return 1852}function Y9e(o,l){return o=o|0,l=l|0,K9e(n[(V9e(o)|0)>>2]|0,l)|0}function V9e(o){return o=o|0,(n[(u_()|0)+24>>2]|0)+(o<<3)|0}function K9e(o,l){o=o|0,l=l|0;var u=0,A=0;return u=I,I=I+16|0,A=u,ld(A,l),l=cd(A,l)|0,l=VP(dd[o&31](l)|0)|0,I=u,l|0}function J9e(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=f_()|0,o=z9e(u)|0,vn(m,l,d,o,Z9e(u,A)|0,A)}function f_(){var o=0,l=0;if(s[8056]|0||(VX(10932),gr(67,10932,U|0)|0,l=8056,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10932)|0)){o=10932,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));VX(10932)}return 10932}function z9e(o){return o=o|0,o|0}function Z9e(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=f_()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(YX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(X9e(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function YX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function X9e(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=$9e(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,eWe(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,YX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,tWe(o,d),rWe(d),I=k;return}}function $9e(o){return o=o|0,536870911}function eWe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function tWe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function rWe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function VX(o){o=o|0,sWe(o)}function nWe(o){o=o|0,iWe(o+24|0)}function iWe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function sWe(o){o=o|0;var l=0;l=en()|0,tn(o,1,7,l,oWe()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function oWe(){return 1860}function aWe(o,l,u){return o=o|0,l=l|0,u=u|0,cWe(n[(lWe(o)|0)>>2]|0,l,u)|0}function lWe(o){return o=o|0,(n[(f_()|0)+24>>2]|0)+(o<<3)|0}function cWe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0;return A=I,I=I+32|0,B=A+12|0,m=A+8|0,k=A,T=A+16|0,d=A+4|0,uWe(T,l),fWe(k,T,l),Hh(d,u),u=jh(d,u)|0,n[B>>2]=n[k>>2],L2[o&15](m,B,u),u=AWe(m)|0,bf(m),qh(d),I=A,u|0}function uWe(o,l){o=o|0,l=l|0}function fWe(o,l,u){o=o|0,l=l|0,u=u|0,pWe(o,u)}function AWe(o){return o=o|0,Ls(o)|0}function pWe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0;d=I,I=I+16|0,u=d,A=l,A&1?(hWe(u,0),Me(A|0,u|0)|0,gWe(o,u),dWe(u)):n[o>>2]=n[l>>2],I=d}function hWe(o,l){o=o|0,l=l|0,bu(o,l),n[o+4>>2]=0,s[o+8>>0]=0}function gWe(o,l){o=o|0,l=l|0,n[o>>2]=n[l+4>>2]}function dWe(o){o=o|0,s[o+8>>0]=0}function mWe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=A_()|0,o=yWe(u)|0,vn(m,l,d,o,EWe(u,A)|0,A)}function A_(){var o=0,l=0;if(s[8064]|0||(JX(10968),gr(68,10968,U|0)|0,l=8064,n[l>>2]=1,n[l+4>>2]=0),!(Ur(10968)|0)){o=10968,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));JX(10968)}return 10968}function yWe(o){return o=o|0,o|0}function EWe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=A_()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(KX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(IWe(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function KX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function IWe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=CWe(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,wWe(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,KX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,BWe(o,d),vWe(d),I=k;return}}function CWe(o){return o=o|0,536870911}function wWe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function BWe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function vWe(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function JX(o){o=o|0,bWe(o)}function SWe(o){o=o|0,DWe(o+24|0)}function DWe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function bWe(o){o=o|0;var l=0;l=en()|0,tn(o,1,1,l,PWe()|0,5),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function PWe(){return 1872}function xWe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,QWe(n[(kWe(o)|0)>>2]|0,l,u,A,d,m)}function kWe(o){return o=o|0,(n[(A_()|0)+24>>2]|0)+(o<<3)|0}function QWe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0;var B=0,k=0,T=0,_=0,M=0,G=0;B=I,I=I+32|0,k=B+16|0,T=B+12|0,_=B+8|0,M=B+4|0,G=B,Hh(k,l),l=jh(k,l)|0,Hh(T,u),u=jh(T,u)|0,Hh(_,A),A=jh(_,A)|0,Hh(M,d),d=jh(M,d)|0,Hh(G,m),m=jh(G,m)|0,m$[o&1](l,u,A,d,m),qh(G),qh(M),qh(_),qh(T),qh(k),I=B}function TWe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;m=n[o>>2]|0,d=p_()|0,o=RWe(u)|0,vn(m,l,d,o,FWe(u,A)|0,A)}function p_(){var o=0,l=0;if(s[8072]|0||(ZX(11004),gr(69,11004,U|0)|0,l=8072,n[l>>2]=1,n[l+4>>2]=0),!(Ur(11004)|0)){o=11004,l=o+36|0;do n[o>>2]=0,o=o+4|0;while((o|0)<(l|0));ZX(11004)}return 11004}function RWe(o){return o=o|0,o|0}function FWe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0,k=0,T=0;return k=I,I=I+16|0,d=k,m=k+4|0,n[d>>2]=o,T=p_()|0,B=T+24|0,l=yr(l,4)|0,n[m>>2]=l,u=T+28|0,A=n[u>>2]|0,A>>>0<(n[T+32>>2]|0)>>>0?(zX(A,o,l),l=(n[u>>2]|0)+8|0,n[u>>2]=l):(NWe(B,d,m),l=n[u>>2]|0),I=k,(l-(n[B>>2]|0)>>3)+-1|0}function zX(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,n[o+4>>2]=u}function NWe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0;if(k=I,I=I+32|0,d=k,m=o+4|0,B=((n[m>>2]|0)-(n[o>>2]|0)>>3)+1|0,A=OWe(o)|0,A>>>0>>0)sn(o);else{T=n[o>>2]|0,M=(n[o+8>>2]|0)-T|0,_=M>>2,LWe(d,M>>3>>>0>>1>>>0?_>>>0>>0?B:_:A,(n[m>>2]|0)-T>>3,o+8|0),B=d+8|0,zX(n[B>>2]|0,n[l>>2]|0,n[u>>2]|0),n[B>>2]=(n[B>>2]|0)+8,MWe(o,d),_We(d),I=k;return}}function OWe(o){return o=o|0,536870911}function LWe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0;n[o+12>>2]=0,n[o+16>>2]=A;do if(l)if(l>>>0>536870911)Nt();else{d=Jt(l<<3)|0;break}else d=0;while(!1);n[o>>2]=d,A=d+(u<<3)|0,n[o+8>>2]=A,n[o+4>>2]=A,n[o+12>>2]=d+(l<<3)}function MWe(o,l){o=o|0,l=l|0;var u=0,A=0,d=0,m=0,B=0;A=n[o>>2]|0,B=o+4|0,m=l+4|0,d=(n[B>>2]|0)-A|0,u=(n[m>>2]|0)+(0-(d>>3)<<3)|0,n[m>>2]=u,(d|0)>0?(Qr(u|0,A|0,d|0)|0,A=m,u=n[m>>2]|0):A=m,m=n[o>>2]|0,n[o>>2]=u,n[A>>2]=m,m=l+8|0,d=n[B>>2]|0,n[B>>2]=n[m>>2],n[m>>2]=d,m=o+8|0,B=l+12|0,o=n[m>>2]|0,n[m>>2]=n[B>>2],n[B>>2]=o,n[l>>2]=n[A>>2]}function _We(o){o=o|0;var l=0,u=0,A=0;l=n[o+4>>2]|0,u=o+8|0,A=n[u>>2]|0,(A|0)!=(l|0)&&(n[u>>2]=A+(~((A+-8-l|0)>>>3)<<3)),o=n[o>>2]|0,o|0&&yt(o)}function ZX(o){o=o|0,jWe(o)}function UWe(o){o=o|0,HWe(o+24|0)}function HWe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function jWe(o){o=o|0;var l=0;l=en()|0,tn(o,1,12,l,qWe()|0,2),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function qWe(){return 1896}function GWe(o,l,u){o=o|0,l=l|0,u=u|0,YWe(n[(WWe(o)|0)>>2]|0,l,u)}function WWe(o){return o=o|0,(n[(p_()|0)+24>>2]|0)+(o<<3)|0}function YWe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;A=I,I=I+16|0,m=A+4|0,d=A,VWe(m,l),l=KWe(m,l)|0,Hh(d,u),u=jh(d,u)|0,ap[o&31](l,u),qh(d),I=A}function VWe(o,l){o=o|0,l=l|0}function KWe(o,l){return o=o|0,l=l|0,JWe(l)|0}function JWe(o){return o=o|0,o|0}function zWe(){var o=0;return s[8080]|0||(XX(11040),gr(70,11040,U|0)|0,o=8080,n[o>>2]=1,n[o+4>>2]=0),Ur(11040)|0||XX(11040),11040}function XX(o){o=o|0,$We(o),fd(o,71)}function ZWe(o){o=o|0,XWe(o+24|0)}function XWe(o){o=o|0;var l=0,u=0,A=0;u=n[o>>2]|0,A=u,u|0&&(o=o+4|0,l=n[o>>2]|0,(l|0)!=(u|0)&&(n[o>>2]=l+(~((l+-8-A|0)>>>3)<<3)),yt(u))}function $We(o){o=o|0;var l=0;l=en()|0,tn(o,5,7,l,nYe()|0,0),n[o+24>>2]=0,n[o+28>>2]=0,n[o+32>>2]=0}function eYe(o){o=o|0,tYe(o)}function tYe(o){o=o|0,rYe(o)}function rYe(o){o=o|0,s[o+8>>0]=1}function nYe(){return 1936}function iYe(){return sYe()|0}function sYe(){var o=0,l=0,u=0,A=0,d=0,m=0,B=0;return l=I,I=I+16|0,d=l+4|0,B=l,u=Fl(8)|0,o=u,m=o+4|0,n[m>>2]=Jt(1)|0,A=Jt(8)|0,m=n[m>>2]|0,n[B>>2]=0,n[d>>2]=n[B>>2],oYe(A,m,d),n[u>>2]=A,I=l,o|0}function oYe(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]=l,u=Jt(16)|0,n[u+4>>2]=0,n[u+8>>2]=0,n[u>>2]=1916,n[u+12>>2]=l,n[o+4>>2]=u}function aYe(o){o=o|0,$y(o),yt(o)}function lYe(o){o=o|0,o=n[o+12>>2]|0,o|0&&yt(o)}function cYe(o){o=o|0,yt(o)}function uYe(){var o=0;return s[8088]|0||(mYe(11076),gr(25,11076,U|0)|0,o=8088,n[o>>2]=1,n[o+4>>2]=0),11076}function fYe(o,l){o=o|0,l=l|0,n[o>>2]=AYe()|0,n[o+4>>2]=pYe()|0,n[o+12>>2]=l,n[o+8>>2]=hYe()|0,n[o+32>>2]=10}function AYe(){return 11745}function pYe(){return 1940}function hYe(){return KP()|0}function gYe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,(Gh(A,896)|0)==512?u|0&&(dYe(u),yt(u)):l|0&&yt(l)}function dYe(o){o=o|0,o=n[o+4>>2]|0,o|0&&Yh(o)}function mYe(o){o=o|0,Uh(o)}function xu(o,l){o=o|0,l=l|0,n[o>>2]=l}function h_(o){return o=o|0,n[o>>2]|0}function yYe(o){return o=o|0,s[n[o>>2]>>0]|0}function EYe(o,l){o=o|0,l=l|0;var u=0,A=0;u=I,I=I+16|0,A=u,n[A>>2]=n[o>>2],IYe(l,A)|0,I=u}function IYe(o,l){o=o|0,l=l|0;var u=0;return u=CYe(n[o>>2]|0,l)|0,l=o+4|0,n[(n[l>>2]|0)+8>>2]=u,n[(n[l>>2]|0)+8>>2]|0}function CYe(o,l){o=o|0,l=l|0;var u=0,A=0;return u=I,I=I+16|0,A=u,Nl(A),o=Ls(o)|0,l=wYe(o,n[l>>2]|0)|0,Ol(A),I=u,l|0}function Nl(o){o=o|0,n[o>>2]=n[2701],n[o+4>>2]=n[2703]}function wYe(o,l){o=o|0,l=l|0;var u=0;return u=ma(BYe()|0)|0,dn(0,u|0,o|0,a_(l)|0)|0}function Ol(o){o=o|0,HX(n[o>>2]|0,n[o+4>>2]|0)}function BYe(){var o=0;return s[8096]|0||(vYe(11120),o=8096,n[o>>2]=1,n[o+4>>2]=0),11120}function vYe(o){o=o|0,Ro(o,SYe()|0,1)}function SYe(){return 1948}function DYe(){bYe()}function bYe(){var o=0,l=0,u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0;if(Le=I,I=I+16|0,M=Le+4|0,G=Le,aa(65536,10804,n[2702]|0,10812),u=wX()|0,l=n[u>>2]|0,o=n[l>>2]|0,o|0)for(A=n[u+8>>2]|0,u=n[u+4>>2]|0;hf(o|0,c[u>>0]|0|0,s[A>>0]|0),l=l+4|0,o=n[l>>2]|0,o;)A=A+1|0,u=u+1|0;if(o=BX()|0,l=n[o>>2]|0,l|0)do LA(l|0,n[o+4>>2]|0),o=o+8|0,l=n[o>>2]|0;while(l|0);LA(PYe()|0,5167),_=Ky()|0,o=n[_>>2]|0;e:do if(o|0){do xYe(n[o+4>>2]|0),o=n[o>>2]|0;while(o|0);if(o=n[_>>2]|0,o|0){T=_;do{for(;d=o,o=n[o>>2]|0,d=n[d+4>>2]|0,!!(kYe(d)|0);)if(n[G>>2]=T,n[M>>2]=n[G>>2],QYe(_,M)|0,!o)break e;if(TYe(d),T=n[T>>2]|0,l=$X(d)|0,m=Ni()|0,B=I,I=I+((1*(l<<2)|0)+15&-16)|0,k=I,I=I+((1*(l<<2)|0)+15&-16)|0,l=n[(NX(d)|0)>>2]|0,l|0)for(u=B,A=k;n[u>>2]=n[(Jy(n[l+4>>2]|0)|0)>>2],n[A>>2]=n[l+8>>2],l=n[l>>2]|0,l;)u=u+4|0,A=A+4|0;Qe=Jy(d)|0,l=RYe(d)|0,u=$X(d)|0,A=FYe(d)|0,ac(Qe|0,l|0,B|0,k|0,u|0,A|0,t_(d)|0),OA(m|0)}while(o|0)}}while(!1);if(o=n[(r_()|0)>>2]|0,o|0)do Qe=o+4|0,_=n_(Qe)|0,d=R2(_)|0,m=Q2(_)|0,B=(T2(_)|0)+1|0,k=$P(_)|0,T=e$(Qe)|0,_=Ur(_)|0,M=zP(Qe)|0,G=g_(Qe)|0,Au(0,d|0,m|0,B|0,k|0,T|0,_|0,M|0,G|0,d_(Qe)|0),o=n[o>>2]|0;while(o|0);o=n[(Ky()|0)>>2]|0;e:do if(o|0){t:for(;;){if(l=n[o+4>>2]|0,l|0&&(ae=n[(Jy(l)|0)>>2]|0,We=n[(OX(l)|0)>>2]|0,We|0)){u=We;do{l=u+4|0,A=n_(l)|0;r:do if(A|0)switch(Ur(A)|0){case 0:break t;case 4:case 3:case 2:{k=R2(A)|0,T=Q2(A)|0,_=(T2(A)|0)+1|0,M=$P(A)|0,G=Ur(A)|0,Qe=zP(l)|0,Au(ae|0,k|0,T|0,_|0,M|0,0,G|0,Qe|0,g_(l)|0,d_(l)|0);break r}case 1:{B=R2(A)|0,k=Q2(A)|0,T=(T2(A)|0)+1|0,_=$P(A)|0,M=e$(l)|0,G=Ur(A)|0,Qe=zP(l)|0,Au(ae|0,B|0,k|0,T|0,_|0,M|0,G|0,Qe|0,g_(l)|0,d_(l)|0);break r}case 5:{_=R2(A)|0,M=Q2(A)|0,G=(T2(A)|0)+1|0,Qe=$P(A)|0,Au(ae|0,_|0,M|0,G|0,Qe|0,NYe(A)|0,Ur(A)|0,0,0,0);break r}default:break r}while(!1);u=n[u>>2]|0}while(u|0)}if(o=n[o>>2]|0,!o)break e}Nt()}while(!1);ve(),I=Le}function PYe(){return 11703}function xYe(o){o=o|0,s[o+40>>0]=0}function kYe(o){return o=o|0,(s[o+40>>0]|0)!=0|0}function QYe(o,l){return o=o|0,l=l|0,l=OYe(l)|0,o=n[l>>2]|0,n[l>>2]=n[o>>2],yt(o),n[l>>2]|0}function TYe(o){o=o|0,s[o+40>>0]=1}function $X(o){return o=o|0,n[o+20>>2]|0}function RYe(o){return o=o|0,n[o+8>>2]|0}function FYe(o){return o=o|0,n[o+32>>2]|0}function $P(o){return o=o|0,n[o+4>>2]|0}function e$(o){return o=o|0,n[o+4>>2]|0}function g_(o){return o=o|0,n[o+8>>2]|0}function d_(o){return o=o|0,n[o+16>>2]|0}function NYe(o){return o=o|0,n[o+20>>2]|0}function OYe(o){return o=o|0,n[o>>2]|0}function ex(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0,tt=0,Ze=0,ct=0,He=0,Ge=0,Lt=0;Lt=I,I=I+16|0,ae=Lt;do if(o>>>0<245){if(_=o>>>0<11?16:o+11&-8,o=_>>>3,G=n[2783]|0,u=G>>>o,u&3|0)return l=(u&1^1)+o|0,o=11172+(l<<1<<2)|0,u=o+8|0,A=n[u>>2]|0,d=A+8|0,m=n[d>>2]|0,(o|0)==(m|0)?n[2783]=G&~(1<>2]=o,n[u>>2]=m),Ge=l<<3,n[A+4>>2]=Ge|3,Ge=A+Ge+4|0,n[Ge>>2]=n[Ge>>2]|1,Ge=d,I=Lt,Ge|0;if(M=n[2785]|0,_>>>0>M>>>0){if(u|0)return l=2<>>12&16,l=l>>>B,u=l>>>5&8,l=l>>>u,d=l>>>2&4,l=l>>>d,o=l>>>1&2,l=l>>>o,A=l>>>1&1,A=(u|B|d|o|A)+(l>>>A)|0,l=11172+(A<<1<<2)|0,o=l+8|0,d=n[o>>2]|0,B=d+8|0,u=n[B>>2]|0,(l|0)==(u|0)?(o=G&~(1<>2]=l,n[o>>2]=u,o=G),m=(A<<3)-_|0,n[d+4>>2]=_|3,A=d+_|0,n[A+4>>2]=m|1,n[A+m>>2]=m,M|0&&(d=n[2788]|0,l=M>>>3,u=11172+(l<<1<<2)|0,l=1<>2]|0):(n[2783]=o|l,l=u,o=u+8|0),n[o>>2]=d,n[l+12>>2]=d,n[d+8>>2]=l,n[d+12>>2]=u),n[2785]=m,n[2788]=A,Ge=B,I=Lt,Ge|0;if(k=n[2784]|0,k){if(u=(k&0-k)+-1|0,B=u>>>12&16,u=u>>>B,m=u>>>5&8,u=u>>>m,T=u>>>2&4,u=u>>>T,A=u>>>1&2,u=u>>>A,o=u>>>1&1,o=n[11436+((m|B|T|A|o)+(u>>>o)<<2)>>2]|0,u=(n[o+4>>2]&-8)-_|0,A=n[o+16+(((n[o+16>>2]|0)==0&1)<<2)>>2]|0,!A)T=o,m=u;else{do B=(n[A+4>>2]&-8)-_|0,T=B>>>0>>0,u=T?B:u,o=T?A:o,A=n[A+16+(((n[A+16>>2]|0)==0&1)<<2)>>2]|0;while(A|0);T=o,m=u}if(B=T+_|0,T>>>0>>0){d=n[T+24>>2]|0,l=n[T+12>>2]|0;do if((l|0)==(T|0)){if(o=T+20|0,l=n[o>>2]|0,!l&&(o=T+16|0,l=n[o>>2]|0,!l)){u=0;break}for(;;){if(u=l+20|0,A=n[u>>2]|0,A|0){l=A,o=u;continue}if(u=l+16|0,A=n[u>>2]|0,A)l=A,o=u;else break}n[o>>2]=0,u=l}else u=n[T+8>>2]|0,n[u+12>>2]=l,n[l+8>>2]=u,u=l;while(!1);do if(d|0){if(l=n[T+28>>2]|0,o=11436+(l<<2)|0,(T|0)==(n[o>>2]|0)){if(n[o>>2]=u,!u){n[2784]=k&~(1<>2]|0)!=(T|0)&1)<<2)>>2]=u,!u)break;n[u+24>>2]=d,l=n[T+16>>2]|0,l|0&&(n[u+16>>2]=l,n[l+24>>2]=u),l=n[T+20>>2]|0,l|0&&(n[u+20>>2]=l,n[l+24>>2]=u)}while(!1);return m>>>0<16?(Ge=m+_|0,n[T+4>>2]=Ge|3,Ge=T+Ge+4|0,n[Ge>>2]=n[Ge>>2]|1):(n[T+4>>2]=_|3,n[B+4>>2]=m|1,n[B+m>>2]=m,M|0&&(A=n[2788]|0,l=M>>>3,u=11172+(l<<1<<2)|0,l=1<>2]|0):(n[2783]=G|l,l=u,o=u+8|0),n[o>>2]=A,n[l+12>>2]=A,n[A+8>>2]=l,n[A+12>>2]=u),n[2785]=m,n[2788]=B),Ge=T+8|0,I=Lt,Ge|0}else G=_}else G=_}else G=_}else if(o>>>0<=4294967231)if(o=o+11|0,_=o&-8,T=n[2784]|0,T){A=0-_|0,o=o>>>8,o?_>>>0>16777215?k=31:(G=(o+1048320|0)>>>16&8,He=o<>>16&4,He=He<>>16&2,k=14-(M|G|k)+(He<>>15)|0,k=_>>>(k+7|0)&1|k<<1):k=0,u=n[11436+(k<<2)>>2]|0;e:do if(!u)u=0,o=0,He=57;else for(o=0,B=_<<((k|0)==31?0:25-(k>>>1)|0),m=0;;){if(d=(n[u+4>>2]&-8)-_|0,d>>>0>>0)if(d)o=u,A=d;else{o=u,A=0,d=u,He=61;break e}if(d=n[u+20>>2]|0,u=n[u+16+(B>>>31<<2)>>2]|0,m=(d|0)==0|(d|0)==(u|0)?m:d,d=(u|0)==0,d){u=m,He=57;break}else B=B<<((d^1)&1)}while(!1);if((He|0)==57){if((u|0)==0&(o|0)==0){if(o=2<>>12&16,G=G>>>B,m=G>>>5&8,G=G>>>m,k=G>>>2&4,G=G>>>k,M=G>>>1&2,G=G>>>M,u=G>>>1&1,o=0,u=n[11436+((m|B|k|M|u)+(G>>>u)<<2)>>2]|0}u?(d=u,He=61):(k=o,B=A)}if((He|0)==61)for(;;)if(He=0,u=(n[d+4>>2]&-8)-_|0,G=u>>>0>>0,u=G?u:A,o=G?d:o,d=n[d+16+(((n[d+16>>2]|0)==0&1)<<2)>>2]|0,d)A=u,He=61;else{k=o,B=u;break}if(k|0&&B>>>0<((n[2785]|0)-_|0)>>>0){if(m=k+_|0,k>>>0>=m>>>0)return Ge=0,I=Lt,Ge|0;d=n[k+24>>2]|0,l=n[k+12>>2]|0;do if((l|0)==(k|0)){if(o=k+20|0,l=n[o>>2]|0,!l&&(o=k+16|0,l=n[o>>2]|0,!l)){l=0;break}for(;;){if(u=l+20|0,A=n[u>>2]|0,A|0){l=A,o=u;continue}if(u=l+16|0,A=n[u>>2]|0,A)l=A,o=u;else break}n[o>>2]=0}else Ge=n[k+8>>2]|0,n[Ge+12>>2]=l,n[l+8>>2]=Ge;while(!1);do if(d){if(o=n[k+28>>2]|0,u=11436+(o<<2)|0,(k|0)==(n[u>>2]|0)){if(n[u>>2]=l,!l){A=T&~(1<>2]|0)!=(k|0)&1)<<2)>>2]=l,!l){A=T;break}n[l+24>>2]=d,o=n[k+16>>2]|0,o|0&&(n[l+16>>2]=o,n[o+24>>2]=l),o=n[k+20>>2]|0,o&&(n[l+20>>2]=o,n[o+24>>2]=l),A=T}else A=T;while(!1);do if(B>>>0>=16){if(n[k+4>>2]=_|3,n[m+4>>2]=B|1,n[m+B>>2]=B,l=B>>>3,B>>>0<256){u=11172+(l<<1<<2)|0,o=n[2783]|0,l=1<>2]|0):(n[2783]=o|l,l=u,o=u+8|0),n[o>>2]=m,n[l+12>>2]=m,n[m+8>>2]=l,n[m+12>>2]=u;break}if(l=B>>>8,l?B>>>0>16777215?l=31:(He=(l+1048320|0)>>>16&8,Ge=l<>>16&4,Ge=Ge<>>16&2,l=14-(ct|He|l)+(Ge<>>15)|0,l=B>>>(l+7|0)&1|l<<1):l=0,u=11436+(l<<2)|0,n[m+28>>2]=l,o=m+16|0,n[o+4>>2]=0,n[o>>2]=0,o=1<>2]=m,n[m+24>>2]=u,n[m+12>>2]=m,n[m+8>>2]=m;break}for(o=B<<((l|0)==31?0:25-(l>>>1)|0),u=n[u>>2]|0;;){if((n[u+4>>2]&-8|0)==(B|0)){He=97;break}if(A=u+16+(o>>>31<<2)|0,l=n[A>>2]|0,l)o=o<<1,u=l;else{He=96;break}}if((He|0)==96){n[A>>2]=m,n[m+24>>2]=u,n[m+12>>2]=m,n[m+8>>2]=m;break}else if((He|0)==97){He=u+8|0,Ge=n[He>>2]|0,n[Ge+12>>2]=m,n[He>>2]=m,n[m+8>>2]=Ge,n[m+12>>2]=u,n[m+24>>2]=0;break}}else Ge=B+_|0,n[k+4>>2]=Ge|3,Ge=k+Ge+4|0,n[Ge>>2]=n[Ge>>2]|1;while(!1);return Ge=k+8|0,I=Lt,Ge|0}else G=_}else G=_;else G=-1;while(!1);if(u=n[2785]|0,u>>>0>=G>>>0)return l=u-G|0,o=n[2788]|0,l>>>0>15?(Ge=o+G|0,n[2788]=Ge,n[2785]=l,n[Ge+4>>2]=l|1,n[Ge+l>>2]=l,n[o+4>>2]=G|3):(n[2785]=0,n[2788]=0,n[o+4>>2]=u|3,Ge=o+u+4|0,n[Ge>>2]=n[Ge>>2]|1),Ge=o+8|0,I=Lt,Ge|0;if(B=n[2786]|0,B>>>0>G>>>0)return ct=B-G|0,n[2786]=ct,Ge=n[2789]|0,He=Ge+G|0,n[2789]=He,n[He+4>>2]=ct|1,n[Ge+4>>2]=G|3,Ge=Ge+8|0,I=Lt,Ge|0;if(n[2901]|0?o=n[2903]|0:(n[2903]=4096,n[2902]=4096,n[2904]=-1,n[2905]=-1,n[2906]=0,n[2894]=0,o=ae&-16^1431655768,n[ae>>2]=o,n[2901]=o,o=4096),k=G+48|0,T=G+47|0,m=o+T|0,d=0-o|0,_=m&d,_>>>0<=G>>>0||(o=n[2893]|0,o|0&&(M=n[2891]|0,ae=M+_|0,ae>>>0<=M>>>0|ae>>>0>o>>>0)))return Ge=0,I=Lt,Ge|0;e:do if(n[2894]&4)l=0,He=133;else{u=n[2789]|0;t:do if(u){for(A=11580;o=n[A>>2]|0,!(o>>>0<=u>>>0&&(Qe=A+4|0,(o+(n[Qe>>2]|0)|0)>>>0>u>>>0));)if(o=n[A+8>>2]|0,o)A=o;else{He=118;break t}if(l=m-B&d,l>>>0<2147483647)if(o=Vh(l|0)|0,(o|0)==((n[A>>2]|0)+(n[Qe>>2]|0)|0)){if((o|0)!=-1){B=l,m=o,He=135;break e}}else A=o,He=126;else l=0}else He=118;while(!1);do if((He|0)==118)if(u=Vh(0)|0,(u|0)!=-1&&(l=u,We=n[2902]|0,Le=We+-1|0,l=(Le&l|0?(Le+l&0-We)-l|0:0)+_|0,We=n[2891]|0,Le=l+We|0,l>>>0>G>>>0&l>>>0<2147483647)){if(Qe=n[2893]|0,Qe|0&&Le>>>0<=We>>>0|Le>>>0>Qe>>>0){l=0;break}if(o=Vh(l|0)|0,(o|0)==(u|0)){B=l,m=u,He=135;break e}else A=o,He=126}else l=0;while(!1);do if((He|0)==126){if(u=0-l|0,!(k>>>0>l>>>0&(l>>>0<2147483647&(A|0)!=-1)))if((A|0)==-1){l=0;break}else{B=l,m=A,He=135;break e}if(o=n[2903]|0,o=T-l+o&0-o,o>>>0>=2147483647){B=l,m=A,He=135;break e}if((Vh(o|0)|0)==-1){Vh(u|0)|0,l=0;break}else{B=o+l|0,m=A,He=135;break e}}while(!1);n[2894]=n[2894]|4,He=133}while(!1);if((He|0)==133&&_>>>0<2147483647&&(ct=Vh(_|0)|0,Qe=Vh(0)|0,tt=Qe-ct|0,Ze=tt>>>0>(G+40|0)>>>0,!((ct|0)==-1|Ze^1|ct>>>0>>0&((ct|0)!=-1&(Qe|0)!=-1)^1))&&(B=Ze?tt:l,m=ct,He=135),(He|0)==135){l=(n[2891]|0)+B|0,n[2891]=l,l>>>0>(n[2892]|0)>>>0&&(n[2892]=l),T=n[2789]|0;do if(T){for(l=11580;;){if(o=n[l>>2]|0,u=l+4|0,A=n[u>>2]|0,(m|0)==(o+A|0)){He=145;break}if(d=n[l+8>>2]|0,d)l=d;else break}if((He|0)==145&&!(n[l+12>>2]&8|0)&&T>>>0>>0&T>>>0>=o>>>0){n[u>>2]=A+B,Ge=T+8|0,Ge=Ge&7|0?0-Ge&7:0,He=T+Ge|0,Ge=(n[2786]|0)+(B-Ge)|0,n[2789]=He,n[2786]=Ge,n[He+4>>2]=Ge|1,n[He+Ge+4>>2]=40,n[2790]=n[2905];break}for(m>>>0<(n[2787]|0)>>>0&&(n[2787]=m),u=m+B|0,l=11580;;){if((n[l>>2]|0)==(u|0)){He=153;break}if(o=n[l+8>>2]|0,o)l=o;else break}if((He|0)==153&&!(n[l+12>>2]&8|0)){n[l>>2]=m,M=l+4|0,n[M>>2]=(n[M>>2]|0)+B,M=m+8|0,M=m+(M&7|0?0-M&7:0)|0,l=u+8|0,l=u+(l&7|0?0-l&7:0)|0,_=M+G|0,k=l-M-G|0,n[M+4>>2]=G|3;do if((l|0)!=(T|0)){if((l|0)==(n[2788]|0)){Ge=(n[2785]|0)+k|0,n[2785]=Ge,n[2788]=_,n[_+4>>2]=Ge|1,n[_+Ge>>2]=Ge;break}if(o=n[l+4>>2]|0,(o&3|0)==1){B=o&-8,A=o>>>3;e:do if(o>>>0<256)if(o=n[l+8>>2]|0,u=n[l+12>>2]|0,(u|0)==(o|0)){n[2783]=n[2783]&~(1<>2]=u,n[u+8>>2]=o;break}else{m=n[l+24>>2]|0,o=n[l+12>>2]|0;do if((o|0)==(l|0)){if(A=l+16|0,u=A+4|0,o=n[u>>2]|0,!o)if(o=n[A>>2]|0,o)u=A;else{o=0;break}for(;;){if(A=o+20|0,d=n[A>>2]|0,d|0){o=d,u=A;continue}if(A=o+16|0,d=n[A>>2]|0,d)o=d,u=A;else break}n[u>>2]=0}else Ge=n[l+8>>2]|0,n[Ge+12>>2]=o,n[o+8>>2]=Ge;while(!1);if(!m)break;u=n[l+28>>2]|0,A=11436+(u<<2)|0;do if((l|0)!=(n[A>>2]|0)){if(n[m+16+(((n[m+16>>2]|0)!=(l|0)&1)<<2)>>2]=o,!o)break e}else{if(n[A>>2]=o,o|0)break;n[2784]=n[2784]&~(1<>2]=m,u=l+16|0,A=n[u>>2]|0,A|0&&(n[o+16>>2]=A,n[A+24>>2]=o),u=n[u+4>>2]|0,!u)break;n[o+20>>2]=u,n[u+24>>2]=o}while(!1);l=l+B|0,d=B+k|0}else d=k;if(l=l+4|0,n[l>>2]=n[l>>2]&-2,n[_+4>>2]=d|1,n[_+d>>2]=d,l=d>>>3,d>>>0<256){u=11172+(l<<1<<2)|0,o=n[2783]|0,l=1<>2]|0):(n[2783]=o|l,l=u,o=u+8|0),n[o>>2]=_,n[l+12>>2]=_,n[_+8>>2]=l,n[_+12>>2]=u;break}l=d>>>8;do if(!l)l=0;else{if(d>>>0>16777215){l=31;break}He=(l+1048320|0)>>>16&8,Ge=l<>>16&4,Ge=Ge<>>16&2,l=14-(ct|He|l)+(Ge<>>15)|0,l=d>>>(l+7|0)&1|l<<1}while(!1);if(A=11436+(l<<2)|0,n[_+28>>2]=l,o=_+16|0,n[o+4>>2]=0,n[o>>2]=0,o=n[2784]|0,u=1<>2]=_,n[_+24>>2]=A,n[_+12>>2]=_,n[_+8>>2]=_;break}for(o=d<<((l|0)==31?0:25-(l>>>1)|0),u=n[A>>2]|0;;){if((n[u+4>>2]&-8|0)==(d|0)){He=194;break}if(A=u+16+(o>>>31<<2)|0,l=n[A>>2]|0,l)o=o<<1,u=l;else{He=193;break}}if((He|0)==193){n[A>>2]=_,n[_+24>>2]=u,n[_+12>>2]=_,n[_+8>>2]=_;break}else if((He|0)==194){He=u+8|0,Ge=n[He>>2]|0,n[Ge+12>>2]=_,n[He>>2]=_,n[_+8>>2]=Ge,n[_+12>>2]=u,n[_+24>>2]=0;break}}else Ge=(n[2786]|0)+k|0,n[2786]=Ge,n[2789]=_,n[_+4>>2]=Ge|1;while(!1);return Ge=M+8|0,I=Lt,Ge|0}for(l=11580;o=n[l>>2]|0,!(o>>>0<=T>>>0&&(Ge=o+(n[l+4>>2]|0)|0,Ge>>>0>T>>>0));)l=n[l+8>>2]|0;d=Ge+-47|0,o=d+8|0,o=d+(o&7|0?0-o&7:0)|0,d=T+16|0,o=o>>>0>>0?T:o,l=o+8|0,u=m+8|0,u=u&7|0?0-u&7:0,He=m+u|0,u=B+-40-u|0,n[2789]=He,n[2786]=u,n[He+4>>2]=u|1,n[He+u+4>>2]=40,n[2790]=n[2905],u=o+4|0,n[u>>2]=27,n[l>>2]=n[2895],n[l+4>>2]=n[2896],n[l+8>>2]=n[2897],n[l+12>>2]=n[2898],n[2895]=m,n[2896]=B,n[2898]=0,n[2897]=l,l=o+24|0;do He=l,l=l+4|0,n[l>>2]=7;while((He+8|0)>>>0>>0);if((o|0)!=(T|0)){if(m=o-T|0,n[u>>2]=n[u>>2]&-2,n[T+4>>2]=m|1,n[o>>2]=m,l=m>>>3,m>>>0<256){u=11172+(l<<1<<2)|0,o=n[2783]|0,l=1<>2]|0):(n[2783]=o|l,l=u,o=u+8|0),n[o>>2]=T,n[l+12>>2]=T,n[T+8>>2]=l,n[T+12>>2]=u;break}if(l=m>>>8,l?m>>>0>16777215?u=31:(He=(l+1048320|0)>>>16&8,Ge=l<>>16&4,Ge=Ge<>>16&2,u=14-(ct|He|u)+(Ge<>>15)|0,u=m>>>(u+7|0)&1|u<<1):u=0,A=11436+(u<<2)|0,n[T+28>>2]=u,n[T+20>>2]=0,n[d>>2]=0,l=n[2784]|0,o=1<>2]=T,n[T+24>>2]=A,n[T+12>>2]=T,n[T+8>>2]=T;break}for(o=m<<((u|0)==31?0:25-(u>>>1)|0),u=n[A>>2]|0;;){if((n[u+4>>2]&-8|0)==(m|0)){He=216;break}if(A=u+16+(o>>>31<<2)|0,l=n[A>>2]|0,l)o=o<<1,u=l;else{He=215;break}}if((He|0)==215){n[A>>2]=T,n[T+24>>2]=u,n[T+12>>2]=T,n[T+8>>2]=T;break}else if((He|0)==216){He=u+8|0,Ge=n[He>>2]|0,n[Ge+12>>2]=T,n[He>>2]=T,n[T+8>>2]=Ge,n[T+12>>2]=u,n[T+24>>2]=0;break}}}else{Ge=n[2787]|0,(Ge|0)==0|m>>>0>>0&&(n[2787]=m),n[2895]=m,n[2896]=B,n[2898]=0,n[2792]=n[2901],n[2791]=-1,l=0;do Ge=11172+(l<<1<<2)|0,n[Ge+12>>2]=Ge,n[Ge+8>>2]=Ge,l=l+1|0;while((l|0)!=32);Ge=m+8|0,Ge=Ge&7|0?0-Ge&7:0,He=m+Ge|0,Ge=B+-40-Ge|0,n[2789]=He,n[2786]=Ge,n[He+4>>2]=Ge|1,n[He+Ge+4>>2]=40,n[2790]=n[2905]}while(!1);if(l=n[2786]|0,l>>>0>G>>>0)return ct=l-G|0,n[2786]=ct,Ge=n[2789]|0,He=Ge+G|0,n[2789]=He,n[He+4>>2]=ct|1,n[Ge+4>>2]=G|3,Ge=Ge+8|0,I=Lt,Ge|0}return n[(Zy()|0)>>2]=12,Ge=0,I=Lt,Ge|0}function tx(o){o=o|0;var l=0,u=0,A=0,d=0,m=0,B=0,k=0,T=0;if(o){u=o+-8|0,d=n[2787]|0,o=n[o+-4>>2]|0,l=o&-8,T=u+l|0;do if(o&1)k=u,B=u;else{if(A=n[u>>2]|0,!(o&3)||(B=u+(0-A)|0,m=A+l|0,B>>>0>>0))return;if((B|0)==(n[2788]|0)){if(o=T+4|0,l=n[o>>2]|0,(l&3|0)!=3){k=B,l=m;break}n[2785]=m,n[o>>2]=l&-2,n[B+4>>2]=m|1,n[B+m>>2]=m;return}if(u=A>>>3,A>>>0<256)if(o=n[B+8>>2]|0,l=n[B+12>>2]|0,(l|0)==(o|0)){n[2783]=n[2783]&~(1<>2]=l,n[l+8>>2]=o,k=B,l=m;break}d=n[B+24>>2]|0,o=n[B+12>>2]|0;do if((o|0)==(B|0)){if(u=B+16|0,l=u+4|0,o=n[l>>2]|0,!o)if(o=n[u>>2]|0,o)l=u;else{o=0;break}for(;;){if(u=o+20|0,A=n[u>>2]|0,A|0){o=A,l=u;continue}if(u=o+16|0,A=n[u>>2]|0,A)o=A,l=u;else break}n[l>>2]=0}else k=n[B+8>>2]|0,n[k+12>>2]=o,n[o+8>>2]=k;while(!1);if(d){if(l=n[B+28>>2]|0,u=11436+(l<<2)|0,(B|0)==(n[u>>2]|0)){if(n[u>>2]=o,!o){n[2784]=n[2784]&~(1<>2]|0)!=(B|0)&1)<<2)>>2]=o,!o){k=B,l=m;break}n[o+24>>2]=d,l=B+16|0,u=n[l>>2]|0,u|0&&(n[o+16>>2]=u,n[u+24>>2]=o),l=n[l+4>>2]|0,l?(n[o+20>>2]=l,n[l+24>>2]=o,k=B,l=m):(k=B,l=m)}else k=B,l=m}while(!1);if(!(B>>>0>=T>>>0)&&(o=T+4|0,A=n[o>>2]|0,!!(A&1))){if(A&2)n[o>>2]=A&-2,n[k+4>>2]=l|1,n[B+l>>2]=l,d=l;else{if(o=n[2788]|0,(T|0)==(n[2789]|0)){if(T=(n[2786]|0)+l|0,n[2786]=T,n[2789]=k,n[k+4>>2]=T|1,(k|0)!=(o|0))return;n[2788]=0,n[2785]=0;return}if((T|0)==(o|0)){T=(n[2785]|0)+l|0,n[2785]=T,n[2788]=B,n[k+4>>2]=T|1,n[B+T>>2]=T;return}d=(A&-8)+l|0,u=A>>>3;do if(A>>>0<256)if(l=n[T+8>>2]|0,o=n[T+12>>2]|0,(o|0)==(l|0)){n[2783]=n[2783]&~(1<>2]=o,n[o+8>>2]=l;break}else{m=n[T+24>>2]|0,o=n[T+12>>2]|0;do if((o|0)==(T|0)){if(u=T+16|0,l=u+4|0,o=n[l>>2]|0,!o)if(o=n[u>>2]|0,o)l=u;else{u=0;break}for(;;){if(u=o+20|0,A=n[u>>2]|0,A|0){o=A,l=u;continue}if(u=o+16|0,A=n[u>>2]|0,A)o=A,l=u;else break}n[l>>2]=0,u=o}else u=n[T+8>>2]|0,n[u+12>>2]=o,n[o+8>>2]=u,u=o;while(!1);if(m|0){if(o=n[T+28>>2]|0,l=11436+(o<<2)|0,(T|0)==(n[l>>2]|0)){if(n[l>>2]=u,!u){n[2784]=n[2784]&~(1<>2]|0)!=(T|0)&1)<<2)>>2]=u,!u)break;n[u+24>>2]=m,o=T+16|0,l=n[o>>2]|0,l|0&&(n[u+16>>2]=l,n[l+24>>2]=u),o=n[o+4>>2]|0,o|0&&(n[u+20>>2]=o,n[o+24>>2]=u)}}while(!1);if(n[k+4>>2]=d|1,n[B+d>>2]=d,(k|0)==(n[2788]|0)){n[2785]=d;return}}if(o=d>>>3,d>>>0<256){u=11172+(o<<1<<2)|0,l=n[2783]|0,o=1<>2]|0):(n[2783]=l|o,o=u,l=u+8|0),n[l>>2]=k,n[o+12>>2]=k,n[k+8>>2]=o,n[k+12>>2]=u;return}o=d>>>8,o?d>>>0>16777215?o=31:(B=(o+1048320|0)>>>16&8,T=o<>>16&4,T=T<>>16&2,o=14-(m|B|o)+(T<>>15)|0,o=d>>>(o+7|0)&1|o<<1):o=0,A=11436+(o<<2)|0,n[k+28>>2]=o,n[k+20>>2]=0,n[k+16>>2]=0,l=n[2784]|0,u=1<>>1)|0),u=n[A>>2]|0;;){if((n[u+4>>2]&-8|0)==(d|0)){o=73;break}if(A=u+16+(l>>>31<<2)|0,o=n[A>>2]|0,o)l=l<<1,u=o;else{o=72;break}}if((o|0)==72){n[A>>2]=k,n[k+24>>2]=u,n[k+12>>2]=k,n[k+8>>2]=k;break}else if((o|0)==73){B=u+8|0,T=n[B>>2]|0,n[T+12>>2]=k,n[B>>2]=k,n[k+8>>2]=T,n[k+12>>2]=u,n[k+24>>2]=0;break}}else n[2784]=l|u,n[A>>2]=k,n[k+24>>2]=A,n[k+12>>2]=k,n[k+8>>2]=k;while(!1);if(T=(n[2791]|0)+-1|0,n[2791]=T,!T)o=11588;else return;for(;o=n[o>>2]|0,o;)o=o+8|0;n[2791]=-1}}}function LYe(){return 11628}function MYe(o){o=o|0;var l=0,u=0;return l=I,I=I+16|0,u=l,n[u>>2]=HYe(n[o+60>>2]|0)|0,o=rx(hu(6,u|0)|0)|0,I=l,o|0}function t$(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0;G=I,I=I+48|0,_=G+16|0,m=G,d=G+32|0,k=o+28|0,A=n[k>>2]|0,n[d>>2]=A,T=o+20|0,A=(n[T>>2]|0)-A|0,n[d+4>>2]=A,n[d+8>>2]=l,n[d+12>>2]=u,A=A+u|0,B=o+60|0,n[m>>2]=n[B>>2],n[m+4>>2]=d,n[m+8>>2]=2,m=rx(Ma(146,m|0)|0)|0;e:do if((A|0)!=(m|0)){for(l=2;!((m|0)<0);)if(A=A-m|0,We=n[d+4>>2]|0,ae=m>>>0>We>>>0,d=ae?d+8|0:d,l=(ae<<31>>31)+l|0,We=m-(ae?We:0)|0,n[d>>2]=(n[d>>2]|0)+We,ae=d+4|0,n[ae>>2]=(n[ae>>2]|0)-We,n[_>>2]=n[B>>2],n[_+4>>2]=d,n[_+8>>2]=l,m=rx(Ma(146,_|0)|0)|0,(A|0)==(m|0)){M=3;break e}n[o+16>>2]=0,n[k>>2]=0,n[T>>2]=0,n[o>>2]=n[o>>2]|32,(l|0)==2?u=0:u=u-(n[d+4>>2]|0)|0}else M=3;while(!1);return(M|0)==3&&(We=n[o+44>>2]|0,n[o+16>>2]=We+(n[o+48>>2]|0),n[k>>2]=We,n[T>>2]=We),I=G,u|0}function _Ye(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;return d=I,I=I+32|0,m=d,A=d+20|0,n[m>>2]=n[o+60>>2],n[m+4>>2]=0,n[m+8>>2]=l,n[m+12>>2]=A,n[m+16>>2]=u,(rx(La(140,m|0)|0)|0)<0?(n[A>>2]=-1,o=-1):o=n[A>>2]|0,I=d,o|0}function rx(o){return o=o|0,o>>>0>4294963200&&(n[(Zy()|0)>>2]=0-o,o=-1),o|0}function Zy(){return(UYe()|0)+64|0}function UYe(){return m_()|0}function m_(){return 2084}function HYe(o){return o=o|0,o|0}function jYe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;return d=I,I=I+32|0,A=d,n[o+36>>2]=1,!(n[o>>2]&64|0)&&(n[A>>2]=n[o+60>>2],n[A+4>>2]=21523,n[A+8>>2]=d+16,io(54,A|0)|0)&&(s[o+75>>0]=-1),A=t$(o,l,u)|0,I=d,A|0}function r$(o,l){o=o|0,l=l|0;var u=0,A=0;if(u=s[o>>0]|0,A=s[l>>0]|0,!(u<<24>>24)||u<<24>>24!=A<<24>>24)o=A;else{do o=o+1|0,l=l+1|0,u=s[o>>0]|0,A=s[l>>0]|0;while(!(!(u<<24>>24)||u<<24>>24!=A<<24>>24));o=A}return(u&255)-(o&255)|0}function qYe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0;e:do if(!u)o=0;else{for(;A=s[o>>0]|0,d=s[l>>0]|0,A<<24>>24==d<<24>>24;)if(u=u+-1|0,u)o=o+1|0,l=l+1|0;else{o=0;break e}o=(A&255)-(d&255)|0}while(!1);return o|0}function n$(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0;Qe=I,I=I+224|0,M=Qe+120|0,G=Qe+80|0,We=Qe,Le=Qe+136|0,A=G,d=A+40|0;do n[A>>2]=0,A=A+4|0;while((A|0)<(d|0));return n[M>>2]=n[u>>2],(y_(0,l,M,We,G)|0)<0?u=-1:((n[o+76>>2]|0)>-1?ae=GYe(o)|0:ae=0,u=n[o>>2]|0,_=u&32,(s[o+74>>0]|0)<1&&(n[o>>2]=u&-33),A=o+48|0,n[A>>2]|0?u=y_(o,l,M,We,G)|0:(d=o+44|0,m=n[d>>2]|0,n[d>>2]=Le,B=o+28|0,n[B>>2]=Le,k=o+20|0,n[k>>2]=Le,n[A>>2]=80,T=o+16|0,n[T>>2]=Le+80,u=y_(o,l,M,We,G)|0,m&&(ox[n[o+36>>2]&7](o,0,0)|0,u=n[k>>2]|0?u:-1,n[d>>2]=m,n[A>>2]=0,n[T>>2]=0,n[B>>2]=0,n[k>>2]=0)),A=n[o>>2]|0,n[o>>2]=A|_,ae|0&&WYe(o),u=A&32|0?-1:u),I=Qe,u|0}function y_(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0,tt=0,Ze=0,ct=0,He=0,Ge=0,Lt=0,qr=0,fr=0,$t=0,Tr=0,Hr=0,cr=0;cr=I,I=I+64|0,fr=cr+16|0,$t=cr,Lt=cr+24|0,Tr=cr+8|0,Hr=cr+20|0,n[fr>>2]=l,ct=(o|0)!=0,He=Lt+40|0,Ge=He,Lt=Lt+39|0,qr=Tr+4|0,B=0,m=0,M=0;e:for(;;){do if((m|0)>-1)if((B|0)>(2147483647-m|0)){n[(Zy()|0)>>2]=75,m=-1;break}else{m=B+m|0;break}while(!1);if(B=s[l>>0]|0,B<<24>>24)k=l;else{Ze=87;break}t:for(;;){switch(B<<24>>24){case 37:{B=k,Ze=9;break t}case 0:{B=k;break t}default:}tt=k+1|0,n[fr>>2]=tt,B=s[tt>>0]|0,k=tt}t:do if((Ze|0)==9)for(;;){if(Ze=0,(s[k+1>>0]|0)!=37)break t;if(B=B+1|0,k=k+2|0,n[fr>>2]=k,(s[k>>0]|0)==37)Ze=9;else break}while(!1);if(B=B-l|0,ct&&Ss(o,l,B),B|0){l=k;continue}T=k+1|0,B=(s[T>>0]|0)+-48|0,B>>>0<10?(tt=(s[k+2>>0]|0)==36,Qe=tt?B:-1,M=tt?1:M,T=tt?k+3|0:T):Qe=-1,n[fr>>2]=T,B=s[T>>0]|0,k=(B<<24>>24)+-32|0;t:do if(k>>>0<32)for(_=0,G=B;;){if(B=1<>2]=T,B=s[T>>0]|0,k=(B<<24>>24)+-32|0,k>>>0>=32)break;G=B}else _=0;while(!1);if(B<<24>>24==42){if(k=T+1|0,B=(s[k>>0]|0)+-48|0,B>>>0<10&&(s[T+2>>0]|0)==36)n[d+(B<<2)>>2]=10,B=n[A+((s[k>>0]|0)+-48<<3)>>2]|0,M=1,T=T+3|0;else{if(M|0){m=-1;break}ct?(M=(n[u>>2]|0)+3&-4,B=n[M>>2]|0,n[u>>2]=M+4,M=0,T=k):(B=0,M=0,T=k)}n[fr>>2]=T,tt=(B|0)<0,B=tt?0-B|0:B,_=tt?_|8192:_}else{if(B=i$(fr)|0,(B|0)<0){m=-1;break}T=n[fr>>2]|0}do if((s[T>>0]|0)==46){if((s[T+1>>0]|0)!=42){n[fr>>2]=T+1,k=i$(fr)|0,T=n[fr>>2]|0;break}if(G=T+2|0,k=(s[G>>0]|0)+-48|0,k>>>0<10&&(s[T+3>>0]|0)==36){n[d+(k<<2)>>2]=10,k=n[A+((s[G>>0]|0)+-48<<3)>>2]|0,T=T+4|0,n[fr>>2]=T;break}if(M|0){m=-1;break e}ct?(tt=(n[u>>2]|0)+3&-4,k=n[tt>>2]|0,n[u>>2]=tt+4):k=0,n[fr>>2]=G,T=G}else k=-1;while(!1);for(Le=0;;){if(((s[T>>0]|0)+-65|0)>>>0>57){m=-1;break e}if(tt=T+1|0,n[fr>>2]=tt,G=s[(s[T>>0]|0)+-65+(5178+(Le*58|0))>>0]|0,ae=G&255,(ae+-1|0)>>>0<8)Le=ae,T=tt;else break}if(!(G<<24>>24)){m=-1;break}We=(Qe|0)>-1;do if(G<<24>>24==19)if(We){m=-1;break e}else Ze=49;else{if(We){n[d+(Qe<<2)>>2]=ae,We=A+(Qe<<3)|0,Qe=n[We+4>>2]|0,Ze=$t,n[Ze>>2]=n[We>>2],n[Ze+4>>2]=Qe,Ze=49;break}if(!ct){m=0;break e}s$($t,ae,u)}while(!1);if((Ze|0)==49&&(Ze=0,!ct)){B=0,l=tt;continue}T=s[T>>0]|0,T=(Le|0)!=0&(T&15|0)==3?T&-33:T,We=_&-65537,Qe=_&8192|0?We:_;t:do switch(T|0){case 110:switch((Le&255)<<24>>24){case 0:{n[n[$t>>2]>>2]=m,B=0,l=tt;continue e}case 1:{n[n[$t>>2]>>2]=m,B=0,l=tt;continue e}case 2:{B=n[$t>>2]|0,n[B>>2]=m,n[B+4>>2]=((m|0)<0)<<31>>31,B=0,l=tt;continue e}case 3:{a[n[$t>>2]>>1]=m,B=0,l=tt;continue e}case 4:{s[n[$t>>2]>>0]=m,B=0,l=tt;continue e}case 6:{n[n[$t>>2]>>2]=m,B=0,l=tt;continue e}case 7:{B=n[$t>>2]|0,n[B>>2]=m,n[B+4>>2]=((m|0)<0)<<31>>31,B=0,l=tt;continue e}default:{B=0,l=tt;continue e}}case 112:{T=120,k=k>>>0>8?k:8,l=Qe|8,Ze=61;break}case 88:case 120:{l=Qe,Ze=61;break}case 111:{T=$t,l=n[T>>2]|0,T=n[T+4>>2]|0,ae=VYe(l,T,He)|0,We=Ge-ae|0,_=0,G=5642,k=(Qe&8|0)==0|(k|0)>(We|0)?k:We+1|0,We=Qe,Ze=67;break}case 105:case 100:if(T=$t,l=n[T>>2]|0,T=n[T+4>>2]|0,(T|0)<0){l=nx(0,0,l|0,T|0)|0,T=Ee,_=$t,n[_>>2]=l,n[_+4>>2]=T,_=1,G=5642,Ze=66;break t}else{_=(Qe&2049|0)!=0&1,G=Qe&2048|0?5643:Qe&1|0?5644:5642,Ze=66;break t}case 117:{T=$t,_=0,G=5642,l=n[T>>2]|0,T=n[T+4>>2]|0,Ze=66;break}case 99:{s[Lt>>0]=n[$t>>2],l=Lt,_=0,G=5642,ae=He,T=1,k=We;break}case 109:{T=KYe(n[(Zy()|0)>>2]|0)|0,Ze=71;break}case 115:{T=n[$t>>2]|0,T=T|0?T:5652,Ze=71;break}case 67:{n[Tr>>2]=n[$t>>2],n[qr>>2]=0,n[$t>>2]=Tr,ae=-1,T=Tr,Ze=75;break}case 83:{l=n[$t>>2]|0,k?(ae=k,T=l,Ze=75):(Ms(o,32,B,0,Qe),l=0,Ze=84);break}case 65:case 71:case 70:case 69:case 97:case 103:case 102:case 101:{B=zYe(o,+E[$t>>3],B,k,Qe,T)|0,l=tt;continue e}default:_=0,G=5642,ae=He,T=k,k=Qe}while(!1);t:do if((Ze|0)==61)Qe=$t,Le=n[Qe>>2]|0,Qe=n[Qe+4>>2]|0,ae=YYe(Le,Qe,He,T&32)|0,G=(l&8|0)==0|(Le|0)==0&(Qe|0)==0,_=G?0:2,G=G?5642:5642+(T>>4)|0,We=l,l=Le,T=Qe,Ze=67;else if((Ze|0)==66)ae=Xy(l,T,He)|0,We=Qe,Ze=67;else if((Ze|0)==71)Ze=0,Qe=JYe(T,0,k)|0,Le=(Qe|0)==0,l=T,_=0,G=5642,ae=Le?T+k|0:Qe,T=Le?k:Qe-T|0,k=We;else if((Ze|0)==75){for(Ze=0,G=T,l=0,k=0;_=n[G>>2]|0,!(!_||(k=o$(Hr,_)|0,(k|0)<0|k>>>0>(ae-l|0)>>>0));)if(l=k+l|0,ae>>>0>l>>>0)G=G+4|0;else break;if((k|0)<0){m=-1;break e}if(Ms(o,32,B,l,Qe),!l)l=0,Ze=84;else for(_=0;;){if(k=n[T>>2]|0,!k){Ze=84;break t}if(k=o$(Hr,k)|0,_=k+_|0,(_|0)>(l|0)){Ze=84;break t}if(Ss(o,Hr,k),_>>>0>=l>>>0){Ze=84;break}else T=T+4|0}}while(!1);if((Ze|0)==67)Ze=0,T=(l|0)!=0|(T|0)!=0,Qe=(k|0)!=0|T,T=((T^1)&1)+(Ge-ae)|0,l=Qe?ae:He,ae=He,T=Qe?(k|0)>(T|0)?k:T:k,k=(k|0)>-1?We&-65537:We;else if((Ze|0)==84){Ze=0,Ms(o,32,B,l,Qe^8192),B=(B|0)>(l|0)?B:l,l=tt;continue}Le=ae-l|0,We=(T|0)<(Le|0)?Le:T,Qe=We+_|0,B=(B|0)<(Qe|0)?Qe:B,Ms(o,32,B,Qe,k),Ss(o,G,_),Ms(o,48,B,Qe,k^65536),Ms(o,48,We,Le,0),Ss(o,l,Le),Ms(o,32,B,Qe,k^8192),l=tt}e:do if((Ze|0)==87&&!o)if(!M)m=0;else{for(m=1;l=n[d+(m<<2)>>2]|0,!!l;)if(s$(A+(m<<3)|0,l,u),m=m+1|0,(m|0)>=10){m=1;break e}for(;;){if(n[d+(m<<2)>>2]|0){m=-1;break e}if(m=m+1|0,(m|0)>=10){m=1;break}}}while(!1);return I=cr,m|0}function GYe(o){return o=o|0,0}function WYe(o){o=o|0}function Ss(o,l,u){o=o|0,l=l|0,u=u|0,n[o>>2]&32||sVe(l,u,o)|0}function i$(o){o=o|0;var l=0,u=0,A=0;if(u=n[o>>2]|0,A=(s[u>>0]|0)+-48|0,A>>>0<10){l=0;do l=A+(l*10|0)|0,u=u+1|0,n[o>>2]=u,A=(s[u>>0]|0)+-48|0;while(A>>>0<10)}else l=0;return l|0}function s$(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;e:do if(l>>>0<=20)do switch(l|0){case 9:{A=(n[u>>2]|0)+3&-4,l=n[A>>2]|0,n[u>>2]=A+4,n[o>>2]=l;break e}case 10:{A=(n[u>>2]|0)+3&-4,l=n[A>>2]|0,n[u>>2]=A+4,A=o,n[A>>2]=l,n[A+4>>2]=((l|0)<0)<<31>>31;break e}case 11:{A=(n[u>>2]|0)+3&-4,l=n[A>>2]|0,n[u>>2]=A+4,A=o,n[A>>2]=l,n[A+4>>2]=0;break e}case 12:{A=(n[u>>2]|0)+7&-8,l=A,d=n[l>>2]|0,l=n[l+4>>2]|0,n[u>>2]=A+8,A=o,n[A>>2]=d,n[A+4>>2]=l;break e}case 13:{d=(n[u>>2]|0)+3&-4,A=n[d>>2]|0,n[u>>2]=d+4,A=(A&65535)<<16>>16,d=o,n[d>>2]=A,n[d+4>>2]=((A|0)<0)<<31>>31;break e}case 14:{d=(n[u>>2]|0)+3&-4,A=n[d>>2]|0,n[u>>2]=d+4,d=o,n[d>>2]=A&65535,n[d+4>>2]=0;break e}case 15:{d=(n[u>>2]|0)+3&-4,A=n[d>>2]|0,n[u>>2]=d+4,A=(A&255)<<24>>24,d=o,n[d>>2]=A,n[d+4>>2]=((A|0)<0)<<31>>31;break e}case 16:{d=(n[u>>2]|0)+3&-4,A=n[d>>2]|0,n[u>>2]=d+4,d=o,n[d>>2]=A&255,n[d+4>>2]=0;break e}case 17:{d=(n[u>>2]|0)+7&-8,m=+E[d>>3],n[u>>2]=d+8,E[o>>3]=m;break e}case 18:{d=(n[u>>2]|0)+7&-8,m=+E[d>>3],n[u>>2]=d+8,E[o>>3]=m;break e}default:break e}while(!1);while(!1)}function YYe(o,l,u,A){if(o=o|0,l=l|0,u=u|0,A=A|0,!((o|0)==0&(l|0)==0))do u=u+-1|0,s[u>>0]=c[5694+(o&15)>>0]|0|A,o=ix(o|0,l|0,4)|0,l=Ee;while(!((o|0)==0&(l|0)==0));return u|0}function VYe(o,l,u){if(o=o|0,l=l|0,u=u|0,!((o|0)==0&(l|0)==0))do u=u+-1|0,s[u>>0]=o&7|48,o=ix(o|0,l|0,3)|0,l=Ee;while(!((o|0)==0&(l|0)==0));return u|0}function Xy(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;if(l>>>0>0|(l|0)==0&o>>>0>4294967295){for(;A=w_(o|0,l|0,10,0)|0,u=u+-1|0,s[u>>0]=A&255|48,A=o,o=C_(o|0,l|0,10,0)|0,l>>>0>9|(l|0)==9&A>>>0>4294967295;)l=Ee;l=o}else l=o;if(l)for(;u=u+-1|0,s[u>>0]=(l>>>0)%10|0|48,!(l>>>0<10);)l=(l>>>0)/10|0;return u|0}function KYe(o){return o=o|0,tVe(o,n[(eVe()|0)+188>>2]|0)|0}function JYe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;m=l&255,A=(u|0)!=0;e:do if(A&(o&3|0)!=0)for(d=l&255;;){if((s[o>>0]|0)==d<<24>>24){B=6;break e}if(o=o+1|0,u=u+-1|0,A=(u|0)!=0,!(A&(o&3|0)!=0)){B=5;break}}else B=5;while(!1);(B|0)==5&&(A?B=6:u=0);e:do if((B|0)==6&&(d=l&255,(s[o>>0]|0)!=d<<24>>24)){A=_e(m,16843009)|0;t:do if(u>>>0>3){for(;m=n[o>>2]^A,!((m&-2139062144^-2139062144)&m+-16843009|0);)if(o=o+4|0,u=u+-4|0,u>>>0<=3){B=11;break t}}else B=11;while(!1);if((B|0)==11&&!u){u=0;break}for(;;){if((s[o>>0]|0)==d<<24>>24)break e;if(o=o+1|0,u=u+-1|0,!u){u=0;break}}}while(!1);return(u|0?o:0)|0}function Ms(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0;if(B=I,I=I+256|0,m=B,(u|0)>(A|0)&(d&73728|0)==0){if(d=u-A|0,eE(m|0,l|0,(d>>>0<256?d:256)|0)|0,d>>>0>255){l=u-A|0;do Ss(o,m,256),d=d+-256|0;while(d>>>0>255);d=l&255}Ss(o,m,d)}I=B}function o$(o,l){return o=o|0,l=l|0,o?o=XYe(o,l,0)|0:o=0,o|0}function zYe(o,l,u,A,d,m){o=o|0,l=+l,u=u|0,A=A|0,d=d|0,m=m|0;var B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0,Qe=0,tt=0,Ze=0,ct=0,He=0,Ge=0,Lt=0,qr=0,fr=0,$t=0,Tr=0,Hr=0,cr=0,Hn=0;Hn=I,I=I+560|0,T=Hn+8|0,tt=Hn,cr=Hn+524|0,Hr=cr,_=Hn+512|0,n[tt>>2]=0,Tr=_+12|0,a$(l)|0,(Ee|0)<0?(l=-l,fr=1,qr=5659):(fr=(d&2049|0)!=0&1,qr=d&2048|0?5662:d&1|0?5665:5660),a$(l)|0,$t=Ee&2146435072;do if($t>>>0<2146435072|($t|0)==2146435072&!1){if(We=+ZYe(l,tt)*2,B=We!=0,B&&(n[tt>>2]=(n[tt>>2]|0)+-1),ct=m|32,(ct|0)==97){Le=m&32,ae=Le|0?qr+9|0:qr,G=fr|2,B=12-A|0;do if(A>>>0>11|(B|0)==0)l=We;else{l=8;do B=B+-1|0,l=l*16;while(B|0);if((s[ae>>0]|0)==45){l=-(l+(-We-l));break}else{l=We+l-l;break}}while(!1);k=n[tt>>2]|0,B=(k|0)<0?0-k|0:k,B=Xy(B,((B|0)<0)<<31>>31,Tr)|0,(B|0)==(Tr|0)&&(B=_+11|0,s[B>>0]=48),s[B+-1>>0]=(k>>31&2)+43,M=B+-2|0,s[M>>0]=m+15,_=(A|0)<1,T=(d&8|0)==0,B=cr;do $t=~~l,k=B+1|0,s[B>>0]=c[5694+$t>>0]|Le,l=(l-+($t|0))*16,(k-Hr|0)==1&&!(T&(_&l==0))?(s[k>>0]=46,B=B+2|0):B=k;while(l!=0);$t=B-Hr|0,Hr=Tr-M|0,Tr=(A|0)!=0&($t+-2|0)<(A|0)?A+2|0:$t,B=Hr+G+Tr|0,Ms(o,32,u,B,d),Ss(o,ae,G),Ms(o,48,u,B,d^65536),Ss(o,cr,$t),Ms(o,48,Tr-$t|0,0,0),Ss(o,M,Hr),Ms(o,32,u,B,d^8192);break}k=(A|0)<0?6:A,B?(B=(n[tt>>2]|0)+-28|0,n[tt>>2]=B,l=We*268435456):(l=We,B=n[tt>>2]|0),$t=(B|0)<0?T:T+288|0,T=$t;do Ge=~~l>>>0,n[T>>2]=Ge,T=T+4|0,l=(l-+(Ge>>>0))*1e9;while(l!=0);if((B|0)>0)for(_=$t,G=T;;){if(M=(B|0)<29?B:29,B=G+-4|0,B>>>0>=_>>>0){T=0;do He=p$(n[B>>2]|0,0,M|0)|0,He=I_(He|0,Ee|0,T|0,0)|0,Ge=Ee,Ze=w_(He|0,Ge|0,1e9,0)|0,n[B>>2]=Ze,T=C_(He|0,Ge|0,1e9,0)|0,B=B+-4|0;while(B>>>0>=_>>>0);T&&(_=_+-4|0,n[_>>2]=T)}for(T=G;!(T>>>0<=_>>>0);)if(B=T+-4|0,!(n[B>>2]|0))T=B;else break;if(B=(n[tt>>2]|0)-M|0,n[tt>>2]=B,(B|0)>0)G=T;else break}else _=$t;if((B|0)<0){A=((k+25|0)/9|0)+1|0,Qe=(ct|0)==102;do{if(Le=0-B|0,Le=(Le|0)<9?Le:9,_>>>0>>0){M=(1<>>Le,ae=0,B=_;do Ge=n[B>>2]|0,n[B>>2]=(Ge>>>Le)+ae,ae=_e(Ge&M,G)|0,B=B+4|0;while(B>>>0>>0);B=n[_>>2]|0?_:_+4|0,ae?(n[T>>2]=ae,_=B,B=T+4|0):(_=B,B=T)}else _=n[_>>2]|0?_:_+4|0,B=T;T=Qe?$t:_,T=(B-T>>2|0)>(A|0)?T+(A<<2)|0:B,B=(n[tt>>2]|0)+Le|0,n[tt>>2]=B}while((B|0)<0);B=_,A=T}else B=_,A=T;if(Ge=$t,B>>>0>>0){if(T=(Ge-B>>2)*9|0,M=n[B>>2]|0,M>>>0>=10){_=10;do _=_*10|0,T=T+1|0;while(M>>>0>=_>>>0)}}else T=0;if(Qe=(ct|0)==103,Ze=(k|0)!=0,_=k-((ct|0)!=102?T:0)+((Ze&Qe)<<31>>31)|0,(_|0)<(((A-Ge>>2)*9|0)+-9|0)){if(_=_+9216|0,Le=$t+4+(((_|0)/9|0)+-1024<<2)|0,_=((_|0)%9|0)+1|0,(_|0)<9){M=10;do M=M*10|0,_=_+1|0;while((_|0)!=9)}else M=10;if(G=n[Le>>2]|0,ae=(G>>>0)%(M>>>0)|0,_=(Le+4|0)==(A|0),_&(ae|0)==0)_=Le;else if(We=((G>>>0)/(M>>>0)|0)&1|0?9007199254740994:9007199254740992,He=(M|0)/2|0,l=ae>>>0>>0?.5:_&(ae|0)==(He|0)?1:1.5,fr&&(He=(s[qr>>0]|0)==45,l=He?-l:l,We=He?-We:We),_=G-ae|0,n[Le>>2]=_,We+l!=We){if(He=_+M|0,n[Le>>2]=He,He>>>0>999999999)for(T=Le;_=T+-4|0,n[T>>2]=0,_>>>0>>0&&(B=B+-4|0,n[B>>2]=0),He=(n[_>>2]|0)+1|0,n[_>>2]=He,He>>>0>999999999;)T=_;else _=Le;if(T=(Ge-B>>2)*9|0,G=n[B>>2]|0,G>>>0>=10){M=10;do M=M*10|0,T=T+1|0;while(G>>>0>=M>>>0)}}else _=Le;_=_+4|0,_=A>>>0>_>>>0?_:A,He=B}else _=A,He=B;for(ct=_;;){if(ct>>>0<=He>>>0){tt=0;break}if(B=ct+-4|0,!(n[B>>2]|0))ct=B;else{tt=1;break}}A=0-T|0;do if(Qe)if(B=((Ze^1)&1)+k|0,(B|0)>(T|0)&(T|0)>-5?(M=m+-1|0,k=B+-1-T|0):(M=m+-2|0,k=B+-1|0),B=d&8,B)Le=B;else{if(tt&&(Lt=n[ct+-4>>2]|0,(Lt|0)!=0))if((Lt>>>0)%10|0)_=0;else{_=0,B=10;do B=B*10|0,_=_+1|0;while(!((Lt>>>0)%(B>>>0)|0|0))}else _=9;if(B=((ct-Ge>>2)*9|0)+-9|0,(M|32|0)==102){Le=B-_|0,Le=(Le|0)>0?Le:0,k=(k|0)<(Le|0)?k:Le,Le=0;break}else{Le=B+T-_|0,Le=(Le|0)>0?Le:0,k=(k|0)<(Le|0)?k:Le,Le=0;break}}else M=m,Le=d&8;while(!1);if(Qe=k|Le,G=(Qe|0)!=0&1,ae=(M|32|0)==102,ae)Ze=0,B=(T|0)>0?T:0;else{if(B=(T|0)<0?A:T,B=Xy(B,((B|0)<0)<<31>>31,Tr)|0,_=Tr,(_-B|0)<2)do B=B+-1|0,s[B>>0]=48;while((_-B|0)<2);s[B+-1>>0]=(T>>31&2)+43,B=B+-2|0,s[B>>0]=M,Ze=B,B=_-B|0}if(B=fr+1+k+G+B|0,Ms(o,32,u,B,d),Ss(o,qr,fr),Ms(o,48,u,B,d^65536),ae){M=He>>>0>$t>>>0?$t:He,Le=cr+9|0,G=Le,ae=cr+8|0,_=M;do{if(T=Xy(n[_>>2]|0,0,Le)|0,(_|0)==(M|0))(T|0)==(Le|0)&&(s[ae>>0]=48,T=ae);else if(T>>>0>cr>>>0){eE(cr|0,48,T-Hr|0)|0;do T=T+-1|0;while(T>>>0>cr>>>0)}Ss(o,T,G-T|0),_=_+4|0}while(_>>>0<=$t>>>0);if(Qe|0&&Ss(o,5710,1),_>>>0>>0&(k|0)>0)for(;;){if(T=Xy(n[_>>2]|0,0,Le)|0,T>>>0>cr>>>0){eE(cr|0,48,T-Hr|0)|0;do T=T+-1|0;while(T>>>0>cr>>>0)}if(Ss(o,T,(k|0)<9?k:9),_=_+4|0,T=k+-9|0,_>>>0>>0&(k|0)>9)k=T;else{k=T;break}}Ms(o,48,k+9|0,9,0)}else{if(Qe=tt?ct:He+4|0,(k|0)>-1){tt=cr+9|0,Le=(Le|0)==0,A=tt,G=0-Hr|0,ae=cr+8|0,M=He;do{T=Xy(n[M>>2]|0,0,tt)|0,(T|0)==(tt|0)&&(s[ae>>0]=48,T=ae);do if((M|0)==(He|0)){if(_=T+1|0,Ss(o,T,1),Le&(k|0)<1){T=_;break}Ss(o,5710,1),T=_}else{if(T>>>0<=cr>>>0)break;eE(cr|0,48,T+G|0)|0;do T=T+-1|0;while(T>>>0>cr>>>0)}while(!1);Hr=A-T|0,Ss(o,T,(k|0)>(Hr|0)?Hr:k),k=k-Hr|0,M=M+4|0}while(M>>>0>>0&(k|0)>-1)}Ms(o,48,k+18|0,18,0),Ss(o,Ze,Tr-Ze|0)}Ms(o,32,u,B,d^8192)}else cr=(m&32|0)!=0,B=fr+3|0,Ms(o,32,u,B,d&-65537),Ss(o,qr,fr),Ss(o,l!=l|!1?cr?5686:5690:cr?5678:5682,3),Ms(o,32,u,B,d^8192);while(!1);return I=Hn,((B|0)<(u|0)?u:B)|0}function a$(o){o=+o;var l=0;return E[S>>3]=o,l=n[S>>2]|0,Ee=n[S+4>>2]|0,l|0}function ZYe(o,l){return o=+o,l=l|0,+ +l$(o,l)}function l$(o,l){o=+o,l=l|0;var u=0,A=0,d=0;switch(E[S>>3]=o,u=n[S>>2]|0,A=n[S+4>>2]|0,d=ix(u|0,A|0,52)|0,d&2047){case 0:{o!=0?(o=+l$(o*18446744073709552e3,l),u=(n[l>>2]|0)+-64|0):u=0,n[l>>2]=u;break}case 2047:break;default:n[l>>2]=(d&2047)+-1022,n[S>>2]=u,n[S+4>>2]=A&-2146435073|1071644672,o=+E[S>>3]}return+o}function XYe(o,l,u){o=o|0,l=l|0,u=u|0;do if(o){if(l>>>0<128){s[o>>0]=l,o=1;break}if(!(n[n[($Ye()|0)+188>>2]>>2]|0))if((l&-128|0)==57216){s[o>>0]=l,o=1;break}else{n[(Zy()|0)>>2]=84,o=-1;break}if(l>>>0<2048){s[o>>0]=l>>>6|192,s[o+1>>0]=l&63|128,o=2;break}if(l>>>0<55296|(l&-8192|0)==57344){s[o>>0]=l>>>12|224,s[o+1>>0]=l>>>6&63|128,s[o+2>>0]=l&63|128,o=3;break}if((l+-65536|0)>>>0<1048576){s[o>>0]=l>>>18|240,s[o+1>>0]=l>>>12&63|128,s[o+2>>0]=l>>>6&63|128,s[o+3>>0]=l&63|128,o=4;break}else{n[(Zy()|0)>>2]=84,o=-1;break}}else o=1;while(!1);return o|0}function $Ye(){return m_()|0}function eVe(){return m_()|0}function tVe(o,l){o=o|0,l=l|0;var u=0,A=0;for(A=0;;){if((c[5712+A>>0]|0)==(o|0)){o=2;break}if(u=A+1|0,(u|0)==87){u=5800,A=87,o=5;break}else A=u}if((o|0)==2&&(A?(u=5800,o=5):u=5800),(o|0)==5)for(;;){do o=u,u=u+1|0;while(s[o>>0]|0);if(A=A+-1|0,A)o=5;else break}return rVe(u,n[l+20>>2]|0)|0}function rVe(o,l){return o=o|0,l=l|0,nVe(o,l)|0}function nVe(o,l){return o=o|0,l=l|0,l?l=iVe(n[l>>2]|0,n[l+4>>2]|0,o)|0:l=0,(l|0?l:o)|0}function iVe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0;ae=(n[o>>2]|0)+1794895138|0,m=pd(n[o+8>>2]|0,ae)|0,A=pd(n[o+12>>2]|0,ae)|0,d=pd(n[o+16>>2]|0,ae)|0;e:do if(m>>>0>>2>>>0&&(G=l-(m<<2)|0,A>>>0>>0&d>>>0>>0)&&!((d|A)&3|0)){for(G=A>>>2,M=d>>>2,_=0;;){if(k=m>>>1,T=_+k|0,B=T<<1,d=B+G|0,A=pd(n[o+(d<<2)>>2]|0,ae)|0,d=pd(n[o+(d+1<<2)>>2]|0,ae)|0,!(d>>>0>>0&A>>>0<(l-d|0)>>>0)){A=0;break e}if(s[o+(d+A)>>0]|0){A=0;break e}if(A=r$(u,o+d|0)|0,!A)break;if(A=(A|0)<0,(m|0)==1){A=0;break e}else _=A?_:T,m=A?k:m-k|0}A=B+M|0,d=pd(n[o+(A<<2)>>2]|0,ae)|0,A=pd(n[o+(A+1<<2)>>2]|0,ae)|0,A>>>0>>0&d>>>0<(l-A|0)>>>0?A=s[o+(A+d)>>0]|0?0:o+A|0:A=0}else A=0;while(!1);return A|0}function pd(o,l){o=o|0,l=l|0;var u=0;return u=d$(o|0)|0,(l|0?u:o)|0}function sVe(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0,k=0;A=u+16|0,d=n[A>>2]|0,d?m=5:oVe(u)|0?A=0:(d=n[A>>2]|0,m=5);e:do if((m|0)==5){if(k=u+20|0,B=n[k>>2]|0,A=B,(d-B|0)>>>0>>0){A=ox[n[u+36>>2]&7](u,o,l)|0;break}t:do if((s[u+75>>0]|0)>-1){for(B=l;;){if(!B){m=0,d=o;break t}if(d=B+-1|0,(s[o+d>>0]|0)==10)break;B=d}if(A=ox[n[u+36>>2]&7](u,o,B)|0,A>>>0>>0)break e;m=B,d=o+B|0,l=l-B|0,A=n[k>>2]|0}else m=0,d=o;while(!1);Qr(A|0,d|0,l|0)|0,n[k>>2]=(n[k>>2]|0)+l,A=m+l|0}while(!1);return A|0}function oVe(o){o=o|0;var l=0,u=0;return l=o+74|0,u=s[l>>0]|0,s[l>>0]=u+255|u,l=n[o>>2]|0,l&8?(n[o>>2]=l|32,o=-1):(n[o+8>>2]=0,n[o+4>>2]=0,u=n[o+44>>2]|0,n[o+28>>2]=u,n[o+20>>2]=u,n[o+16>>2]=u+(n[o+48>>2]|0),o=0),o|0}function $n(o,l){o=y(o),l=y(l);var u=0,A=0;u=c$(o)|0;do if((u&2147483647)>>>0<=2139095040){if(A=c$(l)|0,(A&2147483647)>>>0<=2139095040)if((A^u|0)<0){o=(u|0)<0?l:o;break}else{o=o>2]=o,n[S>>2]|0|0}function hd(o,l){o=y(o),l=y(l);var u=0,A=0;u=u$(o)|0;do if((u&2147483647)>>>0<=2139095040){if(A=u$(l)|0,(A&2147483647)>>>0<=2139095040)if((A^u|0)<0){o=(u|0)<0?o:l;break}else{o=o>2]=o,n[S>>2]|0|0}function E_(o,l){o=y(o),l=y(l);var u=0,A=0,d=0,m=0,B=0,k=0,T=0,_=0;m=(h[S>>2]=o,n[S>>2]|0),k=(h[S>>2]=l,n[S>>2]|0),u=m>>>23&255,B=k>>>23&255,T=m&-2147483648,d=k<<1;e:do if(d|0&&!((u|0)==255|((aVe(l)|0)&2147483647)>>>0>2139095040)){if(A=m<<1,A>>>0<=d>>>0)return l=y(o*y(0)),y((A|0)==(d|0)?l:o);if(u)A=m&8388607|8388608;else{if(u=m<<9,(u|0)>-1){A=u,u=0;do u=u+-1|0,A=A<<1;while((A|0)>-1)}else u=0;A=m<<1-u}if(B)k=k&8388607|8388608;else{if(m=k<<9,(m|0)>-1){d=0;do d=d+-1|0,m=m<<1;while((m|0)>-1)}else d=0;B=d,k=k<<1-d}d=A-k|0,m=(d|0)>-1;t:do if((u|0)>(B|0)){for(;;){if(m)if(d)A=d;else break;if(A=A<<1,u=u+-1|0,d=A-k|0,m=(d|0)>-1,(u|0)<=(B|0))break t}l=y(o*y(0));break e}while(!1);if(m)if(d)A=d;else{l=y(o*y(0));break}if(A>>>0<8388608)do A=A<<1,u=u+-1|0;while(A>>>0<8388608);(u|0)>0?u=A+-8388608|u<<23:u=A>>>(1-u|0),l=(n[S>>2]=u|T,y(h[S>>2]))}else _=3;while(!1);return(_|0)==3&&(l=y(o*l),l=y(l/l)),y(l)}function aVe(o){return o=y(o),h[S>>2]=o,n[S>>2]|0|0}function lVe(o,l){return o=o|0,l=l|0,n$(n[582]|0,o,l)|0}function sn(o){o=o|0,Nt()}function $y(o){o=o|0}function cVe(o,l){return o=o|0,l=l|0,0}function uVe(o){return o=o|0,(f$(o+4|0)|0)==-1?(op[n[(n[o>>2]|0)+8>>2]&127](o),o=1):o=0,o|0}function f$(o){o=o|0;var l=0;return l=n[o>>2]|0,n[o>>2]=l+-1,l+-1|0}function Yh(o){o=o|0,uVe(o)|0&&fVe(o)}function fVe(o){o=o|0;var l=0;l=o+8|0,n[l>>2]|0&&(f$(l)|0)!=-1||op[n[(n[o>>2]|0)+16>>2]&127](o)}function Jt(o){o=o|0;var l=0;for(l=o|0?o:1;o=ex(l)|0,!(o|0);){if(o=pVe()|0,!o){o=0;break}D$[o&0]()}return o|0}function A$(o){return o=o|0,Jt(o)|0}function yt(o){o=o|0,tx(o)}function AVe(o){o=o|0,(s[o+11>>0]|0)<0&&yt(n[o>>2]|0)}function pVe(){var o=0;return o=n[2923]|0,n[2923]=o+0,o|0}function hVe(){}function nx(o,l,u,A){return o=o|0,l=l|0,u=u|0,A=A|0,A=l-A-(u>>>0>o>>>0|0)>>>0,Ee=A,o-u>>>0|0|0}function I_(o,l,u,A){return o=o|0,l=l|0,u=u|0,A=A|0,u=o+u>>>0,Ee=l+A+(u>>>0>>0|0)>>>0,u|0|0}function eE(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0,B=0;if(m=o+u|0,l=l&255,(u|0)>=67){for(;o&3;)s[o>>0]=l,o=o+1|0;for(A=m&-4|0,d=A-64|0,B=l|l<<8|l<<16|l<<24;(o|0)<=(d|0);)n[o>>2]=B,n[o+4>>2]=B,n[o+8>>2]=B,n[o+12>>2]=B,n[o+16>>2]=B,n[o+20>>2]=B,n[o+24>>2]=B,n[o+28>>2]=B,n[o+32>>2]=B,n[o+36>>2]=B,n[o+40>>2]=B,n[o+44>>2]=B,n[o+48>>2]=B,n[o+52>>2]=B,n[o+56>>2]=B,n[o+60>>2]=B,o=o+64|0;for(;(o|0)<(A|0);)n[o>>2]=B,o=o+4|0}for(;(o|0)<(m|0);)s[o>>0]=l,o=o+1|0;return m-u|0}function p$(o,l,u){return o=o|0,l=l|0,u=u|0,(u|0)<32?(Ee=l<>>32-u,o<>>u,o>>>u|(l&(1<>>u-32|0)}function Qr(o,l,u){o=o|0,l=l|0,u=u|0;var A=0,d=0,m=0;if((u|0)>=8192)return MA(o|0,l|0,u|0)|0;if(m=o|0,d=o+u|0,(o&3)==(l&3)){for(;o&3;){if(!u)return m|0;s[o>>0]=s[l>>0]|0,o=o+1|0,l=l+1|0,u=u-1|0}for(u=d&-4|0,A=u-64|0;(o|0)<=(A|0);)n[o>>2]=n[l>>2],n[o+4>>2]=n[l+4>>2],n[o+8>>2]=n[l+8>>2],n[o+12>>2]=n[l+12>>2],n[o+16>>2]=n[l+16>>2],n[o+20>>2]=n[l+20>>2],n[o+24>>2]=n[l+24>>2],n[o+28>>2]=n[l+28>>2],n[o+32>>2]=n[l+32>>2],n[o+36>>2]=n[l+36>>2],n[o+40>>2]=n[l+40>>2],n[o+44>>2]=n[l+44>>2],n[o+48>>2]=n[l+48>>2],n[o+52>>2]=n[l+52>>2],n[o+56>>2]=n[l+56>>2],n[o+60>>2]=n[l+60>>2],o=o+64|0,l=l+64|0;for(;(o|0)<(u|0);)n[o>>2]=n[l>>2],o=o+4|0,l=l+4|0}else for(u=d-4|0;(o|0)<(u|0);)s[o>>0]=s[l>>0]|0,s[o+1>>0]=s[l+1>>0]|0,s[o+2>>0]=s[l+2>>0]|0,s[o+3>>0]=s[l+3>>0]|0,o=o+4|0,l=l+4|0;for(;(o|0)<(d|0);)s[o>>0]=s[l>>0]|0,o=o+1|0,l=l+1|0;return m|0}function h$(o){o=o|0;var l=0;return l=s[N+(o&255)>>0]|0,(l|0)<8?l|0:(l=s[N+(o>>8&255)>>0]|0,(l|0)<8?l+8|0:(l=s[N+(o>>16&255)>>0]|0,(l|0)<8?l+16|0:(s[N+(o>>>24)>>0]|0)+24|0))}function g$(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0;var m=0,B=0,k=0,T=0,_=0,M=0,G=0,ae=0,We=0,Le=0;if(M=o,T=l,_=T,B=u,ae=A,k=ae,!_)return m=(d|0)!=0,k?m?(n[d>>2]=o|0,n[d+4>>2]=l&0,ae=0,d=0,Ee=ae,d|0):(ae=0,d=0,Ee=ae,d|0):(m&&(n[d>>2]=(M>>>0)%(B>>>0),n[d+4>>2]=0),ae=0,d=(M>>>0)/(B>>>0)>>>0,Ee=ae,d|0);m=(k|0)==0;do if(B){if(!m){if(m=(b(k|0)|0)-(b(_|0)|0)|0,m>>>0<=31){G=m+1|0,k=31-m|0,l=m-31>>31,B=G,o=M>>>(G>>>0)&l|_<>>(G>>>0)&l,m=0,k=M<>2]=o|0,n[d+4>>2]=T|l&0,ae=0,d=0,Ee=ae,d|0):(ae=0,d=0,Ee=ae,d|0)}if(m=B-1|0,m&B|0){k=(b(B|0)|0)+33-(b(_|0)|0)|0,Le=64-k|0,G=32-k|0,T=G>>31,We=k-32|0,l=We>>31,B=k,o=G-1>>31&_>>>(We>>>0)|(_<>>(k>>>0))&l,l=l&_>>>(k>>>0),m=M<>>(We>>>0))&T|M<>31;break}return d|0&&(n[d>>2]=m&M,n[d+4>>2]=0),(B|0)==1?(We=T|l&0,Le=o|0|0,Ee=We,Le|0):(Le=h$(B|0)|0,We=_>>>(Le>>>0)|0,Le=_<<32-Le|M>>>(Le>>>0)|0,Ee=We,Le|0)}else{if(m)return d|0&&(n[d>>2]=(_>>>0)%(B>>>0),n[d+4>>2]=0),We=0,Le=(_>>>0)/(B>>>0)>>>0,Ee=We,Le|0;if(!M)return d|0&&(n[d>>2]=0,n[d+4>>2]=(_>>>0)%(k>>>0)),We=0,Le=(_>>>0)/(k>>>0)>>>0,Ee=We,Le|0;if(m=k-1|0,!(m&k))return d|0&&(n[d>>2]=o|0,n[d+4>>2]=m&_|l&0),We=0,Le=_>>>((h$(k|0)|0)>>>0),Ee=We,Le|0;if(m=(b(k|0)|0)-(b(_|0)|0)|0,m>>>0<=30){l=m+1|0,k=31-m|0,B=l,o=_<>>(l>>>0),l=_>>>(l>>>0),m=0,k=M<>2]=o|0,n[d+4>>2]=T|l&0,We=0,Le=0,Ee=We,Le|0):(We=0,Le=0,Ee=We,Le|0)}while(!1);if(!B)_=k,T=0,k=0;else{G=u|0|0,M=ae|A&0,_=I_(G|0,M|0,-1,-1)|0,u=Ee,T=k,k=0;do A=T,T=m>>>31|T<<1,m=k|m<<1,A=o<<1|A>>>31|0,ae=o>>>31|l<<1|0,nx(_|0,u|0,A|0,ae|0)|0,Le=Ee,We=Le>>31|((Le|0)<0?-1:0)<<1,k=We&1,o=nx(A|0,ae|0,We&G|0,(((Le|0)<0?-1:0)>>31|((Le|0)<0?-1:0)<<1)&M|0)|0,l=Ee,B=B-1|0;while(B|0);_=T,T=0}return B=0,d|0&&(n[d>>2]=o,n[d+4>>2]=l),We=(m|0)>>>31|(_|B)<<1|(B<<1|m>>>31)&0|T,Le=(m<<1|0)&-2|k,Ee=We,Le|0}function C_(o,l,u,A){return o=o|0,l=l|0,u=u|0,A=A|0,g$(o,l,u,A,0)|0}function Vh(o){o=o|0;var l=0,u=0;return u=o+15&-16|0,l=n[C>>2]|0,o=l+u|0,(u|0)>0&(o|0)<(l|0)|(o|0)<0?(oe()|0,pu(12),-1):(n[C>>2]=o,(o|0)>($()|0)&&!(Z()|0)?(n[C>>2]=l,pu(12),-1):l|0)}function F2(o,l,u){o=o|0,l=l|0,u=u|0;var A=0;if((l|0)<(o|0)&(o|0)<(l+u|0)){for(A=o,l=l+u|0,o=o+u|0;(u|0)>0;)o=o-1|0,l=l-1|0,u=u-1|0,s[o>>0]=s[l>>0]|0;o=A}else Qr(o,l,u)|0;return o|0}function w_(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0;var d=0,m=0;return m=I,I=I+16|0,d=m|0,g$(o,l,u,A,d)|0,I=m,Ee=n[d+4>>2]|0,n[d>>2]|0|0}function d$(o){return o=o|0,(o&255)<<24|(o>>8&255)<<16|(o>>16&255)<<8|o>>>24|0}function gVe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,m$[o&1](l|0,u|0,A|0,d|0,m|0)}function dVe(o,l,u){o=o|0,l=l|0,u=y(u),y$[o&1](l|0,y(u))}function mVe(o,l,u){o=o|0,l=l|0,u=+u,E$[o&31](l|0,+u)}function yVe(o,l,u,A){return o=o|0,l=l|0,u=y(u),A=y(A),y(I$[o&0](l|0,y(u),y(A)))}function EVe(o,l){o=o|0,l=l|0,op[o&127](l|0)}function IVe(o,l,u){o=o|0,l=l|0,u=u|0,ap[o&31](l|0,u|0)}function CVe(o,l){return o=o|0,l=l|0,dd[o&31](l|0)|0}function wVe(o,l,u,A,d){o=o|0,l=l|0,u=+u,A=+A,d=d|0,C$[o&1](l|0,+u,+A,d|0)}function BVe(o,l,u,A){o=o|0,l=l|0,u=+u,A=+A,n7e[o&1](l|0,+u,+A)}function vVe(o,l,u,A){return o=o|0,l=l|0,u=u|0,A=A|0,ox[o&7](l|0,u|0,A|0)|0}function SVe(o,l,u,A){return o=o|0,l=l|0,u=u|0,A=A|0,+i7e[o&1](l|0,u|0,A|0)}function DVe(o,l){return o=o|0,l=l|0,+w$[o&15](l|0)}function bVe(o,l,u){return o=o|0,l=l|0,u=+u,s7e[o&1](l|0,+u)|0}function PVe(o,l,u){return o=o|0,l=l|0,u=u|0,v_[o&15](l|0,u|0)|0}function xVe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=+A,d=+d,m=m|0,o7e[o&1](l|0,u|0,+A,+d,m|0)}function kVe(o,l,u,A,d,m,B){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,B=B|0,a7e[o&1](l|0,u|0,A|0,d|0,m|0,B|0)}function QVe(o,l,u){return o=o|0,l=l|0,u=u|0,+B$[o&7](l|0,u|0)}function TVe(o){return o=o|0,ax[o&7]()|0}function RVe(o,l,u,A,d,m){return o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,v$[o&1](l|0,u|0,A|0,d|0,m|0)|0}function FVe(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=+d,l7e[o&1](l|0,u|0,A|0,+d)}function NVe(o,l,u,A,d,m,B){o=o|0,l=l|0,u=u|0,A=y(A),d=d|0,m=y(m),B=B|0,S$[o&1](l|0,u|0,y(A),d|0,y(m),B|0)}function OVe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,L2[o&15](l|0,u|0,A|0)}function LVe(o){o=o|0,D$[o&0]()}function MVe(o,l,u,A){o=o|0,l=l|0,u=u|0,A=+A,b$[o&15](l|0,u|0,+A)}function _Ve(o,l,u){return o=o|0,l=+l,u=+u,c7e[o&1](+l,+u)|0}function UVe(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,S_[o&15](l|0,u|0,A|0,d|0)}function HVe(o,l,u,A,d){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,F(0)}function jVe(o,l){o=o|0,l=y(l),F(1)}function Xa(o,l){o=o|0,l=+l,F(2)}function qVe(o,l,u){return o=o|0,l=y(l),u=y(u),F(3),Xe}function wr(o){o=o|0,F(4)}function N2(o,l){o=o|0,l=l|0,F(5)}function Ll(o){return o=o|0,F(6),0}function GVe(o,l,u,A){o=o|0,l=+l,u=+u,A=A|0,F(7)}function WVe(o,l,u){o=o|0,l=+l,u=+u,F(8)}function YVe(o,l,u){return o=o|0,l=l|0,u=u|0,F(9),0}function VVe(o,l,u){return o=o|0,l=l|0,u=u|0,F(10),0}function gd(o){return o=o|0,F(11),0}function KVe(o,l){return o=o|0,l=+l,F(12),0}function O2(o,l){return o=o|0,l=l|0,F(13),0}function JVe(o,l,u,A,d){o=o|0,l=l|0,u=+u,A=+A,d=d|0,F(14)}function zVe(o,l,u,A,d,m){o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,m=m|0,F(15)}function B_(o,l){return o=o|0,l=l|0,F(16),0}function ZVe(){return F(17),0}function XVe(o,l,u,A,d){return o=o|0,l=l|0,u=u|0,A=A|0,d=d|0,F(18),0}function $Ve(o,l,u,A){o=o|0,l=l|0,u=u|0,A=+A,F(19)}function e7e(o,l,u,A,d,m){o=o|0,l=l|0,u=y(u),A=A|0,d=y(d),m=m|0,F(20)}function sx(o,l,u){o=o|0,l=l|0,u=u|0,F(21)}function t7e(){F(22)}function tE(o,l,u){o=o|0,l=l|0,u=+u,F(23)}function r7e(o,l){return o=+o,l=+l,F(24),0}function rE(o,l,u,A){o=o|0,l=l|0,u=u|0,A=A|0,F(25)}var m$=[HVe,XGe],y$=[jVe,Ty],E$=[Xa,$g,Lh,m2,y2,E2,I2,Pf,Uy,C2,xf,ed,td,w2,B2,vu,rd,v2,Hy,Xa,Xa,Xa,Xa,Xa,Xa,Xa,Xa,Xa,Xa,Xa,Xa,Xa],I$=[qVe],op=[wr,$y,TLe,RLe,FLe,c4e,u4e,f4e,bqe,Pqe,xqe,_Ge,UGe,HGe,aYe,lYe,cYe,vl,Xg,p2,sr,gc,qP,GP,wLe,jLe,eMe,yMe,FMe,ZMe,h_e,x_e,G_e,oUe,wUe,MUe,e4e,P4e,G4e,o3e,w3e,M3e,e8e,E8e,F8e,K8e,uHe,kP,HHe,nje,wje,Uje,t6e,w6e,T6e,N6e,X6e,tqe,yqe,Qqe,Fqe,Zqe,gGe,nZ,z5e,b9e,j9e,nWe,SWe,UWe,ZWe,eYe,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr,wr],ap=[N2,Ly,nM,h2,g2,xr,oo,Zi,Os,Bs,_y,Oh,D2,NP,sd,oM,aM,OP,LP,uM,kf,ne,B8e,L8e,Gje,$5e,BGe,HX,N2,N2,N2,N2],dd=[Ll,MYe,Ny,id,qy,da,QP,Mh,S2,sM,RP,Gy,MP,fM,Vy,pHe,s6e,eGe,n9e,Fl,Ll,Ll,Ll,Ll,Ll,Ll,Ll,Ll,Ll,Ll,Ll,Ll],C$=[GVe,dM],n7e=[WVe,Cqe],ox=[YVe,t$,_Ye,jYe,t_e,T4e,WHe,aWe],i7e=[VVe,DUe],w$=[gd,_h,FP,tp,mM,v,D,Q,H,V,gd,gd,gd,gd,gd,gd],s7e=[KVe,x6e],v_=[O2,cVe,_P,DLe,wMe,m_e,T_e,i4e,K4e,X8e,Ry,Y9e,O2,O2,O2,O2],o7e=[JVe,iMe],a7e=[zVe,xWe],B$=[B_,lM,Se,Ue,At,jUe,B_,B_],ax=[ZVe,Gt,Fy,xP,_6e,sqe,Mqe,iYe],v$=[XVe,Sy],l7e=[$Ve,D3e],S$=[e7e,AM],L2=[sx,To,TP,cM,Du,MMe,V_e,j3e,i8e,rM,C5e,Q9e,GWe,sx,sx,sx],D$=[t7e],b$=[tE,iM,My,ep,d2,Su,jy,nd,u3e,aje,S6e,tE,tE,tE,tE,tE],c7e=[r7e,Sqe],S_=[rE,uUe,CHe,Dje,h6e,W6e,fqe,Wqe,IGe,c9e,gYe,rE,rE,rE,rE,rE];return{_llvm_bswap_i32:d$,dynCall_idd:_Ve,dynCall_i:TVe,_i64Subtract:nx,___udivdi3:C_,dynCall_vif:dVe,setThrew:ua,dynCall_viii:OVe,_bitshift64Lshr:ix,_bitshift64Shl:p$,dynCall_vi:EVe,dynCall_viiddi:xVe,dynCall_diii:SVe,dynCall_iii:PVe,_memset:eE,_sbrk:Vh,_memcpy:Qr,__GLOBAL__sub_I_Yoga_cpp:u2,dynCall_vii:IVe,___uremdi3:w_,dynCall_vid:mVe,stackAlloc:Ha,_nbind_init:DYe,getTempRet0:UA,dynCall_di:DVe,dynCall_iid:bVe,setTempRet0:_A,_i64Add:I_,dynCall_fiff:yVe,dynCall_iiii:vVe,_emscripten_get_global_libc:LYe,dynCall_viid:MVe,dynCall_viiid:FVe,dynCall_viififi:NVe,dynCall_ii:CVe,__GLOBAL__sub_I_Binding_cc:j5e,dynCall_viiii:UVe,dynCall_iiiiii:RVe,stackSave:gf,dynCall_viiiii:gVe,__GLOBAL__sub_I_nbind_cc:vr,dynCall_vidd:BVe,_free:tx,runPostSets:hVe,dynCall_viiiiii:kVe,establishStackSpace:wn,_memmove:F2,stackRestore:cc,_malloc:ex,__GLOBAL__sub_I_common_cc:lGe,dynCall_viddi:wVe,dynCall_dii:QVe,dynCall_v:LVe}}(Module.asmGlobalArg,Module.asmLibraryArg,buffer),_llvm_bswap_i32=Module._llvm_bswap_i32=asm._llvm_bswap_i32,getTempRet0=Module.getTempRet0=asm.getTempRet0,___udivdi3=Module.___udivdi3=asm.___udivdi3,setThrew=Module.setThrew=asm.setThrew,_bitshift64Lshr=Module._bitshift64Lshr=asm._bitshift64Lshr,_bitshift64Shl=Module._bitshift64Shl=asm._bitshift64Shl,_memset=Module._memset=asm._memset,_sbrk=Module._sbrk=asm._sbrk,_memcpy=Module._memcpy=asm._memcpy,stackAlloc=Module.stackAlloc=asm.stackAlloc,___uremdi3=Module.___uremdi3=asm.___uremdi3,_nbind_init=Module._nbind_init=asm._nbind_init,_i64Subtract=Module._i64Subtract=asm._i64Subtract,setTempRet0=Module.setTempRet0=asm.setTempRet0,_i64Add=Module._i64Add=asm._i64Add,_emscripten_get_global_libc=Module._emscripten_get_global_libc=asm._emscripten_get_global_libc,__GLOBAL__sub_I_Yoga_cpp=Module.__GLOBAL__sub_I_Yoga_cpp=asm.__GLOBAL__sub_I_Yoga_cpp,__GLOBAL__sub_I_Binding_cc=Module.__GLOBAL__sub_I_Binding_cc=asm.__GLOBAL__sub_I_Binding_cc,stackSave=Module.stackSave=asm.stackSave,__GLOBAL__sub_I_nbind_cc=Module.__GLOBAL__sub_I_nbind_cc=asm.__GLOBAL__sub_I_nbind_cc,_free=Module._free=asm._free,runPostSets=Module.runPostSets=asm.runPostSets,establishStackSpace=Module.establishStackSpace=asm.establishStackSpace,_memmove=Module._memmove=asm._memmove,stackRestore=Module.stackRestore=asm.stackRestore,_malloc=Module._malloc=asm._malloc,__GLOBAL__sub_I_common_cc=Module.__GLOBAL__sub_I_common_cc=asm.__GLOBAL__sub_I_common_cc,dynCall_viiiii=Module.dynCall_viiiii=asm.dynCall_viiiii,dynCall_vif=Module.dynCall_vif=asm.dynCall_vif,dynCall_vid=Module.dynCall_vid=asm.dynCall_vid,dynCall_fiff=Module.dynCall_fiff=asm.dynCall_fiff,dynCall_vi=Module.dynCall_vi=asm.dynCall_vi,dynCall_vii=Module.dynCall_vii=asm.dynCall_vii,dynCall_ii=Module.dynCall_ii=asm.dynCall_ii,dynCall_viddi=Module.dynCall_viddi=asm.dynCall_viddi,dynCall_vidd=Module.dynCall_vidd=asm.dynCall_vidd,dynCall_iiii=Module.dynCall_iiii=asm.dynCall_iiii,dynCall_diii=Module.dynCall_diii=asm.dynCall_diii,dynCall_di=Module.dynCall_di=asm.dynCall_di,dynCall_iid=Module.dynCall_iid=asm.dynCall_iid,dynCall_iii=Module.dynCall_iii=asm.dynCall_iii,dynCall_viiddi=Module.dynCall_viiddi=asm.dynCall_viiddi,dynCall_viiiiii=Module.dynCall_viiiiii=asm.dynCall_viiiiii,dynCall_dii=Module.dynCall_dii=asm.dynCall_dii,dynCall_i=Module.dynCall_i=asm.dynCall_i,dynCall_iiiiii=Module.dynCall_iiiiii=asm.dynCall_iiiiii,dynCall_viiid=Module.dynCall_viiid=asm.dynCall_viiid,dynCall_viififi=Module.dynCall_viififi=asm.dynCall_viififi,dynCall_viii=Module.dynCall_viii=asm.dynCall_viii,dynCall_v=Module.dynCall_v=asm.dynCall_v,dynCall_viid=Module.dynCall_viid=asm.dynCall_viid,dynCall_idd=Module.dynCall_idd=asm.dynCall_idd,dynCall_viiii=Module.dynCall_viiii=asm.dynCall_viiii;Runtime.stackAlloc=Module.stackAlloc,Runtime.stackSave=Module.stackSave,Runtime.stackRestore=Module.stackRestore,Runtime.establishStackSpace=Module.establishStackSpace,Runtime.setTempRet0=Module.setTempRet0,Runtime.getTempRet0=Module.getTempRet0,Module.asm=asm;function ExitStatus(t){this.name="ExitStatus",this.message="Program terminated with exit("+t+")",this.status=t}ExitStatus.prototype=new Error,ExitStatus.prototype.constructor=ExitStatus;var initialStackTop,preloadStartTime=null,calledMain=!1;dependenciesFulfilled=function t(){Module.calledRun||run(),Module.calledRun||(dependenciesFulfilled=t)},Module.callMain=Module.callMain=function t(e){e=e||[],ensureInitRuntime();var r=e.length+1;function s(){for(var p=0;p<3;p++)a.push(0)}var a=[allocate(intArrayFromString(Module.thisProgram),"i8",ALLOC_NORMAL)];s();for(var n=0;n0||(preRun(),runDependencies>0)||Module.calledRun)return;function e(){Module.calledRun||(Module.calledRun=!0,!ABORT&&(ensureInitRuntime(),preMain(),Module.onRuntimeInitialized&&Module.onRuntimeInitialized(),Module._main&&shouldRunNow&&Module.callMain(t),postRun()))}Module.setStatus?(Module.setStatus("Running..."),setTimeout(function(){setTimeout(function(){Module.setStatus("")},1),e()},1)):e()}Module.run=Module.run=run;function exit(t,e){e&&Module.noExitRuntime||(Module.noExitRuntime||(ABORT=!0,EXITSTATUS=t,STACKTOP=initialStackTop,exitRuntime(),Module.onExit&&Module.onExit(t)),ENVIRONMENT_IS_NODE&&process.exit(t),Module.quit(t,new ExitStatus(t)))}Module.exit=Module.exit=exit;var abortDecorators=[];function abort(t){Module.onAbort&&Module.onAbort(t),t!==void 0?(Module.print(t),Module.printErr(t),t=JSON.stringify(t)):t="",ABORT=!0,EXITSTATUS=1;var e=` +If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.`,r="abort("+t+") at "+stackTrace()+e;throw abortDecorators&&abortDecorators.forEach(function(s){r=s(r,t)}),r}if(Module.abort=Module.abort=abort,Module.preInit)for(typeof Module.preInit=="function"&&(Module.preInit=[Module.preInit]);Module.preInit.length>0;)Module.preInit.pop()();var shouldRunNow=!0;Module.noInitialRun&&(shouldRunNow=!1),run()})});var Nm=L((Khr,KDe)=>{"use strict";var $Pt=YDe(),ext=VDe(),PW=!1,xW=null;ext({},function(t,e){if(!PW){if(PW=!0,t)throw t;xW=e}});if(!PW)throw new Error("Failed to load the yoga module - it needed to be loaded synchronously, but didn't");KDe.exports=$Pt(xW.bind,xW.lib)});var QW=L((Jhr,kW)=>{"use strict";var JDe=t=>Number.isNaN(t)?!1:t>=4352&&(t<=4447||t===9001||t===9002||11904<=t&&t<=12871&&t!==12351||12880<=t&&t<=19903||19968<=t&&t<=42182||43360<=t&&t<=43388||44032<=t&&t<=55203||63744<=t&&t<=64255||65040<=t&&t<=65049||65072<=t&&t<=65131||65281<=t&&t<=65376||65504<=t&&t<=65510||110592<=t&&t<=110593||127488<=t&&t<=127569||131072<=t&&t<=262141);kW.exports=JDe;kW.exports.default=JDe});var ZDe=L((zhr,zDe)=>{"use strict";zDe.exports=function(){return/\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g}});var iD=L((Zhr,TW)=>{"use strict";var txt=bk(),rxt=QW(),nxt=ZDe(),XDe=t=>{if(typeof t!="string"||t.length===0||(t=txt(t),t.length===0))return 0;t=t.replace(nxt()," ");let e=0;for(let r=0;r=127&&s<=159||s>=768&&s<=879||(s>65535&&r++,e+=rxt(s)?2:1)}return e};TW.exports=XDe;TW.exports.default=XDe});var FW=L((Xhr,RW)=>{"use strict";var ixt=iD(),$De=t=>{let e=0;for(let r of t.split(` +`))e=Math.max(e,ixt(r));return e};RW.exports=$De;RW.exports.default=$De});var ebe=L(sD=>{"use strict";var sxt=sD&&sD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(sD,"__esModule",{value:!0});var oxt=sxt(FW()),NW={};sD.default=t=>{if(t.length===0)return{width:0,height:0};if(NW[t])return NW[t];let e=oxt.default(t),r=t.split(` +`).length;return NW[t]={width:e,height:r},{width:e,height:r}}});var tbe=L(oD=>{"use strict";var axt=oD&&oD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(oD,"__esModule",{value:!0});var bn=axt(Nm()),lxt=(t,e)=>{"position"in e&&t.setPositionType(e.position==="absolute"?bn.default.POSITION_TYPE_ABSOLUTE:bn.default.POSITION_TYPE_RELATIVE)},cxt=(t,e)=>{"marginLeft"in e&&t.setMargin(bn.default.EDGE_START,e.marginLeft||0),"marginRight"in e&&t.setMargin(bn.default.EDGE_END,e.marginRight||0),"marginTop"in e&&t.setMargin(bn.default.EDGE_TOP,e.marginTop||0),"marginBottom"in e&&t.setMargin(bn.default.EDGE_BOTTOM,e.marginBottom||0)},uxt=(t,e)=>{"paddingLeft"in e&&t.setPadding(bn.default.EDGE_LEFT,e.paddingLeft||0),"paddingRight"in e&&t.setPadding(bn.default.EDGE_RIGHT,e.paddingRight||0),"paddingTop"in e&&t.setPadding(bn.default.EDGE_TOP,e.paddingTop||0),"paddingBottom"in e&&t.setPadding(bn.default.EDGE_BOTTOM,e.paddingBottom||0)},fxt=(t,e)=>{var r;"flexGrow"in e&&t.setFlexGrow((r=e.flexGrow)!==null&&r!==void 0?r:0),"flexShrink"in e&&t.setFlexShrink(typeof e.flexShrink=="number"?e.flexShrink:1),"flexDirection"in e&&(e.flexDirection==="row"&&t.setFlexDirection(bn.default.FLEX_DIRECTION_ROW),e.flexDirection==="row-reverse"&&t.setFlexDirection(bn.default.FLEX_DIRECTION_ROW_REVERSE),e.flexDirection==="column"&&t.setFlexDirection(bn.default.FLEX_DIRECTION_COLUMN),e.flexDirection==="column-reverse"&&t.setFlexDirection(bn.default.FLEX_DIRECTION_COLUMN_REVERSE)),"flexBasis"in e&&(typeof e.flexBasis=="number"?t.setFlexBasis(e.flexBasis):typeof e.flexBasis=="string"?t.setFlexBasisPercent(Number.parseInt(e.flexBasis,10)):t.setFlexBasis(NaN)),"alignItems"in e&&((e.alignItems==="stretch"||!e.alignItems)&&t.setAlignItems(bn.default.ALIGN_STRETCH),e.alignItems==="flex-start"&&t.setAlignItems(bn.default.ALIGN_FLEX_START),e.alignItems==="center"&&t.setAlignItems(bn.default.ALIGN_CENTER),e.alignItems==="flex-end"&&t.setAlignItems(bn.default.ALIGN_FLEX_END)),"alignSelf"in e&&((e.alignSelf==="auto"||!e.alignSelf)&&t.setAlignSelf(bn.default.ALIGN_AUTO),e.alignSelf==="flex-start"&&t.setAlignSelf(bn.default.ALIGN_FLEX_START),e.alignSelf==="center"&&t.setAlignSelf(bn.default.ALIGN_CENTER),e.alignSelf==="flex-end"&&t.setAlignSelf(bn.default.ALIGN_FLEX_END)),"justifyContent"in e&&((e.justifyContent==="flex-start"||!e.justifyContent)&&t.setJustifyContent(bn.default.JUSTIFY_FLEX_START),e.justifyContent==="center"&&t.setJustifyContent(bn.default.JUSTIFY_CENTER),e.justifyContent==="flex-end"&&t.setJustifyContent(bn.default.JUSTIFY_FLEX_END),e.justifyContent==="space-between"&&t.setJustifyContent(bn.default.JUSTIFY_SPACE_BETWEEN),e.justifyContent==="space-around"&&t.setJustifyContent(bn.default.JUSTIFY_SPACE_AROUND))},Axt=(t,e)=>{var r,s;"width"in e&&(typeof e.width=="number"?t.setWidth(e.width):typeof e.width=="string"?t.setWidthPercent(Number.parseInt(e.width,10)):t.setWidthAuto()),"height"in e&&(typeof e.height=="number"?t.setHeight(e.height):typeof e.height=="string"?t.setHeightPercent(Number.parseInt(e.height,10)):t.setHeightAuto()),"minWidth"in e&&(typeof e.minWidth=="string"?t.setMinWidthPercent(Number.parseInt(e.minWidth,10)):t.setMinWidth((r=e.minWidth)!==null&&r!==void 0?r:0)),"minHeight"in e&&(typeof e.minHeight=="string"?t.setMinHeightPercent(Number.parseInt(e.minHeight,10)):t.setMinHeight((s=e.minHeight)!==null&&s!==void 0?s:0))},pxt=(t,e)=>{"display"in e&&t.setDisplay(e.display==="flex"?bn.default.DISPLAY_FLEX:bn.default.DISPLAY_NONE)},hxt=(t,e)=>{if("borderStyle"in e){let r=typeof e.borderStyle=="string"?1:0;t.setBorder(bn.default.EDGE_TOP,r),t.setBorder(bn.default.EDGE_BOTTOM,r),t.setBorder(bn.default.EDGE_LEFT,r),t.setBorder(bn.default.EDGE_RIGHT,r)}};oD.default=(t,e={})=>{lxt(t,e),cxt(t,e),uxt(t,e),fxt(t,e),Axt(t,e),pxt(t,e),hxt(t,e)}});var ibe=L((t0r,nbe)=>{"use strict";var aD=iD(),gxt=bk(),dxt=IB(),LW=new Set(["\x1B","\x9B"]),mxt=39,rbe=t=>`${LW.values().next().value}[${t}m`,yxt=t=>t.split(" ").map(e=>aD(e)),OW=(t,e,r)=>{let s=[...e],a=!1,n=aD(gxt(t[t.length-1]));for(let[c,f]of s.entries()){let p=aD(f);if(n+p<=r?t[t.length-1]+=f:(t.push(f),n=0),LW.has(f))a=!0;else if(a&&f==="m"){a=!1;continue}a||(n+=p,n===r&&c0&&t.length>1&&(t[t.length-2]+=t.pop())},Ext=t=>{let e=t.split(" "),r=e.length;for(;r>0&&!(aD(e[r-1])>0);)r--;return r===e.length?t:e.slice(0,r).join(" ")+e.slice(r).join("")},Ixt=(t,e,r={})=>{if(r.trim!==!1&&t.trim()==="")return"";let s="",a="",n,c=yxt(t),f=[""];for(let[p,h]of t.split(" ").entries()){r.trim!==!1&&(f[f.length-1]=f[f.length-1].trimLeft());let E=aD(f[f.length-1]);if(p!==0&&(E>=e&&(r.wordWrap===!1||r.trim===!1)&&(f.push(""),E=0),(E>0||r.trim===!1)&&(f[f.length-1]+=" ",E++)),r.hard&&c[p]>e){let C=e-E,S=1+Math.floor((c[p]-C-1)/e);Math.floor((c[p]-1)/e)e&&E>0&&c[p]>0){if(r.wordWrap===!1&&Ee&&r.wordWrap===!1){OW(f,h,e);continue}f[f.length-1]+=h}r.trim!==!1&&(f=f.map(Ext)),s=f.join(` +`);for(let[p,h]of[...s].entries()){if(a+=h,LW.has(h)){let C=parseFloat(/\d[^m]*/.exec(s.slice(p,p+4)));n=C===mxt?null:C}let E=dxt.codes.get(Number(n));n&&E&&(s[p+1]===` +`?a+=rbe(E):h===` +`&&(a+=rbe(n)))}return a};nbe.exports=(t,e,r)=>String(t).normalize().replace(/\r\n/g,` +`).split(` +`).map(s=>Ixt(s,e,r)).join(` +`)});var abe=L((r0r,obe)=>{"use strict";var sbe="[\uD800-\uDBFF][\uDC00-\uDFFF]",Cxt=t=>t&&t.exact?new RegExp(`^${sbe}$`):new RegExp(sbe,"g");obe.exports=Cxt});var MW=L((n0r,fbe)=>{"use strict";var wxt=QW(),Bxt=abe(),lbe=IB(),ube=["\x1B","\x9B"],MF=t=>`${ube[0]}[${t}m`,cbe=(t,e,r)=>{let s=[];t=[...t];for(let a of t){let n=a;a.match(";")&&(a=a.split(";")[0][0]+"0");let c=lbe.codes.get(parseInt(a,10));if(c){let f=t.indexOf(c.toString());f>=0?t.splice(f,1):s.push(MF(e?c:n))}else if(e){s.push(MF(0));break}else s.push(MF(n))}if(e&&(s=s.filter((a,n)=>s.indexOf(a)===n),r!==void 0)){let a=MF(lbe.codes.get(parseInt(r,10)));s=s.reduce((n,c)=>c===a?[c,...n]:[...n,c],[])}return s.join("")};fbe.exports=(t,e,r)=>{let s=[...t.normalize()],a=[];r=typeof r=="number"?r:s.length;let n=!1,c,f=0,p="";for(let[h,E]of s.entries()){let C=!1;if(ube.includes(E)){let S=/\d[^m]*/.exec(t.slice(h,h+18));c=S&&S.length>0?S[0]:void 0,fe&&f<=r)p+=E;else if(f===e&&!n&&c!==void 0)p=cbe(a);else if(f>=r){p+=cbe(a,!0,c);break}}return p}});var pbe=L((i0r,Abe)=>{"use strict";var eg=MW(),vxt=iD();function _F(t,e,r){if(t.charAt(e)===" ")return e;for(let s=1;s<=3;s++)if(r){if(t.charAt(e+s)===" ")return e+s}else if(t.charAt(e-s)===" ")return e-s;return e}Abe.exports=(t,e,r)=>{r={position:"end",preferTruncationOnSpace:!1,...r};let{position:s,space:a,preferTruncationOnSpace:n}=r,c="\u2026",f=1;if(typeof t!="string")throw new TypeError(`Expected \`input\` to be a string, got ${typeof t}`);if(typeof e!="number")throw new TypeError(`Expected \`columns\` to be a number, got ${typeof e}`);if(e<1)return"";if(e===1)return c;let p=vxt(t);if(p<=e)return t;if(s==="start"){if(n){let h=_F(t,p-e+1,!0);return c+eg(t,h,p).trim()}return a===!0&&(c+=" ",f=2),c+eg(t,p-e+f,p)}if(s==="middle"){a===!0&&(c=" "+c+" ",f=3);let h=Math.floor(e/2);if(n){let E=_F(t,h),C=_F(t,p-(e-h)+1,!0);return eg(t,0,E)+c+eg(t,C,p).trim()}return eg(t,0,h)+c+eg(t,p-(e-h)+f,p)}if(s==="end"){if(n){let h=_F(t,e-1);return eg(t,0,h)+c}return a===!0&&(c=" "+c,f=2),eg(t,0,e-f)+c}throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${s}`)}});var UW=L(lD=>{"use strict";var hbe=lD&&lD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(lD,"__esModule",{value:!0});var Sxt=hbe(ibe()),Dxt=hbe(pbe()),_W={};lD.default=(t,e,r)=>{let s=t+String(e)+String(r);if(_W[s])return _W[s];let a=t;if(r==="wrap"&&(a=Sxt.default(t,e,{trim:!1,hard:!0})),r.startsWith("truncate")){let n="end";r==="truncate-middle"&&(n="middle"),r==="truncate-start"&&(n="start"),a=Dxt.default(t,e,{position:n})}return _W[s]=a,a}});var jW=L(HW=>{"use strict";Object.defineProperty(HW,"__esModule",{value:!0});var gbe=t=>{let e="";if(t.childNodes.length>0)for(let r of t.childNodes){let s="";r.nodeName==="#text"?s=r.nodeValue:((r.nodeName==="ink-text"||r.nodeName==="ink-virtual-text")&&(s=gbe(r)),s.length>0&&typeof r.internal_transform=="function"&&(s=r.internal_transform(s))),e+=s}return e};HW.default=gbe});var qW=L(Pi=>{"use strict";var cD=Pi&&Pi.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(Pi,"__esModule",{value:!0});Pi.setTextNodeValue=Pi.createTextNode=Pi.setStyle=Pi.setAttribute=Pi.removeChildNode=Pi.insertBeforeNode=Pi.appendChildNode=Pi.createNode=Pi.TEXT_NAME=void 0;var bxt=cD(Nm()),dbe=cD(ebe()),Pxt=cD(tbe()),xxt=cD(UW()),kxt=cD(jW());Pi.TEXT_NAME="#text";Pi.createNode=t=>{var e;let r={nodeName:t,style:{},attributes:{},childNodes:[],parentNode:null,yogaNode:t==="ink-virtual-text"?void 0:bxt.default.Node.create()};return t==="ink-text"&&((e=r.yogaNode)===null||e===void 0||e.setMeasureFunc(Qxt.bind(null,r))),r};Pi.appendChildNode=(t,e)=>{var r;e.parentNode&&Pi.removeChildNode(e.parentNode,e),e.parentNode=t,t.childNodes.push(e),e.yogaNode&&((r=t.yogaNode)===null||r===void 0||r.insertChild(e.yogaNode,t.yogaNode.getChildCount())),(t.nodeName==="ink-text"||t.nodeName==="ink-virtual-text")&&UF(t)};Pi.insertBeforeNode=(t,e,r)=>{var s,a;e.parentNode&&Pi.removeChildNode(e.parentNode,e),e.parentNode=t;let n=t.childNodes.indexOf(r);if(n>=0){t.childNodes.splice(n,0,e),e.yogaNode&&((s=t.yogaNode)===null||s===void 0||s.insertChild(e.yogaNode,n));return}t.childNodes.push(e),e.yogaNode&&((a=t.yogaNode)===null||a===void 0||a.insertChild(e.yogaNode,t.yogaNode.getChildCount())),(t.nodeName==="ink-text"||t.nodeName==="ink-virtual-text")&&UF(t)};Pi.removeChildNode=(t,e)=>{var r,s;e.yogaNode&&((s=(r=e.parentNode)===null||r===void 0?void 0:r.yogaNode)===null||s===void 0||s.removeChild(e.yogaNode)),e.parentNode=null;let a=t.childNodes.indexOf(e);a>=0&&t.childNodes.splice(a,1),(t.nodeName==="ink-text"||t.nodeName==="ink-virtual-text")&&UF(t)};Pi.setAttribute=(t,e,r)=>{t.attributes[e]=r};Pi.setStyle=(t,e)=>{t.style=e,t.yogaNode&&Pxt.default(t.yogaNode,e)};Pi.createTextNode=t=>{let e={nodeName:"#text",nodeValue:t,yogaNode:void 0,parentNode:null,style:{}};return Pi.setTextNodeValue(e,t),e};var Qxt=function(t,e){var r,s;let a=t.nodeName==="#text"?t.nodeValue:kxt.default(t),n=dbe.default(a);if(n.width<=e||n.width>=1&&e>0&&e<1)return n;let c=(s=(r=t.style)===null||r===void 0?void 0:r.textWrap)!==null&&s!==void 0?s:"wrap",f=xxt.default(a,e,c);return dbe.default(f)},mbe=t=>{var e;if(!(!t||!t.parentNode))return(e=t.yogaNode)!==null&&e!==void 0?e:mbe(t.parentNode)},UF=t=>{let e=mbe(t);e?.markDirty()};Pi.setTextNodeValue=(t,e)=>{typeof e!="string"&&(e=String(e)),t.nodeValue=e,UF(t)}});var wbe=L(uD=>{"use strict";var Cbe=uD&&uD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(uD,"__esModule",{value:!0});var ybe=SW(),Txt=Cbe(UDe()),Ebe=Cbe(Nm()),ta=qW(),Ibe=t=>{t?.unsetMeasureFunc(),t?.freeRecursive()};uD.default=Txt.default({schedulePassiveEffects:ybe.unstable_scheduleCallback,cancelPassiveEffects:ybe.unstable_cancelCallback,now:Date.now,getRootHostContext:()=>({isInsideText:!1}),prepareForCommit:()=>null,preparePortalMount:()=>null,clearContainer:()=>!1,shouldDeprioritizeSubtree:()=>!1,resetAfterCommit:t=>{if(t.isStaticDirty){t.isStaticDirty=!1,typeof t.onImmediateRender=="function"&&t.onImmediateRender();return}typeof t.onRender=="function"&&t.onRender()},getChildHostContext:(t,e)=>{let r=t.isInsideText,s=e==="ink-text"||e==="ink-virtual-text";return r===s?t:{isInsideText:s}},shouldSetTextContent:()=>!1,createInstance:(t,e,r,s)=>{if(s.isInsideText&&t==="ink-box")throw new Error(" can\u2019t be nested inside component");let a=t==="ink-text"&&s.isInsideText?"ink-virtual-text":t,n=ta.createNode(a);for(let[c,f]of Object.entries(e))c!=="children"&&(c==="style"?ta.setStyle(n,f):c==="internal_transform"?n.internal_transform=f:c==="internal_static"?n.internal_static=!0:ta.setAttribute(n,c,f));return n},createTextInstance:(t,e,r)=>{if(!r.isInsideText)throw new Error(`Text string "${t}" must be rendered inside component`);return ta.createTextNode(t)},resetTextContent:()=>{},hideTextInstance:t=>{ta.setTextNodeValue(t,"")},unhideTextInstance:(t,e)=>{ta.setTextNodeValue(t,e)},getPublicInstance:t=>t,hideInstance:t=>{var e;(e=t.yogaNode)===null||e===void 0||e.setDisplay(Ebe.default.DISPLAY_NONE)},unhideInstance:t=>{var e;(e=t.yogaNode)===null||e===void 0||e.setDisplay(Ebe.default.DISPLAY_FLEX)},appendInitialChild:ta.appendChildNode,appendChild:ta.appendChildNode,insertBefore:ta.insertBeforeNode,finalizeInitialChildren:(t,e,r,s)=>(t.internal_static&&(s.isStaticDirty=!0,s.staticNode=t),!1),supportsMutation:!0,appendChildToContainer:ta.appendChildNode,insertInContainerBefore:ta.insertBeforeNode,removeChildFromContainer:(t,e)=>{ta.removeChildNode(t,e),Ibe(e.yogaNode)},prepareUpdate:(t,e,r,s,a)=>{t.internal_static&&(a.isStaticDirty=!0);let n={},c=Object.keys(s);for(let f of c)if(s[f]!==r[f]){if(f==="style"&&typeof s.style=="object"&&typeof r.style=="object"){let h=s.style,E=r.style,C=Object.keys(h);for(let S of C){if(S==="borderStyle"||S==="borderColor"){if(typeof n.style!="object"){let P={};n.style=P}n.style.borderStyle=h.borderStyle,n.style.borderColor=h.borderColor}if(h[S]!==E[S]){if(typeof n.style!="object"){let P={};n.style=P}n.style[S]=h[S]}}continue}n[f]=s[f]}return n},commitUpdate:(t,e)=>{for(let[r,s]of Object.entries(e))r!=="children"&&(r==="style"?ta.setStyle(t,s):r==="internal_transform"?t.internal_transform=s:r==="internal_static"?t.internal_static=!0:ta.setAttribute(t,r,s))},commitTextUpdate:(t,e,r)=>{ta.setTextNodeValue(t,r)},removeChild:(t,e)=>{ta.removeChildNode(t,e),Ibe(e.yogaNode)}})});var vbe=L((c0r,Bbe)=>{"use strict";Bbe.exports=(t,e=1,r)=>{if(r={indent:" ",includeEmptyLines:!1,...r},typeof t!="string")throw new TypeError(`Expected \`input\` to be a \`string\`, got \`${typeof t}\``);if(typeof e!="number")throw new TypeError(`Expected \`count\` to be a \`number\`, got \`${typeof e}\``);if(typeof r.indent!="string")throw new TypeError(`Expected \`options.indent\` to be a \`string\`, got \`${typeof r.indent}\``);if(e===0)return t;let s=r.includeEmptyLines?/^/gm:/^(?!\s*$)/gm;return t.replace(s,r.indent.repeat(e))}});var Sbe=L(fD=>{"use strict";var Rxt=fD&&fD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(fD,"__esModule",{value:!0});var HF=Rxt(Nm());fD.default=t=>t.getComputedWidth()-t.getComputedPadding(HF.default.EDGE_LEFT)-t.getComputedPadding(HF.default.EDGE_RIGHT)-t.getComputedBorder(HF.default.EDGE_LEFT)-t.getComputedBorder(HF.default.EDGE_RIGHT)});var Dbe=L((f0r,Fxt)=>{Fxt.exports={single:{topLeft:"\u250C",topRight:"\u2510",bottomRight:"\u2518",bottomLeft:"\u2514",vertical:"\u2502",horizontal:"\u2500"},double:{topLeft:"\u2554",topRight:"\u2557",bottomRight:"\u255D",bottomLeft:"\u255A",vertical:"\u2551",horizontal:"\u2550"},round:{topLeft:"\u256D",topRight:"\u256E",bottomRight:"\u256F",bottomLeft:"\u2570",vertical:"\u2502",horizontal:"\u2500"},bold:{topLeft:"\u250F",topRight:"\u2513",bottomRight:"\u251B",bottomLeft:"\u2517",vertical:"\u2503",horizontal:"\u2501"},singleDouble:{topLeft:"\u2553",topRight:"\u2556",bottomRight:"\u255C",bottomLeft:"\u2559",vertical:"\u2551",horizontal:"\u2500"},doubleSingle:{topLeft:"\u2552",topRight:"\u2555",bottomRight:"\u255B",bottomLeft:"\u2558",vertical:"\u2502",horizontal:"\u2550"},classic:{topLeft:"+",topRight:"+",bottomRight:"+",bottomLeft:"+",vertical:"|",horizontal:"-"}}});var Pbe=L((A0r,GW)=>{"use strict";var bbe=Dbe();GW.exports=bbe;GW.exports.default=bbe});var kbe=L((p0r,xbe)=>{"use strict";var Nxt=(t,e,r)=>{let s=t.indexOf(e);if(s===-1)return t;let a=e.length,n=0,c="";do c+=t.substr(n,s-n)+e+r,n=s+a,s=t.indexOf(e,n);while(s!==-1);return c+=t.substr(n),c},Oxt=(t,e,r,s)=>{let a=0,n="";do{let c=t[s-1]==="\r";n+=t.substr(a,(c?s-1:s)-a)+e+(c?`\r +`:` +`)+r,a=s+1,s=t.indexOf(` +`,a)}while(s!==-1);return n+=t.substr(a),n};xbe.exports={stringReplaceAll:Nxt,stringEncaseCRLFWithFirstIndex:Oxt}});var Nbe=L((h0r,Fbe)=>{"use strict";var Lxt=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,Qbe=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,Mxt=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,_xt=/\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi,Uxt=new Map([["n",` +`],["r","\r"],["t"," "],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e","\x1B"],["a","\x07"]]);function Rbe(t){let e=t[0]==="u",r=t[1]==="{";return e&&!r&&t.length===5||t[0]==="x"&&t.length===3?String.fromCharCode(parseInt(t.slice(1),16)):e&&r?String.fromCodePoint(parseInt(t.slice(2,-1),16)):Uxt.get(t)||t}function Hxt(t,e){let r=[],s=e.trim().split(/\s*,\s*/g),a;for(let n of s){let c=Number(n);if(!Number.isNaN(c))r.push(c);else if(a=n.match(Mxt))r.push(a[2].replace(_xt,(f,p,h)=>p?Rbe(p):h));else throw new Error(`Invalid Chalk template style argument: ${n} (in style '${t}')`)}return r}function jxt(t){Qbe.lastIndex=0;let e=[],r;for(;(r=Qbe.exec(t))!==null;){let s=r[1];if(r[2]){let a=Hxt(s,r[2]);e.push([s].concat(a))}else e.push([s])}return e}function Tbe(t,e){let r={};for(let a of e)for(let n of a.styles)r[n[0]]=a.inverse?null:n.slice(1);let s=t;for(let[a,n]of Object.entries(r))if(Array.isArray(n)){if(!(a in s))throw new Error(`Unknown Chalk style: ${a}`);s=n.length>0?s[a](...n):s[a]}return s}Fbe.exports=(t,e)=>{let r=[],s=[],a=[];if(e.replace(Lxt,(n,c,f,p,h,E)=>{if(c)a.push(Rbe(c));else if(p){let C=a.join("");a=[],s.push(r.length===0?C:Tbe(t,r)(C)),r.push({inverse:f,styles:jxt(p)})}else if(h){if(r.length===0)throw new Error("Found extraneous } in Chalk template literal");s.push(Tbe(t,r)(a.join(""))),a=[],r.pop()}else a.push(E)}),s.push(a.join("")),r.length>0){let n=`Chalk template literal is missing ${r.length} closing bracket${r.length===1?"":"s"} (\`}\`)`;throw new Error(n)}return s.join("")}});var YF=L((g0r,Hbe)=>{"use strict";var AD=IB(),{stdout:YW,stderr:VW}=c4(),{stringReplaceAll:qxt,stringEncaseCRLFWithFirstIndex:Gxt}=kbe(),{isArray:jF}=Array,Lbe=["ansi","ansi","ansi256","ansi16m"],Iw=Object.create(null),Wxt=(t,e={})=>{if(e.level&&!(Number.isInteger(e.level)&&e.level>=0&&e.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let r=YW?YW.level:0;t.level=e.level===void 0?r:e.level},KW=class{constructor(e){return Mbe(e)}},Mbe=t=>{let e={};return Wxt(e,t),e.template=(...r)=>Ube(e.template,...r),Object.setPrototypeOf(e,qF.prototype),Object.setPrototypeOf(e.template,e),e.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},e.template.Instance=KW,e.template};function qF(t){return Mbe(t)}for(let[t,e]of Object.entries(AD))Iw[t]={get(){let r=GF(this,JW(e.open,e.close,this._styler),this._isEmpty);return Object.defineProperty(this,t,{value:r}),r}};Iw.visible={get(){let t=GF(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:t}),t}};var _be=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(let t of _be)Iw[t]={get(){let{level:e}=this;return function(...r){let s=JW(AD.color[Lbe[e]][t](...r),AD.color.close,this._styler);return GF(this,s,this._isEmpty)}}};for(let t of _be){let e="bg"+t[0].toUpperCase()+t.slice(1);Iw[e]={get(){let{level:r}=this;return function(...s){let a=JW(AD.bgColor[Lbe[r]][t](...s),AD.bgColor.close,this._styler);return GF(this,a,this._isEmpty)}}}}var Yxt=Object.defineProperties(()=>{},{...Iw,level:{enumerable:!0,get(){return this._generator.level},set(t){this._generator.level=t}}}),JW=(t,e,r)=>{let s,a;return r===void 0?(s=t,a=e):(s=r.openAll+t,a=e+r.closeAll),{open:t,close:e,openAll:s,closeAll:a,parent:r}},GF=(t,e,r)=>{let s=(...a)=>jF(a[0])&&jF(a[0].raw)?Obe(s,Ube(s,...a)):Obe(s,a.length===1?""+a[0]:a.join(" "));return Object.setPrototypeOf(s,Yxt),s._generator=t,s._styler=e,s._isEmpty=r,s},Obe=(t,e)=>{if(t.level<=0||!e)return t._isEmpty?"":e;let r=t._styler;if(r===void 0)return e;let{openAll:s,closeAll:a}=r;if(e.indexOf("\x1B")!==-1)for(;r!==void 0;)e=qxt(e,r.close,r.open),r=r.parent;let n=e.indexOf(` +`);return n!==-1&&(e=Gxt(e,a,s,n)),s+e+a},WW,Ube=(t,...e)=>{let[r]=e;if(!jF(r)||!jF(r.raw))return e.join(" ");let s=e.slice(1),a=[r.raw[0]];for(let n=1;n{"use strict";var Vxt=hD&&hD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(hD,"__esModule",{value:!0});var pD=Vxt(YF()),Kxt=/^(rgb|hsl|hsv|hwb)\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/,Jxt=/^(ansi|ansi256)\(\s?(\d+)\s?\)$/,VF=(t,e)=>e==="foreground"?t:"bg"+t[0].toUpperCase()+t.slice(1);hD.default=(t,e,r)=>{if(!e)return t;if(e in pD.default){let a=VF(e,r);return pD.default[a](t)}if(e.startsWith("#")){let a=VF("hex",r);return pD.default[a](e)(t)}if(e.startsWith("ansi")){let a=Jxt.exec(e);if(!a)return t;let n=VF(a[1],r),c=Number(a[2]);return pD.default[n](c)(t)}if(e.startsWith("rgb")||e.startsWith("hsl")||e.startsWith("hsv")||e.startsWith("hwb")){let a=Kxt.exec(e);if(!a)return t;let n=VF(a[1],r),c=Number(a[2]),f=Number(a[3]),p=Number(a[4]);return pD.default[n](c,f,p)(t)}return t}});var qbe=L(gD=>{"use strict";var jbe=gD&&gD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(gD,"__esModule",{value:!0});var zxt=jbe(Pbe()),ZW=jbe(zW());gD.default=(t,e,r,s)=>{if(typeof r.style.borderStyle=="string"){let a=r.yogaNode.getComputedWidth(),n=r.yogaNode.getComputedHeight(),c=r.style.borderColor,f=zxt.default[r.style.borderStyle],p=ZW.default(f.topLeft+f.horizontal.repeat(a-2)+f.topRight,c,"foreground"),h=(ZW.default(f.vertical,c,"foreground")+` +`).repeat(n-2),E=ZW.default(f.bottomLeft+f.horizontal.repeat(a-2)+f.bottomRight,c,"foreground");s.write(t,e,p,{transformers:[]}),s.write(t,e+1,h,{transformers:[]}),s.write(t+a-1,e+1,h,{transformers:[]}),s.write(t,e+n-1,E,{transformers:[]})}}});var Wbe=L(dD=>{"use strict";var Om=dD&&dD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(dD,"__esModule",{value:!0});var Zxt=Om(Nm()),Xxt=Om(FW()),$xt=Om(vbe()),ekt=Om(UW()),tkt=Om(Sbe()),rkt=Om(jW()),nkt=Om(qbe()),ikt=(t,e)=>{var r;let s=(r=t.childNodes[0])===null||r===void 0?void 0:r.yogaNode;if(s){let a=s.getComputedLeft(),n=s.getComputedTop();e=` +`.repeat(n)+$xt.default(e,a)}return e},Gbe=(t,e,r)=>{var s;let{offsetX:a=0,offsetY:n=0,transformers:c=[],skipStaticElements:f}=r;if(f&&t.internal_static)return;let{yogaNode:p}=t;if(p){if(p.getDisplay()===Zxt.default.DISPLAY_NONE)return;let h=a+p.getComputedLeft(),E=n+p.getComputedTop(),C=c;if(typeof t.internal_transform=="function"&&(C=[t.internal_transform,...c]),t.nodeName==="ink-text"){let S=rkt.default(t);if(S.length>0){let P=Xxt.default(S),I=tkt.default(p);if(P>I){let R=(s=t.style.textWrap)!==null&&s!==void 0?s:"wrap";S=ekt.default(S,I,R)}S=ikt(t,S),e.write(h,E,S,{transformers:C})}return}if(t.nodeName==="ink-box"&&nkt.default(h,E,t,e),t.nodeName==="ink-root"||t.nodeName==="ink-box")for(let S of t.childNodes)Gbe(S,e,{offsetX:h,offsetY:E,transformers:C,skipStaticElements:f})}};dD.default=Gbe});var Kbe=L(mD=>{"use strict";var Vbe=mD&&mD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(mD,"__esModule",{value:!0});var Ybe=Vbe(MW()),skt=Vbe(iD()),XW=class{constructor(e){this.writes=[];let{width:r,height:s}=e;this.width=r,this.height=s}write(e,r,s,a){let{transformers:n}=a;s&&this.writes.push({x:e,y:r,text:s,transformers:n})}get(){let e=[];for(let s=0;ss.trimRight()).join(` +`),height:e.length}}};mD.default=XW});var Zbe=L(yD=>{"use strict";var $W=yD&&yD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(yD,"__esModule",{value:!0});var okt=$W(Nm()),Jbe=$W(Wbe()),zbe=$W(Kbe());yD.default=(t,e)=>{var r;if(t.yogaNode.setWidth(e),t.yogaNode){t.yogaNode.calculateLayout(void 0,void 0,okt.default.DIRECTION_LTR);let s=new zbe.default({width:t.yogaNode.getComputedWidth(),height:t.yogaNode.getComputedHeight()});Jbe.default(t,s,{skipStaticElements:!0});let a;!((r=t.staticNode)===null||r===void 0)&&r.yogaNode&&(a=new zbe.default({width:t.staticNode.yogaNode.getComputedWidth(),height:t.staticNode.yogaNode.getComputedHeight()}),Jbe.default(t.staticNode,a,{skipStaticElements:!1}));let{output:n,height:c}=s.get();return{output:n,outputHeight:c,staticOutput:a?`${a.get().output} +`:""}}return{output:"",outputHeight:0,staticOutput:""}}});var tPe=L((C0r,ePe)=>{"use strict";var Xbe=ye("stream"),$be=["assert","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"],eY={},akt=t=>{let e=new Xbe.PassThrough,r=new Xbe.PassThrough;e.write=a=>t("stdout",a),r.write=a=>t("stderr",a);let s=new console.Console(e,r);for(let a of $be)eY[a]=console[a],console[a]=s[a];return()=>{for(let a of $be)console[a]=eY[a];eY={}}};ePe.exports=akt});var rY=L(tY=>{"use strict";Object.defineProperty(tY,"__esModule",{value:!0});tY.default=new WeakMap});var iY=L(nY=>{"use strict";Object.defineProperty(nY,"__esModule",{value:!0});var lkt=hn(),rPe=lkt.createContext({exit:()=>{}});rPe.displayName="InternalAppContext";nY.default=rPe});var oY=L(sY=>{"use strict";Object.defineProperty(sY,"__esModule",{value:!0});var ckt=hn(),nPe=ckt.createContext({stdin:void 0,setRawMode:()=>{},isRawModeSupported:!1,internal_exitOnCtrlC:!0});nPe.displayName="InternalStdinContext";sY.default=nPe});var lY=L(aY=>{"use strict";Object.defineProperty(aY,"__esModule",{value:!0});var ukt=hn(),iPe=ukt.createContext({stdout:void 0,write:()=>{}});iPe.displayName="InternalStdoutContext";aY.default=iPe});var uY=L(cY=>{"use strict";Object.defineProperty(cY,"__esModule",{value:!0});var fkt=hn(),sPe=fkt.createContext({stderr:void 0,write:()=>{}});sPe.displayName="InternalStderrContext";cY.default=sPe});var KF=L(fY=>{"use strict";Object.defineProperty(fY,"__esModule",{value:!0});var Akt=hn(),oPe=Akt.createContext({activeId:void 0,add:()=>{},remove:()=>{},activate:()=>{},deactivate:()=>{},enableFocus:()=>{},disableFocus:()=>{},focusNext:()=>{},focusPrevious:()=>{},focus:()=>{}});oPe.displayName="InternalFocusContext";fY.default=oPe});var lPe=L((P0r,aPe)=>{"use strict";var pkt=/[|\\{}()[\]^$+*?.-]/g;aPe.exports=t=>{if(typeof t!="string")throw new TypeError("Expected a string");return t.replace(pkt,"\\$&")}});var APe=L((x0r,fPe)=>{"use strict";var hkt=lPe(),gkt=typeof process=="object"&&process&&typeof process.cwd=="function"?process.cwd():".",uPe=[].concat(ye("module").builtinModules,"bootstrap_node","node").map(t=>new RegExp(`(?:\\((?:node:)?${t}(?:\\.js)?:\\d+:\\d+\\)$|^\\s*at (?:node:)?${t}(?:\\.js)?:\\d+:\\d+$)`));uPe.push(/\((?:node:)?internal\/[^:]+:\d+:\d+\)$/,/\s*at (?:node:)?internal\/[^:]+:\d+:\d+$/,/\/\.node-spawn-wrap-\w+-\w+\/node:\d+:\d+\)?$/);var AY=class t{constructor(e){e={ignoredPackages:[],...e},"internals"in e||(e.internals=t.nodeInternals()),"cwd"in e||(e.cwd=gkt),this._cwd=e.cwd.replace(/\\/g,"/"),this._internals=[].concat(e.internals,dkt(e.ignoredPackages)),this._wrapCallSite=e.wrapCallSite||!1}static nodeInternals(){return[...uPe]}clean(e,r=0){r=" ".repeat(r),Array.isArray(e)||(e=e.split(` +`)),!/^\s*at /.test(e[0])&&/^\s*at /.test(e[1])&&(e=e.slice(1));let s=!1,a=null,n=[];return e.forEach(c=>{if(c=c.replace(/\\/g,"/"),this._internals.some(p=>p.test(c)))return;let f=/^\s*at /.test(c);s?c=c.trimEnd().replace(/^(\s+)at /,"$1"):(c=c.trim(),f&&(c=c.slice(3))),c=c.replace(`${this._cwd}/`,""),c&&(f?(a&&(n.push(a),a=null),n.push(c)):(s=!0,a=c))}),n.map(c=>`${r}${c} +`).join("")}captureString(e,r=this.captureString){typeof e=="function"&&(r=e,e=1/0);let{stackTraceLimit:s}=Error;e&&(Error.stackTraceLimit=e);let a={};Error.captureStackTrace(a,r);let{stack:n}=a;return Error.stackTraceLimit=s,this.clean(n)}capture(e,r=this.capture){typeof e=="function"&&(r=e,e=1/0);let{prepareStackTrace:s,stackTraceLimit:a}=Error;Error.prepareStackTrace=(f,p)=>this._wrapCallSite?p.map(this._wrapCallSite):p,e&&(Error.stackTraceLimit=e);let n={};Error.captureStackTrace(n,r);let{stack:c}=n;return Object.assign(Error,{prepareStackTrace:s,stackTraceLimit:a}),c}at(e=this.at){let[r]=this.capture(1,e);if(!r)return{};let s={line:r.getLineNumber(),column:r.getColumnNumber()};cPe(s,r.getFileName(),this._cwd),r.isConstructor()&&(s.constructor=!0),r.isEval()&&(s.evalOrigin=r.getEvalOrigin()),r.isNative()&&(s.native=!0);let a;try{a=r.getTypeName()}catch{}a&&a!=="Object"&&a!=="[object Object]"&&(s.type=a);let n=r.getFunctionName();n&&(s.function=n);let c=r.getMethodName();return c&&n!==c&&(s.method=c),s}parseLine(e){let r=e&&e.match(mkt);if(!r)return null;let s=r[1]==="new",a=r[2],n=r[3],c=r[4],f=Number(r[5]),p=Number(r[6]),h=r[7],E=r[8],C=r[9],S=r[10]==="native",P=r[11]===")",I,R={};if(E&&(R.line=Number(E)),C&&(R.column=Number(C)),P&&h){let N=0;for(let U=h.length-1;U>0;U--)if(h.charAt(U)===")")N++;else if(h.charAt(U)==="("&&h.charAt(U-1)===" "&&(N--,N===-1&&h.charAt(U-1)===" ")){let W=h.slice(0,U-1);h=h.slice(U+1),a+=` (${W}`;break}}if(a){let N=a.match(ykt);N&&(a=N[1],I=N[2])}return cPe(R,h,this._cwd),s&&(R.constructor=!0),n&&(R.evalOrigin=n,R.evalLine=f,R.evalColumn=p,R.evalFile=c&&c.replace(/\\/g,"/")),S&&(R.native=!0),a&&(R.function=a),I&&a!==I&&(R.method=I),R}};function cPe(t,e,r){e&&(e=e.replace(/\\/g,"/"),e.startsWith(`${r}/`)&&(e=e.slice(r.length+1)),t.file=e)}function dkt(t){if(t.length===0)return[];let e=t.map(r=>hkt(r));return new RegExp(`[/\\\\]node_modules[/\\\\](?:${e.join("|")})[/\\\\][^:]+:\\d+:\\d+`)}var mkt=new RegExp("^(?:\\s*at )?(?:(new) )?(?:(.*?) \\()?(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?(?:(.+?):(\\d+):(\\d+)|(native))(\\)?)$"),ykt=/^(.*?) \[as (.*?)\]$/;fPe.exports=AY});var hPe=L((k0r,pPe)=>{"use strict";pPe.exports=(t,e)=>t.replace(/^\t+/gm,r=>" ".repeat(r.length*(e||2)))});var dPe=L((Q0r,gPe)=>{"use strict";var Ekt=hPe(),Ikt=(t,e)=>{let r=[],s=t-e,a=t+e;for(let n=s;n<=a;n++)r.push(n);return r};gPe.exports=(t,e,r)=>{if(typeof t!="string")throw new TypeError("Source code is missing.");if(!e||e<1)throw new TypeError("Line number must start from `1`.");if(t=Ekt(t).split(/\r?\n/),!(e>t.length))return r={around:3,...r},Ikt(e,r.around).filter(s=>t[s-1]!==void 0).map(s=>({line:s,value:t[s-1]}))}});var JF=L(nf=>{"use strict";var Ckt=nf&&nf.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),wkt=nf&&nf.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),Bkt=nf&&nf.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.hasOwnProperty.call(t,r)&&Ckt(e,t,r);return wkt(e,t),e},vkt=nf&&nf.__rest||function(t,e){var r={};for(var s in t)Object.prototype.hasOwnProperty.call(t,s)&&e.indexOf(s)<0&&(r[s]=t[s]);if(t!=null&&typeof Object.getOwnPropertySymbols=="function")for(var a=0,s=Object.getOwnPropertySymbols(t);a{var{children:r}=t,s=vkt(t,["children"]);let a=Object.assign(Object.assign({},s),{marginLeft:s.marginLeft||s.marginX||s.margin||0,marginRight:s.marginRight||s.marginX||s.margin||0,marginTop:s.marginTop||s.marginY||s.margin||0,marginBottom:s.marginBottom||s.marginY||s.margin||0,paddingLeft:s.paddingLeft||s.paddingX||s.padding||0,paddingRight:s.paddingRight||s.paddingX||s.padding||0,paddingTop:s.paddingTop||s.paddingY||s.padding||0,paddingBottom:s.paddingBottom||s.paddingY||s.padding||0});return mPe.default.createElement("ink-box",{ref:e,style:a},r)});pY.displayName="Box";pY.defaultProps={flexDirection:"row",flexGrow:0,flexShrink:1};nf.default=pY});var dY=L(ED=>{"use strict";var hY=ED&&ED.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(ED,"__esModule",{value:!0});var Skt=hY(hn()),Cw=hY(YF()),yPe=hY(zW()),gY=({color:t,backgroundColor:e,dimColor:r,bold:s,italic:a,underline:n,strikethrough:c,inverse:f,wrap:p,children:h})=>{if(h==null)return null;let E=C=>(r&&(C=Cw.default.dim(C)),t&&(C=yPe.default(C,t,"foreground")),e&&(C=yPe.default(C,e,"background")),s&&(C=Cw.default.bold(C)),a&&(C=Cw.default.italic(C)),n&&(C=Cw.default.underline(C)),c&&(C=Cw.default.strikethrough(C)),f&&(C=Cw.default.inverse(C)),C);return Skt.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row",textWrap:p},internal_transform:E},h)};gY.displayName="Text";gY.defaultProps={dimColor:!1,bold:!1,italic:!1,underline:!1,strikethrough:!1,wrap:"wrap"};ED.default=gY});var wPe=L(sf=>{"use strict";var Dkt=sf&&sf.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),bkt=sf&&sf.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),Pkt=sf&&sf.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.hasOwnProperty.call(t,r)&&Dkt(e,t,r);return bkt(e,t),e},ID=sf&&sf.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(sf,"__esModule",{value:!0});var EPe=Pkt(ye("fs")),Ts=ID(hn()),IPe=ID(APe()),xkt=ID(dPe()),rh=ID(JF()),hA=ID(dY()),CPe=new IPe.default({cwd:process.cwd(),internals:IPe.default.nodeInternals()}),kkt=({error:t})=>{let e=t.stack?t.stack.split(` +`).slice(1):void 0,r=e?CPe.parseLine(e[0]):void 0,s,a=0;if(r?.file&&r?.line&&EPe.existsSync(r.file)){let n=EPe.readFileSync(r.file,"utf8");if(s=xkt.default(n,r.line),s)for(let{line:c}of s)a=Math.max(a,String(c).length)}return Ts.default.createElement(rh.default,{flexDirection:"column",padding:1},Ts.default.createElement(rh.default,null,Ts.default.createElement(hA.default,{backgroundColor:"red",color:"white"}," ","ERROR"," "),Ts.default.createElement(hA.default,null," ",t.message)),r&&Ts.default.createElement(rh.default,{marginTop:1},Ts.default.createElement(hA.default,{dimColor:!0},r.file,":",r.line,":",r.column)),r&&s&&Ts.default.createElement(rh.default,{marginTop:1,flexDirection:"column"},s.map(({line:n,value:c})=>Ts.default.createElement(rh.default,{key:n},Ts.default.createElement(rh.default,{width:a+1},Ts.default.createElement(hA.default,{dimColor:n!==r.line,backgroundColor:n===r.line?"red":void 0,color:n===r.line?"white":void 0},String(n).padStart(a," "),":")),Ts.default.createElement(hA.default,{key:n,backgroundColor:n===r.line?"red":void 0,color:n===r.line?"white":void 0}," "+c)))),t.stack&&Ts.default.createElement(rh.default,{marginTop:1,flexDirection:"column"},t.stack.split(` +`).slice(1).map(n=>{let c=CPe.parseLine(n);return c?Ts.default.createElement(rh.default,{key:n},Ts.default.createElement(hA.default,{dimColor:!0},"- "),Ts.default.createElement(hA.default,{dimColor:!0,bold:!0},c.function),Ts.default.createElement(hA.default,{dimColor:!0,color:"gray"}," ","(",c.file,":",c.line,":",c.column,")")):Ts.default.createElement(rh.default,{key:n},Ts.default.createElement(hA.default,{dimColor:!0},"- "),Ts.default.createElement(hA.default,{dimColor:!0,bold:!0},n))})))};sf.default=kkt});var vPe=L(of=>{"use strict";var Qkt=of&&of.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),Tkt=of&&of.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),Rkt=of&&of.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.hasOwnProperty.call(t,r)&&Qkt(e,t,r);return Tkt(e,t),e},Mm=of&&of.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(of,"__esModule",{value:!0});var Lm=Rkt(hn()),BPe=Mm(pW()),Fkt=Mm(iY()),Nkt=Mm(oY()),Okt=Mm(lY()),Lkt=Mm(uY()),Mkt=Mm(KF()),_kt=Mm(wPe()),Ukt=" ",Hkt="\x1B[Z",jkt="\x1B",zF=class extends Lm.PureComponent{constructor(){super(...arguments),this.state={isFocusEnabled:!0,activeFocusId:void 0,focusables:[],error:void 0},this.rawModeEnabledCount=0,this.handleSetRawMode=e=>{let{stdin:r}=this.props;if(!this.isRawModeSupported())throw r===process.stdin?new Error(`Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default. +Read about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported`):new Error(`Raw mode is not supported on the stdin provided to Ink. +Read about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported`);if(r.setEncoding("utf8"),e){this.rawModeEnabledCount===0&&(r.addListener("data",this.handleInput),r.resume(),r.setRawMode(!0)),this.rawModeEnabledCount++;return}--this.rawModeEnabledCount===0&&(r.setRawMode(!1),r.removeListener("data",this.handleInput),r.pause())},this.handleInput=e=>{e===""&&this.props.exitOnCtrlC&&this.handleExit(),e===jkt&&this.state.activeFocusId&&this.setState({activeFocusId:void 0}),this.state.isFocusEnabled&&this.state.focusables.length>0&&(e===Ukt&&this.focusNext(),e===Hkt&&this.focusPrevious())},this.handleExit=e=>{this.isRawModeSupported()&&this.handleSetRawMode(!1),this.props.onExit(e)},this.enableFocus=()=>{this.setState({isFocusEnabled:!0})},this.disableFocus=()=>{this.setState({isFocusEnabled:!1})},this.focus=e=>{this.setState(r=>r.focusables.some(a=>a?.id===e)?{activeFocusId:e}:r)},this.focusNext=()=>{this.setState(e=>{var r;let s=(r=e.focusables[0])===null||r===void 0?void 0:r.id;return{activeFocusId:this.findNextFocusable(e)||s}})},this.focusPrevious=()=>{this.setState(e=>{var r;let s=(r=e.focusables[e.focusables.length-1])===null||r===void 0?void 0:r.id;return{activeFocusId:this.findPreviousFocusable(e)||s}})},this.addFocusable=(e,{autoFocus:r})=>{this.setState(s=>{let a=s.activeFocusId;return!a&&r&&(a=e),{activeFocusId:a,focusables:[...s.focusables,{id:e,isActive:!0}]}})},this.removeFocusable=e=>{this.setState(r=>({activeFocusId:r.activeFocusId===e?void 0:r.activeFocusId,focusables:r.focusables.filter(s=>s.id!==e)}))},this.activateFocusable=e=>{this.setState(r=>({focusables:r.focusables.map(s=>s.id!==e?s:{id:e,isActive:!0})}))},this.deactivateFocusable=e=>{this.setState(r=>({activeFocusId:r.activeFocusId===e?void 0:r.activeFocusId,focusables:r.focusables.map(s=>s.id!==e?s:{id:e,isActive:!1})}))},this.findNextFocusable=e=>{var r;let s=e.focusables.findIndex(a=>a.id===e.activeFocusId);for(let a=s+1;a{var r;let s=e.focusables.findIndex(a=>a.id===e.activeFocusId);for(let a=s-1;a>=0;a--)if(!((r=e.focusables[a])===null||r===void 0)&&r.isActive)return e.focusables[a].id}}static getDerivedStateFromError(e){return{error:e}}isRawModeSupported(){return this.props.stdin.isTTY}render(){return Lm.default.createElement(Fkt.default.Provider,{value:{exit:this.handleExit}},Lm.default.createElement(Nkt.default.Provider,{value:{stdin:this.props.stdin,setRawMode:this.handleSetRawMode,isRawModeSupported:this.isRawModeSupported(),internal_exitOnCtrlC:this.props.exitOnCtrlC}},Lm.default.createElement(Okt.default.Provider,{value:{stdout:this.props.stdout,write:this.props.writeToStdout}},Lm.default.createElement(Lkt.default.Provider,{value:{stderr:this.props.stderr,write:this.props.writeToStderr}},Lm.default.createElement(Mkt.default.Provider,{value:{activeId:this.state.activeFocusId,add:this.addFocusable,remove:this.removeFocusable,activate:this.activateFocusable,deactivate:this.deactivateFocusable,enableFocus:this.enableFocus,disableFocus:this.disableFocus,focusNext:this.focusNext,focusPrevious:this.focusPrevious,focus:this.focus}},this.state.error?Lm.default.createElement(_kt.default,{error:this.state.error}):this.props.children)))))}componentDidMount(){BPe.default.hide(this.props.stdout)}componentWillUnmount(){BPe.default.show(this.props.stdout),this.isRawModeSupported()&&this.handleSetRawMode(!1)}componentDidCatch(e){this.handleExit(e)}};of.default=zF;zF.displayName="InternalApp"});var bPe=L(af=>{"use strict";var qkt=af&&af.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),Gkt=af&&af.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),Wkt=af&&af.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.hasOwnProperty.call(t,r)&&qkt(e,t,r);return Gkt(e,t),e},lf=af&&af.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(af,"__esModule",{value:!0});var Ykt=lf(hn()),SPe=rH(),Vkt=lf(wDe()),Kkt=lf(lW()),Jkt=lf(PDe()),zkt=lf(kDe()),mY=lf(wbe()),Zkt=lf(Zbe()),Xkt=lf(AW()),$kt=lf(tPe()),eQt=Wkt(qW()),tQt=lf(rY()),rQt=lf(vPe()),ww=process.env.CI==="false"?!1:Jkt.default,DPe=()=>{},yY=class{constructor(e){this.resolveExitPromise=()=>{},this.rejectExitPromise=()=>{},this.unsubscribeExit=()=>{},this.onRender=()=>{if(this.isUnmounted)return;let{output:r,outputHeight:s,staticOutput:a}=Zkt.default(this.rootNode,this.options.stdout.columns||80),n=a&&a!==` +`;if(this.options.debug){n&&(this.fullStaticOutput+=a),this.options.stdout.write(this.fullStaticOutput+r);return}if(ww){n&&this.options.stdout.write(a),this.lastOutput=r;return}if(n&&(this.fullStaticOutput+=a),s>=this.options.stdout.rows){this.options.stdout.write(Kkt.default.clearTerminal+this.fullStaticOutput+r),this.lastOutput=r;return}n&&(this.log.clear(),this.options.stdout.write(a),this.log(r)),!n&&r!==this.lastOutput&&this.throttledLog(r),this.lastOutput=r},zkt.default(this),this.options=e,this.rootNode=eQt.createNode("ink-root"),this.rootNode.onRender=e.debug?this.onRender:SPe(this.onRender,32,{leading:!0,trailing:!0}),this.rootNode.onImmediateRender=this.onRender,this.log=Vkt.default.create(e.stdout),this.throttledLog=e.debug?this.log:SPe(this.log,void 0,{leading:!0,trailing:!0}),this.isUnmounted=!1,this.lastOutput="",this.fullStaticOutput="",this.container=mY.default.createContainer(this.rootNode,0,!1,null),this.unsubscribeExit=Xkt.default(this.unmount,{alwaysLast:!1}),e.patchConsole&&this.patchConsole(),ww||(e.stdout.on("resize",this.onRender),this.unsubscribeResize=()=>{e.stdout.off("resize",this.onRender)})}render(e){let r=Ykt.default.createElement(rQt.default,{stdin:this.options.stdin,stdout:this.options.stdout,stderr:this.options.stderr,writeToStdout:this.writeToStdout,writeToStderr:this.writeToStderr,exitOnCtrlC:this.options.exitOnCtrlC,onExit:this.unmount},e);mY.default.updateContainer(r,this.container,null,DPe)}writeToStdout(e){if(!this.isUnmounted){if(this.options.debug){this.options.stdout.write(e+this.fullStaticOutput+this.lastOutput);return}if(ww){this.options.stdout.write(e);return}this.log.clear(),this.options.stdout.write(e),this.log(this.lastOutput)}}writeToStderr(e){if(!this.isUnmounted){if(this.options.debug){this.options.stderr.write(e),this.options.stdout.write(this.fullStaticOutput+this.lastOutput);return}if(ww){this.options.stderr.write(e);return}this.log.clear(),this.options.stderr.write(e),this.log(this.lastOutput)}}unmount(e){this.isUnmounted||(this.onRender(),this.unsubscribeExit(),typeof this.restoreConsole=="function"&&this.restoreConsole(),typeof this.unsubscribeResize=="function"&&this.unsubscribeResize(),ww?this.options.stdout.write(this.lastOutput+` +`):this.options.debug||this.log.done(),this.isUnmounted=!0,mY.default.updateContainer(null,this.container,null,DPe),tQt.default.delete(this.options.stdout),e instanceof Error?this.rejectExitPromise(e):this.resolveExitPromise())}waitUntilExit(){return this.exitPromise||(this.exitPromise=new Promise((e,r)=>{this.resolveExitPromise=e,this.rejectExitPromise=r})),this.exitPromise}clear(){!ww&&!this.options.debug&&this.log.clear()}patchConsole(){this.options.debug||(this.restoreConsole=$kt.default((e,r)=>{e==="stdout"&&this.writeToStdout(r),e==="stderr"&&(r.startsWith("The above error occurred")||this.writeToStderr(r))}))}};af.default=yY});var xPe=L(CD=>{"use strict";var PPe=CD&&CD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(CD,"__esModule",{value:!0});var nQt=PPe(bPe()),ZF=PPe(rY()),iQt=ye("stream"),sQt=(t,e)=>{let r=Object.assign({stdout:process.stdout,stdin:process.stdin,stderr:process.stderr,debug:!1,exitOnCtrlC:!0,patchConsole:!0},oQt(e)),s=aQt(r.stdout,()=>new nQt.default(r));return s.render(t),{rerender:s.render,unmount:()=>s.unmount(),waitUntilExit:s.waitUntilExit,cleanup:()=>ZF.default.delete(r.stdout),clear:s.clear}};CD.default=sQt;var oQt=(t={})=>t instanceof iQt.Stream?{stdout:t,stdin:process.stdin}:t,aQt=(t,e)=>{let r;return ZF.default.has(t)?r=ZF.default.get(t):(r=e(),ZF.default.set(t,r)),r}});var QPe=L(nh=>{"use strict";var lQt=nh&&nh.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r),Object.defineProperty(t,s,{enumerable:!0,get:function(){return e[r]}})}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),cQt=nh&&nh.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),uQt=nh&&nh.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.hasOwnProperty.call(t,r)&&lQt(e,t,r);return cQt(e,t),e};Object.defineProperty(nh,"__esModule",{value:!0});var wD=uQt(hn()),kPe=t=>{let{items:e,children:r,style:s}=t,[a,n]=wD.useState(0),c=wD.useMemo(()=>e.slice(a),[e,a]);wD.useLayoutEffect(()=>{n(e.length)},[e.length]);let f=c.map((h,E)=>r(h,a+E)),p=wD.useMemo(()=>Object.assign({position:"absolute",flexDirection:"column"},s),[s]);return wD.default.createElement("ink-box",{internal_static:!0,style:p},f)};kPe.displayName="Static";nh.default=kPe});var RPe=L(BD=>{"use strict";var fQt=BD&&BD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(BD,"__esModule",{value:!0});var AQt=fQt(hn()),TPe=({children:t,transform:e})=>t==null?null:AQt.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row"},internal_transform:e},t);TPe.displayName="Transform";BD.default=TPe});var NPe=L(vD=>{"use strict";var pQt=vD&&vD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(vD,"__esModule",{value:!0});var hQt=pQt(hn()),FPe=({count:t=1})=>hQt.default.createElement("ink-text",null,` +`.repeat(t));FPe.displayName="Newline";vD.default=FPe});var MPe=L(SD=>{"use strict";var OPe=SD&&SD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(SD,"__esModule",{value:!0});var gQt=OPe(hn()),dQt=OPe(JF()),LPe=()=>gQt.default.createElement(dQt.default,{flexGrow:1});LPe.displayName="Spacer";SD.default=LPe});var XF=L(DD=>{"use strict";var mQt=DD&&DD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(DD,"__esModule",{value:!0});var yQt=hn(),EQt=mQt(oY()),IQt=()=>yQt.useContext(EQt.default);DD.default=IQt});var UPe=L(bD=>{"use strict";var CQt=bD&&bD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(bD,"__esModule",{value:!0});var _Pe=hn(),wQt=CQt(XF()),BQt=(t,e={})=>{let{stdin:r,setRawMode:s,internal_exitOnCtrlC:a}=wQt.default();_Pe.useEffect(()=>{if(e.isActive!==!1)return s(!0),()=>{s(!1)}},[e.isActive,s]),_Pe.useEffect(()=>{if(e.isActive===!1)return;let n=c=>{let f=String(c),p={upArrow:f==="\x1B[A",downArrow:f==="\x1B[B",leftArrow:f==="\x1B[D",rightArrow:f==="\x1B[C",pageDown:f==="\x1B[6~",pageUp:f==="\x1B[5~",return:f==="\r",escape:f==="\x1B",ctrl:!1,shift:!1,tab:f===" "||f==="\x1B[Z",backspace:f==="\b",delete:f==="\x7F"||f==="\x1B[3~",meta:!1};f<=""&&!p.return&&(f=String.fromCharCode(f.charCodeAt(0)+97-1),p.ctrl=!0),f.startsWith("\x1B")&&(f=f.slice(1),p.meta=!0);let h=f>="A"&&f<="Z",E=f>="\u0410"&&f<="\u042F";f.length===1&&(h||E)&&(p.shift=!0),p.tab&&f==="[Z"&&(p.shift=!0),(p.tab||p.backspace||p.delete)&&(f=""),(!(f==="c"&&p.ctrl)||!a)&&t(f,p)};return r?.on("data",n),()=>{r?.off("data",n)}},[e.isActive,r,a,t])};bD.default=BQt});var HPe=L(PD=>{"use strict";var vQt=PD&&PD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(PD,"__esModule",{value:!0});var SQt=hn(),DQt=vQt(iY()),bQt=()=>SQt.useContext(DQt.default);PD.default=bQt});var jPe=L(xD=>{"use strict";var PQt=xD&&xD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(xD,"__esModule",{value:!0});var xQt=hn(),kQt=PQt(lY()),QQt=()=>xQt.useContext(kQt.default);xD.default=QQt});var qPe=L(kD=>{"use strict";var TQt=kD&&kD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(kD,"__esModule",{value:!0});var RQt=hn(),FQt=TQt(uY()),NQt=()=>RQt.useContext(FQt.default);kD.default=NQt});var WPe=L(TD=>{"use strict";var GPe=TD&&TD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(TD,"__esModule",{value:!0});var QD=hn(),OQt=GPe(KF()),LQt=GPe(XF()),MQt=({isActive:t=!0,autoFocus:e=!1,id:r}={})=>{let{isRawModeSupported:s,setRawMode:a}=LQt.default(),{activeId:n,add:c,remove:f,activate:p,deactivate:h,focus:E}=QD.useContext(OQt.default),C=QD.useMemo(()=>r??Math.random().toString().slice(2,7),[r]);return QD.useEffect(()=>(c(C,{autoFocus:e}),()=>{f(C)}),[C,e]),QD.useEffect(()=>{t?p(C):h(C)},[t,C]),QD.useEffect(()=>{if(!(!s||!t))return a(!0),()=>{a(!1)}},[t]),{isFocused:!!C&&n===C,focus:E}};TD.default=MQt});var YPe=L(RD=>{"use strict";var _Qt=RD&&RD.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(RD,"__esModule",{value:!0});var UQt=hn(),HQt=_Qt(KF()),jQt=()=>{let t=UQt.useContext(HQt.default);return{enableFocus:t.enableFocus,disableFocus:t.disableFocus,focusNext:t.focusNext,focusPrevious:t.focusPrevious,focus:t.focus}};RD.default=jQt});var VPe=L(EY=>{"use strict";Object.defineProperty(EY,"__esModule",{value:!0});EY.default=t=>{var e,r,s,a;return{width:(r=(e=t.yogaNode)===null||e===void 0?void 0:e.getComputedWidth())!==null&&r!==void 0?r:0,height:(a=(s=t.yogaNode)===null||s===void 0?void 0:s.getComputedHeight())!==null&&a!==void 0?a:0}}});var Vc=L(Eo=>{"use strict";Object.defineProperty(Eo,"__esModule",{value:!0});var qQt=xPe();Object.defineProperty(Eo,"render",{enumerable:!0,get:function(){return qQt.default}});var GQt=JF();Object.defineProperty(Eo,"Box",{enumerable:!0,get:function(){return GQt.default}});var WQt=dY();Object.defineProperty(Eo,"Text",{enumerable:!0,get:function(){return WQt.default}});var YQt=QPe();Object.defineProperty(Eo,"Static",{enumerable:!0,get:function(){return YQt.default}});var VQt=RPe();Object.defineProperty(Eo,"Transform",{enumerable:!0,get:function(){return VQt.default}});var KQt=NPe();Object.defineProperty(Eo,"Newline",{enumerable:!0,get:function(){return KQt.default}});var JQt=MPe();Object.defineProperty(Eo,"Spacer",{enumerable:!0,get:function(){return JQt.default}});var zQt=UPe();Object.defineProperty(Eo,"useInput",{enumerable:!0,get:function(){return zQt.default}});var ZQt=HPe();Object.defineProperty(Eo,"useApp",{enumerable:!0,get:function(){return ZQt.default}});var XQt=XF();Object.defineProperty(Eo,"useStdin",{enumerable:!0,get:function(){return XQt.default}});var $Qt=jPe();Object.defineProperty(Eo,"useStdout",{enumerable:!0,get:function(){return $Qt.default}});var eTt=qPe();Object.defineProperty(Eo,"useStderr",{enumerable:!0,get:function(){return eTt.default}});var tTt=WPe();Object.defineProperty(Eo,"useFocus",{enumerable:!0,get:function(){return tTt.default}});var rTt=YPe();Object.defineProperty(Eo,"useFocusManager",{enumerable:!0,get:function(){return rTt.default}});var nTt=VPe();Object.defineProperty(Eo,"measureElement",{enumerable:!0,get:function(){return nTt.default}})});var CY={};Vt(CY,{Gem:()=>IY});var KPe,_m,IY,$F=It(()=>{KPe=et(Vc()),_m=et(hn()),IY=(0,_m.memo)(({active:t})=>{let e=(0,_m.useMemo)(()=>t?"\u25C9":"\u25EF",[t]),r=(0,_m.useMemo)(()=>t?"green":"yellow",[t]);return _m.default.createElement(KPe.Text,{color:r},e)})});var zPe={};Vt(zPe,{useKeypress:()=>Um});function Um({active:t},e,r){let{stdin:s}=(0,JPe.useStdin)(),a=(0,eN.useCallback)((n,c)=>e(n,c),r);(0,eN.useEffect)(()=>{if(!(!t||!s))return s.on("keypress",a),()=>{s.off("keypress",a)}},[t,a,s])}var JPe,eN,FD=It(()=>{JPe=et(Vc()),eN=et(hn())});var XPe={};Vt(XPe,{FocusRequest:()=>ZPe,useFocusRequest:()=>wY});var ZPe,wY,BY=It(()=>{FD();ZPe=(r=>(r.BEFORE="before",r.AFTER="after",r))(ZPe||{}),wY=function({active:t},e,r){Um({active:t},(s,a)=>{a.name==="tab"&&(a.shift?e("before"):e("after"))},r)}});var $Pe={};Vt($Pe,{useListInput:()=>ND});var ND,tN=It(()=>{FD();ND=function(t,e,{active:r,minus:s,plus:a,set:n,loop:c=!0}){Um({active:r},(f,p)=>{let h=e.indexOf(t);switch(p.name){case s:{let E=h-1;if(c){n(e[(e.length+E)%e.length]);return}if(E<0)return;n(e[E])}break;case a:{let E=h+1;if(c){n(e[E%e.length]);return}if(E>=e.length)return;n(e[E])}break}},[e,t,a,n,c])}});var rN={};Vt(rN,{ScrollableItems:()=>iTt});var tg,ml,iTt,nN=It(()=>{tg=et(Vc()),ml=et(hn());BY();tN();iTt=({active:t=!0,children:e=[],radius:r=10,size:s=1,loop:a=!0,onFocusRequest:n,willReachEnd:c})=>{let f=N=>{if(N.key===null)throw new Error("Expected all children to have a key");return N.key},p=ml.default.Children.map(e,N=>f(N)),h=p[0],[E,C]=(0,ml.useState)(h),S=p.indexOf(E);(0,ml.useEffect)(()=>{p.includes(E)||C(h)},[e]),(0,ml.useEffect)(()=>{c&&S>=p.length-2&&c()},[S]),wY({active:t&&!!n},N=>{n?.(N)},[n]),ND(E,p,{active:t,minus:"up",plus:"down",set:C,loop:a});let P=S-r,I=S+r;I>p.length&&(P-=I-p.length,I=p.length),P<0&&(I+=-P,P=0),I>=p.length&&(I=p.length-1);let R=[];for(let N=P;N<=I;++N){let U=p[N],W=t&&U===E;R.push(ml.default.createElement(tg.Box,{key:U,height:s},ml.default.createElement(tg.Box,{marginLeft:1,marginRight:1},ml.default.createElement(tg.Text,null,W?ml.default.createElement(tg.Text,{color:"cyan",bold:!0},">"):" ")),ml.default.createElement(tg.Box,null,ml.default.cloneElement(e[N],{active:W}))))}return ml.default.createElement(tg.Box,{flexDirection:"column",width:"100%"},R)}});var exe,ih,txe,vY,rxe,SY=It(()=>{exe=et(Vc()),ih=et(hn()),txe=ye("readline"),vY=ih.default.createContext(null),rxe=({children:t})=>{let{stdin:e,setRawMode:r}=(0,exe.useStdin)();(0,ih.useEffect)(()=>{r&&r(!0),e&&(0,txe.emitKeypressEvents)(e)},[e,r]);let[s,a]=(0,ih.useState)(new Map),n=(0,ih.useMemo)(()=>({getAll:()=>s,get:c=>s.get(c),set:(c,f)=>a(new Map([...s,[c,f]]))}),[s,a]);return ih.default.createElement(vY.Provider,{value:n,children:t})}});var DY={};Vt(DY,{useMinistore:()=>sTt});function sTt(t,e){let r=(0,iN.useContext)(vY);if(r===null)throw new Error("Expected this hook to run with a ministore context attached");if(typeof t>"u")return r.getAll();let s=(0,iN.useCallback)(n=>{r.set(t,n)},[t,r.set]),a=r.get(t);return typeof a>"u"&&(a=e),[a,s]}var iN,bY=It(()=>{iN=et(hn());SY()});var oN={};Vt(oN,{renderForm:()=>oTt});async function oTt(t,e,{stdin:r,stdout:s,stderr:a}){let n,c=p=>{let{exit:h}=(0,sN.useApp)();Um({active:!0},(E,C)=>{C.name==="return"&&(n=p,h())},[h,p])},{waitUntilExit:f}=(0,sN.render)(PY.default.createElement(rxe,null,PY.default.createElement(t,{...e,useSubmit:c})),{stdin:r,stdout:s,stderr:a});return await f(),n}var sN,PY,aN=It(()=>{sN=et(Vc()),PY=et(hn());SY();FD()});var oxe=L(OD=>{"use strict";Object.defineProperty(OD,"__esModule",{value:!0});OD.UncontrolledTextInput=void 0;var ixe=hn(),xY=hn(),nxe=Vc(),Hm=YF(),sxe=({value:t,placeholder:e="",focus:r=!0,mask:s,highlightPastedText:a=!1,showCursor:n=!0,onChange:c,onSubmit:f})=>{let[{cursorOffset:p,cursorWidth:h},E]=xY.useState({cursorOffset:(t||"").length,cursorWidth:0});xY.useEffect(()=>{E(R=>{if(!r||!n)return R;let N=t||"";return R.cursorOffset>N.length-1?{cursorOffset:N.length,cursorWidth:0}:R})},[t,r,n]);let C=a?h:0,S=s?s.repeat(t.length):t,P=S,I=e?Hm.grey(e):void 0;if(n&&r){I=e.length>0?Hm.inverse(e[0])+Hm.grey(e.slice(1)):Hm.inverse(" "),P=S.length>0?"":Hm.inverse(" ");let R=0;for(let N of S)R>=p-C&&R<=p?P+=Hm.inverse(N):P+=N,R++;S.length>0&&p===S.length&&(P+=Hm.inverse(" "))}return nxe.useInput((R,N)=>{if(N.upArrow||N.downArrow||N.ctrl&&R==="c"||N.tab||N.shift&&N.tab)return;if(N.return){f&&f(t);return}let U=p,W=t,te=0;N.leftArrow?n&&U--:N.rightArrow?n&&U++:N.backspace||N.delete?p>0&&(W=t.slice(0,p-1)+t.slice(p,t.length),U--):(W=t.slice(0,p)+R+t.slice(p,t.length),U+=R.length,R.length>1&&(te=R.length)),p<0&&(U=0),p>t.length&&(U=t.length),E({cursorOffset:U,cursorWidth:te}),W!==t&&c(W)},{isActive:r}),ixe.createElement(nxe.Text,null,e?S.length>0?P:I:P)};OD.default=sxe;OD.UncontrolledTextInput=({initialValue:t="",...e})=>{let[r,s]=xY.useState(t);return ixe.createElement(sxe,Object.assign({},e,{value:r,onChange:s}))}});var cxe={};Vt(cxe,{Pad:()=>kY});var axe,lxe,kY,QY=It(()=>{axe=et(Vc()),lxe=et(hn()),kY=({length:t,active:e})=>{if(t===0)return null;let r=t>1?` ${"-".repeat(t-1)}`:" ";return lxe.default.createElement(axe.Text,{dimColor:!e},r)}});var uxe={};Vt(uxe,{ItemOptions:()=>aTt});var MD,rg,aTt,fxe=It(()=>{MD=et(Vc()),rg=et(hn());tN();$F();QY();aTt=function({active:t,skewer:e,options:r,value:s,onChange:a,sizes:n=[]}){let c=r.filter(({label:p})=>!!p).map(({value:p})=>p),f=r.findIndex(p=>p.value===s&&p.label!="");return ND(s,c,{active:t,minus:"left",plus:"right",set:a}),rg.default.createElement(rg.default.Fragment,null,r.map(({label:p},h)=>{let E=h===f,C=n[h]-1||0,S=p.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,""),P=Math.max(0,C-S.length-2);return p?rg.default.createElement(MD.Box,{key:p,width:C,marginLeft:1},rg.default.createElement(MD.Text,{wrap:"truncate"},rg.default.createElement(IY,{active:E})," ",p),e?rg.default.createElement(kY,{active:t,length:P}):null):rg.default.createElement(MD.Box,{key:`spacer-${h}`,width:C,marginLeft:1})}))}});var Dxe=L((Ndr,Sxe)=>{var jY;Sxe.exports=()=>(typeof jY>"u"&&(jY=ye("zlib").brotliDecompressSync(Buffer.from("Wx6iVsM8y37oTpDqz9ttuZc9II7bU8Dm0eSoiEX5X+cI6oZJXQfiuc4xndBuXaAQQxqqqnlJZYxtR/YfQKWsqrIlDzhSaK0b0Sl4sGIivE3xwFR3yFnY7YHRO/xw5NmsXhLGMmIJnQ7RQOSgLL9ts5fdaYhcxoWHF7dahKcbL7xdpZna+sOZHQ3C9aU56oudzh85R5BU6q3+VceftEQSBD0HUBi3vlcAQxQJJXS6NubAera9xHt4WLyEj/DTf2xqnfHl9KwwY4nyvz1tK1taQwTRw0R2J01oLV0sv0ZNGpLrcMPW3wSK8dBkiX/hvpvN7J/Pa/EVRKpkyjCk+Hp9OUWGhcRbQBPgmnfO//bO/uubdIUpwz5xJof7RDxrN6HZUguxathf+nrP5eR02lnTdac+CEfPIPEQONnqWLfllz+tvn61uxegTmZDxpeYFBgfTArYbsME6aHr7jHYVfjZ8hXR0aFbef0186b7kBPUWMxO69JY0mkI2VZfSVctgoJx8qX7Vqpmr6ainSnTsfwYuhhPxJq81wGrwRFj82d0+nuz//58jdJ7jNXB6aX3NFIRgdBmnyiQq1SEbAqzxF0WECarcjoIWVuN5tNi+TBQMBscGC0P+rXm1/E6v5mwHsFaHk5AMy03wxY/9YTk6vvpdFwTbscrqwR29Td96Z4dLDi+AISU7/zj4f0CpCXvONrV2ktiQAFDzA0MiOJC2rpUgP/oXOPggHqNG99PQvnC4QcJwmaNBeV61L+1145XwNApR0mrG2akK1l51Fu/En0kzKoo+mGx+cdDD6bo99vjm8kkG2DBbIhIb0jrbIiIatsl+vGNreNhD1LZrh3ffAYcFOqBVHQzXD7kbpi4+6WB7eZoCBPwA+xHP5r/9Pmxu3uJmjzzeaq6uikG0AJ7lPmbMNeCoI43TILGjxpq/fGw+3+wrezIx/eqq6EQYDcKSuSbLE+qiTLBMkqQBh6xdP3x8NsAW49PsiYR3Ww/UmXh7clfY8DSTev96F0FZpBgFDz//6nqDwdJfunT/Q5B4UIVqrZnNmVfyF5k0rny/f/v/dSqqtqBoFwbYybT9hQAqr0dDHvN45979t3Ct2I4SAgArAKNVpKSciUpprH3mPu+DSgiQKkBSJWLpEqV3oza+uGoe9yDWc9GEWCbcmbW/39fqtX2vv8DgQAhUSDFtEHLmUk7exDTXZOrTm87AFC2phxm9TgvNuZ797539N97P9LxfwTKET8ClYgfwDEjQJ5kRAB9CID8PwDQAYhSg5IyG6TtPJTT2U3JzjrMcRJB6hxTlM8xRakGydmD7R7dw7hV1jBOq6pejWdfw9zjsKp973qz7/Wid71c1mrZi2X7/7/8d5bSJNKGeIpHCTJz9+zUqlkY/07d+X+Rge6aUfLOj3lx4D+/5qe99933zpvQZNum6ue3LFSFuW8yf4lUSZlN5v5ZCBQQJHCShfwiuOoq9FXASpDzlbJywbCTVyi8DXFpDl9lsMJzLsv+bIOILqZ/M0P3IBmn2n6SBpZgqcT/fxwsrXPhq74JKKSAEvCEaEV8zVotS7XhUZRHIoxh0yF8v1qJRX1nyWyPu/J3y3SFaNvAGXgquv2y/gRu1v+k28JesS/drYDHCIQgSQiWoFZaVALBPEBXngywzf4PFdg5ef5cgoGESoo2UUYhm5E4tPe3i977UUST2xXhY/MH7K/f9j/Hx84wiyzfr40FNgRURIy6pbfC25T9sv8eOHVhExcSQZ4KxEy8+O/6VmBhIVAIBAYKgcBAIFD4Agez0/9/0Jx38/2f4QyGmODBBCWYoMEQDR40GKpSUTQIKqgGF+5wofn8TF1f9Ne70uHfZ0BAQIOABg0CAqJTgHUKMAgwsJ4MDOpJBrZ08k8q/wNyd9f2gQcCAgwCDAIs1cCqDKzKwMBSFxgYLFiwYFQNiJ/bf/98p8+1z/1atNiixRZbIBAIMkEgSpBMCTJBIJgEUS8pUaLEErxPjZ0N/mZ+xd5RmXiDBygVtROd2c9/hKMk2faG0K3vD1fRE5Cra4OeAqQhJIQSaldpXUAsbd1X/u8Jmcy4OoSb9f/oFaixfWK7BQqFJEhCwAuFIMWkpYhIEqxU//f4PKlHlH8VSgf8q0a+G9cecRRLrDewqDXIr1HkZZwHWG83yHqVyUtb5cXAGmyCEiA/fKbWva8f37WBtBDNhd5ukA/tzc4CosZIjfHUL+E6vhZeA6tt7cdwv3VOu6Ad6hZsEj/dcyf8Koc+Ii/1E0m93QTEr8X7TPx6v0Hw4hgT0NsiBzi/Ojr+aAjNlK5T+VHQGly0ERkOwSh/vRliHz3BItngE8RENKNdGrxiiL5hBGi5rcwT0QlJFatE4bIbzXe0McICrXV/xde1yXPZyaRUs7gU+MpkzOHxhxVGu+jvWUOSpCNhdEBczkhaTU/m9qyaFOTubSWcVZ3SaKxWvsT9oA762PXd6Fpe/O8eGFtrbQv9H5jUkP9Xv4L9yt3GEuZDICzdqhhX6bybxUCiJdKJVt+IvaaA8pBXb9aP2spgL/w4jR8UmO3+smtT0A+0hFLC9wvrMrl8Dd1ndAnhiyfRVSXrzN4LHh9xAHkaO4/8Q8IS00EE3nPzHWfECG3QIQwbjoe0k5iOovmQMBsoifhgSMQWjU0QhkWqELzEYEh0etfEGCG/mT41Cqk+uWKIGR9a3uepyL+fhJbtKzj//RQZtS/ycolxB8RZCGjrzeaK78ojq5ky3j7HIZ76kpqV7qp3f9rsQ9ORRWkEdji+zm/K1QMX8IfIoXv44nD5BcFG3zGUklDKnUTbINPf0KuNprc9I8vRhHEWn6Mevc/kMldwancCJglrytG4wtx+QVKlcdFagd+ifV4h9mkojgAHI0Yutc+QzeZ72wAfQiWJPN6thWo1Fq51zEZ/abkgV1BxRLa/Y3VIyexOxU+B5OHvrXoqIFLo5R+9AjP55vc1dLSvIYxt8fPVD5Bt+aDn/+QUR4BSWphE0j5mFv7eCgkKlCQiFzPG3iehYMSoKF8d5bOx98JIJgq+4cvSv84ye+Uk6+9RW84h4skdf+pKOunpUvu6Yp6K/R+ezL63icRaPpzoIuS9jchG4DXTGeMtW4/ttHAWqEf/yIAM/8oyJoBvylHmB8Uu+9NTMWWMqf18uFrGXgE+VdvznXGVl/+bjv0G2xs0ZSjCu6SlnfQxnoCfh6xvafwQB4N+nJffQKB+vActlnzfHzFclcrXdZS16BjvPr8k4yr9pZZKeUCaO6y7o+zV9OhVKIGzqAQH7M4o+yb6k1JJ3BTl3Poiweyk450Mrjd624ba95IcB8lQRpsMl96/quD8W5Jx/swK6wG2+3Zeyhwu278j8jLzuv6O59ocMbP8JgciFip943CXFsBLWEIYhUW4wC1sb9pYS4kZ3UJ+C/kt5p+dPyctkvzTMs1dWCgvjamuDCDjTghl2ykbWi6TXXkLBmtQfwVxHyb9qAdwCenDxP8EHMA8HzD5+QBap16HHGr5tnstysVebx275eK9qqnLhKZemkf+faykRK0Ihgj/SC/y2JWYYzK4EKN/QFg5m4Le7WJ5Xj50NzPuiBbJpzxltmqmElpC2skoBl+8l6P5H2GtjcVMK4hohyPqSfJKkQMVW0W2u4is8mYeTzug8pSgrTFMRh/m5N4NotSL5IqK6dEWl6rw/KlpSBFVFMgstbby2bKSgMQ1ZcksZBcVYFw7Xoxb0oO3b7BJsD1Sednx5u3Lbm13GGPF1KCdSOkr6Qkzo5Qf/vMDzqrHIedVyZQxwnl9a5toMJGYfJEAbvcRQV8FQdxKJ9Z2T8O4kQ6vtyyesmVPstmSUH5MJ/o7OiWZtrS/QzGINI/IOm4Q8DDSxKI2nQSJ1U3U9vSkxvtdhNCpgwbu5PHRyQNAMA+wKyeCm32Ibd9JyMTIU9OeXynIz3k8q4ovMxbXTxG9nkZWst6eJoOtvXVdLIqO31LBlOrPyitw967ni5roPG92lTTvhNSJf4P4cuMN2pfZspUiBdxNUzHLj5y6qB/2ajpZ+ZP4VPZN+hCzacWYtNdfJF3VlDd78njhx36F7SVFBKm/94aeX/xfskxdBrotrbw6fNiCJaa/g3lksHQrS9/7KyTxkPKqEXv4KNyv5K5cwHthJI7K8vqeKVh3OYro8ESEJz+5TP3eExO6OWaHPEzjjd+Pfg/kqyCifid6BVdaUHgmVFDqT5VHoN47yMsrayq2foT9WaS1f2o1iQPeNdVyjB14t8OrllHUluJ0teDqrYTZFZm6HNQs2AyUei6/8sXt/kpheFe2/0reuhKFxWFRl3zaygGdsepcsjpRP+Fe8QGPnaF1bqISrSPlp4iK0Z6SAJzOQNtxFQb+EoL3EdEv/zNxzBt3scaovgp7S2NsdlRyxyrncjCF9PLQNFsjyZZe5cheSHRin3BouoVTLa4LJR0M+iSUaqh6P9hdewKtOKBjWvbjwcQcllujNcbVX//noV1zBJTM3s+F2McT517FoFbS+tTlS1JQI+OlflmRoIgltiF+3xHaICWpV84rYNfAwYWfU1BDYoyy4vMvy7qaggqZF4FtZQCSxmMMU6n4TVnOoeCKSlW0CaZoihUm0U3mhgL54Z+9YGwHN5raP+eBfJb9T15L60ZP26O7x2tG6sa4f0y/cmf4X9D8/j3lJWlWUyL16zlFF9kssyROJtTZPtVS31cFLDk2dj/+EnkPdwF/toVCQC1vwGL0ZGOKUbXAxxUOhe9UyDMUbHww4VKR2dxXMESDAKmsUCzp7F5h/ToMHVE/7S/A9K/Rb45BhY3HeVOvXRwahS2GUK83vRIT9JZmHhoBvIcW76djG2iljbkX9ZhD2jmIwHIURIz5CgqGGH01FbbPsyFVDcSniN1DJ1K4h1PUdbLNwaaLRYtnWz0sQ8y24JjrBbyPfO4Iwyq6S8Y/ksLC+qz99DNA8iyCJi4C3LsVz5fSubnZn+0pnbquH1uknY4eJivf7DSfl6JIVgSIImtIb1oJFKO2Lip6U+lEZ6ZMmnUG3zcGvX3edi4wrm/unSQdrkmRp/gFt4VwFJb/vJit59ztRLV3anmIDv1sXRcMYTyMXesZiomInUwGW2VX3GIXW3Zp636GGfjIkFTUlti9kHlvwBhdYBlHeg7G4PSwMjGzKw+3o5Y5sSdebUUmc0qwSMsaye19pXS34jpdU4KxVdnVord5RS6Q2Cm9HxTnjeWRQqpkR8vyMWLiFu+QyfzlqM+x+fz8nWyyLvrw/Uc/dlh8UyowXHd0xFZ6rC5uLkd/JHk/mV/k3lLp+ZDl6DddL6acmWlSs02APGrzqCIQexVzhQL7UiLOMzc/REYJCInpVNOsPboHnhYZmE2+yJZnSgZXaveqFjpFdwSU5/Jk9vjIUNaAJdbBABFpKitglNZT2NVltZJWqNp9w69Y3ugmnrEMKHCQZbRPQ8KZ1XrxWsWkM0ir2FD4SeLPPHRlujUVVW/LJ6ramdGe4OCTrX6+MHY2iEQl1fMmYmfiBhFtdCy1ZVc8b/T2Jfv4LppnO1iDd/wnvG3gMSb9aJ6QocuyTC0+NbCGt3A4i/EI2fW8zUmwclImssYsMFP0iSDLcuTlHzbYzSLSF7NohMIVU17BTIMZuJV/BgGFYUFpQjGRm1Y3cJxWaCtOtxfoWInTYU2tTYq6s3VqYSQJ9tRGx+5Yrgp5/BcnTOI9cZmLWpd57+UiuUJd58UbMnevtP2dOBJn1CWmXYxE7KA7Ml2ADIWQQI+RUV1vQoJqbJrEaeUnIhT2tWTGFHw+rlhTqnkMq/6TQmq+ViMg6CCUXmuKMiCk7GZpg8gZwloCUe1jW2EENhXtcq1QdgIN09RWJa7ZRmWInrcB5CwLIQilwfXswDMKSZ5ODv/vazs9+alib8qOJxa1MsrdY9kuwVSvT5Og1r+jNdBGEfEaMg1Nau4HLTiMxnd2pAMopIzdHelTJBPgxG5YqHrvF8jJ1Vosbo/orfJsB1AikDra51HOTEWuZO3aVGzAgzvxuWGZjLayta7CbBE2G1DQOEzOIqXgoeysfN3JTVujkzMZPbl1Gwb8SFF+g/IrX8YEnnNFh9ZAWxWt7ag4RJSGBzDeKLlFBAW/zPaGjubJuU77JFeg1R9hZoBkhkiaTMZd8m277Bm8667+Gw2cD5/8RRPei8999fGxLrFjJ5P7dXzqo+xkD6y4Y2eqcjKh2GWSLwRK34eG+/l6Y3bcAFoOVind+iYaD8sxprepmGEmK6+dpjwXksQqAVhZeBsnPbZp2LyMhxY/TqbKOpiP7fy4ddFygZTQ6s7ePKyN572xEkNh8SWTJ3rnERxUJsVca0FeJNzUUbvHYnEHvbvlJWELivnZLGZI2zENj5ziQAbo0rsewVn0u4huW/WbtXtG4pj1MeAOE3wHwEnpgbxQ8XW5BiTA7TDRv1oxAFgfc1XSr8drtXjrwToIO9HYtFZduXLaMC9jsb1VYBlVrJ//wrQlvuyuowSmEkESBjkA8zscLOUNJ3zsQl4yOA/7cAwz19YxkkH7qEvWIv3yi3hjbeIOTGMh0L6wZtZuzLYb6v/37SNDW0eiYzRst4meHITeTNFPLCdePw67pqhgc+S2vC7DuL99ri1kSwmdSgzEtUp0CjUgLp4XNdzWraF7TcuqZ4bEbqjbY+EyzVLRP9KwXFWmoBdtqEWZ9FW6sEatEBTR8qXrh8BGGOaoJQ1LNHbpui1zepTiw7eGbdBault5lh9bAFPI2NjjkRFhwnFjF7VFvcVpNc0kMLNa5ToGhQMbKdiJJ4riKNsge0PZQ5ZJd6vL2u2Yjt9/KuQybQrlWR4RPQ0BD4PrBUvbtvTZfruOfTwfpmeev+Mv+Q5nqfVif53YxrRRqxdodXLhK6MQ+ZntW4Bd63RVh52+BDn/qitocNnxWKya/N8Zlh9a79SroUbMkyOZ0flWajJAzwDrVJlkA4A9pnrQ1UmszDpPyDoY2CdRx5ck6M6gWToKRi7vXXrLLXwiV3wM0ih1Km+02Eq6pIHxVz0Ems47nJeTYx2hrWHXUOhp4hoDEX93uiM7razDcf6vS7gA+0etv78/cJmdcRv1EWPVSTLF/x6KqcRgc16Ek/PlupbY3gx/+P5HXbiGrh0U4GBqp+1vJHbzVBhe0MwmBcge+Xo9G/uait3PdVjMZtB5WNeeddq5k2KGB5SBOsgBFfpHr1zGB58UwCiNI1dL3NUfxaR2NBK3ZbNMMfPieYL05wtYOmCZADj+h0BKQIff3wMqk4q9u7GMnbzU72qLGMMNvD2MsUWOxqLU03CCiqzs6yagX2sqzcA2X9Q2MaBaQO3vlieqc6pFwCMelwaopCy6MJ3WHAtFjXKWNIRdeULJsc6IYNv57eYd7QJuhs8ywUslcNpjjv6ifH70F96L1eHXie5YeKm6CvsZVdzwP/tW2IxYUOaePGKuel8oSG/Caeiev3M9rFvqW1i5N8yrjN0m5AY++Fjr/nTH+z993cFbnTmxV3cXmIi/MTRQflSbSeVoWY5b+cCXbygn08nvdIVh3wmzGyB775MElntgRQYcTjCNDsZgZxFbhfZj9IWJBob7q3SldTS6M/rUiNApGxpI2m3eSY6MXqW4yRpdK2bBDUcMLXQ2nSyTF9qYQBEx2pzKT01pkT5ttdGNkeCLw9r4E66E3LJ1Mar7Foj829i9CRYY91Cl+hwKmrK+3I6baJIoGoyDBN/5W8rpOZCW+IFKNlMR+Dp4q6iCacF58vzn0bApoZ6r5n6YPympm36TQ7iPaZWjK/iH/hXT788VACV8akU5CjOZaGAYdsgzHaRbWoqcBCopZK2tmkOyqbibkBcNTpRZUyyOGNvrQGLDfJ2mZB1QdqFB8RejGifB2NlV0CKveMWhb5hP+pgxxnqZ7LVOKo6xV9t5D8tOEs1E02WGeXO6aGLJl10Hi0T1yGPhHOyEutgKA/HKRLf60dmM36ybxWtnVyThHL+2FVj+k3tMXHsdyQF9RfBEvUUOP/Elag3lNGRkUIAiqWSKIKSRlTGEGtKgYXC2pxtGG4gktjo0lY9A0HgyjGz7m5Q0F1AnjAvUkrPdjF+JK1TCC3N1IuWkBWcVs56kO9JUn6JX6kh9yIFXpWUt1xfYrUc9+BzpKf/WxX0g1OCkyqWSsk6uTU9GqK1ohho70LhA7OOf4F5NzIiu5jx3X80+kl6YmUeM5JgeHDLq20hcGi/tfPebpFKjFvvNYGrSdOnr4cp831HQthXiJdB8YKsDPyJ0XcTPFvRDYpqiCiUQsTajdyfUV6FeE/7tb0SEojHGQpQt8NLvNTK+aV0qPFTch4rZ+nlnshxQjpAWKQCqM5sBK3xYpXlWUWWXCwH1DIL9Rra//tDtx6SIsMv5kEE2GoBhA0dg4w2SMhbtON44lwSLvXCOcYtNLG9XERChQpptAbDJdd4aML9ma7PxO/cG/pxPa3lxl/JMc/HlnDnRyJ6UI/V6k/tCTeXVkM1P2QgGaow0c4KC4/ZY6Fur4XNqNWG0HqpGqSe1qkVuIIdUWE/GxD/tK4TeM1RV0OHeCxW2hROzET+ECrOxg9EqThvIDC/pKFvOPuk2v2bAzrT6HICV8AUgqRTKQ/RgbCas3lcPe501EOqFy6wWdPjIePkyjZl5M419WnoK2WFyW3OSgnMhVaE8OMAkDnvASBtF/NqhgqEPwaLa5mv9bui6f2YCXrkKt71ZmToxlPwBRU5hmV9MpCm/hQCnMTf5U0BE8+dAsGXXULGLDe8YgDxX03S0T97sW42K9N1OzSSxrPfnz31MBQWOZcMyRInVHtzhnepW9nxrfDsFbMdyzQpGvMHDrCPeYdkV4XtbmzToL+8jgJsyMbSDtey77kANqOi6HFe4cGelZw1Z4y+nNRd7z8STuWs/nY6s07KkGEOY/9ke1tdBZ8InkMUhNnIAAL/7V8Gj2lxQBhlI3YJD+JhP7HNCh6T+M14cNV5M6Q6F2P897hr2If+wvx4/Ws7Ply7zD4f5GVPDyPXxNJQ8lZtOfa71uSZoA+XKe5hHJIaL93CcWWolcUSkXXYjahCtYt/rAvH9QYJTRMzNLXC2oLCpv+KySWe00pbKjMpgaq41ns9MvklMOCmD/6KgDcuMfIO+9LsX+pr8xEuXjh/LWIJJ/dZUD+yS+3r11/84PsEgN+Q6w76Prw8Fo7NZsL5viwFmZHUI4Lh6C7BVj40GdldopvyldjrvzQLMwlluK9WzQyTaHOIOO63s3PoJc46Mrgv/SwuybizrXIuNjKKzaSb3UX7wLZY+/cQjgF0iZEcg6aqPqv8FgWc/SFc2H4sH2pNkTv7+mbBnqzTXhhbFLC11lW4GpSz+ZFYk8I3hxhPPi/fH3CawFiKFZZnSA89e0nrVcHUOOf5tSDNt7VPpP2d/AxTvULMRiMvEHLdj6Q5jWK36swSDXBvZAidsic35GQDK2s8ZnY3h1e78UIXktJ0OnBxqpwSCkzdYDpAVlrnNJKRMZ1ZcULw+0SN74EgbSobXlVFeisXm5YX+mn5hVgb82/X2xo2Te3mvLbOVf+CxfdwbtM8VceWu8tk5PhB/FKIhM9tKSWfw5ivvoV1fUDfQ2urTDPBmVMmbQB9nJes4x0XF8JkBdxBlUuJ7wJR003O1VVFJOVXIiuOTLzFk3D9ePaaVXlxAVNY6d+K0v8bBSFgCq5hgP9dt5nr0gL19PZo8BE0bDC8yHUXXpkCC7/99YgYpmzgApj8+KduQD7dYgtKEI0C9NKGdkbxY06fM2/HyR2xk76lJy5pu1bMg+EIdPOIciMCPL79ch+pSCCVghyiiUwYLD5HADUOkEmHwBYEGb6oMcYYoj5h5rEQulhavWIJ7pGqwhDGKpM3HKgbbpKrxA+QmqbBtmrsLnwqP8XYIsMNsVWITWbQ3CqSBIi7E+lD9XkqkdXnVPfofeOVH/NOPjOd4Q/fsJ9XWM/8fxNLKfBnyHPS1gX69T+bQfabHg/8sxYWoUAZLOLwFqliZd7jlJXW1KIB1Rdj7Eh6TAEYHFLlROlw0I0ucHv8xbYblQ6W8wuuEA0eDBLW8gj/rKm8G5q6W958oLN8qMgULG20cx0CIsjvr7WVcfZt8o5eUrTYFe4T9FYoSZZiHKk/nGJS2s1tbY56aTFlo3y174Mqq8bok1smdOIGXTlitgF5LXtXtYxErgmHKryKz1I577W30j+gax47TjLI6aNop4ZpRbU7UT7s6DBZ5ai/CeqlOHtAt9bnPDb/VbOgGIn4TedKnvx/p5wslnUcxZUD0GSAQWYGgHmRim6P3vPqZqWY1UDzCx9xCzR7joot9CJ6DOHzqcArrhMo8RChDPGaNlJbLhrUzhsc4282Hwjwl46jHwrA0CvpudIVHvNgbDJJKTGiaGlZe0bcbntBhu20bey3vZgGC9vLlHy49rve+lfZD5iknAv6BMbCf76rd6zLq8f8spuWZY2gDo3pl/BEQ0sMvVkqpABbhq+E5Ulcjof/ULuz2va2Ail6ddMoYP1mznysm0f1V+Ib/HLqFgnVy4MHIyEX6fTmxw2pptFa7A8pe9xK6RhK/Hy1k94LSnVtTdDvHtpTp8z904wMqqXh0pCaEtvifxZGzxmlbOUIKCeKE9HKC0T9ElAhabAfguvbp58Vj24AIPW3/EN9m2XYBoI22DTi6//+QL1Prl/DzSm0AzzWlr9DOPc1r1hPz1Xax+9I9g+ewec7vDwsWiL/sukd24e4cp8UvrZXNwL7R//qvEtuz7LxjhdcYVCbSnsmzNdyDSkGUyAZr81K8PF+75ucWTQcM2W2Yrubia7Ze0EYPCa/bmPexZV/1pK5TbSeIpLcbxcBsxmCUTWKZxPDzKDmpR39JIWaMumk5V24g78mYNKRiNUK3lZ7hjB+/cuRkyUQ89G6QSSeW1ChSdufCrr6z4GWFQ61s3JzTxixs8i7f9e7a4hoT7NciIBm693vPB5OkqV60UHzKsHo170G8Y0DvFMTTPy6ZMipyDk0wGG2u7aHULLcqVxhBf88iGNQVtVP6mGLWXx36w2EzaHWHdv+9luNCUb4YWxfw/HpMkgz6hcq4m0ZM5rKdaElTS3uUnEb+gQhPFaM9XzlcHG9cPiDOaOYdpK3wj7qBHtA81qUmRvYGKTYXOEe8gpmKfsqJPm3q3c+hbXA1xFyOHUH3lsj9k2iqLpnmle5JAVz/iqUn0Ft2fNhbYeWL+jQxtV0D0RgJNB6Aht90gVfzxhZsSihlItW9wHaHj0uMdRk89RNOsnU8dxfyho468xTdZ72hsAtfFxNRD5bCyHfv7YL8VWBim2M/4LNixrufrW5oFCqpQ5MMHbUnSwkQPrrSNU7GZ5KLdDRmVBTVwEFOifbnVkxqa1lrdKnwHuzOovBCsu0EO26WEooywCwzDASX+PUaIjGLaYTKQcyE8X6lJc204WMfzuTYGowPSQQg5lwLMyQVUv4aq1L+AEhweCchPh5AM5wStPC6+mLdL1P6ejN6UgN1KUaO7OEZ0KUVui/cpp0gi08dJZVBbqfXbWwGBNMj1hwFAXzW5d2wYgtbBSuFHTPEFvxWABSdUmnxp/klJgggFl2PwOB9+mQ5zjMWCTYiIh8F9UKJHhVL5/ex0zomCFm7+KZPFtz4VUKisNSuAr2Hw7pc9L6GjVBeonECuu1aJ47BlUNVRGgtpfEgRu4x3rYdFI2ZLB9qOB5u5/OQsMUCjbnT6I28ZZbIkvEhvz7MavtWFIz1+Ig6ChPX2Vi2wzCXPMWey6KhlNdHebHRIJAIUdzv75YucVIuCcVlaf9+70jZalSQmcWNzbqbob0s2tXQlqZL7dtuRZ4zhakxSaJMHRX1PLXKm4lCJQ6xx8eKtLDwSZoQvjF0/e150v133+rRMElBrvFqBq/OEBf3PLfKm4tCJQ57xMtKtbElwp/zybl/+P3gmvQi98emOZSONJi74b2XrObpxMkjuh52lO0lNi002Hz57iTd6l56pbbsxMp6BHtxM9B6ZKxi29WgTdHkzTuNa6ATEoTL/Jb+6TSsrGMB1VhF7Jd+PyCtZXoCKlSt3QWYqRP/4ktR/2FHgAHNGESCvSy3LCuK1U4WR74GwHmAt+4Ur333x7SYteEbnk36wpuvjaKgqBJ7N19S1Z/A0P4W7W+IC+qazvBYsgzMGmlh6cr9eU30gSXLwPmKdZbWXJvapPaoXaqZWLZP3Fk8EUjukUhZOxvgONTlAkpLCPz3NoQfPzTLE2nis52HT7eXbdszSg2y2ExTd8EBP8bHJoO5prF/rFgcWCagwyO4e7mVjf/OqeK7Hs+LyM2MZeJ7xOqwuVkU27+TFr+ScqgbqunWBS4UA2fc88OF7jfx/gfvdDj11kvQbGWCUR7FgmyfCLZwp6B2tkybzJlIjTZWlO4ijftEFq7ryLfowF06ZuPIbu7CWhlQqhtgpg6Ll+G/UFc65Nb7CtlGZOGUP4Nu49xKDp/KTCyaJ5zmoWc0Soy50pziMS5V6eOyJCts10RyV3hSZmEOECS+AROgaZW6mfHk4p6wf+0tMdnopfDXfu6oCb8C1fWzMuPgJqG4Hz+AXWocz0+Q7twA5ptvt4KmYrCxU9SatzVsRM1uEibfRGdtYerezLQQmAplnq+1BLOe2E4vs6CLU3Oobof3HTSUDMppgiwSg45GtlqCyipCNYIbHXgLvyvQk59J3X9sxyDeaX3U5mQSPNUi3dE2+6qMktMeEEZjxmbfQSVebl9vFxHjLiKKMr/divOd62GC1mW0Hcl2BD6yTvmFPdg9qsh18SXWHeN2A2knza771/ItrFw7dLsU2g5AxoZLaJ+yJMbZCF4g+23kYMh1ZxCVVRXEA7kxY4+lmD+gpfBWuRhBCeeWQhy1Lqt7KtsZEzM1tpHvyY0VG3C0/xf8z5rEhWXcZ2kK52t7pBH+qou1ZrLRU0lxJ8Jz7YAII93riii6FPiTavFYGNn0BVUUG+nuXFGBuIVqUUE+FEMxBCgLta2rWloVNn+UcX2rjZSUG/AfOdrsGRee6qkw9yhZ3Ky9SAbsQsINYFCZYeSXNuHRg2zhCiOceOVRYQzKwUA/VufjqGKfoUdEs4fOs9YD07/HfocciQYftQDKOUG2a1jNr1rzGVAc10YmCfAjpN9ze3ubSpY4YiClbBhRJ/jym1A9+m3+iqICVmtPkZP1jE0kvV//84IfNDjOWmgbDRWPr7RGwY2uHq0XW3RrSVP5mlaj9+oNn2vwQZ/Owxyboy9WD4KArO+CmD3tcBtCJe/acuW4SL81KkEqxhiKD+3GpBuwJf2DXF1Zoif5GMqwMeJ2I1UlKPZwLKTfrKajNafvDas4ZfWdbiVkLWyTTbt1ayluzbqVuNPercV2+w4ZOldDP51F52Vof0P5ZGD90WxIkaV931VPMAa/EPS1H0quTUQhqScvW4eyQ0ORxKwP1pCzTIohUk+MphN60AdjKLl2EoaonnTtO5YdNprka70++FJuIoI689LVqfZw1hO8CRYETosTvvUND/GUDneyhk3ObmsHcVI5/LEg8UmZZC5EUTnb1zoZb+0FEylmRZmTctVxlXo/7SR3FyIemEONk6ZgrLqs++JPV+Q+FENgMQ5Ggz8N3R8nTp95a9BhiDc5M3BdWDxtf5X0YHinxrDai+P5HvqD3mDRXyju4+eSWC+yRyrBnBJ1gIZgFqwHgnAVFnPElcs2m3qxij6I525oR4v2N1TPhtE336rPcmNoP59pYx3KhquecTP/jbSV/xAAMiPUZxTRI0lrHUk9jDqn2qNmVcniKf0eJnuIZwXmu3lQX6BlDYLKc8WCLX2zQzJjwAPzscdfxSHL7w5axS4DGw2c272jOHgpVhkY8zhLNOzm+CUxt+dD9OlOV7T7XH5Q0GTOi4OBISbjysgvp88FcLNpXKB0mbu2uKMCH9Wy1pfFtcsOBQ222LcVuY17sNfA1YlwNtTHlMTuIIUlCjkcYtLAI+IcdpOxeNfHrNbjH4em8nzudSL0hQZgqrWGClm7LsmG9JZCZMyy6fa5euwx9+V9XA/Wi9R7cQll4ls5C5kZdYhk9SMm4sFDBcBUFgRZlrqwb5CElb2t8RszOH2nsqESZHKqA0Y+iAhCU84OpS4GmLSQCPGRskRBCPqK6rNFCRZyHtqs0fywsKzrwpG7tMG6f+bIz3TqLyjJXU/wzn7cfYL3OXlsVv6BnLctgS6fFvkHZ0kz19fZKz9Qcue8TdlTqzDeErjhuqyt6/JL6cO9hBW6lXXQ7SdhD5LyCtu9RShtX0skEKUKW5/6QzSLfYsIPORl2a6sPn2jDxt+kPPxEK8U25XPjHKAWN2FWkGVwffv/AH9pqkgbBfftSE5O7q1md626NehsrKXGCUZsxVNicx7+3Fe2/PaVAqa47e4gRTZjeHJLLy1+XZFFvth8+YD+dvnSY0ypLYeY/aRk/tQ27DnxpvIc9asZB1m0muX0kvcddkbfFPWf0+tsumlMeUY+VJWAPCLIuTggqH3/vjNRkufLOy7HjdneULDh8QufdqwrfvxnY1FiQX1aBewYEg0apj+ok9bbTagi3YyfEfyeK4KmAgd2o6o89IaI8OhxCujrMFFn7barIeO+latBVHKrsE3PvjhQpt2cpI+tdosN5o3rRET+Pi8JprLnyegn5d/LLSf97K735MMzZIZCcndeI7AtBPf+BxS4dipmufZUlrK1oK/kjjEteIIHxG+MrldtKoiWEj72mU9ZgKrs6qeeFahu63KFoefa25AgpeuikfpxxxD/e07gIyXchDQ4nGyXaONoV+U8uORlE3Raib3gXcxdmHPROWSVZZVNTVoniQW23o5vLaVLU+AgC28EoVdCnQnD/2s9Sj6Ejodtwibt9gWzVSLXIaDCLyxBACyxcXhZfwJyByYjN0lXkwjRQ8pE6CilSXXS8ZJ0LNHwmoJa0RBIFh7h2cZkeHAvzfLjfdHHFqgPBaAPnj4VnQHDYAY2CIK6Oc0QWqwzAD5+sm7xCjunSR174up5j/xlw1lktL3u1/vwvRWm6nwEYVMbKV3PTjtBWPhaXK8fhAuC3wO1MNAyK6WxAFDPbeL3meK88Ac30tAWLu3wMCJ64bBg/A1qPuKgJ+BbDYcK51RyoLW1IFlxbdPWWd4HLXJmLzMdpCUwtYvODQ/l8oWKLJSgfTogRI2nTVgMhDR7HJwFECUTr6hLyB41kye9azmQ2mw4H0SKln+gK6jLDUNlj5rJ/L53ZKYJ3JPS0nDvXXhKXZzU1zIs2VxMObdte8EeWv8UgHg/7XHxrF+4hLB+4EEQOVLxlcL92CDyzrGjzTi5ZJDJ91PHAcu1DLcSEbeajCr1/JM0nO42H58Gde/tI3+st0XjS/Y632VH5Jgof9aWGqbePiAZJ18Tu1C3I5Fvr3kMox+qWKdY0cuhctf4BeJN7jGyICH25JnBfeOo03D/WVF7S2wqF7cKYtLBYl3Fsc6h82V22dyPl6dPYUDxNbGJ/FaTrOPNS6r/mag5SDOL4OkHwmGXnJ0sBbdemg2n9J3Wyysbz/IuAC+4vJe+rYMBDTdaanjqilWzdJ3acSsz1ueyhnNkmyuW+tgkBNajUnD25LqL9timcmv3lYXZLdarQ+jcP3tV/XNB5ZDEentaVJSC7OojjNpnKmhnQydn0XnYujNDNVX3dJrMdPk2vBApqEWVqu/w7BeI8+xwiedQGgSmnLdMz3E3HqIP1Im2GpYnzBN/83HoAKINu2s+uRs+jCRNG/ykDHs4YWKv/SkQbLq9pwxQDsX0Na7JTAdTAk8hIw0MYpeOJ4+Zklh18cusMgHaGZduJ4+lomx6GIaoE46USXML/ZngPuqOpoFawjkA0qOeJa3hcgZnpLnjHLny42S3ZlEkHbXE9PR8hvfogh0Ts4e5VkK/MLn9U2mAuzr2uXh/vT0rniumnnzOzZ25HX5WkaSR3dZ49sNEYLd7OTU+3jaZOMy4bzNBx9YksPhm6LJJZmY0FSkihULfAcorkggDkmHjkCdoSfPmEOGl7eSaOKFkZHpCJQKgafgE4EBdScrs3MPmraQMCV0pfCFdmsaUmfQrC1eDX3iF0D6KgJFtEAuCqMFKQ6X6X42fGXN++eAe4UNYEATNdgT30qTdMZ7xl9kjj5Cw0ng8vhtPc/ew1WV+8/wchlaxTTGbzwBHhxpVea6z0lrdHQxWfKWl6EMiI3shcU6z+Il9nXtUA+2CTfUVnc4TuLmVyeSbmcDrY07/MMThutzGJB9/ol7OM8GXAUq2KRXVg/pySLGdeP34iwhjCU4bTESB+BBLERcLMJdN3svm9M9SQ7xoQ0uNIwGQ5XUtCnRNdkncaN9Q5o358Iuz1iJVhED2CnMeISTTPtpzttvVuOukvkqz2D81AXkXYFKm6XAIXWljcmM6+ulEmKsy4oh1MR0gixCnj7UsgU1lVQZwLyx/3yJ/obUsoMivtfz69ez9g3Mohfy6cyYFVS+sGCjfN0UZ05OeQfW56n7bxdyHXCAwI2ZMSS7MWxMiyE2FQaLAJfXmtcPBZdV3/bgKKU/jiKzAOiVAIshaJfC13dfwQV9e1LOQshbX01f39ZJIVm3k6FeZUZBHXEQnL2h3Q2ds0XnZ2gXQ5I3I9D3gZhb3+0QqUfBraXmAnDogXbr8L9pYneCezaASB3WUnMBOPTwJeZ4FHVKtUWdTZ1DTaq6912opxzUOzLrgbxVk3wwp3uHBv9OcrWlU1KiDqf1bF3Fb/+gH7kFD+Stn2QECN4SQrVlZ6Uk3R9z+KB5Wwl9p6eF9cTngxVHsv52EvouTzGJiLVeqqvt8uOcTMXSs3T3RMu2wfxcEEko+8F8uSPcyoLoTDokqjrKTKPDulgHbayLNuzXd2BGWt+NPhMAYsUV//VtGkmIOtWazvlWf38B/TyDlNDkGp2QLVby6zIo6p+FTR9KK3M0os34Ii2N9Ds96LETuuy0EHex9Ke2BRYopRRSQfT08YNiIgLTs1TomQsMszI4xol4YJtecCDdoL74hQbwMVRsXuciKBWAESfDUTaJicGn9Cey2hTyVs6BwOIN262JCfjCjBBmYtxxfws329OdFdIQBJMfPw1yEdtm+bsftujauGixNN5nMwCO66WNFpHNkrCkCdrp2bFWn11IoHpDY5HhhePlNIrnK0T1qiZWaJxL3zbB7pJ783PBfy+R18Z+6nhnceuE0npit++RAs5yCNtFKVR0HI2aip50bzMW4wG3ZTPVSY54+CJsN8aKSom+IswS8anLJtOmodPKViSbEx6tqI14wayvcoGMaOqMbWjVwhLrHCSyQQpSQ+kqgHhCqKpzlYiMDiyJmWtky8U0bWdPoK9g+hrXFCTxDmbYVdKHzMU7rIiCtgO/FlqLPZYFs80cpVrMs5bEi1fSSSPaAC84LdVAG/XejH3KNw26h3jEAr5aa9pwpp1cbXGGPfdCboj4feUD95z2ssJay3lmczEWT+QCvt7XcSu9J+Sm+cgIaXTi0x26vRaVBZ5w0Tnj0EZibE0tLkOZCkUdbxKhC8pQif2kBERi6+xjbVQU+XlIHpDWTpJDn9ZYB1qYBKEurEpG/bllUSMwkihXS1h/hz2vSkCkYqW1PzrgBzqwT34v4Wtg1lDgU/3zSXYKaeRSxG/oXUtXkW+/5pk3ZMFvd0ub6pW2H8pCG7yqZ4zFtHDIPW/mtHBqtUFA+QMpiOwtL4liGXi2cFrFiLjqfWsNgPPWnsZr3jYGBuqO7MY6os7EV6yPT4F2ncO59Nt6WhMN0+xl/ix1J8ort4LE+K7kTntoKfjfrBjHzh7vOD1uHYtev+V4izcMHzGEzMMxfRuPdrBZibPn15WIhvW0gli1aZNH0xtG66p7bYsXoTIFr//6TjXIYvFt4Tc05cHEFmMhxbVti9dzxGTYQE9VAxA5Nui27WOKQxCVAlbdb/+U5+EFnX/2LhxQasOjAS2d0Sz7xUN6eWkQP2h14xdmmceJq2/5ecsi5L9IzythWlkIxRChjxVWBaXqto9YwTW2AF3ln9dp8NJtkPB99Hezc7tTITmyP8q5cyE7nam7QKdKzApzMeN6fu5IJcKsqjnYtlBqLHaYRWTnc0r6p632ZnvV3wewORq+XyXH6zfrPEU+/DmHje4AP5m8ZGnef9dcnOP71P3j7Bv/8E0iORz8/3QOK4pd43t25UNnqmbuRr11RukS30G9RyPYeylKB4nPie1I3v6wEezqg4UM/OGv09+49ClwqiNamwgIhWGieWFviPn8RMH0hcliQMZBKEa19GrPJTE3Xenk02P6kDWr6i9iv+J/AOVRg+GqaMqpMq8mGM6JqibJw4v4z8Q2pjwqPuqBOKJvVOWy69/LgCn66syey7biQai7vVTFm0Kr9Y0ueRyMLMw2aKqIDEegCLGL7HrcfSotRxPNfdhDolrOpzguRK1Ao1gQy40mqvyY6AHQtchA3DTGWWS2A0zuLbtAsE6Rkzhu2au6h5bqfU7TraoqQj0hRGu+rcRzLdGITa5GNSVU7m7ZNi1F8OdEcsNlakwW5S3A3SJdtNTnF+Wr2m7HEADo5YrkGhkzYUmr2pTJgNjZi+GX+qtXNh7TMkWgs2YWk1n8GZz0hJctOuqXAZByzNSFdQ7Z/GbLIjaYt+XSlXLFqThHReLDxGrjgeeRY2pPMNpjTtaw7LUbyzNGx0e+8uaSZh1/EV5/7gJl8N5PzGYAOOUosMG6AV07H8qwiJc+MSw9l+jzQOZXZwvRu119xhAZA4uYZqamMcdfiOZX2TipuscBNHHU4wG52iYo0Dim46vfETDChYltpfF3D1SB5RGm333Kuym8sf0KYSyitnNtF+eJve+bQq711V9FjLEpUsx6xXhyxJz4az6+I1lZNE51/B1n0Ex0PNNjiWpqLsJGrtdDXp55m/WnF1yfE6UBuU/n+20DZ7xe9wAyIMhdvVCF/bamswNaGCf1CyPsbP3zEZHbE69mUMG0VDh3imY7zkDHbPrLZ336W1wciynkxMcasQ9vN4+YoQ4X49TsEfqQ9c9XO5NfUWe/Dxc2wBMRL+epLY9y9NztlcsWz9OtO55T/qQW7xpUL9fZeW/LyX5+3/jcbuv5g2WL7jcm21dteJz7ipWlPTubLvQSoBxoWgI14pR9uG4hyuTH7DrYIGh5Upf6Xn3Cn00wOJ5ORRHv6BAuSSOB5WZZ92AN2XiB/if0FsIkcArUVk97yo/H850Iov9mvmf8WhwN3ecOgs6zB0HX6u4cesaA0eMiwp0WrZMLIBgBMoGLG0aMNvzUGWqJyj0nphdqg09fYgrIS0W0hWqWjoofwaNnObMOSr84PAhPi+XlnNj0jaGI6KBoDqAPzo8BkGoebfJXetIxCWScR1saBfVpZ7ezXSgWjoqgQAiwlEAP7P4SRx2e2jJvcZtpmZk1aJzG/nrW9XNEnGqBt74k6pibH88E1N/g2HxMs1SRiVTK7S1pHnbTWS0o56tXX5Sj1FPr4kOnkgbKRTuUjTS67lCOB9xLx2L8tMKFdixuuo6yZTlAN9MqXQa68S9G+4FizAeKlYPj7s+1aIIqifJwciGUVjgcGaWiKps8qJXWO4fFE/vNNzAGlJVuiu95dXyTcLqibSz1BAWxERN2nsv5Q8Xpn37FvJJ+t9eo+MheMC4Nmx05gXP1vvfIj3Tomy05z4UC3woYU0y20OPln1x8bKcAT185k4OV0HLHeYJdQ1OpNjp0tvJdxPndNE6C7AiVapL8+wKNgj4RoJoE88Y6N0A5GRp9q7oEXnjsc32k28p9kljcjqSohOr0nOrE1fZWiHvvrGBp/3PFKlVFe8b1Qcx47JmRhMlTYSdf3j8Xc2x/SmhrSiBZTgzN9aANlSYD/IrLYatITsSD00kwlBvZScTLPN13xMj85cdWs8qpzSMezmUs8Ndy8NdyUz8Ltb6b3CxzAqnft8Rgf0oqhvzHgnFYwB8ZJSG0G/cK2o9/VfoOELMHfuzPsrAiTDPJyRLTMIxhtoQcMZBcicfQR2CfzmLwslhKLCti2/1pqrhlkC2fKLdAxHRb/v5hAtk5Rl726elKquXzRxCJwk8ZcJ07O8LtelKHxhMqEea1SWn5IeGmeJaoahXSijBVBhXU9yq2xiMvl+NT5g7iqomC1zpuCRFf/qwyX5n8FA5uk+Uu6WscIF/6/JyX8OwE0dky9/cIXT5T0RiFS9ktuAgysSUPJ2N7xYIDWHmEkGT9U520odgFdUMsnDonTvQ50rbRtq45pzJr1qQ+Aw6o3aD++ukutRss06Gn8l3IKxdtjXUV0qXL1FDMiQLykjI23U6HKdNua4um3cVL9rTbLLgt96Iq0teUTaHs7NwjRUsd9tPAnlqPU1HlVHOJA6wWvzLOxnz+miZm6X9xz9501R4LgAHXx0iGWd4cpEHVIcCdHsVuJSKg07bLc2xsXd4A7J5mWvkhzTmqXxNlfA3qUzD3WvaR5gTQHhGk8PamyOgB1hy/4sxJ7Bttd310eIy82kV+9wX+HuMhcYP68RmTw2QA9r38YSIf9LHkwsjztsnXWYRu7w3+PD9u2dnf2rurfhC321asmLfpPjDJc5yebZ53L8Sg26k0anw7R31mU4/KNKl9pc2VADU5boRNHStLAPM9Z2Haeaaus0hdV+rjE/2gUAbbV3IpC/s0XSP0UTDygSAq3GIsP8dnGtWpXl0ViVBx/UnXukfwlrxlqeSoYsg8Nys6+bMxZgUL8y3MvrMoWnO+Qc+4EpHDVRkdCGD2rX8PLrN3wZ0Jk5b7qIEqxyxRObqD15anacuOvKsq/9EaAIsF6rZLiMOuvbDsWDMmkPItVd6j/e67AQIsAaNymBlqAetaZcRQ9yM6DpasI54Elj5wfDbhQW7mSKz0ObKppgOEOfsq5fByhRzjpLTBdmlFnH3txSL5p/knB8Fn+81xAapZhnktshady2+jAE8ElLeITxZucN/Wy19dKveBY6zIQ5ucY0xL7Mlsz6AEcwyTpzw/yV2T6IWPsggyyJ4x1Eq0mAxcXWoZ5ElzyP8ppcTNCY95JxxFdBb+AUFxuODyAk2eC44xJ0AhQ7zk93nsgCCgBKu0wOZIVYdnegHiql5gBr+HpMnC38o84ps3vUPsAxq9Re5/R4n59NnhqmgzW4mBoNl6kgxq/HQKy1hrxlaaGA7ufWoodjnLnPw9MdJoFu1n6fgcztiqEjYWvSBfOkUtUauQbfyBEauwx3UGR8WiGoXZHFTs4uQ37ZxuuO/mfstMtIzOkYNCLuU7ROigSoNAYQ+oNljYHH5dbIi4bA3qcj4NhXSo+1vLQVSdhoGdkdtWyeyX6erP4nwVvNZmNsXwDHCajVmDZticdVRDxthkXsDcfDeuUzz8mYQUDxJR6vKIDKeFjSLx8xNsSOtwbUg7IKFbfuayRKmR9oc5MqX8LkJx2mUFWw280XpX40ezjNU0x8ahgh0KiaiGwh6Iqji3FWbHF5iIPsz6v+5/G+LhYb3LzdAHFylqQNTsljnbnuOJ9kF/zZHuTlgsWW5HPGDvC8Ulws5Pf6eQbcdnerF050WurlJP5VUGki2hQzFKsISP7pdvnocPPW8b4bzdk7L8kU8xbOppBMRHcg0B4trGABIzgo5tXUjNFihXv0NFsueQfEFIaWtqqXgYTBsAGK1QT3r5Ow0GdSFYYHzjcd+s641fslfxm3JFp1nRgHS/XI+aK5kgu10rhks3mCnPFw7KlQe9uaUS/+BvypZFnEv7U3iy7NQBVkJsvmhGgSmegiYBwL9tLJOSTBpb7HHKMzlaPXiRaWkIYm/BHVcoDeYZL+MlMhr4EquOHVGM1zcHPNRzCiZjtyP15mZ8cF3T5khIu0cn/9RPNAud/WdDFDN/2xEVWyW+BNmrG5GtiuKmTppyM2F12GmGhjWUhgRD8yb/ZEk4KYs7DMNjRJx4+foDW6xinwvPpBBVblsU9MF6kGfhP1zOXcFf1o7zVTn1NwEB7ddEQfSuMg9rRuWgM2et7GExPEzvxAi0fmRyjN58pQClimifXt0izJOxcoOcZdadq/JET18Qn1bnNwNW+0KKfQ2CllLEx+A5/xTvWg0XEdRYlFRH0IEg2Bp0VReR0btu0Er8MVseFkXDq9XAelPgMbsRd6jbcEvnZlYOvhVm+/W3ES6tXCWNSzT4yA0ynkyW4hTj0HNznNKaXuoGHAQZpKoOgNuOdWQbYTZuSQPQyyvvc4V4kVPmHHVn6oylqSyXY6pl6mY4HaTVExoDj3u7ugeHCgxj82yT4gvofcMNGcAPbACaao75VfaKihf3n6z6eDtq3MIubU9nRHQ6uin75/+6jIJigbfaow3d+9B+3aWJ7j7PM209UBNI9yIJKr7HyXLJlD81k1i0OisIhTc51mg3zBfBrAMg1GPzQzCQkLZnV3ul02yglzgHsZwnkKvST41BSEP8BRcIxYgotkI4LtTkrhIgAufCYSBMo3dtVWwNL6zTlbfcXUMNd9y81Uq0rGG8qtGy2MliH1JPbu1QxlD1mCTurim870mImd7+9YT57zaTxScjr8EZpK4gWp9C8pNPantREL9Loabcvm7WqSF+glTqGXnWh9bXMJAgbsJjCAN8PLiIO0M6+mDuuSCNs+S8nuQvfVibczyB3xxbE8JMOK/mlds8LxUY+H0k3TM2pUy8bOJj9CixaJ5x4Okf/CLBggebQLsxrZMUehq7Yu0Xf0RS7WJJ3bkgFEzoxsi8wSi5D3RKTxFc0lVCUb7qLLSBma9vRF5CTGC00Sfg+gohLtTtpNoRPxXc7q2eClpv0X94BOvfuFn/g9nVb2JRAgPNwIbCxWomKsZIgZd0x3Gg25qrOqi4m4jFSZLKlYq/3GNdhmkPNZf1LKVOFIQWKtxwgutq/MGySsFPHCviUJ8nypLd0VSRiCEePVX6jIe0mDqVxQr4GMn4cbvi+5u83Yc8njJMYF/QxxROQniX11NKPFQi2j/XsijgjY5jR3ieHN82JQQphF9GxV2ncDCFfYWH4S+oYWPS+xjwprA2+HDXhTmarb6n/JnmYLmWBf5nipDs+SXK5kqsZfJH7lnPMurqVas30fn7YSOlHmuojQo1/eEFKMuNZ3lHqUat0GNIcUud6oICkUAmFL7ibPYqPdDTQeuBfzHQxijjB/jFBNkYLtBXsGBBwNeJz7+gH1ppcJV7tAVhS55Ovgix3GxZOdoo/dyT2MOZK8KWnOJEZVxYrC6bkcF7+TjWQslTNN6g/491/NMdN3kval+S9ga+OF6Bl1NZ2VWl0+/EoBUqDjW8VxrFOpoB6WTRTV5gIl4r+xcQfocsRyd15rsTyJyEjeLNACHHWe/IeXYaRuQTgmFGEpng4uZ71nZ1qw0bSnGqpdS/GMcWVzEBx1lblDKecYb8MGc4ErnaGYbSBLrFMvd6KCYnGJrdFORe1WcTaDTbUOotNj2zhYrzu8I87JdGdbdme6LcjWz6/CXRhE6DxI+Mbphd9f1Xi21u3WVIUIsyHgHU1lP5QynEaHPJbG1d1tT/Isae94K6pZX3zYmb9xHsQeHviCF2ggGh1Qj7alTAC30mv0J1h50LyWLdyBPDITr1rm0YWVgA7z6WSHIzctWo2tbm3LPNthIGEgEPgKHBSwUuDl+1ATCBJBHnSStuB2CTOuoZjfVnyVM5HFSu/2tmuYsg5Y8AXO3hFpnYG50hQX+vS247Cmvd5ES9NgKtigho7hpQSTyNbWUxDjrY2ssPPXE6nn9X6s9QUOBvrPKKBCUBwQ164UNUjnMNr9fwvZm42URHi8YPt9LvK7MPc/aKsXmEEc4YB7VHiosgmKYTGY2CTQpmNcQY4d4EjeKhL5IvjuwTXhH8LvmtL7Xx7P3A0hIcxKETbI3DD2R4No1gyHwPJe0oLhOs28UHgc2wJreGr4937zBdwPLnvOqRftCmtG33ZJukznJkp6TWptsx5piRj7xaQ43qNkYORhpz5jpVjuNVIas94slPj7Bq0sd8k6n08vuMSJwpejEjim+8lTs6JEVslG/kqda+wELe8vFBrDcx3nwSN+l/BymAnM0JiKEjj/EW8cAOoqqnyqvm0wFW/NlUlFlCuLrhRnHGnRP457S4338XJ8mb5yZBWvedabYHKQoNaO5dajhV3g9OURj661F/TCcoFFdl5q4u+xzqv0vDvknCA0iCfZfhsKRDPpfp32z8cgsuhuxSk80UwL8TiTvpApix0AlEX3xVYipBMU6fxQkUrUolc0hikwhjG2kSU0AqXrDavkv8yYhJ1VBxUBiHMUEKYyLJhFbtINQ4EZluhrC2USuOzjBxoxQ6dsjyEKIz9qBDdg0ssRJXwxV7Iz/ubO7z8GbbxVmg0BNYB5FlrclYdJkQ9iEKlnFJTF7VxvLm00ktw0axrfYMhX6SbfpzwD/NdbM6qfeDh+pYm2bbbZAcP/gINZ7TAMt41KZtfkxtSjoh4jVlNKUc6fdniIcKthJey/TUYvUG/SYblCeA71dcLH2LaWsr5Mctm3fMK7Xzztvm68CMv1hS7kOIixHNbDQ9p3qNnOzgOB5gcK/okP1zTvEv4RR/fRtVaVpZehDAfjDZJ5u2B4B2ylYDMA61kH2yf54L+2ddWNgQgv/uIFP7txSitee/D4nMhETlfbm45Obtf4KVai5YGocovRtdYkUslwswdCE0o6ZeJzlzUUozQcOwarSNwqaM3zUxxsdYxbK6SdB9Y2IrVx22pDD7gCAAnmhM36bmEan2wDCO1Dd1Bp3oJo2mjNoB/JxDuieSqDseDSBgYhoy/CmWlyPFT/oGtKZlBOmXUUUZNeRl1J2XKWBNL9dbGJRjmQ0MZ5qZwnjoCU3ARzQnIcqFS1sJfbFfTrdwVXROrGIG/rAgRt/Qe4z6CHRXMEqvOm33kuJurBP1ib6tVk9In1jQf/y7ZupweTf44YIaN5zAHG7sAjZ1rPkmBZzD7TAuwOj9qwXAfN/bRtKNqLHn+aVwMwIlNm4+YfLBIRyilD95UxtD6w1B6h8rbLbaPMX6y9e+/pRYL0WrklzMYyJZu9si1O4AvkaF5vqBaDgE1cWJgiKsKdaX1fpoIhgJNHkdmoPX19SByl8iwf5GG3zffa6elYql0/i3fS90HcHrSRUZrmTING/PZBKmXTiBY6rt2Rzz2BzPwo0Xpq4Dkf5FI8Qp8nIt/YqR79nPZ1bvYBkidPiZ32z2/NrsOyL5n5dVk7mNKIsLYyy/XUHpQ5+Nz84ugfyMpC5Ej7UYAKAg5NziI3i8Dmk/Be19FAw4eK2MAgCzf3r/4GYBLWzwpd0COUreLQ9OHZnHXkPaX1xDL1Ae9Z8cfnG4vo/gdwcOYYUctkbj3ARKxsyHtzBFmRGmb8B/d+oREDSHq3BnlnMAGjNPy5cRTAWgw1M8/CgqS7jHjKJgOVK+lcOyCfwJAMXSUivRAchfcHQMnBzA2THQOylc/j94Gv3ik14CIx2EakKTjOfiY6uuOm/Hgq2y0htRiScX9T4JrBOxuRBdZksSOnCvJRQEunHLTQwNEiLeXRbRVmLcp/clgrdCTTh7pCa8xuUCqvENVBGvCb7YaCwK1idSlzF6oBHTJNbnFHOPqxarLaY1QUpmiiEJlWR7ISbCVMQ1Fh8QqgrWqJkCW9CcTE8wTpJNgmlKvmdAd46pECi8KrGKBDPxKIGMVyWmKlaCxeo/4SgMJK9I4hM9RhSvV8Hn8i+XB82YoOyJTG4t/0TKT7JZuJ7xpnKH+oGU38xcuF7yI4Tugr8jJZh1wk7ZY2R0vkPKJznCznhzoXtLX7ByiM5yH+EbUpZ0LuwmbiH04CFhRegmoa+8YcSCmN5IVgrrQegHfofQNtIhViqbXugnfjFy4ekTKRs2VeiDH1O5tBRIOWO9EvoVfzDSk96QsmW9EK4qvzDSByuskaJcsTbhaqDzssOQa9IvpNyw3gtXe24utItcMJ3ITXgpT2Qr4eXkiWwpvJw+kS0k7VxQlUSZx+sRu5xOLhbYz/XJR+3Vx1vY3a60k83QRp3tmw26gS4St+g21LvYohM6+7hCZ+hVkgG7Db24pMdO6FmSPXagJ5dssfU0dckabU1TSVpsNzRxid2P9QOAU6JoqRJOShHKJ4wrfqcuxzizud4siy5uulV5n9Z5DCm7pYaCkphwiOhxmjWQSDpNKAo5Lo/bgGOawwJFFmqTgSIKrmMSUx0NdgOFevww7ehqUTZQ9IxogGM7NjAC29HQe4GMGh2I3Zo7llA0I+ojBSKLW/OecN3LnZns+37vUEdzsc6o9D3sfSvIKcqQM0rqRuT0oFdw9NhmR4EKb71BHMc9O2zCM+wGOpg1jg7B0IzoMpJHjQ5hA1V1U2waFzuQQa/g3sImiQ6BZfllamcURVBA0YqmMSkhYJNAEbBSDKlRwFGsM9hAlnxM5w0U6mkFBrGUL4vmSF6ETQJRG05EaBywOuuwgmVQNB7NpXPGZBal3+88HscU9gIZ1BxEIVdwKGJEvo+GnJaJJvCrYtXIkp4lRBih7C9n141ybuGzIyEV3napciPq2iNpQxm8jSZvTEgcbft05SlVyO3iowyZ6X+SZgntIKEMBez9puiB1hsU2ZHjj9bfOXEIBhI82RU8KRhhn6D1hhBmR4PbhQJ+oHZugYolvK0ShTOXeYqX7UVJvi2KobmFk4JFYSscexg4poOgebsItT/SZFd0KMpVhqs0I6W9V+G2MexlZs+JouMYQuHtMK+SWVWgYMOfjTzYnoMMXqBcoC/UHCLXl5slHEYXYxGlw03xeCr9AGdiy+ygpq3UnFygy+x16ewO1+DHhtYG9jdyJi/eacICZYpD04qdKKxedsZst6k+uuXg6GeK+Jfx/p8DncEf+DW5Tx4n2H3JeXkKfppuZrJD0bKrhYJNKUInM4H4el96K6HNPbNfBuBvtDSQLIK4EoVDSGVJDPtvgWrB963IeYo6Hjnw2JomWuJk+GrPa8FGnoEdVHlWNsDxtWfnwA12LY4GjHmoeQ23DctVRsHS4xoTFFIuFkFYXcMhErduWLpRE/94un2nlG/i1jqj3ldguNNXjsDruDDkCxxCDm+fcelKie/2Hgncjsj4zzyShQ4CKKIkL6f7xOR6POnhkMY1eCFYbrsqH7Q7Zo/vFpWJClhI/4qIy7p6Dz5IKNshiympY05GWQFAE3oGFG2Qn3ESiBwMleDp7WyoZB3t399E2CBnFCNiAYXMwl5I59DofCG71/ra9EhgMonMzMkgsrwe0juW8oUMdNWIbmPyYoUc7aNpZsB6rIY1Hamq4xfHDnICfL9RoE9YEBxUmni45b1l/e9ZPE4jEhcGfnRq4PC7ECR0NzAbyEPooYAjgRV6dODezYCi+rKYOzpAEb9TLK59LIEFCpGQmv8Cs2F1oCpV8FYn6BH+hIHtdV8AfAP+XMb3aQRV63FMqpffF/Dnqc1ionahhqKVrpJZpARk6cBgQydMx5S8dkALhTqSl6ED8/MmrcMLtIHqtEzDhi3ibEQ+gTMy+5jygoUdynNkWYl8Dl7xyT3y7Jjyn4lvcMP3LhSebo1Y09miOmrEcRob9kUiPedY8hRTA48ziMRZGZF0eUF6RuBQZXdLlpkoHKzM3wOp7zs5TpM+FBTwX2BL0KODTouAnxOYN6x57AbkPozPjgLVDFC4GQBeZCF7Gvsy/S+QYI4dsDYBqFMPBToocAFEzAE7qAiGrQEmfm7O+YLxevUDIraf1mcAExWwvIRlHO90tOu+SmwHimQx9h19dv+9FYdIXZXTqJy2zGp9xfq6cly0kFK7Be/TLVlE5pHuzWS/r2/I2J8tgukg4jBbthmDt0XPc2+/e8P2MU2gT6xORmcJ1xs1liChExl+v11L+5iTi8jPv9eL1qJDDxIFU4UNVWROAgPHI3Mk8KGSGREo6HwchPbToCCJ12+ipc687gtZ4uGkYdwGKKrZII6uwNiR8XGEAroFNk87END1GeDUCfNrpmx6vrcv045wnQZ4DyDxQC2pJWAf5xIZ5tvdmeMPo707bkOUKiYgrgU44jUcARc0MLUwYTxIhgITRJCgXzGZzDveEqcfeQz4nuxE4VI3u0lTGfKbj0S1MPaCHnSQQLvkRF+IhGhK6GCcQIumO8EEAhPJfzYetjgZFATuRDIMZkbIpdg6jsol0agEn04i4TxdrikmZ1MoRJrDSWO7pbrj+Taxv5Y2vU7FsuGSpmY0Nec/Xeefd+Ktp3sbFvF5oQ1U7LW/GqYPi7lHUkYBpgNvoGZOJwpHCXHO0DESmmRDTs20zZg1t6dG3Hd+LyjnvsnNoC2c4VTLRcfdcDBJNCTTbp/BJBjEFBrXMyxWF2IFDikjnjJtRFOoM0u/K2sdbCLgLk4HLUQwGEgCbvzLQ8DaxNdVFgbs/fFTFQrwqTolEnJc1N5HOfxkVckIRrj4KWPinV48fzqSgOXUgU7sZgTrPgWBLxtLHAXvq2eAvX71DMAjMCkmwYUroJPQ8ZhHyVInF9+onaiKOHg/iQmenPFgWiHe7u1hwrF8KNO71CMWwAJFETNccGdnaJ9iagwIjHrCpCeCrJkJpA9y9egkazbK+nWogGIW+FSGcuMrysueg/i6TzVnB374jDtIwP+zdPjLzfByNaWLmXumJpULJbaF1SGadNEQ3bpAG+aU0dnZeIxzBgZt9xwZ8YypPFdJqsdC5tkgJ5F0NDH2v5DzDrQEFfMibigDv1402MjomQmiMPOTkQFaq8vLMcBbqK0gO5v0ssqSm4xNMEZPbRqxL3Q4NrEBKAcS7QXwibGCwocr7eQZHYk93sptKbBDTZmvIayeLtAbW7lUkxIyUiYCuSTaxbjEzoyrYzaKzWDYqEYPu6gmQs2Q/t81eHgBWbSVk4mpR8gfSkilcWHQ3hL7pZ4Yqs6yIAKHmzI3FRRKys4AhvMm3tN9qMoXnLcWqwcWd8lzUeZDM99DW9/F0LGT6f7P9YN3vK4yqroxHPHJPm4p4IM2PfFQObndnHfvvvuCHttrCARfb+6ku8UGLs1on+5IOLbTKNc6atUDow1/z4qhq4SU5N5GjfzMtorTlCSEHaPGIE5ZOw4X3tnIJQFjeQ2xZqLCho1OYA9xMASrycDg3Bp67NK/G9ptzHz7De3k8a7bBeHdNFJX7AsZYLNnOZoCmk7nOhjUpuF19OP4vn3XSns+ioiwRmLs4tKwl8V/5s+8ya+rksT4a5ep9ze4lqIt4t36vED6UFRuhlM+jjCoDAVjeBOQnafZea2z5PLHIggCiuaOqhoywsfoz1qvhSpkAlaJDrDXhzImXES2Q+J2jGBvleFcSYbFub2c5ztxuZrGMaQwBLRCbFPHRRnsjkcMryUBato4XAhG3D/hrdQl8gwghmOHX5QDSO0ktoojrXOEGs3LC3FRFfISp9+/gPJVMTSY3V9mZLwKBU9V21RKJG4RFDOW0Q/WOhXyj2g8PEGt6s8VumiOgUhhCXtWzJB+PmPKRB/SGl0UCyicrBwmkqiKej3LFkKft4wu6OJLIkufLV77aFCdrUOIrCryA4hl6g4g93e8MRO+rpzboJnnRVP75oQ518KQbBsj+pGh9Qv1dLZr+udjCEO2YLWI91MBjvi1L8t51vLVUzNGukPGLu/PKR6uZTOFVnzjpJyHyOM8ZjL+zkW8WrRkDyefK0xY2hviAnyQ19qCEQwidSuOhHLHCAo4Cm7/2KZz2JNNXBpOH2BCxl/MqB67dm3/OeLHFPlcicudBItEHFeFnyz1RD48odx3PR8SO4jUUzvaAOJr4dLailtRepAp9ZfL+eGjViYvOkKRya4U2A9Z2KjWChZE5zs5QlBRe3OeXSgcLnVl4DBXNCk3NJqqXlj2YtQA09TWLLAXckg/NAea+kqzKTVB9/CP/Up+K3i/aNkOAkUlLj2R7vTHId0Z3GU7ppzmjIIznLzElEMe5w2LsQWe4dwEKuloaUrNBp0QFSr6HsECDofzNAUs2nG1FRJG62zINWStGlu5K0+H43OvAUW89o1nimCPbc5Cjt3lPMUk+6iRwEdBk8nvhYvDNlZB46FbwIfYfbCIc0iCYiNIbFtk74VTDRBbDIgH2HRB5+qzxdpR6Aw4TdDuqful1mJTYZhRmtAqDDAphyrB9X5BJBzUOF0WjbvCdgGMWpE5L+x336tQ9DCQidolhLFmMHgxXe5gJHJg8I4k3IXU3i7s5iWiKHjF60uY7O+vIQn/fNDnNIh9KHemMHpDugrx2utg2X9C0iQ+3BvEcW2OLjubkvBEjwKsfP56Oym13+ayTscGrM4CWm8Hw8EhtCIMkHJ5IypotQ6uJa/L/stcG6JgQJOPy7GsrEMYBmpKDDXd6hFvTW8ZG3W/Qq3r5t5MXZ1rAjWqZnRmfAEZiszTZ0FyGwGkJskM4Hayi95mV8QuDccHIGcnyFFg0vI/XIHe1n6l5H/QniIz4dvuiy1Y1Ek2Q5gsHuHt5Yq6/tNhsYtgy06vpjWl3z0VZifo5jiXeAxvu1nLVV5UORDaW34F3NydiCEtHrytVvbnnYphbPc0ElFT7ZBpywy7DDWoNvN8BDboCsVz3+nD1DZHBUFW6HweKc8UqQpxl3SE8CgamNBTJ0FGoufpqMm/rRzps0U4jaENFgFft8iSsoPqgte9IaOtkiX8ALMuz9WMhNaRwBRkJCobukAgQtp0KSykII8L4SjP2A3UPB7Bd/3RcST6rjc6OcBSoZUOhchZL8HS4S+01XfgRUNjCtyGFWAzXlkDo3vMlgmZEUam2VkKF/bDxd+sfsbJ9wQ7TCWMAOTFzUW4JOixwFGpbV5ez4m0DQ1cBK5SMgIWKHmbJ+fDGNL5HmWCoRYz4a7/4v+rs25EyZEWS5FEQgTh29LUoFiyxqgqtcmdnwamNgTmAwxtCmyb1XbnJ3xEDKP32xqbRzMiCSRjcPa3L0jlmHmxgZ8sEVbRCD7E8sPQ2J6NMN/A0Q8oGMD3wbj//31riDksvBjkBbewMm2eH8TfEZgO1W5PBc9Kubo4lrWenSdVygazNhJfWxSvWkvKLTb5iHoyC1ko3HCQa5K22ZyMRwLP8IybAj6tF7h3SKAWrRI5zMtwMNI8ibUpfTJqLdqCJaJFxPz/ON6th8jQ8KleVa3zlTK+Ts+YW8hMflYSXN41N162KZRk0JqyzzPiFdlLMEKPwVMwmviEXVdBAbuTTeWaPBatrsHrFWikxgr8PoZp6MwOjIdpzFEB0UcF2ivTuvZyOYVNGs5LjPP8O3GXDjRwsPJpom4/bTBdfY04yF4cl65S4uojI2DeO4FZfDbF04KrjeSFOHPttflyJXldCR24eybOHFGIpVkmCx1PBtGrBnsJNI2T4IFZU2i89oI3W1JMUlE4SGIXkOTKMHl8uybOxb0D5SqtdS5WyXjjVNSTYbYSvCkrGwy9oN9ChHequ5jawG3277nkjHVpiaSjh5JiomsojLXF/RTaq6lDD87k5hAMW3czKd5W7Jb6imkzqIlq3zsux3L1sPOP1xod3paRAnbM7REF3QwY8Q0gAcsRR04TMx5BfK7ARWijofHY352bCeBwydD6G3YrISoocEFwfDU+BmFjft59fYxX404HsK4p7y116OIwAtw23S2RLPbisdLdxyDh7tMue3FPSj+hF6/h0QFeLQLLFhYkTdMoTGABubZ3COd9+Z5icu3js7E4HSUVEFAydbBaET/X4UoU6m9gAJZxAUzFMgi6OcRAWLmO4nWcx9K85u7OLLzBViSdnjgofnoJASvKBvWUbUbHHu5zevISWhwjGgsOcbc8jxHU62jFkOnHk2ZS4FK2EDSst3zaCEf14pl0eFdOGtGeLEyOv5Jh2WGmTvbnacCNc+IlzSGvKyb9qqq/3zva3fPSfYtrDE8R1aFZ+3XPnIORiQXqOjhQZU1xbb/89OAV7yEtPoE6VGdiFAz4g9+DlnkxbzVs7nr24cUXlgfom/bwm04IeefhkTcNm7uefPj/XRXuOqS8eTjf9cvz7obV2bnmfDjzSLlbIawwUbvNPrMqHyTEfSITZm2VrLOvZqoqmms6s+j4z/QHvyuiKyKS11tDxZPsm30hjxlaqGM0C+eq8VleuMhR410fimiIScy6ET87qrp9uqi1LbYL0Wvp5kAlnAcHXqaU43qaau4BBckD377U6cxt15FcYL9wCfrKVB4l/PsGnzNvXRWSmPydmjJgb3zZpw0Gkg/+/LJ4IFiT4YOQGzWVks0oAfZu8YaiEu4v88Tnm85RwMmqCrg0W/Pdu/RzfAsnJBVqTSrtvqPFIgztNtwbvtxLw52weBO2ljNpMpcjb+siVLIOHXrVJ9cMqdXVwpsXw/fAwnPYzz2Z1c6GoHgpLfXogIsIH8o3fvHhy3SvLG8IEdo/zoyDrk3qxAQzUmfPjVI81T7MoxT8Nqg3eE1Pk8KIiVpfGV0iSoJPXbAZpTEqRMY0yuADqtdBR+0M+Yc0XRk8S5w8MO66EOu9gJR7m01/EDHhpD8av1Zf9oC/+Iz/YTITB0dHf9B64d2wjqUq7dOXeJMgFOwZFrUjp/tI59KwugfxaaaYoV2WEE7ch62vsJfwPvdnBvdVKJAGAikJeo2G0YVRlb5gOgDFNPikw7Lbp0u7/n8tfxxfw+CcNlhMPa6Mzh7oWSK7qpyaie9mxFsXgVrjuhukYwlj3kstF7DGOJAcMzu4S6d85XwTmeHSMOeQoGrHJmDzUONItDPleLu+14Qz//yC10fqDKwZP+3zDYEC6bioW4zy7r2ZFIE41gEOSWnyF7mIhMBeDjvsHxqc9Vfy+WUkRo8s9+uWuQd0K2f5le/yzhkn/Bzh6gG/3QkTH5x2wt8RGiZ2jpv40+GEf7MolCtD32JVs1Xte6hSEemk90Y/XORgL1fuhu7vu1HXsoelyzmJQtsXNNUA3Do2cuUaC8jJn6cSSU/Pkf/6DVOHHqI4VZbcGlrB6/ligzn4GE5ykyMDO+x8U5zI4sv0jfdRA/QvkS55JE8ifQyto+2fx9Dw0UI4jdgCne+FUIw5U9IY0baAyL+N5NJR8EIwp2/15lqg837nY7A7AI4IiEsI2pTPSpSnlymzIbEFFPH8sdesOjsYfRGedtvBVr66//DXQeJbgTXxBuTWsCi59fWxlV40f9j/+W8f6IiD9X+HhZpjsihKAKUyZQl46T7JbYBXfIuIaLeKBDNqd7qa7Fkbm5C68e+HqbdKmQTAsf23H1V6NwNKdj2E1Loy6g2B8RRPU3j7PLdXUcwfrMsLQ8tolChNcbfU326C5VV5XIbDg9Oz5D0UqvIyjsAovjCE0ASt34HWnu43+OtsQ+ak0mwcz+AoVO+6rv9CSU0FMOT46t60yz1F/ncYc2ZCdRbpDXt5XOYOPeJ5K1mxF3kZT4O1roU8jWx6TOQZjtiH1K/f4aF8vxNcF1mNRetKcQzCM4YfuXN/WBo9CgWdjMjd5QNM11FOLPI8ouO4T9r9GAgGI49v/HffOKzKMUK4XR+MqtRoo9rlk7wLDf9lMhb9qrO55+2II9pAya5A19hyEgxabJTFmyQjvsUvSOMeoB2D1cmPLYE1y0uDIreWr030XKCjaaOwD1U4q1N9TyPrA5kvDLLTH9HmyaMQ5n7HUA696OEJqQwFANb44gGMM3TEEdrvkKTbsKbR9bnv4F+AsstRE0Qv/FtlA+KYeg31/IK/R6OIfP2CgXi99sTSQ1w8rW+okJwrXerrGpLa5jQdwMEPNfuQCAg+tqsmOZsOR+P3nH+eaL3C/UNix8dh+1GgvgOvJXCxdbQ4FHQ02vtPw9sxwLaKlhgYmCcZ0vYwDEgnLKdidYig9cyuARs1rtP8UgbY3AQhGadaosGAdP5cCMt+KLydVDpgr91yEhMsYYvpuQHsfsEKPchErMsMZE0kTFetiS+B4sAa+gLL9maNAhYI6Dbv3g4JG9qHRLV4cLHPx29K/zmnR8f39Ll2NNiapmeeIVmymEMwdCvzVvEyncMXcpjrpxmZYd3dYHpo4IEI4DZnFc1r/n2wH0ytPz8fAye+gzHUEcK6tMkjPkIEwwMG78YSoPZw+DGhiNUoal9c5e4P0x1AbjN+L+AywKMN8hPoinhnbooHhGt/nSfy5YSyMSGL9Rofqd3vm9P9dPprUe5uLayUs2lbIXFhRkdrRxRgQFBFcYrRNjQ9rpBSRjFRS66xAdcK9iTpsSZKqIovI35XvcLqMGr7rFDY9jkYdx6tXPbXw5R1ypCNYAhX36+RDXa/GRhPS3Y3gBSAt0OOYV69pWi9CRRV5VSe6/0AzcIOR9fJtxkF4nS9pg2wYmsaEAx45xYCS6XinxCmE7sRdzt9KhaDWF10d2cVBfN5oVZIKqjMrYjDFi7wLnpF1TASdMemDbkpJF3XoVEKJRvB74z+vkNEf6DW1/sr56Zs07jf1ePeO1EK3eYgGaxzDe/4Mfo9UC7Puo1lAg2r+QXcxnwN5OKglSFxhjvw6WpGR/wo18VC7s9SND5ZEcsnC8/NOkW8YoX4NJLVdVe1FSNoQd8iCJjafgKlwSUioGOH2hLX5OJ8On9UgsCTNW0hnmkT9je4S8TvF8ppyOBb6vSX7FKE0Dr3kOm+UiWuXjqkpnzhynI2XTp5Y5vI60dZDM381E3RT5v2TsmPJS9uuV8p9SwZhPdNSPNLXZSi+5qG/mZq6QMT6G78Ghbi7X8YCZkCFhXidCBf48OTLKT4gDixpDcY33PGMUvTl1R93k6LmqdFbWdeg2jJDfoIh0CMdfVENKsMEUPu8GvPuyuZOqaC7mCC8VUw3o6lx3hV+YIsuTWDQyMvP4T4PmztSeTu18S6wt1y7eW3VbMf/MkI2Vvrq+fyEv3z2uZ6j/6sXeQ5ISXX+UJLxU1j3nN3F4jiKBHWWJcAzKxwRrw8AQ+wRqoGf1hTWjEhhYFFvrOptThEHOj1OFz4vMzNHMLLzaE/Zi9WNhZMBiil+s40fO/0HUCZt31ZdT1bN4VO7W81sQo62pRH0BAlP8bNKPc3Rw4gWf2iwH4EzsoXEf14GJ7x+QORVb3yVkd4uGnt/fOrOE4XMCDWgsN8A/uywbWfcJGVQZp7cO2wIJrx87PQ1QlXOwLaIDytpOYN7tAt+LqKBgcyEHs9rN6Ss3F8J0h51ClDr6cZKxAP6GHqQ9Z9bMILR9wLjqw3kL+ao131xLHhuzVxvXawUrYu82ObpMzMuoE5rVaKsBgY23IOZEsY2PP68K+hZehVLte0c+Knq5sQWkiFYCxljG1wHOvWpWUBeg3Je09SNjQmD+TqfmquFyO3XjlEHBhJZKC9toIImS+JxVkpXAVNtlW0UEcFd+7JWO713l7Jk+gK3R1UhsPAFowW2zcdKacJr1M9/oKu5VQAb2vpbmD+htqhA/JgtHsaD6OkjFVmV3S98KS7ZV8WCwFrokN92Y5a3eApkRXiWpREnHKMrLCC41cvvqSpJVGb/SnyMf+pQLPuMOO2BLEFtYMH8thhUWtGTy2gyBegkgB3TTtQGQoxLB0YcUuEBg6FPaadZK6hq9J0cvcB3ny1Q3SutJ/LABVP0msiHH+IKoYH3VUaU0TydW/NN46p4cLFeFR4zdLp6OTmV0EP/NrvemgHhWXz1k855nTG5NxFFLuh+wG7YEJC5OvDJjA5rdn2y0Byx7uAkuC/u489CrQl6ignSBJB5djKdPVxddbsDOfMPqY0SZh3Xmt6uGvU2EH7RXmXA7Gm0YkoXkms2xQZFNH+oNHlqWtFsnw77Ql766CiPhA2Ts+8rN1KadjLdGh7eN7aFMPLoLcJAszbh5nkycY7QY7J3CdrwYI4p6Udk9QAv6pZawmR5dXwToXL0tv3upUcZM2GvSJDXdfUyGDitfm55n6aP62AKzuGlkEixM5BXaxMx1MpEUv7vFK9Jk/K0hOO/wKEqvJytsitwbaKZRQztOgRSXVmoeo6Z6kKtWcBa0IBZ0K5T7N4WzsR9FKrK2ihrlrl8zzmJdt15AZNterVarynipUYIni7XRNXzAojIlDqla4OKVyHfwv+7SqfL/R0F8XDzTp6+l5wNZ0Gq91O2xveK7rNSF33NN53heDql81oSgLnWLaDEQOV777b6N1YpCgZr6rQ6mohx/dwluc7WjdKXCbRiX3I10/Gy1Zg5YKlTYxZrAB9g15QKFRoUbei6DSQLK51Sjo24nGcCEZyosQj4WKfFR3SZRM105qO3CLLE6ZoKphfCcwasdfF0bx7/gOMuHx26OU3MQkstI9zqkPFqqGLWshRXEsdZuHlCkOURRAvGSnMrVoCM52ruaqM0it+bn0U6NzzgkewbZlPxtMDkmPt7bzIB/+65AS8Th34LTAlCwBJs33REx+E5LgHzf2/Bqy6MZhIPL/QGPAjdIscWcyDKyxbvQPEOxSUzcXUz2b5p/0SqWSpgha1RXR5jTmD8szmGlAn+Rxwd/CNEd4e2ZvQVFol4ox9j3fHbeAjsIGazQmh6bUrWCnRmUrVUCRSolFpy2uJq4ZNpciydWqw11VTBCikWugISpTjAo+LxI1NRbf46XJPPeDTHaLfsBsnh9AI+P0VeXdRNSBUkQmaCQEu8xQtxbBMeaCpk326GXZ50XwUDt5t2rfl/2o/RmK7RMHDCA2Qks+aRwbTXek63mzz8U3vsRRG2rMALvWuf+SvNyKOS5ePYpgxd/jopEr9+3DcjZu13VvF+93aPCm+J3+/oYApL4/S2mgHga6rJtfp9MWV8La3rPJgX44lrixeFZb+vlICzTkgcSrWidmoGDEX1moXSfiBVuL9BKLOAcSIyRpzp4m1UieGwJOXNHqottdv+FYLenQLw85x8gRs96OCKgiH3zdQfb1STcAqOXSCJG4nClAPOWERIG77igTaUjZ2KLXa0dk3yDniZEDYX3kqOuxMOS1XRLy+xIG1pbMUQIXXPsm93zvKOtYuMuS04llDsPtYGe4UnS7x308XjgVaA88GpEh0eWVap5MoYVFXRpIOWEOn5LTRxnl146m+sWOnXUQBhvSMpLTpxwYgyrQiOY49xdhaZ17PCcbD4IZzQEDJ1bQ3/OmImiJO2BZ11Hv9sSNwzzuV+RML8kotV43L+/fDVDZI8mW47bwEpnUAj8o5hJhTtxJMFJdjutM6d1p/Uors6JYnfDq4jxcXm+oMr/60Td3fRWhzo/xXN88WfiO8DyD/CdN9RjFgszJ4vAC1zEFtstgqADVLsh1DnUbgeJbOxo7DTSPVuMzt05xBKt4+TkvGZnTOJRKp1+NUaD9WAgO7GWJ7aJ84g1jUcnUADh3iZiKtuRky0R6OFF1USIXSIMfcshEj2kSLRTTrY37Ll3FyvPqWFaKbwYlz8w4LsC69GUngvQnM4Qy+sJ0yIsbe1558Gp/xEyHgIjTfZQEYvCWodt2lB3CFl9TDpEfZJ+zvgXRwhLTxTusBVkStp+fD2bR3McnRVmdq5CmvR6UIH6II1k5mmzVXYdC84kxrzUufeiIdgvKhAgi6VUVatOC41MP95ogBly8R2JNBwlNb7U/CqqVDSlZrY2r6ZCAQea9z6rYpOvTL4cUwRWLxWM2IAInLa4m8xyodId4o7GO+x4Kluy8EZq0VtPCRgxoirO3nnKNyZWuFCT7A/ztn4hGzOk4s5StaQaGGrnrLueDq0izaoyCc5nMaChGMACScKqzTXVsGTwnONj2lRtrnWZBkiZv3XFSvMK5bq1OC+UwovUtKyPPhExQiT+/tnSdpXWpOiZF31xrQ1iFai972Ji2aE3PrHcQO5cJjsDHzIrXc4Mp1+GOjFHnki4iZpeliZgVO9P7GME8C4c4+H3cxYYmD/h2kTHadwP/wQCsnuT9sS+rzaDXH5e/ZxXbr86m+kqpMDm7jzvU2jeIgUipuGnc/OezyKeUcqzagOtHVOMdJSXCFuNfnq7CEI0u971neSiIvtZ/VG+JoGmVLTJXDO/SrM8qcscR6cFWcJrUEKSGpfHVC0TH/fDSCqtxjOviFaZknISVm9qIm2AvU6WDiaqVkMdWZUJ0s9eAwp5ilJV7e5jCxWfSEcbDlKdaFWigNl3Rsb8cB6pAHczKQIO3Eo5pdrEgPUntSboSoDy6HIFUKjsnfJpxdDIQjvC1Oh8j2HUTzYSDY6JDFOsbvwqO0VG7Vug+9EE+68FoT3s7y9iUkkE1V1mZcqyBEno/7cn6LQGpcfoDA9peaouATSrcoE7BKLnN/WueIaFGxd+kIb7pCj3Bqleu+OBXe6al3A4MYxiYyeT2eKLxVGGGyGsxtRxIm8DbygetdtKa7nAlKdxxVyZRb1IdXBwoB3D0abqg+nVeNCjuco1E2AW0xoOTbk69A1IyFHs4YMslRZ3p/I0M9rqEXt7AgKiUzHGNG3DLBKzmTJX4XRlTN15RM5fRWjhkTGefQMlM7J9VEF4TM6jaH7xEGYzdebsswuHsEdr0e8JGTKaonEbYD3qynmYANLH9fQ6+ejlQLTH8yO2sEwv8k8jpmtUFrcztD0Gc+eoOuD+94awMSV6cnvvidojH3ZssGphzBMYmuVNdZVU3ENmfk+mdSwrXJRpNbCXQF4IGheFSF1t1ocowFz2toar42SG6XeTXsBTZlVs2K+w9Lspgzlnl5bbQZ0kGTLRkThaCLQYpR9kzQXrnbhsZDh9bezURySNH39Vm5EKnHDgwJH3z6QdLglho9hNJv9ywbfqW7GcK/BoJli4PYmCNQykJhnXhjwdYpWzmqXNifNTwGEfcCTnJQxOdWgnFTWdGsiUi/j3Ox5OnlzI2SeJgbELOHIKidHq5aruqDPoyOy5iF1nuYcAFrogH6eV7S0j0sLfynM/7h/oBEHNP9YS6eFd+EBMJP0x1heSFh6bxJhNkn+aU0PXgJkIuXiDF7Rstx9N3QceV27Qa0DdqzgbU/ZlqGQZ4q3UL36Z+51v/BrgRrNLSLiR/+hgDpfYxjjW+miMCEu2BzkWs4gQCQB6Jv4cIfx31bvgedaxgbMDI1zbnEJsCN/PWpw5FWrM/lAlkPSdGjjFGXvKNlOTAztHPuSMcQpD5O83UAVmsg59cn0YaFWbUVhM/PmHixY6O8XxKQoMhSmCe3UBK0L6sl+DOSt+paw864qrR0bKuPzmoHpXbBZJbXSMbcu11mWKXQEqeO8lpNuHjEeFMZzJWTj7gr99O+QLFCjeGk+vROG454IRiKNYxh68gPDUZk2QV1HRpdryLRuvpiSXdwY0hkIVivizlrkPaIYku+mB/CF58rdwXC4OY5kp1sGKxfx6UmIMlTO5eLDEbncIps6tPYkerkCED3w7ATLnD0WJ7m7MXAclbXYuNRvXACRGe1Z1qa2QL2HJxc8RhAIdVr+X43Nivl5PFmMWF1q/TX+g6f2vx4nCdO12ErKqH96C8Add2ZxBWCAq9ik86o2g0bsn44j4vQ66hinOh0/n7ZMY4bga8y5YfW8mxOYvd2G8o0UhEOztZdgmHR50IvXHIpMPKqolj6TSWWC1ZRNMgfxPE0L2pbXCE8bbj3rvpcVB8wRpHgkvt09GgNw4vj0E+G4LoHeUrHekwyq/iJuT8MliVpz+DbIiWh/m0x/im4VZ1oIinhbVrsZpVqW3uS1yOqTlapD+X5DlhJh3+mJgnqS7AMArHz7Zz2BQjxhaxvMR862I3xGu9/fClKsrq4jn122nicPO+uSOsNGP7RIGEOlDvrf3DvwF4UFpkFJDHfTxUXKpl0yxL4nazbr73f3f60zEnm5e+gPLTa97K1mxAWseA/6H9EJiQmla+upBj56bEu3/G9fMNgTpTj4Nkb87SnP7jUQYVqAT2ifNQYuLvvjTh8g0qFjHiyrhGHrkhuPktMYU1yAXj89Bm4AJDnEbthTqkVfMWTRXpcEBSddK7PkHW2rXnobdjUcbHbGTKMKyIbJE2XujFmEEJbJOiYM4HLYxBnfmAqxLgU7j3M11SINbXgwRdy/j5XtsaFzzdduBmeNZ1rwEUvHSxbGHIBMEFNLQMTFTVhaR2DlkJ2hmZ8FfUWJIgKBrgguVcqvZLk9rU5/hxh9y417i4W6VwFjO18AWTLkQdQrWoh//ruoRMCPIkqcXcQzqW6H6PISvI9VrgqORh7f1h4hMRu7sk9JZO2mbhVQ80Hb+XROzgXcxWYsnbt+iQ/3LJmQHffk//E1EcRgUyH7XfXaLXz/ZVD8dS0klG1gJ6XKRerY1DXuJRd4EZqDuSnB6nJm/Ws00vo+9BXi+MsrEyr3WCU57tRSffQ/rq4UTbt8zu1xyMUTYGRRr1HGLwr1uYD0eFpxW7qYpnfXo0CBkO2nw4BF1CeX2OoMmMrvhja6z5g/hqagDNuuEezSTJe0sZuylID35sM2TEPfKh+tgMbGcT5wHdTvcaHNwF2wuSOh0YNLRQ2olDs72wHMs+7s1Ingb5KeUDC00ctS9JIsbSDDxRkrhIQTOlXetzlTgd3hd70srEzKppcFFyfD+PFGLBR4QYrgn12lRuh+p1T4kCMxiaumlDo7ZIsTLTgnmawFH2dd1axjIGZBKLnJv0+Bu8Ang3BLmRC9v+mMNxDo00IX4QG+7DQJmvuSzOhydq9aKzRs4mKhspuqA2oh7GQvHhLUcIC08ZzY6iV7ihvByKgWJcqo5ziuXg/DUHCPPYh7qZkLApfwJMYcP3fsG0DR9XeCgYsK93egOWrKAzAQCoOxSaGss35GhycSJdC7JvBE2T3Ag/fsOENTUM3oVE4eMY6+5kYGiKMNTBSrIQYJyx/aAgCtgLo6hmefIRwIJg8EWLekRgM3sqGkESmSFKZhAorJalFHKWE2QWvqzb5t2M7XuW72rl8SdoJbQkHRmiSScV4ceYDUdoG+My3s3wtXoltJM+v+WVrOZNqDSRNiglJ5hhERckpp1ECvyavpOFaoiISWI8egwoRkWQfD1vgvZFufG0sXB1ez2kQYalEA6sS4HZeEJ5Jpi3Xa73x7HoNIMSvQIEj9wqdyymoxLk3w5RLJSbzpqIoU0bVgGbbLsP5VhHMclh5DhCSwnpAAIKIETN84wzMg0oL/O3zkgbwawjcj86nm5GBRNaZeIP0jJCO8coQRzUdTw6v0ox4L9dCmJsozJ0Cj/OLMUjp2ASgZlAbT3YqP/iFgpLoRZior9Y6kabaxwn0DYxHOZzDP8g0D2BA/OaM/8xTNPzeE0qd71Azij7UMvVrWxZ4kSyeutLRbK1Xu1CCrpz56Urwez4ph+c/vAb2Z9PoYpqaFSGtPY6lBNoAQK22yDWJPpsJSXPup5XHTbLIcawioAxXRd4aRoCtAgFJq0+TWm0ba6r5TJBS4ZldGDwbGHXEzNUhijbOWfBG1OEofr+VkzH7gA2AJaXK4v+hbXBSLKbuxfC+QpZLFTGAyFRZFxG6Gti5oACslpWdBHQVISmpujhdB1mBsunQjVLpXesyp/sq2FOyEnbIuTiAdScgxxkhJ//m8R0ZqikJmEHMBmQBlB+aIKSmLTZG4hbifirNAmQon4FjRuhKViWau8DVaK1K81Vc9oQ2++JKH0L9GpTCsqL/eQfSE77Ngv3nJ4wxgTpPPyUIYywSf/g1RnjP9+IfWuvblEVzwvldtTXfF2NZE6a8wNaq0qlwpOd+oBDKlt0nH2gxyWoFVQBMEHar3whCVssLRsOj24rlpilHgZ4FT3uN4t9aUQBK2V6Bgx9f5NKjklJ5m6PV6hCscH+nwIg/ht5qkfVgMCr6ERtjF6WeUex1F8EZ1QZGMxSG+28QOuamF5zr74ykvi9Kg69QrIZs+TiYUwru1qM08OW7BLKVCuqq3pF8HCB5LDKotVelbuhJZp0i/Yn+rG4VSVAqsvQ4qtbzEHDVTRJ0CEV3xBh76uXEA6w2EjwpRZE/Zu/3Flijm+HcbFDgTLmiLXK1sprYzj+c8CtmWJsnp2avMXtGgRUZnqYKGTY0+PY/R45nQbAaaBX4XvWOCYrHEFcY6wGLRowhLyP8PolWbob3ogNxjR8+4ijHmq1321xHQiNm2UxDjyAYmer8YUw5kHh6JEXuLnG8F5tlsUm1ERsxscZTo1zWY2SNTzLMb/ytWTLkNhj8N2dYy92005lvZj2kH0Whe37edpAlU2sVuGGTyKc9AFxKDOsrsIdpZPZnsMI3ALWTWSvABeSD9L1qmsFB3BUkG0q7mzRI7VYiiQlTR6KxFZ1leA5NR7smnjZC3AbvsN4wEUhF87AbpdSe0YnbGRS+hMOqfxDROVP/WZ/4sh4YAYSrNO7mSDw9UP2P4a5qaMwJw8jpLv+DeYcpQSsiESkm7BN+K2S+PzuJGnaVMjbQpECr8KWnP78lmjwvu28WpCUcH+KBknaijRq9CYKUCD+KFQUO54S5Rd9F2Jq/jG/dPvHhusK4JpO0WZNth8MEk2lRd5Usu901hdjLy/EG45nTvC4StlFpxmyWsGRR3k1ajYPY4MTZtC0Nd0ngY3vws8312bfDrajDX2eZG5Uop6B19s6SrFd44HHBXqAdW4fFheXfLv57dZHucxnVLqu4uMscNfdTx4bw0/rX6y8Ed5hkBJB6sHkXsw2ESQ8TbbapFpzC7aAViJBBbW/zRW0lAryr6+pYX8VotvRU4SuK/nDyO4O8oi4cyLuNhSMR4uT9xolsM65QN2bID0H2siYZ4gMQuMEUd6QUh0VGu5sTGopu5e6ja95awqVpKGn/qvWJOnz1CNHuIRtJy/8GCwrsbPTFqOi1Xkr076/6IHuqqX3rLd0DnwcKsOJx+S/6rexQYRE/CkRnyeIXpNzzc3kxPTbMDO+CpzBqmXu4hxoDtufdNpQGBP0Ue0a5TrpaIfYywW1tSumx+63SrfYiAqoHxzFDk2s9xoEiWZ27Ql1sqJrSAT5QGT+shkE7Om2vo2F8IpG4d9mGNxYxGmNd6c4p7a1zththFc12ukphHWBPNEgC3b7Unr2L79bSjWewA3Cc5jJAThbrXsoOK26npi23680QuqLQ8co7o1igyBCnFh5OsyO7oXM0fto1Dkjyl2ZU++6Ytg5y/ShUufkxI8bMS4xqXrfTeyKJIpRytYp3OssoTTJr1GZVX3pTS3rVN5YLECWrxw49oiEqs5xaf4flhrE51jebrTd4RkLD6jndOO0jOduicoUAIFYtG4O0cfVSw9QJsr9IALJsh0V3u1CER6Bi+ho+QVuAuJOzxQei184QBVcTN/gJceCbYJbn7u41vZ4YwAFLw00OCQj0rjm3WJQgGJBCxNzJPtHhE65f2ADHyJtEnUKjid5YbGHDEHKlNbzm152D02/5lFxs5dyNGGvLatzeywh96mq8nOVKsCoWKFIA2HYeR/uF1tGMQFRwiB7RcKfmmAGPrNEHC/LW3xjJ5JbFvFhktHvzrL64OtUZYaBSsVPXeOfiuC02AglqmN4UT8+WGHOF0ZZgVggqhxGdVAXi4M4+Xsirl7eqLCInJ9oh2K4IHmt9i1ye5yNtnQq0S6RITEbWrgFSRPdRkgme0+deVcfNxcUgOq+KcJnNWRqCNTFQ2iQo5YoF+8Zrc9Nhmoa5YoS7FLOFjJNsj+IlxkXVqSRFR0fCE8ORLVPxXGXISIQgTTWG9tSQGXDnagUG5WOSdGSmOHmB/bmQJSF2JBY9woY6oDlyZEKVrEAa66+bzquXLRnxgg5UiiUsQUhPXxvWKo6Lft2GonqWkbPY1SArgImFxbRehMTdaT708vkv0JecC16oC256UEWZTaS085DEbHxgabL0tFkXBhbRkONPFrhaHX6NAo3AhzuvI4OxxRxxJFNT7beOSwiqCRMxoar1jPI2Tz69cfrT5Wq6uPBsfgfVHxbfBKwt9zWorVxZ1f6kj7WNYi2iUaz86sTnh6tBHFnuNZjuif9y+nQ24sGxH6tMsl6nXyFefi9+9RR68583c3P4sORKI51M9P8ssdWWk6q4t6VWSfAvjxb6mMdWlM7YMUQvDyKMZe1tqSplQMDNgjpgFkTaSP1xbSF0x0vb142qQnEOgz5p0373ftBf4RCAbSiP/QW7n24COnxSsYa8Pvg2y8ZQEMcSHlC1b/xkuQeo2MSjmU+TldyMaNY6svhxK40hv8NgXp1ABqaUgC929Lg40N5FmZ6Bo5i1DV7xsrL5x69mY0RRmVWy4izNxO1VZMzcszJh9RMkJXfd13BMuydtCngeRKvdB5bxyG1oMbA4Ib9H8ga2B9ib02bC0OiJZWWeG3CQscU1LvfTdqAT4pL99bCAy3YJWt6IWaFUPtiUrlFww3AKWuTOIk+6Ugruhwb3X8LsDdIOmmWYmgUw2qbRwF2l5pC1qx0UJVoDje+9oTPCtGP3+fnxvXgbmfTYaM88NCN7HqyjJybxBuL+dpSpB4pAjBDH1zW6ex+9J0/z4a4Y9439aYKNEVhLw7k/kdhDy85h4AdIppfzaiaK5e3i0nDM/BybnduLR4fe9aGbeowML0ZhPcYy31/OQk4bnaF0to6EyyA/zVXZMN8XrqJBsWUWiJNyWK43TFliMJ4x16mXRGxCpkjcYtZxUm4HRtaF3uSxo3gpxrDZo7rBQmCfPrpOg/XEcELM42boN3mF0hDKYxjT73cDbkNMfenAKA2LY3tpYt0ggYVPAc/UnCozWdicr8ciDyiimwGr4NJAhoHZDHL1mhNiILT1BAh0d6jxQHul1eHwIMuCF/5nfoSsrq4MqnxBA5x7uIxOeelSn4PFnLiI0G6SGAeOr1FLbKBv5faxJeJi/DX2zXfh90XooFhdEpIhCkhPYKUYtu26I2AVKc0Dmhizpot0IJR/GCGOUg9x9Qy04Lcym8fCqAyLB/FX0/ezJHDayGWjqfL5HvFqgIeGlfLSIbIZxIWR2DjQOmc2A7aDS6EnYdkMKEYPhN/tjFIdIAZ60v0qwPvcQ3I5chTKj7Kl6GCTULu4lIRDM6HrTKlR3fw5mrdpTszknwEtleel+3GawsK1gq75sBLinVrF+pih+CCCx2TodNe0rG2UFkNpS11wBq1xC2gIMvdjYEkcdh+JCl2k4V5gJWGpR5WfUeBpEltI46quG/1ihhmXX1+hAl/hvXaU9O8AVqanAipj1QueeDib6NfWkhdB2R9gzLmYtmeZw9ZcJuMpu/bppmzYOzyFzKNDrVGiwQeigRgzlX/uaAzSoWq0RxQH/gX4G7/1lstLlZ/B87jk9VqLuXohPJGctTExUJcQu0SeVL77olyidNvLTDtkZScd2Vr9aRkdN0Y0jD2cLmazNXRC46Aw4ITeAK2B9RtNiZrG3FYWmqrqlCWvOLWRESZI0I3KFDeK57TGuZ5FLawuCEEQHLFteH9oqhr3qr9bto1HP7oaaZ+1ZTGX5guKnC9M9fCOu+A78JdMEamrPyelrB/k9PduE4weSYECTkZ5HpYLDMXKjFa0RB6BFgyKzj5fDWgO6zczLmlF/8mcko1nsB0o5QCLl9PpAOX4KM7r7K8rC9gB2Y0+Zs7d/Dksg2bXKdHdz8480HiW5DmSsZTqauHCGvQiy1NgaFe8gzm5034D0mVJEek6R1Mm5Jkr+gk/5CaagNk6NmANIZFFENA1XuSwMphZniArGayZ65y8H2zBr28fUqwXKLe3OfcHsgiS7UQaw3ABZKbmMM/pgoO0yhc2fR+tP+Ar+tOyo357DseUrhmmYy6am0ABw02ErOlYio95SPDBMs+t0riZddvn4zamMuyP7ELu7rQV9HcXRxV+D3zY2ChWUErkqJO1BpFDouA3AhYBOveu+/cYuGgGa4Zga0HUwBfgaGlYjsH/8/+t1v4WfgwYWjAJhlQEt0MU5PJrEeHW/J1jTm/oobxckrk5L6xV0KQ0Ah70SDhUg930pRWrhxQRACR9NrqgC3XcsoXU5gIXmzrKyeCb7GqKMckakLyuFxrXFQ47jO0KTxke5CcKCNwLuBGRrZLAEOtWdWsOl12eVt+lFZO7tc9pOgUiA9C59sY/KRksPcmnHz4lrXlOyLwAHyGgtih31dQbJ4ZkvptqIH0FDfmdG4CLGM6BJM8cKDN+8XwQb3Xty4vWG2kwkqIEc3aoTaN4IoFgWeoueuKbygv8pEOG9HTkQsUVkoXNTclVloZuG/SbPekKaM1Ix7GJXGYeznV6nbbmEwoYo/ot3waKxE5rw/gHG99BpDabavBm4TE6k7vKGbSZA63725GhmQJic+NBd87x6RpwlqEqGIRWJ+atUEDdICVLBR2QzLkDFETYdcNacOFtBjrLu81JWVt3IXoZaMQgFCdsVhLqA0xd8rh3Xx6oBsRJUgSKzylYktcxGCL8V8roEa+OF9fH7mXukEXe6uBgjPub+hsPtNGj4Pk17KAyeAqPkFCgaAuK7io+dmIhjQgvNWLDRzHm6m//Xdk628PfI3jJbXAEs78r+eA/wRzJ9e9CM5Tt35VHtuFrdWv+dxJGPW2pbf8BYaCrcDfmuUOPyujShO5EsKriRISJmOkMt3/B6NlwbyRfpvv/YY1ngzfKPob8UDE0DpVDYeh5aJl5u7PD0GHIhv4Mceiaae7EF/lIph/qfFSHgvf+Tt/g7zCAZZybLCgUrfNI0Gm32Haijw4uKEegrQoFNqLhnWA1g5sCneIiQq0s+YDcHdoqE5GZCv2zkJ1N95unrK6+zxPIYUYbFdO/fEZvFcmjBK7fJuFuU/CMudHk5TjnkCWrs1+HR0OetdiOxKSmTS+CNwao8cvBaMlTY1mIT2FSKcN9wlRK+hMfWrPO7KqVwuVgWxKVIWe+awHGbC2KBt72ClV8oatXMKizR3uQ8HAkykM4sF5ujWNCK4m5BQTwmHXOLFDflLoxq2TF4mE25zhZ6UHMfeEgEcO2lye+B2H7JZKAjju1M8BLtLCMKfgb22+wS6vHUFlGGedcpiL8ftsaJw+F+8NoPV9XHq9Tz8Le0mRZypaw2R/Wz0puErrHTdno6PFrAj0OBnwD4IICHClxj10Ks6bRpOwDr+k5HYqv1xBRc4ORd1WwkqPEVdJ5qU6s0wqhI/QaLT1u7sBDUDHMwnPtS2lph/F8et5u6kxXswrwBZZrhsIhq0nw2ycm1SXh3lB4uMxjYWvrNY0oLULG3Uwa7vdTe8OSBZXEOJOOlXwZpnZCmAQpXZSEeZ/lsft9emjHXqItc06QQuFoJPhqgSn28seOLnhrDskEo8arqSh6uYaYGskwbTgGVQ+bgUAzA581pFPx1DEpThfiDfcmy+ESAJyOKjrMIgTXohziCRadIwkwpeQTAR6qEhKoMVrAK5jkQM/93iG6tiZGDYejjDwpMG8cV1PW5Z9dxhB/tw2gEvLOKXu259JE1abOUy3ruYDcm1FUl6zFFQtH59WI53rD35+xGXfeXbGJM4TvAscLHxDzTsdmm4HpKNAs6RW/BQX3fYCb1EODeEeED1ZMwo2plvVcUSfEnwxf7j6DPB4yKXfOpXOOn2gNoj1+vR6aY1YeBuXLiOODoNxJqBso7XAyqXSnt+ktgBkShB4DRYfX8XNzlxPp2zrlQPN3YLUEBeuXGYkFcU9vZnldcI1veE8RE4THMhpF73toV9L9TWHwgj3LohlS9GQ2CtazV/cmAYo2/rdjP75lFO0Kx+md7/JyTpHsFhJCGncGoWdGhkOllCZ5mLs5W1ytKpfNy9g0PaATAhRu4hXzorMSEdmxzi+hDe+QuCPRW+SIEap8b2UvP+NsiiAvVUjcV3HlLKWzvQIuFDoLH1PpcdP3qJ/99bIxzzFXFDHRrQeyVUURf/W4SEkfupd/pRgwAKgcRAq/WkJjauLmpaLcveUbP/jrfPaMbCOrNZ1URCA930TGuSYvB4qXc9rH+yFtZRh6xrF5FdW331CiERTUhDmEgvFAWDYSYuo/C+Lou752Fmwx3wyzRqQejHEd6MscPQnRaYdpqzJjjrajIYYFzCrcEHw1C7aLkoqromZH7fmhXFcYY0kXGLhhXoxJwVocMzOD5hL0oahi5Dw164wt68STHDwx3sAWRk6OFBgErRiuvPNSjk1y0qHKXqoPHc5mralPZHQIlybKrRCbLko7GWHOjR8okkVWTfxF9MoZIRcJFSbxwJC6lqRNiEmrkMsOys1gYLEw0EnJOSlkZhajGsR8JVtGpZA8mSwOWyrPyX7NXigrQ0MxCniw5dbQDcTBjKLZBBDpcUOCkAb/iKXBl01eRqJ9v8hi4wTrEl0QeQ4ujeC27Ye+VRX1XaJJ5Vw02azmsVII3AFvs30fM5ofUa56SL2e0oh6HvPkqOqbvMgdfvxVPUn9DD6pgvxujqFBdhCB3eO+aQ5qquIqiE309lckE1ws+stBid86NP1DlJB7YpH6BA6i6Y4rWaZuKgm7nj59tp1BXer8mmLy9aSAIjuPMzEjChbsRCr8Zooz85/n2Z94dRQYle0IOrYNnTCUXleMNMrnSeXhsWV6aNqFVAaAUNDBbOlzf0mlbsQT10+bqcWtf5nWP5DYF7cmVK5XkzzWEfK53ndCGVbr9u1NcafygccL+QGSTPQI3CR3iUX1BTayBhCYqybUZqHWLgCOt/MuCC3BgL2Fb/tuaGfX+MNfZzlcNLwgD6e0XikrQEzcb9aiZDouUtj1alrsbTG1/J96lh32KyN0y5LOKtU1fkvFJg9dvP5B7INvNYYTuaHXYLWz6WzrbkHJubrOOrrOX1xDxVErp123A43IJbccZyIIq+3P5dPlPXSdYlJXDMCvN68XDevjtyv2rr8IkNnQcF8Xs4YkZskP+o2vcMEXJ2861nzV5HuhgpJ7FckTDrEjaQub+gOUdN4hFCemDxWWttig5F19T5TwcTrXOxmhpkRMzZiRRpthH0AaPPtCj+wtU0MZyc5Vh7Z4vo8PoOdsALakmrI++GOe19o0vGyUzqDlWI891iFRuEJHaTZ8nJcMWiPN4CUjQxpcldfrB2LUicitliRGNaM0r/QaB1MnvLE2YVLn4cagI0YDcS5c3PSGosC8iffjz94LO5HbWVI9vL6qWTvN2mmrY211rEWrFq3a6UQ7nWivU+11Kt0V8AdDcGxf+wvdPcGKLIib0DK9qbbRwJvxybsLJCnJSe5KpvCXPV7SuXzDsnPOJtF3gafxP3IKPJwsDEPh/H6hs9APDhrei2qBnTrz7d9JL+l8Er2JPp82MUyZiY85brg3Tc8n2w0nv6jPN6H9Nhdl0P5oL0O/BB5kdMbNHfugg4eNLLfzJkiCi2VmzRBJvPGFfzo5VJ0Qf8MIPIRMvgnCNxdTiIlBzmSeoyiVnymzImJ0/myI1DOywRFnhp2/hRBuR7musYHcyiOsXBiUSIFpKKdp2fQUIVfelEcFjYX3pxPvvrdjEqD4yAdU3lF9gR6daXgB01rG1DUrx7n9m4+t5fcyazltZnNGLb+z0miQCWjbGIvJ8MNyRhUX5P9Md+9F9sqlECFdAbglahO7B9LLJClzuiTSE+cLG9zavLPt3s+J+O2fhT8wNsmfYtRx8b+Nmfd/ddXQM6ILFyAkYUIQJiEk9uczaAW1tNuVXnSavx5BFo3Zehc6TohxyK6gg0Ra2FdnpI5A4pejdWlxFLAJT3yObC1C+4VnT54Hdzv4CkMg6Q3GxhdZG7VKM2uV05/Oh29TFgArZfi0ZNxm7AxfKZxSXj95c0qDREffEWx6ZhhCYQ+B+rdp02h+qosC5mHoIZRnSIJtqt/bdODC5yxOHTC2eJFRxrHK7C92VWvwHu1LyfHb+/mQJoookmYQj5RnbFOz2wZZ3IpylssailXX2UrAxoDAWgqE1M3VtiHwQyG46aoO+JwArrDQQ2AAq7+2Z/XrVdV562BkXPAFBRbf5uh0KgLcgV8ayKdBpAfa8je4yKyUDpXv2Xk8skfy4eYdgjwXCijC1/Ep+BJ+1DktlUp6lLeIzXV76njWrZGIX+oPG/IXjsp7RPb2+O+cI3+3NRi8C9JoP01NJ78tDF3tcMEJjg8HM6Z+3j/e4VzRjRorp3f17iPGMsG2YPkq0EFt1zs0hgmK3u0ZRekw02CqhZV50wBcMhvg9uTp/pdRdhWiinkkDI2iOgrsqxdWLZOaDzBSWeZ1ikduAYVXCXTe67jd37q+Rp7OsTX4X0a6E8pEt/zVTfCjg5gLIU1cg7g7SfD7J1Xs8lRpSoYL6Q1MkxBS8SCcriuMk2F3GVVOZgml0PFgfa20yKgTNZVe3t4oci5uJF4+qU7nX0b9lRmLxkgxeajxcDUNFM1SGs0N46Lm0RYwMgjzv2xHgCG/9jtfnutYFpyhE1y/nFM8jIHl7s03ok1lQ1DoD+4Mjs4dR4gD3VTE2mQEBZxSAolHngyYhh6WbzSi3LP1siule+heMR5nqnj3ARmUpW8OxwsAjkNg8kEjKz9xovm+8iNP+oWbe0wNgf1Cm6nb0DTginZmyz0ksDW2V/n1vr5iFS0gPA68RcMzHgtKs3i/N9LlbJodo0qCxWKV2Eo9plwsHJOb+dzKMfzZTERFGIt0s/JX5Q/qFi1xH4wua5t+f7dYD5rs5sbyawj3fuW5SiCjwwchTz62hJk92j24vv7TxR9uv0z7+5gWP+GuvtFZ6lobR/0V/zxjqvL5WNJ9CEWIR1cY1swY4ibor4NCgG7ucD4kFv/2wYdarG4MN87T5QzTY40Xa6d5LFQ9U7DFIolVAekiHZaPe724dWz/7Wycgu/FuGnYO10GP9v+5828zAu27T3WtKdAXm913mkLBhUw2WuOzdQLtYHAbGwRy4c7sixH/Da1KDMRX5KMxsB7MW5fi3HrAOviMf8EqKAUvCocjO7hrv6UimRSeNl0381Pi6ZxU02/b4Mn/RrMx7vkn6xNj6kMaafQoVctjtLh4xbegQz6DsI1qMJ0WVA6SSijVZRYgYdRFn/+4IPgqWkM2djqNqk9HHeWhrUxItjDs01jhRnMNgbeMPECi4zRYWTS5NxxRs7Ec3EGwDvs2MfEARP/nv6Gfe5M43BZBkfxgbJugOXyTbXn3REfR7R+WScMwrB8Z7eV7bS8aMvnHM773JNwMlqsMxwQ4LrsFfE5XEGixLKLF/4T/gfO8RoyvHsHL6+LXM0dP0i+wy4yl0H1hSjdJZNXMpmVoZg3bBnwnU5Hg5CBX25DWxjEQwPtXy3jpuDP2/DDaIzye3rfoZL+1C3cwQPI4Qmq+ZZ5WqyWQqQY14IfkTV4T6cgy/PbNlBDrb1Z1ES8fNO9I11s/Pxo0zVZItyo/PDnGY2WXjH+/bhAMKt26KA05VBr9cnGBd14dNE4WKBOQ/A3e+5NZdDjPOiSK8FLXaDD6Yd1rEV+WDfmLojnDz0njT+aj4LIo70sbgmquZ4Ocz0na+MBO5aZVNjhfIMb7tEzR2aH8hbbzKETkYnlDBwR2cfEI858wOGmzuNfkwxuraaFbqOTDLFc+i7dzX74HMtClnZSUw8SS2c/4Wbsq+aDSJ3hAM7ZK1U9w/tIKJXEOtuG9t2W4w1Z4B85GF0erQteyov5VHuuNpccktl1MvrkPhrkf3KNNmw5Y8YxEIIjMmhqZXcxrto91aEA9zycDXMLnVCln8YKDK9j8ARBkLDn+oWywjQ6r4XBHEPKPa0oMhuGeAp65xgUe+mKdwyxVx2oOUK1/RJ8klh8pEtQN9oIwfqCnXbKJKz3k7nhLIk6MgcViDQmYoiFxTyRSi3PZ+ZFNjbGkMZEDDHGGOIohgEYYogxxgQiClVpW1pyWhFvVyHerkK8Q4R4A3KPk+TW97bJkxvIxftiKeMrj7kTWyxNF34iyvpuUOMfCDzmAwNKy9Aq//J/H7ZhHFDQATudEPGjLh7V8PgLD+66SFCc6YcbjzvsqDt/exaWphc8XQU37bD93pZnpB+HM3gdxx5eSSP5Bytj1G3Dc1Im6fU90vQX7FFU3i54jTXbGPiMM4dZ6GD28ExETkgsUdhDeR/3jPrg5ZabQfwIaokxdUc9g+XyYx0fvx7xN2afLh47Mg6JRWxpGlVtcYWH9VwzgnJK2IcGPUSJYl6FCTW4jdc/qRg/vm9lNDqlgSEcUVHGO9nhWZobJLfQROwV2+z53m6eeYGysWvKGXnOaKivIbfa3Z5u4KZpOPfryWeaMnckMGudqDzt4nBPEKnC3yYMOajT/0KkY9y2RkIp8zOEw+QZ27zOGRWlqodl61oyVscEOafSCKF74LPasL0yl4aChqFHQjTLvSRNHX2cjFh5cXvechXQbb2MkEJ2TvOyR1QfYc/irrGPtrNjNrtq9OduA+vbpxvTaxTlx4R7VGwv3F32iCLb0zYt9TQpiPYMkfFYGdQsmdEaxjJbk9v4y4hf/HObWYflik+LlAtJDznLjssLlc+dlr8MvnW53DJ7MIxpwXZnIRVVQDK1FKGRJ0z90FyYqbOTmX8u64U5tqtJZIIUtojLsDzs7RHQYkJpnPqHX7bbIKQIggmMjP5APEqMipXRYRy8amsN4/UEiwZkuAANK10wyUqOdnupYkN2YOUWXaJOT2mj0Z9sluRXnVae2B2MoGG117xEmjpxRTGB6WEx3aMot7K4SMXYQu09AEdNTE+rSt1U95BnPhiqc9rlDh8QEKH2dft3wnKbYGl//ZjhHAwYvcrVwuBLPtVXfdL/wL6S+9HuEFqNR2EKNNNZxaRPhQQyd/pZQDkeC5kOeknhoqxvcHwr6y3B0oIxPzhv9FwDNqF1ILjZQ1JcelAxEwDWL8KAHZYrPi1TLiQ95HzuuLxQ+dZp+cvgocvllvnpybUxBTF0TMJvYVPMkpH48lTC7ynwUNim8slNorEzT6ZbqbSUFCjohKExMO3oEnKNdUgtwSM1JIh/IaE1390sZcWIYSoxVMWGtKqwTvWCA2xGcU6OMzKn6E3W3xkwyIiZMN6JS/cIcJlV2VUTpIkAh4A/N6k/dTfUCCoXx3pNKfxaaXtRUbs+aj/rCrXhJ2WnRfR6/z1efSytfp8+P+AVZAUb8g/T3ZbbP62fsfaGRVvr5qLz69nim6D1z8CZt0TWAUxstunsz8qL9cY6ZRJ+isXpWtKaeQ+gM6CLMSqER9slRXhgdbVSA9TGXac9rwtHcSKLOJdmxk6ptWqCsgRcrWbmFisaQDl0/MFaHGSSB1U3ROCqKZVlk7+iwXTGC3M61B7Au5G4ZhMsX/HjzZHdp7h0Gt3wimXd+z4VLPwvezHY65fpWgpt2GwbWvOsXx4p66yXGNVA4mS4aEF2e+JWFriTKfxJVcsPkG2VhK7lAsR0rOr3/I36xiCeWF0fE12zS11V5ClpLlOiCy0hCz40ZUua4VtMcrSmTsedUpvK0EMFAkPQAK9mWCjEojPidjS5WdcLdkF+AYNqFbNFtgrvQbfIQXaVtY/YF0QZcjoWvMkcS9wxrERiutLdP1Ay63y7sjyVfAdcqGiQbkovF4FD5/nQA5eGrqxwPrSf4XWZLeQeAE31ERvBKk6GrhvD0sao4fbpKZesExx11gcz11zPCLwFE0aKTaUqT9LraCnVYbPeM3VQAtJ9J0OrWfMFyEqRwWL5wz6hXShsOWBtpzVbHNjIcoeVl5C+qno0AaarTMXRUueOZwrAxyC3g50AKsA+Jb2DFfWIwdMGszT0+EIjCn6ivsJq0IX4FLPJHb064Bd0Ypxso2InXg1GWfVNZUini7jPeDBUstAKW47hA6GR/O0oFgYwU5wahHeB8SaAYr9Nw+8NUR+aHyLTKknYkALA04sZm87WyktBVTlldsWkE7WZljLOSD1pezq1pn5XZ+RunlMmCvZWi5z+XWNprkds0lPQ8RfIpBiawFbf8capPee79rjakCVWTh8WKtgNxIYtoV2dU00chNT6hCrOKQi8ZenEbUnHN3zzbTH4dSTRiGEedDx81nRC4A1uroxklFx7pEYVLY3+vPLZUluO84Sot1Ohrdz2ecTkCMQPhJGRhoWX7gxse/3Fmk6Dm1X0ztjWcBGrbcc5j3fQYjlpWraoQtxVXn3CocD7EREOrkQ1W3pSXblzpkqzkBDu0x42YLPlQqZotD1QqequPw3iYZMW3sfigvQmT7hXCI4RPbP2NK3hZVsXyLTdEv0PSzlpn5TyjI8YgQsOjkMNTJlydTmPXDwIkoiuOdPJiI/QyPNBIKK9NbJ8IBIFixwnIXW4KZWhOjJjNsoclCzKUIGzyw8T7DGD1vdv0bI7LbtPESlKorUEVyxOqgBRWsL2G2ykGrFXgcT9neu3by/cnBgh2LTFeBDuDDfRaE9XfaAFAai/PZPRATslRgxocx+PJH801RgkJM+ApdsOQ98Fmn91RFf3uUnvj1WNFEwKR758XjTMLmX5z7Wj93KypONcUfABBS2OjeYfTKVaPs7eBhAV4JUor9tBjD4eTtUd76JEIZebnGg/X2iB+2v6hSm+LlFyK7qlOgwdN4prVWkexVxHRbrHh+7bYkv++8EcUnszzzIk+7ij6jrGkHylg8jQWafyIFksoP2cxq/jQ9uz4PmvtP823VESMnQzH8n7n2z+5izpTun1fvdJubnaWRbfx5u69HKaWDelTNlNLUckLgEMU+IhIVPSEQEhn7jrPygi5AQsntiEDM5ZXpQpJY6JpCKLUl9goBQzux9GPiCipzHWUBqY8tdkXVLNgDf6X/dA2RXfrbHuoA9sxP6/nYzYPrQ+4Nx//X7gftf8/UWKwzd38WdW8u+m6w/8L7WlTVZd1GgSmMwCjRUmJn+7LhNcHUXvI+r5x+JhlfEGmvtxWI/9VUnvPJlCaqd9lP0saf8sXdmtejvNydSTLVo6cWulLzL7wGeR7nQli30FfXr0gYSl2+BGuIZfXh4qgJ1yWuRg+IABSGYK0gInJjBPf7Op0J9nKAY1h2QLzavESJQAX790GM3S6NmcrVAegw0ODahHdy8olI3mJ9KFxtBFa2goP+2tpmnqMC78i6snOU4Kfwak4Jf/RGGoMD1yvfCOj3sJVIRuMoyO/V0/XBlP+g2lV856FsdIxWxWnZfncJbWRj6brD8CFPBAHih091SCB+AMnPomOA8clb5YFdk0JdkJF7/pfOOVUAkbd1z5EaXgUnmDS4opwWfauMY8AIV1x/k6m1g/G9vU3KbU/4YSnpaOdAxgY/qNXZcZ+Idhxq7x0WuonFi7DdG43N3HbfBYHdWC75J8h9lwODHctvcSrKii4sgzcbCl3h4XNsDZO66YxxN72Ew2sI8hv8U5nK2cAgt2DIqd0vQGjahTwpGxzNj5DCv/IpJpmB/QIKeRgEFp9FAQ00uEsRLcpVymmLxE4O4Nn/gssEvk2RCz8rbCOOXXvqKUppsFig70h24QkTwjsa/JJJyEL2YKctondRt07oRBcO9jBqN6mmaB66pwZ2Z7JzwCDYDmsRnU676ucc/tCNNbWD22vBIQXk2ZwGBn48apdmL5HwBDMeHtlH04WEDX2sluRF1LvMZNjd4dE2KDRMJKFRlRhny0UJjaa/VI+yRiOSbwTCRe1eUxKNLlQLuCZfvfKLai4Ji0jACE7QF7A3oKla0Se5FHthylGYLEJDdWlsPEAA4hLrPVL+0K4ie4D0gwef2qUkaAn1nzHZufVf6KjeHxU5jNiiehdr+kgxtqivJ5ikTROu6NP/+EycJg7dWE4yki/9a0dHwCVeIntabwJ+xZkqUtAVovnaUHdkgb5uzSgwNWl9u7Q4lqkU76J6dz8LYGv/cdUSVcFNcuqRZmUTv/YSKrCOUjIBmfavpicFzmCdBaFLeqCRzHDF7hFL5D+UEnimqEFggoAlJAEFBEIqBRXf/T50JBFaJia9abBgWcA5rHTRef6L2k0GTXky12mj78CqLgv/Sy80VGPml0yfL8wQl4gvLQQdzd3vw0ywqOaXH38HEDMGa0hVRU/k/VzeEleH1zkMSd1hIsGJml+9aAhk152ol9OlbaEW7ely3xbBruF1BIJyMqvVxSw0cG9LGrgbsRK0qCB13yy8wKuTOrd8nXWaQTmR1vBNg8lfQpLvG2rvN6CQ7Yn9PnAwp3JmfvGo7c6SR2CHSsDNoiqwTEw6uneSX+TjypmMOOLTCmAWaD2uMW6QzdJHiYmBc9fKa+t/rBwmNZDGC1OEkHFhX0M8CHOWuG/d9YP1greMbivDufo38kb4eRwmya2nP/d0kRFaQHfV1WgcgbWt5DJHFQWwqjjdhtI0pSiPF6Ygyu1cyYYg2yLyjKdogIrGzt0PVlMusQyXhN8E7iko9eHqP/l8ZKZQ34OiLZDfOny/+YqOtGxnfF6+NMNOBDdPsEtcW8cKjhuDLOHZLjN2eEKhNAh8RDQ7N13uBQ7fGry8wKjYK+v7E/sxcZthxaMGfsKBqztseLOajGt+8cE2d3HveuFx4BailIE+gFDGC79tQGAENoLWebULKti7IVuh26Eht0mIDkFmEbzUj6zEnK/0pHmQedlNqRXEuFyiIkfCwc6kjDze/HaFGJN1SUuhlaBonhUG3Q/DPw/bSScLdHgb41AZ4PZApfQq3X5JjCgSJuHwkNpuXAlNTaF1HhTQb62haYNY4JzjbejV1rVv0v/dHvYUw+lA5zVajrxYHNaLUo0KoR4Lme9XjazzWXy/A1f1Al6Vkcjype9nFAAARynoIV6fpk8eoVKT8gY1aOkpdYtwaiS/XVeNhFLGpo6o4Hd+TbfnTtmqihC7zpccydO6egeC0D6t+vjOgd8DsFNYahXrN1S2mcB4x4YElr8mUvcq5urJT/B0jC/jw6Nx5KQ852phMSSt9aYReEdqvgngsIpr7WkHLrnQZtFCM7wRIjgudtBN8x/tTcuXpKC4LRhxVxgFqeXHCRpMhUuKyw3P8qcliilPf5UOuWyo9REp1uM0GM13M6AEZYVlkqGXWRvu7JfhQwJRzDk/fcNaL9zjpuCyJcu4Y7210runak1ppkaOS7sofZlbvIQFPtJR5MQQGDFcQHNUDWRDGkMUlrA7z8n0s94ierIiNw+hm4IPCZuhj/OyITYBF3MFfVwZEsnY/sgVnbix3QiwF5xnY0sOnIcR04PCeRo9Qh+7H3S/gzxTKiHygEDXqrhdMMIBmK5VNpCPQNa/zyQBWg6CpROBdtRoJ406R/ak4dloRA0cUSuM7HA6Eyg/8hUP9yZVzPzaHkdWdpx4UET+gB3W3HjXSAiQWc9/h5NasKQauQBghvGmdu5mZjwNl45FonoQIDakZfmlZ0gvbT7c8Gks5e4mpzk1yAwkPmh3eDf9qA5aF7P1gBS1ZJCWAGm+TgkekUYAyLlkZ77ND6Cc4vFOc6fSqBkMAKIhPLuAR71ogLtS9QmpBps/JG5b+FsGJhkDQhFAO5cDH0DD+Sy2gFVE5AZZ/pOHr4HRtax445Ghpnub8pBATjF0FNtwIlhIwYgJrZv1vYC+yd9LV7FHkVhwjZued4NiDa25MnFrAM7TlQOoghhDVdfAHQIvMG1+9lMvfZ4OZczvRRpDza/SKUsEAPUmxbpJeAWOwsnXs8mGcG9blxv+r53xitc9u3DhqL0qiuGKQUZRouQSRYYiHsgOMkh0qUUM5ePwM7TLxPo97pwRdGHk0pgVrdYB+Vib/EnvTuyy6oK89RzFJvK9FJGM1LQjRuluwoa5TZbAMDTnPystENTbp8KdtibAvBg0jWN7o73cyRxTbmalFtNasrRKE9O/nddyvwmgy6BKEPKfbQW34TxMZpDJwP5j/HzjrwmpyYmxSXik2Na72wNWonBApNbIW5dwvGheCme7TUfYM7zhfxbagxeTwWA6+xOAd87xkJV3FLQFQlRopB7QowLCO6b/otjqANfElECixmY5tVkH2fHt8DPeQwo05C0PVjH6BDclvmH2HFqMpR1HsWBPZgU44XThIZ3H6E/O4oFXomAA+4V6M96QfW9vGf5HRT62awiKxNf96IgT75UGW/pWgPw3HWC1BUfhQ4QlLhdtg5PxE4LHrR2BlCaNHayM3zchutZXQj4goWVlyvhRELmexcvydNeO7CCiEOJ5LAEltsFiIRvmUi7zcRNRi5SkDcD5dTfOOkc1YF+BY4zZK8PozJc1rEOpYKWXgfKt4UNplK5ug4N1ZVkuIdbPTTpkh7nzSwcbiyL6t+1o7McV6zg0N4oqR69tIE89D8khq8ruZUDKCnhW6gS6NBE6Z9djXbxngfmHyv84nhOwUxC2sxlo5PcKXklIBP4Wggs7SV+SDi9ULi/+M21J/QMWMEzxwuqoJ7I2KLnMT60A+Bxu39t/TXV5SRdEfkDSx7GSM58oRvgXG5Es3SaztnFT/lyujbigLConA603yBl18SzFztoG5FqlE/+pwjQfEiihGGchvsoZLVnvvZ5XUZdY9KKS6iekumUzPJn0XLrg7UYsYigUuGGOB6EdroqukbMZSmmWBz/0bVqRjze0WyHfozUxSTvQS7ExLDcwOWev6MKQ49bXJN+cS872w1iEiqT3OsHcU5iMJtg9yvM6jBhTVuSQIpZ3OMQrvrqjLpaJuP2WDT1Fd2AsLPFIue2inCgCWxzlIJKjt6PlV1hSeD9/7yQyFY7Cl2gZDPsP6iT1bEQueP7kAhoRoXWFnjO0C5ts/cDMbGFwRZhZqcxkHhqik8jVxDEN2crZ3BKJeRhbLyTAvifYGYx+WMWbDK9cxplvNfRmXOSjIU21dxpAij0Kf87wo3VgXjziAo3iBzRfDMiGHdB/jPvOQyL+XyQKMOLAeHovi1NuFDYU0a02sqwdHYZTLAW/2bzKOCpnfOLBt7SDASEjGQUgo7zQOh5qyxjXCQ5fUbCDClhGOJAHBM7DSlka4rAkFFOBZG3jI8x21dJxiwclSakgrccFbt0iv80yqI8BZlcnzJvDWelY41AkrVMFHYR6lbMGt8DJJIV3k7NSWLC+EVnfKU0VPUUTYKSGo6JcCwz8QfbrxbhDBhK14iCN4yYO223ht/POvyNzEZUIoXYuZL8QaR4tQ4JHb8QFQCAF6kcirSKL7K0/Lv7zbtvGrJw1Rgb1FykuRM6oxdDtuzFY4qYG/+SnuFW5AcISbXKchA680wWGGtBdzHRwWm+p/pgGazPpLiW+U0ynGJkjRwJwsZk1VpOh7goghxfZdovFeyLgxoZcR9pSiyERx8bjf6glxhdWIhgXHBZn4bi0AUEKm8ywbstNNyUh7VWhAQcBUrzvdtGkHr8no3t5dWYqVOgrm3+DlVHKYyPI63/UR6b+mjXNMaFx2A2vg4nznEhL5yD/BlkiEMq29nhc/uPrl17hPbym8iq1rjwgBtj+/dABRAR5DlYfmIojyPUvqHM6P36zkNy/WJnoAV/Qnw9ZUP6GYQcYD8ut0yguiOoPhsDuXfC0eisTolFT6pjaP8RF1vajvjKopPgCRPL9gYBwpRuWgArIFozfZZ2M37MDYXZA3wRBT3d0HS4A6pZOiu70DuQ/Eui08jS7Ufqw0gjC058UdjjqcByuUR1qnULCEc4zrPRzb8MrRhiprnxOKH17K9mP8bDP4WGp3f1/zLCICenSfY5B4p4zbEWhAufDCBHCiwgb697QWgo7o3H0ypz2hpulkUX/24pp1bYLEvMJUSBBjAZPmrMsiIxBShXQ3CLtIbI0q5GB/8+NofQ5rmwQAmjz6BATqvCd6Zk8g/xMvklbU3/4b+cKqmAcT3dqPpbmZ+0HjtUJJMhg6NSOpSoUhdHSLkwp//8k2HPLVrbTb1BpFSi52jYrVsi0QILkKXxjS9RIZJe/4AcKTK+m6TRVdpPrj+EBxbkyrel/M1mIVgUZFYCC3meSDWpK7H+DEPr9X+3QptwC/VbiMRbaH69XtK2S8AkrxgwYYqPCr4ylr/wpGof1ehDnMovYpNPy+wC62a2rThj5+DQSVwyUGm8fSRITy3jnv801znYFgc5PH6ROtX7DfJxwOUBgk2xb81viwv+Gh548LFYMkKmIAxrxYN+IWGQvqCc0fqJw10snAZ295cTUOFesFybO2y+quOwWNttl46nesrRetsUOvUeqY4WsIg3/d31z2A2i1N4C/4jxGRpyM/1rb++FyKAkmhdi1BGm8qz8Xmz/+WqUkCmeXAglP7S/q3pn9YCiYY0G3aI+rEmt2UXC+mbbezp2WWSObbVfa3MTb1f9JM8rqcU4rZLDKd5JSjdX5cnGq4acASJRQexegUGBtGV9Y5HSSQkV+N8mSmQNDx8zBAFeCHe9omT3zjl/xnw6+4ua+W3770/mf9gVDu9mmexVSPQdnyS5OU31ozyKsEBDhiHei+5MY4uamAqReMlN5ALuxVw3yQsAxMOdd8Mt0gPeAt5khGEvQfXcJpvEOGpnUWpsosUDqsW8FKWpsiXNNT+KJNysBiw/JzvVWeJfX+UTEf5lD2iv6r8CiPnMJL+OeQQPJRGTC8k0w9yhtnHDcec4qXvXg/lzDN3d4PCfvt6xYMuTC27z4nuNWtfeMCfLSwmLLrmivOt79uZwzfuZo5s9wcmiwArX7aHcLun8SR0fDqjRyCmHWNq8UWIqOzxnwi/8HWK3exlUnyPXsi5GjHXKTxDAr4N54+iih3pR/i5gXbBAjCJZBoE1J8RacYfHHJgu5O55KF2pPyF+Wem3aYGtnDcY0ej4ab0oeDYXetfKd2duzdHbbzpHE17xT7eeIryB5+mz0/kCqGafuDrADvNcuSLx1B0E/43bPQCRYw/Us8oEAfSuw1LtEf2qUrUsq3z4OIbTIiQZcvsLm3mpSMLamsIEFdMEKwh3A8izSw5gVKPJv9OG2BcnNrR6xXF7T1dQiPUlHIiVmcs7M0KZWzyC/4kkbUfH1oReEcsRRORqKYJGSWXDg/Kooylof3pxmdnHM9ig4N/87xbYjvaYXosAZqehs3Jdruj4ZcIINzxKBjV580yYQGUuy/aNfYU5UVmhYwreQRru0N8xC1Rly7WenSEQnDey2XYa8CITcQJE+fuMKnOQt6xzU654AvT6+6/gwYlABBMNoJsIXq1P0NLM+y3v4cpy+wmxwXsgtrRxcuB8ucg/3bQUfmuVPE/riVdiXd8DkMaX/+ITsd5P1m6/1xTWHhaAhJLGWJtx9jBIf+yQdBTmMkcVzme/yXZUgjcYgaESPUXeCe7aOJ9uyWPGr8sZ3Nt1/O2AFH+loWUvT+KzDPnMyTky8TMjTv250z1G2G/67Ym+qwJ//Qkpsfzb+jDMbH4BCYzyWDVmrspsQfMP4gXkg0gG/xtdXcT5SsoMNLfO4wp1dNyTQU9vM90dZw9nMREWan057gCvgsP+Q4FVkW7rZK1gtCyeY5DjKAL5SVQFAurJAMVHBTJSbu4L1BmHXme8sb/nXceNX5ujRvqeVtOsjA3bQscs3OGWnlGG27jJL4t6GXwBa4+/MRjifCjf/GL9fWOM7ZDqNCJXob151I1MqGECGYvBa+dUYEy2ToPTvU1kMeNjb3qhcUdNb4pEiHldqI373pZ4kNmzF2Ac9N5XP2OLDSMpU5JTd3xagtgc9tHiUdyEiJprH0eCPcmm1F188+/ivpnw+JqlQoHJfa2ttC4zLCfF70jHItW4jeLNIN1lmNrHDwFalHbyjLx4eZeuL8Ie8fqW7++cruCVB4W2CDFY07NHwIaZnws0a2CiRB/hQzN8LpJohtFPYeImojNY5EoCC+kGc+XE2H+Ni7+xHrssnC3Fm3JsrdTVhVIZkF03CLJ/QBsAptQHCe8zLDxIwLK1sfniSfbYS+Ad9RoBNJjehPfr9mWiFKlZQX0GBM9RMflCxe8y/RIIQLVgX1QaE5zToE1UbuJzYUtj3wggkdwBl1Z3YZUhpp0sNBZZrRoNMzJTlucYkfxaXPoeL5LzTwjn0EP0MvdTApDfQroDNuJsDFPT7Duf4Vt3a/XSaNjIrWCnG/rBNQBtmybQrYNPXBbjSN5BCcsU4Q1JWO4nhYqGvDM/VRElUqdFu02fW10welbS2ljurO7Zijgy0m5J7tEVawvT63M90RKRqXySm46jo7PdRQbTe3ePFmVguKb28ZEFAzQIF1CVUEgSJXGIQ55PhLMNViyu4H69rc6lDY5rRmQqYf2iqLc28gZW6aPIiR+RcfpWHMC/lX0X/8lv/llQ8sLJr3AM1mX5W756kQjmIsClk4sSwDTZphCDlClUnAVXxh2cix2PcX3ZSEvLLyyoI7xOzm5NzuZF0tNqj5RWvnjOo5VdLCwO8SbRMUmjXHyi/FGosYIYUhRG2WqlWFkKg52bJon9ivbfVOccfmzKuqDLFk6aiJAcbam9rTti+7SiLpcflFsoW/nadkr96UtllEmp3t1VedfqlWmGFsHi0M4WkD2rzVF/dK2ZduuSphR2YCBqcr5PW0XoRF+XMdOi5WfbAV7AMRHYg1bBpLYVIYthbe4A8pKjfooWlWh/Hp0Q4K3z6G6wh/SGIWLjJbINnzNdsbmJwEoE6rX9rbDfMKZQSiZ79Vq3V8kVUm0d0JHIcMavc+YlQ2Nv3ScySb7T1XurLqZezjyNzuh4NCqEqPmaAebwkN8rDCbtl3BsDpeeGPtgqEmDvDuqXtv3bc8SyMLnVvquQjA412O2WstCcBpDsCfdxOiosmAluyu3B7ltxhRVKRq32YjiOShxE2/xygqUvD7I8DCfduNxKmFTaXD+76gPjDtwlU8r4c4QKES7n7xsn+j7s8Mk59+0/SYD0mdWddRyOwJvlpwpGJbBkcd8zwcYsc/vcXWxg4bmdbwx2Fzpr0tnXx0s5lzVt3eXJXd9Z33G30/9yBL+7Mb5GIoauX31VNKGMy2Nog34U6lCkxOhZsifLLu0H/zpk03Kfitn97NKxB7833q+CtWJIQuLu+XAMtcbOF43bi6W+qa62Ld469N7hxeaHM3TmZg4LctEJ2YRRpbOWPf8Tcn6KtW8jVGWiwt5YPu3NaDi20xztu9ii3212k3P428OutLnrTgISbmhFQcWI7KFErjS05Ks51CCRU8kjqZZ6Fz0TEaNEw8Z/PpPYv9zpL7+QmigLQU5aGyklwCKqSjEwrpp84rddT4wDeMZtD6chxYm+8Zyea/wCEpQJrnKTtFyJ6CV2wlRVsaGWfMB2xH/E2hag7GzseIMZO7T6X8HcBpULuektFCsa+qKGx57Fc1d6zfVWE8q5o0YdHIbdriSLZzN7DZG1rvy2QX8JiOUMnTSnfTkWFCe2g0fJw4o7+CSdoSmA5vaZdvEUa9wBmycIHCXpMX2yD8j7NG87PiLiLKEudAaBjHZRY2bCCqSr6+NhyZCK5SZDZqA35TOyG+CGF6HsaNUM162k4nFNXs6n7MUacMld31nncK+KYZgcvw0b6BQZWvqD5qkhJ0rsoSnmpz0IUj3G07AsSxTc4DjhWZyM0zh1bPp6mUaZejjF4SlV31nfo/DTQpo/DNEfe5m46mxVMcI3qyR0hVYzQURgbcB343WFUxn/e5SogIjKBmU+vkT6HI0z8YuYNzWqnn6+TUIFdJEf+/LJrONiWJ+HPhDX+6rCkJ6ZbdveHPH9lFF8I+cWxv6ss2fbgckNOuwwzcxiNKcH/Tcu+J5k4dR9you9bkL9SE/JBa0P6t1zp1XT+ZR5YMPT3kJ9yJLzjxDJFG5q7pioFm2cpQMAckUB9nmZq4r8TbObj/3m2Do6cVAYU88bt/F7JKs9xllXmSamVRzbAukY14OLzgso845Iw91gDBDO9IhVgY0OGLSoeLEdEBRZJvZIn3guukeSEbgQwkzRYL4OQV6D1+cs0b4MrpsfjBBhJozAN1rp+wGdlhh9S5kCloxTrhYUyEGtCm4xelv+ED8brd1w8uEoaVp1IDQ9fnEVnD6JIlGP1rh4riA1j+rllEs/KaPjvV4pF1NBBiyv4Yj0HW1fSiwwDZWDknfTnJ6KFvJrbhm2MhCKhq+8bXNR3U3VlDgR996uNQAZDCTFXGAK6+IIxRXlk+ny0wCar1OC9KsNfQjCH5LeywRZCy05ovpa/ExpkB814UTuGAKhHsdzC/3jAz2upQ0yPm7b0b2G8LAywCzC8S1O5nnqFAG6E5jr6J+sa0suiOL0vXZiOWh8vRnhF5KOJDzdKKy3jP7CpX2Nu45G5A7GRRRUKpr6+2hwKkkRvb1G4gwZEcLm1uZfWENY5zE3c7SIrH9cq3ABKm1aGwigyD0vP/3yE4qJ1+864DqmNpBe6MTROF1qGBr/rGX8A9EtKpgT05FDqBnuzk2X5Pgi/kiXq4eCVMgnEukcmJSPXS/u4/Ol/4yceBkXBgkwwcVLSYMWkpSthdV5Rye+tzLTNMApeI6i+/tHY3phQ56gVWfGgOGZfTYMdV+cgPRFaTGuLJo0q26oqtwi34sJEAMngURS2/QlVS1HtMMoWuO4GE0VErQ5U/RCZBLy9O78pBrsAbQ5iiqALOrG8IvynEQUsGUEuNaVnu176PZPuPaQMUeO5LzVp9spICrFQsimmsXimjKA4/doNAERcCFwxQPjbSn9VwKNpg7oIkkhNAqMn7wEyGCvArQDS/ZuGnPC/K7x7YCohkyb0i2iG6Gnd5kVQG7XXxhsA9l59PXyP/72q3B9O1beFL6+HdBensHfOFHrqrb8s3KWb4xmBER2PCNXnngisL8QTqRDQJszYViilCBeso/VWA5B0XT8+XP/+dKG9e5HFf6pq5zRmmYiFP801g1wa0wGUjhUkzhla/wslPFlfGYcOvvjKYI0TZOH3hI3cYMLnncuYsC0ipv17VpIp76pQd/twq2asYVuLeLfbW/eMGds6ZPWXIektjS7zoH3febv0ScL+magmRGqgt4VBr/zTH9IHa1c2/rL1p957jy4dvXPy1uCNkZFLv10xnyW8qoZl17ZoemlS+C8C/lVQVTrS2F6/R1gTPLkFdZjMHyf4+8nylNjInWEumRLGHMccPkeboymmLobF5MVYiqZgqUSlBhI2mrLnbvpfAaVE/bnIQTiagWdPwKQ+J+rZFSVw2n/paH+IisTWhYMjtYM5OoRkzg8ohmP/RwrMz2Uao4wknsOB33+R0bH/8sE/v5BBckI+T3Lo4L984IdkS/9ny+ibiYhP9lKZZ52/slbcI/xfProfZddUjM0S8iRaKwhp40nukMyf2zQFva448sEM4sEmagk79A8P66FRalgbauBSilLt2kFdpsp1f/nAuF6RptIIQvyN4i5+XpgOEnplbEsERBhLzTzWzLYmf2oCWueS0Z5H2CBnTqmvi8UnIrvt6j3Gz3Vj1uROHG+SJ8Cg+L0ZlfKf5ED8XUF3YRWZ+Y7WvX6VMPUdd6vB8T1JB81O2AmcdFrR0ulLXcDpuBvyHl8vD9jydl3QeY3PPGitcW5SNnxrFHnAJTkk1nvnzTzFVmPX0G1D5FgAxtvpke9KOosR2J8R5R70ZeomlPAXf3MC3heWWvNdLTkYLftWdp2E8IJiY51EaZl4YrAEROpWX9PSLCkpXZ8NbPHPMEPGF3H33M8JR6iQRKGX/naRsET9y89WBFbR4MeP+LMvGw5MYYtIY3FohsPu0VqnCrXGoxWh+egLC5fcjNTqs9es+w1rS36+rZIs736kUuVjRCF5r65iDlF+DATwAjVBQDzg1xxlk451p/L8dhiZupQ8g3KQt9jkwsfdB5EUwzIYbaigjKrnGLJzAz+K4a0uSoj6CR0qKyhYGMgAgOf+mo/KMoUUQLff43qft8iAF3UXjuIHdXdgSQxzvnwnnBm9cfxTFnTegs9D8DxVRYOInoWvLeFp6SD8NY1sOn9XwLG/bZ4WpW68sXNk6rrTqPzWnwYfHKnWZXWXZRqaRON1B/Iw8+Jd1zR33iJE240klXuYFF6Dj2y+RagVadb6Rlqbv595jr1wZ0J/lqRKSrqlrXRP+Sr/pteXAa10w58bs5CLhpv316CWs7Eqb2eTpFOCuVo8A6pmnzwqFns+p2Q1NTITfpKvRo3ai3CtqldGYvTdUzwhoTIQMxZ4F7NNDLOsRLHBtJDvsA19rlO8Q8Ij5jMJEgDX20pSM42n4sS1uy4l7bONg36gKyzcuh218Gt6Lxy3d2J6NCGdRdTHk0MCQ95lKrM/8PqZFHLxw4IQWGqoozyXSY1rXNZRwf5ObiZnDfS1iHgDhXJEV2Y4d1J1JFDUbGFYuQINkVzaErf3i+M9rPBa/ZkPXX4uSnhbSTcWfH5krmBoErtuKSUj6x+TG2qd2+agXkiPch64xxe/dT5gea1zJf1BuzIwXMwYvNkNY4G6yCDwJNRHm/6t1uTZ71BJVLwmjci1YrpbuViE+XE3qiqaxgGY4KaXQtJumBybRkXWK9UR7GLrUktBjaIXi5G+XioxyuRoROLONZ8peB3qq7kN0DdjtEOPBcIT51mUfG3XWo7F0hAzAVV6ScEuSox4bv1Wl+iizFCVTwE5V3KeyI1ikwzVrGoUhqKGAkWBXzgN4efakfLuWOObtEbJ8DJGGeIfgIzLCGQ7HcKPhiuO1e4puGYGZvJkxAJRLZGu2o/cPAf5lovVME5wIR3saqJ4vNoQ2Vr1wa0PNbjXnxKbOfFESgXOn6UQ2S4rWZKc5BzLLxP6DmWbpSlGgXoLQORPr9QO8DqnnyvuDt35otfBDX+3zPJYCIyDbAQeRTtTVIB0zXxNQIA1GPwerAPEwjtoQ0UmvELGMbVFHwUSniZlZ2iSA97db2O6JRrwSR86gBavaST0lWZ4dMjSUjEyMtC7XBnOKeBscy8aHWsk+GfhLaa24cq5t6RwyMJZJHJcZKNtLdnLZd2L0HoRzM0INamKiNBy8T+2UimLSi8ThadDnijLhEmegtbmBElzw7C7QMp8D0mzXPaF55CfZtTUbO/GMxvfre3Z451tpBXobHo6QRH8oyJfCQaSqMFRJVTlqrfSerx95mQ9qxen7s9XwVtWVhwv4EbBKWzHi4zaPO/8GNG9JzqmecvQL+H9fmio7GDkuGKV91G66i7zxjgj19g+nNwtqpl/74E3w63Y3sX2gV/5+uBmbNyy9frdIt5vHaNR3oLM6Wn887sIezDaxEZFmc62YNlf09CXcv/h4w0ML1ynsS58DUVMdjM/UgDFMkM+L/TaIrL2osMN+jGCB1IldWtHu9Pt2BaMxBUgvAWzu/YzIA/sjPgUkRus51hF+2ZsCpNn+9LkAdYnt3UXtHT9NpOQ+T5yObMLR4wxprtEZRmhUsz5mUL5te/2iArezWacAj+zrfWRailWD9egxZr2ZF2dwLjbTYlwU1V8XR2ZIK1QMdN9eWJhbK1rQrECUsnUm1EyGZSCvWQoPQTWB4NaVVkURGbou7gTOaNif4Z6P7nn2iH+4M4dv/lQo5MEKuchvtIrNBwVAjlRIHnlTlwoqXxJB5Fe8e2XTIUq9s0xHHdkeWgh5f8GhEGa7He7PT31guz+nsgyx/2Scjm5JsxD1Wy+HPHt+kOdZkvdIKxUip/dGHqr2sAFNguxsXyzoUgGrxmaHHHgoVeml84otXywX2qr9ogGt2hyfEBvfjFUeIIsUsoEfKO2TlvpaqdR5r4mTKlwM7L0GFg17ztzcwTYiIR9WuGhwgmFMU7MRkNJGvjY1AgPy+CCKPBczpOX/fu76Rvl9UIUs0atFrZtQ3QaGdG0urkPLDzsp/rimNDkL9JRBDgLdv/EWxGRr3Kc3Xu+rbZkzidGYExcdxUp/diYsVAmkf+RMFw2pumBVwjz5KmxYgB8ONZZzI99Effs3YFG9S8+HhB13/dVVO8/9jp+V2KzotbZpN7lNpPq+9s5DeV5A8AOsv37ZayE5dWevVtWS8kWnAN9v9xTeR+wRnfAUJEaWTtQ+AKwes0PS2XW7fxMdkkHRLBlaW8jITUpGlfeUGqRvWUzKM6YvzNn+8hrmwbVhm0n4fHZ61cK++82pJJrYF7THQ4Z23PcSVR/VSR6vS7GNUFSmFhjD/BbGZXNoyWRmMbKB2DITyaqkXpaZ7WbsagVGHApLKKSieQSuaGqcY16DdW1U7Tu+DgCdjRcV7pPYU5ERtXPbxSij49Xsevg8UVXKJVSYPGDnm5xCAqX+mlTSr3vIb2VmWF1t/4Fn866vIDuiwsPeXC1lY5aI7ik73/SEMt+DvJxSMOqzd4lPQs8japU0DGUU+Zt2j443WyTfPhUeis8amLWzuNIGI0a/4hlWmeshkeWZVwZX5dxk/D4wk8yjeyjbOEcgVM8/iwhP+0/Qxl4fzJ1Xv069Ja/EY28x6y/I2qHPhfFZYIY5B9rvmqyUsqdt5jX416IaKeRMX0AiJgIsS7aztFTygl2rixJLSyBPj8qTkJj/lItN6e+02JSafl2y/tlXAvdeRv2nYTBKPexC+PaFhtKEGGjZBW/U6XTFtG5FSwOsmlSwGG200Tc+LgRFImGGuUAJfqqkzlV3ahxjDcPeindNhcsJfnZ3bobb/JcIzTqPxZM49d5Dd4Zmev19Wur0EKzO1+ATsSS1tbNxPl9Nsu0yZrlGaYwzcvKrDjubaxzV6hEIO5VBgHceHT2w9dq6PCoNmLjMdMgDbWw2W48QOdwwdaqLNAWt9vbWO9E23LMqDirnECpNZTyIVB1xifYdfHkZrPU6U8nZ4lk3emXwGzkdjrYNq9/ern0lRLSF4mz2DUqQaN1NXgNuXe+KubafEE3QMGbyBniNe+dPdqLBsTPJic2zwWvKGyJNcTcx2p/P6IIeFuXaDlt2rE3SDGO0fO+RlLQR6+KPWX70nV/j743ycK1mfLQKlXw4+c7HvUv3sbFRtF63fRFhKXTQBoje9Q+HN8bZaXRNKbsv8gE8VvJgRUpXFXA6fCsUMMkiNwhmKa8Rpbt61ajebxtMS6cSbuE5hyfndcqDKRUFx2FTNw/0X7T+3/iDTvOUCdouMnx5ZaKbQqXuPd7V85/Q7Z0PsmVdC8ObqI9D17mutgeEhITDbpX2LwvSWZRGnSgkqrS1VCI7TX0LskLtWHtlvc/JrCyWMdkOwlfs1yOZzwaZQ82vZiJJguHHJcmjkSM8Edn5kw3YTyqfn1qkvqtwlKrhVwbWS3I69ZGZShcuKG10m2b0Rie7eLCIBXP0XTWC+hY1TI67Pj6dE9uIMVQGnzEyJHF9o6to2grmtC1jqYj3brYuGdGp/AY9KQ20Ol/15UPhRENYpvNw39nPj6Izl3hSqP9ftFQBUOabhJU6TDtkMBiscCbgFvdQQqKOj/oZZKoNGsrzyChfKtC4TnJeSjHNMyQR3VR0C2GHwEhes4c1LFUEpQwdPPCL2wB1lDB8KCZWbXbt1NYyXCJnBXh6xPad7SQBDfGvyqJlgv0/3efspM/XAA3z/+PjVd450nCUaD8XYo0M/bCZZx6w+OvnXsDPNtIW3Oj3T4ZiLWM0mGUlIL9xNmtoZ1NO5J143FtxWEdLYXGZjFQHxT2Ypr9yvf6wCdntQJTSal8l+IOFBqvsvLi7q9laOhcHmu8xiIPKTae1OHD3ZP4dK67SB8SniBiHvDWLDwV9HsFKRR6bU7T49YMbZdCgQorhxcWLUtxCGRUtB1ERBKA6qTPjezKUAQ3Y0Mf72xqlekY+oQFsAUOWKKAZ+fAgyM5Li/IioC/48oIgsl7id0SHkjvJCkGHV002SXW4Pz3Ni2jLZMsGLMERsPdQMY5DeHRURrVkTtEleqx6K+BME4wTqMA55Sn0/3agEhAbye6K6AbFP7CQyUzvD5z/DGCQyqevFzKWOTJA7hdENoBDzpUhyoJOOor6hewgnEO2AE7QfdoiyvUKLlZacyjOX1m7KNMrVQhOrJnDpFG5QObwcF1mgujgGL1W7YyNokrEQo9pDjyMx4NF+LpUbQdx4aUf2QaW060xfbsSNIOi3Gek7MiBYC2xDQSClj3irN7z6wJq6QACaYpkJlr88SXikoTNCT8yv0rqFxLj452xehlwCVcZh0RCEf/u8q35PAP0g8ObSCMMZj13RxuRhUkNhC29vdaZDw9Y7p0Mw7z7cdToWF3yy4dietuuq2pVb+emUJSZJmSmHKI9ktu3jAnny6TMjGbmpPH4hQfTO3YtPtzP+eos7MsCPPZrz5PRn/2pHC5wMdgOrc0+ruTdVNAf1inUsZJj1wKqdzbtqj18oGvPppaMQh/h+jrIO1ADubAdcZ0gOUh3Iy1KDwvXcdcjZG/6UPxG5hqEKO018G1o3Rg7fkf2emUDT//F6z7M4Obu6pBu8YLwCcZt6I3xzqRGs4otb4/Rq+Fu8kj2w+SOQwcNgvQClr8weMvk7zD+GgNG6O3Wl2fbGh8ZAyGFxfH8czh7Utp4Sf9/M80UCVq91gbVGt8hCU4O2FNdmT8LsSB9cETn/SBft7gLlMSsWkXn0QJaIz8KrRdx9S5i3eU7kTwI7ubMB0tiP59wjTjUrCGydnVyamiNEjijlNOAz4Jp7XUbt4dbRUDmm0UwN6vL6v2+24ilDhJDAdO3ZuQJpFZKp2YRlG5IhqT+ck+j0DXX8SpNnh7VNc/h5QeFpgyZmdI4P4VPnsJOrJqfXr97kSrovmTo35WMUyNvYLcCw5sUz6slbRtw5rIaDbe1ihAVxRyFOmpHO/EBOPsmcB62y/tkKyQfzalN9GOASJ7qKvAXzlwUXUkvgADuuuX8vBs01U+7kSmmdhjY82IuG/g+TCEr8RBc9t8RfEnkAlN6YCkHURJU25deZNWfpDU1Fc1h780/gK2nv6lHSiScgf4Y55aj2wPtfdQevxmNP+wYXI1Rp2E/Ds5TFP3VCjYy8N+v1oeT7qQ3y5G02d8QlF2QRhHp3+NiX8m5+Rp0nB0qO7D5RyNbskyPhGvlNrPU/YNnPyU68VumaBQ+LcSpRoeX1KLmLBaZJHu8prv8ktE5ipRRxxtu++5EO2IucbfviHF38nOGDH17UVQsOgK3uPVJmRsnOX8ikx3FLa6HP0WftW1KHnRS82ct5PbmmkX211LVXJzS8jY5zE/F1n/vrbJWiG95Mo+jkiMljPktevh64SWIgA1UKWdxCaUYVVrs5OVLA7NgiSJx8uIP5oNf0IJjb2BxzH5LQsTBKqDntWxw7OKrzBgOoTxv/ZiuHMph8LzRDQyJ9IlRZ2ChJIIhzpg4aoDdygIKIc6bZEegZJh0lbcpJLrgUagxyszPWJeMu82XW2TAb62W9FEqFxjob0jLEAQOUo0mbl/Fs2fixfLRvx3Qq8ByMibeE4KtrWAZKjOC2AF+grpIjPlMY+QS6ygYs01AR/rZ1Np8f1aTuo9k7MbYl5IhWjkByujq4I4MNq14KIiI6HVUaHD3Kk9QbIwX0rQTEcVl0cJ5TLCJMXaKFLBCIwbpRXOek6A91NTwku1vLPhojpJGPzoqaixI4te759F4dtoQmImr4NTbl5ifkTjI+lESlZH3+zsGF1HPkuq772ubh88YDttsmtszjynefi4RgfXRramC1+/iFGhUk3GIdctPIvHuddhkHe0cbjGp4svyd0NSf6PM19vGtH/iqCFYGvtX5nmWrztQsXbv0XBY97V2+/3X+H7/j3AtmywYwM2enZ+9bR98vTXNCyBoxJ8LIGZB9wfr4+DUnefpcjTHNwbbSlvbrpZ7+tzPK5n23CBUhwq26GCzrCPIINF0uSoU7nyx5uz6O+DRvELF1yb9yl0poSbJZnsEIXsi0x82F4vlogfm8tZvWpvrhA6AU4go5yRlChkShMn4GkqEcEWHmyS5GkZmiWBSEVm7mjZGJO6elXoYYQpAxPpc7G6ZF7YF98BDXBE23jVjuBTgX+0TpsPd0AdiMRWNxTMP3VtGGvaiYDFV7OU0GfljJ2dS59TRuerCIbfMfGGqhC7KFWJr9Q9esjUb07KC1kgiorjDfXhSgJSJPUmjNEpT/eKlFVRVcksf5zXbs6KE1F/+6DFTSoHqvJ8u/m9n9KjkgsputZ1E3IfywQxkUiwrzpFkVM1rWrLgHiqmhKQJyxRGrUBn8Kj7dxRU4z+/9acC0DobKoZ+7yAKLQ7esyKPUsovdxbN5boAMWJMZ1cR8sJnBN5WzmaeGmwhiC84U+OgudtOPx7h+OPJ4pp8ObzXkRbQEhnq700SpVCJ4FeEnGcSLYU5zzfOH4hGJPDwJECEyRLZFCFZQ1oNPHf0HdsN2ZGwL/rDWO3UplMSMPCQ3uhKyoX6pbnkxKNpjJ9NGW7lpAsgjBKQlizgQU3IxGDraBpVZJjOVVfyJqDrSQx1bQZ97cZVZTZAZitLX+hX5eSrutF84e9w9qYomI0cbXNzeAiDKCo5aGmsAjcD4liIueVHAwQb7R8bu+zWfTQIE37lW6mOSK07h5U7bkUw7mCBA7EVeLzVDi1I0OnnFYfNy5Z/yBwy6xCjndyuiW5sfDesQXYs8A08rT881PtZRc94NM+2DgJmsexlvfKHNua7QVJhA4l3GUXn+7NCnkaJ7cCd+zC60fQFjAL6FV/Bns9tmdjWSmtv3G8Hhw7NM4O2xtxjkKRkcBWqiEkGd8CiR0APQK+9YWLKG/DaMzc2LZBermv1dsfdmWtJT+GyQfxsttUzFai70xhUQZzHDv/1qk26EQ1bjy+UrRTm4Cikxk9g3MUTLAtNWlYXJsFFIpfUES+sxCXw7VBRGS6KUsVTheTPBHhrHGIUwoWWzbYT0iCOd5Qh9v0YqHSYWy0+UljYRlh9SEOZ/xPhnzjf+aQxdbAmp7picOXfyG3tjvwAEdtq7fd7eOcjb5+aDe5DiREVqO2Nmr7SVudq2Uh69SRZQTOjwfRjjfWZZcUtjQq22ijPTPEHO0VSSQMqRftItCVQqsRzFiyGaMdg4pJH39l/NCsjX6PrnEIZBjO0YA0FbwSa3/wq6KIhDpEJlH+oyJ3V8jo8mqjFB4H++xRbBxsLOqC0GrcrBjVK7VrSlAWwjBszAYtzfWABQ17eVzQ/+Ny6/LHKGtjaTO4+K/AwctseL+1iwBVq/3UBVPq16Pap5J3Vv2jKN4eASEbK+QJfmjDHnh7Nie/u7uxqqRNOP96PnNNIlgNBwGhNww+Sr1YeOPPjJmTdU8QKFRY+K3WeDVITFNBsdn0itTa6m54OKWQ+avdz0nmQb6ZqTiCm//qMCfy9xvV0uBTatm37rF9IWCgE0WSwUFdWVZpw0NFaSJgcVK4I/sSe82XWrSM4astSD27aqO8j9Q7a6MdFvGSaXad1OxTpVRlyBAEmCYFU8MlJ6XNBIsJObUPd1WhXyo7BAgfJ5PZ0/cJUV2iw5QUbOocxwNF0ut9qAa+bvYD2qiZuNb4QKo8odb7yDd/Fc68pNhJhUvS2vwM6FU3Ph0k206OKLTlDVjKwSWKPMyCi6F4RKvju/a60dRWAFHXqcux9uLOjphLHlvLlCYsJ0wDRRAT4B65OUZ8x+bJJ1a2POEE55N2r2bay30Elk691+SGE5L9NE4o2wTzSPLQauYUFDUX6CzEU2iJrgj0V4NIgUEKhbKcYI813VD2SXHJVRK9C1Or2uEIpzWgotNjegjGw9ck87SCpLB29Gd6r8cC2FQJlAuvXSAyht8kj9JonHDZZmSMHh4ASxY0wyz9gIC9tjQ3Pa8S5q5OdonkXJmwm6wPmJ26CBqff9UqV9YUld+x/SIhpYRwcsK1zpzLsQ/Ocd4f1cpP9dWXOKZjX8tUHzkTjvPB6D6jK8MDmtjr/yL6mS1JhQbaJ6E2CkPqKkxNKb6qWTEcQH5tWosnTabksIftp8GmEpKOIyJo+BtE5BgoFa3tCaLQTQ7CWhXC6HcJEtAxQO7nyThJqdBp3E8YLIcmLJ7S1VbUKapYWD3HvFatCOlCfzy9luFWZ6rqcYQNlYRE5zScah/daKMmYP2eEk3eCxdApqShu1z2oW9gdUbuA/KILNKMboFnHAxnsfr4brh8yN1cAnFygXF8TtrGpDbX92vARsWWvIR/PvHSrDyLXtSI0dNQe4vDF8lCfV+sTcBdYL+FVCrVmC5qzUeZSc4PWBiUOdMqfiBN46YfAQqLnZkSr8opJLWxn0CdMc6nWDC2wqIdOKidZONx4uWZ6fNeW7trRdHssDajZtBiFDRFpNol90KITdpGbmxrdoIFF2JJfDS/FdKYtJPx8lX13VZuYzCcKcNhMfYsNbQP5i8QYPdjbZoevgNXGgaHLHmQNBhxZUw72sW1edLK2hQswmHVGSkQCDnpyGvwzj7WAI8mqNVoHeUybszbuU50uYLuO01eIFyEzxRwYMgNSw+QesKOd9jQSxWER4v7Lb2QG5KTenbYO5MSMgpoOhRDLjSYc1188ZFt2tkqTJDrNbP+dwV7lZLjX3BjYEnIH2yOW+LBZfZtD3vAiwfSOy+o6fU/kb+pY/mPr3XxjSudXaVRMwExhQkhS8ypUNm4Yi1aAGYb729USMSCpSzwqSgghwXFpJLrOWdhIwRMV5i5+79XjB25/sBBhBtK0AfH9cqzfh0V9igGgrdO/D/tOhZcG6sjY2X6Kj2dtzPI3NLPVmpdcub9ijedifNiaopfQ6BuKzGGyzRon9kMPAj0nGZZe1OEwKiSGJUbraOkQrT9GD1gK7H9GBqi0/00utUT0vxCB1hQNY8JJ4AC1V1phutEYnwzaXtbGywscbQ/krUKtGPOtfcbmTgiThHMcYvUNuxfYuGDCenQfekKFdwe25ZP7Xq2GVkdiQ5R6EbMgqyON1YkpsYTnB7XHEh/nCXXY0waRGWOVscvLKCRHZLKndA49xuGgWXmO80zA3DxPWhciJQfmtMQsC2eEsUCNJP9WbAkDWphbSUtlKFVPTYxRUSP25ooA9UakUHOIOxuBE8Ribq3hIkit6RfT08I/RWpq9aJW14YCpJu+HyVr5muYuyy5xPA+L9u3ijnSrzVq++YyJdJDpGUwmFLWrxJoEkqDD0gS/mu1l3BKgbQKo0KvXLiqPim5fkPJEPcAEpmVcQPFgqtoqtGqNdffnd/xYOo7K8boG1P/7QtlaqmQQV5BaX01SFihjHNVf/pQ6y5e0hZLyLhfVl81m9b6D5cppPivBfSIlXt0EfxIC01sLxKRuob9OtjwvpTI5w5MmB10Urb+mLuDvw++o7w78Py8i3nCKNJmCRygz+PKzPGGR7eqvS9EmPA9coyvOeckOLda9RCgHjvGE2o5AsYhYkYj4REYYqee5QpE9zm/sxu+8sx/eT57OGES1VnEIMRhptAU7Pju+bAx1u+9F/fdTHVigyix/F3AZb0jVTjj9LyBnfEleoLOhVF4+W458wQw2C1WghBGma+/zoubApDIIsRgUzTt+cSSXMfkyJQ7/F7i54qOyyXmVMSofO2o5ygZgYPBjvhKKhP1+cEBW6LlYvoi2IwUqtcUnPjfrYWI6AMNORQ6PunS7q+HhIxJlF6PRAMMZab9PZ9Y4lENwF3SmlVa9WpGvn0vjQYwZMQHGWGQl2PBD7TVFfYxhmmoeVF4rVITRxcRTB9Au8eyR5FxGpB/k9guIOA0Yg+skesBUZ3+YDNOKDpcpfj/snQzRYZZIUmFT8Ge5WWz+LJ5/v0aX0Hx2Mm67tO3JoeOeEfb15S7O+PP2agMyjYN3cDIa5GkaAL5ED7SYK36/Tm7qIn9fDnqTp8Cx/m9KRoKD/7bT7+2D8yogATAgbeyfKAHRuAxtnc8FuL2dbyGZpcYK1VB3nMgz7fbIVmelW5v2Y4FmYNQE2nHT+x+56CreUs7zbVtJu25xu6O8KxqpPAs5fFtBJ9/D1FDKLgl3gkwaF2A6oPVorRJjKCWynvt3/Qdz1Qo418qkRRskkxONYf6yMa7GeQKSDlR0VZgcPc5IBdHGG5uiemIInchEi/6pJZ+48vnbZWtl19AyrdaiJAV7IiBSujGweBnG7rBaHfPeqZF+faveRoTTmh5tRt4s10e4p01qYA4IAPLab4soPBI7l61KgiiihYMvF21zs+avl8U7GWD1liDJWxf0hXPf8OWKdSmv1jEO/ZEYlVlDn1BxM+L/2f3XvWzfB6ZInd9Qa6UtCCIiP3aKoQas3pgVIDCdnjZhEIB1gFonx+96qmbkJmgwYHs9+ptmwHHMfqB2BKECHYX5nm2zMb5Tl6s4j2Gc4to3IxEczQJB8wFO8+/lyReHCPohAIAUIEcA3LaUoo0wF72yv8StRybqI8qcJse7BTjbsbybbFt7Quhu4rsY/dGrpzapT3rHdQOiA1MardnwkAlNwXRiK9OEH6q2Qf/SADOZ/9ts+Cl1gLl67kQPvqJdvqe92gok1ESWV+8Cy/WNY6itb2JgwpL3EHV66HiWJxMXo115hnwyGVHu9ohe0YEkdVTxwE8mdALvVW4KtaMVV2B1P1PNihA2VT0qoLGymOvcpYVAr+qo5rSy46oA3RTC8GaVlIqEpOoxP4QPxzi2V4+r+ohbdJWkn0pQRNI6YCeDZ4PmBtMt0+LoQfdpOCGKFWVjBnGUJDSWx+jWLjE1+Ewozz5C6Uhi5/WJYML50l9WoieCCKhwaz+8Ygf10o24vFLM8NIVekQ9swPfNHnxATrtqidgwvbAnUE69d8K91teOkLyBH8mKyic7OEdMyxQi6yQiKT1YCsi9gQZC75xTjoRxDFmKbCkaFKvjWkF7Z7Ag8W/1OL8mHMxSzWEaW7SWFScNAxQ6XoHG+lpfMnSaKqqZ0diFPOBdkbECckm4AQ69Bwa1x+ad3eZAyQ59pcsgyb7eOscLXdxWx4qgIKwDy+kRRydNo3AHNxLY23z6pNl5WVQUGYzOUYG+EFWYQ3KMPqQ5YDKIxMOCaUvEmKS+/Ky+jOBgoprheyJGFZ8iQHVqkclVKPIwabqPq215V04JmgOLXpl6BDjcJbqrfSUQTfidGnKmnBjKUqFP/hz8MK94N4lX+lxvRzvs2V4cmFY2/iIkX/x2XQyJ8T+AHolbi/AFyZq//B2EwuB1Jzv5d6ObehCCZ/HM4fGukP/0UvTwxePzioxg+6KuPYnXixf4By7/kQhDun6DxOqERsjNtPwuYapuWhAWG/y60U08fhHwHtuLDOX1UF6J7LFH3TbNjqIx46pdxn0EhyU9vq2wE0h1zXkH72BGwPuGPwxaF4BYLgGYc4pEssY8io5wLxbwEESzm7D59HPNZZX5H7/VSIFP4V8YeZy0bQ9Q1Z7TmCN6gAwCDLFeUDOJFUUhljatguzBNMamg0i8nNar3lFuVdOvxKxvKWthni5Ir0HksnkCukXcUVCxxjzlyjBm/0tMvfkCjEjOC8qRAshtQZp4xdXtieT+qHkiv+WmSlD9mDmkXIPPiO/Ulpi75i+cGea9EA2+S0XojUWu5XwI2eQBjmUXe6dU0xcQRj5OVPtAVf3rQJNWXrZiULSOD/MHvGubQ01RVuKF6vJKxpn8uRSfQmtoMr3KPDcb5y4x+Lu8EVzMVj77C0TBlmVPKh3+dieOg0NN+pd9Rx06DzVwJpsTnAgT9D/7Lv3whan41hm6fQhwpaCrirRk19WiF7Psk/WZpU3guxkL8YhbcvGvEc4qweIatMpqXZFqSYInJpGYUBjN2YX2ywaeUKz4+Mr2cLAQ0m0/DInFJ1PpECqU33goJyQHT6cCw6HDeLUvzrn55D/piGbBgtuPMohSzJBZMuZdNX3pWjg/HQQ4vWqcgi0w1jKO3Fdqj5wY0lOC0RGOGajJ2bzifBqrkDs13mr84b/RwArcNhUyBZkJboWx9Js6nnRpNUk/WvqOgSh7wRxo8ayHkitspJMvXba9LP/kIxnY9xrh9QD9jeHNPgKukeuMNXKTWQIiac1YDPI4sDHfjr4Z3BHHkIo+Fpd6gQkemkv221stNmcv17rO255MxM9mH6ZnhiO0HU1bFaSnYZRYKznSz8Nt5H4iOA+k7rG25GXBTAzGZkVKJqFlTaaqrFb0zYtkXAIm7lOLNrngX5w/zhodQ3KK86v74y5ZmqlRI5+uKSoM1Jg3nUiBKHaaWYP1us24i3GQmmfAMv5jodnKDjJbwNPfnxtQj8Ydt2y2Jc93mwfjKacg7C7ubNhestBDfhM419HXV3r98Q3NhF8135hubeL+AzVNh4/miyJ8nHmsT01TwdF/oKTzfzCCVE9/myA2rT8WUK/6dWplQ8n3hPu1QdM9OtG+JR1b2dcvG7rZGM7kqLv82BgmKd7fl48S5ktkYFmTu3rqmaMRrs6RuK0BM5cWfZd/+dVNM/PN8g+NYfee0D8zxa11uJ0nh0sTpxfvU46d9YVOjDcUT/v60VG9NWvCGs/QxFcUgmXAn0mU1BCuei7Fve7+SrPyBVDK/i9qURiI7s9w9hOIdciyuxclcEET7i5ucy8rc+yRGPAimhcXN9iLHU2cdRE1dMNeA5n3L0pKnuKoFjTglU5UR9sCGpd+tOx/p5TQ1KTiWfW0ZOVnp7t26/GRlpvSfJNjl6Nd/vkCRFMQdKNhHvnFBHOON94y6HuuRUCGqHY+x1DXysJD3yYd3VAbD3/YjzvS+lg3HMEQb40Mz3+QJYYTnxBqIP7w4lXFupV5oYfFBj+0bZhDdxdvnwkx0WWT50MQMEd7Fj2yYvLQoHvsI3AP/U98hs5p1Z8vGFfclo7MRHP+/FOQbgKyZ4Pt5z72RGCWXJ0PtOoUBe0QBp5XVFM1tv/yLeQqyUZ/IJqCTR9yoX+ZUJXG7WSGo1R6SSoh9g50pHtbJrMiMM8svEpcOM5a39xtWf74IKYiTmk2O97j6kpnQmjF/xBpB1TB7xYrM9FVn5l8UzRqHGytR2sZRvCkLVhmKTGyrmu7/sihcRY2Pld8oF23TSL36xhj20SIw89xEObyOsgBlJ+Wt/uHEEjL4K6P5+XIt/BOe03+X3xfYf7ecUsubs5obQRmBZRm/RV+cCTHw+bEUfWuNMpzY3UEiC4fMZgJoiuTKHV69Mnvdp/CkIrmoR9b7s2KMN3/CKT9rQcKPZh6zKdDjyQs2nAMptWwzrJSsAbU7e6PGh9lMxRqb5w8oYYQyJtvxSWZE0BEWFXRFcPFFNc1MI0B522IOaj1xunnJAjuMrFxXAJTGuATxIowKjCI0MR/etzl1Pp2wkZZHFzT8Zu+1voqFJk1uCbmqGNEvx87wztBi+TO7O1ThllxdZN71eNPmZf5CyUP1bZ/bbriv/fh+yW2OpQFMbwNqd5bpXPYEofd+fWKR/74f8+Te9fzQiZaDUgqQAcK+R/5INkxlGeZy1uE5c+GmL7h/EhnGHdvn0GWsTRbNAoCcbhZEYdZpvK3CuHdoOZWrrgeiQVwIZTWQwyA/Bj0+7xvpnLqixdbj4qccjo3z43FnxL6dmkbBdgl2x/unVKwnEJTljaFh9RRKtc6VgNXpnA40dq/CsI8f0YJNDFzr14eKatPJCnGrhQUSHmHy9LDLVGqJogm5GZM9LWZxfg06EWIfZz8+V0ClLbYVnBFCNO4/m7B1k6a87SwiY8ETRiFRn6ANf271TC+XDrPHtZsktwmUgy9Z7RUefgEu3FehB58wDXPQ/r5Zzk39Wyrr069JZRTC0imoHiG6lioZhAn7rKSyJy0QbUF6CsRaOGdTriaVFJ9HgC0Eh3LuhgadE030I/CkIiEwnlx05jaHqmS+FxTW3+7gWx7jn+4ytf/gTm+TOnIHx/41nGK/djXIwaLK2Jkh2y/osZgW7N0MpdaFDyrvHhm7xw29YZTnB2aMHyKjlBd6SfRHpghPvMG00PWOv5eiIskEsXodqYgkWNUQl8M8tfnWsZFiqxvUa0ZMOWdoB//IyiFwTJfBezYlFa1Ui4mVsFSP+rf2pBGjTGPhsWsskSmeG/cl6InMkQI64UNsIOWddJ5KnfAQWPChGTPJPFBZIDd/UgFN5pcmSS1g9YolkDfoICI1ASfWA+BN/TJpSaNlVaKldQB7pOWwvol7OE0hZn5VjIcilEYxfl1fIukiOuUAbaA+CvFQa4ujDowAX9e1+F4bfKgTeh5zYqreSXPfVf682yg2WWXK/XU+E6MFWlsP0KTj53zjL5a9WvLDzFSwwE8X1SC3sQkijIJaO5ypPOG4IbLBSfb7NPTXuUON083GxMciw7Fz0NpqqQy6H5up9Hz+uoUvhMECqJoSHQt/+AGnWzGtY7geehYttb7at+MF2Dpn2R0TprJldLB+qhgp0HRhzquq2ugyIxH3QKVZdtKELHgH56rvIjc+FAj3lHhjg3TlHOmznHjsiQUCJOYSJRsVr+1aoS5WRxwVIbsoXav5oP+e2cUZakw31BBI3RO9Hp7+jh9Cj28BpKt8eTqjUqarq8/SuqdaK5uPHI5pnrvQxfBxusmqRt0zdpJRk3+AbZud2meu6fJTiZaURve8zyxU2J/Giwf2LFgX85pO6N9MMW5MiRVXnLbj248XLMfR25zbWp/0wbDBZiOPN1jr/JUorrhSYze0VmuW9lJH/ppwk0xs67eU9ryuZGg6eI86FE4NYzuyfej6fJRP9+swfwflrXznOhpxgyOxXAry5O0fVF/T2B+rBulM34zWzSByqG5DVVjeOJ6l3FHNIYRHTOCxxhyhdivL5nJ3vVgFk7N6v+cHvvlHpN0nGVEUO4GPUxaQCLA7LsFYOvuxvucEwIOYG7AjLNpA1/KL7QbHiBA48EBd/Ytk22VfHMeE+d8pr4+eetFfdSucqPvgHw5yqq07s09YFK5DEKPNdV8cN4YhK2XcrUdY8uqAsAb3vbsgC1vXtgHqAc7lVGRVdCWdhbGsOXETRkriq7qpHstBQ0rp+satld9bYYIqSuhDxymnFe9O1Sg/fhN3/SWVVIlD14CONAfUY+7wIDQsAAGHJOzR1PvC/XJL786Ak5QvDO+ANjwp5qBi+Bhf+YS0ZNcU19ePtSpRfo3PHVPu9eLAoZ29lBQk63nle9vVTF2rFzefEKIOq17fZH2ns2gL7JQdP9+KEx+uBzrxjzHnnMFemETUVd8P6rGInKoTdbI2MOhfxa/u1k9wPDrsogonGzn6lYijQ8bN0s4KUWR5xE71EgX2wb8seSRu/cbE193uKJqeJng0I+ZLAn+g9eWT/zDr9E12ydLgoyAs7jJRlkJNpeePyg0hjjsXDQgDUD7W9DR+I8JDMuLeMsg5bHAuq6LAdOgh2CaEtqdhfnsWjLbxaU6/r871cuMILkaLMJMpAHz6pMCqXiDvjMxYntau8Jtd2kvJ3+OXs0f9TkBXyif2TpGXgzyqF4PJV9elYL6NTpnXRFHEAxs6WPsDMq/AOV5AxizoAyyY+I3PuAhJp2AgOrrkOjtY3qMI4kXMWy0bFkbZ1UZcMDS9J6HtTFKxlDyecxtX8FLL2shs6WJyhaX/wjombOJR9fcrU2lyKayP6c8IUc1fGUg05XzZsDGH5Z9XzRBIvKhPyQhiUaF9lvkA4+6ec4TX43Mz3xpGS6ohQ/FPXO8BxaEqY/6E3bbQYjDwXxluL0u2S4pEbKf2KSGaavUP9O2SobQY/Yc1m1OBPcUUYdrHneB64PGlIh7+3b9K5a1XOqxzxRev21WGdvyQxOXYuYd9ek5wElrUMdf7jHDlxL7qIhCflM6PFTQE5soWTyUSIaY6vDuPkx2cA3ouQ6vd0SBX+UNgzWgIi/ZTKdnGZe4lX7gQFH1fUqbWEOzDwDbLO/QFK0f/Heb6Qqk7dNpLSd0KXAymox8r2iHIMvKOkQp4OTKanuYJX3AgpvWk5i+LJcy8u+aLj/kHT0ssz8VMmhrGbZ42f9DpXuoVY/8ra4wq4aByM5YnX4sZHHbuSdDnvUtEzyecYO7x8C7S0Z3g4tZvtE+Il4Vno2PaDL4a5b+av7IZdX5wzdimJWPmRwmrRfC1H+qY8GBKxXISnnPxFczGzOlzPLrCROTI/16/RgO40rO6ZPNYq7ONeRAQBQqxVc9O0etuhSKgF6WgzQZVFV4Vlula8F1Fxw6d6AE739lVnQ3QWVzobDWMY6TYjEt6P/dshQXdrzjuLqaficoN8WCBoF+agmMmPGurnxGUI1+G7r0i5kXMIspfbHw3Qg10TxEfdS6wYcEekz6VtEnmATzMoTbbxkEGaRlOKqPzH/0k1IhXCjrL8bF/KzRzhtV/H0jDsP/eU/fQDuujulzvvc6mtBymWk7vDcXB5WuE9cAMcveK0gmSG/crRnkRJ/ELzxEu92B3m2JTNc6Gl41Zci0j7lA4QgMoaWeBF06K3UUZe6tNw5E+wEVRHkthSfXwJgvJBOJYMZYNIRVzlvkymMyhYdkmRtjMH15DX8Ugo6JETK+f6s2qgPKaq9QadTxYMv6IsKXKZX6JXgyJuXMsOv8PRNo0RD651lOFl4ga80wrvCEAE+SYkc4v2VFGpeVVzsXkMyzrtCYTPCKSSNCzIn/BUQ8T28AOmsjrPqZXXRb+YqYytBrY0Bbxsx+UY7nv/OM0mdXKPp6QmbonU6nxioDVRAcfkwV7aLcBYA8hllQCb0pa50RiNyClZS/35J4C4k5vFUb1BXOWL0bDIk0Bv2tOlkh/R6WsTzXkaFTNuphLUGyfPkgvsaLqIrEpwiC0dUuSrHdxQ9mjW0k4KGE3qOWf2/tre5woC1RErCvCGJi8mBKMg12qXpgXDKQwr2TmUcgP1mhSBbykZsJkPzXScoPbydkMeEpxO8M721pVLHA+WFQJ6/8Bqtx/95z7mZTisgLxJpncCK4xgaE8Xhq+W3qqm2XT9QRyJFRyT43NOr19Gee9p3fd+2dwsb91Yhi52aGRvXJMRcZZjx/4LV46qHVjmD+YluCq/liAefGXszRbGsFDQ9wncb8o7X8toyfczwWQcE5ZWfvZRGHeSvNWxr0VV0uYP9I0+vtxVATTnsWlIaLXvFhtJIVhcZlNoYJU5GkhxCIw0QiIin0OJJ7pDsMmJz7HzlX2MkZvbM6WmZ+DABsWzrvR5Zz7mvHT2EzCL9guFl6zYuOlbLPn9LU7f7wSGdVB5oUEi40+mBYnfUuo1o5OWRPl552Oc7eu9Y0RwV58zZYZXDHGGBIsxhhDEhfDENQqxhhjQkXz0PXpLe/kuWV5aUmtZZkmeelLghBzTiwTmgAU3GWvpFKBRIplFy+VCjjHa8juy7290nVrGEnico9k6RrJiPeYod4pzKZC4ITWeuV2pTP6c/x75vWcoPjOeXJ4kOBZYLX5bTSLTmQOfjA0hDXqs7ACcw+ZnyKOW3M1SSiNX2HMYz9TaIyprsVTtcgZX9uIjqg45TmK8gnRig3jQKptkvWxEpdXUr2Tur13EpIyRclMWNTXClcZYWnT3Y3tMsnPbPlCsOGlGrZvksLwtkSb/xx1oHE+YKqab2f5unHJcmPwhLReXrplMGOS0pVLljr7hsbh9Rgrb9jOd3HZwsApycK1ZLSlrmfNajWDlYUX9jdxy50BL5NI+8XZcREZaSY85BJk3OaXRdwdh0DHHsWSdKca79POkx4vg9QuWsz8M8Rzf5d9eXaJAus/Hut+hVK+HI8UPS/Rh0IMUb7/rMA2pfqKOeRsICbRGYvHqSMMLGETPBD63QYXetz8KFG6ddd68dk86S+Kg/R5TO7ocHYtvL/9oPAR3GHMhiEFmU07+NnFmgOCcAtbY7s7bsfIG37aMN3iLHHIDO8fvoZnSTS65pnMIyAtAvLUMTZgJEkwI/pNucSlxGCHNWukWEbAnTlFPIcvpE2YX7PSzqhdQhzAA2KmkxNm6y8uLFLsobvXpdlD9mDpHwSD6xAADkUMYqQEK/yPlIKizirCpkyvh9+5lbMov3YE20LvRkkOwdmyLWYrXCGUV2XwKdlRZHD/BAcvpmrfigSCfGFheTJOnY1WtaPYoJ9xOE3fGBOcnQjLl8g2294RMgd5EGYmEDKtI0fuMC2MFV/QpcMPfPhD3edU0QpCoGAXf7DO6VpNo09xsi15EoYFMY0OiZdmU4HQsAgLw79gX0LO8u+08dzgzNxnXIvSGDtD22AhkCjT9OLCZ5GngvFK4FwH6djcGE8lEPfETtILhYOxcO/6PWPvZyaYDYykM01jNHMsir2an0k5bsbkLI58XQeRu/5rxv4U9xL+rNAdNZwEke33/0CH91bYgqzo5PeEpBll+rSy6aObkQ0jwziBVylA9KKthfnCB2yigVVEPn8FPYofgxbZlW11yMAVVxAJMUS5VI4o6IKhHa4QV5ALJwSQTLaBi6IOSnTp73U2xi0T1IN1uxhJUb1RTSf8n66eYP9emgavKc0U/1MbRSQuxR8LrYSbjl9Oa8PVobqYHhcVKW4b3TiC0q0p+uTZj30QfvJ3GaXeCp1unIYRLJZHakoY+4K45JBrFZDFAuvyZmuufi/Kh1AaYrstilVrdeTnKRTk4E4C6papTDQ6zfa3AfH3rl0SsF2SynolXUqMldSbpaKZdmrvzKmcs8mOKDNvEot5PVOu2blaxcwzeIpv3M+jqEoAwV7gDRdJXJnlGo0XONeYSCTLtHEHKqmT2ycYRzIPLRgshe96g1gEOyAp8D85whQA8VF4G3vEsJjdsYUm+NjahLNPlbiwIRSpDMVl5Nq6V80YxUX7iUyrlPAB/6jqCKRkG9S9XbyBC9+cT1PWErPqIxa+jo9FeKG68b2bp6antb9xwU7kAW/iWJXpmgtc32j2E7OUu2K1bN6+X4HB/P24A34RB1Y1SY/xLkBf1FOmkbjIJ4MPEhTxMwek//yNTnB1Ja7me/xJf7yQ3OSrWBxbrlW9JHtylYdFIt6vCoUiIl8PY/VZKOvqGn89Z/1YRMDqlABVGHJqijLZqBLQlMy8mItKbcqamYTthRI8F4KOy2YksJK2Dj3tKI9ZANP1otrwFdNSlzIEG7fow+/oGBkU9evswQl1IEKBDmVvoPx0/23fSy0WSLw1+arws4ub8eDI5Sr+feex66Fo4M/ZRq+m2bxtx+oV377S848tBPlohSlkHWPntcypOt25tmPKsVZgDShEpVootiXv+vELp2l3E+N1No106XQRocPoz8tdxPewAy5M7QrvWpGOnOZ7kK8BtPbmM/+2ZlK3U1rEGvl7vv+NiY0TS17je+Wp/Jooxu6UbACzZ3Wtkk3JD3P1MDAU35a6EP6Jj+oZ+iJYR4sjkKhc1zTH+DqO8a/07ZG/e/UROAJEPvqpQDrGbz6ZaxD2SLsjyXNxZRui/VxC3JcOlFjjg+IKkYDXwZXAutbJ5Q+/F75ZuniE3926bIQBbUu3nGusNr4mSem6545e0r76caONRNBE+MJZraGQwbW2JqO88h9TfIpY3/DdKTZEobMbmrAn6TYUO4ge5AKr3e9QDjtpOWnhb2D1Z69L04PE8xdo5d36udIjJ+Ofvds3Ee2u4ZTJHcCRzjEc5KiH6qPIwHNmOzcNQ76akAIIA9u/zod04ihmqBqux7geYPY46JjmqWwKhTq3oRJbKBjGeqSsDOxmZzAWkqz/N0SkrPyO4HNDHgpCErO/2/RQF7UQxm/mxHEZOaI0fqmX7cNYbNwWrXvvwvHKMUm4hshl4KE6I1L3IVIPyqeC7cRG76jUqVJzS7Pcv+i06WmKmqIdm6K/fOu6vXs0f9fnRoxdMGCv1x8fu9ze0eGi+pqfwxrzUjb5JVITAPNc+LEZ9iFVZqk63mqE13mjoTp8zJkBFjKm4VqdcmX4NknnCGSHHSsrNj7zu4NMyXHiZv5uLjtY6mSr008dWEil8iPEDAjLSUEmLWEvKv+5XgkNaq3FcHDaLnwQ4Nirs4L9GIucOZQkznzgMJ1y9yIe82Atj3mWCqISMqng0FtWtGT/my32cpDwyAr+Wyy+R7oVR2EikErR+7zoaMTmARYfN23qv2V4GKSg+xIxZ1CxUHi+qaiTYpHvZZBa7elV1tlG3a+1gVaePSBduq6vipfKMhOwchzYFoL4zuVsmCcjSO++EgNZZJGCnr7wJ7pKBCkFOcI9yKZWlPx+2C6aIkqSL8OipGmADN9P5B4yIDc35AlGnjNC5xjdI4q/6v9WCigz/I722iMxynMjqUlMW+0rjd8ptN3XJy1hK7As8ecWEvdSAaDfaq/pJjcq32JwRhNKfnfETX444nA/zP3RssehCuy+lOlxKn6SvrzPMKebk+NZ2ovryQBR2qRIStIAeZjIeaAhRQkl0xWbSOnXtxz0Ssskmf2Mtb4lWMsVuxGgiN2lgeFpyk613IFCVMEc9ZgywTKfJ2mTMca7eRg1CxBo6ttagFJCEArYRBONFNpuRXRBWVXFLHXLwZGaUGgjZ5lEK7XIy3n6Yzjuv29DTx83Mc01gVO3tGvc/tXd7kmmY4CZg6zY1hgSvQjI/q3pj8Qr8GTa0AU1Dw63w1h6qVnbNHxBIInp0D9cDNAnD8uBkVHXqHT3jZr+jJbcO1p5wPET8Xh6mSCERBcxQTg9W0xIPEJPPnxQ+D/WiRoBEnDFbxM5CbilTZcyTVjq2FTEive/PuSkmgwnlqfJLsUt1D1e8sK/4sCoQ5DjpeZBwDt39BrWnogx9kLMd9Gm2/NvjTQyEPjCKViCUzhTdl74LcMfIvnZqYtUeI/kyfdUr/scRpmOjK5wVEsaYfGwy00QHGpseB53To2MOzJQABJHz+db0qTB2KOWdkyZ6Bm1u8CC1jSt16nASmvctH26g5zuGRGnHXLqo31TE4zQpYkNfcX5p/qxMf9xKgBYdniSfWAN+y1+l7AzTCFNDKVq5QcX4aYwhCBVwUIL/jQMdBWeGtMC3/tV+LWFn/WDZ15Ok9nbVvG6YDTCGkSMK7xFC2y8Pm66XHC0eC39XlZLDy7JLhyd12XFwxRf/M9lF1kuQtMpXBRQ+xUg6oJpOS6kkHy1IVN7pCQcmrvnvp/kZsShTEVT63FQqDLuL0u6jueDYLQTLyi0Ayae2cdD9wOun7oOmHDjQODCfTMCfdsmlLJ/+TzApZdL9kvQVMRGc2NAsTUqjZfCygQo4QqvZiBlCh1rHeE86jQNqEKq0CLDDw0Te8c5K3fk85Fzfs0re2mHOt2PZHWDSXHTrB/eK5rK4FMYxSNBcfJWkYfSIJlShDaxuJyA7YwYFOzbKIFfJMzdaIJvMix+hc6J55eeB0vBHwv8+ZiMXDtN6RQsjLeopuNOo1RQEqBDCUhEwXD8y55+/7QpwJuSTnn+jZvqiPOCjG/tblp+DgYsw8Y/5KZc8m00aUXnbWiBu56YY3ielp6N4uJeceA5Qf5b4oOj/ysNWHYPT2eudiaFP/FWyyAxb1O5m9N2LmBsYk5JDXifyO1KVXmhtp99+orlG2csbvYtzz2LY9Y/UdyDKmdKFjJd1CXx4sSyVP4eKLAX42IvZl80OmaAYR5GHqNT8km/FwYTE5SU+q+Lizc++IyKg+xclHexSxVbDvZ2W5tNU3Ev6iRVyWv222AvS3W62lP5aSC9wZ5Np12fha79YSQ8whxZ9XcsDHEN2Pz2SNu1xxzKzsOWb+EGl+HckH96l4bqidxF62vvUX1YyNxVh9664lY6BHD7OnYodJPSPjw+7rvq9adL135zwra+TKXglwFlzfGARObD2LSNmujAXJZNYoU8pqU7wWZOWB++0eFGuODasujKk0lWiCJPkIxQoNt8rql4gocDlKvSL/R4FNHBs3PDvP9PtwwDM4EcWfqJ0yfDo+Ybpl5ams5Cy7pVUbtyl19Xzrfa/5kvIwMwJDmK6MqB82LQgy/92uoK5o2OE1YtJirczQ2O4bKppgWJq8CFA5XpQJPYo0+q9SbSXscrbfWOBQor7IRXHdeGLdGz8EjvhNM2HSxMh9HVUYiTqPgEXrHUveCJTzoY1b0gqRZqf1F2DHA70E0DOj6z9+eyWElZyPa3SZXIh6A4J/lhHZemSQrsa2zj9Nn6IaQp36YRr8P2X/8ZQe7mbZAumdVA5baB+JiUmMaAuqlhaHvBadnUJwpm68Ab9dHcQmzsm31b4VUIDXTl+YSTTjBj8YVS7Nj5rvDyPt9JTwjSv71LX6nvb0VTt7sSHLHdrPOidmOGuWUa0HydPKiw7Z/UeQqHxhK6Opp4U/ItdD6VZ2M9oCJeRXquNrSkc16SULgX4l1c8NJ0uHt5XQayiR0475EGB2FUpiNzR2AfGiX1ZEOvzNLd40D8FKgP5FJta8supCg/ytk6mhFEH5iqFEvSM5M0R9jN/ZG+whC8vhUAxJ3ryTHyXW6Ounz3Q9R4QE4FNkGNkGpMkVL/9uXzpQdrp+ZXCrABSb7T94DPAjxxSupfp5gi8HVD5k9uBg9qi0hAwrkIthsdsVzF5cj34u9bRI3eX+XuXaQ8AlNLYyMSiUVh6H+mkA55pRUP2nl4LX3zK4YM2TdA8ATJrWDlpMFUucptEMrr8orEDugu5icnrzX/60jcTVlqfDObrCQi1ZqICFfvSV5LWT6v2H7ruRGeDV1ZYNZ/gDzBjB7TopLgdIhEXmKZSzBIMWy3AR5BDAR88gKQoZo0bIRdKbgji/wOk3cNDFNzxFnxZHqgO9NN8Ou0PMiFsIsEE2KTiv9rhG1vZlCKGU71WFVxuVhBZ0GwMYOMUdjHEeivW2PmIZU6XpvG0tMyh14jihBm5HgJ5wsoL7pBP8xmxONSN+GFETfrYXaChU+u4MaiwFWY7NiuZ+Giy5NksaU/T+0Qdd/y2+3QSzecLIF03UeqU98rlaTqlWMfxWwRUWxj4x5VlelJEe0elIkJQhWHJsvjaO9ik8a+p08A7cWYgaLNNIxY/7qYxG4NcBZmqw4pnfd06GdkBU+GN3AQe5g4V8Gp3aQSWDgAD96lofxK6HWPzddLNGFDqM3YhU3TR/sfEBe9Rn/hEBIUNLoUhxt4vvx7Yy7QoIEj2nfu96tx2ZbyWvqCn99jbh0y2GePpt9YaPQ4VeaoNrakkGDK32+UdMbYNUApLSpcoLGExbIwVDEBLFCZaeTf6lzijOR1z/3vhJ2L68hBiNwPdSBHcposTd2NigVkugfH7R7KQZL4NmP+LpNRou0DqJ5f+R2gjOUxGpSD6TiOG2X3AfEMexFj8f0KcVYWa0xROGnOVOivEL1is0DSEuTXfc326xvHfckBmlJM/NIbECsHPXNvdiJu65w8CkzPgPHhbPom5fw7oFHyLsAiA5Nssf3yXI6HYFNOUabt8ZQ5RGHYAbxTwWyAgVWFsfGrteIu40aOjcLo1LHSxq3Q2kEYyVPifRYIl9mmBHeEbDlH1GBrGZ5MxKQpK+swQ3ZtzYqe7H4WAV4rLrVhusWeRHn8xcUEGX9hY8cwH28FTwqWsD4sgkJqTTp/oZdjFjfw8P5c+vt1oRuv5VDmxNXbP6MXyqmR/8dd8ZKWz9i/jtJH5ow2m2u2dheL/fNVbLovH79f/qch3XK6gPKOBW1kZ6UFmktdtGBrUKdaqpBhpcs9B3659ggW2Ml5zskyaaj7WqgDjRnihpCFharKVRQwciLDiRzuJG39rvtiyMbBbUwADZw3wdL0ami2n/1Z6XMVOcg95C3P9ttn2SHEfXLSD0tsaR57sBPs4lBUatrFojUW0gyYq6yNpHtqNmZpL3I2Zmkv8nAMSagELKhtoIie1uBLeHQzaKQ+QYUxFG1uHjs5M6invQnCbfQ5rtGtO8W8x39n6qtr/Glf52+uhj8LOzQldSPr7IY8WjMqyvBxNZSBowoxUq0A3fjH/YYvbvHE+Sja56wN+Da93j2QjkgsEM5RaAn/YttbAse4PXU0dIbuIfkyKLqV+VdTtFYPiOQ03qC8DuKEVENScDayYG0jNkZZHLbUiN/WVb3EtHoPLdrdDL6VKsAJd7rZ4ullZTSK+y+mATBB1ipu+xaErug5RLBX12/zYyZMiYcWxf3dVd3xwrAMMVZmgQI21OnXqC7PRVt91ArlU+V0OlA2oQ63786wrgIMFwqNipiXNu7fJKIkr7xifk/v5UJnJtiwgLU6kCDjjedme8CBtUgQ8d1N5u0ORYDZm+SRdMMXVbCCrNuh4GaaDImmzTSI/IXXfkOWsmTtYkejIvB6WBgIPzFzBJnDkp0GfvY9UOS4HWxlvRBlgBFA8Gs54Xk18tLSkbPCsIViDGYcJz9XflfFtuUZTJ8P8Ezi2yb0NYeqqnq8yYlZJchhIvOFWMIusMVgaSdDlqHW0g2vlVOvL9kCg+QaxDR60v4lnY1ntc6DjK8gaeedhNiQR4HrqnxbcV5TiHSJCbmpphhi/FU5/Vxm4pdUTJ9tMAhyC6EEIdKtWd+hnj35nCv/YHymP7E0f/lRVJBl8oA2HqAaVgzmP2/XCBJJoHo3+G76B1TWukAkazxvAxsX1RJ2CTUezPckFI9aizbRLK2sOZkaeHQder3d5PWuP3+bMgrOcEhX/L9Hc5+HJGGCEjcIVEkQ9xoZQA5cpfGTmtCf9so+77sBs1BE6HeyfDxYm9npojiszPutDQQYkYISq6JbNWSIG2btltJAqFjHj5ottdNX5aKrrDKv2j5rsu1QyBAZjn6wC3zW2bsk8dS5rvJ8JDwPEOrfJXnBe0q9BCKTRJGVm5BEcjqR3XDRopAkEglJRJwQ3o2NZB7q/mqcfxSHiVO8MCMqlmmwSnAl0rbSWWndp0fO4NSh7DDFI05NiuGCmk9Rhz5MnodAwyXPEqJtsqHcAQ2rfLbRD/gybhRSt4sYMebAlmliRvlsZe7Lqb0De2cFcKUOmeU1fCA40gb3fx7974f6zdQX3HgwjNN/Hdqrqv7CJzt4gf264i4Yvx4YfER2BJhwAk6RgOUqHWrDzt9+5YobllQuUA5fMOA/QzG7mSf4B5pp5OcNerpGEBOOS7xeDLlusWkDRG/MeJuahWsaCn28y+dV0aLp+Rg1x/9R49Cq1x8JwzHdvwp/vbzFtedUOnTCnvHegevZp5L17L5VPQ5MWu+RyxhzoaJHx7A+/gZ2iY5y59MuU9Zj5Kbm5FRHESOyQqTd3l009K9RE6jHzebLi26CtccLEWaT1aNlJNh6cyVhrB8hjMpaSjt4qMqclS84E6wbZTcEG3wPjXp53AaKPvgOomZYXB7wYwZZFc17xtDwvjOhBxdizxBPH+8/Td99NVjjBE5YP6bmJoKXszvL7kX6LCJKqGmxXZ4OLwBrch78ZDx/cskJjrhH7E7BqK9z6GBD44t4Y3E3oIm8iR9wLu3v2N8/uXpw3hXA++UCH4nQEE2kpXXXhaDikKpa7NjjpykxlYaSXHBM+QmqYy4soC+F728VjQorRmEHfew/6oW2TwUBT2oymj2eYHe385fvIo3of28/jYgcZpue/us8ILcgvbgE0UE+qstwmpTNyEDhoT1y0pmFFJ/wGe1L46hkEiQWqe9jlIzhxiiFFxLfkWMa83f4W9d/kQWUBazrVNF1dvIZ2NQ5qKacVer0pJi4mFmi4NjkIXPIKPLssNJEo81S3gz+zssiQ0geNKQsuMnJEo3veMNAdXd+lpTN9r+Dr8P/ZYN+1DDcP4nz/1X3WIf+xwfyz7x9uPonX4R/rf1anfJfSqof/n/zV669fw7mGvxf5sYkJp0meeu0/6Uk/Cw1qH0s6pfL/+5v6NCqFAWVlwc32ig4xk8JzMOdBRk0tuvPweW/5gmZAJrovz+PMpkkz5b4+SGK0EG/HazIfQpKucO4mKXndELrV3YASpONrjxO//oquPD77Nc2wxoo/9Jazfuo+SlCHyzoCOADAV0ziGOu5Qqwk9RELd4jibf+etH3dW6KXW71izwlG3+XXHoX/zDuUYr9yM57JzytsGvoUZ/b6AlcFa0yimoB8EEC9qvY2UI7EDjonfLMRQfg24A/FWoMPXHv9vypqRPqLhF2RfH+8N0ccG251vyfkhVcmAegmuAuCeFwjMXT4GSDRdS4j1WtyqrQgoqRS8inODSLDBugIulvjESXFPbjOpZbidn5SVxTrreiSGnL1wtwViqO6cD/mPIUj6LmrFWia8qp5iOF/Tee5REQR0A7aDj8jqw7+NPd9O/7cNy7phzvVUCostRmlywn7sr7t5Wa+GbwbCX662PyZLoIe7fbd+3XHRT7fZXRCJib6FSmuuihuI+B7nNZDOuv6PauxxOey2I3hcvuEXVFdO6luzxZ/PBv+yWJ7tWZqOFwLezRX30knVKOo/xTGpRNdlL66wrHZUnx5149gtg8gpoj/T/RutLRZRgjr92trF2PrIv+3EplgCaC1zXLevNxKUeDHW0zkeKTvz6iFm8DXlmKQbrcySZxN0FjxXCzScSrxf2I/v1BtA3oR7pef55uk/0pT594i4tn4Ft6/5YEYR9rMRanY5Z+9VJ5nGmh/L+7oMrK2t9V+tWFcyJ4TVmum4HbbzqobFS+krf4KWFrpnBqgjUmDi2xlxF9ZcBuZYUIRey01eE+trK2+dGKVLPOEZxCAHRPpRr3EoWjDV6e97SNXCaHXaUtcOyfGM8GV3a0XAYL+1pHZgBtw9mMq0RKPZDefdxulpDeSOUv7r9U5xS20gH1nuKy22SsiQJf4krnlpJ2NeIl6NQoO9+5U8joaJzBYIndilUnCNxW1hsP/ZepM16ynVp2zs6BaTeKf7Gn3TzZd5nUqdGAo941AyUj4jT1O5n8tUGU5u/Xrc3qDGBN8/mrz43PmSWxWvRjvVfAl3iuULVJf/e4OcQxYKeET2SZ4fSxOzrP7xQX+HbZzQ1QKmewYQng8K/APlBFyvUnHrr6VRbqmkHZoSwEKM3sL0UQq1VuIsMvPwopdrHAt6JBskwuNueWw2zR3r/E8eK2MfQjdixUyFNz1WpF9Fbviii9K11wrCZRZqo+4jf7fZwptC5TlZejKab701fvev1U8yFvt9+igQ77QPkTb8RH1Do34mIX0cfq+wn7vxWCA+A/IQQbcNJVkGUC7iKDEy0gX8Vg9gzKUIE7CXDgBswt5J/i4JUaOm8gfSSgdQeStyCtAHtaaCjAmwDnPgJ5VSH/JQE8+wDdVEF+SMAXH4FNbUCuxOAnG5DARn9I70oAGIAC3PtBRY6l2yQDwN/2Irbh/+9g2J1nFUpoe1HIsr9zpodLce96vrSX9t45PqZuqpYZb8+9HDvwZQir3Ib8PeBNNTZdePfr1BUz33+puEy7/HA+1pKW4bFX8Z3Xvk+9FnhCVWpwRvWcOFeUMzN+omYy+I1asgk8WumcB9o4NQ7JIwV8Ie8pgpC84NwJUDCThHZskYIObEMqOlG5bNBgYXKGruiRLaXSh/SUniuXHcVpTG6JZpbIPdGSXYgTK4PLM7FxY/JCPLKCfhDvWQXNxAvunP6QwGjyQdJxDx3QDtwHLWkndk7HtMHe5C+oz+9bWHE28ex5ooAvll8p9rxG3lOsOPY8kxvvlk84G3j3sOB8YOX5mPOJIMI1pwEoB6dOmNX4vKsKO4M30Q/MB4SiS8wG/DkVYHD6hnpH5jGI+kb2g3PVVNzlPY71mRvTt0fbsK379m5Ltpt+WdkN25/eqLrmhfSZqjcs6OfK7tiGfXO0NTfnvjtaxxb9WtkrN67vlO3ZnvtG2Ypt3q+y/eLS9Vo1KbfSn1Qj3KL/kfkenGuP4oQw17+iDV5bfUHxiTfRyTGL8VbrWcWH9Nugq/icHnFKq/kTjDvOQJd+bxAfOajPKQbVOAAAVJPjn2yWqC4JFrmvLpcO6StBcg+6W8FHYwJM/Rh5lbH0Ob95GCoPFVHGTWfO3vidxtlMdK2LAPshGI4L5Y2zg6AXJdVxTza071XblZMTQl36mXTdAxrB7ln60IvFfgNnSc7azCadQ3WPHVj9apqsdswIARI9UoIYNA/uMO905sexlwVjThb8gxPxYTGL83LKA/O/Msy4OpgEJjRdMLfFxKYHcK52n3Pm6kWfDJB/B4b8iTGHBQrFNl/mReSj7kY8D+IlBGPibiPK8cemLaQDMK07rUJ5f7hO4XVQ9poj2Lw3nMZ0ChsCH+L8kmoN+pGUVP5Rri+1yfbC7eNDZ7pnjlO+CDvLOZM/DbQ2m2DGd5dEb+EC8NlkI64DtUF7GKHpKos2EOAwdr816th2CX9JJB+toO7DJ6eWR6oKbCzZEy2Ke4aobc7+iSFFot+wHXUhITu95OoI+VM7SjPWAg/GEpKzY2BNqHBt2L2cVKfPFPqiZTIMJ9LGf21aRhr8AQC37TGgVpAHRMlw1AIoz0W77FrSGozHiR2hApT5JG3mfnaNqwP+ad67UidVJ6S3DTIbJyv4o+wYfww92naMFF9CVOGEzjYInPoyDkcSksL8qpqHS0haYv4xKixl+Ay47d+QODoSyVcOq7L2zqF7C0wVsZ2baeMgkN6fxZ1XZ67vjKJYGJln/40vBrYn/HRqb8mPMlxtd0lrpejlOasYESobHofYfcufw8jjW+4gSzStBEdRFq6iIJca894w2pFich14Pg/y3vJ0cmYrrHyTHDuNZYjo6IeUYyMLSM7OMzudV9neAXgxO4SNWXUuzi/sZYqA9VXPOEkSlkMSqEhH1iHxy0LnUb3a7lL4HMK3HnXSNhVVJBSV8a3lJbsHoPdnn2cuO1+2hvRsNz0svDmxBCvIPInwdVjS82YBVt6L+D2NUq+b7fdufLp/DTRRo3mpS7CGKG88vPtc5OUmnNJRExtGgEXuosZc9LGq2ckdQrabxQqC7ullG9IjfT6HQ87IKiJ5LlWPiZrsY9bMrXQ2P3e0lW2mwv4Ti8DCmUUVP3wYsRk2cHRd4rK0SyNF0mIvhFcvC1oV8z7j8QTAr5qTa+S5A+i0JOfSA6rufjRqr4Ze6t/NgCY8pqvO25Jk7xKqUMlKM05pn6wBwdwfUjo+OuPK4DxFdpbhHWDaYIaI4o88UWUp2mKRHl+WQfO41rMbvBUJU0rUqDjxjh+Uq1eeaN7jWnZKFyruERIlOgrxj0U3aEAQ1FZE1UXj4E+qMlEDsMH+350j2rPvmqFaf0Bk/ck5onlyxPDk8e+NMTp6YEoPvT6v00G2WlI4nIS05cidt9ZXAEd6bd/8vERTYrSgN9NBDMMwjNufc5q8yH/smp9Nd1Tdp9+CTtfd4exZwkCkApbrEYKjEJn/mL0xYYh2edHru+3n/u6k7jmVOsAObaHSlJrdEiZZzpJEK+o0Gwmkt4++jEZgRAX9OsXnY6onZtNIce3CNBSkhMQeOkSWpaH73r/mj4F15winHMaTOGue6cycDsiuZoafYPslH2QIZZiH3vWKgaiwiFGSJooTCb335JUkcwsCwrEUmLJGs6AuS7PY8zumKdyQI2cPgfmWJkGSC/h3IjPYciY0k+b54VzNsHz5+OSF/TBlMm1H4A5hQMKfE4vqTFwSThCPew/TxjN68W2etQHh3Rem57kMuyMiSdx4zAy5fIa5DO5zELVgdxpoXYwPtZCV6vnR/EgC6Ta9oeysBRtg4fcahozUy8IWGUxRC9+vaUvMl3iYw6TZ2fMN3T5D2BCn50bRRwZHaUTXDTOtXDcvCLyySA6lZo8XqCnR0TPqqekgc/suzsNZA6cbhGOyio/oPhR46WdVcsmGkmKcO6LO3lsl6bvi+M4QxmXvNXGUpm98JfeqvR9T5t+oCSX1Vc77WIEZt5LCvWxR5Pd16rDTcIo3EuQTqlFP3xJv7kfxZs02SHcekWrTPZGkR8eEuYP6gSzy1iNuuu0IrXdH79dTwHxyEsZb5tF5GkJySFbDckDHWuD3SPLO8vhehorOrcITa+ETcKkSMzJceHuf0cBZ0rHnEroAkO+RNq/JRp/d6aeGh1kOFZoXHxUl/ZId62586T2HWjTQk7eFSvyV7tOsQy5RCQQyv67YEDB89roJeWd8UbaOPQ3wn+0HTbI5zS7nb0JHh8ri4+ZzBzdislrwZEdE8+PKMhE9qr6MWUTCnrXenh4WSYbIgM4kifEU9ihlRi3QRece4qVc+J75AYHI27nTI3Fp3aE0/3MQuSxLFBslE4Hhwk6cxzsa+eGi0Gg/1B+QGFE9kvR4HnB5LUcne53FRLCRKyf0c0iQw9tpVCIDJdGkVHUcvgCTBAbafafLnNbM33G2HloQgTFjVZvnZZFZ9OL+s2f8mzz3IHDB06DxXXt7tgWpeWMNAh22xefRxApMwQUqWSG/i/qAOQJpQojITgVFPqhJJ0O3fY/js5mXbzsiBDvpJARZo9wMM0FR+J35YS3/2cHAObtOTxDp73byjlzjDOmIQcs5nueqOTctxvDVbPen8/TSQwcu2uFKexcJ2d83HVvnEr2Npk8smAPOCR9cJ+7S2CDJhRRh6eWPLViM73mXXJ6b+eBVcW7+F96HGbs8k/VNIP3WEHRs8KTtCUZkFa6L7td8RTS3fiUg27ZM05Iw6ma5SfBzQy90oe3HRCEf46Q5qDYherlwJ8ym85EQnBF/wjAMw4gdg531y+4utCVptACKmVkT0ScLIDx3MQeXbg+bKGeCiqYwj85OLsbQMYHVlr68ivGAo/1VJM5I8K9Z7TwL6bSmdxG8APwokhIWLer8YnY7cei5orvDMy84e8hv2n95L53VxBQwoIjlpW4cfEXQ3oEGvCHDMHMQ2FLeiYZMUC7O5NvpRFXhY4gaTZmaSM/38RMkybeNXIcdGYMIkavirnqgSlA1lkiIG/39WWDvnw6opAHZLSRqA+R6hrHnKA+oZAa5qVr67aEqe1KXM94vs5/f8AiPzgdJtomxBMhAHkgF9dCcSCBrBEHNB/dSXYWQDXtySTqaEpFXC5lnWnb9wQnwk1tbhlQbZSXiFl+khkXcMEwSV/z3DJtsn/6JvNfS6trh6+FmQb/Qw8o1iEMg9WRQXHMmcd8rjFQpMT5YB0NrVY3V0pW2C3abHrg//ZhLd+Jt9kjMi6o5n4UvJGq1RsA16DUTEx5vrTy4cdeu+GIWIAdlm+U+a1L74ZRBaaubkSFOlu+/OfNNPbp/9plEjCuaJD5pqOOiQ8+Fe3QS3n5whzm1fQYdO7mJw8Q3G2Gr2D2GtsLf1tsySDWPr/FZ+8/tpXjf/RIlwavUPJl3sGuZ23Ov4HbRJlwhrd3Md5DvXVQH1Wcpd52HKPULDo3Sy+KNalShvejVT9oNGving7BSxon972dytVYkHg8FfpwhXgBrOBEuD1GQLMHGDZmxeKiW6YaqoBqGgKMvj/cM+pktkUMNsRe5lTNE5S9kGl2m3zlqf4TEjlc1TSGBqO39ZSF8k58T6lWdLbLikhd2Dd4a06KYE34k5t4uUmlOb9dWlkXhX+mkQXtYOcqx8Q3eNeZPoc/lfmZN+1nQ8rzAtIBwCHZG3YzV6HmCrWY38uQGdKbDD0/KcyY8m/FhJ3YWEIsvae+EDu39oikLxt20NZbVLbYBXOmAn9D3LGCCsasd6N5OEqdiOTfHx7rkiheXUm6Zz6nuay0kB02KpTDB2rq4XTgxNZkdv/UXw0D9UfFOCn8cBDycamxID+WqXaoLXvgkd6X2mv2g6PUWqOIoreV9tWZjxjnPzWtrsAM4LX++Y0LVYFz8pUwuf26aNMqKHVHO1pOD3zzzfZiXykeVw/TG+Sm6C0yC0MID3kqRRb868WeAJ7GLJAoEHq/ekbdBPXSuOaP8j7PDFOhf+irl4azoaxoqqK6woCWx+Bqn1w8gLJm5LVI06AXioVJTfwFjbdvdi9LYLYmT0gt8mo2HHoyOG9Aa8t5rgElRBzPal85zjfUkbvtRXCNX9IOAchIXOp+7oMW4KGOFrz/0w7K+T2VyvwCvsFm1+1Zaw7pWYaIhYsZ5pc34yQkV2SyaWRyGYRi+PUf9bpA7YCO9bK9FgolGxxGZwJWIqhl84Dqy5TXcFQEoYcDOfpPAy2c7Bp0VB9x//4n771zllKJuqMEkiTXjAqr9fdIDLb+TaLxYkJdDV2uKjoqtxEyqqcXM/F//o/oZdjU+GvFhsuXkLk3DU8Plo3BDQVPR6vvyAcw/MfRWikvBEnXth3A1CaaYOf/0Jsc8RvIPPx2HCGDvy/SHEttSQzNWXHOafiC6+nCpsQ4JdMoTJTMC36ZTiNgH375+wGNbDfaq2DDqLYcFT/DhMiAY+7GgHgk4mWLYII/LU4UNSo2YwpdrViK2ddfi6zutlCWQ1yCCWepcyQaWmZawKRSEaIq8Disjj3KK253TSbdiPJLCLWFy/G0JYuWtj5C/QjXEzBScI8lA2RiMj4Zy42Jv6khq8qqxWzRDgW3VqLjnGtUpq+WbdMHNlW5pqWeSbFeg9kv3AVPWm9pnYuQQamaAH33nzO163bAkisFxLcY47lS7b6P6tRvLFJTWTUZe+YDqrddBgQijxnol9B04khH7OInhZegQOoEBeq5AiqUso3TU+Viw5gMStvejkMbtNe6LZBEmQMp9Avv2OFlRGaVe3TyFUI8bvI1n8yqs9webeTYQHjrU1Fhf1lVc0gSHsEpi7KXjfWH5RL58vn+XI1M5XOCbtw5JckumMdgCbKp03yfWsQKB63Ngx/77dJXifYhwanFRY8UlXqz4cAPA9PgNz218CRusJ/f/HvPX73DAO6ZlfURIoYUMJQIliNSJ+BQW49X1lk6haOtz9qetziNOr8/jS9lHOhWSPqy0HV0vHVGv+9AmqiHEo6mhrlObN9L3KyPzF7R+nGqhOtHsjTv0D6wWlHkFBurcb4gjPbFJBMQEhN8CwkaZhkOHF3b0qS23cDtMt9drwUrqBBqCVGn/jlZJYvjWYKw9BUZF3BzNQoMOQnlly8I9+WhKMQkwVM/USlAH13XygyhIWfhv3E1Qc9nNVL1c5ihQLsKhWdATfQLdVD2sETRbeLL81QApwluHUXSfTG7oExJTT2xydgKsZu+ioCRnm9pNnI0eJv0OZZ7Pwqv3ChBD6oHgJar7lp9GiwJ0zhRf55Phmyod/4aAkOQnhuOtHXwgFynbAV/0NFxiv2mcKvsBmgt2R8BwEDkogIacKxuQ0dQKH476P8hnEpr4+mI+yTjLzfvdgDyi8XwuobbUc2PXOEBjWuga5wQF8eHtDFW9cMlg6Le+bGyxsvmsP0JyV8MLjOMKQuAyDf295cg1sRXYuZK9vqnxY5BieUVJ8do5TrbrbhPUXrPz0Fsbb6DfqpHprDhEesvXKzh3Y1RaOrEUXh85mA5DB+aju7MucCTmqjc5qlWBYRiGcahraqnKsU1PiPDV/79CKbD/2G9HOX9v9tDPU1TcFqy8bPW0jUrqMNol6PbQzdguFDYLENYcUwU5NDBdqtPRFX/co3GO4CKUBwpdd3UNiCL/LWuwLz18PPh8fP66hwf5XVh0LjtXqr1w7sur021RbSaKIJ94T+Iw2rTdh+rlWz4CB5xotTOtBmZ8tWIIFrOzLE/MRG5NHLHp9XvBSmXk/7gKEQOgJxOqwLhknYgzu8h7GVyIyo1Nzj+KVapO3kAFfyU2uAeG/C+Ip6R285Sf8JpTjg3Elo2s+s2ykdx5wE460yE3QWe/4nAErJQ/eoDtGJ3W0F7Rh+fWm2QepKVQivPqwszlp+NNLXiy4PuT8zsc66puBU31A3YqhWV408aM1Cr5sTWEyCbwKop/WR24+qxcAgS6myOlUMJcmLCZEqFdM0nZ9Kdnnxtk7t1RQl4KZ10OvxdRVwCUa5RwE0lD3kUXW7zbUf4TG4CuBOku4Gkgj4kxkR/n5+DfHpPHCoHw99OXp41ZRrJK42WqeWL+ZBSN9LBCwkw5cL0TDXAmodPYh2DDC7FedVyXz6m0tkF0km03T9Lufnw9T6UKLigCSnlGFC4LipfuofHgMsl/vdkaVNrNqdCJef7uUwf/bDKP24eFY73FncR0RXrSVbbsoCLx5OZ8NFdmXG8IWa5e7/VtZ7iF1uHryafPEa9hJhVfTbmi8y+WulikP18iKxeviPnQts7rFTVOoU5K4smpHzmQyHhtqXtrAX4U4+cvDE7khNeR1Eadw+LB5YIaiOY4RNsJKOCpxKtBQzvc7p2j/INTgafb+BX0nTFaCt9esI4Aa0Gxvx9rA6qXSUJ6F+7hHkXh61w7zFyTGTcNfmm0UoGqEhOxkQYsN+BJvHPPOHv6+1EkpYNjYohEIbOgdXABZRw2G5JMYJw7fKntDCa6qRfj2OhG2GyWC4GDxE2ucdH8wbDfKZE69jpnkK2vpzz6SnN9+CwFuWHtLWMfMntMSduUfijOBdI2e/bVB4Wg2I99jpHwSMwDoHsroUMOgrCZG1WWS1kjkGqcCWk1eTG9RRqZ1eZPcmN5gp2McETVfE/14xsYopM1IrdcnYOmTfwqpzidsuNqWoliwKOHXQjMGTD7RP5AyBLuDr9V50oerJQjunXjHPx+OSmVVF5RNESAT2MrUCsd/UngPRknNa+syIppDCnaUpw3lCNdpvBlvRCJyGZetitsjyPixOx7rUwT1/8jQYkOdKCYBJW+r/UaKsIfsuk6LyTX7HjrJh4gZ/2+QgyiisIvZJmljjfNDDnl2d16HbvNP5oXh+RpA5tQ0/hWahICfM4lIBXBPaZbyz1W77JM+hmUllXaMvclEdUSViyLHX5NweYnUxfyKgErdnWPrFbDFd9Yhyz442izIKRD84hyHQS/ovkF5SIIuxnNjLIEYYxo/knZBSErmi8od0EQNO3KVMIuoammXFZhfEPzr1JGFXKB5qkpN1XwJ5pfXZmrsPtAE6fUj4SxRvOnK9kT8hPNR1dufyT4iOboyn5P2N2iuW3K1Z4wvtJ8V4r3hDRo7p1yPRAc0fzkysVA2L2g2TtlGQjjGs3fS9kNhBzSvLpyNxDc0nx1ZfqXsDujuXLK5VIYb2j+W8pYCnlA884pN0vBdzR/uDIvhd1fNFZKTcIQNGdTokKe0Lw3yu0kuEfzzZS9CrstmmujXKkw3tH8LxTfCDlB82CU64ngJzQ/m3IxEXY/0FwYZZkI4wbNP0LZTYR8oflsyt1EsEezmTI9CbuCZjHKZS+M72j+HcrohVyheTbKTS/4O81vpsy9sDug2Rml/iaMUzT/NyULIb/RfDLl9m+CVzQnU/YLYXeP5s4oVwth/E/z1ygdWKQjGZfYxDnAnHSSTC5hog5lTjuS5UtsIg+Y3Emy9AkmfkKYtiOZfCITYcCcd5KMDUyoUKbOKEsbbKIKGJ0lWd7AxD2ESTPK+IVMfAWY9SzJ5BeYWIUwZzPK8i9kIg4YmSVZugMTlxCmzCiTO2RiGjCnsyTjGCaKUGYzoywdYxO7ANPOkiwfw8QohGFEGS02cQgwdZRk0sJEGcp0I8pyi01wwKRRkqVXMPEZwjQjyuQVmXgJmLNRkrGDiSiUORlRlnbYhA2YMkqyvIOJSSiTR5RxhE38BpjNKMnkCCa2Icz5iLJ8hE3ogGElydI5TJxCGF2RTM6xiUeA6VaSjD9gIgtl1iuSpT/IxDrANCtJlv+AibdQRlYk4xGZuAaYk5Ukk0cwsQxlTlcky4/IxDxg8kqSpf9g4j+EaVckk/+wiSDAnK8kGcCIQEsCCXEwYqClgAQejGxACw6FVBiZQUvjUNDCSAMt2aGQJYwQtKghwRmMdNAihoT0MPIBLa0hgQbHBVK0I5EzOGmQsu5IyDNwnoMUQAyDngQFcchQ6ClQICBjBD04EVIRYw09jRPBgBjX0JOdCFkihkCPGgUmyLiBHjEK0iPGKfS0RoEOMS6hJwURMkNGCz0liNBGPgLZMYPgnQp9ER8a8bGUGJ+TiW+wBv/8Nfhe+zue6xR/TzSZ+HticZrtf2O+PHv319bqPMe72wdhq091oe/WF7Grsu2vytNw/e4Ld+V95B7QH/g7uWm209fH0z77eyJP77OOyv/TCfhr677cLt6l3wKsRjcreorg5EZStskvVzGt2z/SP28wykh3fyZZpQAluxSgZNkfkDJNAwRsGFCyTgF/ZujCzhJ8DS8D0HklOiaWxASOTtDwfjo7Gad3mmkJss7ayXtYOqZLwHFndOqe5E78OpyBDZcX8PYbojwUgtwT36CoPNuE6lSdAZezFxzxtL/jh2ifPLf3nhVf+sTA0p6hHyiJMBKtrR3OTFfOK7T1RZBd5r2vb+hAktOmKp6qkX5F0JfgQJGr6e+dpq0dwOaGcYrR2rTTtLZqqnOrlmX3xLHCXiP2pNhqFoEt8HvmX/HAyWoUuyq7Xw5Mt5XEeCK+F1NYRyxSdp3n6a8cL8P/4+Ag3o0tD7k6F4GMlwP8fqqjnt05tbTxe2OOwOAG9mtK5MB+x9xf8YgE1UlRfemRdBwJk4H9uIL9R1dkPp1QX/tLAgM94iTgKGMNM9o4RxQNlshMEpxQM3nwhnb0Ic8kHR78TN6xDBSGOTva55re0vYMwON2BDAWLJyO+kwA71YMxu96FTml22cwXRFcuMbeAvV/nN+QlM5EaBS3LKDipoKrxkzENcVCFB24QDoKXAMISgIqwUigz/pAcZbISAwDMhEbt9CeOFhDQQL30AJDoNGhN7fye0OsjoFK3leMuNgCdQ0ZMTmsUe8hCRzdMFTb/9qWJavEgG1AXUBuMJ3HBfUZkh1OZuisK9I49B7LDKHhraIeII+YHBT1DBFjb8/PRteuA2kNeoHlA/cSC2wTaoHcOlpRG0RXOO0N5gQpAT1iecHTOBiwrVB3kHs39XihPkFSwHGJ2kHqGbYOy1+sEh22C9TUPKd1x3QeX9TBSa44qaFzCqSp6G9YfkAovCXU6siDmRxa1Isjsodjb1hrgbR76K9YDniQ9w7bT9SlI3eGXqPenOiPcFoYzGJIGaCvTQqP8tlhO0TdODKGaR3PUR8cSQMcZ9RwpL6HrcFyzCoxYXtAzY7cBHten1FfnOQlTqNR5yaQZol+w/IHwr/wdoZ6dOQxvOcA6mlFRO3tWzbq3AbSKvodS7hVYo/tCVUrclvRgmojOuH0zWAWR8oEfYNlco/yOWP7grpdkftqWsct6qORNMHxAxVD6jfYTrCsyiph2K5Qe2NN1NDz+KFejeQep7Whczak6dHfsfx0whPeCurekIe9yaGi3hkiCxxvDWv7M9Iu0P9jOSwP8j5i+426MuRujz5FfTWif8Ppq8HMGSkd9KXR1zk8ykeH7RN1Hcg4mNZxg3ofSOrg+IJyRuo1bBnLl7JKjNjuUBeB3Azsec2oz0HyDKdro856RpoZ+heW3074Fd42qIdAHgeTQ0E9ByIjK43RtV0g7Qh9hOXT3UussL2ilkBul+gWtQXRA5zeDOZkSFmhH7G8uqdxOGD7D3UXyP3S1OON+hQkrXD8C7ULpP4P2zmW/yqVCTZDTTyl7Zh6fFAHSAYnMZiTIw1ox7I3AryBWiEPanJoUC8QcThuja49OyOtQy+xLMK9vCu2PeoScqfoinqDaMPp3WAWSDHo3ujrX5NH+ZixLVA3kHEyreMp6gMkGRx/oAakrrAplq9hlXBsI2qG3Ezseb1EfYHkgNONUecSSBPQM5Y3I5zhrUM9Qh4nk0NGPTUi1d6+PRtdW89IW9EfWN7NvcSM7RuqGrnt0QnVTrRw+m4wSyBlD32L5bt5GosB21fUbSP3vanHA/XRSdrD8QAVR+qPsK2x/B9WiR7bNWrvIjCdxx/16iQPcDo1dM6BNAP0C5ZfRngPbw3q3pGHhcnhBPXOEVnieG9YOwTSLtF/YTkKD/I+Y3tDXTlyt0Cfob460X/h9DtpV4DwcN8CpenljMEh1HMIsfeUcsbgGMY3tHoMqZwxON+A+UUEPoAg+J5GjiORI5gdVHlPNGcODiiSA8q8J//5rOFhjMyo9zDiPVN4HBM7Iu9p9yjSPY5BFa/iUM9QHkW3R5F8T7VnKA9qalS3B5Xt+WOpwkPpBRkLppeyVZroSUWLGDdiqsWuBFkUekyso6EXnqxUYxRTV9czsi+YX8ugGD0IBWcpg4pG3yFzYetbsZHYepGnPhqMe7Hiil4RL1jXy6XS0ksntWTjVky74jtBKYC56o7SBPhI4N+CgeREMEPQQUc2EizBOEdbP4aNbZXklLpVRn4ivG1iJOfwJw1qnKcnLdW6tQQE0yYFSDZsgA0MW4CgM6UpYeLbmyBL5YeYR9Y6A06etB1YeUDQ/ESe6HW0oLzJYZInVHdH4o2GLV9zcLIG0efuBKoLxp4DnPEXGC1Djjlwo08jkD3J96WJCzHTUWaTb/lnjuqbKK3Mk33D33PwXMWtH9JKwpOVEI1HKytYVtm/eTs5A+Y5i2F8wWmP8bgL8bDZ/3IBuTb3/CGTj9PmuagjJeMyKh3Ry5wmG41loVRpH/zhKPdE7a+T7e1clfpHze+3JNLjR45hTecuKxebeSWvReQ/kCxun7NHu6+O4w0q9LPKpJrqJj0+S6Gfsw9t2i4pJ9ujILUMX0z1ofPzjI/DHJFqB/bjr+TU5HT7TM+3ou6F6GeeQj0PRXUiAnsX4budc3nLbFDU6GyR4jHRJIYq8vQG79DjNOVd6/VAIeSLZEG+40YswXXDGZ6sRJdDpP1torarBTslsj2c8gEs8wnr94bNDDiQ/O7RVFnTvEQl+Un9PBme9Z789dICxzaXbCS8XB/qRgXOywynHSm9p7M6A8ynqzuuMlBdVIcFkr8lho9tCho1iGa8vw1wYDzJvbNhXMqE2QVAIWll4SS9V0euxRKvSXaM+p1BrWM8syZAWuxmps747BSsRkHht61cv/61knJjmQJ2I6gZrX6i1TeWp1PcJ1fNgkPwR/qd5UuHwBSBK0WltSSaKGC0IY65Tc4B1XDT//49WliDSwJFDA7fvVZuAJtrzRKr6blvJwW3ngh6qgP8yXksi2oH42ZALcZszGUeayLAU6FdXQavrG3HcLhq7IQ/Cl01OsWxx0xaFIBgs0Lzl9Jlhqe+zUdzRNAPvaAZcj0TaXvUJlNA/SNpHoViRQk9FUpXpZVveZ5Er8wu/rrmu+ir7SkuBYPnVdTHU3qcX4+Z1gm1qGslk4FWouj0LuvhsxZtCvJdyeUsVEiiXDIopYgvNeCy8qlphiiup45zUYETqnD0bLR6bBfd+1mhWnOrbvxCewhn/cncxwZZJ8ADfP1cTjobS/KoKml+c+d3aNu73gSnR5frtkKQaeuHhV1ZKJIuWCnrIL2oosWy8Sjsd11lsC89aDfvw34qQRfOPLr06qBOg0AeXqbSEkPfeHSkYMrO+l1WxNF+qhZVTtFEpxWVFy2E6dKQD1Xar00vzyOGfjQpiK8nstaA9CiEoETx3vrbU2PyrFUDdSgCtPiTgz2cqvsD4Ll4a5/SJBrut51uwSBfm4iwjpNiwgGKpl3p7TvCBUACNKXAh6CBQ/Wb5nOaINgGT++KigjERqSSeW4GH1JcHQlCN55QHVKFgFPjZBla4IlOywhHOjHvC+urgx+klTUZfWnV0+rGAqOJMS++lLLdetXzGp0mVvPMqk9xFwHbZJXEQVNpe7CvpCB5eytChbkOlUnKzjtR9hB5TySE8xxMn8GsKUopJzGZPYLlcEXN03o6XxpZEllLYNn6TU9J+2d7zzDuNZSV1Ng09JOn2qeYT23Q+yotTqWAWJK2dMBMNZ/K92M/A+7W4b7LIl9znX2Lzognj/F/HfOJcjcvcb08EMLFb2eWgcRIHG0zL58j3gO0MhNUNyRxV3qUusb0LLx/bXI/h1m9BExGtahQ2mrjPSVLBcDn8WgAVZiv2JwlR9NKTkrQ4n8k/Pb1Pd3mbcO5isQhiil5vcxURje2Yw8Fvmrs3r1UC5XpczMN810LmaqYcb2+uY5HF5+NEQjJWcpNrV/NJsrSz8JkbBdWIiNdQS4Z3gMiUYJcUq83ZdO0f+dmj22d9wG+k+NMenrO2ZzNWfe4pvnA6v5QwabcnecO19LI2k/rA0AXgGwABwCnDsjjY1PvDNyTq/789k9VB6d3Dvecxu+sjjxeJGxOOzXsLmVstv+eVOh9KIZO6xAIuyfVJh2ETDaoofWxM2nomOuo5Qguq8qFzJPxKcJGxSZ9SRvr/uzJ87dD0uG6BOxsdH0kWRIfn0UxR3hcdcBy7lhXQ5W8IglDit8moBpFVqDLc66niddavBdaXyxaTkqSxhMBsQgmMyC+5Q8Cr9CRrJYrcVsd/rq49+i/o1vIwDDxdS0XI4fN3HU0ohgm8AUr35VBJEUcY2YyEM988P350OoSeXr25h11Y1Umja9MBFekufWfCmmzicW2pVpGmIQCVmhDf961T+9y1IKZyPHhTl02sMJHKLrUbq4ThvqsYiQRf8BwgQYGPrKt9WYStJzf2KaILMyF4l+ZVkBdMVZKiYrqWXHmn4r++cRjtlP110yg/ydxCgnsHWVlUBrVm202DlMjfQfJqYBVKpCe9+BxGGU3nCZwfSGAhlWxp3FQB/rejhWaAtG6cXgG6YAtc2Ke/BRmjG2YTy2og4tpYZKZI0alUKeZZASys26g4qyMcSnsWwGtjBGvmkaYCsWs9oloPxmn6KJgl+KcT6F8dJ7xZxNm0Pa1/YLw2h8l8oLzRh9xie8LcCkUnBrzbUJuESSLYJvESevXwp2mwKfwgw8Elv9FftNsEkX3QF6ouFkhKN8SyHbkrOFe9PlGUSuNU9LGWYBkXiJUNCjqpRcttj49nWuPje0YA552g4NTTz6kgWe79U0uSg1PqnzU+S/scs7Gna+U3YGNsEVLy1SKtySuO+p+UxJrkOGpLPELXN7LWhRWwxM4ghCPsvRubHeP8K5HnHXVH9Nq8bjAwWRl7GjHc8ynba6g61iDuZVLT04P66ZCi38lZKmBII1/+0r9l/vCGBku/G8jfYxCrtPh0/+yfsbAPVnEedGTlJPR4N4Nv+ihcToSo4B03rxcCyYxSSnnc6pATLBQGjrsKuBykQO1LZZKY+JXYw0ibPy4VTBhUHlLe2OZF5ZLtQwmalkb6hHyRLiVQHN1pr21u/gRg5RAr2kwF82dFmzhA50CRJFStW6QPxyUYhPxde7dqyGOnfsPp6/eRHlhTInTKkedYc4QfyUVOy1xfMmoIeosWBATcNnoT2ZaOuz++dT9DbidMoH7ThQySPyrtf2Tv5WyBMro/sT7IcSl84aI3EoUjo/uj/yL0SxcfiTcHS2nvlw8MLDf8m5Wx0ewin12eno0vjmIGVueNofvfnnq+sYWJGlP6CWkLOTdXaiu7uAETOxBfzh/S+AE4OfpL4zrC7/V2gZ2wO6zs0qKdR5XAC69138cpnoX9TgBwAUAOsPeVtjpn80PMxlCN+7lR4HZ7m8TK2Xy1+0+TO7jUemDE7CLjubG/xfQW/h6XHStDS4hMxlzTfri98WDNnpzkP6BwVGvvHAAyjQuto0dnNCZU30ZrjSODd8Dstf9whm+8PoMn+zXh6CIK7tPlka1/3HGuCFgl26/c/Od38JtvaXmEEjKo4BiffOAKWs6V8BB5QCkDCyO/1oNWoGGzSv134To1n3jM6ZB8/+AFksrDauJ79kz5qeNKaRtsVP77qJozK73UAC+8flUviS7NvZvDMXP8X/Wpzx0fdnZLv+VY8CGFPDyh25BtqPk2wGPSb0RFRiOTeYW6HawrPk/hTlC0aEO9yi/HLjZUtmZ1iVxPqvGF0g/CFtsdQ9ijev3E6NKLd/z7IoFhqp7F4WtJfZP7Bfu8R2XDcdz4X/gDM3hxxP0/f5L0pHB+LLJL+TKhs30fJZifHLpgp2ezbE24s13g1zOEGnAfqPklUT6lcsb85ALqwANErozYa8WYoBWNDPQSpzGeTknynGXC94vBwlv1nh8GmfKHDrm4q91oedZa/ofti3+5XoRlKT8ozTDO3VfFmHTUaUtWCAh8E5f+HhoFGmFy8VUaZ1KSh+5770pB6i+uDgXJZPBL7pcSWQtc6KhiJNWVmKq84+mBvxWwbTgGSCyJGIsndMOnL8z/loHObTcHRM+3aAXfPsDW1Qf8vIRRv8/R4J9Orr7MnqqLk71VTfe19TmMcFXGk5QuYzhPPFnBDDEbWqqIH8qxGYaYdgpQS6PvGuqBhiXFJs3EXtdZ6fnejrF2dH5MfVRb33lE7YVdN0L6d0V2fq5ntw4dFnpfMlhNtgP53bzZzQveIfKTdSMpuiZ+wTW1L93je+cpMJyk3Ta6eCfTdGv/bnEoyxjn7MsNtu03kXiDwGkt5d/G/n8v7/jFmirYC8u4s8WJ/NXxfXRfXjym+0P6ewWTUTzhh/CC5AUXBexHpbATDGp7W/d2KLgwlI50lGGs5StU8QX3o71w0JdJj57swTiQ80QyBuuPcpYfpfIkgSbLeZ3DVxwcC8SX8eFA0fI+uhmGMTP0ndSPOTPel94BSqgKud16T237KKn06zcQEuw0659NyglqFLXzFKB1bxUmYCedlss/ybHzwd8ncc3h9C2dx/K8i73P0c34jrdDAPnxEb5msrT/PonDbrrZ2EAmmnUy7f6N3+Sbi/HSuWbnxUtxGV0ctNn9TaHicaEfWP0cl7Gukcbw5flZARb6WX5J5pcJ24V5Uurth6nas3e0g2JAY35qULF5DNir/Gn/z+qQYSRyZvyRAcIM2rcXJQRGRm/Bf3Zyroi9zd/hafCNJmbLtF+b42wqr8iLN3YmN+8OxrQ7o3Wzv3s9XV3XVXrWedhCu+wLsXr3h2zytiOu/HGk31mhy9sch0A9onbz3bxdmO6fasG6gdXbg/svG0mLILG2I47TTvVeU3YedeJP2932NN9m4Gh2fGMqVvYl29QNRq7HcyeczupwY6HJveL16zvyMjOR73oVObmx1KR6cN9TWN087z0glxRoKTG9KYn0sHa1a2oojG586QRuFpg5pT/aU71vtSY33LhLZldSzNvUex8evbvusUi3PKZdGwTvuXzf9cFrrvppW7D3jhdu5rD5varXcRGobFy42t+DOFmCGoyN37gwxE3zGgTfukvJBu1gWF+Y37RdBonbqdtjgpt7iazTeFGB4DNXTiXeMH8dAGXlx1gxat2ydU3miroR3dHp3LBmUEicPvzp7sFkfFFjJe1hB0LdVyeBcCcOxZikKNua2Bxah2TNZg/F0hfSZMWx99Qw7KsB47wmmbEdA4daZRK9qGpGULKF4kZFlQwERwUYtFrzGyInnPzQ4eQzsGw2PDV1Zi+e9Whaoon9Y1NB7pFHgk4fMP5XlekeNTVvzvguR90NxgHOSPa7ZPPF77xLQrr1+lNjAFmKB9/tEXDLF6HZzzw8/uF6SE2WXqdUpigTk6KcowSwCgTPjj6hk7sSul1H3cBtwyuNAdt6ggaH2Ovrj+txN+s3jr0JNLQhH0hgr+QS+L1JlphqRcAv5FT/NPa5ihIH+Y2iRcp7NywA2CMc5GcXmRhx+Dxu5ZXDIQglRI+U0Z5/vnaG1C2i5QNi8nCnCnJ/L/Bbw9jE4fP1pNagZSVdLinggwJU32EipqliIfwDJoV9LNWEWsOSzI11T6DT8BTRE9PrgtVRJJqWnouLoFMADk9i6w9WGlUWUmHM0aQMCh0VtBhyMj3nBE+nRJId9KI5ScTEQ0F2cGfmvJ/QFFO77U20iYCTQpy1n9szkzfAqRnXIzwGYcX5XTpJomVhWgN0SmrD9pFQgqZZOSseb8ZJVRWqZles19aQeeoHp5zPyh3iphgSanG0d0Ja6IvAdLTZHB8OukjInqYPJa/8XFoUU4fqX/syoCPYJ2yekumXjje9j1ANYP3yKB6XR9EUUmXjDNEkz1kl2zNBpE7BFVUq5pJ16bDic4q+lmvcgL2NRRqFquLJb/9bJn2IJL8N8HmHdqD8S1fJ3bmwxMWgi09Nj85nuBp5+93J9nnV3v8zMyWaJnb9Jbzlguo2XdwzeNveIzf3Py3NuRU0RUL/m2o4skhsVJ3Bxi1dqqV80/rwpvRvN2cU3+aYp1wduxa6csY2QP1G4pkSyAWlaW5137SipezNcqYvm9OJNJ3QzShFS0H6q8yKWnItd3d2v+xkvVWzYuW0+R/FRLNUFNmyl70ypo2bwDhKNI9cQN9uCZzMc/1O6BCITthbZu8oC+qnZgbxA9P4Iir6BXcIA/vLJjs7+kb1Csut5/dZHmc3xfXoBPhAscrwGv1l1ZF6mX/U8BJYSukbslt+ZcCVMQ7I/eOwqXXBnc3KFFPeliBfSMvluyuL50hL9y4bt8otsF9l0wIh/bYP4vzjjb6pWXte2oJ75CiS3/C9IT5fX2bJnnFtPYxDWkt/vN/wIuZnvAO5UDazmiUE2JE+wct4VwxS4+YjKIVCLuhdf2/p3wrd0GeqZKrhDPs98HupGBbuv1CCmInxEwsGDWITLA2N8BwYLBowEveEOHhEIChIyIlsyCCHfYcCy4oTkeXOIj/qHjEkIsp2J5mn9bYA+2niXCrpQEwSg/m+LjrkrS9mymyD46TLiGTvCht+ClqxeKwBvoi3TuhF2Knix9Z5lPJ97T93pyB1ebiithkv1nKxAR2XLC0k+78DuJivdeQ9b/JPrFPvamIBNEntAh3A/L5EeTBJS8O3iM70/Yn+Pkvz74fbB4TjIYN772l2KJoWOB1PES0FOqU2AddP++W8QSzbNflJB5zkEpctrGnPmkvryxA+HYRSazxILJbZcQhiP7eHHFYNlkrpl0qinqoI3w+cTsA2JuCfWqlVKZJqumLzgAUNO3UpCFfPr53t6xhRycGkE6xxkYI75MXYoLpdzftyOpgEgQzBXqslINCOsbG8Iji3hPtMCcr8UksmC7ub1xZ3kyCYHa4yCNwDMMdiEnMjhhpzKfHDY2KqVpJM/LN2zgkB6tboLlY153oJyUlDdqUhrnTroSHotf/YEiox1FBfXp8lIqSqrpd/WZy7eH7zLVJ9opcFypEGq1INzEaAiNOzErz307iEg0CaLeDAdW0c86wN0L0mmqAoOS+px8KB9BobTU6IcAwyGGRn6dIM+lQdqLeivqKj7iFvFjCsJRgum4R7313Z0DRyXllRnUVVgvXMmIGVQ4q4MgRQD4JcYgv3G4S1nl0QbtDKGt0AVXZE+gtR9mME9nsKfDLjSKfLpzRQ2qHDssIZdP+5L+G7ZhEM2Wgd3FQwKFI2Wof26XtWz3dY6Y8btCjSe+uB4AgXi0TYsuZad2qQpLqVe/KkZBBvApPM7fMt2OJ4KDjWPFU+mdMCGOG9EHnnh1rcrejLkZwXTZ81Zqnqg4nMuM+U5Z+QqFyFG5FXMbDjUCdURiApENPDuBNIlAc8fI6tWVMXf+79BshOtuA18AiD4PNI8S7n52c99IwBff1HMNzo4rW1n7ROrybKWDnSn1YFJZBcl0HcbymKm5krFYx7MhAI51RERrC4NtUR7mhWH7gLbD0mN3LIQZvucJCBjAZtgxw8H/UvdnKvsBIyN7RMyY58awVezXbDKs+uRBVMvk4paB0HzDX1FNilGrWVSkeQqqUWDTbKYhGY8KUuSlTVF9WjF1sqRP1FExyF8LXirmQKpXr5K27s7jEWUC01XWHS9NxJIKZ3wUzFcTYm+wV/JdLO11/mgrAQ/+qGV8JncAjcRwCxRNYRpIJlZCtWHKVTEj9psUTb1sEy1RJFJ0v17qdy07kqOq5ZrkdDu2NtrZQ5zgauNswUGr6S6ziT1D4lTXO4eOlq9q1CtZi6aUQ9MDQ4PRgzyOqlIldFQoMJNmyV7/vg5sKy4Jq2VO5/uSR8TBGQwsKmMHy5diDUblx2NUkN5V1YES8y6JaXZO9JBqgdXJCJTJQXgnQTYn6NftBoQqQtWkeIdSpZDH0WXnGGIkVcrZkPM//tu2coaeLy0iI2XgDwN5cQx7uIoQiaemQ/7Hr6/JcYn9WMT3Sh5GBlq3F9Fc44iEJtlZNIeP58mH0s2w/o4dWDV58jJRJlr4o9PylFuih7pmtXrWamWIVXnJyTZKY3YkgdE9RGVl20rCLtNdEnBHEmh7S4UVNq+eoRhtpxcO6PFEstyIWbMrX5evpp3+zuuYROono4uYdxCNX2xkDW+spw3dQ5FqP1rlvpPJXCP2UV+9Cch5jLC4ObPTx6jsTBHN5zsLEhP/mqfhjwVYtRiRWFjQiNzAsVyxbyIM7rmDOKBAULUj8iHBskLOlBpKzqQZO8xT/iyDbAKn104rUblc2rFrbzEb70DqDVkJU25QWOe+MjD90lcxRZUw9cUlgSzrzYkxqTGV7jDWpD/SRshMb7iQKOxWdHpo0utx9+tSGVgiCNsxjwuA6AyxNwMmnUtS03gl4idhzVIopPy8iN0sOdv46k1znNd5Ff90nr/UuOOyl+b1RjZ4egRNLUJSzqFpHw+UsF+N+TQXPHeeiZNhwqzPH+NqLLGRRTllcmCDiWMDEkJYXoOOylWYfWQW3BI6NyJjp78bjj8fqiUOqvbgZWNRRw0prkjG5ShQWZR4ZCx7i9gn0S+ZAMRl6d/njrBbms+jmehNqhmBZFq+uTD42l4yF+G+qGBXiZa6p2dC8+jop7llbMyRMoa6jMB0Y1X5gQjtA3FIXNl2z7aj1CHCGTRy61pU/RdGWkkqF29Zk6YK3wTFxZOPOwG2nwW0KgqbpZqw657u9gfXiGWkvPoNuKrUAqU1STqgL63wNkKKVYc8U0ZdpRV4uSM6IDTNkPbOrLgoIvZrEJspzn6ish+GtS6JehSoWJA632aS/a1THMMutjplSng/adQD/mQKPk94UQG2YS2WLHXXrHl2BwvlMRahuogMThmtlyjVfpFyj9hSxChguS8HImC++tJWBJhdfHqSZsuPaomjCc9veZnMb41wlhYxdxmZ/xK3knY5uGbQQ/wKWaOhjSCZvOLqwMVjHrF9eZm3pGGGSCSHZMFYszyV+YjnAZdf1V+5cDq5OyVoWYD9wua7aZlXz79OYqjYRrVLeWJ1X9voH/u7y+RhXmJek9F3K7rHF0K9XYg5cDrd7Bgqp/jcBr1KLQmEjtYpaDXmd1eDbPbqjhw38rpgpytzQbz63FiRIX4c1laK1s06B6PWG7RPhaHVccffs95f6TCwsF2xuk2+TCJ2yHGbq8bR2idHUQznNXgI9znkJAlaGqdXDbbtBNYjY/GBZejIkG4v5W0shTMCMuzjMsSv45TrY4wHrisRKLNsU5qYzOTX6Z6Jq0JEwurSskY1KKpE0rEETWaL4DKfYbkHKWAVRA9HGOHwx/X5hRiKB/qG4cGsbqaeu6H2BX5kQBJGmaZvaGVZ0LHiWXifOC5Uq1QfarB72SNiVuTbJeeWGPVVmcT1fFoaN3KdQjWK6XhqNEtZqwt+JAbr7CTCYnZ0zH6BGlWgrPX4ECNjlajbHEAt2q0dJFYTbCE8OnBer5d8Nb4q9HcZkOX5SdAqUt1R3u2I0eXDBsUgjmL75t/vOa0ACMMaC3wFnLqORucCOvTCr1Z/ocxYMgcTgwXSVSFtBCz3MHrIA6GVLj4llY/bu5o6H7/gx7H7izZ+M67+MxIZi6uNQql1OVWaB9vCaQNvQYXqNjMExU2y61J8cC9+6XBBhdg6eGnjGq1rq8ogF6ibuQcf2MmhNXHVLxI37cD7C9QjapFqJCI6Mhi1tUJ1Nao1YsFwAIZKabGfR480HJB5n9K0JbiucX0PcwsioRQmm6PC8Oz7JIO9UUvsDxRN+eEhqnP1kZS+QY+/EQzProSQNTvI07mDLB06HyP6OJlc0dGHG13Fkh+HkO6hHVTtDzFfdFBVdqITV9DUre20mKOpLWo7MLbIGgYmgCLQudNAy9sVMHHXCmGD7xUM9LxgayzET/ULyr0d9L2PFEFTsS9OUMWVDGCmFbBLSG5tIKOIsQq+wpcNqTISrnwl75TSUdDK63eDTjFwZ8bbJLnNsAG5T5dqKpsl6RcHWuLJXv88ATxHkTUgT8ElP5BjQDorVNETH8qShLc4+/aKo7EkeMbhQtvgZYHL1FMnvYFJN2DPT2uzFrMkJVsmZbSSExRNE3RvbTvQh6QDT3UwblQ+mXKPNU4lt3SMtUAtiE6EoBKvJxYgAPdqrL1NHTFeZ0EHA4SpKYMcgudjApz1w40d+Ch5D2R33BpwMf1aBnXIyZxX057463leyaOz4cALE+QmBKiCOOuUeDZWs4ZfSUSGotVnKULb44ehKtIF6CEVSaYijoMynOzqdbKlN3SiQ8Rags70wDHCuG0SbpfuO5rtTWWr4SHEt6LqnH/eAGadtb/wUvYQRWssQQxuWPoa9NQBG1A65yh9U68dzm3Dn5WM9P6QCRi1zrJsYAaPgCy+N/e267txQEOVvzMPiZUduFvEDsbM79/xmaqxW7P3icOgCLtrzPY63bHssfbrF8doKqEMaCe5yBTEHq1rsgcX3zySd5MaWJgOGK3lrZuNBW0sxWCkkMpKLb6vReWxIWY2VVlUG7EIy7pSLpEK30lP0wURxiFq0igE4XYzWfqS0iFVkcVlIe2JUd314jXqCcsCX93YrD99aX9jvQgh5gNRYFOerHjJnnocDX5UU/dz5SCw90rxjaJVfL6TibaoOsr69eBEhIslXncgKF2JHEZMU2bZ9U5ZHFXlMtcwFtVQj9nmtRpBsdLmWnWytVwo+ZRzkQLbGeiZxu9EZjl08lgsCGFva0HZ/fKQlIuyoo7nxi9JIBgmbPnQKza5kt9sQk6KRCfFYCXDBPrZiZesjOJcEDO8uFRSiWEBgWjL0oQysFkxmSkisKJp/YjnFYidcTpAbsoZ+WMnNzVkowaN6P5X5yR0wtpNt9XaOmeJCNoQj0Xr4nvgto86pI1AhDJyB6rPxfn5ncD4uEx5T5XSsZxV/K+xu0VShWx6dURltNrnTylVDrhQLeC1+4qU3cddMTOqSa6eUq5I54q2T18oWGXcH0jYpMchWGAtXJqD0AePUuoC/wsqGHSveWFbKu3nZU8JskZJl0rJ18U8bmUHSb0IV9l+RJxg+K1vNVejDzlstO3y0zhDtRgP4zXRtzVsbWMTIe+fbqo0ghMRfm4i5/3eY8qVgLDNUsZI0gUkyYZTajCQZaviiCBqy9GuTExn1YkR7maMenhMFgHUV7KXH1yL7QAVfQ/uxDn8YmKtTqOn7wA668VC/LkHa7LY9AlYCGKLQqEr1TC+8btkJp1SftG5ZUrWhaUoUwYyyI3L2EGjQei9SzPjhFS9ptXVyuo+V8a4/zdrLQGtWBIfTOqM5WG5GJyAOUdl1uvKAP3Hu/5JP6cwdBt8JE18MtBr5rJ7v9c9Bn40360r57cJQSE6WsVw6eCqGaav4QqiPO2T6c74/0X1ex+0D9dwxM0APCeQi5F6ViFbGOS87aRxwZoSpczUaDzx5cDPYvPxM45KCgVHrBMQzOdKwANASyjliXsES+1vzKhNmV8NovXb4RCLDgkiL2vYEZCwsq6e4JckAdN3MP8ddP0JO+2eghUf0czJK5n+6B1M6Z3vSnLXgyr85qVdyvF2qipxm1xgYc+JXDCA/6bt5IpSYRL9ounaXANItsZKhE69vy4XegT+uByaHaVS9uBoG63Ou01s8ChU/dH7t36BhFHtyKq9E6XcA2mmJKARWPeOR2g1UwtEqFDpJGk1dlnPpyzp6kr65RQqrgwf2gZKSCfelpa8SXIcaY6g7C8HCpUhAdsZCxKVwpELCob8fS6GtGNyc4tsc7mUrjH1zbGiP23NdqnGCkNEqN3ZIYWlo9pLXPi+RrXyXxiVVfPTxYRiDMu06tFRf/qLLjdarLT2PrN/7E2yR+z1K29tme3+S2L2Ndg8+fRza+ZPG7jwau/cI7B4dK4Taev2JcRHGMNFibCfZvZEINDWVP/2n2XwKHth1lIrr2zKOFkrCohy3BqLPHczljL5mk3WjaHhn+txHuWP9hjc36lrNePd0LrBFNLXTgZfvb/HkkP5jcwuPJ4AFBwMXZG0SPO6ytk9R7Fxn85oS8H4H4al11TLihp27s71xccPlTbfcMlV+xvkIqWGasUCAO4yd8FB8CEksdP+3RF5iiE0gW5GmHqOqMMXcvJeQtZJB8zETAi2uxTBLQxaMCAVszGN8XYsRbyhEOSu/bdCDMyupUAhhG1jHAdI3s1LK7Z/MCJ7aKOXmPogntK4n5s2/fnp7TFqvzL0ObktBEaGWPrsRotkxqLKCsDnn5rlhIvHynuVndBw8YswBdsAsbn3uyOz9Th9N4eymy+dxYcHMFWfJ60NYsvxpJ6aueFijQLWg9HA1fb4m/8KtbYzO1CPC9aHcJkB2Xa+2rlFISgODy/ic+qNhx9SUWibwBFo1QP6kZXsRFa0fsxOSbEPIc+sx6llVAgYUkzwffVqtGofyRklLRjYrcdWilM44XRHmHpBsiMfTn5UGm22MtnYY0u6kcq4Sc6VCwdZhrztoa1VuN5YMXyBWaVTjC4qTTP/RPPAoDhBZtjy2xsBP/NZfo7eWSNuQvRQ+VBFxXl5DvmAzFHJNNwQ1nD/wWC+9HZEreXFIwUE4wdngdBKL1wo2l1CwJdrikjDWqLmze9kNG0sb0rBo+uePCvQPUcTX5bHs6saoyWI3P+MapF4ciydrhkKTlnGDdZN4nsTkSM11IZjChETsTEyBapwqFrvGXnsYPqWrbXnnpMNGhc0HWvE02jgpTjJ9CHxAvN6Mibxc7rLYkgTT6yc/AFnGGWn0zu/m4T5s2+aYwoBf3OqDSxeJl+5xIYDIZiUxcM1Up95JVwzJuE9G0ddFFO/7+xBpE6E+S8TudPl4WLJZHBer0caKwxgcXoSZkKNtUT9oJA+48WZpLESxkSU5wSN95sRRuG6aTy1dOTxqn7V39tmaoTxo9o1TKoPkps8wDYebRhtZBM88uCJ1q5w7MOvk3NR0yoRrrtxgZWPWala9U57zRnKui+EWQ4vlBp10qZJ3RLQSx9xsvsGkZ2mYWnyrtLu9vCFn61FaB9qgO2zIoBFZPTLuwtWclb0wPmM3PQEhHkHPXGJdzpYVGYhXDpYT5vKKBFaki5RaAV87j7vksqveNp3UOgpzHCMG7YALaYeszjUEwTMbs9c69cpv7gaE8tBA5ro0IZhevTOKxIacz6Z/JHzVVBQ0m0g+emAO/JJK7OC/XED0wmZr1Wz4LIdI1dEwjBeewfzYwWQkcgMgCnEujxEdc9/mQjqMCk5ldGQcsH/sIpgYwDh4lPRGDYJAUx5ILEhrdpsFxgFhM4sRdcIUTyDuC/HS/8jkM7hA8qLw2fPr6L715Wi2J1sG6YHcG8JP8jR+IzyU+4QqUIqtvioECOkCS0B2HzpJ+qIzCJYH+D4kWR1Q51sbcCZ2IeQZBOcJnGQdvEP1WxOzqxugp4Q+Ddcr0n8nkns/eibXaitNnVTbyKKeAHsdubloySWG911zX8OK+5biQS55yT63h1BxgvCPOcH/1yI0CD/qMYk34Yf3rF3M93wQVfn6xgUw/xiHUcImcgksUVP/0FnSN6Sf5cL3j/X6kQpI//TfmDU8Ev/EyuhP0rHVJdDYL4/47i0SS7WAjXL1hNzY0Ex3GMdk2MTiigbwNUMtWqbJOTPx1SXZCeumdtpOfHniXZbYYMwaZtp8h7ikMcrgQ0jxLVxc4hUfy67BtuD91YyOnrB47gk75EVsHrCcjNTSfm2pZ9W5vG2PTz00LuMZnupLUG18wtXetzcJBsfHntFFJbiGWa4Sjyk4u9w+9oaSkdp0Ca/Wg0reksqX9kCMxiQ/doz3KFE5Y7eIIV3WIybpkBeWiTDZ7AqKpX/6IV5hrwU4SDIn1rIhW54NaiCdr6qhJhKryfq/WiB9moIHlV37Zd9YaMELJxXn2AkiYfW2wlXFNW+z7uPknPfsUTNfTh93myY+ljiGYVSfc4Hkw/nyVsBDI7cQJyx+jI+OWMQ8snVhIoGjd8vRaFb7Mycd/gpXnYfw0XIvuuFVB5/NF4ocM2s0F2gaL0HDZX7hnVl9xwxmeGqQ50uCypdfmfXLqLQnmvfx+M64qm51z/sJdDRM07XS7RoZsA/ZhKD4+MWAMfw7R1Tofug62SINu/zRKUVNzNbCSFSi2z5IMg+DXQaz4PHqczbbfNxJcqn49rEwQXm9Xz1xKRXhXaDkY8H8yXZLCD6uY7KUcnDYdcISBwMtZwC8R1qy7SAj45HqNVTNaGO+tKXJkBQnfr9FJi21+Mx6KQ6N1Bla2aaunkePUz+9nyjDks8PlLKgerdZ6zRplY1hQdlIbXuLiYyFARtbimf4REfz8k1ptvojKL427L8sjJlQqmaqGXWoMpYJVkdWVfiHhaSjASNcSS5FZzFRZ/yEBUrAeykA7+i0Jqw/IezenzB2jyMXg4ZaaD8KWOcyuu4b8rwh5h/aj81UKT920U9p3xA/CgOHdqZZ50T2eEDDPhLbf3SL8m0ZmgipuIlX8LTWANNZZXRrbWp8shC9qWLtSGYZiAFHbKGgQGuo+FzwDm8kmTBXX9btH8Ieo5Djfmnsb9b5E/+GR3ak4iaOF7Smur640B+O/3ynGMPcWQwmWjvLZsf9yYCsUHdlNnlN8sIlRNUA90gSYi1puloNCdKRPuR0dzdx9h9kDi7ZwZU9uKoHl+HgCh4Me5DMdbGTnkJsH8zcOTnVn+oc3zxEJ+FutiEe34N5cC2zlFRF/355+GcUGTTGnPMVBh1zZERI4LBXEf1D1mNF6udRbHXAYgHEhHE9pF+GbWqkMS/7KrLHq/5mTdxsGY4np/fdYYpzyDjfP6unuDS67bCIfjJolUNO9vFv2qLCjw8oM/+2IKMtpwMn6ACicUHfL+T5hWnOda+PWrfUdM9sD6Ypw4V4EfV2HqCyFsrM/1UWRSyEJQfUEu6dzL3sLtLBOzqvzh0QLvemR9bLpRWIFO3VumKnBAYDE+pFDqaXBmiZwVlsiuwNuyrcXyxt9VgNUr6CupgWzvq/MKC6v7UYI0hM9FgO85Arl8Mj8qwl3vj+WMNohasmzczNdIR7uEK4ogcfDQY5+Mwmuq5SbfdNv89DLGKkHcFCBI3LqtZ8jLps5mKVtN4pSI8X/8e40nGoJU5p8jWPiO//AtSzjVeOmfeg1TV5h41JsjMlql6kcn+a2rTRy30Qs/ZO5ZHfjckP/skvt+i/74wvSHDoSM/lDEdTLlsP3BZlHDeXnxQM7U4MLydi7ABhvlAiuWGLouXrY72PXH7hHJ/8I0uyca8fLWbimgEGvWeFa68uabbSe1i1p2vcZi5vqIEGlsVijrOQuk900AZC9UMwwHOJGwrk4HlWD2VvlM/+VayRMmbiTUqGCgSlTw3dMETAMeJLbdVc/jZEy8bgSyuGWFRB3m2krfNbAH5mGz5Ci84fmdDGYsQRIybBx5HYykXU5YqO7KxUOlwW3EfRmFw6RsJiHh4CpIybvHdYPNYFFQQhIu3ltM5evE8bnCblGCktNM+BuwuQ9nbw611gQbBPGLOqAjNBYi/tiQGQROJz106WBx/6KlaMxwH7duqE6H5g+gPeM9refExvHxtP2opZwqFPWy9E97G2mtaCBPTb0KtAZTUSAcnC0/zO8tmhL7Fp0uxYmXM+PJ4/uLoT6PDx1lrYL9TAj52kGtqwF7n/jX2Ze5Mdc7JP/Pcn4QEAnDMCH+txtPfzLOdQAPNz7/3lRfVxsHyCBHO1Ec13Tz0RhDsu1H0p0VyjRfPgAgqwD/YaAPnVl8d026jxXysi9mr2N1xAdXvjPvuCeXbS1ibvdeWDcUxxm4WZPFMtCiXGSAnSKDV5saeVKwoVbrCcux9ZDMwrJA8yxl8TdnAqQpYzvpSwYVpoBqcl0YjUmeRQz19o0oRpYi58NRLpt8FzyT98eeJ82BW4wrXjbl9t581PerzgaGNp+14lkV2W1L2LSjD1yWXZtdJgr6fn63/18FcbDIUb6rJrmvekUuiZnN/4+PWv2OM6MywudjooCJgMHP5sxStoux6T+xNDf1bh7hmplJxkyJIkHYZitfvxvt1fBas8b+75anjduLoe02heY+Gab71vrhdfyY65qZXBz+NRGqJdMfQ59n4s7ew1Tl604hoxmZcyzcPz3hrnfjtWQPVSoD93cSicIgF85rcwzMgbrJrmNCDH+DjW+BWHoo/xVgDrg5huEJrp9B7MgwTtuo7EvkkR3qzK2I1WyvIvnsDweZbx0DbOpoxzjiAboTKu0BX3SjUDJO3akLSmPHe/yIdGXkpdj4mFTYuXxN8bbUD/ON9CFj5ZqUgVXo63LXMROM0Qoz57pxPjm0n8ulx46Qkj7mJfFKWdryLIRFVJ5+yX2+KjzYQk4L5S8xcMI2qixhvmqpniu2ZB1q+D79OrRGqfDyFri1w/t10SIwxpqKS3gJ3wL7wxAuQcrTgsIUARrCVXNpNbL51wg4Qejz5d2mXsM7oJPXvhB18CWvzQlJasmjvKP29knllWAf5a9Mkwtfk196UXiUc9vv/z6NI3fx0E19+cclRRnHZnvgEKCPYqxMdqDtApin+hDIYAO+pVm7Sd3ZP97ImzpMQ4F1uU9wdmrkPW6wcXMNKaOOTiSbFPQ3cjPJYeFipJETAab8j+FK1OjH9zWrZfDYWVP7B0Zy/Hq4bkJLB7mgfEvvMexhGz9vSlHUnaPBGklsaW05k+UUVCexvKotEN3Avn6v0JmFulR3gpnS5em/d2dSKJdiL+014g1a58yEZlyUUq8xWuUA0ZTVhQ6d6YJeDzoDLzJpU53e4zS9OlqY2A3ktTfvuDtf3PeZsfFJoUbtZLSpPM8EBSxj8EUf5IyqemduD8T9FDluMpci9rzc105TSbF+G4EOvlFgGlgjZLrYhDM+OS7KsN8+ivpYUor47v51qZWBCwGFE75QRMyKh6KMfZ0xk+Ly1DCPW1oi//up7Si0RYbJ17F/PNDdCdMhHKsXjojdaLzgQYPSTpp6+Fp+weWul+MlOgujhnvKZ6HSpM/LL1mftSN9pZlsfgkeN0aiGiPPym4yayiQiAEbGbyCVxENRmXOK2SbGuL1WZC/Zi+azKyNi2cu7kz21+FwrQqH07L3NCklutYFjHyubvWneN2XyUUQuxLws7T5kPqmhm5OD2xO6T9oXS/1Kx9B7V/md1jdtw4o0G/cd5DEyM80TLkVk8202w75WT+o+Gf7bwfl+W9XLO/ZTYh9Mo9zMH2pjO5YngvB/LaBXt2b7Vmh4AdnP5Zot4NfTITKvWjLPvOpVunck+y2XjJZU8I2brqHzsH34XYmPxaL/kD1P7cKIprg77PnF8gdFSuGmzDys+zmj/ReYxI3tkd4svsJMj8l0zYiFFi2mMP7luECkQerAwjYPW5cT3wb7d3/zxlO/iqOdfJbXN3j+xrQgbfz3OQIAnDUCZ0r2+gR3PeGb9uqO9bSzVPhMRKl1dWxC9S9oEU8ehoEyjYA3iQMEU2KDjOMqJg/YCIvl4wABEh7HJjziaPWs0O1vtnFBB+3OQWpQ0RgBmq7ezLzewmp+3If8uisifir4yWQ1k5S4bG9mNQLsc7HJ0ly9OgQ+RBfqqk6DsRT3PW7DC1ZFjB4wmY6D4kndxaHJlhABnkSoilbTvHWK4XScU+SrutEpWSwjqEITg0polFgb5Ju4M7GS6Cl79FXHXBVcXeARO+14/xWIxiyXzPj5qOD55/v2R1Jr7ONfO6wXYRr1v4w+7iyYfdbNor/nx40VPyrBJFvit6ASfbmTkBKsoevoRWVvw1gmlFi0WKNfQeXRH4uyISYaU1wVk2IUvWVgueJ3JqFZsiYZSVhICp/Xs2LZhsoArv24T5dnEZva5WvvPslaiGhQFbvO6QosoB/S4L76cEqGOQMKhcCeDh5u01c+uddJIuNIIh8YAL1//q/tNz9B719YmZGrJaxlCBb+WdeMwOuObDS36dxAicDNaDGLluOWLWCZJt0A775NfgRPjnO805HnRtjzorn25vjuh82p9RejWU6kHtYwDOP2uISTpnVx/kELLTHHvTnzHGaZo9cZ9fRQaIoiTqOnRE+02G4YhwmzGiaL+fh7cI+ESsbDWj0UybjcRork2oMgYyVe2eSKR6tGO4g2WLGThRYS5xNKaCIR4ASLe4mPW9kHDMm0Vajz4q3nxTCMOOdQ2D+MGz2MZMFdd0iftYe1x2HgjZWQl8q0xxsOm5Ly79Lb+VUGTd5z0gB2/KhV4+vjIuf78EDcMSKWsT3VnrMxWsFl1hnGO5AQof/GjNvGBsD0+cqx+xAAP7ApQR0DrYP24iU4SoBA2qOR9j133Aa+5f3PeZVrVMguJLWh/svVWJEbu7D0JMDXRaxMvawhj9dlYFVokJkSyxnv3pFCfiM5/0mnN1fVL5tY0rJ4k1GeeFnD1Z5ucaosjPaaVB6JIzZlqKgdmCTESOjTVrHnJsI4SiAWXDcBrGBbqU0yCKD98exOIcGFM9ISQn0Qbmv0eEpNZipNAhLOCPsvjlJQ0NZ+sgl1qilURRUiq+Z5+9h7bFzcu1y/j9xepJa39YiA9T77uiU/40GQ9SOl8+x2VjVbmjWQu27pUAL34H0zTGAZrACzbl2vomPAVT14tIscyzjygS3hMR+CW4HmzXpGyDyQ7CtY74SV+eX8KyyODptYKstVKGYnE2rKinPW8LR1KruGNDhraqsTN7xcGhEloLnlRuEXFVjWZOzlGHhu4JjAlobQ1jUoUCOPyTcxtA5mNpdYyZkbYrnY8nRsb0Ti58C1Bh+YuBGd90FFGuGCmOB5mPBt0DoA1WfRsqOjPrCWCgHqsldrpxc6XkmerBU/GYBRGG9CLr8CP/3O93MvTq8LBtl4KaIraoXp4KpJZmCwQB6VQLK4QNnaQ2WIMHn6WkzaDhQmOCSX50MlItKqI5q55NKZEFsRBXprJJd3UXfxbenEo/xYh5wJKEf78Mw5IIwPtrLKzEjBorYnj/HMYcTNWop9YnmtcTSouEyf0tJSL+u9ybfhSEVmrkCM82/6mlnQOHRyoSM1pZpIGr4oqeltxDmHi3CAhZyjIb8nHrb97drkLSzVRRffXZOuX4rWh2ua3cBZsoGxvH7/e323VaXH7L8QxUBYEXuoo0ooRGmcS/megWU8I2LbgPNvNpB9BGuwW1jj3Od8/uSsF2gSdfhSRDmxE2rBdZvL02PAg+AkUi9Iiaq9+rjGLFJHdOF/Z0ZgaKX8DQ+uZJsZEQd9oqVrNZ4yHtlJpSdPIcFTQRleEmrhjlkgs+453qbn4hxXZvZ93kcO5/0PdxOEcAuTE/EPSicn8188r+XYkj6MZBCcSFdaGYqlfInAbTg6EbKeht0oz8GPA1Nh6c6CCVNYLi83tToCN9mYRvzbqOc8Lor+qiYPSMIPTUmDie/lUGHyoH7vq3tz+k2DTQbkPBkCY+B7TIzmzteQTP9TB54NZwHNpQ8NjOw+qFdmWgtW5gnVDTm0UwGpeYGQC7c3y7MDEQsbounmmgwmn6d2NbUaRM+XOuOItEd3x+m0nPy21wzmrid5skrBu4MoeUqCf84Sm7UEi49yv/wA8+Z1fU1rtoPddbDZ9MBwDqB5POAFWifnftgfLUNZFmBBNPEeBuBvVsawbuT5KNW5Y+3SbuPg9tDSF40K0guI6r1FSdJxeBKU0QOh5E5Xm6qjR8RLGUKqgbDwv6t/AaJ5xWm3JZRG+vxAMhz5wobHnz3PE7GdZQ0h5RrfpEpf4rOH57F2HFQ4p8eIGuoLYHXU+35caQHu1G/tvDfGTaqBMu6bWGa565Iqauqh+Hq+H5KYagsrHCbs5g/tj5HJ1nqcw1H6zSJbRAO60jY9wrD0aoGDi1hDX+/51eu53YkbKiYhpOT4DoBVJu8a0qQm0nCsipsrZ++swTJ1OKMn11kNbyDCPra0grS2fQA0pYBXUo1jQ+/5cYtXJyH/8MNdna5VqQqHr6TNb6TsLOCHUirss4Ha0hUnj5zCq2diik6jKiLtsUp08abhwa/z0FGHCBAy33Kh0vB/ve6O3xkcNnFMRUWeud6En5w5wE84WItg37d6JSvraIw1eVMZ7C2LYWJtiGC8wMh3flU2tAyb0lULRyOKrzFPz+rNeQbXFXxUIwG+jsoga+LPeqpDIGuKGf2kCDNKBFQ4VDlzP54V1plPLqL1MbCr3dJnf0NpYrDTWy8WPssYUd8ibLO1JyrXtKflXaAhhQFMJ9ZQj8Qu1fs8hjWE3QBtEW78ROrwKnMRDs62ZpHulidOvtnJ59a6u9OFTi1OkbkkTkx86UY3t3mnLnP1RHgrpfP7c//L1HDSu7UIVtOfhv7WHrkW3mOCr4B/fnAMHdVxgNKDK/NWC6a2ngX5nphCZbYWc+IPeVCvt1APBF71cyc8f5i1gXEZKjujFqTSQD+YtQPca7Zuq9K1V3tlh0uTgN7su7RUJucgX6/YFXfvRXHZnRJ9Ks96qxBc2et4SfRsHsa3B1HQpTsJJMTgGE6YeeDpgwo9LR2n3EOTrpwYPcAt20qPGdjK2066OU5dreBcUQDdvEIjLVNE0phSZIqoojDxnjCoMfc4LKzou6YM0AhycV829FAWHq9MVrhsCBtG/V78Qm4AbrcGYRYNPpmnnfOppod+JyUszOwoO02+/KlP30el5B1ra32CFhjLfk6fia5VaoULN01WQRtbPGRWEKIRo+kp5JEEFYw/drIVwG2D7t3EXCPy5TcrXTU1jR9/NBICSEiGeHcrSOD5VPMK115Zg3AQvcEOLlgxg9h8a4BtPsWwt3I6NucoSQWjXyQ7oMAC03PUwwPuq24/QE96NMFkkBfSjHy8GdaDMeDbFpINsM2tNUTEAJwqYhYv9LXcaRtc6pocWi+8RNS07Trdg+0yKttJpjbz8VNC7dcnTJuP7ME9RIVp3fNZgJtLS630ljwaGRXTYcpfrYsmLdMlM7obl3w/Az1sSV+fo4yyZSBYyNhirD5toIPewY0Nne0Rcz6TEYYPfRAAMDr5+ul1p2pTVWWqfG5eiYLrBt0nh30bCj19MxSw2i8IsZU1ybN+Ct8X2fbvgh6WOoLhRkK1P2Ffyat27T0/EbBllNT9czB9+ZqGybj5KrHuWyMT/xgQLJrr7j8ilQ5LTO9jIF/UmlWKqZboOIsY+efT4txasFw07LV/YgfoVZjjLrGXuCcHRgNJ0bLF/QwGfkaH6lTfB772DuyvXmy8/NLgTbQD+sFoyQ3a/7us3L5HvhO+RHicAgYDalD27QntY2n0rSPoOL58WC8Vt9SZndeCgqM9bh77uro1eWfzsVj6xg6yJEn1qMTARWjxZrgVKyyN2aa91zT/izn/FkmdeFGzVNQAiWySBgbaTUwYNTT5LPKpEElbFbCZQswE0oiZaxV5Ipx4lqOi3OEY4Z+OPfNWw777OaBTL9jzxMcT0tK3dqCFZBdCuqY9UgBzGXhk9OzjKd3xww1TP4aaCMmV6prDhKA4sijjSoO2d65+0gjQGLQPIkKZFBxeBuHxtT4X+hfrbsJ99p2U7JbkCO4F7f713rNdQjQleoplIHT+4UqSi/hMTXLS62wCSpylj0iX/QAh9+oTrqthQgekVXxUbvSRQEnLgAWl0ExR0DEBe862HcGf8BJN6pea0rmpV83/ybCkNomvRD73/r2ZJxyDm4Sg9QqXCmO7WIZwH0f20lGb1P11uVs+tGj6ER3Sb9O6e1KyBwEaByT0KPHqvYrmFWTSkc4lLa0GaHw56SeOT8Ttv7dplV2WVV/7ENtovu3bVVq9nhj/YzPpIJyyZ4x/ETzuLBNO5ZKKcvz/AalTpx69CImPpOcPn1HBqH17DMk8RX0zzzRDP6NYpDIbypwRaSAR4n3P5rqVYKd2C0v1vuP5Fkd+buL4MSehOHK9t1TysQ723BCqc33Hs4fO6sbi2zt/Bcg94jJ/2gzkd0i+EOYgS9MKHxRspE8Mh5RBcgiWdDzWjm6e6S+Fd2rUa2YOtL66h4iLAGgS848YN2vUmoQVio4kv9qgBUx6FQQZPBgk4w1+/OodpY+xYjfcO8zZBPLHG6j3CT7wcma1jpmJh2MHOiWOB7yAHvP47EM4jb+SE2kd/T7Gay5W4QcJTTLx50b1brKitUHe0q7Gp8QEeJxeccQa8QbjOkl9SEkH/cTA9AtvcAY3HYqeF++DxS8iH4dnRDQjcJv8hEoYkJOW1uJp9wBZQ4qR8dDzeSIiFa+Yy0UVKMq0iEezKxsThQfC/I+PbgQoOVsuJlhcHJe8Z9sU1VvrKC8Vmq94spR+mxH/S1jF0LFZZVSWLjCiE5KARCZZa4pcb1DXBNzlcI81c3RsCuBBdwC9lhq5Pkapmdhfu157lKIROk7jgYPaFg5ImP0qRiA8u+ulPWjRpbUnSdKhECAXlHTiTZIzvJSm3FZy2U5DGZXPkRxUbPwCQMVyy6O0A+G6JMo3TtL3rZ8Y16I6OtTy2WYcL+pESLyJADQC1kt8W+wpJAQjT/GWZob/pi6SxgpqBOimnQu3xS7dtixSClzyqrMSvnQfa0NT2d7ssX52qxvlHZmUzpqZdcyPvuSSFNBF8ezM4CKBjlQ7dp131nxeYg4r2Ti0eR45H1YFvNAJCkFd3f4qL+eHG5HV1o43Oh/amAMkvXRB8wnl0cVsdIJRkrL4MAlWHTOK6Oj96G+YdqHz+ftI30YXZ5QGePQMwrS12scEZHsZ03KH1YcAzN5xWZvsssmXHtn4u01zmCE69WfMFLQy+XYyD+ZJCQjoXefRYIQS81CrYiGF+EPqEIhQ/Mr5EmkySj4uXNoRO9ljyM4ETUTwWZOtxwV91QmH29geNMttwGWpHPmFZYOEqM2lba/MD3DsYRGMAxxbmD4SPsONPh6bOPvODJS7BtgAvADJqKXskfAp1+i2jg3P9YqSUZjEtKFp03/nusOrYC6Q/7IS5AHdDPfWcDqiJhmwvLNXqVuOX4adetQ6UkmU31gcrqHBMCqyzgz3Mrf30f9z+9yLLJ4PxXMJl9Mrm8TNCX/gkvX5JI4KUXNWhVsbtC++aKjHT7bYRyT41qbU/HHpZXTr3TErn935ZB1JBHuMEhkcUNc7ZfNwTv3u3DocxbKzB4GSKbT940mm3ntLq818xZMPd3fPcGe7jSNXqNWCKdjORpP+ME9BI9IXJI3N6tl7ZlM3S3NfHel8f6319Pg8wrA6ZG3BPy11tnb7+QI2b3377FUMSZhfQIPK9YZpVGl0kZVIgB5HYJLsgriFlJcp1aeHYlWCUa1wPAmJw6qVREFgsRbZ+50+Y01WBZvzp4VRNvF184eWN+azUyPU/cI8oy/wRZWk8og2Kwe/t90m0tmgYHvluOTWCTjWil419bIyKIYWKVKzQu0jRslurW8Ss2G0PFusZ7LZyZ/HS6VY9RPjzBknDz0f9XmKZ81ND4AoxEG7clqfG4eOuDRPlQnUU0mDRg+lYGi2A0pcygiWt0+m5Q17Pi3J/jFWHS5qf4S0+dt+cZS+vi7fvMescG2J+p3despFxy05ON8e8y1t+fUuvrmnmZD+2Qfhl6wvWY2Of9Todcsjm8Cbh944RlL+HoVEH5Ys64uHqUnJG5WmlTyWAB0yIZYleIhebW4pGMhugEG8SJzBrA7EkyFmEp0mc0+I7c89fdTvjdpZkpgG7Axh5XdrbW3tNoXEDUyUqAegnJXuDOGyp1tUDZT0adPbGM/5Zm/Be/HoBMrFWBo1f+1scN5w410aEJ8cahbtTn/BalBQZTnjQpEqGIvqz+jBX592bmS0j3pj4S5U8eIUTjZ5rjhUVE5rfhAObjOKJ3mly38sUBPchl20NM0Eucq81P55h49Gv6cFRupgmpt4MZzixo3p/ggmB+T23e8KdHrWrLw1hMmC89spWhjRqOCYNJfrYETSBzYcozRSL3mHcGIPnw8V9sOl09jGykfmGyvaUKAHwctdvpv3HpwoHDTGn3oTZOmW6gwycKfPs5p08ILzM7YMQUSqrVaqdg7XCS9yn4O0a2Gb2hUBxZjV94JrhKq5KvRpmI6luLRkR11+Z5ut4tsuGgcpW8+LCNpr8OtUCNEdWGEzo1D+0zulapjFmao8SIDMVC7mVLW1tjYBvudYBAhCHFtfjBDp8NYeDBfTJM3Ef1gW43Wc4waEDM44WEF0Sl9GO7fcBSDp0GoQ0LbWs8ciNvWQMuhEWRpqNOHO+kUgaev5s3DY2AD7l5g6czXuY76haBkzY3ly6/t9iNqna8PAHHqwv3aRsQR/7nRKapsL4bM3uA5I8bxgM6SF27cUrBVoHbdnhWWIqXdIVJPNidp2HMyNNTXWwfrz0Pvb9EoJ4hhwC6e98TKexxaozAm8ADfiTB4w6cAN2vfDdvmzQcskEVixJ5OpMEh1uYFbXp56wvCRxOjisXB7GHJJ9n8blJSkCfTqeCwVhb3Fj84KeIiCe4wusbnplJmV2jFEoOt2E3mVPbGwexBEUd6/43Vrg8/1brD7SDoRXby6Tw4xubF6emch6gK5W3LZYHkFO4oa1sPzrOgJCAzbwVsAPCO5LFeVZf7YCkXxhfwv1S+qb1Jp0yNjCYmfTdKN28pL9p0PuhhcW7IRcR95XcYgO34ccNk4+7YwOxzfdrsGvRKkat+z9zL28JytNxZh/ZjvITSUFp7YZLLmabVzCwWg0P17exRr3wdIOdd6VwzBVH7ByQ38rkBIKpo2Heb6d4vKJ2Q1Xt7q6vjJQFbz1SAfLBuGBnDoqsNPtLIl1Ly78I26IxwGKvfRfbTUiIt7HJh4OWJr362P9bkb/mZYa9RaI2z9/ylfNV31ktD6Wcz6ZlDRx6cQ+LO58NYeB7f4NYfKWfjtXsQMd1MfiSpZN92El7MkXWU9v9Ua9NwMpiIKgLwJp9OSB+tFIBHsDD1v50+uW3M0/dHv+dAJfWagAMT9+d/c+UeRPwVR+wQFGx1/LrJLLomoFhm+xj24b7ee0IrL3ukiOpIIE26hi+rV51uD9MQpqFuYAtDCnE2IZQv/mhBp37QJg0KibmV8BigYJv4+gE84+tHts2wJCvUsfXaFwvLZZs8xaBTzkMbbcjtDZMbZjHq9L1tMzzTPJoY5PnOWHc4X9pfpJVNj36G6/Yzzc8jcPZJi9TA9qox0imm+s/foxH4n7VgvCBSFV1d8NqkiHW68Km2GcqsICyjRDV3aFuLRqVa8Wm2lO45Dzz9OTOkDeNzaS1LwLivs76idq0XGnjTU6jsefWB1HSMJ3xAF9G4pdlolDXp+4oRh3nb1/ITrNGJu4sTE+YSObeXl4heG3Q8SFN5lK5+PRtHpL1UzJE60CD8TtBogwWCvwdPj60fIn8bLQ7W8hjM7QBIHbnYmoJ0vyTA5bpGM/HglSBs7j5FYzpPfUusNlCdZguiut/pZynAKYqINLYRsJhRnZeD0rjk3SMrK3eHA4ZGujRHy7tGYIcXksSpETXaMySesUb33gl+wmF28yOalTEzjThmKyEAYlPEtgOsO//sizjFg7sVg3aR5hJ9sVxVEiXkBPQUw3lzYf9sXp2okL4/8/veTr7rK8ixso/zz7XmfLKs9agbgTVVehHiLO9fpxcrWXLDALldGOTICwJmZgfgtLeY4P26DGhbEriuOhmwWdhDXrQSZzKSNcHJmpX5op6h7vc7MfM+OBw4UavBawxMx41HDq6Gye2Xkv5w7zHiE2pp0VLzmG3HEKO0060JNkpbtxCNc6br+jVggJeDG6nO3IoE+hw4WWbC3OGTfuBxjxWw3WK0ubKd4owukGIf/zoMSDtTAeG00iQwzcqBQkMlwfrm91Ynn9OZEub0AX68PQCDXAlMElcpacT/4xrKMkEK62e5R3kpbkKsXyjJE4db+jRCD9xbV8y43dqMx3wnu8987U8K1kL+y7Vx1OVuRqhY1n3FzG0Rgm3A93w341xc5u7LdP+8k4WiuL//SMBhztEtUw45/X4im36m7/CpB/B8+4fceF68GbLTzj7QrgB0EwgYeuxTkqzIQc8xjx4ldNdgaPUQcjZ884su+jk3p62Aqpmf5EzPOWgfwKt7gXvPXDcYDZKF99nflUrsgOSW5rsRErTIYcb9Eg3Pj4ONQjujiu3G17Qin1noSzSfqZugNLcsT6NiLHwWp+UMvD9VFt3gJS0GQavP9pMTcVnBhLPvD26wV1NfyheR1S7wy4eZ8t6sPqbd/Pzk6Ff51ADdhRvsrFkMOW3KV2065RIw4scnXwqKMXqRPKFXZzjCgHDNbSr+1f6lorLxR5P7QFb1VFLnJFo23r+aOJ05VtPsu4MxMIovumLqSbLdR0CVsq9P4wC4tOnpq+Xo+OVJTuMaXL6Y82Ktqrq9imor2hlQgiyHgK7cTpvzEktIexwJhv8U3cE9gBoQa3NobG/lF7BNAMKUmJzNWn7YUiBATYRgiUBKGdr6zYUNJ00SR/a30Vt8AZZdatGV7V6C6OhkMwd/N+n65EZ3ECsK3rrSN+3M+cIMzt9HniqXJTl9h8TJf0Tgqtbdd+KV8JDKTgorX8/cT/ReMT2scJPDykTPB+JhUprzrsaMpebDoz0fXemZ7RPGo3KrnZo+oh2GIGuND/UcVl2YqS/AymXSZ+eh2ywfThCt4EwlQmcn+a0HW1aag/lY5ZheHzmwh234Hkev5g0P2/+nQcLwwR8cB4+hcMBgfZyyJRpiQlEBCdepbwbCfEq4xAEztqF5FhVsbZzosHSNoUB1TiKuUR0cJySA3bhQXv38+4NVheQOCLbgYlKhVySyvP3vlJrJru4FxH3YlMmyHOfBjXIZbG3xq0impPDmiFPBf0WUMHaMG9amECQBT+KzhCK53F7AH9RJXCCLc9ZMLmCfUSTCIMsKtQMBBMEOi07tMk0cnFTi4mWvebwcewGqflz4v375QrOeFhTHkB9my0+P03nd1z4hKjtzkNndvhoHXl++1wKrKwr4XvaLfrhRhTiwkSsIF+7YfR3DHPZjs/DJkRvIgx+9lAAoM6bEPlk2NzKxy/f5Kx0/X9kcRq4UHor3JLEiZBw7VmH1O2ZjR6ZitWGCpe9Rx7lHCq41YC42qOhp/VVRryUJIxCBGdUdKp2pt8IbWQ6EuWIfD0/nlp5YYguc2ey3llbyZnKgYXBSxbJwmU5zZIrHgOYGud2r/CdXDbXOFEd+BDdVKqY5x7hfG4xsf0Dnksx6vInCY0elnocJyqE6sQUGAKrt9Ex+MT/hAcBk5qv5vwIJ5Wmkqq8K/S1HyEwac2q+ChLqZz9L7Tc5DhyiwF+hhohPD0qBkmTdcoU2IIid8GOdihTRdGRpJNoeECaqcyC5CA2LBSyhosWvVtotnK5ktTraH6qRz+O6/Tj3U7lZckTabbmj8dh5xHU3yY51DhL0BMJeXaxKny26Md+vlHA9RvxRMavbWQozsm13wA3arkB2ital+IxLP+Jb0TxF2FTMAc+hxCUcg1+j8kxKuZuQ4ASUapFzw6choL59LNR+B8mA6JdK+suDLEpuRS1OCJidLpEGrCSl9Q/XAQxyPB5oM/gnOnahZ/RhPcuJOZPfJR5dx7y26jWrKQ6d8i7gPoU17dKzxDQUQHZeJsMPcXzFIZr9hlIpALoGGNZDXnPsKvIj7ngCqUg+mS6ebXsJERFOlRgkGzeIOWFrFCwqtH0DJJKEe9/GvcNBFHIdQbVDg0cXRf9hNRiaXR5a726bhKIIVHZXbJEAtIJtliU4N3waf23KoXNq/8t4lXIG/guuHf+qZenHLqojichFD0KHoBRobSbJEehRz8IFDM4yRE7J9ZRCgGCrQMnv8vspH4LD42SOaDCKVIAGhAGogNaAMQWnagksBvooeFXBS2+OhdJdVKydLgo4sH/BWu6ISgshUYGSoMSpWfwz/shS04DVWx8CJ/M4FdIYF1WtDIKCPZ9H4ow4x553bP0MLZklJ+AdiqHrFSOUnbDISjkYj8BTps7PGj9K06kQtD+FmOpqWU3HAYsVIDQmezhI4i9QYWBTGoBEwpgbII/g1tBSgNnpXDCEsLff1ttOMqC64HT5wS0D6TBJ/jIwTL1VbUpX4p+bRsjp8JDFhH+H+mSQW43nYSOuBbMQOOf88jvCFlyz/SwBrxqQXJI9sIOtZiOYy+S6jKKsa08G+lsNEEQTtbgGHLSBxFgJPsf18H7DsWJg6FeJb25q984raAAWsBlFYx+d4WCApZwZhAgTq5EnFa/1nabbCLBJuIMnFmAVhwFVyyIi8CivVgiy7FF/98WhHrD4H/jXFyLEF4gn/oTkoFrnCIpVoRW6ziZAic2YnAbESXVrkH9iGJdrnxalOEL2DT+o8Qz4pSdF6gfvhgEvE8T7oGtr56yvnAtqjKtM+qad1RiDWtbt4uknGZbLccYKftfmphKXRUkNq1sTfvX0/tEyJNJTKKVGe261kVM/6rMm5f8Rb6QDLv5IBXDY9PqJMdRO/2PP/7DYJVz9AO1F8yVTNvtcbn2jSRw1rBhmyhW29/jjh6QKuRRSjWVsN86bC8AtXvphl7hHq5OTI80uNZ1aiu0Vgm23PgPf2vMO0qhfQpgOds4ZUuR8KawfZ+/aUWS8GPVFqigdXGGSksyixcCBm0Ei6DM8ba3eg4rWd8XY/Q1ZjuMIS3N+o6XeH7UO/QIWHRgU3AhhzRORpDDVaGuZmIVktW4LkKhgzdeOpR+II/B1oPOzR4e6zQvLaUisHQJs3FDJ6khSoyHJcizWzsPZzLW1U4E3x0/N29378x3Tm3FpDvYvgKVD5t/3SHKSZHiWX5gPKHnUZmvRqL3412w4S1PIOYfhagRSvoS6C3hG7pG2ZINJj7MOfFn5uq5EKuZHCEd74HDhoOjicADU/JqylCBAc10b/a5EWSwM/Ogo3uV5jzudi3igsP6Vp+5xHrttSnRJuqvH7C2YSL7bvsGgobPWLvjXEDbhnYODaQF1FuUNZjSUIt4t5L33qcOG25JEhISP/pSMekReLES5ZfNdbuHYgp10kV1uATsfLZnnKMruIXY6Q/ycB3z7S6Zt0yNf7qZqU12axrBgbFHKzlAzhOGsFBJv3DSxBUDYFkluhhm4PmuhYfBmnPjrRKgp0FpxNw9HJEueiI0SopQceZkY41U29lqvIGvX9fg6j7tphihLFdte8pA3Z0l/4rEwtv7F87mjEuw1GOKAQvjEH36NNoAvX7g2OHW6XvYiur8wn1Dbg+pXnHc7H/sctlY823KLKfAknH6FgNiQHQtUaSnHj+KKjZJO3eEZtVmtUeXFEk2HTXU31SPXDKXXamXzx2+FUrLgw1NL5i2i2L67/PXRVCEeyKMJrebpingC1gl9tOApKkqrTqhUuR/1yV/OJ2YKRUlkx2yxppW8J/hn74/eH+oeuXf4x/+WBabzyJ340R6jAu5sfl98123KEAK6XR238UNl/5pRwrgv3uevqUEnTkkM2DuZBbRlTz9u7HlM8sdGe+X6lwCXk5qz6tT7B+FN1EgGSoMpX7ZZq6YuQBW+cZ3ZAfDbKEMW8xFqHD7hP5pW8Bdl+aUzLH/EmqNG7MwkOeE2sfrltqEJ9w7WOORTp0RD+O935+VfmtwJrcj+02z8ewm5TnIMmbtQL5f1qaD8+vcaB+Kqc8RDG71JSwvLD54zTfw47dwdxDSIGJgozGJE9+bB3n2GaPe5d0po2fIhxUncdk44/YxCUXd+md0pR+GUX6TPjH1tBUUdKw+1oYm6KPM5CmVVc2cRcsqZ7hiUBUzhYp4PsDJIk7+hihaCEQMiyVTg20hYHmzdYep2E0momR/E3vUT4hWa2IUrmP80QTuevLo27fGd4zhg+gU8L78m7V/7oGw2hCR4ckPm1OF8gU4YLqpNvRNN727hHEcF2YLTUNwRVMDMmXCRSVqs3mB4+VE4LlpKRu9yzajAIV1u0Skq9AJyWFIlh7VSpVpG9iC0obZxjjPZRAomHuVPj29QNCyhb+epaBP9D3HE3oexnPtMRncZsCb3YmS4JsS82BdktBcaC1wSjCVOoPoruZ/wwRUC1wCF3n05jg8n94DFJqwOCjh4zQKVi4v4615uY/9BepifQqWde4TvCOUQs1OHTKSSUZ0GrEVurRDCnvs7AsgdmgnHLJjKhbcSJls8ByRBcbV8aNrOfGuwr5Chv12qMgSWUsNSO1RWLj1lrEqsffvAjwZHBdalSXBhUg1xWN36y63k2+7dn3cG00ahwW8sKkXUNVfJmECcJSIj6fiEyIJvsVSia0sJcZDkWRARDsN8tfGZwbf6mGvQNqhIy73FiU5Epm3LjE5Ga8myHN0VjBMWNG5ZHlSOwAmSuOdFGOd86C5XA5rMs/8BWPCNorIrsModEzF+CJAQAp8Wy6pDBmp6fW1ffKpVvhpDyiC/r4gCsUUazwolshY3+2E+5qCjSC3MwR8VCObWoAYIYSCkuQ/tLq5AxuwUxqXEfBIyxn8px5SmrbIyLbIZE9JuoH4jEhTOn6OGdnMHDT8erdxW5K+kNqjsZh2sVZ0KjRstFFnm2Kplb38ZorEZ6TX3D62AgWQ6bN5TZFW5YBItEkQXGWSopCsqpGIk+tBKla0cvit4kE9WRBAoNIRh6+77Pb88BwnllIy3WJ3hEntbZBjiZb1tsVm+6bFJ3aidzht/FksQNmCNmiR5hzmRwjPwc8j4l/w9kEUXnpjdgffy0gIsX8pI8opH3Dt4OFayyOrsUWvhYk4p4p8RhjUn6TJ36LRW8MoAO66G3KK6nMzgRqj6Kwxcf2ic9OEUrX4KtKbuHi3V5JC2TELnK1KBZbpPY+JgpJnu6ogsGqj562+2Pf8j0vpyRhK2DeT8NHunCDOFqcZvwtMvSLRlgftiGo83p4ZbcyJD6/1BjTZ8jNvPkyqs3JSvHDUAIGw3bzkUrnrXvrYBQZbhVKDKG5yuznFFSyo5gokMQpW0a0Gybz3jAoZHqbfdaAQe/l0GLau9HCVCkLOF8kFFBdpTIeg1e7KcFmm6NRpYpAQhnWZyhftqEqrD6xFQt2549hecRWnlRyJfz3GU1YeYtj26J0r8YZzmxaT6tdbS3JgOxxHnau1M6W9uhf3zg0Sjr57Zh8sxW8M9HagjeqH0OxbftCQCv36fyKx7BJsuuyNvnmG3lZ4xhDCp9Eu6o7M3CLDeiQDjzCkwHrD021D6VXyimdhy6cIZeOWQwmoXrtHZ6xu/AONvcBOA0fFqGNCmE/ngw+eXp5KItwJV6ektB928XKYBMkLaLot5BuGI8weRIhLyUzBmIKzbRvs+o1cvlA1hCSu+UVrnXohJhGq+m/EuDjbrEyzuBbTOIIoCKMR6ipMR7eeu2MhA0MYor3CoAUlmrroykJysqkByGCUHr61jB+VG5zfrsayZGwgUX0OgZ98DnZzDSdeEZtTnqwOLiWyVkb2Dlm3O5B+NcINbgM45E4LnIdG0VzCtqUBjr8KzS7jWj5alXq9qJcaKylyEd8rQ1KXjGV/hmZFne4ObYgtkk2gYohPKQKhMoADbeJSiPzF65VN/Y1CzVJwNZG1C/E0SJ2Mrq4qK7qGi/0YuVGn4JkHmZLQU+wsstSx8nodDVfnAcdiri/VmwcU2f/ehU2zPeUYsRqdfspV0ylNEl78lpcODtJrrUlu4N4WwimiTrhmaPK173jsOgBbUVVXaoATPPbEpBrNLqaOB0v6Q+u3lqB7d7yfteyLC77xJZEitmQk4z9IanGovHGV7qPOUd65WfJsNopChsIDinXumctS427GKdGs5kb8chIM3s7z/UguiHWE7KbwFIZKdt3zdQWDz6HZp/73+jycqqFJIeQy3JG+VztZVyVT+NZW+lX3ft8aDtk8ebdr0FvdCVTDY5YfPaUNX9uBYDIqrDrAJ3COzpeUYdrpSUHseb6VaZTaojNCRQyUg1DzQHUNJgurE68zqqEITZZGQdIuYdqsKkBaa1w3UEenTzpxj3Pf7ijadJ4DKCZg0YbKHpv7AX3TRHB09taRzU6bapedbnsXSi1EUxJVYIsiFrj7sjOlFP91U7xtyBx6UruJEPYKqP9mcStW+wTMM0fchnVYmrSGy69fmcjQMQC8TQDly14+v+J5At3pRPR2RuKYZEHIY1w1li4qqi3FBMbgGMqPH0eIuhWzZXG2iMRGzKlLcaq+4If9vZADahxMvrcDWbPWMP4yoqD7p/VB7dFhdVTxA9vm39EMyw2lSWKzX2TTfRSLS+Rdc7FFJXFpmQRQXGiSO7UAhVB1BhkDwpCCPFIKmKmPjt8sOcueYLwHSf+zBn4boYVqFc5B3FBj1JyRTlhWumlsMqxZsE0IF56gvLJVp7sI/Jb4MIEBmoQPtrxN+bBditNA6WDU0Pbsl/GW5Jl9rWHeaavC3k/n1uPQ16syCuutmL5CiqogOKiLElkeAhzmV8iSkZCipZ3vetU5xIrA4gw/u+C7BX9qwA0LedySASXr/4T7j0hT+L25zuOM9NVIv1Y8cMNv4NUlzlEaxYMjqu6aVbnhRPRjCUcfmauXArCY5vwOkofTC/jPJm/8wmpf7Wnvq3IiOkBLPYLLKQdbRv8G2fhHSPHbryyY7UUyr1ftZLWmuXXr9HunmnGvrZDwSe6Z/+oMNiPly9Mkx9fgMWF6Q4vHYSwXEvnBTsD882hY1t0l0VgmUGWOKUiqqbwYYUDn/3rODwTDc+Ckon70BpH9aswnWJqSu9hxu6aQO+PefIkGjk8jm6xf0eX3Cxj98N1lXZq7WU9IF6HN6igIr0pUQ667E/SMvJw8IcXX1vXE4W+ckbmsGHeU0cRET6tmEwpggcJ76Z1Tzj+9rsPgMoELDhcHwjPXoRiICSodwuWKIV725V++zOKUaBWyuHp16n+WWwclh4kXqZSyo5ZO6k525Liav8eOUSQZcuKfcqexROlg1lM19lVKCW4xVcbIvNPJ7Y1ZRb6tuRGnc2lLCrH+QMSq2cJeD3THB5sacuMCZqaScx55fC/UYlAsrn2N2AI2RQANCJvQTRl0y7q4BGgGPVi0NZnpx8P7L/Z5tq+8nH9gx6i8enqB2nf0za+NTAlnQzmhbHgC4yOG1RejRIUQIpR7PX2KtlnikTUE/xo2Kp4S6Q6DnN4iJFijFyu0nrJZsUvsNyoEzx5eirGaqP0lM//iPKqVxfn/2md+/ExX55aXCoubMWk2rGeZthWIkIbusQ2+CPt7sqQV8sstVVhOKSPJZo6GpBqcSTR0aufSe0qU8r4WE8z86BqpGJf8+dZEd5PrS8Aa6Ei9W1UCJoAuq53gfYRSONWH38kMhQMLb7wzdn3sJ3DKpH+lE0pjl+kvMzKOCO61CE4vvq4RcSxj/zl6SEhe/nZT39mw0jx/E/911VY60hI/4yk/7tkkChFdnPxce76eZpsMW+8hBaUZP0ud2pjRkaEMw6JrgH2tHPI0C8yvoFF7mwnXz3u4uyPycCI9jp9UmaeuutDK4MhIsFferTsO/Nnah9x6JMetWZK04g3Y7mQ/m6hpjE13tRE8r28Q+H2oUG4cqlDD0PHqzHMBKOR1vLppvjoJLQf0OLSd4RoCbkLEjqHR3YaspM/vcBrjI+Nd+m6H7YHRXDQX9uXYvVTFxVg8UEviTN5afx+FQrCfKyQY6L/rnYponhmkQOW1YxFAA+MaoSqzNsOS1hPJ9KZLHuJpa/JJJgxgudeIr78slNWFhBhZ48Ua7iG9P4gC8RmAGlvDzat/AkwYFcYr0tbHnoAMzliy+8MFU1JEfdrpqLOidL0Ml3y/FhwTzveABPlZLoDp3stbuxtDR9bwgxfxHnh09g0uKjX0y2EcAxw3rWn9PcrcJqdZmIdS8rmd9hjeDVgNI9B8B7vvXB6OeaDaf2Cin4/oCLt8fkDxRJFVBHzRnYXsK0O92MceW7Q03vd0+shbYCnBlly3BsuNrJ6rH9Fz13KTGkRfLOJAMOs9HfNvHyRg0A8bvjq4TWYJKnYGAz68C+Jp7fnp1hpvzOhhAIsPwVrSYP4+ONirfzVhBdcprAM0gLOE71RheL963zG/K+MBIR0ge9FVnrWMGRlSkhN3qhr/+HP3MN5E3hEKRkZyY+jAYH3MGrsPl578IFR05nijU8MlPmaTLMIn00Al2PVBLkfUXyikFqtaLasZOzbRKlN7wL8G5fk/Hrzei/uX1w8TNudD93lCTNkvNUybU2xv7kvVEfPgNhZFjHbfG8pK0n7pL179T3ZiL5W6pNx0S7WrGox9dkOpIXkpIVFmO1QupOVxPQ1ggpoALijDkSWGnmWxXrylpkBlu+bqXVbtBi1vKgFLp/04WDg/iaSvlXxFCQZCZ7G+N9Wgwq1QwqkXIj4NLH7A2CcFnFS/K18+aDrf/ejtCnV6IYQ/hQyRBGL97KiS6uIn4I73Ht4tz42v6Kp3dfRHGDqM+79hgGATruuWz2gm/7nHXEETuKYvJU+S9ihsdwlnpvzle9hjdDlDY75p9cOEwdSq9dpAubpsu/VKVSREaJMzT3oGgcolgwY853bjvNDvdHXo6eAbDZQYAZ15QchBpSnpmiOsRtwh71wiaklh51QMqDjZqo8Z+Xym2TquSecmD7sNTX+1Z91l/Z29gjtFXLjEv+Qk1meFyxi05UmRMXFZYtzOB41owTVXJo9XFghFDRA8mN7JiJU+L6AEExbuZf5WNAT/vnCY0ql/SNxifOQEc6W6qOhwSamOxNzHuIcnj0NqgmWMyaZBNE3JDj/5Bq9OPsTbIUjfO3AiclRyYkXQPurusIcWCweiXsBqny95XjCQC3qf1LGA3RcYmZfi6msGbPEupM7xB6x++YTBuNE90U6P4FtCG4xs3Y7ldhKpAn3Tu2b1thj22us48sOxWZlX/egazrCtnCkH3lOg1ZlcLDPOynzo0njHCl5CDyakPgOEBpmGywWpMnsb9zbSROneitpDOdp42fMZW3oBpj25NlWLrtRc5ofjL6Q0QQye+UdXvOD0VaaHfyqyxh+Cp6/BgZdG7Tvz93bZIpe61fXugiotqJFZoK8jTjB6dpivbZw5jN1XJkvI55jrWtBMoVTlyLzFHbAlEQXM9QCcqs4pV0ozLofdVvfiSt4FmplgHAmrNFBqmqIMEzaoLqHyQzcMnWOD/QWU/zWJIsUnCxtso+NchlCMWKLretyfK1FYFRRrZv/dU+JLNIZF+UU0RqtjLP0FbU8Ujzxx3dmEnl9ouptS5BDvqOsNsabaVfwAM1TWyZfEsNnhmFQtHmoeHbk/uCVZ2WPLeTqF+uUdyg8hZ/bW1k/Zv0Ff9fUuZCHrmFx2HcJNT1vCIh4PkeDDwsKvttTk8hs0hFN8eLo70w+7CQv4ZDbvOvUmD6mJxyKchFx1y1+HVv8Y5BHyxDBFaad42Fi49ADSNuTJKU+n2AaaJH71lSpZREvAxCb7+BozHgnB3sMrQq/2TkpJ/Z0Qw6Tq/bwpf8o5U6Q4cu+eo5QjUod9MEJkkGoSBHU9gOlqZ9sWUsbyV483wiD3Te/3SatV0g27Ew6QSgGiV0Ip2OObppjp+oGhHGCa6vdKUw7HxQ+aKhWt4m3gDtzahwHF0FEE7D/SzxCVeNCQ9oofV+FGUB03PfZu+ewgZ2uOh0+jbsT0/Y7FZwx1uJWC1yd6b50xPlfTkoSF1B0JjNvK37ZCmGx19l8TW9Y5o2xe4C81avPJhSipY2oM/QYhvrv2KVKShxfI03bGQSdKRumdgaMIFQb/JqVRswrbzAxXi5cq+1MgYQRf/SJqhM42Li5Oyzn3+K3sfF1j6Bxcwc2EUp7rvutceMgG+vOp1ZZnhdI1J3l4dotJeMwS/lpgC3XDTvIefdrGZqTZROsyisMK63SUe9vED7MPaEuWRdASsQ93cl1YKxs3YtxAsuJNMXI4gB3tstyFwABBDbwfa2EDTFAh1cVzLHTH5WTDWLKx3ITx7Pj1HIAuh8/x0eywFynJSWQYqhJvT6Y3Xy9Img8c5uwn3l+H4nuV09h8LT9FAGzg4nftTZqPoTXZspbwjW4zs1UH9u+s3zdO2t9f+MNV+Fs9/tp0wXsXK3xvHqEdry2gUtUfH/3e5WE+ygNttMKL506itoMGp2Qij9ZnLJ9Tj1dwAEVLPjJyysPYSMuvNyBl9lwxOX0Tfw1vTVYGcda72RoCc+CCzIxIjCMNzAR9Gw6ngYmUzlyMQI0w+InvXp/WORH5u6Mja8QyQFxRCEAZmnCMueiuoqsdTdaSJOnL/SjyNTQYBSz3xORk34c7Sg59iamEKQCQ5DEp+Q7kB12k92bJYmfpaQdnFt8jsJYNVRbn3w/aywofiHPc/rF5SETX0K+1NyK953vhc7Nvj7Z2YQGd+4kvqdBPuPX9FDhuT3p7ry6kOZ001UjuAiaSuOc7qWpDPzSXhTno6weipSGgR1oEVpXKuCDParIsae/OaUVvU6moeXbPYC8rJKN/2r2eNaNGsu0ptiqMefcKcN2l9DesMXX/DtRYIkVPyfWZPVqVvjINse4QBu7MRBW+E86hONeQBEWehqzvaNxuNBQYTH/5hxivD/tTOPsvaxq4va10Lf1ysFRb7bPk6zqat9C1XHiLQrRAcLlitIHyf8ug0DkSokbbiVcH92Dh6GoHJ3YP5Ysou2Gu4g2pbNvnwMwJz0Yir5AIWQRRs5herU3wM966wbmTfRxF3IeageJS5YuuAOfpFb677W4SX1xbn1YGVz1GsbAjfXE8CTOufqojf73s9YcVB7oNZKbTUISvfWw2Ur6UrXj6X1xTxDzcEvTcX04tmMJtwb6VkYHSfIijz6OR8Dzn9Hgf9yTSm5MFA5WLX8u3KTQEN92J3vfSRHkyZtNpPxQjp5g2apCnJDgwlSqmwSArGBO6VJkRQ6Wwt6f6J374G8tr/DSFUVP+ugS0KEg1UhmTLnWoWGEDSoHr89BESvGZjUG/RD1ymekYP/L5wB7RiOTM4w8NFDKHYlLNcj95Fv+dMqqai9OQZJONP2v29iZ5CwsJEmpo1BjU+IaujxbsSIcJyS7KXStVYqSe8IkMYlQqtr3CNcOPQglGBH0ylrJ8jjZSjWnY++wyYR3vT4/qN4KYivVnzCbMmdO/numbF8UrNPqFLkPrtOU9oAmfBTpRlsqDJoH1b7+HFEnvY2aKOr6Et3chpk5YrjpT+iFrq3yM0BGWRUSKjESJpwHNN4YVJNJQjFO8g7+q3+xfy/HBnNzaNzXmFwkQ3WxaWuF9z4P8Ia70u9x50A+l/433Rg3u/MBA/w+ZcFgbbcoGOvdpslKRwtxrMeW5pilmvvukQ0DrtwYAEXCt7NhgfDQxLzmSik6EjbZRgBKrzU0bg6NC2gXIrzYchht4iq/uZh4OJ0oLearGNgdfTN7Fls3DwUd0fRa/5Zu2/RRq4hjewIl+WECNF5zf1OG9YZyoe3W2i3wQfdsKAfPecPhYvKj+uxoE95J+RQ3c1oMxdqRYSwym3mU8tTwdfSnDHMw0D6ywLV/fYFmZ+5x7yPO5LOPIc+2XF3r8VrDHldYr+TQna25H9ZL/n495daMRGq5YmXLwU9RcrJMvs6CZVMQ71itmojZo6XeN2NxrWPA6VhldgCj1ZiiNuUs7pGJAlrdezcru7ounYRSYuByb/jIU5xSs2kFneAZ2uZXm+1EugmuCYff7X4zN/om1/1flEWToOyPAw==","base64")).toString()),jY)});var Vxe=L((omr,Yxe)=>{var ZY=Symbol("arg flag"),Kc=class t extends Error{constructor(e,r){super(e),this.name="ArgError",this.code=r,Object.setPrototypeOf(this,t.prototype)}};function tb(t,{argv:e=process.argv.slice(2),permissive:r=!1,stopAtPositional:s=!1}={}){if(!t)throw new Kc("argument specification object is required","ARG_CONFIG_NO_SPEC");let a={_:[]},n={},c={};for(let f of Object.keys(t)){if(!f)throw new Kc("argument key cannot be an empty string","ARG_CONFIG_EMPTY_KEY");if(f[0]!=="-")throw new Kc(`argument key must start with '-' but found: '${f}'`,"ARG_CONFIG_NONOPT_KEY");if(f.length===1)throw new Kc(`argument key must have a name; singular '-' keys are not allowed: ${f}`,"ARG_CONFIG_NONAME_KEY");if(typeof t[f]=="string"){n[f]=t[f];continue}let p=t[f],h=!1;if(Array.isArray(p)&&p.length===1&&typeof p[0]=="function"){let[E]=p;p=(C,S,P=[])=>(P.push(E(C,S,P[P.length-1])),P),h=E===Boolean||E[ZY]===!0}else if(typeof p=="function")h=p===Boolean||p[ZY]===!0;else throw new Kc(`type missing or not a function or valid array type: ${f}`,"ARG_CONFIG_VAD_TYPE");if(f[1]!=="-"&&f.length>2)throw new Kc(`short argument keys (with a single hyphen) must have only one character: ${f}`,"ARG_CONFIG_SHORTOPT_TOOLONG");c[f]=[p,h]}for(let f=0,p=e.length;f0){a._=a._.concat(e.slice(f));break}if(h==="--"){a._=a._.concat(e.slice(f+1));break}if(h.length>1&&h[0]==="-"){let E=h[1]==="-"||h.length===2?[h]:h.slice(1).split("").map(C=>`-${C}`);for(let C=0;C1&&e[f+1][0]==="-"&&!(e[f+1].match(/^-?\d*(\.(?=\d))?\d*$/)&&(N===Number||typeof BigInt<"u"&&N===BigInt))){let W=P===R?"":` (alias for ${R})`;throw new Kc(`option requires argument: ${P}${W}`,"ARG_MISSING_REQUIRED_LONGARG")}a[R]=N(e[f+1],R,a[R]),++f}else a[R]=N(I,R,a[R])}}else a._.push(h)}return a}tb.flag=t=>(t[ZY]=!0,t);tb.COUNT=tb.flag((t,e,r)=>(r||0)+1);tb.ArgError=Kc;Yxe.exports=tb});var tke=L((Omr,eke)=>{var tV;eke.exports=()=>(typeof tV>"u"&&(tV=ye("zlib").brotliDecompressSync(Buffer.from("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","base64")).toString()),tV)});var oke=L((aV,lV)=>{(function(t){aV&&typeof aV=="object"&&typeof lV<"u"?lV.exports=t():typeof define=="function"&&define.amd?define([],t):typeof window<"u"?window.isWindows=t():typeof global<"u"?global.isWindows=t():typeof self<"u"?self.isWindows=t():this.isWindows=t()})(function(){"use strict";return function(){return process&&(process.platform==="win32"||/^(msys|cygwin)$/.test(process.env.OSTYPE))}})});var uke=L((Fyr,cke)=>{"use strict";cV.ifExists=lRt;var xw=ye("util"),Jc=ye("path"),ake=oke(),sRt=/^#!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+)(.*)$/,oRt={createPwshFile:!0,createCmdFile:ake(),fs:ye("fs")},aRt=new Map([[".js","node"],[".cjs","node"],[".mjs","node"],[".cmd","cmd"],[".bat","cmd"],[".ps1","pwsh"],[".sh","sh"]]);function lke(t){let e={...oRt,...t},r=e.fs;return e.fs_={chmod:r.chmod?xw.promisify(r.chmod):async()=>{},mkdir:xw.promisify(r.mkdir),readFile:xw.promisify(r.readFile),stat:xw.promisify(r.stat),unlink:xw.promisify(r.unlink),writeFile:xw.promisify(r.writeFile)},e}async function cV(t,e,r){let s=lke(r);await s.fs_.stat(t),await uRt(t,e,s)}function lRt(t,e,r){return cV(t,e,r).catch(()=>{})}function cRt(t,e){return e.fs_.unlink(t).catch(()=>{})}async function uRt(t,e,r){let s=await gRt(t,r);return await fRt(e,r),ARt(t,e,s,r)}function fRt(t,e){return e.fs_.mkdir(Jc.dirname(t),{recursive:!0})}function ARt(t,e,r,s){let a=lke(s),n=[{generator:yRt,extension:""}];return a.createCmdFile&&n.push({generator:mRt,extension:".cmd"}),a.createPwshFile&&n.push({generator:ERt,extension:".ps1"}),Promise.all(n.map(c=>dRt(t,e+c.extension,r,c.generator,a)))}function pRt(t,e){return cRt(t,e)}function hRt(t,e){return IRt(t,e)}async function gRt(t,e){let a=(await e.fs_.readFile(t,"utf8")).trim().split(/\r*\n/)[0].match(sRt);if(!a){let n=Jc.extname(t).toLowerCase();return{program:aRt.get(n)||null,additionalArgs:""}}return{program:a[1],additionalArgs:a[2]}}async function dRt(t,e,r,s,a){let n=a.preserveSymlinks?"--preserve-symlinks":"",c=[r.additionalArgs,n].filter(f=>f).join(" ");return a=Object.assign({},a,{prog:r.program,args:c}),await pRt(e,a),await a.fs_.writeFile(e,s(t,e,a),"utf8"),hRt(e,a)}function mRt(t,e,r){let a=Jc.relative(Jc.dirname(e),t).split("/").join("\\"),n=Jc.isAbsolute(a)?`"${a}"`:`"%~dp0\\${a}"`,c,f=r.prog,p=r.args||"",h=uV(r.nodePath).win32;f?(c=`"%~dp0\\${f}.exe"`,a=n):(f=n,p="",a="");let E=r.progArgs?`${r.progArgs.join(" ")} `:"",C=h?`@SET NODE_PATH=${h}\r +`:"";return c?C+=`@IF EXIST ${c} (\r + ${c} ${p} ${a} ${E}%*\r +) ELSE (\r + @SETLOCAL\r + @SET PATHEXT=%PATHEXT:;.JS;=;%\r + ${f} ${p} ${a} ${E}%*\r +)\r +`:C+=`@${f} ${p} ${a} ${E}%*\r +`,C}function yRt(t,e,r){let s=Jc.relative(Jc.dirname(e),t),a=r.prog&&r.prog.split("\\").join("/"),n;s=s.split("\\").join("/");let c=Jc.isAbsolute(s)?`"${s}"`:`"$basedir/${s}"`,f=r.args||"",p=uV(r.nodePath).posix;a?(n=`"$basedir/${r.prog}"`,s=c):(a=c,f="",s="");let h=r.progArgs?`${r.progArgs.join(" ")} `:"",E=`#!/bin/sh +basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')") + +case \`uname\` in + *CYGWIN*) basedir=\`cygpath -w "$basedir"\`;; +esac + +`,C=r.nodePath?`export NODE_PATH="${p}" +`:"";return n?E+=`${C}if [ -x ${n} ]; then + exec ${n} ${f} ${s} ${h}"$@" +else + exec ${a} ${f} ${s} ${h}"$@" +fi +`:E+=`${C}${a} ${f} ${s} ${h}"$@" +exit $? +`,E}function ERt(t,e,r){let s=Jc.relative(Jc.dirname(e),t),a=r.prog&&r.prog.split("\\").join("/"),n=a&&`"${a}$exe"`,c;s=s.split("\\").join("/");let f=Jc.isAbsolute(s)?`"${s}"`:`"$basedir/${s}"`,p=r.args||"",h=uV(r.nodePath),E=h.win32,C=h.posix;n?(c=`"$basedir/${r.prog}$exe"`,s=f):(n=f,p="",s="");let S=r.progArgs?`${r.progArgs.join(" ")} `:"",P=`#!/usr/bin/env pwsh +$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent + +$exe="" +${r.nodePath?`$env_node_path=$env:NODE_PATH +$env:NODE_PATH="${E}" +`:""}if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { + # Fix case when both the Windows and Linux builds of Node + # are installed in the same directory + $exe=".exe" +}`;return r.nodePath&&(P+=` else { + $env:NODE_PATH="${C}" +}`),c?P+=` +$ret=0 +if (Test-Path ${c}) { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & ${c} ${p} ${s} ${S}$args + } else { + & ${c} ${p} ${s} ${S}$args + } + $ret=$LASTEXITCODE +} else { + # Support pipeline input + if ($MyInvocation.ExpectingInput) { + $input | & ${n} ${p} ${s} ${S}$args + } else { + & ${n} ${p} ${s} ${S}$args + } + $ret=$LASTEXITCODE +} +${r.nodePath?`$env:NODE_PATH=$env_node_path +`:""}exit $ret +`:P+=` +# Support pipeline input +if ($MyInvocation.ExpectingInput) { + $input | & ${n} ${p} ${s} ${S}$args +} else { + & ${n} ${p} ${s} ${S}$args +} +${r.nodePath?`$env:NODE_PATH=$env_node_path +`:""}exit $LASTEXITCODE +`,P}function IRt(t,e){return e.fs_.chmod(t,493)}function uV(t){if(!t)return{win32:"",posix:""};let e=typeof t=="string"?t.split(Jc.delimiter):Array.from(t),r={};for(let s=0;s`/mnt/${f.toLowerCase()}`):e[s];r.win32=r.win32?`${r.win32};${a}`:a,r.posix=r.posix?`${r.posix}:${n}`:n,r[s]={win32:a,posix:n}}return r}cke.exports=cV});var vV=L((tIr,kke)=>{kke.exports=ye("stream")});var Fke=L((rIr,Rke)=>{"use strict";function Qke(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter(function(a){return Object.getOwnPropertyDescriptor(t,a).enumerable})),r.push.apply(r,s)}return r}function JRt(t){for(var e=1;e0?this.tail.next=s:this.head=s,this.tail=s,++this.length}},{key:"unshift",value:function(r){var s={data:r,next:this.head};this.length===0&&(this.tail=s),this.head=s,++this.length}},{key:"shift",value:function(){if(this.length!==0){var r=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,r}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(r){if(this.length===0)return"";for(var s=this.head,a=""+s.data;s=s.next;)a+=r+s.data;return a}},{key:"concat",value:function(r){if(this.length===0)return CN.alloc(0);for(var s=CN.allocUnsafe(r>>>0),a=this.head,n=0;a;)rFt(a.data,s,n),n+=a.data.length,a=a.next;return s}},{key:"consume",value:function(r,s){var a;return rc.length?c.length:r;if(f===c.length?n+=c:n+=c.slice(0,r),r-=f,r===0){f===c.length?(++a,s.next?this.head=s.next:this.head=this.tail=null):(this.head=s,s.data=c.slice(f));break}++a}return this.length-=a,n}},{key:"_getBuffer",value:function(r){var s=CN.allocUnsafe(r),a=this.head,n=1;for(a.data.copy(s),r-=a.data.length;a=a.next;){var c=a.data,f=r>c.length?c.length:r;if(c.copy(s,s.length-r,0,f),r-=f,r===0){f===c.length?(++n,a.next?this.head=a.next:this.head=this.tail=null):(this.head=a,a.data=c.slice(f));break}++n}return this.length-=n,s}},{key:tFt,value:function(r,s){return SV(this,JRt({},s,{depth:0,customInspect:!1}))}}]),t}()});var bV=L((nIr,Oke)=>{"use strict";function nFt(t,e){var r=this,s=this._readableState&&this._readableState.destroyed,a=this._writableState&&this._writableState.destroyed;return s||a?(e?e(t):t&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,process.nextTick(DV,this,t)):process.nextTick(DV,this,t)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(t||null,function(n){!e&&n?r._writableState?r._writableState.errorEmitted?process.nextTick(wN,r):(r._writableState.errorEmitted=!0,process.nextTick(Nke,r,n)):process.nextTick(Nke,r,n):e?(process.nextTick(wN,r),e(n)):process.nextTick(wN,r)}),this)}function Nke(t,e){DV(t,e),wN(t)}function wN(t){t._writableState&&!t._writableState.emitClose||t._readableState&&!t._readableState.emitClose||t.emit("close")}function iFt(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}function DV(t,e){t.emit("error",e)}function sFt(t,e){var r=t._readableState,s=t._writableState;r&&r.autoDestroy||s&&s.autoDestroy?t.destroy(e):t.emit("error",e)}Oke.exports={destroy:nFt,undestroy:iFt,errorOrDestroy:sFt}});var cg=L((iIr,_ke)=>{"use strict";var Mke={};function Zc(t,e,r){r||(r=Error);function s(n,c,f){return typeof e=="string"?e:e(n,c,f)}class a extends r{constructor(c,f,p){super(s(c,f,p))}}a.prototype.name=r.name,a.prototype.code=t,Mke[t]=a}function Lke(t,e){if(Array.isArray(t)){let r=t.length;return t=t.map(s=>String(s)),r>2?`one of ${e} ${t.slice(0,r-1).join(", ")}, or `+t[r-1]:r===2?`one of ${e} ${t[0]} or ${t[1]}`:`of ${e} ${t[0]}`}else return`of ${e} ${String(t)}`}function oFt(t,e,r){return t.substr(!r||r<0?0:+r,e.length)===e}function aFt(t,e,r){return(r===void 0||r>t.length)&&(r=t.length),t.substring(r-e.length,r)===e}function lFt(t,e,r){return typeof r!="number"&&(r=0),r+e.length>t.length?!1:t.indexOf(e,r)!==-1}Zc("ERR_INVALID_OPT_VALUE",function(t,e){return'The value "'+e+'" is invalid for option "'+t+'"'},TypeError);Zc("ERR_INVALID_ARG_TYPE",function(t,e,r){let s;typeof e=="string"&&oFt(e,"not ")?(s="must not be",e=e.replace(/^not /,"")):s="must be";let a;if(aFt(t," argument"))a=`The ${t} ${s} ${Lke(e,"type")}`;else{let n=lFt(t,".")?"property":"argument";a=`The "${t}" ${n} ${s} ${Lke(e,"type")}`}return a+=`. Received type ${typeof r}`,a},TypeError);Zc("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF");Zc("ERR_METHOD_NOT_IMPLEMENTED",function(t){return"The "+t+" method is not implemented"});Zc("ERR_STREAM_PREMATURE_CLOSE","Premature close");Zc("ERR_STREAM_DESTROYED",function(t){return"Cannot call "+t+" after a stream was destroyed"});Zc("ERR_MULTIPLE_CALLBACK","Callback called multiple times");Zc("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable");Zc("ERR_STREAM_WRITE_AFTER_END","write after end");Zc("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError);Zc("ERR_UNKNOWN_ENCODING",function(t){return"Unknown encoding: "+t},TypeError);Zc("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event");_ke.exports.codes=Mke});var PV=L((sIr,Uke)=>{"use strict";var cFt=cg().codes.ERR_INVALID_OPT_VALUE;function uFt(t,e,r){return t.highWaterMark!=null?t.highWaterMark:e?t[r]:null}function fFt(t,e,r,s){var a=uFt(e,s,r);if(a!=null){if(!(isFinite(a)&&Math.floor(a)===a)||a<0){var n=s?r:"highWaterMark";throw new cFt(n,a)}return Math.floor(a)}return t.objectMode?16:16*1024}Uke.exports={getHighWaterMark:fFt}});var Hke=L((oIr,xV)=>{typeof Object.create=="function"?xV.exports=function(e,r){r&&(e.super_=r,e.prototype=Object.create(r.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:xV.exports=function(e,r){if(r){e.super_=r;var s=function(){};s.prototype=r.prototype,e.prototype=new s,e.prototype.constructor=e}}});var ug=L((aIr,QV)=>{try{if(kV=ye("util"),typeof kV.inherits!="function")throw"";QV.exports=kV.inherits}catch{QV.exports=Hke()}var kV});var qke=L((lIr,jke)=>{jke.exports=ye("util").deprecate});var FV=L((cIr,Jke)=>{"use strict";Jke.exports=Vi;function Wke(t){var e=this;this.next=null,this.entry=null,this.finish=function(){MFt(e,t)}}var Fw;Vi.WritableState=hb;var AFt={deprecate:qke()},Yke=vV(),vN=ye("buffer").Buffer,pFt=global.Uint8Array||function(){};function hFt(t){return vN.from(t)}function gFt(t){return vN.isBuffer(t)||t instanceof pFt}var RV=bV(),dFt=PV(),mFt=dFt.getHighWaterMark,fg=cg().codes,yFt=fg.ERR_INVALID_ARG_TYPE,EFt=fg.ERR_METHOD_NOT_IMPLEMENTED,IFt=fg.ERR_MULTIPLE_CALLBACK,CFt=fg.ERR_STREAM_CANNOT_PIPE,wFt=fg.ERR_STREAM_DESTROYED,BFt=fg.ERR_STREAM_NULL_VALUES,vFt=fg.ERR_STREAM_WRITE_AFTER_END,SFt=fg.ERR_UNKNOWN_ENCODING,Nw=RV.errorOrDestroy;ug()(Vi,Yke);function DFt(){}function hb(t,e,r){Fw=Fw||Vm(),t=t||{},typeof r!="boolean"&&(r=e instanceof Fw),this.objectMode=!!t.objectMode,r&&(this.objectMode=this.objectMode||!!t.writableObjectMode),this.highWaterMark=mFt(this,t,"writableHighWaterMark",r),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var s=t.decodeStrings===!1;this.decodeStrings=!s,this.defaultEncoding=t.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(a){RFt(e,a)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=t.emitClose!==!1,this.autoDestroy=!!t.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new Wke(this)}hb.prototype.getBuffer=function(){for(var e=this.bufferedRequest,r=[];e;)r.push(e),e=e.next;return r};(function(){try{Object.defineProperty(hb.prototype,"buffer",{get:AFt.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch{}})();var BN;typeof Symbol=="function"&&Symbol.hasInstance&&typeof Function.prototype[Symbol.hasInstance]=="function"?(BN=Function.prototype[Symbol.hasInstance],Object.defineProperty(Vi,Symbol.hasInstance,{value:function(e){return BN.call(this,e)?!0:this!==Vi?!1:e&&e._writableState instanceof hb}})):BN=function(e){return e instanceof this};function Vi(t){Fw=Fw||Vm();var e=this instanceof Fw;if(!e&&!BN.call(Vi,this))return new Vi(t);this._writableState=new hb(t,this,e),this.writable=!0,t&&(typeof t.write=="function"&&(this._write=t.write),typeof t.writev=="function"&&(this._writev=t.writev),typeof t.destroy=="function"&&(this._destroy=t.destroy),typeof t.final=="function"&&(this._final=t.final)),Yke.call(this)}Vi.prototype.pipe=function(){Nw(this,new CFt)};function bFt(t,e){var r=new vFt;Nw(t,r),process.nextTick(e,r)}function PFt(t,e,r,s){var a;return r===null?a=new BFt:typeof r!="string"&&!e.objectMode&&(a=new yFt("chunk",["string","Buffer"],r)),a?(Nw(t,a),process.nextTick(s,a),!1):!0}Vi.prototype.write=function(t,e,r){var s=this._writableState,a=!1,n=!s.objectMode&&gFt(t);return n&&!vN.isBuffer(t)&&(t=hFt(t)),typeof e=="function"&&(r=e,e=null),n?e="buffer":e||(e=s.defaultEncoding),typeof r!="function"&&(r=DFt),s.ending?bFt(this,r):(n||PFt(this,s,t,r))&&(s.pendingcb++,a=kFt(this,s,n,t,e,r)),a};Vi.prototype.cork=function(){this._writableState.corked++};Vi.prototype.uncork=function(){var t=this._writableState;t.corked&&(t.corked--,!t.writing&&!t.corked&&!t.bufferProcessing&&t.bufferedRequest&&Vke(this,t))};Vi.prototype.setDefaultEncoding=function(e){if(typeof e=="string"&&(e=e.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((e+"").toLowerCase())>-1))throw new SFt(e);return this._writableState.defaultEncoding=e,this};Object.defineProperty(Vi.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}});function xFt(t,e,r){return!t.objectMode&&t.decodeStrings!==!1&&typeof e=="string"&&(e=vN.from(e,r)),e}Object.defineProperty(Vi.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}});function kFt(t,e,r,s,a,n){if(!r){var c=xFt(e,s,a);s!==c&&(r=!0,a="buffer",s=c)}var f=e.objectMode?1:s.length;e.length+=f;var p=e.length{"use strict";var _Ft=Object.keys||function(t){var e=[];for(var r in t)e.push(r);return e};Zke.exports=yA;var zke=LV(),OV=FV();ug()(yA,zke);for(NV=_Ft(OV.prototype),SN=0;SN{var bN=ye("buffer"),uh=bN.Buffer;function Xke(t,e){for(var r in t)e[r]=t[r]}uh.from&&uh.alloc&&uh.allocUnsafe&&uh.allocUnsafeSlow?$ke.exports=bN:(Xke(bN,MV),MV.Buffer=Ow);function Ow(t,e,r){return uh(t,e,r)}Xke(uh,Ow);Ow.from=function(t,e,r){if(typeof t=="number")throw new TypeError("Argument must not be a number");return uh(t,e,r)};Ow.alloc=function(t,e,r){if(typeof t!="number")throw new TypeError("Argument must be a number");var s=uh(t);return e!==void 0?typeof r=="string"?s.fill(e,r):s.fill(e):s.fill(0),s};Ow.allocUnsafe=function(t){if(typeof t!="number")throw new TypeError("Argument must be a number");return uh(t)};Ow.allocUnsafeSlow=function(t){if(typeof t!="number")throw new TypeError("Argument must be a number");return bN.SlowBuffer(t)}});var HV=L(rQe=>{"use strict";var UV=eQe().Buffer,tQe=UV.isEncoding||function(t){switch(t=""+t,t&&t.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function jFt(t){if(!t)return"utf8";for(var e;;)switch(t){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return t;default:if(e)return;t=(""+t).toLowerCase(),e=!0}}function qFt(t){var e=jFt(t);if(typeof e!="string"&&(UV.isEncoding===tQe||!tQe(t)))throw new Error("Unknown encoding: "+t);return e||t}rQe.StringDecoder=gb;function gb(t){this.encoding=qFt(t);var e;switch(this.encoding){case"utf16le":this.text=JFt,this.end=zFt,e=4;break;case"utf8":this.fillLast=YFt,e=4;break;case"base64":this.text=ZFt,this.end=XFt,e=3;break;default:this.write=$Ft,this.end=eNt;return}this.lastNeed=0,this.lastTotal=0,this.lastChar=UV.allocUnsafe(e)}gb.prototype.write=function(t){if(t.length===0)return"";var e,r;if(this.lastNeed){if(e=this.fillLast(t),e===void 0)return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r>5===6?2:t>>4===14?3:t>>3===30?4:t>>6===2?-1:-2}function GFt(t,e,r){var s=e.length-1;if(s=0?(a>0&&(t.lastNeed=a-1),a):--s=0?(a>0&&(t.lastNeed=a-2),a):--s=0?(a>0&&(a===2?a=0:t.lastNeed=a-3),a):0))}function WFt(t,e,r){if((e[0]&192)!==128)return t.lastNeed=0,"\uFFFD";if(t.lastNeed>1&&e.length>1){if((e[1]&192)!==128)return t.lastNeed=1,"\uFFFD";if(t.lastNeed>2&&e.length>2&&(e[2]&192)!==128)return t.lastNeed=2,"\uFFFD"}}function YFt(t){var e=this.lastTotal-this.lastNeed,r=WFt(this,t,e);if(r!==void 0)return r;if(this.lastNeed<=t.length)return t.copy(this.lastChar,e,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);t.copy(this.lastChar,e,0,t.length),this.lastNeed-=t.length}function VFt(t,e){var r=GFt(this,t,e);if(!this.lastNeed)return t.toString("utf8",e);this.lastTotal=r;var s=t.length-(r-this.lastNeed);return t.copy(this.lastChar,0,s),t.toString("utf8",e,s)}function KFt(t){var e=t&&t.length?this.write(t):"";return this.lastNeed?e+"\uFFFD":e}function JFt(t,e){if((t.length-e)%2===0){var r=t.toString("utf16le",e);if(r){var s=r.charCodeAt(r.length-1);if(s>=55296&&s<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=t[t.length-2],this.lastChar[1]=t[t.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=t[t.length-1],t.toString("utf16le",e,t.length-1)}function zFt(t){var e=t&&t.length?this.write(t):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return e+this.lastChar.toString("utf16le",0,r)}return e}function ZFt(t,e){var r=(t.length-e)%3;return r===0?t.toString("base64",e):(this.lastNeed=3-r,this.lastTotal=3,r===1?this.lastChar[0]=t[t.length-1]:(this.lastChar[0]=t[t.length-2],this.lastChar[1]=t[t.length-1]),t.toString("base64",e,t.length-r))}function XFt(t){var e=t&&t.length?this.write(t):"";return this.lastNeed?e+this.lastChar.toString("base64",0,3-this.lastNeed):e}function $Ft(t){return t.toString(this.encoding)}function eNt(t){return t&&t.length?this.write(t):""}});var PN=L((AIr,sQe)=>{"use strict";var nQe=cg().codes.ERR_STREAM_PREMATURE_CLOSE;function tNt(t){var e=!1;return function(){if(!e){e=!0;for(var r=arguments.length,s=new Array(r),a=0;a{"use strict";var xN;function Ag(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}var iNt=PN(),pg=Symbol("lastResolve"),Km=Symbol("lastReject"),db=Symbol("error"),kN=Symbol("ended"),Jm=Symbol("lastPromise"),jV=Symbol("handlePromise"),zm=Symbol("stream");function hg(t,e){return{value:t,done:e}}function sNt(t){var e=t[pg];if(e!==null){var r=t[zm].read();r!==null&&(t[Jm]=null,t[pg]=null,t[Km]=null,e(hg(r,!1)))}}function oNt(t){process.nextTick(sNt,t)}function aNt(t,e){return function(r,s){t.then(function(){if(e[kN]){r(hg(void 0,!0));return}e[jV](r,s)},s)}}var lNt=Object.getPrototypeOf(function(){}),cNt=Object.setPrototypeOf((xN={get stream(){return this[zm]},next:function(){var e=this,r=this[db];if(r!==null)return Promise.reject(r);if(this[kN])return Promise.resolve(hg(void 0,!0));if(this[zm].destroyed)return new Promise(function(c,f){process.nextTick(function(){e[db]?f(e[db]):c(hg(void 0,!0))})});var s=this[Jm],a;if(s)a=new Promise(aNt(s,this));else{var n=this[zm].read();if(n!==null)return Promise.resolve(hg(n,!1));a=new Promise(this[jV])}return this[Jm]=a,a}},Ag(xN,Symbol.asyncIterator,function(){return this}),Ag(xN,"return",function(){var e=this;return new Promise(function(r,s){e[zm].destroy(null,function(a){if(a){s(a);return}r(hg(void 0,!0))})})}),xN),lNt),uNt=function(e){var r,s=Object.create(cNt,(r={},Ag(r,zm,{value:e,writable:!0}),Ag(r,pg,{value:null,writable:!0}),Ag(r,Km,{value:null,writable:!0}),Ag(r,db,{value:null,writable:!0}),Ag(r,kN,{value:e._readableState.endEmitted,writable:!0}),Ag(r,jV,{value:function(n,c){var f=s[zm].read();f?(s[Jm]=null,s[pg]=null,s[Km]=null,n(hg(f,!1))):(s[pg]=n,s[Km]=c)},writable:!0}),r));return s[Jm]=null,iNt(e,function(a){if(a&&a.code!=="ERR_STREAM_PREMATURE_CLOSE"){var n=s[Km];n!==null&&(s[Jm]=null,s[pg]=null,s[Km]=null,n(a)),s[db]=a;return}var c=s[pg];c!==null&&(s[Jm]=null,s[pg]=null,s[Km]=null,c(hg(void 0,!0))),s[kN]=!0}),e.on("readable",oNt.bind(null,s)),s};oQe.exports=uNt});var fQe=L((hIr,uQe)=>{"use strict";function lQe(t,e,r,s,a,n,c){try{var f=t[n](c),p=f.value}catch(h){r(h);return}f.done?e(p):Promise.resolve(p).then(s,a)}function fNt(t){return function(){var e=this,r=arguments;return new Promise(function(s,a){var n=t.apply(e,r);function c(p){lQe(n,s,a,c,f,"next",p)}function f(p){lQe(n,s,a,c,f,"throw",p)}c(void 0)})}}function cQe(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter(function(a){return Object.getOwnPropertyDescriptor(t,a).enumerable})),r.push.apply(r,s)}return r}function ANt(t){for(var e=1;e{"use strict";CQe.exports=Pn;var Lw;Pn.ReadableState=gQe;var gIr=ye("events").EventEmitter,hQe=function(e,r){return e.listeners(r).length},yb=vV(),QN=ye("buffer").Buffer,dNt=global.Uint8Array||function(){};function mNt(t){return QN.from(t)}function yNt(t){return QN.isBuffer(t)||t instanceof dNt}var qV=ye("util"),ln;qV&&qV.debuglog?ln=qV.debuglog("stream"):ln=function(){};var ENt=Fke(),zV=bV(),INt=PV(),CNt=INt.getHighWaterMark,TN=cg().codes,wNt=TN.ERR_INVALID_ARG_TYPE,BNt=TN.ERR_STREAM_PUSH_AFTER_EOF,vNt=TN.ERR_METHOD_NOT_IMPLEMENTED,SNt=TN.ERR_STREAM_UNSHIFT_AFTER_END_EVENT,Mw,GV,WV;ug()(Pn,yb);var mb=zV.errorOrDestroy,YV=["error","close","destroy","pause","resume"];function DNt(t,e,r){if(typeof t.prependListener=="function")return t.prependListener(e,r);!t._events||!t._events[e]?t.on(e,r):Array.isArray(t._events[e])?t._events[e].unshift(r):t._events[e]=[r,t._events[e]]}function gQe(t,e,r){Lw=Lw||Vm(),t=t||{},typeof r!="boolean"&&(r=e instanceof Lw),this.objectMode=!!t.objectMode,r&&(this.objectMode=this.objectMode||!!t.readableObjectMode),this.highWaterMark=CNt(this,t,"readableHighWaterMark",r),this.buffer=new ENt,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=t.emitClose!==!1,this.autoDestroy=!!t.autoDestroy,this.destroyed=!1,this.defaultEncoding=t.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,t.encoding&&(Mw||(Mw=HV().StringDecoder),this.decoder=new Mw(t.encoding),this.encoding=t.encoding)}function Pn(t){if(Lw=Lw||Vm(),!(this instanceof Pn))return new Pn(t);var e=this instanceof Lw;this._readableState=new gQe(t,this,e),this.readable=!0,t&&(typeof t.read=="function"&&(this._read=t.read),typeof t.destroy=="function"&&(this._destroy=t.destroy)),yb.call(this)}Object.defineProperty(Pn.prototype,"destroyed",{enumerable:!1,get:function(){return this._readableState===void 0?!1:this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}});Pn.prototype.destroy=zV.destroy;Pn.prototype._undestroy=zV.undestroy;Pn.prototype._destroy=function(t,e){e(t)};Pn.prototype.push=function(t,e){var r=this._readableState,s;return r.objectMode?s=!0:typeof t=="string"&&(e=e||r.defaultEncoding,e!==r.encoding&&(t=QN.from(t,e),e=""),s=!0),dQe(this,t,e,!1,s)};Pn.prototype.unshift=function(t){return dQe(this,t,null,!0,!1)};function dQe(t,e,r,s,a){ln("readableAddChunk",e);var n=t._readableState;if(e===null)n.reading=!1,xNt(t,n);else{var c;if(a||(c=bNt(n,e)),c)mb(t,c);else if(n.objectMode||e&&e.length>0)if(typeof e!="string"&&!n.objectMode&&Object.getPrototypeOf(e)!==QN.prototype&&(e=mNt(e)),s)n.endEmitted?mb(t,new SNt):VV(t,n,e,!0);else if(n.ended)mb(t,new BNt);else{if(n.destroyed)return!1;n.reading=!1,n.decoder&&!r?(e=n.decoder.write(e),n.objectMode||e.length!==0?VV(t,n,e,!1):JV(t,n)):VV(t,n,e,!1)}else s||(n.reading=!1,JV(t,n))}return!n.ended&&(n.length=AQe?t=AQe:(t--,t|=t>>>1,t|=t>>>2,t|=t>>>4,t|=t>>>8,t|=t>>>16,t++),t}function pQe(t,e){return t<=0||e.length===0&&e.ended?0:e.objectMode?1:t!==t?e.flowing&&e.length?e.buffer.head.data.length:e.length:(t>e.highWaterMark&&(e.highWaterMark=PNt(t)),t<=e.length?t:e.ended?e.length:(e.needReadable=!0,0))}Pn.prototype.read=function(t){ln("read",t),t=parseInt(t,10);var e=this._readableState,r=t;if(t!==0&&(e.emittedReadable=!1),t===0&&e.needReadable&&((e.highWaterMark!==0?e.length>=e.highWaterMark:e.length>0)||e.ended))return ln("read: emitReadable",e.length,e.ended),e.length===0&&e.ended?KV(this):RN(this),null;if(t=pQe(t,e),t===0&&e.ended)return e.length===0&&KV(this),null;var s=e.needReadable;ln("need readable",s),(e.length===0||e.length-t0?a=EQe(t,e):a=null,a===null?(e.needReadable=e.length<=e.highWaterMark,t=0):(e.length-=t,e.awaitDrain=0),e.length===0&&(e.ended||(e.needReadable=!0),r!==t&&e.ended&&KV(this)),a!==null&&this.emit("data",a),a};function xNt(t,e){if(ln("onEofChunk"),!e.ended){if(e.decoder){var r=e.decoder.end();r&&r.length&&(e.buffer.push(r),e.length+=e.objectMode?1:r.length)}e.ended=!0,e.sync?RN(t):(e.needReadable=!1,e.emittedReadable||(e.emittedReadable=!0,mQe(t)))}}function RN(t){var e=t._readableState;ln("emitReadable",e.needReadable,e.emittedReadable),e.needReadable=!1,e.emittedReadable||(ln("emitReadable",e.flowing),e.emittedReadable=!0,process.nextTick(mQe,t))}function mQe(t){var e=t._readableState;ln("emitReadable_",e.destroyed,e.length,e.ended),!e.destroyed&&(e.length||e.ended)&&(t.emit("readable"),e.emittedReadable=!1),e.needReadable=!e.flowing&&!e.ended&&e.length<=e.highWaterMark,ZV(t)}function JV(t,e){e.readingMore||(e.readingMore=!0,process.nextTick(kNt,t,e))}function kNt(t,e){for(;!e.reading&&!e.ended&&(e.length1&&IQe(s.pipes,t)!==-1)&&!h&&(ln("false write response, pause",s.awaitDrain),s.awaitDrain++),r.pause())}function S(N){ln("onerror",N),R(),t.removeListener("error",S),hQe(t,"error")===0&&mb(t,N)}DNt(t,"error",S);function P(){t.removeListener("finish",I),R()}t.once("close",P);function I(){ln("onfinish"),t.removeListener("close",P),R()}t.once("finish",I);function R(){ln("unpipe"),r.unpipe(t)}return t.emit("pipe",r),s.flowing||(ln("pipe resume"),r.resume()),t};function QNt(t){return function(){var r=t._readableState;ln("pipeOnDrain",r.awaitDrain),r.awaitDrain&&r.awaitDrain--,r.awaitDrain===0&&hQe(t,"data")&&(r.flowing=!0,ZV(t))}}Pn.prototype.unpipe=function(t){var e=this._readableState,r={hasUnpiped:!1};if(e.pipesCount===0)return this;if(e.pipesCount===1)return t&&t!==e.pipes?this:(t||(t=e.pipes),e.pipes=null,e.pipesCount=0,e.flowing=!1,t&&t.emit("unpipe",this,r),this);if(!t){var s=e.pipes,a=e.pipesCount;e.pipes=null,e.pipesCount=0,e.flowing=!1;for(var n=0;n0,s.flowing!==!1&&this.resume()):t==="readable"&&!s.endEmitted&&!s.readableListening&&(s.readableListening=s.needReadable=!0,s.flowing=!1,s.emittedReadable=!1,ln("on readable",s.length,s.reading),s.length?RN(this):s.reading||process.nextTick(TNt,this)),r};Pn.prototype.addListener=Pn.prototype.on;Pn.prototype.removeListener=function(t,e){var r=yb.prototype.removeListener.call(this,t,e);return t==="readable"&&process.nextTick(yQe,this),r};Pn.prototype.removeAllListeners=function(t){var e=yb.prototype.removeAllListeners.apply(this,arguments);return(t==="readable"||t===void 0)&&process.nextTick(yQe,this),e};function yQe(t){var e=t._readableState;e.readableListening=t.listenerCount("readable")>0,e.resumeScheduled&&!e.paused?e.flowing=!0:t.listenerCount("data")>0&&t.resume()}function TNt(t){ln("readable nexttick read 0"),t.read(0)}Pn.prototype.resume=function(){var t=this._readableState;return t.flowing||(ln("resume"),t.flowing=!t.readableListening,RNt(this,t)),t.paused=!1,this};function RNt(t,e){e.resumeScheduled||(e.resumeScheduled=!0,process.nextTick(FNt,t,e))}function FNt(t,e){ln("resume",e.reading),e.reading||t.read(0),e.resumeScheduled=!1,t.emit("resume"),ZV(t),e.flowing&&!e.reading&&t.read(0)}Pn.prototype.pause=function(){return ln("call pause flowing=%j",this._readableState.flowing),this._readableState.flowing!==!1&&(ln("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this};function ZV(t){var e=t._readableState;for(ln("flow",e.flowing);e.flowing&&t.read()!==null;);}Pn.prototype.wrap=function(t){var e=this,r=this._readableState,s=!1;t.on("end",function(){if(ln("wrapped end"),r.decoder&&!r.ended){var c=r.decoder.end();c&&c.length&&e.push(c)}e.push(null)}),t.on("data",function(c){if(ln("wrapped data"),r.decoder&&(c=r.decoder.write(c)),!(r.objectMode&&c==null)&&!(!r.objectMode&&(!c||!c.length))){var f=e.push(c);f||(s=!0,t.pause())}});for(var a in t)this[a]===void 0&&typeof t[a]=="function"&&(this[a]=function(f){return function(){return t[f].apply(t,arguments)}}(a));for(var n=0;n=e.length?(e.decoder?r=e.buffer.join(""):e.buffer.length===1?r=e.buffer.first():r=e.buffer.concat(e.length),e.buffer.clear()):r=e.buffer.consume(t,e.decoder),r}function KV(t){var e=t._readableState;ln("endReadable",e.endEmitted),e.endEmitted||(e.ended=!0,process.nextTick(NNt,e,t))}function NNt(t,e){if(ln("endReadableNT",t.endEmitted,t.length),!t.endEmitted&&t.length===0&&(t.endEmitted=!0,e.readable=!1,e.emit("end"),t.autoDestroy)){var r=e._writableState;(!r||r.autoDestroy&&r.finished)&&e.destroy()}}typeof Symbol=="function"&&(Pn.from=function(t,e){return WV===void 0&&(WV=fQe()),WV(Pn,t,e)});function IQe(t,e){for(var r=0,s=t.length;r{"use strict";BQe.exports=fh;var FN=cg().codes,ONt=FN.ERR_METHOD_NOT_IMPLEMENTED,LNt=FN.ERR_MULTIPLE_CALLBACK,MNt=FN.ERR_TRANSFORM_ALREADY_TRANSFORMING,_Nt=FN.ERR_TRANSFORM_WITH_LENGTH_0,NN=Vm();ug()(fh,NN);function UNt(t,e){var r=this._transformState;r.transforming=!1;var s=r.writecb;if(s===null)return this.emit("error",new LNt);r.writechunk=null,r.writecb=null,e!=null&&this.push(e),s(t);var a=this._readableState;a.reading=!1,(a.needReadable||a.length{"use strict";SQe.exports=Eb;var vQe=XV();ug()(Eb,vQe);function Eb(t){if(!(this instanceof Eb))return new Eb(t);vQe.call(this,t)}Eb.prototype._transform=function(t,e,r){r(null,t)}});var QQe=L((EIr,kQe)=>{"use strict";var $V;function jNt(t){var e=!1;return function(){e||(e=!0,t.apply(void 0,arguments))}}var xQe=cg().codes,qNt=xQe.ERR_MISSING_ARGS,GNt=xQe.ERR_STREAM_DESTROYED;function bQe(t){if(t)throw t}function WNt(t){return t.setHeader&&typeof t.abort=="function"}function YNt(t,e,r,s){s=jNt(s);var a=!1;t.on("close",function(){a=!0}),$V===void 0&&($V=PN()),$V(t,{readable:e,writable:r},function(c){if(c)return s(c);a=!0,s()});var n=!1;return function(c){if(!a&&!n){if(n=!0,WNt(t))return t.abort();if(typeof t.destroy=="function")return t.destroy();s(c||new GNt("pipe"))}}}function PQe(t){t()}function VNt(t,e){return t.pipe(e)}function KNt(t){return!t.length||typeof t[t.length-1]!="function"?bQe:t.pop()}function JNt(){for(var t=arguments.length,e=new Array(t),r=0;r0;return YNt(c,p,h,function(E){a||(a=E),E&&n.forEach(PQe),!p&&(n.forEach(PQe),s(a))})});return e.reduce(VNt)}kQe.exports=JNt});var _w=L((Xc,Cb)=>{var Ib=ye("stream");process.env.READABLE_STREAM==="disable"&&Ib?(Cb.exports=Ib.Readable,Object.assign(Cb.exports,Ib),Cb.exports.Stream=Ib):(Xc=Cb.exports=LV(),Xc.Stream=Ib||Xc,Xc.Readable=Xc,Xc.Writable=FV(),Xc.Duplex=Vm(),Xc.Transform=XV(),Xc.PassThrough=DQe(),Xc.finished=PN(),Xc.pipeline=QQe())});var FQe=L((IIr,RQe)=>{"use strict";var{Buffer:uf}=ye("buffer"),TQe=Symbol.for("BufferList");function Ci(t){if(!(this instanceof Ci))return new Ci(t);Ci._init.call(this,t)}Ci._init=function(e){Object.defineProperty(this,TQe,{value:!0}),this._bufs=[],this.length=0,e&&this.append(e)};Ci.prototype._new=function(e){return new Ci(e)};Ci.prototype._offset=function(e){if(e===0)return[0,0];let r=0;for(let s=0;sthis.length||e<0)return;let r=this._offset(e);return this._bufs[r[0]][r[1]]};Ci.prototype.slice=function(e,r){return typeof e=="number"&&e<0&&(e+=this.length),typeof r=="number"&&r<0&&(r+=this.length),this.copy(null,0,e,r)};Ci.prototype.copy=function(e,r,s,a){if((typeof s!="number"||s<0)&&(s=0),(typeof a!="number"||a>this.length)&&(a=this.length),s>=this.length||a<=0)return e||uf.alloc(0);let n=!!e,c=this._offset(s),f=a-s,p=f,h=n&&r||0,E=c[1];if(s===0&&a===this.length){if(!n)return this._bufs.length===1?this._bufs[0]:uf.concat(this._bufs,this.length);for(let C=0;CS)this._bufs[C].copy(e,h,E),h+=S;else{this._bufs[C].copy(e,h,E,E+p),h+=S;break}p-=S,E&&(E=0)}return e.length>h?e.slice(0,h):e};Ci.prototype.shallowSlice=function(e,r){if(e=e||0,r=typeof r!="number"?this.length:r,e<0&&(e+=this.length),r<0&&(r+=this.length),e===r)return this._new();let s=this._offset(e),a=this._offset(r),n=this._bufs.slice(s[0],a[0]+1);return a[1]===0?n.pop():n[n.length-1]=n[n.length-1].slice(0,a[1]),s[1]!==0&&(n[0]=n[0].slice(s[1])),this._new(n)};Ci.prototype.toString=function(e,r,s){return this.slice(r,s).toString(e)};Ci.prototype.consume=function(e){if(e=Math.trunc(e),Number.isNaN(e)||e<=0)return this;for(;this._bufs.length;)if(e>=this._bufs[0].length)e-=this._bufs[0].length,this.length-=this._bufs[0].length,this._bufs.shift();else{this._bufs[0]=this._bufs[0].slice(e),this.length-=e;break}return this};Ci.prototype.duplicate=function(){let e=this._new();for(let r=0;rthis.length?this.length:e;let s=this._offset(e),a=s[0],n=s[1];for(;a=t.length){let p=c.indexOf(t,n);if(p!==-1)return this._reverseOffset([a,p]);n=c.length-t.length+1}else{let p=this._reverseOffset([a,n]);if(this._match(p,t))return p;n++}n=0}return-1};Ci.prototype._match=function(t,e){if(this.length-t{"use strict";var e7=_w().Duplex,zNt=ug(),wb=FQe();function na(t){if(!(this instanceof na))return new na(t);if(typeof t=="function"){this._callback=t;let e=function(s){this._callback&&(this._callback(s),this._callback=null)}.bind(this);this.on("pipe",function(s){s.on("error",e)}),this.on("unpipe",function(s){s.removeListener("error",e)}),t=null}wb._init.call(this,t),e7.call(this)}zNt(na,e7);Object.assign(na.prototype,wb.prototype);na.prototype._new=function(e){return new na(e)};na.prototype._write=function(e,r,s){this._appendBuffer(e),typeof s=="function"&&s()};na.prototype._read=function(e){if(!this.length)return this.push(null);e=Math.min(e,this.length),this.push(this.slice(0,e)),this.consume(e)};na.prototype.end=function(e){e7.prototype.end.call(this,e),this._callback&&(this._callback(null,this.slice()),this._callback=null)};na.prototype._destroy=function(e,r){this._bufs.length=0,this.length=0,r(e)};na.prototype._isBufferList=function(e){return e instanceof na||e instanceof wb||na.isBufferList(e)};na.isBufferList=wb.isBufferList;ON.exports=na;ON.exports.BufferListStream=na;ON.exports.BufferList=wb});var n7=L(Hw=>{var ZNt=Buffer.alloc,XNt="0000000000000000000",$Nt="7777777777777777777",OQe=48,LQe=Buffer.from("ustar\0","binary"),eOt=Buffer.from("00","binary"),tOt=Buffer.from("ustar ","binary"),rOt=Buffer.from(" \0","binary"),nOt=parseInt("7777",8),Bb=257,r7=263,iOt=function(t,e,r){return typeof t!="number"?r:(t=~~t,t>=e?e:t>=0||(t+=e,t>=0)?t:0)},sOt=function(t){switch(t){case 0:return"file";case 1:return"link";case 2:return"symlink";case 3:return"character-device";case 4:return"block-device";case 5:return"directory";case 6:return"fifo";case 7:return"contiguous-file";case 72:return"pax-header";case 55:return"pax-global-header";case 27:return"gnu-long-link-path";case 28:case 30:return"gnu-long-path"}return null},oOt=function(t){switch(t){case"file":return 0;case"link":return 1;case"symlink":return 2;case"character-device":return 3;case"block-device":return 4;case"directory":return 5;case"fifo":return 6;case"contiguous-file":return 7;case"pax-header":return 72}return 0},MQe=function(t,e,r,s){for(;re?$Nt.slice(0,e)+" ":XNt.slice(0,e-t.length)+t+" "};function aOt(t){var e;if(t[0]===128)e=!0;else if(t[0]===255)e=!1;else return null;for(var r=[],s=t.length-1;s>0;s--){var a=t[s];e?r.push(a):r.push(255-a)}var n=0,c=r.length;for(s=0;s=Math.pow(10,r)&&r++,e+r+t};Hw.decodeLongPath=function(t,e){return Uw(t,0,t.length,e)};Hw.encodePax=function(t){var e="";t.name&&(e+=t7(" path="+t.name+` +`)),t.linkname&&(e+=t7(" linkpath="+t.linkname+` +`));var r=t.pax;if(r)for(var s in r)e+=t7(" "+s+"="+r[s]+` +`);return Buffer.from(e)};Hw.decodePax=function(t){for(var e={};t.length;){for(var r=0;r100;){var a=r.indexOf("/");if(a===-1)return null;s+=s?"/"+r.slice(0,a):r.slice(0,a),r=r.slice(a+1)}return Buffer.byteLength(r)>100||Buffer.byteLength(s)>155||t.linkname&&Buffer.byteLength(t.linkname)>100?null:(e.write(r),e.write(gg(t.mode&nOt,6),100),e.write(gg(t.uid,6),108),e.write(gg(t.gid,6),116),e.write(gg(t.size,11),124),e.write(gg(t.mtime.getTime()/1e3|0,11),136),e[156]=OQe+oOt(t.type),t.linkname&&e.write(t.linkname,157),LQe.copy(e,Bb),eOt.copy(e,r7),t.uname&&e.write(t.uname,265),t.gname&&e.write(t.gname,297),e.write(gg(t.devmajor||0,6),329),e.write(gg(t.devminor||0,6),337),s&&e.write(s,345),e.write(gg(_Qe(e),6),148),e)};Hw.decode=function(t,e,r){var s=t[156]===0?0:t[156]-OQe,a=Uw(t,0,100,e),n=dg(t,100,8),c=dg(t,108,8),f=dg(t,116,8),p=dg(t,124,12),h=dg(t,136,12),E=sOt(s),C=t[157]===0?null:Uw(t,157,100,e),S=Uw(t,265,32),P=Uw(t,297,32),I=dg(t,329,8),R=dg(t,337,8),N=_Qe(t);if(N===8*32)return null;if(N!==dg(t,148,8))throw new Error("Invalid tar header. Maybe the tar is corrupted or it needs to be gunzipped?");if(LQe.compare(t,Bb,Bb+6)===0)t[345]&&(a=Uw(t,345,155,e)+"/"+a);else if(!(tOt.compare(t,Bb,Bb+6)===0&&rOt.compare(t,r7,r7+2)===0)){if(!r)throw new Error("Invalid tar header: unknown format.")}return s===0&&a&&a[a.length-1]==="/"&&(s=5),{name:a,mode:n,uid:c,gid:f,size:p,mtime:new Date(1e3*h),type:E,linkname:C,uname:S,gname:P,devmajor:I,devminor:R}}});var YQe=L((BIr,WQe)=>{var HQe=ye("util"),lOt=NQe(),vb=n7(),jQe=_w().Writable,qQe=_w().PassThrough,GQe=function(){},UQe=function(t){return t&=511,t&&512-t},cOt=function(t,e){var r=new LN(t,e);return r.end(),r},uOt=function(t,e){return e.path&&(t.name=e.path),e.linkpath&&(t.linkname=e.linkpath),e.size&&(t.size=parseInt(e.size,10)),t.pax=e,t},LN=function(t,e){this._parent=t,this.offset=e,qQe.call(this,{autoDestroy:!1})};HQe.inherits(LN,qQe);LN.prototype.destroy=function(t){this._parent.destroy(t)};var Ah=function(t){if(!(this instanceof Ah))return new Ah(t);jQe.call(this,t),t=t||{},this._offset=0,this._buffer=lOt(),this._missing=0,this._partial=!1,this._onparse=GQe,this._header=null,this._stream=null,this._overflow=null,this._cb=null,this._locked=!1,this._destroyed=!1,this._pax=null,this._paxGlobal=null,this._gnuLongPath=null,this._gnuLongLinkPath=null;var e=this,r=e._buffer,s=function(){e._continue()},a=function(S){if(e._locked=!1,S)return e.destroy(S);e._stream||s()},n=function(){e._stream=null;var S=UQe(e._header.size);S?e._parse(S,c):e._parse(512,C),e._locked||s()},c=function(){e._buffer.consume(UQe(e._header.size)),e._parse(512,C),s()},f=function(){var S=e._header.size;e._paxGlobal=vb.decodePax(r.slice(0,S)),r.consume(S),n()},p=function(){var S=e._header.size;e._pax=vb.decodePax(r.slice(0,S)),e._paxGlobal&&(e._pax=Object.assign({},e._paxGlobal,e._pax)),r.consume(S),n()},h=function(){var S=e._header.size;this._gnuLongPath=vb.decodeLongPath(r.slice(0,S),t.filenameEncoding),r.consume(S),n()},E=function(){var S=e._header.size;this._gnuLongLinkPath=vb.decodeLongPath(r.slice(0,S),t.filenameEncoding),r.consume(S),n()},C=function(){var S=e._offset,P;try{P=e._header=vb.decode(r.slice(0,512),t.filenameEncoding,t.allowUnknownFormat)}catch(I){e.emit("error",I)}if(r.consume(512),!P){e._parse(512,C),s();return}if(P.type==="gnu-long-path"){e._parse(P.size,h),s();return}if(P.type==="gnu-long-link-path"){e._parse(P.size,E),s();return}if(P.type==="pax-global-header"){e._parse(P.size,f),s();return}if(P.type==="pax-header"){e._parse(P.size,p),s();return}if(e._gnuLongPath&&(P.name=e._gnuLongPath,e._gnuLongPath=null),e._gnuLongLinkPath&&(P.linkname=e._gnuLongLinkPath,e._gnuLongLinkPath=null),e._pax&&(e._header=P=uOt(P,e._pax),e._pax=null),e._locked=!0,!P.size||P.type==="directory"){e._parse(512,C),e.emit("entry",P,cOt(e,S),a);return}e._stream=new LN(e,S),e.emit("entry",P,e._stream,a),e._parse(P.size,n),s()};this._onheader=C,this._parse(512,C)};HQe.inherits(Ah,jQe);Ah.prototype.destroy=function(t){this._destroyed||(this._destroyed=!0,t&&this.emit("error",t),this.emit("close"),this._stream&&this._stream.emit("close"))};Ah.prototype._parse=function(t,e){this._destroyed||(this._offset+=t,this._missing=t,e===this._onheader&&(this._partial=!1),this._onparse=e)};Ah.prototype._continue=function(){if(!this._destroyed){var t=this._cb;this._cb=GQe,this._overflow?this._write(this._overflow,void 0,t):t()}};Ah.prototype._write=function(t,e,r){if(!this._destroyed){var s=this._stream,a=this._buffer,n=this._missing;if(t.length&&(this._partial=!0),t.lengthn&&(c=t.slice(n),t=t.slice(0,n)),s?s.end(t):a.append(t),this._overflow=c,this._onparse()}};Ah.prototype._final=function(t){if(this._partial)return this.destroy(new Error("Unexpected end of data"));t()};WQe.exports=Ah});var KQe=L((vIr,VQe)=>{VQe.exports=ye("fs").constants||ye("constants")});var $Qe=L((SIr,XQe)=>{var jw=KQe(),JQe=kH(),_N=ug(),fOt=Buffer.alloc,zQe=_w().Readable,qw=_w().Writable,AOt=ye("string_decoder").StringDecoder,MN=n7(),pOt=parseInt("755",8),hOt=parseInt("644",8),ZQe=fOt(1024),s7=function(){},i7=function(t,e){e&=511,e&&t.push(ZQe.slice(0,512-e))};function gOt(t){switch(t&jw.S_IFMT){case jw.S_IFBLK:return"block-device";case jw.S_IFCHR:return"character-device";case jw.S_IFDIR:return"directory";case jw.S_IFIFO:return"fifo";case jw.S_IFLNK:return"symlink"}return"file"}var UN=function(t){qw.call(this),this.written=0,this._to=t,this._destroyed=!1};_N(UN,qw);UN.prototype._write=function(t,e,r){if(this.written+=t.length,this._to.push(t))return r();this._to._drain=r};UN.prototype.destroy=function(){this._destroyed||(this._destroyed=!0,this.emit("close"))};var HN=function(){qw.call(this),this.linkname="",this._decoder=new AOt("utf-8"),this._destroyed=!1};_N(HN,qw);HN.prototype._write=function(t,e,r){this.linkname+=this._decoder.write(t),r()};HN.prototype.destroy=function(){this._destroyed||(this._destroyed=!0,this.emit("close"))};var Sb=function(){qw.call(this),this._destroyed=!1};_N(Sb,qw);Sb.prototype._write=function(t,e,r){r(new Error("No body allowed for this entry"))};Sb.prototype.destroy=function(){this._destroyed||(this._destroyed=!0,this.emit("close"))};var EA=function(t){if(!(this instanceof EA))return new EA(t);zQe.call(this,t),this._drain=s7,this._finalized=!1,this._finalizing=!1,this._destroyed=!1,this._stream=null};_N(EA,zQe);EA.prototype.entry=function(t,e,r){if(this._stream)throw new Error("already piping an entry");if(!(this._finalized||this._destroyed)){typeof e=="function"&&(r=e,e=null),r||(r=s7);var s=this;if((!t.size||t.type==="symlink")&&(t.size=0),t.type||(t.type=gOt(t.mode)),t.mode||(t.mode=t.type==="directory"?pOt:hOt),t.uid||(t.uid=0),t.gid||(t.gid=0),t.mtime||(t.mtime=new Date),typeof e=="string"&&(e=Buffer.from(e)),Buffer.isBuffer(e)){t.size=e.length,this._encode(t);var a=this.push(e);return i7(s,t.size),a?process.nextTick(r):this._drain=r,new Sb}if(t.type==="symlink"&&!t.linkname){var n=new HN;return JQe(n,function(f){if(f)return s.destroy(),r(f);t.linkname=n.linkname,s._encode(t),r()}),n}if(this._encode(t),t.type!=="file"&&t.type!=="contiguous-file")return process.nextTick(r),new Sb;var c=new UN(this);return this._stream=c,JQe(c,function(f){if(s._stream=null,f)return s.destroy(),r(f);if(c.written!==t.size)return s.destroy(),r(new Error("size mismatch"));i7(s,t.size),s._finalizing&&s.finalize(),r()}),c}};EA.prototype.finalize=function(){if(this._stream){this._finalizing=!0;return}this._finalized||(this._finalized=!0,this.push(ZQe),this.push(null))};EA.prototype.destroy=function(t){this._destroyed||(this._destroyed=!0,t&&this.emit("error",t),this.emit("close"),this._stream&&this._stream.destroy&&this._stream.destroy())};EA.prototype._encode=function(t){if(!t.pax){var e=MN.encode(t);if(e){this.push(e);return}}this._encodePax(t)};EA.prototype._encodePax=function(t){var e=MN.encodePax({name:t.name,linkname:t.linkname,pax:t.pax}),r={name:"PaxHeader",mode:t.mode,uid:t.uid,gid:t.gid,size:e.length,mtime:t.mtime,type:"pax-header",linkname:t.linkname&&"PaxHeader",uname:t.uname,gname:t.gname,devmajor:t.devmajor,devminor:t.devminor};this.push(MN.encode(r)),this.push(e),i7(this,e.length),r.size=t.size,r.type=t.type,this.push(MN.encode(r))};EA.prototype._read=function(t){var e=this._drain;this._drain=s7,e()};XQe.exports=EA});var eTe=L(o7=>{o7.extract=YQe();o7.pack=$Qe()});var pTe=L(Ra=>{"use strict";var POt=Ra&&Ra.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(Ra,"__esModule",{value:!0});Ra.Minipass=Ra.isWritable=Ra.isReadable=Ra.isStream=void 0;var lTe=typeof process=="object"&&process?process:{stdout:null,stderr:null},y7=ye("node:events"),ATe=POt(ye("node:stream")),xOt=ye("node:string_decoder"),kOt=t=>!!t&&typeof t=="object"&&(t instanceof zN||t instanceof ATe.default||(0,Ra.isReadable)(t)||(0,Ra.isWritable)(t));Ra.isStream=kOt;var QOt=t=>!!t&&typeof t=="object"&&t instanceof y7.EventEmitter&&typeof t.pipe=="function"&&t.pipe!==ATe.default.Writable.prototype.pipe;Ra.isReadable=QOt;var TOt=t=>!!t&&typeof t=="object"&&t instanceof y7.EventEmitter&&typeof t.write=="function"&&typeof t.end=="function";Ra.isWritable=TOt;var ph=Symbol("EOF"),hh=Symbol("maybeEmitEnd"),mg=Symbol("emittedEnd"),GN=Symbol("emittingEnd"),Db=Symbol("emittedError"),WN=Symbol("closed"),cTe=Symbol("read"),YN=Symbol("flush"),uTe=Symbol("flushChunk"),ff=Symbol("encoding"),Ww=Symbol("decoder"),zs=Symbol("flowing"),bb=Symbol("paused"),Yw=Symbol("resume"),Zs=Symbol("buffer"),Ta=Symbol("pipes"),Xs=Symbol("bufferLength"),A7=Symbol("bufferPush"),VN=Symbol("bufferShift"),ia=Symbol("objectMode"),ts=Symbol("destroyed"),p7=Symbol("error"),h7=Symbol("emitData"),fTe=Symbol("emitEnd"),g7=Symbol("emitEnd2"),CA=Symbol("async"),d7=Symbol("abort"),KN=Symbol("aborted"),Pb=Symbol("signal"),Zm=Symbol("dataListeners"),nc=Symbol("discarded"),xb=t=>Promise.resolve().then(t),ROt=t=>t(),FOt=t=>t==="end"||t==="finish"||t==="prefinish",NOt=t=>t instanceof ArrayBuffer||!!t&&typeof t=="object"&&t.constructor&&t.constructor.name==="ArrayBuffer"&&t.byteLength>=0,OOt=t=>!Buffer.isBuffer(t)&&ArrayBuffer.isView(t),JN=class{src;dest;opts;ondrain;constructor(e,r,s){this.src=e,this.dest=r,this.opts=s,this.ondrain=()=>e[Yw](),this.dest.on("drain",this.ondrain)}unpipe(){this.dest.removeListener("drain",this.ondrain)}proxyErrors(e){}end(){this.unpipe(),this.opts.end&&this.dest.end()}},m7=class extends JN{unpipe(){this.src.removeListener("error",this.proxyErrors),super.unpipe()}constructor(e,r,s){super(e,r,s),this.proxyErrors=a=>r.emit("error",a),e.on("error",this.proxyErrors)}},LOt=t=>!!t.objectMode,MOt=t=>!t.objectMode&&!!t.encoding&&t.encoding!=="buffer",zN=class extends y7.EventEmitter{[zs]=!1;[bb]=!1;[Ta]=[];[Zs]=[];[ia];[ff];[CA];[Ww];[ph]=!1;[mg]=!1;[GN]=!1;[WN]=!1;[Db]=null;[Xs]=0;[ts]=!1;[Pb];[KN]=!1;[Zm]=0;[nc]=!1;writable=!0;readable=!0;constructor(...e){let r=e[0]||{};if(super(),r.objectMode&&typeof r.encoding=="string")throw new TypeError("Encoding and objectMode may not be used together");LOt(r)?(this[ia]=!0,this[ff]=null):MOt(r)?(this[ff]=r.encoding,this[ia]=!1):(this[ia]=!1,this[ff]=null),this[CA]=!!r.async,this[Ww]=this[ff]?new xOt.StringDecoder(this[ff]):null,r&&r.debugExposeBuffer===!0&&Object.defineProperty(this,"buffer",{get:()=>this[Zs]}),r&&r.debugExposePipes===!0&&Object.defineProperty(this,"pipes",{get:()=>this[Ta]});let{signal:s}=r;s&&(this[Pb]=s,s.aborted?this[d7]():s.addEventListener("abort",()=>this[d7]()))}get bufferLength(){return this[Xs]}get encoding(){return this[ff]}set encoding(e){throw new Error("Encoding must be set at instantiation time")}setEncoding(e){throw new Error("Encoding must be set at instantiation time")}get objectMode(){return this[ia]}set objectMode(e){throw new Error("objectMode must be set at instantiation time")}get async(){return this[CA]}set async(e){this[CA]=this[CA]||!!e}[d7](){this[KN]=!0,this.emit("abort",this[Pb]?.reason),this.destroy(this[Pb]?.reason)}get aborted(){return this[KN]}set aborted(e){}write(e,r,s){if(this[KN])return!1;if(this[ph])throw new Error("write after end");if(this[ts])return this.emit("error",Object.assign(new Error("Cannot call write after a stream was destroyed"),{code:"ERR_STREAM_DESTROYED"})),!0;typeof r=="function"&&(s=r,r="utf8"),r||(r="utf8");let a=this[CA]?xb:ROt;if(!this[ia]&&!Buffer.isBuffer(e)){if(OOt(e))e=Buffer.from(e.buffer,e.byteOffset,e.byteLength);else if(NOt(e))e=Buffer.from(e);else if(typeof e!="string")throw new Error("Non-contiguous data written to non-objectMode stream")}return this[ia]?(this[zs]&&this[Xs]!==0&&this[YN](!0),this[zs]?this.emit("data",e):this[A7](e),this[Xs]!==0&&this.emit("readable"),s&&a(s),this[zs]):e.length?(typeof e=="string"&&!(r===this[ff]&&!this[Ww]?.lastNeed)&&(e=Buffer.from(e,r)),Buffer.isBuffer(e)&&this[ff]&&(e=this[Ww].write(e)),this[zs]&&this[Xs]!==0&&this[YN](!0),this[zs]?this.emit("data",e):this[A7](e),this[Xs]!==0&&this.emit("readable"),s&&a(s),this[zs]):(this[Xs]!==0&&this.emit("readable"),s&&a(s),this[zs])}read(e){if(this[ts])return null;if(this[nc]=!1,this[Xs]===0||e===0||e&&e>this[Xs])return this[hh](),null;this[ia]&&(e=null),this[Zs].length>1&&!this[ia]&&(this[Zs]=[this[ff]?this[Zs].join(""):Buffer.concat(this[Zs],this[Xs])]);let r=this[cTe](e||null,this[Zs][0]);return this[hh](),r}[cTe](e,r){if(this[ia])this[VN]();else{let s=r;e===s.length||e===null?this[VN]():typeof s=="string"?(this[Zs][0]=s.slice(e),r=s.slice(0,e),this[Xs]-=e):(this[Zs][0]=s.subarray(e),r=s.subarray(0,e),this[Xs]-=e)}return this.emit("data",r),!this[Zs].length&&!this[ph]&&this.emit("drain"),r}end(e,r,s){return typeof e=="function"&&(s=e,e=void 0),typeof r=="function"&&(s=r,r="utf8"),e!==void 0&&this.write(e,r),s&&this.once("end",s),this[ph]=!0,this.writable=!1,(this[zs]||!this[bb])&&this[hh](),this}[Yw](){this[ts]||(!this[Zm]&&!this[Ta].length&&(this[nc]=!0),this[bb]=!1,this[zs]=!0,this.emit("resume"),this[Zs].length?this[YN]():this[ph]?this[hh]():this.emit("drain"))}resume(){return this[Yw]()}pause(){this[zs]=!1,this[bb]=!0,this[nc]=!1}get destroyed(){return this[ts]}get flowing(){return this[zs]}get paused(){return this[bb]}[A7](e){this[ia]?this[Xs]+=1:this[Xs]+=e.length,this[Zs].push(e)}[VN](){return this[ia]?this[Xs]-=1:this[Xs]-=this[Zs][0].length,this[Zs].shift()}[YN](e=!1){do;while(this[uTe](this[VN]())&&this[Zs].length);!e&&!this[Zs].length&&!this[ph]&&this.emit("drain")}[uTe](e){return this.emit("data",e),this[zs]}pipe(e,r){if(this[ts])return e;this[nc]=!1;let s=this[mg];return r=r||{},e===lTe.stdout||e===lTe.stderr?r.end=!1:r.end=r.end!==!1,r.proxyErrors=!!r.proxyErrors,s?r.end&&e.end():(this[Ta].push(r.proxyErrors?new m7(this,e,r):new JN(this,e,r)),this[CA]?xb(()=>this[Yw]()):this[Yw]()),e}unpipe(e){let r=this[Ta].find(s=>s.dest===e);r&&(this[Ta].length===1?(this[zs]&&this[Zm]===0&&(this[zs]=!1),this[Ta]=[]):this[Ta].splice(this[Ta].indexOf(r),1),r.unpipe())}addListener(e,r){return this.on(e,r)}on(e,r){let s=super.on(e,r);if(e==="data")this[nc]=!1,this[Zm]++,!this[Ta].length&&!this[zs]&&this[Yw]();else if(e==="readable"&&this[Xs]!==0)super.emit("readable");else if(FOt(e)&&this[mg])super.emit(e),this.removeAllListeners(e);else if(e==="error"&&this[Db]){let a=r;this[CA]?xb(()=>a.call(this,this[Db])):a.call(this,this[Db])}return s}removeListener(e,r){return this.off(e,r)}off(e,r){let s=super.off(e,r);return e==="data"&&(this[Zm]=this.listeners("data").length,this[Zm]===0&&!this[nc]&&!this[Ta].length&&(this[zs]=!1)),s}removeAllListeners(e){let r=super.removeAllListeners(e);return(e==="data"||e===void 0)&&(this[Zm]=0,!this[nc]&&!this[Ta].length&&(this[zs]=!1)),r}get emittedEnd(){return this[mg]}[hh](){!this[GN]&&!this[mg]&&!this[ts]&&this[Zs].length===0&&this[ph]&&(this[GN]=!0,this.emit("end"),this.emit("prefinish"),this.emit("finish"),this[WN]&&this.emit("close"),this[GN]=!1)}emit(e,...r){let s=r[0];if(e!=="error"&&e!=="close"&&e!==ts&&this[ts])return!1;if(e==="data")return!this[ia]&&!s?!1:this[CA]?(xb(()=>this[h7](s)),!0):this[h7](s);if(e==="end")return this[fTe]();if(e==="close"){if(this[WN]=!0,!this[mg]&&!this[ts])return!1;let n=super.emit("close");return this.removeAllListeners("close"),n}else if(e==="error"){this[Db]=s,super.emit(p7,s);let n=!this[Pb]||this.listeners("error").length?super.emit("error",s):!1;return this[hh](),n}else if(e==="resume"){let n=super.emit("resume");return this[hh](),n}else if(e==="finish"||e==="prefinish"){let n=super.emit(e);return this.removeAllListeners(e),n}let a=super.emit(e,...r);return this[hh](),a}[h7](e){for(let s of this[Ta])s.dest.write(e)===!1&&this.pause();let r=this[nc]?!1:super.emit("data",e);return this[hh](),r}[fTe](){return this[mg]?!1:(this[mg]=!0,this.readable=!1,this[CA]?(xb(()=>this[g7]()),!0):this[g7]())}[g7](){if(this[Ww]){let r=this[Ww].end();if(r){for(let s of this[Ta])s.dest.write(r);this[nc]||super.emit("data",r)}}for(let r of this[Ta])r.end();let e=super.emit("end");return this.removeAllListeners("end"),e}async collect(){let e=Object.assign([],{dataLength:0});this[ia]||(e.dataLength=0);let r=this.promise();return this.on("data",s=>{e.push(s),this[ia]||(e.dataLength+=s.length)}),await r,e}async concat(){if(this[ia])throw new Error("cannot concat in objectMode");let e=await this.collect();return this[ff]?e.join(""):Buffer.concat(e,e.dataLength)}async promise(){return new Promise((e,r)=>{this.on(ts,()=>r(new Error("stream destroyed"))),this.on("error",s=>r(s)),this.on("end",()=>e())})}[Symbol.asyncIterator](){this[nc]=!1;let e=!1,r=async()=>(this.pause(),e=!0,{value:void 0,done:!0});return{next:()=>{if(e)return r();let a=this.read();if(a!==null)return Promise.resolve({done:!1,value:a});if(this[ph])return r();let n,c,f=C=>{this.off("data",p),this.off("end",h),this.off(ts,E),r(),c(C)},p=C=>{this.off("error",f),this.off("end",h),this.off(ts,E),this.pause(),n({value:C,done:!!this[ph]})},h=()=>{this.off("error",f),this.off("data",p),this.off(ts,E),r(),n({done:!0,value:void 0})},E=()=>f(new Error("stream destroyed"));return new Promise((C,S)=>{c=S,n=C,this.once(ts,E),this.once("error",f),this.once("end",h),this.once("data",p)})},throw:r,return:r,[Symbol.asyncIterator](){return this}}}[Symbol.iterator](){this[nc]=!1;let e=!1,r=()=>(this.pause(),this.off(p7,r),this.off(ts,r),this.off("end",r),e=!0,{done:!0,value:void 0}),s=()=>{if(e)return r();let a=this.read();return a===null?r():{done:!1,value:a}};return this.once("end",r),this.once(p7,r),this.once(ts,r),{next:s,throw:r,return:r,[Symbol.iterator](){return this}}}destroy(e){if(this[ts])return e?this.emit("error",e):this.emit(ts),this;this[ts]=!0,this[nc]=!0,this[Zs].length=0,this[Xs]=0;let r=this;return typeof r.close=="function"&&!this[WN]&&r.close(),e?this.emit("error",e):this.emit(ts),this}static get isStream(){return Ra.isStream}};Ra.Minipass=zN});var dTe=L((YIr,wA)=>{"use strict";var Qb=ye("crypto"),{Minipass:_Ot}=pTe(),I7=["sha512","sha384","sha256"],w7=["sha512"],UOt=/^[a-z0-9+/]+(?:=?=?)$/i,HOt=/^([a-z0-9]+)-([^?]+)([?\S*]*)$/,jOt=/^([a-z0-9]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)?$/,qOt=/^[\x21-\x7E]+$/,Tb=t=>t?.length?`?${t.join("?")}`:"",C7=class extends _Ot{#t;#r;#i;constructor(e){super(),this.size=0,this.opts=e,this.#e(),e?.algorithms?this.algorithms=[...e.algorithms]:this.algorithms=[...w7],this.algorithm!==null&&!this.algorithms.includes(this.algorithm)&&this.algorithms.push(this.algorithm),this.hashes=this.algorithms.map(Qb.createHash)}#e(){this.sri=this.opts?.integrity?ic(this.opts?.integrity,this.opts):null,this.expectedSize=this.opts?.size,this.sri?this.sri.isHash?(this.goodSri=!0,this.algorithm=this.sri.algorithm):(this.goodSri=!this.sri.isEmpty(),this.algorithm=this.sri.pickAlgorithm(this.opts)):this.algorithm=null,this.digests=this.goodSri?this.sri[this.algorithm]:null,this.optString=Tb(this.opts?.options)}on(e,r){return e==="size"&&this.#r?r(this.#r):e==="integrity"&&this.#t?r(this.#t):e==="verified"&&this.#i?r(this.#i):super.on(e,r)}emit(e,r){return e==="end"&&this.#n(),super.emit(e,r)}write(e){return this.size+=e.length,this.hashes.forEach(r=>r.update(e)),super.write(e)}#n(){this.goodSri||this.#e();let e=ic(this.hashes.map((s,a)=>`${this.algorithms[a]}-${s.digest("base64")}${this.optString}`).join(" "),this.opts),r=this.goodSri&&e.match(this.sri,this.opts);if(typeof this.expectedSize=="number"&&this.size!==this.expectedSize){let s=new Error(`stream size mismatch when checking ${this.sri}. + Wanted: ${this.expectedSize} + Found: ${this.size}`);s.code="EBADSIZE",s.found=this.size,s.expected=this.expectedSize,s.sri=this.sri,this.emit("error",s)}else if(this.sri&&!r){let s=new Error(`${this.sri} integrity checksum failed when using ${this.algorithm}: wanted ${this.digests} but got ${e}. (${this.size} bytes)`);s.code="EINTEGRITY",s.found=e,s.expected=this.digests,s.algorithm=this.algorithm,s.sri=this.sri,this.emit("error",s)}else this.#r=this.size,this.emit("size",this.size),this.#t=e,this.emit("integrity",e),r&&(this.#i=r,this.emit("verified",r))}},gh=class{get isHash(){return!0}constructor(e,r){let s=r?.strict;this.source=e.trim(),this.digest="",this.algorithm="",this.options=[];let a=this.source.match(s?jOt:HOt);if(!a||s&&!I7.includes(a[1]))return;this.algorithm=a[1],this.digest=a[2];let n=a[3];n&&(this.options=n.slice(1).split("?"))}hexDigest(){return this.digest&&Buffer.from(this.digest,"base64").toString("hex")}toJSON(){return this.toString()}match(e,r){let s=ic(e,r);if(!s)return!1;if(s.isIntegrity){let a=s.pickAlgorithm(r,[this.algorithm]);if(!a)return!1;let n=s[a].find(c=>c.digest===this.digest);return n||!1}return s.digest===this.digest?s:!1}toString(e){return e?.strict&&!(I7.includes(this.algorithm)&&this.digest.match(UOt)&&this.options.every(r=>r.match(qOt)))?"":`${this.algorithm}-${this.digest}${Tb(this.options)}`}};function hTe(t,e,r,s){let a=t!=="",n=!1,c="",f=s.length-1;for(let h=0;hs[a].find(c=>n.digest===c.digest)))throw new Error("hashes do not match, cannot update integrity")}else this[a]=s[a]}match(e,r){let s=ic(e,r);if(!s)return!1;let a=s.pickAlgorithm(r,Object.keys(this));return!!a&&this[a]&&s[a]&&this[a].find(n=>s[a].find(c=>n.digest===c.digest))||!1}pickAlgorithm(e,r){let s=e?.pickAlgorithm||ZOt,a=Object.keys(this).filter(n=>r?.length?r.includes(n):!0);return a.length?a.reduce((n,c)=>s(n,c)||n):null}};wA.exports.parse=ic;function ic(t,e){if(!t)return null;if(typeof t=="string")return E7(t,e);if(t.algorithm&&t.digest){let r=new Xm;return r[t.algorithm]=[t],E7(kb(r,e),e)}else return E7(kb(t,e),e)}function E7(t,e){if(e?.single)return new gh(t,e);let r=t.trim().split(/\s+/).reduce((s,a)=>{let n=new gh(a,e);if(n.algorithm&&n.digest){let c=n.algorithm;s[c]||(s[c]=[]),s[c].push(n)}return s},new Xm);return r.isEmpty()?null:r}wA.exports.stringify=kb;function kb(t,e){return t.algorithm&&t.digest?gh.prototype.toString.call(t,e):typeof t=="string"?kb(ic(t,e),e):Xm.prototype.toString.call(t,e)}wA.exports.fromHex=GOt;function GOt(t,e,r){let s=Tb(r?.options);return ic(`${e}-${Buffer.from(t,"hex").toString("base64")}${s}`,r)}wA.exports.fromData=WOt;function WOt(t,e){let r=e?.algorithms||[...w7],s=Tb(e?.options);return r.reduce((a,n)=>{let c=Qb.createHash(n).update(t).digest("base64"),f=new gh(`${n}-${c}${s}`,e);if(f.algorithm&&f.digest){let p=f.algorithm;a[p]||(a[p]=[]),a[p].push(f)}return a},new Xm)}wA.exports.fromStream=YOt;function YOt(t,e){let r=B7(e);return new Promise((s,a)=>{t.pipe(r),t.on("error",a),r.on("error",a);let n;r.on("integrity",c=>{n=c}),r.on("end",()=>s(n)),r.resume()})}wA.exports.checkData=VOt;function VOt(t,e,r){if(e=ic(e,r),!e||!Object.keys(e).length){if(r?.error)throw Object.assign(new Error("No valid integrity hashes to check against"),{code:"EINTEGRITY"});return!1}let s=e.pickAlgorithm(r),a=Qb.createHash(s).update(t).digest("base64"),n=ic({algorithm:s,digest:a}),c=n.match(e,r);if(r=r||{},c||!r.error)return c;if(typeof r.size=="number"&&t.length!==r.size){let f=new Error(`data size mismatch when checking ${e}. + Wanted: ${r.size} + Found: ${t.length}`);throw f.code="EBADSIZE",f.found=t.length,f.expected=r.size,f.sri=e,f}else{let f=new Error(`Integrity checksum failed when using ${s}: Wanted ${e}, but got ${n}. (${t.length} bytes)`);throw f.code="EINTEGRITY",f.found=n,f.expected=e,f.algorithm=s,f.sri=e,f}}wA.exports.checkStream=KOt;function KOt(t,e,r){if(r=r||Object.create(null),r.integrity=e,e=ic(e,r),!e||!Object.keys(e).length)return Promise.reject(Object.assign(new Error("No valid integrity hashes to check against"),{code:"EINTEGRITY"}));let s=B7(r);return new Promise((a,n)=>{t.pipe(s),t.on("error",n),s.on("error",n);let c;s.on("verified",f=>{c=f}),s.on("end",()=>a(c)),s.resume()})}wA.exports.integrityStream=B7;function B7(t=Object.create(null)){return new C7(t)}wA.exports.create=JOt;function JOt(t){let e=t?.algorithms||[...w7],r=Tb(t?.options),s=e.map(Qb.createHash);return{update:function(a,n){return s.forEach(c=>c.update(a,n)),this},digest:function(){return e.reduce((n,c)=>{let f=s.shift().digest("base64"),p=new gh(`${c}-${f}${r}`,t);if(p.algorithm&&p.digest){let h=p.algorithm;n[h]||(n[h]=[]),n[h].push(p)}return n},new Xm)}}}var zOt=Qb.getHashes(),gTe=["md5","whirlpool","sha1","sha224","sha256","sha384","sha512","sha3","sha3-256","sha3-384","sha3-512","sha3_256","sha3_384","sha3_512"].filter(t=>zOt.includes(t));function ZOt(t,e){return gTe.indexOf(t.toLowerCase())>=gTe.indexOf(e.toLowerCase())?t:e}});var v7=L(yg=>{"use strict";Object.defineProperty(yg,"__esModule",{value:!0});yg.Signature=yg.Envelope=void 0;yg.Envelope={fromJSON(t){return{payload:ZN(t.payload)?Buffer.from(mTe(t.payload)):Buffer.alloc(0),payloadType:ZN(t.payloadType)?globalThis.String(t.payloadType):"",signatures:globalThis.Array.isArray(t?.signatures)?t.signatures.map(e=>yg.Signature.fromJSON(e)):[]}},toJSON(t){let e={};return t.payload.length!==0&&(e.payload=yTe(t.payload)),t.payloadType!==""&&(e.payloadType=t.payloadType),t.signatures?.length&&(e.signatures=t.signatures.map(r=>yg.Signature.toJSON(r))),e}};yg.Signature={fromJSON(t){return{sig:ZN(t.sig)?Buffer.from(mTe(t.sig)):Buffer.alloc(0),keyid:ZN(t.keyid)?globalThis.String(t.keyid):""}},toJSON(t){let e={};return t.sig.length!==0&&(e.sig=yTe(t.sig)),t.keyid!==""&&(e.keyid=t.keyid),e}};function mTe(t){return Uint8Array.from(globalThis.Buffer.from(t,"base64"))}function yTe(t){return globalThis.Buffer.from(t).toString("base64")}function ZN(t){return t!=null}});var ITe=L(XN=>{"use strict";Object.defineProperty(XN,"__esModule",{value:!0});XN.Timestamp=void 0;XN.Timestamp={fromJSON(t){return{seconds:ETe(t.seconds)?globalThis.String(t.seconds):"0",nanos:ETe(t.nanos)?globalThis.Number(t.nanos):0}},toJSON(t){let e={};return t.seconds!=="0"&&(e.seconds=t.seconds),t.nanos!==0&&(e.nanos=Math.round(t.nanos)),e}};function ETe(t){return t!=null}});var Vw=L(_r=>{"use strict";Object.defineProperty(_r,"__esModule",{value:!0});_r.TimeRange=_r.X509CertificateChain=_r.SubjectAlternativeName=_r.X509Certificate=_r.DistinguishedName=_r.ObjectIdentifierValuePair=_r.ObjectIdentifier=_r.PublicKeyIdentifier=_r.PublicKey=_r.RFC3161SignedTimestamp=_r.LogId=_r.MessageSignature=_r.HashOutput=_r.SubjectAlternativeNameType=_r.PublicKeyDetails=_r.HashAlgorithm=void 0;_r.hashAlgorithmFromJSON=wTe;_r.hashAlgorithmToJSON=BTe;_r.publicKeyDetailsFromJSON=vTe;_r.publicKeyDetailsToJSON=STe;_r.subjectAlternativeNameTypeFromJSON=DTe;_r.subjectAlternativeNameTypeToJSON=bTe;var XOt=ITe(),El;(function(t){t[t.HASH_ALGORITHM_UNSPECIFIED=0]="HASH_ALGORITHM_UNSPECIFIED",t[t.SHA2_256=1]="SHA2_256",t[t.SHA2_384=2]="SHA2_384",t[t.SHA2_512=3]="SHA2_512",t[t.SHA3_256=4]="SHA3_256",t[t.SHA3_384=5]="SHA3_384"})(El||(_r.HashAlgorithm=El={}));function wTe(t){switch(t){case 0:case"HASH_ALGORITHM_UNSPECIFIED":return El.HASH_ALGORITHM_UNSPECIFIED;case 1:case"SHA2_256":return El.SHA2_256;case 2:case"SHA2_384":return El.SHA2_384;case 3:case"SHA2_512":return El.SHA2_512;case 4:case"SHA3_256":return El.SHA3_256;case 5:case"SHA3_384":return El.SHA3_384;default:throw new globalThis.Error("Unrecognized enum value "+t+" for enum HashAlgorithm")}}function BTe(t){switch(t){case El.HASH_ALGORITHM_UNSPECIFIED:return"HASH_ALGORITHM_UNSPECIFIED";case El.SHA2_256:return"SHA2_256";case El.SHA2_384:return"SHA2_384";case El.SHA2_512:return"SHA2_512";case El.SHA3_256:return"SHA3_256";case El.SHA3_384:return"SHA3_384";default:throw new globalThis.Error("Unrecognized enum value "+t+" for enum HashAlgorithm")}}var rn;(function(t){t[t.PUBLIC_KEY_DETAILS_UNSPECIFIED=0]="PUBLIC_KEY_DETAILS_UNSPECIFIED",t[t.PKCS1_RSA_PKCS1V5=1]="PKCS1_RSA_PKCS1V5",t[t.PKCS1_RSA_PSS=2]="PKCS1_RSA_PSS",t[t.PKIX_RSA_PKCS1V5=3]="PKIX_RSA_PKCS1V5",t[t.PKIX_RSA_PSS=4]="PKIX_RSA_PSS",t[t.PKIX_RSA_PKCS1V15_2048_SHA256=9]="PKIX_RSA_PKCS1V15_2048_SHA256",t[t.PKIX_RSA_PKCS1V15_3072_SHA256=10]="PKIX_RSA_PKCS1V15_3072_SHA256",t[t.PKIX_RSA_PKCS1V15_4096_SHA256=11]="PKIX_RSA_PKCS1V15_4096_SHA256",t[t.PKIX_RSA_PSS_2048_SHA256=16]="PKIX_RSA_PSS_2048_SHA256",t[t.PKIX_RSA_PSS_3072_SHA256=17]="PKIX_RSA_PSS_3072_SHA256",t[t.PKIX_RSA_PSS_4096_SHA256=18]="PKIX_RSA_PSS_4096_SHA256",t[t.PKIX_ECDSA_P256_HMAC_SHA_256=6]="PKIX_ECDSA_P256_HMAC_SHA_256",t[t.PKIX_ECDSA_P256_SHA_256=5]="PKIX_ECDSA_P256_SHA_256",t[t.PKIX_ECDSA_P384_SHA_384=12]="PKIX_ECDSA_P384_SHA_384",t[t.PKIX_ECDSA_P521_SHA_512=13]="PKIX_ECDSA_P521_SHA_512",t[t.PKIX_ED25519=7]="PKIX_ED25519",t[t.PKIX_ED25519_PH=8]="PKIX_ED25519_PH",t[t.LMS_SHA256=14]="LMS_SHA256",t[t.LMOTS_SHA256=15]="LMOTS_SHA256"})(rn||(_r.PublicKeyDetails=rn={}));function vTe(t){switch(t){case 0:case"PUBLIC_KEY_DETAILS_UNSPECIFIED":return rn.PUBLIC_KEY_DETAILS_UNSPECIFIED;case 1:case"PKCS1_RSA_PKCS1V5":return rn.PKCS1_RSA_PKCS1V5;case 2:case"PKCS1_RSA_PSS":return rn.PKCS1_RSA_PSS;case 3:case"PKIX_RSA_PKCS1V5":return rn.PKIX_RSA_PKCS1V5;case 4:case"PKIX_RSA_PSS":return rn.PKIX_RSA_PSS;case 9:case"PKIX_RSA_PKCS1V15_2048_SHA256":return rn.PKIX_RSA_PKCS1V15_2048_SHA256;case 10:case"PKIX_RSA_PKCS1V15_3072_SHA256":return rn.PKIX_RSA_PKCS1V15_3072_SHA256;case 11:case"PKIX_RSA_PKCS1V15_4096_SHA256":return rn.PKIX_RSA_PKCS1V15_4096_SHA256;case 16:case"PKIX_RSA_PSS_2048_SHA256":return rn.PKIX_RSA_PSS_2048_SHA256;case 17:case"PKIX_RSA_PSS_3072_SHA256":return rn.PKIX_RSA_PSS_3072_SHA256;case 18:case"PKIX_RSA_PSS_4096_SHA256":return rn.PKIX_RSA_PSS_4096_SHA256;case 6:case"PKIX_ECDSA_P256_HMAC_SHA_256":return rn.PKIX_ECDSA_P256_HMAC_SHA_256;case 5:case"PKIX_ECDSA_P256_SHA_256":return rn.PKIX_ECDSA_P256_SHA_256;case 12:case"PKIX_ECDSA_P384_SHA_384":return rn.PKIX_ECDSA_P384_SHA_384;case 13:case"PKIX_ECDSA_P521_SHA_512":return rn.PKIX_ECDSA_P521_SHA_512;case 7:case"PKIX_ED25519":return rn.PKIX_ED25519;case 8:case"PKIX_ED25519_PH":return rn.PKIX_ED25519_PH;case 14:case"LMS_SHA256":return rn.LMS_SHA256;case 15:case"LMOTS_SHA256":return rn.LMOTS_SHA256;default:throw new globalThis.Error("Unrecognized enum value "+t+" for enum PublicKeyDetails")}}function STe(t){switch(t){case rn.PUBLIC_KEY_DETAILS_UNSPECIFIED:return"PUBLIC_KEY_DETAILS_UNSPECIFIED";case rn.PKCS1_RSA_PKCS1V5:return"PKCS1_RSA_PKCS1V5";case rn.PKCS1_RSA_PSS:return"PKCS1_RSA_PSS";case rn.PKIX_RSA_PKCS1V5:return"PKIX_RSA_PKCS1V5";case rn.PKIX_RSA_PSS:return"PKIX_RSA_PSS";case rn.PKIX_RSA_PKCS1V15_2048_SHA256:return"PKIX_RSA_PKCS1V15_2048_SHA256";case rn.PKIX_RSA_PKCS1V15_3072_SHA256:return"PKIX_RSA_PKCS1V15_3072_SHA256";case rn.PKIX_RSA_PKCS1V15_4096_SHA256:return"PKIX_RSA_PKCS1V15_4096_SHA256";case rn.PKIX_RSA_PSS_2048_SHA256:return"PKIX_RSA_PSS_2048_SHA256";case rn.PKIX_RSA_PSS_3072_SHA256:return"PKIX_RSA_PSS_3072_SHA256";case rn.PKIX_RSA_PSS_4096_SHA256:return"PKIX_RSA_PSS_4096_SHA256";case rn.PKIX_ECDSA_P256_HMAC_SHA_256:return"PKIX_ECDSA_P256_HMAC_SHA_256";case rn.PKIX_ECDSA_P256_SHA_256:return"PKIX_ECDSA_P256_SHA_256";case rn.PKIX_ECDSA_P384_SHA_384:return"PKIX_ECDSA_P384_SHA_384";case rn.PKIX_ECDSA_P521_SHA_512:return"PKIX_ECDSA_P521_SHA_512";case rn.PKIX_ED25519:return"PKIX_ED25519";case rn.PKIX_ED25519_PH:return"PKIX_ED25519_PH";case rn.LMS_SHA256:return"LMS_SHA256";case rn.LMOTS_SHA256:return"LMOTS_SHA256";default:throw new globalThis.Error("Unrecognized enum value "+t+" for enum PublicKeyDetails")}}var BA;(function(t){t[t.SUBJECT_ALTERNATIVE_NAME_TYPE_UNSPECIFIED=0]="SUBJECT_ALTERNATIVE_NAME_TYPE_UNSPECIFIED",t[t.EMAIL=1]="EMAIL",t[t.URI=2]="URI",t[t.OTHER_NAME=3]="OTHER_NAME"})(BA||(_r.SubjectAlternativeNameType=BA={}));function DTe(t){switch(t){case 0:case"SUBJECT_ALTERNATIVE_NAME_TYPE_UNSPECIFIED":return BA.SUBJECT_ALTERNATIVE_NAME_TYPE_UNSPECIFIED;case 1:case"EMAIL":return BA.EMAIL;case 2:case"URI":return BA.URI;case 3:case"OTHER_NAME":return BA.OTHER_NAME;default:throw new globalThis.Error("Unrecognized enum value "+t+" for enum SubjectAlternativeNameType")}}function bTe(t){switch(t){case BA.SUBJECT_ALTERNATIVE_NAME_TYPE_UNSPECIFIED:return"SUBJECT_ALTERNATIVE_NAME_TYPE_UNSPECIFIED";case BA.EMAIL:return"EMAIL";case BA.URI:return"URI";case BA.OTHER_NAME:return"OTHER_NAME";default:throw new globalThis.Error("Unrecognized enum value "+t+" for enum SubjectAlternativeNameType")}}_r.HashOutput={fromJSON(t){return{algorithm:ms(t.algorithm)?wTe(t.algorithm):0,digest:ms(t.digest)?Buffer.from($m(t.digest)):Buffer.alloc(0)}},toJSON(t){let e={};return t.algorithm!==0&&(e.algorithm=BTe(t.algorithm)),t.digest.length!==0&&(e.digest=ey(t.digest)),e}};_r.MessageSignature={fromJSON(t){return{messageDigest:ms(t.messageDigest)?_r.HashOutput.fromJSON(t.messageDigest):void 0,signature:ms(t.signature)?Buffer.from($m(t.signature)):Buffer.alloc(0)}},toJSON(t){let e={};return t.messageDigest!==void 0&&(e.messageDigest=_r.HashOutput.toJSON(t.messageDigest)),t.signature.length!==0&&(e.signature=ey(t.signature)),e}};_r.LogId={fromJSON(t){return{keyId:ms(t.keyId)?Buffer.from($m(t.keyId)):Buffer.alloc(0)}},toJSON(t){let e={};return t.keyId.length!==0&&(e.keyId=ey(t.keyId)),e}};_r.RFC3161SignedTimestamp={fromJSON(t){return{signedTimestamp:ms(t.signedTimestamp)?Buffer.from($m(t.signedTimestamp)):Buffer.alloc(0)}},toJSON(t){let e={};return t.signedTimestamp.length!==0&&(e.signedTimestamp=ey(t.signedTimestamp)),e}};_r.PublicKey={fromJSON(t){return{rawBytes:ms(t.rawBytes)?Buffer.from($m(t.rawBytes)):void 0,keyDetails:ms(t.keyDetails)?vTe(t.keyDetails):0,validFor:ms(t.validFor)?_r.TimeRange.fromJSON(t.validFor):void 0}},toJSON(t){let e={};return t.rawBytes!==void 0&&(e.rawBytes=ey(t.rawBytes)),t.keyDetails!==0&&(e.keyDetails=STe(t.keyDetails)),t.validFor!==void 0&&(e.validFor=_r.TimeRange.toJSON(t.validFor)),e}};_r.PublicKeyIdentifier={fromJSON(t){return{hint:ms(t.hint)?globalThis.String(t.hint):""}},toJSON(t){let e={};return t.hint!==""&&(e.hint=t.hint),e}};_r.ObjectIdentifier={fromJSON(t){return{id:globalThis.Array.isArray(t?.id)?t.id.map(e=>globalThis.Number(e)):[]}},toJSON(t){let e={};return t.id?.length&&(e.id=t.id.map(r=>Math.round(r))),e}};_r.ObjectIdentifierValuePair={fromJSON(t){return{oid:ms(t.oid)?_r.ObjectIdentifier.fromJSON(t.oid):void 0,value:ms(t.value)?Buffer.from($m(t.value)):Buffer.alloc(0)}},toJSON(t){let e={};return t.oid!==void 0&&(e.oid=_r.ObjectIdentifier.toJSON(t.oid)),t.value.length!==0&&(e.value=ey(t.value)),e}};_r.DistinguishedName={fromJSON(t){return{organization:ms(t.organization)?globalThis.String(t.organization):"",commonName:ms(t.commonName)?globalThis.String(t.commonName):""}},toJSON(t){let e={};return t.organization!==""&&(e.organization=t.organization),t.commonName!==""&&(e.commonName=t.commonName),e}};_r.X509Certificate={fromJSON(t){return{rawBytes:ms(t.rawBytes)?Buffer.from($m(t.rawBytes)):Buffer.alloc(0)}},toJSON(t){let e={};return t.rawBytes.length!==0&&(e.rawBytes=ey(t.rawBytes)),e}};_r.SubjectAlternativeName={fromJSON(t){return{type:ms(t.type)?DTe(t.type):0,identity:ms(t.regexp)?{$case:"regexp",regexp:globalThis.String(t.regexp)}:ms(t.value)?{$case:"value",value:globalThis.String(t.value)}:void 0}},toJSON(t){let e={};return t.type!==0&&(e.type=bTe(t.type)),t.identity?.$case==="regexp"?e.regexp=t.identity.regexp:t.identity?.$case==="value"&&(e.value=t.identity.value),e}};_r.X509CertificateChain={fromJSON(t){return{certificates:globalThis.Array.isArray(t?.certificates)?t.certificates.map(e=>_r.X509Certificate.fromJSON(e)):[]}},toJSON(t){let e={};return t.certificates?.length&&(e.certificates=t.certificates.map(r=>_r.X509Certificate.toJSON(r))),e}};_r.TimeRange={fromJSON(t){return{start:ms(t.start)?CTe(t.start):void 0,end:ms(t.end)?CTe(t.end):void 0}},toJSON(t){let e={};return t.start!==void 0&&(e.start=t.start.toISOString()),t.end!==void 0&&(e.end=t.end.toISOString()),e}};function $m(t){return Uint8Array.from(globalThis.Buffer.from(t,"base64"))}function ey(t){return globalThis.Buffer.from(t).toString("base64")}function $Ot(t){let e=(globalThis.Number(t.seconds)||0)*1e3;return e+=(t.nanos||0)/1e6,new globalThis.Date(e)}function CTe(t){return t instanceof globalThis.Date?t:typeof t=="string"?new globalThis.Date(t):$Ot(XOt.Timestamp.fromJSON(t))}function ms(t){return t!=null}});var S7=L(ys=>{"use strict";Object.defineProperty(ys,"__esModule",{value:!0});ys.TransparencyLogEntry=ys.InclusionPromise=ys.InclusionProof=ys.Checkpoint=ys.KindVersion=void 0;var PTe=Vw();ys.KindVersion={fromJSON(t){return{kind:Fa(t.kind)?globalThis.String(t.kind):"",version:Fa(t.version)?globalThis.String(t.version):""}},toJSON(t){let e={};return t.kind!==""&&(e.kind=t.kind),t.version!==""&&(e.version=t.version),e}};ys.Checkpoint={fromJSON(t){return{envelope:Fa(t.envelope)?globalThis.String(t.envelope):""}},toJSON(t){let e={};return t.envelope!==""&&(e.envelope=t.envelope),e}};ys.InclusionProof={fromJSON(t){return{logIndex:Fa(t.logIndex)?globalThis.String(t.logIndex):"0",rootHash:Fa(t.rootHash)?Buffer.from($N(t.rootHash)):Buffer.alloc(0),treeSize:Fa(t.treeSize)?globalThis.String(t.treeSize):"0",hashes:globalThis.Array.isArray(t?.hashes)?t.hashes.map(e=>Buffer.from($N(e))):[],checkpoint:Fa(t.checkpoint)?ys.Checkpoint.fromJSON(t.checkpoint):void 0}},toJSON(t){let e={};return t.logIndex!=="0"&&(e.logIndex=t.logIndex),t.rootHash.length!==0&&(e.rootHash=eO(t.rootHash)),t.treeSize!=="0"&&(e.treeSize=t.treeSize),t.hashes?.length&&(e.hashes=t.hashes.map(r=>eO(r))),t.checkpoint!==void 0&&(e.checkpoint=ys.Checkpoint.toJSON(t.checkpoint)),e}};ys.InclusionPromise={fromJSON(t){return{signedEntryTimestamp:Fa(t.signedEntryTimestamp)?Buffer.from($N(t.signedEntryTimestamp)):Buffer.alloc(0)}},toJSON(t){let e={};return t.signedEntryTimestamp.length!==0&&(e.signedEntryTimestamp=eO(t.signedEntryTimestamp)),e}};ys.TransparencyLogEntry={fromJSON(t){return{logIndex:Fa(t.logIndex)?globalThis.String(t.logIndex):"0",logId:Fa(t.logId)?PTe.LogId.fromJSON(t.logId):void 0,kindVersion:Fa(t.kindVersion)?ys.KindVersion.fromJSON(t.kindVersion):void 0,integratedTime:Fa(t.integratedTime)?globalThis.String(t.integratedTime):"0",inclusionPromise:Fa(t.inclusionPromise)?ys.InclusionPromise.fromJSON(t.inclusionPromise):void 0,inclusionProof:Fa(t.inclusionProof)?ys.InclusionProof.fromJSON(t.inclusionProof):void 0,canonicalizedBody:Fa(t.canonicalizedBody)?Buffer.from($N(t.canonicalizedBody)):Buffer.alloc(0)}},toJSON(t){let e={};return t.logIndex!=="0"&&(e.logIndex=t.logIndex),t.logId!==void 0&&(e.logId=PTe.LogId.toJSON(t.logId)),t.kindVersion!==void 0&&(e.kindVersion=ys.KindVersion.toJSON(t.kindVersion)),t.integratedTime!=="0"&&(e.integratedTime=t.integratedTime),t.inclusionPromise!==void 0&&(e.inclusionPromise=ys.InclusionPromise.toJSON(t.inclusionPromise)),t.inclusionProof!==void 0&&(e.inclusionProof=ys.InclusionProof.toJSON(t.inclusionProof)),t.canonicalizedBody.length!==0&&(e.canonicalizedBody=eO(t.canonicalizedBody)),e}};function $N(t){return Uint8Array.from(globalThis.Buffer.from(t,"base64"))}function eO(t){return globalThis.Buffer.from(t).toString("base64")}function Fa(t){return t!=null}});var D7=L($c=>{"use strict";Object.defineProperty($c,"__esModule",{value:!0});$c.Bundle=$c.VerificationMaterial=$c.TimestampVerificationData=void 0;var xTe=v7(),vA=Vw(),kTe=S7();$c.TimestampVerificationData={fromJSON(t){return{rfc3161Timestamps:globalThis.Array.isArray(t?.rfc3161Timestamps)?t.rfc3161Timestamps.map(e=>vA.RFC3161SignedTimestamp.fromJSON(e)):[]}},toJSON(t){let e={};return t.rfc3161Timestamps?.length&&(e.rfc3161Timestamps=t.rfc3161Timestamps.map(r=>vA.RFC3161SignedTimestamp.toJSON(r))),e}};$c.VerificationMaterial={fromJSON(t){return{content:Eg(t.publicKey)?{$case:"publicKey",publicKey:vA.PublicKeyIdentifier.fromJSON(t.publicKey)}:Eg(t.x509CertificateChain)?{$case:"x509CertificateChain",x509CertificateChain:vA.X509CertificateChain.fromJSON(t.x509CertificateChain)}:Eg(t.certificate)?{$case:"certificate",certificate:vA.X509Certificate.fromJSON(t.certificate)}:void 0,tlogEntries:globalThis.Array.isArray(t?.tlogEntries)?t.tlogEntries.map(e=>kTe.TransparencyLogEntry.fromJSON(e)):[],timestampVerificationData:Eg(t.timestampVerificationData)?$c.TimestampVerificationData.fromJSON(t.timestampVerificationData):void 0}},toJSON(t){let e={};return t.content?.$case==="publicKey"?e.publicKey=vA.PublicKeyIdentifier.toJSON(t.content.publicKey):t.content?.$case==="x509CertificateChain"?e.x509CertificateChain=vA.X509CertificateChain.toJSON(t.content.x509CertificateChain):t.content?.$case==="certificate"&&(e.certificate=vA.X509Certificate.toJSON(t.content.certificate)),t.tlogEntries?.length&&(e.tlogEntries=t.tlogEntries.map(r=>kTe.TransparencyLogEntry.toJSON(r))),t.timestampVerificationData!==void 0&&(e.timestampVerificationData=$c.TimestampVerificationData.toJSON(t.timestampVerificationData)),e}};$c.Bundle={fromJSON(t){return{mediaType:Eg(t.mediaType)?globalThis.String(t.mediaType):"",verificationMaterial:Eg(t.verificationMaterial)?$c.VerificationMaterial.fromJSON(t.verificationMaterial):void 0,content:Eg(t.messageSignature)?{$case:"messageSignature",messageSignature:vA.MessageSignature.fromJSON(t.messageSignature)}:Eg(t.dsseEnvelope)?{$case:"dsseEnvelope",dsseEnvelope:xTe.Envelope.fromJSON(t.dsseEnvelope)}:void 0}},toJSON(t){let e={};return t.mediaType!==""&&(e.mediaType=t.mediaType),t.verificationMaterial!==void 0&&(e.verificationMaterial=$c.VerificationMaterial.toJSON(t.verificationMaterial)),t.content?.$case==="messageSignature"?e.messageSignature=vA.MessageSignature.toJSON(t.content.messageSignature):t.content?.$case==="dsseEnvelope"&&(e.dsseEnvelope=xTe.Envelope.toJSON(t.content.dsseEnvelope)),e}};function Eg(t){return t!=null}});var b7=L(Ti=>{"use strict";Object.defineProperty(Ti,"__esModule",{value:!0});Ti.ClientTrustConfig=Ti.SigningConfig=Ti.TrustedRoot=Ti.CertificateAuthority=Ti.TransparencyLogInstance=void 0;var Il=Vw();Ti.TransparencyLogInstance={fromJSON(t){return{baseUrl:sa(t.baseUrl)?globalThis.String(t.baseUrl):"",hashAlgorithm:sa(t.hashAlgorithm)?(0,Il.hashAlgorithmFromJSON)(t.hashAlgorithm):0,publicKey:sa(t.publicKey)?Il.PublicKey.fromJSON(t.publicKey):void 0,logId:sa(t.logId)?Il.LogId.fromJSON(t.logId):void 0,checkpointKeyId:sa(t.checkpointKeyId)?Il.LogId.fromJSON(t.checkpointKeyId):void 0}},toJSON(t){let e={};return t.baseUrl!==""&&(e.baseUrl=t.baseUrl),t.hashAlgorithm!==0&&(e.hashAlgorithm=(0,Il.hashAlgorithmToJSON)(t.hashAlgorithm)),t.publicKey!==void 0&&(e.publicKey=Il.PublicKey.toJSON(t.publicKey)),t.logId!==void 0&&(e.logId=Il.LogId.toJSON(t.logId)),t.checkpointKeyId!==void 0&&(e.checkpointKeyId=Il.LogId.toJSON(t.checkpointKeyId)),e}};Ti.CertificateAuthority={fromJSON(t){return{subject:sa(t.subject)?Il.DistinguishedName.fromJSON(t.subject):void 0,uri:sa(t.uri)?globalThis.String(t.uri):"",certChain:sa(t.certChain)?Il.X509CertificateChain.fromJSON(t.certChain):void 0,validFor:sa(t.validFor)?Il.TimeRange.fromJSON(t.validFor):void 0}},toJSON(t){let e={};return t.subject!==void 0&&(e.subject=Il.DistinguishedName.toJSON(t.subject)),t.uri!==""&&(e.uri=t.uri),t.certChain!==void 0&&(e.certChain=Il.X509CertificateChain.toJSON(t.certChain)),t.validFor!==void 0&&(e.validFor=Il.TimeRange.toJSON(t.validFor)),e}};Ti.TrustedRoot={fromJSON(t){return{mediaType:sa(t.mediaType)?globalThis.String(t.mediaType):"",tlogs:globalThis.Array.isArray(t?.tlogs)?t.tlogs.map(e=>Ti.TransparencyLogInstance.fromJSON(e)):[],certificateAuthorities:globalThis.Array.isArray(t?.certificateAuthorities)?t.certificateAuthorities.map(e=>Ti.CertificateAuthority.fromJSON(e)):[],ctlogs:globalThis.Array.isArray(t?.ctlogs)?t.ctlogs.map(e=>Ti.TransparencyLogInstance.fromJSON(e)):[],timestampAuthorities:globalThis.Array.isArray(t?.timestampAuthorities)?t.timestampAuthorities.map(e=>Ti.CertificateAuthority.fromJSON(e)):[]}},toJSON(t){let e={};return t.mediaType!==""&&(e.mediaType=t.mediaType),t.tlogs?.length&&(e.tlogs=t.tlogs.map(r=>Ti.TransparencyLogInstance.toJSON(r))),t.certificateAuthorities?.length&&(e.certificateAuthorities=t.certificateAuthorities.map(r=>Ti.CertificateAuthority.toJSON(r))),t.ctlogs?.length&&(e.ctlogs=t.ctlogs.map(r=>Ti.TransparencyLogInstance.toJSON(r))),t.timestampAuthorities?.length&&(e.timestampAuthorities=t.timestampAuthorities.map(r=>Ti.CertificateAuthority.toJSON(r))),e}};Ti.SigningConfig={fromJSON(t){return{mediaType:sa(t.mediaType)?globalThis.String(t.mediaType):"",caUrl:sa(t.caUrl)?globalThis.String(t.caUrl):"",oidcUrl:sa(t.oidcUrl)?globalThis.String(t.oidcUrl):"",tlogUrls:globalThis.Array.isArray(t?.tlogUrls)?t.tlogUrls.map(e=>globalThis.String(e)):[],tsaUrls:globalThis.Array.isArray(t?.tsaUrls)?t.tsaUrls.map(e=>globalThis.String(e)):[]}},toJSON(t){let e={};return t.mediaType!==""&&(e.mediaType=t.mediaType),t.caUrl!==""&&(e.caUrl=t.caUrl),t.oidcUrl!==""&&(e.oidcUrl=t.oidcUrl),t.tlogUrls?.length&&(e.tlogUrls=t.tlogUrls),t.tsaUrls?.length&&(e.tsaUrls=t.tsaUrls),e}};Ti.ClientTrustConfig={fromJSON(t){return{mediaType:sa(t.mediaType)?globalThis.String(t.mediaType):"",trustedRoot:sa(t.trustedRoot)?Ti.TrustedRoot.fromJSON(t.trustedRoot):void 0,signingConfig:sa(t.signingConfig)?Ti.SigningConfig.fromJSON(t.signingConfig):void 0}},toJSON(t){let e={};return t.mediaType!==""&&(e.mediaType=t.mediaType),t.trustedRoot!==void 0&&(e.trustedRoot=Ti.TrustedRoot.toJSON(t.trustedRoot)),t.signingConfig!==void 0&&(e.signingConfig=Ti.SigningConfig.toJSON(t.signingConfig)),e}};function sa(t){return t!=null}});var RTe=L(Vr=>{"use strict";Object.defineProperty(Vr,"__esModule",{value:!0});Vr.Input=Vr.Artifact=Vr.ArtifactVerificationOptions_ObserverTimestampOptions=Vr.ArtifactVerificationOptions_TlogIntegratedTimestampOptions=Vr.ArtifactVerificationOptions_TimestampAuthorityOptions=Vr.ArtifactVerificationOptions_CtlogOptions=Vr.ArtifactVerificationOptions_TlogOptions=Vr.ArtifactVerificationOptions=Vr.PublicKeyIdentities=Vr.CertificateIdentities=Vr.CertificateIdentity=void 0;var QTe=D7(),Ig=Vw(),TTe=b7();Vr.CertificateIdentity={fromJSON(t){return{issuer:hi(t.issuer)?globalThis.String(t.issuer):"",san:hi(t.san)?Ig.SubjectAlternativeName.fromJSON(t.san):void 0,oids:globalThis.Array.isArray(t?.oids)?t.oids.map(e=>Ig.ObjectIdentifierValuePair.fromJSON(e)):[]}},toJSON(t){let e={};return t.issuer!==""&&(e.issuer=t.issuer),t.san!==void 0&&(e.san=Ig.SubjectAlternativeName.toJSON(t.san)),t.oids?.length&&(e.oids=t.oids.map(r=>Ig.ObjectIdentifierValuePair.toJSON(r))),e}};Vr.CertificateIdentities={fromJSON(t){return{identities:globalThis.Array.isArray(t?.identities)?t.identities.map(e=>Vr.CertificateIdentity.fromJSON(e)):[]}},toJSON(t){let e={};return t.identities?.length&&(e.identities=t.identities.map(r=>Vr.CertificateIdentity.toJSON(r))),e}};Vr.PublicKeyIdentities={fromJSON(t){return{publicKeys:globalThis.Array.isArray(t?.publicKeys)?t.publicKeys.map(e=>Ig.PublicKey.fromJSON(e)):[]}},toJSON(t){let e={};return t.publicKeys?.length&&(e.publicKeys=t.publicKeys.map(r=>Ig.PublicKey.toJSON(r))),e}};Vr.ArtifactVerificationOptions={fromJSON(t){return{signers:hi(t.certificateIdentities)?{$case:"certificateIdentities",certificateIdentities:Vr.CertificateIdentities.fromJSON(t.certificateIdentities)}:hi(t.publicKeys)?{$case:"publicKeys",publicKeys:Vr.PublicKeyIdentities.fromJSON(t.publicKeys)}:void 0,tlogOptions:hi(t.tlogOptions)?Vr.ArtifactVerificationOptions_TlogOptions.fromJSON(t.tlogOptions):void 0,ctlogOptions:hi(t.ctlogOptions)?Vr.ArtifactVerificationOptions_CtlogOptions.fromJSON(t.ctlogOptions):void 0,tsaOptions:hi(t.tsaOptions)?Vr.ArtifactVerificationOptions_TimestampAuthorityOptions.fromJSON(t.tsaOptions):void 0,integratedTsOptions:hi(t.integratedTsOptions)?Vr.ArtifactVerificationOptions_TlogIntegratedTimestampOptions.fromJSON(t.integratedTsOptions):void 0,observerOptions:hi(t.observerOptions)?Vr.ArtifactVerificationOptions_ObserverTimestampOptions.fromJSON(t.observerOptions):void 0}},toJSON(t){let e={};return t.signers?.$case==="certificateIdentities"?e.certificateIdentities=Vr.CertificateIdentities.toJSON(t.signers.certificateIdentities):t.signers?.$case==="publicKeys"&&(e.publicKeys=Vr.PublicKeyIdentities.toJSON(t.signers.publicKeys)),t.tlogOptions!==void 0&&(e.tlogOptions=Vr.ArtifactVerificationOptions_TlogOptions.toJSON(t.tlogOptions)),t.ctlogOptions!==void 0&&(e.ctlogOptions=Vr.ArtifactVerificationOptions_CtlogOptions.toJSON(t.ctlogOptions)),t.tsaOptions!==void 0&&(e.tsaOptions=Vr.ArtifactVerificationOptions_TimestampAuthorityOptions.toJSON(t.tsaOptions)),t.integratedTsOptions!==void 0&&(e.integratedTsOptions=Vr.ArtifactVerificationOptions_TlogIntegratedTimestampOptions.toJSON(t.integratedTsOptions)),t.observerOptions!==void 0&&(e.observerOptions=Vr.ArtifactVerificationOptions_ObserverTimestampOptions.toJSON(t.observerOptions)),e}};Vr.ArtifactVerificationOptions_TlogOptions={fromJSON(t){return{threshold:hi(t.threshold)?globalThis.Number(t.threshold):0,performOnlineVerification:hi(t.performOnlineVerification)?globalThis.Boolean(t.performOnlineVerification):!1,disable:hi(t.disable)?globalThis.Boolean(t.disable):!1}},toJSON(t){let e={};return t.threshold!==0&&(e.threshold=Math.round(t.threshold)),t.performOnlineVerification!==!1&&(e.performOnlineVerification=t.performOnlineVerification),t.disable!==!1&&(e.disable=t.disable),e}};Vr.ArtifactVerificationOptions_CtlogOptions={fromJSON(t){return{threshold:hi(t.threshold)?globalThis.Number(t.threshold):0,disable:hi(t.disable)?globalThis.Boolean(t.disable):!1}},toJSON(t){let e={};return t.threshold!==0&&(e.threshold=Math.round(t.threshold)),t.disable!==!1&&(e.disable=t.disable),e}};Vr.ArtifactVerificationOptions_TimestampAuthorityOptions={fromJSON(t){return{threshold:hi(t.threshold)?globalThis.Number(t.threshold):0,disable:hi(t.disable)?globalThis.Boolean(t.disable):!1}},toJSON(t){let e={};return t.threshold!==0&&(e.threshold=Math.round(t.threshold)),t.disable!==!1&&(e.disable=t.disable),e}};Vr.ArtifactVerificationOptions_TlogIntegratedTimestampOptions={fromJSON(t){return{threshold:hi(t.threshold)?globalThis.Number(t.threshold):0,disable:hi(t.disable)?globalThis.Boolean(t.disable):!1}},toJSON(t){let e={};return t.threshold!==0&&(e.threshold=Math.round(t.threshold)),t.disable!==!1&&(e.disable=t.disable),e}};Vr.ArtifactVerificationOptions_ObserverTimestampOptions={fromJSON(t){return{threshold:hi(t.threshold)?globalThis.Number(t.threshold):0,disable:hi(t.disable)?globalThis.Boolean(t.disable):!1}},toJSON(t){let e={};return t.threshold!==0&&(e.threshold=Math.round(t.threshold)),t.disable!==!1&&(e.disable=t.disable),e}};Vr.Artifact={fromJSON(t){return{data:hi(t.artifactUri)?{$case:"artifactUri",artifactUri:globalThis.String(t.artifactUri)}:hi(t.artifact)?{$case:"artifact",artifact:Buffer.from(eLt(t.artifact))}:hi(t.artifactDigest)?{$case:"artifactDigest",artifactDigest:Ig.HashOutput.fromJSON(t.artifactDigest)}:void 0}},toJSON(t){let e={};return t.data?.$case==="artifactUri"?e.artifactUri=t.data.artifactUri:t.data?.$case==="artifact"?e.artifact=tLt(t.data.artifact):t.data?.$case==="artifactDigest"&&(e.artifactDigest=Ig.HashOutput.toJSON(t.data.artifactDigest)),e}};Vr.Input={fromJSON(t){return{artifactTrustRoot:hi(t.artifactTrustRoot)?TTe.TrustedRoot.fromJSON(t.artifactTrustRoot):void 0,artifactVerificationOptions:hi(t.artifactVerificationOptions)?Vr.ArtifactVerificationOptions.fromJSON(t.artifactVerificationOptions):void 0,bundle:hi(t.bundle)?QTe.Bundle.fromJSON(t.bundle):void 0,artifact:hi(t.artifact)?Vr.Artifact.fromJSON(t.artifact):void 0}},toJSON(t){let e={};return t.artifactTrustRoot!==void 0&&(e.artifactTrustRoot=TTe.TrustedRoot.toJSON(t.artifactTrustRoot)),t.artifactVerificationOptions!==void 0&&(e.artifactVerificationOptions=Vr.ArtifactVerificationOptions.toJSON(t.artifactVerificationOptions)),t.bundle!==void 0&&(e.bundle=QTe.Bundle.toJSON(t.bundle)),t.artifact!==void 0&&(e.artifact=Vr.Artifact.toJSON(t.artifact)),e}};function eLt(t){return Uint8Array.from(globalThis.Buffer.from(t,"base64"))}function tLt(t){return globalThis.Buffer.from(t).toString("base64")}function hi(t){return t!=null}});var Rb=L(eu=>{"use strict";var rLt=eu&&eu.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),Kw=eu&&eu.__exportStar||function(t,e){for(var r in t)r!=="default"&&!Object.prototype.hasOwnProperty.call(e,r)&&rLt(e,t,r)};Object.defineProperty(eu,"__esModule",{value:!0});Kw(v7(),eu);Kw(D7(),eu);Kw(Vw(),eu);Kw(S7(),eu);Kw(b7(),eu);Kw(RTe(),eu)});var tO=L(Cl=>{"use strict";Object.defineProperty(Cl,"__esModule",{value:!0});Cl.BUNDLE_V03_MEDIA_TYPE=Cl.BUNDLE_V03_LEGACY_MEDIA_TYPE=Cl.BUNDLE_V02_MEDIA_TYPE=Cl.BUNDLE_V01_MEDIA_TYPE=void 0;Cl.isBundleWithCertificateChain=nLt;Cl.isBundleWithPublicKey=iLt;Cl.isBundleWithMessageSignature=sLt;Cl.isBundleWithDsseEnvelope=oLt;Cl.BUNDLE_V01_MEDIA_TYPE="application/vnd.dev.sigstore.bundle+json;version=0.1";Cl.BUNDLE_V02_MEDIA_TYPE="application/vnd.dev.sigstore.bundle+json;version=0.2";Cl.BUNDLE_V03_LEGACY_MEDIA_TYPE="application/vnd.dev.sigstore.bundle+json;version=0.3";Cl.BUNDLE_V03_MEDIA_TYPE="application/vnd.dev.sigstore.bundle.v0.3+json";function nLt(t){return t.verificationMaterial.content.$case==="x509CertificateChain"}function iLt(t){return t.verificationMaterial.content.$case==="publicKey"}function sLt(t){return t.content.$case==="messageSignature"}function oLt(t){return t.content.$case==="dsseEnvelope"}});var NTe=L(nO=>{"use strict";Object.defineProperty(nO,"__esModule",{value:!0});nO.toMessageSignatureBundle=lLt;nO.toDSSEBundle=cLt;var aLt=Rb(),rO=tO();function lLt(t){return{mediaType:t.certificateChain?rO.BUNDLE_V02_MEDIA_TYPE:rO.BUNDLE_V03_MEDIA_TYPE,content:{$case:"messageSignature",messageSignature:{messageDigest:{algorithm:aLt.HashAlgorithm.SHA2_256,digest:t.digest},signature:t.signature}},verificationMaterial:FTe(t)}}function cLt(t){return{mediaType:t.certificateChain?rO.BUNDLE_V02_MEDIA_TYPE:rO.BUNDLE_V03_MEDIA_TYPE,content:{$case:"dsseEnvelope",dsseEnvelope:uLt(t)},verificationMaterial:FTe(t)}}function uLt(t){return{payloadType:t.artifactType,payload:t.artifact,signatures:[fLt(t)]}}function fLt(t){return{keyid:t.keyHint||"",sig:t.signature}}function FTe(t){return{content:ALt(t),tlogEntries:[],timestampVerificationData:{rfc3161Timestamps:[]}}}function ALt(t){return t.certificate?t.certificateChain?{$case:"x509CertificateChain",x509CertificateChain:{certificates:[{rawBytes:t.certificate}]}}:{$case:"certificate",certificate:{rawBytes:t.certificate}}:{$case:"publicKey",publicKey:{hint:t.keyHint||""}}}});var x7=L(iO=>{"use strict";Object.defineProperty(iO,"__esModule",{value:!0});iO.ValidationError=void 0;var P7=class extends Error{constructor(e,r){super(e),this.fields=r}};iO.ValidationError=P7});var k7=L(ty=>{"use strict";Object.defineProperty(ty,"__esModule",{value:!0});ty.assertBundle=pLt;ty.assertBundleV01=OTe;ty.isBundleV01=hLt;ty.assertBundleV02=gLt;ty.assertBundleLatest=dLt;var sO=x7();function pLt(t){let e=oO(t);if(e.length>0)throw new sO.ValidationError("invalid bundle",e)}function OTe(t){let e=[];if(e.push(...oO(t)),e.push(...mLt(t)),e.length>0)throw new sO.ValidationError("invalid v0.1 bundle",e)}function hLt(t){try{return OTe(t),!0}catch{return!1}}function gLt(t){let e=[];if(e.push(...oO(t)),e.push(...LTe(t)),e.length>0)throw new sO.ValidationError("invalid v0.2 bundle",e)}function dLt(t){let e=[];if(e.push(...oO(t)),e.push(...LTe(t)),e.push(...yLt(t)),e.length>0)throw new sO.ValidationError("invalid bundle",e)}function oO(t){let e=[];if((t.mediaType===void 0||!t.mediaType.match(/^application\/vnd\.dev\.sigstore\.bundle\+json;version=\d\.\d/)&&!t.mediaType.match(/^application\/vnd\.dev\.sigstore\.bundle\.v\d\.\d\+json/))&&e.push("mediaType"),t.content===void 0)e.push("content");else switch(t.content.$case){case"messageSignature":t.content.messageSignature.messageDigest===void 0?e.push("content.messageSignature.messageDigest"):t.content.messageSignature.messageDigest.digest.length===0&&e.push("content.messageSignature.messageDigest.digest"),t.content.messageSignature.signature.length===0&&e.push("content.messageSignature.signature");break;case"dsseEnvelope":t.content.dsseEnvelope.payload.length===0&&e.push("content.dsseEnvelope.payload"),t.content.dsseEnvelope.signatures.length!==1?e.push("content.dsseEnvelope.signatures"):t.content.dsseEnvelope.signatures[0].sig.length===0&&e.push("content.dsseEnvelope.signatures[0].sig");break}if(t.verificationMaterial===void 0)e.push("verificationMaterial");else{if(t.verificationMaterial.content===void 0)e.push("verificationMaterial.content");else switch(t.verificationMaterial.content.$case){case"x509CertificateChain":t.verificationMaterial.content.x509CertificateChain.certificates.length===0&&e.push("verificationMaterial.content.x509CertificateChain.certificates"),t.verificationMaterial.content.x509CertificateChain.certificates.forEach((r,s)=>{r.rawBytes.length===0&&e.push(`verificationMaterial.content.x509CertificateChain.certificates[${s}].rawBytes`)});break;case"certificate":t.verificationMaterial.content.certificate.rawBytes.length===0&&e.push("verificationMaterial.content.certificate.rawBytes");break}t.verificationMaterial.tlogEntries===void 0?e.push("verificationMaterial.tlogEntries"):t.verificationMaterial.tlogEntries.length>0&&t.verificationMaterial.tlogEntries.forEach((r,s)=>{r.logId===void 0&&e.push(`verificationMaterial.tlogEntries[${s}].logId`),r.kindVersion===void 0&&e.push(`verificationMaterial.tlogEntries[${s}].kindVersion`)})}return e}function mLt(t){let e=[];return t.verificationMaterial&&t.verificationMaterial.tlogEntries?.length>0&&t.verificationMaterial.tlogEntries.forEach((r,s)=>{r.inclusionPromise===void 0&&e.push(`verificationMaterial.tlogEntries[${s}].inclusionPromise`)}),e}function LTe(t){let e=[];return t.verificationMaterial&&t.verificationMaterial.tlogEntries?.length>0&&t.verificationMaterial.tlogEntries.forEach((r,s)=>{r.inclusionProof===void 0?e.push(`verificationMaterial.tlogEntries[${s}].inclusionProof`):r.inclusionProof.checkpoint===void 0&&e.push(`verificationMaterial.tlogEntries[${s}].inclusionProof.checkpoint`)}),e}function yLt(t){let e=[];return t.verificationMaterial?.content?.$case==="x509CertificateChain"&&e.push("verificationMaterial.content.$case"),e}});var _Te=L(SA=>{"use strict";Object.defineProperty(SA,"__esModule",{value:!0});SA.envelopeToJSON=SA.envelopeFromJSON=SA.bundleToJSON=SA.bundleFromJSON=void 0;var aO=Rb(),MTe=tO(),Q7=k7(),ELt=t=>{let e=aO.Bundle.fromJSON(t);switch(e.mediaType){case MTe.BUNDLE_V01_MEDIA_TYPE:(0,Q7.assertBundleV01)(e);break;case MTe.BUNDLE_V02_MEDIA_TYPE:(0,Q7.assertBundleV02)(e);break;default:(0,Q7.assertBundleLatest)(e);break}return e};SA.bundleFromJSON=ELt;var ILt=t=>aO.Bundle.toJSON(t);SA.bundleToJSON=ILt;var CLt=t=>aO.Envelope.fromJSON(t);SA.envelopeFromJSON=CLt;var wLt=t=>aO.Envelope.toJSON(t);SA.envelopeToJSON=wLt});var Nb=L(Zr=>{"use strict";Object.defineProperty(Zr,"__esModule",{value:!0});Zr.isBundleV01=Zr.assertBundleV02=Zr.assertBundleV01=Zr.assertBundleLatest=Zr.assertBundle=Zr.envelopeToJSON=Zr.envelopeFromJSON=Zr.bundleToJSON=Zr.bundleFromJSON=Zr.ValidationError=Zr.isBundleWithPublicKey=Zr.isBundleWithMessageSignature=Zr.isBundleWithDsseEnvelope=Zr.isBundleWithCertificateChain=Zr.BUNDLE_V03_MEDIA_TYPE=Zr.BUNDLE_V03_LEGACY_MEDIA_TYPE=Zr.BUNDLE_V02_MEDIA_TYPE=Zr.BUNDLE_V01_MEDIA_TYPE=Zr.toMessageSignatureBundle=Zr.toDSSEBundle=void 0;var UTe=NTe();Object.defineProperty(Zr,"toDSSEBundle",{enumerable:!0,get:function(){return UTe.toDSSEBundle}});Object.defineProperty(Zr,"toMessageSignatureBundle",{enumerable:!0,get:function(){return UTe.toMessageSignatureBundle}});var Cg=tO();Object.defineProperty(Zr,"BUNDLE_V01_MEDIA_TYPE",{enumerable:!0,get:function(){return Cg.BUNDLE_V01_MEDIA_TYPE}});Object.defineProperty(Zr,"BUNDLE_V02_MEDIA_TYPE",{enumerable:!0,get:function(){return Cg.BUNDLE_V02_MEDIA_TYPE}});Object.defineProperty(Zr,"BUNDLE_V03_LEGACY_MEDIA_TYPE",{enumerable:!0,get:function(){return Cg.BUNDLE_V03_LEGACY_MEDIA_TYPE}});Object.defineProperty(Zr,"BUNDLE_V03_MEDIA_TYPE",{enumerable:!0,get:function(){return Cg.BUNDLE_V03_MEDIA_TYPE}});Object.defineProperty(Zr,"isBundleWithCertificateChain",{enumerable:!0,get:function(){return Cg.isBundleWithCertificateChain}});Object.defineProperty(Zr,"isBundleWithDsseEnvelope",{enumerable:!0,get:function(){return Cg.isBundleWithDsseEnvelope}});Object.defineProperty(Zr,"isBundleWithMessageSignature",{enumerable:!0,get:function(){return Cg.isBundleWithMessageSignature}});Object.defineProperty(Zr,"isBundleWithPublicKey",{enumerable:!0,get:function(){return Cg.isBundleWithPublicKey}});var BLt=x7();Object.defineProperty(Zr,"ValidationError",{enumerable:!0,get:function(){return BLt.ValidationError}});var lO=_Te();Object.defineProperty(Zr,"bundleFromJSON",{enumerable:!0,get:function(){return lO.bundleFromJSON}});Object.defineProperty(Zr,"bundleToJSON",{enumerable:!0,get:function(){return lO.bundleToJSON}});Object.defineProperty(Zr,"envelopeFromJSON",{enumerable:!0,get:function(){return lO.envelopeFromJSON}});Object.defineProperty(Zr,"envelopeToJSON",{enumerable:!0,get:function(){return lO.envelopeToJSON}});var Fb=k7();Object.defineProperty(Zr,"assertBundle",{enumerable:!0,get:function(){return Fb.assertBundle}});Object.defineProperty(Zr,"assertBundleLatest",{enumerable:!0,get:function(){return Fb.assertBundleLatest}});Object.defineProperty(Zr,"assertBundleV01",{enumerable:!0,get:function(){return Fb.assertBundleV01}});Object.defineProperty(Zr,"assertBundleV02",{enumerable:!0,get:function(){return Fb.assertBundleV02}});Object.defineProperty(Zr,"isBundleV01",{enumerable:!0,get:function(){return Fb.isBundleV01}})});var Ob=L(uO=>{"use strict";Object.defineProperty(uO,"__esModule",{value:!0});uO.ByteStream=void 0;var T7=class extends Error{},cO=class t{constructor(e){this.start=0,e?(this.buf=e,this.view=Buffer.from(e)):(this.buf=new ArrayBuffer(0),this.view=Buffer.from(this.buf))}get buffer(){return this.view.subarray(0,this.start)}get length(){return this.view.byteLength}get position(){return this.start}seek(e){this.start=e}slice(e,r){let s=e+r;if(s>this.length)throw new T7("request past end of buffer");return this.view.subarray(e,s)}appendChar(e){this.ensureCapacity(1),this.view[this.start]=e,this.start+=1}appendUint16(e){this.ensureCapacity(2);let r=new Uint16Array([e]),s=new Uint8Array(r.buffer);this.view[this.start]=s[1],this.view[this.start+1]=s[0],this.start+=2}appendUint24(e){this.ensureCapacity(3);let r=new Uint32Array([e]),s=new Uint8Array(r.buffer);this.view[this.start]=s[2],this.view[this.start+1]=s[1],this.view[this.start+2]=s[0],this.start+=3}appendView(e){this.ensureCapacity(e.length),this.view.set(e,this.start),this.start+=e.length}getBlock(e){if(e<=0)return Buffer.alloc(0);if(this.start+e>this.view.length)throw new Error("request past end of buffer");let r=this.view.subarray(this.start,this.start+e);return this.start+=e,r}getUint8(){return this.getBlock(1)[0]}getUint16(){let e=this.getBlock(2);return e[0]<<8|e[1]}ensureCapacity(e){if(this.start+e>this.view.byteLength){let r=t.BLOCK_SIZE+(e>t.BLOCK_SIZE?e:0);this.realloc(this.view.byteLength+r)}}realloc(e){let r=new ArrayBuffer(e),s=Buffer.from(r);s.set(this.view),this.buf=r,this.view=s}};uO.ByteStream=cO;cO.BLOCK_SIZE=1024});var fO=L(Jw=>{"use strict";Object.defineProperty(Jw,"__esModule",{value:!0});Jw.ASN1TypeError=Jw.ASN1ParseError=void 0;var R7=class extends Error{};Jw.ASN1ParseError=R7;var F7=class extends Error{};Jw.ASN1TypeError=F7});var jTe=L(AO=>{"use strict";Object.defineProperty(AO,"__esModule",{value:!0});AO.decodeLength=vLt;AO.encodeLength=SLt;var HTe=fO();function vLt(t){let e=t.getUint8();if(!(e&128))return e;let r=e&127;if(r>6)throw new HTe.ASN1ParseError("length exceeds 6 byte limit");let s=0;for(let a=0;a0n;)r.unshift(Number(e&255n)),e=e>>8n;return Buffer.from([128|r.length,...r])}});var GTe=L(wg=>{"use strict";Object.defineProperty(wg,"__esModule",{value:!0});wg.parseInteger=PLt;wg.parseStringASCII=qTe;wg.parseTime=xLt;wg.parseOID=kLt;wg.parseBoolean=QLt;wg.parseBitString=TLt;var DLt=/^(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\.\d{3})?Z$/,bLt=/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\.\d{3})?Z$/;function PLt(t){let e=0,r=t.length,s=t[e],a=s>127,n=a?255:0;for(;s==n&&++e=50?1900:2e3,s[1]=a.toString()}return new Date(`${s[1]}-${s[2]}-${s[3]}T${s[4]}:${s[5]}:${s[6]}Z`)}function kLt(t){let e=0,r=t.length,s=t[e++],a=Math.floor(s/40),n=s%40,c=`${a}.${n}`,f=0;for(;e=f;--p)a.push(c>>p&1)}return a}});var YTe=L(pO=>{"use strict";Object.defineProperty(pO,"__esModule",{value:!0});pO.ASN1Tag=void 0;var WTe=fO(),ry={BOOLEAN:1,INTEGER:2,BIT_STRING:3,OCTET_STRING:4,OBJECT_IDENTIFIER:6,SEQUENCE:16,SET:17,PRINTABLE_STRING:19,UTC_TIME:23,GENERALIZED_TIME:24},N7={UNIVERSAL:0,APPLICATION:1,CONTEXT_SPECIFIC:2,PRIVATE:3},O7=class{constructor(e){if(this.number=e&31,this.constructed=(e&32)===32,this.class=e>>6,this.number===31)throw new WTe.ASN1ParseError("long form tags not supported");if(this.class===N7.UNIVERSAL&&this.number===0)throw new WTe.ASN1ParseError("unsupported tag 0x00")}isUniversal(){return this.class===N7.UNIVERSAL}isContextSpecific(e){let r=this.class===N7.CONTEXT_SPECIFIC;return e!==void 0?r&&this.number===e:r}isBoolean(){return this.isUniversal()&&this.number===ry.BOOLEAN}isInteger(){return this.isUniversal()&&this.number===ry.INTEGER}isBitString(){return this.isUniversal()&&this.number===ry.BIT_STRING}isOctetString(){return this.isUniversal()&&this.number===ry.OCTET_STRING}isOID(){return this.isUniversal()&&this.number===ry.OBJECT_IDENTIFIER}isUTCTime(){return this.isUniversal()&&this.number===ry.UTC_TIME}isGeneralizedTime(){return this.isUniversal()&&this.number===ry.GENERALIZED_TIME}toDER(){return this.number|(this.constructed?32:0)|this.class<<6}};pO.ASN1Tag=O7});var zTe=L(gO=>{"use strict";Object.defineProperty(gO,"__esModule",{value:!0});gO.ASN1Obj=void 0;var L7=Ob(),ny=fO(),KTe=jTe(),zw=GTe(),RLt=YTe(),hO=class{constructor(e,r,s){this.tag=e,this.value=r,this.subs=s}static parseBuffer(e){return JTe(new L7.ByteStream(e))}toDER(){let e=new L7.ByteStream;if(this.subs.length>0)for(let a of this.subs)e.appendView(a.toDER());else e.appendView(this.value);let r=e.buffer,s=new L7.ByteStream;return s.appendChar(this.tag.toDER()),s.appendView((0,KTe.encodeLength)(r.length)),s.appendView(r),s.buffer}toBoolean(){if(!this.tag.isBoolean())throw new ny.ASN1TypeError("not a boolean");return(0,zw.parseBoolean)(this.value)}toInteger(){if(!this.tag.isInteger())throw new ny.ASN1TypeError("not an integer");return(0,zw.parseInteger)(this.value)}toOID(){if(!this.tag.isOID())throw new ny.ASN1TypeError("not an OID");return(0,zw.parseOID)(this.value)}toDate(){switch(!0){case this.tag.isUTCTime():return(0,zw.parseTime)(this.value,!0);case this.tag.isGeneralizedTime():return(0,zw.parseTime)(this.value,!1);default:throw new ny.ASN1TypeError("not a date")}}toBitString(){if(!this.tag.isBitString())throw new ny.ASN1TypeError("not a bit string");return(0,zw.parseBitString)(this.value)}};gO.ASN1Obj=hO;function JTe(t){let e=new RLt.ASN1Tag(t.getUint8()),r=(0,KTe.decodeLength)(t),s=t.slice(t.position,r),a=t.position,n=[];if(e.constructed)n=VTe(t,r);else if(e.isOctetString())try{n=VTe(t,r)}catch{}return n.length===0&&t.seek(a+r),new hO(e,s,n)}function VTe(t,e){let r=t.position+e;if(r>t.length)throw new ny.ASN1ParseError("invalid length");let s=[];for(;t.position{"use strict";Object.defineProperty(dO,"__esModule",{value:!0});dO.ASN1Obj=void 0;var FLt=zTe();Object.defineProperty(dO,"ASN1Obj",{enumerable:!0,get:function(){return FLt.ASN1Obj}})});var Zw=L(Bg=>{"use strict";var NLt=Bg&&Bg.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(Bg,"__esModule",{value:!0});Bg.createPublicKey=OLt;Bg.digest=LLt;Bg.verify=MLt;Bg.bufferEqual=_Lt;var Lb=NLt(ye("crypto"));function OLt(t,e="spki"){return typeof t=="string"?Lb.default.createPublicKey(t):Lb.default.createPublicKey({key:t,format:"der",type:e})}function LLt(t,...e){let r=Lb.default.createHash(t);for(let s of e)r.update(s);return r.digest()}function MLt(t,e,r,s){try{return Lb.default.verify(s,t,e,r)}catch{return!1}}function _Lt(t,e){try{return Lb.default.timingSafeEqual(t,e)}catch{return!1}}});var ZTe=L(M7=>{"use strict";Object.defineProperty(M7,"__esModule",{value:!0});M7.preAuthEncoding=HLt;var ULt="DSSEv1";function HLt(t,e){let r=[ULt,t.length,t,e.length,""].join(" ");return Buffer.concat([Buffer.from(r,"ascii"),e])}});var eRe=L(yO=>{"use strict";Object.defineProperty(yO,"__esModule",{value:!0});yO.base64Encode=jLt;yO.base64Decode=qLt;var XTe="base64",$Te="utf-8";function jLt(t){return Buffer.from(t,$Te).toString(XTe)}function qLt(t){return Buffer.from(t,XTe).toString($Te)}});var tRe=L(U7=>{"use strict";Object.defineProperty(U7,"__esModule",{value:!0});U7.canonicalize=_7;function _7(t){let e="";if(t===null||typeof t!="object"||t.toJSON!=null)e+=JSON.stringify(t);else if(Array.isArray(t)){e+="[";let r=!0;t.forEach(s=>{r||(e+=","),r=!1,e+=_7(s)}),e+="]"}else{e+="{";let r=!0;Object.keys(t).sort().forEach(s=>{r||(e+=","),r=!1,e+=JSON.stringify(s),e+=":",e+=_7(t[s])}),e+="}"}return e}});var H7=L(EO=>{"use strict";Object.defineProperty(EO,"__esModule",{value:!0});EO.toDER=YLt;EO.fromDER=VLt;var GLt=/-----BEGIN (.*)-----/,WLt=/-----END (.*)-----/;function YLt(t){let e="";return t.split(` +`).forEach(r=>{r.match(GLt)||r.match(WLt)||(e+=r)}),Buffer.from(e,"base64")}function VLt(t,e="CERTIFICATE"){let s=t.toString("base64").match(/.{1,64}/g)||"";return[`-----BEGIN ${e}-----`,...s,`-----END ${e}-----`].join(` +`).concat(` +`)}});var IO=L(Xw=>{"use strict";Object.defineProperty(Xw,"__esModule",{value:!0});Xw.SHA2_HASH_ALGOS=Xw.ECDSA_SIGNATURE_ALGOS=void 0;Xw.ECDSA_SIGNATURE_ALGOS={"1.2.840.10045.4.3.1":"sha224","1.2.840.10045.4.3.2":"sha256","1.2.840.10045.4.3.3":"sha384","1.2.840.10045.4.3.4":"sha512"};Xw.SHA2_HASH_ALGOS={"2.16.840.1.101.3.4.2.1":"sha256","2.16.840.1.101.3.4.2.2":"sha384","2.16.840.1.101.3.4.2.3":"sha512"}});var q7=L(CO=>{"use strict";Object.defineProperty(CO,"__esModule",{value:!0});CO.RFC3161TimestampVerificationError=void 0;var j7=class extends Error{};CO.RFC3161TimestampVerificationError=j7});var nRe=L(DA=>{"use strict";var KLt=DA&&DA.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),JLt=DA&&DA.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),zLt=DA&&DA.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&KLt(e,t,r);return JLt(e,t),e};Object.defineProperty(DA,"__esModule",{value:!0});DA.TSTInfo=void 0;var rRe=zLt(Zw()),ZLt=IO(),XLt=q7(),G7=class{constructor(e){this.root=e}get version(){return this.root.subs[0].toInteger()}get genTime(){return this.root.subs[4].toDate()}get messageImprintHashAlgorithm(){let e=this.messageImprintObj.subs[0].subs[0].toOID();return ZLt.SHA2_HASH_ALGOS[e]}get messageImprintHashedMessage(){return this.messageImprintObj.subs[1].value}get raw(){return this.root.toDER()}verify(e){let r=rRe.digest(this.messageImprintHashAlgorithm,e);if(!rRe.bufferEqual(r,this.messageImprintHashedMessage))throw new XLt.RFC3161TimestampVerificationError("message imprint does not match artifact")}get messageImprintObj(){return this.root.subs[2]}};DA.TSTInfo=G7});var sRe=L(bA=>{"use strict";var $Lt=bA&&bA.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),eMt=bA&&bA.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),tMt=bA&&bA.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&$Lt(e,t,r);return eMt(e,t),e};Object.defineProperty(bA,"__esModule",{value:!0});bA.RFC3161Timestamp=void 0;var rMt=mO(),W7=tMt(Zw()),iRe=IO(),Mb=q7(),nMt=nRe(),iMt="1.2.840.113549.1.7.2",sMt="1.2.840.113549.1.9.16.1.4",oMt="1.2.840.113549.1.9.4",Y7=class t{constructor(e){this.root=e}static parse(e){let r=rMt.ASN1Obj.parseBuffer(e);return new t(r)}get status(){return this.pkiStatusInfoObj.subs[0].toInteger()}get contentType(){return this.contentTypeObj.toOID()}get eContentType(){return this.eContentTypeObj.toOID()}get signingTime(){return this.tstInfo.genTime}get signerIssuer(){return this.signerSidObj.subs[0].value}get signerSerialNumber(){return this.signerSidObj.subs[1].value}get signerDigestAlgorithm(){let e=this.signerDigestAlgorithmObj.subs[0].toOID();return iRe.SHA2_HASH_ALGOS[e]}get signatureAlgorithm(){let e=this.signatureAlgorithmObj.subs[0].toOID();return iRe.ECDSA_SIGNATURE_ALGOS[e]}get signatureValue(){return this.signatureValueObj.value}get tstInfo(){return new nMt.TSTInfo(this.eContentObj.subs[0].subs[0])}verify(e,r){if(!this.timeStampTokenObj)throw new Mb.RFC3161TimestampVerificationError("timeStampToken is missing");if(this.contentType!==iMt)throw new Mb.RFC3161TimestampVerificationError(`incorrect content type: ${this.contentType}`);if(this.eContentType!==sMt)throw new Mb.RFC3161TimestampVerificationError(`incorrect encapsulated content type: ${this.eContentType}`);this.tstInfo.verify(e),this.verifyMessageDigest(),this.verifySignature(r)}verifyMessageDigest(){let e=W7.digest(this.signerDigestAlgorithm,this.tstInfo.raw),r=this.messageDigestAttributeObj.subs[1].subs[0].value;if(!W7.bufferEqual(e,r))throw new Mb.RFC3161TimestampVerificationError("signed data does not match tstInfo")}verifySignature(e){let r=this.signedAttrsObj.toDER();if(r[0]=49,!W7.verify(r,e,this.signatureValue,this.signatureAlgorithm))throw new Mb.RFC3161TimestampVerificationError("signature verification failed")}get pkiStatusInfoObj(){return this.root.subs[0]}get timeStampTokenObj(){return this.root.subs[1]}get contentTypeObj(){return this.timeStampTokenObj.subs[0]}get signedDataObj(){return this.timeStampTokenObj.subs.find(r=>r.tag.isContextSpecific(0)).subs[0]}get encapContentInfoObj(){return this.signedDataObj.subs[2]}get signerInfosObj(){let e=this.signedDataObj;return e.subs[e.subs.length-1]}get signerInfoObj(){return this.signerInfosObj.subs[0]}get eContentTypeObj(){return this.encapContentInfoObj.subs[0]}get eContentObj(){return this.encapContentInfoObj.subs[1]}get signedAttrsObj(){return this.signerInfoObj.subs.find(r=>r.tag.isContextSpecific(0))}get messageDigestAttributeObj(){return this.signedAttrsObj.subs.find(r=>r.subs[0].tag.isOID()&&r.subs[0].toOID()===oMt)}get signerSidObj(){return this.signerInfoObj.subs[1]}get signerDigestAlgorithmObj(){return this.signerInfoObj.subs[2]}get signatureAlgorithmObj(){return this.signerInfoObj.subs[4]}get signatureValueObj(){return this.signerInfoObj.subs[5]}};bA.RFC3161Timestamp=Y7});var oRe=L(wO=>{"use strict";Object.defineProperty(wO,"__esModule",{value:!0});wO.RFC3161Timestamp=void 0;var aMt=sRe();Object.defineProperty(wO,"RFC3161Timestamp",{enumerable:!0,get:function(){return aMt.RFC3161Timestamp}})});var lRe=L(PA=>{"use strict";var lMt=PA&&PA.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),cMt=PA&&PA.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),uMt=PA&&PA.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&lMt(e,t,r);return cMt(e,t),e};Object.defineProperty(PA,"__esModule",{value:!0});PA.SignedCertificateTimestamp=void 0;var fMt=uMt(Zw()),aRe=Ob(),V7=class t{constructor(e){this.version=e.version,this.logID=e.logID,this.timestamp=e.timestamp,this.extensions=e.extensions,this.hashAlgorithm=e.hashAlgorithm,this.signatureAlgorithm=e.signatureAlgorithm,this.signature=e.signature}get datetime(){return new Date(Number(this.timestamp.readBigInt64BE()))}get algorithm(){switch(this.hashAlgorithm){case 0:return"none";case 1:return"md5";case 2:return"sha1";case 3:return"sha224";case 4:return"sha256";case 5:return"sha384";case 6:return"sha512";default:return"unknown"}}verify(e,r){let s=new aRe.ByteStream;return s.appendChar(this.version),s.appendChar(0),s.appendView(this.timestamp),s.appendUint16(1),s.appendView(e),s.appendUint16(this.extensions.byteLength),this.extensions.byteLength>0&&s.appendView(this.extensions),fMt.verify(s.buffer,r,this.signature,this.algorithm)}static parse(e){let r=new aRe.ByteStream(e),s=r.getUint8(),a=r.getBlock(32),n=r.getBlock(8),c=r.getUint16(),f=r.getBlock(c),p=r.getUint8(),h=r.getUint8(),E=r.getUint16(),C=r.getBlock(E);if(r.position!==e.length)throw new Error("SCT buffer length mismatch");return new t({version:s,logID:a,timestamp:n,extensions:f,hashAlgorithm:p,signatureAlgorithm:h,signature:C})}};PA.SignedCertificateTimestamp=V7});var eK=L(oa=>{"use strict";Object.defineProperty(oa,"__esModule",{value:!0});oa.X509SCTExtension=oa.X509SubjectKeyIDExtension=oa.X509AuthorityKeyIDExtension=oa.X509SubjectAlternativeNameExtension=oa.X509KeyUsageExtension=oa.X509BasicConstraintsExtension=oa.X509Extension=void 0;var AMt=Ob(),pMt=lRe(),dh=class{constructor(e){this.root=e}get oid(){return this.root.subs[0].toOID()}get critical(){return this.root.subs.length===3?this.root.subs[1].toBoolean():!1}get value(){return this.extnValueObj.value}get valueObj(){return this.extnValueObj}get extnValueObj(){return this.root.subs[this.root.subs.length-1]}};oa.X509Extension=dh;var K7=class extends dh{get isCA(){return this.sequence.subs[0]?.toBoolean()??!1}get pathLenConstraint(){return this.sequence.subs.length>1?this.sequence.subs[1].toInteger():void 0}get sequence(){return this.extnValueObj.subs[0]}};oa.X509BasicConstraintsExtension=K7;var J7=class extends dh{get digitalSignature(){return this.bitString[0]===1}get keyCertSign(){return this.bitString[5]===1}get crlSign(){return this.bitString[6]===1}get bitString(){return this.extnValueObj.subs[0].toBitString()}};oa.X509KeyUsageExtension=J7;var z7=class extends dh{get rfc822Name(){return this.findGeneralName(1)?.value.toString("ascii")}get uri(){return this.findGeneralName(6)?.value.toString("ascii")}otherName(e){let r=this.findGeneralName(0);return r===void 0||r.subs[0].toOID()!==e?void 0:r.subs[1].subs[0].value.toString("ascii")}findGeneralName(e){return this.generalNames.find(r=>r.tag.isContextSpecific(e))}get generalNames(){return this.extnValueObj.subs[0].subs}};oa.X509SubjectAlternativeNameExtension=z7;var Z7=class extends dh{get keyIdentifier(){return this.findSequenceMember(0)?.value}findSequenceMember(e){return this.sequence.subs.find(r=>r.tag.isContextSpecific(e))}get sequence(){return this.extnValueObj.subs[0]}};oa.X509AuthorityKeyIDExtension=Z7;var X7=class extends dh{get keyIdentifier(){return this.extnValueObj.subs[0].value}};oa.X509SubjectKeyIDExtension=X7;var $7=class extends dh{constructor(e){super(e)}get signedCertificateTimestamps(){let e=this.extnValueObj.subs[0].value,r=new AMt.ByteStream(e),s=r.getUint16()+2,a=[];for(;r.position{"use strict";var hMt=sc&&sc.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),gMt=sc&&sc.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),uRe=sc&&sc.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&hMt(e,t,r);return gMt(e,t),e};Object.defineProperty(sc,"__esModule",{value:!0});sc.X509Certificate=sc.EXTENSION_OID_SCT=void 0;var dMt=mO(),cRe=uRe(Zw()),mMt=IO(),yMt=uRe(H7()),iy=eK(),EMt="2.5.29.14",IMt="2.5.29.15",CMt="2.5.29.17",wMt="2.5.29.19",BMt="2.5.29.35";sc.EXTENSION_OID_SCT="1.3.6.1.4.1.11129.2.4.2";var tK=class t{constructor(e){this.root=e}static parse(e){let r=typeof e=="string"?yMt.toDER(e):e,s=dMt.ASN1Obj.parseBuffer(r);return new t(s)}get tbsCertificate(){return this.tbsCertificateObj}get version(){return`v${(this.versionObj.subs[0].toInteger()+BigInt(1)).toString()}`}get serialNumber(){return this.serialNumberObj.value}get notBefore(){return this.validityObj.subs[0].toDate()}get notAfter(){return this.validityObj.subs[1].toDate()}get issuer(){return this.issuerObj.value}get subject(){return this.subjectObj.value}get publicKey(){return this.subjectPublicKeyInfoObj.toDER()}get signatureAlgorithm(){let e=this.signatureAlgorithmObj.subs[0].toOID();return mMt.ECDSA_SIGNATURE_ALGOS[e]}get signatureValue(){return this.signatureValueObj.value.subarray(1)}get subjectAltName(){let e=this.extSubjectAltName;return e?.uri||e?.rfc822Name}get extensions(){return this.extensionsObj?.subs[0]?.subs||[]}get extKeyUsage(){let e=this.findExtension(IMt);return e?new iy.X509KeyUsageExtension(e):void 0}get extBasicConstraints(){let e=this.findExtension(wMt);return e?new iy.X509BasicConstraintsExtension(e):void 0}get extSubjectAltName(){let e=this.findExtension(CMt);return e?new iy.X509SubjectAlternativeNameExtension(e):void 0}get extAuthorityKeyID(){let e=this.findExtension(BMt);return e?new iy.X509AuthorityKeyIDExtension(e):void 0}get extSubjectKeyID(){let e=this.findExtension(EMt);return e?new iy.X509SubjectKeyIDExtension(e):void 0}get extSCT(){let e=this.findExtension(sc.EXTENSION_OID_SCT);return e?new iy.X509SCTExtension(e):void 0}get isCA(){let e=this.extBasicConstraints?.isCA||!1;return this.extKeyUsage?e&&this.extKeyUsage.keyCertSign:e}extension(e){let r=this.findExtension(e);return r?new iy.X509Extension(r):void 0}verify(e){let r=e?.publicKey||this.publicKey,s=cRe.createPublicKey(r);return cRe.verify(this.tbsCertificate.toDER(),s,this.signatureValue,this.signatureAlgorithm)}validForDate(e){return this.notBefore<=e&&e<=this.notAfter}equals(e){return this.root.toDER().equals(e.root.toDER())}clone(){let e=this.root.toDER(),r=Buffer.alloc(e.length);return e.copy(r),t.parse(r)}findExtension(e){return this.extensions.find(r=>r.subs[0].toOID()===e)}get tbsCertificateObj(){return this.root.subs[0]}get signatureAlgorithmObj(){return this.root.subs[1]}get signatureValueObj(){return this.root.subs[2]}get versionObj(){return this.tbsCertificateObj.subs[0]}get serialNumberObj(){return this.tbsCertificateObj.subs[1]}get issuerObj(){return this.tbsCertificateObj.subs[3]}get validityObj(){return this.tbsCertificateObj.subs[4]}get subjectObj(){return this.tbsCertificateObj.subs[5]}get subjectPublicKeyInfoObj(){return this.tbsCertificateObj.subs[6]}get extensionsObj(){return this.tbsCertificateObj.subs.find(e=>e.tag.isContextSpecific(3))}};sc.X509Certificate=tK});var pRe=L(vg=>{"use strict";Object.defineProperty(vg,"__esModule",{value:!0});vg.X509SCTExtension=vg.X509Certificate=vg.EXTENSION_OID_SCT=void 0;var ARe=fRe();Object.defineProperty(vg,"EXTENSION_OID_SCT",{enumerable:!0,get:function(){return ARe.EXTENSION_OID_SCT}});Object.defineProperty(vg,"X509Certificate",{enumerable:!0,get:function(){return ARe.X509Certificate}});var vMt=eK();Object.defineProperty(vg,"X509SCTExtension",{enumerable:!0,get:function(){return vMt.X509SCTExtension}})});var wl=L(Kn=>{"use strict";var SMt=Kn&&Kn.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),DMt=Kn&&Kn.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),_b=Kn&&Kn.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&SMt(e,t,r);return DMt(e,t),e};Object.defineProperty(Kn,"__esModule",{value:!0});Kn.X509SCTExtension=Kn.X509Certificate=Kn.EXTENSION_OID_SCT=Kn.ByteStream=Kn.RFC3161Timestamp=Kn.pem=Kn.json=Kn.encoding=Kn.dsse=Kn.crypto=Kn.ASN1Obj=void 0;var bMt=mO();Object.defineProperty(Kn,"ASN1Obj",{enumerable:!0,get:function(){return bMt.ASN1Obj}});Kn.crypto=_b(Zw());Kn.dsse=_b(ZTe());Kn.encoding=_b(eRe());Kn.json=_b(tRe());Kn.pem=_b(H7());var PMt=oRe();Object.defineProperty(Kn,"RFC3161Timestamp",{enumerable:!0,get:function(){return PMt.RFC3161Timestamp}});var xMt=Ob();Object.defineProperty(Kn,"ByteStream",{enumerable:!0,get:function(){return xMt.ByteStream}});var rK=pRe();Object.defineProperty(Kn,"EXTENSION_OID_SCT",{enumerable:!0,get:function(){return rK.EXTENSION_OID_SCT}});Object.defineProperty(Kn,"X509Certificate",{enumerable:!0,get:function(){return rK.X509Certificate}});Object.defineProperty(Kn,"X509SCTExtension",{enumerable:!0,get:function(){return rK.X509SCTExtension}})});var hRe=L(nK=>{"use strict";Object.defineProperty(nK,"__esModule",{value:!0});nK.extractJWTSubject=QMt;var kMt=wl();function QMt(t){let e=t.split(".",3),r=JSON.parse(kMt.encoding.base64Decode(e[1]));switch(r.iss){case"https://accounts.google.com":case"https://oauth2.sigstore.dev/auth":return r.email;default:return r.sub}}});var gRe=L((kCr,TMt)=>{TMt.exports={name:"@sigstore/sign",version:"3.1.0",description:"Sigstore signing library",main:"dist/index.js",types:"dist/index.d.ts",scripts:{clean:"shx rm -rf dist *.tsbuildinfo",build:"tsc --build",test:"jest"},files:["dist"],author:"bdehamer@github.com",license:"Apache-2.0",repository:{type:"git",url:"git+https://github.com/sigstore/sigstore-js.git"},bugs:{url:"https://github.com/sigstore/sigstore-js/issues"},homepage:"https://github.com/sigstore/sigstore-js/tree/main/packages/sign#readme",publishConfig:{provenance:!0},devDependencies:{"@sigstore/jest":"^0.0.0","@sigstore/mock":"^0.10.0","@sigstore/rekor-types":"^3.0.0","@types/make-fetch-happen":"^10.0.4","@types/promise-retry":"^1.1.6"},dependencies:{"@sigstore/bundle":"^3.1.0","@sigstore/core":"^2.0.0","@sigstore/protobuf-specs":"^0.4.0","make-fetch-happen":"^14.0.2","proc-log":"^5.0.0","promise-retry":"^2.0.1"},engines:{node:"^18.17.0 || >=20.5.0"}}});var mRe=L($w=>{"use strict";var RMt=$w&&$w.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty($w,"__esModule",{value:!0});$w.getUserAgent=void 0;var dRe=RMt(ye("os")),FMt=()=>{let t=gRe().version,e=process.version,r=dRe.default.platform(),s=dRe.default.arch();return`sigstore-js/${t} (Node ${e}) (${r}/${s})`};$w.getUserAgent=FMt});var Sg=L(Ki=>{"use strict";var NMt=Ki&&Ki.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),OMt=Ki&&Ki.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),yRe=Ki&&Ki.__importStar||function(){var t=function(e){return t=Object.getOwnPropertyNames||function(r){var s=[];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(s[s.length]=a);return s},t(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null)for(var s=t(e),a=0;a{"use strict";Object.defineProperty(BO,"__esModule",{value:!0});BO.BaseBundleBuilder=void 0;var iK=class{constructor(e){this.signer=e.signer,this.witnesses=e.witnesses}async create(e){let r=await this.prepare(e).then(f=>this.signer.sign(f)),s=await this.package(e,r),a=await Promise.all(this.witnesses.map(f=>f.testify(s.content,LMt(r.key)))),n=[],c=[];return a.forEach(({tlogEntries:f,rfc3161Timestamps:p})=>{n.push(...f??[]),c.push(...p??[])}),s.verificationMaterial.tlogEntries=n,s.verificationMaterial.timestampVerificationData={rfc3161Timestamps:c},s}async prepare(e){return e.data}};BO.BaseBundleBuilder=iK;function LMt(t){switch(t.$case){case"publicKey":return t.publicKey;case"x509Certificate":return t.certificate}}});var aK=L(xA=>{"use strict";var MMt=xA&&xA.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),_Mt=xA&&xA.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),UMt=xA&&xA.__importStar||function(){var t=function(e){return t=Object.getOwnPropertyNames||function(r){var s=[];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(s[s.length]=a);return s},t(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null)for(var s=t(e),a=0;a{"use strict";Object.defineProperty(vO,"__esModule",{value:!0});vO.DSSEBundleBuilder=void 0;var qMt=Sg(),GMt=sK(),WMt=aK(),lK=class extends GMt.BaseBundleBuilder{constructor(e){super(e),this.certificateChain=e.certificateChain??!1}async prepare(e){let r=IRe(e);return qMt.dsse.preAuthEncoding(r.type,r.data)}async package(e,r){return(0,WMt.toDSSEBundle)(IRe(e),r,this.certificateChain)}};vO.DSSEBundleBuilder=lK;function IRe(t){return{...t,type:t.type??""}}});var wRe=L(SO=>{"use strict";Object.defineProperty(SO,"__esModule",{value:!0});SO.MessageSignatureBundleBuilder=void 0;var YMt=sK(),VMt=aK(),cK=class extends YMt.BaseBundleBuilder{constructor(e){super(e)}async package(e,r){return(0,VMt.toMessageSignatureBundle)(e,r)}};SO.MessageSignatureBundleBuilder=cK});var BRe=L(e1=>{"use strict";Object.defineProperty(e1,"__esModule",{value:!0});e1.MessageSignatureBundleBuilder=e1.DSSEBundleBuilder=void 0;var KMt=CRe();Object.defineProperty(e1,"DSSEBundleBuilder",{enumerable:!0,get:function(){return KMt.DSSEBundleBuilder}});var JMt=wRe();Object.defineProperty(e1,"MessageSignatureBundleBuilder",{enumerable:!0,get:function(){return JMt.MessageSignatureBundleBuilder}})});var bO=L(DO=>{"use strict";Object.defineProperty(DO,"__esModule",{value:!0});DO.HTTPError=void 0;var uK=class extends Error{constructor({status:e,message:r,location:s}){super(`(${e}) ${r}`),this.statusCode=e,this.location=s}};DO.HTTPError=uK});var t1=L(Hb=>{"use strict";Object.defineProperty(Hb,"__esModule",{value:!0});Hb.InternalError=void 0;Hb.internalError=ZMt;var zMt=bO(),PO=class extends Error{constructor({code:e,message:r,cause:s}){super(r),this.name=this.constructor.name,this.cause=s,this.code=e}};Hb.InternalError=PO;function ZMt(t,e,r){throw t instanceof zMt.HTTPError&&(r+=` - ${t.message}`),new PO({code:e,message:r,cause:t})}});var xO=L((UCr,vRe)=>{vRe.exports=fetch});var SRe=L(r1=>{"use strict";var XMt=r1&&r1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(r1,"__esModule",{value:!0});r1.CIContextProvider=void 0;var $Mt=XMt(xO()),e_t=[t_t,r_t],fK=class{constructor(e="sigstore"){this.audience=e}async getToken(){return Promise.any(e_t.map(e=>e(this.audience))).catch(()=>Promise.reject("CI: no tokens available"))}};r1.CIContextProvider=fK;async function t_t(t){if(!process.env.ACTIONS_ID_TOKEN_REQUEST_URL||!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN)return Promise.reject("no token available");let e=new URL(process.env.ACTIONS_ID_TOKEN_REQUEST_URL);return e.searchParams.append("audience",t),(await(0,$Mt.default)(e.href,{retry:2,headers:{Accept:"application/json",Authorization:`Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`}})).json().then(s=>s.value)}async function r_t(){return process.env.SIGSTORE_ID_TOKEN?process.env.SIGSTORE_ID_TOKEN:Promise.reject("no token available")}});var DRe=L(kO=>{"use strict";Object.defineProperty(kO,"__esModule",{value:!0});kO.CIContextProvider=void 0;var n_t=SRe();Object.defineProperty(kO,"CIContextProvider",{enumerable:!0,get:function(){return n_t.CIContextProvider}})});var PRe=L((qCr,bRe)=>{var i_t=Symbol("proc-log.meta");bRe.exports={META:i_t,output:{LEVELS:["standard","error","buffer","flush"],KEYS:{standard:"standard",error:"error",buffer:"buffer",flush:"flush"},standard:function(...t){return process.emit("output","standard",...t)},error:function(...t){return process.emit("output","error",...t)},buffer:function(...t){return process.emit("output","buffer",...t)},flush:function(...t){return process.emit("output","flush",...t)}},log:{LEVELS:["notice","error","warn","info","verbose","http","silly","timing","pause","resume"],KEYS:{notice:"notice",error:"error",warn:"warn",info:"info",verbose:"verbose",http:"http",silly:"silly",timing:"timing",pause:"pause",resume:"resume"},error:function(...t){return process.emit("log","error",...t)},notice:function(...t){return process.emit("log","notice",...t)},warn:function(...t){return process.emit("log","warn",...t)},info:function(...t){return process.emit("log","info",...t)},verbose:function(...t){return process.emit("log","verbose",...t)},http:function(...t){return process.emit("log","http",...t)},silly:function(...t){return process.emit("log","silly",...t)},timing:function(...t){return process.emit("log","timing",...t)},pause:function(){return process.emit("log","pause")},resume:function(){return process.emit("log","resume")}},time:{LEVELS:["start","end"],KEYS:{start:"start",end:"end"},start:function(t,e){process.emit("time","start",t);function r(){return process.emit("time","end",t)}if(typeof e=="function"){let s=e();return s&&s.finally?s.finally(r):(r(),s)}return r},end:function(t){return process.emit("time","end",t)}},input:{LEVELS:["start","end","read"],KEYS:{start:"start",end:"end",read:"read"},start:function(t){process.emit("input","start");function e(){return process.emit("input","end")}if(typeof t=="function"){let r=t();return r&&r.finally?r.finally(e):(e(),r)}return e},end:function(){return process.emit("input","end")},read:function(...t){let e,r,s=new Promise((a,n)=>{e=a,r=n});return process.emit("input","read",e,r,...t),s}}}});var QRe=L((GCr,kRe)=>{"use strict";function xRe(t,e){for(let r in e)Object.defineProperty(t,r,{value:e[r],enumerable:!0,configurable:!0});return t}function s_t(t,e,r){if(!t||typeof t=="string")throw new TypeError("Please pass an Error to err-code");r||(r={}),typeof e=="object"&&(r=e,e=void 0),e!=null&&(r.code=e);try{return xRe(t,r)}catch{r.message=t.message,r.stack=t.stack;let a=function(){};return a.prototype=Object.create(Object.getPrototypeOf(t)),xRe(new a,r)}}kRe.exports=s_t});var RRe=L((WCr,TRe)=>{function tu(t,e){typeof e=="boolean"&&(e={forever:e}),this._originalTimeouts=JSON.parse(JSON.stringify(t)),this._timeouts=t,this._options=e||{},this._maxRetryTime=e&&e.maxRetryTime||1/0,this._fn=null,this._errors=[],this._attempts=1,this._operationTimeout=null,this._operationTimeoutCb=null,this._timeout=null,this._operationStart=null,this._options.forever&&(this._cachedTimeouts=this._timeouts.slice(0))}TRe.exports=tu;tu.prototype.reset=function(){this._attempts=1,this._timeouts=this._originalTimeouts};tu.prototype.stop=function(){this._timeout&&clearTimeout(this._timeout),this._timeouts=[],this._cachedTimeouts=null};tu.prototype.retry=function(t){if(this._timeout&&clearTimeout(this._timeout),!t)return!1;var e=new Date().getTime();if(t&&e-this._operationStart>=this._maxRetryTime)return this._errors.unshift(new Error("RetryOperation timeout occurred")),!1;this._errors.push(t);var r=this._timeouts.shift();if(r===void 0)if(this._cachedTimeouts)this._errors.splice(this._errors.length-1,this._errors.length),this._timeouts=this._cachedTimeouts.slice(0),r=this._timeouts.shift();else return!1;var s=this,a=setTimeout(function(){s._attempts++,s._operationTimeoutCb&&(s._timeout=setTimeout(function(){s._operationTimeoutCb(s._attempts)},s._operationTimeout),s._options.unref&&s._timeout.unref()),s._fn(s._attempts)},r);return this._options.unref&&a.unref(),!0};tu.prototype.attempt=function(t,e){this._fn=t,e&&(e.timeout&&(this._operationTimeout=e.timeout),e.cb&&(this._operationTimeoutCb=e.cb));var r=this;this._operationTimeoutCb&&(this._timeout=setTimeout(function(){r._operationTimeoutCb()},r._operationTimeout)),this._operationStart=new Date().getTime(),this._fn(this._attempts)};tu.prototype.try=function(t){console.log("Using RetryOperation.try() is deprecated"),this.attempt(t)};tu.prototype.start=function(t){console.log("Using RetryOperation.start() is deprecated"),this.attempt(t)};tu.prototype.start=tu.prototype.try;tu.prototype.errors=function(){return this._errors};tu.prototype.attempts=function(){return this._attempts};tu.prototype.mainError=function(){if(this._errors.length===0)return null;for(var t={},e=null,r=0,s=0;s=r&&(e=a,r=c)}return e}});var FRe=L(sy=>{var o_t=RRe();sy.operation=function(t){var e=sy.timeouts(t);return new o_t(e,{forever:t&&t.forever,unref:t&&t.unref,maxRetryTime:t&&t.maxRetryTime})};sy.timeouts=function(t){if(t instanceof Array)return[].concat(t);var e={retries:10,factor:2,minTimeout:1*1e3,maxTimeout:1/0,randomize:!1};for(var r in t)e[r]=t[r];if(e.minTimeout>e.maxTimeout)throw new Error("minTimeout is greater than maxTimeout");for(var s=[],a=0;a{NRe.exports=FRe()});var _Re=L((KCr,MRe)=>{"use strict";var a_t=QRe(),l_t=ORe(),c_t=Object.prototype.hasOwnProperty;function LRe(t){return t&&t.code==="EPROMISERETRY"&&c_t.call(t,"retried")}function u_t(t,e){var r,s;return typeof t=="object"&&typeof e=="function"&&(r=e,e=t,t=r),s=l_t.operation(e),new Promise(function(a,n){s.attempt(function(c){Promise.resolve().then(function(){return t(function(f){throw LRe(f)&&(f=f.retried),a_t(new Error("Retrying"),"EPROMISERETRY",{retried:f})},c)}).then(a,function(f){LRe(f)&&(f=f.retried,s.retry(f||new Error))||n(f)})})})}MRe.exports=u_t});var QO=L(jb=>{"use strict";var HRe=jb&&jb.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(jb,"__esModule",{value:!0});jb.fetchWithRetry=w_t;var f_t=ye("http2"),A_t=HRe(xO()),URe=PRe(),p_t=HRe(_Re()),h_t=Sg(),g_t=bO(),{HTTP2_HEADER_LOCATION:d_t,HTTP2_HEADER_CONTENT_TYPE:m_t,HTTP2_HEADER_USER_AGENT:y_t,HTTP_STATUS_INTERNAL_SERVER_ERROR:E_t,HTTP_STATUS_TOO_MANY_REQUESTS:I_t,HTTP_STATUS_REQUEST_TIMEOUT:C_t}=f_t.constants;async function w_t(t,e){return(0,p_t.default)(async(r,s)=>{let a=e.method||"POST",n={[y_t]:h_t.ua.getUserAgent(),...e.headers},c=await(0,A_t.default)(t,{method:a,headers:n,body:e.body,timeout:e.timeout,retry:!1}).catch(f=>(URe.log.http("fetch",`${a} ${t} attempt ${s} failed with ${f}`),r(f)));if(c.ok)return c;{let f=await B_t(c);if(URe.log.http("fetch",`${a} ${t} attempt ${s} failed with ${c.status}`),v_t(c.status))return r(f);throw f}},S_t(e.retry))}var B_t=async t=>{let e=t.statusText,r=t.headers.get(d_t)||void 0;if(t.headers.get(m_t)?.includes("application/json"))try{e=(await t.json()).message||e}catch{}return new g_t.HTTPError({status:t.status,message:e,location:r})},v_t=t=>[C_t,I_t].includes(t)||t>=E_t,S_t=t=>typeof t=="boolean"?{retries:t?1:0}:typeof t=="number"?{retries:t}:{retries:0,...t}});var jRe=L(TO=>{"use strict";Object.defineProperty(TO,"__esModule",{value:!0});TO.Fulcio=void 0;var D_t=QO(),AK=class{constructor(e){this.options=e}async createSigningCertificate(e){let{baseURL:r,retry:s,timeout:a}=this.options,n=`${r}/api/v2/signingCert`;return(await(0,D_t.fetchWithRetry)(n,{headers:{"Content-Type":"application/json"},body:JSON.stringify(e),timeout:a,retry:s})).json()}};TO.Fulcio=AK});var qRe=L(RO=>{"use strict";Object.defineProperty(RO,"__esModule",{value:!0});RO.CAClient=void 0;var b_t=t1(),P_t=jRe(),pK=class{constructor(e){this.fulcio=new P_t.Fulcio({baseURL:e.fulcioBaseURL,retry:e.retry,timeout:e.timeout})}async createSigningCertificate(e,r,s){let a=x_t(e,r,s);try{let n=await this.fulcio.createSigningCertificate(a);return(n.signedCertificateEmbeddedSct?n.signedCertificateEmbeddedSct:n.signedCertificateDetachedSct).chain.certificates}catch(n){(0,b_t.internalError)(n,"CA_CREATE_SIGNING_CERTIFICATE_ERROR","error creating signing certificate")}}};RO.CAClient=pK;function x_t(t,e,r){return{credentials:{oidcIdentityToken:t},publicKeyRequest:{publicKey:{algorithm:"ECDSA",content:e},proofOfPossession:r.toString("base64")}}}});var WRe=L(n1=>{"use strict";var k_t=n1&&n1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(n1,"__esModule",{value:!0});n1.EphemeralSigner=void 0;var GRe=k_t(ye("crypto")),Q_t="ec",T_t="P-256",hK=class{constructor(){this.keypair=GRe.default.generateKeyPairSync(Q_t,{namedCurve:T_t})}async sign(e){let r=GRe.default.sign(null,e,this.keypair.privateKey),s=this.keypair.publicKey.export({format:"pem",type:"spki"}).toString("ascii");return{signature:r,key:{$case:"publicKey",publicKey:s}}}};n1.EphemeralSigner=hK});var YRe=L(oy=>{"use strict";Object.defineProperty(oy,"__esModule",{value:!0});oy.FulcioSigner=oy.DEFAULT_FULCIO_URL=void 0;var gK=t1(),R_t=Sg(),F_t=qRe(),N_t=WRe();oy.DEFAULT_FULCIO_URL="https://fulcio.sigstore.dev";var dK=class{constructor(e){this.ca=new F_t.CAClient({...e,fulcioBaseURL:e.fulcioBaseURL||oy.DEFAULT_FULCIO_URL}),this.identityProvider=e.identityProvider,this.keyHolder=e.keyHolder||new N_t.EphemeralSigner}async sign(e){let r=await this.getIdentityToken(),s;try{s=R_t.oidc.extractJWTSubject(r)}catch(f){throw new gK.InternalError({code:"IDENTITY_TOKEN_PARSE_ERROR",message:`invalid identity token: ${r}`,cause:f})}let a=await this.keyHolder.sign(Buffer.from(s));if(a.key.$case!=="publicKey")throw new gK.InternalError({code:"CA_CREATE_SIGNING_CERTIFICATE_ERROR",message:"unexpected format for signing key"});let n=await this.ca.createSigningCertificate(r,a.key.publicKey,a.signature);return{signature:(await this.keyHolder.sign(e)).signature,key:{$case:"x509Certificate",certificate:n[0]}}}async getIdentityToken(){try{return await this.identityProvider.getToken()}catch(e){throw new gK.InternalError({code:"IDENTITY_TOKEN_READ_ERROR",message:"error retrieving identity token",cause:e})}}};oy.FulcioSigner=dK});var KRe=L(i1=>{"use strict";Object.defineProperty(i1,"__esModule",{value:!0});i1.FulcioSigner=i1.DEFAULT_FULCIO_URL=void 0;var VRe=YRe();Object.defineProperty(i1,"DEFAULT_FULCIO_URL",{enumerable:!0,get:function(){return VRe.DEFAULT_FULCIO_URL}});Object.defineProperty(i1,"FulcioSigner",{enumerable:!0,get:function(){return VRe.FulcioSigner}})});var ZRe=L(FO=>{"use strict";Object.defineProperty(FO,"__esModule",{value:!0});FO.Rekor=void 0;var JRe=QO(),mK=class{constructor(e){this.options=e}async createEntry(e){let{baseURL:r,timeout:s,retry:a}=this.options,n=`${r}/api/v1/log/entries`,f=await(await(0,JRe.fetchWithRetry)(n,{headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e),timeout:s,retry:a})).json();return zRe(f)}async getEntry(e){let{baseURL:r,timeout:s,retry:a}=this.options,n=`${r}/api/v1/log/entries/${e}`,f=await(await(0,JRe.fetchWithRetry)(n,{method:"GET",headers:{Accept:"application/json"},timeout:s,retry:a})).json();return zRe(f)}};FO.Rekor=mK;function zRe(t){let e=Object.entries(t);if(e.length!=1)throw new Error("Received multiple entries in Rekor response");let[r,s]=e[0];return{...s,uuid:r}}});var $Re=L(NO=>{"use strict";Object.defineProperty(NO,"__esModule",{value:!0});NO.TLogClient=void 0;var XRe=t1(),O_t=bO(),L_t=ZRe(),yK=class{constructor(e){this.fetchOnConflict=e.fetchOnConflict??!1,this.rekor=new L_t.Rekor({baseURL:e.rekorBaseURL,retry:e.retry,timeout:e.timeout})}async createEntry(e){let r;try{r=await this.rekor.createEntry(e)}catch(s){if(M_t(s)&&this.fetchOnConflict){let a=s.location.split("/").pop()||"";try{r=await this.rekor.getEntry(a)}catch(n){(0,XRe.internalError)(n,"TLOG_FETCH_ENTRY_ERROR","error fetching tlog entry")}}else(0,XRe.internalError)(s,"TLOG_CREATE_ENTRY_ERROR","error creating tlog entry")}return r}};NO.TLogClient=yK;function M_t(t){return t instanceof O_t.HTTPError&&t.statusCode===409&&t.location!==void 0}});var eFe=L(EK=>{"use strict";Object.defineProperty(EK,"__esModule",{value:!0});EK.toProposedEntry=U_t;var __t=Nb(),Dg=Sg(),qb="sha256";function U_t(t,e,r="dsse"){switch(t.$case){case"dsseEnvelope":return r==="intoto"?q_t(t.dsseEnvelope,e):j_t(t.dsseEnvelope,e);case"messageSignature":return H_t(t.messageSignature,e)}}function H_t(t,e){let r=t.messageDigest.digest.toString("hex"),s=t.signature.toString("base64"),a=Dg.encoding.base64Encode(e);return{apiVersion:"0.0.1",kind:"hashedrekord",spec:{data:{hash:{algorithm:qb,value:r}},signature:{content:s,publicKey:{content:a}}}}}function j_t(t,e){let r=JSON.stringify((0,__t.envelopeToJSON)(t)),s=Dg.encoding.base64Encode(e);return{apiVersion:"0.0.1",kind:"dsse",spec:{proposedContent:{envelope:r,verifiers:[s]}}}}function q_t(t,e){let r=Dg.crypto.digest(qb,t.payload).toString("hex"),s=G_t(t,e),a=Dg.encoding.base64Encode(t.payload.toString("base64")),n=Dg.encoding.base64Encode(t.signatures[0].sig.toString("base64")),c=t.signatures[0].keyid,f=Dg.encoding.base64Encode(e),p={payloadType:t.payloadType,payload:a,signatures:[{sig:n,publicKey:f}]};return c.length>0&&(p.signatures[0].keyid=c),{apiVersion:"0.0.2",kind:"intoto",spec:{content:{envelope:p,hash:{algorithm:qb,value:s},payloadHash:{algorithm:qb,value:r}}}}}function G_t(t,e){let r={payloadType:t.payloadType,payload:t.payload.toString("base64"),signatures:[{sig:t.signatures[0].sig.toString("base64"),publicKey:e}]};return t.signatures[0].keyid.length>0&&(r.signatures[0].keyid=t.signatures[0].keyid),Dg.crypto.digest(qb,Dg.json.canonicalize(r)).toString("hex")}});var tFe=L(ay=>{"use strict";Object.defineProperty(ay,"__esModule",{value:!0});ay.RekorWitness=ay.DEFAULT_REKOR_URL=void 0;var W_t=Sg(),Y_t=$Re(),V_t=eFe();ay.DEFAULT_REKOR_URL="https://rekor.sigstore.dev";var IK=class{constructor(e){this.entryType=e.entryType,this.tlog=new Y_t.TLogClient({...e,rekorBaseURL:e.rekorBaseURL||ay.DEFAULT_REKOR_URL})}async testify(e,r){let s=(0,V_t.toProposedEntry)(e,r,this.entryType),a=await this.tlog.createEntry(s);return K_t(a)}};ay.RekorWitness=IK;function K_t(t){let e=Buffer.from(t.logID,"hex"),r=W_t.encoding.base64Decode(t.body),s=JSON.parse(r),a=t?.verification?.signedEntryTimestamp?J_t(t.verification.signedEntryTimestamp):void 0,n=t?.verification?.inclusionProof?z_t(t.verification.inclusionProof):void 0;return{tlogEntries:[{logIndex:t.logIndex.toString(),logId:{keyId:e},integratedTime:t.integratedTime.toString(),kindVersion:{kind:s.kind,version:s.apiVersion},inclusionPromise:a,inclusionProof:n,canonicalizedBody:Buffer.from(t.body,"base64")}]}}function J_t(t){return{signedEntryTimestamp:Buffer.from(t,"base64")}}function z_t(t){return{logIndex:t.logIndex.toString(),treeSize:t.treeSize.toString(),rootHash:Buffer.from(t.rootHash,"hex"),hashes:t.hashes.map(e=>Buffer.from(e,"hex")),checkpoint:{envelope:t.checkpoint}}}});var rFe=L(OO=>{"use strict";Object.defineProperty(OO,"__esModule",{value:!0});OO.TimestampAuthority=void 0;var Z_t=QO(),CK=class{constructor(e){this.options=e}async createTimestamp(e){let{baseURL:r,timeout:s,retry:a}=this.options,n=`${r}/api/v1/timestamp`;return(await(0,Z_t.fetchWithRetry)(n,{headers:{"Content-Type":"application/json"},body:JSON.stringify(e),timeout:s,retry:a})).buffer()}};OO.TimestampAuthority=CK});var iFe=L(LO=>{"use strict";Object.defineProperty(LO,"__esModule",{value:!0});LO.TSAClient=void 0;var X_t=t1(),$_t=rFe(),eUt=Sg(),nFe="sha256",wK=class{constructor(e){this.tsa=new $_t.TimestampAuthority({baseURL:e.tsaBaseURL,retry:e.retry,timeout:e.timeout})}async createTimestamp(e){let r={artifactHash:eUt.crypto.digest(nFe,e).toString("base64"),hashAlgorithm:nFe};try{return await this.tsa.createTimestamp(r)}catch(s){(0,X_t.internalError)(s,"TSA_CREATE_TIMESTAMP_ERROR","error creating timestamp")}}};LO.TSAClient=wK});var sFe=L(MO=>{"use strict";Object.defineProperty(MO,"__esModule",{value:!0});MO.TSAWitness=void 0;var tUt=iFe(),BK=class{constructor(e){this.tsa=new tUt.TSAClient({tsaBaseURL:e.tsaBaseURL,retry:e.retry,timeout:e.timeout})}async testify(e){let r=rUt(e);return{rfc3161Timestamps:[{signedTimestamp:await this.tsa.createTimestamp(r)}]}}};MO.TSAWitness=BK;function rUt(t){switch(t.$case){case"dsseEnvelope":return t.dsseEnvelope.signatures[0].sig;case"messageSignature":return t.messageSignature.signature}}});var aFe=L(bg=>{"use strict";Object.defineProperty(bg,"__esModule",{value:!0});bg.TSAWitness=bg.RekorWitness=bg.DEFAULT_REKOR_URL=void 0;var oFe=tFe();Object.defineProperty(bg,"DEFAULT_REKOR_URL",{enumerable:!0,get:function(){return oFe.DEFAULT_REKOR_URL}});Object.defineProperty(bg,"RekorWitness",{enumerable:!0,get:function(){return oFe.RekorWitness}});var nUt=sFe();Object.defineProperty(bg,"TSAWitness",{enumerable:!0,get:function(){return nUt.TSAWitness}})});var SK=L(Es=>{"use strict";Object.defineProperty(Es,"__esModule",{value:!0});Es.TSAWitness=Es.RekorWitness=Es.DEFAULT_REKOR_URL=Es.FulcioSigner=Es.DEFAULT_FULCIO_URL=Es.CIContextProvider=Es.InternalError=Es.MessageSignatureBundleBuilder=Es.DSSEBundleBuilder=void 0;var lFe=BRe();Object.defineProperty(Es,"DSSEBundleBuilder",{enumerable:!0,get:function(){return lFe.DSSEBundleBuilder}});Object.defineProperty(Es,"MessageSignatureBundleBuilder",{enumerable:!0,get:function(){return lFe.MessageSignatureBundleBuilder}});var iUt=t1();Object.defineProperty(Es,"InternalError",{enumerable:!0,get:function(){return iUt.InternalError}});var sUt=DRe();Object.defineProperty(Es,"CIContextProvider",{enumerable:!0,get:function(){return sUt.CIContextProvider}});var cFe=KRe();Object.defineProperty(Es,"DEFAULT_FULCIO_URL",{enumerable:!0,get:function(){return cFe.DEFAULT_FULCIO_URL}});Object.defineProperty(Es,"FulcioSigner",{enumerable:!0,get:function(){return cFe.FulcioSigner}});var vK=aFe();Object.defineProperty(Es,"DEFAULT_REKOR_URL",{enumerable:!0,get:function(){return vK.DEFAULT_REKOR_URL}});Object.defineProperty(Es,"RekorWitness",{enumerable:!0,get:function(){return vK.RekorWitness}});Object.defineProperty(Es,"TSAWitness",{enumerable:!0,get:function(){return vK.TSAWitness}})});var fFe=L(Gb=>{"use strict";var uFe=Gb&&Gb.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(Gb,"__esModule",{value:!0});Gb.appDataPath=aUt;var oUt=uFe(ye("os")),s1=uFe(ye("path"));function aUt(t){let e=oUt.default.homedir();switch(process.platform){case"darwin":{let r=s1.default.join(e,"Library","Application Support");return s1.default.join(r,t)}case"win32":{let r=process.env.LOCALAPPDATA||s1.default.join(e,"AppData","Local");return s1.default.join(r,t,"Data")}default:{let r=process.env.XDG_DATA_HOME||s1.default.join(e,".local","share");return s1.default.join(r,t)}}}});var kA=L(Bl=>{"use strict";Object.defineProperty(Bl,"__esModule",{value:!0});Bl.UnsupportedAlgorithmError=Bl.CryptoError=Bl.LengthOrHashMismatchError=Bl.UnsignedMetadataError=Bl.RepositoryError=Bl.ValueError=void 0;var DK=class extends Error{};Bl.ValueError=DK;var Wb=class extends Error{};Bl.RepositoryError=Wb;var bK=class extends Wb{};Bl.UnsignedMetadataError=bK;var PK=class extends Wb{};Bl.LengthOrHashMismatchError=PK;var _O=class extends Error{};Bl.CryptoError=_O;var xK=class extends _O{};Bl.UnsupportedAlgorithmError=xK});var pFe=L(Pg=>{"use strict";Object.defineProperty(Pg,"__esModule",{value:!0});Pg.isDefined=lUt;Pg.isObject=AFe;Pg.isStringArray=cUt;Pg.isObjectArray=uUt;Pg.isStringRecord=fUt;Pg.isObjectRecord=AUt;function lUt(t){return t!==void 0}function AFe(t){return typeof t=="object"&&t!==null}function cUt(t){return Array.isArray(t)&&t.every(e=>typeof e=="string")}function uUt(t){return Array.isArray(t)&&t.every(AFe)}function fUt(t){return typeof t=="object"&&t!==null&&Object.keys(t).every(e=>typeof e=="string")&&Object.values(t).every(e=>typeof e=="string")}function AUt(t){return typeof t=="object"&&t!==null&&Object.keys(t).every(e=>typeof e=="string")&&Object.values(t).every(e=>typeof e=="object"&&e!==null)}});var QK=L((pwr,dFe)=>{var hFe=",",pUt=":",hUt="[",gUt="]",dUt="{",mUt="}";function kK(t){let e=[];if(typeof t=="string")e.push(gFe(t));else if(typeof t=="boolean")e.push(JSON.stringify(t));else if(Number.isInteger(t))e.push(JSON.stringify(t));else if(t===null)e.push(JSON.stringify(t));else if(Array.isArray(t)){e.push(hUt);let r=!0;t.forEach(s=>{r||e.push(hFe),r=!1,e.push(kK(s))}),e.push(gUt)}else if(typeof t=="object"){e.push(dUt);let r=!0;Object.keys(t).sort().forEach(s=>{r||e.push(hFe),r=!1,e.push(gFe(s)),e.push(pUt),e.push(kK(t[s]))}),e.push(mUt)}else throw new TypeError("cannot encode "+t.toString());return e.join("")}function gFe(t){return'"'+t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'}dFe.exports={canonicalize:kK}});var mFe=L(o1=>{"use strict";var yUt=o1&&o1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(o1,"__esModule",{value:!0});o1.verifySignature=void 0;var EUt=QK(),IUt=yUt(ye("crypto")),CUt=(t,e,r)=>{let s=Buffer.from((0,EUt.canonicalize)(t));return IUt.default.verify(void 0,s,e,Buffer.from(r,"hex"))};o1.verifySignature=CUt});var Af=L(ru=>{"use strict";var wUt=ru&&ru.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),BUt=ru&&ru.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),yFe=ru&&ru.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&wUt(e,t,r);return BUt(e,t),e};Object.defineProperty(ru,"__esModule",{value:!0});ru.crypto=ru.guard=void 0;ru.guard=yFe(pFe());ru.crypto=yFe(mFe())});var ly=L(mh=>{"use strict";var vUt=mh&&mh.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(mh,"__esModule",{value:!0});mh.Signed=mh.MetadataKind=void 0;mh.isMetadataKind=DUt;var SUt=vUt(ye("util")),Yb=kA(),TK=Af(),EFe=["1","0","31"],RK;(function(t){t.Root="root",t.Timestamp="timestamp",t.Snapshot="snapshot",t.Targets="targets"})(RK||(mh.MetadataKind=RK={}));function DUt(t){return typeof t=="string"&&Object.values(RK).includes(t)}var FK=class t{constructor(e){this.specVersion=e.specVersion||EFe.join(".");let r=this.specVersion.split(".");if(!(r.length===2||r.length===3)||!r.every(s=>bUt(s)))throw new Yb.ValueError("Failed to parse specVersion");if(r[0]!=EFe[0])throw new Yb.ValueError("Unsupported specVersion");this.expires=e.expires,this.version=e.version,this.unrecognizedFields=e.unrecognizedFields||{}}equals(e){return e instanceof t?this.specVersion===e.specVersion&&this.expires===e.expires&&this.version===e.version&&SUt.default.isDeepStrictEqual(this.unrecognizedFields,e.unrecognizedFields):!1}isExpired(e){return e||(e=new Date),e>=new Date(this.expires)}static commonFieldsFromJSON(e){let{spec_version:r,expires:s,version:a,...n}=e;if(TK.guard.isDefined(r)){if(typeof r!="string")throw new TypeError("spec_version must be a string")}else throw new Yb.ValueError("spec_version is not defined");if(TK.guard.isDefined(s)){if(typeof s!="string")throw new TypeError("expires must be a string")}else throw new Yb.ValueError("expires is not defined");if(TK.guard.isDefined(a)){if(typeof a!="number")throw new TypeError("version must be a number")}else throw new Yb.ValueError("version is not defined");return{specVersion:r,expires:s,version:a,unrecognizedFields:n}}};mh.Signed=FK;function bUt(t){return!isNaN(Number(t))}});var Vb=L(kg=>{"use strict";var IFe=kg&&kg.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(kg,"__esModule",{value:!0});kg.TargetFile=kg.MetaFile=void 0;var CFe=IFe(ye("crypto")),HO=IFe(ye("util")),xg=kA(),UO=Af(),NK=class t{constructor(e){if(e.version<=0)throw new xg.ValueError("Metafile version must be at least 1");e.length!==void 0&&wFe(e.length),this.version=e.version,this.length=e.length,this.hashes=e.hashes,this.unrecognizedFields=e.unrecognizedFields||{}}equals(e){return e instanceof t?this.version===e.version&&this.length===e.length&&HO.default.isDeepStrictEqual(this.hashes,e.hashes)&&HO.default.isDeepStrictEqual(this.unrecognizedFields,e.unrecognizedFields):!1}verify(e){if(this.length!==void 0&&e.length!==this.length)throw new xg.LengthOrHashMismatchError(`Expected length ${this.length} but got ${e.length}`);this.hashes&&Object.entries(this.hashes).forEach(([r,s])=>{let a;try{a=CFe.default.createHash(r)}catch{throw new xg.LengthOrHashMismatchError(`Hash algorithm ${r} not supported`)}let n=a.update(e).digest("hex");if(n!==s)throw new xg.LengthOrHashMismatchError(`Expected hash ${s} but got ${n}`)})}toJSON(){let e={version:this.version,...this.unrecognizedFields};return this.length!==void 0&&(e.length=this.length),this.hashes&&(e.hashes=this.hashes),e}static fromJSON(e){let{version:r,length:s,hashes:a,...n}=e;if(typeof r!="number")throw new TypeError("version must be a number");if(UO.guard.isDefined(s)&&typeof s!="number")throw new TypeError("length must be a number");if(UO.guard.isDefined(a)&&!UO.guard.isStringRecord(a))throw new TypeError("hashes must be string keys and values");return new t({version:r,length:s,hashes:a,unrecognizedFields:n})}};kg.MetaFile=NK;var OK=class t{constructor(e){wFe(e.length),this.length=e.length,this.path=e.path,this.hashes=e.hashes,this.unrecognizedFields=e.unrecognizedFields||{}}get custom(){let e=this.unrecognizedFields.custom;return!e||Array.isArray(e)||typeof e!="object"?{}:e}equals(e){return e instanceof t?this.length===e.length&&this.path===e.path&&HO.default.isDeepStrictEqual(this.hashes,e.hashes)&&HO.default.isDeepStrictEqual(this.unrecognizedFields,e.unrecognizedFields):!1}async verify(e){let r=0,s=Object.keys(this.hashes).reduce((a,n)=>{try{a[n]=CFe.default.createHash(n)}catch{throw new xg.LengthOrHashMismatchError(`Hash algorithm ${n} not supported`)}return a},{});for await(let a of e)r+=a.length,Object.values(s).forEach(n=>{n.update(a)});if(r!==this.length)throw new xg.LengthOrHashMismatchError(`Expected length ${this.length} but got ${r}`);Object.entries(s).forEach(([a,n])=>{let c=this.hashes[a],f=n.digest("hex");if(f!==c)throw new xg.LengthOrHashMismatchError(`Expected hash ${c} but got ${f}`)})}toJSON(){return{length:this.length,hashes:this.hashes,...this.unrecognizedFields}}static fromJSON(e,r){let{length:s,hashes:a,...n}=r;if(typeof s!="number")throw new TypeError("length must be a number");if(!UO.guard.isStringRecord(a))throw new TypeError("hashes must have string keys and values");return new t({length:s,path:e,hashes:a,unrecognizedFields:n})}};kg.TargetFile=OK;function wFe(t){if(t<0)throw new xg.ValueError("Length must be at least 0")}});var BFe=L(LK=>{"use strict";Object.defineProperty(LK,"__esModule",{value:!0});LK.encodeOIDString=xUt;var PUt=6;function xUt(t){let e=t.split("."),r=parseInt(e[0],10)*40+parseInt(e[1],10),s=[];e.slice(2).forEach(n=>{let c=kUt(parseInt(n,10));s.push(...c)});let a=Buffer.from([r,...s]);return Buffer.from([PUt,a.length,...a])}function kUt(t){let e=[],r=0;for(;t>0;)e.unshift(t&127|r),t>>=7,r=128;return e}});var bFe=L(Jb=>{"use strict";var QUt=Jb&&Jb.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(Jb,"__esModule",{value:!0});Jb.getPublicKey=NUt;var a1=QUt(ye("crypto")),Kb=kA(),MK=BFe(),jO=48,vFe=3,SFe=0,TUt="1.3.101.112",RUt="1.2.840.10045.2.1",FUt="1.2.840.10045.3.1.7",_K="-----BEGIN PUBLIC KEY-----";function NUt(t){switch(t.keyType){case"rsa":return OUt(t);case"ed25519":return LUt(t);case"ecdsa":case"ecdsa-sha2-nistp256":case"ecdsa-sha2-nistp384":return MUt(t);default:throw new Kb.UnsupportedAlgorithmError(`Unsupported key type: ${t.keyType}`)}}function OUt(t){if(!t.keyVal.startsWith(_K))throw new Kb.CryptoError("Invalid key format");let e=a1.default.createPublicKey(t.keyVal);switch(t.scheme){case"rsassa-pss-sha256":return{key:e,padding:a1.default.constants.RSA_PKCS1_PSS_PADDING};default:throw new Kb.UnsupportedAlgorithmError(`Unsupported RSA scheme: ${t.scheme}`)}}function LUt(t){let e;if(t.keyVal.startsWith(_K))e=a1.default.createPublicKey(t.keyVal);else{if(!DFe(t.keyVal))throw new Kb.CryptoError("Invalid key format");e=a1.default.createPublicKey({key:_Ut.hexToDER(t.keyVal),format:"der",type:"spki"})}return{key:e}}function MUt(t){let e;if(t.keyVal.startsWith(_K))e=a1.default.createPublicKey(t.keyVal);else{if(!DFe(t.keyVal))throw new Kb.CryptoError("Invalid key format");e=a1.default.createPublicKey({key:UUt.hexToDER(t.keyVal),format:"der",type:"spki"})}return{key:e}}var _Ut={hexToDER:t=>{let e=Buffer.from(t,"hex"),r=(0,MK.encodeOIDString)(TUt),s=Buffer.concat([Buffer.concat([Buffer.from([jO]),Buffer.from([r.length]),r]),Buffer.concat([Buffer.from([vFe]),Buffer.from([e.length+1]),Buffer.from([SFe]),e])]);return Buffer.concat([Buffer.from([jO]),Buffer.from([s.length]),s])}},UUt={hexToDER:t=>{let e=Buffer.from(t,"hex"),r=Buffer.concat([Buffer.from([vFe]),Buffer.from([e.length+1]),Buffer.from([SFe]),e]),s=Buffer.concat([(0,MK.encodeOIDString)(RUt),(0,MK.encodeOIDString)(FUt)]),a=Buffer.concat([Buffer.from([jO]),Buffer.from([s.length]),s]);return Buffer.concat([Buffer.from([jO]),Buffer.from([a.length+r.length]),a,r])}},DFe=t=>/^[0-9a-fA-F]+$/.test(t)});var qO=L(l1=>{"use strict";var HUt=l1&&l1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(l1,"__esModule",{value:!0});l1.Key=void 0;var PFe=HUt(ye("util")),zb=kA(),xFe=Af(),jUt=bFe(),UK=class t{constructor(e){let{keyID:r,keyType:s,scheme:a,keyVal:n,unrecognizedFields:c}=e;this.keyID=r,this.keyType=s,this.scheme=a,this.keyVal=n,this.unrecognizedFields=c||{}}verifySignature(e){let r=e.signatures[this.keyID];if(!r)throw new zb.UnsignedMetadataError("no signature for key found in metadata");if(!this.keyVal.public)throw new zb.UnsignedMetadataError("no public key found");let s=(0,jUt.getPublicKey)({keyType:this.keyType,scheme:this.scheme,keyVal:this.keyVal.public}),a=e.signed.toJSON();try{if(!xFe.crypto.verifySignature(a,s,r.sig))throw new zb.UnsignedMetadataError(`failed to verify ${this.keyID} signature`)}catch(n){throw n instanceof zb.UnsignedMetadataError?n:new zb.UnsignedMetadataError(`failed to verify ${this.keyID} signature`)}}equals(e){return e instanceof t?this.keyID===e.keyID&&this.keyType===e.keyType&&this.scheme===e.scheme&&PFe.default.isDeepStrictEqual(this.keyVal,e.keyVal)&&PFe.default.isDeepStrictEqual(this.unrecognizedFields,e.unrecognizedFields):!1}toJSON(){return{keytype:this.keyType,scheme:this.scheme,keyval:this.keyVal,...this.unrecognizedFields}}static fromJSON(e,r){let{keytype:s,scheme:a,keyval:n,...c}=r;if(typeof s!="string")throw new TypeError("keytype must be a string");if(typeof a!="string")throw new TypeError("scheme must be a string");if(!xFe.guard.isStringRecord(n))throw new TypeError("keyval must be a string record");return new t({keyID:e,keyType:s,scheme:a,keyVal:n,unrecognizedFields:c})}};l1.Key=UK});var FFe=L((Cwr,RFe)=>{"use strict";RFe.exports=QFe;function QFe(t,e,r){t instanceof RegExp&&(t=kFe(t,r)),e instanceof RegExp&&(e=kFe(e,r));var s=TFe(t,e,r);return s&&{start:s[0],end:s[1],pre:r.slice(0,s[0]),body:r.slice(s[0]+t.length,s[1]),post:r.slice(s[1]+e.length)}}function kFe(t,e){var r=e.match(t);return r?r[0]:null}QFe.range=TFe;function TFe(t,e,r){var s,a,n,c,f,p=r.indexOf(t),h=r.indexOf(e,p+1),E=p;if(p>=0&&h>0){for(s=[],n=r.length;E>=0&&!f;)E==p?(s.push(E),p=r.indexOf(t,E+1)):s.length==1?f=[s.pop(),h]:(a=s.pop(),a=0?p:h;s.length&&(f=[n,c])}return f}});var jFe=L((wwr,HFe)=>{var NFe=FFe();HFe.exports=WUt;var OFe="\0SLASH"+Math.random()+"\0",LFe="\0OPEN"+Math.random()+"\0",jK="\0CLOSE"+Math.random()+"\0",MFe="\0COMMA"+Math.random()+"\0",_Fe="\0PERIOD"+Math.random()+"\0";function HK(t){return parseInt(t,10)==t?parseInt(t,10):t.charCodeAt(0)}function qUt(t){return t.split("\\\\").join(OFe).split("\\{").join(LFe).split("\\}").join(jK).split("\\,").join(MFe).split("\\.").join(_Fe)}function GUt(t){return t.split(OFe).join("\\").split(LFe).join("{").split(jK).join("}").split(MFe).join(",").split(_Fe).join(".")}function UFe(t){if(!t)return[""];var e=[],r=NFe("{","}",t);if(!r)return t.split(",");var s=r.pre,a=r.body,n=r.post,c=s.split(",");c[c.length-1]+="{"+a+"}";var f=UFe(n);return n.length&&(c[c.length-1]+=f.shift(),c.push.apply(c,f)),e.push.apply(e,c),e}function WUt(t){return t?(t.substr(0,2)==="{}"&&(t="\\{\\}"+t.substr(2)),Zb(qUt(t),!0).map(GUt)):[]}function YUt(t){return"{"+t+"}"}function VUt(t){return/^-?0\d/.test(t)}function KUt(t,e){return t<=e}function JUt(t,e){return t>=e}function Zb(t,e){var r=[],s=NFe("{","}",t);if(!s)return[t];var a=s.pre,n=s.post.length?Zb(s.post,!1):[""];if(/\$$/.test(s.pre))for(var c=0;c=0;if(!E&&!C)return s.post.match(/,.*\}/)?(t=s.pre+"{"+s.body+jK+s.post,Zb(t)):[t];var S;if(E)S=s.body.split(/\.\./);else if(S=UFe(s.body),S.length===1&&(S=Zb(S[0],!1).map(YUt),S.length===1))return n.map(function(Ce){return s.pre+S[0]+Ce});var P;if(E){var I=HK(S[0]),R=HK(S[1]),N=Math.max(S[0].length,S[1].length),U=S.length==3?Math.abs(HK(S[2])):1,W=KUt,te=R0){var pe=new Array(me+1).join("0");Ae<0?ce="-"+pe+ce.slice(1):ce=pe+ce}}P.push(ce)}}else{P=[];for(var Be=0;Be{"use strict";Object.defineProperty(GO,"__esModule",{value:!0});GO.assertValidPattern=void 0;var zUt=1024*64,ZUt=t=>{if(typeof t!="string")throw new TypeError("invalid pattern");if(t.length>zUt)throw new TypeError("pattern is too long")};GO.assertValidPattern=ZUt});var WFe=L(WO=>{"use strict";Object.defineProperty(WO,"__esModule",{value:!0});WO.parseClass=void 0;var XUt={"[:alnum:]":["\\p{L}\\p{Nl}\\p{Nd}",!0],"[:alpha:]":["\\p{L}\\p{Nl}",!0],"[:ascii:]":["\\x00-\\x7f",!1],"[:blank:]":["\\p{Zs}\\t",!0],"[:cntrl:]":["\\p{Cc}",!0],"[:digit:]":["\\p{Nd}",!0],"[:graph:]":["\\p{Z}\\p{C}",!0,!0],"[:lower:]":["\\p{Ll}",!0],"[:print:]":["\\p{C}",!0],"[:punct:]":["\\p{P}",!0],"[:space:]":["\\p{Z}\\t\\r\\n\\v\\f",!0],"[:upper:]":["\\p{Lu}",!0],"[:word:]":["\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}",!0],"[:xdigit:]":["A-Fa-f0-9",!1]},Xb=t=>t.replace(/[[\]\\-]/g,"\\$&"),$Ut=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),GFe=t=>t.join(""),e4t=(t,e)=>{let r=e;if(t.charAt(r)!=="[")throw new Error("not in a brace expression");let s=[],a=[],n=r+1,c=!1,f=!1,p=!1,h=!1,E=r,C="";e:for(;nC?s.push(Xb(C)+"-"+Xb(R)):R===C&&s.push(Xb(R)),C="",n++;continue}if(t.startsWith("-]",n+1)){s.push(Xb(R+"-")),n+=2;continue}if(t.startsWith("-",n+1)){C=R,n+=2;continue}s.push(Xb(R)),n++}if(E{"use strict";Object.defineProperty(YO,"__esModule",{value:!0});YO.unescape=void 0;var t4t=(t,{windowsPathsNoEscape:e=!1}={})=>e?t.replace(/\[([^\/\\])\]/g,"$1"):t.replace(/((?!\\).|^)\[([^\/\\])\]/g,"$1$2").replace(/\\([^\/])/g,"$1");YO.unescape=t4t});var WK=L(zO=>{"use strict";Object.defineProperty(zO,"__esModule",{value:!0});zO.AST=void 0;var r4t=WFe(),KO=VO(),n4t=new Set(["!","?","+","*","@"]),YFe=t=>n4t.has(t),i4t="(?!(?:^|/)\\.\\.?(?:$|/))",JO="(?!\\.)",s4t=new Set(["[","."]),o4t=new Set(["..","."]),a4t=new Set("().*{}+?[]^$\\!"),l4t=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),GK="[^/]",VFe=GK+"*?",KFe=GK+"+?",qK=class t{type;#t;#r;#i=!1;#e=[];#n;#o;#l;#a=!1;#s;#c;#f=!1;constructor(e,r,s={}){this.type=e,e&&(this.#r=!0),this.#n=r,this.#t=this.#n?this.#n.#t:this,this.#s=this.#t===this?s:this.#t.#s,this.#l=this.#t===this?[]:this.#t.#l,e==="!"&&!this.#t.#a&&this.#l.push(this),this.#o=this.#n?this.#n.#e.length:0}get hasMagic(){if(this.#r!==void 0)return this.#r;for(let e of this.#e)if(typeof e!="string"&&(e.type||e.hasMagic))return this.#r=!0;return this.#r}toString(){return this.#c!==void 0?this.#c:this.type?this.#c=this.type+"("+this.#e.map(e=>String(e)).join("|")+")":this.#c=this.#e.map(e=>String(e)).join("")}#p(){if(this!==this.#t)throw new Error("should only call on root");if(this.#a)return this;this.toString(),this.#a=!0;let e;for(;e=this.#l.pop();){if(e.type!=="!")continue;let r=e,s=r.#n;for(;s;){for(let a=r.#o+1;!s.type&&atypeof r=="string"?r:r.toJSON()):[this.type,...this.#e.map(r=>r.toJSON())];return this.isStart()&&!this.type&&e.unshift([]),this.isEnd()&&(this===this.#t||this.#t.#a&&this.#n?.type==="!")&&e.push({}),e}isStart(){if(this.#t===this)return!0;if(!this.#n?.isStart())return!1;if(this.#o===0)return!0;let e=this.#n;for(let r=0;r{let[I,R,N,U]=typeof P=="string"?t.#h(P,this.#r,p):P.toRegExpSource(e);return this.#r=this.#r||N,this.#i=this.#i||U,I}).join(""),E="";if(this.isStart()&&typeof this.#e[0]=="string"&&!(this.#e.length===1&&o4t.has(this.#e[0]))){let I=s4t,R=r&&I.has(h.charAt(0))||h.startsWith("\\.")&&I.has(h.charAt(2))||h.startsWith("\\.\\.")&&I.has(h.charAt(4)),N=!r&&!e&&I.has(h.charAt(0));E=R?i4t:N?JO:""}let C="";return this.isEnd()&&this.#t.#a&&this.#n?.type==="!"&&(C="(?:$|\\/)"),[E+h+C,(0,KO.unescape)(h),this.#r=!!this.#r,this.#i]}let s=this.type==="*"||this.type==="+",a=this.type==="!"?"(?:(?!(?:":"(?:",n=this.#A(r);if(this.isStart()&&this.isEnd()&&!n&&this.type!=="!"){let p=this.toString();return this.#e=[p],this.type=null,this.#r=void 0,[p,(0,KO.unescape)(this.toString()),!1,!1]}let c=!s||e||r||!JO?"":this.#A(!0);c===n&&(c=""),c&&(n=`(?:${n})(?:${c})*?`);let f="";if(this.type==="!"&&this.#f)f=(this.isStart()&&!r?JO:"")+KFe;else{let p=this.type==="!"?"))"+(this.isStart()&&!r&&!e?JO:"")+VFe+")":this.type==="@"?")":this.type==="?"?")?":this.type==="+"&&c?")":this.type==="*"&&c?")?":`)${this.type}`;f=a+n+p}return[f,(0,KO.unescape)(n),this.#r=!!this.#r,this.#i]}#A(e){return this.#e.map(r=>{if(typeof r=="string")throw new Error("string type in extglob ast??");let[s,a,n,c]=r.toRegExpSource(e);return this.#i=this.#i||c,s}).filter(r=>!(this.isStart()&&this.isEnd())||!!r).join("|")}static#h(e,r,s=!1){let a=!1,n="",c=!1;for(let f=0;f{"use strict";Object.defineProperty(ZO,"__esModule",{value:!0});ZO.escape=void 0;var c4t=(t,{windowsPathsNoEscape:e=!1}={})=>e?t.replace(/[?*()[\]]/g,"[$&]"):t.replace(/[?*()[\]\\]/g,"\\$&");ZO.escape=c4t});var tNe=L(pr=>{"use strict";var u4t=pr&&pr.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(pr,"__esModule",{value:!0});pr.unescape=pr.escape=pr.AST=pr.Minimatch=pr.match=pr.makeRe=pr.braceExpand=pr.defaults=pr.filter=pr.GLOBSTAR=pr.sep=pr.minimatch=void 0;var f4t=u4t(jFe()),XO=qFe(),ZFe=WK(),A4t=YK(),p4t=VO(),h4t=(t,e,r={})=>((0,XO.assertValidPattern)(e),!r.nocomment&&e.charAt(0)==="#"?!1:new cy(e,r).match(t));pr.minimatch=h4t;var g4t=/^\*+([^+@!?\*\[\(]*)$/,d4t=t=>e=>!e.startsWith(".")&&e.endsWith(t),m4t=t=>e=>e.endsWith(t),y4t=t=>(t=t.toLowerCase(),e=>!e.startsWith(".")&&e.toLowerCase().endsWith(t)),E4t=t=>(t=t.toLowerCase(),e=>e.toLowerCase().endsWith(t)),I4t=/^\*+\.\*+$/,C4t=t=>!t.startsWith(".")&&t.includes("."),w4t=t=>t!=="."&&t!==".."&&t.includes("."),B4t=/^\.\*+$/,v4t=t=>t!=="."&&t!==".."&&t.startsWith("."),S4t=/^\*+$/,D4t=t=>t.length!==0&&!t.startsWith("."),b4t=t=>t.length!==0&&t!=="."&&t!=="..",P4t=/^\?+([^+@!?\*\[\(]*)?$/,x4t=([t,e=""])=>{let r=XFe([t]);return e?(e=e.toLowerCase(),s=>r(s)&&s.toLowerCase().endsWith(e)):r},k4t=([t,e=""])=>{let r=$Fe([t]);return e?(e=e.toLowerCase(),s=>r(s)&&s.toLowerCase().endsWith(e)):r},Q4t=([t,e=""])=>{let r=$Fe([t]);return e?s=>r(s)&&s.endsWith(e):r},T4t=([t,e=""])=>{let r=XFe([t]);return e?s=>r(s)&&s.endsWith(e):r},XFe=([t])=>{let e=t.length;return r=>r.length===e&&!r.startsWith(".")},$Fe=([t])=>{let e=t.length;return r=>r.length===e&&r!=="."&&r!==".."},eNe=typeof process=="object"&&process?typeof process.env=="object"&&process.env&&process.env.__MINIMATCH_TESTING_PLATFORM__||process.platform:"posix",JFe={win32:{sep:"\\"},posix:{sep:"/"}};pr.sep=eNe==="win32"?JFe.win32.sep:JFe.posix.sep;pr.minimatch.sep=pr.sep;pr.GLOBSTAR=Symbol("globstar **");pr.minimatch.GLOBSTAR=pr.GLOBSTAR;var R4t="[^/]",F4t=R4t+"*?",N4t="(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?",O4t="(?:(?!(?:\\/|^)\\.).)*?",L4t=(t,e={})=>r=>(0,pr.minimatch)(r,t,e);pr.filter=L4t;pr.minimatch.filter=pr.filter;var nu=(t,e={})=>Object.assign({},t,e),M4t=t=>{if(!t||typeof t!="object"||!Object.keys(t).length)return pr.minimatch;let e=pr.minimatch;return Object.assign((s,a,n={})=>e(s,a,nu(t,n)),{Minimatch:class extends e.Minimatch{constructor(a,n={}){super(a,nu(t,n))}static defaults(a){return e.defaults(nu(t,a)).Minimatch}},AST:class extends e.AST{constructor(a,n,c={}){super(a,n,nu(t,c))}static fromGlob(a,n={}){return e.AST.fromGlob(a,nu(t,n))}},unescape:(s,a={})=>e.unescape(s,nu(t,a)),escape:(s,a={})=>e.escape(s,nu(t,a)),filter:(s,a={})=>e.filter(s,nu(t,a)),defaults:s=>e.defaults(nu(t,s)),makeRe:(s,a={})=>e.makeRe(s,nu(t,a)),braceExpand:(s,a={})=>e.braceExpand(s,nu(t,a)),match:(s,a,n={})=>e.match(s,a,nu(t,n)),sep:e.sep,GLOBSTAR:pr.GLOBSTAR})};pr.defaults=M4t;pr.minimatch.defaults=pr.defaults;var _4t=(t,e={})=>((0,XO.assertValidPattern)(t),e.nobrace||!/\{(?:(?!\{).)*\}/.test(t)?[t]:(0,f4t.default)(t));pr.braceExpand=_4t;pr.minimatch.braceExpand=pr.braceExpand;var U4t=(t,e={})=>new cy(t,e).makeRe();pr.makeRe=U4t;pr.minimatch.makeRe=pr.makeRe;var H4t=(t,e,r={})=>{let s=new cy(e,r);return t=t.filter(a=>s.match(a)),s.options.nonull&&!t.length&&t.push(e),t};pr.match=H4t;pr.minimatch.match=pr.match;var zFe=/[?*]|[+@!]\(.*?\)|\[|\]/,j4t=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),cy=class{options;set;pattern;windowsPathsNoEscape;nonegate;negate;comment;empty;preserveMultipleSlashes;partial;globSet;globParts;nocase;isWindows;platform;windowsNoMagicRoot;regexp;constructor(e,r={}){(0,XO.assertValidPattern)(e),r=r||{},this.options=r,this.pattern=e,this.platform=r.platform||eNe,this.isWindows=this.platform==="win32",this.windowsPathsNoEscape=!!r.windowsPathsNoEscape||r.allowWindowsEscape===!1,this.windowsPathsNoEscape&&(this.pattern=this.pattern.replace(/\\/g,"/")),this.preserveMultipleSlashes=!!r.preserveMultipleSlashes,this.regexp=null,this.negate=!1,this.nonegate=!!r.nonegate,this.comment=!1,this.empty=!1,this.partial=!!r.partial,this.nocase=!!this.options.nocase,this.windowsNoMagicRoot=r.windowsNoMagicRoot!==void 0?r.windowsNoMagicRoot:!!(this.isWindows&&this.nocase),this.globSet=[],this.globParts=[],this.set=[],this.make()}hasMagic(){if(this.options.magicalBraces&&this.set.length>1)return!0;for(let e of this.set)for(let r of e)if(typeof r!="string")return!0;return!1}debug(...e){}make(){let e=this.pattern,r=this.options;if(!r.nocomment&&e.charAt(0)==="#"){this.comment=!0;return}if(!e){this.empty=!0;return}this.parseNegate(),this.globSet=[...new Set(this.braceExpand())],r.debug&&(this.debug=(...n)=>console.error(...n)),this.debug(this.pattern,this.globSet);let s=this.globSet.map(n=>this.slashSplit(n));this.globParts=this.preprocess(s),this.debug(this.pattern,this.globParts);let a=this.globParts.map((n,c,f)=>{if(this.isWindows&&this.windowsNoMagicRoot){let p=n[0]===""&&n[1]===""&&(n[2]==="?"||!zFe.test(n[2]))&&!zFe.test(n[3]),h=/^[a-z]:/i.test(n[0]);if(p)return[...n.slice(0,4),...n.slice(4).map(E=>this.parse(E))];if(h)return[n[0],...n.slice(1).map(E=>this.parse(E))]}return n.map(p=>this.parse(p))});if(this.debug(this.pattern,a),this.set=a.filter(n=>n.indexOf(!1)===-1),this.isWindows)for(let n=0;n=2?(e=this.firstPhasePreProcess(e),e=this.secondPhasePreProcess(e)):r>=1?e=this.levelOneOptimize(e):e=this.adjascentGlobstarOptimize(e),e}adjascentGlobstarOptimize(e){return e.map(r=>{let s=-1;for(;(s=r.indexOf("**",s+1))!==-1;){let a=s;for(;r[a+1]==="**";)a++;a!==s&&r.splice(s,a-s)}return r})}levelOneOptimize(e){return e.map(r=>(r=r.reduce((s,a)=>{let n=s[s.length-1];return a==="**"&&n==="**"?s:a===".."&&n&&n!==".."&&n!=="."&&n!=="**"?(s.pop(),s):(s.push(a),s)},[]),r.length===0?[""]:r))}levelTwoFileOptimize(e){Array.isArray(e)||(e=this.slashSplit(e));let r=!1;do{if(r=!1,!this.preserveMultipleSlashes){for(let a=1;aa&&s.splice(a+1,c-a);let f=s[a+1],p=s[a+2],h=s[a+3];if(f!==".."||!p||p==="."||p===".."||!h||h==="."||h==="..")continue;r=!0,s.splice(a,1);let E=s.slice(0);E[a]="**",e.push(E),a--}if(!this.preserveMultipleSlashes){for(let c=1;cr.length)}partsMatch(e,r,s=!1){let a=0,n=0,c=[],f="";for(;ate?r=r.slice(ie):te>ie&&(e=e.slice(te)))}}let{optimizationLevel:n=1}=this.options;n>=2&&(e=this.levelTwoFileOptimize(e)),this.debug("matchOne",this,{file:e,pattern:r}),this.debug("matchOne",e.length,r.length);for(var c=0,f=0,p=e.length,h=r.length;c>> no match, partial?`,e,S,r,P),S===p))}let R;if(typeof E=="string"?(R=C===E,this.debug("string match",E,C,R)):(R=E.test(C),this.debug("pattern match",E,C,R)),!R)return!1}if(c===p&&f===h)return!0;if(c===p)return s;if(f===h)return c===p-1&&e[c]==="";throw new Error("wtf?")}braceExpand(){return(0,pr.braceExpand)(this.pattern,this.options)}parse(e){(0,XO.assertValidPattern)(e);let r=this.options;if(e==="**")return pr.GLOBSTAR;if(e==="")return"";let s,a=null;(s=e.match(S4t))?a=r.dot?b4t:D4t:(s=e.match(g4t))?a=(r.nocase?r.dot?E4t:y4t:r.dot?m4t:d4t)(s[1]):(s=e.match(P4t))?a=(r.nocase?r.dot?k4t:x4t:r.dot?Q4t:T4t)(s):(s=e.match(I4t))?a=r.dot?w4t:C4t:(s=e.match(B4t))&&(a=v4t);let n=ZFe.AST.fromGlob(e,this.options).toMMPattern();return a&&typeof n=="object"&&Reflect.defineProperty(n,"test",{value:a}),n}makeRe(){if(this.regexp||this.regexp===!1)return this.regexp;let e=this.set;if(!e.length)return this.regexp=!1,this.regexp;let r=this.options,s=r.noglobstar?F4t:r.dot?N4t:O4t,a=new Set(r.nocase?["i"]:[]),n=e.map(p=>{let h=p.map(E=>{if(E instanceof RegExp)for(let C of E.flags.split(""))a.add(C);return typeof E=="string"?j4t(E):E===pr.GLOBSTAR?pr.GLOBSTAR:E._src});return h.forEach((E,C)=>{let S=h[C+1],P=h[C-1];E!==pr.GLOBSTAR||P===pr.GLOBSTAR||(P===void 0?S!==void 0&&S!==pr.GLOBSTAR?h[C+1]="(?:\\/|"+s+"\\/)?"+S:h[C]=s:S===void 0?h[C-1]=P+"(?:\\/|"+s+")?":S!==pr.GLOBSTAR&&(h[C-1]=P+"(?:\\/|\\/"+s+"\\/)"+S,h[C+1]=pr.GLOBSTAR))}),h.filter(E=>E!==pr.GLOBSTAR).join("/")}).join("|"),[c,f]=e.length>1?["(?:",")"]:["",""];n="^"+c+n+f+"$",this.negate&&(n="^(?!"+n+").+$");try{this.regexp=new RegExp(n,[...a].join(""))}catch{this.regexp=!1}return this.regexp}slashSplit(e){return this.preserveMultipleSlashes?e.split("/"):this.isWindows&&/^\/\/[^\/]+/.test(e)?["",...e.split(/\/+/)]:e.split(/\/+/)}match(e,r=this.partial){if(this.debug("match",e,this.pattern),this.comment)return!1;if(this.empty)return e==="";if(e==="/"&&r)return!0;let s=this.options;this.isWindows&&(e=e.split("\\").join("/"));let a=this.slashSplit(e);this.debug(this.pattern,"split",a);let n=this.set;this.debug(this.pattern,"set",n);let c=a[a.length-1];if(!c)for(let f=a.length-2;!c&&f>=0;f--)c=a[f];for(let f=0;f{"use strict";var rNe=iu&&iu.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(iu,"__esModule",{value:!0});iu.SuccinctRoles=iu.DelegatedRole=iu.Role=iu.TOP_LEVEL_ROLE_NAMES=void 0;var nNe=rNe(ye("crypto")),Y4t=tNe(),$O=rNe(ye("util")),eL=kA(),uy=Af();iu.TOP_LEVEL_ROLE_NAMES=["root","targets","snapshot","timestamp"];var $b=class t{constructor(e){let{keyIDs:r,threshold:s,unrecognizedFields:a}=e;if(V4t(r))throw new eL.ValueError("duplicate key IDs found");if(s<1)throw new eL.ValueError("threshold must be at least 1");this.keyIDs=r,this.threshold=s,this.unrecognizedFields=a||{}}equals(e){return e instanceof t?this.threshold===e.threshold&&$O.default.isDeepStrictEqual(this.keyIDs,e.keyIDs)&&$O.default.isDeepStrictEqual(this.unrecognizedFields,e.unrecognizedFields):!1}toJSON(){return{keyids:this.keyIDs,threshold:this.threshold,...this.unrecognizedFields}}static fromJSON(e){let{keyids:r,threshold:s,...a}=e;if(!uy.guard.isStringArray(r))throw new TypeError("keyids must be an array");if(typeof s!="number")throw new TypeError("threshold must be a number");return new t({keyIDs:r,threshold:s,unrecognizedFields:a})}};iu.Role=$b;function V4t(t){return new Set(t).size!==t.length}var VK=class t extends $b{constructor(e){super(e);let{name:r,terminating:s,paths:a,pathHashPrefixes:n}=e;if(this.name=r,this.terminating=s,e.paths&&e.pathHashPrefixes)throw new eL.ValueError("paths and pathHashPrefixes are mutually exclusive");this.paths=a,this.pathHashPrefixes=n}equals(e){return e instanceof t?super.equals(e)&&this.name===e.name&&this.terminating===e.terminating&&$O.default.isDeepStrictEqual(this.paths,e.paths)&&$O.default.isDeepStrictEqual(this.pathHashPrefixes,e.pathHashPrefixes):!1}isDelegatedPath(e){if(this.paths)return this.paths.some(r=>J4t(e,r));if(this.pathHashPrefixes){let s=nNe.default.createHash("sha256").update(e).digest("hex");return this.pathHashPrefixes.some(a=>s.startsWith(a))}return!1}toJSON(){let e={...super.toJSON(),name:this.name,terminating:this.terminating};return this.paths&&(e.paths=this.paths),this.pathHashPrefixes&&(e.path_hash_prefixes=this.pathHashPrefixes),e}static fromJSON(e){let{keyids:r,threshold:s,name:a,terminating:n,paths:c,path_hash_prefixes:f,...p}=e;if(!uy.guard.isStringArray(r))throw new TypeError("keyids must be an array of strings");if(typeof s!="number")throw new TypeError("threshold must be a number");if(typeof a!="string")throw new TypeError("name must be a string");if(typeof n!="boolean")throw new TypeError("terminating must be a boolean");if(uy.guard.isDefined(c)&&!uy.guard.isStringArray(c))throw new TypeError("paths must be an array of strings");if(uy.guard.isDefined(f)&&!uy.guard.isStringArray(f))throw new TypeError("path_hash_prefixes must be an array of strings");return new t({keyIDs:r,threshold:s,name:a,terminating:n,paths:c,pathHashPrefixes:f,unrecognizedFields:p})}};iu.DelegatedRole=VK;var K4t=(t,e)=>t.map((r,s)=>[r,e[s]]);function J4t(t,e){let r=t.split("/"),s=e.split("/");return s.length!=r.length?!1:K4t(r,s).every(([a,n])=>(0,Y4t.minimatch)(a,n))}var KK=class t extends $b{constructor(e){super(e);let{bitLength:r,namePrefix:s}=e;if(r<=0||r>32)throw new eL.ValueError("bitLength must be between 1 and 32");this.bitLength=r,this.namePrefix=s,this.numberOfBins=Math.pow(2,r),this.suffixLen=(this.numberOfBins-1).toString(16).length}equals(e){return e instanceof t?super.equals(e)&&this.bitLength===e.bitLength&&this.namePrefix===e.namePrefix:!1}getRoleForTarget(e){let a=nNe.default.createHash("sha256").update(e).digest().subarray(0,4),n=32-this.bitLength,f=(a.readUInt32BE()>>>n).toString(16).padStart(this.suffixLen,"0");return`${this.namePrefix}-${f}`}*getRoles(){for(let e=0;e{"use strict";var z4t=c1&&c1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(c1,"__esModule",{value:!0});c1.Root=void 0;var iNe=z4t(ye("util")),zK=ly(),sNe=kA(),Z4t=qO(),tL=JK(),rL=Af(),ZK=class t extends zK.Signed{constructor(e){if(super(e),this.type=zK.MetadataKind.Root,this.keys=e.keys||{},this.consistentSnapshot=e.consistentSnapshot??!0,!e.roles)this.roles=tL.TOP_LEVEL_ROLE_NAMES.reduce((r,s)=>({...r,[s]:new tL.Role({keyIDs:[],threshold:1})}),{});else{let r=new Set(Object.keys(e.roles));if(!tL.TOP_LEVEL_ROLE_NAMES.every(s=>r.has(s)))throw new sNe.ValueError("missing top-level role");this.roles=e.roles}}addKey(e,r){if(!this.roles[r])throw new sNe.ValueError(`role ${r} does not exist`);this.roles[r].keyIDs.includes(e.keyID)||this.roles[r].keyIDs.push(e.keyID),this.keys[e.keyID]=e}equals(e){return e instanceof t?super.equals(e)&&this.consistentSnapshot===e.consistentSnapshot&&iNe.default.isDeepStrictEqual(this.keys,e.keys)&&iNe.default.isDeepStrictEqual(this.roles,e.roles):!1}toJSON(){return{_type:this.type,spec_version:this.specVersion,version:this.version,expires:this.expires,keys:X4t(this.keys),roles:$4t(this.roles),consistent_snapshot:this.consistentSnapshot,...this.unrecognizedFields}}static fromJSON(e){let{unrecognizedFields:r,...s}=zK.Signed.commonFieldsFromJSON(e),{keys:a,roles:n,consistent_snapshot:c,...f}=r;if(typeof c!="boolean")throw new TypeError("consistent_snapshot must be a boolean");return new t({...s,keys:e3t(a),roles:t3t(n),consistentSnapshot:c,unrecognizedFields:f})}};c1.Root=ZK;function X4t(t){return Object.entries(t).reduce((e,[r,s])=>({...e,[r]:s.toJSON()}),{})}function $4t(t){return Object.entries(t).reduce((e,[r,s])=>({...e,[r]:s.toJSON()}),{})}function e3t(t){let e;if(rL.guard.isDefined(t)){if(!rL.guard.isObjectRecord(t))throw new TypeError("keys must be an object");e=Object.entries(t).reduce((r,[s,a])=>({...r,[s]:Z4t.Key.fromJSON(s,a)}),{})}return e}function t3t(t){let e;if(rL.guard.isDefined(t)){if(!rL.guard.isObjectRecord(t))throw new TypeError("roles must be an object");e=Object.entries(t).reduce((r,[s,a])=>({...r,[s]:tL.Role.fromJSON(a)}),{})}return e}});var eJ=L(nL=>{"use strict";Object.defineProperty(nL,"__esModule",{value:!0});nL.Signature=void 0;var $K=class t{constructor(e){let{keyID:r,sig:s}=e;this.keyID=r,this.sig=s}toJSON(){return{keyid:this.keyID,sig:this.sig}}static fromJSON(e){let{keyid:r,sig:s}=e;if(typeof r!="string")throw new TypeError("keyid must be a string");if(typeof s!="string")throw new TypeError("sig must be a string");return new t({keyID:r,sig:s})}};nL.Signature=$K});var nJ=L(u1=>{"use strict";var r3t=u1&&u1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(u1,"__esModule",{value:!0});u1.Snapshot=void 0;var n3t=r3t(ye("util")),tJ=ly(),aNe=Vb(),oNe=Af(),rJ=class t extends tJ.Signed{constructor(e){super(e),this.type=tJ.MetadataKind.Snapshot,this.meta=e.meta||{"targets.json":new aNe.MetaFile({version:1})}}equals(e){return e instanceof t?super.equals(e)&&n3t.default.isDeepStrictEqual(this.meta,e.meta):!1}toJSON(){return{_type:this.type,meta:i3t(this.meta),spec_version:this.specVersion,version:this.version,expires:this.expires,...this.unrecognizedFields}}static fromJSON(e){let{unrecognizedFields:r,...s}=tJ.Signed.commonFieldsFromJSON(e),{meta:a,...n}=r;return new t({...s,meta:s3t(a),unrecognizedFields:n})}};u1.Snapshot=rJ;function i3t(t){return Object.entries(t).reduce((e,[r,s])=>({...e,[r]:s.toJSON()}),{})}function s3t(t){let e;if(oNe.guard.isDefined(t))if(oNe.guard.isObjectRecord(t))e=Object.entries(t).reduce((r,[s,a])=>({...r,[s]:aNe.MetaFile.fromJSON(a)}),{});else throw new TypeError("meta field is malformed");return e}});var lNe=L(f1=>{"use strict";var o3t=f1&&f1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(f1,"__esModule",{value:!0});f1.Delegations=void 0;var iL=o3t(ye("util")),a3t=kA(),l3t=qO(),iJ=JK(),sL=Af(),sJ=class t{constructor(e){if(this.keys=e.keys,this.unrecognizedFields=e.unrecognizedFields||{},e.roles&&Object.keys(e.roles).some(r=>iJ.TOP_LEVEL_ROLE_NAMES.includes(r)))throw new a3t.ValueError("Delegated role name conflicts with top-level role name");this.succinctRoles=e.succinctRoles,this.roles=e.roles}equals(e){return e instanceof t?iL.default.isDeepStrictEqual(this.keys,e.keys)&&iL.default.isDeepStrictEqual(this.roles,e.roles)&&iL.default.isDeepStrictEqual(this.unrecognizedFields,e.unrecognizedFields)&&iL.default.isDeepStrictEqual(this.succinctRoles,e.succinctRoles):!1}*rolesForTarget(e){if(this.roles)for(let r of Object.values(this.roles))r.isDelegatedPath(e)&&(yield{role:r.name,terminating:r.terminating});else this.succinctRoles&&(yield{role:this.succinctRoles.getRoleForTarget(e),terminating:!0})}toJSON(){let e={keys:c3t(this.keys),...this.unrecognizedFields};return this.roles?e.roles=u3t(this.roles):this.succinctRoles&&(e.succinct_roles=this.succinctRoles.toJSON()),e}static fromJSON(e){let{keys:r,roles:s,succinct_roles:a,...n}=e,c;return sL.guard.isObject(a)&&(c=iJ.SuccinctRoles.fromJSON(a)),new t({keys:f3t(r),roles:A3t(s),unrecognizedFields:n,succinctRoles:c})}};f1.Delegations=sJ;function c3t(t){return Object.entries(t).reduce((e,[r,s])=>({...e,[r]:s.toJSON()}),{})}function u3t(t){return Object.values(t).map(e=>e.toJSON())}function f3t(t){if(!sL.guard.isObjectRecord(t))throw new TypeError("keys is malformed");return Object.entries(t).reduce((e,[r,s])=>({...e,[r]:l3t.Key.fromJSON(r,s)}),{})}function A3t(t){let e;if(sL.guard.isDefined(t)){if(!sL.guard.isObjectArray(t))throw new TypeError("roles is malformed");e=t.reduce((r,s)=>{let a=iJ.DelegatedRole.fromJSON(s);return{...r,[a.name]:a}},{})}return e}});var lJ=L(A1=>{"use strict";var p3t=A1&&A1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(A1,"__esModule",{value:!0});A1.Targets=void 0;var cNe=p3t(ye("util")),oJ=ly(),h3t=lNe(),g3t=Vb(),oL=Af(),aJ=class t extends oJ.Signed{constructor(e){super(e),this.type=oJ.MetadataKind.Targets,this.targets=e.targets||{},this.delegations=e.delegations}addTarget(e){this.targets[e.path]=e}equals(e){return e instanceof t?super.equals(e)&&cNe.default.isDeepStrictEqual(this.targets,e.targets)&&cNe.default.isDeepStrictEqual(this.delegations,e.delegations):!1}toJSON(){let e={_type:this.type,spec_version:this.specVersion,version:this.version,expires:this.expires,targets:d3t(this.targets),...this.unrecognizedFields};return this.delegations&&(e.delegations=this.delegations.toJSON()),e}static fromJSON(e){let{unrecognizedFields:r,...s}=oJ.Signed.commonFieldsFromJSON(e),{targets:a,delegations:n,...c}=r;return new t({...s,targets:m3t(a),delegations:y3t(n),unrecognizedFields:c})}};A1.Targets=aJ;function d3t(t){return Object.entries(t).reduce((e,[r,s])=>({...e,[r]:s.toJSON()}),{})}function m3t(t){let e;if(oL.guard.isDefined(t))if(oL.guard.isObjectRecord(t))e=Object.entries(t).reduce((r,[s,a])=>({...r,[s]:g3t.TargetFile.fromJSON(s,a)}),{});else throw new TypeError("targets must be an object");return e}function y3t(t){let e;if(oL.guard.isDefined(t))if(oL.guard.isObject(t))e=h3t.Delegations.fromJSON(t);else throw new TypeError("delegations must be an object");return e}});var AJ=L(aL=>{"use strict";Object.defineProperty(aL,"__esModule",{value:!0});aL.Timestamp=void 0;var cJ=ly(),uNe=Vb(),uJ=Af(),fJ=class t extends cJ.Signed{constructor(e){super(e),this.type=cJ.MetadataKind.Timestamp,this.snapshotMeta=e.snapshotMeta||new uNe.MetaFile({version:1})}equals(e){return e instanceof t?super.equals(e)&&this.snapshotMeta.equals(e.snapshotMeta):!1}toJSON(){return{_type:this.type,spec_version:this.specVersion,version:this.version,expires:this.expires,meta:{"snapshot.json":this.snapshotMeta.toJSON()},...this.unrecognizedFields}}static fromJSON(e){let{unrecognizedFields:r,...s}=cJ.Signed.commonFieldsFromJSON(e),{meta:a,...n}=r;return new t({...s,snapshotMeta:E3t(a),unrecognizedFields:n})}};aL.Timestamp=fJ;function E3t(t){let e;if(uJ.guard.isDefined(t)){let r=t["snapshot.json"];if(!uJ.guard.isDefined(r)||!uJ.guard.isObject(r))throw new TypeError("missing snapshot.json in meta");e=uNe.MetaFile.fromJSON(r)}return e}});var ANe=L(h1=>{"use strict";var I3t=h1&&h1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(h1,"__esModule",{value:!0});h1.Metadata=void 0;var C3t=QK(),fNe=I3t(ye("util")),p1=ly(),eP=kA(),w3t=XK(),B3t=eJ(),v3t=nJ(),S3t=lJ(),D3t=AJ(),pJ=Af(),hJ=class t{constructor(e,r,s){this.signed=e,this.signatures=r||{},this.unrecognizedFields=s||{}}sign(e,r=!0){let s=Buffer.from((0,C3t.canonicalize)(this.signed.toJSON())),a=e(s);r||(this.signatures={}),this.signatures[a.keyID]=a}verifyDelegate(e,r){let s,a={};switch(this.signed.type){case p1.MetadataKind.Root:a=this.signed.keys,s=this.signed.roles[e];break;case p1.MetadataKind.Targets:if(!this.signed.delegations)throw new eP.ValueError(`No delegations found for ${e}`);a=this.signed.delegations.keys,this.signed.delegations.roles?s=this.signed.delegations.roles[e]:this.signed.delegations.succinctRoles&&this.signed.delegations.succinctRoles.isDelegatedRole(e)&&(s=this.signed.delegations.succinctRoles);break;default:throw new TypeError("invalid metadata type")}if(!s)throw new eP.ValueError(`no delegation found for ${e}`);let n=new Set;if(s.keyIDs.forEach(c=>{let f=a[c];if(f)try{f.verifySignature(r),n.add(f.keyID)}catch{}}),n.sizer.toJSON()),signed:this.signed.toJSON(),...this.unrecognizedFields}}static fromJSON(e,r){let{signed:s,signatures:a,...n}=r;if(!pJ.guard.isDefined(s)||!pJ.guard.isObject(s))throw new TypeError("signed is not defined");if(e!==s._type)throw new eP.ValueError(`expected '${e}', got ${s._type}`);if(!pJ.guard.isObjectArray(a))throw new TypeError("signatures is not an array");let c;switch(e){case p1.MetadataKind.Root:c=w3t.Root.fromJSON(s);break;case p1.MetadataKind.Timestamp:c=D3t.Timestamp.fromJSON(s);break;case p1.MetadataKind.Snapshot:c=v3t.Snapshot.fromJSON(s);break;case p1.MetadataKind.Targets:c=S3t.Targets.fromJSON(s);break;default:throw new TypeError("invalid metadata type")}let f={};return a.forEach(p=>{let h=B3t.Signature.fromJSON(p);if(f[h.keyID])throw new eP.ValueError(`multiple signatures found for keyid: ${h.keyID}`);f[h.keyID]=h}),new t(c,f,n)}};h1.Metadata=hJ});var lL=L(Ri=>{"use strict";Object.defineProperty(Ri,"__esModule",{value:!0});Ri.Timestamp=Ri.Targets=Ri.Snapshot=Ri.Signature=Ri.Root=Ri.Metadata=Ri.Key=Ri.TargetFile=Ri.MetaFile=Ri.ValueError=Ri.MetadataKind=void 0;var b3t=ly();Object.defineProperty(Ri,"MetadataKind",{enumerable:!0,get:function(){return b3t.MetadataKind}});var P3t=kA();Object.defineProperty(Ri,"ValueError",{enumerable:!0,get:function(){return P3t.ValueError}});var pNe=Vb();Object.defineProperty(Ri,"MetaFile",{enumerable:!0,get:function(){return pNe.MetaFile}});Object.defineProperty(Ri,"TargetFile",{enumerable:!0,get:function(){return pNe.TargetFile}});var x3t=qO();Object.defineProperty(Ri,"Key",{enumerable:!0,get:function(){return x3t.Key}});var k3t=ANe();Object.defineProperty(Ri,"Metadata",{enumerable:!0,get:function(){return k3t.Metadata}});var Q3t=XK();Object.defineProperty(Ri,"Root",{enumerable:!0,get:function(){return Q3t.Root}});var T3t=eJ();Object.defineProperty(Ri,"Signature",{enumerable:!0,get:function(){return T3t.Signature}});var R3t=nJ();Object.defineProperty(Ri,"Snapshot",{enumerable:!0,get:function(){return R3t.Snapshot}});var F3t=lJ();Object.defineProperty(Ri,"Targets",{enumerable:!0,get:function(){return F3t.Targets}});var N3t=AJ();Object.defineProperty(Ri,"Timestamp",{enumerable:!0,get:function(){return N3t.Timestamp}})});var gNe=L((Uwr,hNe)=>{var g1=1e3,d1=g1*60,m1=d1*60,fy=m1*24,O3t=fy*7,L3t=fy*365.25;hNe.exports=function(t,e){e=e||{};var r=typeof t;if(r==="string"&&t.length>0)return M3t(t);if(r==="number"&&isFinite(t))return e.long?U3t(t):_3t(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))};function M3t(t){if(t=String(t),!(t.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),s=(e[2]||"ms").toLowerCase();switch(s){case"years":case"year":case"yrs":case"yr":case"y":return r*L3t;case"weeks":case"week":case"w":return r*O3t;case"days":case"day":case"d":return r*fy;case"hours":case"hour":case"hrs":case"hr":case"h":return r*m1;case"minutes":case"minute":case"mins":case"min":case"m":return r*d1;case"seconds":case"second":case"secs":case"sec":case"s":return r*g1;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function _3t(t){var e=Math.abs(t);return e>=fy?Math.round(t/fy)+"d":e>=m1?Math.round(t/m1)+"h":e>=d1?Math.round(t/d1)+"m":e>=g1?Math.round(t/g1)+"s":t+"ms"}function U3t(t){var e=Math.abs(t);return e>=fy?cL(t,e,fy,"day"):e>=m1?cL(t,e,m1,"hour"):e>=d1?cL(t,e,d1,"minute"):e>=g1?cL(t,e,g1,"second"):t+" ms"}function cL(t,e,r,s){var a=e>=r*1.5;return Math.round(t/r)+" "+s+(a?"s":"")}});var gJ=L((Hwr,dNe)=>{function H3t(t){r.debug=r,r.default=r,r.coerce=p,r.disable=c,r.enable=a,r.enabled=f,r.humanize=gNe(),r.destroy=h,Object.keys(t).forEach(E=>{r[E]=t[E]}),r.names=[],r.skips=[],r.formatters={};function e(E){let C=0;for(let S=0;S{if(ce==="%%")return"%";ie++;let pe=r.formatters[me];if(typeof pe=="function"){let Be=N[ie];ce=pe.call(U,Be),N.splice(ie,1),ie--}return ce}),r.formatArgs.call(U,N),(U.log||r.log).apply(U,N)}return R.namespace=E,R.useColors=r.useColors(),R.color=r.selectColor(E),R.extend=s,R.destroy=r.destroy,Object.defineProperty(R,"enabled",{enumerable:!0,configurable:!1,get:()=>S!==null?S:(P!==r.namespaces&&(P=r.namespaces,I=r.enabled(E)),I),set:N=>{S=N}}),typeof r.init=="function"&&r.init(R),R}function s(E,C){let S=r(this.namespace+(typeof C>"u"?":":C)+E);return S.log=this.log,S}function a(E){r.save(E),r.namespaces=E,r.names=[],r.skips=[];let C=(typeof E=="string"?E:"").trim().replace(" ",",").split(",").filter(Boolean);for(let S of C)S[0]==="-"?r.skips.push(S.slice(1)):r.names.push(S)}function n(E,C){let S=0,P=0,I=-1,R=0;for(;S"-"+C)].join(",");return r.enable(""),E}function f(E){for(let C of r.skips)if(n(E,C))return!1;for(let C of r.names)if(n(E,C))return!0;return!1}function p(E){return E instanceof Error?E.stack||E.message:E}function h(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return r.enable(r.load()),r}dNe.exports=H3t});var mNe=L((oc,uL)=>{oc.formatArgs=q3t;oc.save=G3t;oc.load=W3t;oc.useColors=j3t;oc.storage=Y3t();oc.destroy=(()=>{let t=!1;return()=>{t||(t=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();oc.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function j3t(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let t;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(t=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(t[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function q3t(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+uL.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;t.splice(1,0,e,"color: inherit");let r=0,s=0;t[0].replace(/%[a-zA-Z%]/g,a=>{a!=="%%"&&(r++,a==="%c"&&(s=r))}),t.splice(s,0,e)}oc.log=console.debug||console.log||(()=>{});function G3t(t){try{t?oc.storage.setItem("debug",t):oc.storage.removeItem("debug")}catch{}}function W3t(){let t;try{t=oc.storage.getItem("debug")}catch{}return!t&&typeof process<"u"&&"env"in process&&(t=process.env.DEBUG),t}function Y3t(){try{return localStorage}catch{}}uL.exports=gJ()(oc);var{formatters:V3t}=uL.exports;V3t.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var ENe=L(($s,AL)=>{var K3t=ye("tty"),fL=ye("util");$s.init=t8t;$s.log=X3t;$s.formatArgs=z3t;$s.save=$3t;$s.load=e8t;$s.useColors=J3t;$s.destroy=fL.deprecate(()=>{},"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");$s.colors=[6,2,3,4,5,1];try{let t=ye("supports-color");t&&(t.stderr||t).level>=2&&($s.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch{}$s.inspectOpts=Object.keys(process.env).filter(t=>/^debug_/i.test(t)).reduce((t,e)=>{let r=e.substring(6).toLowerCase().replace(/_([a-z])/g,(a,n)=>n.toUpperCase()),s=process.env[e];return/^(yes|on|true|enabled)$/i.test(s)?s=!0:/^(no|off|false|disabled)$/i.test(s)?s=!1:s==="null"?s=null:s=Number(s),t[r]=s,t},{});function J3t(){return"colors"in $s.inspectOpts?!!$s.inspectOpts.colors:K3t.isatty(process.stderr.fd)}function z3t(t){let{namespace:e,useColors:r}=this;if(r){let s=this.color,a="\x1B[3"+(s<8?s:"8;5;"+s),n=` ${a};1m${e} \x1B[0m`;t[0]=n+t[0].split(` +`).join(` +`+n),t.push(a+"m+"+AL.exports.humanize(this.diff)+"\x1B[0m")}else t[0]=Z3t()+e+" "+t[0]}function Z3t(){return $s.inspectOpts.hideDate?"":new Date().toISOString()+" "}function X3t(...t){return process.stderr.write(fL.formatWithOptions($s.inspectOpts,...t)+` +`)}function $3t(t){t?process.env.DEBUG=t:delete process.env.DEBUG}function e8t(){return process.env.DEBUG}function t8t(t){t.inspectOpts={};let e=Object.keys($s.inspectOpts);for(let r=0;re.trim()).join(" ")};yNe.O=function(t){return this.inspectOpts.colors=this.useColors,fL.inspect(t,this.inspectOpts)}});var mJ=L((jwr,dJ)=>{typeof process>"u"||process.type==="renderer"||process.browser===!0||process.__nwjs?dJ.exports=mNe():dJ.exports=ENe()});var hL=L(Ji=>{"use strict";Object.defineProperty(Ji,"__esModule",{value:!0});Ji.DownloadHTTPError=Ji.DownloadLengthMismatchError=Ji.DownloadError=Ji.ExpiredMetadataError=Ji.EqualVersionError=Ji.BadVersionError=Ji.RepositoryError=Ji.PersistError=Ji.RuntimeError=Ji.ValueError=void 0;var yJ=class extends Error{};Ji.ValueError=yJ;var EJ=class extends Error{};Ji.RuntimeError=EJ;var IJ=class extends Error{};Ji.PersistError=IJ;var tP=class extends Error{};Ji.RepositoryError=tP;var pL=class extends tP{};Ji.BadVersionError=pL;var CJ=class extends pL{};Ji.EqualVersionError=CJ;var wJ=class extends tP{};Ji.ExpiredMetadataError=wJ;var rP=class extends Error{};Ji.DownloadError=rP;var BJ=class extends rP{};Ji.DownloadLengthMismatchError=BJ;var vJ=class extends rP{constructor(e,r){super(e),this.statusCode=r}};Ji.DownloadHTTPError=vJ});var CNe=L(y1=>{"use strict";var DJ=y1&&y1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(y1,"__esModule",{value:!0});y1.withTempFile=void 0;var SJ=DJ(ye("fs/promises")),r8t=DJ(ye("os")),INe=DJ(ye("path")),n8t=async t=>i8t(async e=>t(INe.default.join(e,"tempfile")));y1.withTempFile=n8t;var i8t=async t=>{let e=await SJ.default.realpath(r8t.default.tmpdir()),r=await SJ.default.mkdtemp(e+INe.default.sep);try{return await t(r)}finally{await SJ.default.rm(r,{force:!0,recursive:!0,maxRetries:3})}}});var PJ=L(Qg=>{"use strict";var dL=Qg&&Qg.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(Qg,"__esModule",{value:!0});Qg.DefaultFetcher=Qg.BaseFetcher=void 0;var s8t=dL(mJ()),wNe=dL(ye("fs")),o8t=dL(xO()),a8t=dL(ye("util")),BNe=hL(),l8t=CNe(),c8t=(0,s8t.default)("tuf:fetch"),gL=class{async downloadFile(e,r,s){return(0,l8t.withTempFile)(async a=>{let n=await this.fetch(e),c=0,f=wNe.default.createWriteStream(a);try{for await(let p of n){let h=Buffer.from(p);if(c+=h.length,c>r)throw new BNe.DownloadLengthMismatchError("Max length reached");await u8t(f,h)}}finally{await a8t.default.promisify(f.close).bind(f)()}return s(a)})}async downloadBytes(e,r){return this.downloadFile(e,r,async s=>{let a=wNe.default.createReadStream(s),n=[];for await(let c of a)n.push(c);return Buffer.concat(n)})}};Qg.BaseFetcher=gL;var bJ=class extends gL{constructor(e={}){super(),this.timeout=e.timeout,this.retry=e.retry}async fetch(e){c8t("GET %s",e);let r=await(0,o8t.default)(e,{timeout:this.timeout,retry:this.retry});if(!r.ok||!r?.body)throw new BNe.DownloadHTTPError("Failed to download",r.status);return r.body}};Qg.DefaultFetcher=bJ;var u8t=async(t,e)=>new Promise((r,s)=>{t.write(e,a=>{a&&s(a),r(!0)})})});var vNe=L(mL=>{"use strict";Object.defineProperty(mL,"__esModule",{value:!0});mL.defaultConfig=void 0;mL.defaultConfig={maxRootRotations:256,maxDelegations:32,rootMaxLength:512e3,timestampMaxLength:16384,snapshotMaxLength:2e6,targetsMaxLength:5e6,prefixTargetsWithHash:!0,fetchTimeout:1e5,fetchRetries:void 0,fetchRetry:2}});var SNe=L(yL=>{"use strict";Object.defineProperty(yL,"__esModule",{value:!0});yL.TrustedMetadataStore=void 0;var Is=lL(),Ui=hL(),xJ=class{constructor(e){this.trustedSet={},this.referenceTime=new Date,this.loadTrustedRoot(e)}get root(){if(!this.trustedSet.root)throw new ReferenceError("No trusted root metadata");return this.trustedSet.root}get timestamp(){return this.trustedSet.timestamp}get snapshot(){return this.trustedSet.snapshot}get targets(){return this.trustedSet.targets}getRole(e){return this.trustedSet[e]}updateRoot(e){let r=JSON.parse(e.toString("utf8")),s=Is.Metadata.fromJSON(Is.MetadataKind.Root,r);if(s.signed.type!=Is.MetadataKind.Root)throw new Ui.RepositoryError(`Expected 'root', got ${s.signed.type}`);if(this.root.verifyDelegate(Is.MetadataKind.Root,s),s.signed.version!=this.root.signed.version+1)throw new Ui.BadVersionError(`Expected version ${this.root.signed.version+1}, got ${s.signed.version}`);return s.verifyDelegate(Is.MetadataKind.Root,s),this.trustedSet.root=s,s}updateTimestamp(e){if(this.snapshot)throw new Ui.RuntimeError("Cannot update timestamp after snapshot");if(this.root.signed.isExpired(this.referenceTime))throw new Ui.ExpiredMetadataError("Final root.json is expired");let r=JSON.parse(e.toString("utf8")),s=Is.Metadata.fromJSON(Is.MetadataKind.Timestamp,r);if(s.signed.type!=Is.MetadataKind.Timestamp)throw new Ui.RepositoryError(`Expected 'timestamp', got ${s.signed.type}`);if(this.root.verifyDelegate(Is.MetadataKind.Timestamp,s),this.timestamp){if(s.signed.version{let p=n.signed.meta[c];if(!p)throw new Ui.RepositoryError(`Missing file ${c} in new snapshot`);if(p.version{"use strict";Object.defineProperty(kJ,"__esModule",{value:!0});kJ.join=A8t;var f8t=ye("url");function A8t(t,e){return new f8t.URL(p8t(t)+h8t(e)).toString()}function p8t(t){return t.endsWith("/")?t:t+"/"}function h8t(t){return t.startsWith("/")?t.slice(1):t}});var bNe=L(su=>{"use strict";var g8t=su&&su.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),d8t=su&&su.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),RJ=su&&su.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(t!=null)for(var r in t)r!=="default"&&Object.prototype.hasOwnProperty.call(t,r)&&g8t(e,t,r);return d8t(e,t),e},m8t=su&&su.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(su,"__esModule",{value:!0});su.Updater=void 0;var QA=lL(),y8t=m8t(mJ()),E1=RJ(ye("fs")),EL=RJ(ye("path")),E8t=vNe(),Ay=hL(),I8t=PJ(),C8t=SNe(),nP=RJ(DNe()),QJ=(0,y8t.default)("tuf:cache"),TJ=class{constructor(e){let{metadataDir:r,metadataBaseUrl:s,targetDir:a,targetBaseUrl:n,fetcher:c,config:f}=e;this.dir=r,this.metadataBaseUrl=s,this.targetDir=a,this.targetBaseUrl=n,this.forceCache=e.forceCache??!1;let p=this.loadLocalMetadata(QA.MetadataKind.Root);this.trustedSet=new C8t.TrustedMetadataStore(p),this.config={...E8t.defaultConfig,...f},this.fetcher=c||new I8t.DefaultFetcher({timeout:this.config.fetchTimeout,retry:this.config.fetchRetries??this.config.fetchRetry})}async refresh(){if(this.forceCache)try{await this.loadTimestamp({checkRemote:!1})}catch{await this.loadRoot(),await this.loadTimestamp()}else await this.loadRoot(),await this.loadTimestamp();await this.loadSnapshot(),await this.loadTargets(QA.MetadataKind.Targets,QA.MetadataKind.Root)}async getTargetInfo(e){return this.trustedSet.targets||await this.refresh(),this.preorderDepthFirstWalk(e)}async downloadTarget(e,r,s){let a=r||this.generateTargetPath(e);if(!s){if(!this.targetBaseUrl)throw new Ay.ValueError("Target base URL not set");s=this.targetBaseUrl}let n=e.path;if(this.trustedSet.root.signed.consistentSnapshot&&this.config.prefixTargetsWithHash){let p=Object.values(e.hashes),{dir:h,base:E}=EL.parse(n),C=`${p[0]}.${E}`;n=h?`${h}/${C}`:C}let f=nP.join(s,n);return await this.fetcher.downloadFile(f,e.length,async p=>{await e.verify(E1.createReadStream(p)),QJ("WRITE %s",a),E1.copyFileSync(p,a)}),a}async findCachedTarget(e,r){r||(r=this.generateTargetPath(e));try{if(E1.existsSync(r))return await e.verify(E1.createReadStream(r)),r}catch{return}}loadLocalMetadata(e){let r=EL.join(this.dir,`${e}.json`);return QJ("READ %s",r),E1.readFileSync(r)}async loadRoot(){let r=this.trustedSet.root.signed.version+1,s=r+this.config.maxRootRotations;for(let a=r;a0;){let{roleName:a,parentRoleName:n}=r.pop();if(s.has(a))continue;let c=(await this.loadTargets(a,n))?.signed;if(!c)continue;let f=c.targets?.[e];if(f)return f;if(s.add(a),c.delegations){let p=[],h=c.delegations.rolesForTarget(e);for(let{role:E,terminating:C}of h)if(p.push({roleName:E,parentRoleName:a}),C){r.splice(0);break}p.reverse(),r.push(...p)}}}generateTargetPath(e){if(!this.targetDir)throw new Ay.ValueError("Target directory not set");let r=encodeURIComponent(e.path);return EL.join(this.targetDir,r)}persistMetadata(e,r){let s=encodeURIComponent(e);try{let a=EL.join(this.dir,`${s}.json`);QJ("WRITE %s",a),E1.writeFileSync(a,r.toString("utf8"))}catch(a){throw new Ay.PersistError(`Failed to persist metadata ${s} error: ${a}`)}}};su.Updater=TJ});var PNe=L(Tg=>{"use strict";Object.defineProperty(Tg,"__esModule",{value:!0});Tg.Updater=Tg.BaseFetcher=Tg.TargetFile=void 0;var w8t=lL();Object.defineProperty(Tg,"TargetFile",{enumerable:!0,get:function(){return w8t.TargetFile}});var B8t=PJ();Object.defineProperty(Tg,"BaseFetcher",{enumerable:!0,get:function(){return B8t.BaseFetcher}});var v8t=bNe();Object.defineProperty(Tg,"Updater",{enumerable:!0,get:function(){return v8t.Updater}})});var NJ=L(IL=>{"use strict";Object.defineProperty(IL,"__esModule",{value:!0});IL.TUFError=void 0;var FJ=class extends Error{constructor({code:e,message:r,cause:s}){super(r),this.code=e,this.cause=s,this.name=this.constructor.name}};IL.TUFError=FJ});var xNe=L(iP=>{"use strict";var S8t=iP&&iP.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(iP,"__esModule",{value:!0});iP.readTarget=b8t;var D8t=S8t(ye("fs")),CL=NJ();async function b8t(t,e){let r=await P8t(t,e);return new Promise((s,a)=>{D8t.default.readFile(r,"utf-8",(n,c)=>{n?a(new CL.TUFError({code:"TUF_READ_TARGET_ERROR",message:`error reading target ${r}`,cause:n})):s(c)})})}async function P8t(t,e){let r;try{r=await t.getTargetInfo(e)}catch(a){throw new CL.TUFError({code:"TUF_REFRESH_METADATA_ERROR",message:"error refreshing TUF metadata",cause:a})}if(!r)throw new CL.TUFError({code:"TUF_FIND_TARGET_ERROR",message:`target ${e} not found`});let s=await t.findCachedTarget(r);if(!s)try{s=await t.downloadTarget(r)}catch(a){throw new CL.TUFError({code:"TUF_DOWNLOAD_TARGET_ERROR",message:`error downloading target ${s}`,cause:a})}return s}});var kNe=L(($wr,x8t)=>{x8t.exports={"https://tuf-repo-cdn.sigstore.dev":{"root.json":"ewogInNpZ25hdHVyZXMiOiBbCiAgewogICAia2V5aWQiOiAiNmYyNjAwODlkNTkyM2RhZjIwMTY2Y2E2NTdjNTQzYWY2MTgzNDZhYjk3MTg4NGE5OTk2MmIwMTk4OGJiZTBjMyIsCiAgICJzaWciOiAiMzA0NjAyMjEwMDhhYjFmNmYxN2Q0ZjllNmQ3ZGNmMWM4ODkxMmI2YjUzY2MxMDM4ODY0NGFlMWYwOWJjMzdhMDgyY2QwNjAwM2UwMjIxMDBlMTQ1ZWY0YzdiNzgyZDRlODEwN2I1MzQzN2U2NjlkMDQ3Njg5MmNlOTk5OTAzYWUzM2QxNDQ0ODM2Njk5NmU3IgogIH0sCiAgewogICAia2V5aWQiOiAiZTcxYTU0ZDU0MzgzNWJhODZhZGFkOTQ2MDM3OWM3NjQxZmI4NzI2ZDE2NGVhNzY2ODAxYTFjNTIyYWJhN2VhMiIsCiAgICJzaWciOiAiMzA0NTAyMjEwMGM3NjhiMmY4NmRhOTk1NjkwMTljMTYwYTA4MWRhNTRhZTM2YzM0YzBhMzEyMGQzY2I2OWI1M2I3ZDExMzc1OGUwMjIwNGY2NzE1MThmNjE3YjIwZDQ2NTM3ZmFlNmMzYjYzYmFlODkxM2Y0ZjE5NjIxNTYxMDVjYzRmMDE5YWMzNWM2YSIKICB9LAogIHsKICAgImtleWlkIjogIjIyZjRjYWVjNmQ4ZTZmOTU1NWFmNjZiM2Q0YzNjYjA2YTNiYjIzZmRjN2UzOWM5MTZjNjFmNDYyZTZmNTJiMDYiLAogICAic2lnIjogIjMwNDUwMjIxMDBiNDQzNGU2OTk1ZDM2OGQyM2U3NDc1OWFjZDBjYjkwMTNjODNhNWQzNTExZjBmOTk3ZWM1NGM0NTZhZTQzNTBhMDIyMDE1YjBlMjY1ZDE4MmQyYjYxZGM3NGUxNTVkOThiM2MzZmJlNTY0YmEwNTI4NmFhMTRjOGRmMDJjOWI3NTY1MTYiCiAgfSwKICB7CiAgICJrZXlpZCI6ICI2MTY0MzgzODEyNWI0NDBiNDBkYjY5NDJmNWNiNWEzMWMwZGMwNDM2ODMxNmViMmFhYTU4Yjk1OTA0YTU4MjIyIiwKICAgInNpZyI6ICIzMDQ1MDIyMTAwODJjNTg0MTFkOTg5ZWI5Zjg2MTQxMDg1N2Q0MjM4MTU5MGVjOTQyNGRiZGFhNTFlNzhlZDEzNTE1NDMxOTA0ZTAyMjAxMTgxODVkYTZhNmMyOTQ3MTMxYzE3Nzk3ZTJiYjc2MjBjZTI2ZTVmMzAxZDFjZWFjNWYyYTdlNThmOWRjZjJlIgogIH0sCiAgewogICAia2V5aWQiOiAiYTY4N2U1YmY0ZmFiODJiMGVlNThkNDZlMDVjOTUzNTE0NWEyYzlhZmI0NThmNDNkNDJiNDVjYTBmZGNlMmE3MCIsCiAgICJzaWciOiAiMzA0NjAyMjEwMGM3ODUxMzg1NGNhZTljMzJlYWE2Yjg4ZTE4OTEyZjQ4MDA2YzI3NTdhMjU4ZjkxNzMxMmNhYmE3NTk0OGViOWUwMjIxMDBkOWUxYjRjZTBhZGZlOWZkMmUyMTQ4ZDdmYTI3YTJmNDBiYTExMjJiZDY5ZGE3NjEyZDhkMTc3NmIwMTNjOTFkIgogIH0sCiAgewogICAia2V5aWQiOiAiZmRmYTgzYTA3YjVhODM1ODliODdkZWQ0MWY3N2YzOWQyMzJhZDkxZjdjY2U1Mjg2OGRhY2QwNmJhMDg5ODQ5ZiIsCiAgICJzaWciOiAiMzA0NTAyMjA1NjQ4M2EyZDVkOWVhOWNlYzZlMTFlYWRmYjMzYzQ4NGI2MTQyOThmYWNhMTVhY2YxYzQzMWIxMWVkN2Y3MzRjMDIyMTAwZDBjMWQ3MjZhZjkyYTg3ZTRlNjY0NTljYTVhZGYzOGEwNWI0NGUxZjk0MzE4NDIzZjk1NGJhZThiY2E1YmIyZSIKICB9LAogIHsKICAgImtleWlkIjogImUyZjU5YWNiOTQ4ODUxOTQwN2UxOGNiZmM5MzI5NTEwYmUwM2MwNGFjYTk5MjlkMmYwMzAxMzQzZmVjODU1MjMiLAogICAic2lnIjogIjMwNDYwMjIxMDBkMDA0ZGU4ODAyNGMzMmRjNTY1M2E5ZjQ4NDNjZmM1MjE1NDI3MDQ4YWQ5NjAwZDJjZjljOTY5ZTZlZGZmM2QyMDIyMTAwZDllYmI3OThmNWZjNjZhZjEwODk5ZGVjZTAxNGE4NjI4Y2NmM2M1NDAyY2Q0YTQyNzAyMDc0NzJmOGY2ZTcxMiIKICB9LAogIHsKICAgImtleWlkIjogIjNjMzQ0YWEwNjhmZDRjYzRlODdkYzUwYjYxMmMwMjQzMWZiYzc3MWU5NTAwMzk5MzY4M2EyYjBiZjI2MGNmMGUiLAogICAic2lnIjogIjMwNDYwMjIxMDBiN2IwOTk5NmM0NWNhMmQ0YjA1NjAzZTU2YmFlZmEyOTcxOGEwYjcxMTQ3Y2Y4YzZlNjYzNDliYWE2MTQ3N2RmMDIyMTAwYzRkYTgwYzcxN2I0ZmE3YmJhMGZkNWM3MmRhOGEwNDk5MzU4YjAxMzU4YjIzMDlmNDFkMTQ1NmVhMWU3ZTFkOSIKICB9LAogIHsKICAgImtleWlkIjogImVjODE2Njk3MzRlMDE3OTk2YzViODVmM2QwMmMzZGUxZGQ0NjM3YTE1MjAxOWZlMWFmMTI1ZDJmOTM2OGI5NWUiLAogICAic2lnIjogIjMwNDYwMjIxMDBiZTk3ODJjMzA3NDRlNDExYTgyZmE4NWI1MTM4ZDYwMWNlMTQ4YmMxOTI1OGFlYzY0ZTdlYzI0NDc4ZjM4ODEyMDIyMTAwY2FlZjYzZGNhZjFhNGI5YTUwMGQzYmQwZTNmMTY0ZWMxOGYxYjYzZDdhOTQ2MGQ5YWNhYjEwNjZkYjBmMDE2ZCIKICB9LAogIHsKICAgImtleWlkIjogIjFlMWQ2NWNlOThiMTBhZGRhZDQ3NjRmZWJmN2RkYTJkMDQzNmIzZDNhMzg5MzU3OWMwZGRkYWVhMjBlNTQ4NDkiLAogICAic2lnIjogIjMwNDUwMjIwNzQ2ZWMzZjg1MzRjZTU1NTMxZDBkMDFmZjY0OTY0ZWY0NDBkMWU3ZDJjNGMxNDI0MDliOGU5NzY5ZjFhZGE2ZjAyMjEwMGUzYjkyOWZjZDkzZWExOGZlYWEwODI1ODg3YTcyMTA0ODk4NzlhNjY3ODBjMDdhODNmNGJkNDZlMmYwOWFiM2IiCiAgfQogXSwKICJzaWduZWQiOiB7CiAgIl90eXBlIjogInJvb3QiLAogICJjb25zaXN0ZW50X3NuYXBzaG90IjogdHJ1ZSwKICAiZXhwaXJlcyI6ICIyMDI1LTAyLTE5VDA4OjA0OjMyWiIsCiAgImtleXMiOiB7CiAgICIyMmY0Y2FlYzZkOGU2Zjk1NTVhZjY2YjNkNGMzY2IwNmEzYmIyM2ZkYzdlMzljOTE2YzYxZjQ2MmU2ZjUyYjA2IjogewogICAgImtleWlkX2hhc2hfYWxnb3JpdGhtcyI6IFsKICAgICAic2hhMjU2IiwKICAgICAic2hhNTEyIgogICAgXSwKICAgICJrZXl0eXBlIjogImVjZHNhIiwKICAgICJrZXl2YWwiOiB7CiAgICAgInB1YmxpYyI6ICItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxuTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFekJ6Vk9tSENQb2pNVkxTSTM2NFdpaVY4TlByRFxuNklnUnhWbGlza3ovdit5M0pFUjVtY1ZHY09ObGlEY1dNQzVKMmxmSG1qUE5QaGI0SDd4bThMemZTQT09XG4tLS0tLUVORCBQVUJMSUMgS0VZLS0tLS1cbiIKICAgIH0sCiAgICAic2NoZW1lIjogImVjZHNhLXNoYTItbmlzdHAyNTYiLAogICAgIngtdHVmLW9uLWNpLWtleW93bmVyIjogIkBzYW50aWFnb3RvcnJlcyIKICAgfSwKICAgIjYxNjQzODM4MTI1YjQ0MGI0MGRiNjk0MmY1Y2I1YTMxYzBkYzA0MzY4MzE2ZWIyYWFhNThiOTU5MDRhNTgyMjIiOiB7CiAgICAia2V5aWRfaGFzaF9hbGdvcml0aG1zIjogWwogICAgICJzaGEyNTYiLAogICAgICJzaGE1MTIiCiAgICBdLAogICAgImtleXR5cGUiOiAiZWNkc2EiLAogICAgImtleXZhbCI6IHsKICAgICAicHVibGljIjogIi0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tXG5NRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVpbmlrU3NBUW1Za05lSDVlWXEvQ25JekxhYWNPXG54bFNhYXdRRE93cUt5L3RDcXhxNXh4UFNKYzIxSzRXSWhzOUd5T2tLZnp1ZVkzR0lMemNNSlo0Y1d3PT1cbi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLVxuIgogICAgfSwKICAgICJzY2hlbWUiOiAiZWNkc2Etc2hhMi1uaXN0cDI1NiIsCiAgICAieC10dWYtb24tY2kta2V5b3duZXIiOiAiQGJvYmNhbGxhd2F5IgogICB9LAogICAiNmYyNjAwODlkNTkyM2RhZjIwMTY2Y2E2NTdjNTQzYWY2MTgzNDZhYjk3MTg4NGE5OTk2MmIwMTk4OGJiZTBjMyI6IHsKICAgICJrZXlpZF9oYXNoX2FsZ29yaXRobXMiOiBbCiAgICAgInNoYTI1NiIsCiAgICAgInNoYTUxMiIKICAgIF0sCiAgICAia2V5dHlwZSI6ICJlY2RzYSIsCiAgICAia2V5dmFsIjogewogICAgICJwdWJsaWMiOiAiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXk4WEtzbWhCWURJOEpjMEd3ekJ4ZUtheDBjbTVcblNUS0VVNjVIUEZ1blVuNDFzVDhwaTBGak00SWtIei9ZVW13bUxVTzBXdDdseGhqNkJrTElLNHFZQXc9PVxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tXG4iCiAgICB9LAogICAgInNjaGVtZSI6ICJlY2RzYS1zaGEyLW5pc3RwMjU2IiwKICAgICJ4LXR1Zi1vbi1jaS1rZXlvd25lciI6ICJAZGxvcmVuYyIKICAgfSwKICAgIjcyNDdmMGRiYWQ4NWIxNDdlMTg2M2JhZGU3NjEyNDNjYzc4NWRjYjdhYTQxMGU3MTA1ZGQzZDJiNjFhMzZkMmMiOiB7CiAgICAia2V5aWRfaGFzaF9hbGdvcml0aG1zIjogWwogICAgICJzaGEyNTYiLAogICAgICJzaGE1MTIiCiAgICBdLAogICAgImtleXR5cGUiOiAiZWNkc2EiLAogICAgImtleXZhbCI6IHsKICAgICAicHVibGljIjogIi0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tXG5NRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVXUmlHcjUraiszSjVTc0grWnRyNW5FMkgyd083XG5CVituTzNzOTNnTGNhMThxVE96SFkxb1d5QUdEeWtNU3NHVFVCU3Q5RCtBbjBLZktzRDJtZlNNNDJRPT1cbi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLVxuIgogICAgfSwKICAgICJzY2hlbWUiOiAiZWNkc2Etc2hhMi1uaXN0cDI1NiIsCiAgICAieC10dWYtb24tY2ktb25saW5lLXVyaSI6ICJnY3BrbXM6Ly9wcm9qZWN0cy9zaWdzdG9yZS1yb290LXNpZ25pbmcvbG9jYXRpb25zL2dsb2JhbC9rZXlSaW5ncy9yb290L2NyeXB0b0tleXMvdGltZXN0YW1wIgogICB9LAogICAiYTY4N2U1YmY0ZmFiODJiMGVlNThkNDZlMDVjOTUzNTE0NWEyYzlhZmI0NThmNDNkNDJiNDVjYTBmZGNlMmE3MCI6IHsKICAgICJrZXlpZF9oYXNoX2FsZ29yaXRobXMiOiBbCiAgICAgInNoYTI1NiIsCiAgICAgInNoYTUxMiIKICAgIF0sCiAgICAia2V5dHlwZSI6ICJlY2RzYSIsCiAgICAia2V5dmFsIjogewogICAgICJwdWJsaWMiOiAiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTBnaHJoOTJMdzFZcjNpZEdWNVdxQ3RNREI4Q3hcbitEOGhkQzR3MlpMTklwbFZSb1ZHTHNrWWEzZ2hlTXlPamlKOGtQaTE1YVEyLy83UCtvajdVdkpQR3c9PVxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tXG4iCiAgICB9LAogICAgInNjaGVtZSI6ICJlY2RzYS1zaGEyLW5pc3RwMjU2IiwKICAgICJ4LXR1Zi1vbi1jaS1rZXlvd25lciI6ICJAam9zaHVhZ2wiCiAgIH0sCiAgICJlNzFhNTRkNTQzODM1YmE4NmFkYWQ5NDYwMzc5Yzc2NDFmYjg3MjZkMTY0ZWE3NjY4MDFhMWM1MjJhYmE3ZWEyIjogewogICAgImtleWlkX2hhc2hfYWxnb3JpdGhtcyI6IFsKICAgICAic2hhMjU2IiwKICAgICAic2hhNTEyIgogICAgXSwKICAgICJrZXl0eXBlIjogImVjZHNhIiwKICAgICJrZXl2YWwiOiB7CiAgICAgInB1YmxpYyI6ICItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxuTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFRVhzejNTWlhGYjhqTVY0Mmo2cEpseWpialI4S1xuTjNCd29jZXhxNkxNSWI1cXNXS09RdkxOMTZOVWVmTGM0SHN3T291bVJzVlZhYWpTcFFTNmZvYmtSdz09XG4tLS0tLUVORCBQVUJMSUMgS0VZLS0tLS1cbiIKICAgIH0sCiAgICAic2NoZW1lIjogImVjZHNhLXNoYTItbmlzdHAyNTYiLAogICAgIngtdHVmLW9uLWNpLWtleW93bmVyIjogIkBtbm02NzgiCiAgIH0KICB9LAogICJyb2xlcyI6IHsKICAgInJvb3QiOiB7CiAgICAia2V5aWRzIjogWwogICAgICI2ZjI2MDA4OWQ1OTIzZGFmMjAxNjZjYTY1N2M1NDNhZjYxODM0NmFiOTcxODg0YTk5OTYyYjAxOTg4YmJlMGMzIiwKICAgICAiZTcxYTU0ZDU0MzgzNWJhODZhZGFkOTQ2MDM3OWM3NjQxZmI4NzI2ZDE2NGVhNzY2ODAxYTFjNTIyYWJhN2VhMiIsCiAgICAgIjIyZjRjYWVjNmQ4ZTZmOTU1NWFmNjZiM2Q0YzNjYjA2YTNiYjIzZmRjN2UzOWM5MTZjNjFmNDYyZTZmNTJiMDYiLAogICAgICI2MTY0MzgzODEyNWI0NDBiNDBkYjY5NDJmNWNiNWEzMWMwZGMwNDM2ODMxNmViMmFhYTU4Yjk1OTA0YTU4MjIyIiwKICAgICAiYTY4N2U1YmY0ZmFiODJiMGVlNThkNDZlMDVjOTUzNTE0NWEyYzlhZmI0NThmNDNkNDJiNDVjYTBmZGNlMmE3MCIKICAgIF0sCiAgICAidGhyZXNob2xkIjogMwogICB9LAogICAic25hcHNob3QiOiB7CiAgICAia2V5aWRzIjogWwogICAgICI3MjQ3ZjBkYmFkODViMTQ3ZTE4NjNiYWRlNzYxMjQzY2M3ODVkY2I3YWE0MTBlNzEwNWRkM2QyYjYxYTM2ZDJjIgogICAgXSwKICAgICJ0aHJlc2hvbGQiOiAxLAogICAgIngtdHVmLW9uLWNpLWV4cGlyeS1wZXJpb2QiOiAzNjUwLAogICAgIngtdHVmLW9uLWNpLXNpZ25pbmctcGVyaW9kIjogMzY1CiAgIH0sCiAgICJ0YXJnZXRzIjogewogICAgImtleWlkcyI6IFsKICAgICAiNmYyNjAwODlkNTkyM2RhZjIwMTY2Y2E2NTdjNTQzYWY2MTgzNDZhYjk3MTg4NGE5OTk2MmIwMTk4OGJiZTBjMyIsCiAgICAgImU3MWE1NGQ1NDM4MzViYTg2YWRhZDk0NjAzNzljNzY0MWZiODcyNmQxNjRlYTc2NjgwMWExYzUyMmFiYTdlYTIiLAogICAgICIyMmY0Y2FlYzZkOGU2Zjk1NTVhZjY2YjNkNGMzY2IwNmEzYmIyM2ZkYzdlMzljOTE2YzYxZjQ2MmU2ZjUyYjA2IiwKICAgICAiNjE2NDM4MzgxMjViNDQwYjQwZGI2OTQyZjVjYjVhMzFjMGRjMDQzNjgzMTZlYjJhYWE1OGI5NTkwNGE1ODIyMiIsCiAgICAgImE2ODdlNWJmNGZhYjgyYjBlZTU4ZDQ2ZTA1Yzk1MzUxNDVhMmM5YWZiNDU4ZjQzZDQyYjQ1Y2EwZmRjZTJhNzAiCiAgICBdLAogICAgInRocmVzaG9sZCI6IDMKICAgfSwKICAgInRpbWVzdGFtcCI6IHsKICAgICJrZXlpZHMiOiBbCiAgICAgIjcyNDdmMGRiYWQ4NWIxNDdlMTg2M2JhZGU3NjEyNDNjYzc4NWRjYjdhYTQxMGU3MTA1ZGQzZDJiNjFhMzZkMmMiCiAgICBdLAogICAgInRocmVzaG9sZCI6IDEsCiAgICAieC10dWYtb24tY2ktZXhwaXJ5LXBlcmlvZCI6IDcsCiAgICAieC10dWYtb24tY2ktc2lnbmluZy1wZXJpb2QiOiA0CiAgIH0KICB9LAogICJzcGVjX3ZlcnNpb24iOiAiMS4wIiwKICAidmVyc2lvbiI6IDEwLAogICJ4LXR1Zi1vbi1jaS1leHBpcnktcGVyaW9kIjogMTgyLAogICJ4LXR1Zi1vbi1jaS1zaWduaW5nLXBlcmlvZCI6IDMxCiB9Cn0=",targets:{"trusted_root.json":"ewogICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRldi5zaWdzdG9yZS50cnVzdGVkcm9vdCtqc29uO3ZlcnNpb249MC4xIiwKICAidGxvZ3MiOiBbCiAgICB7CiAgICAgICJiYXNlVXJsIjogImh0dHBzOi8vcmVrb3Iuc2lnc3RvcmUuZGV2IiwKICAgICAgImhhc2hBbGdvcml0aG0iOiAiU0hBMl8yNTYiLAogICAgICAicHVibGljS2V5IjogewogICAgICAgICJyYXdCeXRlcyI6ICJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUUyRzJZKzJ0YWJkVFY1QmNHaUJJeDBhOWZBRndya0JibUxTR3RrczRMM3FYNnlZWTB6dWZCbmhDOFVyL2l5NTVHaFdQLzlBL2JZMkxoQzMwTTkrUll0dz09IiwKICAgICAgICAia2V5RGV0YWlscyI6ICJQS0lYX0VDRFNBX1AyNTZfU0hBXzI1NiIsCiAgICAgICAgInZhbGlkRm9yIjogewogICAgICAgICAgInN0YXJ0IjogIjIwMjEtMDEtMTJUMTE6NTM6MjcuMDAwWiIKICAgICAgICB9CiAgICAgIH0sCiAgICAgICJsb2dJZCI6IHsKICAgICAgICAia2V5SWQiOiAid05JOWF0UUdseitWV2ZPNkxSeWdINFFVZlkvOFc0UkZ3aVQ1aTVXUmdCMD0iCiAgICAgIH0KICAgIH0KICBdLAogICJjZXJ0aWZpY2F0ZUF1dGhvcml0aWVzIjogWwogICAgewogICAgICAic3ViamVjdCI6IHsKICAgICAgICAib3JnYW5pemF0aW9uIjogInNpZ3N0b3JlLmRldiIsCiAgICAgICAgImNvbW1vbk5hbWUiOiAic2lnc3RvcmUiCiAgICAgIH0sCiAgICAgICJ1cmkiOiAiaHR0cHM6Ly9mdWxjaW8uc2lnc3RvcmUuZGV2IiwKICAgICAgImNlcnRDaGFpbiI6IHsKICAgICAgICAiY2VydGlmaWNhdGVzIjogWwogICAgICAgICAgewogICAgICAgICAgICAicmF3Qnl0ZXMiOiAiTUlJQitEQ0NBWDZnQXdJQkFnSVROVmtEWm9DaW9mUERzeTdkZm02Z2VMYnVoekFLQmdncWhrak9QUVFEQXpBcU1SVXdFd1lEVlFRS0V3eHphV2R6ZEc5eVpTNWtaWFl4RVRBUEJnTlZCQU1UQ0hOcFozTjBiM0psTUI0WERUSXhNRE13TnpBek1qQXlPVm9YRFRNeE1ESXlNekF6TWpBeU9Wb3dLakVWTUJNR0ExVUVDaE1NYzJsbmMzUnZjbVV1WkdWMk1SRXdEd1lEVlFRREV3aHphV2R6ZEc5eVpUQjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkxTeUE3SWk1aytwTk84WkVXWTB5bGVtV0Rvd09rTmEza0wrR1pFNVo1R1dlaEw5L0E5YlJOQTNSYnJzWjVpMEpjYXN0YVJMN1NwNWZwL2pENWR4cWMvVWRUVm5sdlMxNmFuKzJZZnN3ZS9RdUxvbFJVQ3JjT0UyKzJpQTUrdHpkNk5tTUdRd0RnWURWUjBQQVFIL0JBUURBZ0VHTUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDQVFFd0hRWURWUjBPQkJZRUZNakZIUUJCbWlRcE1sRWs2dzJ1U3UxS0J0UHNNQjhHQTFVZEl3UVlNQmFBRk1qRkhRQkJtaVFwTWxFazZ3MnVTdTFLQnRQc01Bb0dDQ3FHU000OUJBTURBMmdBTUdVQ01IOGxpV0pmTXVpNnZYWEJoakRnWTRNd3NsbU4vVEp4VmUvODNXckZvbXdtTmYwNTZ5MVg0OEY5YzRtM2Ezb3pYQUl4QUtqUmF5NS9hai9qc0tLR0lrbVFhdGpJOHV1cEhyLytDeEZ2YUpXbXBZcU5rTERHUlUrOW9yemg1aEkyUnJjdWFRPT0iCiAgICAgICAgICB9CiAgICAgICAgXQogICAgICB9LAogICAgICAidmFsaWRGb3IiOiB7CiAgICAgICAgInN0YXJ0IjogIjIwMjEtMDMtMDdUMDM6MjA6MjkuMDAwWiIsCiAgICAgICAgImVuZCI6ICIyMDIyLTEyLTMxVDIzOjU5OjU5Ljk5OVoiCiAgICAgIH0KICAgIH0sCiAgICB7CiAgICAgICJzdWJqZWN0IjogewogICAgICAgICJvcmdhbml6YXRpb24iOiAic2lnc3RvcmUuZGV2IiwKICAgICAgICAiY29tbW9uTmFtZSI6ICJzaWdzdG9yZSIKICAgICAgfSwKICAgICAgInVyaSI6ICJodHRwczovL2Z1bGNpby5zaWdzdG9yZS5kZXYiLAogICAgICAiY2VydENoYWluIjogewogICAgICAgICJjZXJ0aWZpY2F0ZXMiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICJyYXdCeXRlcyI6ICJNSUlDR2pDQ0FhR2dBd0lCQWdJVUFMblZpVmZuVTBickphc21Sa0hybi9VbmZhUXdDZ1lJS29aSXpqMEVBd013S2pFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUkV3RHdZRFZRUURFd2h6YVdkemRHOXlaVEFlRncweU1qQTBNVE15TURBMk1UVmFGdzB6TVRFd01EVXhNelUyTlRoYU1EY3hGVEFUQmdOVkJBb1RESE5wWjNOMGIzSmxMbVJsZGpFZU1Cd0dBMVVFQXhNVmMybG5jM1J2Y21VdGFXNTBaWEp0WldScFlYUmxNSFl3RUFZSEtvWkl6ajBDQVFZRks0RUVBQ0lEWWdBRThSVlMveXNIK05PdnVEWnlQSVp0aWxnVUY5TmxhcllwQWQ5SFAxdkJCSDFVNUNWNzdMU1M3czBaaUg0bkU3SHY3cHRTNkx2dlIvU1RrNzk4TFZnTXpMbEo0SGVJZkYzdEhTYWV4TGNZcFNBU3Ixa1MwTi9SZ0JKei85aldDaVhubzNzd2VUQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0V3WURWUjBsQkF3d0NnWUlLd1lCQlFVSEF3TXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMzlQcHoxWWtFWmI1cU5qcEtGV2l4aTRZWkQ4d0h3WURWUjBqQkJnd0ZvQVVXTUFlWDVGRnBXYXBlc3lRb1pNaTBDckZ4Zm93Q2dZSUtvWkl6ajBFQXdNRFp3QXdaQUl3UENzUUs0RFlpWllEUElhRGk1SEZLbmZ4WHg2QVNTVm1FUmZzeW5ZQmlYMlg2U0pSblpVODQvOURaZG5GdnZ4bUFqQk90NlFwQmxjNEovMER4dmtUQ3FwY2x2emlMNkJDQ1BuamRsSUIzUHUzQnhzUG15Z1VZN0lpMnpiZENkbGlpb3c9IgogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgInJhd0J5dGVzIjogIk1JSUI5ekNDQVh5Z0F3SUJBZ0lVQUxaTkFQRmR4SFB3amVEbG9Ed3lZQ2hBTy80d0NnWUlLb1pJemowRUF3TXdLakVWTUJNR0ExVUVDaE1NYzJsbmMzUnZjbVV1WkdWMk1SRXdEd1lEVlFRREV3aHphV2R6ZEc5eVpUQWVGdzB5TVRFd01EY3hNelUyTlRsYUZ3MHpNVEV3TURVeE16VTJOVGhhTUNveEZUQVRCZ05WQkFvVERITnBaM04wYjNKbExtUmxkakVSTUE4R0ExVUVBeE1JYzJsbmMzUnZjbVV3ZGpBUUJnY3Foa2pPUFFJQkJnVXJnUVFBSWdOaUFBVDdYZUZUNHJiM1BRR3dTNElhanRMazMvT2xucGdhbmdhQmNsWXBzWUJyNWkrNHluQjA3Y2ViM0xQME9JT1pkeGV4WDY5YzVpVnV5SlJRK0h6MDV5aStVRjN1QldBbEhwaVM1c2gwK0gyR0hFN1NYcmsxRUM1bTFUcjE5TDlnZzkyall6QmhNQTRHQTFVZER3RUIvd1FFQXdJQkJqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01CMEdBMVVkRGdRV0JCUll3QjVma1VXbFpxbDZ6SkNoa3lMUUtzWEYrakFmQmdOVkhTTUVHREFXZ0JSWXdCNWZrVVdsWnFsNnpKQ2hreUxRS3NYRitqQUtCZ2dxaGtqT1BRUURBd05wQURCbUFqRUFqMW5IZVhacCsxM05XQk5hK0VEc0RQOEcxV1dnMXRDTVdQL1dIUHFwYVZvMGpoc3dlTkZaZ1NzMGVFN3dZSTRxQWpFQTJXQjlvdDk4c0lrb0YzdlpZZGQzL1Z0V0I1YjlUTk1lYTdJeC9zdEo1VGZjTExlQUJMRTRCTkpPc1E0dm5CSEoiCiAgICAgICAgICB9CiAgICAgICAgXQogICAgICB9LAogICAgICAidmFsaWRGb3IiOiB7CiAgICAgICAgInN0YXJ0IjogIjIwMjItMDQtMTNUMjA6MDY6MTUuMDAwWiIKICAgICAgfQogICAgfQogIF0sCiAgImN0bG9ncyI6IFsKICAgIHsKICAgICAgImJhc2VVcmwiOiAiaHR0cHM6Ly9jdGZlLnNpZ3N0b3JlLmRldi90ZXN0IiwKICAgICAgImhhc2hBbGdvcml0aG0iOiAiU0hBMl8yNTYiLAogICAgICAicHVibGljS2V5IjogewogICAgICAgICJyYXdCeXRlcyI6ICJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUViZndSK1JKdWRYc2NnUkJScEtYMVhGRHkzUHl1ZER4ei9TZm5SaTFmVDhla3BmQmQyTzF1b3o3anIzWjhuS3p4QTY5RVVRK2VGQ0ZJM3pldWJQV1U3dz09IiwKICAgICAgICAia2V5RGV0YWlscyI6ICJQS0lYX0VDRFNBX1AyNTZfU0hBXzI1NiIsCiAgICAgICAgInZhbGlkRm9yIjogewogICAgICAgICAgInN0YXJ0IjogIjIwMjEtMDMtMTRUMDA6MDA6MDAuMDAwWiIsCiAgICAgICAgICAiZW5kIjogIjIwMjItMTAtMzFUMjM6NTk6NTkuOTk5WiIKICAgICAgICB9CiAgICAgIH0sCiAgICAgICJsb2dJZCI6IHsKICAgICAgICAia2V5SWQiOiAiQ0dDUzhDaFMvMmhGMGRGcko0U2NSV2NZckJZOXd6alNiZWE4SWdZMmIzST0iCiAgICAgIH0KICAgIH0sCiAgICB7CiAgICAgICJiYXNlVXJsIjogImh0dHBzOi8vY3RmZS5zaWdzdG9yZS5kZXYvMjAyMiIsCiAgICAgICJoYXNoQWxnb3JpdGhtIjogIlNIQTJfMjU2IiwKICAgICAgInB1YmxpY0tleSI6IHsKICAgICAgICAicmF3Qnl0ZXMiOiAiTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFaVBTbEZpMENtRlRmRWpDVXFGOUh1Q0VjWVhOS0FhWWFsSUptQlo4eXllelBqVHFoeHJLQnBNbmFvY1Z0TEpCSTFlTTN1WG5RelFHQUpkSjRnczlGeXc9PSIsCiAgICAgICAgImtleURldGFpbHMiOiAiUEtJWF9FQ0RTQV9QMjU2X1NIQV8yNTYiLAogICAgICAgICJ2YWxpZEZvciI6IHsKICAgICAgICAgICJzdGFydCI6ICIyMDIyLTEwLTIwVDAwOjAwOjAwLjAwMFoiCiAgICAgICAgfQogICAgICB9LAogICAgICAibG9nSWQiOiB7CiAgICAgICAgImtleUlkIjogIjNUMHdhc2JIRVRKakdSNGNtV2MzQXFKS1hyamVQSzMvaDRweWdDOHA3bzQ9IgogICAgICB9CiAgICB9CiAgXSwKICAidGltZXN0YW1wQXV0aG9yaXRpZXMiOiBbCiAgICB7CiAgICAgICJzdWJqZWN0IjogewogICAgICAgICJvcmdhbml6YXRpb24iOiAiR2l0SHViLCBJbmMuIiwKICAgICAgICAiY29tbW9uTmFtZSI6ICJJbnRlcm5hbCBTZXJ2aWNlcyBSb290IgogICAgICB9LAogICAgICAiY2VydENoYWluIjogewogICAgICAgICJjZXJ0aWZpY2F0ZXMiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICJyYXdCeXRlcyI6ICJNSUlCM0RDQ0FXS2dBd0lCQWdJVWNoa05zSDM2WGEwNGIxTHFJYytxcjlEVmVjTXdDZ1lJS29aSXpqMEVBd013TWpFVk1CTUdBMVVFQ2hNTVIybDBTSFZpTENCSmJtTXVNUmt3RndZRFZRUURFeEJVVTBFZ2FXNTBaWEp0WldScFlYUmxNQjRYRFRJek1EUXhOREF3TURBd01Gb1hEVEkwTURReE16QXdNREF3TUZvd01qRVZNQk1HQTFVRUNoTU1SMmwwU0hWaUxDQkpibU11TVJrd0Z3WURWUVFERXhCVVUwRWdWR2x0WlhOMFlXMXdhVzVuTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFVUQ1Wk5iU3FZTWQ2cjhxcE9PRVg5aWJHblpUOUdzdVhPaHIvZjhVOUZKdWdCR0V4S1lwNDBPVUxTMGVyalpXN3hWOXhWNTJObkpmNU9lRHE0ZTVaS3FOV01GUXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1CTUdBMVVkSlFRTU1Bb0dDQ3NHQVFVRkJ3TUlNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVhVzFSdWRPZ1Z0MGxlcVkwV0tZYnVQcjQ3d0F3Q2dZSUtvWkl6ajBFQXdNRGFBQXdaUUl3YlVIOUh2RDRlakNaSk9XUW5xQWxrcVVSbGx2dTlNOCtWcUxiaVJLK3pTZlpDWndzaWxqUm44TVFRUlNrWEVFNUFqRUFnK1Z4cXRvamZWZnU4RGh6emhDeDlHS0VUYkpIYjE5aVY3Mm1NS1ViREFGbXpaNmJROGI1NFpiOHRpZHk1YVdlIgogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgInJhd0J5dGVzIjogIk1JSUNFRENDQVpXZ0F3SUJBZ0lVWDhaTzVRWFA3dk40ZE1RNWU5c1UzbnViOE9nd0NnWUlLb1pJemowRUF3TXdPREVWTUJNR0ExVUVDaE1NUjJsMFNIVmlMQ0JKYm1NdU1SOHdIUVlEVlFRREV4WkpiblJsY201aGJDQlRaWEoyYVdObGN5QlNiMjkwTUI0WERUSXpNRFF4TkRBd01EQXdNRm9YRFRJNE1EUXhNakF3TURBd01Gb3dNakVWTUJNR0ExVUVDaE1NUjJsMFNIVmlMQ0JKYm1NdU1Sa3dGd1lEVlFRREV4QlVVMEVnYVc1MFpYSnRaV1JwWVhSbE1IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFdk1MWS9kVFZidklKWUFOQXVzekV3Sm5RRTFsbGZ0eW55TUtJTWhoNDhIbXFiVnI1eWd5YnpzTFJMVktiQldPZFoyMWFlSnorZ1ppeXRaZXRxY3lGOVdsRVI1TkVNZjZKVjdaTm9qUXB4SHE0UkhHb0dTY2VRdi9xdlRpWnhFREtvMll3WkRBT0JnTlZIUThCQWY4RUJBTUNBUVl3RWdZRFZSMFRBUUgvQkFnd0JnRUIvd0lCQURBZEJnTlZIUTRFRmdRVWFXMVJ1ZE9nVnQwbGVxWTBXS1lidVByNDd3QXdId1lEVlIwakJCZ3dGb0FVOU5ZWWxvYm5BRzRjMC9xanh5SC9scS93eitRd0NnWUlLb1pJemowRUF3TURhUUF3WmdJeEFLMUIxODV5Z0NySVlGbElzM0dqc3dqbndTTUc2TFk4d29MVmRha0tEWnhWYThmOGNxTXMxRGhjeEowKzA5dzk1UUl4QU8rdEJ6Wms3dmpVSjlpSmdENFI2WldUeFFXS3FObTc0ak85OW8rbzlzdjRGSS9TWlRaVEZ5TW4wSUpFSGRObXlBPT0iCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAicmF3Qnl0ZXMiOiAiTUlJQjlEQ0NBWHFnQXdJQkFnSVVhL0pBa2RVaks0SlV3c3F0YWlSSkdXaHFMU293Q2dZSUtvWkl6ajBFQXdNd09ERVZNQk1HQTFVRUNoTU1SMmwwU0hWaUxDQkpibU11TVI4d0hRWURWUVFERXhaSmJuUmxjbTVoYkNCVFpYSjJhV05sY3lCU2IyOTBNQjRYRFRJek1EUXhOREF3TURBd01Gb1hEVE16TURReE1UQXdNREF3TUZvd09ERVZNQk1HQTFVRUNoTU1SMmwwU0hWaUxDQkpibU11TVI4d0hRWURWUVFERXhaSmJuUmxjbTVoYkNCVFpYSjJhV05sY3lCU2IyOTBNSFl3RUFZSEtvWkl6ajBDQVFZRks0RUVBQ0lEWWdBRWY5akZBWHh6NGt4NjhBSFJNT2tGQmhmbERjTVR2emFYejR4L0ZDY1hqSi8xcUVLb24vcVBJR25hVVJza0R0eU5iTkRPcGVKVERERnF0NDhpTVBybnpweDZJWndxZW1mVUpONHhCRVpmemErcFl0L2l5b2QrOXRacjIwUlJXU3YvbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFqQWRCZ05WSFE0RUZnUVU5TllZbG9ibkFHNGMwL3FqeHlIL2xxL3d6K1F3Q2dZSUtvWkl6ajBFQXdNRGFBQXdaUUl4QUxaTFo4QmdSWHpLeExNTU45VklsTytlNGhyQm5OQmdGN3R6N0hucm93djJOZXRaRXJJQUNLRnltQmx2V0R2dE1BSXdaTytraTZzc1ExYnNabzk4TzhtRUFmMk5aN2lpQ2dERFUwVndqZWNvNnp5ZWgwekJUczkvN2dWNkFITlE1M3hEIgogICAgICAgICAgfQogICAgICAgIF0KICAgICAgfSwKICAgICAgInZhbGlkRm9yIjogewogICAgICAgICJzdGFydCI6ICIyMDIzLTA0LTE0VDAwOjAwOjAwLjAwMFoiCiAgICAgIH0KICAgIH0KICBdCn0K","registry.npmjs.org%2Fkeys.json":"ewogICAgImtleXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAia2V5SWQiOiAiU0hBMjU2OmpsM2J3c3d1ODBQampva0NnaDBvMnc1YzJVNExoUUFFNTdnajljejFrekEiLAogICAgICAgICAgICAia2V5VXNhZ2UiOiAibnBtOnNpZ25hdHVyZXMiLAogICAgICAgICAgICAicHVibGljS2V5IjogewogICAgICAgICAgICAgICAgInJhd0J5dGVzIjogIk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTFPbGIzek1BRkZ4WEtIaUlrUU81Y0ozWWhsNWk2VVBwK0lodXRlQkpidUhjQTVVb2dLbzBFV3RsV3dXNktTYUtvVE5FWUw3SmxDUWlWbmtoQmt0VWdnPT0iLAogICAgICAgICAgICAgICAgImtleURldGFpbHMiOiAiUEtJWF9FQ0RTQV9QMjU2X1NIQV8yNTYiLAogICAgICAgICAgICAgICAgInZhbGlkRm9yIjogewogICAgICAgICAgICAgICAgICAgICJzdGFydCI6ICIxOTk5LTAxLTAxVDAwOjAwOjAwLjAwMFoiLAogICAgICAgICAgICAgICAgICAgICJlbmQiOiAiMjAyNS0wMS0yOVQwMDowMDowMC4wMDBaIgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAgICJrZXlJZCI6ICJTSEEyNTY6amwzYndzd3U4MFBqam9rQ2doMG8ydzVjMlU0TGhRQUU1N2dqOWN6MWt6QSIsCiAgICAgICAgICAgICJrZXlVc2FnZSI6ICJucG06YXR0ZXN0YXRpb25zIiwKICAgICAgICAgICAgInB1YmxpY0tleSI6IHsKICAgICAgICAgICAgICAgICJyYXdCeXRlcyI6ICJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUUxT2xiM3pNQUZGeFhLSGlJa1FPNWNKM1lobDVpNlVQcCtJaHV0ZUJKYnVIY0E1VW9nS28wRVd0bFd3VzZLU2FLb1RORVlMN0psQ1FpVm5raEJrdFVnZz09IiwKICAgICAgICAgICAgICAgICJrZXlEZXRhaWxzIjogIlBLSVhfRUNEU0FfUDI1Nl9TSEFfMjU2IiwKICAgICAgICAgICAgICAgICJ2YWxpZEZvciI6IHsKICAgICAgICAgICAgICAgICAgICAic3RhcnQiOiAiMjAyMi0xMi0wMVQwMDowMDowMC4wMDBaIiwKICAgICAgICAgICAgICAgICAgICAiZW5kIjogIjIwMjUtMDEtMjlUMDA6MDA6MDAuMDAwWiIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgICAia2V5SWQiOiAiU0hBMjU2OkRoUTh3UjVBUEJ2RkhMRi8rVGMrQVl2UE9kVHBjSURxT2h4c0JIUndDN1UiLAogICAgICAgICAgICAia2V5VXNhZ2UiOiAibnBtOnNpZ25hdHVyZXMiLAogICAgICAgICAgICAicHVibGljS2V5IjogewogICAgICAgICAgICAgICAgInJhd0J5dGVzIjogIk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRVk2WWE3VysrN2FVUHp2TVRyZXpINlljeDNjK0hPS1lDY05HeWJKWlNDSnEvZmQ3UWE4dXVBS3RkSWtVUXRRaUVLRVJoQW1FNWxNTUpoUDhPa0RPYTJnPT0iLAogICAgICAgICAgICAgICAgImtleURldGFpbHMiOiAiUEtJWF9FQ0RTQV9QMjU2X1NIQV8yNTYiLAogICAgICAgICAgICAgICAgInZhbGlkRm9yIjogewogICAgICAgICAgICAgICAgICAgICJzdGFydCI6ICIyMDI1LTAxLTEzVDAwOjAwOjAwLjAwMFoiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHsKICAgICAgICAgICAgImtleUlkIjogIlNIQTI1NjpEaFE4d1I1QVBCdkZITEYvK1RjK0FZdlBPZFRwY0lEcU9oeHNCSFJ3QzdVIiwKICAgICAgICAgICAgImtleVVzYWdlIjogIm5wbTphdHRlc3RhdGlvbnMiLAogICAgICAgICAgICAicHVibGljS2V5IjogewogICAgICAgICAgICAgICAgInJhd0J5dGVzIjogIk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRVk2WWE3VysrN2FVUHp2TVRyZXpINlljeDNjK0hPS1lDY05HeWJKWlNDSnEvZmQ3UWE4dXVBS3RkSWtVUXRRaUVLRVJoQW1FNWxNTUpoUDhPa0RPYTJnPT0iLAogICAgICAgICAgICAgICAgImtleURldGFpbHMiOiAiUEtJWF9FQ0RTQV9QMjU2X1NIQV8yNTYiLAogICAgICAgICAgICAgICAgInZhbGlkRm9yIjogewogICAgICAgICAgICAgICAgICAgICJzdGFydCI6ICIyMDI1LTAxLTEzVDAwOjAwOjAwLjAwMFoiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICBdCn0K"}}}});var TNe=L(I1=>{"use strict";var QNe=I1&&I1.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(I1,"__esModule",{value:!0});I1.TUFClient=void 0;var Rg=QNe(ye("fs")),sP=QNe(ye("path")),k8t=PNe(),Q8t=wL(),T8t=xNe(),LJ="targets",OJ=class{constructor(e){let r=new URL(e.mirrorURL),s=encodeURIComponent(r.host+r.pathname.replace(/\/$/,"")),a=sP.default.join(e.cachePath,s);R8t(a),F8t({cachePath:a,mirrorURL:e.mirrorURL,tufRootPath:e.rootPath,forceInit:e.forceInit}),this.updater=N8t({mirrorURL:e.mirrorURL,cachePath:a,forceCache:e.forceCache,retry:e.retry,timeout:e.timeout})}async refresh(){return this.updater.refresh()}getTarget(e){return(0,T8t.readTarget)(this.updater,e)}};I1.TUFClient=OJ;function R8t(t){let e=sP.default.join(t,LJ);Rg.default.existsSync(t)||Rg.default.mkdirSync(t,{recursive:!0}),Rg.default.existsSync(e)||Rg.default.mkdirSync(e)}function F8t({cachePath:t,mirrorURL:e,tufRootPath:r,forceInit:s}){let a=sP.default.join(t,"root.json");if(!Rg.default.existsSync(a)||s)if(r)Rg.default.copyFileSync(r,a);else{let c=kNe()[e];if(!c)throw new Q8t.TUFError({code:"TUF_INIT_CACHE_ERROR",message:`No root.json found for mirror: ${e}`});Rg.default.writeFileSync(a,Buffer.from(c["root.json"],"base64")),Object.entries(c.targets).forEach(([f,p])=>{Rg.default.writeFileSync(sP.default.join(t,LJ,f),Buffer.from(p,"base64"))})}}function N8t(t){let e={fetchTimeout:t.timeout,fetchRetry:t.retry};return new k8t.Updater({metadataBaseUrl:t.mirrorURL,targetBaseUrl:`${t.mirrorURL}/targets`,metadataDir:t.cachePath,targetDir:sP.default.join(t.cachePath,LJ),forceCache:t.forceCache,config:e})}});var wL=L(yh=>{"use strict";Object.defineProperty(yh,"__esModule",{value:!0});yh.TUFError=yh.DEFAULT_MIRROR_URL=void 0;yh.getTrustedRoot=q8t;yh.initTUF=G8t;var O8t=Rb(),L8t=fFe(),M8t=TNe();yh.DEFAULT_MIRROR_URL="https://tuf-repo-cdn.sigstore.dev";var _8t="sigstore-js",U8t={retries:2},H8t=5e3,j8t="trusted_root.json";async function q8t(t={}){let r=await RNe(t).getTarget(j8t);return O8t.TrustedRoot.fromJSON(JSON.parse(r))}async function G8t(t={}){let e=RNe(t);return e.refresh().then(()=>e)}function RNe(t){return new M8t.TUFClient({cachePath:t.cachePath||(0,L8t.appDataPath)(_8t),rootPath:t.rootPath,mirrorURL:t.mirrorURL||yh.DEFAULT_MIRROR_URL,retry:t.retry??U8t,timeout:t.timeout??H8t,forceCache:t.forceCache??!1,forceInit:t.forceInit??t.force??!1})}var W8t=NJ();Object.defineProperty(yh,"TUFError",{enumerable:!0,get:function(){return W8t.TUFError}})});var FNe=L(BL=>{"use strict";Object.defineProperty(BL,"__esModule",{value:!0});BL.DSSESignatureContent=void 0;var oP=wl(),MJ=class{constructor(e){this.env=e}compareDigest(e){return oP.crypto.bufferEqual(e,oP.crypto.digest("sha256",this.env.payload))}compareSignature(e){return oP.crypto.bufferEqual(e,this.signature)}verifySignature(e){return oP.crypto.verify(this.preAuthEncoding,e,this.signature)}get signature(){return this.env.signatures.length>0?this.env.signatures[0].sig:Buffer.from("")}get preAuthEncoding(){return oP.dsse.preAuthEncoding(this.env.payloadType,this.env.payload)}};BL.DSSESignatureContent=MJ});var NNe=L(vL=>{"use strict";Object.defineProperty(vL,"__esModule",{value:!0});vL.MessageSignatureContent=void 0;var _J=wl(),UJ=class{constructor(e,r){this.signature=e.signature,this.messageDigest=e.messageDigest.digest,this.artifact=r}compareSignature(e){return _J.crypto.bufferEqual(e,this.signature)}compareDigest(e){return _J.crypto.bufferEqual(e,this.messageDigest)}verifySignature(e){return _J.crypto.verify(this.artifact,e,this.signature)}};vL.MessageSignatureContent=UJ});var LNe=L(SL=>{"use strict";Object.defineProperty(SL,"__esModule",{value:!0});SL.toSignedEntity=K8t;SL.signatureContent=ONe;var HJ=wl(),Y8t=FNe(),V8t=NNe();function K8t(t,e){let{tlogEntries:r,timestampVerificationData:s}=t.verificationMaterial,a=[];for(let n of r)a.push({$case:"transparency-log",tlogEntry:n});for(let n of s?.rfc3161Timestamps??[])a.push({$case:"timestamp-authority",timestamp:HJ.RFC3161Timestamp.parse(n.signedTimestamp)});return{signature:ONe(t,e),key:J8t(t),tlogEntries:r,timestamps:a}}function ONe(t,e){switch(t.content.$case){case"dsseEnvelope":return new Y8t.DSSESignatureContent(t.content.dsseEnvelope);case"messageSignature":return new V8t.MessageSignatureContent(t.content.messageSignature,e)}}function J8t(t){switch(t.verificationMaterial.content.$case){case"publicKey":return{$case:"public-key",hint:t.verificationMaterial.content.publicKey.hint};case"x509CertificateChain":return{$case:"certificate",certificate:HJ.X509Certificate.parse(t.verificationMaterial.content.x509CertificateChain.certificates[0].rawBytes)};case"certificate":return{$case:"certificate",certificate:HJ.X509Certificate.parse(t.verificationMaterial.content.certificate.rawBytes)}}}});var Co=L(C1=>{"use strict";Object.defineProperty(C1,"__esModule",{value:!0});C1.PolicyError=C1.VerificationError=void 0;var DL=class extends Error{constructor({code:e,message:r,cause:s}){super(r),this.code=e,this.cause=s,this.name=this.constructor.name}},jJ=class extends DL{};C1.VerificationError=jJ;var qJ=class extends DL{};C1.PolicyError=qJ});var MNe=L(bL=>{"use strict";Object.defineProperty(bL,"__esModule",{value:!0});bL.filterCertAuthorities=z8t;bL.filterTLogAuthorities=Z8t;function z8t(t,e){return t.filter(r=>r.validFor.start<=e.start&&r.validFor.end>=e.end)}function Z8t(t,e){return t.filter(r=>e.logID&&!r.logID.equals(e.logID)?!1:r.validFor.start<=e.targetDate&&e.targetDate<=r.validFor.end)}});var hy=L(py=>{"use strict";Object.defineProperty(py,"__esModule",{value:!0});py.filterTLogAuthorities=py.filterCertAuthorities=void 0;py.toTrustMaterial=$8t;var GJ=wl(),aP=Rb(),X8t=Co(),WJ=new Date(0),YJ=new Date(864e13),HNe=MNe();Object.defineProperty(py,"filterCertAuthorities",{enumerable:!0,get:function(){return HNe.filterCertAuthorities}});Object.defineProperty(py,"filterTLogAuthorities",{enumerable:!0,get:function(){return HNe.filterTLogAuthorities}});function $8t(t,e){let r=typeof e=="function"?e:eHt(e);return{certificateAuthorities:t.certificateAuthorities.map(UNe),timestampAuthorities:t.timestampAuthorities.map(UNe),tlogs:t.tlogs.map(_Ne),ctlogs:t.ctlogs.map(_Ne),publicKey:r}}function _Ne(t){let e=t.publicKey.keyDetails,r=e===aP.PublicKeyDetails.PKCS1_RSA_PKCS1V5||e===aP.PublicKeyDetails.PKIX_RSA_PKCS1V5||e===aP.PublicKeyDetails.PKIX_RSA_PKCS1V15_2048_SHA256||e===aP.PublicKeyDetails.PKIX_RSA_PKCS1V15_3072_SHA256||e===aP.PublicKeyDetails.PKIX_RSA_PKCS1V15_4096_SHA256?"pkcs1":"spki";return{logID:t.logId.keyId,publicKey:GJ.crypto.createPublicKey(t.publicKey.rawBytes,r),validFor:{start:t.publicKey.validFor?.start||WJ,end:t.publicKey.validFor?.end||YJ}}}function UNe(t){return{certChain:t.certChain.certificates.map(e=>GJ.X509Certificate.parse(e.rawBytes)),validFor:{start:t.validFor?.start||WJ,end:t.validFor?.end||YJ}}}function eHt(t){return e=>{let r=(t||{})[e];if(!r)throw new X8t.VerificationError({code:"PUBLIC_KEY_ERROR",message:`key not found: ${e}`});return{publicKey:GJ.crypto.createPublicKey(r.rawBytes),validFor:s=>(r.validFor?.start||WJ)<=s&&(r.validFor?.end||YJ)>=s}}}});var VJ=L(lP=>{"use strict";Object.defineProperty(lP,"__esModule",{value:!0});lP.CertificateChainVerifier=void 0;lP.verifyCertificateChain=rHt;var gy=Co(),tHt=hy();function rHt(t,e){let r=(0,tHt.filterCertAuthorities)(e,{start:t.notBefore,end:t.notAfter}),s;for(let a of r)try{return new PL({trustedCerts:a.certChain,untrustedCert:t}).verify()}catch(n){s=n}throw new gy.VerificationError({code:"CERTIFICATE_ERROR",message:"Failed to verify certificate chain",cause:s})}var PL=class{constructor(e){this.untrustedCert=e.untrustedCert,this.trustedCerts=e.trustedCerts,this.localCerts=nHt([...e.trustedCerts,e.untrustedCert])}verify(){let e=this.sort();return this.checkPath(e),e}sort(){let e=this.untrustedCert,r=this.buildPaths(e);if(r=r.filter(a=>a.some(n=>this.trustedCerts.includes(n))),r.length===0)throw new gy.VerificationError({code:"CERTIFICATE_ERROR",message:"no trusted certificate path found"});let s=r.reduce((a,n)=>a.length{if(s&&a.extSubjectKeyID){a.extSubjectKeyID.keyIdentifier.equals(s)&&r.push(a);return}a.subject.equals(e.issuer)&&r.push(a)}),r=r.filter(a=>{try{return e.verify(a)}catch{return!1}}),r)}checkPath(e){if(e.length<1)throw new gy.VerificationError({code:"CERTIFICATE_ERROR",message:"certificate chain must contain at least one certificate"});if(!e.slice(1).every(s=>s.isCA))throw new gy.VerificationError({code:"CERTIFICATE_ERROR",message:"intermediate certificate is not a CA"});for(let s=e.length-2;s>=0;s--)if(!e[s].issuer.equals(e[s+1].subject))throw new gy.VerificationError({code:"CERTIFICATE_ERROR",message:"incorrect certificate name chaining"});for(let s=0;s{"use strict";Object.defineProperty(KJ,"__esModule",{value:!0});KJ.verifySCTs=oHt;var xL=wl(),iHt=Co(),sHt=hy();function oHt(t,e,r){let s,a=t.clone();for(let p=0;p{if(!(0,sHt.filterTLogAuthorities)(r,{logID:p.logID,targetDate:p.datetime}).some(C=>p.verify(n.buffer,C.publicKey)))throw new iHt.VerificationError({code:"CERTIFICATE_ERROR",message:"SCT verification failed"});return p.logID})}});var GNe=L(kL=>{"use strict";Object.defineProperty(kL,"__esModule",{value:!0});kL.verifyPublicKey=AHt;kL.verifyCertificate=pHt;var aHt=wl(),qNe=Co(),lHt=VJ(),cHt=jNe(),uHt="1.3.6.1.4.1.57264.1.1",fHt="1.3.6.1.4.1.57264.1.8";function AHt(t,e,r){let s=r.publicKey(t);return e.forEach(a=>{if(!s.validFor(a))throw new qNe.VerificationError({code:"PUBLIC_KEY_ERROR",message:`Public key is not valid for timestamp: ${a.toISOString()}`})}),{key:s.publicKey}}function pHt(t,e,r){let s=(0,lHt.verifyCertificateChain)(t,r.certificateAuthorities);if(!e.every(n=>s.every(c=>c.validForDate(n))))throw new qNe.VerificationError({code:"CERTIFICATE_ERROR",message:"certificate is not valid or expired at the specified date"});return{scts:(0,cHt.verifySCTs)(s[0],s[1],r.ctlogs),signer:hHt(s[0])}}function hHt(t){let e,r=t.extension(fHt);r?e=r.valueObj.subs?.[0]?.value.toString("ascii"):e=t.extension(uHt)?.value.toString("ascii");let s={extensions:{issuer:e},subjectAlternativeName:t.subjectAltName};return{key:aHt.crypto.createPublicKey(t.publicKey),identity:s}}});var YNe=L(QL=>{"use strict";Object.defineProperty(QL,"__esModule",{value:!0});QL.verifySubjectAlternativeName=gHt;QL.verifyExtensions=dHt;var WNe=Co();function gHt(t,e){if(e===void 0||!e.match(t))throw new WNe.PolicyError({code:"UNTRUSTED_SIGNER_ERROR",message:`certificate identity error - expected ${t}, got ${e}`})}function dHt(t,e={}){let r;for(r in t)if(e[r]!==t[r])throw new WNe.PolicyError({code:"UNTRUSTED_SIGNER_ERROR",message:`invalid certificate extension - expected ${r}=${t[r]}, got ${r}=${e[r]}`})}});var VNe=L($J=>{"use strict";Object.defineProperty($J,"__esModule",{value:!0});$J.verifyCheckpoint=EHt;var zJ=wl(),w1=Co(),mHt=hy(),JJ=` + +`,yHt=/\u2014 (\S+) (\S+)\n/g;function EHt(t,e){let r=(0,mHt.filterTLogAuthorities)(e,{targetDate:new Date(Number(t.integratedTime)*1e3)}),s=t.inclusionProof,a=ZJ.fromString(s.checkpoint.envelope),n=XJ.fromString(a.note);if(!IHt(a,r))throw new w1.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"invalid checkpoint signature"});if(!zJ.crypto.bufferEqual(n.logHash,s.rootHash))throw new w1.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"root hash mismatch"})}function IHt(t,e){let r=Buffer.from(t.note,"utf-8");return t.signatures.every(s=>{let a=e.find(n=>zJ.crypto.bufferEqual(n.logID.subarray(0,4),s.keyHint));return a?zJ.crypto.verify(r,a.publicKey,s.signature):!1})}var ZJ=class t{constructor(e,r){this.note=e,this.signatures=r}static fromString(e){if(!e.includes(JJ))throw new w1.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"missing checkpoint separator"});let r=e.indexOf(JJ),s=e.slice(0,r+1),n=e.slice(r+JJ.length).matchAll(yHt),c=Array.from(n,f=>{let[,p,h]=f,E=Buffer.from(h,"base64");if(E.length<5)throw new w1.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"malformed checkpoint signature"});return{name:p,keyHint:E.subarray(0,4),signature:E.subarray(4)}});if(c.length===0)throw new w1.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"no signatures found in checkpoint"});return new t(s,c)}},XJ=class t{constructor(e,r,s,a){this.origin=e,this.logSize=r,this.logHash=s,this.rest=a}static fromString(e){let r=e.trimEnd().split(` +`);if(r.length<3)throw new w1.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"too few lines in checkpoint header"});let s=r[0],a=BigInt(r[1]),n=Buffer.from(r[2],"base64"),c=r.slice(3);return new t(s,a,n,c)}}});var KNe=L(nz=>{"use strict";Object.defineProperty(nz,"__esModule",{value:!0});nz.verifyMerkleInclusion=BHt;var rz=wl(),ez=Co(),CHt=Buffer.from([0]),wHt=Buffer.from([1]);function BHt(t){let e=t.inclusionProof,r=BigInt(e.logIndex),s=BigInt(e.treeSize);if(r<0n||r>=s)throw new ez.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:`invalid index: ${r}`});let{inner:a,border:n}=vHt(r,s);if(e.hashes.length!==a+n)throw new ez.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"invalid hash count"});let c=e.hashes.slice(0,a),f=e.hashes.slice(a),p=kHt(t.canonicalizedBody),h=DHt(SHt(p,c,r),f);if(!rz.crypto.bufferEqual(h,e.rootHash))throw new ez.VerificationError({code:"TLOG_INCLUSION_PROOF_ERROR",message:"calculated root hash does not match inclusion proof"})}function vHt(t,e){let r=bHt(t,e),s=PHt(t>>BigInt(r));return{inner:r,border:s}}function SHt(t,e,r){return e.reduce((s,a,n)=>r>>BigInt(n)&BigInt(1)?tz(a,s):tz(s,a),t)}function DHt(t,e){return e.reduce((r,s)=>tz(s,r),t)}function bHt(t,e){return xHt(t^e-BigInt(1))}function PHt(t){return t.toString(2).split("1").length-1}function xHt(t){return t===0n?0:t.toString(2).length}function tz(t,e){return rz.crypto.digest("sha256",wHt,t,e)}function kHt(t){return rz.crypto.digest("sha256",CHt,t)}});var zNe=L(iz=>{"use strict";Object.defineProperty(iz,"__esModule",{value:!0});iz.verifyTLogSET=RHt;var JNe=wl(),QHt=Co(),THt=hy();function RHt(t,e){if(!(0,THt.filterTLogAuthorities)(e,{logID:t.logId.keyId,targetDate:new Date(Number(t.integratedTime)*1e3)}).some(a=>{let n=FHt(t),c=Buffer.from(JNe.json.canonicalize(n),"utf8"),f=t.inclusionPromise.signedEntryTimestamp;return JNe.crypto.verify(c,a.publicKey,f)}))throw new QHt.VerificationError({code:"TLOG_INCLUSION_PROMISE_ERROR",message:"inclusion promise could not be verified"})}function FHt(t){let{integratedTime:e,logIndex:r,logId:s,canonicalizedBody:a}=t;return{body:a.toString("base64"),integratedTime:Number(e),logIndex:Number(r),logID:s.keyId.toString("hex")}}});var ZNe=L(az=>{"use strict";Object.defineProperty(az,"__esModule",{value:!0});az.verifyRFC3161Timestamp=LHt;var sz=wl(),oz=Co(),NHt=VJ(),OHt=hy();function LHt(t,e,r){let s=t.signingTime;if(r=(0,OHt.filterCertAuthorities)(r,{start:s,end:s}),r=_Ht(r,{serialNumber:t.signerSerialNumber,issuer:t.signerIssuer}),!r.some(n=>{try{return MHt(t,e,n),!0}catch{return!1}}))throw new oz.VerificationError({code:"TIMESTAMP_ERROR",message:"timestamp could not be verified"})}function MHt(t,e,r){let[s,...a]=r.certChain,n=sz.crypto.createPublicKey(s.publicKey),c=t.signingTime;try{new NHt.CertificateChainVerifier({untrustedCert:s,trustedCerts:a}).verify()}catch{throw new oz.VerificationError({code:"TIMESTAMP_ERROR",message:"invalid certificate chain"})}if(!r.certChain.every(p=>p.validForDate(c)))throw new oz.VerificationError({code:"TIMESTAMP_ERROR",message:"timestamp was signed with an expired certificate"});t.verify(e,n)}function _Ht(t,e){return t.filter(r=>r.certChain.length>0&&sz.crypto.bufferEqual(r.certChain[0].serialNumber,e.serialNumber)&&sz.crypto.bufferEqual(r.certChain[0].issuer,e.issuer))}});var XNe=L(TL=>{"use strict";Object.defineProperty(TL,"__esModule",{value:!0});TL.verifyTSATimestamp=WHt;TL.verifyTLogTimestamp=YHt;var UHt=Co(),HHt=VNe(),jHt=KNe(),qHt=zNe(),GHt=ZNe();function WHt(t,e,r){return(0,GHt.verifyRFC3161Timestamp)(t,e,r),{type:"timestamp-authority",logID:t.signerSerialNumber,timestamp:t.signingTime}}function YHt(t,e){let r=!1;if(VHt(t)&&((0,qHt.verifyTLogSET)(t,e),r=!0),KHt(t)&&((0,jHt.verifyMerkleInclusion)(t),(0,HHt.verifyCheckpoint)(t,e),r=!0),!r)throw new UHt.VerificationError({code:"TLOG_MISSING_INCLUSION_ERROR",message:"inclusion could not be verified"});return{type:"transparency-log",logID:t.logId.keyId,timestamp:new Date(Number(t.integratedTime)*1e3)}}function VHt(t){return t.inclusionPromise!==void 0}function KHt(t){return t.inclusionProof!==void 0}});var $Ne=L(lz=>{"use strict";Object.defineProperty(lz,"__esModule",{value:!0});lz.verifyDSSETLogBody=JHt;var RL=Co();function JHt(t,e){switch(t.apiVersion){case"0.0.1":return zHt(t,e);default:throw new RL.VerificationError({code:"TLOG_BODY_ERROR",message:`unsupported dsse version: ${t.apiVersion}`})}}function zHt(t,e){if(t.spec.signatures?.length!==1)throw new RL.VerificationError({code:"TLOG_BODY_ERROR",message:"signature count mismatch"});let r=t.spec.signatures[0].signature;if(!e.compareSignature(Buffer.from(r,"base64")))throw new RL.VerificationError({code:"TLOG_BODY_ERROR",message:"tlog entry signature mismatch"});let s=t.spec.payloadHash?.value||"";if(!e.compareDigest(Buffer.from(s,"hex")))throw new RL.VerificationError({code:"TLOG_BODY_ERROR",message:"DSSE payload hash mismatch"})}});var eOe=L(uz=>{"use strict";Object.defineProperty(uz,"__esModule",{value:!0});uz.verifyHashedRekordTLogBody=ZHt;var cz=Co();function ZHt(t,e){switch(t.apiVersion){case"0.0.1":return XHt(t,e);default:throw new cz.VerificationError({code:"TLOG_BODY_ERROR",message:`unsupported hashedrekord version: ${t.apiVersion}`})}}function XHt(t,e){let r=t.spec.signature.content||"";if(!e.compareSignature(Buffer.from(r,"base64")))throw new cz.VerificationError({code:"TLOG_BODY_ERROR",message:"signature mismatch"});let s=t.spec.data.hash?.value||"";if(!e.compareDigest(Buffer.from(s,"hex")))throw new cz.VerificationError({code:"TLOG_BODY_ERROR",message:"digest mismatch"})}});var tOe=L(fz=>{"use strict";Object.defineProperty(fz,"__esModule",{value:!0});fz.verifyIntotoTLogBody=$Ht;var FL=Co();function $Ht(t,e){switch(t.apiVersion){case"0.0.2":return ejt(t,e);default:throw new FL.VerificationError({code:"TLOG_BODY_ERROR",message:`unsupported intoto version: ${t.apiVersion}`})}}function ejt(t,e){if(t.spec.content.envelope.signatures?.length!==1)throw new FL.VerificationError({code:"TLOG_BODY_ERROR",message:"signature count mismatch"});let r=tjt(t.spec.content.envelope.signatures[0].sig);if(!e.compareSignature(Buffer.from(r,"base64")))throw new FL.VerificationError({code:"TLOG_BODY_ERROR",message:"tlog entry signature mismatch"});let s=t.spec.content.payloadHash?.value||"";if(!e.compareDigest(Buffer.from(s,"hex")))throw new FL.VerificationError({code:"TLOG_BODY_ERROR",message:"DSSE payload hash mismatch"})}function tjt(t){return Buffer.from(t,"base64").toString("utf-8")}});var nOe=L(Az=>{"use strict";Object.defineProperty(Az,"__esModule",{value:!0});Az.verifyTLogBody=sjt;var rOe=Co(),rjt=$Ne(),njt=eOe(),ijt=tOe();function sjt(t,e){let{kind:r,version:s}=t.kindVersion,a=JSON.parse(t.canonicalizedBody.toString("utf8"));if(r!==a.kind||s!==a.apiVersion)throw new rOe.VerificationError({code:"TLOG_BODY_ERROR",message:`kind/version mismatch - expected: ${r}/${s}, received: ${a.kind}/${a.apiVersion}`});switch(a.kind){case"dsse":return(0,rjt.verifyDSSETLogBody)(a,e);case"intoto":return(0,ijt.verifyIntotoTLogBody)(a,e);case"hashedrekord":return(0,njt.verifyHashedRekordTLogBody)(a,e);default:throw new rOe.VerificationError({code:"TLOG_BODY_ERROR",message:`unsupported kind: ${r}`})}}});var lOe=L(NL=>{"use strict";Object.defineProperty(NL,"__esModule",{value:!0});NL.Verifier=void 0;var ojt=ye("util"),B1=Co(),iOe=GNe(),sOe=YNe(),oOe=XNe(),ajt=nOe(),pz=class{constructor(e,r={}){this.trustMaterial=e,this.options={ctlogThreshold:r.ctlogThreshold??1,tlogThreshold:r.tlogThreshold??1,tsaThreshold:r.tsaThreshold??0}}verify(e,r){let s=this.verifyTimestamps(e),a=this.verifySigningKey(e,s);return this.verifyTLogs(e),this.verifySignature(e,a),r&&this.verifyPolicy(r,a.identity||{}),a}verifyTimestamps(e){let r=0,s=0,a=e.timestamps.map(n=>{switch(n.$case){case"timestamp-authority":return s++,(0,oOe.verifyTSATimestamp)(n.timestamp,e.signature.signature,this.trustMaterial.timestampAuthorities);case"transparency-log":return r++,(0,oOe.verifyTLogTimestamp)(n.tlogEntry,this.trustMaterial.tlogs)}});if(aOe(a))throw new B1.VerificationError({code:"TIMESTAMP_ERROR",message:"duplicate timestamp"});if(rn.timestamp)}verifySigningKey({key:e},r){switch(e.$case){case"public-key":return(0,iOe.verifyPublicKey)(e.hint,r,this.trustMaterial);case"certificate":{let s=(0,iOe.verifyCertificate)(e.certificate,r,this.trustMaterial);if(aOe(s.scts))throw new B1.VerificationError({code:"CERTIFICATE_ERROR",message:"duplicate SCT"});if(s.scts.length(0,ajt.verifyTLogBody)(s,e))}verifySignature(e,r){if(!e.signature.verifySignature(r.key))throw new B1.VerificationError({code:"SIGNATURE_ERROR",message:"signature verification failed"})}verifyPolicy(e,r){e.subjectAlternativeName&&(0,sOe.verifySubjectAlternativeName)(e.subjectAlternativeName,r.subjectAlternativeName),e.extensions&&(0,sOe.verifyExtensions)(e.extensions,r.extensions)}};NL.Verifier=pz;function aOe(t){for(let e=0;e{"use strict";Object.defineProperty(ou,"__esModule",{value:!0});ou.Verifier=ou.toTrustMaterial=ou.VerificationError=ou.PolicyError=ou.toSignedEntity=void 0;var ljt=LNe();Object.defineProperty(ou,"toSignedEntity",{enumerable:!0,get:function(){return ljt.toSignedEntity}});var cOe=Co();Object.defineProperty(ou,"PolicyError",{enumerable:!0,get:function(){return cOe.PolicyError}});Object.defineProperty(ou,"VerificationError",{enumerable:!0,get:function(){return cOe.VerificationError}});var cjt=hy();Object.defineProperty(ou,"toTrustMaterial",{enumerable:!0,get:function(){return cjt.toTrustMaterial}});var ujt=lOe();Object.defineProperty(ou,"Verifier",{enumerable:!0,get:function(){return ujt.Verifier}})});var uOe=L(Na=>{"use strict";Object.defineProperty(Na,"__esModule",{value:!0});Na.DEFAULT_TIMEOUT=Na.DEFAULT_RETRY=void 0;Na.createBundleBuilder=pjt;Na.createKeyFinder=hjt;Na.createVerificationPolicy=gjt;var fjt=wl(),v1=SK(),Ajt=OL();Na.DEFAULT_RETRY={retries:2};Na.DEFAULT_TIMEOUT=5e3;function pjt(t,e){let r={signer:djt(e),witnesses:yjt(e)};switch(t){case"messageSignature":return new v1.MessageSignatureBundleBuilder(r);case"dsseEnvelope":return new v1.DSSEBundleBuilder({...r,certificateChain:e.legacyCompatibility})}}function hjt(t){return e=>{let r=t(e);if(!r)throw new Ajt.VerificationError({code:"PUBLIC_KEY_ERROR",message:`key not found: ${e}`});return{publicKey:fjt.crypto.createPublicKey(r),validFor:()=>!0}}}function gjt(t){let e={},r=t.certificateIdentityEmail||t.certificateIdentityURI;return r&&(e.subjectAlternativeName=r),t.certificateIssuer&&(e.extensions={issuer:t.certificateIssuer}),e}function djt(t){return new v1.FulcioSigner({fulcioBaseURL:t.fulcioURL,identityProvider:t.identityProvider||mjt(t),retry:t.retry??Na.DEFAULT_RETRY,timeout:t.timeout??Na.DEFAULT_TIMEOUT})}function mjt(t){let e=t.identityToken;return e?{getToken:()=>Promise.resolve(e)}:new v1.CIContextProvider("sigstore")}function yjt(t){let e=[];return Ejt(t)&&e.push(new v1.RekorWitness({rekorBaseURL:t.rekorURL,entryType:t.legacyCompatibility?"intoto":"dsse",fetchOnConflict:!1,retry:t.retry??Na.DEFAULT_RETRY,timeout:t.timeout??Na.DEFAULT_TIMEOUT})),Ijt(t)&&e.push(new v1.TSAWitness({tsaBaseURL:t.tsaServerURL,retry:t.retry??Na.DEFAULT_RETRY,timeout:t.timeout??Na.DEFAULT_TIMEOUT})),e}function Ejt(t){return t.tlogUpload!==!1}function Ijt(t){return t.tsaServerURL!==void 0}});var pOe=L(au=>{"use strict";var Cjt=au&&au.__createBinding||(Object.create?function(t,e,r,s){s===void 0&&(s=r);var a=Object.getOwnPropertyDescriptor(e,r);(!a||("get"in a?!e.__esModule:a.writable||a.configurable))&&(a={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,s,a)}:function(t,e,r,s){s===void 0&&(s=r),t[s]=e[r]}),wjt=au&&au.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),fOe=au&&au.__importStar||function(){var t=function(e){return t=Object.getOwnPropertyNames||function(r){var s=[];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(s[s.length]=a);return s},t(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null)for(var s=t(e),a=0;aa.verify(t,s))}async function AOe(t={}){let e=await Bjt.getTrustedRoot({mirrorURL:t.tufMirrorURL,rootPath:t.tufRootPath,cachePath:t.tufCachePath,forceCache:t.tufForceCache,retry:t.retry??S1.DEFAULT_RETRY,timeout:t.timeout??S1.DEFAULT_TIMEOUT}),r=t.keySelector?S1.createKeyFinder(t.keySelector):void 0,s=(0,hz.toTrustMaterial)(e,r),a={ctlogThreshold:t.ctLogThreshold,tlogThreshold:t.tlogThreshold},n=new hz.Verifier(s,a),c=S1.createVerificationPolicy(t);return{verify:(f,p)=>{let h=(0,gz.bundleFromJSON)(f),E=(0,hz.toSignedEntity)(h,p);n.verify(E,c)}}}});var gOe=L(Fi=>{"use strict";Object.defineProperty(Fi,"__esModule",{value:!0});Fi.verify=Fi.sign=Fi.createVerifier=Fi.attest=Fi.VerificationError=Fi.PolicyError=Fi.TUFError=Fi.InternalError=Fi.DEFAULT_REKOR_URL=Fi.DEFAULT_FULCIO_URL=Fi.ValidationError=void 0;var bjt=Nb();Object.defineProperty(Fi,"ValidationError",{enumerable:!0,get:function(){return bjt.ValidationError}});var dz=SK();Object.defineProperty(Fi,"DEFAULT_FULCIO_URL",{enumerable:!0,get:function(){return dz.DEFAULT_FULCIO_URL}});Object.defineProperty(Fi,"DEFAULT_REKOR_URL",{enumerable:!0,get:function(){return dz.DEFAULT_REKOR_URL}});Object.defineProperty(Fi,"InternalError",{enumerable:!0,get:function(){return dz.InternalError}});var Pjt=wL();Object.defineProperty(Fi,"TUFError",{enumerable:!0,get:function(){return Pjt.TUFError}});var hOe=OL();Object.defineProperty(Fi,"PolicyError",{enumerable:!0,get:function(){return hOe.PolicyError}});Object.defineProperty(Fi,"VerificationError",{enumerable:!0,get:function(){return hOe.VerificationError}});var LL=pOe();Object.defineProperty(Fi,"attest",{enumerable:!0,get:function(){return LL.attest}});Object.defineProperty(Fi,"createVerifier",{enumerable:!0,get:function(){return LL.createVerifier}});Object.defineProperty(Fi,"sign",{enumerable:!0,get:function(){return LL.sign}});Object.defineProperty(Fi,"verify",{enumerable:!0,get:function(){return LL.verify}})});var GOe=L((xSr,qOe)=>{var M6t=t3();function _6t(t){return M6t(t)?void 0:t}qOe.exports=_6t});var YOe=L((kSr,WOe)=>{var U6t=RT(),H6t=x5(),j6t=R5(),q6t=wm(),G6t=Jd(),W6t=GOe(),Y6t=BG(),V6t=P5(),K6t=1,J6t=2,z6t=4,Z6t=Y6t(function(t,e){var r={};if(t==null)return r;var s=!1;e=U6t(e,function(n){return n=q6t(n,t),s||(s=n.length>1),n}),G6t(t,V6t(t),r),s&&(r=H6t(r,K6t|J6t|z6t,W6t));for(var a=e.length;a--;)j6t(r,e[a]);return r});WOe.exports=Z6t});bt();Ve();bt();var ZOe=ye("child_process"),XOe=et(Nd());Wt();var tC=new Map([]);var iS={};Vt(iS,{BaseCommand:()=>ut,WorkspaceRequiredError:()=>ar,getCli:()=>cwe,getDynamicLibs:()=>lwe,getPluginConfiguration:()=>nC,openWorkspace:()=>rC,pluginCommands:()=>tC,runExit:()=>zR});Wt();var ut=class extends ot{constructor(){super(...arguments);this.cwd=ge.String("--cwd",{hidden:!0})}validateAndExecute(){if(typeof this.cwd<"u")throw new nt("The --cwd option is ambiguous when used anywhere else than the very first parameter provided in the command line, before even the command path");return super.validateAndExecute()}};Ve();bt();Wt();var ar=class extends nt{constructor(e,r){let s=K.relative(e,r),a=K.join(e,Ht.fileName);super(`This command can only be run from within a workspace of your project (${s} isn't a workspace of ${a}).`)}};Ve();bt();rA();Bc();bv();Wt();var zwt=et(fi());Ul();var lwe=()=>new Map([["@yarnpkg/cli",iS],["@yarnpkg/core",nS],["@yarnpkg/fslib",q2],["@yarnpkg/libzip",Sv],["@yarnpkg/parsers",Z2],["@yarnpkg/shell",Qv],["clipanion",cB],["semver",zwt],["typanion",Ia]]);Ve();async function rC(t,e){let{project:r,workspace:s}=await Tt.find(t,e);if(!s)throw new ar(r.cwd,e);return s}Ve();bt();rA();Bc();bv();Wt();var oqt=et(fi());Ul();var $5={};Vt($5,{AddCommand:()=>aC,BinCommand:()=>lC,CacheCleanCommand:()=>cC,ClipanionCommand:()=>gC,ConfigCommand:()=>pC,ConfigGetCommand:()=>uC,ConfigSetCommand:()=>fC,ConfigUnsetCommand:()=>AC,DedupeCommand:()=>hC,EntryCommand:()=>mC,ExecCommand:()=>EC,ExplainCommand:()=>wC,ExplainPeerRequirementsCommand:()=>IC,HelpCommand:()=>dC,InfoCommand:()=>BC,LinkCommand:()=>SC,NodeCommand:()=>DC,PluginCheckCommand:()=>bC,PluginImportCommand:()=>kC,PluginImportSourcesCommand:()=>QC,PluginListCommand:()=>PC,PluginRemoveCommand:()=>TC,PluginRuntimeCommand:()=>RC,RebuildCommand:()=>FC,RemoveCommand:()=>NC,RunCommand:()=>LC,RunIndexCommand:()=>OC,SetResolutionCommand:()=>MC,SetVersionCommand:()=>CC,SetVersionSourcesCommand:()=>xC,UnlinkCommand:()=>_C,UpCommand:()=>UC,VersionCommand:()=>yC,WhyCommand:()=>HC,WorkspaceCommand:()=>YC,WorkspacesListCommand:()=>WC,YarnCommand:()=>vC,dedupeUtils:()=>oF,default:()=>WSt,suggestUtils:()=>Xu});var UBe=et(Nd());Ve();Ve();Ve();Wt();var z1e=et(lS());Ul();var Xu={};Vt(Xu,{Modifier:()=>B5,Strategy:()=>nF,Target:()=>cS,WorkspaceModifier:()=>W1e,applyModifier:()=>d2t,extractDescriptorFromPath:()=>v5,extractRangeModifier:()=>Y1e,fetchDescriptorFrom:()=>S5,findProjectDescriptors:()=>J1e,getModifier:()=>uS,getSuggestedDescriptors:()=>fS,makeWorkspaceDescriptor:()=>K1e,toWorkspaceModifier:()=>V1e});Ve();Ve();bt();var w5=et(fi()),h2t="workspace:",cS=(s=>(s.REGULAR="dependencies",s.DEVELOPMENT="devDependencies",s.PEER="peerDependencies",s))(cS||{}),B5=(s=>(s.CARET="^",s.TILDE="~",s.EXACT="",s))(B5||{}),W1e=(s=>(s.CARET="^",s.TILDE="~",s.EXACT="*",s))(W1e||{}),nF=(n=>(n.KEEP="keep",n.REUSE="reuse",n.PROJECT="project",n.LATEST="latest",n.CACHE="cache",n))(nF||{});function uS(t,e){return t.exact?"":t.caret?"^":t.tilde?"~":e.configuration.get("defaultSemverRangePrefix")}var g2t=/^([\^~]?)[0-9]+(?:\.[0-9]+){0,2}(?:-\S+)?$/;function Y1e(t,{project:e}){let r=t.match(g2t);return r?r[1]:e.configuration.get("defaultSemverRangePrefix")}function d2t(t,e){let{protocol:r,source:s,params:a,selector:n}=q.parseRange(t.range);return w5.default.valid(n)&&(n=`${e}${t.range}`),q.makeDescriptor(t,q.makeRange({protocol:r,source:s,params:a,selector:n}))}function V1e(t){switch(t){case"^":return"^";case"~":return"~";case"":return"*";default:throw new Error(`Assertion failed: Unknown modifier: "${t}"`)}}function K1e(t,e){return q.makeDescriptor(t.anchoredDescriptor,`${h2t}${V1e(e)}`)}async function J1e(t,{project:e,target:r}){let s=new Map,a=n=>{let c=s.get(n.descriptorHash);return c||s.set(n.descriptorHash,c={descriptor:n,locators:[]}),c};for(let n of e.workspaces)if(r==="peerDependencies"){let c=n.manifest.peerDependencies.get(t.identHash);c!==void 0&&a(c).locators.push(n.anchoredLocator)}else{let c=n.manifest.dependencies.get(t.identHash),f=n.manifest.devDependencies.get(t.identHash);r==="devDependencies"?f!==void 0?a(f).locators.push(n.anchoredLocator):c!==void 0&&a(c).locators.push(n.anchoredLocator):c!==void 0?a(c).locators.push(n.anchoredLocator):f!==void 0&&a(f).locators.push(n.anchoredLocator)}return s}async function v5(t,{cwd:e,workspace:r}){return await y2t(async s=>{K.isAbsolute(t)||(t=K.relative(r.cwd,K.resolve(e,t)),t.match(/^\.{0,2}\//)||(t=`./${t}`));let{project:a}=r,n=await S5(q.makeIdent(null,"archive"),t,{project:r.project,cache:s,workspace:r});if(!n)throw new Error("Assertion failed: The descriptor should have been found");let c=new Wi,f=a.configuration.makeResolver(),p=a.configuration.makeFetcher(),h={checksums:a.storedChecksums,project:a,cache:s,fetcher:p,report:c,resolver:f},E=f.bindDescriptor(n,r.anchoredLocator,h),C=q.convertDescriptorToLocator(E),S=await p.fetch(C,h),P=await Ht.find(S.prefixPath,{baseFs:S.packageFs});if(!P.name)throw new Error("Target path doesn't have a name");return q.makeDescriptor(P.name,t)})}function m2t(t){if(t.range==="unknown")return{type:"resolve",range:"latest"};if(Or.validRange(t.range))return{type:"fixed",range:t.range};if(Hp.test(t.range))return{type:"resolve",range:t.range};let e=t.range.match(/^(?:jsr:|npm:)(.*)/);if(!e)return{type:"fixed",range:t.range};let[,r]=e,s=`${q.stringifyIdent(t)}@`;return r.startsWith(s)&&(r=r.slice(s.length)),Or.validRange(r)?{type:"fixed",range:t.range}:Hp.test(r)?{type:"resolve",range:t.range}:{type:"fixed",range:t.range}}async function fS(t,{project:e,workspace:r,cache:s,target:a,fixed:n,modifier:c,strategies:f,maxResults:p=1/0}){if(!(p>=0))throw new Error(`Invalid maxResults (${p})`);let h=!n||t.range==="unknown"?m2t(t):{type:"fixed",range:t.range};if(h.type==="fixed")return{suggestions:[{descriptor:t,name:`Use ${q.prettyDescriptor(e.configuration,t)}`,reason:"(unambiguous explicit request)"}],rejections:[]};let E=typeof r<"u"&&r!==null&&r.manifest[a].get(t.identHash)||null,C=[],S=[],P=async I=>{try{await I()}catch(R){S.push(R)}};for(let I of f){if(C.length>=p)break;switch(I){case"keep":await P(async()=>{E&&C.push({descriptor:E,name:`Keep ${q.prettyDescriptor(e.configuration,E)}`,reason:"(no changes)"})});break;case"reuse":await P(async()=>{for(let{descriptor:R,locators:N}of(await J1e(t,{project:e,target:a})).values()){if(N.length===1&&N[0].locatorHash===r.anchoredLocator.locatorHash&&f.includes("keep"))continue;let U=`(originally used by ${q.prettyLocator(e.configuration,N[0])}`;U+=N.length>1?` and ${N.length-1} other${N.length>2?"s":""})`:")",C.push({descriptor:R,name:`Reuse ${q.prettyDescriptor(e.configuration,R)}`,reason:U})}});break;case"cache":await P(async()=>{for(let R of e.storedDescriptors.values())R.identHash===t.identHash&&C.push({descriptor:R,name:`Reuse ${q.prettyDescriptor(e.configuration,R)}`,reason:"(already used somewhere in the lockfile)"})});break;case"project":await P(async()=>{if(r.manifest.name!==null&&t.identHash===r.manifest.name.identHash)return;let R=e.tryWorkspaceByIdent(t);if(R===null)return;let N=K1e(R,c);C.push({descriptor:N,name:`Attach ${q.prettyDescriptor(e.configuration,N)}`,reason:`(local workspace at ${he.pretty(e.configuration,R.relativeCwd,he.Type.PATH)})`})});break;case"latest":{let R=e.configuration.get("enableNetwork"),N=e.configuration.get("enableOfflineMode");await P(async()=>{if(a==="peerDependencies")C.push({descriptor:q.makeDescriptor(t,"*"),name:"Use *",reason:"(catch-all peer dependency pattern)"});else if(!R&&!N)C.push({descriptor:null,name:"Resolve from latest",reason:he.pretty(e.configuration,"(unavailable because enableNetwork is toggled off)","grey")});else{let U=await S5(t,h.range,{project:e,cache:s,workspace:r,modifier:c});U&&C.push({descriptor:U,name:`Use ${q.prettyDescriptor(e.configuration,U)}`,reason:`(resolved from ${N?"the cache":"latest"})`})}})}break}}return{suggestions:C.slice(0,p),rejections:S.slice(0,p)}}async function S5(t,e,{project:r,cache:s,workspace:a,preserveModifier:n=!0,modifier:c}){let f=r.configuration.normalizeDependency(q.makeDescriptor(t,e)),p=new Wi,h=r.configuration.makeFetcher(),E=r.configuration.makeResolver(),C={project:r,fetcher:h,cache:s,checksums:r.storedChecksums,report:p,cacheOptions:{skipIntegrityCheck:!0}},S={...C,resolver:E,fetchOptions:C},P=E.bindDescriptor(f,a.anchoredLocator,S),I=await E.getCandidates(P,{},S);if(I.length===0)return null;let R=I[0],{protocol:N,source:U,params:W,selector:te}=q.parseRange(q.convertToManifestRange(R.reference));if(N===r.configuration.get("defaultProtocol")&&(N=null),w5.default.valid(te)){let ie=te;if(typeof c<"u")te=c+te;else if(n!==!1){let me=typeof n=="string"?n:f.range;te=Y1e(me,{project:r})+te}let Ae=q.makeDescriptor(R,q.makeRange({protocol:N,source:U,params:W,selector:te}));(await E.getCandidates(r.configuration.normalizeDependency(Ae),{},S)).length!==1&&(te=ie)}return q.makeDescriptor(R,q.makeRange({protocol:N,source:U,params:W,selector:te}))}async function y2t(t){return await le.mktempPromise(async e=>{let r=ze.create(e);return r.useWithSource(e,{enableMirror:!1,compressionLevel:0},e,{overwrite:!0}),await t(new Jr(e,{configuration:r,check:!1,immutable:!1}))})}var aC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.fixed=ge.Boolean("-F,--fixed",!1,{description:"Store dependency tags as-is instead of resolving them"});this.exact=ge.Boolean("-E,--exact",!1,{description:"Don't use any semver modifier on the resolved range"});this.tilde=ge.Boolean("-T,--tilde",!1,{description:"Use the `~` semver modifier on the resolved range"});this.caret=ge.Boolean("-C,--caret",!1,{description:"Use the `^` semver modifier on the resolved range"});this.dev=ge.Boolean("-D,--dev",!1,{description:"Add a package as a dev dependency"});this.peer=ge.Boolean("-P,--peer",!1,{description:"Add a package as a peer dependency"});this.optional=ge.Boolean("-O,--optional",!1,{description:"Add / upgrade a package to an optional regular / peer dependency"});this.preferDev=ge.Boolean("--prefer-dev",!1,{description:"Add / upgrade a package to a dev dependency"});this.interactive=ge.Boolean("-i,--interactive",{description:"Reuse the specified package from other workspaces in the project"});this.cached=ge.Boolean("--cached",!1,{description:"Reuse the highest version already used somewhere within the project"});this.mode=ge.String("--mode",{description:"Change what artifacts installs generate",validator:Ao(ec)});this.silent=ge.Boolean("--silent",{hidden:!0});this.packages=ge.Rest()}static{this.paths=[["add"]]}static{this.usage=ot.Usage({description:"add dependencies to the project",details:"\n This command adds a package to the package.json for the nearest workspace.\n\n - If it didn't exist before, the package will by default be added to the regular `dependencies` field, but this behavior can be overriden thanks to the `-D,--dev` flag (which will cause the dependency to be added to the `devDependencies` field instead) and the `-P,--peer` flag (which will do the same but for `peerDependencies`).\n\n - If the package was already listed in your dependencies, it will by default be upgraded whether it's part of your `dependencies` or `devDependencies` (it won't ever update `peerDependencies`, though).\n\n - If set, the `--prefer-dev` flag will operate as a more flexible `-D,--dev` in that it will add the package to your `devDependencies` if it isn't already listed in either `dependencies` or `devDependencies`, but it will also happily upgrade your `dependencies` if that's what you already use (whereas `-D,--dev` would throw an exception).\n\n - If set, the `-O,--optional` flag will add the package to the `optionalDependencies` field and, in combination with the `-P,--peer` flag, it will add the package as an optional peer dependency. If the package was already listed in your `dependencies`, it will be upgraded to `optionalDependencies`. If the package was already listed in your `peerDependencies`, in combination with the `-P,--peer` flag, it will be upgraded to an optional peer dependency: `\"peerDependenciesMeta\": { \"\": { \"optional\": true } }`\n\n - If the added package doesn't specify a range at all its `latest` tag will be resolved and the returned version will be used to generate a new semver range (using the `^` modifier by default unless otherwise configured via the `defaultSemverRangePrefix` configuration, or the `~` modifier if `-T,--tilde` is specified, or no modifier at all if `-E,--exact` is specified). Two exceptions to this rule: the first one is that if the package is a workspace then its local version will be used, and the second one is that if you use `-P,--peer` the default range will be `*` and won't be resolved at all.\n\n - If the added package specifies a range (such as `^1.0.0`, `latest`, or `rc`), Yarn will add this range as-is in the resulting package.json entry (in particular, tags such as `rc` will be encoded as-is rather than being converted into a semver range).\n\n If the `--cached` option is used, Yarn will preferably reuse the highest version already used somewhere within the project, even if through a transitive dependency.\n\n If the `-i,--interactive` option is used (or if the `preferInteractive` settings is toggled on) the command will first try to check whether other workspaces in the project use the specified package and, if so, will offer to reuse them.\n\n If the `--mode=` option is set, Yarn will change which artifacts are generated. The modes currently supported are:\n\n - `skip-build` will not run the build scripts at all. Note that this is different from setting `enableScripts` to false because the latter will disable build scripts, and thus affect the content of the artifacts generated on disk, whereas the former will just disable the build step - but not the scripts themselves, which just won't run.\n\n - `update-lockfile` will skip the link step altogether, and only fetch packages that are missing from the lockfile (or that have no associated checksums). This mode is typically used by tools like Renovate or Dependabot to keep a lockfile up-to-date without incurring the full install cost.\n\n For a compilation of all the supported protocols, please consult the dedicated page from our website: https://yarnpkg.com/protocols.\n ",examples:[["Add a regular package to the current workspace","$0 add lodash"],["Add a specific version for a package to the current workspace","$0 add lodash@1.2.3"],["Add a package from a GitHub repository (the master branch) to the current workspace using a URL","$0 add lodash@https://github.com/lodash/lodash"],["Add a package from a GitHub repository (the master branch) to the current workspace using the GitHub protocol","$0 add lodash@github:lodash/lodash"],["Add a package from a GitHub repository (the master branch) to the current workspace using the GitHub protocol (shorthand)","$0 add lodash@lodash/lodash"],["Add a package from a specific branch of a GitHub repository to the current workspace using the GitHub protocol (shorthand)","$0 add lodash-es@lodash/lodash#es"],["Add a local package (gzipped tarball format) to the current workspace","$0 add local-package-name@file:../path/to/local-package-name-v0.1.2.tgz"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState({restoreResolutions:!1});let c=this.fixed,f=r.isInteractive({interactive:this.interactive,stdout:this.context.stdout}),p=f||r.get("preferReuse"),h=uS(this,s),E=[p?"reuse":void 0,"project",this.cached?"cache":void 0,"latest"].filter(W=>typeof W<"u"),C=f?1/0:1,S=W=>{let te=q.tryParseDescriptor(W.slice(4));return te?te.range==="unknown"?q.makeDescriptor(te,`jsr:${q.stringifyIdent(te)}@latest`):q.makeDescriptor(te,`jsr:${te.range}`):null},P=await Promise.all(this.packages.map(async W=>{let te=W.match(/^\.{0,2}\//)?await v5(W,{cwd:this.context.cwd,workspace:a}):W.startsWith("jsr:")?S(W):q.tryParseDescriptor(W),ie=W.match(/^(https?:|git@github)/);if(ie)throw new nt(`It seems you are trying to add a package using a ${he.pretty(r,`${ie[0]}...`,he.Type.RANGE)} url; we now require package names to be explicitly specified. +Try running the command again with the package name prefixed: ${he.pretty(r,"yarn add",he.Type.CODE)} ${he.pretty(r,q.makeDescriptor(q.makeIdent(null,"my-package"),`${ie[0]}...`),he.Type.DESCRIPTOR)}`);if(!te)throw new nt(`The ${he.pretty(r,W,he.Type.CODE)} string didn't match the required format (package-name@range). Did you perhaps forget to explicitly reference the package name?`);let Ae=E2t(a,te,{dev:this.dev,peer:this.peer,preferDev:this.preferDev,optional:this.optional});return await Promise.all(Ae.map(async me=>{let pe=await fS(te,{project:s,workspace:a,cache:n,fixed:c,target:me,modifier:h,strategies:E,maxResults:C});return{request:te,suggestedDescriptors:pe,target:me}}))})).then(W=>W.flat()),I=await uA.start({configuration:r,stdout:this.context.stdout,suggestInstall:!1},async W=>{for(let{request:te,suggestedDescriptors:{suggestions:ie,rejections:Ae}}of P)if(ie.filter(me=>me.descriptor!==null).length===0){let[me]=Ae;if(typeof me>"u")throw new Error("Assertion failed: Expected an error to have been set");s.configuration.get("enableNetwork")?W.reportError(27,`${q.prettyDescriptor(r,te)} can't be resolved to a satisfying range`):W.reportError(27,`${q.prettyDescriptor(r,te)} can't be resolved to a satisfying range (note: network resolution has been disabled)`),W.reportSeparator(),W.reportExceptionOnce(me)}});if(I.hasErrors())return I.exitCode();let R=!1,N=[],U=[];for(let{suggestedDescriptors:{suggestions:W},target:te}of P){let ie,Ae=W.filter(Be=>Be.descriptor!==null),ce=Ae[0].descriptor,me=Ae.every(Be=>q.areDescriptorsEqual(Be.descriptor,ce));Ae.length===1||me?ie=ce:(R=!0,{answer:ie}=await(0,z1e.prompt)({type:"select",name:"answer",message:"Which range do you want to use?",choices:W.map(({descriptor:Be,name:Ce,reason:g})=>Be?{name:Ce,hint:g,descriptor:Be}:{name:Ce,hint:g,disabled:!0}),onCancel:()=>process.exit(130),result(Be){return this.find(Be,"descriptor")},stdin:this.context.stdin,stdout:this.context.stdout}));let pe=a.manifest[te].get(ie.identHash);(typeof pe>"u"||pe.descriptorHash!==ie.descriptorHash)&&(a.manifest[te].set(ie.identHash,ie),this.optional&&(te==="dependencies"?a.manifest.ensureDependencyMeta({...ie,range:"unknown"}).optional=!0:te==="peerDependencies"&&(a.manifest.ensurePeerDependencyMeta({...ie,range:"unknown"}).optional=!0)),typeof pe>"u"?N.push([a,te,ie,E]):U.push([a,te,pe,ie]))}return await r.triggerMultipleHooks(W=>W.afterWorkspaceDependencyAddition,N),await r.triggerMultipleHooks(W=>W.afterWorkspaceDependencyReplacement,U),R&&this.context.stdout.write(` +`),await s.installWithNewReport({json:this.json,stdout:this.context.stdout,quiet:this.context.quiet},{cache:n,mode:this.mode})}};function E2t(t,e,{dev:r,peer:s,preferDev:a,optional:n}){let c=t.manifest.dependencies.has(e.identHash),f=t.manifest.devDependencies.has(e.identHash),p=t.manifest.peerDependencies.has(e.identHash);if((r||s)&&c)throw new nt(`Package "${q.prettyIdent(t.project.configuration,e)}" is already listed as a regular dependency - remove the -D,-P flags or remove it from your dependencies first`);if(!r&&!s&&p)throw new nt(`Package "${q.prettyIdent(t.project.configuration,e)}" is already listed as a peer dependency - use either of -D or -P, or remove it from your peer dependencies first`);if(n&&f)throw new nt(`Package "${q.prettyIdent(t.project.configuration,e)}" is already listed as a dev dependency - remove the -O flag or remove it from your dev dependencies first`);if(n&&!s&&p)throw new nt(`Package "${q.prettyIdent(t.project.configuration,e)}" is already listed as a peer dependency - remove the -O flag or add the -P flag or remove it from your peer dependencies first`);if((r||a)&&n)throw new nt(`Package "${q.prettyIdent(t.project.configuration,e)}" cannot simultaneously be a dev dependency and an optional dependency`);let h=[];return s&&h.push("peerDependencies"),(r||a)&&h.push("devDependencies"),n&&h.push("dependencies"),h.length>0?h:f?["devDependencies"]:p?["peerDependencies"]:["dependencies"]}Ve();Ve();Wt();var lC=class extends ut{constructor(){super(...arguments);this.verbose=ge.Boolean("-v,--verbose",!1,{description:"Print both the binary name and the locator of the package that provides the binary"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.name=ge.String({required:!1})}static{this.paths=[["bin"]]}static{this.usage=ot.Usage({description:"get the path to a binary script",details:` + When used without arguments, this command will print the list of all the binaries available in the current workspace. Adding the \`-v,--verbose\` flag will cause the output to contain both the binary name and the locator of the package that provides the binary. + + When an argument is specified, this command will just print the path to the binary on the standard output and exit. Note that the reported path may be stored within a zip archive. + `,examples:[["List all the available binaries","$0 bin"],["Print the path to a specific binary","$0 bin eslint"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,locator:a}=await Tt.find(r,this.context.cwd);if(await s.restoreInstallState(),this.name){let f=(await In.getPackageAccessibleBinaries(a,{project:s})).get(this.name);if(!f)throw new nt(`Couldn't find a binary named "${this.name}" for package "${q.prettyLocator(r,a)}"`);let[,p]=f;return this.context.stdout.write(`${p} +`),0}return(await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async c=>{let f=await In.getPackageAccessibleBinaries(a,{project:s}),h=Array.from(f.keys()).reduce((E,C)=>Math.max(E,C.length),0);for(let[E,[C,S]]of f)c.reportJson({name:E,source:q.stringifyIdent(C),path:S});if(this.verbose)for(let[E,[C]]of f)c.reportInfo(null,`${E.padEnd(h," ")} ${q.prettyLocator(r,C)}`);else for(let E of f.keys())c.reportInfo(null,E)})).exitCode()}};Ve();bt();Wt();var cC=class extends ut{constructor(){super(...arguments);this.mirror=ge.Boolean("--mirror",!1,{description:"Remove the global cache files instead of the local cache files"});this.all=ge.Boolean("--all",!1,{description:"Remove both the global cache files and the local cache files of the current project"})}static{this.paths=[["cache","clean"],["cache","clear"]]}static{this.usage=ot.Usage({description:"remove the shared cache files",details:` + This command will remove all the files from the cache. + `,examples:[["Remove all the local archives","$0 cache clean"],["Remove all the archives stored in the ~/.yarn directory","$0 cache clean --mirror"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);if(!r.get("enableCacheClean"))throw new nt("Cache cleaning is currently disabled. To enable it, set `enableCacheClean: true` in your configuration file. Note: Cache cleaning is typically not required and should be avoided when using Zero-Installs.");let s=await Jr.find(r);return(await Ot.start({configuration:r,stdout:this.context.stdout},async()=>{let n=(this.all||this.mirror)&&s.mirrorCwd!==null,c=!this.mirror;n&&(await le.removePromise(s.mirrorCwd),await r.triggerHook(f=>f.cleanGlobalArtifacts,r)),c&&await le.removePromise(s.cwd)})).exitCode()}};Ve();Wt();var X1e=et(AS()),D5=ye("util"),uC=class extends ut{constructor(){super(...arguments);this.why=ge.Boolean("--why",!1,{description:"Print the explanation for why a setting has its value"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.unsafe=ge.Boolean("--no-redacted",!1,{description:"Don't redact secrets (such as tokens) from the output"});this.name=ge.String()}static{this.paths=[["config","get"]]}static{this.usage=ot.Usage({description:"read a configuration settings",details:` + This command will print a configuration setting. + + Secrets (such as tokens) will be redacted from the output by default. If this behavior isn't desired, set the \`--no-redacted\` to get the untransformed value. + `,examples:[["Print a simple configuration setting","yarn config get yarnPath"],["Print a complex configuration setting","yarn config get packageExtensions"],["Print a nested field from the configuration",`yarn config get 'npmScopes["my-company"].npmRegistryServer'`],["Print a token from the configuration","yarn config get npmAuthToken --no-redacted"],["Print a configuration setting as JSON","yarn config get packageExtensions --json"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=this.name.replace(/[.[].*$/,""),a=this.name.replace(/^[^.[]*/,"");if(typeof r.settings.get(s)>"u")throw new nt(`Couldn't find a configuration settings named "${s}"`);let c=r.getSpecial(s,{hideSecrets:!this.unsafe,getNativePaths:!0}),f=je.convertMapsToIndexableObjects(c),p=a?(0,X1e.default)(f,a):f,h=await Ot.start({configuration:r,includeFooter:!1,json:this.json,stdout:this.context.stdout},async E=>{E.reportJson(p)});if(!this.json){if(typeof p=="string")return this.context.stdout.write(`${p} +`),h.exitCode();D5.inspect.styles.name="cyan",this.context.stdout.write(`${(0,D5.inspect)(p,{depth:1/0,colors:r.get("enableColors"),compact:!1})} +`)}return h.exitCode()}};Ve();Wt();var j2e=et(k5()),q2e=et(AS()),G2e=et(Q5()),T5=ye("util"),fC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Set complex configuration settings to JSON values"});this.home=ge.Boolean("-H,--home",!1,{description:"Update the home configuration instead of the project configuration"});this.name=ge.String();this.value=ge.String()}static{this.paths=[["config","set"]]}static{this.usage=ot.Usage({description:"change a configuration settings",details:` + This command will set a configuration setting. + + When used without the \`--json\` flag, it can only set a simple configuration setting (a string, a number, or a boolean). + + When used with the \`--json\` flag, it can set both simple and complex configuration settings, including Arrays and Objects. + `,examples:[["Set a simple configuration setting (a string, a number, or a boolean)","yarn config set initScope myScope"],["Set a simple configuration setting (a string, a number, or a boolean) using the `--json` flag",'yarn config set initScope --json \\"myScope\\"'],["Set a complex configuration setting (an Array) using the `--json` flag",`yarn config set unsafeHttpWhitelist --json '["*.example.com", "example.com"]'`],["Set a complex configuration setting (an Object) using the `--json` flag",`yarn config set packageExtensions --json '{ "@babel/parser@*": { "dependencies": { "@babel/types": "*" } } }'`],["Set a nested configuration setting",'yarn config set npmScopes.company.npmRegistryServer "https://npm.example.com"'],["Set a nested configuration setting using indexed access for non-simple keys",`yarn config set 'npmRegistries["//npm.example.com"].npmAuthToken' "ffffffff-ffff-ffff-ffff-ffffffffffff"`]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=()=>{if(!r.projectCwd)throw new nt("This command must be run from within a project folder");return r.projectCwd},a=this.name.replace(/[.[].*$/,""),n=this.name.replace(/^[^.[]*\.?/,"");if(typeof r.settings.get(a)>"u")throw new nt(`Couldn't find a configuration settings named "${a}"`);if(a==="enableStrictSettings")throw new nt("This setting only affects the file it's in, and thus cannot be set from the CLI");let f=this.json?JSON.parse(this.value):this.value;await(this.home?I=>ze.updateHomeConfiguration(I):I=>ze.updateConfiguration(s(),I))(I=>{if(n){let R=(0,j2e.default)(I);return(0,G2e.default)(R,this.name,f),R}else return{...I,[a]:f}});let E=(await ze.find(this.context.cwd,this.context.plugins)).getSpecial(a,{hideSecrets:!0,getNativePaths:!0}),C=je.convertMapsToIndexableObjects(E),S=n?(0,q2e.default)(C,n):C;return(await Ot.start({configuration:r,includeFooter:!1,stdout:this.context.stdout},async I=>{T5.inspect.styles.name="cyan",I.reportInfo(0,`Successfully set ${this.name} to ${(0,T5.inspect)(S,{depth:1/0,colors:r.get("enableColors"),compact:!1})}`)})).exitCode()}};Ve();Wt();var tBe=et(k5()),rBe=et(K2e()),nBe=et(F5()),AC=class extends ut{constructor(){super(...arguments);this.home=ge.Boolean("-H,--home",!1,{description:"Update the home configuration instead of the project configuration"});this.name=ge.String()}static{this.paths=[["config","unset"]]}static{this.usage=ot.Usage({description:"unset a configuration setting",details:` + This command will unset a configuration setting. + `,examples:[["Unset a simple configuration setting","yarn config unset initScope"],["Unset a complex configuration setting","yarn config unset packageExtensions"],["Unset a nested configuration setting","yarn config unset npmScopes.company.npmRegistryServer"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=()=>{if(!r.projectCwd)throw new nt("This command must be run from within a project folder");return r.projectCwd},a=this.name.replace(/[.[].*$/,""),n=this.name.replace(/^[^.[]*\.?/,"");if(typeof r.settings.get(a)>"u")throw new nt(`Couldn't find a configuration settings named "${a}"`);let f=this.home?h=>ze.updateHomeConfiguration(h):h=>ze.updateConfiguration(s(),h);return(await Ot.start({configuration:r,includeFooter:!1,stdout:this.context.stdout},async h=>{let E=!1;await f(C=>{if(!(0,rBe.default)(C,this.name))return h.reportWarning(0,`Configuration doesn't contain setting ${this.name}; there is nothing to unset`),E=!0,C;let S=n?(0,tBe.default)(C):{...C};return(0,nBe.default)(S,this.name),S}),E||h.reportInfo(0,`Successfully unset ${this.name}`)})).exitCode()}};Ve();bt();Wt();var sF=ye("util"),pC=class extends ut{constructor(){super(...arguments);this.noDefaults=ge.Boolean("--no-defaults",!1,{description:"Omit the default values from the display"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.verbose=ge.Boolean("-v,--verbose",{hidden:!0});this.why=ge.Boolean("--why",{hidden:!0});this.names=ge.Rest()}static{this.paths=[["config"]]}static{this.usage=ot.Usage({description:"display the current configuration",details:` + This command prints the current active configuration settings. + `,examples:[["Print the active configuration settings","$0 config"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins,{strict:!1}),s=await DI({configuration:r,stdout:this.context.stdout,forceError:this.json},[{option:this.verbose,message:"The --verbose option is deprecated, the settings' descriptions are now always displayed"},{option:this.why,message:"The --why option is deprecated, the settings' sources are now always displayed"}]);if(s!==null)return s;let a=this.names.length>0?[...new Set(this.names)].sort():[...r.settings.keys()].sort(),n,c=await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout,includeFooter:!1},async f=>{if(r.invalid.size>0&&!this.json){for(let[p,h]of r.invalid)f.reportError(34,`Invalid configuration key "${p}" in ${h}`);f.reportSeparator()}if(this.json)for(let p of a){if(this.noDefaults&&!r.sources.has(p))continue;let h=r.settings.get(p);typeof h>"u"&&f.reportError(34,`No configuration key named "${p}"`);let E=r.getSpecial(p,{hideSecrets:!0,getNativePaths:!0}),C=r.sources.get(p)??"",S=C&&C[0]!=="<"?ue.fromPortablePath(C):C;f.reportJson({key:p,effective:E,source:S,...h})}else{let p={breakLength:1/0,colors:r.get("enableColors"),maxArrayLength:2},h={},E={children:h};for(let C of a){if(this.noDefaults&&!r.sources.has(C))continue;let S=r.settings.get(C),P=r.sources.get(C)??"",I=r.getSpecial(C,{hideSecrets:!0,getNativePaths:!0}),R={Description:{label:"Description",value:he.tuple(he.Type.MARKDOWN,{text:S.description,format:this.cli.format(),paragraphs:!1})},Source:{label:"Source",value:he.tuple(P[0]==="<"?he.Type.CODE:he.Type.PATH,P)}};h[C]={value:he.tuple(he.Type.CODE,C),children:R};let N=(U,W)=>{for(let[te,ie]of W)if(ie instanceof Map){let Ae={};U[te]={children:Ae},N(Ae,ie)}else U[te]={label:te,value:he.tuple(he.Type.NO_HINT,(0,sF.inspect)(ie,p))}};I instanceof Map?N(R,I):R.Value={label:"Value",value:he.tuple(he.Type.NO_HINT,(0,sF.inspect)(I,p))}}a.length!==1&&(n=void 0),ks.emitTree(E,{configuration:r,json:this.json,stdout:this.context.stdout,separators:2})}});if(!this.json&&typeof n<"u"){let f=a[0],p=(0,sF.inspect)(r.getSpecial(f,{hideSecrets:!0,getNativePaths:!0}),{colors:r.get("enableColors")});this.context.stdout.write(` +`),this.context.stdout.write(`${p} +`)}return c.exitCode()}};Ve();Wt();Ul();var oF={};Vt(oF,{Strategy:()=>pS,acceptedStrategies:()=>tSt,dedupe:()=>N5});Ve();Ve();var iBe=et(Sa()),pS=(e=>(e.HIGHEST="highest",e))(pS||{}),tSt=new Set(Object.values(pS)),rSt={highest:async(t,e,{resolver:r,fetcher:s,resolveOptions:a,fetchOptions:n})=>{let c=new Map;for(let[p,h]of t.storedResolutions){let E=t.storedDescriptors.get(p);if(typeof E>"u")throw new Error(`Assertion failed: The descriptor (${p}) should have been registered`);je.getSetWithDefault(c,E.identHash).add(h)}let f=new Map(je.mapAndFilter(t.storedDescriptors.values(),p=>q.isVirtualDescriptor(p)?je.mapAndFilter.skip:[p.descriptorHash,je.makeDeferred()]));for(let p of t.storedDescriptors.values()){let h=f.get(p.descriptorHash);if(typeof h>"u")throw new Error(`Assertion failed: The descriptor (${p.descriptorHash}) should have been registered`);let E=t.storedResolutions.get(p.descriptorHash);if(typeof E>"u")throw new Error(`Assertion failed: The resolution (${p.descriptorHash}) should have been registered`);let C=t.originalPackages.get(E);if(typeof C>"u")throw new Error(`Assertion failed: The package (${E}) should have been registered`);Promise.resolve().then(async()=>{let S=r.getResolutionDependencies(p,a),P=Object.fromEntries(await je.allSettledSafe(Object.entries(S).map(async([te,ie])=>{let Ae=f.get(ie.descriptorHash);if(typeof Ae>"u")throw new Error(`Assertion failed: The descriptor (${ie.descriptorHash}) should have been registered`);let ce=await Ae.promise;if(!ce)throw new Error("Assertion failed: Expected the dependency to have been through the dedupe process itself");return[te,ce.updatedPackage]})));if(e.length&&!iBe.default.isMatch(q.stringifyIdent(p),e)||!r.shouldPersistResolution(C,a))return C;let I=c.get(p.identHash);if(typeof I>"u")throw new Error(`Assertion failed: The resolutions (${p.identHash}) should have been registered`);if(I.size===1)return C;let R=[...I].map(te=>{let ie=t.originalPackages.get(te);if(typeof ie>"u")throw new Error(`Assertion failed: The package (${te}) should have been registered`);return ie}),N=await r.getSatisfying(p,P,R,a),U=N.locators?.[0];if(typeof U>"u"||!N.sorted)return C;let W=t.originalPackages.get(U.locatorHash);if(typeof W>"u")throw new Error(`Assertion failed: The package (${U.locatorHash}) should have been registered`);return W}).then(async S=>{let P=await t.preparePackage(S,{resolver:r,resolveOptions:a});h.resolve({descriptor:p,currentPackage:C,updatedPackage:S,resolvedPackage:P})}).catch(S=>{h.reject(S)})}return[...f.values()].map(p=>p.promise)}};async function N5(t,{strategy:e,patterns:r,cache:s,report:a}){let{configuration:n}=t,c=new Wi,f=n.makeResolver(),p=n.makeFetcher(),h={cache:s,checksums:t.storedChecksums,fetcher:p,project:t,report:c,cacheOptions:{skipIntegrityCheck:!0}},E={project:t,resolver:f,report:c,fetchOptions:h};return await a.startTimerPromise("Deduplication step",async()=>{let C=rSt[e],S=await C(t,r,{resolver:f,resolveOptions:E,fetcher:p,fetchOptions:h}),P=ho.progressViaCounter(S.length);await a.reportProgress(P);let I=0;await Promise.all(S.map(U=>U.then(W=>{if(W===null||W.currentPackage.locatorHash===W.updatedPackage.locatorHash)return;I++;let{descriptor:te,currentPackage:ie,updatedPackage:Ae}=W;a.reportInfo(0,`${q.prettyDescriptor(n,te)} can be deduped from ${q.prettyLocator(n,ie)} to ${q.prettyLocator(n,Ae)}`),a.reportJson({descriptor:q.stringifyDescriptor(te),currentResolution:q.stringifyLocator(ie),updatedResolution:q.stringifyLocator(Ae)}),t.storedResolutions.set(te.descriptorHash,Ae.locatorHash)}).finally(()=>P.tick())));let R;switch(I){case 0:R="No packages";break;case 1:R="One package";break;default:R=`${I} packages`}let N=he.pretty(n,e,he.Type.CODE);return a.reportInfo(0,`${R} can be deduped using the ${N} strategy`),I})}var hC=class extends ut{constructor(){super(...arguments);this.strategy=ge.String("-s,--strategy","highest",{description:"The strategy to use when deduping dependencies",validator:Ao(pS)});this.check=ge.Boolean("-c,--check",!1,{description:"Exit with exit code 1 when duplicates are found, without persisting the dependency tree"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.mode=ge.String("--mode",{description:"Change what artifacts installs generate",validator:Ao(ec)});this.patterns=ge.Rest()}static{this.paths=[["dedupe"]]}static{this.usage=ot.Usage({description:"deduplicate dependencies with overlapping ranges",details:"\n Duplicates are defined as descriptors with overlapping ranges being resolved and locked to different locators. They are a natural consequence of Yarn's deterministic installs, but they can sometimes pile up and unnecessarily increase the size of your project.\n\n This command dedupes dependencies in the current project using different strategies (only one is implemented at the moment):\n\n - `highest`: Reuses (where possible) the locators with the highest versions. This means that dependencies can only be upgraded, never downgraded. It's also guaranteed that it never takes more than a single pass to dedupe the entire dependency tree.\n\n **Note:** Even though it never produces a wrong dependency tree, this command should be used with caution, as it modifies the dependency tree, which can sometimes cause problems when packages don't strictly follow semver recommendations. Because of this, it is recommended to also review the changes manually.\n\n If set, the `-c,--check` flag will only report the found duplicates, without persisting the modified dependency tree. If changes are found, the command will exit with a non-zero exit code, making it suitable for CI purposes.\n\n If the `--mode=` option is set, Yarn will change which artifacts are generated. The modes currently supported are:\n\n - `skip-build` will not run the build scripts at all. Note that this is different from setting `enableScripts` to false because the latter will disable build scripts, and thus affect the content of the artifacts generated on disk, whereas the former will just disable the build step - but not the scripts themselves, which just won't run.\n\n - `update-lockfile` will skip the link step altogether, and only fetch packages that are missing from the lockfile (or that have no associated checksums). This mode is typically used by tools like Renovate or Dependabot to keep a lockfile up-to-date without incurring the full install cost.\n\n This command accepts glob patterns as arguments (if valid Idents and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them.\n\n ### In-depth explanation:\n\n Yarn doesn't deduplicate dependencies by default, otherwise installs wouldn't be deterministic and the lockfile would be useless. What it actually does is that it tries to not duplicate dependencies in the first place.\n\n **Example:** If `foo@^2.3.4` (a dependency of a dependency) has already been resolved to `foo@2.3.4`, running `yarn add foo@*`will cause Yarn to reuse `foo@2.3.4`, even if the latest `foo` is actually `foo@2.10.14`, thus preventing unnecessary duplication.\n\n Duplication happens when Yarn can't unlock dependencies that have already been locked inside the lockfile.\n\n **Example:** If `foo@^2.3.4` (a dependency of a dependency) has already been resolved to `foo@2.3.4`, running `yarn add foo@2.10.14` will cause Yarn to install `foo@2.10.14` because the existing resolution doesn't satisfy the range `2.10.14`. This behavior can lead to (sometimes) unwanted duplication, since now the lockfile contains 2 separate resolutions for the 2 `foo` descriptors, even though they have overlapping ranges, which means that the lockfile can be simplified so that both descriptors resolve to `foo@2.10.14`.\n ",examples:[["Dedupe all packages","$0 dedupe"],["Dedupe all packages using a specific strategy","$0 dedupe --strategy highest"],["Dedupe a specific package","$0 dedupe lodash"],["Dedupe all packages with the `@babel/*` scope","$0 dedupe '@babel/*'"],["Check for duplicates (can be used as a CI step)","$0 dedupe --check"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd),a=await Jr.find(r);await s.restoreInstallState({restoreResolutions:!1});let n=0,c=await Ot.start({configuration:r,includeFooter:!1,stdout:this.context.stdout,json:this.json},async f=>{n=await N5(s,{strategy:this.strategy,patterns:this.patterns,cache:a,report:f})});return c.hasErrors()?c.exitCode():this.check?n?1:0:await s.installWithNewReport({json:this.json,stdout:this.context.stdout},{cache:a,mode:this.mode})}};Ve();Wt();var gC=class extends ut{static{this.paths=[["--clipanion=definitions"]]}async execute(){let{plugins:e}=await ze.find(this.context.cwd,this.context.plugins),r=[];for(let c of e){let{commands:f}=c[1];if(f){let h=wa.from(f).definitions();r.push([c[0],h])}}let s=this.cli.definitions(),a=(c,f)=>c.split(" ").slice(1).join()===f.split(" ").slice(1).join(),n=sBe()["@yarnpkg/builder"].bundles.standard;for(let c of r){let f=c[1];for(let p of f)s.find(h=>a(h.path,p.path)).plugin={name:c[0],isDefault:n.includes(c[0])}}this.context.stdout.write(`${JSON.stringify(s,null,2)} +`)}};var dC=class extends ut{static{this.paths=[["help"],["--help"],["-h"]]}async execute(){this.context.stdout.write(this.cli.usage(null))}};Ve();bt();Wt();var mC=class extends ut{constructor(){super(...arguments);this.leadingArgument=ge.String();this.args=ge.Proxy()}async execute(){if(this.leadingArgument.match(/[\\/]/)&&!q.tryParseIdent(this.leadingArgument)){let r=K.resolve(this.context.cwd,ue.toPortablePath(this.leadingArgument));return await this.cli.run(this.args,{cwd:r})}else return await this.cli.run(["run",this.leadingArgument,...this.args])}};Ve();var yC=class extends ut{static{this.paths=[["-v"],["--version"]]}async execute(){this.context.stdout.write(`${un||""} +`)}};Ve();Ve();Wt();var EC=class extends ut{constructor(){super(...arguments);this.commandName=ge.String();this.args=ge.Proxy()}static{this.paths=[["exec"]]}static{this.usage=ot.Usage({description:"execute a shell script",details:` + This command simply executes a shell script within the context of the root directory of the active workspace using the portable shell. + + It also makes sure to call it in a way that's compatible with the current project (for example, on PnP projects the environment will be setup in such a way that PnP will be correctly injected into the environment). + `,examples:[["Execute a single shell command","$0 exec echo Hello World"],["Execute a shell script",'$0 exec "tsc & babel src --out-dir lib"']]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,locator:a}=await Tt.find(r,this.context.cwd);return await s.restoreInstallState(),await In.executePackageShellcode(a,this.commandName,this.args,{cwd:this.context.cwd,stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr,project:s})}};Ve();Wt();Ul();var IC=class extends ut{constructor(){super(...arguments);this.hash=ge.String({required:!1,validator:Jx(wE(),[tB(/^p[0-9a-f]{5}$/)])})}static{this.paths=[["explain","peer-requirements"]]}static{this.usage=ot.Usage({description:"explain a set of peer requirements",details:` + A peer requirement represents all peer requests that a subject must satisfy when providing a requested package to requesters. + + When the hash argument is specified, this command prints a detailed explanation of the peer requirement corresponding to the hash and whether it is satisfied or not. + + When used without arguments, this command lists all peer requirements and the corresponding hash that can be used to get detailed information about a given requirement. + + **Note:** A hash is a six-letter p-prefixed code that can be obtained from peer dependency warnings or from the list of all peer requirements (\`yarn explain peer-requirements\`). + `,examples:[["Explain the corresponding peer requirement for a hash","$0 explain peer-requirements p1a4ed"],["List all peer requirements","$0 explain peer-requirements"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd);return await s.restoreInstallState({restoreResolutions:!1}),await s.applyLightResolution(),typeof this.hash<"u"?await iSt(this.hash,s,{stdout:this.context.stdout}):await sSt(s,{stdout:this.context.stdout})}};async function iSt(t,e,r){let s=e.peerRequirementNodes.get(t);if(typeof s>"u")throw new Error(`No peerDependency requirements found for hash: "${t}"`);let a=new Set,n=p=>a.has(p.requester.locatorHash)?{value:he.tuple(he.Type.DEPENDENT,{locator:p.requester,descriptor:p.descriptor}),children:p.children.size>0?[{value:he.tuple(he.Type.NO_HINT,"...")}]:[]}:(a.add(p.requester.locatorHash),{value:he.tuple(he.Type.DEPENDENT,{locator:p.requester,descriptor:p.descriptor}),children:Object.fromEntries(Array.from(p.children.values(),h=>[q.stringifyLocator(h.requester),n(h)]))}),c=e.peerWarnings.find(p=>p.hash===t);return(await Ot.start({configuration:e.configuration,stdout:r.stdout,includeFooter:!1,includePrefix:!1},async p=>{let h=he.mark(e.configuration),E=c?h.Cross:h.Check;if(p.reportInfo(0,`Package ${he.pretty(e.configuration,s.subject,he.Type.LOCATOR)} is requested to provide ${he.pretty(e.configuration,s.ident,he.Type.IDENT)} by its descendants`),p.reportSeparator(),p.reportInfo(0,he.pretty(e.configuration,s.subject,he.Type.LOCATOR)),ks.emitTree({children:Object.fromEntries(Array.from(s.requests.values(),C=>[q.stringifyLocator(C.requester),n(C)]))},{configuration:e.configuration,stdout:r.stdout,json:!1}),p.reportSeparator(),s.provided.range==="missing:"){let C=c?"":" , but all peer requests are optional";p.reportInfo(0,`${E} Package ${he.pretty(e.configuration,s.subject,he.Type.LOCATOR)} does not provide ${he.pretty(e.configuration,s.ident,he.Type.IDENT)}${C}.`)}else{let C=e.storedResolutions.get(s.provided.descriptorHash);if(!C)throw new Error("Assertion failed: Expected the descriptor to be registered");let S=e.storedPackages.get(C);if(!S)throw new Error("Assertion failed: Expected the package to be registered");p.reportInfo(0,`${E} Package ${he.pretty(e.configuration,s.subject,he.Type.LOCATOR)} provides ${he.pretty(e.configuration,s.ident,he.Type.IDENT)} with version ${q.prettyReference(e.configuration,S.version??"0.0.0")}, ${c?"which does not satisfy all requests.":"which satisfies all requests"}`),c?.type===3&&(c.range?p.reportInfo(0,` The combined requested range is ${he.pretty(e.configuration,c.range,he.Type.RANGE)}`):p.reportInfo(0," Unfortunately, the requested ranges have no overlap"))}})).exitCode()}async function sSt(t,e){return(await Ot.start({configuration:t.configuration,stdout:e.stdout,includeFooter:!1,includePrefix:!1},async s=>{let a=he.mark(t.configuration),n=je.sortMap(t.peerRequirementNodes,[([,c])=>q.stringifyLocator(c.subject),([,c])=>q.stringifyIdent(c.ident)]);for(let[,c]of n.values()){if(!c.root)continue;let f=t.peerWarnings.find(E=>E.hash===c.hash),p=[...q.allPeerRequests(c)],h;if(p.length>2?h=` and ${p.length-1} other dependencies`:p.length===2?h=" and 1 other dependency":h="",c.provided.range!=="missing:"){let E=t.storedResolutions.get(c.provided.descriptorHash);if(!E)throw new Error("Assertion failed: Expected the resolution to have been registered");let C=t.storedPackages.get(E);if(!C)throw new Error("Assertion failed: Expected the provided package to have been registered");let S=`${he.pretty(t.configuration,c.hash,he.Type.CODE)} \u2192 ${f?a.Cross:a.Check} ${q.prettyLocator(t.configuration,c.subject)} provides ${q.prettyLocator(t.configuration,C)} to ${q.prettyLocator(t.configuration,p[0].requester)}${h}`;f?s.reportWarning(0,S):s.reportInfo(0,S)}else{let E=`${he.pretty(t.configuration,c.hash,he.Type.CODE)} \u2192 ${f?a.Cross:a.Check} ${q.prettyLocator(t.configuration,c.subject)} doesn't provide ${q.prettyIdent(t.configuration,c.ident)} to ${q.prettyLocator(t.configuration,p[0].requester)}${h}`;f?s.reportWarning(0,E):s.reportInfo(0,E)}}})).exitCode()}Ve();Wt();Ul();Ve();Ve();bt();Wt();var oBe=et(fi()),CC=class extends ut{constructor(){super(...arguments);this.useYarnPath=ge.Boolean("--yarn-path",{description:"Set the yarnPath setting even if the version can be accessed by Corepack"});this.onlyIfNeeded=ge.Boolean("--only-if-needed",!1,{description:"Only lock the Yarn version if it isn't already locked"});this.version=ge.String()}static{this.paths=[["set","version"]]}static{this.usage=ot.Usage({description:"lock the Yarn version used by the project",details:"\n This command will set a specific release of Yarn to be used by Corepack: https://nodejs.org/api/corepack.html.\n\n By default it only will set the `packageManager` field at the root of your project, but if the referenced release cannot be represented this way, if you already have `yarnPath` configured, or if you set the `--yarn-path` command line flag, then the release will also be downloaded from the Yarn GitHub repository, stored inside your project, and referenced via the `yarnPath` settings from your project `.yarnrc.yml` file.\n\n A very good use case for this command is to enforce the version of Yarn used by any single member of your team inside the same project - by doing this you ensure that you have control over Yarn upgrades and downgrades (including on your deployment servers), and get rid of most of the headaches related to someone using a slightly different version and getting different behavior.\n\n The version specifier can be:\n\n - a tag:\n - `latest` / `berry` / `stable` -> the most recent stable berry (`>=2.0.0`) release\n - `canary` -> the most recent canary (release candidate) berry (`>=2.0.0`) release\n - `classic` -> the most recent classic (`^0.x || ^1.x`) release\n\n - a semver range (e.g. `2.x`) -> the most recent version satisfying the range (limited to berry releases)\n\n - a semver version (e.g. `2.4.1`, `1.22.1`)\n\n - a local file referenced through either a relative or absolute path\n\n - `self` -> the version used to invoke the command\n ",examples:[["Download the latest release from the Yarn repository","$0 set version latest"],["Download the latest canary release from the Yarn repository","$0 set version canary"],["Download the latest classic release from the Yarn repository","$0 set version classic"],["Download the most recent Yarn 3 build","$0 set version 3.x"],["Download a specific Yarn 2 build","$0 set version 2.0.0-rc.30"],["Switch back to a specific Yarn 1 release","$0 set version 1.22.1"],["Use a release from the local filesystem","$0 set version ./yarn.cjs"],["Use a release from a URL","$0 set version https://repo.yarnpkg.com/3.1.0/packages/yarnpkg-cli/bin/yarn.js"],["Download the version used to invoke the command","$0 set version self"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);if(this.onlyIfNeeded&&r.get("yarnPath")){let f=r.sources.get("yarnPath");if(!f)throw new Error("Assertion failed: Expected 'yarnPath' to have a source");let p=r.projectCwd??r.startingCwd;if(K.contains(p,f))return 0}let s=()=>{if(typeof un>"u")throw new nt("The --install flag can only be used without explicit version specifier from the Yarn CLI");return`file://${process.argv[1]}`},a,n=(f,p)=>({version:p,url:f.replace(/\{\}/g,p)});if(this.version==="self")a={url:s(),version:un??"self"};else if(this.version==="latest"||this.version==="berry"||this.version==="stable")a=n("https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js",await hS(r,"stable"));else if(this.version==="canary")a=n("https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js",await hS(r,"canary"));else if(this.version==="classic")a={url:"https://classic.yarnpkg.com/latest.js",version:"classic"};else if(this.version.match(/^https?:/))a={url:this.version,version:"remote"};else if(this.version.match(/^\.{0,2}[\\/]/)||ue.isAbsolute(this.version))a={url:`file://${K.resolve(ue.toPortablePath(this.version))}`,version:"file"};else if(Or.satisfiesWithPrereleases(this.version,">=2.0.0"))a=n("https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js",this.version);else if(Or.satisfiesWithPrereleases(this.version,"^0.x || ^1.x"))a=n("https://github.com/yarnpkg/yarn/releases/download/v{}/yarn-{}.js",this.version);else if(Or.validRange(this.version))a=n("https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js",await oSt(r,this.version));else throw new nt(`Invalid version descriptor "${this.version}"`);return(await Ot.start({configuration:r,stdout:this.context.stdout,includeLogs:!this.context.quiet},async f=>{let p=async()=>{let h="file://";return a.url.startsWith(h)?(f.reportInfo(0,`Retrieving ${he.pretty(r,a.url,he.Type.PATH)}`),await le.readFilePromise(a.url.slice(h.length))):(f.reportInfo(0,`Downloading ${he.pretty(r,a.url,he.Type.URL)}`),await An.get(a.url,{configuration:r}))};await O5(r,a.version,p,{report:f,useYarnPath:this.useYarnPath})})).exitCode()}};async function oSt(t,e){let s=(await An.get("https://repo.yarnpkg.com/tags",{configuration:t,jsonResponse:!0})).tags.filter(a=>Or.satisfiesWithPrereleases(a,e));if(s.length===0)throw new nt(`No matching release found for range ${he.pretty(t,e,he.Type.RANGE)}.`);return s[0]}async function hS(t,e){let r=await An.get("https://repo.yarnpkg.com/tags",{configuration:t,jsonResponse:!0});if(!r.latest[e])throw new nt(`Tag ${he.pretty(t,e,he.Type.RANGE)} not found`);return r.latest[e]}async function O5(t,e,r,{report:s,useYarnPath:a}){let n,c=async()=>(typeof n>"u"&&(n=await r()),n);if(e===null){let te=await c();await le.mktempPromise(async ie=>{let Ae=K.join(ie,"yarn.cjs");await le.writeFilePromise(Ae,te);let{stdout:ce}=await Gr.execvp(process.execPath,[ue.fromPortablePath(Ae),"--version"],{cwd:ie,env:{...t.env,YARN_IGNORE_PATH:"1"}});if(e=ce.trim(),!oBe.default.valid(e))throw new Error(`Invalid semver version. ${he.pretty(t,"yarn --version",he.Type.CODE)} returned: +${e}`)})}let f=t.projectCwd??t.startingCwd,p=K.resolve(f,".yarn/releases"),h=K.resolve(p,`yarn-${e}.cjs`),E=K.relative(t.startingCwd,h),C=je.isTaggedYarnVersion(e),S=t.get("yarnPath"),P=!C,I=P||!!S||!!a;if(a===!1){if(P)throw new Yt(0,"You explicitly opted out of yarnPath usage in your command line, but the version you specified cannot be represented by Corepack");I=!1}else!I&&!process.env.COREPACK_ROOT&&(s.reportWarning(0,`You don't seem to have ${he.applyHyperlink(t,"Corepack","https://nodejs.org/api/corepack.html")} enabled; we'll have to rely on ${he.applyHyperlink(t,"yarnPath","https://yarnpkg.com/configuration/yarnrc#yarnPath")} instead`),I=!0);if(I){let te=await c();s.reportInfo(0,`Saving the new release in ${he.pretty(t,E,"magenta")}`),await le.removePromise(K.dirname(h)),await le.mkdirPromise(K.dirname(h),{recursive:!0}),await le.writeFilePromise(h,te,{mode:493}),await ze.updateConfiguration(f,{yarnPath:K.relative(f,h)})}else await le.removePromise(K.dirname(h)),await ze.updateConfiguration(f,{yarnPath:ze.deleteProperty});let R=await Ht.tryFind(f)||new Ht;R.packageManager=`yarn@${C?e:await hS(t,"stable")}`;let N={};R.exportTo(N);let U=K.join(f,Ht.fileName),W=`${JSON.stringify(N,null,R.indent)} +`;return await le.changeFilePromise(U,W,{automaticNewlines:!0}),{bundleVersion:e}}function aBe(t){return Dr[rk(t)]}var aSt=/## (?YN[0-9]{4}) - `(?[A-Z_]+)`\n\n(?
(?:.(?!##))+)/gs;async function lSt(t){let r=`https://repo.yarnpkg.com/${je.isTaggedYarnVersion(un)?un:await hS(t,"canary")}/packages/docusaurus/docs/advanced/01-general-reference/error-codes.mdx`,s=await An.get(r,{configuration:t});return new Map(Array.from(s.toString().matchAll(aSt),({groups:a})=>{if(!a)throw new Error("Assertion failed: Expected the match to have been successful");let n=aBe(a.code);if(a.name!==n)throw new Error(`Assertion failed: Invalid error code data: Expected "${a.name}" to be named "${n}"`);return[a.code,a.details]}))}var wC=class extends ut{constructor(){super(...arguments);this.code=ge.String({required:!1,validator:rB(wE(),[tB(/^YN[0-9]{4}$/)])});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["explain"]]}static{this.usage=ot.Usage({description:"explain an error code",details:` + When the code argument is specified, this command prints its name and its details. + + When used without arguments, this command lists all error codes and their names. + `,examples:[["Explain an error code","$0 explain YN0006"],["List all error codes","$0 explain"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);if(typeof this.code<"u"){let s=aBe(this.code),a=he.pretty(r,s,he.Type.CODE),n=this.cli.format().header(`${this.code} - ${a}`),f=(await lSt(r)).get(this.code),p=typeof f<"u"?he.jsonOrPretty(this.json,r,he.tuple(he.Type.MARKDOWN,{text:f,format:this.cli.format(),paragraphs:!0})):`This error code does not have a description. + +You can help us by editing this page on GitHub \u{1F642}: +${he.jsonOrPretty(this.json,r,he.tuple(he.Type.URL,"https://github.com/yarnpkg/berry/blob/master/packages/docusaurus/docs/advanced/01-general-reference/error-codes.mdx"))} +`;this.json?this.context.stdout.write(`${JSON.stringify({code:this.code,name:s,details:p})} +`):this.context.stdout.write(`${n} + +${p} +`)}else{let s={children:je.mapAndFilter(Object.entries(Dr),([a,n])=>Number.isNaN(Number(a))?je.mapAndFilter.skip:{label:Vf(Number(a)),value:he.tuple(he.Type.CODE,n)})};ks.emitTree(s,{configuration:r,stdout:this.context.stdout,json:this.json})}}};Ve();bt();Wt();var lBe=et(Sa()),BC=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("-A,--all",!1,{description:"Print versions of a package from the whole project"});this.recursive=ge.Boolean("-R,--recursive",!1,{description:"Print information for all packages, including transitive dependencies"});this.extra=ge.Array("-X,--extra",[],{description:"An array of requests of extra data provided by plugins"});this.cache=ge.Boolean("--cache",!1,{description:"Print information about the cache entry of a package (path, size, checksum)"});this.dependents=ge.Boolean("--dependents",!1,{description:"Print all dependents for each matching package"});this.manifest=ge.Boolean("--manifest",!1,{description:"Print data obtained by looking at the package archive (license, homepage, ...)"});this.nameOnly=ge.Boolean("--name-only",!1,{description:"Only print the name for the matching packages"});this.virtuals=ge.Boolean("--virtuals",!1,{description:"Print each instance of the virtual packages"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.patterns=ge.Rest()}static{this.paths=[["info"]]}static{this.usage=ot.Usage({description:"see information related to packages",details:"\n This command prints various information related to the specified packages, accepting glob patterns.\n\n By default, if the locator reference is missing, Yarn will default to print the information about all the matching direct dependencies of the package for the active workspace. To instead print all versions of the package that are direct dependencies of any of your workspaces, use the `-A,--all` flag. Adding the `-R,--recursive` flag will also report transitive dependencies.\n\n Some fields will be hidden by default in order to keep the output readable, but can be selectively displayed by using additional options (`--dependents`, `--manifest`, `--virtuals`, ...) described in the option descriptions.\n\n Note that this command will only print the information directly related to the selected packages - if you wish to know why the package is there in the first place, use `yarn why` which will do just that (it also provides a `-R,--recursive` flag that may be of some help).\n ",examples:[["Show information about Lodash","$0 info lodash"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a&&!this.all)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState();let c=new Set(this.extra);this.cache&&c.add("cache"),this.dependents&&c.add("dependents"),this.manifest&&c.add("manifest");let f=(ie,{recursive:Ae})=>{let ce=ie.anchoredLocator.locatorHash,me=new Map,pe=[ce];for(;pe.length>0;){let Be=pe.shift();if(me.has(Be))continue;let Ce=s.storedPackages.get(Be);if(typeof Ce>"u")throw new Error("Assertion failed: Expected the package to be registered");if(me.set(Be,Ce),q.isVirtualLocator(Ce)&&pe.push(q.devirtualizeLocator(Ce).locatorHash),!(!Ae&&Be!==ce))for(let g of Ce.dependencies.values()){let we=s.storedResolutions.get(g.descriptorHash);if(typeof we>"u")throw new Error("Assertion failed: Expected the resolution to be registered");pe.push(we)}}return me.values()},p=({recursive:ie})=>{let Ae=new Map;for(let ce of s.workspaces)for(let me of f(ce,{recursive:ie}))Ae.set(me.locatorHash,me);return Ae.values()},h=({all:ie,recursive:Ae})=>ie&&Ae?s.storedPackages.values():ie?p({recursive:Ae}):f(a,{recursive:Ae}),E=({all:ie,recursive:Ae})=>{let ce=h({all:ie,recursive:Ae}),me=this.patterns.map(Ce=>{let g=q.parseLocator(Ce),we=lBe.default.makeRe(q.stringifyIdent(g)),Ee=q.isVirtualLocator(g),fe=Ee?q.devirtualizeLocator(g):g;return se=>{let X=q.stringifyIdent(se);if(!we.test(X))return!1;if(g.reference==="unknown")return!0;let De=q.isVirtualLocator(se),Re=De?q.devirtualizeLocator(se):se;return!(Ee&&De&&g.reference!==se.reference||fe.reference!==Re.reference)}}),pe=je.sortMap([...ce],Ce=>q.stringifyLocator(Ce));return{selection:pe.filter(Ce=>me.length===0||me.some(g=>g(Ce))),sortedLookup:pe}},{selection:C,sortedLookup:S}=E({all:this.all,recursive:this.recursive});if(C.length===0)throw new nt("No package matched your request");let P=new Map;if(this.dependents)for(let ie of S)for(let Ae of ie.dependencies.values()){let ce=s.storedResolutions.get(Ae.descriptorHash);if(typeof ce>"u")throw new Error("Assertion failed: Expected the resolution to be registered");je.getArrayWithDefault(P,ce).push(ie)}let I=new Map;for(let ie of S){if(!q.isVirtualLocator(ie))continue;let Ae=q.devirtualizeLocator(ie);je.getArrayWithDefault(I,Ae.locatorHash).push(ie)}let R={},N={children:R},U=r.makeFetcher(),W={project:s,fetcher:U,cache:n,checksums:s.storedChecksums,report:new Wi,cacheOptions:{skipIntegrityCheck:!0}},te=[async(ie,Ae,ce)=>{if(!Ae.has("manifest"))return;let me=await U.fetch(ie,W),pe;try{pe=await Ht.find(me.prefixPath,{baseFs:me.packageFs})}finally{me.releaseFs?.()}ce("Manifest",{License:he.tuple(he.Type.NO_HINT,pe.license),Homepage:he.tuple(he.Type.URL,pe.raw.homepage??null)})},async(ie,Ae,ce)=>{if(!Ae.has("cache"))return;let me=s.storedChecksums.get(ie.locatorHash)??null,pe=n.getLocatorPath(ie,me),Be;if(pe!==null)try{Be=await le.statPromise(pe)}catch{}let Ce=typeof Be<"u"?[Be.size,he.Type.SIZE]:void 0;ce("Cache",{Checksum:he.tuple(he.Type.NO_HINT,me),Path:he.tuple(he.Type.PATH,pe),Size:Ce})}];for(let ie of C){let Ae=q.isVirtualLocator(ie);if(!this.virtuals&&Ae)continue;let ce={},me={value:[ie,he.Type.LOCATOR],children:ce};if(R[q.stringifyLocator(ie)]=me,this.nameOnly){delete me.children;continue}let pe=I.get(ie.locatorHash);typeof pe<"u"&&(ce.Instances={label:"Instances",value:he.tuple(he.Type.NUMBER,pe.length)}),ce.Version={label:"Version",value:he.tuple(he.Type.NO_HINT,ie.version)};let Be=(g,we)=>{let Ee={};if(ce[g]=Ee,Array.isArray(we))Ee.children=we.map(fe=>({value:fe}));else{let fe={};Ee.children=fe;for(let[se,X]of Object.entries(we))typeof X>"u"||(fe[se]={label:se,value:X})}};if(!Ae){for(let g of te)await g(ie,c,Be);await r.triggerHook(g=>g.fetchPackageInfo,ie,c,Be)}ie.bin.size>0&&!Ae&&Be("Exported Binaries",[...ie.bin.keys()].map(g=>he.tuple(he.Type.PATH,g)));let Ce=P.get(ie.locatorHash);typeof Ce<"u"&&Ce.length>0&&Be("Dependents",Ce.map(g=>he.tuple(he.Type.LOCATOR,g))),ie.dependencies.size>0&&!Ae&&Be("Dependencies",[...ie.dependencies.values()].map(g=>{let we=s.storedResolutions.get(g.descriptorHash),Ee=typeof we<"u"?s.storedPackages.get(we)??null:null;return he.tuple(he.Type.RESOLUTION,{descriptor:g,locator:Ee})})),ie.peerDependencies.size>0&&Ae&&Be("Peer dependencies",[...ie.peerDependencies.values()].map(g=>{let we=ie.dependencies.get(g.identHash),Ee=typeof we<"u"?s.storedResolutions.get(we.descriptorHash)??null:null,fe=Ee!==null?s.storedPackages.get(Ee)??null:null;return he.tuple(he.Type.RESOLUTION,{descriptor:g,locator:fe})}))}ks.emitTree(N,{configuration:r,json:this.json,stdout:this.context.stdout,separators:this.nameOnly?0:2})}};Ve();bt();Bc();var aF=et(Nd());Wt();var L5=et(fi());Ul();var cSt=[{selector:t=>t===-1,name:"nodeLinker",value:"node-modules"},{selector:t=>t!==-1&&t<8,name:"enableGlobalCache",value:!1},{selector:t=>t!==-1&&t<8,name:"compressionLevel",value:"mixed"}],vC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.immutable=ge.Boolean("--immutable",{description:"Abort with an error exit code if the lockfile was to be modified"});this.immutableCache=ge.Boolean("--immutable-cache",{description:"Abort with an error exit code if the cache folder was to be modified"});this.refreshLockfile=ge.Boolean("--refresh-lockfile",{description:"Refresh the package metadata stored in the lockfile"});this.checkCache=ge.Boolean("--check-cache",{description:"Always refetch the packages and ensure that their checksums are consistent"});this.checkResolutions=ge.Boolean("--check-resolutions",{description:"Validates that the package resolutions are coherent"});this.inlineBuilds=ge.Boolean("--inline-builds",{description:"Verbosely print the output of the build steps of dependencies"});this.mode=ge.String("--mode",{description:"Change what artifacts installs generate",validator:Ao(ec)});this.cacheFolder=ge.String("--cache-folder",{hidden:!0});this.frozenLockfile=ge.Boolean("--frozen-lockfile",{hidden:!0});this.ignoreEngines=ge.Boolean("--ignore-engines",{hidden:!0});this.nonInteractive=ge.Boolean("--non-interactive",{hidden:!0});this.preferOffline=ge.Boolean("--prefer-offline",{hidden:!0});this.production=ge.Boolean("--production",{hidden:!0});this.registry=ge.String("--registry",{hidden:!0});this.silent=ge.Boolean("--silent",{hidden:!0});this.networkTimeout=ge.String("--network-timeout",{hidden:!0})}static{this.paths=[["install"],ot.Default]}static{this.usage=ot.Usage({description:"install the project dependencies",details:"\n This command sets up your project if needed. The installation is split into four different steps that each have their own characteristics:\n\n - **Resolution:** First the package manager will resolve your dependencies. The exact way a dependency version is privileged over another isn't standardized outside of the regular semver guarantees. If a package doesn't resolve to what you would expect, check that all dependencies are correctly declared (also check our website for more information: ).\n\n - **Fetch:** Then we download all the dependencies if needed, and make sure that they're all stored within our cache (check the value of `cacheFolder` in `yarn config` to see where the cache files are stored).\n\n - **Link:** Then we send the dependency tree information to internal plugins tasked with writing them on the disk in some form (for example by generating the `.pnp.cjs` file you might know).\n\n - **Build:** Once the dependency tree has been written on the disk, the package manager will now be free to run the build scripts for all packages that might need it, in a topological order compatible with the way they depend on one another. See https://yarnpkg.com/advanced/lifecycle-scripts for detail.\n\n Note that running this command is not part of the recommended workflow. Yarn supports zero-installs, which means that as long as you store your cache and your `.pnp.cjs` file inside your repository, everything will work without requiring any install right after cloning your repository or switching branches.\n\n If the `--immutable` option is set (defaults to true on CI), Yarn will abort with an error exit code if the lockfile was to be modified (other paths can be added using the `immutablePatterns` configuration setting). For backward compatibility we offer an alias under the name of `--frozen-lockfile`, but it will be removed in a later release.\n\n If the `--immutable-cache` option is set, Yarn will abort with an error exit code if the cache folder was to be modified (either because files would be added, or because they'd be removed).\n\n If the `--refresh-lockfile` option is set, Yarn will keep the same resolution for the packages currently in the lockfile but will refresh their metadata. If used together with `--immutable`, it can validate that the lockfile information are consistent. This flag is enabled by default when Yarn detects it runs within a pull request context.\n\n If the `--check-cache` option is set, Yarn will always refetch the packages and will ensure that their checksum matches what's 1/ described in the lockfile 2/ inside the existing cache files (if present). This is recommended as part of your CI workflow if you're both following the Zero-Installs model and accepting PRs from third-parties, as they'd otherwise have the ability to alter the checked-in packages before submitting them.\n\n If the `--inline-builds` option is set, Yarn will verbosely print the output of the build steps of your dependencies (instead of writing them into individual files). This is likely useful mostly for debug purposes only when using Docker-like environments.\n\n If the `--mode=` option is set, Yarn will change which artifacts are generated. The modes currently supported are:\n\n - `skip-build` will not run the build scripts at all. Note that this is different from setting `enableScripts` to false because the latter will disable build scripts, and thus affect the content of the artifacts generated on disk, whereas the former will just disable the build step - but not the scripts themselves, which just won't run.\n\n - `update-lockfile` will skip the link step altogether, and only fetch packages that are missing from the lockfile (or that have no associated checksums). This mode is typically used by tools like Renovate or Dependabot to keep a lockfile up-to-date without incurring the full install cost.\n ",examples:[["Install the project","$0 install"],["Validate a project when using Zero-Installs","$0 install --immutable --immutable-cache"],["Validate a project when using Zero-Installs (slightly safer if you accept external PRs)","$0 install --immutable --immutable-cache --check-cache"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);typeof this.inlineBuilds<"u"&&r.useWithSource("",{enableInlineBuilds:this.inlineBuilds},r.startingCwd,{overwrite:!0});let s=!!process.env.FUNCTION_TARGET||!!process.env.GOOGLE_RUNTIME,a=await DI({configuration:r,stdout:this.context.stdout},[{option:this.ignoreEngines,message:"The --ignore-engines option is deprecated; engine checking isn't a core feature anymore",error:!aF.default.VERCEL},{option:this.registry,message:"The --registry option is deprecated; prefer setting npmRegistryServer in your .yarnrc.yml file"},{option:this.preferOffline,message:"The --prefer-offline flag is deprecated; use the --cached flag with 'yarn add' instead",error:!aF.default.VERCEL},{option:this.production,message:"The --production option is deprecated on 'install'; use 'yarn workspaces focus' instead",error:!0},{option:this.nonInteractive,message:"The --non-interactive option is deprecated",error:!s},{option:this.frozenLockfile,message:"The --frozen-lockfile option is deprecated; use --immutable and/or --immutable-cache instead",callback:()=>this.immutable=this.frozenLockfile},{option:this.cacheFolder,message:"The cache-folder option has been deprecated; use rc settings instead",error:!aF.default.NETLIFY}]);if(a!==null)return a;let n=this.mode==="update-lockfile";if(n&&(this.immutable||this.immutableCache))throw new nt(`${he.pretty(r,"--immutable",he.Type.CODE)} and ${he.pretty(r,"--immutable-cache",he.Type.CODE)} cannot be used with ${he.pretty(r,"--mode=update-lockfile",he.Type.CODE)}`);let c=(this.immutable??r.get("enableImmutableInstalls"))&&!n,f=this.immutableCache&&!n;if(r.projectCwd!==null){let R=await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout,includeFooter:!1},async N=>{let U=!1;await ASt(r,c)&&(N.reportInfo(48,"Automatically removed core plugins that are now builtins \u{1F44D}"),U=!0),await fSt(r,c)&&(N.reportInfo(48,"Automatically fixed merge conflicts \u{1F44D}"),U=!0),U&&N.reportSeparator()});if(R.hasErrors())return R.exitCode()}if(r.projectCwd!==null){let R=await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout,includeFooter:!1},async N=>{if(ze.telemetry?.isNew)ze.telemetry.commitTips(),N.reportInfo(65,"Yarn will periodically gather anonymous telemetry: https://yarnpkg.com/advanced/telemetry"),N.reportInfo(65,`Run ${he.pretty(r,"yarn config set --home enableTelemetry 0",he.Type.CODE)} to disable`),N.reportSeparator();else if(ze.telemetry?.shouldShowTips){let U=await An.get("https://repo.yarnpkg.com/tags",{configuration:r,jsonResponse:!0}).catch(()=>null);if(U!==null){let W=null;if(un!==null){let ie=L5.default.prerelease(un)?"canary":"stable",Ae=U.latest[ie];L5.default.gt(Ae,un)&&(W=[ie,Ae])}if(W)ze.telemetry.commitTips(),N.reportInfo(88,`${he.applyStyle(r,`A new ${W[0]} version of Yarn is available:`,he.Style.BOLD)} ${q.prettyReference(r,W[1])}!`),N.reportInfo(88,`Upgrade now by running ${he.pretty(r,`yarn set version ${W[1]}`,he.Type.CODE)}`),N.reportSeparator();else{let te=ze.telemetry.selectTip(U.tips);te&&(N.reportInfo(89,he.pretty(r,te.message,he.Type.MARKDOWN_INLINE)),te.url&&N.reportInfo(89,`Learn more at ${te.url}`),N.reportSeparator())}}}});if(R.hasErrors())return R.exitCode()}let{project:p,workspace:h}=await Tt.find(r,this.context.cwd),E=p.lockfileLastVersion;if(E!==null){let R=await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout,includeFooter:!1},async N=>{let U={};for(let W of cSt)W.selector(E)&&typeof r.sources.get(W.name)>"u"&&(r.use("",{[W.name]:W.value},p.cwd,{overwrite:!0}),U[W.name]=W.value);Object.keys(U).length>0&&(await ze.updateConfiguration(p.cwd,U),N.reportInfo(87,"Migrated your project to the latest Yarn version \u{1F680}"),N.reportSeparator())});if(R.hasErrors())return R.exitCode()}let C=await Jr.find(r,{immutable:f,check:this.checkCache});if(!h)throw new ar(p.cwd,this.context.cwd);await p.restoreInstallState({restoreResolutions:!1});let S=r.get("enableHardenedMode");S&&typeof r.sources.get("enableHardenedMode")>"u"&&await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout,includeFooter:!1},async R=>{R.reportWarning(0,"Yarn detected that the current workflow is executed from a public pull request. For safety the hardened mode has been enabled."),R.reportWarning(0,`It will prevent malicious lockfile manipulations, in exchange for a slower install time. You can opt-out if necessary; check our ${he.applyHyperlink(r,"documentation","https://yarnpkg.com/features/security#hardened-mode")} for more details.`),R.reportSeparator()}),(this.refreshLockfile??S)&&(p.lockfileNeedsRefresh=!0);let P=this.checkResolutions??S;return(await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout,forceSectionAlignment:!0,includeLogs:!0,includeVersion:!0},async R=>{await p.install({cache:C,report:R,immutable:c,checkResolutions:P,mode:this.mode})})).exitCode()}},uSt="<<<<<<<";async function fSt(t,e){if(!t.projectCwd)return!1;let r=K.join(t.projectCwd,Er.lockfile);if(!await le.existsPromise(r)||!(await le.readFilePromise(r,"utf8")).includes(uSt))return!1;if(e)throw new Yt(47,"Cannot autofix a lockfile when running an immutable install");let a=await Gr.execvp("git",["rev-parse","MERGE_HEAD","HEAD"],{cwd:t.projectCwd});if(a.code!==0&&(a=await Gr.execvp("git",["rev-parse","REBASE_HEAD","HEAD"],{cwd:t.projectCwd})),a.code!==0&&(a=await Gr.execvp("git",["rev-parse","CHERRY_PICK_HEAD","HEAD"],{cwd:t.projectCwd})),a.code!==0)throw new Yt(83,"Git returned an error when trying to find the commits pertaining to the conflict");let n=await Promise.all(a.stdout.trim().split(/\n/).map(async f=>{let p=await Gr.execvp("git",["show",`${f}:./${Er.lockfile}`],{cwd:t.projectCwd});if(p.code!==0)throw new Yt(83,`Git returned an error when trying to access the lockfile content in ${f}`);try{return ls(p.stdout)}catch{throw new Yt(46,"A variant of the conflicting lockfile failed to parse")}}));n=n.filter(f=>!!f.__metadata);for(let f of n){if(f.__metadata.version<7)for(let p of Object.keys(f)){if(p==="__metadata")continue;let h=q.parseDescriptor(p,!0),E=t.normalizeDependency(h),C=q.stringifyDescriptor(E);C!==p&&(f[C]=f[p],delete f[p])}for(let p of Object.keys(f)){if(p==="__metadata")continue;let h=f[p].checksum;typeof h>"u"||h.includes("/")||(f[p].checksum=`${f.__metadata.cacheKey}/${h}`)}}let c=Object.assign({},...n);c.__metadata.version=`${Math.min(...n.map(f=>parseInt(f.__metadata.version??0)))}`,c.__metadata.cacheKey="merged";for(let[f,p]of Object.entries(c))typeof p=="string"&&delete c[f];return await le.changeFilePromise(r,il(c),{automaticNewlines:!0}),!0}async function ASt(t,e){if(!t.projectCwd)return!1;let r=[],s=K.join(t.projectCwd,".yarn/plugins/@yarnpkg");return await ze.updateConfiguration(t.projectCwd,{plugins:n=>{if(!Array.isArray(n))return n;let c=n.filter(f=>{if(!f.path)return!0;let p=K.resolve(t.projectCwd,f.path),h=Ev.has(f.spec)&&K.contains(s,p);return h&&r.push(p),!h});return c.length===0?ze.deleteProperty:c.length===n.length?n:c}},{immutable:e})?(await Promise.all(r.map(async n=>{await le.removePromise(n)})),!0):!1}Ve();bt();Wt();var SC=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("-A,--all",!1,{description:"Link all workspaces belonging to the target projects to the current one"});this.private=ge.Boolean("-p,--private",!1,{description:"Also link private workspaces belonging to the target projects to the current one"});this.relative=ge.Boolean("-r,--relative",!1,{description:"Link workspaces using relative paths instead of absolute paths"});this.destinations=ge.Rest()}static{this.paths=[["link"]]}static{this.usage=ot.Usage({description:"connect the local project to another one",details:"\n This command will set a new `resolutions` field in the project-level manifest and point it to the workspace at the specified location (even if part of another project).\n ",examples:[["Register one or more remote workspaces for use in the current project","$0 link ~/ts-loader ~/jest"],["Register all workspaces from a remote project for use in the current project","$0 link ~/jest --all"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState({restoreResolutions:!1});let c=s.topLevelWorkspace,f=[];for(let p of this.destinations){let h=K.resolve(this.context.cwd,ue.toPortablePath(p)),E=await ze.find(h,this.context.plugins,{useRc:!1,strict:!1}),{project:C,workspace:S}=await Tt.find(E,h);if(s.cwd===C.cwd)throw new nt(`Invalid destination '${p}'; Can't link the project to itself`);if(!S)throw new ar(C.cwd,h);if(this.all){let P=!1;for(let I of C.workspaces)I.manifest.name&&(!I.manifest.private||this.private)&&(f.push(I),P=!0);if(!P)throw new nt(`No workspace found to be linked in the target project: ${p}`)}else{if(!S.manifest.name)throw new nt(`The target workspace at '${p}' doesn't have a name and thus cannot be linked`);if(S.manifest.private&&!this.private)throw new nt(`The target workspace at '${p}' is marked private - use the --private flag to link it anyway`);f.push(S)}}for(let p of f){let h=q.stringifyIdent(p.anchoredLocator),E=this.relative?K.relative(s.cwd,p.cwd):p.cwd;c.manifest.resolutions.push({pattern:{descriptor:{fullName:h}},reference:`portal:${E}`})}return await s.installWithNewReport({stdout:this.context.stdout},{cache:n})}};Wt();var DC=class extends ut{constructor(){super(...arguments);this.args=ge.Proxy()}static{this.paths=[["node"]]}static{this.usage=ot.Usage({description:"run node with the hook already setup",details:` + This command simply runs Node. It also makes sure to call it in a way that's compatible with the current project (for example, on PnP projects the environment will be setup in such a way that PnP will be correctly injected into the environment). + + The Node process will use the exact same version of Node as the one used to run Yarn itself, which might be a good way to ensure that your commands always use a consistent Node version. + `,examples:[["Run a Node script","$0 node ./my-script.js"]]})}async execute(){return this.cli.run(["exec","node",...this.args])}};Ve();Wt();var bC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["plugin","check"]]}static{this.usage=ot.Usage({category:"Plugin-related commands",description:"find all third-party plugins that differ from their own spec",details:` + Check only the plugins from https. + + If this command detects any plugin differences in the CI environment, it will throw an error. + `,examples:[["find all third-party plugins that differ from their own spec","$0 plugin check"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=await ze.findRcFiles(this.context.cwd);return(await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async n=>{for(let c of s)if(c.data?.plugins)for(let f of c.data.plugins){if(!f.checksum||!f.spec.match(/^https?:/))continue;let p=await An.get(f.spec,{configuration:r}),h=Nn.makeHash(p);if(f.checksum===h)continue;let E=he.pretty(r,f.path,he.Type.PATH),C=he.pretty(r,f.spec,he.Type.URL),S=`${E} is different from the file provided by ${C}`;n.reportJson({...f,newChecksum:h}),n.reportError(0,S)}})).exitCode()}};Ve();Ve();bt();Wt();var pBe=ye("os");Ve();bt();Wt();var cBe=ye("os");Ve();Bc();Wt();var pSt="https://raw.githubusercontent.com/yarnpkg/berry/master/plugins.yml";async function Dm(t,e){let r=await An.get(pSt,{configuration:t}),s=ls(r.toString());return Object.fromEntries(Object.entries(s).filter(([a,n])=>!e||Or.satisfiesWithPrereleases(e,n.range??"<4.0.0-rc.1")))}var PC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["plugin","list"]]}static{this.usage=ot.Usage({category:"Plugin-related commands",description:"list the available official plugins",details:"\n This command prints the plugins available directly from the Yarn repository. Only those plugins can be referenced by name in `yarn plugin import`.\n ",examples:[["List the official plugins","$0 plugin list"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);return(await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async a=>{let n=await Dm(r,un);for(let[c,{experimental:f,...p}]of Object.entries(n)){let h=c;f&&(h+=" [experimental]"),a.reportJson({name:c,experimental:f,...p}),a.reportInfo(null,h)}})).exitCode()}};var hSt=/^[0-9]+$/,gSt=process.platform==="win32";function uBe(t){return hSt.test(t)?`pull/${t}/head`:t}var dSt=({repository:t,branch:e},r)=>[["git","init",ue.fromPortablePath(r)],["git","remote","add","origin",t],["git","fetch","origin","--depth=1",uBe(e)],["git","reset","--hard","FETCH_HEAD"]],mSt=({branch:t})=>[["git","fetch","origin","--depth=1",uBe(t),"--force"],["git","reset","--hard","FETCH_HEAD"],["git","clean","-dfx","-e","packages/yarnpkg-cli/bundles"]],ySt=({plugins:t,noMinify:e},r,s)=>[["yarn","build:cli",...new Array().concat(...t.map(a=>["--plugin",K.resolve(s,a)])),...e?["--no-minify"]:[],"|"],[gSt?"move":"mv","packages/yarnpkg-cli/bundles/yarn.js",ue.fromPortablePath(r),"|"]],xC=class extends ut{constructor(){super(...arguments);this.installPath=ge.String("--path",{description:"The path where the repository should be cloned to"});this.repository=ge.String("--repository","https://github.com/yarnpkg/berry.git",{description:"The repository that should be cloned"});this.branch=ge.String("--branch","master",{description:"The branch of the repository that should be cloned"});this.plugins=ge.Array("--plugin",[],{description:"An array of additional plugins that should be included in the bundle"});this.dryRun=ge.Boolean("-n,--dry-run",!1,{description:"If set, the bundle will be built but not added to the project"});this.noMinify=ge.Boolean("--no-minify",!1,{description:"Build a bundle for development (debugging) - non-minified and non-mangled"});this.force=ge.Boolean("-f,--force",!1,{description:"Always clone the repository instead of trying to fetch the latest commits"});this.skipPlugins=ge.Boolean("--skip-plugins",!1,{description:"Skip updating the contrib plugins"})}static{this.paths=[["set","version","from","sources"]]}static{this.usage=ot.Usage({description:"build Yarn from master",details:` + This command will clone the Yarn repository into a temporary folder, then build it. The resulting bundle will then be copied into the local project. + + By default, it also updates all contrib plugins to the same commit the bundle is built from. This behavior can be disabled by using the \`--skip-plugins\` flag. + `,examples:[["Build Yarn from master","$0 set version from sources"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd),a=typeof this.installPath<"u"?K.resolve(this.context.cwd,ue.toPortablePath(this.installPath)):K.resolve(ue.toPortablePath((0,cBe.tmpdir)()),"yarnpkg-sources",Nn.makeHash(this.repository).slice(0,6));return(await Ot.start({configuration:r,stdout:this.context.stdout},async c=>{await M5(this,{configuration:r,report:c,target:a}),c.reportSeparator(),c.reportInfo(0,"Building a fresh bundle"),c.reportSeparator();let f=await Gr.execvp("git",["rev-parse","--short","HEAD"],{cwd:a,strict:!0}),p=K.join(a,`packages/yarnpkg-cli/bundles/yarn-${f.stdout.trim()}.js`);le.existsSync(p)||(await gS(ySt(this,p,a),{configuration:r,context:this.context,target:a}),c.reportSeparator());let h=await le.readFilePromise(p);if(!this.dryRun){let{bundleVersion:E}=await O5(r,null,async()=>h,{report:c});this.skipPlugins||await ESt(this,E,{project:s,report:c,target:a})}})).exitCode()}};async function gS(t,{configuration:e,context:r,target:s}){for(let[a,...n]of t){let c=n[n.length-1]==="|";if(c&&n.pop(),c)await Gr.pipevp(a,n,{cwd:s,stdin:r.stdin,stdout:r.stdout,stderr:r.stderr,strict:!0});else{r.stdout.write(`${he.pretty(e,` $ ${[a,...n].join(" ")}`,"grey")} +`);try{await Gr.execvp(a,n,{cwd:s,strict:!0})}catch(f){throw r.stdout.write(f.stdout||f.stack),f}}}}async function M5(t,{configuration:e,report:r,target:s}){let a=!1;if(!t.force&&le.existsSync(K.join(s,".git"))){r.reportInfo(0,"Fetching the latest commits"),r.reportSeparator();try{await gS(mSt(t),{configuration:e,context:t.context,target:s}),a=!0}catch{r.reportSeparator(),r.reportWarning(0,"Repository update failed; we'll try to regenerate it")}}a||(r.reportInfo(0,"Cloning the remote repository"),r.reportSeparator(),await le.removePromise(s),await le.mkdirPromise(s,{recursive:!0}),await gS(dSt(t,s),{configuration:e,context:t.context,target:s}))}async function ESt(t,e,{project:r,report:s,target:a}){let n=await Dm(r.configuration,e),c=new Set(Object.keys(n));for(let f of r.configuration.plugins.keys())c.has(f)&&await _5(f,t,{project:r,report:s,target:a})}Ve();Ve();bt();Wt();var fBe=et(fi()),ABe=ye("vm");var kC=class extends ut{constructor(){super(...arguments);this.name=ge.String();this.checksum=ge.Boolean("--checksum",!0,{description:"Whether to care if this plugin is modified"})}static{this.paths=[["plugin","import"]]}static{this.usage=ot.Usage({category:"Plugin-related commands",description:"download a plugin",details:` + This command downloads the specified plugin from its remote location and updates the configuration to reference it in further CLI invocations. + + Three types of plugin references are accepted: + + - If the plugin is stored within the Yarn repository, it can be referenced by name. + - Third-party plugins can be referenced directly through their public urls. + - Local plugins can be referenced by their path on the disk. + + If the \`--no-checksum\` option is set, Yarn will no longer care if the plugin is modified. + + Plugins cannot be downloaded from the npm registry, and aren't allowed to have dependencies (they need to be bundled into a single file, possibly thanks to the \`@yarnpkg/builder\` package). + `,examples:[['Download and activate the "@yarnpkg/plugin-exec" plugin',"$0 plugin import @yarnpkg/plugin-exec"],['Download and activate the "@yarnpkg/plugin-exec" plugin (shorthand)',"$0 plugin import exec"],["Download and activate a community plugin","$0 plugin import https://example.org/path/to/plugin.js"],["Activate a local plugin","$0 plugin import ./path/to/plugin.js"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);return(await Ot.start({configuration:r,stdout:this.context.stdout},async a=>{let{project:n}=await Tt.find(r,this.context.cwd),c,f;if(this.name.match(/^\.{0,2}[\\/]/)||ue.isAbsolute(this.name)){let p=K.resolve(this.context.cwd,ue.toPortablePath(this.name));a.reportInfo(0,`Reading ${he.pretty(r,p,he.Type.PATH)}`),c=K.relative(n.cwd,p),f=await le.readFilePromise(p)}else{let p;if(this.name.match(/^https?:/)){try{new URL(this.name)}catch{throw new Yt(52,`Plugin specifier "${this.name}" is neither a plugin name nor a valid url`)}c=this.name,p=this.name}else{let h=q.parseLocator(this.name.replace(/^((@yarnpkg\/)?plugin-)?/,"@yarnpkg/plugin-"));if(h.reference!=="unknown"&&!fBe.default.valid(h.reference))throw new Yt(0,"Official plugins only accept strict version references. Use an explicit URL if you wish to download them from another location.");let E=q.stringifyIdent(h),C=await Dm(r,un);if(!Object.hasOwn(C,E)){let S=`Couldn't find a plugin named ${q.prettyIdent(r,h)} on the remote registry. +`;throw r.plugins.has(E)?S+=`A plugin named ${q.prettyIdent(r,h)} is already installed; possibly attempting to import a built-in plugin.`:S+=`Note that only the plugins referenced on our website (${he.pretty(r,"https://github.com/yarnpkg/berry/blob/master/plugins.yml",he.Type.URL)}) can be referenced by their name; any other plugin will have to be referenced through its public url (for example ${he.pretty(r,"https://github.com/yarnpkg/berry/raw/master/packages/plugin-typescript/bin/%40yarnpkg/plugin-typescript.js",he.Type.URL)}).`,new Yt(51,S)}c=E,p=C[E].url,h.reference!=="unknown"?p=p.replace(/\/master\//,`/${E}/${h.reference}/`):un!==null&&(p=p.replace(/\/master\//,`/@yarnpkg/cli/${un}/`))}a.reportInfo(0,`Downloading ${he.pretty(r,p,"green")}`),f=await An.get(p,{configuration:r})}await U5(c,f,{checksum:this.checksum,project:n,report:a})})).exitCode()}};async function U5(t,e,{checksum:r=!0,project:s,report:a}){let{configuration:n}=s,c={},f={exports:c};(0,ABe.runInNewContext)(e.toString(),{module:f,exports:c});let h=`.yarn/plugins/${f.exports.name}.cjs`,E=K.resolve(s.cwd,h);a.reportInfo(0,`Saving the new plugin in ${he.pretty(n,h,"magenta")}`),await le.mkdirPromise(K.dirname(E),{recursive:!0}),await le.writeFilePromise(E,e);let C={path:h,spec:t};r&&(C.checksum=Nn.makeHash(e)),await ze.addPlugin(s.cwd,[C])}var ISt=({pluginName:t,noMinify:e},r)=>[["yarn",`build:${t}`,...e?["--no-minify"]:[],"|"]],QC=class extends ut{constructor(){super(...arguments);this.installPath=ge.String("--path",{description:"The path where the repository should be cloned to"});this.repository=ge.String("--repository","https://github.com/yarnpkg/berry.git",{description:"The repository that should be cloned"});this.branch=ge.String("--branch","master",{description:"The branch of the repository that should be cloned"});this.noMinify=ge.Boolean("--no-minify",!1,{description:"Build a plugin for development (debugging) - non-minified and non-mangled"});this.force=ge.Boolean("-f,--force",!1,{description:"Always clone the repository instead of trying to fetch the latest commits"});this.name=ge.String()}static{this.paths=[["plugin","import","from","sources"]]}static{this.usage=ot.Usage({category:"Plugin-related commands",description:"build a plugin from sources",details:` + This command clones the Yarn repository into a temporary folder, builds the specified contrib plugin and updates the configuration to reference it in further CLI invocations. + + The plugins can be referenced by their short name if sourced from the official Yarn repository. + `,examples:[['Build and activate the "@yarnpkg/plugin-exec" plugin',"$0 plugin import from sources @yarnpkg/plugin-exec"],['Build and activate the "@yarnpkg/plugin-exec" plugin (shorthand)',"$0 plugin import from sources exec"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=typeof this.installPath<"u"?K.resolve(this.context.cwd,ue.toPortablePath(this.installPath)):K.resolve(ue.toPortablePath((0,pBe.tmpdir)()),"yarnpkg-sources",Nn.makeHash(this.repository).slice(0,6));return(await Ot.start({configuration:r,stdout:this.context.stdout},async n=>{let{project:c}=await Tt.find(r,this.context.cwd),f=q.parseIdent(this.name.replace(/^((@yarnpkg\/)?plugin-)?/,"@yarnpkg/plugin-")),p=q.stringifyIdent(f),h=await Dm(r,un);if(!Object.hasOwn(h,p))throw new Yt(51,`Couldn't find a plugin named "${p}" on the remote registry. Note that only the plugins referenced on our website (https://github.com/yarnpkg/berry/blob/master/plugins.yml) can be built and imported from sources.`);let E=p;await M5(this,{configuration:r,report:n,target:s}),await _5(E,this,{project:c,report:n,target:s})})).exitCode()}};async function _5(t,{context:e,noMinify:r},{project:s,report:a,target:n}){let c=t.replace(/@yarnpkg\//,""),{configuration:f}=s;a.reportSeparator(),a.reportInfo(0,`Building a fresh ${c}`),a.reportSeparator(),await gS(ISt({pluginName:c,noMinify:r},n),{configuration:f,context:e,target:n}),a.reportSeparator();let p=K.resolve(n,`packages/${c}/bundles/${t}.js`),h=await le.readFilePromise(p);await U5(t,h,{project:s,report:a})}Ve();bt();Wt();var TC=class extends ut{constructor(){super(...arguments);this.name=ge.String()}static{this.paths=[["plugin","remove"]]}static{this.usage=ot.Usage({category:"Plugin-related commands",description:"remove a plugin",details:` + This command deletes the specified plugin from the .yarn/plugins folder and removes it from the configuration. + + **Note:** The plugins have to be referenced by their name property, which can be obtained using the \`yarn plugin runtime\` command. Shorthands are not allowed. + `,examples:[["Remove a plugin imported from the Yarn repository","$0 plugin remove @yarnpkg/plugin-typescript"],["Remove a plugin imported from a local file","$0 plugin remove my-local-plugin"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd);return(await Ot.start({configuration:r,stdout:this.context.stdout},async n=>{let c=this.name,f=q.parseIdent(c);if(!r.plugins.has(c))throw new nt(`${q.prettyIdent(r,f)} isn't referenced by the current configuration`);let p=`.yarn/plugins/${c}.cjs`,h=K.resolve(s.cwd,p);le.existsSync(h)&&(n.reportInfo(0,`Removing ${he.pretty(r,p,he.Type.PATH)}...`),await le.removePromise(h)),n.reportInfo(0,"Updating the configuration..."),await ze.updateConfiguration(s.cwd,{plugins:E=>{if(!Array.isArray(E))return E;let C=E.filter(S=>S.path!==p);return C.length===0?ze.deleteProperty:C.length===E.length?E:C}})})).exitCode()}};Ve();Wt();var RC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["plugin","runtime"]]}static{this.usage=ot.Usage({category:"Plugin-related commands",description:"list the active plugins",details:` + This command prints the currently active plugins. Will be displayed both builtin plugins and external plugins. + `,examples:[["List the currently active plugins","$0 plugin runtime"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins);return(await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async a=>{for(let n of r.plugins.keys()){let c=this.context.plugins.plugins.has(n),f=n;c&&(f+=" [builtin]"),a.reportJson({name:n,builtin:c}),a.reportInfo(null,`${f}`)}})).exitCode()}};Ve();Ve();Wt();var FC=class extends ut{constructor(){super(...arguments);this.idents=ge.Rest()}static{this.paths=[["rebuild"]]}static{this.usage=ot.Usage({description:"rebuild the project's native packages",details:` + This command will automatically cause Yarn to forget about previous compilations of the given packages and to run them again. + + Note that while Yarn forgets the compilation, the previous artifacts aren't erased from the filesystem and may affect the next builds (in good or bad). To avoid this, you may remove the .yarn/unplugged folder, or any other relevant location where packages might have been stored (Yarn may offer a way to do that automatically in the future). + + By default all packages will be rebuilt, but you can filter the list by specifying the names of the packages you want to clear from memory. + `,examples:[["Rebuild all packages","$0 rebuild"],["Rebuild fsevents only","$0 rebuild fsevents"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);let c=new Set;for(let f of this.idents)c.add(q.parseIdent(f).identHash);if(await s.restoreInstallState({restoreResolutions:!1}),await s.resolveEverything({cache:n,report:new Wi}),c.size>0)for(let f of s.storedPackages.values())c.has(f.identHash)&&(s.storedBuildState.delete(f.locatorHash),s.skippedBuilds.delete(f.locatorHash));else s.storedBuildState.clear(),s.skippedBuilds.clear();return await s.installWithNewReport({stdout:this.context.stdout,quiet:this.context.quiet},{cache:n})}};Ve();Ve();Ve();Wt();var H5=et(Sa());Ul();var NC=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("-A,--all",!1,{description:"Apply the operation to all workspaces from the current project"});this.mode=ge.String("--mode",{description:"Change what artifacts installs generate",validator:Ao(ec)});this.patterns=ge.Rest()}static{this.paths=[["remove"]]}static{this.usage=ot.Usage({description:"remove dependencies from the project",details:` + This command will remove the packages matching the specified patterns from the current workspace. + + If the \`--mode=\` option is set, Yarn will change which artifacts are generated. The modes currently supported are: + + - \`skip-build\` will not run the build scripts at all. Note that this is different from setting \`enableScripts\` to false because the latter will disable build scripts, and thus affect the content of the artifacts generated on disk, whereas the former will just disable the build step - but not the scripts themselves, which just won't run. + + - \`update-lockfile\` will skip the link step altogether, and only fetch packages that are missing from the lockfile (or that have no associated checksums). This mode is typically used by tools like Renovate or Dependabot to keep a lockfile up-to-date without incurring the full install cost. + + This command accepts glob patterns as arguments (if valid Idents and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them. + `,examples:[["Remove a dependency from the current project","$0 remove lodash"],["Remove a dependency from all workspaces at once","$0 remove lodash --all"],["Remove all dependencies starting with `eslint-`","$0 remove 'eslint-*'"],["Remove all dependencies with the `@babel` scope","$0 remove '@babel/*'"],["Remove all dependencies matching `react-dom` or `react-helmet`","$0 remove 'react-{dom,helmet}'"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState({restoreResolutions:!1});let c=this.all?s.workspaces:[a],f=["dependencies","devDependencies","peerDependencies"],p=[],h=!1,E=[];for(let I of this.patterns){let R=!1,N=q.parseIdent(I);for(let U of c){let W=[...U.manifest.peerDependenciesMeta.keys()];for(let te of(0,H5.default)(W,I))U.manifest.peerDependenciesMeta.delete(te),h=!0,R=!0;for(let te of f){let ie=U.manifest.getForScope(te),Ae=[...ie.values()].map(ce=>q.stringifyIdent(ce));for(let ce of(0,H5.default)(Ae,q.stringifyIdent(N))){let{identHash:me}=q.parseIdent(ce),pe=ie.get(me);if(typeof pe>"u")throw new Error("Assertion failed: Expected the descriptor to be registered");U.manifest[te].delete(me),E.push([U,te,pe]),h=!0,R=!0}}}R||p.push(I)}let C=p.length>1?"Patterns":"Pattern",S=p.length>1?"don't":"doesn't",P=this.all?"any":"this";if(p.length>0)throw new nt(`${C} ${he.prettyList(r,p,he.Type.CODE)} ${S} match any packages referenced by ${P} workspace`);return h?(await r.triggerMultipleHooks(I=>I.afterWorkspaceDependencyRemoval,E),await s.installWithNewReport({stdout:this.context.stdout},{cache:n,mode:this.mode})):0}};Ve();Ve();Wt();var hBe=ye("util"),OC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["run"]]}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);return(await Ot.start({configuration:r,stdout:this.context.stdout,json:this.json},async c=>{let f=a.manifest.scripts,p=je.sortMap(f.keys(),C=>C),h={breakLength:1/0,colors:r.get("enableColors"),maxArrayLength:2},E=p.reduce((C,S)=>Math.max(C,S.length),0);for(let[C,S]of f.entries())c.reportInfo(null,`${C.padEnd(E," ")} ${(0,hBe.inspect)(S,h)}`),c.reportJson({name:C,script:S})})).exitCode()}};Ve();Ve();Wt();var LC=class extends ut{constructor(){super(...arguments);this.inspect=ge.String("--inspect",!1,{tolerateBoolean:!0,description:"Forwarded to the underlying Node process when executing a binary"});this.inspectBrk=ge.String("--inspect-brk",!1,{tolerateBoolean:!0,description:"Forwarded to the underlying Node process when executing a binary"});this.topLevel=ge.Boolean("-T,--top-level",!1,{description:"Check the root workspace for scripts and/or binaries instead of the current one"});this.binariesOnly=ge.Boolean("-B,--binaries-only",!1,{description:"Ignore any user defined scripts and only check for binaries"});this.require=ge.String("--require",{description:"Forwarded to the underlying Node process when executing a binary"});this.silent=ge.Boolean("--silent",{hidden:!0});this.scriptName=ge.String();this.args=ge.Proxy()}static{this.paths=[["run"]]}static{this.usage=ot.Usage({description:"run a script defined in the package.json",details:` + This command will run a tool. The exact tool that will be executed will depend on the current state of your workspace: + + - If the \`scripts\` field from your local package.json contains a matching script name, its definition will get executed. + + - Otherwise, if one of the local workspace's dependencies exposes a binary with a matching name, this binary will get executed. + + - Otherwise, if the specified name contains a colon character and if one of the workspaces in the project contains exactly one script with a matching name, then this script will get executed. + + Whatever happens, the cwd of the spawned process will be the workspace that declares the script (which makes it possible to call commands cross-workspaces using the third syntax). + `,examples:[["Run the tests from the local workspace","$0 run test"],['Same thing, but without the "run" keyword',"$0 test"],["Inspect Webpack while running","$0 run --inspect-brk webpack"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a,locator:n}=await Tt.find(r,this.context.cwd);await s.restoreInstallState();let c=this.topLevel?s.topLevelWorkspace.anchoredLocator:n;if(!this.binariesOnly&&await In.hasPackageScript(c,this.scriptName,{project:s}))return await In.executePackageScript(c,this.scriptName,this.args,{project:s,stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr});let f=await In.getPackageAccessibleBinaries(c,{project:s});if(f.get(this.scriptName)){let h=[];return this.inspect&&(typeof this.inspect=="string"?h.push(`--inspect=${this.inspect}`):h.push("--inspect")),this.inspectBrk&&(typeof this.inspectBrk=="string"?h.push(`--inspect-brk=${this.inspectBrk}`):h.push("--inspect-brk")),this.require&&h.push(`--require=${this.require}`),await In.executePackageAccessibleBinary(c,this.scriptName,this.args,{cwd:this.context.cwd,project:s,stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr,nodeArgs:h,packageAccessibleBinaries:f})}if(!this.topLevel&&!this.binariesOnly&&a&&this.scriptName.includes(":")){let E=(await Promise.all(s.workspaces.map(async C=>C.manifest.scripts.has(this.scriptName)?C:null))).filter(C=>C!==null);if(E.length===1)return await In.executeWorkspaceScript(E[0],this.scriptName,this.args,{stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr})}if(this.topLevel)throw this.scriptName==="node-gyp"?new nt(`Couldn't find a script name "${this.scriptName}" in the top-level (used by ${q.prettyLocator(r,n)}). This typically happens because some package depends on "node-gyp" to build itself, but didn't list it in their dependencies. To fix that, please run "yarn add node-gyp" into your top-level workspace. You also can open an issue on the repository of the specified package to suggest them to use an optional peer dependency.`):new nt(`Couldn't find a script name "${this.scriptName}" in the top-level (used by ${q.prettyLocator(r,n)}).`);{if(this.scriptName==="global")throw new nt("The 'yarn global' commands have been removed in 2.x - consider using 'yarn dlx' or a third-party plugin instead");let h=[this.scriptName].concat(this.args);for(let[E,C]of tC)for(let S of C)if(h.length>=S.length&&JSON.stringify(h.slice(0,S.length))===JSON.stringify(S))throw new nt(`Couldn't find a script named "${this.scriptName}", but a matching command can be found in the ${E} plugin. You can install it with "yarn plugin import ${E}".`);throw new nt(`Couldn't find a script named "${this.scriptName}".`)}}};Ve();Ve();Wt();var MC=class extends ut{constructor(){super(...arguments);this.descriptor=ge.String();this.resolution=ge.String()}static{this.paths=[["set","resolution"]]}static{this.usage=ot.Usage({description:"enforce a package resolution",details:'\n This command updates the resolution table so that `descriptor` is resolved by `resolution`.\n\n Note that by default this command only affect the current resolution table - meaning that this "manual override" will disappear if you remove the lockfile, or if the package disappear from the table. If you wish to make the enforced resolution persist whatever happens, edit the `resolutions` field in your top-level manifest.\n\n Note that no attempt is made at validating that `resolution` is a valid resolution entry for `descriptor`.\n ',examples:[["Force all instances of lodash@npm:^1.2.3 to resolve to 1.5.0","$0 set resolution lodash@npm:^1.2.3 npm:1.5.0"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(await s.restoreInstallState({restoreResolutions:!1}),!a)throw new ar(s.cwd,this.context.cwd);let c=q.parseDescriptor(this.descriptor,!0),f=q.makeDescriptor(c,this.resolution);return s.storedDescriptors.set(c.descriptorHash,c),s.storedDescriptors.set(f.descriptorHash,f),s.resolutionAliases.set(c.descriptorHash,f.descriptorHash),await s.installWithNewReport({stdout:this.context.stdout},{cache:n})}};Ve();bt();Wt();var gBe=et(Sa()),_C=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("-A,--all",!1,{description:"Unlink all workspaces belonging to the target project from the current one"});this.leadingArguments=ge.Rest()}static{this.paths=[["unlink"]]}static{this.usage=ot.Usage({description:"disconnect the local project from another one",details:` + This command will remove any resolutions in the project-level manifest that would have been added via a yarn link with similar arguments. + `,examples:[["Unregister a remote workspace in the current project","$0 unlink ~/ts-loader"],["Unregister all workspaces from a remote project in the current project","$0 unlink ~/jest --all"],["Unregister all previously linked workspaces","$0 unlink --all"],["Unregister all workspaces matching a glob","$0 unlink '@babel/*' 'pkg-{a,b}'"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);let c=s.topLevelWorkspace,f=new Set;if(this.leadingArguments.length===0&&this.all)for(let{pattern:p,reference:h}of c.manifest.resolutions)h.startsWith("portal:")&&f.add(p.descriptor.fullName);if(this.leadingArguments.length>0)for(let p of this.leadingArguments){let h=K.resolve(this.context.cwd,ue.toPortablePath(p));if(je.isPathLike(p)){let E=await ze.find(h,this.context.plugins,{useRc:!1,strict:!1}),{project:C,workspace:S}=await Tt.find(E,h);if(!S)throw new ar(C.cwd,h);if(this.all){for(let P of C.workspaces)P.manifest.name&&f.add(q.stringifyIdent(P.anchoredLocator));if(f.size===0)throw new nt("No workspace found to be unlinked in the target project")}else{if(!S.manifest.name)throw new nt("The target workspace doesn't have a name and thus cannot be unlinked");f.add(q.stringifyIdent(S.anchoredLocator))}}else{let E=[...c.manifest.resolutions.map(({pattern:C})=>C.descriptor.fullName)];for(let C of(0,gBe.default)(E,p))f.add(C)}}return c.manifest.resolutions=c.manifest.resolutions.filter(({pattern:p})=>!f.has(p.descriptor.fullName)),await s.installWithNewReport({stdout:this.context.stdout,quiet:this.context.quiet},{cache:n})}};Ve();Ve();Ve();Wt();var dBe=et(lS()),j5=et(Sa());Ul();var UC=class extends ut{constructor(){super(...arguments);this.interactive=ge.Boolean("-i,--interactive",{description:"Offer various choices, depending on the detected upgrade paths"});this.fixed=ge.Boolean("-F,--fixed",!1,{description:"Store dependency tags as-is instead of resolving them"});this.exact=ge.Boolean("-E,--exact",!1,{description:"Don't use any semver modifier on the resolved range"});this.tilde=ge.Boolean("-T,--tilde",!1,{description:"Use the `~` semver modifier on the resolved range"});this.caret=ge.Boolean("-C,--caret",!1,{description:"Use the `^` semver modifier on the resolved range"});this.recursive=ge.Boolean("-R,--recursive",!1,{description:"Resolve again ALL resolutions for those packages"});this.mode=ge.String("--mode",{description:"Change what artifacts installs generate",validator:Ao(ec)});this.patterns=ge.Rest()}static{this.paths=[["up"]]}static{this.usage=ot.Usage({description:"upgrade dependencies across the project",details:"\n This command upgrades the packages matching the list of specified patterns to their latest available version across the whole project (regardless of whether they're part of `dependencies` or `devDependencies` - `peerDependencies` won't be affected). This is a project-wide command: all workspaces will be upgraded in the process.\n\n If `-R,--recursive` is set the command will change behavior and no other switch will be allowed. When operating under this mode `yarn up` will force all ranges matching the selected packages to be resolved again (often to the highest available versions) before being stored in the lockfile. It however won't touch your manifests anymore, so depending on your needs you might want to run both `yarn up` and `yarn up -R` to cover all bases.\n\n If `-i,--interactive` is set (or if the `preferInteractive` settings is toggled on) the command will offer various choices, depending on the detected upgrade paths. Some upgrades require this flag in order to resolve ambiguities.\n\n The, `-C,--caret`, `-E,--exact` and `-T,--tilde` options have the same meaning as in the `add` command (they change the modifier used when the range is missing or a tag, and are ignored when the range is explicitly set).\n\n If the `--mode=` option is set, Yarn will change which artifacts are generated. The modes currently supported are:\n\n - `skip-build` will not run the build scripts at all. Note that this is different from setting `enableScripts` to false because the latter will disable build scripts, and thus affect the content of the artifacts generated on disk, whereas the former will just disable the build step - but not the scripts themselves, which just won't run.\n\n - `update-lockfile` will skip the link step altogether, and only fetch packages that are missing from the lockfile (or that have no associated checksums). This mode is typically used by tools like Renovate or Dependabot to keep a lockfile up-to-date without incurring the full install cost.\n\n Generally you can see `yarn up` as a counterpart to what was `yarn upgrade --latest` in Yarn 1 (ie it ignores the ranges previously listed in your manifests), but unlike `yarn upgrade` which only upgraded dependencies in the current workspace, `yarn up` will upgrade all workspaces at the same time.\n\n This command accepts glob patterns as arguments (if valid Descriptors and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them.\n\n **Note:** The ranges have to be static, only the package scopes and names can contain glob patterns.\n ",examples:[["Upgrade all instances of lodash to the latest release","$0 up lodash"],["Upgrade all instances of lodash to the latest release, but ask confirmation for each","$0 up lodash -i"],["Upgrade all instances of lodash to 1.2.3","$0 up lodash@1.2.3"],["Upgrade all instances of packages with the `@babel` scope to the latest release","$0 up '@babel/*'"],["Upgrade all instances of packages containing the word `jest` to the latest release","$0 up '*jest*'"],["Upgrade all instances of packages with the `@babel` scope to 7.0.0","$0 up '@babel/*@7.0.0'"]]})}static{this.schema=[iB("recursive",Wf.Forbids,["interactive","exact","tilde","caret"],{ignore:[void 0,!1]})]}async execute(){return this.recursive?await this.executeUpRecursive():await this.executeUpClassic()}async executeUpRecursive(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState({restoreResolutions:!1});let c=[...s.storedDescriptors.values()],f=c.map(E=>q.stringifyIdent(E)),p=new Set;for(let E of this.patterns){if(q.parseDescriptor(E).range!=="unknown")throw new nt("Ranges aren't allowed when using --recursive");for(let C of(0,j5.default)(f,E)){let S=q.parseIdent(C);p.add(S.identHash)}}let h=c.filter(E=>p.has(E.identHash));for(let E of h)s.storedDescriptors.delete(E.descriptorHash),s.storedResolutions.delete(E.descriptorHash);return await s.installWithNewReport({stdout:this.context.stdout},{cache:n,mode:this.mode})}async executeUpClassic(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState({restoreResolutions:!1});let c=this.fixed,f=r.isInteractive({interactive:this.interactive,stdout:this.context.stdout}),p=uS(this,s),h=f?["keep","reuse","project","latest"]:["project","latest"],E=[],C=[];for(let N of this.patterns){let U=!1,W=q.parseDescriptor(N),te=q.stringifyIdent(W);for(let ie of s.workspaces)for(let Ae of["dependencies","devDependencies"]){let me=[...ie.manifest.getForScope(Ae).values()].map(Be=>q.stringifyIdent(Be)),pe=te==="*"?me:(0,j5.default)(me,te);for(let Be of pe){let Ce=q.parseIdent(Be),g=ie.manifest[Ae].get(Ce.identHash);if(typeof g>"u")throw new Error("Assertion failed: Expected the descriptor to be registered");let we=q.makeDescriptor(Ce,W.range);E.push(Promise.resolve().then(async()=>[ie,Ae,g,await fS(we,{project:s,workspace:ie,cache:n,target:Ae,fixed:c,modifier:p,strategies:h})])),U=!0}}U||C.push(N)}if(C.length>1)throw new nt(`Patterns ${he.prettyList(r,C,he.Type.CODE)} don't match any packages referenced by any workspace`);if(C.length>0)throw new nt(`Pattern ${he.prettyList(r,C,he.Type.CODE)} doesn't match any packages referenced by any workspace`);let S=await Promise.all(E),P=await uA.start({configuration:r,stdout:this.context.stdout,suggestInstall:!1},async N=>{for(let[,,U,{suggestions:W,rejections:te}]of S){let ie=W.filter(Ae=>Ae.descriptor!==null);if(ie.length===0){let[Ae]=te;if(typeof Ae>"u")throw new Error("Assertion failed: Expected an error to have been set");let ce=this.cli.error(Ae);s.configuration.get("enableNetwork")?N.reportError(27,`${q.prettyDescriptor(r,U)} can't be resolved to a satisfying range + +${ce}`):N.reportError(27,`${q.prettyDescriptor(r,U)} can't be resolved to a satisfying range (note: network resolution has been disabled) + +${ce}`)}else ie.length>1&&!f&&N.reportError(27,`${q.prettyDescriptor(r,U)} has multiple possible upgrade strategies; use -i to disambiguate manually`)}});if(P.hasErrors())return P.exitCode();let I=!1,R=[];for(let[N,U,,{suggestions:W}]of S){let te,ie=W.filter(pe=>pe.descriptor!==null),Ae=ie[0].descriptor,ce=ie.every(pe=>q.areDescriptorsEqual(pe.descriptor,Ae));ie.length===1||ce?te=Ae:(I=!0,{answer:te}=await(0,dBe.prompt)({type:"select",name:"answer",message:`Which range do you want to use in ${q.prettyWorkspace(r,N)} \u276F ${U}?`,choices:W.map(({descriptor:pe,name:Be,reason:Ce})=>pe?{name:Be,hint:Ce,descriptor:pe}:{name:Be,hint:Ce,disabled:!0}),onCancel:()=>process.exit(130),result(pe){return this.find(pe,"descriptor")},stdin:this.context.stdin,stdout:this.context.stdout}));let me=N.manifest[U].get(te.identHash);if(typeof me>"u")throw new Error("Assertion failed: This descriptor should have a matching entry");if(me.descriptorHash!==te.descriptorHash)N.manifest[U].set(te.identHash,te),R.push([N,U,me,te]);else{let pe=r.makeResolver(),Be={project:s,resolver:pe},Ce=r.normalizeDependency(me),g=pe.bindDescriptor(Ce,N.anchoredLocator,Be);s.forgetResolution(g)}}return await r.triggerMultipleHooks(N=>N.afterWorkspaceDependencyReplacement,R),I&&this.context.stdout.write(` +`),await s.installWithNewReport({stdout:this.context.stdout},{cache:n,mode:this.mode})}};Ve();Ve();Ve();Wt();var HC=class extends ut{constructor(){super(...arguments);this.recursive=ge.Boolean("-R,--recursive",!1,{description:"List, for each workspace, what are all the paths that lead to the dependency"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.peers=ge.Boolean("--peers",!1,{description:"Also print the peer dependencies that match the specified name"});this.package=ge.String()}static{this.paths=[["why"]]}static{this.usage=ot.Usage({description:"display the reason why a package is needed",details:` + This command prints the exact reasons why a package appears in the dependency tree. + + If \`-R,--recursive\` is set, the listing will go in depth and will list, for each workspaces, what are all the paths that lead to the dependency. Note that the display is somewhat optimized in that it will not print the package listing twice for a single package, so if you see a leaf named "Foo" when looking for "Bar", it means that "Foo" already got printed higher in the tree. + `,examples:[["Explain why lodash is used in your project","$0 why lodash"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState();let n=q.parseIdent(this.package).identHash,c=this.recursive?wSt(s,n,{configuration:r,peers:this.peers}):CSt(s,n,{configuration:r,peers:this.peers});ks.emitTree(c,{configuration:r,stdout:this.context.stdout,json:this.json,separators:1})}};function CSt(t,e,{configuration:r,peers:s}){let a=je.sortMap(t.storedPackages.values(),f=>q.stringifyLocator(f)),n={},c={children:n};for(let f of a){let p={};for(let E of f.dependencies.values()){if(!s&&f.peerDependencies.has(E.identHash))continue;let C=t.storedResolutions.get(E.descriptorHash);if(!C)throw new Error("Assertion failed: The resolution should have been registered");let S=t.storedPackages.get(C);if(!S)throw new Error("Assertion failed: The package should have been registered");if(S.identHash!==e)continue;{let I=q.stringifyLocator(f);n[I]={value:[f,he.Type.LOCATOR],children:p}}let P=q.stringifyLocator(S);p[P]={value:[{descriptor:E,locator:S},he.Type.DEPENDENT]}}}return c}function wSt(t,e,{configuration:r,peers:s}){let a=je.sortMap(t.workspaces,S=>q.stringifyLocator(S.anchoredLocator)),n=new Set,c=new Set,f=S=>{if(n.has(S.locatorHash))return c.has(S.locatorHash);if(n.add(S.locatorHash),S.identHash===e)return c.add(S.locatorHash),!0;let P=!1;S.identHash===e&&(P=!0);for(let I of S.dependencies.values()){if(!s&&S.peerDependencies.has(I.identHash))continue;let R=t.storedResolutions.get(I.descriptorHash);if(!R)throw new Error("Assertion failed: The resolution should have been registered");let N=t.storedPackages.get(R);if(!N)throw new Error("Assertion failed: The package should have been registered");f(N)&&(P=!0)}return P&&c.add(S.locatorHash),P};for(let S of a)f(S.anchoredPackage);let p=new Set,h={},E={children:h},C=(S,P,I)=>{if(!c.has(S.locatorHash))return;let R=I!==null?he.tuple(he.Type.DEPENDENT,{locator:S,descriptor:I}):he.tuple(he.Type.LOCATOR,S),N={},U={value:R,children:N},W=q.stringifyLocator(S);if(P[W]=U,!(I!==null&&t.tryWorkspaceByLocator(S))&&!p.has(S.locatorHash)){p.add(S.locatorHash);for(let te of S.dependencies.values()){if(!s&&S.peerDependencies.has(te.identHash))continue;let ie=t.storedResolutions.get(te.descriptorHash);if(!ie)throw new Error("Assertion failed: The resolution should have been registered");let Ae=t.storedPackages.get(ie);if(!Ae)throw new Error("Assertion failed: The package should have been registered");C(Ae,N,te)}}};for(let S of a)C(S.anchoredPackage,h,null);return E}Ve();var X5={};Vt(X5,{GitFetcher:()=>mS,GitResolver:()=>yS,default:()=>qSt,gitUtils:()=>Qa});Ve();bt();var Qa={};Vt(Qa,{TreeishProtocols:()=>dS,clone:()=>Z5,fetchBase:()=>MBe,fetchChangedFiles:()=>_Be,fetchChangedWorkspaces:()=>HSt,fetchRoot:()=>LBe,isGitUrl:()=>GC,lsRemote:()=>OBe,normalizeLocator:()=>USt,normalizeRepoUrl:()=>jC,resolveUrl:()=>z5,splitRepoUrl:()=>Y0,validateRepoUrl:()=>J5});Ve();bt();Wt();var RBe=et(kBe()),FBe=et(d6()),qC=et(ye("querystring")),V5=et(fi());function Y5(t,e,r){let s=t.indexOf(r);return t.lastIndexOf(e,s>-1?s:1/0)}function QBe(t){try{return new URL(t)}catch{return}}function MSt(t){let e=Y5(t,"@","#"),r=Y5(t,":","#");return r>e&&(t=`${t.slice(0,r)}/${t.slice(r+1)}`),Y5(t,":","#")===-1&&t.indexOf("//")===-1&&(t=`ssh://${t}`),t}function TBe(t){return QBe(t)||QBe(MSt(t))}function jC(t,{git:e=!1}={}){if(t=t.replace(/^git\+https:/,"https:"),t=t.replace(/^(?:github:|https:\/\/github\.com\/|git:\/\/github\.com\/)?(?!\.{1,2}\/)([a-zA-Z0-9._-]+)\/(?!\.{1,2}(?:#|$))([a-zA-Z0-9._-]+?)(?:\.git)?(#.*)?$/,"https://github.com/$1/$2.git$3"),t=t.replace(/^https:\/\/github\.com\/(?!\.{1,2}\/)([a-zA-Z0-9._-]+)\/(?!\.{1,2}(?:#|$))([a-zA-Z0-9._-]+?)\/tarball\/(.+)?$/,"https://github.com/$1/$2.git#$3"),e){let r=TBe(t);r&&(t=r.href),t=t.replace(/^git\+([^:]+):/,"$1:")}return t}function NBe(){return{...process.env,GIT_SSH_COMMAND:process.env.GIT_SSH_COMMAND||`${process.env.GIT_SSH||"ssh"} -o BatchMode=yes`}}var _St=[/^ssh:/,/^git(?:\+[^:]+)?:/,/^(?:git\+)?https?:[^#]+\/[^#]+(?:\.git)(?:#.*)?$/,/^git@[^#]+\/[^#]+\.git(?:#.*)?$/,/^(?:github:|https:\/\/github\.com\/)?(?!\.{1,2}\/)([a-zA-Z._0-9-]+)\/(?!\.{1,2}(?:#|$))([a-zA-Z._0-9-]+?)(?:\.git)?(?:#.*)?$/,/^https:\/\/github\.com\/(?!\.{1,2}\/)([a-zA-Z0-9._-]+)\/(?!\.{1,2}(?:#|$))([a-zA-Z0-9._-]+?)\/tarball\/(.+)?$/],dS=(a=>(a.Commit="commit",a.Head="head",a.Tag="tag",a.Semver="semver",a))(dS||{});function GC(t){return t?_St.some(e=>!!t.match(e)):!1}function Y0(t){t=jC(t);let e=t.indexOf("#");if(e===-1)return{repo:t,treeish:{protocol:"head",request:"HEAD"},extra:{}};let r=t.slice(0,e),s=t.slice(e+1);if(s.match(/^[a-z]+=/)){let a=qC.default.parse(s);for(let[p,h]of Object.entries(a))if(typeof h!="string")throw new Error(`Assertion failed: The ${p} parameter must be a literal string`);let n=Object.values(dS).find(p=>Object.hasOwn(a,p)),[c,f]=typeof n<"u"?[n,a[n]]:["head","HEAD"];for(let p of Object.values(dS))delete a[p];return{repo:r,treeish:{protocol:c,request:f},extra:a}}else{let a=s.indexOf(":"),[n,c]=a===-1?[null,s]:[s.slice(0,a),s.slice(a+1)];return{repo:r,treeish:{protocol:n,request:c},extra:{}}}}function USt(t){return q.makeLocator(t,jC(t.reference))}function J5(t,{configuration:e}){let r=jC(t,{git:!0});if(!An.getNetworkSettings(`https://${(0,RBe.default)(r).resource}`,{configuration:e}).enableNetwork)throw new Yt(80,`Request to '${r}' has been blocked because of your configuration settings`);return r}async function OBe(t,e){let r=J5(t,{configuration:e}),s=await K5("listing refs",["ls-remote",r],{cwd:e.startingCwd,env:NBe()},{configuration:e,normalizedRepoUrl:r}),a=new Map,n=/^([a-f0-9]{40})\t([^\n]+)/gm,c;for(;(c=n.exec(s.stdout))!==null;)a.set(c[2],c[1]);return a}async function z5(t,e){let{repo:r,treeish:{protocol:s,request:a},extra:n}=Y0(t),c=await OBe(r,e),f=(h,E)=>{switch(h){case"commit":{if(!E.match(/^[a-f0-9]{40}$/))throw new Error("Invalid commit hash");return qC.default.stringify({...n,commit:E})}case"head":{let C=c.get(E==="HEAD"?E:`refs/heads/${E}`);if(typeof C>"u")throw new Error(`Unknown head ("${E}")`);return qC.default.stringify({...n,commit:C})}case"tag":{let C=c.get(`refs/tags/${E}`);if(typeof C>"u")throw new Error(`Unknown tag ("${E}")`);return qC.default.stringify({...n,commit:C})}case"semver":{let C=Or.validRange(E);if(!C)throw new Error(`Invalid range ("${E}")`);let S=new Map([...c.entries()].filter(([I])=>I.startsWith("refs/tags/")).map(([I,R])=>[V5.default.parse(I.slice(10)),R]).filter(I=>I[0]!==null)),P=V5.default.maxSatisfying([...S.keys()],C);if(P===null)throw new Error(`No matching range ("${E}")`);return qC.default.stringify({...n,commit:S.get(P)})}case null:{let C;if((C=p("commit",E))!==null||(C=p("tag",E))!==null||(C=p("head",E))!==null)return C;throw E.match(/^[a-f0-9]+$/)?new Error(`Couldn't resolve "${E}" as either a commit, a tag, or a head - if a commit, use the 40-characters commit hash`):new Error(`Couldn't resolve "${E}" as either a commit, a tag, or a head`)}default:throw new Error(`Invalid Git resolution protocol ("${h}")`)}},p=(h,E)=>{try{return f(h,E)}catch{return null}};return jC(`${r}#${f(s,a)}`)}async function Z5(t,e){return await e.getLimit("cloneConcurrency")(async()=>{let{repo:r,treeish:{protocol:s,request:a}}=Y0(t);if(s!=="commit")throw new Error("Invalid treeish protocol when cloning");let n=J5(r,{configuration:e}),c=await le.mktempPromise(),f={cwd:c,env:NBe()};return await K5("cloning the repository",["clone","-c core.autocrlf=false",n,ue.fromPortablePath(c)],f,{configuration:e,normalizedRepoUrl:n}),await K5("switching branch",["checkout",`${a}`],f,{configuration:e,normalizedRepoUrl:n}),c})}async function LBe(t){let e,r=t;do{if(e=r,await le.existsPromise(K.join(e,".git")))return e;r=K.dirname(e)}while(r!==e);return null}async function MBe(t,{baseRefs:e}){if(e.length===0)throw new nt("Can't run this command with zero base refs specified.");let r=[];for(let f of e){let{code:p}=await Gr.execvp("git",["merge-base",f,"HEAD"],{cwd:t});p===0&&r.push(f)}if(r.length===0)throw new nt(`No ancestor could be found between any of HEAD and ${e.join(", ")}`);let{stdout:s}=await Gr.execvp("git",["merge-base","HEAD",...r],{cwd:t,strict:!0}),a=s.trim(),{stdout:n}=await Gr.execvp("git",["show","--quiet","--pretty=format:%s",a],{cwd:t,strict:!0}),c=n.trim();return{hash:a,title:c}}async function _Be(t,{base:e,project:r}){let s=je.buildIgnorePattern(r.configuration.get("changesetIgnorePatterns")),{stdout:a}=await Gr.execvp("git",["diff","--name-only",`${e}`],{cwd:t,strict:!0}),n=a.split(/\r\n|\r|\n/).filter(h=>h.length>0).map(h=>K.resolve(t,ue.toPortablePath(h))),{stdout:c}=await Gr.execvp("git",["ls-files","--others","--exclude-standard"],{cwd:t,strict:!0}),f=c.split(/\r\n|\r|\n/).filter(h=>h.length>0).map(h=>K.resolve(t,ue.toPortablePath(h))),p=[...new Set([...n,...f].sort())];return s?p.filter(h=>!K.relative(r.cwd,h).match(s)):p}async function HSt({ref:t,project:e}){if(e.configuration.projectCwd===null)throw new nt("This command can only be run from within a Yarn project");let r=[K.resolve(e.cwd,Er.lockfile),K.resolve(e.cwd,e.configuration.get("cacheFolder")),K.resolve(e.cwd,e.configuration.get("installStatePath")),K.resolve(e.cwd,e.configuration.get("virtualFolder"))];await e.configuration.triggerHook(c=>c.populateYarnPaths,e,c=>{c!=null&&r.push(c)});let s=await LBe(e.configuration.projectCwd);if(s==null)throw new nt("This command can only be run on Git repositories");let a=await MBe(s,{baseRefs:typeof t=="string"?[t]:e.configuration.get("changesetBaseRefs")}),n=await _Be(s,{base:a.hash,project:e});return new Set(je.mapAndFilter(n,c=>{let f=e.tryWorkspaceByFilePath(c);return f===null?je.mapAndFilter.skip:r.some(p=>c.startsWith(p))?je.mapAndFilter.skip:f}))}async function K5(t,e,r,{configuration:s,normalizedRepoUrl:a}){try{return await Gr.execvp("git",e,{...r,strict:!0})}catch(n){if(!(n instanceof Gr.ExecError))throw n;let c=n.reportExtra,f=n.stderr.toString();throw new Yt(1,`Failed ${t}`,p=>{p.reportError(1,` ${he.prettyField(s,{label:"Repository URL",value:he.tuple(he.Type.URL,a)})}`);for(let h of f.matchAll(/^(.+?): (.*)$/gm)){let[,E,C]=h;E=E.toLowerCase();let S=E==="error"?"Error":`${(0,FBe.default)(E)} Error`;p.reportError(1,` ${he.prettyField(s,{label:S,value:he.tuple(he.Type.NO_HINT,C)})}`)}c?.(p)})}}var mS=class{supports(e,r){return GC(e.reference)}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,a=new Map(r.checksums);a.set(e.locatorHash,s);let n={...r,checksums:a},c=await this.downloadHosted(e,n);if(c!==null)return c;let[f,p,h]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the remote repository`),loader:()=>this.cloneFromRemote(e,n),...r.cacheOptions});return{packageFs:f,releaseFs:p,prefixPath:q.getIdentVendorPath(e),checksum:h}}async downloadHosted(e,r){return r.project.configuration.reduceHook(s=>s.fetchHostedRepository,null,e,r)}async cloneFromRemote(e,r){let s=Y0(e.reference),a=await Z5(e.reference,r.project.configuration),n=K.resolve(a,s.extra.cwd??vt.dot),c=K.join(n,"package.tgz");await In.prepareExternalProject(n,c,{configuration:r.project.configuration,report:r.report,workspace:s.extra.workspace,locator:e});let f=await le.readFilePromise(c);return await je.releaseAfterUseAsync(async()=>await hs.convertToZip(f,{configuration:r.project.configuration,prefixPath:q.getIdentVendorPath(e),stripComponents:1}))}};Ve();Ve();var yS=class{supportsDescriptor(e,r){return GC(e.range)}supportsLocator(e,r){return GC(e.reference)}shouldPersistResolution(e,r){return!0}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){let a=await z5(e.range,s.project.configuration);return[q.makeLocator(e,a)]}async getSatisfying(e,r,s,a){let n=Y0(e.range);return{locators:s.filter(f=>{if(f.identHash!==e.identHash)return!1;let p=Y0(f.reference);return!(n.repo!==p.repo||n.treeish.protocol==="commit"&&n.treeish.request!==p.treeish.request)}),sorted:!1}}async resolve(e,r){if(!r.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let s=await r.fetchOptions.fetcher.fetch(e,r.fetchOptions),a=await je.releaseAfterUseAsync(async()=>await Ht.find(s.prefixPath,{baseFs:s.packageFs}),s.releaseFs);return{...e,version:a.version||"0.0.0",languageName:a.languageName||r.project.configuration.get("defaultLanguageName"),linkType:"HARD",conditions:a.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(a.dependencies),peerDependencies:a.peerDependencies,dependenciesMeta:a.dependenciesMeta,peerDependenciesMeta:a.peerDependenciesMeta,bin:a.bin}}};var jSt={configuration:{changesetBaseRefs:{description:"The base git refs that the current HEAD is compared against when detecting changes. Supports git branches, tags, and commits.",type:"STRING",isArray:!0,isNullable:!1,default:["master","origin/master","upstream/master","main","origin/main","upstream/main"]},changesetIgnorePatterns:{description:"Array of glob patterns; files matching them will be ignored when fetching the changed files",type:"STRING",default:[],isArray:!0},cloneConcurrency:{description:"Maximal number of concurrent clones",type:"NUMBER",default:2}},fetchers:[mS],resolvers:[yS]};var qSt=jSt;Wt();var WC=class extends ut{constructor(){super(...arguments);this.since=ge.String("--since",{description:"Only include workspaces that have been changed since the specified ref.",tolerateBoolean:!0});this.recursive=ge.Boolean("-R,--recursive",!1,{description:"Find packages via dependencies/devDependencies instead of using the workspaces field"});this.noPrivate=ge.Boolean("--no-private",{description:"Exclude workspaces that have the private field set to true"});this.verbose=ge.Boolean("-v,--verbose",!1,{description:"Also return the cross-dependencies between workspaces"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["workspaces","list"]]}static{this.usage=ot.Usage({category:"Workspace-related commands",description:"list all available workspaces",details:"\n This command will print the list of all workspaces in the project.\n\n - If `--since` is set, Yarn will only list workspaces that have been modified since the specified ref. By default Yarn will use the refs specified by the `changesetBaseRefs` configuration option.\n\n - If `-R,--recursive` is set, Yarn will find workspaces to run the command on by recursively evaluating `dependencies` and `devDependencies` fields, instead of looking at the `workspaces` fields.\n\n - If `--no-private` is set, Yarn will not list any workspaces that have the `private` field set to `true`.\n\n - If both the `-v,--verbose` and `--json` options are set, Yarn will also return the cross-dependencies between each workspaces (useful when you wish to automatically generate Buck / Bazel rules).\n "})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd);return(await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async n=>{let c=this.since?await Qa.fetchChangedWorkspaces({ref:this.since,project:s}):s.workspaces,f=new Set(c);if(this.recursive)for(let p of[...c].map(h=>h.getRecursiveWorkspaceDependents()))for(let h of p)f.add(h);for(let p of f){let{manifest:h}=p;if(h.private&&this.noPrivate)continue;let E;if(this.verbose){let C=new Set,S=new Set;for(let P of Ht.hardDependencies)for(let[I,R]of h.getForScope(P)){let N=s.tryWorkspaceByDescriptor(R);N===null?s.workspacesByIdent.has(I)&&S.add(R):C.add(N)}E={workspaceDependencies:Array.from(C).map(P=>P.relativeCwd),mismatchedWorkspaceDependencies:Array.from(S).map(P=>q.stringifyDescriptor(P))}}n.reportInfo(null,`${p.relativeCwd}`),n.reportJson({location:p.relativeCwd,name:h.name?q.stringifyIdent(h.name):null,...E})}})).exitCode()}};Ve();Ve();Wt();var YC=class extends ut{constructor(){super(...arguments);this.workspaceName=ge.String();this.commandName=ge.String();this.args=ge.Proxy()}static{this.paths=[["workspace"]]}static{this.usage=ot.Usage({category:"Workspace-related commands",description:"run a command within the specified workspace",details:` + This command will run a given sub-command on a single workspace. + `,examples:[["Add a package to a single workspace","yarn workspace components add -D react"],["Run build script on a single workspace","yarn workspace components run build"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);let n=s.workspaces,c=new Map(n.map(p=>[q.stringifyIdent(p.anchoredLocator),p])),f=c.get(this.workspaceName);if(f===void 0){let p=Array.from(c.keys()).sort();throw new nt(`Workspace '${this.workspaceName}' not found. Did you mean any of the following: + - ${p.join(` + - `)}?`)}return this.cli.run([this.commandName,...this.args],{cwd:f.cwd})}};var GSt={configuration:{enableImmutableInstalls:{description:"If true (the default on CI), prevents the install command from modifying the lockfile",type:"BOOLEAN",default:UBe.isCI},defaultSemverRangePrefix:{description:"The default save prefix: '^', '~' or ''",type:"STRING",values:["^","~",""],default:"^"},preferReuse:{description:"If true, `yarn add` will attempt to reuse the most common dependency range in other workspaces.",type:"BOOLEAN",default:!1}},commands:[cC,uC,fC,AC,MC,xC,CC,WC,gC,dC,mC,yC,aC,lC,pC,hC,EC,IC,wC,BC,vC,SC,_C,DC,bC,QC,kC,TC,PC,RC,FC,NC,OC,LC,UC,HC,YC]},WSt=GSt;var i9={};Vt(i9,{default:()=>VSt});Ve();var Qt={optional:!0},e9=[["@tailwindcss/aspect-ratio@<0.2.1",{peerDependencies:{tailwindcss:"^2.0.2"}}],["@tailwindcss/line-clamp@<0.2.1",{peerDependencies:{tailwindcss:"^2.0.2"}}],["@fullhuman/postcss-purgecss@3.1.3 || 3.1.3-alpha.0",{peerDependencies:{postcss:"^8.0.0"}}],["@samverschueren/stream-to-observable@<0.3.1",{peerDependenciesMeta:{rxjs:Qt,zenObservable:Qt}}],["any-observable@<0.5.1",{peerDependenciesMeta:{rxjs:Qt,zenObservable:Qt}}],["@pm2/agent@<1.0.4",{dependencies:{debug:"*"}}],["debug@<4.2.0",{peerDependenciesMeta:{"supports-color":Qt}}],["got@<11",{dependencies:{"@types/responselike":"^1.0.0","@types/keyv":"^3.1.1"}}],["cacheable-lookup@<4.1.2",{dependencies:{"@types/keyv":"^3.1.1"}}],["http-link-dataloader@*",{peerDependencies:{graphql:"^0.13.1 || ^14.0.0"}}],["typescript-language-server@*",{dependencies:{"vscode-jsonrpc":"^5.0.1","vscode-languageserver-protocol":"^3.15.0"}}],["postcss-syntax@*",{peerDependenciesMeta:{"postcss-html":Qt,"postcss-jsx":Qt,"postcss-less":Qt,"postcss-markdown":Qt,"postcss-scss":Qt}}],["jss-plugin-rule-value-function@<=10.1.1",{dependencies:{"tiny-warning":"^1.0.2"}}],["ink-select-input@<4.1.0",{peerDependencies:{react:"^16.8.2"}}],["license-webpack-plugin@<2.3.18",{peerDependenciesMeta:{webpack:Qt}}],["snowpack@>=3.3.0",{dependencies:{"node-gyp":"^7.1.0"}}],["promise-inflight@*",{peerDependenciesMeta:{bluebird:Qt}}],["reactcss@*",{peerDependencies:{react:"*"}}],["react-color@<=2.19.0",{peerDependencies:{react:"*"}}],["gatsby-plugin-i18n@*",{dependencies:{ramda:"^0.24.1"}}],["useragent@^2.0.0",{dependencies:{request:"^2.88.0",yamlparser:"0.0.x",semver:"5.5.x"}}],["@apollographql/apollo-tools@<=0.5.2",{peerDependencies:{graphql:"^14.2.1 || ^15.0.0"}}],["material-table@^2.0.0",{dependencies:{"@babel/runtime":"^7.11.2"}}],["@babel/parser@*",{dependencies:{"@babel/types":"^7.8.3"}}],["fork-ts-checker-webpack-plugin@<=6.3.4",{peerDependencies:{eslint:">= 6",typescript:">= 2.7",webpack:">= 4","vue-template-compiler":"*"},peerDependenciesMeta:{eslint:Qt,"vue-template-compiler":Qt}}],["rc-animate@<=3.1.1",{peerDependencies:{react:">=16.9.0","react-dom":">=16.9.0"}}],["react-bootstrap-table2-paginator@*",{dependencies:{classnames:"^2.2.6"}}],["react-draggable@<=4.4.3",{peerDependencies:{react:">= 16.3.0","react-dom":">= 16.3.0"}}],["apollo-upload-client@<14",{peerDependencies:{graphql:"14 - 15"}}],["react-instantsearch-core@<=6.7.0",{peerDependencies:{algoliasearch:">= 3.1 < 5"}}],["react-instantsearch-dom@<=6.7.0",{dependencies:{"react-fast-compare":"^3.0.0"}}],["ws@<7.2.1",{peerDependencies:{bufferutil:"^4.0.1","utf-8-validate":"^5.0.2"},peerDependenciesMeta:{bufferutil:Qt,"utf-8-validate":Qt}}],["react-portal@<4.2.2",{peerDependencies:{"react-dom":"^15.0.0-0 || ^16.0.0-0 || ^17.0.0-0"}}],["react-scripts@<=4.0.1",{peerDependencies:{react:"*"}}],["testcafe@<=1.10.1",{dependencies:{"@babel/plugin-transform-for-of":"^7.12.1","@babel/runtime":"^7.12.5"}}],["testcafe-legacy-api@<=4.2.0",{dependencies:{"testcafe-hammerhead":"^17.0.1","read-file-relative":"^1.2.0"}}],["@google-cloud/firestore@<=4.9.3",{dependencies:{protobufjs:"^6.8.6"}}],["gatsby-source-apiserver@*",{dependencies:{"babel-polyfill":"^6.26.0"}}],["@webpack-cli/package-utils@<=1.0.1-alpha.4",{dependencies:{"cross-spawn":"^7.0.3"}}],["gatsby-remark-prismjs@<3.3.28",{dependencies:{lodash:"^4"}}],["gatsby-plugin-favicon@*",{peerDependencies:{webpack:"*"}}],["gatsby-plugin-sharp@<=4.6.0-next.3",{dependencies:{debug:"^4.3.1"}}],["gatsby-react-router-scroll@<=5.6.0-next.0",{dependencies:{"prop-types":"^15.7.2"}}],["@rebass/forms@*",{dependencies:{"@styled-system/should-forward-prop":"^5.0.0"},peerDependencies:{react:"^16.8.6"}}],["rebass@*",{peerDependencies:{react:"^16.8.6"}}],["@ant-design/react-slick@<=0.28.3",{peerDependencies:{react:">=16.0.0"}}],["mqtt@<4.2.7",{dependencies:{duplexify:"^4.1.1"}}],["vue-cli-plugin-vuetify@<=2.0.3",{dependencies:{semver:"^6.3.0"},peerDependenciesMeta:{"sass-loader":Qt,"vuetify-loader":Qt}}],["vue-cli-plugin-vuetify@<=2.0.4",{dependencies:{"null-loader":"^3.0.0"}}],["vue-cli-plugin-vuetify@>=2.4.3",{peerDependencies:{vue:"*"}}],["@vuetify/cli-plugin-utils@<=0.0.4",{dependencies:{semver:"^6.3.0"},peerDependenciesMeta:{"sass-loader":Qt}}],["@vue/cli-plugin-typescript@<=5.0.0-alpha.0",{dependencies:{"babel-loader":"^8.1.0"}}],["@vue/cli-plugin-typescript@<=5.0.0-beta.0",{dependencies:{"@babel/core":"^7.12.16"},peerDependencies:{"vue-template-compiler":"^2.0.0"},peerDependenciesMeta:{"vue-template-compiler":Qt}}],["cordova-ios@<=6.3.0",{dependencies:{underscore:"^1.9.2"}}],["cordova-lib@<=10.0.1",{dependencies:{underscore:"^1.9.2"}}],["git-node-fs@*",{peerDependencies:{"js-git":"^0.7.8"},peerDependenciesMeta:{"js-git":Qt}}],["consolidate@<0.16.0",{peerDependencies:{mustache:"^3.0.0"},peerDependenciesMeta:{mustache:Qt}}],["consolidate@<=0.16.0",{peerDependencies:{velocityjs:"^2.0.1",tinyliquid:"^0.2.34","liquid-node":"^3.0.1",jade:"^1.11.0","then-jade":"*",dust:"^0.3.0","dustjs-helpers":"^1.7.4","dustjs-linkedin":"^2.7.5",swig:"^1.4.2","swig-templates":"^2.0.3","razor-tmpl":"^1.3.1",atpl:">=0.7.6",liquor:"^0.0.5",twig:"^1.15.2",ejs:"^3.1.5",eco:"^1.1.0-rc-3",jazz:"^0.0.18",jqtpl:"~1.1.0",hamljs:"^0.6.2",hamlet:"^0.3.3",whiskers:"^0.4.0","haml-coffee":"^1.14.1","hogan.js":"^3.0.2",templayed:">=0.2.3",handlebars:"^4.7.6",underscore:"^1.11.0",lodash:"^4.17.20",pug:"^3.0.0","then-pug":"*",qejs:"^3.0.5",walrus:"^0.10.1",mustache:"^4.0.1",just:"^0.1.8",ect:"^0.5.9",mote:"^0.2.0",toffee:"^0.3.6",dot:"^1.1.3","bracket-template":"^1.1.5",ractive:"^1.3.12",nunjucks:"^3.2.2",htmling:"^0.0.8","babel-core":"^6.26.3",plates:"~0.4.11","react-dom":"^16.13.1",react:"^16.13.1","arc-templates":"^0.5.3",vash:"^0.13.0",slm:"^2.0.0",marko:"^3.14.4",teacup:"^2.0.0","coffee-script":"^1.12.7",squirrelly:"^5.1.0",twing:"^5.0.2"},peerDependenciesMeta:{velocityjs:Qt,tinyliquid:Qt,"liquid-node":Qt,jade:Qt,"then-jade":Qt,dust:Qt,"dustjs-helpers":Qt,"dustjs-linkedin":Qt,swig:Qt,"swig-templates":Qt,"razor-tmpl":Qt,atpl:Qt,liquor:Qt,twig:Qt,ejs:Qt,eco:Qt,jazz:Qt,jqtpl:Qt,hamljs:Qt,hamlet:Qt,whiskers:Qt,"haml-coffee":Qt,"hogan.js":Qt,templayed:Qt,handlebars:Qt,underscore:Qt,lodash:Qt,pug:Qt,"then-pug":Qt,qejs:Qt,walrus:Qt,mustache:Qt,just:Qt,ect:Qt,mote:Qt,toffee:Qt,dot:Qt,"bracket-template":Qt,ractive:Qt,nunjucks:Qt,htmling:Qt,"babel-core":Qt,plates:Qt,"react-dom":Qt,react:Qt,"arc-templates":Qt,vash:Qt,slm:Qt,marko:Qt,teacup:Qt,"coffee-script":Qt,squirrelly:Qt,twing:Qt}}],["vue-loader@<=16.3.3",{peerDependencies:{"@vue/compiler-sfc":"^3.0.8",webpack:"^4.1.0 || ^5.0.0-0"},peerDependenciesMeta:{"@vue/compiler-sfc":Qt}}],["vue-loader@^16.7.0",{peerDependencies:{"@vue/compiler-sfc":"^3.0.8",vue:"^3.2.13"},peerDependenciesMeta:{"@vue/compiler-sfc":Qt,vue:Qt}}],["scss-parser@<=1.0.5",{dependencies:{lodash:"^4.17.21"}}],["query-ast@<1.0.5",{dependencies:{lodash:"^4.17.21"}}],["redux-thunk@<=2.3.0",{peerDependencies:{redux:"^4.0.0"}}],["skypack@<=0.3.2",{dependencies:{tar:"^6.1.0"}}],["@npmcli/metavuln-calculator@<2.0.0",{dependencies:{"json-parse-even-better-errors":"^2.3.1"}}],["bin-links@<2.3.0",{dependencies:{"mkdirp-infer-owner":"^1.0.2"}}],["rollup-plugin-polyfill-node@<=0.8.0",{peerDependencies:{rollup:"^1.20.0 || ^2.0.0"}}],["snowpack@<3.8.6",{dependencies:{"magic-string":"^0.25.7"}}],["elm-webpack-loader@*",{dependencies:{temp:"^0.9.4"}}],["winston-transport@<=4.4.0",{dependencies:{logform:"^2.2.0"}}],["jest-vue-preprocessor@*",{dependencies:{"@babel/core":"7.8.7","@babel/template":"7.8.6"},peerDependencies:{pug:"^2.0.4"},peerDependenciesMeta:{pug:Qt}}],["redux-persist@*",{peerDependencies:{react:">=16"},peerDependenciesMeta:{react:Qt}}],["sodium@>=3",{dependencies:{"node-gyp":"^3.8.0"}}],["babel-plugin-graphql-tag@<=3.1.0",{peerDependencies:{graphql:"^14.0.0 || ^15.0.0"}}],["@playwright/test@<=1.14.1",{dependencies:{"jest-matcher-utils":"^26.4.2"}}],...["babel-plugin-remove-graphql-queries@<3.14.0-next.1","babel-preset-gatsby-package@<1.14.0-next.1","create-gatsby@<1.14.0-next.1","gatsby-admin@<0.24.0-next.1","gatsby-cli@<3.14.0-next.1","gatsby-core-utils@<2.14.0-next.1","gatsby-design-tokens@<3.14.0-next.1","gatsby-legacy-polyfills@<1.14.0-next.1","gatsby-plugin-benchmark-reporting@<1.14.0-next.1","gatsby-plugin-graphql-config@<0.23.0-next.1","gatsby-plugin-image@<1.14.0-next.1","gatsby-plugin-mdx@<2.14.0-next.1","gatsby-plugin-netlify-cms@<5.14.0-next.1","gatsby-plugin-no-sourcemaps@<3.14.0-next.1","gatsby-plugin-page-creator@<3.14.0-next.1","gatsby-plugin-preact@<5.14.0-next.1","gatsby-plugin-preload-fonts@<2.14.0-next.1","gatsby-plugin-schema-snapshot@<2.14.0-next.1","gatsby-plugin-styletron@<6.14.0-next.1","gatsby-plugin-subfont@<3.14.0-next.1","gatsby-plugin-utils@<1.14.0-next.1","gatsby-recipes@<0.25.0-next.1","gatsby-source-shopify@<5.6.0-next.1","gatsby-source-wikipedia@<3.14.0-next.1","gatsby-transformer-screenshot@<3.14.0-next.1","gatsby-worker@<0.5.0-next.1"].map(t=>[t,{dependencies:{"@babel/runtime":"^7.14.8"}}]),["gatsby-core-utils@<2.14.0-next.1",{dependencies:{got:"8.3.2"}}],["gatsby-plugin-gatsby-cloud@<=3.1.0-next.0",{dependencies:{"gatsby-core-utils":"^2.13.0-next.0"}}],["gatsby-plugin-gatsby-cloud@<=3.2.0-next.1",{peerDependencies:{webpack:"*"}}],["babel-plugin-remove-graphql-queries@<=3.14.0-next.1",{dependencies:{"gatsby-core-utils":"^2.8.0-next.1"}}],["gatsby-plugin-netlify@3.13.0-next.1",{dependencies:{"gatsby-core-utils":"^2.13.0-next.0"}}],["clipanion-v3-codemod@<=0.2.0",{peerDependencies:{jscodeshift:"^0.11.0"}}],["react-live@*",{peerDependencies:{"react-dom":"*",react:"*"}}],["webpack@<4.44.1",{peerDependenciesMeta:{"webpack-cli":Qt,"webpack-command":Qt}}],["webpack@<5.0.0-beta.23",{peerDependenciesMeta:{"webpack-cli":Qt}}],["webpack-dev-server@<3.10.2",{peerDependenciesMeta:{"webpack-cli":Qt}}],["@docusaurus/responsive-loader@<1.5.0",{peerDependenciesMeta:{sharp:Qt,jimp:Qt}}],["eslint-module-utils@*",{peerDependenciesMeta:{"eslint-import-resolver-node":Qt,"eslint-import-resolver-typescript":Qt,"eslint-import-resolver-webpack":Qt,"@typescript-eslint/parser":Qt}}],["eslint-plugin-import@*",{peerDependenciesMeta:{"@typescript-eslint/parser":Qt}}],["critters-webpack-plugin@<3.0.2",{peerDependenciesMeta:{"html-webpack-plugin":Qt}}],["terser@<=5.10.0",{dependencies:{acorn:"^8.5.0"}}],["babel-preset-react-app@10.0.x <10.0.2",{dependencies:{"@babel/plugin-proposal-private-property-in-object":"^7.16.7"}}],["eslint-config-react-app@*",{peerDependenciesMeta:{typescript:Qt}}],["@vue/eslint-config-typescript@<11.0.0",{peerDependenciesMeta:{typescript:Qt}}],["unplugin-vue2-script-setup@<0.9.1",{peerDependencies:{"@vue/composition-api":"^1.4.3","@vue/runtime-dom":"^3.2.26"}}],["@cypress/snapshot@*",{dependencies:{debug:"^3.2.7"}}],["auto-relay@<=0.14.0",{peerDependencies:{"reflect-metadata":"^0.1.13"}}],["vue-template-babel-compiler@<1.2.0",{peerDependencies:{"vue-template-compiler":"^2.6.0"}}],["@parcel/transformer-image@<2.5.0",{peerDependencies:{"@parcel/core":"*"}}],["@parcel/transformer-js@<2.5.0",{peerDependencies:{"@parcel/core":"*"}}],["parcel@*",{peerDependenciesMeta:{"@parcel/core":Qt}}],["react-scripts@*",{peerDependencies:{eslint:"*"}}],["focus-trap-react@^8.0.0",{dependencies:{tabbable:"^5.3.2"}}],["react-rnd@<10.3.7",{peerDependencies:{react:">=16.3.0","react-dom":">=16.3.0"}}],["connect-mongo@<5.0.0",{peerDependencies:{"express-session":"^1.17.1"}}],["vue-i18n@<9",{peerDependencies:{vue:"^2"}}],["vue-router@<4",{peerDependencies:{vue:"^2"}}],["unified@<10",{dependencies:{"@types/unist":"^2.0.0"}}],["react-github-btn@<=1.3.0",{peerDependencies:{react:">=16.3.0"}}],["react-dev-utils@*",{peerDependencies:{typescript:">=2.7",webpack:">=4"},peerDependenciesMeta:{typescript:Qt}}],["@asyncapi/react-component@<=1.0.0-next.39",{peerDependencies:{react:">=16.8.0","react-dom":">=16.8.0"}}],["xo@*",{peerDependencies:{webpack:">=1.11.0"},peerDependenciesMeta:{webpack:Qt}}],["babel-plugin-remove-graphql-queries@<=4.20.0-next.0",{dependencies:{"@babel/types":"^7.15.4"}}],["gatsby-plugin-page-creator@<=4.20.0-next.1",{dependencies:{"fs-extra":"^10.1.0"}}],["gatsby-plugin-utils@<=3.14.0-next.1",{dependencies:{fastq:"^1.13.0"},peerDependencies:{graphql:"^15.0.0"}}],["gatsby-plugin-mdx@<3.1.0-next.1",{dependencies:{mkdirp:"^1.0.4"}}],["gatsby-plugin-mdx@^2",{peerDependencies:{gatsby:"^3.0.0-next"}}],["fdir@<=5.2.0",{peerDependencies:{picomatch:"2.x"},peerDependenciesMeta:{picomatch:Qt}}],["babel-plugin-transform-typescript-metadata@<=0.3.2",{peerDependencies:{"@babel/core":"^7","@babel/traverse":"^7"},peerDependenciesMeta:{"@babel/traverse":Qt}}],["graphql-compose@>=9.0.10",{peerDependencies:{graphql:"^14.2.0 || ^15.0.0 || ^16.0.0"}}],["vite-plugin-vuetify@<=1.0.2",{peerDependencies:{vue:"^3.0.0"}}],["webpack-plugin-vuetify@<=2.0.1",{peerDependencies:{vue:"^3.2.6"}}],["eslint-import-resolver-vite@<2.0.1",{dependencies:{debug:"^4.3.4",resolve:"^1.22.8"}}],["notistack@^3.0.0",{dependencies:{csstype:"^3.0.10"}}]];var t9;function HBe(){return typeof t9>"u"&&(t9=ye("zlib").brotliDecompressSync(Buffer.from("G7weAByFTVk3Vs7UfHhq4yykgEM7pbW7TI43SG2S5tvGrwHBAzdz+s/npQ6tgEvobvxisrPIadkXeUAJotBn5bDZ5kAhcRqsIHe3F75Walet5hNalwgFDtxb0BiDUjiUQkjG0yW2hto9HPgiCkm316d6bC0kST72YN7D7rfkhCE9x4J0XwB0yavalxpUu2t9xszHrmtwalOxT7VslsxWcB1qpqZwERUra4psWhTV8BgwWeizurec82Caf1ABL11YMfbf8FJ9JBceZOkgmvrQPbC9DUldX/yMbmX06UQluCEjSwUoyO+EZPIjofr+/oAZUck2enraRD+oWLlnlYnj8xB+gwSo9lmmks4fXv574qSqcWA6z21uYkzMu3EWj+K23RxeQlLqiE35/rC8GcS4CGkKHKKq+zAIQwD9iRDNfiAqueLLpicFFrNsAI4zeTD/eO9MHcnRa5m8UT+M2+V+AkFST4BlKneiAQRSdST8KEAIyFlULt6wa9EBd0Ds28VmpaxquJdVt+nwdEs5xUskI13OVtFyY0UrQIRAlCuvvWivvlSKQfTO+2Q8OyUR1W5RvetaPz4jD27hdtwHFFA1Ptx6Ee/t2cY2rg2G46M1pNDRf2pWhvpy8pqMnuI3++4OF3+7OFIWXGjh+o7Nr2jNvbiYcQdQS1h903/jVFgOpA0yJ78z+x759bFA0rq+6aY5qPB4FzS3oYoLupDUhD9nDz6F6H7hpnlMf18KNKDu4IKjTWwrAnY6MFQw1W6ymOALHlFyCZmQhldg1MQHaMVVQTVgDC60TfaBqG++Y8PEoFhN/PBTZT175KNP/BlHDYGOOBmnBdzqJKplZ/ljiVG0ZBzfqeBRrrUkn6rA54462SgiliKoYVnbeptMdXNfAuaupIEi0bApF10TlgHfmEJAPUVidRVFyDupSem5po5vErPqWKhKbUIp0LozpYsIKK57dM/HKr+nguF+7924IIWMICkQ8JUigs9D+W+c4LnNoRtPPKNRUiCYmP+Jfo2lfKCKw8qpraEeWU3uiNRO6zcyKQoXPR5htmzzLznke7b4YbXW3I1lIRzmgG02Udb58U+7TpwyN7XymCgH+wuPDthZVQvRZuEP+SnLtMicz9m5zASWOBiAcLmkuFlTKuHspSIhCBD0yUPKcxu81A+4YD78rA2vtwsUEday9WNyrShyrl60rWmA+SmbYZkQOwFJWArxRYYc5jGhA5ikxYw1rx3ei4NmeX/lKiwpZ9Ln1tV2Ae7sArvxuVLbJjqJRjW1vFXAyHpvLG+8MJ6T2Ubx5M2KDa2SN6vuIGxJ9WQM9Mk3Q7aCNiZONXllhqq24DmoLbQfW2rYWsOgHWjtOmIQMyMKdiHZDjoyIq5+U700nZ6odJAoYXPQBvFNiQ78d5jaXliBqLTJEqUCwi+LiH2mx92EmNKDsJL74Z613+3lf20pxkV1+erOrjj8pW00vsPaahKUM+05ssd5uwM7K482KWEf3TCwlg/o3e5ngto7qSMz7YteIgCsF1UOcsLk7F7MxWbvrPMY473ew0G+noVL8EPbkmEMftMSeL6HFub/zy+2JQ==","base64")).toString()),t9}var r9;function jBe(){return typeof r9>"u"&&(r9=ye("zlib").brotliDecompressSync(Buffer.from("G8MSIIzURnVBnObTcvb3XE6v2S9Qgc2K801Oa5otNKEtK8BINZNcaQHy+9/vf/WXBimwutXC33P2DPc64pps5rz7NGGWaOKNSPL4Y2KRE8twut2lFOIN+OXPtRmPMRhMTILib2bEQx43az2I5d3YS8Roa5UZpF/ujHb3Djd3GDvYUfvFYSUQ39vb2cmifp/rgB4J/65JK3wRBTvMBoNBmn3mbXC63/gbBkW/2IRPri0O8bcsRBsmarF328pAln04nyJFkwUAvNu934supAqLtyerZZpJ8I8suJHhf/ocMV+scKwa8NOiDKIPXw6Ex/EEZD6TEGaW8N5zvNHYF10l6Lfooj7D5W2k3dgvQSbp2Wv8TGOayS978gxlOLVjTGXs66ozewbrjwElLtyrYNnWTfzzdEutgROUFPVMhnMoy8EjJLLlWwIEoySxliim9kYW30JUHiPVyjt0iAw/ZpPmCbUCltYPnq6ZNblIKhTNhqS/oqC9iya5sGKZTOVsTEg34n92uZTf2iPpcZih8rPW8CzA+adIGmyCPcKdLMsBLShd+zuEbTrqpwuh+DLmracZcjPC5Sdf5odDAhKpFuOsQS67RT+1VgWWygSv3YwxDnylc04/PYuaMeIzhBkLrvs7e/OUzRTF56MmfY6rI63QtEjEQzq637zQqJ39nNhu3NmoRRhW/086bHGBUtx0PE0j3aEGvkdh9WJC8y8j8mqqke9/dQ5la+Q3ba4RlhvTbnfQhPDDab3tUifkjKuOsp13mXEmO00Mu88F/M67R7LXfoFDFLNtgCSWjWX+3Jn1371pJTK9xPBiMJafvDjtFyAzu8rxeQ0TKMQXNPs5xxiBOd+BRJP8KP88XPtJIbZKh/cdW8KvBUkpqKpGoiIaA32c3/JnQr4efXt85mXvidOvn/eU3Pase1typLYBalJ14mCso9h79nuMOuCa/kZAOkJHmTjP5RM2WNoPasZUAnT1TAE/NH25hUxcQv6hQWR/m1PKk4ooXMcM4SR1iYU3fUohvqk4RY2hbmTVVIXv6TvqO+0doOjgeVFAcom+RlwJQmOVH7pr1Q9LoJT6n1DeQEB+NHygsATbIwTcOKZlJsY8G4+suX1uQLjUWwLjjs0mvSvZcLTpIGAekeR7GCgl8eo3ndAqEe2XCav4huliHjdbIPBsGJuPX7lrO9HX1UbXRH5opOe1x6JsOSgHZR+EaxuXVhpLLxm6jk1LJtZfHSc6BKPun3CpYYVMJGwEUyk8MTGG0XL5MfEwaXpnc9TKnBmlGn6nHiGREc3ysn47XIBDzA+YvFdjZzVIEDcKGpS6PbUJehFRjEne8D0lVU1XuRtlgszq6pTNlQ/3MzNOEgCWPyTct22V2mEi2krizn5VDo9B19/X2DB3hCGRMM7ONbtnAcIx/OWB1u5uPbW1gsH8irXxT/IzG0PoXWYjhbMsH3KTuoOl5o17PulcgvsfTSnKFM354GWI8luqZnrswWjiXy3G+Vbyo1KMopFmmvBwNELgaS8z8dNZchx/Cl/xjddxhMcyqtzFyONb2Zdu90NkI8pAeufe7YlXrp53v8Dj/l8vWeVspRKBGXScBBPI/HinSTGmLDOGGOCIyH0JFdOZx0gWsacNlQLJMIrBhqRxXxHF/5pseWwejlAAvZ3klZSDSYY8mkToaWejXhgNomeGtx1DTLEUFMRkgF5yFB22WYdJnaWN14r1YJj81hGi45+jrADS5nYRhCiSlCJJ1nL8pYX+HDSMhdTEWyRcgHVp/IsUIZYMfT+YYncUQPgcxNGCHfZ88vDdrcUuaGIl6zhAsiaq7R5dfqrqXH/JcBhfjT8D0azayIyEz75Nxp6YkcyDxlJq3EXnJUpqDohJJOysL1t1uNiHESlvsxPb5cpbW0+ICZqJmUZus1BMW0F5IVBODLIo2zHHjA0=","base64")).toString()),r9}var n9;function qBe(){return typeof n9>"u"&&(n9=ye("zlib").brotliDecompressSync(Buffer.from("m9XmPqMRsZ7bFo1U5CxexdgYepcdMsrcAbbqv7/rCXGM7SZhmJ2jPScITf1tA+qxuDFE8KC9mQaCs84ftss/pB0UrlDfSS52Q7rXyYIcHbrGG2egYMqC8FFfnNfZVLU+4ZieJEVLu1qxY0MYkbD8opX7TYstjKzqxwBObq8HUIQwogljOgs72xyCrxj0q79cf/hN2Ys/0fU6gkRgxFedikACuQLS4lvO/N5NpZ85m+BdO3c5VplDLMcfEDt6umRCbfM16uxnqUKPvPFg/qtuzzId3SjAxZFoZRqK3pdtWt/C+VU6+zuX09NsoBs3MwobpU1yyoXZnzA1EmiMRS5GfJeLxV51/jSXrfgTWr1af9hwKvqCfSVHiQuk+uO/N16Cror2c1QlthM7WkS/86azhK3b47PG6f5TAJVtrK7g+zlR2boyKBV+QkdOXcfBDrI8yCciS3LktLb+d3gopE3R1QYFN1QWdQtrso2qK3+OTVYpTdPAfICTe9//3y/1+6mixIob4kfOI1WT3DxyD2ZuR06a6RPOPlftc/bZeqWqUtoqSetJlgP0AOBsOOeWqkpKJDtgP25CmIz+ZAo8+zwb3wI5ZD/0a7Qb7Q8Ag8HkWzhVQqzLFksA/nKSsR6hEu4tymzAQcZUDV4D2f17NbNSreHMVG0D1Knfa5n//prG6IzFVH7GSdEZn+1eEohVH5hmz6wxnj0biDxnMlq0fHQ2v7ogu8tEBnHaJICmVgLINf+jr4b/AVtDfPSZWelMen+u+pT60nu+9LrK0z0L/oyvC+kDtsi13AdC/i6pd29uB/1alOsA0Kc6N0wICwzbHkBQGJ94pBZ5TyKj7lzzUQ5CYn3Xp/cLhrJ2GpBakWmkymfeKcX2Vy2QEDcIxnju2369rf+l+H7E96GzyVs0gyDzUD0ipfKdmd7LN80sxjSiau/0PX2e7EMt4hNqThHEad9B1L44EDU1ZyFL+QJ0n1v7McxqupfO9zYGEBGJ0XxHdZmWuNKcV+0WJmzGd4y1qu3RfbunEBAQgZyBUWwjoXAwxk2XVRjBAy1jWcGsnb/Tu2oRKUbqGxHjFxUihoreyXW2M2ZnxkQYPfCorcVYq7rnrfuUV1ZYBNakboTPj+b+PLaIyFVsA5nmcP8ZS23WpTvTnSog5wfhixjwbRCqUZs5CmhOL9EgGmgj/26ysZ0jCMvtwDK2F7UktN2QnwoB1S1oLmpPmOrFf/CT8ITb/UkMLLqMjdVY/y/EH/MtrH9VkMaxM7mf8v/TkuD1ov5CqEgw9xvc/+8UXQ/+Idb2isH35w98+skf/i3b72L4ElozP8Dyc9wbdJcY70N/9F9PVz4uSI/nhcrSt21q/fpyf6UbWyso4Ds08/rSPGAcAJs8sBMCYualxyZxlLqfQnp9jYxdy/TQVs6vYmnTgEERAfmtB2No5xf8eqN4yCWgmnR91NQZQ4CmYCqijiU983mMTgUPedf8L8/XiCu9jbsDMIARuL0a0MZlq7lU2nxB8T+N/F7EFutvEuWhxf3XFlS0KcKMiAbpPy3gv/6r+NIQcVkdlqicBgiYOnzr6FjwJVz+QQxpM+uMAIW4F13oWQzNh95KZlI9LOFocgrLUo8g+i+ZNTor6ypk+7O/PlsJ9WsFhRgnLuNv5P2Isk25gqT6i2tMopOL1+RQcnRBuKZ06E8Ri4/BOrY/bQ4GAZPE+LXKsS5jTYjEl5jHNgnm+kjV9trqJ4C9pcDVxTWux8uovsXQUEYh9BP+NR07OqmcjOsakIEI/xofJioScCLW09tzJAVwZwgbQtVnkX3x8H1sI2y8Hs4AiQYfXRNklTmb9mn9RgbJl2yf19aSzCGZqFq79dXW791Na6an1ydMUb/LNp5HdEZkkmTAdP7EPMC563MSh6zxa+Bz5hMDuNq43JYIRJRIWCuNWvM1xTjf8XaHnVPKElBLyFDMJyWiSAElJ0FJVA++8CIBc8ItAWrxhecW+tOoGq4yReF6Dcz615ifhRWLpIOaf8WTs3zUcjEBS1JEXbIByQhm6+oAoTb3QPkok35qz9L2c/mp5WEuCJgerL5QCxMXUWHBJ80t+LevvZ65pBkFa72ITFw4oGQ05TynQJyDjU1AqBylBAdTE9uIflWo0b+xSUCJ9Ty3GlCggfasdT0PX/ue3w16GUfU+QVQddTm9XiY2Bckz2tKt2il7oUIGBRa7Ft5qJfrRIK3mVs9QsDo9higyTz0N9jmILeRhROdecjV44DDZzYnJNryISvfdIq2x4c2/8e2UXrlRm303TE6kxkQ/0kylxgtsQimZ/nb6jUaggIXXN+F2vyIqMGIuJXQR8yzdFIHknqeWFDgsdvcftmkZyWojcZc+ZFY4rua8nU3XuMNchfTDpBbrjMXsJGonJ+vKX0sZbNcoakrr9c9i+bj6uf6f4yNDdaiXLRhJrlh5zmfbkOGQkosfTqWYgpEKdYx2Kxfb+ZDz4Ufteybj63LzVc7oklSvXHh5Nab4+b8DeoXZihVLRZRCBJuj0J6zk3PtbkjaEH3sD3j6hHhwmufk+pBoGYd9qCJEFL21AmLzzHHktN9jW7GSpe1p91X10Bm5/Dhxo3BNex+EtiAFD3dTK0NcvT58F0IFIQIhgLP6s1MX8wofvtnPX1PQ/bLAwNP+ulKiokjXruRYKzTErNjFrvX5n6QD7oiRbOs3OQUswDgOxzcd+WwGZH1ONZJLEKk2T4VGPrrdkN9ncxP/oQ8UFvRbI7zGVrpNjlniCHT6nYmp7SlDcZ1XmS7tm9CXTMumh89LnaNuF3/wPVa/NLSE195Ntstwz1V2ZLc/sULMGaL4gdF3src9sR1Fh33/xiS3qOrJQlLpy2luR0/y+0q0RnVBBBe4yi4ueiNOdNAq/pR8JehYiEiu7YVJJcGBNBHlCOREQviO39dwxTxdulwW+UOO+OrXOskQ/csaLPIKxUOUHktlUtch/SkuaV5QD2G4vweAaCoSxMZ8k9jagIRR/irArsMUBBkvwQBZj1NYclQ1WtdeoYsd38CObL/DJksETohDEy6ZCixViSEPvNKiV1SSCwIiVk0dPGwTZxeNwPoA0BDhYNc4tIkej3DcTHVTS8W1vYFlURRUS4k2naQ5xI0fseTRBHJQ3WJ6Tn45afc9k9VffnLeTH+Kdd9X9Rnont4E39i8pr21YM+umrbIBTB8Ex2jNapeDYMPaeXACP6jpZnFy8NEyG2AF+Ega5vkvKIWjidXnkItArCkmeU63Fx+eg8KiP95JfLbUQus2hJTKPeGTz9b9A0TJtnTVcdJW15L/+3ZIOQ3jeoFsEuB9IGzxFY52ntO1vJvNdPQMJhXkvTNcRYz7Qz6l09rNUNGbfVNOW7tQgzdp42/0sZtnFW0+64nFJ127Niq3QLT8vwHYw3kOplK43u3yllVjU+RYv76vu3JMghXWGsSB0u3ESlir8CjF5ZIflzQoMn0xbP3qWknhPYHTAfu11TcndM/gV+npAK5/yKkwjnzWs5UXGXJHwAFo1FU99jtfiDBlqk9Xmq1YKsy7YkB5nOmw6dy9mjCqYT72Nz9S4+BsTCObdH/e/YZR3MzUt/j/sjQMujqJNOqABq9wAJCDwn/vwSbELgikVGYviA89VqCQjLBkWsMBf7qNjRT3hPXMbT+DM+fsTUEgPlFV5oq2qzdgZ6uAb0yK/szd/zKqTdSC0GlgQ//otU9TAFEtm4moY7QTBAIb2YdPBQAqhW1LevpeqAvf9tku0fT+IfpA8fDsqAOAQxGbPa0YLgAOIZRFlh3WHrFyBDcFLdrSJP+9Ikfv1V16ukcQt9i8sBbU/+m0SAUsjdTq6mtQfoeI7xPWpsP+1vTo73Rz8VnYLmgxaDWgOuNmD8+vxzpyCIC1upRk0+Wd7Z0smljU7G9IdJYlY5vyGTyzRkkN88RMEm9OKFJ4IHwBxzcQtMNeMUwwUATphdaafYwiPK8NptzFLY0dUIAFj2UVoHzUBmmTP1mWCmKvvesqnrG3hj+FHkfjO3nN+MaWXgorgAAA6K9IXTUD1+uwaqHXsEALRgD82K6GVuzjQznaC89QI2B34wNf1dPIwydDO38xCsAKCdf19/ePn1xejxPZgLmzLlTLvloYWMde1luC66/CFwUdwGF5iJ4QIAM5jvbl94r6EYr52H2W12SlcjAHBSzoVjusrp7UZh18Z/J+vwjQccSS/JBNE2b1adygAAyNgJ5P+bqz5+CPu24bqx6Gjcz84IAtVx2VEyBJTqrocOCI9I7r4vD7cz9L3AGZ6DBzEu36w6fQsAkN2IsmzCZWMxqbMTE75ymnyFiK09l327D2K9sywTANigkEkmLwTn4RqDiPxpy5HKA4aeYqbSoi0AUAKsGA5go3ZXjR0qpUsAoMWolyNxzyiIPZ+qsEM7QDgbHW9WJWwBADq5800tDEPPiPa6ialFj0uNAEDJEC4am4A/oPGPxmDmXdikl4cLKa8CgG7265rxY/wjtmbutfwJ6M9Mer8dKHyeZkalbAEA49jkE8MATNz+qKwsMOlGAEC+lkvGJh0ds/j5uNtg3tilTY+NTe/JnqF4N6uSDACAHKQP1Lht8vSzU7iEyzPjut2EPs/Y38IspIepXm+8s+bS2w8QPd+8ONuavlmV3gIAJLA8T+O2x6fBKOJyYweNq/YsVtd2SjETADgxiwkX4POo7fsmuHnc8rCP05hqlnABgBq023MivCisNnZRtK+sru0oXAIAK+fRHim5pkf85kL/YfPLQ/xReQkXAChjtR0XhfDJaiOHaB9ZXctR2AQARsyesDkUv0deoTWmffvT4f6SYAUA6+xXzrX3Smi6X8zthH22b/w19LM0XlWqr0rjAgAWs1Wq4T6AhPsAVGoEAAa5PpwVKjiHWlfJ2TZJf63FjF8SUG6KBOOL9A4PW3qOHE295pQyfVPIvxcJeU+CKduBk6Q+a2BAVtKhf4QnHrHLFpj6sNDUDvhCfNPmtn4pdDSUkHE1wPPrF1UvkQS/L1S52Zv0Sb/r9YK+jx51oWU+i39Owb1p4MDw3LcwvjpMvtDXPEWBlLcw4DNpOOC8f11nKez61/hc4txssbudIo5lL+aszAI1EiiSfkCetqOyBs4trCbou3jqJZ4diL4zvDnDBRgP+086X66Tvj3JOY1rJwmj/sJrubDrVb32PWhOs6BN+sJXQ+6nOZJTgPRg4PWz8sp/wWI3wsGBQoSU6tr0dWOkrwhDNCN5mfGAM5vfnawcoCdm2CdzIN0r72XbbDWqjom1cMjYh229sPnvzWLZAaSiQR3bSL1XjCwFH1wa4ZmmLeiaD4xutxAZfzu0FwMUkXTsvb7SX7TLM4zwjGg+HbjiaRWI92lgwaxTyKgiXbnThL9j7uBDihzuMULvXXes0e9x7PwRK+6mBLGD9z7PAt7b7va1J2EHu/zZfZ6JPoQVd849MZCk3RJOxd5Nsxi+O0lUD4Pochlk5+4naG1j6yiVRKBPobLOad//hDECeD1ORiB9M37JsSxMC6yAkKEdy7S1aRmXRGrLECneqByM8iQ8x6d71F1uhkYUi3WEjh/A9Yw//HCidh7pl7XD8vEkuN/f7XQ3+fhmSfR/9fHkNcRp4qCD13IGIBIAsQXtoDUnASJc+5H5f7YWufNDdZ3SiHJqVvKw8K1RNB/4mJi3YzQP47nmN2cw2BH4yKk+zk7wcLx2bVzeS773YW/7nMg8DMlWZGeYPJ8lYLzOnN4o/0fk9Fb9upq1yXbRyN7iDSRnOnj+kn3vLjHbn3NmA2tRwcfVd/KHGxPybUwcg9e742hY/XBtEgCQYe9Qh8t8fte6aEo1Lt7a9rryutsDxLxo0o9/lhdL/GMs9n3cCxZiuv3as0lchJm9dQGckDBOT/R+y2ft/W/eswB4NFnsqcrBTerQmx0BTPclttiZPF+ctHerFc2RW9MJzpuGOShqyTLCNsCjhPV3EtMF8nVQf2TL6GzI6EphQEjQgG6JrtMu/0zWg2e97o/uoTIf4ipUvVVM0KYey+VkMCWrFynVZh/hpTTXcm3+EV7yX7W6Ehrz8KON4P9MrENJx2msYomlnUT80OrH6Y1+KEfOWn8KyenbZuHQkjBZcDAx5+J64Aj6TSooLJw3anwLeZGOQeSSPXLe6dVY7MF7HhAl2HU9fwES3l2dLETAm5btht91AwjpdUoQghLn7RhAIRWFRVWJa2Jtc0Tm+dHRGiAvx6wG/OCGa7BsWuJ6U3LwfOzSY5qNsj3Qpt6+JyEhflEfl2YZ7jhjJ3y+3ehNh4IBG4eEmVuhYdlx/EQQvnVDqC5Lodj7NWEXjMFyT14tjF768alhticUJrdl3w6P7cKsF4rhxIKWxOSELDHpzaBPR0EgNZlKdZrSiJfPGaWK++nvRxwoo0gt4maZU1CAx33oq3e+NirCq8K514FHpLc0jbti5KzNlr3ttdqoSeYKrOsq+jS0w4q5Z2AMeYnbAgCra8oCHFF0wJ/PTdXUMVyIdTRhS8cJZVr5dTMliVhKm9/TZduaYLTA346l+ILCTo1es+CVq/f+2MU+XuX47AuupenBsoFCNMV/2ywHjCr2flEAWipfnI46tqmjq81ytF7IWoydKyHCSI4ew+k4+ATvUzq2buldaR6SAI4VKAMyMT7zkBkAMB00NLbwmtJqj2k7NAGAqHKufA41DAksWEk7A33esJTuBprShiAOZCMOdd72+E7b1umdzQCSOsdaB3BxZgCAIhUUSdbxYbW7MfnSRjQBAOeidlz5FgodFOhlNAn2jcFu6KmERUygbnHGMpnfdLZ+KTEVgF9WExaIcJy8hr/tp7Y+ofIvp0nKjrUMZqLMAMAsmaCWuxWW9dpVpoxoAgBXKtOVhyhPGCAhWFJty3Ija39F5udrAvbBC+QD+d2Qpx5Dhfh+FqLgzUW10AwAWChUQzuhruPOnJ3rUZXMdgmhZDvzdRCfX1UCN4/l/wPrk1X0qHN3KbpjTKBihdxy04nZgZFKr7EcDqvvSSpivzg7QGxmssgfLo5KZRV1TZtdbR+k3S/kYjTNfDUZyWrcFtxkiVhetaWfvcxumYBgVeSozNkvIgSbt+L/2Cl6TuiPToNFUi3gzvnWRxo0ES1a/Wjq0Zc47dikmBBXXE4/cj/BEnTUGU8vsXsssBsmrEbCzB27QqDQGPdcgFpmIb3VQSk9zfTyXFlADILp0V5qUnuHn2SAu8QszfXheW/UnD34sJXHTECWUYQhLc5QozwqlP1qnYO/j2pQmGU03C06s3d2EjlIdLNuy+Z0X9GIUUWCXDpwtAPYI/zXrF26ADyEpyyj5o5bn4GKoyNdkhskDGYenTTQ+fRqo0EL0yIqcAfyVOvo2jq3CjCRKOLgRzv8NZ30rd0sMLzpKrIwt866C8KrAes6AeYvDWFOdG2WjV8dNiG2wUyaYIU3T/cDo3COPFw8EPEFcIZAcCNE6BpH0CBPxefguDvpbTKPZF5TYE+uaLtxvaIUB3bIQI6/yK34JNzrQt1az5ucZEtXCMlBED4lW3rAfndm6l/kCGLzwMc1jaGqJo9VNR0VIO4dMQMAo+m4cpFwrKQXPzW3czk7Vehrc4bS6j+UCQBQhrljlDaOxR/+L+5R2jt6Tz+GWNGIJbKP1cd9mk9gzEk9hjdUxnNNvHTW4dOvtRS4MRoQDFpUwYuR+pe67JmTNfNtDqx7LG4zNLjh8a/7i6F+adgW4ci+DW1Ilf9ok+1zg/3+lfN6pK5X6QelSexeWGj2JnH1ym6sQa173zvfno297vUcHC6hAoTC/3enX+ej+9JNHu5RQubQD4++jHOK2fiK8Df3A4QC1LZSDmK46S0VdPvZ8VSJnWHbWlJDsshRGb3dyRkMr3d8VnqqBEcrMSKUyBqMsk6yUayfov2tM+rgwqxlrsiFu4pvawUNfFtcuWrc8FmGXzmz8Vn5LxfzeQoLfUX/JWNR9xC9tZZamjtBesX5eUAqtw7rpFfDcdbgXsMcsICLg6iqrNnoDTf4umgefPn5ZdXLAEaKmKr9K2jWq3EjfHsxMwBg48Ul4dwopQnV1GzvwQsXaQIAGfxz3b1L+LfNKAGAuxiMqmZyB+AYNU1XTRJXly88AYU39jt8cP2yet2jRRzcU6scgDEiEryUmuE0/9XcsZcfId18ZowZMT1Pn3IAxpBI9rrhhqfOkyl7L398ZNuIPH7ElH1o1LGcrV7PCOR1IzMAwAuoc0mYU0VR8SZmewtvuEATAGjx8Jyr7ndZRRabBAAakrqa1eFyutex5al/HR9+Pg/51BPSD406ljMQA8pRvJ9nBgCMQyre6J1RTDLuzPw1pAsbjcEeOqQ1rdTmu87PE3XTX6L5Gyznwp9PhH9fPkpGQ8UNREgtj619rgZb/3wPFNQVbHc/a4jvwl/8oBKYjqAA6N6ujHBoGb4ATrvhNBnDILjc0CJKnveWTCZsDPoCAtX87ot1zaqQIOzniFoY5+YhQw5B2c/phhnSAZA9ApFkx0IJ7sCLThlPpxnHyv9oR13WpgPR4gUqXIl2N4nXnTkJrp58Eu4njBlKzTOEZg8IxnUq8+sqOnQo9N2SE6jdRZ1z/fsQ3CJqNvCck7DRQdc3RveF/dc5mlOPI8T4uL+oz+Z8sJ9wZo/NELlDNct9N677yFvr2oYCQ3/83EfWnj06lnR27o268AYQhVTPo3RYYPpkhgyVUD50TQGcbIPBCGxagjGtFBjceJbYSX958r3v5q3JbgoA8LXamYl9ce+UOusgjorz1/LGw/LsWuxIqVZLUflBNNzqe8wfBnngUekITgge65Xj6xD8Ero1H/HAEgzxiww6j8ZB7I9hA4PQLxy2xTCSF3tJ/60ye1nRAiEhHZjEwgdaaD7HdmaDiTG4HD0ArtUhToud4pjcKlanIcEUD7j13JTtBA9u040VgeqfcMoXejWyk7YDcHR0TNJsYM2cyGylQEg654jKROckKeaXtByXo7DqAQhhd+e41CpRPIm6zoUBBU30L6veKGoHUvVujt12wrswKY0GCX7BAJ1ePs85euedVbtDdCFD6u6HVpjhIAJuyalS4D2EoUBc+OfKne64AHj8o92ql+v1XqI15bZv54pNU+xgh2zxoFup3vOQ40Jgk6wnrxfKqgVYJ8SCL5iRzYqxfYJEKQ6I4V7umobUg1tBdDZCI6wYso5GIsPj5aztuwBIib7SFoG3neHuUIkB0omw3HgYMqAVKWPKX3j0zEOeXOXa53uihs/cCwK2zTUdWfmdaBXGvP2ca3oubeEUEhTjUTjLD469sBTbSoNat4Q6NAHDoLn1d7TVHjJAmwfrggxygS3ojqv4siKiccTvzqizQ/sT37uxiPOJBH54kEryjipahqC4WYQ3Ztrduw39FZkaL80/Kl1M7mFa0VRxRoxS2hASYUpIdRLxT54CSsaACskZURcD6T7DueOjXevevtHYqtG2ZT+lHHVdNiMYIjJ4fu/nmbJp1zaOCONKPSKaP8J95Ije8V4Dnzyb3018HkdmaFbKBJDZMrXEB/VBy2mXVnq8WJSTK8CQuWPax3x8N3IdHtP+nKkRuXSj644Hnl38rAj9tk+2VVRuWRjNa1nsrvymeydN2VmUP4vo65rVvUozV8g+vFK0Pl3TTFjraGzjnpqnYj8fEn7y8xRGCb8o0PpJFDvkn5OOcISVLmQL98k0v89Y4snCvN8eEeM3lT34MjVzW2tBDx823AnRhLHF+wMcfn1USCfNH/y2+Nkmud//9f0xIbj11Zu5Zj4+4VjnVY/3brOKzwL+ejBmAOA47WPUljHF/2vcrorTjC9qauGcdjWqnl4Xqn61TABAfHiRvtpVT/BXt6udWv7G98iwegCujaC1eL1yhl59ATcUPRL3AaIOA+I5uupJcT1P8HWp2/hzT0Sgulz3jhhpRAGwRce+/k0LmNKMTfgx0HDnnYCoD4hwwcoVOwxDBCUhRKsQoCSRhCue2/9c9F4/djN/iU8vqQQAu2W7NleXuELigy7hrrH0ugYBzkBDFOm6hLH5gmTFDrY922J2jrjyFiDRWEKvovHJtvocMB+GdcfEc26nXAIxds31Zvyjgg9jDEkcu356cP45FQyWQ/2Xr9D3uuWTcP5rnCe2ZJ0E+rAzmSuB7q8l5kKexhJKIEgrqufzwt4z0Ma+6Z2Tc87Mxal5/108FsEkt5OMAUkkyPVYQvnEFI//BZi8mLGfYTCJKmKnPSOjj6PKKtrk9r4yTzXtIoLNfgCFXbO64O3y2dHOc0mB/cn4z5fkuA4VivPPReLcHVz8e0Cn05dLt14MyJdAU5yPV1oQSPcU194ylCH1I3Xt+oTMx7XGZgDuxpWddWvXNDuvgrl5OdL1SFnrVEM9U/0qfyz+6vo/VODmhzpDG/dFXZtJ7jTriHeSCKPhhLO5/uYBuSfw1POp6E8u60XdpKOROkyUcoWjqimnNyHhPDDdV1/7ND2Bh/7aiuxpFbYlYhwZNrk3v2ylTvyNsFmfuRontBwiqKx329Zob7jLYDIb9PrG+AWk4nN4QAF3naK32CroJjFK0dzBGBdbhqGvOwlO4Bqc2B+K8vMn9SgTYKOTXQpGthMF0aJQHsdrTiN+fG+eK6bKky6CiukeqBgoB0KYhl0ngc3MWhYQhR6ULDmmmrqvURCguRGH+xUW59GyJPI78e38CbKxEQpOnYlmZUheRl8+5Orw0KnDEZXpMdVzYEcr8V95gf54U3cS7adnQVQm9yAR5pkyblumE52RaVLbIouY4WxcNzoLJraAqsbN7CUaEyQRtqm83YVxgTXFBNPk2z9SfS/2mTSulgEfWUOYmQEfiAaWnX+P0ezKFz1BzO/T9SX4B8Sm7NUmDnbHI74izpe3Dq/k2jqvsxNBX7keI1eux798aA+Ee3pag6xpPDa7uIun6dXBDb9xrdpAFa1TYvlj/3iacVrXUYInG3OQv5lASKQr6Ok3CWTOFrkE3Ab4lFR8hbY0DZsgpiXw3Ic8YccFXomJeuZ+zNjq4CmlxYhcXQnrgtpWb2S+JXEp5JHh9APA4IjKN4hdm0qnHRzhSFfJCcOkg/RinGMzwtgNDahb4H/uNWjrIexsVRC9uYlMT3CCWCLeq12rSi3BlAQrnIAdFhL2INatBUy7ruc1TE+6eZ2XkZ/C6d6+CJrwouvF0ghjWDogxPbgxotmr56iGJoKnuwNF/VWHb037trPU+K8a9PCmGGWrqdiVkSOISAAc7D91xXG8Svq43DBvltxo/jeFylAbMWcCDXDm0rM6DbyRvFtLzAazwd/SPi1x5/NHyxHgX5VESDDn1tRHXzSlbjz2ulMvtv9Dp+Ic6KQZ3edNwa+9iZsx7kIwYF4aRfPuiAwhoYbkgvhVzlgwfF3Z5tX5KgmwkDs6AQdqyuZv1U3sFzdM7UxaJQ6JM5ELO+d+/k6PEylnYrwSOBlurpS2rECSHSp8S5Sbrm9jweZ44BxmkOBY4P5BmhH1PRRkCRcXYG91K0JRzOD/B1vQCcHf//8atBI/HuWuilLAbut+HwOMwBwqaIhe73RUkx4vCmUs4j6ALwz2cUa21NgLwszAYDj7hk5AvfEbG4HnKsavV0z2HZTPwBwNCiFQ3kIus/yxQ2assWZAi2zvyzAEU2C3XdnMwLHq7+vztaFd9UtqeZAqkKXkjoBs2vNdgByZS2cA1XNs70DCmO/0wQp1xWZZFWF8W3oy6uDaQnLF/YRxHk4rtJAAui5f4zymPhhpt+bgyGzSZdePfx3cSoXJIAuErW2pSJav7eSO0FL2bOd0eNgTenDatV0qcMQm4q085gBgJZgp6OlHCwNuT4pJjv46ZFji8t1ho8XaAIABIPsmTYL/HWV3harXQv7AQAWvtqIyuK3dJ+Cj9PGMb7K/JvB5xoGYzzTeucCQeXKMYa5Jh9EzhnyD3aGdQvU/FS1qMnjkPpyqtBQbX+HZgCANU1TteXcz9EMPZ0a78Xu1gxoX41fMf9Gx5SxOfgyF43WlePpTPS7KysCZeKjhxfH8OR2QZTGU8btjQNsDjEviJ5zZ659N/5Cs3tCTKjmg9XhwU2AieBC2CpJAc9MszqjvkvHbiHW4L7rMM9qMRXNBirYkwJvjoctYaKk80gNWxIUK2xDd1rykGGMhRq2glXBCIanrVbE4ctMSCncz7rDmN8J8+7xEr+37HpwPbbLV7DuIoUNODXiuNOYAYAdqqXg3NFSErZEqkops7NsF4dEt0pzJgBg3t6nyOT+ujWUO3o/HWboODheW/ZPjzH7Y2vJl5Vf1yz6cJxee134g1HHKtqNR06Yb1afnVoMAHh1fMz7KJmMuovLqpY/VRzDP+iqbrVar9VPSZxLCflzMZyzGDZ8juE3iuEfdIFWywg4UAxhvkt7H3Vz2Nmijfg10C3pDCGbW5HkGR033VTgXud+mVEqiPa0FRwBokdONicFMVWtN2cDyUBXkaaL5B06Dqt35stna5O88Hr68+Z+0vHQeOL7mZXCPby/RztHkz1eoTOcHLwcfGzDjP9lqtKlou5FzABAt+Kmy07cqDp8+QpF+lRyz702fCBvwQM5RRMAiMkiog3HhpH3/YCarpVzwsDVzQUBQNA83tWEAQVHZpGCKOs9UgWB0sS0CoJt+jEqKJxR4KigJF3udZC6mslAYLpqlIKwZZRLawYKHLe1OAacLM8+C5yT/b4tcDp1RVdidcVxOsa8Vfh2fiRZ4tPLrNuhQJAAyu8f42gdo2Z48/uSo/P29+J71n4oGiSAghLF0zoExPPe086JT6uNadoIQf+UfWOXtuWPNasWv/o8ZgCguhluxCuXg+UWd3uW2hGf5Yq3s0gTAMDia0wbFX5SKZfmYVwWGgQAHXyMEWXhV+k+Ar+tjd34iPkX4kOGQRqfp70XJHXkjm/sJ/ruOb4mSeuYnTfjCWFvoEcG4BwfnEtpFvRelrlGIum4+DYYBA7AtEQyHmxHxTHP/CVxmr/Sp7QXobUx4qP+rGJRXehvjg/uZD3fs2M5+cf7E5+fOPC8KOzGyYE0ZYwhuF0MBVh+MePAVk05a3djJn7kqrUyvLsOroqbM46Z+nM6JvdaGsEjVfwqoN2SfHc135EyJUq88XZEIX8I5nbsDEklYj4fVQqmNM/LjlmbbOv7O+qij/N1bqYrmUIugDHNlrEKYJjRKVYXlHSPdfyGYRC+RPqs64u/jo2ougiKUNbbpI+Db/x2xXsz0rs6VPAcqFgWBi/RYfXDhM5Ens0FyhIjELEM6DiViir7E6DJ9dNP4HqWVSnodz119e7ebZ8KbVAEGh++0g/ApiYn5VRNSkMFBkNiOgyUXPxXrPkCEEh32BdBNi3O8TCdjh1Kx36Mgtx2wdrve3T5Tblwg3Dy+gFH1Y8bEJ4Y8CpF3f2ifCSfFN4eSp3qgkZwRVzRWFGKT6KmfJbumRyGcIXhjcutiG3UCPipFIo5tES/QJQ4o5fA1zjdnptOZ6UTfGNOqVAk55iL3/7V9vAJgEzoLJTAOcpesyuSLJ9+IW+7q3ToWSR3w5Y1jIGVKSSunuyIIgcV81NlP/hsnTQRh8qFuSJCUR//D4NH89aIdvtqj5KNjOeCsW9jtsu+p9no9a8geJI1GJXPffb0anRpeUfz4mHRTMBWKl2PDpgKGxjEFyPzEZovmYVbBJqzI/RTaIuAbGwW7lIsDnvF2tLp7Hu1b3qfcsk+/G3PLnDBtaF3JHFxcZZjXgxceGu9ILgKdVl711k70N7xjW3vWAcAGE3Dl1+jmMZYWowjir3aY4c8NRZirPY0Ev1+E7PCsPpUUrFDWx5UL3Rodd/wKDQrtaeR5aVhbA3ILyE3ZJhjvRLYnEuAOyGwKzeB1SZsOJCWaGuT/p5rkM+b8QSzB+lVCEqxH0kxZyEM08yz5OVyjGpfkg0zhcnqroQ1mRg3mTReLxNIU9elAcNGtsPJ5lXSDFeEIunTdwmY2MhZ8LoROcH35TLh3OplkQ6JJnwA1CB9d6SN0ThG3scVgT6N+LHBf3cmMBRjqZn7XbXIGemgb/Xk8bt/mx5VZe42eAID680ptynUQBNR9Rf8HbSWhuPaSJA7qG83SvHE4ZU8OEZqIpGXZ2GlaMKbIbq4uiDYovInRvGODQYcpAO4zgeB4dnzqV7jSqHt230tB5CUBEsE9/4cJkpF0SBAh3k35zXTHvCenvz1Ud2TezFEu6rBNFZnsbQrAZqU7ErkypRSf6XKqPZigpk+a+0vsVaED2D3JhRNwxIY2pE+dvJNX6SJNv8AiFzDxFryAUsX4o48r+31f43Yzj4WI6eSDCeJu+GPFvJDu133wd1RnUutlzOH90ntQT/X7R/amKrLW7A0s7jEKi1VMJ5La3AvXzgwxMrp+bww7wFh1HKN3Xhvv+lKLFWQ4sUEOD0zd8CG7eucPfHjJI21YN1vyB1iSH3wVqtyGD321FZKYMEewOQgYKGh26SN3RxAK4uhux5ehCjaQ3GjyCMS4cIeECSG9Ami/Bv5lzzDc4SKixDRO7muxtyUi7xbSGtZIACJ1BYtKuVj8nKICZEkv6tAB0p5TtJpK/9/XVrKVqIC5Gn5Gl+0A2Rp6qk+LbeXn8lN20x2VCwnMxjORdqIQiITNmlKN5I4thKV3Ze3OPhGP46gumAIlPrjldf1dBKZVqhtblr7/oNQt+T9uE7exCNrEZu9oghu1pbzbmo/SpgGJQZbzXpocaLCH1LDy+GH68PkYGdP4CubBJyQ1g6E90ERC3NTSp0QBu/GHRqDgqyK3V2j9dxCEcVLFpXzSIB7on3SnT1kN8WtZr7ekIrjZi5f0VjZ7TRFA2LXcUfw+v714j3uPV07vb6V+Guqzup7wTfa5UOr6bDQ1T3NbY5CGPvUfib/szeX2BjA7h6u+ioHp1/cw2IrfMVok9S9Z7yhpsnxkOmq8Xo0MV1RmRf8bpBvDNH6cgLW961Vv5SeD4Jpn5HEoPWpbBq9Bpna680qtL7lTEt5D8J1k+uhkho8aCcB6XQ2X8v3eZNlMhvyPqR7PLF2hJCMfG8uj+rFeMWAK3akFPtO/o/VbnP2iGtkR7/rWe7ck92lDvk8q6oXiA3cZktHYFYSaLq/Wd2Evot7Yw3RHQToOu7B9UKkrATgIggmR6iaaXml2a1gHX2n548XA7GA0NQHEl1jZVE8ujv65YK5p+tg0LLvdzacpN/toxn+ebxUhZ9WrxYP/6fr9Dd/3jKT9qPcwb0ZHjwa/vmHOeZ72aED+8NvjT7aj4YMnL9DKEMLCLsQsf5EarQaDzcmTWgys8xKOyFBrbcOon9JCV+wNpa53kzxvzJ5O7bVGIgO402v5IAgHbO+6RUbSNbEWEGK5hXuh+Ctu9QahUtfNk/FnItXny1lltmcqOehqOIVT1blWCfzlpMrYeA2qZwB3KGKD+QmDdOALt20yVYVTB5tTj2+GmMDy7xkk08/ezZRHkiu8F0SYN6kOz01gIVGhx4PnxMBNNZ19oSmZ0G7FbhqlOWIIN2tq4hR3nQRsLN+eWFM6eCpGpYrQ5lDB1p4wKcLgCNRIbYX1syQAvEl1a7llGiQmb6ECq/7/nV3Xt89iAoMLWoQN9mTtC42bTObuALCdRI0FV310Ea36gJCuyQ4X4E50iOCXlEIKYZ45eU7UrnNCS17WqO8MCAmY/Yand6v9O4d4kmT7ZC6qk2ekv8GIkgTdUVpWwTWFjLkaZ6q9fkiCDJsYM825A3DCEUh5hZUZGJFNwjUOTlKo3HuGa4aRV7sQlx3cjhkPGRIchPPtePHjmm8Ip2DZR/q5o86FVBaF5Sk9XumrXpwRZPTIQ8bJxNId0kTDy1nEIPjmvYo3kUVH3D7CVqAmawsvm8JH2Z8KLO8/ycLE/DBQ4WvxhWo0Pph5K98UQLfVWZ/UytitHvuWl11gNnpSwBMZijoDMvuarjMIyi2buz2w3nFt2lpdsU17X3m7DfPdSAU9ozBqxNBx8mWf4WzrW5IfaqvHR+vH+6YsTi6rz0tLf4aYgt3gu05+/SiYYq5pqhILfws18fN2XL7xjVL8jw9EWjAFXcAuix8blRIvBCOgrr//dB0izhF6Q4oWfD+aK30NB7cqT/Opn3kXl2QFB4JyrpPrPt0JPzeIdIfbzbr/hE9plcxZZnOkVdFV/zSp8FxdslyWpjEPNJJXZ1ePgtW8Q+fbzcSjnd79KdsHHypr2ZwICYguSrAJJFHlydIA6Ttjc067yPgP6S3LV3rdJuwzy3VURPPHcEuBE9RKTDdFVjDOea4iMrycYG+WNjo2W4TIQg4t+3bQ0kjB2yZ4EE1MQaEyWQTd7kBeL8RFGoyLWXUR5C3g+NeYxfCxVsIvZVoBp9HFHTUJCbXacDeU4pAR7s52EfaGGusTdyg4bF2zu/jkG6jO2B4phg6J6GFn4PPaNgei5xBroUV92Oj5wuQfwYpJO3/plgv5Y0r80XSsnGEXuAWiWmZmY1lsQ8US4K1dYzPRcTy5Jlxw4fYlmKuVWTRbRMYKmuw1I33DmDEq1P8VP92Od4QKQnw9hFYWJPYbHR0xKSftb2WMjZ8tBAxQRPsko2tgFd8fyI6MCWnUbiNYeCpRs+YHAIoP5A+IMw7ilfD67stGzBQbPe0rkPkdzvafekGuhsTZkCc1If+8DSkV43eb9zvJrl1ePyIq5kn1iSK48mmVI5s6WKnHAb87PJYKWmHAK/LiVmO1GT1IDxFSZpp6kLIrQ7z8uqWdiM1+HzjCOwrqHqwKVQCrrOeaQZV3Cn2NWhvzqwXdibTusuLztkgAGUlBxHXhPHbYl7s4t/uGwwBytV2qw66lXlF+tFiQG8sAr/l2+r8X+oPmPxVda9IVEtMFPehuoD+szcvsVuBjanjPfYXvZ1sY08gp19W6SxEGa5MH9kyBEfRetwvbGSqFojHD2jSJn5jmQ3OFTtWNPaj6WgL4LGDmfRvLGMwm5o3lTJkx2kAkCf27T4iS0PfW7p0PeQeHjoPZ90eKsPWr9dxgOSg7PKMbAB5+v0/X3SUGA8BZjFKz+g1kLfK4vgHtHa9G7ODeBAEKJ7NZ+pZtitnlTsDdSbUu3PeQvYjt8EhRO0QBPg22kUkFv+JRStiXAXYTTqYAjjf+cCyqr7UJcxbMM371xP4jigI4Kub0l4rz7G2iqZkzSvv47XPVqmV/l/qyRaVUsyrWGaB8Foer1e7OepmcSpQxfAbod3dnOIX4z27UQXtQgJobSIkWYTYZkjCAP37uo9WcCNqL9w4NRW40ADhRMYBmRub96mtPmEO9KOezoayE3UFzDVvk8YxLZha/Bzt9LXEfY5sF/FVyV4e+iHBKpbaCoIB/I7Ntfnf+qFO6ZQlYjH5ecDmKYSk61/ngM7IN9BaZKepxqwDSNsMK7eQ/gnoyGTVPFcPQgoPz7GMBocsvBftsYYjogrg5iLJtK+2TCKSnAt8VEF6h8ypqi4A7HaAjqhK8eQZOfi9fjaw35vff2n6/3Hy5fs4iRuaT43Vwu+NN/BLTk6tyTyTsd6o3OFwet5g6ojRzhtMnS3peiBHGEcGtg2GVTrJWp2gIFIs5KPyrAophV8Onw+qo/HH+YrmB6vkPieGt7VPry2xQCKnJ+lVCQrgZd0AQMCqvBgQp+mYcCLJzoVtart15zDIVzi0momismLW61a7tTrqbvnlGgR2GxHMECE3111MlUkwFXYtx1vcYe3fbYFXXPoPAKAoMCf2s2xwctbtusDZ1cPHEXsrhg3/zviTN7gbp4AtQqyGI8COwAUt782BS/OxOwDrfsN2AABVtfQvvN+Hai79m45zarWdRnmo7b48HqADqqPphAJOcVWmE6TrpjEPAGAPOIiNuy1QkZ2ZPlALnj0c0LW8YUJQOzVQI7Hs7nij+oX37OGikkz/Wu24Xl39/yx0G2C/WP7edwTWwENB1ZgUIXWF4/F+Hr/JnytTZk0+iu+3VNsAqsF0OLj5/sh79nCxF2bkfPhkWvtMijpO7Xf5R9kf4nyPCXtlFsb3H7YCf10Rc171fYX4MvixfNsA9tosnsxd4BIi9GaGT9iv+W53tfpIK2XugXoVRKRQcdx53QCAj68BNFTUdcqnmZ0LqS3ukg5q5isckmNHUVkxdEhOiVRJXISuGBHtETFhrrvIs0ngCmrX4y0mW/s3YzC3S/8BgF4cqD32EwR0ZN2mDHppiwcL+sT+RgXMwSnAcSFsTduP80FQBb4rDv49Ge9DKs6aW2psI90rV4gcAt7Eced1AQDnKIrYj0f8uwKmfu8wMr+ex/at+DweCrbC59l7ZD2HUL4oysJnurkIaug40ygE01hSAAAwASJFtvhpiPUHId5mMwgZ6lpROiDZvVwHAFBCCGOLuZhnvWQqIkz3JdKaxm5xUzevRXZkZY2929k7imOvtveTwVj3lH3OvBEvfIB4tw9/pcogEIS51MV2nLx6pta2ufndi5N/XyuzHOp4tX07VU0OQJPa84WmSZDrrfWbtTcfv/T39LPko+c1rF7YEz9rM6U1rF96M59g9cktVllRpsCqYhx3PjcAsAqrGUXBMKXcZPANOTGTJeUMraxbO2swl+LlKxzaRURxdsUEzquwS5GzJE5olHIeIgAQaVnLCVY9BRMda0k5d/1pC0gNvOwfANA6kA2xHyfxZ0FOob30iIXKxTmcqD8XxRNkr+jI0nuOA5Q5l/Jq2URemRf4ru8IkTdlT1JNaolgiwm6GXecj6Cx55gVt7BVgStP9CpJzZzxZDKMpraMBPF149VfuDk5W+JGpq7KhshgFoHBMTY8t4SruiUqOBuCgtuPmODsnl5BFd3SdTQ73pZ8fnYEBJfWAo1wYJhoYDrBwFRigU2n1YOJBAYIBC6Vl740850tyXxjgoDL/nFsp8JEAHMIANYhIQCe+XZ6Ki4wtj9z4s37J596qh8oJuSRpUTYdqvLqsl1IUNgMbGRMMVQqerjwIoOBIvhvCkAwLkOnN3usRMeBy7stGOP+bpL3ptAVFwl49CpoGt7WR4AcBwjboIWbqo65luDaW/ux0yvmj+YTumfhIntczgdVuwSmAxrg0FquqAGm9CpGElDj+MzoaBJj1s1e8vq2PD8Ub2HA5/0xTXL6K5pu/r9MM/tLnWJod96/hO400WAK2z3904HZ8b1HBMZXTWZkKNVzTR4IrD65o26AQALhQp4AbG8mTGwc8Xd5VXAeQsBSI0FsgDUVRK44G+FVjUhAgAtQ+sCJ9jUbPh1vDfcvcq/u15rNNB14z8A4DLk6XV+vLY4F6t5HHCxBfFN67IRXJ6mvw0U11QrpXisIL3DrfdWpyz1CcoU42Cq6+fWA06z7mHXSHJldz1Bkhc25j3eTjWa2gGAlJE0ZPmG5u00UW83EtQFOSsNCaSuMQ8AcA48R8Oh45ZVgdmyMih2uCIF5pZlo6wCC7EG1KjAVndAsbwg4+KWFd314aQ4TlpwPkNrbKkHhuodKaKYFRv6GbIfc/DTIS/9MrZTgbEBVOVonNhbndOIfBT6ofxW+ho/Rk89QuxZWDnKVkL8bABfj2PvaSj90uinomMD2POweJQ+Be/a1Cs42xFUIjL6yvFiE2NViUHkDnHced0AwLTOPzTImzsFZKTtprPxkryFUOjqikroqCpQTJVErdB9TYgAQEPQ4oYTrGru8jzeG2ZV+zfX4LSW/gMAWhl0k/3EBfraag4BBtTFkzBTRYeW3rOkWslLmQW+pPdhq706C5QyfZhgboceEvIzWO9lEqQ/ZO9xT/HNeinsY643vp+BGEBexdfzbQAABp/qaNw2vRWCquO3vPmnlM4CUVXQ3ZaB1pHCzA0IZ/H5u0IIma4MsYIQth1nEYuQ0CoWEwAA0w7bVYgUzJcJKp0cm5hka1dmMgCz4uQadgCA2UKsWExpLWFdNnMDYE1LvDGwFmySEogbcIxKHHj06/lwe8wpUMf+TymTqZT6cQlfVbGD4QS7nmACn+6OoP3enWfJG24ruwwvWxvb68HL+c16gt2TNasMXmaRIQBw0wgS+ynUJluos5PourUM3SwnJ0+i6Jh8vnMBH/+0qCq7K1ACAtXukEDFAHoaEAEAAARd7lPLiAJJU3vVf9PRNLE6vfgfABhAc5D5sxXKqv6W3tzG39LG2/hb36bb5EtKrTsBavpEC4MXLK+L+eAi1n/VrN8H+SC7f/79K/05bxVuEMRc/u+Ca6A8krSyN+q8ZhSj3vrcZL3BMXZZjEh+4pkDr12cFHsL/559wPd/sIUbHivH/4Z5/tj48SgOcLjTe8v3zOSy2/2M/gD9GkMWsVtTdyTVvg+3W6uwXhxk1FmId6QMP/uZeku8OJb5sRrrttOGRRDG+lpD88P7L10woNhld50dJssC2L3OGDzF47ApDuFpTp8CAII2lRzF8nnl43Csejuv2TTXrZuiCoipt3LVOC0PABikV4MhsqosnJsXcqNaGTOB3Fwn21xB7shpsLqgtLcrKqoQbBdOMXxwF9rGKrzKaemo3h+DlyEn+EL3F9zk7rf19d/HjKBNRb3EHooiBcy33plc/Tq+s+a6zu92p3tcZQgAjDX4ErKRamcBDryZOGA15vzu1LqhQJ9MYfDu3aUOAXV1EvABnDIihDlXeK67OE1OtL0glpV/vEGwZDDsxn8AYCRou9f8WQRwqr+tN5f4C228xF9cW+ZKN5RiEvjuRGUEldYn6Vt6kYQpp0tCIGG2M1CioNRuuxtMQ+kqZyxYIdOdZe0AQFgFBdiWL2IhA6bbLuIhJbK0klBFVWCVpjwAgOXhVVVBBTZuakC27IxTIAme7VmQXt6QEkijCio1Ltwj4zaUKHzkPcM5RXxjvU0t/cBQqSFFqKKiiIIb/jhTMe8lrqmdy2oNoAJD4wToKYbsWyW9Ofg7we/ImDz9CLE/XaFI8Oi10pejA7vfHCY/l9oawP52tWFpigZrOPMgp/nE2huTszl7klaVCKxzoloEDgCk2x8faoc3NwRE0HbZXL8sZyH17dVYFBuoUp1EWUDHRgR6xv+f6y66tlSUkduLpmZr/6Z3ZEMdTFfjPwAwIDTXNH+2QtTUn9Ob2/hb2ngbf+vadq70glDzAu6AcGy/akkqsE1/TKEItTbUb1F8oT/nBx9PzPQmWmTCtfG1dm8LcVdwF5g4UxQft+VK5Nvoj208DiQ8dQu3/atIawDmRPJ43jNDVrWAFTJ0OAJEYJGQzpeDGKkybTYd5mukPmldavVcjb4/dyfi/gLd/Ozoq0tIKBWjJy2eLim1ITyuoX2Edm7GMqOichceVrfRhypP98e5uOAaIt1SMlMZ2IhIq6e3SphC+I/h0nbG27Ai2dMU2mYYBoNsoANzwdjT0gvkUj0hNRpsDGuJBYmO1C7D5OPki6qP4mLe/obk8oiOTLSuUWjYBtLtYyCHeyA5Tw3tYSJItv1hitwsHaSGHT2dNhvkLxqYUw9Hu7C9CIQD18omTNkPwc1IQXEGbuS07nkzR6JsqXjCoNSB/tnqWkLsaDcUAmA8z86JiEM/Ni+SODFvBxi1gEAWZHLIlnoB1VkBkOBrf239cXXlpVD8c2NFej6ddl8uARiyiGrmQ9Hka+APe1xY9NRUTfwzLfv6FcD5A6WEtXxtbID+ymrVY9/J4iwNREZjukGdhjkX8hGsswGUWk7vnC9l7ibCX6ASP04eueRlIMD4qCzdpyeVoe+2oS3Uyi7xW4CtNYNLneV35GHLjDUvqWAwFviZPsYXKd3Uqh3A9GlyAfPGM0WbZ5+eTm8XiG9bTN+ULlK8BXWhTt9eX0xw6fmhzbNPz7XywsmFvyOUfKx3j5Wv9QMd33Kp0ouJJv36ePfA/bGqXGotwjghbiLn9s4bFtrzcNYh5vdx9wS8PmsHjblJ8rX0ORBx4SCS1KvrdExAQ9xPWeNmlEJnwqBsif2jfm+PyTxBNaN3rYpFkTQK+0rrGNAOxWV/wBCJ0kwgxiXHwLVoG8NTIrrxMiIcUDX6olm6hzE3XbRZFf1Psjqff6ujR29sTcPei1pgfGRzvgAqIHDToyngNbDbYTzaHmDsZMwrhVALcC6VHdMmJNirZ+h4+Aqx1qof3sHNn848n6ekkUKtk4gQdIA2AD2rUSVwMTGA95YBHeotFyOYhipzN3srWpDN6Iflf14z5Ob9ObbbRt2rWegh7JrzO+k0WiiO3AYhqgJrXDZ2t8iMcJNlDZRCMV8DndlBfACGGHAiLJcZtnQk7PVJE6jP8ceelv9dOzC53kfXG+wBAH1T9CXY8UBfmYmhWLzTo5rAMblPkTRKEaBgtZkotQhQ7LLEKNFqfgwbPtog3XsLUMN2ClDrVbGAADVaNwDlEhNsrXS6Fh2BW9tuLbBiz44n5lsQyCo5cbubMgQ5d85YKiOkr0f5k9PV5zqcONcoRMnJkGJoUL1q4RSvmp3aVQeS0lXTQxLDB3tHSL1gYmoFOfhhlYFVoBnIPzXLs4M6sfAJNaRCERBjfr4x17J5b7xCQllj2FP/auE0VrHLhG4qKin4El9AiQ9IcW4M8pntZMUtXK5iTkRlzvjn7m0nwtCCXVkoqCIlK6MULVW0ja07CkDffd/ZVrm6DRDZeDQv+PL2Pp6XH5qd5BLchhHXRrowk70ZsWolmlycHZeoRNFvkmOKUHKbe+0bYAslGi3kgZycD86ZfTZmRG4vKBRMphUh1Fh9Fyxz3n5RsXa4Fg9wYMTpDx4t5qxHiwKc9GSKY51QEz8zu/ENXOaQh+f8YjWU34kzjdUuErVYbcqaQkD6BQqcfSpwev9ejYSyePgOtL5aFtgex6x8BCSSdarUMGq9tUM+h7pXYPAnPvxK/trfumJ1bVjGnipf9E19v5hwCkD6GkwAgIDA0KbHTMcJyqIElfmfNAhW0nXG7kKw5twCNhvBunaR2DIAlxHBWm6unYoAAIgDcKLFgUb0ddjaX3MDHDhqAAgAcgPyiv0YByqrMdO9MjKCLhXFyfWXFHSblSYEBzYKdrKXAAVHZQbsqWAE3rVVYFw1hFuLXOXsbizkapuNJcPbVzcNEAFAlmDqdN/2OGovNz01d7tgMgPJVU6FTCfNhAAAF8As2rgpAgylZ3bHfVXaGDx7r5hsZmUQhwMzqBE7mFVjglV1DsU4rHmlNPXnfG4FjY7fKtQNoFpGYwS66swnSb8lOekLqzlu++bV36rWDWBfvdqocZ33hBvhXyZ3r8G/Gvvp1d8mlzydVnUtBMW2bB4ObwAT5g2gVoMJAKBewCzTwzOGq2ZRAqr4HwQm2HQoY1SflfFGpgGCtzGSVHhyqa2mhdv52no9+aJxO0zx0cU1B1GL+QH6viaAAEAH/LX5A+GHWrPCAHcFsZJY9ojfZZZ68VGlgozuYRGP1v5ZE1vnlIRkfUa71ybJ9dO1uT3X5/5+4usJ2R6uGEEGCTDhlSIelpNdDXBgDfkhCBXLMqgScP45B8E35l8YsGcK4Fw7QxJghRXQANhjyxkDshs+AACXENSWw0JPISL192ZMEJPWDZvfcaNoUgUWr8my5pPkuicgZwfXzWjenE2FgLkUZ0UjcwqkCxvDOpLUmfI84zmoYq4lrtJtYlvE0Rg2OJGLBAwb6zDa3AKN0xtp9MFLGD3+0V35Odcp3O5aBh7+rXbNUcL9weBlnWkPdwtovF19Mk3c9umJgmBvNLbXy/I4RKcX1VEid0n29ti6Wru6riQeoFgn7W2ZsDdAig0mAEBqgOnh6eMB1GUAyrXvEuyg9owogT3MgADAXpZECI9aJAoAqCAKw4hoGqCovAslO1ssU2z+xIvrKK6WagMAKHdsYcxmqYUBGtQ1dLmFHLASXdRstJktG2pqLXHrVu9Km2j6dKTaNSRecmGA9qR1RQ8ybuAEjYHGvy5OlEYDp5devkvTF9419AjUSoOS5RqG+RsheEFXiOU99MAgRldcPnYA8spa/hAAHFTSddLyHYfI69FHjjvfTtr1GStXaUzA5sw2rd/bwkxqm3uXVrj2bTNHsIXt+zFbJgi2cKeKY9tlsEVYYQ+eGGyzT6kR88DR5/KUvrhw0VS4vVLkuHwZmhvWJcb9+vDTWxjn+VWHK/kX/SoUq3XqR0HBGTPh2QLmpsEEANhq4LoN9XPvOoKU+F8UBOnUn1Glx5gGAh7XSBLxrEWiAIAPYtCMiINxvTWehk9Wqi4xuspxDTzbEA8ATDcorOHi3J3Pg4quWM3oQAuaOJv+nCho05SaGjfypyDOlHa9bu2tZMVZa/9jA26ti1vDuy4Gt11HeEMwHM276IdGeBEfuyWDSxogAoBbgzdj++6Wwc3W3N0ddJriKpdNi1hptqqGbxb5nHT+/YIBNdzO2JKvoMZaZqCCOhrZIxV0H4OYKdDNGrFJoAbFpivYPtPh8zIXnWTb4NoMHX9Ry20AdRga5LxjHugH46M3mZujv7QGO7LVx3JrfbcB7NhWfIaTEPDHbemR6f1aLg16p7axgc96WnvDbFfX3mDZOmlPyYQ9BnxoMAEAfAGmwtNHAXhn/kkD4OGGbFt7xj6AHWZANMAelkQQj1wkCgDwIKrDiGiM3q4BivTrJaIktTL/gMNFewCAKzU3zCRFgIYLM84tHjj8KvxqvSnhc7TxCk/L23TBjwvXHiotEtbfKvw5+lkkFSKsNf9Thf0xxbdyL0dmfhsdeZV96q/qm31cL/cESbWfcYgVSXcZmWQwLWX/OcrSNJ3jpCS+0D1+A3c9q/MHX0J4ghoN41Frez4G87xwUEUa3SS4QtPiGQjKX3b3V3oW8PrArxQTyNmt9IIQV8IZNPPN+xiDR7jOYBlumI9m+ndavwQK8ml2TBDE7KrwJRJLIrn933ZRANS++RXGPp5aMdhSrynKLZVl246VVuF28T/3Hn5NBXZYO3PdwK5YwbGAq7bkp0NM8ZZ8AABTuwjFcFc0An8wqrLx71lPM8Nb7ER+vOdplI0sAMBin1K76Ch1eqH2yGZ2Lu3EDKrTZYurZ3nk8Y3q4OOG8SVdqLdVwHYO1puo1IsrUjqt6k1Phhu+CwaMh00+Km9c85JuEr71c6VVc6coTDYFApkwkL5KBMBGkf7cdn4lfi756Ou6Iy5S8+ndlkiwa9w/tg7BPXed8XgIXq2t5KXgpeNnDGFXYCAtFKodFqHWisX+NAQAQNKCjEjHjDI6QG/rdRLRB9bgS/YaTXsAQN9mECdZpIQpcB+s8gqBTWC2tJk4uAlsR0uMy9xNswksRi6FG5OXWJJ+ZU+6uIlKLJ8pQMyjuLRZO127IrQ5dg/uumPEImCZvK/Lml4CluX7+axh4z38jDODyjDNmCHlRwt7m+xaULzsS+/TFP+b2XbHspvwWjdkEDxXhn/+BvDZ6YmXQQ6sjdKFuQiUIcsugueudKltySz0EOPMn0RzN0l5hU0iIj7H5H1Gz+NIo14fqzygBDhyqr6EhzVel9pnCR4A5ye8oyUn4drLXgFM3DSeijXfhN5+ndLoizM2fjpdAmKqvn+Snqv+DW0Rk5GiKkcF03T2GfKlFk7koDmkTRmuCo6N/+zDxA9a0gLghsGHa3f7GzHXnwufk7RCTgAGCjS113fL3VyubGSz8C9VH+J/TK/wlYbHe0XiOoCssAqQhVkOS85pjRk2/zek1zm94jq4saDT5fWk/ic7uyhNxQaIu7LyxeJbA2YtXN1P8V+fA+oqF+5lf1IrZOQoEtY1WkB4fxbUSPoEY/6uc8T/1/ZhckpcKWjvprk6wVs6sg3IUODu0ZONHFcd5ZLmswfUJMfvlsiykJf3jDY0f+sAYIYjjho0sQ2dX8JZIXw89IAQsCMyZnx3zb0lYgpPOEjADm2GTHmEMGSyRfXChbWO2QPb1UZmJNavM3IH52+cZz5oByzl+TwmeeBoGVT4zh2AHcEd2CTOq5zP2JnU9ZIhEU3pEacXOubXNmPYT9Iyrz2PkZDbaY4WD/ht8sKMY9q9r4QvYas9aWviMNFJ7+q9aTPy/dt0kK9cnAfMlygmIvIQnsU/inaR6Tqd2tTz6bImJEJrFGYCwef/j8G584jsg7cSkZ1JF7UcWR22TCVpWf993SKBcqVNaP6vE2h0aYGTARq0Jjksjoe12bjEw032fDSJyPo4Bj9xi9L9O1yaT3PfAikuJrNzdXzglixr6TVyW9QzWhZk588b3VhVCbcC4xJTFxmnmDpX3GLqAY5jTDVTGFTkj1k0gaF7sdGOfOKJtC34HbEThv/ggIetpwlCFx6rmTp37GbqgujyqYuM7QyKgtJjP1OXKRb0zm/d6pY/XjR1aeJHUxcST5o6pzcy2PGmqQ5+/GnqIRKPmmph8ampSxavyhWCsQWKjmflDxIyLTn48a5yuvCMFxofIbGbU486JeA8t6yE1FZkNQufzUtrjxxFUZqkrRb2bTiFNhiUFOkCkzvjRVs3+aQn9s+dK3UXPLHo6UEST47bcLYJGx5JyYXpCWpTCk4rYnqgJwpNKUPiECRAmoNrbKSqfJtl4GbRdC1ZtfiNNVsnc5QVV2ZQiC+Z7KDjcoTZG7RxejediCl9yz/pDuqIWIO7v8c6o26FgDWcOKdW2qUNpk5wVqZ7ptFicadaSggAbPUME2/Blh11ariFwULd92UWmY1TY4TgZCMXELL7gAFASrd5nTm20qrowm2O0CZ0+fa8hEMp+VDfYeNfM73HtRrCU936vdKrvZ2nniDHEYbSlRIGzTajAABaAClphug+jeeCBFabf1QPM439WLly2aO58otQF1wCtUUMYVdgIk0EbBsR5Jmiu9MQAADJ1WMSuftRfQBU7eskAt2jRClNewAAeuaMqUxS2Iv5w5rVDXyc3mTjs7QxG59lTLGZgghu8cozqD3JijALFJ0U7Ukv0uFieJ16c5d/rCI8scluSbvbRFbhssluR6vflGlG6h44PE0v1L1aehIANKeQjcJSuwGgBUFNleVrp+PcBWxq45x6tt0YTNtUh6kya7DVlNJMCAAwAcZVyHWi8K1gynpm50IIyLOxByE6BoFriBHrxHhNcgY6eZNjNMYb9XN/jvYv8QwfriF/EQKegg4B6o66JycYhQ3/gt8TNnbp1ww6pQJB/iMzP1UdAlQoyG9/mDg3Ka+NJbtD+ZDoVVWZIP+3VeaOqpnlsf2PBdz2cZHwYETZAuOijAIAzNGsbHlXe4jpul6Isq3L6V9z+S53FV57s2dYur2pDXToHok04xKlpSclUQCAWtQQRD3ZgTpUnE1s0KhLewDAZF57QdJ1rqUPcxgOh3Kc2TpUDsTnTYZ6SZ26LYJIdt3145JnScv+tSRc8pb7FhtjgQf6vRj++ubchl+5sg5v9gEyLz1kYmWXk62IXeBlOdlNA7fTXAIA3BXC3dAN7g4qlnMQpmH+jUrIe5qxR/047jpiuT7FOGsrJx0bGcfNGL68lS4nhNEu+gAA5vImDjGNuCyDjgTaXTWQggSvl7IAAHABIkrMhex5e3g6EjGxmeQN2beiyFIsMcXT9hZ3iuyPG+xLwkZ0je1mWAbOHxQNfKQpTmx6utzIWX3CX3kE3jpVnVXcTXJZCUe/tcVqnzf82BTL1RHGinX5gk01owAAG7FypjoLb2AATgBlas80DSjLDDQENMWSNAH2VG67rHZ9nrYUejhRlKgUI1qpTGTGF3BJr5fDAwCcXlAK+1EKkkWrqewEvULy2BZrcEF5WZuGkObGuuqUfsEkKmkb9kSXnAomtUSlWMAa3PdzsXaHIWs4UdUo7dmdYd2c+PANkUj5mKNI0finPMZ+7Q5msZJbXywQAmte7Cnnh4AIx+4TS5oJIjFCTBcDy+MV4BASLz0JALBuJLJcajcA4MoQFrF8LJ1nmNgilrLejmU3h9yVoTCYvedGEsw0EgIAmCQ5IpvLtrRwFBa7UcG6ui3NGr1awncZ2ga+y4QwofRV11jkIzgc831wRyDcOfZ9wuF8ujaslSif6D1qlWhvh0erDpx815boU9Cr1KLjboNFyIRZ7GvDwHIUp6MAAAr20U0nSOBQBuBlksIR2mzXma6B0G67BToSoavmSDqPxezCtWtGuM/7f56GAACIsTlRYnxOZSIXyZlr1AYAeD1DEM6oqJj9aA7ScNpM7RakydliXc/yg6hZLqUDyUu6a/3qPrPClqjkqmgU9+kSttRiwKbAu9ie6H6RzVoltjmJKhJMBLfdpUCIcDlsFAMRicNDGRAxu/QkAKAiJHFZajcA0L1Iiqf7kq4xPKBUc8cMpKp2VgRSHNZiQgDg4oTUauPSAlHOYKZRT5Qgo9K2IKOGsPluuPIquJia7Nufg4G3vbzgle+an/rvjhIrkkdV8vSiyY9lgfZxkXAaK9ey5KKIAgDcpWVv9UHkSpghSn0tAS+jlbvU2vmzK/RObXBA79VIJ85ccydtbi5QRKe03cTCKVGigz/+PQ67vqfziSqw0toAQFIrt7eSTrjssPD1jSVsyFzDbt8UKhDfeknToq27Ma/VLILrCknIq1vdzfGkfZYf9ZBRkydeukarr4LTHYTj3U7fmBxSsz48bCRP1SNCuQWUAMCm2Vm6GwDqgOI+9x4Jq+Fm7uL3eAcFCoZBm/3YTPOXj3u/dodfCq9c7Sr9478LSSSCQ4BKAPnt8RFmePFS/GQXvScfH5UKAPnP/GhWjT2uNvJPhw2292QYi3DRA5VSAAABI9UbVTFgYAs7yjNoOSDSoKFslJSKOlgwcduCqmxaW6QsEoh8IsEsxgMAOUAVkBcEcwY0HxcY4dbg8Ddo5thf+Or2EaYtZpAaF1cr2j59eY/k8Naz34seqeGRQSO5bhwydxXC3YniHBMA4ASoiwakl6g5B2F5DHDHQOZqZ6YHyJWuHE6sOcdQmIotHwvYqf/lXd/fFAn/IrGkC+jKzMsKG72neWn9SgIMsZb0gFdVW3Mn8JjlLAAAywXOwHDZ61tZUxJXozMvs129AjtniVWVBoJQcfffVak6ZognkNVP0rE+MijVuHUtoVZ7UQkaA41/VZxg8FE/kVvCOfkeIhEmfDpSQocNvw/f8R4uGSfp859wPXeh6nPW+BNxc6zfmDBuANxFcVoKAOAKDfUecH0lwJr9vJReqfpsVeMvb9s02OAtTaQ9wIUHXWM8bJOTKS9s3l1+DE6Zs0mUO5/eFUA99zqJEK7rFSaF3oZ4AEB0V1IlN8J+jBxRODTKapqeY73IUFli805CgE9geLP0VnmSFnsYwPK13nD62MBJa2QKhKCqeZcDUHUPeuq1xJBt7MI8D3lu+yBlRJuYz75QuY4eDVN/v/mwJRiiwrOMep/u1Qw7Boqcn6jpOpjfhm/FvzwPNuLtrWabFcXgVWG9nBXG/FP3N5slV1GFVP2BcohbSVCoXrdT3gNr7w3KIMOut9BvxuXNTe3gami2d2hgW7A8QabjNRuaaAkZkGmRFSH76GMMtFKFF6VJ4Uk/YIv/iZQooCIDM7pFPSQzdF2/py+WDSQo9rU0Q+FWmX3+t1DKAxY3EyLKkl0CC6AJmtF4eRiEqgChrTDnsh09afuxJ9csBnUPYVk35msPV7WwyOp94BCpCvT7TvyTaqY33Lgq5XAIY5butFhBbjePXBgoRYpxNObIQbCz3csteRS/Y0EWHXc/4gp8MA6BCw/mcqvz8y4kSiAYbIJFhjzwzQ5mXg7Fgl1oFHSKB1FRQ8hxY/qFJ8RHJz0PfDInOMJNxcuVPWiQ7nfORkOaaKIRaKEL8U5h3cf9ad3HCa378I+OqNf707oPi3wrHIAew+4tfQMpqChw+0EvGZ7pow/ub0BNi5yLvx78hDIKKaXMOUxKEKYekUoU7gfrPoYWiBUR9j45q3jGPQsjh1z+aRO6Bjnjwzj8El9kRqyraAuDfhWNNQ5YuDmIVjteui6G2rVJChUNWOnidyteR21FVirTNPBOzlnqOQjmclsbhdH3SMKeoktqZ2QQN9OLakubJS8mIGcB6ZArqOPhJXwgFqOiuycvMyMcatrFJ2bLsKAkuMb6VQkBgNzKzcTMqga1eAGOsqz4cJdkgqKo+DSXZQdoUfENL38INKIyXfvk4erResTmPg3OhDBdBdj6neA1KyFTSxVNuut6XZv8wHE1H3xq5dEiRPGueZJ5Rcc973b8I5quLGvS5D43j6or2+R3nrqKnGvVGOqyeEDPD+BhmkwoL3CfTRF7Xy7xm3cRKhw82Kq1Pj/QfJWv0EPRiRbc7pTb4/FqWa1QYWdkMWH25IuiwN7lKAAA+xirKBDL0plFqEz+p7pvwFjp323tmUvrTwFczQxcAVxkSa7FQzfvAgAYCrfHiaZu5oNNxKFVidrrH3hHarggHgCwJBNl/lh7wezEKrysprWgqMLYkiX7du5JjKm9txJqr4mT1QxYuElUS9aFnrwhZ5MowM5E9BI4tkOgBoAT9bA6MclJo376/N/FYJSFy3Vtq9Pg7S4nEwDUZ0hNt6dijFSLjECcqns/By5c2VhxF0+UCkZbvbdr/l1EouPM7GRskga1MrxBptUsW21kOsMgpAZZyLlWnmwdqBH3a7xpiG2Or1z4XkcTYqL/hS6wEvOvVTF07bUi4dtd3LLXvdMoAIAd2XU6zZlKsiLAHY7bzur25s9ce/WXdtUGLrSrSnJxZtT9L14AwIgCS8SKibYoXIui2cQJTTG5BwBUkFlhUuoWP76pxp15Fmfyxt44BDPx6BBTS+2gpaP33O0xtsjH/u0dqSy6UrDhOtScTxxBQE3QhCgWxrJtPUglqWpkgJrdNmjmlsoEgA2EHFMdGkoQpICMiMBd70UycRc2MGvGYVenseu8jVaekEL8m87+AEIM8TtT5989vD9lOjZNbhqj8EIG707iqQ6t03YLLYYNTCkFABigpbpRrAF3odnps31ZQGus2EALOkrSgirxAgAGpi7aBZ1NHG7oS+4BAJ2y1DAplvwRTS9zEkQoPjdccYBcT79lBR7BfaDZv/E1qef/onV5e7KR/4/t5Pf0CzxQ+7+qPP1X9c3e17palAmNWjQBAEBUmGFzFJrYQS3VgFvoNTviIgDHfqowrVLB+DuZ89x+zu953TiSprj7L+uPO6uJPq+ykAMAwGhd3JJaGW1w8H+vYfXZpBdaAIAx+qZyuU4FDIaSBpx5o+tY6ysxMbXW16qJ1Ky7ir2RUMZ/T91WKEiT+YGjqL2fzz/hHILfaDlBfarPwwjhnUJLzm0XUgCAKtpWcUMPQxQHvSiOAIvWO0s3smfOL+MtDQuD0SJZ9hxfazCqOwGEaWJ5FwDYwWhcnFF0nEtLProykWAVXhQPAHDxO2UX1g2yB9WH9CYXH6ONBXysKSXi6/R3hO8yBBKo1cO62lMDdm6yBduZ2N4ApBwCGgaoOGw0l0/T/10MRq3AQdc2HYG8Xk4mANC3EM1tTzlZJK0wAs60sUxy4AJruYqsxlS0gppaSAgATGX59QrWroVjGumTixk0g3y31hdazoZb69vzNuQgxIbqyVTFeM7P+6EhF+CDRh6WG1wf8aE4lFQvVYwDFc3u36vTOeHtZ1Txj6ejAAAqHpVTX52cnsoEVDNxVTzzzJl/fWTlSgZjZOWMpmPYogCkcRcAwDY0BXKiaaaBlhOpxqpE9wPu/46kuCAeAPBKpmW6WJ08zIO+UIzW9O52o2RlLbHTzeQlNag5JhUWmJ3idbsKocmKUyj+t1EQOpJQLMML/fhSJRT3GnpuonCa23qVCFY4nxVWO+eES6PG/5PwV5JjFG7dsa2eQapKy8kEAKEbUrvbU3EbqfZ1DYpXwKHZijtb5BQxUUMhAMCrZcrpY3WczSBNPaNmkLaZLTJIrwkhk/HEninzMcz0nzcDTo/z2RgbWqo9Z7SJof1NQSycOWQ6SokUAEDreTj+aCM/Bim1SwLejgZ1eTeyo9Kb1chc3cWVuZ8pf51qVt20ijFR9yzwAgADdCsuygvaOvGcqcSH6r7VcArxAMBokSx+dgOFsgjDmpOoZFrk4+IqZD0cqFoKDc2yK2ooeL9eyzEOKIvgHULLrn0MflgNbjpRfbQkAbSgwnAK0XaYCiUZ/UPfWNntSHdWoUwAKC0SGHV0sLKDq762BIrdk9PYYeP5CxDvGAte8KL06EJC/1ygT2p9ANGGeH50zxuWpP5ojzHlEiqVIw0J+tOCHkYMZ4pvPTVWKQUAWBXij8Z7YJBSqQbcheYyaARKHBiAcBqgS7wAQICKizJDn4fqM59YXMdiPAAQQBUQFgRzBjQfFxgx1eCE77oT8aG1hn+95Xg+xvMXOaKLqezwhuK7lqc/qjx4YZa9HELc2NV1mT1F6MFFEwDAQMRt0IMacEC98/td9tQ8eRs4/GBSFZlDFMve1d00hqHsblKeWYuQ8FFBMdFaXny6/Jou6idliJ+l3XXWcr3WLGpPXXl5UI4NLWx4V8qNCa14+0nhSQkOEAKyd3GFiuo18uLGPC+8MGFqQrFj3kmpv67078hXk0stMi2+frECpzezP5xLzKqmaqr+BIwIAHlx0mWje/pBvMGCHABgKMRMgbHMHJOxRSGZoLLmvMLsI3mdZhYAQEVB8pTposztl6cjSUFspm4WH/1BKVsPVEEcQaWYe6LeHZzl1vpL29NBmCA2NVDrsLRGsA60Uofd2c0BR4OG3DvDvOoIWsBXqc8/KWXy6td56555jDWs9IKBNcgXZK0vttHbZw6L7aiJj0RqozCEw6v8WHSlmhJqSqRATNPjaCEl9KYqiKQ73l9EeRL00EAN3JG8B59DKynocr5jPTlSDj6WNkLiMEHZhGxGciDWQnd3go42qClbafoELdPTDKM+/PrHeW+Iw/tdlTu5vqxiVkqanOxXrlg9QVTfbdZysCRR6mYUAEAaARNohgUb1yYPJIVYNgHFLe4B1Ecxhi+XUo0zYqzdTqFdJCR8VF0j2qqN9Ezkg8Mkz2lYRF/L5PHRJp2uINr+hcNcT/RitpEddkKCh4aWVF3zLjXuXw4XTpe/KzfMNa6xwnwF58PaMBxDV0J+hKulnP6E252B+GxGD6U1Ert8FwDQhkHX8iPOnlG09fitJ2NRl2heeaMiTXRDPABgubJ8pQA2f8ICOpHC7tuRaXaYWygUb0dWXCARUGjejnK7Rt8MEGfsNzI1hCLFC0MgQ0BY5XgRU5MCyrcqE6eQko8PxIWUprVwkrL/pFCltM0XM0RKN3Xb2WPgTkOZADAgmNCi7pFBpg2Cqw3NMP+tdLTGyu48xidts5kQAHA53Y0gi23jPAUNdu3MONCwwrPHCw0JBjEpaJXpMtsRJaPsxNklyHI7eR6H+EyAFr+Wu1tt+t7CSZCs/r/ONq6YFQWqy4bqrYWpLdVSUwspAADFht6u04NaSe5T0RpQ5HuGETJrbi5gZQYBsMQLACyomOgGejrYU4n1xIuDldwDAJr07YFSVPQzFfQdrKC5A146CsG4RnTvQch3ggndi56+BzucCEwxwnndLnYfcElnIhsD7AwjcGUO7aN2GZtrQe0xRteBuq7ddhf+saFMAHALdK1FNZuBa+sGTUCphKGE9aQzzU53X4hSIQDQYIW4+iXXwQkyPbSiHrDIHnuw4wd7MHkyMNDhKrwhI9zDMe6C+OWIeUU66f88q+/5bW7dywGKJYYbYCkFACAwoaGjCxYFSTgRSEC5uQUnMwggJV4AoFF7WjR34OQTl+u6GA8ACGwBZLCYUyD5eAHV7zrQDF7gSAHQnu60i91p7NkG57E7n9gb3yRlBYFnVZ0DJdhGB0owrpauzG3XaTVwoUwAoBYNGLV0sHKDraU9FQquNhPfk9rG91ypqz/kOwT2Ff2wRbbifQr3p/RAgEhX/K4dAJNcD2hetJu2v4D6iES54v9LDbPOdVxpeGK4AJRSAAAAkeoFrAgEwNzcgMkMNuASLwBQ4ERFj2Z9C5NPHLAW4wEAESz5Ixpc0Gxo9DqIUKyDlO8LiF/T1n/2LCb8d+qfvfXzbgzq18A/vhj2xwCb7fLg95bz4BvVQeTDRAPfs50lK1CV+dDjBRMAYJZ2qrlhmsbZkYMtCwKQBbuE1bV75mcPPbrSByhaGu+r6q74MPzus25ffqCBnb4/swfE/1X++1BdqH41n57m2UV39mbKtBUa2mmbMo3pijBXLQnXETtN1rJbid0/qYtdNeobpJrXZAEACO6JN86opJvmSq6FXDqt6U59KTfLta0uNqRy3fe3l9E7xFJQxtJ6l5XlmwRl3FqUsjiR5/hA8mtVILxavKcfPQIzjR8zj6aU0NEUTq9YsFYCk4oaMWHNAbo0owAArgLCMdMz3fQbIcYmoPTE498wUXHN1csxAqmtFVQVYBekfFwGOzu1EwAIaI62uZxooaSCmmx1baLjCXe16l0UDwBM42vzP+c+S4rv0ZvT+KnCeCoMky8lrfE+wV/o7xv8lSlwh7fNvHCDt6hPxC3ekBPogDfibDrhjTmjzngztdu6sDq3oEwAqGKgk0bt4WGdKgd7GXRPCcU3pWykNMvNhACAJeBgC5e+hhWkArOyM1uuUIZptsCztwaaxTKI7YL2wm6yA8/1mfYPU3HjUuX1KQBnOHmBh/jMaqX+RvfOlLzGFyswVv/5nL+qwNpM09lQw1qYyv3LNLWUAgBQtGHq9EzXU+FMjE4ApdqfxL9n9oXJmpsjaq4W5B2kK+oCAAInIjqQ2unBmkoswqGsG+YS8QBAffvuICOXfWTvG9vkQmal8dMDHYybhpAOtnwH6OB6noLlW6xwckiCBU4vEsHwLvLqlxUipK5Eqiy5bXfAVCB3xgqbPjjaSZ3GT5erYy7mJPexY9tc83aj0UwmAKgPafrsqfd4u5kxCHwVTEoOXDSdkWJlivj2HlSaEAB4pvs7qADXNEPvQYaZdI7HwY6zdXAiCB3E1JznlOvllt0FxUOllxDdpDdXOB5bcZf9EyOGg9qlFABAB0CqB+UqkAd0bs4AZwZ5KC3qAgA+ELKIIPOJAqcUDwBMt+3DwhFADSZsdgrqHsYnHwss+W6wGTwghcCyITCnXeRuq6UdwSsTyWPjVv6TwOTENNl4g/AptNhBapOVjAWtZrcn3FAslgkABRanFo1XEGybnj8GlxCBkjV2ui/HdD9v/xrmsdqFjZTKBItmxfcSFEjigQDRrfhdewJmzdTXA9cuZRLtdCWyFf/LTuD5Jbfu9VpBi2EDU0oBABboSL3ZSWiBYsAdK8CCys0JRGZwARZ1AYAFOyrqvcdZiHwiwSzGAwA5MAKoAB85c+CyMWl88l1gMbhBsP/ga70JnBvwnJXpxVHhNbLd7ylG7fI9tRH4kDISAKY4gQate1Cx0nMYOyWmaQiB4cRZeURPolI7P5cY/UImFqe7Ptx3/mWSDm4C7Hlb3c4bwRCm6nPMAqbyj/fYoyx8Pw9W77Z5aBpW6sERWsYBCUkKeAXWLb65e3yvxWCRRWniEIzl7Qhf+rFTQr83mCUQtK1DrWnuwj82gX2cp0vK7f0a1a075sa4iCnp6FqsoRcVp9w98OxdpKHRn9KNK15VN3oEIzK7mIWuGWyVGuwGfH58x4KvDEIVM0FsFm8AgAZKzNwfK7L4dlFptgaVQf58X62yzAIAREdJlnTZznr7jw+6Pg3I4MydDgg9ICaG9wtI+lDr5R2brvFXBIEa4LFH1uJN5c04CEpJNg2d7DKdYo6NJnEgQMyzHVxKb9MEHa7ZW3tum9WxwijycNI0itQ3Tseox9mncAd3S9gKAAvg4Bnm8X2a85Vj852EwM6fX+PDqV2BaNC+L6ymBfnXy8rqC87WjZkp7GZJFwDoQGpBlNOxqx5QLjFd5xYHWdoDAHgoTxQohRMl2pWp/K6jBeWweQh21aMmGNsDM+swNzJw/yeYg+Hu8zVkjX+fYAocLnMQbIvFSa/aQg4ul2NGsexGKwqOblKi7ehmSjQe3Wzy20e35cUyAcDF5RmyattdanbQoEvjVCWcnnK8G+okCgGAnj2LpRmWQ8kVbNGZZfbQjsahpsg+HeLVEBA0midLc2eZLlBPJYeBwipvDhNL8B2sGeN2zkTsBPCbzBUA3k8zd8L5lf4BFAVeedXP+pya8zsaJwb9TGdSFwCQVIIoH5oY6ANyKjFlvHYQyT0A4BhVOFAKG5d0tLP8igqaDUJ5BxOGj1YfboqJfR5AB4FPSAB/fLBY0OHfW24JjfDS9pawJex8oti6E0lAtu5ZyUa27l3JSLZGKbstXjTAYpkAIDpOsWpYczY/GMiSKPMIuL37Qk/vHbvJxvCCOa4rQwAHxDJztFHfg4iyvb9wI4iMts1BTpQ5UHo49E7S3c/QD0Annn/AwVGYJm4FgAUF8Qzz+J76M3cZZcEisIDOzQVkZrAAFXUBgAIpiwwyn2ium2I8AABwRA/B8CZofHxssLIPARG8979uBxVQPFzcElzhpa13YUso+USxdXskAdm6c5KNbN1zkpFs3efsNnnRaBXLBADRMc2qYc1cfjCQKVFmF57dD83ptfkYPWNU0zVv76h7ErsCwMKnSJNzAFH4eD4jhDIktZVbYwT3W+YdReCT0BUAFmjG08zt698j/RelKpAHVG7OAGYGeSgu6gIAPhCySCDyieK6FOMBgAYjegA6bDb5hixcNhaNL/tgsMPrkauPZ5Hh/xTVx9cy8jhHMpzD47/4Fx99uptiNG6wG0M4Wxt16Kmzte735N/vgqq3BxDt4vuLXcuP+m5O/KrHNQOEt3e3r3MTR7zVhdiXtWt+OywrmazPDUA93Fd82qtWXlzDyREPXF0sFF2rpHiSRAqkm9O0vnks6JXW0auyN3kfrYqZzW01yFo6JSEMGEDoBHISrfXXnaGBn2PjjPi+NnGstVVr1s/TIu6iYgQ+YbAPYGN56wZnTGXU89pAVxIAAudXACJYLd7u5Hvn3hQsXE/1FcZ4gX0WQHXr/hQ/PRI6rf9AIZYYkUnwuCN2bL5AhOglScUiRHdVXGRT9J9hTa0H+dZKTgIfURn9ZCuJxD1q+feF48pEzVHxf6ZtDotC6aiPBpTXnYNmibyhxiWQ16hJGk2TTk5j49pcHznrISXLcPjoXjyL7qO12v4raIhVQOLpe8qCLLNZZPeMTX6tkvcoY1N+3Lg+clEl6S7CRFWURYeLjv0yT9uU/urrwkbNt+Ms+ysCjcAKz7N1tc6uFqHVQYvQoX32t/je8bVtNyQQP6rWCrvAa/vDNeWZ7nnOsDUxfEVIgQxzPmSaC5kFfrecfUoKW/lHUhGY0xBayFMsQBzRTW9d/5m3qdcTVj9/h9BZWAf9ScJkpocTjamoWmXZOJMEhuMGgWpWHGmUyE9msihjgijVMayAsVUeG8zpC7L6YqEHGeBIIiJpAW808RWYRE6HofNLAmKkXFs70Nxl/70AMe1jfUm+wKJJxLalbtlCU+ABmc2IWeVjgVYyuIh+SrLeyQ9DXUScL8SpKUA+bTEtCIgKOa3jvWSVu0B/3AqoqHepvrEA3nB0LSQxy3dMX8RpZJ5BSUMAqYumdWepHnuI/XQewBJXXw2mrjhzjlCehsGI6MSKvXqaNFQvncKU+fAmGIGsBHNDlRBk1eaU+3Gvu/yN+g7BRp1z0FUQkPXkZRjxEzE3VLJZQcFsxoJ5aAtb/zLKbBpk6aQYjInSGrQlnrnzuvOfOYV5qjQtT0XJd5oq+pYJmV39gxMgLlB9uLT9vNhCMpk7A9PJeasWPBbOUlxIJEBqorrIesY35MkdxrFj9WrFDCDCkeyg7Je92OW05tDhKwiEnIWGwKkRpXURVNugtDIoMtm/XAKxpYZnzkT0YYnwxifqwmBJbqW0PtTNZvDU3te/d6b0Pt0X6kNuuKGHIxKDnyDu2Nq9Y3DYcPzDEtHiWZFDck++iCdgE9esQsy40FLokvtZ61HRKCrLTUIfBssNEEmHqbqfik6yMHX2w3v8hqGXdqyQjp0LDb8qhT7G/2Nvu73a78QS+5pYL6H5r9inSqjp8DJNqLnqoP7NvdlQMYSs0W3lopkwOX8O678qIepfbHXEH+ZGCq6yLd6yUA98mJLRse4/6Keyoa+zBb+bnzYhVeddHdxu6zBFhgxX6d63qeoJ6K4wu/seG7C+x49C6HWkkMTli+C1RBMSUdnmAiFYPRAPDHtUHqLPeReao6lgFEeI3EhzfReP1gjC8KlrdklHZoSX7Bj1W0Jnj7Ymv5tnADH3FDh+nVIytDyo1grvA0Do1k1IpVgE7nU8bFBDGRZD69nFSy3UvJf1OWwFrIhmWt90NtqgBDvj0fNHycyDc9QRRGvvgGUshqGtX42vAsO4tSt1DvJQ6UkBEIc+aXWOTVa99+WbOxDhMwRyYCZY7zYk3oihjI4Bj3kL7zfJ+BKQWzHwKH3DpQTdqeg7ED9yoRnQNJDCf7jcillJGhJxBYjYAdKwAaBsJ18S6D9nXmo4/0Lh+nPA8d9ZmIKPXeTN3dBwYB9C0UZp3KYoqKdEXz9k9zMNeD/9a0DyAwKKOmik5CAYeynb8raKJhY0Hc1g6fuEgWwmDO1mktqcDtBQXN5nqXnccYk8F1vfqQz7LE8mGKhHfkgsgwrUyHhBBdQO9F0QmHPB9MQU/YoUL/aNBXi5wPbup2Oa7DLrnACEWxzoLQ9QcTySOhYFZXvgQXcG8zE6q7xukivOOz8H44YT7rJJikywt0kwt1viT6vxy5oDz83yTouI78Z9Ux4EDbiWewhiI0fXSWVKSd+nUSdo2ZnBazv9m/rI9l1cH06KAswFolWytH4qZgmUJoE+lawZcgBlmXclXECDeU123a198j4H7Sq6GWUOTmj6tmqPJxGlopoSbbSo04Ci+jsTiUrROSNhs29ox7p2O98gnnrWh0S6UopfF8fRVZG6/o0nMEt8YpJH0iYKH3oXtdURpgo+zZI0pOnsWBZ5ha+gCftYn2KLHKSbUFQMC49QBm31FifBBwFENHeL0iTllYE5hRs57GbQ0LCI/z+gc5v+qZGBUY9HHYBU100FmUDfBVpn2QrLNamEbNhNWA+ynkyYvoLkZw1HdlmJ0dBB4ZhdmB/+DXVx3/Te3NZymCwMGM4MACcAvRGom6bwE2eKhIqHYVOtV2TgmoQDYw3qHl2HwrD+tM2+1ULm12r5nr4QjRzihyLnP4/edfJtsQWxdvD9YyfJxv/OeGDXhlF0x59Xv+UVvZm9XWFedVoyfQH2I0ztSxo20r1ZKcNmYXJC6PmIRwpNZp9S6lYVLsiUe5jR7JE35OFk1Ozsgojavt1k1ER7IohaZnd7lG8tmreZuYf2C43UlDQOfKx3WICBfv2VmUMjfcmdMTRyJOZ+KZGQ1eolpSWsOZ4qVm/qTnxP/6pP528flWdyglLkU5m6vnxPWUUFAptK2lE3ulEYfoiUlKlzR2TZ4EbuZDYDZwBYRfpZzvraIWXfTgZGt9t5YGE4435gov8/AwAC69pNBjLaXTJwe7sSckCDL15JSOvAiswKkb8HZr4YSLFd4EOchsPx6SL4efP+zAj6uIh2tqyebeyKLeqWraPrvGNyalt0n0tqRy99JfD5NOIPi4QCuTSTZyCZN0z+k9JewzvYJKhG7Kvkb+C/VPzjt3To9L7d5CPHfeXJembyomMU6pqBrBpcPgBncB8GdHkXgBPdZwEt7v4AnFtN0Hgz+wBM4RpYtPUuANO+Bhal2K0/DeT3zp9CPzGBb5MOCQhmi0oUuC4oHJzeUqkCV1gI22uNUzTGm2htZcG/r5QHAIYtTE5JBObnIiy/e4LVSVwaKCltZzKRuLu3rqBNp/eIkDZylGZ5iKMqoI01UReLUOSCj7DIgoEucKMXV4qKb6PKqT8HAj1Djqx/H3a5Fs8Gi2FZ+QVnERFZbSKHHHUN4TdjKApEeG9djAnBN8VfZPXMWsKxZZFvEb/SfJZOfvylx66TqaA2UjxdEG3TyEsSoUQtvZGkAxmzSov9x5toHtyz8+LXAiW68vpsbSnysrUogBb735H6ym8QdV5goZgU/qlQSMj3zjAIVzuFlfZP67IzcKUqA9hWiySaQiksO6PW6oZFO+vkQXcTKJX+asdnsYO7k2364jUgyVxH4jyuT3jl4jOFaOd4PCYixU28cAzA9kxmxEccZ5W+vgP7GIguiEjJc8x5CBsyX2gGQXvtHjQN7C3qAzjYxrKe0y+8RXAt7c4qEQixhKmPGUrUVqHR1/z8iMlni/EVOA29I+fINkuIQEDH59HwqBSfmitPhR/PM0RfBOLM/nyc0Nog1BON5D3QWzrGkMLaEbEkwqTR+V8f3y5gv+n0zn5M850OGBtfAApiQVsVfwwXEJVCH4WQTAl/5dvKHUF8UwJeSWeMRFdgUTnArtnOOdusnXNyWne2c153bnJid8ad2TK4GVI/a0jjrGKyxNhJQC/g6u+U5vLvFLv+O8c+gM7ufQGdYZ+ANyA0BBLy/OULODoFRJg6VoJwIUpx1Q5ZlDeqYRIVFgcTza1wmBQ7Iff+Oo6b7nq0qyjgQSqJSbUwnrDfOQaHtLm1/1GHd/PueSO0kCCUiSxb2Meps4Bad7mIfw39a1lJi0VlI765sx+ESHyMMyLHtuOD0QTK2yLayTMT3spDbUne9K0rp5iUA6XTrEpMk0tzs16wkk8oZzMhe8OHHoWA0sJIJsVXdjWnatsyay3IZRzCeqwY671Eza1dvLGVDCRJOfQDe0TMcB+sHoNJQemqQa2jjXaNyVlbGbtDQ4rfXSh8VfcN6N4xFR1rcp5Z4Jn9OCXcM9NGjSWbZIrBesmF1/iN86BGWmtvuQKJcpVGyYqbTdqAscRuR7cAD1d0p9z5TtnBGAYDRwqt+9ySNJvONDrn2TsDj3pWzmhQWN9R2oF27vxz1ZstYWeyUfI8qFMm5r4MDo+Ctsr+87qX0hum3GVWMnQlG4XCKSnql5PcV/e1RK0sW6K3/viVL6QqwJZkrPRasrNa1YLJxCg+GZMCM0dGRTYrUwDWo88FEaDCcG70apOyr8mXjNXqk7Fa3i6NKI7DKxNmJAwVrMlqh+XWSFHUOrAlVO+1ZGKWliI9qia9ymoJ2UHZqqmWJNZPLdFzQEZDk2Q45f4dufuyS8o1FRlzScWW+ZMeT7YpV1TIuaDiCIr7ur3KycRbtD+jTZyQbYnxmJKzKZThW4vzhdl9lTFufS6uqRIakE5ZNJACeJEQBS5xGgvljbLLN12Dk46bL0dx8TVwgfyy8XfXztmllhRfw7TpInvu/If6SrqmIuEr9krZsr8Ejc0Ts7hEvkwtsUEfGUterwtS5J98OfW5N1wzR8RbUgdCYq9GpuZvp5gHNEM5lZAFJCgJXbElXuiGByUFsMUl/yzkL4nILR4EgzmP4SVD9vyBVOu+ppTAacGj+v65MAWLr55QTV9kMTCfw+GiTCPM25vmGY/4E9+yD9T4hx4XX8pG/iT80Mx8Svng1YFTYKHgtXYqFz4CoTLA647tVU4I7tyfqyMsZX3XHfbFqSVtvZbbn9Hy/ORLoKNYofGbgo28BLeJapnGfgPig6vMrYu9okWpg2IzOyG3fiXpFeW834Q9yuNjJRF0nRjE0fZ7vv05MmviuhRP1dQP13cpQY3Ikf2AJU6UujIlOM5LzEXAi7QYN+iv1OL4Jgwau3Tresb39peHUu+2w591fvm9jY/Ivs5d2VHqqf694D4e9Hb1JnH3/Sx7XOag75knrm9oEFkEfZOChrCJy6RxVY+mUo/OKE6M34npq4GyF8enXlZf1ZBQSj4p8X1PA7hdkMREmnEgCa4iE8CU/Bp4oVCI5sKRaYp+tlQKweAJoJHwJpU7fHwOEQmhk/ntgyLZIGJB6ASXF5aWA6pT76qitdCeKT2QTYcFbffZ1s/7pqnywq3rWziqIKyvGnWIqlexPNQ1nJ+UP3vNTEIzjQksk/Lvy7DvKzGlLMBK/bC2AFjt2Ce+g0kg8gXdVfVW2wk7bstlfOjQAniWAA5wENiA6eLHcmubmEzvObFM+m6z77tB2qlNNcF/EKZWYU4Ty5gjOB0uBgt0GiGcofPoxOJgI0rc4oZRvCWB88saKH8wK6IFCRf4WgmuKMa9kg85JXjvEFKptgC+bQC2ADkDIISw06Li6lgbBlzSOcTlSitaDvhmAdyg0eFisQYARUSlXyPXgqGZdImceg/s3rWzr6sweDPYfqBVDKbaAvh6ACJtg0lTqSZk3mJbZmQmr1qDjAD2hwMGW7fRK77mUitexpHlc1msfthDomF11HS+hC7iq4IvNJhUmg+ONqc8l5R0QmPL89cKWUdTS3zxP8T6bgBB/DPok2JZOob4BOVxrENbnShM98RMysmfaXwqnbBlKYEO54w9X4wABB1OY8eOc3zWgkCodEEh5HqSqJ+aWLVmE//JKkBVrlqdjiJD+Wp9ukD451E7eM/As1ZCpOO7NaSZ13mh8fqGkFptLBwQ5uZ/4mXwf+K7Z8hvL8UmOHxZ0xWokU6fXq0BbuFfC/Lcxv2btgYYUW/YWLekvdmoKxN6qXV8qmEZdfj9d+CAzJudUy91O1bu4og01lJkTOTFHFHRO9frAEkHTzydVJwAQFDCC5wh2TOK6+enMTnXwVNK5RvCOWAFB5I94RgXL4ALTyk1CHLVgmKpIH301fWB8ibto2hKqRhhxQbECESYwtmTffMwaPV5lDDippaKi6GcQVjSBboYG0AODD2g5xXgTQWzKvPV/4IUDNQtRxdMrVYCNU3lT7ZZT3nzCBBAYK8F8DEFjD3RHvLw3sIdSE0GBuhXAELBWbdzUzbxq1A+aYWnYEt7PIxyZgF61g81yJa18fRK+hEl8ifpxh+Piz/xC5QFTuGaOZJsaXYINUAved54PjbeFwUHS5w8kc28cYfGno4OJizliCkGweF0sazgAkhMF/MPxIfj6tWUe+Ve4CTZW2Azf+zx2dM5o8ufVzqdYIoJazr/+HB8sFhuUAJCZw7nm388giN/2eLT4QIzfDocTofzD0ekw8VwASqIMQUxBZ+gEsJMUTv36ivJg5fgcdKsCT6/7IFI7IlGfM7ZE0JF1ndZeh1c50uDytl1k5Gj+UagknbzWfiVteODp9prGD3Fgtek4I65leMugso978cunBIfI8221n9WdL51XyAVAoOdDcc23YDZPt2muhvoS+NhdIbUuylyusTq9HIafR4dP/1zwFurCzmnm6r14eC5Z5cyFG3Icp8oOmLk9xGiQ7ePyOWRv+CFxXxKHhWR9JXwYAj7aqzQy2HtFX4CAKDzUwop3Kj9nAr+BK8I6QgKQipCA4GIAB9BB09owkQtPHUtCgy3wfSvtCzG6sABoxRV4mtaLOZW1Nyhj+Xady2aLyn/yRJcP86JBX2JRXWvHh5fH0N0QTujs5anK1eD9TgfRhJQi3zDL8/hC/kPvW/l0yvzFWOuT7dGZWE4gdFVMT1mTkbBjApPlBihJORJxsYKbxSo6b8r2Ow9WrA3aoEFmxxLGinRqEjEp+FR0ClQN39bcNyzsT3m73wUWguBiACg+/yVXFrBKv9tCbcXUq5bz8Dppkjpq75IvmROd0fGWVSgyQXYJlmjUdOIYIfAQnCCHm64d9LUPqk6KO1NlLGPsiaBGjNqkikJxKGnpx6dEHNlRT7MBRZL1psDk4eR2gN+RXt4M6hZye2qt1iP3xyAkHb6qv2eABhSnUVPIfAUM0JHPAIAFsrs8V0BTIRzxLwph/SN1g9OfWku8e3rCXY36mYvCj41ooH7Y57cpc0s10f4Oc2+Fox36Xv2+QVnCiQEv17N4zMZZAhE/Z2259iqT2baI2Y86YwnA5225+mCdNl5YZKJpQNe8P2HzwAAL1Yz46XcICq45KiUaLaHEzNHIPyZX5f0fY21m899lfmKUfwwUbdx8cGO0E3mvTfUPUOIkNO9FDKA0ViJSQCz4h5bhvuCY2foju96LsPldrCrolih55QtV4rMRHaruo43hCnaOeKBljBczeXNkUm4E7CsEIgnWTyJHry2askAXIS+mt0TV/xV0QAA3W6/ay9u9c1uGkW+QTRnPMqcZXmIyAVr+mn7Ka8ERWFD/moxtAiEQoBTP4OmsArmMYz1Dmmyrt2cwUc0XF2mzHWHC8EeB12GF6FpolsFosagKaJ7Kz2/GlVi3QJxYC+R9Wslt/w6S03FSVwT7eXXXUpy9k0sEZAwcQZXhNsDTWX0SRffyIprm1dJhFynuhD2ObfW3jn50W86OT0J/r4XmCHpKqLHyQLjhhIcnVySdhY7Xv75xrapwWY/MFfwPTn1wjSgsSxdUgmDk7C9WAeMI8kjil2onrJLbrrkSXrasCGQ8p422/I3YfAiXoqnYd6LptEZDxLPS808G7YlzW3RG9ETZ50DN7Z7uevubJaamvpOn0qjdovkBBN3hkq8pcTk+Gv4L82LZQ6aETE7bBQJEB1takIqYVyKUPYZpkT/pbNOZ19smJMNSmTURiiK77wKlZvYu8LmXmQFWP7zwaDaHbgNzBdgNBa+vHgA4TtnwO9I5N2RXI7etwscg7GFisbJi5v6o+68k5pPCiuvaIPwvkjbzOn1smMR7lzRyUKHhGFpzmdRTfOTpKiTOng3ehoHW/5UFM2LkgUg2wgnbcjAmsh+y0zQJj03oA8HJVNColAPYW9cVszdrRntOO2c5OBNqqitHOD1ZP0TiiX+noPLDLTMsx+7FtpmpgUFUsK6clkVK5bnQTn0Dv1WRcoj5qmhf4DN6jPP0xBt/Kk2X5KxA7NmWjs+MBe/zQNFbF+2jvwy0QdG5m6jmaIAHigFhb5LobPU1/My/2TeurS61yasvwNNbVkdM8AgMPSx4oL0yRm1DPqYaWP63AR9vGtb+myCPnW3eX0OQV96Wre+GYK+EK1p3xzJm08RJniX4vz88O5aiH5EegRIWr1q7VMNjO4zY8TcR51Wb8Qp2sQwKeNCUcCG4X1Am0kK0Tfqpw5vLMnjBpLS7ZRUhu7wds3dlAu2/vlaiS6Q/s06h11CjxfxcaoUKzCcx45U9M900Flq4HaXoAEArBWC8LFJcl1vnB1BVAxuZnq9EbNEZ97cDDQ71cG+pUPMXnXtbE1DyZ3rkt0yPYWECgcR1x/UAEKmjYFkAgh3bQukI4DY3eZBLgLIPa0bNEUAmWhNoQH1On103C3+/K2r3vy17GFlcQub/XBW/focHAPICc6nUOAtQ3c/c2JLbrAERGZM0Lpy5F5igG4U8Nm8JoFojvsJL5M/y/zJAHjAg30e2srcWH5yx7VFylr1i2/ZzhZZkrIYSUIDZXLX2ofdKejVbE8P4SFaX9/O4HZ1/5+JuqXnUwfAtqGpuWHvC5xKQ0eqsoJAsLsJ5iBBYXlCAABvQdDJPcQYEAE6/9QOxDm1HaptpH1tL3YO6dAW+UAo1ji6WQ7UFbV/zRmoMWnr20fCpvF1ydcO72AMXxTviK93PFn74/M6cGg8L/4SUpNwwwPRWhMu4PzSBYGIvWfrCpnu+n43ONzQ3Zk/fJxmIOd9zufJ6nSP42x+nd7qB5jucv+YfcTQ3eHW2gCAuvGwtluFwQ2NkS/Ma2h+IvCbm8DcRuNyNZM9JfrMp/dmxbB/MPpW/vz0ri5dSwg03CgdFRnOih9cfEaCwD2nghM13EJ79R6hw220qMI4jTskJhIFOD6fLOn4CFxLB6rZBCJOikDM14zAhHtkDEHA73ediZn8qdYFg0kQ4veVe19nci5/dxNv9XfesugnyIdnOfOolbWxdO+x8K1Vh8mlxMtx05pL1G4i/gr+QYsdFK67TfrGLgV42nwEXlFA9qYaxEUB7WxqQTYU0N2mPOSWHqb8u92V6GFQv9ceTMFqXm4COKQ+yKsinh6LwZ/fAazWf6039dGtZH7/MZKprOkc4TOTLuBLVfOmjzX1OmDHkiQ/OfIHQN0bgVLX+JCYnHC/XhKS89DfbylLpxaALXq63RR6Hdaro05eyxyGixAO65PR7mY9V0iC3Lq3+x/10KBo9f65U0d+L020uPWOAMCdZaK9f9zrNROd+W3UJ4r16UbfnQqvELGaJe3VUPbXoL435ou+fzNxmkn96ZH3j6aQDix1jykaDGOGvv77oexh4UAmz9433Levmf0wG8+yc6l+DfW6db9XyeWvUveUTUiElu5dbconDnSvsKUKocJjqNTjN758m/v0EXl8NLp4fXpIEAHEFMfGE7oDWrlkQZ/Po2J1VRArAoi/nWy42Rbc8Y4AYEqLTvX3eoct7H7EEQV4rpTn0+DYhyu9ubVjWDPvhLU93kHs9bVwewDDhEv3POHt7LGDRL1L0ACARGKYBOcEJ1mFAcHdW6wN66vDMP3M9kxypRPQQ2XF95PTbu1g7aAt3TVPpRVEdmvJtLx081zfBkemU3w0Uyg7mi4hTVzCFr/uzbuyorQR+sOJaNI07YfeeCT+kO2QLDmbIkdBEaZZpTRxoZ2VJSZ8ixPahjMTfYjn1Bi4QxzlmOtyJo7SQ0nOqP2mKz8K6wO0v+3Pr9NmPctarUhmuybxustm3pwRt4U3XZ23xYB1Z4R598GfZWqGGhJXuTMCJ81CrgIuYGVuQH+t+y6oquVLm7wRNB5Kfw1Vg79mfCcKSFEWhPkO/nnQUa02yaStZCVle9twrJ0Qn4Dhxto9COnri5l3buRlSuCV5bDJScQkAbjcNSmWWj3oYJk0yZQvJT2/YoagJNO8d/cqfIpqvRSPdPTw/q0DPyDbIx0/oj8ryM9Ds/3se5JEONLqIfNfN39k/Sck41nltNPfT0eoWWoPvei5O1J3JG98l5d9XQGUrR9v8skdAU7/eDAwfzoVp5zDWL2qlHR4aw0o8xu4LBIWahVb3xrdY3U/rMBWW4UtkX/t2SJneC67unXOuL+WoV1QW2HXVnhQhqqJjdg0x5CoNpEtDZYzkGCh3XN2HcRyloIBAGyjZyaQbK+kpmKBskLNjj9sMKQJt9Nfk5iD6/O2BpoLa9i3hZhb1u5sB5recV6G2WOcbhayR3AGVuZ84Jasy52B7bR5rhq+5EIHY66O0WTgohNr0IytX6Pzn82lO5Pj4DZsqvvqF8pX1zgFiy92MTHTzFutXSjP6x5yRUiLdglda9JV3UKRebjnO3O8mtGEpg/3+tEWO3VSNBow98QxxFRb6m20rTF2V87GETJu/3C7EHanrSdKhGFw6Drh8Lpt5O4VoHiq6lPWdtQeZNdK5Fq7t2Ta/Onm3XzLZJhmXUetz7pM473r3/Ngxg6mfyDu6tqBuzn/46ZaAFIxCGd9OcrrmQYTWPdQ6dPvOO9Q0t6ah/IO7L8LxFEuvNyh4ui4VjpUqozjPGlAi/csEW1L4/ItJQ2VKu2Mg8B8bHLA9tT+XQ5Yu4vapWamWn/HXTGuEHKBdyV0gx7Y/UkDu+2QsKaBE1obNge4UevCHgK3afPYa77EvisIsP0oeZ21jY99atCOjxomXbp0CP+OIWojqOah3Fc7Ptw/Z3ucENRt/oTu7V+vrfvwL12zwA83rNQMBY2qkXr/G3dWIWGVfxfTxztWnIgF3Qx0hVxWDgrycMt53Ic8bV9QpwxBN51OGAAJdzqUMDFzgus1jJCss4fjQBjzMsTCEmx1+J/glnge3v0i/ZfWfw4TOuUAQxzSbfWEESzdc7GSf3e/tP7kMmE8lx2Wl1djmpDsuaxofeylk6uRUn3P1RV5tNF2FWgLuwcrvA3FcqgXDhDeeYIVIwH0q+sBcAQQNh+zntA1UIklhWbD7yHBWap9aHcHnhhGrEhHADAHFh6fG2SEI2Depj46r1hfr1+DC9+b5DUeRxlWorgfhYRAMTaueIhzxT0/o6CzeikYAHAO09k6zM1ce5VbOtGX6elmfqFunYzSZhGXeP2rvM5fp0VfMhH8iM/q++1T7zMjvNLGq77GtxUk5DTfShc7jXcuFq6k43LugpTtTrRgek3BNL21eW56lasMjDrLYDU3SbC9jPVqgJY4HGSATI2eZLxRHbt76J1qdswjQLGsioHIpQDFrGJh3KvDTkap6ncWW5yMUvOqdmYgRz8fz2wcR7ggYxe/Mf8ezLRz5+feSh19zQ78H1WkPNGOi6anWzbV9/zsswMAk1/Q/VF98LP7ICi2MyMGYfjyXAhXD6sz6vCuonwvt542Mj555mIAAMChF1qextCbMMFWgUSZzEe8Rfl8ggcp2D2LwQAAtBRQO8uqF+1sWr0zizuC3k5tXhPILbh+HSVoS67dAQIq5C6RIMNwQSwKMts2xq4d2cJ1mBrbYpPrMFPugu3u/kzaGVfH40XaSyfWs8XIu7wHu/IWsyVMufQn27tMau6ga1x301FEXmuXIwQAxw10rHIPz16kU2L9m4XS43t+FHCiNbi5tmKRgbbA9njZDVzi6B4ciK5t/7hoiNNs61UswkRfkbzRjkI6qg6T6MnT0woyu9LDg+E04AAAo1L/lBYm1eFtXpcwhQVRMKu36Z/L0e6S8NcLzQCAHbxFVOf2qLdiZIvlbZPOPxcWvFYdelcBR9XHNIC3+x1pAqzc6qcoJNXHR1LHgFptk2FAt3aZRtKY3+kgU4v3PT4YH5zcB2nkYFbzITgYih0dyWBcLPhsSKW+xwgmdCR40FllwEcX+NJyK6u/Ny4Pq3uUDxmwakvVBZUl0ar0jg1OPT748z/OHsb/N/QQW9nIqaS3xGeLozO2Yyn+Ox4zRMoVSJtBkrPcc41GIJFzgg0JpPWYdqUkl/Dk6MYxkbRJ0R49xencyZ+rwXV7A2EPl5nuLHAKByZQnnzpVkSyLpUMC0mLF52VOIkbmrJGjkDz7L1zUEh1VSRcHkOHXeXRrfZg8Kqu/FXXmgdU9+F5BFDfAGg8oRRQiSWFvsZNz7EX3MH5QnUv0RfGkhhx4yYBwA648h99YCxDF+aPC+EPPYOfz7YgOd5X0PveM+rnVYeeYebN0cFxLgYo0g1OKQwAOGhLxAazAn7dt/Vi8HdjwvO58/2vN28eex/g8+Ojzpg247mlzEXvHnkO6L1a8EQ7mfp8u5/bWN0WlsEAgI39HLsAKop0yqZxASEmnDHa2W0gvVbnDSTEqcfGHDMkZFK1s3iyid4ZXRAUAPWp2hjUFdQ3aFvQCNS3dhfQPCT66OqAGiRQ5y6DOcKBipTffBT4V5EN8S5pI0F7K92zQnQrUZwLAACcQMfuCAUwxwRFAmky5mwAzjB0xaAaDWEAgGuB6dJXy3HhN4tWbBccuAUPWpzq88QDSdSwuxugUbdjErpyuS4HNpTVcZApjmzAm8g1tDJT1zcCMSfrMk0o53EXprXK6ZjtDN0tnOX0No8dDiMJiZwlbBZib0wpsucGBtOlUcUMkHY8pLbtZ85Ff0GLW/5oYkm7Pl3J69NPs3ToB6fyNeec9ryRFkyjVxU/1ESapHn/HPpfIC3o6n9ga0B8t9HjaA9if1aBk/pt4n+TiT735J/uB3VtBZPBIkgcUvRt0pdw6AhxfiTbW7rS6i0Fccd6MLiqtSpbzKHBdWEVpsteyZ60f949yLPd1qduuSEK6fUajgI732mg7x6Rp2bP0XQOkKoGHAAg1WDQ+gULBjAKcXgas9qGGoCZze6MgYOGF5oBADS+XdmTpX9ZZ8zdYMOdsu6PDaT7tgadK8jorY1RBeDgbuQUNALs/qQlV4WRuG8Oc0NX2hojAt3VtphVkLvlLpjNTZoAO7LR7wUGJnmwLdDBXcYrNlgHnSB2E2KjLytsEcnWsp6eAjtzQe09gimCqhiCtU5lH5p5rUk+7voUhTcSAACmfN3EglP5WnlOf27UCaZ0UsUcJ2xFwWDKc8rFcC3HRzHQ67vA9PmIDZJumwMbnsrj0q1kxpdKJ4bs7Uusd8EMVYbh4AeBcP2f1BeHe7wGrdFkwRHt/Qx55GI5gxWbgWpnOx/NFqHnzk+1WF51H55HAHUGAMcKsjtgicWFdsHqgYvOLvrqAhXcYFQIPP99BACpoF3nP86CkwxzmD/qgrRs07u/vQ323ixbI/agZ9BkHWPhszOz3saCo5WDCphmCX3yYwMFR3umwTg3yf5t+GKKnbBsVgwbwAunu6/dLAk6eI2PfesKE3IlhU6A6alZGhR4mEJn2spewVO9EtdXbbp+gK4Z+3EXxK0rn2diuop4UpXBlfOT7Mm/h6Cq0fCpGuuCMNbAF7p/jYPNjVNqtzTO9tehdaLuTGqKWI/mxerjx3dlUfrb5k8odZ1dOCA31SR72qON0BuV4sZAXYnwU4lz9CbIK8JUKrKxzJD+YO7Oky2gbI0QVFciRHRbGSAg2tYFLCboQMbADgNOGTuGA3AZMyzCwdv87k1rgz9fVet7FU8S37rZz0jeHI13tRAAADiCauidCSjYENwrDie6eznGPAIgwzy3Ik4l4u+cDwYArJHeLoO/ZsFXM9MXCsX2ksMtMR6I0nKmQs/QV1ex+/DEyp00dHCZL6fjXiinUkYIFPIPNA1amWFD07Z1GQqaznCGoV3lmDsOqzyj1gvshC+x9kJUtSvFNERh640iMJCmOSAAyBpMkR9uGtracfuXbjBpy3JaUBlrMTbobns8d6AspjsSlGq2fyGCDHptvWnCvR+8hVdHMfZe4B/tXTon74qzugFIVLmic3EAANPLWhhy6W39XtL1Kk7XkgFdwRCzThHvaGbvgMQ2mQEAYoHB/g7Gl+D9uTjpH85JOXCH0iWXx3YEFZ0YPCv/rkHMVGspCbhJJq93UxmzBuS+K4UHptfubw2IJiNREcTE2mgaZK11cQ1IFGNwHwNj2dFgGFjiwaMDlr7HpDTIbhYPoggKubBEAXNb6rnxXRTZi0SnUHGq6qIOZjB9TR8BwGWBHRuP3d2sEKfuYjkNJiTjBSYNpHlXi5IJMMvLZWoJ3F07FVYBW26NtmuA1bX3225gDrUVVzd8jD6GKqe/rwqbW/B0BaH6A/X5+EICqPQAZE/IC9RiSaOn6fdQ4CJWFGgHo1SMqOhHALAEVzePfb1wB+OrgtQR8jmSTztL6bmcWLsArN9kc/XJY/fymgogbeUQAcMxz8eHnEnBGSwGAwDmfDqppmw9FWflwCmGc1X0volr9L5s5epn8vDVXuXB7Wm1jhZvVbGz5oM7/7t41favd++//fife+PD3MryGqE8eqfrGCrC1vDB7aZ/Jj9PVR/kUeB2m8EAgJRUAHv1BZwFvDTisim1C8yoPm+X4DZq2M8WlqjduRnQFAvJHOgbHTN6omAI7TLbDu+ESIwBc0iswXZYhcRmeSwLJG8Y8JXWufUDI4SzT0KlhiRtLyp+0u0OgVAdPDHMSMk4Q9tKq2OnGdr2uYJ2wIa93fI3DnPv6nAqeikTPYcfLgoDAIb0jrULqgA4l+I0rJTSalOfFzZoqCJsKjkXzc4FS7U7A1/8jPmyBi0YIQNxUlZm5phMVFqXZYMxGMOK4KacnS03uBOHdmuIJKcuHB6x6+9g/D+JsaX5lBZm/39/j/8BVLxy5pQarOp6I7QZFKo5IACAF+yJgSgmmpY0t2GFC5O2vOonjfFUSzB+8x6dl2D0ridY/z1EBbpiPJESKuiKNp4zHpeJV1HaBb6qAHTmZ6n4siYOSKIZD8NOmtL85JCj6wOtrwr2ybvCwo5Ar5pOAIDeYV/7mU784ZCoHIV+GR/CRFAPL9QOkByvHi0ghWdbBWq7yQwA8BKc7Zq2awCd4mMsAXTX/rkIcq8O3WNAdbUxvgEc3o3GDW2l7f7CeVOm7zgk3l1x0tbmHHAu1uXOwNa6C6kaZKrjGgVtZIpwggMOGOKuExMM5m64Kva/S+2MIbeM2f/f7xOhDQ/hwMsKWoSAas4DIeP62yK48qKaWhA5E0E3ypPl7xxgd6EAAGAO5GTzF3oa4lWVIJureE1ZSKJ9gdE10jjWongKGO9lJOVl/K7j/0W2bPvn+3Drf/Zg87cglrtXhSH+2u/j0eUE7tWHMJcWaev2ACFeKY0v4G8qGK5IOHMcvGEE309e79B28qscVtOAbHFUaAOitQzRWqgzcreZh7mtc89zi6zkIcitFNX5YABAHCa1VsHVm7mfqbPScKjh5fSCJH6tof9L+vv6uPWpryoJez6948M7VDedwe7TOwHYhCk4RqbQefQ028JPLQoDANJshCnrC6QDEhlxk46XAWtX6F3y8EFvrx6bRWbI/jU5A8tPcj0p92AAXOiEgF35XByxkDaGPYFYaetC9OB0RKwhYyAwVztJYvvdSNHjYmFPSMd/1inf0e94n36o999UHX7hvMxf+DFpaAZJ3DixlIcp9LeMkGwUlMDanPg3KPO7yidJvXHRM51hTgHm9AInwyWcx+nMtBcqprbQmQJxFAy6LLhGeoPfhZO3f3drbiY7O0+F6cwFJCihz3gfqmBuzgkDAManVVXL1tXYpdNM9sAMYNaEc5WLtbH2WZ03Ja1vath3ho1Nj5U2c1LV4B8WnIWoF+VQRBDGQbpSlMZe4NcU9Pwkb6gkkW/4w626ZtNJwsEQdJ2MuILsWTAF+mmyLvkD+FT+CcF6KjzIcWIF5ilc6IJsyy2DtpA2ZtGEttJty8KAtobuwiJCLrYdoNWgy7Wfs07s6sR67kNHNlTFkhFVIa+nUsRxKatAcw2McVFk5JJyeDqwp7p/rgAy8tsj+Dacpol4U+wY6DLrnxx0Pb68nYJ8ncLtWIvG1B0GdtEiNxu4Ga4L5IueC4oTC5idcW0bZsYWTy0ryP5e2hp2cR5588OvEuHeENRY/wd+gaeeWYu7vt+IW9mpx3H7/vE7nuFhh6dJ+hk2kGmcJwG+Yk+Lvxl6ssISfPkkku8QOKj9bMCC7cFvaZVAmUU44kCP7Tdfq9qV891AIPcirduHo/6FQM3C2UuI4Qe31FqOBmirjr3x0zsV+kUTqjOZFwuDbuIKErqcOddRgcA6615enHLHxd9maKDSF+uQPaWw02DtBsA17AAAIOxl9IuZQF9ANG5hrBOGxau3Ds9laKfwrYVmAEDEYKWKtjEI0hybAQVV/k1ABbXo0dJb2PNMkRdq8FUIc1daCFT4O4pxSx8/pYAf4JsBfOwui/DSrWrz4QlTBfEuVG+mVeWU7jNJwikAyk/rmxAKeqxL1NmGIQZwGCLsNhDndxRmvD/xE9jxX0Em4e73sSWhh7P/UEamG5x4W2wVR7nLnBdCOY4OkEOCxoXFAzAs1rNuYJuXVRYH2Bo3o4sgxzUGvOEiSxYAgK4x+f3x3g1u4To23FBX5jLZFCCOdYlRsSBvuwsldYCCrctVvNUSqzKuu+huF3KJtkUBkcvY2ieDPHbXY6TNDx+1z2YeTbjH/MG3u/tP3t5A/wy4kmwmZlNnR2+6fL7RrqjgVRaDAQAHFWxtaf0arm1WDEsK+X08a/PeNZbeF5+plr2+qoPbC3VOiNj21DhtJ3xTgatiR1OHtQK8YYNSXQBn85waBY0UJGsxGADAU4HwKgwG4Zvav9S7h5W2GH/Wx6FtviD4bl9sWIfRqM0p3N+B4TXUzU8Tvn9uHpmlQtxcqqJUtOIL5K16mGwnjg2HwpsiPhLsuo/p1Gmy5zIOKmiKih501YqKtFY9Zks2r674l5Mza8zV7P863Tf9qtocqqPvE6lvjPrvCS1CMmE85aWQGrogSERZGWnwxbZFrsMXGYOMKVxaynMOkIZspgcpn3msxvlWVvKtohruZL0wb4X8xZvQnmjBHQnbn27dMz0hEymQuGkAAEgWuJLWucyEOwpcDxe8bQQ65z4DAv3L8HOVd6+0qapgMxgAoDoVj11e10Hum0khZx63RBlVYu9UoXc9FWP4V/rqwNxExZVhNBwmZ4xMXmr2uQPtqhZKpcMMCzk5YuzpqLIyZ0DHsXU5BzruMIbzIM93DtDNlfLSdmhvG5CbxYlMRh0qOZYj5Y0h9smmUJVcsr1kdH1xdH1BdH0F0/X9dM02mim1eKOrJJrWiHLGyPaS0vUZdE3+c+J5S7f30zWf0lipRTpdicw5hwyG4EoTp/9qFFmowXUrqi5sIiXctrUgMitgEAtqjckGxMs5boKPauDcUn0a/JfNhvXuDr4Hth6qifu+cVjpsFpX6iP3w9nvMn6kutByExbVhJ/SNdOO1gJeZW7Ipz1W63zQxB3qwdoy9QaEqu1fHYVp/Gri/e6KOHn7adnAtAi3ntbhfA55EzzG5r6tk7c3peumADcvDO4wx//BTx/GbV8WDUzICZdkaFU7CrP6JMwdz94juFSDGQBwDIQWOtqAIWCtRslNnxn72RjpHylrpqZuJwPkxJqzqbCayr+75zVt6F1bMjW7qUSonjXO4tTpGIfMuaAslMgqbJIlP2Bm969s0afumU7bAed16vPQ6SSm8SMlNftvpt+Mmw2nHGGvCborDTRX6dNlr4W9nW1iVBqhGcmkU4A2Gq3amskcNO6zLjO9ch6iMdtdmGFtckZ0mOYE5IzPCZ6LoC0XLYITAySH69ALMfFlhbuGeCLrUadDt5NafUkVYwhKMQ1kR7Cb/NYmobmmBQAAg9HqJrcvITR7xNXIdIMYXChxB3mqLjG+CTQzXYuypekkgxbM5WrNbLSKL7k7CcEVq+4TXaVAcEXxfv1VZIJr7Kpivz64q731t+j/Fxo6l8QIL0AqRH8oQycvx+/ti+LoD5fGF//K4BOdT1Yb8CgTLB5c9sU2rQo9fS9Zv5v0uBAGAKS1WgHVuqarUe6NRjxCD9nr4mDgFzx87jRotXJwk1ITO8lV8B6phnXYS26ttapiQR29G6EPQ7wOgYkwAMBeAjIGjbaqORvgdN6Yw+tAsxWdUlS1ZPAoxBvmXbMYhSy9IR2dHGXcIZnaSWWxi+2kFg1KnaO+r8BbDTTHOuoT5q3GgHmUd57xSvpd47IX3BH6VLs8AABMo+bIMw2h5KDQgxg6JFMtVfJcSzSkn8s7O2XgdJK6JNZxbPf2VNhIrowqR00+TzroSXgd8Ow9j0LFHxkENkjCCHH3c37FPxcyK55oXS4AT2IMF3LnYmkCraLRXlmdKsfGsf7aJNoDp86UOoRHKpFVj9CtMhGNV41v1z/Inrll6QkVUakZbHOlPsi+t8gW2cecWnZ+LXuP9xKXaWc20ZiarTdyKmqGIQ4Npo737xDE9oXNWSS7bS1UBDtljaVFqqtMN96CufIkFnfH/qEKeZWz79wQNuQeUjkaBevufHF3x8nbKxaCFaypYbP3sUqpw3upuIfcR6oMd7uS83UAgOOKihhxJWXDcGXL1sMKctqZjvBq77lmAMCh+HRlW8IKTLYNV3r+X9/993aUoiTOkxT3rkDf3vyf+XuFrwKNetwKyrpbi5mL37uyfI+gu584vL2CPe/n9g+p6/ZK8lvvL3EGM65h3/n1lmjHmG0isu15X9ayVBOu+jMGSQa0yt4MjT/WLyP8nRLDJohSyuqdyXQLbtsN3kKBXbnbsBcUwXUig4O+uJwa787kARZ0EhHv5qIqNOjMg3MoFZH9V8Zg/DBPs/CTuGHgzR/VuAAADLa3/89oo68mV82D8cMcdAYuGgxG4o/DGhMACMt6j7LLU24G1vG294qtNL7OfjOxwkKXmXQVeJVKlN78UIqW05eszbSYwoX3iqAYXTQcCwAU1La2n53dhxUUOnr9O4hC1cNOsw+D3wAYL3TwmZFby4HQKCDI5I42+6Nm1egSFC+FAQA76O4ZhAAT9Gf3tufFyMuWvCbCx9+TPLq9NFjpDvZQvyLUayethS3ExXjkYr+CDltjn14/3tf6LDEPuU4fn5X2XBW3C81zF0yq4vZsDN4xtBZ0z60dAmu9qhaDAQAHh3ZnugtsGKG037Oa3r3Pll+Um9J8FkLXqs9zIUE7JZ1hrVzH3ESFbkDuvmPK9p+Z9uwH3aN7PJsq7vVNr12XGsSZ3Lp8MJNv/FXyVLkgXg3kCdsYXxvy3OoXX850St4uxuDLZMcoU4ADlJ7dZIrLY4PKISiTN6zw7qa+92GMz65grmcc0HEk+/cx+B5Jn4K/N4xmuXFldyOqsWn6kHCt0FcFP9XBzfcT+/kBXXUCnGLACoHI1sX/zqsV63KPoYQG1g3964Dbhv7VEmevBynsEMJs6aIH+A3YOQBjKIwXewqwhifIscrtDAY/vx2l+b0oHJ5DMsSJtRjMVe8PXU/djVB7XIFAzhYMeDSyuV3urD1142583+I32Z2NWc03BJI4Oo3ew1QLpql0kLYoFInsqzpYe/No6WJL4Dn5wZcML+kXj4sOt7LX9Ql5wU7+r0+eDSRPhFs9+kwzH0bC+4Q/pBCV/N9j99bG99MjXrah7FP888CcJRPL5hfHSwJBMXaHLgSlY4N0IzjVaoznicLGGehOWry0qR25IAwAcBzqHb7OglNVikjl5MVzhY6KDK8zL7uBMjNd8DkvInPTuZHbgrBoZ4BVas3fgLW0C8KuDiXagLW3bQy7loB1pH5h53pMxDpdY+cXvM5ujwPEprnO7qFLy+ZA27RDtFRDm6MjtVeBMuxHcppXmih/rS/rLcCctbfx7yMZ15v9SO74SiPnMQEAa8bfNMjlhDct5Rrvgenh+qeDXJqkLpj94kBMsHnaGi9trhsow2krprBQZvO9NzVDoivLjG2I855042Qv6qQGo5Mhh5/5ML3dtLnZge3OzGyH0JQryQo0I7gZxjW+LYQ5bWI52VmIp0k+Fmsz5PMLxRNdcW9QX9qJWIyVee04ez8dcvZGUVGVvkcKMONiZ7PfKgVm1xRcRheGApmY50MVnO7FYADAjApUp76gawCRPM8MvUGNnpbApPWVbtlHOz/R/mwbDbp1IG1Gf58TPI8RcnXELe94+9Qy08Ba1iXV6/hQ8iYuQwrQHxlA4H66IqtX5VibvGGOfThx5zD6y/G3a2GBG7kie5xiOfR6yhlFqJxXonHYV6G/PExfYCdvz6UDXYQ76syf6CFdhsdA9dW/5O0PcpEcBK+0WAEAKAHI6R1yhaEkiIUzSGr1TAM6BRAwz9VrsGQF6akykJ2bZD9B3YJnA0JEpG8MvbBYURHtVuglUAxXw2cQsVxJkYFwfS4Bu3CvEnywDFItJBPx10XMrDpvIz6qaOmFgXLEJ0wGmFVVHqhfDkdWnZysI+WchhO1CRrFpYYEtq/TaYqODxGZ5eqjqZUd7umoAICUu/DDgfPwtM0T27J+eeck+c1z4by4mQ3luluLQfW9RMBL2We4wPOaxnCciCR2ktU8FNj8Er/D/o/SH4be//bMaS23l3LG1IsVvXbULkuH3GzimLOp7o4iiFRRyXgWYAgi1VFKg+lm6J+s7cfOJnpd4D9SHW5RGABQBzTowDdhpnLYEjyPoZfC056d5+5GrnjrSvjmcHgxcZWt3DCg+GSGZM59b1DisTPZymsJIQfrklWuU38nU/qHYCyk1MgTCcO92bNlGD2Ewz/FffCn4E7Y9xMfuroecun6/G5w9+qUsx7/BdRn/2A/gOe49gdftOrTCi8BqAHSb1fOQydWHq5SsmL5ejYbTp5uaGQG1FxuBAYw5SccEFU98jfgGwcWPaqaSnh8TDp6BK7k+eWFeP++s3kQ6PK7sSSwZOMFX1iH5+gSOPi9XH+6b3Y/cBe/Njjxd3h9Lub2VIfg7m/Wkp+fFaehNuqdqY7ORDGO8ewz/p9h5vPT4qo55YurCjzaLX8STLKf3ya4xZamKR30krko8TSYZDFNOu0u7rmLOqZigLFAU5AvYd9lS8pn7Ic+RzyBW5/D3K5n5gsjJ6Lt2NBHfV5KuWVZWr71XOmHmOFbXqFzXlvpmWjWXY6UoLYL+SJh09cnt+Q3hubO8COP6War8uqA+M9XqMh1l2+vFpfL4TU4H7gWB1cBfE7g+UFteZ7vI05o+u3xUsP9UZK3bgCNNCoAAI0D6NY76sWwwgYZaQyKByN1wjQ1oHfxTuXzPe7tCgq3GAwAMFRgKBN+05NcZkfAmOepBTipzpueqSzvJEXPhN9wHt9IQGs3tlLAJ5EEH6A72McDtjmqTJBB2bEBO1WKjpk1YIdWdMvCgB2NYi6sDNhrt25EiT9gb/afYgEQx7Vvp94/l4lQs3y6CpjUYRYL6FszcVtDtcmxChhMZolEADDXAGfpIG4dgHO/+42ekjghnfPv9q0OWvv8q/5UZR8eYx/f3Bvb+L6w7/pON2u7fbO85b0+3MlVn3053tMWO4O5xmTC1TofFrnRPXjqV+QxerGjYvs5jkrsR0f07/RUYf0w5vURO62d6WOAT+g4YLNWNuULi6qrWhCPU+jskS+PeK7S4LlRhzWPfrpIJ9ILzzZo5yfpZcvwbpisaQijY3lrQK64Oq/nkHdP3AUr4aEYG/qyG18xuJYrb+j2zYsdi1sFzZjG586pDdm9b/ZVu28Ca8fKT3aktXL+4rMD4H4jsyPodkZvG7OjPnfMKFeh/TmbB1kgnkauWMd0NbZUxN/JXs5nzij+XXnBF2UTNX/7m3YL63UvByhLwwXhxY7E6cOb7J8rx/4V9POIDU/l+xnxOsT4TbQn6svnbM8VFhiirzobqG7CMllCe++j7cI3F2l9Fnpwe67vKl14wWIFACDG2yl0vCDbVVBV5mBCT8efBwLEyqMvkagiXnxaGABgxJsqw98xPJ0dgTkzzxVnlhvJ2jP0dummQxlAX+Xm2ef5idunR18xMJThcjCJIR0Cbqf687AUB0F1F29XYG9sDGpV4AjbgoYKnMQX0HSLaEPrRhmJjq0BI2ANl+jKA/LuN0k3zNWcDWcUnDBQ+h7AOTO5krUrz+cekJFCPLOL/0THPo/AKTDmixuvK0vq9Ulp3dBwnWkOLa/4R9nkfs4U+aMIo00vYzBL1SeYrb3XoZplSZPq1Mvt2iUSAcDShVxM8UOzkFaK9Q8CpveiHw20NW0tlmkafNyGfV41X7yO/PcUnp3XZ+c1DM43ifNdG/8MbPHaM7ctvH7Bfe58+qy89rq+m+ziscCOY86oWkGDYscthaWA1uVBK5rxV1p9XuVEpti6T79c8Tg7i9Gl/YPz9uvXa4xrQ7a9TcBvPdn3rNsxnjiOveaCMABAc/iioafZem8NEzrTrSm8MECeZ+JARW/YPKvz4gUe8cSeqK0GiQz5/ETRF6Y8InJsl0NmmKSmSUfPzGTmhZOJe7MtW4OchAbDdjJnvzG7bfu2xQH21EJsOTxPXp8nr2ExvnyIdPR26W1/eH5x+D6ensGb1zDs4OA6HwX4qryTBV9CT8HeStOs6KvOZqiL3kwhONHhH+b156T7iGeuqDX6s9CDb73cd5M5wHONCgCAF8CWip1N5zMV2J7S4Pq0qkRnTa1mH8XLjT6SpoF5dvCLXtcnl02dqpxH8t42gwEAvps8UZ92+ka2PkQKETOT9WOHRTjexQxntaCiMg97QDODWT2nPlXwjN+Y1fcVA0N5UfojCuMOSN76sUtoaYQkcZ5DsGRjMJweBbcIz226ZcYtwteaC7MqsHXtG6sALNASsNAEKkiqDCJpMGIJVNt96k6qusBNfp1x5rVkx2sHMvorxoZ/qfU/87VzW1T9Hqi2arYe58Xt4n/WAYCthkgunYswtQKy/iD02p+bEGyVpIofsiQOxfsnBW7rgr8iQaruFF3BbUh3SrUU7SwapCkq//ZDm2P8bd+VPw8n6NvuWj/1sZt6S3d2UOFzb/eMqosIfIhLKXYsxK2UBuOkVa1BZePpFoUBAO4YpoHRVhcsm4VdjefJ6W2KNzo7b6NS9I7T7Znw9o7D1lSeBafbBFm3W5CCM9Ayh2ZhH8yWdrkwmG2D4Qbcon3bPnDLNmLRzKJzqCt5Ps+lYuchzZfhu/7UP+Hl9g2YZmXOe1PfTU4BaSxWAADSzb7uLTXPFd7aGLxG8e7Ka2P60duYUxPgqIYwAGCKfdsWB6xcYPA2Rt4dkd5MZR4xM4ArA7QKq0uxr+YniqC4snpAsQ2CdBewJYTHQbA4DzigBqeqmNkYj/Ex+gWHh1HKDCfiYt/YBnFjC9iDgqriRCmDN7KbvaEhH7bV4/9o8iqpt0UijZeK23fqXPbwbLEu9l5qH4qOLfxsXPvOyZqOi7ptV29mkEylzceyh1rHKduSdPqEVtt98zl85h7vsomK8+M9/w++WIvOoaq8J3yCf7UYvCR8OKm+lE/yGH2CB+m5Dv6JidLoIU/mh/hiOQXtjzhatQ85YkdsD7v/8VPmJEog7ZUKj2jCxvO6LsXNCcLK7+niPQryHDEdafxurmo3xH/8VbK/jwV5rg03y/tvC9T1Rd8JKI2usEZSQgV1ss8+gJtjtpcD","base64")).toString()),n9}var GBe=new Map([[q.makeIdent(null,"fsevents").identHash,HBe],[q.makeIdent(null,"resolve").identHash,jBe],[q.makeIdent(null,"typescript").identHash,qBe]]),YSt={hooks:{registerPackageExtensions:async(t,e)=>{for(let[r,s]of e9)e(q.parseDescriptor(r,!0),s)},getBuiltinPatch:async(t,e)=>{let r="compat/";if(!e.startsWith(r))return;let s=q.parseIdent(e.slice(r.length)),a=GBe.get(s.identHash)?.();return typeof a<"u"?a:null},reduceDependency:async(t,e,r,s)=>typeof GBe.get(t.identHash)>"u"?t:q.makeDescriptor(t,q.makeRange({protocol:"patch:",source:q.stringifyDescriptor(t),selector:`optional!builtin`,params:null}))}},VSt=YSt;var w9={};Vt(w9,{ConstraintsCheckCommand:()=>ew,ConstraintsQueryCommand:()=>XC,ConstraintsSourceCommand:()=>$C,default:()=>IDt});Ve();Ve();IS();var KC=class{constructor(e){this.project=e}createEnvironment(){let e=new VC(["cwd","ident"]),r=new VC(["workspace","type","ident"]),s=new VC(["ident"]),a={manifestUpdates:new Map,reportedErrors:new Map},n=new Map,c=new Map;for(let f of this.project.storedPackages.values()){let p=Array.from(f.peerDependencies.values(),h=>[q.stringifyIdent(h),h.range]);n.set(f.locatorHash,{workspace:null,ident:q.stringifyIdent(f),version:f.version,dependencies:new Map,peerDependencies:new Map(p.filter(([h])=>f.peerDependenciesMeta.get(h)?.optional!==!0)),optionalPeerDependencies:new Map(p.filter(([h])=>f.peerDependenciesMeta.get(h)?.optional===!0))})}for(let f of this.project.storedPackages.values()){let p=n.get(f.locatorHash);p.dependencies=new Map(Array.from(f.dependencies.values(),h=>{let E=this.project.storedResolutions.get(h.descriptorHash);if(typeof E>"u")throw new Error("Assertion failed: The resolution should have been registered");let C=n.get(E);if(typeof C>"u")throw new Error("Assertion failed: The package should have been registered");return[q.stringifyIdent(h),C]})),p.dependencies.delete(p.ident)}for(let f of this.project.workspaces){let p=q.stringifyIdent(f.anchoredLocator),h=f.manifest.exportTo({}),E=n.get(f.anchoredLocator.locatorHash);if(typeof E>"u")throw new Error("Assertion failed: The package should have been registered");let C=(R,N,{caller:U=As.getCaller()}={})=>{let W=ES(R),te=je.getMapWithDefault(a.manifestUpdates,f.cwd),ie=je.getMapWithDefault(te,W),Ae=je.getSetWithDefault(ie,N);U!==null&&Ae.add(U)},S=R=>C(R,void 0,{caller:As.getCaller()}),P=R=>{je.getArrayWithDefault(a.reportedErrors,f.cwd).push(R)},I=e.insert({cwd:f.relativeCwd,ident:p,manifest:h,pkg:E,set:C,unset:S,error:P});c.set(f,I);for(let R of Ht.allDependencies)for(let N of f.manifest[R].values()){let U=q.stringifyIdent(N),W=()=>{C([R,U],void 0,{caller:As.getCaller()})},te=Ae=>{C([R,U],Ae,{caller:As.getCaller()})},ie=null;if(R!=="peerDependencies"&&(R!=="dependencies"||!f.manifest.devDependencies.has(N.identHash))){let Ae=f.anchoredPackage.dependencies.get(N.identHash);if(Ae){if(typeof Ae>"u")throw new Error("Assertion failed: The dependency should have been registered");let ce=this.project.storedResolutions.get(Ae.descriptorHash);if(typeof ce>"u")throw new Error("Assertion failed: The resolution should have been registered");let me=n.get(ce);if(typeof me>"u")throw new Error("Assertion failed: The package should have been registered");ie=me}}r.insert({workspace:I,ident:U,range:N.range,type:R,resolution:ie,update:te,delete:W,error:P})}}for(let f of this.project.storedPackages.values()){let p=this.project.tryWorkspaceByLocator(f);if(!p)continue;let h=c.get(p);if(typeof h>"u")throw new Error("Assertion failed: The workspace should have been registered");let E=n.get(f.locatorHash);if(typeof E>"u")throw new Error("Assertion failed: The package should have been registered");E.workspace=h}return{workspaces:e,dependencies:r,packages:s,result:a}}async process(){let e=this.createEnvironment(),r={Yarn:{workspace:a=>e.workspaces.find(a)[0]??null,workspaces:a=>e.workspaces.find(a),dependency:a=>e.dependencies.find(a)[0]??null,dependencies:a=>e.dependencies.find(a),package:a=>e.packages.find(a)[0]??null,packages:a=>e.packages.find(a)}},s=await this.project.loadUserConfig();return s?.constraints?(await s.constraints(r),e.result):null}};Ve();Ve();Wt();var XC=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.query=ge.String()}static{this.paths=[["constraints","query"]]}static{this.usage=ot.Usage({category:"Constraints-related commands",description:"query the constraints fact database",details:` + This command will output all matches to the given prolog query. + `,examples:[["List all dependencies throughout the workspace","yarn constraints query 'workspace_has_dependency(_, DependencyName, _, _).'"]]})}async execute(){let{Constraints:r}=await Promise.resolve().then(()=>(vS(),BS)),s=await ze.find(this.context.cwd,this.context.plugins),{project:a}=await Tt.find(s,this.context.cwd),n=await r.find(a),c=this.query;return c.endsWith(".")||(c=`${c}.`),(await Ot.start({configuration:s,json:this.json,stdout:this.context.stdout},async p=>{for await(let h of n.query(c)){let E=Array.from(Object.entries(h)),C=E.length,S=E.reduce((P,[I])=>Math.max(P,I.length),0);for(let P=0;P(vS(),BS)),s=await ze.find(this.context.cwd,this.context.plugins),{project:a}=await Tt.find(s,this.context.cwd),n=await r.find(a);this.context.stdout.write(this.verbose?n.fullSource:n.source)}};Ve();Ve();Wt();IS();var ew=class extends ut{constructor(){super(...arguments);this.fix=ge.Boolean("--fix",!1,{description:"Attempt to automatically fix unambiguous issues, following a multi-pass process"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["constraints"]]}static{this.usage=ot.Usage({category:"Constraints-related commands",description:"check that the project constraints are met",details:` + This command will run constraints on your project and emit errors for each one that is found but isn't met. If any error is emitted the process will exit with a non-zero exit code. + + If the \`--fix\` flag is used, Yarn will attempt to automatically fix the issues the best it can, following a multi-pass process (with a maximum of 10 iterations). Some ambiguous patterns cannot be autofixed, in which case you'll have to manually specify the right resolution. + + For more information as to how to write constraints, please consult our dedicated page on our website: https://yarnpkg.com/features/constraints. + `,examples:[["Check that all constraints are satisfied","yarn constraints"],["Autofix all unmet constraints","yarn constraints --fix"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd);await s.restoreInstallState();let a=await s.loadUserConfig(),n;if(a?.constraints)n=new KC(s);else{let{Constraints:h}=await Promise.resolve().then(()=>(vS(),BS));n=await h.find(s)}let c,f=!1,p=!1;for(let h=this.fix?10:1;h>0;--h){let E=await n.process();if(!E)break;let{changedWorkspaces:C,remainingErrors:S}=lF(s,E,{fix:this.fix}),P=[];for(let[I,R]of C){let N=I.manifest.indent;I.manifest=new Ht,I.manifest.indent=N,I.manifest.load(R),P.push(I.persistManifest())}if(await Promise.all(P),!(C.size>0&&h>1)){c=ZBe(S,{configuration:r}),f=!1,p=!0;for(let[,I]of S)for(let R of I)R.fixable?f=!0:p=!1}}if(c.children.length===0)return 0;if(f){let h=p?`Those errors can all be fixed by running ${he.pretty(r,"yarn constraints --fix",he.Type.CODE)}`:`Errors prefixed by '\u2699' can be fixed by running ${he.pretty(r,"yarn constraints --fix",he.Type.CODE)}`;await Ot.start({configuration:r,stdout:this.context.stdout,includeNames:!1,includeFooter:!1},async E=>{E.reportInfo(0,h),E.reportSeparator()})}return c.children=je.sortMap(c.children,h=>h.value[1]),ks.emitTree(c,{configuration:r,stdout:this.context.stdout,json:this.json,separators:1}),1}};IS();var EDt={configuration:{enableConstraintsChecks:{description:"If true, constraints will run during installs",type:"BOOLEAN",default:!1},constraintsPath:{description:"The path of the constraints file.",type:"ABSOLUTE_PATH",default:"./constraints.pro"}},commands:[XC,$C,ew],hooks:{async validateProjectAfterInstall(t,{reportError:e}){if(!t.configuration.get("enableConstraintsChecks"))return;let r=await t.loadUserConfig(),s;if(r?.constraints)s=new KC(t);else{let{Constraints:c}=await Promise.resolve().then(()=>(vS(),BS));s=await c.find(t)}let a=await s.process();if(!a)return;let{remainingErrors:n}=lF(t,a);if(n.size!==0)if(t.configuration.isCI)for(let[c,f]of n)for(let p of f)e(84,`${he.pretty(t.configuration,c.anchoredLocator,he.Type.IDENT)}: ${p.text}`);else e(84,`Constraint check failed; run ${he.pretty(t.configuration,"yarn constraints",he.Type.CODE)} for more details`)}}},IDt=EDt;var B9={};Vt(B9,{CreateCommand:()=>tw,DlxCommand:()=>rw,default:()=>wDt});Ve();Wt();var tw=class extends ut{constructor(){super(...arguments);this.pkg=ge.String("-p,--package",{description:"The package to run the provided command from"});this.quiet=ge.Boolean("-q,--quiet",!1,{description:"Only report critical errors instead of printing the full install logs"});this.command=ge.String();this.args=ge.Proxy()}static{this.paths=[["create"]]}async execute(){let r=[];this.pkg&&r.push("--package",this.pkg),this.quiet&&r.push("--quiet");let s=this.command.replace(/^(@[^@/]+)(@|$)/,"$1/create$2"),a=q.parseDescriptor(s),n=a.name.match(/^create(-|$)/)?a:a.scope?q.makeIdent(a.scope,`create-${a.name}`):q.makeIdent(null,`create-${a.name}`),c=q.stringifyIdent(n);return a.range!=="unknown"&&(c+=`@${a.range}`),this.cli.run(["dlx",...r,c,...this.args])}};Ve();Ve();bt();Wt();var rw=class extends ut{constructor(){super(...arguments);this.packages=ge.Array("-p,--package",{description:"The package(s) to install before running the command"});this.quiet=ge.Boolean("-q,--quiet",!1,{description:"Only report critical errors instead of printing the full install logs"});this.command=ge.String();this.args=ge.Proxy()}static{this.paths=[["dlx"]]}static{this.usage=ot.Usage({description:"run a package in a temporary environment",details:"\n This command will install a package within a temporary environment, and run its binary script if it contains any. The binary will run within the current cwd.\n\n By default Yarn will download the package named `command`, but this can be changed through the use of the `-p,--package` flag which will instruct Yarn to still run the same command but from a different package.\n\n Using `yarn dlx` as a replacement of `yarn add` isn't recommended, as it makes your project non-deterministic (Yarn doesn't keep track of the packages installed through `dlx` - neither their name, nor their version).\n ",examples:[["Use create-react-app to create a new React app","yarn dlx create-react-app ./my-app"],["Install multiple packages for a single command",`yarn dlx -p typescript -p ts-node ts-node --transpile-only -e "console.log('hello!')"`]]})}async execute(){return ze.telemetry=null,await le.mktempPromise(async r=>{let s=K.join(r,`dlx-${process.pid}`);await le.mkdirPromise(s),await le.writeFilePromise(K.join(s,"package.json"),`{} +`),await le.writeFilePromise(K.join(s,"yarn.lock"),"");let a=K.join(s,".yarnrc.yml"),n=await ze.findProjectCwd(this.context.cwd),f={enableGlobalCache:!(await ze.find(this.context.cwd,null,{strict:!1})).get("enableGlobalCache"),enableTelemetry:!1,logFilters:[{code:Vf(68),level:he.LogLevel.Discard}]},p=n!==null?K.join(n,".yarnrc.yml"):null;p!==null&&le.existsSync(p)?(await le.copyFilePromise(p,a),await ze.updateConfiguration(s,N=>{let U=je.toMerged(N,f);return Array.isArray(N.plugins)&&(U.plugins=N.plugins.map(W=>{let te=typeof W=="string"?W:W.path,ie=ue.isAbsolute(te)?te:ue.resolve(ue.fromPortablePath(n),te);return typeof W=="string"?ie:{path:ie,spec:W.spec}})),U})):await le.writeJsonPromise(a,f);let h=this.packages??[this.command],E=q.parseDescriptor(this.command).name,C=await this.cli.run(["add","--fixed","--",...h],{cwd:s,quiet:this.quiet});if(C!==0)return C;this.quiet||this.context.stdout.write(` +`);let S=await ze.find(s,this.context.plugins),{project:P,workspace:I}=await Tt.find(S,s);if(I===null)throw new ar(P.cwd,s);await P.restoreInstallState();let R=await In.getWorkspaceAccessibleBinaries(I);return R.has(E)===!1&&R.size===1&&typeof this.packages>"u"&&(E=Array.from(R)[0][0]),await In.executeWorkspaceAccessibleBinary(I,E,this.args,{packageAccessibleBinaries:R,cwd:this.context.cwd,stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr})})}};var CDt={commands:[tw,rw]},wDt=CDt;var D9={};Vt(D9,{ExecFetcher:()=>DS,ExecResolver:()=>bS,default:()=>SDt,execUtils:()=>AF});Ve();Ve();bt();var fA="exec:";var AF={};Vt(AF,{loadGeneratorFile:()=>SS,makeLocator:()=>S9,makeSpec:()=>Bve,parseSpec:()=>v9});Ve();bt();function v9(t){let{params:e,selector:r}=q.parseRange(t),s=ue.toPortablePath(r);return{parentLocator:e&&typeof e.locator=="string"?q.parseLocator(e.locator):null,path:s}}function Bve({parentLocator:t,path:e,generatorHash:r,protocol:s}){let a=t!==null?{locator:q.stringifyLocator(t)}:{},n=typeof r<"u"?{hash:r}:{};return q.makeRange({protocol:s,source:e,selector:e,params:{...n,...a}})}function S9(t,{parentLocator:e,path:r,generatorHash:s,protocol:a}){return q.makeLocator(t,Bve({parentLocator:e,path:r,generatorHash:s,protocol:a}))}async function SS(t,e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(t,{protocol:e}),n=K.isAbsolute(a)?{packageFs:new Sn(vt.root),prefixPath:vt.dot,localPath:vt.root}:await r.fetcher.fetch(s,r),c=n.localPath?{packageFs:new Sn(vt.root),prefixPath:K.relative(vt.root,n.localPath)}:n;n!==c&&n.releaseFs&&n.releaseFs();let f=c.packageFs,p=K.join(c.prefixPath,a);return await f.readFilePromise(p,"utf8")}var DS=class{supports(e,r){return!!e.reference.startsWith(fA)}getLocalPath(e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(e.reference,{protocol:fA});if(K.isAbsolute(a))return a;let n=r.fetcher.getLocalPath(s,r);return n===null?null:K.resolve(n,a)}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e),loader:()=>this.fetchFromDisk(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),localPath:this.getLocalPath(e,r),checksum:c}}async fetchFromDisk(e,r){let s=await SS(e.reference,fA,r);return le.mktempPromise(async a=>{let n=K.join(a,"generator.js");return await le.writeFilePromise(n,s),le.mktempPromise(async c=>{if(await this.generatePackage(c,e,n,r),!le.existsSync(K.join(c,"build")))throw new Error("The script should have generated a build directory");return await hs.makeArchiveFromDirectory(K.join(c,"build"),{prefixPath:q.getIdentVendorPath(e),compressionLevel:r.project.configuration.get("compressionLevel")})})})}async generatePackage(e,r,s,a){return await le.mktempPromise(async n=>{let c=await In.makeScriptEnv({project:a.project,binFolder:n}),f=K.join(e,"runtime.js");return await le.mktempPromise(async p=>{let h=K.join(p,"buildfile.log"),E=K.join(e,"generator"),C=K.join(e,"build");await le.mkdirPromise(E),await le.mkdirPromise(C);let S={tempDir:ue.fromPortablePath(E),buildDir:ue.fromPortablePath(C),locator:q.stringifyLocator(r)};await le.writeFilePromise(f,` + // Expose 'Module' as a global variable + Object.defineProperty(global, 'Module', { + get: () => require('module'), + configurable: true, + enumerable: false, + }); + + // Expose non-hidden built-in modules as global variables + for (const name of Module.builtinModules.filter((name) => name !== 'module' && !name.startsWith('_'))) { + Object.defineProperty(global, name, { + get: () => require(name), + configurable: true, + enumerable: false, + }); + } + + // Expose the 'execEnv' global variable + Object.defineProperty(global, 'execEnv', { + value: { + ...${JSON.stringify(S)}, + }, + enumerable: true, + }); + `);let P=c.NODE_OPTIONS||"",I=/\s*--require\s+\S*\.pnp\.c?js\s*/g;P=P.replace(I," ").trim(),c.NODE_OPTIONS=P;let{stdout:R,stderr:N}=a.project.configuration.getSubprocessStreams(h,{header:`# This file contains the result of Yarn generating a package (${q.stringifyLocator(r)}) +`,prefix:q.prettyLocator(a.project.configuration,r),report:a.report}),{code:U}=await Gr.pipevp(process.execPath,["--require",ue.fromPortablePath(f),ue.fromPortablePath(s),q.stringifyIdent(r)],{cwd:e,env:c,stdin:null,stdout:R,stderr:N});if(U!==0)throw le.detachTemp(p),new Error(`Package generation failed (exit code ${U}, logs can be found here: ${he.pretty(a.project.configuration,h,he.Type.PATH)})`)})})}};Ve();Ve();var BDt=2,bS=class{supportsDescriptor(e,r){return!!e.range.startsWith(fA)}supportsLocator(e,r){return!!e.reference.startsWith(fA)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){return q.bindDescriptor(e,{locator:q.stringifyLocator(r)})}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){if(!s.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let{path:a,parentLocator:n}=v9(e.range);if(n===null)throw new Error("Assertion failed: The descriptor should have been bound");let c=await SS(q.makeRange({protocol:fA,source:a,selector:a,params:{locator:q.stringifyLocator(n)}}),fA,s.fetchOptions),f=Nn.makeHash(`${BDt}`,c).slice(0,6);return[S9(e,{parentLocator:n,path:a,generatorHash:f,protocol:fA})]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){if(!r.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let s=await r.fetchOptions.fetcher.fetch(e,r.fetchOptions),a=await je.releaseAfterUseAsync(async()=>await Ht.find(s.prefixPath,{baseFs:s.packageFs}),s.releaseFs);return{...e,version:a.version||"0.0.0",languageName:a.languageName||r.project.configuration.get("defaultLanguageName"),linkType:"HARD",conditions:a.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(a.dependencies),peerDependencies:a.peerDependencies,dependenciesMeta:a.dependenciesMeta,peerDependenciesMeta:a.peerDependenciesMeta,bin:a.bin}}};var vDt={fetchers:[DS],resolvers:[bS]},SDt=vDt;var P9={};Vt(P9,{FileFetcher:()=>QS,FileResolver:()=>TS,TarballFileFetcher:()=>RS,TarballFileResolver:()=>NS,default:()=>PDt,fileUtils:()=>km});Ve();bt();var nw=/^(?:[a-zA-Z]:[\\/]|\.{0,2}\/)/,PS=/^[^?]*\.(?:tar\.gz|tgz)(?:::.*)?$/,es="file:";var km={};Vt(km,{fetchArchiveFromLocator:()=>kS,makeArchiveFromLocator:()=>pF,makeBufferFromLocator:()=>b9,makeLocator:()=>iw,makeSpec:()=>vve,parseSpec:()=>xS});Ve();bt();function xS(t){let{params:e,selector:r}=q.parseRange(t),s=ue.toPortablePath(r);return{parentLocator:e&&typeof e.locator=="string"?q.parseLocator(e.locator):null,path:s}}function vve({parentLocator:t,path:e,hash:r,protocol:s}){let a=t!==null?{locator:q.stringifyLocator(t)}:{},n=typeof r<"u"?{hash:r}:{};return q.makeRange({protocol:s,source:e,selector:e,params:{...n,...a}})}function iw(t,{parentLocator:e,path:r,hash:s,protocol:a}){return q.makeLocator(t,vve({parentLocator:e,path:r,hash:s,protocol:a}))}async function kS(t,e){let{parentLocator:r,path:s}=q.parseFileStyleRange(t.reference,{protocol:es}),a=K.isAbsolute(s)?{packageFs:new Sn(vt.root),prefixPath:vt.dot,localPath:vt.root}:await e.fetcher.fetch(r,e),n=a.localPath?{packageFs:new Sn(vt.root),prefixPath:K.relative(vt.root,a.localPath)}:a;a!==n&&a.releaseFs&&a.releaseFs();let c=n.packageFs,f=K.join(n.prefixPath,s);return await je.releaseAfterUseAsync(async()=>await c.readFilePromise(f),n.releaseFs)}async function pF(t,{protocol:e,fetchOptions:r,inMemory:s=!1}){let{parentLocator:a,path:n}=q.parseFileStyleRange(t.reference,{protocol:e}),c=K.isAbsolute(n)?{packageFs:new Sn(vt.root),prefixPath:vt.dot,localPath:vt.root}:await r.fetcher.fetch(a,r),f=c.localPath?{packageFs:new Sn(vt.root),prefixPath:K.relative(vt.root,c.localPath)}:c;c!==f&&c.releaseFs&&c.releaseFs();let p=f.packageFs,h=K.join(f.prefixPath,n);return await je.releaseAfterUseAsync(async()=>await hs.makeArchiveFromDirectory(h,{baseFs:p,prefixPath:q.getIdentVendorPath(t),compressionLevel:r.project.configuration.get("compressionLevel"),inMemory:s}),f.releaseFs)}async function b9(t,{protocol:e,fetchOptions:r}){return(await pF(t,{protocol:e,fetchOptions:r,inMemory:!0})).getBufferAndClose()}var QS=class{supports(e,r){return!!e.reference.startsWith(es)}getLocalPath(e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(e.reference,{protocol:es});if(K.isAbsolute(a))return a;let n=r.fetcher.getLocalPath(s,r);return n===null?null:K.resolve(n,a)}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the disk`),loader:()=>this.fetchFromDisk(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),localPath:this.getLocalPath(e,r),checksum:c}}async fetchFromDisk(e,r){return pF(e,{protocol:es,fetchOptions:r})}};Ve();Ve();var DDt=2,TS=class{supportsDescriptor(e,r){return e.range.match(nw)?!0:!!e.range.startsWith(es)}supportsLocator(e,r){return!!e.reference.startsWith(es)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){return nw.test(e.range)&&(e=q.makeDescriptor(e,`${es}${e.range}`)),q.bindDescriptor(e,{locator:q.stringifyLocator(r)})}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){if(!s.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let{path:a,parentLocator:n}=xS(e.range);if(n===null)throw new Error("Assertion failed: The descriptor should have been bound");let c=await b9(q.makeLocator(e,q.makeRange({protocol:es,source:a,selector:a,params:{locator:q.stringifyLocator(n)}})),{protocol:es,fetchOptions:s.fetchOptions}),f=Nn.makeHash(`${DDt}`,c).slice(0,6);return[iw(e,{parentLocator:n,path:a,hash:f,protocol:es})]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){if(!r.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let s=await r.fetchOptions.fetcher.fetch(e,r.fetchOptions),a=await je.releaseAfterUseAsync(async()=>await Ht.find(s.prefixPath,{baseFs:s.packageFs}),s.releaseFs);return{...e,version:a.version||"0.0.0",languageName:a.languageName||r.project.configuration.get("defaultLanguageName"),linkType:"HARD",conditions:a.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(a.dependencies),peerDependencies:a.peerDependencies,dependenciesMeta:a.dependenciesMeta,peerDependenciesMeta:a.peerDependenciesMeta,bin:a.bin}}};Ve();var RS=class{supports(e,r){return PS.test(e.reference)?!!e.reference.startsWith(es):!1}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the disk`),loader:()=>this.fetchFromDisk(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),checksum:c}}async fetchFromDisk(e,r){let s=await kS(e,r);return await hs.convertToZip(s,{configuration:r.project.configuration,prefixPath:q.getIdentVendorPath(e),stripComponents:1})}};Ve();Ve();Ve();var NS=class{supportsDescriptor(e,r){return PS.test(e.range)?!!(e.range.startsWith(es)||nw.test(e.range)):!1}supportsLocator(e,r){return PS.test(e.reference)?!!e.reference.startsWith(es):!1}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){return nw.test(e.range)&&(e=q.makeDescriptor(e,`${es}${e.range}`)),q.bindDescriptor(e,{locator:q.stringifyLocator(r)})}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){if(!s.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let{path:a,parentLocator:n}=xS(e.range);if(n===null)throw new Error("Assertion failed: The descriptor should have been bound");let c=iw(e,{parentLocator:n,path:a,hash:"",protocol:es}),f=await kS(c,s.fetchOptions),p=Nn.makeHash(f).slice(0,6);return[iw(e,{parentLocator:n,path:a,hash:p,protocol:es})]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){if(!r.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let s=await r.fetchOptions.fetcher.fetch(e,r.fetchOptions),a=await je.releaseAfterUseAsync(async()=>await Ht.find(s.prefixPath,{baseFs:s.packageFs}),s.releaseFs);return{...e,version:a.version||"0.0.0",languageName:a.languageName||r.project.configuration.get("defaultLanguageName"),linkType:"HARD",conditions:a.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(a.dependencies),peerDependencies:a.peerDependencies,dependenciesMeta:a.dependenciesMeta,peerDependenciesMeta:a.peerDependenciesMeta,bin:a.bin}}};var bDt={fetchers:[RS,QS],resolvers:[NS,TS]},PDt=bDt;var Q9={};Vt(Q9,{GithubFetcher:()=>OS,default:()=>kDt,githubUtils:()=>hF});Ve();bt();var hF={};Vt(hF,{invalidGithubUrlMessage:()=>bve,isGithubUrl:()=>x9,parseGithubUrl:()=>k9});var Sve=et(ye("querystring")),Dve=[/^https?:\/\/(?:([^/]+?)@)?github.com\/([^/#]+)\/([^/#]+)\/tarball\/([^/#]+)(?:#(.*))?$/,/^https?:\/\/(?:([^/]+?)@)?github.com\/([^/#]+)\/([^/#]+?)(?:\.git)?(?:#(.*))?$/];function x9(t){return t?Dve.some(e=>!!t.match(e)):!1}function k9(t){let e;for(let f of Dve)if(e=t.match(f),e)break;if(!e)throw new Error(bve(t));let[,r,s,a,n="master"]=e,{commit:c}=Sve.default.parse(n);return n=c||n.replace(/[^:]*:/,""),{auth:r,username:s,reponame:a,treeish:n}}function bve(t){return`Input cannot be parsed as a valid GitHub URL ('${t}').`}var OS=class{supports(e,r){return!!x9(e.reference)}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from GitHub`),loader:()=>this.fetchFromNetwork(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),checksum:c}}async fetchFromNetwork(e,r){let s=await An.get(this.getLocatorUrl(e,r),{configuration:r.project.configuration});return await le.mktempPromise(async a=>{let n=new Sn(a);await hs.extractArchiveTo(s,n,{stripComponents:1});let c=Qa.splitRepoUrl(e.reference),f=K.join(a,"package.tgz");await In.prepareExternalProject(a,f,{configuration:r.project.configuration,report:r.report,workspace:c.extra.workspace,locator:e});let p=await le.readFilePromise(f);return await hs.convertToZip(p,{configuration:r.project.configuration,prefixPath:q.getIdentVendorPath(e),stripComponents:1})})}getLocatorUrl(e,r){let{auth:s,username:a,reponame:n,treeish:c}=k9(e.reference);return`https://${s?`${s}@`:""}github.com/${a}/${n}/archive/${c}.tar.gz`}};var xDt={hooks:{async fetchHostedRepository(t,e,r){if(t!==null)return t;let s=new OS;if(!s.supports(e,r))return null;try{return await s.fetch(e,r)}catch{return null}}}},kDt=xDt;var T9={};Vt(T9,{TarballHttpFetcher:()=>MS,TarballHttpResolver:()=>_S,default:()=>TDt});Ve();function LS(t){let e;try{e=new URL(t)}catch{return!1}return!(e.protocol!=="http:"&&e.protocol!=="https:"||!e.pathname.match(/(\.tar\.gz|\.tgz|\/[^.]+)$/))}var MS=class{supports(e,r){return LS(e.reference)}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the remote server`),loader:()=>this.fetchFromNetwork(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),checksum:c}}async fetchFromNetwork(e,r){let s=await An.get(e.reference,{configuration:r.project.configuration});return await hs.convertToZip(s,{configuration:r.project.configuration,prefixPath:q.getIdentVendorPath(e),stripComponents:1})}};Ve();Ve();var _S=class{supportsDescriptor(e,r){return LS(e.range)}supportsLocator(e,r){return LS(e.reference)}shouldPersistResolution(e,r){return!0}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){return[q.convertDescriptorToLocator(e)]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){if(!r.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let s=await r.fetchOptions.fetcher.fetch(e,r.fetchOptions),a=await je.releaseAfterUseAsync(async()=>await Ht.find(s.prefixPath,{baseFs:s.packageFs}),s.releaseFs);return{...e,version:a.version||"0.0.0",languageName:a.languageName||r.project.configuration.get("defaultLanguageName"),linkType:"HARD",conditions:a.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(a.dependencies),peerDependencies:a.peerDependencies,dependenciesMeta:a.dependenciesMeta,peerDependenciesMeta:a.peerDependenciesMeta,bin:a.bin}}};var QDt={fetchers:[MS],resolvers:[_S]},TDt=QDt;var R9={};Vt(R9,{InitCommand:()=>Z0,InitInitializerCommand:()=>sw,default:()=>FDt});Wt();Ve();Ve();bt();Wt();var Z0=class extends ut{constructor(){super(...arguments);this.private=ge.Boolean("-p,--private",!1,{description:"Initialize a private package"});this.workspace=ge.Boolean("-w,--workspace",!1,{description:"Initialize a workspace root with a `packages/` directory"});this.install=ge.String("-i,--install",!1,{tolerateBoolean:!0,description:"Initialize a package with a specific bundle that will be locked in the project"});this.name=ge.String("-n,--name",{description:"Initialize a package with the given name"});this.usev2=ge.Boolean("-2",!1,{hidden:!0});this.yes=ge.Boolean("-y,--yes",{hidden:!0})}static{this.paths=[["init"]]}static{this.usage=ot.Usage({description:"create a new package",details:"\n This command will setup a new package in your local directory.\n\n If the `-p,--private` or `-w,--workspace` options are set, the package will be private by default.\n\n If the `-w,--workspace` option is set, the package will be configured to accept a set of workspaces in the `packages/` directory.\n\n If the `-i,--install` option is given a value, Yarn will first download it using `yarn set version` and only then forward the init call to the newly downloaded bundle. Without arguments, the downloaded bundle will be `latest`.\n\n The initial settings of the manifest can be changed by using the `initScope` and `initFields` configuration values. Additionally, Yarn will generate an EditorConfig file whose rules can be altered via `initEditorConfig`, and will initialize a Git repository in the current directory.\n ",examples:[["Create a new package in the local directory","yarn init"],["Create a new private package in the local directory","yarn init -p"],["Create a new package and store the Yarn release inside","yarn init -i=latest"],["Create a new private package and defines it as a workspace root","yarn init -w"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=typeof this.install=="string"?this.install:this.usev2||this.install===!0?"latest":null;return s!==null?await this.executeProxy(r,s):await this.executeRegular(r)}async executeProxy(r,s){if(r.projectCwd!==null&&r.projectCwd!==this.context.cwd)throw new nt("Cannot use the --install flag from within a project subdirectory");le.existsSync(this.context.cwd)||await le.mkdirPromise(this.context.cwd,{recursive:!0});let a=K.join(this.context.cwd,Er.lockfile);le.existsSync(a)||await le.writeFilePromise(a,"");let n=await this.cli.run(["set","version",s],{quiet:!0});if(n!==0)return n;let c=[];return this.private&&c.push("-p"),this.workspace&&c.push("-w"),this.name&&c.push(`-n=${this.name}`),this.yes&&c.push("-y"),await le.mktempPromise(async f=>{let{code:p}=await Gr.pipevp("yarn",["init",...c],{cwd:this.context.cwd,stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr,env:await In.makeScriptEnv({binFolder:f})});return p})}async initialize(){}async executeRegular(r){let s=null;try{s=(await Tt.find(r,this.context.cwd)).project}catch{s=null}le.existsSync(this.context.cwd)||await le.mkdirPromise(this.context.cwd,{recursive:!0});let a=await Ht.tryFind(this.context.cwd),n=a??new Ht,c=Object.fromEntries(r.get("initFields").entries());n.load(c),n.name=n.name??q.makeIdent(r.get("initScope"),this.name??K.basename(this.context.cwd)),n.packageManager=un&&je.isTaggedYarnVersion(un)?`yarn@${un}`:null,(!a&&this.workspace||this.private)&&(n.private=!0),this.workspace&&n.workspaceDefinitions.length===0&&(await le.mkdirPromise(K.join(this.context.cwd,"packages"),{recursive:!0}),n.workspaceDefinitions=[{pattern:"packages/*"}]);let f={};n.exportTo(f);let p=K.join(this.context.cwd,Ht.fileName);await le.changeFilePromise(p,`${JSON.stringify(f,null,2)} +`,{automaticNewlines:!0});let h=[p],E=K.join(this.context.cwd,"README.md");if(le.existsSync(E)||(await le.writeFilePromise(E,`# ${q.stringifyIdent(n.name)} +`),h.push(E)),!s||s.cwd===this.context.cwd){let C=K.join(this.context.cwd,Er.lockfile);le.existsSync(C)||(await le.writeFilePromise(C,""),h.push(C));let P=[".yarn/*","!.yarn/patches","!.yarn/plugins","!.yarn/releases","!.yarn/sdks","!.yarn/versions","","# Whether you use PnP or not, the node_modules folder is often used to store","# build artifacts that should be gitignored","node_modules","","# Swap the comments on the following lines if you wish to use zero-installs","# In that case, don't forget to run `yarn config set enableGlobalCache false`!","# Documentation here: https://yarnpkg.com/features/caching#zero-installs","","#!.yarn/cache",".pnp.*"].map(Ae=>`${Ae} +`).join(""),I=K.join(this.context.cwd,".gitignore");le.existsSync(I)||(await le.writeFilePromise(I,P),h.push(I));let N=["/.yarn/** linguist-vendored","/.yarn/releases/* binary","/.yarn/plugins/**/* binary","/.pnp.* binary linguist-generated"].map(Ae=>`${Ae} +`).join(""),U=K.join(this.context.cwd,".gitattributes");le.existsSync(U)||(await le.writeFilePromise(U,N),h.push(U));let W={"*":{charset:"utf-8",endOfLine:"lf",indentSize:2,indentStyle:"space",insertFinalNewline:!0}};je.mergeIntoTarget(W,r.get("initEditorConfig"));let te=`root = true +`;for(let[Ae,ce]of Object.entries(W)){te+=` +[${Ae}] +`;for(let[me,pe]of Object.entries(ce)){let Be=me.replace(/[A-Z]/g,Ce=>`_${Ce.toLowerCase()}`);te+=`${Be} = ${pe} +`}}let ie=K.join(this.context.cwd,".editorconfig");le.existsSync(ie)||(await le.writeFilePromise(ie,te),h.push(ie)),await this.cli.run(["install"],{quiet:!0}),await this.initialize(),le.existsSync(K.join(this.context.cwd,".git"))||(await Gr.execvp("git",["init"],{cwd:this.context.cwd}),await Gr.execvp("git",["add","--",...h],{cwd:this.context.cwd}),await Gr.execvp("git",["commit","--allow-empty","-m","First commit"],{cwd:this.context.cwd}))}}};var sw=class extends Z0{constructor(){super(...arguments);this.initializer=ge.String();this.argv=ge.Proxy()}static{this.paths=[["init"]]}async initialize(){this.context.stdout.write(` +`),await this.cli.run(["dlx",this.initializer,...this.argv],{quiet:!0})}};var RDt={configuration:{initScope:{description:"Scope used when creating packages via the init command",type:"STRING",default:null},initFields:{description:"Additional fields to set when creating packages via the init command",type:"MAP",valueDefinition:{description:"",type:"ANY"}},initEditorConfig:{description:"Extra rules to define in the generator editorconfig",type:"MAP",valueDefinition:{description:"",type:"ANY"}}},commands:[Z0,sw]},FDt=RDt;var TY={};Vt(TY,{SearchCommand:()=>Bw,UpgradeInteractiveCommand:()=>vw,default:()=>cTt});Ve();var xve=et(ye("os"));function ow({stdout:t}){if(xve.default.endianness()==="BE")throw new Error("Interactive commands cannot be used on big-endian systems because ink depends on yoga-layout-prebuilt which only supports little-endian architectures");if(!t.isTTY)throw new Error("Interactive commands can only be used inside a TTY environment")}Wt();var HSe=et(Z9()),X9={appId:"OFCNCOG2CU",apiKey:"6fe4476ee5a1832882e326b506d14126",indexName:"npm-search"},QPt=(0,HSe.default)(X9.appId,X9.apiKey).initIndex(X9.indexName),$9=async(t,e=0)=>await QPt.search(t,{analyticsTags:["yarn-plugin-interactive-tools"],attributesToRetrieve:["name","version","owner","repository","humanDownloadsLast30Days"],page:e,hitsPerPage:10});var LD=["regular","dev","peer"],Bw=class extends ut{static{this.paths=[["search"]]}static{this.usage=ot.Usage({category:"Interactive commands",description:"open the search interface",details:` + This command opens a fullscreen terminal interface where you can search for and install packages from the npm registry. + `,examples:[["Open the search window","yarn search"]]})}async execute(){ow(this.context);let{Gem:e}=await Promise.resolve().then(()=>($F(),CY)),{ScrollableItems:r}=await Promise.resolve().then(()=>(nN(),rN)),{useKeypress:s}=await Promise.resolve().then(()=>(FD(),zPe)),{useMinistore:a}=await Promise.resolve().then(()=>(bY(),DY)),{renderForm:n}=await Promise.resolve().then(()=>(aN(),oN)),{default:c}=await Promise.resolve().then(()=>et(oxe())),{Box:f,Text:p}=await Promise.resolve().then(()=>et(Vc())),{default:h,useEffect:E,useState:C}=await Promise.resolve().then(()=>et(hn())),S=await ze.find(this.context.cwd,this.context.plugins),P=()=>h.createElement(f,{flexDirection:"row"},h.createElement(f,{flexDirection:"column",width:48},h.createElement(f,null,h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},""),"/",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to move between packages.")),h.createElement(f,null,h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to select a package.")),h.createElement(f,null,h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," again to change the target."))),h.createElement(f,{flexDirection:"column"},h.createElement(f,{marginLeft:1},h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to install the selected packages.")),h.createElement(f,{marginLeft:1},h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to abort.")))),I=()=>h.createElement(h.Fragment,null,h.createElement(f,{width:15},h.createElement(p,{bold:!0,underline:!0,color:"gray"},"Owner")),h.createElement(f,{width:11},h.createElement(p,{bold:!0,underline:!0,color:"gray"},"Version")),h.createElement(f,{width:10},h.createElement(p,{bold:!0,underline:!0,color:"gray"},"Downloads"))),R=()=>h.createElement(f,{width:17},h.createElement(p,{bold:!0,underline:!0,color:"gray"},"Target")),N=({hit:pe,active:Be})=>{let[Ce,g]=a(pe.name,null);s({active:Be},(fe,se)=>{if(se.name!=="space")return;if(!Ce){g(LD[0]);return}let X=LD.indexOf(Ce)+1;X===LD.length?g(null):g(LD[X])},[Ce,g]);let we=q.parseIdent(pe.name),Ee=q.prettyIdent(S,we);return h.createElement(f,null,h.createElement(f,{width:45},h.createElement(p,{bold:!0,wrap:"wrap"},Ee)),h.createElement(f,{width:14,marginLeft:1},h.createElement(p,{bold:!0,wrap:"truncate"},pe.owner.name)),h.createElement(f,{width:10,marginLeft:1},h.createElement(p,{italic:!0,wrap:"truncate"},pe.version)),h.createElement(f,{width:16,marginLeft:1},h.createElement(p,null,pe.humanDownloadsLast30Days)))},U=({name:pe,active:Be})=>{let[Ce]=a(pe,null),g=q.parseIdent(pe);return h.createElement(f,null,h.createElement(f,{width:47},h.createElement(p,{bold:!0}," - ",q.prettyIdent(S,g))),LD.map(we=>h.createElement(f,{key:we,width:14,marginLeft:1},h.createElement(p,null," ",h.createElement(e,{active:Ce===we})," ",h.createElement(p,{bold:!0},we)))))},W=()=>h.createElement(f,{marginTop:1},h.createElement(p,null,"Powered by Algolia.")),ie=await n(({useSubmit:pe})=>{let Be=a();pe(Be);let Ce=Array.from(Be.keys()).filter(j=>Be.get(j)!==null),[g,we]=C(""),[Ee,fe]=C(0),[se,X]=C([]),De=j=>{j.match(/\t| /)||we(j)},Re=async()=>{fe(0);let j=await $9(g);j.query===g&&X(j.hits)},gt=async()=>{let j=await $9(g,Ee+1);j.query===g&&j.page-1===Ee&&(fe(j.page),X([...se,...j.hits]))};return E(()=>{g?Re():X([])},[g]),h.createElement(f,{flexDirection:"column"},h.createElement(P,null),h.createElement(f,{flexDirection:"row",marginTop:1},h.createElement(p,{bold:!0},"Search: "),h.createElement(f,{width:41},h.createElement(c,{value:g,onChange:De,placeholder:"i.e. babel, webpack, react...",showCursor:!1})),h.createElement(I,null)),se.length?h.createElement(r,{radius:2,loop:!1,children:se.map(j=>h.createElement(N,{key:j.name,hit:j,active:!1})),willReachEnd:gt}):h.createElement(p,{color:"gray"},"Start typing..."),h.createElement(f,{flexDirection:"row",marginTop:1},h.createElement(f,{width:49},h.createElement(p,{bold:!0},"Selected:")),h.createElement(R,null)),Ce.length?Ce.map(j=>h.createElement(U,{key:j,name:j,active:!1})):h.createElement(p,{color:"gray"},"No selected packages..."),h.createElement(W,null))},{},{stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr});if(typeof ie>"u")return 1;let Ae=Array.from(ie.keys()).filter(pe=>ie.get(pe)==="regular"),ce=Array.from(ie.keys()).filter(pe=>ie.get(pe)==="dev"),me=Array.from(ie.keys()).filter(pe=>ie.get(pe)==="peer");return Ae.length&&await this.cli.run(["add",...Ae]),ce.length&&await this.cli.run(["add","--dev",...ce]),me&&await this.cli.run(["add","--peer",...me]),0}};Ve();Wt();yG();var pxe=et(fi()),Axe=/^((?:[\^~]|>=?)?)([0-9]+)(\.[0-9]+)(\.[0-9]+)((?:-\S+)?)$/;function hxe(t,e){return t.length>0?[t.slice(0,e)].concat(hxe(t.slice(e),e)):[]}var vw=class extends ut{static{this.paths=[["upgrade-interactive"]]}static{this.usage=ot.Usage({category:"Interactive commands",description:"open the upgrade interface",details:` + This command opens a fullscreen terminal interface where you can see any out of date packages used by your application, their status compared to the latest versions available on the remote registry, and select packages to upgrade. + `,examples:[["Open the upgrade window","yarn upgrade-interactive"]]})}async execute(){ow(this.context);let{ItemOptions:e}=await Promise.resolve().then(()=>(fxe(),uxe)),{Pad:r}=await Promise.resolve().then(()=>(QY(),cxe)),{ScrollableItems:s}=await Promise.resolve().then(()=>(nN(),rN)),{useMinistore:a}=await Promise.resolve().then(()=>(bY(),DY)),{renderForm:n}=await Promise.resolve().then(()=>(aN(),oN)),{Box:c,Text:f}=await Promise.resolve().then(()=>et(Vc())),{default:p,useEffect:h,useRef:E,useState:C}=await Promise.resolve().then(()=>et(hn())),S=await ze.find(this.context.cwd,this.context.plugins),{project:P,workspace:I}=await Tt.find(S,this.context.cwd),R=await Jr.find(S);if(!I)throw new ar(P.cwd,this.context.cwd);await P.restoreInstallState({restoreResolutions:!1});let N=this.context.stdout.rows-7,U=(we,Ee)=>{let fe=ICe(we,Ee),se="";for(let X of fe)X.added?se+=he.pretty(S,X.value,"green"):X.removed||(se+=X.value);return se},W=(we,Ee)=>{if(we===Ee)return Ee;let fe=q.parseRange(we),se=q.parseRange(Ee),X=fe.selector.match(Axe),De=se.selector.match(Axe);if(!X||!De)return U(we,Ee);let Re=["gray","red","yellow","green","magenta"],gt=null,j="";for(let rt=1;rt{let se=await Xu.fetchDescriptorFrom(we,fe,{project:P,cache:R,preserveModifier:Ee,workspace:I});return se!==null?se.range:we.range},ie=async we=>{let Ee=pxe.default.valid(we.range)?`^${we.range}`:we.range,[fe,se]=await Promise.all([te(we,we.range,Ee).catch(()=>null),te(we,we.range,"latest").catch(()=>null)]),X=[{value:null,label:we.range}];return fe&&fe!==we.range?X.push({value:fe,label:W(we.range,fe)}):X.push({value:null,label:""}),se&&se!==fe&&se!==we.range?X.push({value:se,label:W(we.range,se)}):X.push({value:null,label:""}),X},Ae=()=>p.createElement(c,{flexDirection:"row"},p.createElement(c,{flexDirection:"column",width:49},p.createElement(c,{marginLeft:1},p.createElement(f,null,"Press ",p.createElement(f,{bold:!0,color:"cyanBright"},""),"/",p.createElement(f,{bold:!0,color:"cyanBright"},"")," to select packages.")),p.createElement(c,{marginLeft:1},p.createElement(f,null,"Press ",p.createElement(f,{bold:!0,color:"cyanBright"},""),"/",p.createElement(f,{bold:!0,color:"cyanBright"},"")," to select versions."))),p.createElement(c,{flexDirection:"column"},p.createElement(c,{marginLeft:1},p.createElement(f,null,"Press ",p.createElement(f,{bold:!0,color:"cyanBright"},"")," to install.")),p.createElement(c,{marginLeft:1},p.createElement(f,null,"Press ",p.createElement(f,{bold:!0,color:"cyanBright"},"")," to abort.")))),ce=()=>p.createElement(c,{flexDirection:"row",paddingTop:1,paddingBottom:1},p.createElement(c,{width:50},p.createElement(f,{bold:!0},p.createElement(f,{color:"greenBright"},"?")," Pick the packages you want to upgrade.")),p.createElement(c,{width:17},p.createElement(f,{bold:!0,underline:!0,color:"gray"},"Current")),p.createElement(c,{width:17},p.createElement(f,{bold:!0,underline:!0,color:"gray"},"Range")),p.createElement(c,{width:17},p.createElement(f,{bold:!0,underline:!0,color:"gray"},"Latest"))),me=({active:we,descriptor:Ee,suggestions:fe})=>{let[se,X]=a(Ee.descriptorHash,null),De=q.stringifyIdent(Ee),Re=Math.max(0,45-De.length);return p.createElement(p.Fragment,null,p.createElement(c,null,p.createElement(c,{width:45},p.createElement(f,{bold:!0},q.prettyIdent(S,Ee)),p.createElement(r,{active:we,length:Re})),p.createElement(e,{active:we,options:fe,value:se,skewer:!0,onChange:X,sizes:[17,17,17]})))},pe=({dependencies:we})=>{let[Ee,fe]=C(we.map(()=>null)),se=E(!0),X=async De=>{let Re=await ie(De);return Re.filter(gt=>gt.label!=="").length<=1?null:{descriptor:De,suggestions:Re}};return h(()=>()=>{se.current=!1},[]),h(()=>{let De=Math.trunc(N*1.75),Re=we.slice(0,De),gt=we.slice(De),j=hxe(gt,N),rt=Re.map(X).reduce(async(Fe,Ne)=>{await Fe;let Pe=await Ne;Pe!==null&&se.current&&fe(Ye=>{let ke=Ye.findIndex(_e=>_e===null),it=[...Ye];return it[ke]=Pe,it})},Promise.resolve());j.reduce((Fe,Ne)=>Promise.all(Ne.map(Pe=>Promise.resolve().then(()=>X(Pe)))).then(async Pe=>{Pe=Pe.filter(Ye=>Ye!==null),await Fe,se.current&&fe(Ye=>{let ke=Ye.findIndex(it=>it===null);return Ye.slice(0,ke).concat(Pe).concat(Ye.slice(ke+Pe.length))})}),rt).then(()=>{se.current&&fe(Fe=>Fe.filter(Ne=>Ne!==null))})},[]),Ee.length?p.createElement(s,{radius:N>>1,children:Ee.map((De,Re)=>De!==null?p.createElement(me,{key:Re,active:!1,descriptor:De.descriptor,suggestions:De.suggestions}):p.createElement(f,{key:Re},"Loading..."))}):p.createElement(f,null,"No upgrades found")},Ce=await n(({useSubmit:we})=>{we(a());let Ee=new Map;for(let se of P.workspaces)for(let X of["dependencies","devDependencies"])for(let De of se.manifest[X].values())P.tryWorkspaceByDescriptor(De)===null&&(De.range.startsWith("link:")||Ee.set(De.descriptorHash,De));let fe=je.sortMap(Ee.values(),se=>q.stringifyDescriptor(se));return p.createElement(c,{flexDirection:"column"},p.createElement(Ae,null),p.createElement(ce,null),p.createElement(pe,{dependencies:fe}))},{},{stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr});if(typeof Ce>"u")return 1;let g=!1;for(let we of P.workspaces)for(let Ee of["dependencies","devDependencies"]){let fe=we.manifest[Ee];for(let se of fe.values()){let X=Ce.get(se.descriptorHash);typeof X<"u"&&X!==null&&(fe.set(se.identHash,q.makeDescriptor(se,X)),g=!0)}}return g?await P.installWithNewReport({quiet:this.context.quiet,stdout:this.context.stdout},{cache:R}):0}};var lTt={commands:[Bw,vw]},cTt=lTt;var FY={};Vt(FY,{default:()=>pTt});Ve();var _D="jsr:";Ve();Ve();function Sw(t){let e=t.range.slice(4);if(Or.validRange(e))return q.makeDescriptor(t,`npm:${q.stringifyIdent(q.wrapIdentIntoScope(t,"jsr"))}@${e}`);let r=q.tryParseDescriptor(e,!0);if(r!==null)return q.makeDescriptor(t,`npm:${q.stringifyIdent(q.wrapIdentIntoScope(r,"jsr"))}@${r.range}`);throw new Error(`Invalid range: ${t.range}`)}function Dw(t){return q.makeLocator(q.wrapIdentIntoScope(t,"jsr"),`npm:${t.reference.slice(4)}`)}function RY(t){return q.makeLocator(q.unwrapIdentFromScope(t,"jsr"),`jsr:${t.reference.slice(4)}`)}var lN=class{supports(e,r){return e.reference.startsWith(_D)}getLocalPath(e,r){let s=Dw(e);return r.fetcher.getLocalPath(s,r)}fetch(e,r){let s=Dw(e);return r.fetcher.fetch(s,r)}};var cN=class{supportsDescriptor(e,r){return!!e.range.startsWith(_D)}supportsLocator(e,r){return!!e.reference.startsWith(_D)}shouldPersistResolution(e,r){let s=Dw(e);return r.resolver.shouldPersistResolution(s,r)}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{inner:Sw(e)}}async getCandidates(e,r,s){let a=s.project.configuration.normalizeDependency(Sw(e));return(await s.resolver.getCandidates(a,r,s)).map(c=>RY(c))}async getSatisfying(e,r,s,a){let n=a.project.configuration.normalizeDependency(Sw(e));return a.resolver.getSatisfying(n,r,s,a)}async resolve(e,r){let s=Dw(e),a=await r.resolver.resolve(s,r);return{...a,...RY(a)}}};var uTt=["dependencies","devDependencies","peerDependencies"];function fTt(t,e){for(let r of uTt)for(let s of t.manifest.getForScope(r).values()){if(!s.range.startsWith("jsr:"))continue;let a=Sw(s),n=r==="dependencies"?q.makeDescriptor(s,"unknown"):null,c=n!==null&&t.manifest.ensureDependencyMeta(n).optional?"optionalDependencies":r;e[c][q.stringifyIdent(s)]=a.range}}var ATt={hooks:{beforeWorkspacePacking:fTt},resolvers:[cN],fetchers:[lN]},pTt=ATt;var NY={};Vt(NY,{LinkFetcher:()=>UD,LinkResolver:()=>HD,PortalFetcher:()=>jD,PortalResolver:()=>qD,default:()=>gTt});Ve();bt();var sh="portal:",oh="link:";var UD=class{supports(e,r){return!!e.reference.startsWith(oh)}getLocalPath(e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(e.reference,{protocol:oh});if(K.isAbsolute(a))return a;let n=r.fetcher.getLocalPath(s,r);return n===null?null:K.resolve(n,a)}async fetch(e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(e.reference,{protocol:oh}),n=K.isAbsolute(a)?{packageFs:new Sn(vt.root),prefixPath:vt.dot,localPath:vt.root}:await r.fetcher.fetch(s,r),c=n.localPath?{packageFs:new Sn(vt.root),prefixPath:K.relative(vt.root,n.localPath),localPath:vt.root}:n;n!==c&&n.releaseFs&&n.releaseFs();let f=c.packageFs,p=K.resolve(c.localPath??c.packageFs.getRealPath(),c.prefixPath,a);return n.localPath?{packageFs:new Sn(p,{baseFs:f}),releaseFs:c.releaseFs,prefixPath:vt.dot,discardFromLookup:!0,localPath:p}:{packageFs:new jf(p,{baseFs:f}),releaseFs:c.releaseFs,prefixPath:vt.dot,discardFromLookup:!0}}};Ve();bt();var HD=class{supportsDescriptor(e,r){return!!e.range.startsWith(oh)}supportsLocator(e,r){return!!e.reference.startsWith(oh)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){return q.bindDescriptor(e,{locator:q.stringifyLocator(r)})}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){let a=e.range.slice(oh.length);return[q.makeLocator(e,`${oh}${ue.toPortablePath(a)}`)]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){return{...e,version:"0.0.0",languageName:r.project.configuration.get("defaultLanguageName"),linkType:"SOFT",conditions:null,dependencies:new Map,peerDependencies:new Map,dependenciesMeta:new Map,peerDependenciesMeta:new Map,bin:new Map}}};Ve();bt();var jD=class{supports(e,r){return!!e.reference.startsWith(sh)}getLocalPath(e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(e.reference,{protocol:sh});if(K.isAbsolute(a))return a;let n=r.fetcher.getLocalPath(s,r);return n===null?null:K.resolve(n,a)}async fetch(e,r){let{parentLocator:s,path:a}=q.parseFileStyleRange(e.reference,{protocol:sh}),n=K.isAbsolute(a)?{packageFs:new Sn(vt.root),prefixPath:vt.dot,localPath:vt.root}:await r.fetcher.fetch(s,r),c=n.localPath?{packageFs:new Sn(vt.root),prefixPath:K.relative(vt.root,n.localPath),localPath:vt.root}:n;n!==c&&n.releaseFs&&n.releaseFs();let f=c.packageFs,p=K.resolve(c.localPath??c.packageFs.getRealPath(),c.prefixPath,a);return n.localPath?{packageFs:new Sn(p,{baseFs:f}),releaseFs:c.releaseFs,prefixPath:vt.dot,localPath:p}:{packageFs:new jf(p,{baseFs:f}),releaseFs:c.releaseFs,prefixPath:vt.dot}}};Ve();Ve();bt();var qD=class{supportsDescriptor(e,r){return!!e.range.startsWith(sh)}supportsLocator(e,r){return!!e.reference.startsWith(sh)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){return q.bindDescriptor(e,{locator:q.stringifyLocator(r)})}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){let a=e.range.slice(sh.length);return[q.makeLocator(e,`${sh}${ue.toPortablePath(a)}`)]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){if(!r.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let s=await r.fetchOptions.fetcher.fetch(e,r.fetchOptions),a=await je.releaseAfterUseAsync(async()=>await Ht.find(s.prefixPath,{baseFs:s.packageFs}),s.releaseFs);return{...e,version:a.version||"0.0.0",languageName:a.languageName||r.project.configuration.get("defaultLanguageName"),linkType:"SOFT",conditions:a.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(a.dependencies),peerDependencies:a.peerDependencies,dependenciesMeta:a.dependenciesMeta,peerDependenciesMeta:a.peerDependenciesMeta,bin:a.bin}}};var hTt={fetchers:[UD,jD],resolvers:[HD,qD]},gTt=hTt;var yV={};Vt(yV,{NodeModulesLinker:()=>ib,NodeModulesMode:()=>hV,PnpLooseLinker:()=>sb,default:()=>QRt});bt();Ve();bt();bt();var LY=(t,e)=>`${t}@${e}`,gxe=(t,e)=>{let r=e.indexOf("#"),s=r>=0?e.substring(r+1):e;return LY(t,s)};var mxe=(t,e={})=>{let r=e.debugLevel||Number(process.env.NM_DEBUG_LEVEL||-1),s=e.check||r>=9,a=e.hoistingLimits||new Map,n={check:s,debugLevel:r,hoistingLimits:a,fastLookupPossible:!0},c;n.debugLevel>=0&&(c=Date.now());let f=wTt(t,n),p=!1,h=0;do{let E=MY(f,[f],new Set([f.locator]),new Map,n);p=E.anotherRoundNeeded||E.isGraphChanged,n.fastLookupPossible=!1,h++}while(p);if(n.debugLevel>=0&&console.log(`hoist time: ${Date.now()-c}ms, rounds: ${h}`),n.debugLevel>=1){let E=GD(f);if(MY(f,[f],new Set([f.locator]),new Map,n).isGraphChanged)throw new Error(`The hoisting result is not terminal, prev tree: +${E}, next tree: +${GD(f)}`);let S=yxe(f);if(S)throw new Error(`${S}, after hoisting finished: +${GD(f)}`)}return n.debugLevel>=2&&console.log(GD(f)),BTt(f)},dTt=t=>{let e=t[t.length-1],r=new Map,s=new Set,a=n=>{if(!s.has(n)){s.add(n);for(let c of n.hoistedDependencies.values())r.set(c.name,c);for(let c of n.dependencies.values())n.peerNames.has(c.name)||a(c)}};return a(e),r},mTt=t=>{let e=t[t.length-1],r=new Map,s=new Set,a=new Set,n=(c,f)=>{if(s.has(c))return;s.add(c);for(let h of c.hoistedDependencies.values())if(!f.has(h.name)){let E;for(let C of t)E=C.dependencies.get(h.name),E&&r.set(E.name,E)}let p=new Set;for(let h of c.dependencies.values())p.add(h.name);for(let h of c.dependencies.values())c.peerNames.has(h.name)||n(h,p)};return n(e,a),r},dxe=(t,e)=>{if(e.decoupled)return e;let{name:r,references:s,ident:a,locator:n,dependencies:c,originalDependencies:f,hoistedDependencies:p,peerNames:h,reasons:E,isHoistBorder:C,hoistPriority:S,dependencyKind:P,hoistedFrom:I,hoistedTo:R}=e,N={name:r,references:new Set(s),ident:a,locator:n,dependencies:new Map(c),originalDependencies:new Map(f),hoistedDependencies:new Map(p),peerNames:new Set(h),reasons:new Map(E),decoupled:!0,isHoistBorder:C,hoistPriority:S,dependencyKind:P,hoistedFrom:new Map(I),hoistedTo:new Map(R)},U=N.dependencies.get(r);return U&&U.ident==N.ident&&N.dependencies.set(r,N),t.dependencies.set(N.name,N),N},yTt=(t,e)=>{let r=new Map([[t.name,[t.ident]]]);for(let a of t.dependencies.values())t.peerNames.has(a.name)||r.set(a.name,[a.ident]);let s=Array.from(e.keys());s.sort((a,n)=>{let c=e.get(a),f=e.get(n);if(f.hoistPriority!==c.hoistPriority)return f.hoistPriority-c.hoistPriority;{let p=c.dependents.size+c.peerDependents.size;return f.dependents.size+f.peerDependents.size-p}});for(let a of s){let n=a.substring(0,a.indexOf("@",1)),c=a.substring(n.length+1);if(!t.peerNames.has(n)){let f=r.get(n);f||(f=[],r.set(n,f)),f.indexOf(c)<0&&f.push(c)}}return r},OY=t=>{let e=new Set,r=(s,a=new Set)=>{if(!a.has(s)){a.add(s);for(let n of s.peerNames)if(!t.peerNames.has(n)){let c=t.dependencies.get(n);c&&!e.has(c)&&r(c,a)}e.add(s)}};for(let s of t.dependencies.values())t.peerNames.has(s.name)||r(s);return e},MY=(t,e,r,s,a,n=new Set)=>{let c=e[e.length-1];if(n.has(c))return{anotherRoundNeeded:!1,isGraphChanged:!1};n.add(c);let f=vTt(c),p=yTt(c,f),h=t==c?new Map:a.fastLookupPossible?dTt(e):mTt(e),E,C=!1,S=!1,P=new Map(Array.from(p.entries()).map(([R,N])=>[R,N[0]])),I=new Map;do{let R=CTt(t,e,r,h,P,p,s,I,a);R.isGraphChanged&&(S=!0),R.anotherRoundNeeded&&(C=!0),E=!1;for(let[N,U]of p)U.length>1&&!c.dependencies.has(N)&&(P.delete(N),U.shift(),P.set(N,U[0]),E=!0)}while(E);for(let R of c.dependencies.values())if(!c.peerNames.has(R.name)&&!r.has(R.locator)){r.add(R.locator);let N=MY(t,[...e,R],r,I,a);N.isGraphChanged&&(S=!0),N.anotherRoundNeeded&&(C=!0),r.delete(R.locator)}return{anotherRoundNeeded:C,isGraphChanged:S}},ETt=t=>{for(let[e,r]of t.dependencies)if(!t.peerNames.has(e)&&r.ident!==t.ident)return!0;return!1},ITt=(t,e,r,s,a,n,c,f,{outputReason:p,fastLookupPossible:h})=>{let E,C=null,S=new Set;p&&(E=`${Array.from(e).map(N=>Io(N)).join("\u2192")}`);let P=r[r.length-1],R=!(s.ident===P.ident);if(p&&!R&&(C="- self-reference"),R&&(R=s.dependencyKind!==1,p&&!R&&(C="- workspace")),R&&s.dependencyKind===2&&(R=!ETt(s),p&&!R&&(C="- external soft link with unhoisted dependencies")),R&&(R=!t.peerNames.has(s.name),p&&!R&&(C=`- cannot shadow peer: ${Io(t.originalDependencies.get(s.name).locator)} at ${E}`)),R){let N=!1,U=a.get(s.name);if(N=!U||U.ident===s.ident,p&&!N&&(C=`- filled by: ${Io(U.locator)} at ${E}`),N)for(let W=r.length-1;W>=1;W--){let ie=r[W].dependencies.get(s.name);if(ie&&ie.ident!==s.ident){N=!1;let Ae=f.get(P);Ae||(Ae=new Set,f.set(P,Ae)),Ae.add(s.name),p&&(C=`- filled by ${Io(ie.locator)} at ${r.slice(0,W).map(ce=>Io(ce.locator)).join("\u2192")}`);break}}R=N}if(R&&(R=n.get(s.name)===s.ident,p&&!R&&(C=`- filled by: ${Io(c.get(s.name)[0])} at ${E}`)),R){let N=!0,U=new Set(s.peerNames);for(let W=r.length-1;W>=1;W--){let te=r[W];for(let ie of U){if(te.peerNames.has(ie)&&te.originalDependencies.has(ie))continue;let Ae=te.dependencies.get(ie);Ae&&t.dependencies.get(ie)!==Ae&&(W===r.length-1?S.add(Ae):(S=null,N=!1,p&&(C=`- peer dependency ${Io(Ae.locator)} from parent ${Io(te.locator)} was not hoisted to ${E}`))),U.delete(ie)}if(!N)break}R=N}if(R&&!h)for(let N of s.hoistedDependencies.values()){let U=a.get(N.name)||t.dependencies.get(N.name);if(!U||N.ident!==U.ident){R=!1,p&&(C=`- previously hoisted dependency mismatch, needed: ${Io(N.locator)}, available: ${Io(U?.locator)}`);break}}return S!==null&&S.size>0?{isHoistable:2,dependsOn:S,reason:C}:{isHoistable:R?0:1,reason:C}},uN=t=>`${t.name}@${t.locator}`,CTt=(t,e,r,s,a,n,c,f,p)=>{let h=e[e.length-1],E=new Set,C=!1,S=!1,P=(U,W,te,ie,Ae)=>{if(E.has(ie))return;let ce=[...W,uN(ie)],me=[...te,uN(ie)],pe=new Map,Be=new Map;for(let fe of OY(ie)){let se=ITt(h,r,[h,...U,ie],fe,s,a,n,f,{outputReason:p.debugLevel>=2,fastLookupPossible:p.fastLookupPossible});if(Be.set(fe,se),se.isHoistable===2)for(let X of se.dependsOn){let De=pe.get(X.name)||new Set;De.add(fe.name),pe.set(X.name,De)}}let Ce=new Set,g=(fe,se,X)=>{if(!Ce.has(fe)){Ce.add(fe),Be.set(fe,{isHoistable:1,reason:X});for(let De of pe.get(fe.name)||[])g(ie.dependencies.get(De),se,p.debugLevel>=2?`- peer dependency ${Io(fe.locator)} from parent ${Io(ie.locator)} was not hoisted`:"")}};for(let[fe,se]of Be)se.isHoistable===1&&g(fe,se,se.reason);let we=!1;for(let fe of Be.keys())if(!Ce.has(fe)){S=!0;let se=c.get(ie);se&&se.has(fe.name)&&(C=!0),we=!0,ie.dependencies.delete(fe.name),ie.hoistedDependencies.set(fe.name,fe),ie.reasons.delete(fe.name);let X=h.dependencies.get(fe.name);if(p.debugLevel>=2){let De=Array.from(W).concat([ie.locator]).map(gt=>Io(gt)).join("\u2192"),Re=h.hoistedFrom.get(fe.name);Re||(Re=[],h.hoistedFrom.set(fe.name,Re)),Re.push(De),ie.hoistedTo.set(fe.name,Array.from(e).map(gt=>Io(gt.locator)).join("\u2192"))}if(!X)h.ident!==fe.ident&&(h.dependencies.set(fe.name,fe),Ae.add(fe));else for(let De of fe.references)X.references.add(De)}if(ie.dependencyKind===2&&we&&(C=!0),p.check){let fe=yxe(t);if(fe)throw new Error(`${fe}, after hoisting dependencies of ${[h,...U,ie].map(se=>Io(se.locator)).join("\u2192")}: +${GD(t)}`)}let Ee=OY(ie);for(let fe of Ee)if(Ce.has(fe)){let se=Be.get(fe);if((a.get(fe.name)===fe.ident||!ie.reasons.has(fe.name))&&se.isHoistable!==0&&ie.reasons.set(fe.name,se.reason),!fe.isHoistBorder&&me.indexOf(uN(fe))<0){E.add(ie);let De=dxe(ie,fe);P([...U,ie],ce,me,De,R),E.delete(ie)}}},I,R=new Set(OY(h)),N=Array.from(e).map(U=>uN(U));do{I=R,R=new Set;for(let U of I){if(U.locator===h.locator||U.isHoistBorder)continue;let W=dxe(h,U);P([],Array.from(r),N,W,R)}}while(R.size>0);return{anotherRoundNeeded:C,isGraphChanged:S}},yxe=t=>{let e=[],r=new Set,s=new Set,a=(n,c,f)=>{if(r.has(n)||(r.add(n),s.has(n)))return;let p=new Map(c);for(let h of n.dependencies.values())n.peerNames.has(h.name)||p.set(h.name,h);for(let h of n.originalDependencies.values()){let E=p.get(h.name),C=()=>`${Array.from(s).concat([n]).map(S=>Io(S.locator)).join("\u2192")}`;if(n.peerNames.has(h.name)){let S=c.get(h.name);(S!==E||!S||S.ident!==h.ident)&&e.push(`${C()} - broken peer promise: expected ${h.ident} but found ${S&&S.ident}`)}else{let S=f.hoistedFrom.get(n.name),P=n.hoistedTo.get(h.name),I=`${S?` hoisted from ${S.join(", ")}`:""}`,R=`${P?` hoisted to ${P}`:""}`,N=`${C()}${I}`;E?E.ident!==h.ident&&e.push(`${N} - broken require promise for ${h.name}${R}: expected ${h.ident}, but found: ${E.ident}`):e.push(`${N} - broken require promise: no required dependency ${h.name}${R} found`)}}s.add(n);for(let h of n.dependencies.values())n.peerNames.has(h.name)||a(h,p,n);s.delete(n)};return a(t,t.dependencies,t),e.join(` +`)},wTt=(t,e)=>{let{identName:r,name:s,reference:a,peerNames:n}=t,c={name:s,references:new Set([a]),locator:LY(r,a),ident:gxe(r,a),dependencies:new Map,originalDependencies:new Map,hoistedDependencies:new Map,peerNames:new Set(n),reasons:new Map,decoupled:!0,isHoistBorder:!0,hoistPriority:0,dependencyKind:1,hoistedFrom:new Map,hoistedTo:new Map},f=new Map([[t,c]]),p=(h,E)=>{let C=f.get(h),S=!!C;if(!C){let{name:P,identName:I,reference:R,peerNames:N,hoistPriority:U,dependencyKind:W}=h,te=e.hoistingLimits.get(E.locator);C={name:P,references:new Set([R]),locator:LY(I,R),ident:gxe(I,R),dependencies:new Map,originalDependencies:new Map,hoistedDependencies:new Map,peerNames:new Set(N),reasons:new Map,decoupled:!0,isHoistBorder:te?te.has(P):!1,hoistPriority:U||0,dependencyKind:W||0,hoistedFrom:new Map,hoistedTo:new Map},f.set(h,C)}if(E.dependencies.set(h.name,C),E.originalDependencies.set(h.name,C),S){let P=new Set,I=R=>{if(!P.has(R)){P.add(R),R.decoupled=!1;for(let N of R.dependencies.values())R.peerNames.has(N.name)||I(N)}};I(C)}else for(let P of h.dependencies)p(P,C)};for(let h of t.dependencies)p(h,c);return c},_Y=t=>t.substring(0,t.indexOf("@",1)),BTt=t=>{let e={name:t.name,identName:_Y(t.locator),references:new Set(t.references),dependencies:new Set},r=new Set([t]),s=(a,n,c)=>{let f=r.has(a),p;if(n===a)p=c;else{let{name:h,references:E,locator:C}=a;p={name:h,identName:_Y(C),references:E,dependencies:new Set}}if(c.dependencies.add(p),!f){r.add(a);for(let h of a.dependencies.values())a.peerNames.has(h.name)||s(h,a,p);r.delete(a)}};for(let a of t.dependencies.values())s(a,t,e);return e},vTt=t=>{let e=new Map,r=new Set([t]),s=c=>`${c.name}@${c.ident}`,a=c=>{let f=s(c),p=e.get(f);return p||(p={dependents:new Set,peerDependents:new Set,hoistPriority:0},e.set(f,p)),p},n=(c,f)=>{let p=!!r.has(f);if(a(f).dependents.add(c.ident),!p){r.add(f);for(let E of f.dependencies.values()){let C=a(E);C.hoistPriority=Math.max(C.hoistPriority,E.hoistPriority),f.peerNames.has(E.name)?C.peerDependents.add(f.ident):n(f,E)}}};for(let c of t.dependencies.values())t.peerNames.has(c.name)||n(t,c);return e},Io=t=>{if(!t)return"none";let e=t.indexOf("@",1),r=t.substring(0,e);r.endsWith("$wsroot$")&&(r=`wh:${r.replace("$wsroot$","")}`);let s=t.substring(e+1);if(s==="workspace:.")return".";if(s){let a=(s.indexOf("#")>0?s.split("#")[1]:s).replace("npm:","");return s.startsWith("virtual")&&(r=`v:${r}`),a.startsWith("workspace")&&(r=`w:${r}`,a=""),`${r}${a?`@${a}`:""}`}else return`${r}`};var GD=t=>{let e=0,r=(a,n,c="")=>{if(e>5e4||n.has(a))return"";e++;let f=Array.from(a.dependencies.values()).sort((h,E)=>h.name===E.name?0:h.name>E.name?1:-1),p="";n.add(a);for(let h=0;h":"")+(S!==E.name?`a:${E.name}:`:"")+Io(E.locator)+(C?` ${C}`:"")} +`,p+=r(E,n,`${c}${h5e4?` +Tree is too large, part of the tree has been dunped +`:"")};var WD=(s=>(s.WORKSPACES="workspaces",s.DEPENDENCIES="dependencies",s.NONE="none",s))(WD||{}),Exe="node_modules",ng="$wsroot$";var YD=(t,e)=>{let{packageTree:r,hoistingLimits:s,errors:a,preserveSymlinksRequired:n}=DTt(t,e),c=null;if(a.length===0){let f=mxe(r,{hoistingLimits:s});c=PTt(t,f,e)}return{tree:c,errors:a,preserveSymlinksRequired:n}},gA=t=>`${t.name}@${t.reference}`,HY=t=>{let e=new Map;for(let[r,s]of t.entries())if(!s.dirList){let a=e.get(s.locator);a||(a={target:s.target,linkType:s.linkType,locations:[],aliases:s.aliases},e.set(s.locator,a)),a.locations.push(r)}for(let r of e.values())r.locations=r.locations.sort((s,a)=>{let n=s.split(K.delimiter).length,c=a.split(K.delimiter).length;return a===s?0:n!==c?c-n:a>s?1:-1});return e},Ixe=(t,e)=>{let r=q.isVirtualLocator(t)?q.devirtualizeLocator(t):t,s=q.isVirtualLocator(e)?q.devirtualizeLocator(e):e;return q.areLocatorsEqual(r,s)},UY=(t,e,r,s)=>{if(t.linkType!=="SOFT")return!1;let a=ue.toPortablePath(r.resolveVirtual&&e.reference&&e.reference.startsWith("virtual:")?r.resolveVirtual(t.packageLocation):t.packageLocation);return K.contains(s,a)===null},STt=t=>{let e=t.getPackageInformation(t.topLevel);if(e===null)throw new Error("Assertion failed: Expected the top-level package to have been registered");if(t.findPackageLocator(e.packageLocation)===null)throw new Error("Assertion failed: Expected the top-level package to have a physical locator");let s=ue.toPortablePath(e.packageLocation.slice(0,-1)),a=new Map,n={children:new Map},c=t.getDependencyTreeRoots(),f=new Map,p=new Set,h=(S,P)=>{let I=gA(S);if(p.has(I))return;p.add(I);let R=t.getPackageInformation(S);if(R){let N=P?gA(P):"";if(gA(S)!==N&&R.linkType==="SOFT"&&!S.reference.startsWith("link:")&&!UY(R,S,t,s)){let U=Cxe(R,S,t);(!f.get(U)||S.reference.startsWith("workspace:"))&&f.set(U,S)}for(let[U,W]of R.packageDependencies)W!==null&&(R.packagePeers.has(U)||h(t.getLocator(U,W),S))}};for(let S of c)h(S,null);let E=s.split(K.sep);for(let S of f.values()){let P=t.getPackageInformation(S),R=ue.toPortablePath(P.packageLocation.slice(0,-1)).split(K.sep).slice(E.length),N=n;for(let U of R){let W=N.children.get(U);W||(W={children:new Map},N.children.set(U,W)),N=W}N.workspaceLocator=S}let C=(S,P)=>{if(S.workspaceLocator){let I=gA(P),R=a.get(I);R||(R=new Set,a.set(I,R)),R.add(S.workspaceLocator)}for(let I of S.children.values())C(I,S.workspaceLocator||P)};for(let S of n.children.values())C(S,n.workspaceLocator);return a},DTt=(t,e)=>{let r=[],s=!1,a=new Map,n=STt(t),c=t.getPackageInformation(t.topLevel);if(c===null)throw new Error("Assertion failed: Expected the top-level package to have been registered");let f=t.findPackageLocator(c.packageLocation);if(f===null)throw new Error("Assertion failed: Expected the top-level package to have a physical locator");let p=ue.toPortablePath(c.packageLocation.slice(0,-1)),h={name:f.name,identName:f.name,reference:f.reference,peerNames:c.packagePeers,dependencies:new Set,dependencyKind:1},E=new Map,C=(P,I)=>`${gA(I)}:${P}`,S=(P,I,R,N,U,W,te,ie)=>{let Ae=C(P,R),ce=E.get(Ae),me=!!ce;!me&&R.name===f.name&&R.reference===f.reference&&(ce=h,E.set(Ae,h));let pe=UY(I,R,t,p);if(!ce){let fe=0;pe?fe=2:I.linkType==="SOFT"&&R.name.endsWith(ng)&&(fe=1),ce={name:P,identName:R.name,reference:R.reference,dependencies:new Set,peerNames:fe===1?new Set:I.packagePeers,dependencyKind:fe},E.set(Ae,ce)}let Be;if(pe?Be=2:U.linkType==="SOFT"?Be=1:Be=0,ce.hoistPriority=Math.max(ce.hoistPriority||0,Be),ie&&!pe){let fe=gA({name:N.identName,reference:N.reference}),se=a.get(fe)||new Set;a.set(fe,se),se.add(ce.name)}let Ce=new Map(I.packageDependencies);if(e.project){let fe=e.project.workspacesByCwd.get(ue.toPortablePath(I.packageLocation.slice(0,-1)));if(fe){let se=new Set([...Array.from(fe.manifest.peerDependencies.values(),X=>q.stringifyIdent(X)),...Array.from(fe.manifest.peerDependenciesMeta.keys())]);for(let X of se)Ce.has(X)||(Ce.set(X,W.get(X)||null),ce.peerNames.add(X))}}let g=gA({name:R.name.replace(ng,""),reference:R.reference}),we=n.get(g);if(we)for(let fe of we)Ce.set(`${fe.name}${ng}`,fe.reference);(I!==U||I.linkType!=="SOFT"||!pe&&(!e.selfReferencesByCwd||e.selfReferencesByCwd.get(te)))&&N.dependencies.add(ce);let Ee=R!==f&&I.linkType==="SOFT"&&!R.name.endsWith(ng)&&!pe;if(!me&&!Ee){let fe=new Map;for(let[se,X]of Ce)if(X!==null){let De=t.getLocator(se,X),Re=t.getLocator(se.replace(ng,""),X),gt=t.getPackageInformation(Re);if(gt===null)throw new Error("Assertion failed: Expected the package to have been registered");let j=UY(gt,De,t,p);if(e.validateExternalSoftLinks&&e.project&&j){gt.packageDependencies.size>0&&(s=!0);for(let[Ye,ke]of gt.packageDependencies)if(ke!==null){let it=q.parseLocator(Array.isArray(ke)?`${ke[0]}@${ke[1]}`:`${Ye}@${ke}`);if(gA(it)!==gA(De)){let _e=Ce.get(Ye);if(_e){let x=q.parseLocator(Array.isArray(_e)?`${_e[0]}@${_e[1]}`:`${Ye}@${_e}`);Ixe(x,it)||r.push({messageName:71,text:`Cannot link ${q.prettyIdent(e.project.configuration,q.parseIdent(De.name))} into ${q.prettyLocator(e.project.configuration,q.parseLocator(`${R.name}@${R.reference}`))} dependency ${q.prettyLocator(e.project.configuration,it)} conflicts with parent dependency ${q.prettyLocator(e.project.configuration,x)}`})}else{let x=fe.get(Ye);if(x){let w=x.target,b=q.parseLocator(Array.isArray(w)?`${w[0]}@${w[1]}`:`${Ye}@${w}`);Ixe(b,it)||r.push({messageName:71,text:`Cannot link ${q.prettyIdent(e.project.configuration,q.parseIdent(De.name))} into ${q.prettyLocator(e.project.configuration,q.parseLocator(`${R.name}@${R.reference}`))} dependency ${q.prettyLocator(e.project.configuration,it)} conflicts with dependency ${q.prettyLocator(e.project.configuration,b)} from sibling portal ${q.prettyIdent(e.project.configuration,q.parseIdent(x.portal.name))}`})}else fe.set(Ye,{target:it.reference,portal:De})}}}}let rt=e.hoistingLimitsByCwd?.get(te),Fe=j?te:K.relative(p,ue.toPortablePath(gt.packageLocation))||vt.dot,Ne=e.hoistingLimitsByCwd?.get(Fe);S(se,gt,De,ce,I,Ce,Fe,rt==="dependencies"||Ne==="dependencies"||Ne==="workspaces")}}};return S(f.name,c,f,h,c,c.packageDependencies,vt.dot,!1),{packageTree:h,hoistingLimits:a,errors:r,preserveSymlinksRequired:s}};function Cxe(t,e,r){let s=r.resolveVirtual&&e.reference&&e.reference.startsWith("virtual:")?r.resolveVirtual(t.packageLocation):t.packageLocation;return ue.toPortablePath(s||t.packageLocation)}function bTt(t,e,r){let s=e.getLocator(t.name.replace(ng,""),t.reference),a=e.getPackageInformation(s);if(a===null)throw new Error("Assertion failed: Expected the package to be registered");return r.pnpifyFs?{linkType:"SOFT",target:ue.toPortablePath(a.packageLocation)}:{linkType:a.linkType,target:Cxe(a,t,e)}}var PTt=(t,e,r)=>{let s=new Map,a=(E,C,S)=>{let{linkType:P,target:I}=bTt(E,t,r);return{locator:gA(E),nodePath:C,target:I,linkType:P,aliases:S}},n=E=>{let[C,S]=E.split("/");return S?{scope:C,name:S}:{scope:null,name:C}},c=new Set,f=(E,C,S)=>{if(c.has(E))return;c.add(E);let P=Array.from(E.references).sort().join("#");for(let I of E.dependencies){let R=Array.from(I.references).sort().join("#");if(I.identName===E.identName.replace(ng,"")&&R===P)continue;let N=Array.from(I.references).sort(),U={name:I.identName,reference:N[0]},{name:W,scope:te}=n(I.name),ie=te?[te,W]:[W],Ae=K.join(C,Exe),ce=K.join(Ae,...ie),me=`${S}/${U.name}`,pe=a(U,S,N.slice(1)),Be=!1;if(pe.linkType==="SOFT"&&r.project){let Ce=r.project.workspacesByCwd.get(pe.target.slice(0,-1));Be=!!(Ce&&!Ce.manifest.name)}if(!I.name.endsWith(ng)&&!Be){let Ce=s.get(ce);if(Ce){if(Ce.dirList)throw new Error(`Assertion failed: ${ce} cannot merge dir node with leaf node`);{let Ee=q.parseLocator(Ce.locator),fe=q.parseLocator(pe.locator);if(Ce.linkType!==pe.linkType)throw new Error(`Assertion failed: ${ce} cannot merge nodes with different link types ${Ce.nodePath}/${q.stringifyLocator(Ee)} and ${S}/${q.stringifyLocator(fe)}`);if(Ee.identHash!==fe.identHash)throw new Error(`Assertion failed: ${ce} cannot merge nodes with different idents ${Ce.nodePath}/${q.stringifyLocator(Ee)} and ${S}/s${q.stringifyLocator(fe)}`);pe.aliases=[...pe.aliases,...Ce.aliases,q.parseLocator(Ce.locator).reference]}}s.set(ce,pe);let g=ce.split("/"),we=g.indexOf(Exe);for(let Ee=g.length-1;we>=0&&Ee>we;Ee--){let fe=ue.toPortablePath(g.slice(0,Ee).join(K.sep)),se=g[Ee],X=s.get(fe);if(!X)s.set(fe,{dirList:new Set([se])});else if(X.dirList){if(X.dirList.has(se))break;X.dirList.add(se)}}}f(I,pe.linkType==="SOFT"?pe.target:ce,me)}},p=a({name:e.name,reference:Array.from(e.references)[0]},"",[]),h=p.target;return s.set(h,p),f(e,h,""),s};Ve();Ve();bt();bt();rA();Bc();var oV={};Vt(oV,{PnpInstaller:()=>Gm,PnpLinker:()=>og,UnplugCommand:()=>Pw,default:()=>iRt,getPnpPath:()=>ag,jsInstallUtils:()=>mA,pnpUtils:()=>nb,quotePathIfNeeded:()=>ske});bt();var ike=ye("url");Ve();Ve();bt();bt();var wxe={DEFAULT:{collapsed:!1,next:{"*":"DEFAULT"}},TOP_LEVEL:{collapsed:!1,next:{fallbackExclusionList:"FALLBACK_EXCLUSION_LIST",packageRegistryData:"PACKAGE_REGISTRY_DATA","*":"DEFAULT"}},FALLBACK_EXCLUSION_LIST:{collapsed:!1,next:{"*":"FALLBACK_EXCLUSION_ENTRIES"}},FALLBACK_EXCLUSION_ENTRIES:{collapsed:!0,next:{"*":"FALLBACK_EXCLUSION_DATA"}},FALLBACK_EXCLUSION_DATA:{collapsed:!0,next:{"*":"DEFAULT"}},PACKAGE_REGISTRY_DATA:{collapsed:!1,next:{"*":"PACKAGE_REGISTRY_ENTRIES"}},PACKAGE_REGISTRY_ENTRIES:{collapsed:!0,next:{"*":"PACKAGE_STORE_DATA"}},PACKAGE_STORE_DATA:{collapsed:!1,next:{"*":"PACKAGE_STORE_ENTRIES"}},PACKAGE_STORE_ENTRIES:{collapsed:!0,next:{"*":"PACKAGE_INFORMATION_DATA"}},PACKAGE_INFORMATION_DATA:{collapsed:!1,next:{packageDependencies:"PACKAGE_DEPENDENCIES","*":"DEFAULT"}},PACKAGE_DEPENDENCIES:{collapsed:!1,next:{"*":"PACKAGE_DEPENDENCY"}},PACKAGE_DEPENDENCY:{collapsed:!0,next:{"*":"DEFAULT"}}};function xTt(t,e,r){let s="";s+="[";for(let a=0,n=t.length;a"u"||(f!==0&&(a+=", "),a+=JSON.stringify(p),a+=": ",a+=fN(p,h,e,r).replace(/^ +/g,""),f+=1)}return a+="}",a}function TTt(t,e,r){let s=Object.keys(t),a=`${r} `,n="";n+=r,n+=`{ +`;let c=0;for(let f=0,p=s.length;f"u"||(c!==0&&(n+=",",n+=` +`),n+=a,n+=JSON.stringify(h),n+=": ",n+=fN(h,E,e,a).replace(/^ +/g,""),c+=1)}return c!==0&&(n+=` +`),n+=r,n+="}",n}function fN(t,e,r,s){let{next:a}=wxe[r],n=a[t]||a["*"];return Bxe(e,n,s)}function Bxe(t,e,r){let{collapsed:s}=wxe[e];return Array.isArray(t)?s?xTt(t,e,r):kTt(t,e,r):typeof t=="object"&&t!==null?s?QTt(t,e,r):TTt(t,e,r):JSON.stringify(t)}function vxe(t){return Bxe(t,"TOP_LEVEL","")}function VD(t,e){let r=Array.from(t);Array.isArray(e)||(e=[e]);let s=[];for(let n of e)s.push(r.map(c=>n(c)));let a=r.map((n,c)=>c);return a.sort((n,c)=>{for(let f of s){let p=f[n]f[c]?1:0;if(p!==0)return p}return 0}),a.map(n=>r[n])}function RTt(t){let e=new Map,r=VD(t.fallbackExclusionList||[],[({name:s,reference:a})=>s,({name:s,reference:a})=>a]);for(let{name:s,reference:a}of r){let n=e.get(s);typeof n>"u"&&e.set(s,n=new Set),n.add(a)}return Array.from(e).map(([s,a])=>[s,Array.from(a)])}function FTt(t){return VD(t.fallbackPool||[],([e])=>e)}function NTt(t){let e=[],r=t.dependencyTreeRoots.find(s=>t.packageRegistry.get(s.name)?.get(s.reference)?.packageLocation==="./");for(let[s,a]of VD(t.packageRegistry,([n])=>n===null?"0":`1${n}`)){if(s===null)continue;let n=[];e.push([s,n]);for(let[c,{packageLocation:f,packageDependencies:p,packagePeers:h,linkType:E,discardFromLookup:C}]of VD(a,([S])=>S===null?"0":`1${S}`)){if(c===null)continue;let S=[];s!==null&&c!==null&&!p.has(s)&&S.push([s,c]);for(let[U,W]of p)S.push([U,W]);let P=VD(S,([U])=>U),I=h&&h.size>0?Array.from(h):void 0,N={packageLocation:f,packageDependencies:P,packagePeers:I,linkType:E,discardFromLookup:C||void 0};n.push([c,N]),r&&s===r.name&&c===r.reference&&e.unshift([null,[[null,N]]])}}return e}function KD(t){return{__info:["This file is automatically generated. Do not touch it, or risk","your modifications being lost."],dependencyTreeRoots:t.dependencyTreeRoots,enableTopLevelFallback:t.enableTopLevelFallback||!1,ignorePatternData:t.ignorePattern||null,pnpZipBackend:t.pnpZipBackend,fallbackExclusionList:RTt(t),fallbackPool:FTt(t),packageRegistryData:NTt(t)}}var bxe=et(Dxe());function Pxe(t,e){return[t?`${t} +`:"",`/* eslint-disable */ +`,`// @ts-nocheck +`,`"use strict"; +`,` +`,e,` +`,(0,bxe.default)()].join("")}function OTt(t){return JSON.stringify(t,null,2)}function LTt(t){return`'${t.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/\n/g,`\\ +`)}'`}function MTt(t){return[`const RAW_RUNTIME_STATE = +`,`${LTt(vxe(t))}; + +`,`function $$SETUP_STATE(hydrateRuntimeState, basePath) { +`,` return hydrateRuntimeState(JSON.parse(RAW_RUNTIME_STATE), {basePath: basePath || __dirname}); +`,`} +`].join("")}function _Tt(){return[`function $$SETUP_STATE(hydrateRuntimeState, basePath) { +`,` const fs = require('fs'); +`,` const path = require('path'); +`,` const pnpDataFilepath = path.resolve(__dirname, ${JSON.stringify(Er.pnpData)}); +`,` return hydrateRuntimeState(JSON.parse(fs.readFileSync(pnpDataFilepath, 'utf8')), {basePath: basePath || __dirname}); +`,`} +`].join("")}function xxe(t){let e=KD(t),r=MTt(e);return Pxe(t.shebang,r)}function kxe(t){let e=KD(t),r=_Tt(),s=Pxe(t.shebang,r);return{dataFile:OTt(e),loaderFile:s}}bt();function qY(t,{basePath:e}){let r=ue.toPortablePath(e),s=K.resolve(r),a=t.ignorePatternData!==null?new RegExp(t.ignorePatternData):null,n=new Map,c=new Map(t.packageRegistryData.map(([C,S])=>[C,new Map(S.map(([P,I])=>{if(C===null!=(P===null))throw new Error("Assertion failed: The name and reference should be null, or neither should");let R=I.discardFromLookup??!1,N={name:C,reference:P},U=n.get(I.packageLocation);U?(U.discardFromLookup=U.discardFromLookup&&R,R||(U.locator=N)):n.set(I.packageLocation,{locator:N,discardFromLookup:R});let W=null;return[P,{packageDependencies:new Map(I.packageDependencies),packagePeers:new Set(I.packagePeers),linkType:I.linkType,discardFromLookup:R,get packageLocation(){return W||(W=K.join(s,I.packageLocation))}}]}))])),f=new Map(t.fallbackExclusionList.map(([C,S])=>[C,new Set(S)])),p=new Map(t.fallbackPool),h=t.dependencyTreeRoots,E=t.enableTopLevelFallback;return{basePath:r,dependencyTreeRoots:h,enableTopLevelFallback:E,fallbackExclusionList:f,pnpZipBackend:t.pnpZipBackend,fallbackPool:p,ignorePattern:a,packageLocatorsByLocations:n,packageRegistry:c}}bt();bt();var lh=ye("module"),qm=ye("url"),$Y=ye("util");var ra=ye("url");var Fxe=et(ye("assert"));var GY=Array.isArray,JD=JSON.stringify,zD=Object.getOwnPropertyNames,jm=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),WY=(t,e)=>RegExp.prototype.exec.call(t,e),YY=(t,...e)=>RegExp.prototype[Symbol.replace].apply(t,e),ig=(t,...e)=>String.prototype.endsWith.apply(t,e),VY=(t,...e)=>String.prototype.includes.apply(t,e),KY=(t,...e)=>String.prototype.lastIndexOf.apply(t,e),ZD=(t,...e)=>String.prototype.indexOf.apply(t,e),Qxe=(t,...e)=>String.prototype.replace.apply(t,e),sg=(t,...e)=>String.prototype.slice.apply(t,e),dA=(t,...e)=>String.prototype.startsWith.apply(t,e),Txe=Map,Rxe=JSON.parse;function XD(t,e,r){return class extends r{constructor(...s){super(e(...s)),this.code=t,this.name=`${r.name} [${t}]`}}}var Nxe=XD("ERR_PACKAGE_IMPORT_NOT_DEFINED",(t,e,r)=>`Package import specifier "${t}" is not defined${e?` in package ${e}package.json`:""} imported from ${r}`,TypeError),JY=XD("ERR_INVALID_MODULE_SPECIFIER",(t,e,r=void 0)=>`Invalid module "${t}" ${e}${r?` imported from ${r}`:""}`,TypeError),Oxe=XD("ERR_INVALID_PACKAGE_TARGET",(t,e,r,s=!1,a=void 0)=>{let n=typeof r=="string"&&!s&&r.length&&!dA(r,"./");return e==="."?((0,Fxe.default)(s===!1),`Invalid "exports" main target ${JD(r)} defined in the package config ${t}package.json${a?` imported from ${a}`:""}${n?'; targets must start with "./"':""}`):`Invalid "${s?"imports":"exports"}" target ${JD(r)} defined for '${e}' in the package config ${t}package.json${a?` imported from ${a}`:""}${n?'; targets must start with "./"':""}`},Error),$D=XD("ERR_INVALID_PACKAGE_CONFIG",(t,e,r)=>`Invalid package config ${t}${e?` while importing ${e}`:""}${r?`. ${r}`:""}`,Error),Lxe=XD("ERR_PACKAGE_PATH_NOT_EXPORTED",(t,e,r=void 0)=>e==="."?`No "exports" main defined in ${t}package.json${r?` imported from ${r}`:""}`:`Package subpath '${e}' is not defined by "exports" in ${t}package.json${r?` imported from ${r}`:""}`,Error);var pN=ye("url");function Mxe(t,e){let r=Object.create(null);for(let s=0;se):t+e}eb(r,t,s,c,a)}WY(Uxe,sg(t,2))!==null&&eb(r,t,s,c,a);let p=new URL(t,s),h=p.pathname,E=new URL(".",s).pathname;if(dA(h,E)||eb(r,t,s,c,a),e==="")return p;if(WY(Uxe,e)!==null){let C=n?Qxe(r,"*",()=>e):r+e;jTt(C,s,c,a)}return n?new URL(YY(Hxe,p.href,()=>e)):new URL(e,p)}function GTt(t){let e=+t;return`${e}`!==t?!1:e>=0&&e<4294967295}function bw(t,e,r,s,a,n,c,f){if(typeof e=="string")return qTt(e,r,s,t,a,n,c,f);if(GY(e)){if(e.length===0)return null;let p;for(let h=0;hn?-1:n>a||r===-1?1:s===-1||t.length>e.length?-1:e.length>t.length?1:0}function WTt(t,e,r){if(typeof t=="string"||GY(t))return!0;if(typeof t!="object"||t===null)return!1;let s=zD(t),a=!1,n=0;for(let c=0;c=h.length&&ig(e,C)&&qxe(n,h)===1&&KY(h,"*")===E&&(n=h,c=sg(e,E,e.length-C.length))}}if(n){let p=r[n],h=bw(t,p,c,n,s,!0,!1,a);return h==null&&zY(e,t,s),h}zY(e,t,s)}function Wxe({name:t,base:e,conditions:r,readFileSyncFn:s}){if(t==="#"||dA(t,"#/")||ig(t,"/")){let c="is not a valid internal imports specifier name";throw new JY(t,c,(0,ra.fileURLToPath)(e))}let a,n=_xe(e,s);if(n.exists){a=(0,ra.pathToFileURL)(n.pjsonPath);let c=n.imports;if(c)if(jm(c,t)&&!VY(t,"*")){let f=bw(a,c[t],"",t,e,!1,!0,r);if(f!=null)return f}else{let f="",p,h=zD(c);for(let E=0;E=C.length&&ig(t,P)&&qxe(f,C)===1&&KY(C,"*")===S&&(f=C,p=sg(t,S,t.length-P.length))}}if(f){let E=c[f],C=bw(a,E,p,f,e,!0,!0,r);if(C!=null)return C}}}HTt(t,a,e)}bt();var VTt=new Set(["BUILTIN_NODE_RESOLUTION_FAILED","MISSING_DEPENDENCY","MISSING_PEER_DEPENDENCY","QUALIFIED_PATH_RESOLUTION_FAILED","UNDECLARED_DEPENDENCY"]);function ds(t,e,r={},s){s??=VTt.has(t)?"MODULE_NOT_FOUND":t;let a={configurable:!0,writable:!0,enumerable:!1};return Object.defineProperties(new Error(e),{code:{...a,value:s},pnpCode:{...a,value:t},data:{...a,value:r}})}function cf(t){return ue.normalize(ue.fromPortablePath(t))}var Jxe=et(Vxe());function zxe(t){return KTt(),XY[t]}var XY;function KTt(){XY||(XY={"--conditions":[],...Kxe(JTt()),...Kxe(process.execArgv)})}function Kxe(t){return(0,Jxe.default)({"--conditions":[String],"-C":"--conditions"},{argv:t,permissive:!0})}function JTt(){let t=[],e=zTt(process.env.NODE_OPTIONS||"",t);return t.length,e}function zTt(t,e){let r=[],s=!1,a=!0;for(let n=0;nparseInt(t,10)),Zxe=yl>19||yl===19&&ah>=2||yl===18&&ah>=13,lmr=yl===20&&ah<6||yl===19&&ah>=3,cmr=yl>19||yl===19&&ah>=6,umr=yl>=21||yl===20&&ah>=10||yl===18&&ah>=19,fmr=yl>=21||yl===20&&ah>=10||yl===18&&ah>=20,Amr=yl>=22;function Xxe(t){if(process.env.WATCH_REPORT_DEPENDENCIES&&process.send)if(t=t.map(e=>ue.fromPortablePath(fo.resolveVirtual(ue.toPortablePath(e)))),Zxe)process.send({"watch:require":t});else for(let e of t)process.send({"watch:require":e})}function eV(t,e){let r=Number(process.env.PNP_ALWAYS_WARN_ON_FALLBACK)>0,s=Number(process.env.PNP_DEBUG_LEVEL),a=/^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/,n=/^(\/|\.{1,2}(\/|$))/,c=/\/$/,f=/^\.{0,2}\//,p={name:null,reference:null},h=[],E=new Set;if(t.enableTopLevelFallback===!0&&h.push(p),e.compatibilityMode!==!1)for(let Fe of["react-scripts","gatsby"]){let Ne=t.packageRegistry.get(Fe);if(Ne)for(let Pe of Ne.keys()){if(Pe===null)throw new Error("Assertion failed: This reference shouldn't be null");h.push({name:Fe,reference:Pe})}}let{ignorePattern:C,packageRegistry:S,packageLocatorsByLocations:P}=t;function I(Fe,Ne){return{fn:Fe,args:Ne,error:null,result:null}}function R(Fe){let Ne=process.stderr?.hasColors?.()??process.stdout.isTTY,Pe=(it,_e)=>`\x1B[${it}m${_e}\x1B[0m`,Ye=Fe.error;console.error(Ye?Pe("31;1",`\u2716 ${Fe.error?.message.replace(/\n.*/s,"")}`):Pe("33;1","\u203C Resolution")),Fe.args.length>0&&console.error();for(let it of Fe.args)console.error(` ${Pe("37;1","In \u2190")} ${(0,$Y.inspect)(it,{colors:Ne,compact:!0})}`);Fe.result&&(console.error(),console.error(` ${Pe("37;1","Out \u2192")} ${(0,$Y.inspect)(Fe.result,{colors:Ne,compact:!0})}`));let ke=new Error().stack.match(/(?<=^ +)at.*/gm)?.slice(2)??[];if(ke.length>0){console.error();for(let it of ke)console.error(` ${Pe("38;5;244",it)}`)}console.error()}function N(Fe,Ne){if(e.allowDebug===!1)return Ne;if(Number.isFinite(s)){if(s>=2)return(...Pe)=>{let Ye=I(Fe,Pe);try{return Ye.result=Ne(...Pe)}catch(ke){throw Ye.error=ke}finally{R(Ye)}};if(s>=1)return(...Pe)=>{try{return Ne(...Pe)}catch(Ye){let ke=I(Fe,Pe);throw ke.error=Ye,R(ke),Ye}}}return Ne}function U(Fe){let Ne=g(Fe);if(!Ne)throw ds("INTERNAL","Couldn't find a matching entry in the dependency tree for the specified parent (this is probably an internal error)");return Ne}function W(Fe){if(Fe.name===null)return!0;for(let Ne of t.dependencyTreeRoots)if(Ne.name===Fe.name&&Ne.reference===Fe.reference)return!0;return!1}let te=new Set(["node","require",...zxe("--conditions")]);function ie(Fe,Ne=te,Pe){let Ye=fe(K.join(Fe,"internal.js"),{resolveIgnored:!0,includeDiscardFromLookup:!0});if(Ye===null)throw ds("INTERNAL",`The locator that owns the "${Fe}" path can't be found inside the dependency tree (this is probably an internal error)`);let{packageLocation:ke}=U(Ye),it=K.join(ke,Er.manifest);if(!e.fakeFs.existsSync(it))return null;let _e=JSON.parse(e.fakeFs.readFileSync(it,"utf8"));if(_e.exports==null)return null;let x=K.contains(ke,Fe);if(x===null)throw ds("INTERNAL","unqualifiedPath doesn't contain the packageLocation (this is probably an internal error)");x!=="."&&!f.test(x)&&(x=`./${x}`);try{let w=Gxe({packageJSONUrl:(0,qm.pathToFileURL)(ue.fromPortablePath(it)),packageSubpath:x,exports:_e.exports,base:Pe?(0,qm.pathToFileURL)(ue.fromPortablePath(Pe)):null,conditions:Ne});return ue.toPortablePath((0,qm.fileURLToPath)(w))}catch(w){throw ds("EXPORTS_RESOLUTION_FAILED",w.message,{unqualifiedPath:cf(Fe),locator:Ye,pkgJson:_e,subpath:cf(x),conditions:Ne},w.code)}}function Ae(Fe,Ne,{extensions:Pe}){let Ye;try{Ne.push(Fe),Ye=e.fakeFs.statSync(Fe)}catch{}if(Ye&&!Ye.isDirectory())return e.fakeFs.realpathSync(Fe);if(Ye&&Ye.isDirectory()){let ke;try{ke=JSON.parse(e.fakeFs.readFileSync(K.join(Fe,Er.manifest),"utf8"))}catch{}let it;if(ke&&ke.main&&(it=K.resolve(Fe,ke.main)),it&&it!==Fe){let _e=Ae(it,Ne,{extensions:Pe});if(_e!==null)return _e}}for(let ke=0,it=Pe.length;ke{let x=JSON.stringify(_e.name);if(Ye.has(x))return;Ye.add(x);let w=we(_e);for(let b of w)if(U(b).packagePeers.has(Fe))ke(b);else{let F=Pe.get(b.name);typeof F>"u"&&Pe.set(b.name,F=new Set),F.add(b.reference)}};ke(Ne);let it=[];for(let _e of[...Pe.keys()].sort())for(let x of[...Pe.get(_e)].sort())it.push({name:_e,reference:x});return it}function fe(Fe,{resolveIgnored:Ne=!1,includeDiscardFromLookup:Pe=!1}={}){if(pe(Fe)&&!Ne)return null;let Ye=K.relative(t.basePath,Fe);Ye.match(n)||(Ye=`./${Ye}`),Ye.endsWith("/")||(Ye=`${Ye}/`);do{let ke=P.get(Ye);if(typeof ke>"u"||ke.discardFromLookup&&!Pe){Ye=Ye.substring(0,Ye.lastIndexOf("/",Ye.length-2)+1);continue}return ke.locator}while(Ye!=="");return null}function se(Fe){try{return e.fakeFs.readFileSync(ue.toPortablePath(Fe),"utf8")}catch(Ne){if(Ne.code==="ENOENT")return;throw Ne}}function X(Fe,Ne,{considerBuiltins:Pe=!0}={}){if(Fe.startsWith("#"))throw new Error("resolveToUnqualified can not handle private import mappings");if(Fe==="pnpapi")return ue.toPortablePath(e.pnpapiResolution);if(Pe&&(0,lh.isBuiltin)(Fe))return null;let Ye=cf(Fe),ke=Ne&&cf(Ne);if(Ne&&pe(Ne)&&(!K.isAbsolute(Fe)||fe(Fe)===null)){let x=me(Fe,Ne);if(x===!1)throw ds("BUILTIN_NODE_RESOLUTION_FAILED",`The builtin node resolution algorithm was unable to resolve the requested module (it didn't go through the pnp resolver because the issuer was explicitely ignored by the regexp) + +Require request: "${Ye}" +Required by: ${ke} +`,{request:Ye,issuer:ke});return ue.toPortablePath(x)}let it,_e=Fe.match(a);if(_e){if(!Ne)throw ds("API_ERROR","The resolveToUnqualified function must be called with a valid issuer when the path isn't a builtin nor absolute",{request:Ye,issuer:ke});let[,x,w]=_e,b=fe(Ne);if(!b){let Te=me(Fe,Ne);if(Te===!1)throw ds("BUILTIN_NODE_RESOLUTION_FAILED",`The builtin node resolution algorithm was unable to resolve the requested module (it didn't go through the pnp resolver because the issuer doesn't seem to be part of the Yarn-managed dependency tree). + +Require path: "${Ye}" +Required by: ${ke} +`,{request:Ye,issuer:ke});return ue.toPortablePath(Te)}let F=U(b).packageDependencies.get(x),z=null;if(F==null&&b.name!==null){let Te=t.fallbackExclusionList.get(b.name);if(!Te||!Te.has(b.reference)){for(let Et=0,qt=h.length;EtW(lt))?Z=ds("MISSING_PEER_DEPENDENCY",`${b.name} tried to access ${x} (a peer dependency) but it isn't provided by your application; this makes the require call ambiguous and unsound. + +Required package: ${x}${x!==Ye?` (via "${Ye}")`:""} +Required by: ${b.name}@${b.reference} (via ${ke}) +${Te.map(lt=>`Ancestor breaking the chain: ${lt.name}@${lt.reference} +`).join("")} +`,{request:Ye,issuer:ke,issuerLocator:Object.assign({},b),dependencyName:x,brokenAncestors:Te}):Z=ds("MISSING_PEER_DEPENDENCY",`${b.name} tried to access ${x} (a peer dependency) but it isn't provided by its ancestors; this makes the require call ambiguous and unsound. + +Required package: ${x}${x!==Ye?` (via "${Ye}")`:""} +Required by: ${b.name}@${b.reference} (via ${ke}) + +${Te.map(lt=>`Ancestor breaking the chain: ${lt.name}@${lt.reference} +`).join("")} +`,{request:Ye,issuer:ke,issuerLocator:Object.assign({},b),dependencyName:x,brokenAncestors:Te})}else F===void 0&&(!Pe&&(0,lh.isBuiltin)(Fe)?W(b)?Z=ds("UNDECLARED_DEPENDENCY",`Your application tried to access ${x}. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since ${x} isn't otherwise declared in your dependencies, this makes the require call ambiguous and unsound. + +Required package: ${x}${x!==Ye?` (via "${Ye}")`:""} +Required by: ${ke} +`,{request:Ye,issuer:ke,dependencyName:x}):Z=ds("UNDECLARED_DEPENDENCY",`${b.name} tried to access ${x}. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since ${x} isn't otherwise declared in ${b.name}'s dependencies, this makes the require call ambiguous and unsound. + +Required package: ${x}${x!==Ye?` (via "${Ye}")`:""} +Required by: ${ke} +`,{request:Ye,issuer:ke,issuerLocator:Object.assign({},b),dependencyName:x}):W(b)?Z=ds("UNDECLARED_DEPENDENCY",`Your application tried to access ${x}, but it isn't declared in your dependencies; this makes the require call ambiguous and unsound. + +Required package: ${x}${x!==Ye?` (via "${Ye}")`:""} +Required by: ${ke} +`,{request:Ye,issuer:ke,dependencyName:x}):Z=ds("UNDECLARED_DEPENDENCY",`${b.name} tried to access ${x}, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound. + +Required package: ${x}${x!==Ye?` (via "${Ye}")`:""} +Required by: ${b.name}@${b.reference} (via ${ke}) +`,{request:Ye,issuer:ke,issuerLocator:Object.assign({},b),dependencyName:x}));if(F==null){if(z===null||Z===null)throw Z||new Error("Assertion failed: Expected an error to have been set");F=z;let Te=Z.message.replace(/\n.*/g,"");Z.message=Te,!E.has(Te)&&s!==0&&(E.add(Te),process.emitWarning(Z))}let $=Array.isArray(F)?{name:F[0],reference:F[1]}:{name:x,reference:F},oe=U($);if(!oe.packageLocation)throw ds("MISSING_DEPENDENCY",`A dependency seems valid but didn't get installed for some reason. This might be caused by a partial install, such as dev vs prod. + +Required package: ${$.name}@${$.reference}${$.name!==Ye?` (via "${Ye}")`:""} +Required by: ${b.name}@${b.reference} (via ${ke}) +`,{request:Ye,issuer:ke,dependencyLocator:Object.assign({},$)});let xe=oe.packageLocation;w?it=K.join(xe,w):it=xe}else if(K.isAbsolute(Fe))it=K.normalize(Fe);else{if(!Ne)throw ds("API_ERROR","The resolveToUnqualified function must be called with a valid issuer when the path isn't a builtin nor absolute",{request:Ye,issuer:ke});let x=K.resolve(Ne);Ne.match(c)?it=K.normalize(K.join(x,Fe)):it=K.normalize(K.join(K.dirname(x),Fe))}return K.normalize(it)}function De(Fe,Ne,Pe=te,Ye){if(n.test(Fe))return Ne;let ke=ie(Ne,Pe,Ye);return ke?K.normalize(ke):Ne}function Re(Fe,{extensions:Ne=Object.keys(lh.Module._extensions)}={}){let Pe=[],Ye=Ae(Fe,Pe,{extensions:Ne});if(Ye)return K.normalize(Ye);{Xxe(Pe.map(_e=>ue.fromPortablePath(_e)));let ke=cf(Fe),it=fe(Fe);if(it){let{packageLocation:_e}=U(it),x=!0;try{e.fakeFs.accessSync(_e)}catch(w){if(w?.code==="ENOENT")x=!1;else{let b=(w?.message??w??"empty exception thrown").replace(/^[A-Z]/,y=>y.toLowerCase());throw ds("QUALIFIED_PATH_RESOLUTION_FAILED",`Required package exists but could not be accessed (${b}). + +Missing package: ${it.name}@${it.reference} +Expected package location: ${cf(_e)} +`,{unqualifiedPath:ke,extensions:Ne})}}if(!x){let w=_e.includes("/unplugged/")?"Required unplugged package missing from disk. This may happen when switching branches without running installs (unplugged packages must be fully materialized on disk to work).":"Required package missing from disk. If you keep your packages inside your repository then restarting the Node process may be enough. Otherwise, try to run an install first.";throw ds("QUALIFIED_PATH_RESOLUTION_FAILED",`${w} + +Missing package: ${it.name}@${it.reference} +Expected package location: ${cf(_e)} +`,{unqualifiedPath:ke,extensions:Ne})}}throw ds("QUALIFIED_PATH_RESOLUTION_FAILED",`Qualified path resolution failed: we looked for the following paths, but none could be accessed. + +Source path: ${ke} +${Pe.map(_e=>`Not found: ${cf(_e)} +`).join("")}`,{unqualifiedPath:ke,extensions:Ne})}}function gt(Fe,Ne,Pe){if(!Ne)throw new Error("Assertion failed: An issuer is required to resolve private import mappings");let Ye=Wxe({name:Fe,base:(0,qm.pathToFileURL)(ue.fromPortablePath(Ne)),conditions:Pe.conditions??te,readFileSyncFn:se});if(Ye instanceof URL)return Re(ue.toPortablePath((0,qm.fileURLToPath)(Ye)),{extensions:Pe.extensions});if(Ye.startsWith("#"))throw new Error("Mapping from one private import to another isn't allowed");return j(Ye,Ne,Pe)}function j(Fe,Ne,Pe={}){try{if(Fe.startsWith("#"))return gt(Fe,Ne,Pe);let{considerBuiltins:Ye,extensions:ke,conditions:it}=Pe,_e=X(Fe,Ne,{considerBuiltins:Ye});if(Fe==="pnpapi")return _e;if(_e===null)return null;let x=()=>Ne!==null?pe(Ne):!1,w=(!Ye||!(0,lh.isBuiltin)(Fe))&&!x()?De(Fe,_e,it,Ne):_e;return Re(w,{extensions:ke})}catch(Ye){throw Object.hasOwn(Ye,"pnpCode")&&Object.assign(Ye.data,{request:cf(Fe),issuer:Ne&&cf(Ne)}),Ye}}function rt(Fe){let Ne=K.normalize(Fe),Pe=fo.resolveVirtual(Ne);return Pe!==Ne?Pe:null}return{VERSIONS:Be,topLevel:Ce,getLocator:(Fe,Ne)=>Array.isArray(Ne)?{name:Ne[0],reference:Ne[1]}:{name:Fe,reference:Ne},getDependencyTreeRoots:()=>[...t.dependencyTreeRoots],getAllLocators(){let Fe=[];for(let[Ne,Pe]of S)for(let Ye of Pe.keys())Ne!==null&&Ye!==null&&Fe.push({name:Ne,reference:Ye});return Fe},getPackageInformation:Fe=>{let Ne=g(Fe);if(Ne===null)return null;let Pe=ue.fromPortablePath(Ne.packageLocation);return{...Ne,packageLocation:Pe}},findPackageLocator:Fe=>fe(ue.toPortablePath(Fe)),resolveToUnqualified:N("resolveToUnqualified",(Fe,Ne,Pe)=>{let Ye=Ne!==null?ue.toPortablePath(Ne):null,ke=X(ue.toPortablePath(Fe),Ye,Pe);return ke===null?null:ue.fromPortablePath(ke)}),resolveUnqualified:N("resolveUnqualified",(Fe,Ne)=>ue.fromPortablePath(Re(ue.toPortablePath(Fe),Ne))),resolveRequest:N("resolveRequest",(Fe,Ne,Pe)=>{let Ye=Ne!==null?ue.toPortablePath(Ne):null,ke=j(ue.toPortablePath(Fe),Ye,Pe);return ke===null?null:ue.fromPortablePath(ke)}),resolveVirtual:N("resolveVirtual",Fe=>{let Ne=rt(ue.toPortablePath(Fe));return Ne!==null?ue.fromPortablePath(Ne):null})}}bt();var $xe=(t,e,r)=>{let s=KD(t),a=qY(s,{basePath:e}),n=ue.join(e,Er.pnpCjs);return eV(a,{fakeFs:r,pnpapiResolution:n})};var rV=et(tke());Wt();var mA={};Vt(mA,{checkManifestCompatibility:()=>rke,extractBuildRequest:()=>hN,getExtractHint:()=>nV,hasBindingGyp:()=>iV});Ve();bt();function rke(t){return q.isPackageCompatible(t,As.getArchitectureSet())}function hN(t,e,r,{configuration:s}){let a=[];for(let n of["preinstall","install","postinstall"])e.manifest.scripts.has(n)&&a.push({type:0,script:n});return!e.manifest.scripts.has("install")&&e.misc.hasBindingGyp&&a.push({type:1,script:"node-gyp rebuild"}),a.length===0?null:t.linkType!=="HARD"?{skipped:!0,explain:n=>n.reportWarningOnce(6,`${q.prettyLocator(s,t)} lists build scripts, but is referenced through a soft link. Soft links don't support build scripts, so they'll be ignored.`)}:r&&r.built===!1?{skipped:!0,explain:n=>n.reportInfoOnce(5,`${q.prettyLocator(s,t)} lists build scripts, but its build has been explicitly disabled through configuration.`)}:!s.get("enableScripts")&&!r.built?{skipped:!0,explain:n=>n.reportWarningOnce(4,`${q.prettyLocator(s,t)} lists build scripts, but all build scripts have been disabled.`)}:rke(t)?{skipped:!1,directives:a}:{skipped:!0,explain:n=>n.reportWarningOnce(76,`${q.prettyLocator(s,t)} The ${As.getArchitectureName()} architecture is incompatible with this package, build skipped.`)}}var XTt=new Set([".exe",".bin",".h",".hh",".hpp",".c",".cc",".cpp",".java",".jar",".node"]);function nV(t){return t.packageFs.getExtractHint({relevantExtensions:XTt})}function iV(t){let e=K.join(t.prefixPath,"binding.gyp");return t.packageFs.existsSync(e)}var nb={};Vt(nb,{getUnpluggedPath:()=>rb});Ve();bt();function rb(t,{configuration:e}){return K.resolve(e.get("pnpUnpluggedFolder"),q.slugifyLocator(t))}var $Tt=new Set([q.makeIdent(null,"open").identHash,q.makeIdent(null,"opn").identHash]),og=class{constructor(){this.mode="strict";this.pnpCache=new Map}getCustomDataKey(){return JSON.stringify({name:"PnpLinker",version:2})}supportsPackage(e,r){return this.isEnabled(r)}async findPackageLocation(e,r){if(!this.isEnabled(r))throw new Error("Assertion failed: Expected the PnP linker to be enabled");let s=ag(r.project).cjs;if(!le.existsSync(s))throw new nt(`The project in ${he.pretty(r.project.configuration,`${r.project.cwd}/package.json`,he.Type.PATH)} doesn't seem to have been installed - running an install there might help`);let a=je.getFactoryWithDefault(this.pnpCache,s,()=>je.dynamicRequire(s,{cachingStrategy:je.CachingStrategy.FsTime})),n={name:q.stringifyIdent(e),reference:e.reference},c=a.getPackageInformation(n);if(!c)throw new nt(`Couldn't find ${q.prettyLocator(r.project.configuration,e)} in the currently installed PnP map - running an install might help`);return ue.toPortablePath(c.packageLocation)}async findPackageLocator(e,r){if(!this.isEnabled(r))return null;let s=ag(r.project).cjs;if(!le.existsSync(s))return null;let n=je.getFactoryWithDefault(this.pnpCache,s,()=>je.dynamicRequire(s,{cachingStrategy:je.CachingStrategy.FsTime})).findPackageLocator(ue.fromPortablePath(e));return n?q.makeLocator(q.parseIdent(n.name),n.reference):null}makeInstaller(e){return new Gm(e)}isEnabled(e){return!(e.project.configuration.get("nodeLinker")!=="pnp"||e.project.configuration.get("pnpMode")!==this.mode)}},Gm=class{constructor(e){this.opts=e;this.mode="strict";this.asyncActions=new je.AsyncActions(10);this.packageRegistry=new Map;this.virtualTemplates=new Map;this.isESMLoaderRequired=!1;this.customData={store:new Map};this.unpluggedPaths=new Set;this.opts=e}attachCustomData(e){this.customData=e}async installPackage(e,r,s){let a=q.stringifyIdent(e),n=e.reference,c=!!this.opts.project.tryWorkspaceByLocator(e),f=q.isVirtualLocator(e),p=e.peerDependencies.size>0&&!f,h=!p&&!c,E=!p&&e.linkType!=="SOFT",C,S;if(h||E){let te=f?q.devirtualizeLocator(e):e;C=this.customData.store.get(te.locatorHash),typeof C>"u"&&(C=await eRt(r),e.linkType==="HARD"&&this.customData.store.set(te.locatorHash,C)),C.manifest.type==="module"&&(this.isESMLoaderRequired=!0),S=this.opts.project.getDependencyMeta(te,e.version)}let P=h?hN(e,C,S,{configuration:this.opts.project.configuration}):null,I=E?await this.unplugPackageIfNeeded(e,C,r,S,s):r.packageFs;if(K.isAbsolute(r.prefixPath))throw new Error(`Assertion failed: Expected the prefix path (${r.prefixPath}) to be relative to the parent`);let R=K.resolve(I.getRealPath(),r.prefixPath),N=sV(this.opts.project.cwd,R),U=new Map,W=new Set;if(f){for(let te of e.peerDependencies.values())U.set(q.stringifyIdent(te),null),W.add(q.stringifyIdent(te));if(!c){let te=q.devirtualizeLocator(e);this.virtualTemplates.set(te.locatorHash,{location:sV(this.opts.project.cwd,fo.resolveVirtual(R)),locator:te})}}return je.getMapWithDefault(this.packageRegistry,a).set(n,{packageLocation:N,packageDependencies:U,packagePeers:W,linkType:e.linkType,discardFromLookup:r.discardFromLookup||!1}),{packageLocation:R,buildRequest:P}}async attachInternalDependencies(e,r){let s=this.getPackageInformation(e);for(let[a,n]of r){let c=q.areIdentsEqual(a,n)?n.reference:[q.stringifyIdent(n),n.reference];s.packageDependencies.set(q.stringifyIdent(a),c)}}async attachExternalDependents(e,r){for(let s of r)this.getDiskInformation(s).packageDependencies.set(q.stringifyIdent(e),e.reference)}async finalizeInstall(){if(this.opts.project.configuration.get("pnpMode")!==this.mode)return;let e=ag(this.opts.project);if(this.isEsmEnabled()||await le.removePromise(e.esmLoader),this.opts.project.configuration.get("nodeLinker")!=="pnp"){await le.removePromise(e.cjs),await le.removePromise(e.data),await le.removePromise(e.esmLoader),await le.removePromise(this.opts.project.configuration.get("pnpUnpluggedFolder"));return}for(let{locator:C,location:S}of this.virtualTemplates.values())je.getMapWithDefault(this.packageRegistry,q.stringifyIdent(C)).set(C.reference,{packageLocation:S,packageDependencies:new Map,packagePeers:new Set,linkType:"SOFT",discardFromLookup:!1});let r=this.opts.project.configuration.get("pnpFallbackMode"),s=this.opts.project.workspaces.map(({anchoredLocator:C})=>({name:q.stringifyIdent(C),reference:C.reference})),a=r!=="none",n=[],c=new Map,f=je.buildIgnorePattern([".yarn/sdks/**",...this.opts.project.configuration.get("pnpIgnorePatterns")]),p=this.packageRegistry,h=this.opts.project.configuration.get("pnpShebang"),E=this.opts.project.configuration.get("pnpZipBackend");if(r==="dependencies-only")for(let C of this.opts.project.storedPackages.values())this.opts.project.tryWorkspaceByLocator(C)&&n.push({name:q.stringifyIdent(C),reference:C.reference});return await this.asyncActions.wait(),await this.finalizeInstallWithPnp({dependencyTreeRoots:s,enableTopLevelFallback:a,fallbackExclusionList:n,fallbackPool:c,ignorePattern:f,pnpZipBackend:E,packageRegistry:p,shebang:h}),{customData:this.customData}}async transformPnpSettings(e){}isEsmEnabled(){if(this.opts.project.configuration.sources.has("pnpEnableEsmLoader"))return this.opts.project.configuration.get("pnpEnableEsmLoader");if(this.isESMLoaderRequired)return!0;for(let e of this.opts.project.workspaces)if(e.manifest.type==="module")return!0;return!1}async finalizeInstallWithPnp(e){let r=ag(this.opts.project),s=await this.locateNodeModules(e.ignorePattern);if(s.length>0){this.opts.report.reportWarning(31,"One or more node_modules have been detected and will be removed. This operation may take some time.");for(let n of s)await le.removePromise(n)}if(await this.transformPnpSettings(e),this.opts.project.configuration.get("pnpEnableInlining")){let n=xxe(e);await le.changeFilePromise(r.cjs,n,{automaticNewlines:!0,mode:493}),await le.removePromise(r.data)}else{let{dataFile:n,loaderFile:c}=kxe(e);await le.changeFilePromise(r.cjs,c,{automaticNewlines:!0,mode:493}),await le.changeFilePromise(r.data,n,{automaticNewlines:!0,mode:420})}this.isEsmEnabled()&&(this.opts.report.reportWarning(0,"ESM support for PnP uses the experimental loader API and is therefore experimental"),await le.changeFilePromise(r.esmLoader,(0,rV.default)(),{automaticNewlines:!0,mode:420}));let a=this.opts.project.configuration.get("pnpUnpluggedFolder");if(this.unpluggedPaths.size===0)await le.removePromise(a);else for(let n of await le.readdirPromise(a)){let c=K.resolve(a,n);this.unpluggedPaths.has(c)||await le.removePromise(c)}}async locateNodeModules(e){let r=[],s=e?new RegExp(e):null;for(let a of this.opts.project.workspaces){let n=K.join(a.cwd,"node_modules");if(s&&s.test(K.relative(this.opts.project.cwd,a.cwd))||!le.existsSync(n))continue;let c=await le.readdirPromise(n,{withFileTypes:!0}),f=c.filter(p=>!p.isDirectory()||p.name===".bin"||!p.name.startsWith("."));if(f.length===c.length)r.push(n);else for(let p of f)r.push(K.join(n,p.name))}return r}async unplugPackageIfNeeded(e,r,s,a,n){return this.shouldBeUnplugged(e,r,a)?this.unplugPackage(e,s,n):s.packageFs}shouldBeUnplugged(e,r,s){return typeof s.unplugged<"u"?s.unplugged:$Tt.has(e.identHash)||e.conditions!=null?!0:r.manifest.preferUnplugged!==null?r.manifest.preferUnplugged:!!(hN(e,r,s,{configuration:this.opts.project.configuration})?.skipped===!1||r.misc.extractHint)}async unplugPackage(e,r,s){let a=rb(e,{configuration:this.opts.project.configuration});return this.opts.project.disabledLocators.has(e.locatorHash)?new Hf(a,{baseFs:r.packageFs,pathUtils:K}):(this.unpluggedPaths.add(a),s.holdFetchResult(this.asyncActions.set(e.locatorHash,async()=>{let n=K.join(a,r.prefixPath,".ready");await le.existsPromise(n)||(this.opts.project.storedBuildState.delete(e.locatorHash),await le.mkdirPromise(a,{recursive:!0}),await le.copyPromise(a,vt.dot,{baseFs:r.packageFs,overwrite:!1}),await le.writeFilePromise(n,""))})),new Sn(a))}getPackageInformation(e){let r=q.stringifyIdent(e),s=e.reference,a=this.packageRegistry.get(r);if(!a)throw new Error(`Assertion failed: The package information store should have been available (for ${q.prettyIdent(this.opts.project.configuration,e)})`);let n=a.get(s);if(!n)throw new Error(`Assertion failed: The package information should have been available (for ${q.prettyLocator(this.opts.project.configuration,e)})`);return n}getDiskInformation(e){let r=je.getMapWithDefault(this.packageRegistry,"@@disk"),s=sV(this.opts.project.cwd,e);return je.getFactoryWithDefault(r,s,()=>({packageLocation:s,packageDependencies:new Map,packagePeers:new Set,linkType:"SOFT",discardFromLookup:!1}))}};function sV(t,e){let r=K.relative(t,e);return r.match(/^\.{0,2}\//)||(r=`./${r}`),r.replace(/\/?$/,"/")}async function eRt(t){let e=await Ht.tryFind(t.prefixPath,{baseFs:t.packageFs})??new Ht,r=new Set(["preinstall","install","postinstall"]);for(let s of e.scripts.keys())r.has(s)||e.scripts.delete(s);return{manifest:{scripts:e.scripts,preferUnplugged:e.preferUnplugged,type:e.type},misc:{extractHint:nV(t),hasBindingGyp:iV(t)}}}Ve();Ve();Wt();var nke=et(Sa());var Pw=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("-A,--all",!1,{description:"Unplug direct dependencies from the entire project"});this.recursive=ge.Boolean("-R,--recursive",!1,{description:"Unplug both direct and transitive dependencies"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.patterns=ge.Rest()}static{this.paths=[["unplug"]]}static{this.usage=ot.Usage({description:"force the unpacking of a list of packages",details:"\n This command will add the selectors matching the specified patterns to the list of packages that must be unplugged when installed.\n\n A package being unplugged means that instead of being referenced directly through its archive, it will be unpacked at install time in the directory configured via `pnpUnpluggedFolder`. Note that unpacking packages this way is generally not recommended because it'll make it harder to store your packages within the repository. However, it's a good approach to quickly and safely debug some packages, and can even sometimes be required depending on the context (for example when the package contains shellscripts).\n\n Running the command will set a persistent flag inside your top-level `package.json`, in the `dependenciesMeta` field. As such, to undo its effects, you'll need to revert the changes made to the manifest and run `yarn install` to apply the modification.\n\n By default, only direct dependencies from the current workspace are affected. If `-A,--all` is set, direct dependencies from the entire project are affected. Using the `-R,--recursive` flag will affect transitive dependencies as well as direct ones.\n\n This command accepts glob patterns inside the scope and name components (not the range). Make sure to escape the patterns to prevent your own shell from trying to expand them.\n ",examples:[["Unplug the lodash dependency from the active workspace","yarn unplug lodash"],["Unplug all instances of lodash referenced by any workspace","yarn unplug lodash -A"],["Unplug all instances of lodash referenced by the active workspace and its dependencies","yarn unplug lodash -R"],["Unplug all instances of lodash, anywhere","yarn unplug lodash -AR"],["Unplug one specific version of lodash","yarn unplug lodash@1.2.3"],["Unplug all packages with the `@babel` scope","yarn unplug '@babel/*'"],["Unplug all packages (only for testing, not recommended)","yarn unplug -R '*'"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);if(r.get("nodeLinker")!=="pnp")throw new nt("This command can only be used if the `nodeLinker` option is set to `pnp`");await s.restoreInstallState();let c=new Set(this.patterns),f=this.patterns.map(P=>{let I=q.parseDescriptor(P),R=I.range!=="unknown"?I:q.makeDescriptor(I,"*");if(!Or.validRange(R.range))throw new nt(`The range of the descriptor patterns must be a valid semver range (${q.prettyDescriptor(r,R)})`);return N=>{let U=q.stringifyIdent(N);return!nke.default.isMatch(U,q.stringifyIdent(R))||N.version&&!Or.satisfiesWithPrereleases(N.version,R.range)?!1:(c.delete(P),!0)}}),p=()=>{let P=[];for(let I of s.storedPackages.values())!s.tryWorkspaceByLocator(I)&&!q.isVirtualLocator(I)&&f.some(R=>R(I))&&P.push(I);return P},h=P=>{let I=new Set,R=[],N=(U,W)=>{if(I.has(U.locatorHash))return;let te=!!s.tryWorkspaceByLocator(U);if(!(W>0&&!this.recursive&&te)&&(I.add(U.locatorHash),!s.tryWorkspaceByLocator(U)&&f.some(ie=>ie(U))&&R.push(U),!(W>0&&!this.recursive)))for(let ie of U.dependencies.values()){let Ae=s.storedResolutions.get(ie.descriptorHash);if(!Ae)throw new Error("Assertion failed: The resolution should have been registered");let ce=s.storedPackages.get(Ae);if(!ce)throw new Error("Assertion failed: The package should have been registered");N(ce,W+1)}};for(let U of P)N(U.anchoredPackage,0);return R},E,C;if(this.all&&this.recursive?(E=p(),C="the project"):this.all?(E=h(s.workspaces),C="any workspace"):(E=h([a]),C="this workspace"),c.size>1)throw new nt(`Patterns ${he.prettyList(r,c,he.Type.CODE)} don't match any packages referenced by ${C}`);if(c.size>0)throw new nt(`Pattern ${he.prettyList(r,c,he.Type.CODE)} doesn't match any packages referenced by ${C}`);E=je.sortMap(E,P=>q.stringifyLocator(P));let S=await Ot.start({configuration:r,stdout:this.context.stdout,json:this.json},async P=>{for(let I of E){let R=I.version??"unknown",N=s.topLevelWorkspace.manifest.ensureDependencyMeta(q.makeDescriptor(I,R));N.unplugged=!0,P.reportInfo(0,`Will unpack ${q.prettyLocator(r,I)} to ${he.pretty(r,rb(I,{configuration:r}),he.Type.PATH)}`),P.reportJson({locator:q.stringifyLocator(I),version:R})}await s.topLevelWorkspace.persistManifest(),this.json||P.reportSeparator()});return S.hasErrors()?S.exitCode():await s.installWithNewReport({json:this.json,stdout:this.context.stdout},{cache:n})}};var ag=t=>({cjs:K.join(t.cwd,Er.pnpCjs),data:K.join(t.cwd,Er.pnpData),esmLoader:K.join(t.cwd,Er.pnpEsmLoader)}),ske=t=>/\s/.test(t)?JSON.stringify(t):t;async function tRt(t,e,r){let s=/\s*--require\s+\S*\.pnp\.c?js\s*/g,a=/\s*--experimental-loader\s+\S*\.pnp\.loader\.mjs\s*/,n=(e.NODE_OPTIONS??"").replace(s," ").replace(a," ").trim();if(t.configuration.get("nodeLinker")!=="pnp"){e.NODE_OPTIONS=n||void 0;return}let c=ag(t),f=`--require ${ske(ue.fromPortablePath(c.cjs))}`;le.existsSync(c.esmLoader)&&(f=`${f} --experimental-loader ${(0,ike.pathToFileURL)(ue.fromPortablePath(c.esmLoader)).href}`),le.existsSync(c.cjs)&&(e.NODE_OPTIONS=n?`${f} ${n}`:f)}async function rRt(t,e){let r=ag(t);e(r.cjs),e(r.data),e(r.esmLoader),e(t.configuration.get("pnpUnpluggedFolder"))}var nRt={hooks:{populateYarnPaths:rRt,setupScriptEnvironment:tRt},configuration:{nodeLinker:{description:'The linker used for installing Node packages, one of: "pnp", "pnpm", or "node-modules"',type:"STRING",default:"pnp"},minizip:{description:"Whether Yarn should use minizip to extract archives",type:"BOOLEAN",default:!1},winLinkType:{description:"Whether Yarn should use Windows Junctions or symlinks when creating links on Windows.",type:"STRING",values:["junctions","symlinks"],default:"junctions"},pnpMode:{description:"If 'strict', generates standard PnP maps. If 'loose', merges them with the n_m resolution.",type:"STRING",default:"strict"},pnpShebang:{description:"String to prepend to the generated PnP script",type:"STRING",default:"#!/usr/bin/env node"},pnpIgnorePatterns:{description:"Array of glob patterns; files matching them will use the classic resolution",type:"STRING",default:[],isArray:!0},pnpZipBackend:{description:"Whether to use the experimental js implementation for the ZipFS",type:"STRING",values:["libzip","js"],default:"libzip"},pnpEnableEsmLoader:{description:"If true, Yarn will generate an ESM loader (`.pnp.loader.mjs`). If this is not explicitly set Yarn tries to automatically detect whether ESM support is required.",type:"BOOLEAN",default:!1},pnpEnableInlining:{description:"If true, the PnP data will be inlined along with the generated loader",type:"BOOLEAN",default:!0},pnpFallbackMode:{description:"If true, the generated PnP loader will follow the top-level fallback rule",type:"STRING",default:"dependencies-only"},pnpUnpluggedFolder:{description:"Folder where the unplugged packages must be stored",type:"ABSOLUTE_PATH",default:"./.yarn/unplugged"}},linkers:[og],commands:[Pw]},iRt=nRt;var pke=et(uke());Wt();var pV=et(ye("crypto")),hke=et(ye("fs")),gke=1,_i="node_modules",gN=".bin",dke=".yarn-state.yml",CRt=1e3,hV=(s=>(s.CLASSIC="classic",s.HARDLINKS_LOCAL="hardlinks-local",s.HARDLINKS_GLOBAL="hardlinks-global",s))(hV||{}),ib=class{constructor(){this.installStateCache=new Map}getCustomDataKey(){return JSON.stringify({name:"NodeModulesLinker",version:3})}supportsPackage(e,r){return this.isEnabled(r)}async findPackageLocation(e,r){if(!this.isEnabled(r))throw new Error("Assertion failed: Expected the node-modules linker to be enabled");let s=r.project.tryWorkspaceByLocator(e);if(s)return s.cwd;let a=await je.getFactoryWithDefault(this.installStateCache,r.project.cwd,async()=>await AV(r.project,{unrollAliases:!0}));if(a===null)throw new nt("Couldn't find the node_modules state file - running an install might help (findPackageLocation)");let n=a.locatorMap.get(q.stringifyLocator(e));if(!n){let p=new nt(`Couldn't find ${q.prettyLocator(r.project.configuration,e)} in the currently installed node_modules map - running an install might help`);throw p.code="LOCATOR_NOT_INSTALLED",p}let c=n.locations.sort((p,h)=>p.split(K.sep).length-h.split(K.sep).length),f=K.join(r.project.configuration.startingCwd,_i);return c.find(p=>K.contains(f,p))||n.locations[0]}async findPackageLocator(e,r){if(!this.isEnabled(r))return null;let s=await je.getFactoryWithDefault(this.installStateCache,r.project.cwd,async()=>await AV(r.project,{unrollAliases:!0}));if(s===null)return null;let{locationRoot:a,segments:n}=dN(K.resolve(e),{skipPrefix:r.project.cwd}),c=s.locationTree.get(a);if(!c)return null;let f=c.locator;for(let p of n){if(c=c.children.get(p),!c)break;f=c.locator||f}return q.parseLocator(f)}makeInstaller(e){return new fV(e)}isEnabled(e){return e.project.configuration.get("nodeLinker")==="node-modules"}},fV=class{constructor(e){this.opts=e;this.localStore=new Map;this.realLocatorChecksums=new Map;this.customData={store:new Map}}attachCustomData(e){this.customData=e}async installPackage(e,r){let s=K.resolve(r.packageFs.getRealPath(),r.prefixPath),a=this.customData.store.get(e.locatorHash);if(typeof a>"u"&&(a=await wRt(e,r),e.linkType==="HARD"&&this.customData.store.set(e.locatorHash,a)),!q.isPackageCompatible(e,this.opts.project.configuration.getSupportedArchitectures()))return{packageLocation:null,buildRequest:null};let n=new Map,c=new Set;n.has(q.stringifyIdent(e))||n.set(q.stringifyIdent(e),e.reference);let f=e;if(q.isVirtualLocator(e)){f=q.devirtualizeLocator(e);for(let E of e.peerDependencies.values())n.set(q.stringifyIdent(E),null),c.add(q.stringifyIdent(E))}let p={packageLocation:`${ue.fromPortablePath(s)}/`,packageDependencies:n,packagePeers:c,linkType:e.linkType,discardFromLookup:r.discardFromLookup??!1};this.localStore.set(e.locatorHash,{pkg:e,customPackageData:a,dependencyMeta:this.opts.project.getDependencyMeta(e,e.version),pnpNode:p});let h=r.checksum?r.checksum.substring(r.checksum.indexOf("/")+1):null;return this.realLocatorChecksums.set(f.locatorHash,h),{packageLocation:s,buildRequest:null}}async attachInternalDependencies(e,r){let s=this.localStore.get(e.locatorHash);if(typeof s>"u")throw new Error("Assertion failed: Expected information object to have been registered");for(let[a,n]of r){let c=q.areIdentsEqual(a,n)?n.reference:[q.stringifyIdent(n),n.reference];s.pnpNode.packageDependencies.set(q.stringifyIdent(a),c)}}async attachExternalDependents(e,r){throw new Error("External dependencies haven't been implemented for the node-modules linker")}async finalizeInstall(){if(this.opts.project.configuration.get("nodeLinker")!=="node-modules")return;let e=new fo({baseFs:new tA({maxOpenFiles:80,readOnlyArchives:!0})}),r=await AV(this.opts.project),s=this.opts.project.configuration.get("nmMode");(r===null||s!==r.nmMode)&&(this.opts.project.storedBuildState.clear(),r={locatorMap:new Map,binSymlinks:new Map,locationTree:new Map,nmMode:s,mtimeMs:0});let a=new Map(this.opts.project.workspaces.map(S=>{let P=this.opts.project.configuration.get("nmHoistingLimits");try{P=je.validateEnum(WD,S.manifest.installConfig?.hoistingLimits??P)}catch{let I=q.prettyWorkspace(this.opts.project.configuration,S);this.opts.report.reportWarning(57,`${I}: Invalid 'installConfig.hoistingLimits' value. Expected one of ${Object.values(WD).join(", ")}, using default: "${P}"`)}return[S.relativeCwd,P]})),n=new Map(this.opts.project.workspaces.map(S=>{let P=this.opts.project.configuration.get("nmSelfReferences");return P=S.manifest.installConfig?.selfReferences??P,[S.relativeCwd,P]})),c={VERSIONS:{std:1},topLevel:{name:null,reference:null},getLocator:(S,P)=>Array.isArray(P)?{name:P[0],reference:P[1]}:{name:S,reference:P},getDependencyTreeRoots:()=>this.opts.project.workspaces.map(S=>{let P=S.anchoredLocator;return{name:q.stringifyIdent(P),reference:P.reference}}),getPackageInformation:S=>{let P=S.reference===null?this.opts.project.topLevelWorkspace.anchoredLocator:q.makeLocator(q.parseIdent(S.name),S.reference),I=this.localStore.get(P.locatorHash);if(typeof I>"u")throw new Error("Assertion failed: Expected the package reference to have been registered");return I.pnpNode},findPackageLocator:S=>{let P=this.opts.project.tryWorkspaceByCwd(ue.toPortablePath(S));if(P!==null){let I=P.anchoredLocator;return{name:q.stringifyIdent(I),reference:I.reference}}throw new Error("Assertion failed: Unimplemented")},resolveToUnqualified:()=>{throw new Error("Assertion failed: Unimplemented")},resolveUnqualified:()=>{throw new Error("Assertion failed: Unimplemented")},resolveRequest:()=>{throw new Error("Assertion failed: Unimplemented")},resolveVirtual:S=>ue.fromPortablePath(fo.resolveVirtual(ue.toPortablePath(S)))},{tree:f,errors:p,preserveSymlinksRequired:h}=YD(c,{pnpifyFs:!1,validateExternalSoftLinks:!0,hoistingLimitsByCwd:a,project:this.opts.project,selfReferencesByCwd:n});if(!f){for(let{messageName:S,text:P}of p)this.opts.report.reportError(S,P);return}let E=HY(f);await PRt(r,E,{baseFs:e,project:this.opts.project,report:this.opts.report,realLocatorChecksums:this.realLocatorChecksums,loadManifest:async S=>{let P=q.parseLocator(S),I=this.localStore.get(P.locatorHash);if(typeof I>"u")throw new Error("Assertion failed: Expected the slot to exist");return I.customPackageData.manifest}});let C=[];for(let[S,P]of E.entries()){if(Eke(S))continue;let I=q.parseLocator(S),R=this.localStore.get(I.locatorHash);if(typeof R>"u")throw new Error("Assertion failed: Expected the slot to exist");if(this.opts.project.tryWorkspaceByLocator(R.pkg))continue;let N=mA.extractBuildRequest(R.pkg,R.customPackageData,R.dependencyMeta,{configuration:this.opts.project.configuration});N&&C.push({buildLocations:P.locations,locator:I,buildRequest:N})}return h&&this.opts.report.reportWarning(72,`The application uses portals and that's why ${he.pretty(this.opts.project.configuration,"--preserve-symlinks",he.Type.CODE)} Node option is required for launching it`),{customData:this.customData,records:C}}};async function wRt(t,e){let r=await Ht.tryFind(e.prefixPath,{baseFs:e.packageFs})??new Ht,s=new Set(["preinstall","install","postinstall"]);for(let a of r.scripts.keys())s.has(a)||r.scripts.delete(a);return{manifest:{bin:r.bin,scripts:r.scripts},misc:{hasBindingGyp:mA.hasBindingGyp(e)}}}async function BRt(t,e,r,s,{installChangedByUser:a}){let n="";n+=`# Warning: This file is automatically generated. Removing it is fine, but will +`,n+=`# cause your node_modules installation to become invalidated. +`,n+=` +`,n+=`__metadata: +`,n+=` version: ${gke} +`,n+=` nmMode: ${s.value} +`;let c=Array.from(e.keys()).sort(),f=q.stringifyLocator(t.topLevelWorkspace.anchoredLocator);for(let E of c){let C=e.get(E);n+=` +`,n+=`${JSON.stringify(E)}: +`,n+=` locations: +`;for(let S of C.locations){let P=K.contains(t.cwd,S);if(P===null)throw new Error(`Assertion failed: Expected the path to be within the project (${S})`);n+=` - ${JSON.stringify(P)} +`}if(C.aliases.length>0){n+=` aliases: +`;for(let S of C.aliases)n+=` - ${JSON.stringify(S)} +`}if(E===f&&r.size>0){n+=` bin: +`;for(let[S,P]of r){let I=K.contains(t.cwd,S);if(I===null)throw new Error(`Assertion failed: Expected the path to be within the project (${S})`);n+=` ${JSON.stringify(I)}: +`;for(let[R,N]of P){let U=K.relative(K.join(S,_i),N);n+=` ${JSON.stringify(R)}: ${JSON.stringify(U)} +`}}}}let p=t.cwd,h=K.join(p,_i,dke);a&&await le.removePromise(h),await le.changeFilePromise(h,n,{automaticNewlines:!0})}async function AV(t,{unrollAliases:e=!1}={}){let r=t.cwd,s=K.join(r,_i,dke),a;try{a=await le.statPromise(s)}catch{}if(!a)return null;let n=ls(await le.readFilePromise(s,"utf8"));if(n.__metadata.version>gke)return null;let c=n.__metadata.nmMode||"classic",f=new Map,p=new Map;delete n.__metadata;for(let[h,E]of Object.entries(n)){let C=E.locations.map(P=>K.join(r,P)),S=E.bin;if(S)for(let[P,I]of Object.entries(S)){let R=K.join(r,ue.toPortablePath(P)),N=je.getMapWithDefault(p,R);for(let[U,W]of Object.entries(I))N.set(U,ue.toPortablePath([R,_i,W].join(K.sep)))}if(f.set(h,{target:vt.dot,linkType:"HARD",locations:C,aliases:E.aliases||[]}),e&&E.aliases)for(let P of E.aliases){let{scope:I,name:R}=q.parseLocator(h),N=q.makeLocator(q.makeIdent(I,R),P),U=q.stringifyLocator(N);f.set(U,{target:vt.dot,linkType:"HARD",locations:C,aliases:[]})}}return{locatorMap:f,binSymlinks:p,locationTree:mke(f,{skipPrefix:t.cwd}),nmMode:c,mtimeMs:a.mtimeMs}}var kw=async(t,e)=>{if(t.split(K.sep).indexOf(_i)<0)throw new Error(`Assertion failed: trying to remove dir that doesn't contain node_modules: ${t}`);try{let r;if(!e.innerLoop&&(r=await le.lstatPromise(t),!r.isDirectory()&&!r.isSymbolicLink()||r.isSymbolicLink()&&!e.isWorkspaceDir)){await le.unlinkPromise(t);return}let s=await le.readdirPromise(t,{withFileTypes:!0});for(let n of s){let c=K.join(t,n.name);n.isDirectory()?(n.name!==_i||e&&e.innerLoop)&&await kw(c,{innerLoop:!0,contentsOnly:!1}):await le.unlinkPromise(c)}let a=!e.innerLoop&&e.isWorkspaceDir&&r?.isSymbolicLink();!e.contentsOnly&&!a&&await le.rmdirPromise(t)}catch(r){if(r.code!=="ENOENT"&&r.code!=="ENOTEMPTY")throw r}},fke=4,dN=(t,{skipPrefix:e})=>{let r=K.contains(e,t);if(r===null)throw new Error(`Assertion failed: Writing attempt prevented to ${t} which is outside project root: ${e}`);let s=r.split(K.sep).filter(p=>p!==""),a=s.indexOf(_i),n=s.slice(0,a).join(K.sep),c=K.join(e,n),f=s.slice(a);return{locationRoot:c,segments:f}},mke=(t,{skipPrefix:e})=>{let r=new Map;if(t===null)return r;let s=()=>({children:new Map,linkType:"HARD"});for(let[a,n]of t.entries()){if(n.linkType==="SOFT"&&K.contains(e,n.target)!==null){let f=je.getFactoryWithDefault(r,n.target,s);f.locator=a,f.linkType=n.linkType}for(let c of n.locations){let{locationRoot:f,segments:p}=dN(c,{skipPrefix:e}),h=je.getFactoryWithDefault(r,f,s);for(let E=0;E{if(process.platform==="win32"&&r==="junctions"){let s;try{s=await le.lstatPromise(t)}catch{}if(!s||s.isDirectory()){await le.symlinkPromise(t,e,"junction");return}}await le.symlinkPromise(K.relative(K.dirname(e),t),e)};async function yke(t,e,r){let s=K.join(t,`${pV.default.randomBytes(16).toString("hex")}.tmp`);try{await le.writeFilePromise(s,r);try{await le.linkPromise(s,e)}catch{}}finally{await le.unlinkPromise(s)}}async function vRt({srcPath:t,dstPath:e,entry:r,globalHardlinksStore:s,baseFs:a,nmMode:n}){if(r.kind==="file"){if(n.value==="hardlinks-global"&&s&&r.digest){let f=K.join(s,r.digest.substring(0,2),`${r.digest.substring(2)}.dat`),p;try{let h=await le.statPromise(f);if(h&&(!r.mtimeMs||h.mtimeMs>r.mtimeMs||h.mtimeMs{await le.mkdirPromise(t,{recursive:!0});let f=async(E=vt.dot)=>{let C=K.join(e,E),S=await r.readdirPromise(C,{withFileTypes:!0}),P=new Map;for(let I of S){let R=K.join(E,I.name),N,U=K.join(C,I.name);if(I.isFile()){if(N={kind:"file",mode:(await r.lstatPromise(U)).mode},a.value==="hardlinks-global"){let W=await Nn.checksumFile(U,{baseFs:r,algorithm:"sha1"});N.digest=W}}else if(I.isDirectory())N={kind:"directory"};else if(I.isSymbolicLink())N={kind:"symlink",symlinkTo:await r.readlinkPromise(U)};else throw new Error(`Unsupported file type (file: ${U}, mode: 0o${await r.statSync(U).mode.toString(8).padStart(6,"0")})`);if(P.set(R,N),I.isDirectory()&&R!==_i){let W=await f(R);for(let[te,ie]of W)P.set(te,ie)}}return P},p;if(a.value==="hardlinks-global"&&s&&c){let E=K.join(s,c.substring(0,2),`${c.substring(2)}.json`);try{p=new Map(Object.entries(JSON.parse(await le.readFilePromise(E,"utf8"))))}catch{p=await f()}}else p=await f();let h=!1;for(let[E,C]of p){let S=K.join(e,E),P=K.join(t,E);if(C.kind==="directory")await le.mkdirPromise(P,{recursive:!0});else if(C.kind==="file"){let I=C.mtimeMs;await vRt({srcPath:S,dstPath:P,entry:C,nmMode:a,baseFs:r,globalHardlinksStore:s}),C.mtimeMs!==I&&(h=!0)}else C.kind==="symlink"&&await gV(K.resolve(K.dirname(P),C.symlinkTo),P,n)}if(a.value==="hardlinks-global"&&s&&h&&c){let E=K.join(s,c.substring(0,2),`${c.substring(2)}.json`);await le.removePromise(E),await yke(s,E,Buffer.from(JSON.stringify(Object.fromEntries(p))))}};function DRt(t,e,r,s){let a=new Map,n=new Map,c=new Map,f=!1,p=(h,E,C,S,P)=>{let I=!0,R=K.join(h,E),N=new Set;if(E===_i||E.startsWith("@")){let W;try{W=le.statSync(R)}catch{}I=!!W,W?W.mtimeMs>r?(f=!0,N=new Set(le.readdirSync(R))):N=new Set(C.children.get(E).children.keys()):f=!0;let te=e.get(h);if(te){let ie=K.join(h,_i,gN),Ae;try{Ae=le.statSync(ie)}catch{}if(!Ae)f=!0;else if(Ae.mtimeMs>r){f=!0;let ce=new Set(le.readdirSync(ie)),me=new Map;n.set(h,me);for(let[pe,Be]of te)ce.has(pe)&&me.set(pe,Be)}else n.set(h,te)}}else I=P.has(E);let U=C.children.get(E);if(I){let{linkType:W,locator:te}=U,ie={children:new Map,linkType:W,locator:te};if(S.children.set(E,ie),te){let Ae=je.getSetWithDefault(c,te);Ae.add(R),c.set(te,Ae)}for(let Ae of U.children.keys())p(R,Ae,U,ie,N)}else U.locator&&s.storedBuildState.delete(q.parseLocator(U.locator).locatorHash)};for(let[h,E]of t){let{linkType:C,locator:S}=E,P={children:new Map,linkType:C,locator:S};if(a.set(h,P),S){let I=je.getSetWithDefault(c,E.locator);I.add(h),c.set(E.locator,I)}E.children.has(_i)&&p(h,_i,E,P,new Set)}return{locationTree:a,binSymlinks:n,locatorLocations:c,installChangedByUser:f}}function Eke(t){let e=q.parseDescriptor(t);return q.isVirtualDescriptor(e)&&(e=q.devirtualizeDescriptor(e)),e.range.startsWith("link:")}async function bRt(t,e,r,{loadManifest:s}){let a=new Map;for(let[f,{locations:p}]of t){let h=Eke(f)?null:await s(f,p[0]),E=new Map;if(h)for(let[C,S]of h.bin){let P=K.join(p[0],S);S!==""&&le.existsSync(P)&&E.set(C,S)}a.set(f,E)}let n=new Map,c=(f,p,h)=>{let E=new Map,C=K.contains(r,f);if(h.locator&&C!==null){let S=a.get(h.locator);for(let[P,I]of S){let R=K.join(f,ue.toPortablePath(I));E.set(P,R)}for(let[P,I]of h.children){let R=K.join(f,P),N=c(R,R,I);N.size>0&&n.set(f,new Map([...n.get(f)||new Map,...N]))}}else for(let[S,P]of h.children){let I=c(K.join(f,S),p,P);for(let[R,N]of I)E.set(R,N)}return E};for(let[f,p]of e){let h=c(f,f,p);h.size>0&&n.set(f,new Map([...n.get(f)||new Map,...h]))}return n}var Ake=(t,e)=>{if(!t||!e)return t===e;let r=q.parseLocator(t);q.isVirtualLocator(r)&&(r=q.devirtualizeLocator(r));let s=q.parseLocator(e);return q.isVirtualLocator(s)&&(s=q.devirtualizeLocator(s)),q.areLocatorsEqual(r,s)};function dV(t){return K.join(t.get("globalFolder"),"store")}async function PRt(t,e,{baseFs:r,project:s,report:a,loadManifest:n,realLocatorChecksums:c}){let f=K.join(s.cwd,_i),{locationTree:p,binSymlinks:h,locatorLocations:E,installChangedByUser:C}=DRt(t.locationTree,t.binSymlinks,t.mtimeMs,s),S=mke(e,{skipPrefix:s.cwd}),P=[],I=async({srcDir:Be,dstDir:Ce,linkType:g,globalHardlinksStore:we,nmMode:Ee,windowsLinkType:fe,packageChecksum:se})=>{let X=(async()=>{try{g==="SOFT"?(await le.mkdirPromise(K.dirname(Ce),{recursive:!0}),await gV(K.resolve(Be),Ce,fe)):await SRt(Ce,Be,{baseFs:r,globalHardlinksStore:we,nmMode:Ee,windowsLinkType:fe,packageChecksum:se})}catch(De){throw De.message=`While persisting ${Be} -> ${Ce} ${De.message}`,De}finally{ie.tick()}})().then(()=>P.splice(P.indexOf(X),1));P.push(X),P.length>fke&&await Promise.race(P)},R=async(Be,Ce,g)=>{let we=(async()=>{let Ee=async(fe,se,X)=>{try{X.innerLoop||await le.mkdirPromise(se,{recursive:!0});let De=await le.readdirPromise(fe,{withFileTypes:!0});for(let Re of De){if(!X.innerLoop&&Re.name===gN)continue;let gt=K.join(fe,Re.name),j=K.join(se,Re.name);Re.isDirectory()?(Re.name!==_i||X&&X.innerLoop)&&(await le.mkdirPromise(j,{recursive:!0}),await Ee(gt,j,{...X,innerLoop:!0})):me.value==="hardlinks-local"||me.value==="hardlinks-global"?await le.linkPromise(gt,j):await le.copyFilePromise(gt,j,hke.default.constants.COPYFILE_FICLONE)}}catch(De){throw X.innerLoop||(De.message=`While cloning ${fe} -> ${se} ${De.message}`),De}finally{X.innerLoop||ie.tick()}};await Ee(Be,Ce,g)})().then(()=>P.splice(P.indexOf(we),1));P.push(we),P.length>fke&&await Promise.race(P)},N=async(Be,Ce,g)=>{if(g)for(let[we,Ee]of Ce.children){let fe=g.children.get(we);await N(K.join(Be,we),Ee,fe)}else{Ce.children.has(_i)&&await kw(K.join(Be,_i),{contentsOnly:!1});let we=K.basename(Be)===_i&&p.has(K.join(K.dirname(Be)));await kw(Be,{contentsOnly:Be===f,isWorkspaceDir:we})}};for(let[Be,Ce]of p){let g=S.get(Be);for(let[we,Ee]of Ce.children){if(we===".")continue;let fe=g&&g.children.get(we),se=K.join(Be,we);await N(se,Ee,fe)}}let U=async(Be,Ce,g)=>{if(g){Ake(Ce.locator,g.locator)||await kw(Be,{contentsOnly:Ce.linkType==="HARD"});for(let[we,Ee]of Ce.children){let fe=g.children.get(we);await U(K.join(Be,we),Ee,fe)}}else{Ce.children.has(_i)&&await kw(K.join(Be,_i),{contentsOnly:!0});let we=K.basename(Be)===_i&&S.has(K.join(K.dirname(Be)));await kw(Be,{contentsOnly:Ce.linkType==="HARD",isWorkspaceDir:we})}};for(let[Be,Ce]of S){let g=p.get(Be);for(let[we,Ee]of Ce.children){if(we===".")continue;let fe=g&&g.children.get(we);await U(K.join(Be,we),Ee,fe)}}let W=new Map,te=[];for(let[Be,Ce]of E)for(let g of Ce){let{locationRoot:we,segments:Ee}=dN(g,{skipPrefix:s.cwd}),fe=S.get(we),se=we;if(fe){for(let X of Ee)if(se=K.join(se,X),fe=fe.children.get(X),!fe)break;if(fe){let X=Ake(fe.locator,Be),De=e.get(fe.locator),Re=De.target,gt=se,j=De.linkType;if(X)W.has(Re)||W.set(Re,gt);else if(Re!==gt){let rt=q.parseLocator(fe.locator);q.isVirtualLocator(rt)&&(rt=q.devirtualizeLocator(rt)),te.push({srcDir:Re,dstDir:gt,linkType:j,realLocatorHash:rt.locatorHash})}}}}for(let[Be,{locations:Ce}]of e.entries())for(let g of Ce){let{locationRoot:we,segments:Ee}=dN(g,{skipPrefix:s.cwd}),fe=p.get(we),se=S.get(we),X=we,De=e.get(Be),Re=q.parseLocator(Be);q.isVirtualLocator(Re)&&(Re=q.devirtualizeLocator(Re));let gt=Re.locatorHash,j=De.target,rt=g;if(j===rt)continue;let Fe=De.linkType;for(let Ne of Ee)se=se.children.get(Ne);if(!fe)te.push({srcDir:j,dstDir:rt,linkType:Fe,realLocatorHash:gt});else for(let Ne of Ee)if(X=K.join(X,Ne),fe=fe.children.get(Ne),!fe){te.push({srcDir:j,dstDir:rt,linkType:Fe,realLocatorHash:gt});break}}let ie=ho.progressViaCounter(te.length),Ae=a.reportProgress(ie),ce=s.configuration.get("nmMode"),me={value:ce},pe=s.configuration.get("winLinkType");try{let Be=me.value==="hardlinks-global"?`${dV(s.configuration)}/v1`:null;if(Be&&!await le.existsPromise(Be)){await le.mkdirpPromise(Be);for(let g=0;g<256;g++)await le.mkdirPromise(K.join(Be,g.toString(16).padStart(2,"0")))}for(let g of te)(g.linkType==="SOFT"||!W.has(g.srcDir))&&(W.set(g.srcDir,g.dstDir),await I({...g,globalHardlinksStore:Be,nmMode:me,windowsLinkType:pe,packageChecksum:c.get(g.realLocatorHash)||null}));await Promise.all(P),P.length=0;for(let g of te){let we=W.get(g.srcDir);g.linkType!=="SOFT"&&g.dstDir!==we&&await R(we,g.dstDir,{nmMode:me})}await Promise.all(P),await le.mkdirPromise(f,{recursive:!0});let Ce=await bRt(e,S,s.cwd,{loadManifest:n});await xRt(h,Ce,s.cwd,pe),await BRt(s,e,Ce,me,{installChangedByUser:C}),ce=="hardlinks-global"&&me.value=="hardlinks-local"&&a.reportWarningOnce(74,"'nmMode' has been downgraded to 'hardlinks-local' due to global cache and install folder being on different devices")}finally{Ae.stop()}}async function xRt(t,e,r,s){for(let a of t.keys()){if(K.contains(r,a)===null)throw new Error(`Assertion failed. Excepted bin symlink location to be inside project dir, instead it was at ${a}`);if(!e.has(a)){let n=K.join(a,_i,gN);await le.removePromise(n)}}for(let[a,n]of e){if(K.contains(r,a)===null)throw new Error(`Assertion failed. Excepted bin symlink location to be inside project dir, instead it was at ${a}`);let c=K.join(a,_i,gN),f=t.get(a)||new Map;await le.mkdirPromise(c,{recursive:!0});for(let p of f.keys())n.has(p)||(await le.removePromise(K.join(c,p)),process.platform==="win32"&&await le.removePromise(K.join(c,`${p}.cmd`)));for(let[p,h]of n){let E=f.get(p),C=K.join(c,p);E!==h&&(process.platform==="win32"?await(0,pke.default)(ue.fromPortablePath(h),ue.fromPortablePath(C),{createPwshFile:!1}):(await le.removePromise(C),await gV(h,C,s),K.contains(r,await le.realpathPromise(h))!==null&&await le.chmodPromise(h,493)))}}}Ve();bt();rA();var sb=class extends og{constructor(){super(...arguments);this.mode="loose"}makeInstaller(r){return new mV(r)}},mV=class extends Gm{constructor(){super(...arguments);this.mode="loose"}async transformPnpSettings(r){let s=new fo({baseFs:new tA({maxOpenFiles:80,readOnlyArchives:!0})}),a=$xe(r,this.opts.project.cwd,s),{tree:n,errors:c}=YD(a,{pnpifyFs:!1,project:this.opts.project});if(!n){for(let{messageName:C,text:S}of c)this.opts.report.reportError(C,S);return}let f=new Map;r.fallbackPool=f;let p=(C,S)=>{let P=q.parseLocator(S.locator),I=q.stringifyIdent(P);I===C?f.set(C,P.reference):f.set(C,[I,P.reference])},h=K.join(this.opts.project.cwd,Er.nodeModules),E=n.get(h);if(!(typeof E>"u")){if("target"in E)throw new Error("Assertion failed: Expected the root junction point to be a directory");for(let C of E.dirList){let S=K.join(h,C),P=n.get(S);if(typeof P>"u")throw new Error("Assertion failed: Expected the child to have been registered");if("target"in P)p(C,P);else for(let I of P.dirList){let R=K.join(S,I),N=n.get(R);if(typeof N>"u")throw new Error("Assertion failed: Expected the subchild to have been registered");if("target"in N)p(`${C}/${I}`,N);else throw new Error("Assertion failed: Expected the leaf junction to be a package")}}}}};var kRt={hooks:{cleanGlobalArtifacts:async t=>{let e=dV(t);await le.removePromise(e)}},configuration:{nmHoistingLimits:{description:"Prevents packages to be hoisted past specific levels",type:"STRING",values:["workspaces","dependencies","none"],default:"none"},nmMode:{description:"Defines in which measure Yarn must use hardlinks and symlinks when generated `node_modules` directories.",type:"STRING",values:["classic","hardlinks-local","hardlinks-global"],default:"classic"},nmSelfReferences:{description:"Defines whether the linker should generate self-referencing symlinks for workspaces.",type:"BOOLEAN",default:!0}},linkers:[ib,sb]},QRt=kRt;var yz={};Vt(yz,{NpmHttpFetcher:()=>lb,NpmRemapResolver:()=>ub,NpmSemverFetcher:()=>ch,NpmSemverResolver:()=>fb,NpmTagResolver:()=>Ab,default:()=>Hjt,npmConfigUtils:()=>pi,npmHttpUtils:()=>an,npmPublishUtils:()=>D1});Ve();var bke=et(fi());var si="npm:";var an={};Vt(an,{AuthType:()=>vke,customPackageError:()=>Wm,del:()=>WRt,get:()=>Ym,getIdentUrl:()=>mN,getPackageMetadata:()=>Rw,handleInvalidAuthenticationError:()=>lg,post:()=>qRt,put:()=>GRt});Ve();Ve();bt();var CV=et(lS()),wke=et(vG()),Bke=et(fi());var pi={};Vt(pi,{RegistryType:()=>Ike,getAuditRegistry:()=>TRt,getAuthConfiguration:()=>IV,getDefaultRegistry:()=>ob,getPublishRegistry:()=>RRt,getRegistryConfiguration:()=>Cke,getScopeConfiguration:()=>EV,getScopeRegistry:()=>Qw,normalizeRegistry:()=>zc});var Ike=(s=>(s.AUDIT_REGISTRY="npmAuditRegistry",s.FETCH_REGISTRY="npmRegistryServer",s.PUBLISH_REGISTRY="npmPublishRegistry",s))(Ike||{});function zc(t){return t.replace(/\/$/,"")}function TRt({configuration:t}){return ob({configuration:t,type:"npmAuditRegistry"})}function RRt(t,{configuration:e}){return t.publishConfig?.registry?zc(t.publishConfig.registry):t.name?Qw(t.name.scope,{configuration:e,type:"npmPublishRegistry"}):ob({configuration:e,type:"npmPublishRegistry"})}function Qw(t,{configuration:e,type:r="npmRegistryServer"}){let s=EV(t,{configuration:e});if(s===null)return ob({configuration:e,type:r});let a=s.get(r);return a===null?ob({configuration:e,type:r}):zc(a)}function ob({configuration:t,type:e="npmRegistryServer"}){let r=t.get(e);return zc(r!==null?r:t.get("npmRegistryServer"))}function Cke(t,{configuration:e}){let r=e.get("npmRegistries"),s=zc(t),a=r.get(s);if(typeof a<"u")return a;let n=r.get(s.replace(/^[a-z]+:/,""));return typeof n<"u"?n:null}var FRt=new Map([["npmRegistryServer","https://npm.jsr.io/"]]);function EV(t,{configuration:e}){if(t===null)return null;let s=e.get("npmScopes").get(t);return s||(t==="jsr"?FRt:null)}function IV(t,{configuration:e,ident:r}){let s=r&&EV(r.scope,{configuration:e});return s?.get("npmAuthIdent")||s?.get("npmAuthToken")?s:Cke(t,{configuration:e})||e}var vke=(a=>(a[a.NO_AUTH=0]="NO_AUTH",a[a.BEST_EFFORT=1]="BEST_EFFORT",a[a.CONFIGURATION=2]="CONFIGURATION",a[a.ALWAYS_AUTH=3]="ALWAYS_AUTH",a))(vke||{});async function lg(t,{attemptedAs:e,registry:r,headers:s,configuration:a}){if(EN(t))throw new Yt(41,"Invalid OTP token");if(t.originalError?.name==="HTTPError"&&t.originalError?.response.statusCode===401)throw new Yt(41,`Invalid authentication (${typeof e!="string"?`as ${await VRt(r,s,{configuration:a})}`:`attempted as ${e}`})`)}function Wm(t,e){let r=t.response?.statusCode;return r?r===404?"Package not found":r>=500&&r<600?`The registry appears to be down (using a ${he.applyHyperlink(e,"local cache","https://yarnpkg.com/advanced/lexicon#local-cache")} might have protected you against such outages)`:null:null}function mN(t){return t.scope?`/@${t.scope}%2f${t.name}`:`/${t.name}`}var Ske=new Map,NRt=new Map;async function ORt(t){return await je.getFactoryWithDefault(Ske,t,async()=>{let e=null;try{e=await le.readJsonPromise(t)}catch{}return e})}async function LRt(t,e,{configuration:r,cached:s,registry:a,headers:n,version:c,...f}){return await je.getFactoryWithDefault(NRt,t,async()=>await Ym(mN(e),{...f,customErrorMessage:Wm,configuration:r,registry:a,ident:e,headers:{...n,"If-None-Match":s?.etag,"If-Modified-Since":s?.lastModified},wrapNetworkRequest:async p=>async()=>{let h=await p();if(h.statusCode===304){if(s===null)throw new Error("Assertion failed: cachedMetadata should not be null");return{...h,body:s.metadata}}let E=_Rt(JSON.parse(h.body.toString())),C={metadata:E,etag:h.headers.etag,lastModified:h.headers["last-modified"]};return Ske.set(t,Promise.resolve(C)),Promise.resolve().then(async()=>{let S=`${t}-${process.pid}.tmp`;await le.mkdirPromise(K.dirname(S),{recursive:!0}),await le.writeJsonPromise(S,C,{compact:!0}),await le.renamePromise(S,t)}).catch(()=>{}),{...h,body:E}}}))}function MRt(t){return t.scope!==null?`@${t.scope}-${t.name}-${t.scope.length}`:t.name}async function Rw(t,{cache:e,project:r,registry:s,headers:a,version:n,...c}){let{configuration:f}=r;s=ab(f,{ident:t,registry:s});let p=HRt(f,s),h=K.join(p,`${MRt(t)}.json`),E=null;if(!r.lockfileNeedsRefresh&&(E=await ORt(h),E)){if(typeof n<"u"&&typeof E.metadata.versions[n]<"u")return E.metadata;if(f.get("enableOfflineMode")){let C=structuredClone(E.metadata),S=new Set;if(e){for(let I of Object.keys(C.versions)){let R=q.makeLocator(t,`npm:${I}`),N=e.getLocatorMirrorPath(R);(!N||!le.existsSync(N))&&(delete C.versions[I],S.add(I))}let P=C["dist-tags"].latest;if(S.has(P)){let I=Object.keys(E.metadata.versions).sort(Bke.default.compare),R=I.indexOf(P);for(;S.has(I[R])&&R>=0;)R-=1;R>=0?C["dist-tags"].latest=I[R]:delete C["dist-tags"].latest}}return C}}return await LRt(h,t,{...c,configuration:f,cached:E,registry:s,headers:a,version:n})}var Dke=["name","dist.tarball","bin","scripts","os","cpu","libc","dependencies","dependenciesMeta","optionalDependencies","peerDependencies","peerDependenciesMeta","deprecated"];function _Rt(t){return{"dist-tags":t["dist-tags"],versions:Object.fromEntries(Object.entries(t.versions).map(([e,r])=>[e,(0,wke.default)(r,Dke)]))}}var URt=Nn.makeHash(...Dke).slice(0,6);function HRt(t,e){let r=jRt(t),s=new URL(e);return K.join(r,URt,s.hostname)}function jRt(t){return K.join(t.get("globalFolder"),"metadata/npm")}async function Ym(t,{configuration:e,headers:r,ident:s,authType:a,registry:n,...c}){n=ab(e,{ident:s,registry:n}),s&&s.scope&&typeof a>"u"&&(a=1);let f=await yN(n,{authType:a,configuration:e,ident:s});f&&(r={...r,authorization:f});try{return await An.get(t.charAt(0)==="/"?`${n}${t}`:t,{configuration:e,headers:r,...c})}catch(p){throw await lg(p,{registry:n,configuration:e,headers:r}),p}}async function qRt(t,e,{attemptedAs:r,configuration:s,headers:a,ident:n,authType:c=3,registry:f,otp:p,...h}){f=ab(s,{ident:n,registry:f});let E=await yN(f,{authType:c,configuration:s,ident:n});E&&(a={...a,authorization:E}),p&&(a={...a,...Tw(p)});try{return await An.post(f+t,e,{configuration:s,headers:a,...h})}catch(C){if(!EN(C)||p)throw await lg(C,{attemptedAs:r,registry:f,configuration:s,headers:a}),C;p=await wV(C,{configuration:s});let S={...a,...Tw(p)};try{return await An.post(`${f}${t}`,e,{configuration:s,headers:S,...h})}catch(P){throw await lg(P,{attemptedAs:r,registry:f,configuration:s,headers:a}),P}}}async function GRt(t,e,{attemptedAs:r,configuration:s,headers:a,ident:n,authType:c=3,registry:f,otp:p,...h}){f=ab(s,{ident:n,registry:f});let E=await yN(f,{authType:c,configuration:s,ident:n});E&&(a={...a,authorization:E}),p&&(a={...a,...Tw(p)});try{return await An.put(f+t,e,{configuration:s,headers:a,...h})}catch(C){if(!EN(C))throw await lg(C,{attemptedAs:r,registry:f,configuration:s,headers:a}),C;p=await wV(C,{configuration:s});let S={...a,...Tw(p)};try{return await An.put(`${f}${t}`,e,{configuration:s,headers:S,...h})}catch(P){throw await lg(P,{attemptedAs:r,registry:f,configuration:s,headers:a}),P}}}async function WRt(t,{attemptedAs:e,configuration:r,headers:s,ident:a,authType:n=3,registry:c,otp:f,...p}){c=ab(r,{ident:a,registry:c});let h=await yN(c,{authType:n,configuration:r,ident:a});h&&(s={...s,authorization:h}),f&&(s={...s,...Tw(f)});try{return await An.del(c+t,{configuration:r,headers:s,...p})}catch(E){if(!EN(E)||f)throw await lg(E,{attemptedAs:e,registry:c,configuration:r,headers:s}),E;f=await wV(E,{configuration:r});let C={...s,...Tw(f)};try{return await An.del(`${c}${t}`,{configuration:r,headers:C,...p})}catch(S){throw await lg(S,{attemptedAs:e,registry:c,configuration:r,headers:s}),S}}}function ab(t,{ident:e,registry:r}){if(typeof r>"u"&&e)return Qw(e.scope,{configuration:t});if(typeof r!="string")throw new Error("Assertion failed: The registry should be a string");return zc(r)}async function yN(t,{authType:e=2,configuration:r,ident:s}){let a=IV(t,{configuration:r,ident:s}),n=YRt(a,e);if(!n)return null;let c=await r.reduceHook(f=>f.getNpmAuthenticationHeader,void 0,t,{configuration:r,ident:s});if(c)return c;if(a.get("npmAuthToken"))return`Bearer ${a.get("npmAuthToken")}`;if(a.get("npmAuthIdent")){let f=a.get("npmAuthIdent");return f.includes(":")?`Basic ${Buffer.from(f).toString("base64")}`:`Basic ${f}`}if(n&&e!==1)throw new Yt(33,"No authentication configured for request");return null}function YRt(t,e){switch(e){case 2:return t.get("npmAlwaysAuth");case 1:case 3:return!0;case 0:return!1;default:throw new Error("Unreachable")}}async function VRt(t,e,{configuration:r}){if(typeof e>"u"||typeof e.authorization>"u")return"an anonymous user";try{return(await An.get(new URL(`${t}/-/whoami`).href,{configuration:r,headers:e,jsonResponse:!0})).username??"an unknown user"}catch{return"an unknown user"}}async function wV(t,{configuration:e}){let r=t.originalError?.response.headers["npm-notice"];if(r&&(await Ot.start({configuration:e,stdout:process.stdout,includeFooter:!1},async a=>{if(a.reportInfo(0,r.replace(/(https?:\/\/\S+)/g,he.pretty(e,"$1",he.Type.URL))),!process.env.YARN_IS_TEST_ENV){let n=r.match(/open (https?:\/\/\S+)/i);if(n&&As.openUrl){let{openNow:c}=await(0,CV.prompt)({type:"confirm",name:"openNow",message:"Do you want to try to open this url now?",required:!0,initial:!0,onCancel:()=>process.exit(130)});c&&(await As.openUrl(n[1])||(a.reportSeparator(),a.reportWarning(0,"We failed to automatically open the url; you'll have to open it yourself in your browser of choice.")))}}}),process.stdout.write(` +`)),process.env.YARN_IS_TEST_ENV)return process.env.YARN_INJECT_NPM_2FA_TOKEN||"";let{otp:s}=await(0,CV.prompt)({type:"password",name:"otp",message:"One-time password:",required:!0,onCancel:()=>process.exit(130)});return process.stdout.write(` +`),s}function EN(t){if(t.originalError?.name!=="HTTPError")return!1;try{return(t.originalError?.response.headers["www-authenticate"].split(/,\s*/).map(r=>r.toLowerCase())).includes("otp")}catch{return!1}}function Tw(t){return{"npm-otp":t}}var lb=class{supports(e,r){if(!e.reference.startsWith(si))return!1;let{selector:s,params:a}=q.parseRange(e.reference);return!(!bke.default.valid(s)||a===null||typeof a.__archiveUrl!="string")}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the remote server`),loader:()=>this.fetchFromNetwork(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),checksum:c}}async fetchFromNetwork(e,r){let{params:s}=q.parseRange(e.reference);if(s===null||typeof s.__archiveUrl!="string")throw new Error("Assertion failed: The archiveUrl querystring parameter should have been available");let a=await Ym(s.__archiveUrl,{customErrorMessage:Wm,configuration:r.project.configuration,ident:e});return await hs.convertToZip(a,{configuration:r.project.configuration,prefixPath:q.getIdentVendorPath(e),stripComponents:1})}};Ve();var ub=class{supportsDescriptor(e,r){return!(!e.range.startsWith(si)||!q.tryParseDescriptor(e.range.slice(si.length),!0))}supportsLocator(e,r){return!1}shouldPersistResolution(e,r){throw new Error("Unreachable")}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){let s=r.project.configuration.normalizeDependency(q.parseDescriptor(e.range.slice(si.length),!0));return r.resolver.getResolutionDependencies(s,r)}async getCandidates(e,r,s){let a=s.project.configuration.normalizeDependency(q.parseDescriptor(e.range.slice(si.length),!0));return await s.resolver.getCandidates(a,r,s)}async getSatisfying(e,r,s,a){let n=a.project.configuration.normalizeDependency(q.parseDescriptor(e.range.slice(si.length),!0));return a.resolver.getSatisfying(n,r,s,a)}resolve(e,r){throw new Error("Unreachable")}};Ve();Ve();var Pke=et(fi());var ch=class t{supports(e,r){if(!e.reference.startsWith(si))return!1;let s=new URL(e.reference);return!(!Pke.default.valid(s.pathname)||s.searchParams.has("__archiveUrl"))}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the remote registry`),loader:()=>this.fetchFromNetwork(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),checksum:c}}async fetchFromNetwork(e,r){let s;try{s=await Ym(t.getLocatorUrl(e),{customErrorMessage:Wm,configuration:r.project.configuration,ident:e})}catch{s=await Ym(t.getLocatorUrl(e).replace(/%2f/g,"/"),{customErrorMessage:Wm,configuration:r.project.configuration,ident:e})}return await hs.convertToZip(s,{configuration:r.project.configuration,prefixPath:q.getIdentVendorPath(e),stripComponents:1})}static isConventionalTarballUrl(e,r,{configuration:s}){let a=Qw(e.scope,{configuration:s}),n=t.getLocatorUrl(e);return r=r.replace(/^https?:(\/\/(?:[^/]+\.)?npmjs.org(?:$|\/))/,"https:$1"),a=a.replace(/^https:\/\/registry\.npmjs\.org($|\/)/,"https://registry.yarnpkg.com$1"),r=r.replace(/^https:\/\/registry\.npmjs\.org($|\/)/,"https://registry.yarnpkg.com$1"),r===a+n||r===a+n.replace(/%2f/g,"/")}static getLocatorUrl(e){let r=Or.clean(e.reference.slice(si.length));if(r===null)throw new Yt(10,"The npm semver resolver got selected, but the version isn't semver");return`${mN(e)}/-/${e.name}-${r}.tgz`}};Ve();Ve();Ve();var BV=et(fi());var IN=q.makeIdent(null,"node-gyp"),KRt=/\b(node-gyp|prebuild-install)\b/,fb=class{supportsDescriptor(e,r){return e.range.startsWith(si)?!!Or.validRange(e.range.slice(si.length)):!1}supportsLocator(e,r){if(!e.reference.startsWith(si))return!1;let{selector:s}=q.parseRange(e.reference);return!!BV.default.valid(s)}shouldPersistResolution(e,r){return!0}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){let a=Or.validRange(e.range.slice(si.length));if(a===null)throw new Error(`Expected a valid range, got ${e.range.slice(si.length)}`);let n=await Rw(e,{cache:s.fetchOptions?.cache,project:s.project,version:BV.default.valid(a.raw)?a.raw:void 0}),c=je.mapAndFilter(Object.keys(n.versions),h=>{try{let E=new Or.SemVer(h);if(a.test(E))return E}catch{}return je.mapAndFilter.skip}),f=c.filter(h=>!n.versions[h.raw].deprecated),p=f.length>0?f:c;return p.sort((h,E)=>-h.compare(E)),p.map(h=>{let E=q.makeLocator(e,`${si}${h.raw}`),C=n.versions[h.raw].dist.tarball;return ch.isConventionalTarballUrl(E,C,{configuration:s.project.configuration})?E:q.bindLocator(E,{__archiveUrl:C})})}async getSatisfying(e,r,s,a){let n=Or.validRange(e.range.slice(si.length));if(n===null)throw new Error(`Expected a valid range, got ${e.range.slice(si.length)}`);return{locators:je.mapAndFilter(s,p=>{if(p.identHash!==e.identHash)return je.mapAndFilter.skip;let h=q.tryParseRange(p.reference,{requireProtocol:si});if(!h)return je.mapAndFilter.skip;let E=new Or.SemVer(h.selector);return n.test(E)?{locator:p,version:E}:je.mapAndFilter.skip}).sort((p,h)=>-p.version.compare(h.version)).map(({locator:p})=>p),sorted:!0}}async resolve(e,r){let{selector:s}=q.parseRange(e.reference),a=Or.clean(s);if(a===null)throw new Yt(10,"The npm semver resolver got selected, but the version isn't semver");let n=await Rw(e,{cache:r.fetchOptions?.cache,project:r.project,version:a});if(!Object.hasOwn(n,"versions"))throw new Yt(15,'Registry returned invalid data for - missing "versions" field');if(!Object.hasOwn(n.versions,a))throw new Yt(16,`Registry failed to return reference "${a}"`);let c=new Ht;if(c.load(n.versions[a]),!c.dependencies.has(IN.identHash)&&!c.peerDependencies.has(IN.identHash)){for(let f of c.scripts.values())if(f.match(KRt)){c.dependencies.set(IN.identHash,q.makeDescriptor(IN,"latest"));break}}return{...e,version:a,languageName:"node",linkType:"HARD",conditions:c.getConditions(),dependencies:r.project.configuration.normalizeDependencyMap(c.dependencies),peerDependencies:c.peerDependencies,dependenciesMeta:c.dependenciesMeta,peerDependenciesMeta:c.peerDependenciesMeta,bin:c.bin}}};Ve();Ve();var xke=et(fi());var Ab=class{supportsDescriptor(e,r){return!(!e.range.startsWith(si)||!Hp.test(e.range.slice(si.length)))}supportsLocator(e,r){return!1}shouldPersistResolution(e,r){throw new Error("Unreachable")}bindDescriptor(e,r,s){return e}getResolutionDependencies(e,r){return{}}async getCandidates(e,r,s){let a=e.range.slice(si.length),n=await Rw(e,{cache:s.fetchOptions?.cache,project:s.project});if(!Object.hasOwn(n,"dist-tags"))throw new Yt(15,'Registry returned invalid data - missing "dist-tags" field');let c=n["dist-tags"];if(!Object.hasOwn(c,a))throw new Yt(16,`Registry failed to return tag "${a}"`);let f=c[a],p=q.makeLocator(e,`${si}${f}`),h=n.versions[f].dist.tarball;return ch.isConventionalTarballUrl(p,h,{configuration:s.project.configuration})?[p]:[q.bindLocator(p,{__archiveUrl:h})]}async getSatisfying(e,r,s,a){let n=[];for(let c of s){if(c.identHash!==e.identHash)continue;let f=q.tryParseRange(c.reference,{requireProtocol:si});if(!(!f||!xke.default.valid(f.selector))){if(f.params?.__archiveUrl){let p=q.makeRange({protocol:si,selector:f.selector,source:null,params:null}),[h]=await a.resolver.getCandidates(q.makeDescriptor(e,p),r,a);if(c.reference!==h.reference)continue}n.push(c)}}return{locators:n,sorted:!1}}async resolve(e,r){throw new Error("Unreachable")}};var D1={};Vt(D1,{getGitHead:()=>_jt,getPublishAccess:()=>EOe,getReadmeContent:()=>IOe,makePublishBody:()=>Mjt});Ve();Ve();bt();var f7={};Vt(f7,{PackCommand:()=>Gw,default:()=>bOt,packUtils:()=>IA});Ve();Ve();Ve();bt();Wt();var IA={};Vt(IA,{genPackList:()=>qN,genPackStream:()=>u7,genPackageManifest:()=>oTe,hasPackScripts:()=>l7,prepareForPack:()=>c7});Ve();bt();var a7=et(Sa()),iTe=et(eTe()),sTe=ye("zlib"),dOt=["/package.json","/readme","/readme.*","/license","/license.*","/licence","/licence.*","/changelog","/changelog.*"],mOt=["/package.tgz",".github",".git",".hg","node_modules",".npmignore",".gitignore",".#*",".DS_Store"];async function l7(t){return!!(In.hasWorkspaceScript(t,"prepack")||In.hasWorkspaceScript(t,"postpack"))}async function c7(t,{report:e},r){await In.maybeExecuteWorkspaceLifecycleScript(t,"prepack",{report:e});try{let s=K.join(t.cwd,Ht.fileName);await le.existsPromise(s)&&await t.manifest.loadFile(s,{baseFs:le}),await r()}finally{await In.maybeExecuteWorkspaceLifecycleScript(t,"postpack",{report:e})}}async function u7(t,e){typeof e>"u"&&(e=await qN(t));let r=new Set;for(let n of t.manifest.publishConfig?.executableFiles??new Set)r.add(K.normalize(n));for(let n of t.manifest.bin.values())r.add(K.normalize(n));let s=iTe.default.pack();process.nextTick(async()=>{for(let n of e){let c=K.normalize(n),f=K.resolve(t.cwd,c),p=K.join("package",c),h=await le.lstatPromise(f),E={name:p,mtime:new Date(ui.SAFE_TIME*1e3)},C=r.has(c)?493:420,S,P,I=new Promise((N,U)=>{S=N,P=U}),R=N=>{N?P(N):S()};if(h.isFile()){let N;c==="package.json"?N=Buffer.from(JSON.stringify(await oTe(t),null,2)):N=await le.readFilePromise(f),s.entry({...E,mode:C,type:"file"},N,R)}else h.isSymbolicLink()?s.entry({...E,mode:C,type:"symlink",linkname:await le.readlinkPromise(f)},R):R(new Error(`Unsupported file type ${h.mode} for ${ue.fromPortablePath(c)}`));await I}s.finalize()});let a=(0,sTe.createGzip)();return s.pipe(a),a}async function oTe(t){let e=JSON.parse(JSON.stringify(t.manifest.raw));return await t.project.configuration.triggerHook(r=>r.beforeWorkspacePacking,t,e),e}async function qN(t){let e=t.project,r=e.configuration,s={accept:[],reject:[]};for(let C of mOt)s.reject.push(C);for(let C of dOt)s.accept.push(C);s.reject.push(r.get("rcFilename"));let a=C=>{if(C===null||!C.startsWith(`${t.cwd}/`))return;let S=K.relative(t.cwd,C),P=K.resolve(vt.root,S);s.reject.push(P)};a(K.resolve(e.cwd,Er.lockfile)),a(r.get("cacheFolder")),a(r.get("globalFolder")),a(r.get("installStatePath")),a(r.get("virtualFolder")),a(r.get("yarnPath")),await r.triggerHook(C=>C.populateYarnPaths,e,C=>{a(C)});for(let C of e.workspaces){let S=K.relative(t.cwd,C.cwd);S!==""&&!S.match(/^(\.\.)?\//)&&s.reject.push(`/${S}`)}let n={accept:[],reject:[]},c=t.manifest.publishConfig?.main??t.manifest.main,f=t.manifest.publishConfig?.module??t.manifest.module,p=t.manifest.publishConfig?.browser??t.manifest.browser,h=t.manifest.publishConfig?.bin??t.manifest.bin;c!=null&&n.accept.push(K.resolve(vt.root,c)),f!=null&&n.accept.push(K.resolve(vt.root,f)),typeof p=="string"&&n.accept.push(K.resolve(vt.root,p));for(let C of h.values())n.accept.push(K.resolve(vt.root,C));if(p instanceof Map)for(let[C,S]of p.entries())n.accept.push(K.resolve(vt.root,C)),typeof S=="string"&&n.accept.push(K.resolve(vt.root,S));let E=t.manifest.files!==null;if(E){n.reject.push("/*");for(let C of t.manifest.files)aTe(n.accept,C,{cwd:vt.root})}return await yOt(t.cwd,{hasExplicitFileList:E,globalList:s,ignoreList:n})}async function yOt(t,{hasExplicitFileList:e,globalList:r,ignoreList:s}){let a=[],n=new jf(t),c=[[vt.root,[s]]];for(;c.length>0;){let[f,p]=c.pop(),h=await n.lstatPromise(f);if(!rTe(f,{globalList:r,ignoreLists:h.isDirectory()?null:p}))if(h.isDirectory()){let E=await n.readdirPromise(f),C=!1,S=!1;if(!e||f!==vt.root)for(let R of E)C=C||R===".gitignore",S=S||R===".npmignore";let P=S?await tTe(n,f,".npmignore"):C?await tTe(n,f,".gitignore"):null,I=P!==null?[P].concat(p):p;rTe(f,{globalList:r,ignoreLists:p})&&(I=[...p,{accept:[],reject:["**/*"]}]);for(let R of E)c.push([K.resolve(f,R),I])}else(h.isFile()||h.isSymbolicLink())&&a.push(K.relative(vt.root,f))}return a.sort()}async function tTe(t,e,r){let s={accept:[],reject:[]},a=await t.readFilePromise(K.join(e,r),"utf8");for(let n of a.split(/\n/g))aTe(s.reject,n,{cwd:e});return s}function EOt(t,{cwd:e}){let r=t[0]==="!";return r&&(t=t.slice(1)),t.match(/\.{0,1}\//)&&(t=K.resolve(e,t)),r&&(t=`!${t}`),t}function aTe(t,e,{cwd:r}){let s=e.trim();s===""||s[0]==="#"||t.push(EOt(s,{cwd:r}))}function rTe(t,{globalList:e,ignoreLists:r}){let s=jN(t,e.accept);if(s!==0)return s===2;let a=jN(t,e.reject);if(a!==0)return a===1;if(r!==null)for(let n of r){let c=jN(t,n.accept);if(c!==0)return c===2;let f=jN(t,n.reject);if(f!==0)return f===1}return!1}function jN(t,e){let r=e,s=[];for(let a=0;a{await c7(a,{report:p},async()=>{p.reportJson({base:ue.fromPortablePath(a.cwd)});let h=await qN(a);for(let E of h)p.reportInfo(null,ue.fromPortablePath(E)),p.reportJson({location:ue.fromPortablePath(E)});if(!this.dryRun){let E=await u7(a,h);await le.mkdirPromise(K.dirname(c),{recursive:!0});let C=le.createWriteStream(c);E.pipe(C),await new Promise(S=>{C.on("finish",S)})}}),this.dryRun||(p.reportInfo(0,`Package archive generated in ${he.pretty(r,c,he.Type.PATH)}`),p.reportJson({output:ue.fromPortablePath(c)}))})).exitCode()}};function IOt(t,{workspace:e}){let r=t.replace("%s",COt(e)).replace("%v",wOt(e));return ue.toPortablePath(r)}function COt(t){return t.manifest.name!==null?q.slugifyIdent(t.manifest.name):"package"}function wOt(t){return t.manifest.version!==null?t.manifest.version:"unknown"}var BOt=["dependencies","devDependencies","peerDependencies"],vOt="workspace:",SOt=(t,e)=>{e.publishConfig&&(e.publishConfig.type&&(e.type=e.publishConfig.type),e.publishConfig.main&&(e.main=e.publishConfig.main),e.publishConfig.browser&&(e.browser=e.publishConfig.browser),e.publishConfig.module&&(e.module=e.publishConfig.module),e.publishConfig.exports&&(e.exports=e.publishConfig.exports),e.publishConfig.imports&&(e.imports=e.publishConfig.imports),e.publishConfig.bin&&(e.bin=e.publishConfig.bin));let r=t.project;for(let s of BOt)for(let a of t.manifest.getForScope(s).values()){let n=r.tryWorkspaceByDescriptor(a),c=q.parseRange(a.range);if(c.protocol===vOt)if(n===null){if(r.tryWorkspaceByIdent(a)===null)throw new Yt(21,`${q.prettyDescriptor(r.configuration,a)}: No local workspace found for this range`)}else{let f;q.areDescriptorsEqual(a,n.anchoredDescriptor)||c.selector==="*"?f=n.manifest.version??"0.0.0":c.selector==="~"||c.selector==="^"?f=`${c.selector}${n.manifest.version??"0.0.0"}`:f=c.selector;let p=s==="dependencies"?q.makeDescriptor(a,"unknown"):null,h=p!==null&&t.manifest.ensureDependencyMeta(p).optional?"optionalDependencies":s;e[h][q.stringifyIdent(a)]=f}}},DOt={hooks:{beforeWorkspacePacking:SOt},commands:[Gw]},bOt=DOt;var yOe=et(dTe());Ve();var dOe=et(gOe()),{env:Bt}=process,xjt="application/vnd.in-toto+json",kjt="https://in-toto.io/Statement/v0.1",Qjt="https://in-toto.io/Statement/v1",Tjt="https://slsa.dev/provenance/v0.2",Rjt="https://slsa.dev/provenance/v1",Fjt="https://github.com/actions/runner",Njt="https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",Ojt="https://github.com/npm/cli/gitlab",Ljt="v0alpha1",mOe=async(t,e)=>{let r;if(Bt.GITHUB_ACTIONS){if(!Bt.ACTIONS_ID_TOKEN_REQUEST_URL)throw new Yt(91,'Provenance generation in GitHub Actions requires "write" access to the "id-token" permission');let s=(Bt.GITHUB_WORKFLOW_REF||"").replace(`${Bt.GITHUB_REPOSITORY}/`,""),a=s.indexOf("@"),n=s.slice(0,a),c=s.slice(a+1);r={_type:Qjt,subject:t,predicateType:Rjt,predicate:{buildDefinition:{buildType:Njt,externalParameters:{workflow:{ref:c,repository:`${Bt.GITHUB_SERVER_URL}/${Bt.GITHUB_REPOSITORY}`,path:n}},internalParameters:{github:{event_name:Bt.GITHUB_EVENT_NAME,repository_id:Bt.GITHUB_REPOSITORY_ID,repository_owner_id:Bt.GITHUB_REPOSITORY_OWNER_ID}},resolvedDependencies:[{uri:`git+${Bt.GITHUB_SERVER_URL}/${Bt.GITHUB_REPOSITORY}@${Bt.GITHUB_REF}`,digest:{gitCommit:Bt.GITHUB_SHA}}]},runDetails:{builder:{id:`${Fjt}/${Bt.RUNNER_ENVIRONMENT}`},metadata:{invocationId:`${Bt.GITHUB_SERVER_URL}/${Bt.GITHUB_REPOSITORY}/actions/runs/${Bt.GITHUB_RUN_ID}/attempts/${Bt.GITHUB_RUN_ATTEMPT}`}}}}}else if(Bt.GITLAB_CI){if(!Bt.SIGSTORE_ID_TOKEN)throw new Yt(91,`Provenance generation in GitLab CI requires "SIGSTORE_ID_TOKEN" with "sigstore" audience to be present in "id_tokens". For more info see: +https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html`);r={_type:kjt,subject:t,predicateType:Tjt,predicate:{buildType:`${Ojt}/${Ljt}`,builder:{id:`${Bt.CI_PROJECT_URL}/-/runners/${Bt.CI_RUNNER_ID}`},invocation:{configSource:{uri:`git+${Bt.CI_PROJECT_URL}`,digest:{sha1:Bt.CI_COMMIT_SHA},entryPoint:Bt.CI_JOB_NAME},parameters:{CI:Bt.CI,CI_API_GRAPHQL_URL:Bt.CI_API_GRAPHQL_URL,CI_API_V4_URL:Bt.CI_API_V4_URL,CI_BUILD_BEFORE_SHA:Bt.CI_BUILD_BEFORE_SHA,CI_BUILD_ID:Bt.CI_BUILD_ID,CI_BUILD_NAME:Bt.CI_BUILD_NAME,CI_BUILD_REF:Bt.CI_BUILD_REF,CI_BUILD_REF_NAME:Bt.CI_BUILD_REF_NAME,CI_BUILD_REF_SLUG:Bt.CI_BUILD_REF_SLUG,CI_BUILD_STAGE:Bt.CI_BUILD_STAGE,CI_COMMIT_BEFORE_SHA:Bt.CI_COMMIT_BEFORE_SHA,CI_COMMIT_BRANCH:Bt.CI_COMMIT_BRANCH,CI_COMMIT_REF_NAME:Bt.CI_COMMIT_REF_NAME,CI_COMMIT_REF_PROTECTED:Bt.CI_COMMIT_REF_PROTECTED,CI_COMMIT_REF_SLUG:Bt.CI_COMMIT_REF_SLUG,CI_COMMIT_SHA:Bt.CI_COMMIT_SHA,CI_COMMIT_SHORT_SHA:Bt.CI_COMMIT_SHORT_SHA,CI_COMMIT_TIMESTAMP:Bt.CI_COMMIT_TIMESTAMP,CI_COMMIT_TITLE:Bt.CI_COMMIT_TITLE,CI_CONFIG_PATH:Bt.CI_CONFIG_PATH,CI_DEFAULT_BRANCH:Bt.CI_DEFAULT_BRANCH,CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX:Bt.CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX,CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX:Bt.CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX,CI_DEPENDENCY_PROXY_SERVER:Bt.CI_DEPENDENCY_PROXY_SERVER,CI_DEPENDENCY_PROXY_USER:Bt.CI_DEPENDENCY_PROXY_USER,CI_JOB_ID:Bt.CI_JOB_ID,CI_JOB_NAME:Bt.CI_JOB_NAME,CI_JOB_NAME_SLUG:Bt.CI_JOB_NAME_SLUG,CI_JOB_STAGE:Bt.CI_JOB_STAGE,CI_JOB_STARTED_AT:Bt.CI_JOB_STARTED_AT,CI_JOB_URL:Bt.CI_JOB_URL,CI_NODE_TOTAL:Bt.CI_NODE_TOTAL,CI_PAGES_DOMAIN:Bt.CI_PAGES_DOMAIN,CI_PAGES_URL:Bt.CI_PAGES_URL,CI_PIPELINE_CREATED_AT:Bt.CI_PIPELINE_CREATED_AT,CI_PIPELINE_ID:Bt.CI_PIPELINE_ID,CI_PIPELINE_IID:Bt.CI_PIPELINE_IID,CI_PIPELINE_SOURCE:Bt.CI_PIPELINE_SOURCE,CI_PIPELINE_URL:Bt.CI_PIPELINE_URL,CI_PROJECT_CLASSIFICATION_LABEL:Bt.CI_PROJECT_CLASSIFICATION_LABEL,CI_PROJECT_DESCRIPTION:Bt.CI_PROJECT_DESCRIPTION,CI_PROJECT_ID:Bt.CI_PROJECT_ID,CI_PROJECT_NAME:Bt.CI_PROJECT_NAME,CI_PROJECT_NAMESPACE:Bt.CI_PROJECT_NAMESPACE,CI_PROJECT_NAMESPACE_ID:Bt.CI_PROJECT_NAMESPACE_ID,CI_PROJECT_PATH:Bt.CI_PROJECT_PATH,CI_PROJECT_PATH_SLUG:Bt.CI_PROJECT_PATH_SLUG,CI_PROJECT_REPOSITORY_LANGUAGES:Bt.CI_PROJECT_REPOSITORY_LANGUAGES,CI_PROJECT_ROOT_NAMESPACE:Bt.CI_PROJECT_ROOT_NAMESPACE,CI_PROJECT_TITLE:Bt.CI_PROJECT_TITLE,CI_PROJECT_URL:Bt.CI_PROJECT_URL,CI_PROJECT_VISIBILITY:Bt.CI_PROJECT_VISIBILITY,CI_REGISTRY:Bt.CI_REGISTRY,CI_REGISTRY_IMAGE:Bt.CI_REGISTRY_IMAGE,CI_REGISTRY_USER:Bt.CI_REGISTRY_USER,CI_RUNNER_DESCRIPTION:Bt.CI_RUNNER_DESCRIPTION,CI_RUNNER_ID:Bt.CI_RUNNER_ID,CI_RUNNER_TAGS:Bt.CI_RUNNER_TAGS,CI_SERVER_HOST:Bt.CI_SERVER_HOST,CI_SERVER_NAME:Bt.CI_SERVER_NAME,CI_SERVER_PORT:Bt.CI_SERVER_PORT,CI_SERVER_PROTOCOL:Bt.CI_SERVER_PROTOCOL,CI_SERVER_REVISION:Bt.CI_SERVER_REVISION,CI_SERVER_SHELL_SSH_HOST:Bt.CI_SERVER_SHELL_SSH_HOST,CI_SERVER_SHELL_SSH_PORT:Bt.CI_SERVER_SHELL_SSH_PORT,CI_SERVER_URL:Bt.CI_SERVER_URL,CI_SERVER_VERSION:Bt.CI_SERVER_VERSION,CI_SERVER_VERSION_MAJOR:Bt.CI_SERVER_VERSION_MAJOR,CI_SERVER_VERSION_MINOR:Bt.CI_SERVER_VERSION_MINOR,CI_SERVER_VERSION_PATCH:Bt.CI_SERVER_VERSION_PATCH,CI_TEMPLATE_REGISTRY_HOST:Bt.CI_TEMPLATE_REGISTRY_HOST,GITLAB_CI:Bt.GITLAB_CI,GITLAB_FEATURES:Bt.GITLAB_FEATURES,GITLAB_USER_ID:Bt.GITLAB_USER_ID,GITLAB_USER_LOGIN:Bt.GITLAB_USER_LOGIN,RUNNER_GENERATE_ARTIFACTS_METADATA:Bt.RUNNER_GENERATE_ARTIFACTS_METADATA},environment:{name:Bt.CI_RUNNER_DESCRIPTION,architecture:Bt.CI_RUNNER_EXECUTABLE_ARCH,server:Bt.CI_SERVER_URL,project:Bt.CI_PROJECT_PATH,job:{id:Bt.CI_JOB_ID},pipeline:{id:Bt.CI_PIPELINE_ID,ref:Bt.CI_CONFIG_PATH}}},metadata:{buildInvocationId:`${Bt.CI_JOB_URL}`,completeness:{parameters:!0,environment:!0,materials:!1},reproducible:!1},materials:[{uri:`git+${Bt.CI_PROJECT_URL}`,digest:{sha1:Bt.CI_COMMIT_SHA}}]}}}else throw new Yt(91,"Provenance generation is only supported in GitHub Actions and GitLab CI");return dOe.attest(Buffer.from(JSON.stringify(r)),xjt,e)};async function Mjt(t,e,{access:r,tag:s,registry:a,gitHead:n,provenance:c}){let f=t.manifest.name,p=t.manifest.version,h=q.stringifyIdent(f),E=yOe.default.fromData(e,{algorithms:["sha1","sha512"]}),C=r??EOe(t,f),S=await IOe(t),P=await IA.genPackageManifest(t),I=`${h}-${p}.tgz`,R=new URL(`${zc(a)}/${h}/-/${I}`),N={[I]:{content_type:"application/octet-stream",data:e.toString("base64"),length:e.length}};if(c){let U={name:`pkg:npm/${h.replace(/^@/,"%40")}@${p}`,digest:{sha512:E.sha512[0].hexDigest()}},W=await mOe([U]),te=JSON.stringify(W);N[`${h}-${p}.sigstore`]={content_type:W.mediaType,data:te,length:te.length}}return{_id:h,_attachments:N,name:h,access:C,"dist-tags":{[s]:p},versions:{[p]:{...P,_id:`${h}@${p}`,name:h,version:p,gitHead:n,dist:{shasum:E.sha1[0].hexDigest(),integrity:E.sha512[0].toString(),tarball:R.toString()}}},readme:S}}async function _jt(t){try{let{stdout:e}=await Gr.execvp("git",["rev-parse","--revs-only","HEAD"],{cwd:t});return e.trim()===""?void 0:e.trim()}catch{return}}function EOe(t,e){let r=t.project.configuration;return t.manifest.publishConfig&&typeof t.manifest.publishConfig.access=="string"?t.manifest.publishConfig.access:r.get("npmPublishAccess")!==null?r.get("npmPublishAccess"):e.scope?"restricted":"public"}async function IOe(t){let e=ue.toPortablePath(`${t.cwd}/README.md`),r=t.manifest.name,a=`# ${q.stringifyIdent(r)} +`;try{a=await le.readFilePromise(e,"utf8")}catch(n){if(n.code==="ENOENT")return a;throw n}return a}var mz={npmAlwaysAuth:{description:"URL of the selected npm registry (note: npm enterprise isn't supported)",type:"BOOLEAN",default:!1},npmAuthIdent:{description:"Authentication identity for the npm registry (_auth in npm and yarn v1)",type:"SECRET",default:null},npmAuthToken:{description:"Authentication token for the npm registry (_authToken in npm and yarn v1)",type:"SECRET",default:null}},COe={npmAuditRegistry:{description:"Registry to query for audit reports",type:"STRING",default:null},npmPublishRegistry:{description:"Registry to push packages to",type:"STRING",default:null},npmRegistryServer:{description:"URL of the selected npm registry (note: npm enterprise isn't supported)",type:"STRING",default:"https://registry.yarnpkg.com"}},Ujt={configuration:{...mz,...COe,npmScopes:{description:"Settings per package scope",type:"MAP",valueDefinition:{description:"",type:"SHAPE",properties:{...mz,...COe}}},npmRegistries:{description:"Settings per registry",type:"MAP",normalizeKeys:zc,valueDefinition:{description:"",type:"SHAPE",properties:{...mz}}}},fetchers:[lb,ch],resolvers:[ub,fb,Ab]},Hjt=Ujt;var bz={};Vt(bz,{NpmAuditCommand:()=>P1,NpmInfoCommand:()=>x1,NpmLoginCommand:()=>k1,NpmLogoutCommand:()=>T1,NpmPublishCommand:()=>R1,NpmTagAddCommand:()=>N1,NpmTagListCommand:()=>F1,NpmTagRemoveCommand:()=>O1,NpmWhoamiCommand:()=>L1,default:()=>Kjt,npmAuditTypes:()=>fP,npmAuditUtils:()=>ML});Ve();Ve();Wt();var Bz=et(Sa());Ul();var fP={};Vt(fP,{Environment:()=>cP,Severity:()=>uP});var cP=(s=>(s.All="all",s.Production="production",s.Development="development",s))(cP||{}),uP=(n=>(n.Info="info",n.Low="low",n.Moderate="moderate",n.High="high",n.Critical="critical",n))(uP||{});var ML={};Vt(ML,{allSeverities:()=>b1,getPackages:()=>wz,getReportTree:()=>Iz,getSeverityInclusions:()=>Ez,getTopLevelDependencies:()=>Cz});Ve();var wOe=et(fi());var b1=["info","low","moderate","high","critical"];function Ez(t){if(typeof t>"u")return new Set(b1);let e=b1.indexOf(t),r=b1.slice(e);return new Set(r)}function Iz(t){let e={},r={children:e};for(let[s,a]of je.sortMap(Object.entries(t),n=>n[0]))for(let n of je.sortMap(a,c=>`${c.id}`))e[`${s}/${n.id}`]={value:he.tuple(he.Type.IDENT,q.parseIdent(s)),children:{ID:typeof n.id<"u"&&{label:"ID",value:he.tuple(he.Type.ID,n.id)},Issue:{label:"Issue",value:he.tuple(he.Type.NO_HINT,n.title)},URL:typeof n.url<"u"&&{label:"URL",value:he.tuple(he.Type.URL,n.url)},Severity:{label:"Severity",value:he.tuple(he.Type.NO_HINT,n.severity)},"Vulnerable Versions":{label:"Vulnerable Versions",value:he.tuple(he.Type.RANGE,n.vulnerable_versions)},"Tree Versions":{label:"Tree Versions",children:[...n.versions].sort(wOe.default.compare).map(c=>({value:he.tuple(he.Type.REFERENCE,c)}))},Dependents:{label:"Dependents",children:je.sortMap(n.dependents,c=>q.stringifyLocator(c)).map(c=>({value:he.tuple(he.Type.LOCATOR,c)}))}}};return r}function Cz(t,e,{all:r,environment:s}){let a=[],n=r?t.workspaces:[e],c=["all","production"].includes(s),f=["all","development"].includes(s);for(let p of n)for(let h of p.anchoredPackage.dependencies.values())(p.manifest.devDependencies.has(h.identHash)?!f:!c)||a.push({workspace:p,dependency:h});return a}function wz(t,e,{recursive:r}){let s=new Map,a=new Set,n=[],c=(f,p)=>{let h=t.storedResolutions.get(p.descriptorHash);if(typeof h>"u")throw new Error("Assertion failed: The resolution should have been registered");if(!a.has(h))a.add(h);else return;let E=t.storedPackages.get(h);if(typeof E>"u")throw new Error("Assertion failed: The package should have been registered");if(q.ensureDevirtualizedLocator(E).reference.startsWith("npm:")&&E.version!==null){let S=q.stringifyIdent(E),P=je.getMapWithDefault(s,S);je.getArrayWithDefault(P,E.version).push(f)}if(r)for(let S of E.dependencies.values())n.push([E,S])};for(let{workspace:f,dependency:p}of e)n.push([f.anchoredLocator,p]);for(;n.length>0;){let[f,p]=n.shift();c(f,p)}return s}var P1=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("-A,--all",!1,{description:"Audit dependencies from all workspaces"});this.recursive=ge.Boolean("-R,--recursive",!1,{description:"Audit transitive dependencies as well"});this.environment=ge.String("--environment","all",{description:"Which environments to cover",validator:Ao(cP)});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.noDeprecations=ge.Boolean("--no-deprecations",!1,{description:"Don't warn about deprecated packages"});this.severity=ge.String("--severity","info",{description:"Minimal severity requested for packages to be displayed",validator:Ao(uP)});this.excludes=ge.Array("--exclude",[],{description:"Array of glob patterns of packages to exclude from audit"});this.ignores=ge.Array("--ignore",[],{description:"Array of glob patterns of advisory ID's to ignore in the audit report"})}static{this.paths=[["npm","audit"]]}static{this.usage=ot.Usage({description:"perform a vulnerability audit against the installed packages",details:` + This command checks for known security reports on the packages you use. The reports are by default extracted from the npm registry, and may or may not be relevant to your actual program (not all vulnerabilities affect all code paths). + + For consistency with our other commands the default is to only check the direct dependencies for the active workspace. To extend this search to all workspaces, use \`-A,--all\`. To extend this search to both direct and transitive dependencies, use \`-R,--recursive\`. + + Applying the \`--severity\` flag will limit the audit table to vulnerabilities of the corresponding severity and above. Valid values are ${b1.map(r=>`\`${r}\``).join(", ")}. + + If the \`--json\` flag is set, Yarn will print the output exactly as received from the registry. Regardless of this flag, the process will exit with a non-zero exit code if a report is found for the selected packages. + + If certain packages produce false positives for a particular environment, the \`--exclude\` flag can be used to exclude any number of packages from the audit. This can also be set in the configuration file with the \`npmAuditExcludePackages\` option. + + If particular advisories are needed to be ignored, the \`--ignore\` flag can be used with Advisory ID's to ignore any number of advisories in the audit report. This can also be set in the configuration file with the \`npmAuditIgnoreAdvisories\` option. + + To understand the dependency tree requiring vulnerable packages, check the raw report with the \`--json\` flag or use \`yarn why package\` to get more information as to who depends on them. + `,examples:[["Checks for known security issues with the installed packages. The output is a list of known issues.","yarn npm audit"],["Audit dependencies in all workspaces","yarn npm audit --all"],["Limit auditing to `dependencies` (excludes `devDependencies`)","yarn npm audit --environment production"],["Show audit report as valid JSON","yarn npm audit --json"],["Audit all direct and transitive dependencies","yarn npm audit --recursive"],["Output moderate (or more severe) vulnerabilities","yarn npm audit --severity moderate"],["Exclude certain packages","yarn npm audit --exclude package1 --exclude package2"],["Ignore specific advisories","yarn npm audit --ignore 1234567 --ignore 7654321"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState();let n=Cz(s,a,{all:this.all,environment:this.environment}),c=wz(s,n,{recursive:this.recursive}),f=Array.from(new Set([...r.get("npmAuditExcludePackages"),...this.excludes])),p=Object.create(null);for(let[N,U]of c)f.some(W=>Bz.default.isMatch(N,W))||(p[N]=[...U.keys()]);let h=pi.getAuditRegistry({configuration:r}),E,C=await uA.start({configuration:r,stdout:this.context.stdout},async()=>{let N=an.post("/-/npm/v1/security/advisories/bulk",p,{authType:an.AuthType.BEST_EFFORT,configuration:r,jsonResponse:!0,registry:h}),U=this.noDeprecations?[]:await Promise.all(Array.from(Object.entries(p),async([te,ie])=>{let Ae=await an.getPackageMetadata(q.parseIdent(te),{project:s});return je.mapAndFilter(ie,ce=>{let{deprecated:me}=Ae.versions[ce];return me?[te,ce,me]:je.mapAndFilter.skip})})),W=await N;for(let[te,ie,Ae]of U.flat(1))Object.hasOwn(W,te)&&W[te].some(ce=>Or.satisfiesWithPrereleases(ie,ce.vulnerable_versions))||(W[te]??=[],W[te].push({id:`${te} (deprecation)`,title:(typeof Ae=="string"?Ae:"").trim()||"This package has been deprecated.",severity:"moderate",vulnerable_versions:ie}));E=W});if(C.hasErrors())return C.exitCode();let S=Ez(this.severity),P=Array.from(new Set([...r.get("npmAuditIgnoreAdvisories"),...this.ignores])),I=Object.create(null);for(let[N,U]of Object.entries(E)){let W=U.filter(te=>!Bz.default.isMatch(`${te.id}`,P)&&S.has(te.severity));W.length>0&&(I[N]=W.map(te=>{let ie=c.get(N);if(typeof ie>"u")throw new Error("Assertion failed: Expected the registry to only return packages that were requested");let Ae=[...ie.keys()].filter(me=>Or.satisfiesWithPrereleases(me,te.vulnerable_versions)),ce=new Map;for(let me of Ae)for(let pe of ie.get(me))ce.set(pe.locatorHash,pe);return{...te,versions:Ae,dependents:[...ce.values()]}}))}let R=Object.keys(I).length>0;return R?(ks.emitTree(Iz(I),{configuration:r,json:this.json,stdout:this.context.stdout,separators:2}),1):(await Ot.start({configuration:r,includeFooter:!1,json:this.json,stdout:this.context.stdout},async N=>{N.reportInfo(1,"No audit suggestions")}),R?1:0)}};Ve();Ve();bt();Wt();var vz=et(fi()),Sz=ye("util"),x1=class extends ut{constructor(){super(...arguments);this.fields=ge.String("-f,--fields",{description:"A comma-separated list of manifest fields that should be displayed"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.packages=ge.Rest()}static{this.paths=[["npm","info"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"show information about a package",details:"\n This command fetches information about a package from the npm registry and prints it in a tree format.\n\n The package does not have to be installed locally, but needs to have been published (in particular, local changes will be ignored even for workspaces).\n\n Append `@` to the package argument to provide information specific to the latest version that satisfies the range or to the corresponding tagged version. If the range is invalid or if there is no version satisfying the range, the command will print a warning and fall back to the latest version.\n\n If the `-f,--fields` option is set, it's a comma-separated list of fields which will be used to only display part of the package information.\n\n By default, this command won't return the `dist`, `readme`, and `users` fields, since they are often very long. To explicitly request those fields, explicitly list them with the `--fields` flag or request the output in JSON mode.\n ",examples:[["Show all available information about react (except the `dist`, `readme`, and `users` fields)","yarn npm info react"],["Show all available information about react as valid JSON (including the `dist`, `readme`, and `users` fields)","yarn npm info react --json"],["Show all available information about react@16.12.0","yarn npm info react@16.12.0"],["Show all available information about react@next","yarn npm info react@next"],["Show the description of react","yarn npm info react --fields description"],["Show all available versions of react","yarn npm info react --fields versions"],["Show the readme of react","yarn npm info react --fields readme"],["Show a few fields of react","yarn npm info react --fields homepage,repository"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd),a=typeof this.fields<"u"?new Set(["name",...this.fields.split(/\s*,\s*/)]):null,n=[],c=!1,f=await Ot.start({configuration:r,includeFooter:!1,json:this.json,stdout:this.context.stdout},async p=>{for(let h of this.packages){let E;if(h==="."){let ie=s.topLevelWorkspace;if(!ie.manifest.name)throw new nt(`Missing ${he.pretty(r,"name",he.Type.CODE)} field in ${ue.fromPortablePath(K.join(ie.cwd,Er.manifest))}`);E=q.makeDescriptor(ie.manifest.name,"unknown")}else E=q.parseDescriptor(h);let C=an.getIdentUrl(E),S=Dz(await an.get(C,{configuration:r,ident:E,jsonResponse:!0,customErrorMessage:an.customPackageError})),P=Object.keys(S.versions).sort(vz.default.compareLoose),R=S["dist-tags"].latest||P[P.length-1],N=Or.validRange(E.range);if(N){let ie=vz.default.maxSatisfying(P,N);ie!==null?R=ie:(p.reportWarning(0,`Unmet range ${q.prettyRange(r,E.range)}; falling back to the latest version`),c=!0)}else Object.hasOwn(S["dist-tags"],E.range)?R=S["dist-tags"][E.range]:E.range!=="unknown"&&(p.reportWarning(0,`Unknown tag ${q.prettyRange(r,E.range)}; falling back to the latest version`),c=!0);let U=S.versions[R],W={...S,...U,version:R,versions:P},te;if(a!==null){te={};for(let ie of a){let Ae=W[ie];if(typeof Ae<"u")te[ie]=Ae;else{p.reportWarning(1,`The ${he.pretty(r,ie,he.Type.CODE)} field doesn't exist inside ${q.prettyIdent(r,E)}'s information`),c=!0;continue}}}else this.json||(delete W.dist,delete W.readme,delete W.users),te=W;p.reportJson(te),this.json||n.push(te)}});Sz.inspect.styles.name="cyan";for(let p of n)(p!==n[0]||c)&&this.context.stdout.write(` +`),this.context.stdout.write(`${(0,Sz.inspect)(p,{depth:1/0,colors:!0,compact:!1})} +`);return f.exitCode()}};function Dz(t){if(Array.isArray(t)){let e=[];for(let r of t)r=Dz(r),r&&e.push(r);return e}else if(typeof t=="object"&&t!==null){let e={};for(let r of Object.keys(t)){if(r.startsWith("_"))continue;let s=Dz(t[r]);s&&(e[r]=s)}return e}else return t||null}Ve();Ve();Wt();var BOe=et(lS()),k1=class extends ut{constructor(){super(...arguments);this.scope=ge.String("-s,--scope",{description:"Login to the registry configured for a given scope"});this.publish=ge.Boolean("--publish",!1,{description:"Login to the publish registry"});this.alwaysAuth=ge.Boolean("--always-auth",{description:"Set the npmAlwaysAuth configuration"})}static{this.paths=[["npm","login"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"store new login info to access the npm registry",details:"\n This command will ask you for your username, password, and 2FA One-Time-Password (when it applies). It will then modify your local configuration (in your home folder, never in the project itself) to reference the new tokens thus generated.\n\n Adding the `-s,--scope` flag will cause the authentication to be done against whatever registry is configured for the associated scope (see also `npmScopes`).\n\n Adding the `--publish` flag will cause the authentication to be done against the registry used when publishing the package (see also `publishConfig.registry` and `npmPublishRegistry`).\n ",examples:[["Login to the default registry","yarn npm login"],["Login to the registry linked to the @my-scope registry","yarn npm login --scope my-scope"],["Login to the publish registry for the current package","yarn npm login --publish"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=await _L({configuration:r,cwd:this.context.cwd,publish:this.publish,scope:this.scope});return(await Ot.start({configuration:r,stdout:this.context.stdout,includeFooter:!1},async n=>{let c=await Gjt({configuration:r,registry:s,report:n,stdin:this.context.stdin,stdout:this.context.stdout}),f=await jjt(s,c,r);return await qjt(s,f,{alwaysAuth:this.alwaysAuth,scope:this.scope}),n.reportInfo(0,"Successfully logged in")})).exitCode()}};async function _L({scope:t,publish:e,configuration:r,cwd:s}){return t&&e?pi.getScopeRegistry(t,{configuration:r,type:pi.RegistryType.PUBLISH_REGISTRY}):t?pi.getScopeRegistry(t,{configuration:r}):e?pi.getPublishRegistry((await rC(r,s)).manifest,{configuration:r}):pi.getDefaultRegistry({configuration:r})}async function jjt(t,e,r){let s=`/-/user/org.couchdb.user:${encodeURIComponent(e.name)}`,a={_id:`org.couchdb.user:${e.name}`,name:e.name,password:e.password,type:"user",roles:[],date:new Date().toISOString()},n={attemptedAs:e.name,configuration:r,registry:t,jsonResponse:!0,authType:an.AuthType.NO_AUTH};try{return(await an.put(s,a,n)).token}catch(E){if(!(E.originalError?.name==="HTTPError"&&E.originalError?.response.statusCode===409))throw E}let c={...n,authType:an.AuthType.NO_AUTH,headers:{authorization:`Basic ${Buffer.from(`${e.name}:${e.password}`).toString("base64")}`}},f=await an.get(s,c);for(let[E,C]of Object.entries(f))(!a[E]||E==="roles")&&(a[E]=C);let p=`${s}/-rev/${a._rev}`;return(await an.put(p,a,c)).token}async function qjt(t,e,{alwaysAuth:r,scope:s}){let a=c=>f=>{let p=je.isIndexableObject(f)?f:{},h=p[c],E=je.isIndexableObject(h)?h:{};return{...p,[c]:{...E,...r!==void 0?{npmAlwaysAuth:r}:{},npmAuthToken:e}}},n=s?{npmScopes:a(s)}:{npmRegistries:a(t)};return await ze.updateHomeConfiguration(n)}async function Gjt({configuration:t,registry:e,report:r,stdin:s,stdout:a}){r.reportInfo(0,`Logging in to ${he.pretty(t,e,he.Type.URL)}`);let n=!1;if(e.match(/^https:\/\/npm\.pkg\.github\.com(\/|$)/)&&(r.reportInfo(0,"You seem to be using the GitHub Package Registry. Tokens must be generated with the 'repo', 'write:packages', and 'read:packages' permissions."),n=!0),r.reportSeparator(),t.env.YARN_IS_TEST_ENV)return{name:t.env.YARN_INJECT_NPM_USER||"",password:t.env.YARN_INJECT_NPM_PASSWORD||""};let c=await(0,BOe.prompt)([{type:"input",name:"name",message:"Username:",required:!0,onCancel:()=>process.exit(130),stdin:s,stdout:a},{type:"password",name:"password",message:n?"Token:":"Password:",required:!0,onCancel:()=>process.exit(130),stdin:s,stdout:a}]);return r.reportSeparator(),c}Ve();Ve();Wt();var Q1=new Set(["npmAuthIdent","npmAuthToken"]),T1=class extends ut{constructor(){super(...arguments);this.scope=ge.String("-s,--scope",{description:"Logout of the registry configured for a given scope"});this.publish=ge.Boolean("--publish",!1,{description:"Logout of the publish registry"});this.all=ge.Boolean("-A,--all",!1,{description:"Logout of all registries"})}static{this.paths=[["npm","logout"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"logout of the npm registry",details:"\n This command will log you out by modifying your local configuration (in your home folder, never in the project itself) to delete all credentials linked to a registry.\n\n Adding the `-s,--scope` flag will cause the deletion to be done against whatever registry is configured for the associated scope (see also `npmScopes`).\n\n Adding the `--publish` flag will cause the deletion to be done against the registry used when publishing the package (see also `publishConfig.registry` and `npmPublishRegistry`).\n\n Adding the `-A,--all` flag will cause the deletion to be done against all registries and scopes.\n ",examples:[["Logout of the default registry","yarn npm logout"],["Logout of the @my-scope scope","yarn npm logout --scope my-scope"],["Logout of the publish registry for the current package","yarn npm logout --publish"],["Logout of all registries","yarn npm logout --all"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s=async()=>{let n=await _L({configuration:r,cwd:this.context.cwd,publish:this.publish,scope:this.scope}),c=await ze.find(this.context.cwd,this.context.plugins),f=q.makeIdent(this.scope??null,"pkg");return!pi.getAuthConfiguration(n,{configuration:c,ident:f}).get("npmAuthToken")};return(await Ot.start({configuration:r,stdout:this.context.stdout},async n=>{if(this.all&&(await Yjt(),n.reportInfo(0,"Successfully logged out from everything")),this.scope){await vOe("npmScopes",this.scope),await s()?n.reportInfo(0,`Successfully logged out from ${this.scope}`):n.reportWarning(0,"Scope authentication settings removed, but some other ones settings still apply to it");return}let c=await _L({configuration:r,cwd:this.context.cwd,publish:this.publish});await vOe("npmRegistries",c),await s()?n.reportInfo(0,`Successfully logged out from ${c}`):n.reportWarning(0,"Registry authentication settings removed, but some other ones settings still apply to it")})).exitCode()}};function Wjt(t,e){let r=t[e];if(!je.isIndexableObject(r))return!1;let s=new Set(Object.keys(r));if([...Q1].every(n=>!s.has(n)))return!1;for(let n of Q1)s.delete(n);if(s.size===0)return t[e]=void 0,!0;let a={...r};for(let n of Q1)delete a[n];return t[e]=a,!0}async function Yjt(){let t=e=>{let r=!1,s=je.isIndexableObject(e)?{...e}:{};s.npmAuthToken&&(delete s.npmAuthToken,r=!0);for(let a of Object.keys(s))Wjt(s,a)&&(r=!0);if(Object.keys(s).length!==0)return r?s:e};return await ze.updateHomeConfiguration({npmRegistries:t,npmScopes:t})}async function vOe(t,e){return await ze.updateHomeConfiguration({[t]:r=>{let s=je.isIndexableObject(r)?r:{};if(!Object.hasOwn(s,e))return r;let a=s[e],n=je.isIndexableObject(a)?a:{},c=new Set(Object.keys(n));if([...Q1].every(p=>!c.has(p)))return r;for(let p of Q1)c.delete(p);if(c.size===0)return Object.keys(s).length===1?void 0:{...s,[e]:void 0};let f={};for(let p of Q1)f[p]=void 0;return{...s,[e]:{...n,...f}}}})}Ve();Wt();var R1=class extends ut{constructor(){super(...arguments);this.access=ge.String("--access",{description:"The access for the published package (public or restricted)"});this.tag=ge.String("--tag","latest",{description:"The tag on the registry that the package should be attached to"});this.tolerateRepublish=ge.Boolean("--tolerate-republish",!1,{description:"Warn and exit when republishing an already existing version of a package"});this.otp=ge.String("--otp",{description:"The OTP token to use with the command"});this.provenance=ge.Boolean("--provenance",!1,{description:"Generate provenance for the package. Only available in GitHub Actions and GitLab CI. Can be set globally through the `npmPublishProvenance` setting or the `YARN_NPM_CONFIG_PROVENANCE` environment variable, or per-package through the `publishConfig.provenance` field in package.json."})}static{this.paths=[["npm","publish"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"publish the active workspace to the npm registry",details:'\n This command will pack the active workspace into a fresh archive and upload it to the npm registry.\n\n The package will by default be attached to the `latest` tag on the registry, but this behavior can be overridden by using the `--tag` option.\n\n Note that for legacy reasons scoped packages are by default published with an access set to `restricted` (aka "private packages"). This requires you to register for a paid npm plan. In case you simply wish to publish a public scoped package to the registry (for free), just add the `--access public` flag. This behavior can be enabled by default through the `npmPublishAccess` settings.\n ',examples:[["Publish the active workspace","yarn npm publish"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);if(a.manifest.private)throw new nt("Private workspaces cannot be published");if(a.manifest.name===null||a.manifest.version===null)throw new nt("Workspaces must have valid names and versions to be published on an external registry");await s.restoreInstallState();let n=a.manifest.name,c=a.manifest.version,f=pi.getPublishRegistry(a.manifest,{configuration:r});return(await Ot.start({configuration:r,stdout:this.context.stdout},async h=>{if(this.tolerateRepublish)try{let E=await an.get(an.getIdentUrl(n),{configuration:r,registry:f,ident:n,jsonResponse:!0});if(!Object.hasOwn(E,"versions"))throw new Yt(15,'Registry returned invalid data for - missing "versions" field');if(Object.hasOwn(E.versions,c)){h.reportWarning(0,`Registry already knows about version ${c}; skipping.`);return}}catch(E){if(E.originalError?.response?.statusCode!==404)throw E}await In.maybeExecuteWorkspaceLifecycleScript(a,"prepublish",{report:h}),await IA.prepareForPack(a,{report:h},async()=>{let E=await IA.genPackList(a);for(let N of E)h.reportInfo(null,N);let C=await IA.genPackStream(a,E),S=await je.bufferStream(C),P=await D1.getGitHead(a.cwd),I=!1;a.manifest.publishConfig&&"provenance"in a.manifest.publishConfig?(I=!!a.manifest.publishConfig.provenance,I?h.reportInfo(null,"Generating provenance statement because `publishConfig.provenance` field is set."):h.reportInfo(null,"Skipping provenance statement because `publishConfig.provenance` field is set to false.")):this.provenance?(I=!0,h.reportInfo(null,"Generating provenance statement because `--provenance` flag is set.")):r.get("npmPublishProvenance")&&(I=!0,h.reportInfo(null,"Generating provenance statement because `npmPublishProvenance` setting is set."));let R=await D1.makePublishBody(a,S,{access:this.access,tag:this.tag,registry:f,gitHead:P,provenance:I});await an.put(an.getIdentUrl(n),R,{configuration:r,registry:f,ident:n,otp:this.otp,jsonResponse:!0})}),h.reportInfo(0,"Package archive published")})).exitCode()}};Ve();Wt();var SOe=et(fi());Ve();bt();Wt();var F1=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.package=ge.String({required:!1})}static{this.paths=[["npm","tag","list"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"list all dist-tags of a package",details:` + This command will list all tags of a package from the npm registry. + + If the package is not specified, Yarn will default to the current workspace. + `,examples:[["List all tags of package `my-pkg`","yarn npm tag list my-pkg"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n;if(typeof this.package<"u")n=q.parseIdent(this.package);else{if(!a)throw new ar(s.cwd,this.context.cwd);if(!a.manifest.name)throw new nt(`Missing 'name' field in ${ue.fromPortablePath(K.join(a.cwd,Er.manifest))}`);n=a.manifest.name}let c=await AP(n,r),p={children:je.sortMap(Object.entries(c),([h])=>h).map(([h,E])=>({value:he.tuple(he.Type.RESOLUTION,{descriptor:q.makeDescriptor(n,h),locator:q.makeLocator(n,E)})}))};return ks.emitTree(p,{configuration:r,json:this.json,stdout:this.context.stdout})}};async function AP(t,e){let r=`/-/package${an.getIdentUrl(t)}/dist-tags`;return an.get(r,{configuration:e,ident:t,jsonResponse:!0,customErrorMessage:an.customPackageError})}var N1=class extends ut{constructor(){super(...arguments);this.package=ge.String();this.tag=ge.String()}static{this.paths=[["npm","tag","add"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"add a tag for a specific version of a package",details:` + This command will add a tag to the npm registry for a specific version of a package. If the tag already exists, it will be overwritten. + `,examples:[["Add a `beta` tag for version `2.3.4-beta.4` of package `my-pkg`","yarn npm tag add my-pkg@2.3.4-beta.4 beta"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);let n=q.parseDescriptor(this.package,!0),c=n.range;if(!SOe.default.valid(c))throw new nt(`The range ${he.pretty(r,n.range,he.Type.RANGE)} must be a valid semver version`);let f=pi.getPublishRegistry(a.manifest,{configuration:r}),p=he.pretty(r,n,he.Type.IDENT),h=he.pretty(r,c,he.Type.RANGE),E=he.pretty(r,this.tag,he.Type.CODE);return(await Ot.start({configuration:r,stdout:this.context.stdout},async S=>{let P=await AP(n,r);Object.hasOwn(P,this.tag)&&P[this.tag]===c&&S.reportWarning(0,`Tag ${E} is already set to version ${h}`);let I=`/-/package${an.getIdentUrl(n)}/dist-tags/${encodeURIComponent(this.tag)}`;await an.put(I,c,{configuration:r,registry:f,ident:n,jsonRequest:!0,jsonResponse:!0}),S.reportInfo(0,`Tag ${E} added to version ${h} of package ${p}`)})).exitCode()}};Ve();Wt();var O1=class extends ut{constructor(){super(...arguments);this.package=ge.String();this.tag=ge.String()}static{this.paths=[["npm","tag","remove"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"remove a tag from a package",details:` + This command will remove a tag from a package from the npm registry. + `,examples:[["Remove the `beta` tag from package `my-pkg`","yarn npm tag remove my-pkg beta"]]})}async execute(){if(this.tag==="latest")throw new nt("The 'latest' tag cannot be removed.");let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);let n=q.parseIdent(this.package),c=pi.getPublishRegistry(a.manifest,{configuration:r}),f=he.pretty(r,this.tag,he.Type.CODE),p=he.pretty(r,n,he.Type.IDENT),h=await AP(n,r);if(!Object.hasOwn(h,this.tag))throw new nt(`${f} is not a tag of package ${p}`);return(await Ot.start({configuration:r,stdout:this.context.stdout},async C=>{let S=`/-/package${an.getIdentUrl(n)}/dist-tags/${encodeURIComponent(this.tag)}`;await an.del(S,{configuration:r,registry:c,ident:n,jsonResponse:!0}),C.reportInfo(0,`Tag ${f} removed from package ${p}`)})).exitCode()}};Ve();Ve();Wt();var L1=class extends ut{constructor(){super(...arguments);this.scope=ge.String("-s,--scope",{description:"Print username for the registry configured for a given scope"});this.publish=ge.Boolean("--publish",!1,{description:"Print username for the publish registry"})}static{this.paths=[["npm","whoami"]]}static{this.usage=ot.Usage({category:"Npm-related commands",description:"display the name of the authenticated user",details:"\n Print the username associated with the current authentication settings to the standard output.\n\n When using `-s,--scope`, the username printed will be the one that matches the authentication settings of the registry associated with the given scope (those settings can be overriden using the `npmRegistries` map, and the registry associated with the scope is configured via the `npmScopes` map).\n\n When using `--publish`, the registry we'll select will by default be the one used when publishing packages (`publishConfig.registry` or `npmPublishRegistry` if available, otherwise we'll fallback to the regular `npmRegistryServer`).\n ",examples:[["Print username for the default registry","yarn npm whoami"],["Print username for the registry on a given scope","yarn npm whoami --scope company"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),s;return this.scope&&this.publish?s=pi.getScopeRegistry(this.scope,{configuration:r,type:pi.RegistryType.PUBLISH_REGISTRY}):this.scope?s=pi.getScopeRegistry(this.scope,{configuration:r}):this.publish?s=pi.getPublishRegistry((await rC(r,this.context.cwd)).manifest,{configuration:r}):s=pi.getDefaultRegistry({configuration:r}),(await Ot.start({configuration:r,stdout:this.context.stdout},async n=>{let c;try{c=await an.get("/-/whoami",{configuration:r,registry:s,authType:an.AuthType.ALWAYS_AUTH,jsonResponse:!0,ident:this.scope?q.makeIdent(this.scope,""):void 0})}catch(f){if(f.response?.statusCode===401||f.response?.statusCode===403){n.reportError(41,"Authentication failed - your credentials may have expired");return}else throw f}n.reportInfo(0,c.username)})).exitCode()}};var Vjt={configuration:{npmPublishAccess:{description:"Default access of the published packages",type:"STRING",default:null},npmPublishProvenance:{description:"Whether to generate provenance for the published packages",type:"BOOLEAN",default:!1},npmAuditExcludePackages:{description:"Array of glob patterns of packages to exclude from npm audit",type:"STRING",default:[],isArray:!0},npmAuditIgnoreAdvisories:{description:"Array of glob patterns of advisory IDs to exclude from npm audit",type:"STRING",default:[],isArray:!0}},commands:[P1,x1,k1,T1,R1,N1,F1,O1,L1]},Kjt=Vjt;var Fz={};Vt(Fz,{PatchCommand:()=>q1,PatchCommitCommand:()=>j1,PatchFetcher:()=>mP,PatchResolver:()=>yP,default:()=>A6t,patchUtils:()=>dy});Ve();Ve();bt();rA();var dy={};Vt(dy,{applyPatchFile:()=>HL,diffFolders:()=>Tz,ensureUnpatchedDescriptor:()=>Pz,ensureUnpatchedLocator:()=>qL,extractPackageToDisk:()=>Qz,extractPatchFlags:()=>TOe,isParentRequired:()=>kz,isPatchDescriptor:()=>jL,isPatchLocator:()=>Fg,loadPatchFiles:()=>dP,makeDescriptor:()=>WL,makeLocator:()=>xz,makePatchHash:()=>Rz,parseDescriptor:()=>hP,parseLocator:()=>gP,parsePatchFile:()=>pP,unpatchDescriptor:()=>c6t,unpatchLocator:()=>u6t});Ve();bt();Ve();bt();var Jjt=/^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@.*/;function M1(t){return K.relative(vt.root,K.resolve(vt.root,ue.toPortablePath(t)))}function zjt(t){let e=t.trim().match(Jjt);if(!e)throw new Error(`Bad header line: '${t}'`);return{original:{start:Math.max(Number(e[1]),1),length:Number(e[3]||1)},patched:{start:Math.max(Number(e[4]),1),length:Number(e[6]||1)}}}var Zjt=420,Xjt=493;var DOe=()=>({semverExclusivity:null,diffLineFromPath:null,diffLineToPath:null,oldMode:null,newMode:null,deletedFileMode:null,newFileMode:null,renameFrom:null,renameTo:null,beforeHash:null,afterHash:null,fromPath:null,toPath:null,hunks:null}),$jt=t=>({header:zjt(t),parts:[]}),e6t={"@":"header","-":"deletion","+":"insertion"," ":"context","\\":"pragma",undefined:"context"};function t6t(t){let e=[],r=DOe(),s="parsing header",a=null,n=null;function c(){a&&(n&&(a.parts.push(n),n=null),r.hunks.push(a),a=null)}function f(){c(),e.push(r),r=DOe()}for(let p=0;p0?"patch":"mode change",W=null;switch(U){case"rename":{if(!E||!C)throw new Error("Bad parser state: rename from & to not given");e.push({type:"rename",semverExclusivity:s,fromPath:M1(E),toPath:M1(C)}),W=C}break;case"file deletion":{let te=a||I;if(!te)throw new Error("Bad parse state: no path given for file deletion");e.push({type:"file deletion",semverExclusivity:s,hunk:N&&N[0]||null,path:M1(te),mode:UL(p),hash:S})}break;case"file creation":{let te=n||R;if(!te)throw new Error("Bad parse state: no path given for file creation");e.push({type:"file creation",semverExclusivity:s,hunk:N&&N[0]||null,path:M1(te),mode:UL(h),hash:P})}break;case"patch":case"mode change":W=R||n;break;default:je.assertNever(U);break}W&&c&&f&&c!==f&&e.push({type:"mode change",semverExclusivity:s,path:M1(W),oldMode:UL(c),newMode:UL(f)}),W&&N&&N.length&&e.push({type:"patch",semverExclusivity:s,path:M1(W),hunks:N,beforeHash:S,afterHash:P})}if(e.length===0)throw new Error("Unable to parse patch file: No changes found. Make sure the patch is a valid UTF8 encoded string");return e}function UL(t){let e=parseInt(t,8)&511;if(e!==Zjt&&e!==Xjt)throw new Error(`Unexpected file mode string: ${t}`);return e}function pP(t){let e=t.split(/\n/g);return e[e.length-1]===""&&e.pop(),r6t(t6t(e))}function n6t(t){let e=0,r=0;for(let{type:s,lines:a}of t.parts)switch(s){case"context":r+=a.length,e+=a.length;break;case"deletion":e+=a.length;break;case"insertion":r+=a.length;break;default:je.assertNever(s);break}if(e!==t.header.original.length||r!==t.header.patched.length){let s=a=>a<0?a:`+${a}`;throw new Error(`hunk header integrity check failed (expected @@ ${s(t.header.original.length)} ${s(t.header.patched.length)} @@, got @@ ${s(e)} ${s(r)} @@)`)}}Ve();bt();var _1=class extends Error{constructor(r,s){super(`Cannot apply hunk #${r+1}`);this.hunk=s}};async function U1(t,e,r){let s=await t.lstatPromise(e),a=await r();typeof a<"u"&&(e=a),await t.lutimesPromise(e,s.atime,s.mtime)}async function HL(t,{baseFs:e=new Yn,dryRun:r=!1,version:s=null}={}){for(let a of t)if(!(a.semverExclusivity!==null&&s!==null&&!Or.satisfiesWithPrereleases(s,a.semverExclusivity)))switch(a.type){case"file deletion":if(r){if(!e.existsSync(a.path))throw new Error(`Trying to delete a file that doesn't exist: ${a.path}`)}else await U1(e,K.dirname(a.path),async()=>{await e.unlinkPromise(a.path)});break;case"rename":if(r){if(!e.existsSync(a.fromPath))throw new Error(`Trying to move a file that doesn't exist: ${a.fromPath}`)}else await U1(e,K.dirname(a.fromPath),async()=>{await U1(e,K.dirname(a.toPath),async()=>{await U1(e,a.fromPath,async()=>(await e.movePromise(a.fromPath,a.toPath),a.toPath))})});break;case"file creation":if(r){if(e.existsSync(a.path))throw new Error(`Trying to create a file that already exists: ${a.path}`)}else{let n=a.hunk?a.hunk.parts[0].lines.join(` +`)+(a.hunk.parts[0].noNewlineAtEndOfFile?"":` +`):"";await e.mkdirpPromise(K.dirname(a.path),{chmod:493,utimes:[ui.SAFE_TIME,ui.SAFE_TIME]}),await e.writeFilePromise(a.path,n,{mode:a.mode}),await e.utimesPromise(a.path,ui.SAFE_TIME,ui.SAFE_TIME)}break;case"patch":await U1(e,a.path,async()=>{await o6t(a,{baseFs:e,dryRun:r})});break;case"mode change":{let c=(await e.statPromise(a.path)).mode;if(bOe(a.newMode)!==bOe(c))continue;await U1(e,a.path,async()=>{await e.chmodPromise(a.path,a.newMode)})}break;default:je.assertNever(a);break}}function bOe(t){return(t&64)>0}function POe(t){return t.replace(/\s+$/,"")}function s6t(t,e){return POe(t)===POe(e)}async function o6t({hunks:t,path:e},{baseFs:r,dryRun:s=!1}){let a=await r.statSync(e).mode,c=(await r.readFileSync(e,"utf8")).split(/\n/),f=[],p=0,h=0;for(let C of t){let S=Math.max(h,C.header.patched.start+p),P=Math.max(0,S-h),I=Math.max(0,c.length-S-C.header.original.length),R=Math.max(P,I),N=0,U=0,W=null;for(;N<=R;){if(N<=P&&(U=S-N,W=xOe(C,c,U),W!==null)){N=-N;break}if(N<=I&&(U=S+N,W=xOe(C,c,U),W!==null))break;N+=1}if(W===null)throw new _1(t.indexOf(C),C);f.push(W),p+=N,h=U+C.header.original.length}if(s)return;let E=0;for(let C of f)for(let S of C)switch(S.type){case"splice":{let P=S.index+E;c.splice(P,S.numToDelete,...S.linesToInsert),E+=S.linesToInsert.length-S.numToDelete}break;case"pop":c.pop();break;case"push":c.push(S.line);break;default:je.assertNever(S);break}await r.writeFilePromise(e,c.join(` +`),{mode:a})}function xOe(t,e,r){let s=[];for(let a of t.parts)switch(a.type){case"context":case"deletion":{for(let n of a.lines){let c=e[r];if(c==null||!s6t(c,n))return null;r+=1}a.type==="deletion"&&(s.push({type:"splice",index:r-a.lines.length,numToDelete:a.lines.length,linesToInsert:[]}),a.noNewlineAtEndOfFile&&s.push({type:"push",line:""}))}break;case"insertion":s.push({type:"splice",index:r,numToDelete:0,linesToInsert:a.lines}),a.noNewlineAtEndOfFile&&s.push({type:"pop"});break;default:je.assertNever(a.type);break}return s}var l6t=/^builtin<([^>]+)>$/;function H1(t,e){let{protocol:r,source:s,selector:a,params:n}=q.parseRange(t);if(r!=="patch:")throw new Error("Invalid patch range");if(s===null)throw new Error("Patch locators must explicitly define their source");let c=a?a.split(/&/).map(E=>ue.toPortablePath(E)):[],f=n&&typeof n.locator=="string"?q.parseLocator(n.locator):null,p=n&&typeof n.version=="string"?n.version:null,h=e(s);return{parentLocator:f,sourceItem:h,patchPaths:c,sourceVersion:p}}function jL(t){return t.range.startsWith("patch:")}function Fg(t){return t.reference.startsWith("patch:")}function hP(t){let{sourceItem:e,...r}=H1(t.range,q.parseDescriptor);return{...r,sourceDescriptor:e}}function gP(t){let{sourceItem:e,...r}=H1(t.reference,q.parseLocator);return{...r,sourceLocator:e}}function c6t(t){let{sourceItem:e}=H1(t.range,q.parseDescriptor);return e}function u6t(t){let{sourceItem:e}=H1(t.reference,q.parseLocator);return e}function Pz(t){if(!jL(t))return t;let{sourceItem:e}=H1(t.range,q.parseDescriptor);return e}function qL(t){if(!Fg(t))return t;let{sourceItem:e}=H1(t.reference,q.parseLocator);return e}function kOe({parentLocator:t,sourceItem:e,patchPaths:r,sourceVersion:s,patchHash:a},n){let c=t!==null?{locator:q.stringifyLocator(t)}:{},f=typeof s<"u"?{version:s}:{},p=typeof a<"u"?{hash:a}:{};return q.makeRange({protocol:"patch:",source:n(e),selector:r.join("&"),params:{...f,...p,...c}})}function WL(t,{parentLocator:e,sourceDescriptor:r,patchPaths:s}){return q.makeDescriptor(t,kOe({parentLocator:e,sourceItem:r,patchPaths:s},q.stringifyDescriptor))}function xz(t,{parentLocator:e,sourcePackage:r,patchPaths:s,patchHash:a}){return q.makeLocator(t,kOe({parentLocator:e,sourceItem:r,sourceVersion:r.version,patchPaths:s,patchHash:a},q.stringifyLocator))}function QOe({onAbsolute:t,onRelative:e,onProject:r,onBuiltin:s},a){let n=a.lastIndexOf("!");n!==-1&&(a=a.slice(n+1));let c=a.match(l6t);return c!==null?s(c[1]):a.startsWith("~/")?r(a.slice(2)):K.isAbsolute(a)?t(a):e(a)}function TOe(t){let e=t.lastIndexOf("!");return{optional:(e!==-1?new Set(t.slice(0,e).split(/!/)):new Set).has("optional")}}function kz(t){return QOe({onAbsolute:()=>!1,onRelative:()=>!0,onProject:()=>!1,onBuiltin:()=>!1},t)}async function dP(t,e,r){let s=t!==null?await r.fetcher.fetch(t,r):null,a=s&&s.localPath?{packageFs:new Sn(vt.root),prefixPath:K.relative(vt.root,s.localPath)}:s;s&&s!==a&&s.releaseFs&&s.releaseFs();let n=await je.releaseAfterUseAsync(async()=>await Promise.all(e.map(async c=>{let f=TOe(c),p=await QOe({onAbsolute:async h=>await le.readFilePromise(h,"utf8"),onRelative:async h=>{if(a===null)throw new Error("Assertion failed: The parent locator should have been fetched");return await a.packageFs.readFilePromise(K.join(a.prefixPath,h),"utf8")},onProject:async h=>await le.readFilePromise(K.join(r.project.cwd,h),"utf8"),onBuiltin:async h=>await r.project.configuration.firstHook(E=>E.getBuiltinPatch,r.project,h)},c);return{...f,source:p}})));for(let c of n)typeof c.source=="string"&&(c.source=c.source.replace(/\r\n?/g,` +`));return n}async function Qz(t,{cache:e,project:r}){let s=r.storedPackages.get(t.locatorHash);if(typeof s>"u")throw new Error("Assertion failed: Expected the package to be registered");let a=qL(t),n=r.storedChecksums,c=new Wi,f=await le.mktempPromise(),p=K.join(f,"source"),h=K.join(f,"user"),E=K.join(f,".yarn-patch.json"),C=r.configuration.makeFetcher(),S=[];try{let P,I;if(t.locatorHash===a.locatorHash){let R=await C.fetch(t,{cache:e,project:r,fetcher:C,checksums:n,report:c});S.push(()=>R.releaseFs?.()),P=R,I=R}else P=await C.fetch(t,{cache:e,project:r,fetcher:C,checksums:n,report:c}),S.push(()=>P.releaseFs?.()),I=await C.fetch(t,{cache:e,project:r,fetcher:C,checksums:n,report:c}),S.push(()=>I.releaseFs?.());await Promise.all([le.copyPromise(p,P.prefixPath,{baseFs:P.packageFs}),le.copyPromise(h,I.prefixPath,{baseFs:I.packageFs}),le.writeJsonPromise(E,{locator:q.stringifyLocator(t),version:s.version})])}finally{for(let P of S)P()}return le.detachTemp(f),h}async function Tz(t,e){let r=ue.fromPortablePath(t).replace(/\\/g,"/"),s=ue.fromPortablePath(e).replace(/\\/g,"/"),{stdout:a,stderr:n}=await Gr.execvp("git",["-c","core.safecrlf=false","diff","--src-prefix=a/","--dst-prefix=b/","--ignore-cr-at-eol","--full-index","--no-index","--no-renames","--text",r,s],{cwd:ue.toPortablePath(process.cwd()),env:{...process.env,GIT_CONFIG_NOSYSTEM:"1",HOME:"",XDG_CONFIG_HOME:"",USERPROFILE:""}});if(n.length>0)throw new Error(`Unable to diff directories. Make sure you have a recent version of 'git' available in PATH. +The following error was reported by 'git': +${n}`);let c=r.startsWith("/")?f=>f.slice(1):f=>f;return a.replace(new RegExp(`(a|b)(${je.escapeRegExp(`/${c(r)}/`)})`,"g"),"$1/").replace(new RegExp(`(a|b)${je.escapeRegExp(`/${c(s)}/`)}`,"g"),"$1/").replace(new RegExp(je.escapeRegExp(`${r}/`),"g"),"").replace(new RegExp(je.escapeRegExp(`${s}/`),"g"),"")}function Rz(t,e){let r=[];for(let{source:s}of t){if(s===null)continue;let a=pP(s);for(let n of a){let{semverExclusivity:c,...f}=n;c!==null&&e!==null&&!Or.satisfiesWithPrereleases(e,c)||r.push(JSON.stringify(f))}}return Nn.makeHash(`${3}`,...r).slice(0,6)}Ve();function ROe(t,{configuration:e,report:r}){for(let s of t.parts)for(let a of s.lines)switch(s.type){case"context":r.reportInfo(null,` ${he.pretty(e,a,"grey")}`);break;case"deletion":r.reportError(28,`- ${he.pretty(e,a,he.Type.REMOVED)}`);break;case"insertion":r.reportError(28,`+ ${he.pretty(e,a,he.Type.ADDED)}`);break;default:je.assertNever(s.type)}}var mP=class{supports(e,r){return!!Fg(e)}getLocalPath(e,r){return null}async fetch(e,r){let s=r.checksums.get(e.locatorHash)||null,[a,n,c]=await r.cache.fetchPackageFromCache(e,s,{onHit:()=>r.report.reportCacheHit(e),onMiss:()=>r.report.reportCacheMiss(e,`${q.prettyLocator(r.project.configuration,e)} can't be found in the cache and will be fetched from the disk`),loader:()=>this.patchPackage(e,r),...r.cacheOptions});return{packageFs:a,releaseFs:n,prefixPath:q.getIdentVendorPath(e),localPath:this.getLocalPath(e,r),checksum:c}}async patchPackage(e,r){let{parentLocator:s,sourceLocator:a,sourceVersion:n,patchPaths:c}=gP(e),f=await dP(s,c,r),p=await le.mktempPromise(),h=K.join(p,"current.zip"),E=await r.fetcher.fetch(a,r),C=q.getIdentVendorPath(e),S=new ps(h,{create:!0,level:r.project.configuration.get("compressionLevel")});await je.releaseAfterUseAsync(async()=>{await S.copyPromise(C,E.prefixPath,{baseFs:E.packageFs,stableSort:!0})},E.releaseFs),S.saveAndClose();for(let{source:P,optional:I}of f){if(P===null)continue;let R=new ps(h,{level:r.project.configuration.get("compressionLevel")}),N=new Sn(K.resolve(vt.root,C),{baseFs:R});try{await HL(pP(P),{baseFs:N,version:n})}catch(U){if(!(U instanceof _1))throw U;let W=r.project.configuration.get("enableInlineHunks"),te=!W&&!I?" (set enableInlineHunks for details)":"",ie=`${q.prettyLocator(r.project.configuration,e)}: ${U.message}${te}`,Ae=ce=>{W&&ROe(U.hunk,{configuration:r.project.configuration,report:ce})};if(R.discardAndClose(),I){r.report.reportWarningOnce(66,ie,{reportExtra:Ae});continue}else throw new Yt(66,ie,Ae)}R.saveAndClose()}return new ps(h,{level:r.project.configuration.get("compressionLevel")})}};Ve();var yP=class{supportsDescriptor(e,r){return!!jL(e)}supportsLocator(e,r){return!!Fg(e)}shouldPersistResolution(e,r){return!1}bindDescriptor(e,r,s){let{patchPaths:a}=hP(e);return a.every(n=>!kz(n))?e:q.bindDescriptor(e,{locator:q.stringifyLocator(r)})}getResolutionDependencies(e,r){let{sourceDescriptor:s}=hP(e);return{sourceDescriptor:r.project.configuration.normalizeDependency(s)}}async getCandidates(e,r,s){if(!s.fetchOptions)throw new Error("Assertion failed: This resolver cannot be used unless a fetcher is configured");let{parentLocator:a,patchPaths:n}=hP(e),c=await dP(a,n,s.fetchOptions),f=r.sourceDescriptor;if(typeof f>"u")throw new Error("Assertion failed: The dependency should have been resolved");let p=Rz(c,f.version);return[xz(e,{parentLocator:a,sourcePackage:f,patchPaths:n,patchHash:p})]}async getSatisfying(e,r,s,a){let[n]=await this.getCandidates(e,r,a);return{locators:s.filter(c=>c.locatorHash===n.locatorHash),sorted:!1}}async resolve(e,r){let{sourceLocator:s}=gP(e);return{...await r.resolver.resolve(s,r),...e}}};Ve();bt();Wt();var j1=class extends ut{constructor(){super(...arguments);this.save=ge.Boolean("-s,--save",!1,{description:"Add the patch to your resolution entries"});this.patchFolder=ge.String()}static{this.paths=[["patch-commit"]]}static{this.usage=ot.Usage({description:"generate a patch out of a directory",details:"\n By default, this will print a patchfile on stdout based on the diff between the folder passed in and the original version of the package. Such file is suitable for consumption with the `patch:` protocol.\n\n With the `-s,--save` option set, the patchfile won't be printed on stdout anymore and will instead be stored within a local file (by default kept within `.yarn/patches`, but configurable via the `patchFolder` setting). A `resolutions` entry will also be added to your top-level manifest, referencing the patched package via the `patch:` protocol.\n\n Note that only folders generated by `yarn patch` are accepted as valid input for `yarn patch-commit`.\n "})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState();let n=K.resolve(this.context.cwd,ue.toPortablePath(this.patchFolder)),c=K.join(n,"../source"),f=K.join(n,"../.yarn-patch.json");if(!le.existsSync(c))throw new nt("The argument folder didn't get created by 'yarn patch'");let p=await Tz(c,n),h=await le.readJsonPromise(f),E=q.parseLocator(h.locator,!0);if(!s.storedPackages.has(E.locatorHash))throw new nt("No package found in the project for the given locator");if(!this.save){this.context.stdout.write(p);return}let C=r.get("patchFolder"),S=K.join(C,`${q.slugifyLocator(E)}.patch`);await le.mkdirPromise(C,{recursive:!0}),await le.writeFilePromise(S,p);let P=[],I=new Map;for(let R of s.storedPackages.values()){if(q.isVirtualLocator(R))continue;let N=R.dependencies.get(E.identHash);if(!N)continue;let U=q.ensureDevirtualizedDescriptor(N),W=Pz(U),te=s.storedResolutions.get(W.descriptorHash);if(!te)throw new Error("Assertion failed: Expected the resolution to have been registered");if(!s.storedPackages.get(te))throw new Error("Assertion failed: Expected the package to have been registered");let Ae=s.tryWorkspaceByLocator(R);if(Ae)P.push(Ae);else{let ce=s.originalPackages.get(R.locatorHash);if(!ce)throw new Error("Assertion failed: Expected the original package to have been registered");let me=ce.dependencies.get(N.identHash);if(!me)throw new Error("Assertion failed: Expected the original dependency to have been registered");I.set(me.descriptorHash,me)}}for(let R of P)for(let N of Ht.hardDependencies){let U=R.manifest[N].get(E.identHash);if(!U)continue;let W=WL(U,{parentLocator:null,sourceDescriptor:q.convertLocatorToDescriptor(E),patchPaths:[K.join(Er.home,K.relative(s.cwd,S))]});R.manifest[N].set(U.identHash,W)}for(let R of I.values()){let N=WL(R,{parentLocator:null,sourceDescriptor:q.convertLocatorToDescriptor(E),patchPaths:[K.join(Er.home,K.relative(s.cwd,S))]});s.topLevelWorkspace.manifest.resolutions.push({pattern:{descriptor:{fullName:q.stringifyIdent(N),description:R.range}},reference:N.range})}await s.persist()}};Ve();bt();Wt();var q1=class extends ut{constructor(){super(...arguments);this.update=ge.Boolean("-u,--update",!1,{description:"Reapply local patches that already apply to this packages"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.package=ge.String()}static{this.paths=[["patch"]]}static{this.usage=ot.Usage({description:"prepare a package for patching",details:"\n This command will cause a package to be extracted in a temporary directory intended to be editable at will.\n\n Once you're done with your changes, run `yarn patch-commit -s path` (with `path` being the temporary directory you received) to generate a patchfile and register it into your top-level manifest via the `patch:` protocol. Run `yarn patch-commit -h` for more details.\n\n Calling the command when you already have a patch won't import it by default (in other words, the default behavior is to reset existing patches). However, adding the `-u,--update` flag will import any current patch.\n "})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState();let c=q.parseLocator(this.package);if(c.reference==="unknown"){let f=je.mapAndFilter([...s.storedPackages.values()],p=>p.identHash!==c.identHash?je.mapAndFilter.skip:q.isVirtualLocator(p)?je.mapAndFilter.skip:Fg(p)!==this.update?je.mapAndFilter.skip:p);if(f.length===0)throw new nt("No package found in the project for the given locator");if(f.length>1)throw new nt(`Multiple candidate packages found; explicitly choose one of them (use \`yarn why \` to get more information as to who depends on them): +${f.map(p=>` +- ${q.prettyLocator(r,p)}`).join("")}`);c=f[0]}if(!s.storedPackages.has(c.locatorHash))throw new nt("No package found in the project for the given locator");await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async f=>{let p=qL(c),h=await Qz(c,{cache:n,project:s});f.reportJson({locator:q.stringifyLocator(p),path:ue.fromPortablePath(h)});let E=this.update?" along with its current modifications":"";f.reportInfo(0,`Package ${q.prettyLocator(r,p)} got extracted with success${E}!`),f.reportInfo(0,`You can now edit the following folder: ${he.pretty(r,ue.fromPortablePath(h),"magenta")}`),f.reportInfo(0,`Once you are done run ${he.pretty(r,`yarn patch-commit -s ${process.platform==="win32"?'"':""}${ue.fromPortablePath(h)}${process.platform==="win32"?'"':""}`,"cyan")} and Yarn will store a patchfile based on your changes.`)})}};var f6t={configuration:{enableInlineHunks:{description:"If true, the installs will print unmatched patch hunks",type:"BOOLEAN",default:!1},patchFolder:{description:"Folder where the patch files must be written",type:"ABSOLUTE_PATH",default:"./.yarn/patches"}},commands:[j1,q1],fetchers:[mP],resolvers:[yP]},A6t=f6t;var Lz={};Vt(Lz,{PnpmLinker:()=>EP,default:()=>y6t});Ve();bt();Wt();var EP=class{getCustomDataKey(){return JSON.stringify({name:"PnpmLinker",version:3})}supportsPackage(e,r){return this.isEnabled(r)}async findPackageLocation(e,r){if(!this.isEnabled(r))throw new Error("Assertion failed: Expected the pnpm linker to be enabled");let s=this.getCustomDataKey(),a=r.project.linkersCustomData.get(s);if(!a)throw new nt(`The project in ${he.pretty(r.project.configuration,`${r.project.cwd}/package.json`,he.Type.PATH)} doesn't seem to have been installed - running an install there might help`);let n=a.pathsByLocator.get(e.locatorHash);if(typeof n>"u")throw new nt(`Couldn't find ${q.prettyLocator(r.project.configuration,e)} in the currently installed pnpm map - running an install might help`);return n.packageLocation}async findPackageLocator(e,r){if(!this.isEnabled(r))return null;let s=this.getCustomDataKey(),a=r.project.linkersCustomData.get(s);if(!a)throw new nt(`The project in ${he.pretty(r.project.configuration,`${r.project.cwd}/package.json`,he.Type.PATH)} doesn't seem to have been installed - running an install there might help`);let n=e.match(/(^.*\/node_modules\/(@[^/]*\/)?[^/]+)(\/.*$)/);if(n){let p=a.locatorByPath.get(n[1]);if(p)return p}let c=e,f=e;do{f=c,c=K.dirname(f);let p=a.locatorByPath.get(f);if(p)return p}while(c!==f);return null}makeInstaller(e){return new Nz(e)}isEnabled(e){return e.project.configuration.get("nodeLinker")==="pnpm"}},Nz=class{constructor(e){this.opts=e;this.asyncActions=new je.AsyncActions(10);this.customData={pathsByLocator:new Map,locatorByPath:new Map};this.indexFolderPromise=px(le,{indexPath:K.join(e.project.configuration.get("globalFolder"),"index")})}attachCustomData(e){}async installPackage(e,r,s){switch(e.linkType){case"SOFT":return this.installPackageSoft(e,r,s);case"HARD":return this.installPackageHard(e,r,s)}throw new Error("Assertion failed: Unsupported package link type")}async installPackageSoft(e,r,s){let a=K.resolve(r.packageFs.getRealPath(),r.prefixPath),n=this.opts.project.tryWorkspaceByLocator(e)?K.join(a,Er.nodeModules):null;return this.customData.pathsByLocator.set(e.locatorHash,{packageLocation:a,dependenciesLocation:n}),{packageLocation:a,buildRequest:null}}async installPackageHard(e,r,s){let a=h6t(e,{project:this.opts.project}),n=a.packageLocation;this.customData.locatorByPath.set(n,q.stringifyLocator(e)),this.customData.pathsByLocator.set(e.locatorHash,a),s.holdFetchResult(this.asyncActions.set(e.locatorHash,async()=>{await le.mkdirPromise(n,{recursive:!0}),await le.copyPromise(n,r.prefixPath,{baseFs:r.packageFs,overwrite:!1,linkStrategy:{type:"HardlinkFromIndex",indexPath:await this.indexFolderPromise,autoRepair:!0}})}));let f=q.isVirtualLocator(e)?q.devirtualizeLocator(e):e,p={manifest:await Ht.tryFind(r.prefixPath,{baseFs:r.packageFs})??new Ht,misc:{hasBindingGyp:mA.hasBindingGyp(r)}},h=this.opts.project.getDependencyMeta(f,e.version),E=mA.extractBuildRequest(e,p,h,{configuration:this.opts.project.configuration});return{packageLocation:n,buildRequest:E}}async attachInternalDependencies(e,r){if(this.opts.project.configuration.get("nodeLinker")!=="pnpm"||!FOe(e,{project:this.opts.project}))return;let s=this.customData.pathsByLocator.get(e.locatorHash);if(typeof s>"u")throw new Error(`Assertion failed: Expected the package to have been registered (${q.stringifyLocator(e)})`);let{dependenciesLocation:a}=s;a&&this.asyncActions.reduce(e.locatorHash,async n=>{await le.mkdirPromise(a,{recursive:!0});let c=await g6t(a),f=new Map(c),p=[n],h=(C,S)=>{let P=S;FOe(S,{project:this.opts.project})||(this.opts.report.reportWarningOnce(0,"The pnpm linker doesn't support providing different versions to workspaces' peer dependencies"),P=q.devirtualizeLocator(S));let I=this.customData.pathsByLocator.get(P.locatorHash);if(typeof I>"u")throw new Error(`Assertion failed: Expected the package to have been registered (${q.stringifyLocator(S)})`);let R=q.stringifyIdent(C),N=K.join(a,R),U=K.relative(K.dirname(N),I.packageLocation),W=f.get(R);f.delete(R),p.push(Promise.resolve().then(async()=>{if(W){if(W.isSymbolicLink()&&await le.readlinkPromise(N)===U)return;await le.removePromise(N)}await le.mkdirpPromise(K.dirname(N)),process.platform=="win32"&&this.opts.project.configuration.get("winLinkType")==="junctions"?await le.symlinkPromise(I.packageLocation,N,"junction"):await le.symlinkPromise(U,N)}))},E=!1;for(let[C,S]of r)C.identHash===e.identHash&&(E=!0),h(C,S);!E&&!this.opts.project.tryWorkspaceByLocator(e)&&h(q.convertLocatorToDescriptor(e),e),p.push(d6t(a,f)),await Promise.all(p)})}async attachExternalDependents(e,r){throw new Error("External dependencies haven't been implemented for the pnpm linker")}async finalizeInstall(){let e=NOe(this.opts.project);if(this.opts.project.configuration.get("nodeLinker")!=="pnpm")await le.removePromise(e);else{let r;try{r=new Set(await le.readdirPromise(e))}catch{r=new Set}for(let{dependenciesLocation:s}of this.customData.pathsByLocator.values()){if(!s)continue;let a=K.contains(e,s);if(a===null)continue;let[n]=a.split(K.sep);r.delete(n)}await Promise.all([...r].map(async s=>{await le.removePromise(K.join(e,s))}))}return await this.asyncActions.wait(),await Oz(e),this.opts.project.configuration.get("nodeLinker")!=="node-modules"&&await Oz(p6t(this.opts.project)),{customData:this.customData}}};function p6t(t){return K.join(t.cwd,Er.nodeModules)}function NOe(t){return t.configuration.get("pnpmStoreFolder")}function h6t(t,{project:e}){let r=q.slugifyLocator(t),s=NOe(e),a=K.join(s,r,"package"),n=K.join(s,r,Er.nodeModules);return{packageLocation:a,dependenciesLocation:n}}function FOe(t,{project:e}){return!q.isVirtualLocator(t)||!e.tryWorkspaceByLocator(t)}async function g6t(t){let e=new Map,r=[];try{r=await le.readdirPromise(t,{withFileTypes:!0})}catch(s){if(s.code!=="ENOENT")throw s}try{for(let s of r)if(!s.name.startsWith("."))if(s.name.startsWith("@")){let a=await le.readdirPromise(K.join(t,s.name),{withFileTypes:!0});if(a.length===0)e.set(s.name,s);else for(let n of a)e.set(`${s.name}/${n.name}`,n)}else e.set(s.name,s)}catch(s){if(s.code!=="ENOENT")throw s}return e}async function d6t(t,e){let r=[],s=new Set;for(let a of e.keys()){r.push(le.removePromise(K.join(t,a)));let n=q.tryParseIdent(a)?.scope;n&&s.add(`@${n}`)}return Promise.all(r).then(()=>Promise.all([...s].map(a=>Oz(K.join(t,a)))))}async function Oz(t){try{await le.rmdirPromise(t)}catch(e){if(e.code!=="ENOENT"&&e.code!=="ENOTEMPTY")throw e}}var m6t={configuration:{pnpmStoreFolder:{description:"By default, the store is stored in the 'node_modules/.store' of the project. Sometimes in CI scenario's it is convenient to store this in a different location so it can be cached and reused.",type:"ABSOLUTE_PATH",default:"./node_modules/.store"}},linkers:[EP]},y6t=m6t;var Gz={};Vt(Gz,{StageCommand:()=>G1,default:()=>x6t,stageUtils:()=>VL});Ve();bt();Wt();Ve();bt();var VL={};Vt(VL,{ActionType:()=>Mz,checkConsensus:()=>YL,expandDirectory:()=>Hz,findConsensus:()=>jz,findVcsRoot:()=>_z,genCommitMessage:()=>qz,getCommitPrefix:()=>OOe,isYarnFile:()=>Uz});bt();var Mz=(n=>(n[n.CREATE=0]="CREATE",n[n.DELETE=1]="DELETE",n[n.ADD=2]="ADD",n[n.REMOVE=3]="REMOVE",n[n.MODIFY=4]="MODIFY",n))(Mz||{});async function _z(t,{marker:e}){do if(!le.existsSync(K.join(t,e)))t=K.dirname(t);else return t;while(t!=="/");return null}function Uz(t,{roots:e,names:r}){if(r.has(K.basename(t)))return!0;do if(!e.has(t))t=K.dirname(t);else return!0;while(t!=="/");return!1}function Hz(t){let e=[],r=[t];for(;r.length>0;){let s=r.pop(),a=le.readdirSync(s);for(let n of a){let c=K.resolve(s,n);le.lstatSync(c).isDirectory()?r.push(c):e.push(c)}}return e}function YL(t,e){let r=0,s=0;for(let a of t)a!=="wip"&&(e.test(a)?r+=1:s+=1);return r>=s}function jz(t){let e=YL(t,/^(\w\(\w+\):\s*)?\w+s/),r=YL(t,/^(\w\(\w+\):\s*)?[A-Z]/),s=YL(t,/^\w\(\w+\):/);return{useThirdPerson:e,useUpperCase:r,useComponent:s}}function OOe(t){return t.useComponent?"chore(yarn): ":""}var E6t=new Map([[0,"create"],[1,"delete"],[2,"add"],[3,"remove"],[4,"update"]]);function qz(t,e){let r=OOe(t),s=[],a=e.slice().sort((n,c)=>n[0]-c[0]);for(;a.length>0;){let[n,c]=a.shift(),f=E6t.get(n);t.useUpperCase&&s.length===0&&(f=`${f[0].toUpperCase()}${f.slice(1)}`),t.useThirdPerson&&(f+="s");let p=[c];for(;a.length>0&&a[0][0]===n;){let[,E]=a.shift();p.push(E)}p.sort();let h=p.shift();p.length===1?h+=" (and one other)":p.length>1&&(h+=` (and ${p.length} others)`),s.push(`${f} ${h}`)}return`${r}${s.join(", ")}`}var I6t="Commit generated via `yarn stage`",C6t=11;async function LOe(t){let{code:e,stdout:r}=await Gr.execvp("git",["log","-1","--pretty=format:%H"],{cwd:t});return e===0?r.trim():null}async function w6t(t,e){let r=[],s=e.filter(h=>K.basename(h.path)==="package.json");for(let{action:h,path:E}of s){let C=K.relative(t,E);if(h===4){let S=await LOe(t),{stdout:P}=await Gr.execvp("git",["show",`${S}:${C}`],{cwd:t,strict:!0}),I=await Ht.fromText(P),R=await Ht.fromFile(E),N=new Map([...R.dependencies,...R.devDependencies]),U=new Map([...I.dependencies,...I.devDependencies]);for(let[W,te]of U){let ie=q.stringifyIdent(te),Ae=N.get(W);Ae?Ae.range!==te.range&&r.push([4,`${ie} to ${Ae.range}`]):r.push([3,ie])}for(let[W,te]of N)U.has(W)||r.push([2,q.stringifyIdent(te)])}else if(h===0){let S=await Ht.fromFile(E);S.name?r.push([0,q.stringifyIdent(S.name)]):r.push([0,"a package"])}else if(h===1){let S=await LOe(t),{stdout:P}=await Gr.execvp("git",["show",`${S}:${C}`],{cwd:t,strict:!0}),I=await Ht.fromText(P);I.name?r.push([1,q.stringifyIdent(I.name)]):r.push([1,"a package"])}else throw new Error("Assertion failed: Unsupported action type")}let{code:a,stdout:n}=await Gr.execvp("git",["log",`-${C6t}`,"--pretty=format:%s"],{cwd:t}),c=a===0?n.split(/\n/g).filter(h=>h!==""):[],f=jz(c);return qz(f,r)}var B6t={0:[" A ","?? "],4:[" M "],1:[" D "]},v6t={0:["A "],4:["M "],1:["D "]},MOe={async findRoot(t){return await _z(t,{marker:".git"})},async filterChanges(t,e,r,s){let{stdout:a}=await Gr.execvp("git",["status","-s"],{cwd:t,strict:!0}),n=a.toString().split(/\n/g),c=s?.staged?v6t:B6t;return[].concat(...n.map(p=>{if(p==="")return[];let h=p.slice(0,3),E=K.resolve(t,p.slice(3));if(!s?.staged&&h==="?? "&&p.endsWith("/"))return Hz(E).map(C=>({action:0,path:C}));{let S=[0,4,1].find(P=>c[P].includes(h));return S!==void 0?[{action:S,path:E}]:[]}})).filter(p=>Uz(p.path,{roots:e,names:r}))},async genCommitMessage(t,e){return await w6t(t,e)},async makeStage(t,e){let r=e.map(s=>ue.fromPortablePath(s.path));await Gr.execvp("git",["add","--",...r],{cwd:t,strict:!0})},async makeCommit(t,e,r){let s=e.map(a=>ue.fromPortablePath(a.path));await Gr.execvp("git",["add","-N","--",...s],{cwd:t,strict:!0}),await Gr.execvp("git",["commit","-m",`${r} + +${I6t} +`,"--",...s],{cwd:t,strict:!0})},async makeReset(t,e){let r=e.map(s=>ue.fromPortablePath(s.path));await Gr.execvp("git",["reset","HEAD","--",...r],{cwd:t,strict:!0})}};var S6t=[MOe],G1=class extends ut{constructor(){super(...arguments);this.commit=ge.Boolean("-c,--commit",!1,{description:"Commit the staged files"});this.reset=ge.Boolean("-r,--reset",!1,{description:"Remove all files from the staging area"});this.dryRun=ge.Boolean("-n,--dry-run",!1,{description:"Print the commit message and the list of modified files without staging / committing"});this.update=ge.Boolean("-u,--update",!1,{hidden:!0})}static{this.paths=[["stage"]]}static{this.usage=ot.Usage({description:"add all yarn files to your vcs",details:"\n This command will add to your staging area the files belonging to Yarn (typically any modified `package.json` and `.yarnrc.yml` files, but also linker-generated files, cache data, etc). It will take your ignore list into account, so the cache files won't be added if the cache is ignored in a `.gitignore` file (assuming you use Git).\n\n Running `--reset` will instead remove them from the staging area (the changes will still be there, but won't be committed until you stage them back).\n\n Since the staging area is a non-existent concept in Mercurial, Yarn will always create a new commit when running this command on Mercurial repositories. You can get this behavior when using Git by using the `--commit` flag which will directly create a commit.\n ",examples:[["Adds all modified project files to the staging area","yarn stage"],["Creates a new commit containing all modified project files","yarn stage --commit"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s}=await Tt.find(r,this.context.cwd),{driver:a,root:n}=await D6t(s.cwd),c=[r.get("cacheFolder"),r.get("globalFolder"),r.get("virtualFolder"),r.get("yarnPath")];await r.triggerHook(C=>C.populateYarnPaths,s,C=>{c.push(C)});let f=new Set;for(let C of c)for(let S of b6t(n,C))f.add(S);let p=new Set([r.get("rcFilename"),Er.lockfile,Er.manifest]),h=await a.filterChanges(n,f,p),E=await a.genCommitMessage(n,h);if(this.dryRun)if(this.commit)this.context.stdout.write(`${E} +`);else for(let C of h)this.context.stdout.write(`${ue.fromPortablePath(C.path)} +`);else if(this.reset){let C=await a.filterChanges(n,f,p,{staged:!0});C.length===0?this.context.stdout.write("No staged changes found!"):await a.makeReset(n,C)}else h.length===0?this.context.stdout.write("No changes found!"):this.commit?await a.makeCommit(n,h,E):(await a.makeStage(n,h),this.context.stdout.write(E))}};async function D6t(t){let e=null,r=null;for(let s of S6t)if((r=await s.findRoot(t))!==null){e=s;break}if(e===null||r===null)throw new nt("No stage driver has been found for your current project");return{driver:e,root:r}}function b6t(t,e){let r=[];if(e===null)return r;for(;;){(e===t||e.startsWith(`${t}/`))&&r.push(e);let s;try{s=le.statSync(e)}catch{break}if(s.isSymbolicLink())e=K.resolve(K.dirname(e),le.readlinkSync(e));else break}return r}var P6t={commands:[G1]},x6t=P6t;var Wz={};Vt(Wz,{default:()=>L6t});Ve();Ve();bt();var HOe=et(fi());Ve();var _Oe=et(Z9()),k6t="e8e1bd300d860104bb8c58453ffa1eb4",Q6t="OFCNCOG2CU",UOe=async(t,e)=>{let r=q.stringifyIdent(t),a=T6t(e).initIndex("npm-search");try{return(await a.getObject(r,{attributesToRetrieve:["types"]})).types?.ts==="definitely-typed"}catch{return!1}},T6t=t=>(0,_Oe.default)(Q6t,k6t,{requester:{async send(r){try{let s=await An.request(r.url,r.data||null,{configuration:t,headers:r.headers});return{content:s.body,isTimedOut:!1,status:s.statusCode}}catch(s){return{content:s.response.body,isTimedOut:!1,status:s.response.statusCode}}}}});var jOe=t=>t.scope?`${t.scope}__${t.name}`:`${t.name}`,R6t=async(t,e,r,s)=>{if(r.scope==="types")return;let{project:a}=t,{configuration:n}=a;if(!(n.get("tsEnableAutoTypes")??(le.existsSync(K.join(t.cwd,"tsconfig.json"))||le.existsSync(K.join(a.cwd,"tsconfig.json")))))return;let f=n.makeResolver(),p={project:a,resolver:f,report:new Wi};if(!await UOe(r,n))return;let E=jOe(r),C=q.parseRange(r.range).selector;if(!Or.validRange(C)){let N=n.normalizeDependency(r),U=await f.getCandidates(N,{},p);C=q.parseRange(U[0].reference).selector}let S=HOe.default.coerce(C);if(S===null)return;let P=`${Xu.Modifier.CARET}${S.major}`,I=q.makeDescriptor(q.makeIdent("types",E),P),R=je.mapAndFind(a.workspaces,N=>{let U=N.manifest.dependencies.get(r.identHash)?.descriptorHash,W=N.manifest.devDependencies.get(r.identHash)?.descriptorHash;if(U!==r.descriptorHash&&W!==r.descriptorHash)return je.mapAndFind.skip;let te=[];for(let ie of Ht.allDependencies){let Ae=N.manifest[ie].get(I.identHash);typeof Ae>"u"||te.push([ie,Ae])}return te.length===0?je.mapAndFind.skip:te});if(typeof R<"u")for(let[N,U]of R)t.manifest[N].set(U.identHash,U);else{try{let N=n.normalizeDependency(I);if((await f.getCandidates(N,{},p)).length===0)return}catch{return}t.manifest[Xu.Target.DEVELOPMENT].set(I.identHash,I)}},F6t=async(t,e,r)=>{if(r.scope==="types")return;let{project:s}=t,{configuration:a}=s;if(!(a.get("tsEnableAutoTypes")??(le.existsSync(K.join(t.cwd,"tsconfig.json"))||le.existsSync(K.join(s.cwd,"tsconfig.json")))))return;let c=jOe(r),f=q.makeIdent("types",c);for(let p of Ht.allDependencies)typeof t.manifest[p].get(f.identHash)>"u"||t.manifest[p].delete(f.identHash)},N6t=(t,e)=>{e.publishConfig&&e.publishConfig.typings&&(e.typings=e.publishConfig.typings),e.publishConfig&&e.publishConfig.types&&(e.types=e.publishConfig.types)},O6t={configuration:{tsEnableAutoTypes:{description:"Whether Yarn should auto-install @types/ dependencies on 'yarn add'",type:"BOOLEAN",isNullable:!0,default:null}},hooks:{afterWorkspaceDependencyAddition:R6t,afterWorkspaceDependencyRemoval:F6t,beforeWorkspacePacking:N6t}},L6t=O6t;var zz={};Vt(zz,{VersionApplyCommand:()=>J1,VersionCheckCommand:()=>z1,VersionCommand:()=>Z1,default:()=>rqt,versionUtils:()=>K1});Ve();Ve();Wt();var K1={};Vt(K1,{Decision:()=>Y1,applyPrerelease:()=>KOe,applyReleases:()=>Jz,applyStrategy:()=>JL,clearVersionFiles:()=>Yz,getUndecidedDependentWorkspaces:()=>CP,getUndecidedWorkspaces:()=>KL,openVersionFile:()=>V1,requireMoreDecisions:()=>$6t,resolveVersionFiles:()=>IP,suggestStrategy:()=>Kz,updateVersionFiles:()=>Vz,validateReleaseDecision:()=>W1});Ve();bt();Bc();Wt();var VOe=et(YOe()),TA=et(fi()),X6t=/^(>=|[~^]|)(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/,Y1=(c=>(c.UNDECIDED="undecided",c.DECLINE="decline",c.MAJOR="major",c.MINOR="minor",c.PATCH="patch",c.PRERELEASE="prerelease",c))(Y1||{});function W1(t){let e=TA.default.valid(t);return e||je.validateEnum((0,VOe.default)(Y1,"UNDECIDED"),t)}async function IP(t,{prerelease:e=null}={}){let r=new Map,s=t.configuration.get("deferredVersionFolder");if(!le.existsSync(s))return r;let a=await le.readdirPromise(s);for(let n of a){if(!n.endsWith(".yml"))continue;let c=K.join(s,n),f=await le.readFilePromise(c,"utf8"),p=ls(f);for(let[h,E]of Object.entries(p.releases||{})){if(E==="decline")continue;let C=q.parseIdent(h),S=t.tryWorkspaceByIdent(C);if(S===null)throw new Error(`Assertion failed: Expected a release definition file to only reference existing workspaces (${K.basename(c)} references ${h})`);if(S.manifest.version===null)throw new Error(`Assertion failed: Expected the workspace to have a version (${q.prettyLocator(t.configuration,S.anchoredLocator)})`);let P=S.manifest.raw.stableVersion??S.manifest.version,I=r.get(S),R=JL(P,W1(E));if(R===null)throw new Error(`Assertion failed: Expected ${P} to support being bumped via strategy ${E}`);let N=typeof I<"u"?TA.default.gt(R,I)?R:I:R;r.set(S,N)}}return e&&(r=new Map([...r].map(([n,c])=>[n,KOe(c,{current:n.manifest.version,prerelease:e})]))),r}async function Yz(t){let e=t.configuration.get("deferredVersionFolder");le.existsSync(e)&&await le.removePromise(e)}async function Vz(t,e){let r=new Set(e),s=t.configuration.get("deferredVersionFolder");if(!le.existsSync(s))return;let a=await le.readdirPromise(s);for(let n of a){if(!n.endsWith(".yml"))continue;let c=K.join(s,n),f=await le.readFilePromise(c,"utf8"),p=ls(f),h=p?.releases;if(h){for(let E of Object.keys(h)){let C=q.parseIdent(E),S=t.tryWorkspaceByIdent(C);(S===null||r.has(S))&&delete p.releases[E]}Object.keys(p.releases).length>0?await le.changeFilePromise(c,il(new il.PreserveOrdering(p))):await le.unlinkPromise(c)}}}async function V1(t,{allowEmpty:e=!1}={}){let r=t.configuration;if(r.projectCwd===null)throw new nt("This command can only be run from within a Yarn project");let s=await Qa.fetchRoot(r.projectCwd),a=s!==null?await Qa.fetchBase(s,{baseRefs:r.get("changesetBaseRefs")}):null,n=s!==null?await Qa.fetchChangedFiles(s,{base:a.hash,project:t}):[],c=r.get("deferredVersionFolder"),f=n.filter(P=>K.contains(c,P)!==null);if(f.length>1)throw new nt(`Your current branch contains multiple versioning files; this isn't supported: +- ${f.map(P=>ue.fromPortablePath(P)).join(` +- `)}`);let p=new Set(je.mapAndFilter(n,P=>{let I=t.tryWorkspaceByFilePath(P);return I===null?je.mapAndFilter.skip:I}));if(f.length===0&&p.size===0&&!e)return null;let h=f.length===1?f[0]:K.join(c,`${Nn.makeHash(Math.random().toString()).slice(0,8)}.yml`),E=le.existsSync(h)?await le.readFilePromise(h,"utf8"):"{}",C=ls(E),S=new Map;for(let P of C.declined||[]){let I=q.parseIdent(P),R=t.getWorkspaceByIdent(I);S.set(R,"decline")}for(let[P,I]of Object.entries(C.releases||{})){let R=q.parseIdent(P),N=t.getWorkspaceByIdent(R);S.set(N,W1(I))}return{project:t,root:s,baseHash:a!==null?a.hash:null,baseTitle:a!==null?a.title:null,changedFiles:new Set(n),changedWorkspaces:p,releaseRoots:new Set([...p].filter(P=>P.manifest.version!==null)),releases:S,async saveAll(){let P={},I=[],R=[];for(let N of t.workspaces){if(N.manifest.version===null)continue;let U=q.stringifyIdent(N.anchoredLocator),W=S.get(N);W==="decline"?I.push(U):typeof W<"u"?P[U]=W1(W):p.has(N)&&R.push(U)}await le.mkdirPromise(K.dirname(h),{recursive:!0}),await le.changeFilePromise(h,il(new il.PreserveOrdering({releases:Object.keys(P).length>0?P:void 0,declined:I.length>0?I:void 0,undecided:R.length>0?R:void 0})))}}}function $6t(t){return KL(t).size>0||CP(t).length>0}function KL(t){let e=new Set;for(let r of t.changedWorkspaces)r.manifest.version!==null&&(t.releases.has(r)||e.add(r));return e}function CP(t,{include:e=new Set}={}){let r=[],s=new Map(je.mapAndFilter([...t.releases],([n,c])=>c==="decline"?je.mapAndFilter.skip:[n.anchoredLocator.locatorHash,n])),a=new Map(je.mapAndFilter([...t.releases],([n,c])=>c!=="decline"?je.mapAndFilter.skip:[n.anchoredLocator.locatorHash,n]));for(let n of t.project.workspaces)if(!(!e.has(n)&&(a.has(n.anchoredLocator.locatorHash)||s.has(n.anchoredLocator.locatorHash)))&&n.manifest.version!==null)for(let c of Ht.hardDependencies)for(let f of n.manifest.getForScope(c).values()){let p=t.project.tryWorkspaceByDescriptor(f);p!==null&&s.has(p.anchoredLocator.locatorHash)&&r.push([n,p])}return r}function Kz(t,e){let r=TA.default.clean(e);for(let s of Object.values(Y1))if(s!=="undecided"&&s!=="decline"&&TA.default.inc(t,s)===r)return s;return null}function JL(t,e){if(TA.default.valid(e))return e;if(t===null)throw new nt(`Cannot apply the release strategy "${e}" unless the workspace already has a valid version`);if(!TA.default.valid(t))throw new nt(`Cannot apply the release strategy "${e}" on a non-semver version (${t})`);let r=TA.default.inc(t,e);if(r===null)throw new nt(`Cannot apply the release strategy "${e}" on the specified version (${t})`);return r}function Jz(t,e,{report:r,exact:s}){let a=new Map;for(let n of t.workspaces)for(let c of Ht.allDependencies)for(let f of n.manifest[c].values()){let p=t.tryWorkspaceByDescriptor(f);if(p===null||!e.has(p))continue;je.getArrayWithDefault(a,p).push([n,c,f.identHash])}for(let[n,c]of e){let f=n.manifest.version;n.manifest.version=c,TA.default.prerelease(c)===null?delete n.manifest.raw.stableVersion:n.manifest.raw.stableVersion||(n.manifest.raw.stableVersion=f);let p=n.manifest.name!==null?q.stringifyIdent(n.manifest.name):null;r.reportInfo(0,`${q.prettyLocator(t.configuration,n.anchoredLocator)}: Bumped to ${c}`),r.reportJson({cwd:ue.fromPortablePath(n.cwd),ident:p,oldVersion:f,newVersion:c});let h=a.get(n);if(!(typeof h>"u"))for(let[E,C,S]of h){let P=E.manifest[C].get(S);if(typeof P>"u")throw new Error("Assertion failed: The dependency should have existed");let I=P.range,R=!1;if(I.startsWith(yi.protocol)&&(I=I.slice(yi.protocol.length),R=!0,I===n.relativeCwd))continue;let N=I.match(X6t);if(!N){r.reportWarning(0,`Couldn't auto-upgrade range ${I} (in ${q.prettyLocator(t.configuration,E.anchoredLocator)})`);continue}let U=s?`${c}`:`${N[1]}${c}`;R&&(U=`${yi.protocol}${U}`);let W=q.makeDescriptor(P,U);E.manifest[C].set(S,W)}}}var eqt=new Map([["%n",{extract:t=>t.length>=1?[t[0],t.slice(1)]:null,generate:(t=0)=>`${t+1}`}]]);function KOe(t,{current:e,prerelease:r}){let s=new TA.default.SemVer(e),a=s.prerelease.slice(),n=[];s.prerelease=[],s.format()!==t&&(a.length=0);let c=!0,f=r.split(/\./g);for(let p of f){let h=eqt.get(p);if(typeof h>"u")n.push(p),a[0]===p?a.shift():c=!1;else{let E=c?h.extract(a):null;E!==null&&typeof E[0]=="number"?(n.push(h.generate(E[0])),a=E[1]):(n.push(h.generate()),c=!1)}}return s.prerelease&&(s.prerelease=[]),`${t}-${n.join(".")}`}var J1=class extends ut{constructor(){super(...arguments);this.all=ge.Boolean("--all",!1,{description:"Apply the deferred version changes on all workspaces"});this.dryRun=ge.Boolean("--dry-run",!1,{description:"Print the versions without actually generating the package archive"});this.prerelease=ge.String("--prerelease",{description:"Add a prerelease identifier to new versions",tolerateBoolean:!0});this.exact=ge.Boolean("--exact",!1,{description:"Use the exact version of each package, removes any range. Useful for nightly releases where the range might match another version."});this.recursive=ge.Boolean("-R,--recursive",{description:"Release the transitive workspaces as well"});this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"})}static{this.paths=[["version","apply"]]}static{this.usage=ot.Usage({category:"Release-related commands",description:"apply all the deferred version bumps at once",details:` + This command will apply the deferred version changes and remove their definitions from the repository. + + Note that if \`--prerelease\` is set, the given prerelease identifier (by default \`rc.%n\`) will be used on all new versions and the version definitions will be kept as-is. + + By default only the current workspace will be bumped, but you can configure this behavior by using one of: + + - \`--recursive\` to also apply the version bump on its dependencies + - \`--all\` to apply the version bump on all packages in the repository + + Note that this command will also update the \`workspace:\` references across all your local workspaces, thus ensuring that they keep referring to the same workspaces even after the version bump. + `,examples:[["Apply the version change to the local workspace","yarn version apply"],["Apply the version change to all the workspaces in the local workspace","yarn version apply --all"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);if(!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState({restoreResolutions:!1});let c=await Ot.start({configuration:r,json:this.json,stdout:this.context.stdout},async f=>{let p=this.prerelease?typeof this.prerelease!="boolean"?this.prerelease:"rc.%n":null,h=await IP(s,{prerelease:p}),E=new Map;if(this.all)E=h;else{let C=this.recursive?a.getRecursiveWorkspaceDependencies():[a];for(let S of C){let P=h.get(S);typeof P<"u"&&E.set(S,P)}}if(E.size===0){let C=h.size>0?" Did you want to add --all?":"";f.reportWarning(0,`The current workspace doesn't seem to require a version bump.${C}`);return}Jz(s,E,{report:f,exact:this.exact}),this.dryRun||(p||(this.all?await Yz(s):await Vz(s,[...E.keys()])),f.reportSeparator())});return this.dryRun||c.hasErrors()?c.exitCode():await s.installWithNewReport({json:this.json,stdout:this.context.stdout},{cache:n})}};Ve();bt();Wt();var zL=et(fi());var z1=class extends ut{constructor(){super(...arguments);this.interactive=ge.Boolean("-i,--interactive",{description:"Open an interactive interface used to set version bumps"})}static{this.paths=[["version","check"]]}static{this.usage=ot.Usage({category:"Release-related commands",description:"check that all the relevant packages have been bumped",details:"\n **Warning:** This command currently requires Git.\n\n This command will check that all the packages covered by the files listed in argument have been properly bumped or declined to bump.\n\n In the case of a bump, the check will also cover transitive packages - meaning that should `Foo` be bumped, a package `Bar` depending on `Foo` will require a decision as to whether `Bar` will need to be bumped. This check doesn't cross packages that have declined to bump.\n\n In case no arguments are passed to the function, the list of modified files will be generated by comparing the HEAD against `master`.\n ",examples:[["Check whether the modified packages need a bump","yarn version check"]]})}async execute(){return this.interactive?await this.executeInteractive():await this.executeStandard()}async executeInteractive(){ow(this.context);let{Gem:r}=await Promise.resolve().then(()=>($F(),CY)),{ScrollableItems:s}=await Promise.resolve().then(()=>(nN(),rN)),{FocusRequest:a}=await Promise.resolve().then(()=>(BY(),XPe)),{useListInput:n}=await Promise.resolve().then(()=>(tN(),$Pe)),{renderForm:c}=await Promise.resolve().then(()=>(aN(),oN)),{Box:f,Text:p}=await Promise.resolve().then(()=>et(Vc())),{default:h,useCallback:E,useState:C}=await Promise.resolve().then(()=>et(hn())),S=await ze.find(this.context.cwd,this.context.plugins),{project:P,workspace:I}=await Tt.find(S,this.context.cwd);if(!I)throw new ar(P.cwd,this.context.cwd);await P.restoreInstallState();let R=await V1(P);if(R===null||R.releaseRoots.size===0)return 0;if(R.root===null)throw new nt("This command can only be run on Git repositories");let N=()=>h.createElement(f,{flexDirection:"row",paddingBottom:1},h.createElement(f,{flexDirection:"column",width:60},h.createElement(f,null,h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},""),"/",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to select workspaces.")),h.createElement(f,null,h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},""),"/",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to select release strategies."))),h.createElement(f,{flexDirection:"column"},h.createElement(f,{marginLeft:1},h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to save.")),h.createElement(f,{marginLeft:1},h.createElement(p,null,"Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to abort.")))),U=({workspace:me,active:pe,decision:Be,setDecision:Ce})=>{let g=me.manifest.raw.stableVersion??me.manifest.version;if(g===null)throw new Error(`Assertion failed: The version should have been set (${q.prettyLocator(S,me.anchoredLocator)})`);if(zL.default.prerelease(g)!==null)throw new Error(`Assertion failed: Prerelease identifiers shouldn't be found (${g})`);let we=["undecided","decline","patch","minor","major"];n(Be,we,{active:pe,minus:"left",plus:"right",set:Ce});let Ee=Be==="undecided"?h.createElement(p,{color:"yellow"},g):Be==="decline"?h.createElement(p,{color:"green"},g):h.createElement(p,null,h.createElement(p,{color:"magenta"},g)," \u2192 ",h.createElement(p,{color:"green"},zL.default.valid(Be)?Be:zL.default.inc(g,Be)));return h.createElement(f,{flexDirection:"column"},h.createElement(f,null,h.createElement(p,null,q.prettyLocator(S,me.anchoredLocator)," - ",Ee)),h.createElement(f,null,we.map(fe=>h.createElement(f,{key:fe,paddingLeft:2},h.createElement(p,null,h.createElement(r,{active:fe===Be})," ",fe)))))},W=me=>{let pe=new Set(R.releaseRoots),Be=new Map([...me].filter(([Ce])=>pe.has(Ce)));for(;;){let Ce=CP({project:R.project,releases:Be}),g=!1;if(Ce.length>0){for(let[we]of Ce)if(!pe.has(we)){pe.add(we),g=!0;let Ee=me.get(we);typeof Ee<"u"&&Be.set(we,Ee)}}if(!g)break}return{relevantWorkspaces:pe,relevantReleases:Be}},te=()=>{let[me,pe]=C(()=>new Map(R.releases)),Be=E((Ce,g)=>{let we=new Map(me);g!=="undecided"?we.set(Ce,g):we.delete(Ce);let{relevantReleases:Ee}=W(we);pe(Ee)},[me,pe]);return[me,Be]},ie=({workspaces:me,releases:pe})=>{let Be=[];Be.push(`${me.size} total`);let Ce=0,g=0;for(let we of me){let Ee=pe.get(we);typeof Ee>"u"?g+=1:Ee!=="decline"&&(Ce+=1)}return Be.push(`${Ce} release${Ce===1?"":"s"}`),Be.push(`${g} remaining`),h.createElement(p,{color:"yellow"},Be.join(", "))},ce=await c(({useSubmit:me})=>{let[pe,Be]=te();me(pe);let{relevantWorkspaces:Ce}=W(pe),g=new Set([...Ce].filter(se=>!R.releaseRoots.has(se))),[we,Ee]=C(0),fe=E(se=>{switch(se){case a.BEFORE:Ee(we-1);break;case a.AFTER:Ee(we+1);break}},[we,Ee]);return h.createElement(f,{flexDirection:"column"},h.createElement(N,null),h.createElement(f,null,h.createElement(p,{wrap:"wrap"},"The following files have been modified in your local checkout.")),h.createElement(f,{flexDirection:"column",marginTop:1,paddingLeft:2},[...R.changedFiles].map(se=>h.createElement(f,{key:se},h.createElement(p,null,h.createElement(p,{color:"grey"},ue.fromPortablePath(R.root)),ue.sep,ue.relative(ue.fromPortablePath(R.root),ue.fromPortablePath(se)))))),R.releaseRoots.size>0&&h.createElement(h.Fragment,null,h.createElement(f,{marginTop:1},h.createElement(p,{wrap:"wrap"},"Because of those files having been modified, the following workspaces may need to be released again (note that private workspaces are also shown here, because even though they won't be published, releasing them will allow us to flag their dependents for potential re-release):")),g.size>3?h.createElement(f,{marginTop:1},h.createElement(ie,{workspaces:R.releaseRoots,releases:pe})):null,h.createElement(f,{marginTop:1,flexDirection:"column"},h.createElement(s,{active:we%2===0,radius:1,size:2,onFocusRequest:fe},[...R.releaseRoots].map(se=>h.createElement(U,{key:se.cwd,workspace:se,decision:pe.get(se)||"undecided",setDecision:X=>Be(se,X)}))))),g.size>0?h.createElement(h.Fragment,null,h.createElement(f,{marginTop:1},h.createElement(p,{wrap:"wrap"},"The following workspaces depend on other workspaces that have been marked for release, and thus may need to be released as well:")),h.createElement(f,null,h.createElement(p,null,"(Press ",h.createElement(p,{bold:!0,color:"cyanBright"},"")," to move the focus between the workspace groups.)")),g.size>5?h.createElement(f,{marginTop:1},h.createElement(ie,{workspaces:g,releases:pe})):null,h.createElement(f,{marginTop:1,flexDirection:"column"},h.createElement(s,{active:we%2===1,radius:2,size:2,onFocusRequest:fe},[...g].map(se=>h.createElement(U,{key:se.cwd,workspace:se,decision:pe.get(se)||"undecided",setDecision:X=>Be(se,X)}))))):null)},{versionFile:R},{stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr});if(typeof ce>"u")return 1;R.releases.clear();for(let[me,pe]of ce)R.releases.set(me,pe);await R.saveAll()}async executeStandard(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);return await s.restoreInstallState(),(await Ot.start({configuration:r,stdout:this.context.stdout},async c=>{let f=await V1(s);if(f===null||f.releaseRoots.size===0)return;if(f.root===null)throw new nt("This command can only be run on Git repositories");if(c.reportInfo(0,`Your PR was started right after ${he.pretty(r,f.baseHash.slice(0,7),"yellow")} ${he.pretty(r,f.baseTitle,"magenta")}`),f.changedFiles.size>0){c.reportInfo(0,"You have changed the following files since then:"),c.reportSeparator();for(let S of f.changedFiles)c.reportInfo(null,`${he.pretty(r,ue.fromPortablePath(f.root),"gray")}${ue.sep}${ue.relative(ue.fromPortablePath(f.root),ue.fromPortablePath(S))}`)}let p=!1,h=!1,E=KL(f);if(E.size>0){p||c.reportSeparator();for(let S of E)c.reportError(0,`${q.prettyLocator(r,S.anchoredLocator)} has been modified but doesn't have a release strategy attached`);p=!0}let C=CP(f);for(let[S,P]of C)h||c.reportSeparator(),c.reportError(0,`${q.prettyLocator(r,S.anchoredLocator)} doesn't have a release strategy attached, but depends on ${q.prettyWorkspace(r,P)} which is planned for release.`),h=!0;(p||h)&&(c.reportSeparator(),c.reportInfo(0,"This command detected that at least some workspaces have received modifications without explicit instructions as to how they had to be released (if needed)."),c.reportInfo(0,"To correct these errors, run `yarn version check --interactive` then follow the instructions."))})).exitCode()}};Ve();Wt();var ZL=et(fi());var Z1=class extends ut{constructor(){super(...arguments);this.deferred=ge.Boolean("-d,--deferred",{description:"Prepare the version to be bumped during the next release cycle"});this.immediate=ge.Boolean("-i,--immediate",{description:"Bump the version immediately"});this.strategy=ge.String()}static{this.paths=[["version"]]}static{this.usage=ot.Usage({category:"Release-related commands",description:"apply a new version to the current package",details:"\n This command will bump the version number for the given package, following the specified strategy:\n\n - If `major`, the first number from the semver range will be increased (`X.0.0`).\n - If `minor`, the second number from the semver range will be increased (`0.X.0`).\n - If `patch`, the third number from the semver range will be increased (`0.0.X`).\n - If prefixed by `pre` (`premajor`, ...), a `-0` suffix will be set (`0.0.0-0`).\n - If `prerelease`, the suffix will be increased (`0.0.0-X`); the third number from the semver range will also be increased if there was no suffix in the previous version.\n - If `decline`, the nonce will be increased for `yarn version check` to pass without version bump.\n - If a valid semver range, it will be used as new version.\n - If unspecified, Yarn will ask you for guidance.\n\n For more information about the `--deferred` flag, consult our documentation (https://yarnpkg.com/features/release-workflow#deferred-versioning).\n ",examples:[["Immediately bump the version to the next major","yarn version major"],["Prepare the version to be bumped to the next major","yarn version major --deferred"]]})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!a)throw new ar(s.cwd,this.context.cwd);let n=r.get("preferDeferredVersions");this.deferred&&(n=!0),this.immediate&&(n=!1);let c=ZL.default.valid(this.strategy),f=this.strategy==="decline",p;if(c)if(a.manifest.version!==null){let E=Kz(a.manifest.version,this.strategy);E!==null?p=E:p=this.strategy}else p=this.strategy;else{let E=a.manifest.version;if(!f){if(E===null)throw new nt("Can't bump the version if there wasn't a version to begin with - use 0.0.0 as initial version then run the command again.");if(typeof E!="string"||!ZL.default.valid(E))throw new nt(`Can't bump the version (${E}) if it's not valid semver`)}p=W1(this.strategy)}if(!n){let C=(await IP(s)).get(a);if(typeof C<"u"&&p!=="decline"){let S=JL(a.manifest.version,p);if(ZL.default.lt(S,C))throw new nt(`Can't bump the version to one that would be lower than the current deferred one (${C})`)}}let h=await V1(s,{allowEmpty:!0});return h.releases.set(a,p),await h.saveAll(),n?0:await this.cli.run(["version","apply"])}};var tqt={configuration:{deferredVersionFolder:{description:"Folder where are stored the versioning files",type:"ABSOLUTE_PATH",default:"./.yarn/versions"},preferDeferredVersions:{description:"If true, running `yarn version` will assume the `--deferred` flag unless `--immediate` is set",type:"BOOLEAN",default:!1}},commands:[J1,z1,Z1]},rqt=tqt;var Zz={};Vt(Zz,{WorkspacesFocusCommand:()=>X1,WorkspacesForeachCommand:()=>e2,default:()=>sqt});Ve();Ve();Wt();var X1=class extends ut{constructor(){super(...arguments);this.json=ge.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.production=ge.Boolean("--production",!1,{description:"Only install regular dependencies by omitting dev dependencies"});this.all=ge.Boolean("-A,--all",!1,{description:"Install the entire project"});this.workspaces=ge.Rest()}static{this.paths=[["workspaces","focus"]]}static{this.usage=ot.Usage({category:"Workspace-related commands",description:"install a single workspace and its dependencies",details:"\n This command will run an install as if the specified workspaces (and all other workspaces they depend on) were the only ones in the project. If no workspaces are explicitly listed, the active one will be assumed.\n\n Note that this command is only very moderately useful when using zero-installs, since the cache will contain all the packages anyway - meaning that the only difference between a full install and a focused install would just be a few extra lines in the `.pnp.cjs` file, at the cost of introducing an extra complexity.\n\n If the `-A,--all` flag is set, the entire project will be installed. Combine with `--production` to replicate the old `yarn install --production`.\n "})}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd),n=await Jr.find(r);await s.restoreInstallState({restoreResolutions:!1});let c;if(this.all)c=new Set(s.workspaces);else if(this.workspaces.length===0){if(!a)throw new ar(s.cwd,this.context.cwd);c=new Set([a])}else c=new Set(this.workspaces.map(f=>s.getWorkspaceByIdent(q.parseIdent(f))));for(let f of c)for(let p of this.production?["dependencies"]:Ht.hardDependencies)for(let h of f.manifest.getForScope(p).values()){let E=s.tryWorkspaceByDescriptor(h);E!==null&&c.add(E)}for(let f of s.workspaces)c.has(f)?this.production&&f.manifest.devDependencies.clear():(f.manifest.installConfig=f.manifest.installConfig||{},f.manifest.installConfig.selfReferences=!1,f.manifest.dependencies.clear(),f.manifest.devDependencies.clear(),f.manifest.peerDependencies.clear(),f.manifest.scripts.clear());return await s.installWithNewReport({json:this.json,stdout:this.context.stdout},{cache:n,persistProject:!1})}};Ve();Ve();Ve();Wt();var $1=et(Sa()),zOe=et(Md());Ul();var e2=class extends ut{constructor(){super(...arguments);this.from=ge.Array("--from",{description:"An array of glob pattern idents or paths from which to base any recursion"});this.all=ge.Boolean("-A,--all",{description:"Run the command on all workspaces of a project"});this.recursive=ge.Boolean("-R,--recursive",{description:"Run the command on the current workspace and all of its recursive dependencies"});this.worktree=ge.Boolean("-W,--worktree",{description:"Run the command on all workspaces of the current worktree"});this.verbose=ge.Counter("-v,--verbose",{description:"Increase level of logging verbosity up to 2 times"});this.parallel=ge.Boolean("-p,--parallel",!1,{description:"Run the commands in parallel"});this.interlaced=ge.Boolean("-i,--interlaced",!1,{description:"Print the output of commands in real-time instead of buffering it"});this.jobs=ge.String("-j,--jobs",{description:"The maximum number of parallel tasks that the execution will be limited to; or `unlimited`",validator:vU([Ao(["unlimited"]),rB(BU(),[DU(),SU(1)])])});this.topological=ge.Boolean("-t,--topological",!1,{description:"Run the command after all workspaces it depends on (regular) have finished"});this.topologicalDev=ge.Boolean("--topological-dev",!1,{description:"Run the command after all workspaces it depends on (regular + dev) have finished"});this.include=ge.Array("--include",[],{description:"An array of glob pattern idents or paths; only matching workspaces will be traversed"});this.exclude=ge.Array("--exclude",[],{description:"An array of glob pattern idents or paths; matching workspaces won't be traversed"});this.publicOnly=ge.Boolean("--no-private",{description:"Avoid running the command on private workspaces"});this.since=ge.String("--since",{description:"Only include workspaces that have been changed since the specified ref.",tolerateBoolean:!0});this.dryRun=ge.Boolean("-n,--dry-run",{description:"Print the commands that would be run, without actually running them"});this.commandName=ge.String();this.args=ge.Proxy()}static{this.paths=[["workspaces","foreach"]]}static{this.usage=ot.Usage({category:"Workspace-related commands",description:"run a command on all workspaces",details:"\n This command will run a given sub-command on current and all its descendant workspaces. Various flags can alter the exact behavior of the command:\n\n - If `-p,--parallel` is set, the commands will be ran in parallel; they'll by default be limited to a number of parallel tasks roughly equal to half your core number, but that can be overridden via `-j,--jobs`, or disabled by setting `-j unlimited`.\n\n - If `-p,--parallel` and `-i,--interlaced` are both set, Yarn will print the lines from the output as it receives them. If `-i,--interlaced` wasn't set, it would instead buffer the output from each process and print the resulting buffers only after their source processes have exited.\n\n - If `-t,--topological` is set, Yarn will only run the command after all workspaces that it depends on through the `dependencies` field have successfully finished executing. If `--topological-dev` is set, both the `dependencies` and `devDependencies` fields will be considered when figuring out the wait points.\n\n - If `-A,--all` is set, Yarn will run the command on all the workspaces of a project.\n\n - If `-R,--recursive` is set, Yarn will find workspaces to run the command on by recursively evaluating `dependencies` and `devDependencies` fields, instead of looking at the `workspaces` fields.\n\n - If `-W,--worktree` is set, Yarn will find workspaces to run the command on by looking at the current worktree.\n\n - If `--from` is set, Yarn will use the packages matching the 'from' glob as the starting point for any recursive search.\n\n - If `--since` is set, Yarn will only run the command on workspaces that have been modified since the specified ref. By default Yarn will use the refs specified by the `changesetBaseRefs` configuration option.\n\n - If `--dry-run` is set, Yarn will explain what it would do without actually doing anything.\n\n - The command may apply to only some workspaces through the use of `--include` which acts as a whitelist. The `--exclude` flag will do the opposite and will be a list of packages that mustn't execute the script. Both flags accept glob patterns (if valid Idents and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them. You can also use the `--no-private` flag to avoid running the command in private workspaces.\n\n The `-v,--verbose` flag can be passed up to twice: once to prefix output lines with the originating workspace's name, and again to include start/finish/timing log lines. Maximum verbosity is enabled by default in terminal environments.\n\n If the command is `run` and the script being run does not exist the child workspace will be skipped without error.\n ",examples:[["Publish all packages","yarn workspaces foreach -A --no-private npm publish --tolerate-republish"],["Run the build script on all descendant packages","yarn workspaces foreach -A run build"],["Run the build script on current and all descendant packages in parallel, building package dependencies first","yarn workspaces foreach -Apt run build"],["Run the build script on several packages and all their dependencies, building dependencies first","yarn workspaces foreach -Rpt --from '{workspace-a,workspace-b}' run build"]]})}static{this.schema=[iB("all",Wf.Forbids,["from","recursive","since","worktree"],{missingIf:"undefined"}),bU(["all","recursive","since","worktree"],{missingIf:"undefined"})]}async execute(){let r=await ze.find(this.context.cwd,this.context.plugins),{project:s,workspace:a}=await Tt.find(r,this.context.cwd);if(!this.all&&!a)throw new ar(s.cwd,this.context.cwd);await s.restoreInstallState();let n=this.cli.process([this.commandName,...this.args]),c=n.path.length===1&&n.path[0]==="run"&&typeof n.scriptName<"u"?n.scriptName:null;if(n.path.length===0)throw new nt("Invalid subcommand name for iteration - use the 'run' keyword if you wish to execute a script");let f=Ce=>{this.dryRun&&this.context.stdout.write(`${Ce} +`)},p=()=>{let Ce=this.from.map(g=>$1.default.matcher(g));return s.workspaces.filter(g=>{let we=q.stringifyIdent(g.anchoredLocator),Ee=g.relativeCwd;return Ce.some(fe=>fe(we)||fe(Ee))})},h=[];if(this.since?(f("Option --since is set; selecting the changed workspaces as root for workspace selection"),h=Array.from(await Qa.fetchChangedWorkspaces({ref:this.since,project:s}))):this.from?(f("Option --from is set; selecting the specified workspaces"),h=[...p()]):this.worktree?(f("Option --worktree is set; selecting the current workspace"),h=[a]):this.recursive?(f("Option --recursive is set; selecting the current workspace"),h=[a]):this.all&&(f("Option --all is set; selecting all workspaces"),h=[...s.workspaces]),this.dryRun&&!this.all){for(let Ce of h)f(` +- ${Ce.relativeCwd} + ${q.prettyLocator(r,Ce.anchoredLocator)}`);h.length>0&&f("")}let E;if(this.recursive?this.since?(f("Option --recursive --since is set; recursively selecting all dependent workspaces"),E=new Set(h.map(Ce=>[...Ce.getRecursiveWorkspaceDependents()]).flat())):(f("Option --recursive is set; recursively selecting all transitive dependencies"),E=new Set(h.map(Ce=>[...Ce.getRecursiveWorkspaceDependencies()]).flat())):this.worktree?(f("Option --worktree is set; recursively selecting all nested workspaces"),E=new Set(h.map(Ce=>[...Ce.getRecursiveWorkspaceChildren()]).flat())):E=null,E!==null&&(h=[...new Set([...h,...E])],this.dryRun))for(let Ce of E)f(` +- ${Ce.relativeCwd} + ${q.prettyLocator(r,Ce.anchoredLocator)}`);let C=[],S=!1;if(c?.includes(":")){for(let Ce of s.workspaces)if(Ce.manifest.scripts.has(c)&&(S=!S,S===!1))break}for(let Ce of h){if(c&&!Ce.manifest.scripts.has(c)&&!S&&!(await In.getWorkspaceAccessibleBinaries(Ce)).has(c)){f(`Excluding ${Ce.relativeCwd} because it doesn't have a "${c}" script`);continue}if(!(c===r.env.npm_lifecycle_event&&Ce.cwd===a.cwd)){if(this.include.length>0&&!$1.default.isMatch(q.stringifyIdent(Ce.anchoredLocator),this.include)&&!$1.default.isMatch(Ce.relativeCwd,this.include)){f(`Excluding ${Ce.relativeCwd} because it doesn't match the --include filter`);continue}if(this.exclude.length>0&&($1.default.isMatch(q.stringifyIdent(Ce.anchoredLocator),this.exclude)||$1.default.isMatch(Ce.relativeCwd,this.exclude))){f(`Excluding ${Ce.relativeCwd} because it matches the --exclude filter`);continue}if(this.publicOnly&&Ce.manifest.private===!0){f(`Excluding ${Ce.relativeCwd} because it's a private workspace and --no-private was set`);continue}C.push(Ce)}}if(this.dryRun)return 0;let P=this.verbose??(this.context.stdout.isTTY?1/0:0),I=P>0,R=P>1,N=this.parallel?this.jobs==="unlimited"?1/0:Number(this.jobs)||Math.ceil(As.availableParallelism()/2):1,U=N===1?!1:this.parallel,W=U?this.interlaced:!0,te=(0,zOe.default)(N),ie=new Map,Ae=new Set,ce=0,me=null,pe=!1,Be=await Ot.start({configuration:r,stdout:this.context.stdout,includePrefix:!1},async Ce=>{let g=async(we,{commandIndex:Ee})=>{if(pe)return-1;!U&&R&&Ee>1&&Ce.reportSeparator();let fe=nqt(we,{configuration:r,label:I,commandIndex:Ee}),[se,X]=JOe(Ce,{prefix:fe,interlaced:W}),[De,Re]=JOe(Ce,{prefix:fe,interlaced:W});try{R&&Ce.reportInfo(null,`${fe?`${fe} `:""}Process started`);let gt=Date.now(),j=await this.cli.run([this.commandName,...this.args],{cwd:we.cwd,stdout:se,stderr:De})||0;se.end(),De.end(),await X,await Re;let rt=Date.now();if(R){let Fe=r.get("enableTimers")?`, completed in ${he.pretty(r,rt-gt,he.Type.DURATION)}`:"";Ce.reportInfo(null,`${fe?`${fe} `:""}Process exited (exit code ${j})${Fe}`)}return j===130&&(pe=!0,me=j),j}catch(gt){throw se.end(),De.end(),await X,await Re,gt}};for(let we of C)ie.set(we.anchoredLocator.locatorHash,we);for(;ie.size>0&&!Ce.hasErrors();){let we=[];for(let[X,De]of ie){if(Ae.has(De.anchoredDescriptor.descriptorHash))continue;let Re=!0;if(this.topological||this.topologicalDev){let gt=this.topologicalDev?new Map([...De.manifest.dependencies,...De.manifest.devDependencies]):De.manifest.dependencies;for(let j of gt.values()){let rt=s.tryWorkspaceByDescriptor(j);if(Re=rt===null||!ie.has(rt.anchoredLocator.locatorHash),!Re)break}}if(Re&&(Ae.add(De.anchoredDescriptor.descriptorHash),we.push(te(async()=>{let gt=await g(De,{commandIndex:++ce});return ie.delete(X),Ae.delete(De.anchoredDescriptor.descriptorHash),{workspace:De,exitCode:gt}})),!U))break}if(we.length===0){let X=Array.from(ie.values()).map(De=>q.prettyLocator(r,De.anchoredLocator)).join(", ");Ce.reportError(3,`Dependency cycle detected (${X})`);return}let Ee=await Promise.all(we);Ee.forEach(({workspace:X,exitCode:De})=>{De!==0&&Ce.reportError(0,`The command failed in workspace ${q.prettyLocator(r,X.anchoredLocator)} with exit code ${De}`)});let se=Ee.map(X=>X.exitCode).find(X=>X!==0);(this.topological||this.topologicalDev)&&typeof se<"u"&&Ce.reportError(0,"The command failed for workspaces that are depended upon by other workspaces; can't satisfy the dependency graph")}});return me!==null?me:Be.exitCode()}};function JOe(t,{prefix:e,interlaced:r}){let s=t.createStreamReporter(e),a=new je.DefaultStream;a.pipe(s,{end:!1}),a.on("finish",()=>{s.end()});let n=new Promise(f=>{s.on("finish",()=>{f(a.active)})});if(r)return[a,n];let c=new je.BufferStream;return c.pipe(a,{end:!1}),c.on("finish",()=>{a.end()}),[c,n]}function nqt(t,{configuration:e,commandIndex:r,label:s}){if(!s)return null;let n=`[${q.stringifyIdent(t.anchoredLocator)}]:`,c=["#2E86AB","#A23B72","#F18F01","#C73E1D","#CCE2A3"],f=c[r%c.length];return he.pretty(e,n,f)}var iqt={commands:[X1,e2]},sqt=iqt;var nC=()=>({modules:new Map([["@yarnpkg/cli",iS],["@yarnpkg/core",nS],["@yarnpkg/fslib",q2],["@yarnpkg/libzip",Sv],["@yarnpkg/parsers",Z2],["@yarnpkg/shell",Qv],["clipanion",cB],["semver",oqt],["typanion",Ia],["@yarnpkg/plugin-essentials",$5],["@yarnpkg/plugin-compat",i9],["@yarnpkg/plugin-constraints",w9],["@yarnpkg/plugin-dlx",B9],["@yarnpkg/plugin-exec",D9],["@yarnpkg/plugin-file",P9],["@yarnpkg/plugin-git",X5],["@yarnpkg/plugin-github",Q9],["@yarnpkg/plugin-http",T9],["@yarnpkg/plugin-init",R9],["@yarnpkg/plugin-interactive-tools",TY],["@yarnpkg/plugin-jsr",FY],["@yarnpkg/plugin-link",NY],["@yarnpkg/plugin-nm",yV],["@yarnpkg/plugin-npm",yz],["@yarnpkg/plugin-npm-cli",bz],["@yarnpkg/plugin-pack",f7],["@yarnpkg/plugin-patch",Fz],["@yarnpkg/plugin-pnp",oV],["@yarnpkg/plugin-pnpm",Lz],["@yarnpkg/plugin-stage",Gz],["@yarnpkg/plugin-typescript",Wz],["@yarnpkg/plugin-version",zz],["@yarnpkg/plugin-workspace-tools",Zz]]),plugins:new Set(["@yarnpkg/plugin-essentials","@yarnpkg/plugin-compat","@yarnpkg/plugin-constraints","@yarnpkg/plugin-dlx","@yarnpkg/plugin-exec","@yarnpkg/plugin-file","@yarnpkg/plugin-git","@yarnpkg/plugin-github","@yarnpkg/plugin-http","@yarnpkg/plugin-init","@yarnpkg/plugin-interactive-tools","@yarnpkg/plugin-jsr","@yarnpkg/plugin-link","@yarnpkg/plugin-nm","@yarnpkg/plugin-npm","@yarnpkg/plugin-npm-cli","@yarnpkg/plugin-pack","@yarnpkg/plugin-patch","@yarnpkg/plugin-pnp","@yarnpkg/plugin-pnpm","@yarnpkg/plugin-stage","@yarnpkg/plugin-typescript","@yarnpkg/plugin-version","@yarnpkg/plugin-workspace-tools"])});function $Oe({cwd:t,pluginConfiguration:e}){let r=new wa({binaryLabel:"Yarn Package Manager",binaryName:"yarn",binaryVersion:un??""});return Object.assign(r,{defaultContext:{...wa.defaultContext,cwd:t,plugins:e,quiet:!1,stdin:process.stdin,stdout:process.stdout,stderr:process.stderr}})}function aqt(t){if(je.parseOptionalBoolean(process.env.YARN_IGNORE_NODE))return!0;let r=process.versions.node,s=">=18.12.0";if(Or.satisfiesWithPrereleases(r,s))return!0;let a=new nt(`This tool requires a Node version compatible with ${s} (got ${r}). Upgrade Node, or set \`YARN_IGNORE_NODE=1\` in your environment.`);return wa.defaultContext.stdout.write(t.error(a)),!1}async function eLe({selfPath:t,pluginConfiguration:e}){return await ze.find(ue.toPortablePath(process.cwd()),e,{strict:!1,usePathCheck:t})}function lqt(t,e,{yarnPath:r}){if(!le.existsSync(r))return t.error(new Error(`The "yarn-path" option has been set, but the specified location doesn't exist (${r}).`)),1;process.on("SIGINT",()=>{});let s={stdio:"inherit",env:{...process.env,YARN_IGNORE_PATH:"1"}};try{(0,ZOe.execFileSync)(process.execPath,[ue.fromPortablePath(r),...e],s)}catch(a){return a.status??1}return 0}function cqt(t,e){let r=null,s=e;return e.length>=2&&e[0]==="--cwd"?(r=ue.toPortablePath(e[1]),s=e.slice(2)):e.length>=1&&e[0].startsWith("--cwd=")?(r=ue.toPortablePath(e[0].slice(6)),s=e.slice(1)):e[0]==="add"&&e[e.length-2]==="--cwd"&&(r=ue.toPortablePath(e[e.length-1]),s=e.slice(0,e.length-2)),t.defaultContext.cwd=r!==null?K.resolve(r):K.cwd(),s}function uqt(t,{configuration:e}){if(!e.get("enableTelemetry")||XOe.isCI||!process.stdout.isTTY)return;ze.telemetry=new eC(e,"puba9cdc10ec5790a2cf4969dd413a47270");let s=/^@yarnpkg\/plugin-(.*)$/;for(let a of e.plugins.keys())tC.has(a.match(s)?.[1]??"")&&ze.telemetry?.reportPluginName(a);t.binaryVersion&&ze.telemetry.reportVersion(t.binaryVersion)}function tLe(t,{configuration:e}){for(let r of e.plugins.values())for(let s of r.commands||[])t.register(s)}async function fqt(t,e,{selfPath:r,pluginConfiguration:s}){if(!aqt(t))return 1;let a=await eLe({selfPath:r,pluginConfiguration:s}),n=a.get("yarnPath"),c=a.get("ignorePath");if(n&&!c)return lqt(t,e,{yarnPath:n});delete process.env.YARN_IGNORE_PATH;let f=cqt(t,e);uqt(t,{configuration:a}),tLe(t,{configuration:a});let p=t.process(f,t.defaultContext);return p.help||ze.telemetry?.reportCommandName(p.path.join(" ")),await t.run(p,t.defaultContext)}async function cwe({cwd:t=K.cwd(),pluginConfiguration:e=nC()}={}){let r=$Oe({cwd:t,pluginConfiguration:e}),s=await eLe({pluginConfiguration:e,selfPath:null});return tLe(r,{configuration:s}),r}async function zR(t,{cwd:e=K.cwd(),selfPath:r,pluginConfiguration:s}){let a=$Oe({cwd:e,pluginConfiguration:s});function n(){wa.defaultContext.stdout.write(`ERROR: Yarn is terminating due to an unexpected empty event loop. +Please report this issue at https://github.com/yarnpkg/berry/issues.`)}process.once("beforeExit",n);try{process.exitCode=42,process.exitCode=await fqt(a,t,{selfPath:r,pluginConfiguration:s})}catch(c){wa.defaultContext.stdout.write(a.error(c)),process.exitCode=1}finally{process.off("beforeExit",n),await le.rmtempPromise()}}zR(process.argv.slice(2),{cwd:K.cwd(),selfPath:ue.toPortablePath(ue.resolve(process.argv[1])),pluginConfiguration:nC()});})(); +/** + @license + Copyright (c) 2015, Rebecca Turner + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + */ +/** + @license + Copyright Node.js contributors. All rights reserved. + + 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. +*/ +/** + @license + The MIT License (MIT) + + Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) + + 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. +*/ +/** + @license + Copyright Joyent, Inc. and other Node contributors. + + 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. +*/ +/*! Bundled license information: + +is-number/index.js: + (*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + *) + +to-regex-range/index.js: + (*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + *) + +fill-range/index.js: + (*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + *) + +is-extglob/index.js: + (*! + * is-extglob + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + *) + +is-glob/index.js: + (*! + * is-glob + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + *) + +queue-microtask/index.js: + (*! queue-microtask. MIT License. Feross Aboukhadijeh *) + +run-parallel/index.js: + (*! run-parallel. MIT License. Feross Aboukhadijeh *) + +git-url-parse/lib/index.js: + (*! + * buildToken + * Builds OAuth token prefix (helper function) + * + * @name buildToken + * @function + * @param {GitUrl} obj The parsed Git url object. + * @return {String} token prefix + *) + +object-assign/index.js: + (* + object-assign + (c) Sindre Sorhus + @license MIT + *) + +react/cjs/react.production.min.js: + (** @license React v17.0.2 + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +scheduler/cjs/scheduler.production.min.js: + (** @license React v0.20.2 + * scheduler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react-reconciler/cjs/react-reconciler.production.min.js: + (** @license React v0.26.2 + * react-reconciler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +is-windows/index.js: + (*! + * is-windows + * + * Copyright © 2015-2018, Jon Schlinkert. + * Released under the MIT License. + *) +*/ diff --git a/backend/.yarnrc.yml b/backend/.yarnrc.yml new file mode 100644 index 0000000..62657af --- /dev/null +++ b/backend/.yarnrc.yml @@ -0,0 +1,5 @@ +compressionLevel: mixed + +nodeLinker: node-modules + +yarnPath: .yarn/releases/yarn-4.9.1.cjs diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..3254ff7 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,26 @@ +# Rifeberry backend + +## Общее описание системы + +* [Описание системы](https://olivergrand.atlassian.net/wiki/spaces/TEAM/pages/6291457) +* [Упрощенная ER диаграмма](https://olivergrand.atlassian.net/wiki/spaces/BACK/pages/6389854/ER). + +## Настройка локального окружения для разработки + +[Setup local environment](doc/setup-local.md). + +## Deploy + +Деплой: [Gitlab](https://gitlab.com/rifeberry1/nest-backend/-/pipelines). + +## Links + +* [Jira project board](https://olivergrand.atlassian.net/jira/software/projects/BACK/boards/1) +* [Confluence space](https://olivergrand.atlassian.net/wiki/spaces/BACK/pages) + +## Именование веток и комитов + +Ветки именуем следующим образом: название задачи и описание в настоящем времени, +например, `BACK-123-add-api-doc` + +Комиты именуем название задачи + описание комита: `BACK-123: Add api documentation` diff --git a/backend/artifacts/_environment-config/http-client.env.json.dist b/backend/artifacts/_environment-config/http-client.env.json.dist new file mode 100644 index 0000000..c0285ff --- /dev/null +++ b/backend/artifacts/_environment-config/http-client.env.json.dist @@ -0,0 +1,104 @@ +{ + "nestdev": { + "url": "http://test.amwork.loc", + "baseDomain": "amwork.loc", + "subdomain": "test", + "email": "test@test.com", + "password": "test", + "entityTypeId": 123, + "entityId": 123, + "teslaDealEntityId": 123, + "elonContactEntityId": 123, + "boardId": 123, + "stageId": 123, + "fieldGroupId": 123, + "activityTypeId": 123, + "taskId": 123, + "subtaskId": 123, + "taskBoardId": 123, + "taskStageId": 15022001, + "taskSettingsId": 123, + "taskCommentId": 123, + "activityId": 123, + "noteId": 123, + "userId": 123, + "budgetFieldId": 123, + "fileId": "11111111-1111-1111-1111-111111111111", + "mailboxId": 123, + "folderId": 123, + "messageId": 123, + "payloadId": 123, + "automationId": 123, + "taskAutomationId": 123, + "changeStageAutomationId": 123, + "notificationId": 123 + }, + "nestprod": { + "url": "https://test.amwork.com", + "baseDomain": "amwork.com", + "subdomain": "test", + "email": "test@test.com", + "password": "test", + "entityTypeId": 123, + "entityId": 123, + "teslaDealEntityId": 123, + "elonContactEntityId": 123, + "boardId": 123, + "stageId": 123, + "fieldGroupId": 123, + "activityTypeId": 123, + "taskId": 123, + "subtaskId": 123, + "taskBoardId": 123, + "taskStageId": 15022001, + "taskSettingsId": 123, + "taskCommentId": 123, + "activityId": 123, + "noteId": 123, + "userId": 123, + "budgetFieldId": 123, + "fileId": "11111111-1111-1111-1111-111111111111", + "mailboxId": 123, + "folderId": 123, + "messageId": 123, + "payloadId": 123, + "automationId": 123, + "taskAutomationId": 123, + "changeStageAutomationId": 123, + "notificationId": 123 + }, + "neststaging": { + "url": "https://test.amwork.com", + "baseDomain": "amwork.com", + "subdomain": "test", + "email": "test@test.com", + "password": "test", + "entityTypeId": 123, + "entityId": 123, + "teslaDealEntityId": 123, + "elonContactEntityId": 123, + "boardId": 123, + "stageId": 123, + "fieldGroupId": 123, + "activityTypeId": 123, + "taskId": 123, + "subtaskId": 123, + "taskBoardId": 123, + "taskStageId": 15022001, + "taskSettingsId": 123, + "taskCommentId": 123, + "activityId": 123, + "noteId": 123, + "userId": 123, + "budgetFieldId": 123, + "fileId": "11111111-1111-1111-1111-111111111111", + "mailboxId": 123, + "folderId": 123, + "messageId": 123, + "payloadId": 123, + "automationId": 123, + "taskAutomationId": 123, + "changeStageAutomationId": 123, + "notificationId": 123 + } +} diff --git a/backend/artifacts/_resources/test.png b/backend/artifacts/_resources/test.png new file mode 100644 index 0000000000000000000000000000000000000000..9947820ddc69a89fdc9fbc95a3852a3863613a3e GIT binary patch literal 98415 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

=h?;3oN#A=9i4kqA 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 {% +client.global.set("token", response.body.token); +client.global.set("userId", response.body.userId); +%} + +### Login for site + +POST {{url}}/api/auth/login-site +Content-Type: application/json + +{ + "email": "test@test.com", + "password": "test" +} + +> {% +client.global.set("loginLink", response.body.loginLink); +client.global.set("subdomain", response.body.subdomain); +%} + +### Login for extension + +POST {{url}}/api/auth/login-ext +Content-Type: application/json + +{ + "email": "test@test.com", + "password": "test" +} + +> {% +client.global.set("token", response.body.token); +client.global.set("userId", response.body.userId); +%} + +### Decode login link + +POST {{subdomain}}.{{baseDomain}}/api/auth/decode-login-link +Content-Type: application/json + +{ + "loginLink": "{{loginLink}}" +} + +> {% +client.global.set("token", response.body.token); +client.global.set("userId", response.body.userId); +%} + +### Refresh token + +POST {{url}}/api/auth/refresh-token +Content-Type: application/json +Authorization: Bearer {{token}} + +> {% +client.global.set("token", response.body.token); +client.global.set("userId", response.body.userId); + +%} + +### Create account + +POST {{url}}/api/auth/accounts +Content-Type: application/json + +{ + "firstName": "John", + "lastName": "Doe", + "email": "test{{$randomInt}}@test.com", + "phone": "+79998887766", + "companyName": "test5", + "password": "test", + "ref": "{{partnerRef}}", + "promoCode": "some-promo-code", + "rmsCode": "demo" +} + +> {% +client.global.set("loginLink", response.body.loginLink); +client.global.set("subdomain", response.body.subdomain); +%} \ No newline at end of file diff --git a/backend/artifacts/automation/activity-automation.http b/backend/artifacts/automation/activity-automation.http new file mode 100644 index 0000000..84b061c --- /dev/null +++ b/backend/artifacts/automation/activity-automation.http @@ -0,0 +1,93 @@ +### Create automation with activity action + +POST {{url}}/api/automation/automations +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "create_activity", + "delay": 300, + "settings": { + "responsibleUserType": "custom", + "responsibleUserId": {{userId}}, + "activityTypeId": {{activityTypeId}}, + "text": "some-text", + "deadlineType": "immediately", + "deadlineTime": null + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [ + { + "type": "responsible_user", + "settings": { + "userIds": [{{userId}}] + } + }, + { + "type": "field", + "settings": { + "fieldId": {{budgetFieldId}}, + "fieldType": "value", + "payload": { + "from": 100, + "to": 200 + } + } + } + ] +} + +### Update automation with activity action + +PUT {{url}}/api/automation/automations/{{activityAutomationId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "create_activity", + "delay": 300, + "settings": { + "responsibleUserType": "custom", + "responsibleUserId": {{userId}}, + "activityTypeId": {{activityTypeId}}, + "text": "test", + "deadlineType": "immediately", + "deadlineTime": null + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [ + { + "type": "responsible_user", + "settings": { + "userIds": [{{userId}}] + } + }, + { + "type": "field", + "settings": { + "fieldId": {{budgetFieldId}}, + "fieldType": "value", + "payload": { + "from": 100, + "to": 200 + } + } + } + ] +} \ No newline at end of file diff --git a/backend/artifacts/automation/automation.http b/backend/artifacts/automation/automation.http new file mode 100644 index 0000000..48402dd --- /dev/null +++ b/backend/artifacts/automation/automation.http @@ -0,0 +1,11 @@ +### Get automations + +GET {{url}}/api/automation/board/{{boardId}}/automations +Content-Type: application/json +Authorization: Bearer {{token}} + +### Delete automation + +DELETE {{url}}/api/automation/automations/51011020 +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/automation/change-stage-automation.http b/backend/artifacts/automation/change-stage-automation.http new file mode 100644 index 0000000..369baa3 --- /dev/null +++ b/backend/artifacts/automation/change-stage-automation.http @@ -0,0 +1,47 @@ +### Create automation with task action + +POST {{url}}/api/automation/automations +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "change_stage", + "delay": null, + "settings": { + "stageId": {{stageId}} + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [] +} + +### Update automation with task action + +PUT {{url}}/api/automation/automations/{{changeStageAutomationId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "change_stage", + "delay": 300, + "settings": { + "stageId": {{stageId}} + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [] +} \ No newline at end of file diff --git a/backend/artifacts/automation/email-automation.http b/backend/artifacts/automation/email-automation.http new file mode 100644 index 0000000..b4c032d --- /dev/null +++ b/backend/artifacts/automation/email-automation.http @@ -0,0 +1,63 @@ +### Create automation with email action + +POST {{url}}/api/automation/automations +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "send_email", + "delay": null, + "settings": { + "subject": "email subject", + "content": "email body", + "signature": "some signature", + "sendAsHtml": false, + "mailboxId": 27023020, + "userId": {{userId}}, + "fileIds": [] + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [], + "isActive": true, + "applyTrigger": false +} + +### Update automation with email action + +PUT {{url}}/api/automation/automations/{{emailAutomationId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "send_email", + "delay": null, + "settings": { + "subject": "email subject", + "content": "email body", + "signature": "new signature", + "sendAsHtml": false, + "mailboxId": 27023020, + "userId": {{userId}}, + "fileIds": [] + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [], + "isActive": true, + "applyTrigger": false +} \ No newline at end of file diff --git a/backend/artifacts/automation/task-automation.http b/backend/artifacts/automation/task-automation.http new file mode 100644 index 0000000..2b19a10 --- /dev/null +++ b/backend/artifacts/automation/task-automation.http @@ -0,0 +1,57 @@ +### Create automation with task action + +POST {{url}}/api/automation/automations +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_or_create_entity", + "settings": null + }, + "action": { + "type": "create_task", + "delay": 300, + "settings": { + "responsibleUserType": "custom", + "responsibleUserId": {{userId}}, + "title": "some-title", + "text": "some-text", + "deadlineType": "immediately", + "deadlineTime": null + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [] +} + +### Update automation with task action + +PUT {{url}}/api/automation/automations/{{taskAutomationId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "trigger": { + "type": "move_entity", + "settings": null + }, + "action": { + "type": "create_task", + "delay": 3000, + "settings": { + "responsibleUserType": "custom", + "responsibleUserId": {{userId}}, + "title": "new-title", + "text": "new-text", + "deadlineType": "custom", + "deadlineTime": 3000 + } + }, + "stageIds": [ + {{stageId}} + ], + "conditions": [] +} \ No newline at end of file diff --git a/backend/artifacts/board.http b/backend/artifacts/board.http new file mode 100644 index 0000000..a241131 --- /dev/null +++ b/backend/artifacts/board.http @@ -0,0 +1,35 @@ +### Get boards + +GET {{url}}/api/crm/boards +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create board + +POST {{url}}/api/crm/boards +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Board name", + "sortOrder": 1, + "type": "entity_type", + "recordId": {{entityTypeId}} +} + +### Update board + +PUT {{url}}/api/crm/boards/{{boardId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Board name", + "sortOrder": 1 +} + +### Delete board + +DELETE {{url}}/api/crm/boards/321 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/constructor.http b/backend/artifacts/constructor.http new file mode 100644 index 0000000..f3fb006 --- /dev/null +++ b/backend/artifacts/constructor.http @@ -0,0 +1,72 @@ +### Create entity type + +POST {{url}}/api/crm/constructor/create-entity-type +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Object", + "entityCategory": "universal", + "fieldGroups": [ + { + "id": {{fieldGroupId}}, + "name": "Details", + "sortOrder": 0, + "state": "created" + } + ], + "fields": [ + { + "id": 4201, + "name": "Field name", + "type": "text", + "sortOrder": 1, + "fieldGroupId": {{fieldGroupId}}, + "state": "created" + }, + { + "id": 4202, + "name": "Field name", + "type": "select", + "sortOrder": 1, + "fieldGroupId": {{fieldGroupId}}, + "options": [ + { + "id": 1, + "label": "Option 1", + "sortOrder": 0, + "state": "created" + }, + { + "id": 2, + "label": "Option 2", + "sortOrder": 1, + "state": "created" + } + ], + "state": "created" + } + ], + "featureIds": [1, 2, 3], + "taskSettingsActiveFields": ["planned_time", "board_name", "start_date", "end_date", "description", "subtasks"], + "linkedEntityTypes": [ + { + "sortOrder": 0, + "targetEntityTypeId": "{{entityTypeId}}" + } + ], + "linkedEntityCategories": [{ + "entityCategory": "contact", + "entityTypeName": "Partner contact", + "sectionName": "Partner contacts" + }, { + "entityCategory": "company", + "entityTypeName": "Partner company", + "sectionName": "Partner companies" + }], + "section": { + "name": "Objects", + "view": "board", + "icon": "crown" + } +} diff --git a/backend/artifacts/crm.http b/backend/artifacts/crm.http new file mode 100644 index 0000000..60de027 --- /dev/null +++ b/backend/artifacts/crm.http @@ -0,0 +1,10 @@ +### Delete user and change responsible + +DELETE {{url}}/api/crm/user/delete +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "currentUserId": 12022001, + "newUserId": 12022002 +} \ No newline at end of file diff --git a/backend/artifacts/demo.http b/backend/artifacts/demo.http new file mode 100644 index 0000000..e87cce0 --- /dev/null +++ b/backend/artifacts/demo.http @@ -0,0 +1,5 @@ +### Create demo massive data + +POST {{url}}/api/crm/demo-massive-data +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/entity-type.http b/backend/artifacts/entity-type.http new file mode 100644 index 0000000..bd9fa8a --- /dev/null +++ b/backend/artifacts/entity-type.http @@ -0,0 +1,76 @@ +### Get entity type + +GET {{url}}/api/crm/entity-types/{{entityTypeId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get entity types +GET {{url}}/api/crm/entity-types +Content-Type: application/json +Authorization: Bearer {{token}} + +### Delete entity type + +DELETE {{url}}/api/crm/entity-types/321 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Update entity type + +PUT {{url}}/api/crm/entity-types/{{entityTypeId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "id": {{entityTypeId}}, + "name": "Deal", + "cardView": "fields_and_notes", + "fieldGroups": [ + { + "id": {{fieldGroupId}}, + "name": "Details", + "sortOrder": 0, + "state": "created" + } + ], + "fields": [ + { + "id": 4201, + "name": "Field name", + "type": "text", + "sortOrder": 1, + "fieldGroupId": {{fieldGroupId}}, + "state": "updated" + }, + { + "id": 4202, + "name": "Field name", + "type": "select", + "sortOrder": 1, + "fieldGroupId": {{fieldGroupId}}, + "options": [ + { + "id": 1, + "label": "Option 1", + "sortOrder": 0, + "state": "created" + }, + { + "id": 2, + "label": "Option 2", + "sortOrder": 1, + "state": "created" + } + ], + "state": "created" + } + ], + "featureIds": [1, 2, 3], + "taskSettingsActiveFields": ["planned_time", "board_name", "start_date", "end_date", "description", "subtasks"], + "linkedEntityTypes": [], + "section": { + "name": "Deals", + "view": "board", + "icon": "crown" + } +} diff --git a/backend/artifacts/entity.http b/backend/artifacts/entity.http new file mode 100644 index 0000000..fa96ea5 --- /dev/null +++ b/backend/artifacts/entity.http @@ -0,0 +1,108 @@ +### Get entity + +GET {{url}}/api/crm/entities/{{entityId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create entity + +POST {{url}}/api/crm/entities +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "id": {{$randomInt}}, + "name": "Tesla", + "responsibleUserId": {{userId}}, + "entityTypeId": {{entityTypeId}}, + "stageId": {{stageId}}, + "fieldValues": [ + { + "id": {{$randomInt}}, + "fieldId": {{budgetFieldId}}, + "fieldType": "number", + "payload": { + "value": 100000 + }, + "state": "created" + } + ], + "entityLinks": [ + { + "id": {{$randomInt}}, + "sourceId": {{entityId}}, + "targetId": {{elonContactEntityId}}, + "sortOrder": 0, + "state": "created" + } + ] +} + +### Update entity + +PUT {{url}}/api/crm/entities/{{entityId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "id": {{entityId}}, + "name": "Tesla", + "responsibleUserId": {{userId}}, + "entityTypeId": {{entityTypeId}}, + "stageId": {{stageId}}, + "fieldValues": [ + { + "fieldId": {{budgetFieldId}}, + "fieldType": "number", + "payload": { + "value": 10000 + }, + "state": "updated" + } + ], + "entityLinks": [ + { + "id": {{$randomInt}}, + "sourceId": {{entityId}}, + "targetId": {{elonContactEntityId}}, + "sortOrder": 0, + "state": "created" + } + ] +} + +### Update entity stage + +PUT {{url}}/api/crm/entities/{{entityId}}/stage/{{stageId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Delete entity + +DELETE {{url}}/api/crm/entities/321 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get entities for board + +GET {{url}}/api/crm/entities/{{entityTypeId}}/{{boardId}}/cards +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get entities list items + +GET {{url}}/api/crm/entities/{{entityTypeId}}/list-items +Content-Type: application/json +Authorization: Bearer {{token}} + +### Search entities in name, all fields and linked entities + +GET {{url}}/api/crm/entities/{{entityTypeId}}/search?value=book +Content-Type: application/json +Authorization: Bearer {{token}} + +### Search entities by name or field value + +GET {{url}}/api/crm/entities/{{entityTypeId}}/search/fields?name=book&field[42022001]=4&field[42022003]=com +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/extension.http b/backend/artifacts/extension.http new file mode 100644 index 0000000..7f4da00 --- /dev/null +++ b/backend/artifacts/extension.http @@ -0,0 +1,15 @@ +### Create external link + +POST {{url}}/api/extension/external-link +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "url": "https://salesforce.com", + "name": "Tesla", + "responsibleUserId": {{userId}}, + "entityTypeId": {{entityTypeId}}, + "stageId": {{stageId}}, + "fieldValues": [], + "entityLinks": [] +} \ No newline at end of file diff --git a/backend/artifacts/feed-items.http b/backend/artifacts/feed-items.http new file mode 100644 index 0000000..27d8c35 --- /dev/null +++ b/backend/artifacts/feed-items.http @@ -0,0 +1,5 @@ +### Get feed items for entity with paging + +GET {{url}}/api/crm/feed-items?entityId={{entityId}}&offset=0&limit=10 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/feedback.http b/backend/artifacts/feedback.http new file mode 100644 index 0000000..28d29fc --- /dev/null +++ b/backend/artifacts/feedback.http @@ -0,0 +1,17 @@ +### Send feedback + +POST {{url}}/api/mailing/feedback +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "type": "trial_expired", + "payload": { + "name": "John Dow", + "phone": "+1234567890", + "email": "john.dow@company.com", + "userNumber": 10, + "subscribe": "yearly", + "plan": "basic" + } +} \ No newline at end of file diff --git a/backend/artifacts/field-settings.http b/backend/artifacts/field-settings.http new file mode 100644 index 0000000..16e9769 --- /dev/null +++ b/backend/artifacts/field-settings.http @@ -0,0 +1,18 @@ +### Get fields settings + +GET {{url}}/api/crm/entity-types/13022735/fields-settings +Content-Type: application/json +Authorization: Bearer {{token}} + +### Update fields settings + +PUT {{url}}/api/crm/entity-types/13022735/fields-settings +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "activeFieldCodes": [ + "participants", + "description" + ] +} \ No newline at end of file diff --git a/backend/artifacts/field-value.http b/backend/artifacts/field-value.http new file mode 100644 index 0000000..ad483e9 --- /dev/null +++ b/backend/artifacts/field-value.http @@ -0,0 +1,14 @@ +### Save field value + +POST {{url}}/api/crm/entities/{{entityId}}/field-values/{{budgetFieldId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "fieldId": {{budgetFieldId}}, + "fieldType": "value", + "payload": { + "value": 500 + }, + "state": "updated" +} \ No newline at end of file diff --git a/backend/artifacts/fields.http b/backend/artifacts/fields.http new file mode 100644 index 0000000..9a89893 --- /dev/null +++ b/backend/artifacts/fields.http @@ -0,0 +1,48 @@ +### Update entity type fields and groups + +PUT {{url}}/api/crm/entity-types/{{entityTypeId}}/fields-and-groups +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "fieldGroups": [ + { + "id": {{fieldGroupId}}, + "name": "Details", + "sortOrder": 0, + "state": "created" + } + ], + "fields": [ + { + "id": 4201, + "name": "Field name", + "type": "text", + "sortOrder": 1, + "fieldGroupId": {{fieldGroupId}}, + "state": "updated" + }, + { + "id": 4202, + "name": "Field name", + "type": "select", + "sortOrder": 1, + "fieldGroupId": {{fieldGroupId}}, + "options": [ + { + "id": 1, + "label": "Option 1", + "sortOrder": 0, + "state": "created" + }, + { + "id": 2, + "label": "Option 2", + "sortOrder": 1, + "state": "created" + } + ], + "state": "created" + } + ] +} \ No newline at end of file diff --git a/backend/artifacts/file-link.http b/backend/artifacts/file-link.http new file mode 100644 index 0000000..9a8420f --- /dev/null +++ b/backend/artifacts/file-link.http @@ -0,0 +1,5 @@ +### Delete file + +DELETE {{url}}/api/crm/file-link/1 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/forms.http b/backend/artifacts/forms.http new file mode 100644 index 0000000..8b551d7 --- /dev/null +++ b/backend/artifacts/forms.http @@ -0,0 +1,31 @@ +### Send contact us form + +POST {{url}}/api/forms/contact-us +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "test 10", + "phone": "+7999888776633", + "email": "test@test.com", + "comment": "some comment" +} + +### Send partner form + +POST {{url}}/api/forms/partner +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "firstName": "John", + "lastName": "Doe", + "phone": "+7999888776633", + "email": "test@test.com", + "company": "Tesla", + "country": "USA", + "website": "tesla.com", + "employees": "10000", + "comment": "some comment" +} + diff --git a/backend/artifacts/identity.http b/backend/artifacts/identity.http new file mode 100644 index 0000000..2296d03 --- /dev/null +++ b/backend/artifacts/identity.http @@ -0,0 +1,17 @@ +### Get next identity for sequence + +GET {{url}}/api/crm/identity/board_id_seq +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get all identities pool + +GET {{url}}/api/crm/identities/all +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get identities pool + +GET {{url}}/api/crm/identities/feed_item_id_seq +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/import.http b/backend/artifacts/import.http new file mode 100644 index 0000000..b79ea88 --- /dev/null +++ b/backend/artifacts/import.http @@ -0,0 +1,5 @@ +### Get import template for entityType + +GET {{url}}/api/crm/entity-types/{{entityTypeId}}/template +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/mail-messages.http b/backend/artifacts/mail-messages.http new file mode 100644 index 0000000..789d024 --- /dev/null +++ b/backend/artifacts/mail-messages.http @@ -0,0 +1,30 @@ +### Get mails for mailbox + +GET {{url}}/api/mailing/mailboxes/{{mailboxId}}/messages?folderId={{folderId}}&offset=0&limit=50 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mails for section + +GET {{url}}/api/mailing/section/inbox/messages?mailboxId={{mailboxId}}&offset=0&limit=50 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mail thread + +GET {{url}}/api/mailing/mailboxes/{{mailboxId}}/threads/1234567890 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mail message + +GET {{url}}/api/mailing/mailboxes/{{mailboxId}}/messages/{{messageId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mail attachment + +GET {{url}}'/api/mailing/mailboxes/{{mailboxId}}/messages/{{messageId}}/attachments/{{payloadId}} +Content-Type: application/json +Authorization: Bearer {{token}} + diff --git a/backend/artifacts/mailbox-settings.http b/backend/artifacts/mailbox-settings.http new file mode 100644 index 0000000..b7703e1 --- /dev/null +++ b/backend/artifacts/mailbox-settings.http @@ -0,0 +1,76 @@ +### Create mailbox + +POST {{url}}/api/mailing/settings/mailboxes +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "email": "test@company.com" +} + + +### Update mailbox + +PUT {{url}}/api/mailing/settings/mailboxes/{{mailboxId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "email": "test2@company.com", + "ownerId": {{userid}}, + "accessibleUserIds": [{{userid}}], + "createContact": true, + "syncDays": 7 +} + +### Delete mailbox + +DELETE {{url}}/api/mailing/settings/mailboxes/{{mailboxId}}?save=true +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mailboxes for settings + +GET {{url}}/api/mailing/settings/mailboxes +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mailbox for settings + +GET {{url}}/api/mailing/settings/mailboxes/{{mailboxId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get mailbox manual settings + +GET {{url}}/api/mailing/settings/mailboxes/{{mailboxId}}/manual +Content-Type: application/json +Authorization: Bearer {{token}} + +### Update mailbox manual settings + +POST {{url}}/api/mailing/settings/mailboxes/{{mailboxId}}/manual +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "password": "12345678", + "imapServer": "imap.company.com", + "imapPort": 993, + "imapSecure": true, + "smtpServer": "smtp.company.com", + "smtpPort": 465, + "smtpSecure": true +} + +### Get Gmail connection string for redirect + +GET {{url}}/api/mailing/settings/mailboxes/gmail/connect/{{mailboxId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Gmail callback endpoint + +GET {{url}}/api/mailing/settings/mailboxes/gmail/callback?state={{mailboxId}}&code=1234567890 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/mailbox.http b/backend/artifacts/mailbox.http new file mode 100644 index 0000000..bb5d8e2 --- /dev/null +++ b/backend/artifacts/mailbox.http @@ -0,0 +1,5 @@ +### Get mailboxes + +GET {{url}}/api/mailing/mailboxes +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/migration.http b/backend/artifacts/migration.http new file mode 100644 index 0000000..9259ed4 --- /dev/null +++ b/backend/artifacts/migration.http @@ -0,0 +1,5 @@ +### Run migration + +POST {{url}}/api/crm/run-migration +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/module/module.http b/backend/artifacts/module/module.http new file mode 100644 index 0000000..be0715e --- /dev/null +++ b/backend/artifacts/module/module.http @@ -0,0 +1,17 @@ +### Get modules + +GET {{url}}/api/crm/modules +Content-Type: application/json +Authorization: Bearer {{token}} + +### Activate module + +POST {{url}}/api/crm/modules/{{moduleId}}/activate +Content-Type: application/json +Authorization: Bearer {{token}} + +### Deactivate module + +POST {{url}}/api/crm/modules/{{moduleId}}/deactivate +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/note.http b/backend/artifacts/note.http new file mode 100644 index 0000000..8fbcda1 --- /dev/null +++ b/backend/artifacts/note.http @@ -0,0 +1,27 @@ +### Create note + +POST {{url}}/api/crm/notes +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "entityId": {{entityId}}, + "text": "Hello world!!!" +} + +### Update note + +PUT {{url}}/api/crm/notes/{{noteId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "entityId": {{entityId}}, + "text": "Updated note" +} + +### Delete note + +DELETE {{url}}/api/crm/notes/321 +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/notification.http b/backend/artifacts/notification.http new file mode 100644 index 0000000..8330591 --- /dev/null +++ b/backend/artifacts/notification.http @@ -0,0 +1,23 @@ +### Get notifications + +GET {{url}}/api/notifications +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get unseen count + +GET {{url}}/api/notifications/unseen-count +Content-Type: application/json +Authorization: Bearer {{token}} + +### Mark seen all + +PUT {{url}}/api/notifications/seen +Content-Type: application/json +Authorization: Bearer {{token}} + +### Mark seen notification + +PUT {{url}}/api/notifications/{{notificationId}}/seen +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/partner.http b/backend/artifacts/partner.http new file mode 100644 index 0000000..ee455d4 --- /dev/null +++ b/backend/artifacts/partner.http @@ -0,0 +1,11 @@ +### Get partner summary + +GET {{url}}/api/partners/{{partnerId}}/summary +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get partner leads + +GET {{url}}/api/partners/{{partnerId}}/leads +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/products/order-status.http b/backend/artifacts/products/order-status.http new file mode 100644 index 0000000..e11a7e9 --- /dev/null +++ b/backend/artifacts/products/order-status.http @@ -0,0 +1,5 @@ +### Get order statuses + +GET {{url}}/api/products/order-statuses +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/products/order.http b/backend/artifacts/products/order.http new file mode 100644 index 0000000..cc97ec8 --- /dev/null +++ b/backend/artifacts/products/order.http @@ -0,0 +1,92 @@ +### Get entity order + +GET {{url}}/api/products/orders/entity/{{entityId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get entity order products count + +GET {{url}}/api/products/orders/entity/{{entityId}}/products-count +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create order + +POST {{url}}/api/products/orders +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "entityId": {{entityId}}, + "currency": "USD", + "taxIncluded": true, + "statusId": {{orderStatusId}}, + "warehouseId": {{warehouseId}}, + "items": [ + { + "productId": {{productId}}, + "quantity": 5, + "unitPrice": 100, + "tax": 10, + "discount": 0, + "sortOrder": 0, + "reservations": [ + { + "warehouseId": {{warehouseId}}, + "quantity": 5 + }, + { + "warehouseId": {{secondWarehouseId}}, + "quantity": 10 + } + ] + } + ] +} + +### Update order + +PUT {{url}}/api/products/orders/{{orderId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "entityId": {{entityId}}, + "currency": "USD", + "taxIncluded": true, + "statusId": {{orderStatusId}}, + "warehouseId": {{secondWarehouseId}}, + "items": [ + { + "id": 14, + "productId": {{productId}}, + "quantity": 10, + "unitPrice": 200, + "tax": 10, + "discount": 30, + "sortOrder": 2, + "reservations": [ + { + "warehouseId": {{warehouseId}}, + "quantity": 5 + } + ] + }, + { + "id": -1, + "productId": {{productId}}, + "quantity": 20, + "unitPrice": 100, + "tax": 20, + "discount": 30, + "sortOrder": 3, + "reservations": [] + } + ] +} + +### Change order status + +PUT {{url}}/api/products/orders/{{orderId}}/status/{{orderStatusId}} +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/products/product-category.http b/backend/artifacts/products/product-category.http new file mode 100644 index 0000000..015bf9b --- /dev/null +++ b/backend/artifacts/products/product-category.http @@ -0,0 +1,32 @@ +### Get categories + +GET {{url}}/api/products/categories +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create category + +POST {{url}}/api/products/categories +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Category 1", + "parentId": null +} + +### Update category + +PUT {{url}}/api/products/categories/{{productCategoryId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Category new name" +} + +### Delete category + +DELETE {{url}}/api/products/categories/123 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/products/product-price.http b/backend/artifacts/products/product-price.http new file mode 100644 index 0000000..20344f7 --- /dev/null +++ b/backend/artifacts/products/product-price.http @@ -0,0 +1,29 @@ +### Create product price + +POST {{url}}/api/products/{{productId}}/prices +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": null, + "unitPrice": 100, + "currency": "EUR" +} + +### Update product price + +PUT {{url}}/api/products/{{productId}}/prices/{{productPriceId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Updated price", + "unitPrice": 200, + "currency": "USD" +} + +### Delete product price + +DELETE {{url}}/api/products/{{productId}}/prices/67 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/products/product.http b/backend/artifacts/products/product.http new file mode 100644 index 0000000..32fc616 --- /dev/null +++ b/backend/artifacts/products/product.http @@ -0,0 +1,86 @@ +### Get products + +GET {{url}}/api/products?categoryId={{productCategoryId}}&search=some&offset=0&limit=10 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get products by ids + +GET {{url}}/api/products?ids=1,2,3 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get product + +GET {{url}}/api/products/{{productId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create product + +POST {{url}}/api/products +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Some product", + "type": "service", + "description": "Some description", + "sku": "some-product", + "unit": "some-unit", + "tax": 10, + "categoryId": {{productCategoryId}}, + "prices": [ + { + "name": "Price in USD", + "unitPrice": 100, + "currency": "USD" + }, + { + "name": "Price in KZT", + "unitPrice": 200, + "currency": "KZT" + } + ], + "photoFileIds": ["c7d48430-a2b7-4ea5-96a2-58bc40d90574"], + "stocks": [ + { + "warehouseId": {{warehouseId}}, + "stockQuantity": 100 + } + ] +} + +### Update product + +PUT {{url}}/api/products/{{productId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "New product name", + "description": "New description", + "sku": "new-product", + "unit": "new-unit", + "tax": 20, + "categoryId": {{productCategoryId}} +} + +### Delete product + +DELETE {{url}}/api/products/123 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Upload product photos + +POST {{url}}/api/products/{{productId}}/photos +Authorization: Bearer {{token}} +Content-Type: multipart/form-data; boundary="abcd" + +--abcd +Content-Disposition: form-data; name="test"; filename="test.png" +Content-Type: image/png + +< ../_resources/test.png +--abcd-- diff --git a/backend/artifacts/products/shipment-status.http b/backend/artifacts/products/shipment-status.http new file mode 100644 index 0000000..c386449 --- /dev/null +++ b/backend/artifacts/products/shipment-status.http @@ -0,0 +1,5 @@ +### Get shipment statuses + +GET {{url}}/api/products/shipment-statuses +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/products/shipment.http b/backend/artifacts/products/shipment.http new file mode 100644 index 0000000..f124f80 --- /dev/null +++ b/backend/artifacts/products/shipment.http @@ -0,0 +1,17 @@ +### Get shipments + +GET {{url}}/api/products/shipments?offset=0&limit=10 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get shipment by id + +GET {{url}}/api/products/shipments/{{shipmentId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Change shipment status + +PUT {{url}}/api/products/shipments/{{shipmentId}}/status/{{shipmentStatusId}} +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/products/stock.http b/backend/artifacts/products/stock.http new file mode 100644 index 0000000..677dc74 --- /dev/null +++ b/backend/artifacts/products/stock.http @@ -0,0 +1,18 @@ +### Update product stocks + +PUT {{url}}/api/products/{{productId}}/stocks +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "stocks": [ + { + "warehouseId": {{warehouseId}}, + "stockQuantity": 100 + }, + { + "warehouseId": {{secondWarehouseId}}, + "stockQuantity": 200 + } + ] +} \ No newline at end of file diff --git a/backend/artifacts/products/warehouse.http b/backend/artifacts/products/warehouse.http new file mode 100644 index 0000000..019da49 --- /dev/null +++ b/backend/artifacts/products/warehouse.http @@ -0,0 +1,31 @@ +### Get warehouses + +GET {{url}}/api/products/warehouses +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create warehouse + +POST {{url}}/api/products/warehouses +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Some warehouse" +} + +### Update warehouse + +PUT {{url}}/api/products/warehouses/{{warehouseId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "New warehouse name" +} + +### Delete warehouse + +DELETE {{url}}/api/products/warehouses/123 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/rabbit.http b/backend/artifacts/rabbit.http new file mode 100644 index 0000000..389eca1 --- /dev/null +++ b/backend/artifacts/rabbit.http @@ -0,0 +1,5 @@ +### Send event to rabbit + +POST {{url}}/api/rabbit +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/rms.http b/backend/artifacts/rms.http new file mode 100644 index 0000000..f9e5691 --- /dev/null +++ b/backend/artifacts/rms.http @@ -0,0 +1,10 @@ +### Get industries + +GET {{url}}/api/crm/rms/industries +Content-Type: application/json + +### Delete demo data + +DELETE {{url}}/api/crm/rms/demo-data +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/stage.http b/backend/artifacts/stage.http new file mode 100644 index 0000000..5c6f461 --- /dev/null +++ b/backend/artifacts/stage.http @@ -0,0 +1,47 @@ +### Get all stages or by boardId + +GET {{url}}/api/crm/stages +Content-Type: application/json +Authorization: Bearer {{token}} + +### Save stages batch + +POST {{url}}/api/crm/stages/batch +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "stages": [ + { + "id": "172", + "name": "Stage 1", + "color": "#fff", + "code": null, + "isSystem": false, + "sortOrder": 1, + "boardId": {{boardId}}, + "state": "unchanged" + }, + { + "id": "298", + "name": "Stage 2", + "color": "#fff", + "code": null, + "isSystem": false, + "sortOrder": 2, + "boardId": {{boardId}}, + "state": "updated" + }, + { + "id": "{{$randomInt}}", + "name": "Stage 4", + "color": "#fff", + "code": null, + "isSystem": false, + "sortOrder": 1, + "boardId": {{boardId}}, + "state": "added" + } + ] +} + diff --git a/backend/artifacts/storage/file-link.http b/backend/artifacts/storage/file-link.http new file mode 100644 index 0000000..4b94f10 --- /dev/null +++ b/backend/artifacts/storage/file-link.http @@ -0,0 +1,12 @@ +### Delete file link by id + +DELETE {{url}}/api/crm/file-links/123 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Delete file links by ids + +DELETE {{url}}/api/crm/file-links?ids=72,73 +Content-Type: application/json +Authorization: Bearer {{token}} + diff --git a/backend/artifacts/storage/file.http b/backend/artifacts/storage/file.http new file mode 100644 index 0000000..5aa4486 --- /dev/null +++ b/backend/artifacts/storage/file.http @@ -0,0 +1,24 @@ +### Upload file + +POST {{url}}/api/storage/upload +Authorization: Bearer {{token}} +Content-Type: multipart/form-data; boundary="abcd" + +--abcd +Content-Disposition: form-data; name="test"; filename="test.png" +Content-Type: image/png + +< ../_resources/test.png +--abcd-- + +### Delete file + +DELETE {{url}}/api/storage/file/{{fileId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get file + +GET {{url}}/api/storage/file/{{fileId}} +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/subtasks.http b/backend/artifacts/subtasks.http new file mode 100644 index 0000000..dc64317 --- /dev/null +++ b/backend/artifacts/subtasks.http @@ -0,0 +1,27 @@ +### Create subtask + +POST {{url}}/api/crm/tasks/{{taskId}}/subtasks +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "text": "new subtask", + "resolved": false +} + +### Update subtask + +PUT {{url}}/api/crm/tasks/{{taskId}}/subtasks/{{subtaskId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "text": "updated subtask", + "resolved": false +} + +### Delete subtask + +DELETE {{url}}/api/crm/tasks/{{taskId}}/subtasks/46022010 +Content-Type: application/json +Authorization: Bearer {{token}} diff --git a/backend/artifacts/task-comment-like.http b/backend/artifacts/task-comment-like.http new file mode 100644 index 0000000..8c2a213 --- /dev/null +++ b/backend/artifacts/task-comment-like.http @@ -0,0 +1,11 @@ +### Like task comment + +POST {{url}}/api/crm/tasks/{{taskId}}/comments/{{taskCommentId}}/like +Content-Type: application/json +Authorization: Bearer {{token}} + +### Unlike task comment + +POST {{url}}/api/crm/tasks/{{taskId}}/comments/{{taskCommentId}}/unlike +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/task-comment.http b/backend/artifacts/task-comment.http new file mode 100644 index 0000000..2f76dfc --- /dev/null +++ b/backend/artifacts/task-comment.http @@ -0,0 +1,33 @@ +### Get task comments + +GET {{url}}/api/crm/tasks/{{taskId}}/comments?offset=0&limit=10 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create task comment + +POST {{url}}/api/crm/tasks/{{taskId}}/comments +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "text": "Hello world!!!", + "fileIds": [] +} + +### Update task comment + +PUT {{url}}/api/crm/tasks/{{taskId}}/comments/{{taskCommentId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "text": "New text" +} + +### Delete task comment + +DELETE {{url}}/api/crm/tasks/{{taskId}}/comments/47022018 +Content-Type: application/json +Authorization: Bearer {{token}} + diff --git a/backend/artifacts/task-settings.http b/backend/artifacts/task-settings.http new file mode 100644 index 0000000..9d76149 --- /dev/null +++ b/backend/artifacts/task-settings.http @@ -0,0 +1,39 @@ +### Get task settings + +GET {{url}}/api/crm/task-settings +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create task settings + +POST {{url}}/api/crm/task-settings +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "type": "task_board", + "recordId": 123, + "activeFields": [ + "planned_time", + "board_name", + "start_date", + "end_date" + ] +} + +### Update task settings + +PUT {{url}}/api/crm/task-settings/{{taskSettingsId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "activeFields": [ + "planned_time", + "board_name", + "start_date", + "end_date", + "description", + "subtasks" + ] +} \ No newline at end of file diff --git a/backend/artifacts/task-type.http b/backend/artifacts/task-type.http new file mode 100644 index 0000000..821687d --- /dev/null +++ b/backend/artifacts/task-type.http @@ -0,0 +1,21 @@ +### Get task types + +GET {{url}}/api/crm/task-types +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create task type + +POST {{url}}/api/crm/task-types +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "name": "Call" +} + +### Delete task type + +DELETE {{url}}/api/crm/task-types/321 +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/artifacts/task.http b/backend/artifacts/task.http new file mode 100644 index 0000000..721ee6f --- /dev/null +++ b/backend/artifacts/task.http @@ -0,0 +1,134 @@ +### Get task by id + +GET {{url}}/api/crm/tasks/{{taskId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create task + +POST {{url}}/api/crm/tasks +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "responsibleUserId": {{userId}}, + "startDate": "2022-11-21T17:37:03", + "endDate": "2022-11-22T17:37:03", + "text": "Task with start and end time", + "isResolved": false, + "result": null, + "entityId": {{entityId}}, + "title": "Task 1", + "plannedTime": 3600, + "stageId": {{taskStageId}}, + "settingsId": {{taskSettingsId}}, + "subtasks": [ + {"text": "subtask 1", "resolved": true}, + {"text": "subtask 2", "resolved": false} + ] +} + +### Update task + +PUT {{url}}/api/crm/tasks/{{taskId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "responsibleUserId": {{userId}}, + "startDate": "2022-11-23T17:37:03", + "endDate": "2022-11-24T17:37:03", + "text": "Updated Task 1 with start and end time", + "isResolved": true, + "result": "Result", + "entityId": {{entityId}}, + "title": "Updated Task 1" +} + +### Delete task + +DELETE {{url}}/api/crm/tasks/321 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Create activity + +POST {{url}}/api/crm/activities +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "responsibleUserId": {{userId}}, + "startDate": "2022-11-21T17:37:03", + "endDate": "2022-11-22T17:37:03", + "text": "Activity with start and end time", + "isResolved": false, + "result": null, + "entityId": {{entityId}}, + "activityTypeId": {{activityTypeId}} +} + +### Update activity + +PUT {{url}}/api/crm/activities/{{activityId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "responsibleUserId": {{userId}}, + "startDate": "2022-11-23T17:37:03", + "endDate": "2022-11-24T17:37:03", + "text": "Updated Activity 1 with start and end time", + "isResolved": true, + "result": "Result", + "entityId": {{entityId}}, + "activityTypeId": {{activityTypeId}} +} + +### Delete activity + +DELETE {{url}}/api/crm/activities/321 +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get activities + +GET {{url}}/api/crm/activities/cards?responsibleUserId={{userId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get tasks by time + +POST {{url}}/api/crm/tasks/by_time +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get time board meta + +POST {{url}}/api/crm/tasks/by_time/meta +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "stageIds": [{{taskStageId}}] +} + +### Get tasks by board + +POST {{url}}/api/crm/tasks/boards/{{taskBoardId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "ownerIds": [{{userId}}] +} + +### Get tasks board meta + +POST {{url}}/api/crm/tasks/boards/{{taskBoardId}}/meta +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "stageIds": [{{taskStageId}}] +} diff --git a/backend/artifacts/user.http b/backend/artifacts/user.http new file mode 100644 index 0000000..f980c1a --- /dev/null +++ b/backend/artifacts/user.http @@ -0,0 +1,73 @@ +### Get user by id + +GET {{url}}/api/users/{{userId}} +Content-Type: application/json +Authorization: Bearer {{token}} + + +### Create user + +POST {{url}}/api/settings/users +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "firstName": "Genadiy", + "lastName": "Genadiyev", + "email": "genadiy.genadiyev@test1.amwork.com", + "password": "123", + "phone": "+79998887766", + "role": "user" +} + +### Update user + +PUT {{url}}/api/settings/users/{{userId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "firstName": "Genadiy!", + "lastName": "Genadiyev!", + "email": "genadiy.genadiyev@test1.amwork.com", + "password": "123", + "phone": "+79998887766", + "role": "admin" +} + +### Delete activity + +DELETE {{url}}/api/settings/users/{{userId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +### Get user list + +GET {{url}}/api/settings/users +Content-Type: application/json +Authorization: Bearer {{token}} + + +### Get user by id + +GET {{url}}/api/settings/users/{{userId}} +Content-Type: application/json +Authorization: Bearer {{token}} + + +### Update user profile + +PUT {{url}}/api/user/{{userId}}/profile +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "birthDate": "1996-11-25T14:36:45", + "phone": "+79998887766" +} + +### Get user profile by user id + +GET {{url}}/api/user/{{userId}}/profile +Content-Type: application/json +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/backend/doc/camunda.md b/backend/doc/camunda.md new file mode 100644 index 0000000..62fdb04 --- /dev/null +++ b/backend/doc/camunda.md @@ -0,0 +1,32 @@ +# camunda platform + +## configuration + +github: https://github.com/camunda/camunda-platform +vps: **/home/devops/lvluu/camunda-platform/docker-compose/camunda-8.6** + +## start all services + +please note that you HAVE to cd to `/home/devops/lvluu/camunda-platform/docker-compose/camunda-8.6` before executing any command with docker compose: + +```bash +docker compose --profile full up -d +``` + +## stop all services + +```bash +docker compose --profile full down -d +``` + +## restart a container + +you can press tab to auto suggest the [service_name] + +```bash +docker compose --profile full restart [service_name] +``` + +## change version of a service + +edit the `/home/devops/lvluu/camunda-platform/docker-compose/camunda-8.6/.env` \ No newline at end of file diff --git a/backend/doc/commands.md b/backend/doc/commands.md new file mode 100644 index 0000000..25de3a0 --- /dev/null +++ b/backend/doc/commands.md @@ -0,0 +1,11 @@ +### Create migration + +```bash +yarn typeorm:create-migration AddAccount +``` + +### Run migrations + +```bash +yarn typeorm:run-migrations +``` \ No newline at end of file diff --git a/backend/doc/setup-local.md b/backend/doc/setup-local.md new file mode 100644 index 0000000..2326c12 --- /dev/null +++ b/backend/doc/setup-local.md @@ -0,0 +1,56 @@ +# Setup local development environment + +## Start docker services + +``` +docker-compose up -d +``` + +## Setup database + +- Add new postgres connection in PHP Storm + +If you have different psql version on your host, you can manually restore dump inside container: +- Put dump to `./dumps` directory +- Connect to postgres container +- Restore database: `psql -h localhost --set ON_ERROR_STOP=on -U root -d rifeberry -1 -f /dumps/rifeberry.sql` + +## Connect to node service and run other commands inside container + +``` +docker exec -it nest-backend_node_1 sh +``` + +### Install packages + +``` +yarn +``` + +### Generate JWT keys if not exists + +``` +openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem +``` + +### Migrate database + +``` +yarn typeorm:run-migrations +``` + +### Add host to /etc/hosts + +``` +127.0.0.1 test.rifeberry.loc +``` + +### Setup node debug + +[Настройка node debug](https://olivergrand.atlassian.net/wiki/spaces/BACK/pages/14319617/Node+debug) + +### Send test request to test.rifeberry.loc/api + +### Optional: Setup wildcard subdomains using dnsmasq + +https://askubuntu.com/questions/1029882/how-can-i-set-up-local-wildcard-127-0-0-1-domain-resolution-on-18-04-20-04 \ No newline at end of file diff --git a/backend/eslint.config.js b/backend/eslint.config.js new file mode 100644 index 0000000..679b48e --- /dev/null +++ b/backend/eslint.config.js @@ -0,0 +1,35 @@ +const eslint = require('@eslint/js'); +const tseslint = require('typescript-eslint'); +const eslintPluginPrettierRecommended = require('eslint-plugin-prettier/recommended'); +const globals = require('globals'); + +module.exports = tseslint.config( + { + files: ["**/*.ts"], + languageOptions: { + globals: { + ...globals.node, + 'Express': false, + 'BufferEncoding': false, + } + } + }, + { + ignores: ['eslint.config.js'], + }, + eslint.configs.recommended, + ...tseslint.configs.strict, + ...tseslint.configs.stylistic, + eslintPluginPrettierRecommended, + { + rules: { + '@typescript-eslint/no-extraneous-class': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/consistent-type-definitions': 'warn', + "no-undef": "error", + "no-global-assign": "error", + "no-new-object": "error", + 'max-len': ['warn', 120, 2], + }, + }, +); diff --git a/backend/knip.json b/backend/knip.json new file mode 100644 index 0000000..6482938 --- /dev/null +++ b/backend/knip.json @@ -0,0 +1,4 @@ +{ + "entry": ["src/main.ts"], + "project": ["src/**/*.ts"] +} \ No newline at end of file diff --git a/backend/nest-cli.json b/backend/nest-cli.json new file mode 100644 index 0000000..4f71b1e --- /dev/null +++ b/backend/nest-cli.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "assets": [ + { "include": "Mailing/system-mailing/templates/**/*.html", "outDir": "./dist", "watchAssets": true }, + { "include": "modules/**/templates/**", "outDir": "./dist", "watchAssets": true } + ] + } +} diff --git a/backend/newrelic.js b/backend/newrelic.js new file mode 100644 index 0000000..00f5fd9 --- /dev/null +++ b/backend/newrelic.js @@ -0,0 +1,53 @@ +'use strict'; +/** + * New Relic agent configuration. + * + * See lib/config/default.js in the agent distribution for a more complete + * description of configuration variables and their potential values. + */ +exports.config = { + /** + * Array of application names. + */ + app_name: [process.env.NEW_RELIC_APP_NAME], + /** + * Your New Relic license key. + */ + license_key: process.env.NEW_RELIC_LICENSE_KEY, + logging: { + /** + * Level at which to log. 'trace' is most useful to New Relic when diagnosing + * issues with the agent, 'info' and higher will impose the least overhead on + * production applications. + */ + level: process.env.NEW_RELIC_LOG_LEVEL, + }, + /** + * When true, all request headers except for those listed in attributes.exclude + * will be captured for all traces, unless otherwise specified in a destination's + * attributes include/exclude lists. + */ + allow_all_headers: true, + attributes: { + /** + * Prefix of attributes to exclude from all destinations. Allows * as wildcard + * at end. + * + * NOTE: If excluding headers, they must be in camelCase form to be filtered. + * + * @name NEW_RELIC_ATTRIBUTES_EXCLUDE + */ + exclude: [ + 'request.headers.cookie', + 'request.headers.authorization', + 'request.headers.proxyAuthorization', + 'request.headers.setCookie*', + 'request.headers.x*', + 'response.headers.cookie', + 'response.headers.authorization', + 'response.headers.proxyAuthorization', + 'response.headers.setCookie*', + 'response.headers.x*', + ], + }, +}; diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..a4e7968 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,18536 @@ +{ + "name": "amwork-backend", + "version": "3.14.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "amwork-backend", + "version": "3.14.1", + "license": "UNLICENSED", + "dependencies": { + "@amwork/voximplant-apiclient-nodejs": "^2.3.0-f", + "@aws-sdk/client-s3": "^3.817.0", + "@camunda8/sdk": "^8.7.9", + "@date-fns/tz": "^1.2.0", + "@date-fns/utc": "^2.1.0", + "@esm2cjs/cacheable-lookup": "^7.0.0", + "@faker-js/faker": "^9.8.0", + "@nestjs-modules/mailer": "2.0.2", + "@nestjs/axios": "^4.0.0", + "@nestjs/common": "^11.1.2", + "@nestjs/config": "^4.0.2", + "@nestjs/core": "^11.1.2", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/jwt": "^11.0.0", + "@nestjs/platform-express": "^11.1.2", + "@nestjs/platform-socket.io": "^11.1.2", + "@nestjs/schedule": "^6.0.0", + "@nestjs/swagger": "^11.2.0", + "@nestjs/terminus": "^11.0.0", + "@nestjs/typeorm": "^11.0.0", + "@nestjs/websockets": "^11.1.2", + "@newrelic/native-metrics": "^11.1.0", + "@voximplant/apiclient-nodejs": "^4.2.0", + "angular-expressions": "^1.4.3", + "axios": "^1.9.0", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "decimal.js": "^10.5.0", + "docxtemplater": "^3.63.2", + "dotenv": "^16.6.1", + "exceljs": "^4.4.0", + "express": "^5.1.0", + "form-data": "^4.0.2", + "generate-password": "^1.7.1", + "googleapis": "^149.0.0", + "handlebars": "^4.7.8", + "heapdump": "^0.3.15", + "html-to-text": "^9.0.5", + "iconv-lite": "^0.6.3", + "imap-simple": "^5.1.0", + "imapflow": "^1.0.187", + "ioredis": "^5.6.1", + "jsonwebtoken": "^9.0.2", + "libphonenumber-js": "^1.12.8", + "lvovich": "^2.0.2", + "mailparser": "^3.7.3", + "mathjs": "^14.5.1", + "mime-types": "^3.0.1", + "multer": "^2.0.0", + "nest-winston": "^1.10.2", + "newrelic": "^12.20.0", + "nodemailer": "^7.0.3", + "number-to-words-ru": "^2.4.1", + "path-to-regexp": "^8.2.0", + "pg": "^8.16.0", + "pizzip": "^3.2.0", + "qs": "^6.14.0", + "quoted-printable": "^1.0.1", + "reflect-metadata": "^0.2.2", + "rimraf": "^6.0.1", + "rxjs": "^7.8.2", + "sharp": "^0.34.2", + "slugify": "^1.6.6", + "socket.io": "^4.8.1", + "stripe": "^17.7.0", + "twilio": "^5.7.0", + "typeorm": "^0.3.24", + "typeorm-naming-strategies": "^4.1.0", + "uuid": "^11.1.0", + "winston": "^3.17.0", + "written-number": "^0.11.1" + }, + "devDependencies": { + "@eslint/js": "^9.27.0", + "@nestjs/cli": "^11.0.7", + "@nestjs/schematics": "^11.0.5", + "@swc/cli": "^0.7.7", + "@swc/core": "^1.11.29", + "@total-typescript/ts-reset": "^0.6.1", + "@types/bcrypt": "^5.0.2", + "@types/express": "^5.0.2", + "@types/form-data": "^2.5.2", + "@types/heapdump": "^0", + "@types/html-to-text": "^9.0.4", + "@types/imap-simple": "^4.2.10", + "@types/mailparser": "^3.4.6", + "@types/mime-types": "^2", + "@types/multer": "^1.4.12", + "@types/node": "^22.15.24", + "@types/nodemailer": "^6.4.17", + "@types/quoted-printable": "^1", + "@types/uuid": "^10.0.0", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.4.0", + "knip": "^5.59.1", + "prettier": "^3.5.3", + "ts-node": "^10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.33.0" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs": { + "version": "2.3.0-f", + "resolved": "https://registry.npmjs.org/@amwork/voximplant-apiclient-nodejs/-/voximplant-apiclient-nodejs-2.3.0-f.tgz", + "integrity": "sha512-tveFRjiAoox4fPed2kSyzVecgofueYuaUkZyB5w8AEGC3ooG4mzH0QNkLB8swNFRCEotY0RpvGF7VOIOwuznnQ==", + "dependencies": { + "axios": "0.21.4", + "form-data": "2.5.1", + "jsonwebtoken": "8.5.1" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "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" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/jws": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "dependencies": { + "jwa": "^1.4.2", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@amwork/voximplant-apiclient-nodejs/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", + "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.19", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", + "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.965.0.tgz", + "integrity": "sha512-BTeaaU1iK0BfatTCrtYjNkIHCoZH256qOI18l9bK4z6mVOgpHkYN4RvOu+NnKgyX58n+HWfOuhtKUD4OE33Vdw==", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.965.0", + "@aws-sdk/credential-provider-node": "3.965.0", + "@aws-sdk/middleware-bucket-endpoint": "3.965.0", + "@aws-sdk/middleware-expect-continue": "3.965.0", + "@aws-sdk/middleware-flexible-checksums": "3.965.0", + "@aws-sdk/middleware-host-header": "3.965.0", + "@aws-sdk/middleware-location-constraint": "3.965.0", + "@aws-sdk/middleware-logger": "3.965.0", + "@aws-sdk/middleware-recursion-detection": "3.965.0", + "@aws-sdk/middleware-sdk-s3": "3.965.0", + "@aws-sdk/middleware-ssec": "3.965.0", + "@aws-sdk/middleware-user-agent": "3.965.0", + "@aws-sdk/region-config-resolver": "3.965.0", + "@aws-sdk/signature-v4-multi-region": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-endpoints": "3.965.0", + "@aws-sdk/util-user-agent-browser": "3.965.0", + "@aws-sdk/util-user-agent-node": "3.965.0", + "@smithy/config-resolver": "^4.4.5", + "@smithy/core": "^3.20.0", + "@smithy/eventstream-serde-browser": "^4.2.7", + "@smithy/eventstream-serde-config-resolver": "^4.3.7", + "@smithy/eventstream-serde-node": "^4.2.7", + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/hash-blob-browser": "^4.2.8", + "@smithy/hash-node": "^4.2.7", + "@smithy/hash-stream-node": "^4.2.7", + "@smithy/invalid-dependency": "^4.2.7", + "@smithy/md5-js": "^4.2.7", + "@smithy/middleware-content-length": "^4.2.7", + "@smithy/middleware-endpoint": "^4.4.1", + "@smithy/middleware-retry": "^4.4.17", + "@smithy/middleware-serde": "^4.2.8", + "@smithy/middleware-stack": "^4.2.7", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.16", + "@smithy/util-defaults-mode-node": "^4.2.19", + "@smithy/util-endpoints": "^3.2.7", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-retry": "^4.2.7", + "@smithy/util-stream": "^4.5.8", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ses": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.965.0.tgz", + "integrity": "sha512-RZXJBoHA3I6Ts1/bjOLDceT0hbK00lVkXAXFpcz5At+p6Yu52jVmdAdKDmLuf1IgCDw7s2IsrR4Us2Od1AabCg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.965.0", + "@aws-sdk/credential-provider-node": "3.965.0", + "@aws-sdk/middleware-host-header": "3.965.0", + "@aws-sdk/middleware-logger": "3.965.0", + "@aws-sdk/middleware-recursion-detection": "3.965.0", + "@aws-sdk/middleware-user-agent": "3.965.0", + "@aws-sdk/region-config-resolver": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-endpoints": "3.965.0", + "@aws-sdk/util-user-agent-browser": "3.965.0", + "@aws-sdk/util-user-agent-node": "3.965.0", + "@smithy/config-resolver": "^4.4.5", + "@smithy/core": "^3.20.0", + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/hash-node": "^4.2.7", + "@smithy/invalid-dependency": "^4.2.7", + "@smithy/middleware-content-length": "^4.2.7", + "@smithy/middleware-endpoint": "^4.4.1", + "@smithy/middleware-retry": "^4.4.17", + "@smithy/middleware-serde": "^4.2.8", + "@smithy/middleware-stack": "^4.2.7", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.16", + "@smithy/util-defaults-mode-node": "^4.2.19", + "@smithy/util-endpoints": "^3.2.7", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-retry": "^4.2.7", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.965.0.tgz", + "integrity": "sha512-iv2tr+n4aZ+nPUFFvG00hISPuEd4DU+1/Q8rPAYKXsM+vEPJ2nAnP5duUOa2fbOLIUCRxX3dcQaQaghVHDHzQw==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.965.0", + "@aws-sdk/middleware-host-header": "3.965.0", + "@aws-sdk/middleware-logger": "3.965.0", + "@aws-sdk/middleware-recursion-detection": "3.965.0", + "@aws-sdk/middleware-user-agent": "3.965.0", + "@aws-sdk/region-config-resolver": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-endpoints": "3.965.0", + "@aws-sdk/util-user-agent-browser": "3.965.0", + "@aws-sdk/util-user-agent-node": "3.965.0", + "@smithy/config-resolver": "^4.4.5", + "@smithy/core": "^3.20.0", + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/hash-node": "^4.2.7", + "@smithy/invalid-dependency": "^4.2.7", + "@smithy/middleware-content-length": "^4.2.7", + "@smithy/middleware-endpoint": "^4.4.1", + "@smithy/middleware-retry": "^4.4.17", + "@smithy/middleware-serde": "^4.2.8", + "@smithy/middleware-stack": "^4.2.7", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.16", + "@smithy/util-defaults-mode-node": "^4.2.19", + "@smithy/util-endpoints": "^3.2.7", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-retry": "^4.2.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.965.0.tgz", + "integrity": "sha512-aq9BhQxdHit8UUJ9C0im9TtuKeK0pT6NXmNJxMTCFeStI7GG7ImIsSislg3BZTIifVg1P6VLdzMyz9de85iutQ==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@aws-sdk/xml-builder": "3.965.0", + "@smithy/core": "^3.20.0", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/signature-v4": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.965.0.tgz", + "integrity": "sha512-9FbIyJ/Zz1AdEIrb0+Pn7wRi+F/0Y566ooepg0hDyHUzRV3ZXKjOlu3wJH3YwTz2UkdwQmldfUos2yDJps7RyA==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.965.0.tgz", + "integrity": "sha512-mdGnaIjMxTIjsb70dEj3VsWPWpoq1V5MWzBSfJq2H8zgMBXjn6d5/qHP8HMf53l9PrsgqzMpXGv3Av549A2x1g==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/property-provider": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.965.0.tgz", + "integrity": "sha512-YuGQel9EgA/z25oeLM+GYYQS750+8AESvr7ZEmVnRPL0sg+K3DmGqdv+9gFjFd0UkLjTlC/jtbP2cuY6UcPiHQ==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/util-stream": "^4.5.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.965.0.tgz", + "integrity": "sha512-xRo72Prer5s0xYVSCxCymVIRSqrVlevK5cmU0GWq9yJtaBNpnx02jwdJg80t/Ni7pgbkQyFWRMcq38c1tc6M/w==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/credential-provider-env": "3.965.0", + "@aws-sdk/credential-provider-http": "3.965.0", + "@aws-sdk/credential-provider-login": "3.965.0", + "@aws-sdk/credential-provider-process": "3.965.0", + "@aws-sdk/credential-provider-sso": "3.965.0", + "@aws-sdk/credential-provider-web-identity": "3.965.0", + "@aws-sdk/nested-clients": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/credential-provider-imds": "^4.2.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.965.0.tgz", + "integrity": "sha512-43/H8Qku8LHyugbhLo8kjD+eauhybCeVkmrnvWl8bXNHJP7xi1jCdtBQJKKJqiIHZws4MOEwkji8kFdAVRCe6g==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/nested-clients": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/property-provider": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.965.0.tgz", + "integrity": "sha512-cRxmMHF+Zh2lkkkEVduKl+8OQdtg/DhYA69+/7SPSQURlgyjFQGlRQ58B7q8abuNlrGT3sV+UzeOylZpJbV61Q==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.965.0", + "@aws-sdk/credential-provider-http": "3.965.0", + "@aws-sdk/credential-provider-ini": "3.965.0", + "@aws-sdk/credential-provider-process": "3.965.0", + "@aws-sdk/credential-provider-sso": "3.965.0", + "@aws-sdk/credential-provider-web-identity": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/credential-provider-imds": "^4.2.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.965.0.tgz", + "integrity": "sha512-gmkPmdiR0yxnTzLPDb7rwrDhGuCUjtgnj8qWP+m0gSz/W43rR4jRPVEf6DUX2iC+ImQhxo3NFhuB3V42Kzo3TQ==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.965.0.tgz", + "integrity": "sha512-N01AYvtCqG3Wo/s/LvYt19ity18/FqggiXT+elAs3X9Om/Wfx+hw9G+i7jaDmy+/xewmv8AdQ2SK5Q30dXw/Fw==", + "dependencies": { + "@aws-sdk/client-sso": "3.965.0", + "@aws-sdk/core": "3.965.0", + "@aws-sdk/token-providers": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.965.0.tgz", + "integrity": "sha512-T4gMZ2JzXnfxe1oTD+EDGLSxFfk1+WkLZdiHXEMZp8bFI1swP/3YyDFXI+Ib9Uq1JhnAmrCXtOnkicKEhDkdhQ==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/nested-clients": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.965.0.tgz", + "integrity": "sha512-gbdv3Dl8l8xmg4oH60fXvfDyTxfx28w5/Hxdymx3vurM07tAyd4qld8zEXejnSpraTo45QcHRtk5auELIMfeag==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-arn-parser": "3.965.0", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.965.0.tgz", + "integrity": "sha512-UBxVytsmhEmFwkBnt+aV0eAJ7uc+ouNokCqMBrQ7Oc5A77qhlcHfOgXIKz2SxqsiYTsDq+a0lWFM/XpyRWraqA==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.965.0.tgz", + "integrity": "sha512-5rzEW08trcpHMe6jkQyYc4PL1KG/H7BbnySFSzhih+r/gktQEiE36sb1BNf7av9I0Vk2Ccmt7wocB5PIT7GDkQ==", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.965.0", + "@aws-sdk/crc64-nvme": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-stream": "^4.5.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.965.0.tgz", + "integrity": "sha512-SfpSYqoPOAmdb3DBsnNsZ0vix+1VAtkUkzXM79JL3R5IfacpyKE2zytOgVAQx/FjhhlpSTwuXd+LRhUEVb3MaA==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.965.0.tgz", + "integrity": "sha512-07T1rwAarQs33mVg5U28AsSdLB5JUXu9yBTBmspFGajKVsEahIyntf53j9mAXF1N2KR0bNdP0J4A0kst4t43UQ==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.965.0.tgz", + "integrity": "sha512-gjUvJRZT1bUABKewnvkj51LAynFrfz2h5DYAg5/2F4Utx6UOGByTSr9Rq8JCLbURvvzAbCtcMkkIJRxw+8Zuzw==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.965.0.tgz", + "integrity": "sha512-6dvD+18Ni14KCRu+tfEoNxq1sIGVp9tvoZDZ7aMvpnA7mDXuRLrOjRQ/TAZqXwr9ENKVGyxcPl0cRK8jk1YWjA==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.965.0.tgz", + "integrity": "sha512-dXEgnojaaVRl+OlOx35mg3rYEbfffIN4X6tLmIfDnaKz0hMaDMvsE9jJXb/vBvokbdO1sVB27/2FEM4ttLSLnw==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-arn-parser": "3.965.0", + "@smithy/core": "^3.20.0", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/signature-v4": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-stream": "^4.5.8", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.965.0.tgz", + "integrity": "sha512-dke++CTw26y+a2D1DdVuZ4+2TkgItdx6TeuE0zOl4lsqXGvTBUG4eaIZalt7ZOAW5ys2pbDOk1bPuh4opoD3pQ==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.965.0.tgz", + "integrity": "sha512-RBEYVGgu/WeAt+H/qLrGc+t8LqAUkbyvh3wBfTiuAD+uBcWsKnvnB1iSBX75FearC0fmoxzXRUc0PMxMdqpjJQ==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-endpoints": "3.965.0", + "@smithy/core": "^3.20.0", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.965.0.tgz", + "integrity": "sha512-muNVUjUEU+/KLFrLzQ8PMXyw4+a/MP6t4GIvwLtyx/kH0rpSy5s0YmqacMXheuIe6F/5QT8uksXGNAQenitkGQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.965.0", + "@aws-sdk/middleware-host-header": "3.965.0", + "@aws-sdk/middleware-logger": "3.965.0", + "@aws-sdk/middleware-recursion-detection": "3.965.0", + "@aws-sdk/middleware-user-agent": "3.965.0", + "@aws-sdk/region-config-resolver": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@aws-sdk/util-endpoints": "3.965.0", + "@aws-sdk/util-user-agent-browser": "3.965.0", + "@aws-sdk/util-user-agent-node": "3.965.0", + "@smithy/config-resolver": "^4.4.5", + "@smithy/core": "^3.20.0", + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/hash-node": "^4.2.7", + "@smithy/invalid-dependency": "^4.2.7", + "@smithy/middleware-content-length": "^4.2.7", + "@smithy/middleware-endpoint": "^4.4.1", + "@smithy/middleware-retry": "^4.4.17", + "@smithy/middleware-serde": "^4.2.8", + "@smithy/middleware-stack": "^4.2.7", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/smithy-client": "^4.10.2", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.16", + "@smithy/util-defaults-mode-node": "^4.2.19", + "@smithy/util-endpoints": "^3.2.7", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-retry": "^4.2.7", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.965.0.tgz", + "integrity": "sha512-RoMhu9ly2B0coxn8ctXosPP2WmDD0MkQlZGLjoYHQUOCBmty5qmCxOqBmBDa6wbWbB8xKtMQ/4VXloQOgzjHXg==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/config-resolver": "^4.4.5", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.965.0.tgz", + "integrity": "sha512-hgbAThbsUrWtNpFBQxzXevIfd5Qgr4TLbXY1AIbmpSX9fPVC114pdieRMpopJ0fYaJ7v5/blTiS6wzVdXleZ/w==", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/protocol-http": "^5.3.7", + "@smithy/signature-v4": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.965.0.tgz", + "integrity": "sha512-aR0qxg0b8flkXJVE+CM1gzo7uJ57md50z2eyCwofC0QIz5Y0P7/7vvb9/dmUQt6eT9XRN5iRcUqq2IVxVDvJOw==", + "dependencies": { + "@aws-sdk/core": "3.965.0", + "@aws-sdk/nested-clients": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.965.0.tgz", + "integrity": "sha512-jvodoJdMavvg8faN7co58vVJRO5MVep4JFPRzUNCzpJ98BDqWDk/ad045aMJcmxkLzYLS2UAnUmqjJ/tUPNlzQ==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.965.0.tgz", + "integrity": "sha512-bNGKr5Tct28jGLkL8xIkGu7swpDgBpkTVbGaofhzr/X80iclbOv656RGxhMpDvmc4S9UuQnqLRXyceNFNF2V7Q==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.965.0.tgz", + "integrity": "sha512-WqSCB0XIsGUwZWvrYkuoofi2vzoVHqyeJ2kN+WyoOsxPLTiQSBIoqm/01R/qJvoxwK/gOOF7su9i84Vw2NQQpQ==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-endpoints": "^3.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.0.tgz", + "integrity": "sha512-9LJFand4bIoOjOF4x3wx0UZYiFZRo4oUauxQSiEX2dVg+5qeBOJSjp2SeWykIE6+6frCZ5wvWm2fGLK8D32aJw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.965.0.tgz", + "integrity": "sha512-Xiza/zMntQGpkd2dETQeAK8So1pg5+STTzpcdGWxj5q0jGO5ayjqT/q1Q7BrsX5KIr6PvRkl9/V7lLCv04wGjQ==", + "dependencies": { + "@aws-sdk/types": "3.965.0", + "@smithy/types": "^4.11.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.965.0.tgz", + "integrity": "sha512-kokIHUfNT3/P55E4fUJJrFHuuA9BbjFKUIxoLrd3UaRfdafT0ScRfg2eaZie6arf60EuhlUIZH0yALxttMEjxQ==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.965.0", + "@aws-sdk/types": "3.965.0", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.965.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.965.0.tgz", + "integrity": "sha512-Tcod25/BTupraQwtb+Q+GX8bmEZfxIFjjJ/AvkhUZsZlkPeVluzq1uu3Oeqf145DCdMjzLIN6vab5MrykbDP+g==", + "dependencies": { + "@smithy/types": "^4.11.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "devOptional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "optional": true, + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "optional": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", + "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@camunda8/orchestration-cluster-api": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@camunda8/orchestration-cluster-api/-/orchestration-cluster-api-1.2.3.tgz", + "integrity": "sha512-JQm/rOjPmC63iw25Vuf45YxiCB98gwatLoaHdKOZLGFamMckX5fJspgxipEERF/aEj6DvqdxADlhxWM7WNL2Xg==", + "dependencies": { + "p-retry": "^6.0.1", + "typed-env": "^2.0.0", + "zod": "^4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@camunda8/sdk": { + "version": "8.8.4", + "resolved": "https://registry.npmjs.org/@camunda8/sdk/-/sdk-8.8.4.tgz", + "integrity": "sha512-Ie7kfTaXSKws8t3dM9FLR6lbw5u1ZUHB/LzlHpW0MbindFC04o4dOgSVwqlrHhUSdCe1djXr3K62xw2l65D6Og==", + "dependencies": { + "@camunda8/orchestration-cluster-api": "^1.2.2", + "@grpc/grpc-js": "1.12.5", + "@grpc/proto-loader": "0.7.13", + "@types/form-data": "^2.2.1", + "@types/node": "^22.18.6", + "chalk": "^2.4.2", + "console-stamp": "^3.0.2", + "dayjs": "^1.8.15", + "debug": "^4.3.4", + "fast-xml-parser": "^4.1.3", + "form-data": "^4.0.2", + "got": "^11.8.6", + "jwt-decode": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "long": "^4.0.0", + "lossless-json": "^4.0.1", + "p-cancelable": "^2.1.1", + "promise-retry": "^1.1.1", + "reflect-metadata": "^0.2.1", + "stack-trace": "0.0.10", + "typed-duration": "^1.0.12", + "typed-emitter": "^2.1.0", + "typed-env": "^2.0.0", + "winston": "^3.14.2" + }, + "optionalDependencies": { + "win-ca": "3.5.1" + } + }, + "node_modules/@camunda8/sdk/node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@camunda8/sdk/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@css-inline/css-inline": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.14.1.tgz", + "integrity": "sha512-u4eku+hnPqqHIGq/ZUQcaP0TrCbYeLIYBaK7qClNRGZbnh8RC4gVxLEIo8Pceo1nOK9E5G4Lxzlw5KnXcvflfA==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@css-inline/css-inline-android-arm-eabi": "0.14.1", + "@css-inline/css-inline-android-arm64": "0.14.1", + "@css-inline/css-inline-darwin-arm64": "0.14.1", + "@css-inline/css-inline-darwin-x64": "0.14.1", + "@css-inline/css-inline-linux-arm-gnueabihf": "0.14.1", + "@css-inline/css-inline-linux-arm64-gnu": "0.14.1", + "@css-inline/css-inline-linux-arm64-musl": "0.14.1", + "@css-inline/css-inline-linux-x64-gnu": "0.14.1", + "@css-inline/css-inline-linux-x64-musl": "0.14.1", + "@css-inline/css-inline-win32-x64-msvc": "0.14.1" + } + }, + "node_modules/@css-inline/css-inline-android-arm-eabi": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm-eabi/-/css-inline-android-arm-eabi-0.14.1.tgz", + "integrity": "sha512-LNUR8TY4ldfYi0mi/d4UNuHJ+3o8yLQH9r2Nt6i4qeg1i7xswfL3n/LDLRXvGjBYqeEYNlhlBQzbPwMX1qrU6A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-android-arm64": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm64/-/css-inline-android-arm64-0.14.1.tgz", + "integrity": "sha512-tH5us0NYGoTNBHOUHVV7j9KfJ4DtFOeTLA3cM0XNoMtArNu2pmaaBMFJPqECzavfXkLc7x5Z22UPZYjoyHfvCA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-darwin-arm64": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.14.1.tgz", + "integrity": "sha512-QE5W1YRIfRayFrtrcK/wqEaxNaqLULPI0gZB4ArbFRd3d56IycvgBasDTHPre5qL2cXCO3VyPx+80XyHOaVkag==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-darwin-x64": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.14.1.tgz", + "integrity": "sha512-mAvv2sN8awNFsbvBzlFkZPbCNZ6GCWY5/YcIz7V5dPYw+bHHRbjnlkNTEZq5BsDxErVrMIGvz05PGgzuNvZvdQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm-gnueabihf": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.14.1.tgz", + "integrity": "sha512-AWC44xL0X7BgKvrWEqfSqkT2tJA5kwSGrAGT+m0gt11wnTYySvQ6YpX0fTY9i3ppYGu4bEdXFjyK2uY1DTQMHA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-gnu": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.14.1.tgz", + "integrity": "sha512-drj0ciiJgdP3xKXvNAt4W+FH4KKMs8vB5iKLJ3HcH07sNZj58Sx++2GxFRS1el3p+GFp9OoYA6dgouJsGEqt0Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-musl": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.14.1.tgz", + "integrity": "sha512-FzknI+st8eA8YQSdEJU9ykcM0LZjjigBuynVF5/p7hiMm9OMP8aNhWbhZ8LKJpKbZrQsxSGS4g9Vnr6n6FiSdQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-gnu": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.14.1.tgz", + "integrity": "sha512-yubbEye+daDY/4vXnyASAxH88s256pPati1DfVoZpU1V0+KP0BZ1dByZOU1ktExurbPH3gZOWisAnBE9xon0Uw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-musl": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.14.1.tgz", + "integrity": "sha512-6CRAZzoy1dMLPC/tns2rTt1ZwPo0nL/jYBEIAsYTCWhfAnNnpoLKVh5Nm+fSU3OOwTTqU87UkGrFJhObD/wobQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-win32-x64-msvc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.14.1.tgz", + "integrity": "sha512-nzotGiaiuiQW78EzsiwsHZXbxEt6DiMUFcDJ6dhiliomXxnlaPyBfZb6/FMBgRJOf6sknDt/5695OttNmbMYzg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@date-fns/tz": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", + "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==" + }, + "node_modules/@date-fns/utc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", + "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==" + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "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==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@esm2cjs/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@esm2cjs/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-5HzrA5N0lSMtx2RdXfD9Z4HUFaRGwVOFs7jsFG8jDivoZjYYwZFsSqvA17TaNZYFcwBrkSCqHlxDu2YDpjjUBA==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone" + } + }, + "node_modules/@faker-js/faker": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.9.0.tgz", + "integrity": "sha512-OEl393iCOoo/z8bMezRlJu+GlRGlsKbUAN7jKB6LhnKoqKve5DXRpalbItIIcwnCjs1k/FOPjFzcA6Qn+H+YbA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@ioredis/commands": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.0.tgz", + "integrity": "sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==" + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "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" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "devOptional": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==" + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@nestjs-modules/mailer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-2.0.2.tgz", + "integrity": "sha512-+z4mADQasg0H1ZaGu4zZTuKv2pu+XdErqx99PLFPzCDNTN/q9U59WPgkxVaHnsvKHNopLj5Xap7G4ZpptduoYw==", + "dependencies": { + "@css-inline/css-inline": "0.14.1", + "glob": "10.3.12" + }, + "optionalDependencies": { + "@types/ejs": "^3.1.5", + "@types/mjml": "^4.7.4", + "@types/pug": "^2.0.10", + "ejs": "^3.1.10", + "handlebars": "^4.7.8", + "liquidjs": "^10.11.1", + "mjml": "^4.15.3", + "preview-email": "^3.0.19", + "pug": "^3.0.2" + }, + "peerDependencies": { + "@nestjs/common": ">=7.0.9", + "@nestjs/core": ">=7.0.9", + "@types/ejs": ">=3.0.3", + "@types/mjml": ">=4.7.4", + "@types/pug": ">=2.0.6", + "ejs": ">=3.1.2", + "handlebars": ">=4.7.6", + "liquidjs": ">=10.8.2", + "mjml": ">=4.15.3", + "nodemailer": ">=6.4.6", + "preview-email": ">=3.0.19", + "pug": ">=3.0.1" + } + }, + "node_modules/@nestjs/axios": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.1.tgz", + "integrity": "sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "axios": "^1.3.1", + "rxjs": "^7.0.0" + } + }, + "node_modules/@nestjs/cli": { + "version": "11.0.14", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.14.tgz", + "integrity": "sha512-YwP03zb5VETTwelXU+AIzMVbEZKk/uxJL+z9pw0mdG9ogAtqZ6/mpmIM4nEq/NU8D0a7CBRLcMYUmWW/55pfqw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", + "@angular-devkit/schematics-cli": "19.2.19", + "@inquirer/prompts": "7.10.1", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.2.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "13.0.0", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.9.3", + "webpack": "5.103.0", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 20.11" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs/cli/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@nestjs/cli/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs/cli/node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.11.tgz", + "integrity": "sha512-R/+A8XFqLgN8zNs2twhrOaE7dJbRQhdPX3g46am4RT/x8xGLqDphrXkUIno4cGUZHxbczChBAaAPTdPv73wDZA==", + "dependencies": { + "file-type": "21.2.0", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.2.tgz", + "integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==", + "dependencies": { + "dotenv": "16.4.7", + "dotenv-expand": "12.0.1", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@nestjs/core": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.11.tgz", + "integrity": "sha512-H9i+zT3RvHi7tDc+lCmWHJ3ustXveABCr+Vcpl96dNOxgmrx4elQSTC4W93Mlav2opfLV+p0UTHY6L+bpUA4zA==", + "hasInstallScript": true, + "dependencies": { + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/event-emitter": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", + "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", + "dependencies": { + "eventemitter2": "6.4.9" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0" + } + }, + "node_modules/@nestjs/jwt": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.2.tgz", + "integrity": "sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==", + "dependencies": { + "@types/jsonwebtoken": "9.0.10", + "jsonwebtoken": "9.0.3" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" + } + }, + "node_modules/@nestjs/mapped-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", + "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/platform-express": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.11.tgz", + "integrity": "sha512-kyABSskdMRIAMWL0SlbwtDy4yn59RL4HDdwHDz/fxWuv7/53YP8Y2DtV3/sHqY5Er0msMVTZrM38MjqXhYL7gw==", + "dependencies": { + "cors": "2.8.5", + "express": "5.2.1", + "multer": "2.0.2", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" + } + }, + "node_modules/@nestjs/platform-socket.io": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.11.tgz", + "integrity": "sha512-0z6pLg9CuTXtz7q2lRZoPOU94DN28OTa39f4cQrlZysKA6QrKM7w7z6xqb4g32qjF+LQHFNRmMJtE/pLrxBaig==", + "dependencies": { + "socket.io": "4.8.3", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/schedule": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.1.0.tgz", + "integrity": "sha512-W25Ydc933Gzb1/oo7+bWzzDiOissE+h/dhIAPugA39b9MuIzBbLybuXpc1AjoQLczO3v0ldmxaffVl87W0uqoQ==", + "dependencies": { + "cron": "4.3.5" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "11.0.9", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", + "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.17", + "@angular-devkit/schematics": "19.2.17", + "comment-json": "4.4.1", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", + "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", + "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "19.2.17", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@nestjs/swagger": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.4.tgz", + "integrity": "sha512-7MLqtHfD2qfhZUyg13FyX6liwigtXUL8gHXq7PaBcGo9cu8QWDDT//Fn3qzJx59+Wh+Ly/Zn+prCMpskPI5nrQ==", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "@nestjs/mapped-types": "2.1.0", + "js-yaml": "4.1.1", + "lodash": "4.17.21", + "path-to-regexp": "8.3.0", + "swagger-ui-dist": "5.31.0" + }, + "peerDependencies": { + "@fastify/static": "^8.0.0 || ^9.0.0", + "@nestjs/common": "^11.0.1", + "@nestjs/core": "^11.0.1", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/terminus": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-11.0.0.tgz", + "integrity": "sha512-c55LOo9YGovmQHtFUMa/vDaxGZ2cglMTZejqgHREaApt/GArTfgYYGwhRXPLq8ZwiQQlLuYB+79e9iA8mlDSLA==", + "dependencies": { + "boxen": "5.1.2", + "check-disk-space": "3.4.0" + }, + "peerDependencies": { + "@grpc/grpc-js": "*", + "@grpc/proto-loader": "*", + "@mikro-orm/core": "*", + "@mikro-orm/nestjs": "*", + "@nestjs/axios": "^2.0.0 || ^3.0.0 || ^4.0.0", + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0", + "@nestjs/microservices": "^10.0.0 || ^11.0.0", + "@nestjs/mongoose": "^11.0.0", + "@nestjs/sequelize": "^10.0.0 || ^11.0.0", + "@nestjs/typeorm": "^10.0.0 || ^11.0.0", + "@prisma/client": "*", + "mongoose": "*", + "reflect-metadata": "0.1.x || 0.2.x", + "rxjs": "7.x", + "sequelize": "*", + "typeorm": "*" + }, + "peerDependenciesMeta": { + "@grpc/grpc-js": { + "optional": true + }, + "@grpc/proto-loader": { + "optional": true + }, + "@mikro-orm/core": { + "optional": true + }, + "@mikro-orm/nestjs": { + "optional": true + }, + "@nestjs/axios": { + "optional": true + }, + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/mongoose": { + "optional": true + }, + "@nestjs/sequelize": { + "optional": true + }, + "@nestjs/typeorm": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "mongoose": { + "optional": true + }, + "sequelize": { + "optional": true + }, + "typeorm": { + "optional": true + } + } + }, + "node_modules/@nestjs/typeorm": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", + "integrity": "sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, + "node_modules/@nestjs/websockets": { + "version": "11.1.11", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.11.tgz", + "integrity": "sha512-apuP7C/gtMBIYNgA8IWt75GTZeWya5JQCnrLZFcOu+IZt00j9Xd/Bm7hbj/Qr/JVoM/7q6c/4p4oOZtBGx4aeA==", + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-socket.io": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } + } + }, + "node_modules/@newrelic/fn-inspect": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@newrelic/fn-inspect/-/fn-inspect-4.4.0.tgz", + "integrity": "sha512-VgoXZp3zqP1167XvrA772EHDFUNuYGQh14whFq1d2sE6dC3ZL46tXI9JY0yZdAAeyCERi7mhq7CPx9BYiTOl/A==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.22.2", + "node-gyp-build": "^4.8.1", + "prebuildify": "^6.0.1" + }, + "engines": { + "node": ">=16.9.1" + } + }, + "node_modules/@newrelic/native-metrics": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@newrelic/native-metrics/-/native-metrics-11.1.0.tgz", + "integrity": "sha512-V+FRgRgv6R8LAnVsix8d1bL5SMmBONURaT/6F//rOgycvU7PfPoVEocacpSRzPFxMI27e46iAf3W5fGosbQ8yQ==", + "hasInstallScript": true, + "dependencies": { + "nan": "^2.22.2", + "node-gyp-build": "^4.8.1", + "prebuildify": "^6.0.1" + }, + "engines": { + "node": ">=18", + "npm": ">=6" + } + }, + "node_modules/@newrelic/security-agent": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@newrelic/security-agent/-/security-agent-2.4.4.tgz", + "integrity": "sha512-KQs76SGqTOG/pTuqRGwzy1lNAm96z8gbb5XHl9FsbOY7Kt9Ltbr46AiiovDHhNQbXFTPnHEY7FfmNJ4GK4W5dQ==", + "dependencies": { + "axios": "^1.12.0", + "check-disk-space": "^3.4.0", + "content-type": "^1.0.5", + "cron": "^3.1.7", + "fast-safe-stringify": "^2.1.1", + "find-package-json": "^1.2.0", + "hash.js": "^1.1.7", + "html-entities": "^2.3.6", + "https-proxy-agent": "^7.0.4", + "is-invalid-path": "^1.0.2", + "js-yaml": "^4.1.0", + "jsonschema": "^1.4.1", + "lodash": "^4.17.21", + "log4js": "^6.9.1", + "pretty-bytes": "^5.6.0", + "request-ip": "^3.3.0", + "ringbufferjs": "^2.0.0", + "semver": "^7.5.4", + "unescape": "^1.0.1", + "unescape-js": "^1.1.4", + "uuid": "^9.0.1", + "ws": "^8.17.1" + }, + "engines": { + "node": ">=18", + "npm": ">=6.0.0" + } + }, + "node_modules/@newrelic/security-agent/node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" + }, + "node_modules/@newrelic/security-agent/node_modules/cron": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.5.0.tgz", + "integrity": "sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.5.0" + } + }, + "node_modules/@newrelic/security-agent/node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/@newrelic/security-agent/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "optional": true + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.201.1.tgz", + "integrity": "sha512-IxcFDP1IGMDemVFG2by/AMK+/o6EuBQ8idUq3xZ6MxgQGeumYZuX5OwR0h9HuvcUc/JPjQGfU5OHKIKYDJcXeA==", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", + "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.201.1.tgz", + "integrity": "sha512-LMRVg2yTev28L51RLLUK3gY0avMa1RVBq7IkYNtXDBxJRcd0TGGq/0rqfk7Y4UIM9NCJhDIUFHeGg8NpSgSWcw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.201.1.tgz", + "integrity": "sha512-9ie2jcaUQZdIoe6B02r0rF4Gz+JsZ9mev/2pYou1N0woOUkFM8xwO6BAlORnrFVslqF/XO5WG3q5FsTbuC5iiw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.201.1", + "@opentelemetry/otlp-exporter-base": "0.201.1", + "@opentelemetry/otlp-transformer": "0.201.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-metrics": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.201.1.tgz", + "integrity": "sha512-FiS/mIWmZXyRxYGyXPHY+I/4+XrYVTD7Fz/zwOHkVPQsA1JTakAOP9fAi6trXMio0dIpzvQujLNiBqGM7ExrQw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/otlp-transformer": "0.201.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.201.1.tgz", + "integrity": "sha512-+q/8Yuhtu9QxCcjEAXEO8fXLjlSnrnVwfzi9jiWaMAppQp69MoagHHomQj02V2WnGjvBod5ajgkbK4IoWab50A==", + "dependencies": { + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.201.1", + "@opentelemetry/sdk-metrics": "2.0.1", + "@opentelemetry/sdk-trace-base": "2.0.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", + "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", + "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.201.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.201.1.tgz", + "integrity": "sha512-Ug8gtpssUNUnfpotB9ZhnSsPSGDu+7LngTMgKl31mmVJwLAKyl6jC8diZrMcGkSgBh0o5dbg9puvLyR25buZfw==", + "dependencies": { + "@opentelemetry/api-logs": "0.201.1", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", + "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", + "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", + "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", + "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", + "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@oxc-resolver/binding-android-arm-eabi": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.16.2.tgz", + "integrity": "sha512-lVJbvydLQIDZHKUb6Zs9Rq80QVTQ9xdCQE30eC9/cjg4wsMoEOg65QZPymUAIVJotpUAWJD0XYcwE7ugfxx5kQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-android-arm64": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.16.2.tgz", + "integrity": "sha512-fEk+g/g2rJ6LnBVPqeLcx+/alWZ/Db1UlXG+ZVivip0NdrnOzRL48PAmnxTMGOrLwsH1UDJkwY3wOjrrQltCqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-arm64": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.16.2.tgz", + "integrity": "sha512-Pkbp1qi7kdUX6k3Fk1PvAg6p7ruwaWKg1AhOlDgrg2vLXjtv9ZHo7IAQN6kLj0W771dPJZWqNxoqTPacp2oYWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.16.2.tgz", + "integrity": "sha512-FYCGcU1iSoPkADGLfQbuj0HWzS+0ItjDCt9PKtu2Hzy6T0dxO4Y1enKeCOxCweOlmLEkSxUlW5UPT4wvT3LnAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-freebsd-x64": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.16.2.tgz", + "integrity": "sha512-1zHCoK6fMcBjE54P2EG/z70rTjcRxvyKfvk4E/QVrWLxNahuGDFZIxoEoo4kGnnEcmPj41F0c2PkrQbqlpja5g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.16.2.tgz", + "integrity": "sha512-+ucLYz8EO5FDp6kZ4o1uDmhoP+M98ysqiUW4hI3NmfiOJQWLrAzQjqaTdPfIOzlCXBU9IHp5Cgxu6wPjVb8dbA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.16.2.tgz", + "integrity": "sha512-qq+TpNXyw1odDgoONRpMLzH4hzhwnEw55398dL8rhKGvvYbio71WrJ00jE+hGlEi7H1Gkl11KoPJRaPlRAVGPw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.16.2.tgz", + "integrity": "sha512-xlMh4gNtplNQEwuF5icm69udC7un0WyzT5ywOeHrPMEsghKnLjXok2wZgAA7ocTm9+JsI+nVXIQa5XO1x+HPQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-musl": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.16.2.tgz", + "integrity": "sha512-OZs33QTMi0xmHv/4P0+RAKXJTBk7UcMH5tpTaCytWRXls/DGaJ48jOHmriQGK2YwUqXl+oneuNyPOUO0obJ+Hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.16.2.tgz", + "integrity": "sha512-UVyuhaV32dJGtF6fDofOcBstg9JwB2Jfnjfb8jGlu3xcG+TsubHRhuTwQ6JZ1sColNT1nMxBiu7zdKUEZi1kwg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.16.2.tgz", + "integrity": "sha512-YZZS0yv2q5nE1uL/Fk4Y7m9018DSEmDNSG8oJzy1TJjA1jx5HL52hEPxi98XhU6OYhSO/vC1jdkJeE8TIHugug==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.16.2.tgz", + "integrity": "sha512-9VYuypwtx4kt1lUcwJAH4dPmgJySh4/KxtAPdRoX2BTaZxVm/yEXHq0mnl/8SEarjzMvXKbf7Cm6UBgptm3DZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.16.2.tgz", + "integrity": "sha512-3gbwQ+xlL5gpyzgSDdC8B4qIM4mZaPDLaFOi3c/GV7CqIdVJc5EZXW4V3T6xwtPBOpXPXfqQLbhTnUD4SqwJtA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.16.2.tgz", + "integrity": "sha512-m0WcK0j54tSwWa+hQaJMScZdWneqE7xixp/vpFqlkbhuKW9dRHykPAFvSYg1YJ3MJgu9ZzVNpYHhPKJiEQq57Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-musl": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.16.2.tgz", + "integrity": "sha512-ZjUm3w96P2t47nWywGwj1A2mAVBI/8IoS7XHhcogWCfXnEI3M6NPIRQPYAZW4s5/u3u6w1uPtgOwffj2XIOb/g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-openharmony-arm64": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.16.2.tgz", + "integrity": "sha512-OFVQ2x3VenTp13nIl6HcQ/7dmhFmM9dg2EjKfHcOtYfrVLQdNR6THFU7GkMdmc8DdY1zLUeilHwBIsyxv5hkwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.16.2.tgz", + "integrity": "sha512-+O1sY3RrGyA2AqDnd3yaDCsqZqCblSTEpY7TbbaOaw0X7iIbGjjRLdrQk9StG3QSiZuBy9FdFwotIiSXtwvbAQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.16.2.tgz", + "integrity": "sha512-jMrMJL+fkx6xoSMFPOeyQ1ctTFjavWPOSZEKUY5PebDwQmC9cqEr4LhdTnGsOtFrWYLXlEU4xWeMdBoc/XKkOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-ia32-msvc": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.16.2.tgz", + "integrity": "sha512-tl0xDA5dcQplG2yg2ZhgVT578dhRFafaCfyqMEAXq8KNpor85nJ53C3PLpfxD2NKzPioFgWEexNsjqRi+kW2Mg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-x64-msvc": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.16.2.tgz", + "integrity": "sha512-M7z0xjYQq1HdJk2DxTSLMvRMyBSI4wn4FXGcVQBsbAihgXevAReqwMdb593nmCK/OiFwSNcOaGIzUvzyzQ+95w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/prisma-fmt-wasm": { + "version": "4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085", + "resolved": "https://registry.npmjs.org/@prisma/prisma-fmt-wasm/-/prisma-fmt-wasm-4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085.tgz", + "integrity": "sha512-zYz3rFwPB82mVlHGknAPdnSY/a308dhPOblxQLcZgZTDRtDXOE1MgxoRAys+jekwR4/bm3+rZDPs1xsFMsPZig==", + "optional": true + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.7.tgz", + "integrity": "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", + "dependencies": { + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.5.tgz", + "integrity": "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==", + "dependencies": { + "@smithy/node-config-provider": "^4.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.7", + "@smithy/util-middleware": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.1.tgz", + "integrity": "sha512-wOboSEdQ85dbKAJ0zL+wQ6b0HTSBRhtGa0PYKysQXkRg+vK0tdCRRVruiFM2QMprkOQwSYOnwF4og96PAaEGag==", + "dependencies": { + "@smithy/middleware-serde": "^4.2.8", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-stream": "^4.5.8", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.7.tgz", + "integrity": "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==", + "dependencies": { + "@smithy/node-config-provider": "^4.3.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.7.tgz", + "integrity": "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.11.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.7.tgz", + "integrity": "sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.7.tgz", + "integrity": "sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.7.tgz", + "integrity": "sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.7.tgz", + "integrity": "sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.8.tgz", + "integrity": "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==", + "dependencies": { + "@smithy/protocol-http": "^5.3.7", + "@smithy/querystring-builder": "^4.2.7", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.8.tgz", + "integrity": "sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.7.tgz", + "integrity": "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==", + "dependencies": { + "@smithy/types": "^4.11.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.7.tgz", + "integrity": "sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==", + "dependencies": { + "@smithy/types": "^4.11.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.7.tgz", + "integrity": "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.7.tgz", + "integrity": "sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==", + "dependencies": { + "@smithy/types": "^4.11.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.7.tgz", + "integrity": "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==", + "dependencies": { + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.2.tgz", + "integrity": "sha512-mqpAdux0BNmZu/SqkFhQEnod4fX23xxTvU2LUpmKp0JpSI+kPYCiHJMmzREr8yxbNxKL2/DU1UZm9i++ayU+2g==", + "dependencies": { + "@smithy/core": "^3.20.1", + "@smithy/middleware-serde": "^4.2.8", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "@smithy/url-parser": "^4.2.7", + "@smithy/util-middleware": "^4.2.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.18", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.18.tgz", + "integrity": "sha512-E5hulijA59nBk/zvcwVMaS7FG7Y4l6hWA9vrW018r+8kiZef4/ETQaPI4oY+3zsy9f6KqDv3c4VKtO4DwwgpCg==", + "dependencies": { + "@smithy/node-config-provider": "^4.3.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/service-error-classification": "^4.2.7", + "@smithy/smithy-client": "^4.10.3", + "@smithy/types": "^4.11.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-retry": "^4.2.7", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.8.tgz", + "integrity": "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==", + "dependencies": { + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.7.tgz", + "integrity": "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.7.tgz", + "integrity": "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==", + "dependencies": { + "@smithy/property-provider": "^4.2.7", + "@smithy/shared-ini-file-loader": "^4.4.2", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.7.tgz", + "integrity": "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==", + "dependencies": { + "@smithy/abort-controller": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/querystring-builder": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.7.tgz", + "integrity": "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.7.tgz", + "integrity": "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.7.tgz", + "integrity": "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==", + "dependencies": { + "@smithy/types": "^4.11.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.7.tgz", + "integrity": "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.7.tgz", + "integrity": "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==", + "dependencies": { + "@smithy/types": "^4.11.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.2.tgz", + "integrity": "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.7.tgz", + "integrity": "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.7", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.3.tgz", + "integrity": "sha512-EfECiO/0fAfb590LBnUe7rI5ux7XfquQ8LBzTe7gxw0j9QW/q8UT/EHWHlxV/+jhQ3+Ssga9uUYXCQgImGMbNg==", + "dependencies": { + "@smithy/core": "^3.20.1", + "@smithy/middleware-endpoint": "^4.4.2", + "@smithy/middleware-stack": "^4.2.7", + "@smithy/protocol-http": "^5.3.7", + "@smithy/types": "^4.11.0", + "@smithy/util-stream": "^4.5.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.11.0.tgz", + "integrity": "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.7.tgz", + "integrity": "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==", + "dependencies": { + "@smithy/querystring-parser": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.17", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.17.tgz", + "integrity": "sha512-dwN4GmivYF1QphnP3xJESXKtHvkkvKHSZI8GrSKMVoENVSKW2cFPRYC4ZgstYjUHdR3zwaDkIaTDIp26JuY7Cw==", + "dependencies": { + "@smithy/property-provider": "^4.2.7", + "@smithy/smithy-client": "^4.10.3", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.20.tgz", + "integrity": "sha512-VD/I4AEhF1lpB3B//pmOIMBNLMrtdMXwy9yCOfa2QkJGDr63vH3RqPbSAKzoGMov3iryCxTXCxSsyGmEB8PDpg==", + "dependencies": { + "@smithy/config-resolver": "^4.4.5", + "@smithy/credential-provider-imds": "^4.2.7", + "@smithy/node-config-provider": "^4.3.7", + "@smithy/property-provider": "^4.2.7", + "@smithy/smithy-client": "^4.10.3", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.7.tgz", + "integrity": "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==", + "dependencies": { + "@smithy/node-config-provider": "^4.3.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.7.tgz", + "integrity": "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==", + "dependencies": { + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.7.tgz", + "integrity": "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==", + "dependencies": { + "@smithy/service-error-classification": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.8.tgz", + "integrity": "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.8", + "@smithy/node-http-handler": "^4.4.7", + "@smithy/types": "^4.11.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.7.tgz", + "integrity": "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==", + "dependencies": { + "@smithy/abort-controller": "^4.2.7", + "@smithy/types": "^4.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, + "node_modules/@swc/cli": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.7.9.tgz", + "integrity": "sha512-AFQu3ZZ9IcdClTknxbug08S9ed/q8F3aYkO5NoZ+6IjQ5UEo1s2HN1GRKNvUslYx2EoVYxd+6xGcp6C7wwtxyQ==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@xhmikosr/bin-wrapper": "^13.0.5", + "commander": "^8.3.0", + "minimatch": "^9.0.3", + "piscina": "^4.3.1", + "semver": "^7.3.8", + "slash": "3.0.0", + "source-map": "^0.7.3", + "tinyglobby": "^0.2.13" + }, + "bin": { + "spack": "bin/spack.js", + "swc": "bin/swc.js", + "swcx": "bin/swcx.js" + }, + "engines": { + "node": ">= 16.14.0" + }, + "peerDependencies": { + "@swc/core": "^1.2.66", + "chokidar": "^4.0.1" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@swc/cli/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@swc/core": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.8.tgz", + "integrity": "sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.8", + "@swc/core-darwin-x64": "1.15.8", + "@swc/core-linux-arm-gnueabihf": "1.15.8", + "@swc/core-linux-arm64-gnu": "1.15.8", + "@swc/core-linux-arm64-musl": "1.15.8", + "@swc/core-linux-x64-gnu": "1.15.8", + "@swc/core-linux-x64-musl": "1.15.8", + "@swc/core-win32-arm64-msvc": "1.15.8", + "@swc/core-win32-ia32-msvc": "1.15.8", + "@swc/core-win32-x64-msvc": "1.15.8" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.8.tgz", + "integrity": "sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.8.tgz", + "integrity": "sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.8.tgz", + "integrity": "sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.8.tgz", + "integrity": "sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.8.tgz", + "integrity": "sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.8.tgz", + "integrity": "sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.8.tgz", + "integrity": "sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.8.tgz", + "integrity": "sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.8.tgz", + "integrity": "sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.8.tgz", + "integrity": "sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "node_modules/@total-typescript/ts-reset": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.6.1.tgz", + "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==", + "dev": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "devOptional": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "optional": true + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-tfmcyHn1Pp9YHAO5r40+UuZUPAZbUEgqTel3EuEKpmF9hPkXgR4l41853raliXnb4gwyPNoQOfvgGGlHN5WSog==", + "deprecated": "This is a stub types definition. form-data provides its own type definitions, so you do not need this installed.", + "dependencies": { + "form-data": "*" + } + }, + "node_modules/@types/heapdump": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@types/heapdump/-/heapdump-0.3.4.tgz", + "integrity": "sha512-gTGn7W7i58F1yzvLZ5+hRE7M/VqgEzgccERJ9ZThC6qor5R8wo3oP7iiHvwgHIZzoqPF22ngu8mBagcqPWMVTg==", + "dev": true + }, + "node_modules/@types/html-to-text": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.4.tgz", + "integrity": "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true + }, + "node_modules/@types/imap": { + "version": "0.8.43", + "resolved": "https://registry.npmjs.org/@types/imap/-/imap-0.8.43.tgz", + "integrity": "sha512-POPoqrDax9mxM2N4ITZYCWaFtg1ORVfzJe4S7xwSh9aHawdEb7FwWTJYiAhzIvWp7DM+6BajnzYOwZ1BUrqtow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/imap-simple": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@types/imap-simple/-/imap-simple-4.2.10.tgz", + "integrity": "sha512-ZLITvpfY4+oPtH3z7axYRGJqOB/9M6cb4ye8eEydOlgXZuJFX0z9l+hxzXgrFDnWKwxH39ZPCT4bGIFUKSJ52g==", + "dev": true, + "dependencies": { + "@types/imap": "*", + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==" + }, + "node_modules/@types/mailparser": { + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.6.tgz", + "integrity": "sha512-wVV3cnIKzxTffaPH8iRnddX1zahbYB1ZEoAxyhoBo3TBCBuK6nZ8M8JYO/RhsCuuBVOw/DEN/t/ENbruwlxn6Q==", + "dev": true, + "dependencies": { + "@types/node": "*", + "iconv-lite": "^0.6.3" + } + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "dev": true + }, + "node_modules/@types/mjml": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", + "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==", + "optional": true, + "dependencies": { + "@types/mjml-core": "*" + } + }, + "node_modules/@types/mjml-core": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.2.tgz", + "integrity": "sha512-Q7SxFXgoX979HP57DEVsRI50TV8x1V4lfCA4Up9AvfINDM5oD/X9ARgfoyX1qS987JCnDLv85JjkqAjt3hZSiQ==", + "optional": true + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "node_modules/@types/multer": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", + "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "22.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "6.4.21", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.21.tgz", + "integrity": "sha512-Eix+sb/Nj28MNnWvO2X1OLrk5vuD4C9SMnb2Vf4itWnxphYeSceqkFX7IdmxTzn+dvmnNz7paMbg4Uc60wSfJg==", + "dev": true, + "dependencies": { + "@aws-sdk/client-ses": "^3.731.1", + "@types/node": "*" + } + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "optional": true + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true + }, + "node_modules/@types/quoted-printable": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/quoted-printable/-/quoted-printable-1.0.2.tgz", + "integrity": "sha512-3B28oB1rRaZNb3N5dlxysm8lH1ujzvReDuYBiIO4jvpTIg9ksrILCNgPxSGVyTWE/qwuxzgHaVehwMK3CVqAtA==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz", + "integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.52.0", + "@typescript-eslint/type-utils": "8.52.0", + "@typescript-eslint/utils": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.52.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz", + "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.52.0", + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz", + "integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==", + "dev": true, + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.52.0", + "@typescript-eslint/types": "^8.52.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz", + "integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz", + "integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz", + "integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0", + "@typescript-eslint/utils": "8.52.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz", + "integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz", + "integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/project-service": "8.52.0", + "@typescript-eslint/tsconfig-utils": "8.52.0", + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz", + "integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.52.0", + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz", + "integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.52.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@tyriar/fibonacci-heap": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@tyriar/fibonacci-heap/-/fibonacci-heap-2.0.9.tgz", + "integrity": "sha512-bYuSNomfn4hu2tPiDN+JZtnzCpSpbJ/PNeulmocDy3xN2X5OkJL65zo6rPZp65cPPhLF9vfT/dgE+RtFRCSxOA==" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" + }, + "node_modules/@voximplant/apiclient-nodejs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@voximplant/apiclient-nodejs/-/apiclient-nodejs-4.5.0.tgz", + "integrity": "sha512-YceoIpEAVGyVL2rVncdCXCgIg3UkkclIU5dyCcIlmZpdTkVs1c/36WKEuALnCzZeSVJiWEBn+wGakyvs4cCcNw==", + "dependencies": { + "axios": "0.21.4", + "form-data": "2.5.1", + "jsonwebtoken": "9.0.2" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/jsonwebtoken": { + "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" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/jws": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "dependencies": { + "jwa": "^1.4.2", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@voximplant/apiclient-nodejs/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xhmikosr/archive-type": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/archive-type/-/archive-type-7.1.0.tgz", + "integrity": "sha512-xZEpnGplg1sNPyEgFh0zbHxqlw5dtYg6viplmWSxUj12+QjU9SKu3U/2G73a15pEjLaOqTefNSZ1fOPUOT4Xgg==", + "dev": true, + "dependencies": { + "file-type": "^20.5.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/archive-type/node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@xhmikosr/archive-type/node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/bin-check": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/bin-check/-/bin-check-7.1.0.tgz", + "integrity": "sha512-y1O95J4mnl+6MpVmKfMYXec17hMEwE/yeCglFNdx+QvLLtP0yN4rSYcbkXnth+lElBuKKek2NbvOfOGPpUXCvw==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "isexe": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/bin-wrapper": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/bin-wrapper/-/bin-wrapper-13.2.0.tgz", + "integrity": "sha512-t9U9X0sDPRGDk5TGx4dv5xiOvniVJpXnfTuynVKwHgtib95NYEw4MkZdJqhoSiz820D9m0o6PCqOPMXz0N9fIw==", + "dev": true, + "dependencies": { + "@xhmikosr/bin-check": "^7.1.0", + "@xhmikosr/downloader": "^15.2.0", + "@xhmikosr/os-filter-obj": "^3.0.0", + "bin-version-check": "^5.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress/-/decompress-10.2.0.tgz", + "integrity": "sha512-MmDBvu0+GmADyQWHolcZuIWffgfnuTo4xpr2I/Qw5Ox0gt+e1Be7oYqJM4te5ylL6mzlcoicnHVDvP27zft8tg==", + "dev": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^8.1.0", + "@xhmikosr/decompress-tarbz2": "^8.1.0", + "@xhmikosr/decompress-targz": "^8.1.0", + "@xhmikosr/decompress-unzip": "^7.1.0", + "graceful-fs": "^4.2.11", + "strip-dirs": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-tar": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-tar/-/decompress-tar-8.1.0.tgz", + "integrity": "sha512-m0q8x6lwxenh1CrsTby0Jrjq4vzW/QU1OLhTHMQLEdHpmjR1lgahGz++seZI0bXF3XcZw3U3xHfqZSz+JPP2Gg==", + "dev": true, + "dependencies": { + "file-type": "^20.5.0", + "is-stream": "^2.0.1", + "tar-stream": "^3.1.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-tar/node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@xhmikosr/decompress-tar/node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-tarbz2/-/decompress-tarbz2-8.1.0.tgz", + "integrity": "sha512-aCLfr3A/FWZnOu5eqnJfme1Z1aumai/WRw55pCvBP+hCGnTFrcpsuiaVN5zmWTR53a8umxncY2JuYsD42QQEbw==", + "dev": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^8.0.1", + "file-type": "^20.5.0", + "is-stream": "^2.0.1", + "seek-bzip": "^2.0.0", + "unbzip2-stream": "^1.4.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2/node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2/node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/decompress-targz": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-targz/-/decompress-targz-8.1.0.tgz", + "integrity": "sha512-fhClQ2wTmzxzdz2OhSQNo9ExefrAagw93qaG1YggoIz/QpI7atSRa7eOHv4JZkpHWs91XNn8Hry3CwUlBQhfPA==", + "dev": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^8.0.1", + "file-type": "^20.5.0", + "is-stream": "^2.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-targz/node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@xhmikosr/decompress-targz/node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/decompress-unzip": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-unzip/-/decompress-unzip-7.1.0.tgz", + "integrity": "sha512-oqTYAcObqTlg8owulxFTqiaJkfv2SHsxxxz9Wg4krJAHVzGWlZsU8tAB30R6ow+aHrfv4Kub6WQ8u04NWVPUpA==", + "dev": true, + "dependencies": { + "file-type": "^20.5.0", + "get-stream": "^6.0.1", + "yauzl": "^3.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-unzip/node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@xhmikosr/decompress-unzip/node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/downloader": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/downloader/-/downloader-15.2.0.tgz", + "integrity": "sha512-lAqbig3uRGTt0sHNIM4vUG9HoM+mRl8K28WuYxyXLCUT6pyzl4Y4i0LZ3jMEsCYZ6zjPZbO9XkG91OSTd4si7g==", + "dev": true, + "dependencies": { + "@xhmikosr/archive-type": "^7.1.0", + "@xhmikosr/decompress": "^10.2.0", + "content-disposition": "^0.5.4", + "defaults": "^2.0.2", + "ext-name": "^5.0.0", + "file-type": "^20.5.0", + "filenamify": "^6.0.0", + "get-stream": "^6.0.1", + "got": "^13.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/os-filter-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/os-filter-obj/-/os-filter-obj-3.0.0.tgz", + "integrity": "sha512-siPY6BD5dQ2SZPl3I0OZBHL27ZqZvLEosObsZRQ1NUB8qcxegwt0T9eKtV96JMFQpIz1elhkzqOg4c/Ri6Dp9A==", + "dev": true, + "dependencies": { + "arch": "^3.0.0" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@zone-eu/mailsplit": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.8.tgz", + "integrity": "sha512-eEyACj4JZ7sjzRvy26QhLgKEMWwQbsw1+QZnlLX+/gihcNH07lVPOcnwf5U6UAL7gkc//J3jVd76o/WS+taUiA==", + "dependencies": { + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "devOptional": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/alce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/alce/-/alce-1.2.0.tgz", + "integrity": "sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==", + "optional": true, + "dependencies": { + "esprima": "^1.2.0", + "estraverse": "^1.5.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/alce/node_modules/esprima": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", + "integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alce/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/angular-expressions": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/angular-expressions/-/angular-expressions-1.5.1.tgz", + "integrity": "sha512-Ukcyuye0eb15zuvMFR7Kziuf7gV0gYAZXMevNI40rXF8f9k+/yk8+r5edFWRFOwnqSvAEFoP2bKNXaXgYa+Ayw==" + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/arch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-3.0.0.tgz", + "integrity": "sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "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" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/archiver/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "optional": true + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "optional": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "optional": true, + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.12.tgz", + "integrity": "sha512-Mij6Lij93pTAIsSYy5cyBQ975Qh9uLEc5rwGTpomiZeXZL9yIS6uORJakb3ScHgfs0serMMfIbXzokPMuEiRyw==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "engines": { + "node": "*" + } + }, + "node_modules/bin-version": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", + "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "find-versions": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version-check": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", + "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", + "dev": true, + "dependencies": { + "bin-version": "^6.0.0", + "semver": "^7.5.3", + "semver-truncate": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "optional": true + }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "devOptional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/busboy": { + "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" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "optional": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001763", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001763.tgz", + "integrity": "sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "optional": true, + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true + }, + "node_modules/check-disk-space": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz", + "integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==", + "engines": { + "node": ">=16" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "optional": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", + "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.20" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "optional": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/combined-stream": { + "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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "dev": true, + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/complex.js": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz", + "integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/console-stamp": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/console-stamp/-/console-stamp-3.1.2.tgz", + "integrity": "sha512-ab66x3NxOTxPuq71dI6gXEiw2X6ql4Le5gZz0bm7FW3FSCB00eztra/oQUuCoCGlsyKOxtULnHwphzMrRtzMBg==", + "dependencies": { + "chalk": "^4.1.2", + "dateformat": "^4.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/console-stamp/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/console-stamp/node_modules/chalk": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/console-stamp/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/console-stamp/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/console-stamp/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/console-stamp/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "optional": true, + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true + }, + "node_modules/cron": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.5.tgz", + "integrity": "sha512-hKPP7fq1+OfyCqoePkKfVq7tNAdFwiQORr4lZUHwrf0tebC65fYEeWgOrXOL6prn1/fegGOdTfrM6e34PJfksg==", + "dependencies": { + "@types/luxon": "~3.7.0", + "luxon": "~3.7.0" + }, + "engines": { + "node": ">=18.x" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "optional": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-tz": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.2.0.tgz", + "integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==", + "peerDependencies": { + "date-fns": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-2.0.2.tgz", + "integrity": "sha512-cuIw0PImdp76AOfgkjbW4VhQODRmNNcKR73vdCH5cLd/ifj7aamfoXvYgfGkEAjNJZ3ozMIy9Gu2LutUkGEPbA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "optional": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/display-notification": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/display-notification/-/display-notification-2.0.0.tgz", + "integrity": "sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==", + "optional": true, + "dependencies": { + "escape-string-applescript": "^1.0.0", + "run-applescript": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "optional": true + }, + "node_modules/docxtemplater": { + "version": "3.67.6", + "resolved": "https://registry.npmjs.org/docxtemplater/-/docxtemplater-3.67.6.tgz", + "integrity": "sha512-IvdTz9druTlQrsB0zlqvAqrImEydFgtvHp0uUnx+hQ9W4hmBsorrJIBJKCfkhK6QRz/jpJnA6BaTd6rEK6/Usw==", + "dependencies": { + "@xmldom/xmldom": "^0.9.8" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz", + "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "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" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "optional": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "optional": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-japanese": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha512-CJAN+O0/yA1CKfRn9SXOGctSpEM7DCon/r/5r2eXFMY2zCCJBasFhcM5I+1kh3Ap11FsQCX+vGHceNPvpWKhoA==" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, + "node_modules/escape-string-applescript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz", + "integrity": "sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "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==", + "dev": true + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/exceljs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "dev": true, + "dependencies": { + "mime-db": "^1.28.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "dev": true, + "dependencies": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", + "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==", + "optional": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dev": true, + "dependencies": { + "walk-up-path": "^4.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.2.0.tgz", + "integrity": "sha512-vCYBgFOrJQLoTzDyAXAL/RFfKnXXpUYt4+tipVy26nJJhT7ftgGETf2tAQF59EEL61i3MrorV/PG6tf7LJK7eg==", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "optional": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", + "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", + "dev": true, + "dependencies": { + "filename-reserved-regex": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "devOptional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-package-json": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/find-package-json/-/find-package-json-1.2.0.tgz", + "integrity": "sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", + "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", + "dev": true, + "dependencies": { + "semver-regex": "^4.0.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fixpack": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fixpack/-/fixpack-4.0.0.tgz", + "integrity": "sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==", + "optional": true, + "dependencies": { + "alce": "1.2.0", + "chalk": "^3.0.0", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "extend-object": "^1.0.0", + "rc": "^1.2.8" + }, + "bin": { + "fixpack": "bin/fixpack" + } + }, + "node_modules/fixpack/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fixpack/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "optional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fixpack/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/fixpack/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "node_modules/fixpack/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/fixpack/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "optional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formatly": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", + "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==", + "dev": true, + "dependencies": { + "fd-package-json": "^2.0.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/generate-password": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/generate-password/-/generate-password-1.7.1.tgz", + "integrity": "sha512-9bVYY+16m7W7GczRBDqXE+VVuCX+bWNrfYKC/2p2JkZukFb2sKxT6E3zZ3mJGz7GMe5iRK0A/WawSL3jQfJuNQ==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis": { + "version": "149.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-149.0.0.tgz", + "integrity": "sha512-LTMc/njwYy7KTeaUHDcQt7KxftHyghdzm2XzbL46PRLd1AXB09utT9Po2ZJn2X0EApz0pE2T5x5A9zM8iue6zw==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heapdump": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.15.tgz", + "integrity": "sha512-n8aSFscI9r3gfhOcAECAtXFaQ1uy4QSke6bnaL+iymYZ/dWs9cqDqHM+rALfsHUwukUbxsdlECZ0pKmJdQ/4OA==", + "hasInstallScript": true, + "dependencies": { + "nan": "^2.13.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "optional": true, + "dependencies": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "optional": true + }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/imap": { + "version": "0.8.19", + "resolved": "https://registry.npmjs.org/imap/-/imap-0.8.19.tgz", + "integrity": "sha512-z5DxEA1uRnZG73UcPA4ES5NSCGnPuuouUx43OPX7KZx1yzq3N8/vx2mtXEShT5inxB3pRgnfG1hijfu7XN2YMw==", + "dependencies": { + "readable-stream": "1.1.x", + "utf7": ">=1.0.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/imap-simple": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/imap-simple/-/imap-simple-5.1.0.tgz", + "integrity": "sha512-FLZm1v38C5ekN46l/9X5gBRNMQNVc5TSLYQ3Hsq3xBLvKwt1i5fcuShyth8MYMPuvId1R46oaPNrH92hFGHr/g==", + "dependencies": { + "iconv-lite": "~0.4.13", + "imap": "^0.8.18", + "nodeify": "^1.0.0", + "quoted-printable": "^1.0.0", + "utf8": "^2.1.1", + "uuencode": "0.0.4" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/imap-simple/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imap/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/imap/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "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" + } + }, + "node_modules/imap/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/imapflow": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.2.4.tgz", + "integrity": "sha512-X/eRQeje33uZycfopjwoQKKbya+bBIaqpviOFxhPOD24DXU2hMfXwYe9e8j1+ADwFVgTvKq4G2/ljjZK3Y8mvg==", + "dependencies": { + "@zone-eu/mailsplit": "5.4.8", + "encoding-japanese": "2.2.0", + "iconv-lite": "0.7.1", + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1", + "nodemailer": "7.0.12", + "pino": "10.1.0", + "socks": "2.8.7" + } + }, + "node_modules/imapflow/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", + "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/inspect-with-kind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz", + "integrity": "sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + } + }, + "node_modules/ioredis": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.0.tgz", + "integrity": "sha512-T3VieIilNumOJCXI9SDgo4NnF6sZkd6XcmPi6qWtw4xqbt8nNz/ZVNiIH1L9puMTSHZh1mUWA4xKa2nWPF4NwQ==", + "dependencies": { + "@ioredis/commands": "1.5.0", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "optional": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", + "optional": true + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "optional": true, + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-invalid-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-1.0.2.tgz", + "integrity": "sha512-6KLcFrPCEP3AFXMfnWrIFkZpYNBVzZAoBJJDEZKtI3LXkaDjM3uFMJQjxiizUuZTZ9Oh9FNv/soXbx5TcpaDmA==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/is-network-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha512-mjWH5XxnhMA8cFnDchr6qRP9S/kLntKuEfIYku+PaN1CnS8v+OG9O/BKpRCVRJvpIkgAZm0Pf5Is3iSSOILlcg==" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "optional": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "optional": true, + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "optional": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "optional": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "optional": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "optional": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-parse-even-better-errors": { + "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==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "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==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", + "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", + "engines": { + "node": "*" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "optional": true, + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/jstransformer/node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "optional": true + }, + "node_modules/jstransformer/node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "optional": true, + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "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" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/juice": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", + "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", + "optional": true, + "dependencies": { + "cheerio": "1.0.0-rc.12", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/juice/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/knip": { + "version": "5.80.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.80.0.tgz", + "integrity": "sha512-K/Ga2f/SHEUXXriVdaw2GfeIUJ5muwdqusHGkCtaG/1qeMmQJiuwZj9KnPxaDbnYPAu8RWjYYh8Nyb+qlJ3d8A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + } + ], + "dependencies": { + "@nodelib/fs.walk": "^1.2.3", + "fast-glob": "^3.3.3", + "formatly": "^0.3.0", + "jiti": "^2.6.0", + "js-yaml": "^4.1.1", + "minimist": "^1.2.8", + "oxc-resolver": "^11.15.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.1", + "smol-toml": "^1.5.2", + "strip-json-comments": "5.0.3", + "zod": "^4.1.11" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4 <7" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "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" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libbase64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==" + }, + "node_modules/libmime": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz", + "integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==", + "dependencies": { + "encoding-japanese": "2.2.0", + "iconv-lite": "0.6.3", + "libbase64": "1.3.0", + "libqp": "2.1.1" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.33", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.33.tgz", + "integrity": "sha512-r9kw4OA6oDO4dPXkOrXTkArQAafIKAU71hChInV4FxZ69dxCfbwQGDPzqR5/vea94wU705/3AZroEbSoeVWrQw==" + }, + "node_modules/libqp": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==" + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/liquidjs": { + "version": "10.24.0", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.24.0.tgz", + "integrity": "sha512-TAUNAdgwaAXjjcUFuYVJm9kOVH7zc0mTKxsG9t9Lu4qdWjB2BEblyVIYpjWcmJLMGgiYqnGNJjpNMHx0gp/46A==", + "optional": true, + "dependencies": { + "commander": "^10.0.0" + }, + "bin": { + "liquid": "bin/liquid.js", + "liquidjs": "bin/liquid.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/liquidjs" + } + }, + "node_modules/liquidjs/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "dev": true, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/lossless-json": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lossless-json/-/lossless-json-4.3.0.tgz", + "integrity": "sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g==" + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "optional": true + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "engines": { + "node": ">=12" + } + }, + "node_modules/lvovich": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lvovich/-/lvovich-2.1.0.tgz", + "integrity": "sha512-+dfkF0+5MKgNU/IuEtfwsodeD1snhR1DDB57KeLdH+M7TEI7OZzd4qbrAFQSzAPYNdC8mcLb+OQqvFijVrGStg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/mailparser": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.1.tgz", + "integrity": "sha512-6vHZcco3fWsDMkf4Vz9iAfxvwrKNGbHx0dV1RKVphQ/zaNY34Buc7D37LSa09jeSeybWzYcTPjhiZFxzVRJedA==", + "dependencies": { + "@zone-eu/mailsplit": "5.4.8", + "encoding-japanese": "2.2.0", + "he": "1.2.0", + "html-to-text": "9.0.5", + "iconv-lite": "0.7.0", + "libmime": "5.3.7", + "linkify-it": "5.0.0", + "nodemailer": "7.0.11", + "punycode.js": "2.3.1", + "tlds": "1.261.0" + } + }, + "node_modules/mailparser/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mailparser/node_modules/nodemailer": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.11.tgz", + "integrity": "sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "optional": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathjs": { + "version": "14.9.1", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.9.1.tgz", + "integrity": "sha512-xhqv8Xjf+caWG3WlaPekg4v8QFOR3D5+8ycfcjMcPcnCNDgAONQLaLfyGgrggJrcHx2yUGCpACRpiD4GmXwX+Q==", + "dependencies": { + "@babel/runtime": "^7.26.10", + "complex.js": "^2.2.5", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "^5.2.1", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.2.1" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimatch": { + "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" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mjml": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.18.0.tgz", + "integrity": "sha512-rQM4aqFRrNvV1k733e8hJSopBjZvoSdBpRYzNTMAN+As0jqJsO5eN0wTT2IFtfe4PREzzu5b06RkPiUQdd0IIg==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "mjml-cli": "4.18.0", + "mjml-core": "4.18.0", + "mjml-migrate": "4.18.0", + "mjml-preset-core": "4.18.0", + "mjml-validator": "4.18.0" + }, + "bin": { + "mjml": "bin/mjml" + } + }, + "node_modules/mjml-accordion": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.18.0.tgz", + "integrity": "sha512-9PUmy2JxIOGgAaVHvgVYX21nVAo3o/+wJckTTF/YTLGAqB+nm+44buxRzaXxVk7qXRwbCNfE8c8mlGVNh7vB1g==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-body": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.18.0.tgz", + "integrity": "sha512-34AwX70/7NkRIajPsa5j6NySRiNrlLatTKhiLwTVFiVtrEFlfCcbeMNmdVixI3Ldvs8209ZC6euaAnXDRyR1zw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-button": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.18.0.tgz", + "integrity": "sha512-ZsWMI0j7EcFCMqbqdVwMWhmsVc03FhmypWXokKopGhwySn4IAB4AOURonRmFrO7k6sDeQ+iJ9QtTu7jA+S8wmg==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-carousel": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.18.0.tgz", + "integrity": "sha512-wY4g1CHCOoVSZuar7CLFon/qkPbICu71IT+6pa4BDwkAiaAMAemZPyy+a+iIUgdc8kHgSuHGsGf6PQzBSMWRZA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-cli": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.18.0.tgz", + "integrity": "sha512-N6CnA4o/q/VRnGPxTzvVnjAEcF7WUVVQGYfS9SPAp0qwyf7RysMmewdS9yN8GwXwZV6L2sKdn+3ANNi2FNsJ7w==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "chokidar": "^3.0.0", + "glob": "^10.3.10", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "minimatch": "^9.0.3", + "mjml-core": "4.18.0", + "mjml-migrate": "4.18.0", + "mjml-parser-xml": "4.18.0", + "mjml-validator": "4.18.0", + "yargs": "^17.7.2" + }, + "bin": { + "mjml-cli": "bin/mjml" + } + }, + "node_modules/mjml-cli/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mjml-cli/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mjml-cli/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mjml-cli/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mjml-column": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.18.0.tgz", + "integrity": "sha512-0QZ1whxbHUmJaRT8tW+wmr3fWZ/kpsHKAd24c7Z/N1Otm/U2G0T/FFEFJ6cB25X6ZN0K40QZ8L9gdLfiSVuRbA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-core": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.18.0.tgz", + "integrity": "sha512-yey72LszXvIo5p0R6DB+YU8er/nP2wPsqpLKQCB0H8vG0WRT1sbSUvnCUOkKGn7subuyWDTdzHKbQO3XYIOmvg==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "cheerio": "1.0.0-rc.12", + "detect-node": "^2.0.4", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "juice": "^10.0.0", + "lodash": "^4.17.21", + "mjml-migrate": "4.18.0", + "mjml-parser-xml": "4.18.0", + "mjml-validator": "4.18.0" + } + }, + "node_modules/mjml-divider": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.18.0.tgz", + "integrity": "sha512-FmGUVJqi4RYroh7y85vDx0aUKZgECkxHtMQ4pkLGQbZ2g93/Qt0Ek88DVCNJ5XwUAQQkE/TvrGMLHp3CIqpQ9Q==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-group": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.18.0.tgz", + "integrity": "sha512-28ABkXsKljBqj7XCC8GkQ94xz8HEU2XTyD+9LTlkDafzGp/MGJb8DcLh/7IkxCwqkQWyeMiDNLf1djsQ909Vxw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.18.0.tgz", + "integrity": "sha512-DS0adpIAsVMDIk2DOsHzjg+RNjQU0fF8jiVP9BmdRHVGrLPmpL9wIHZk2KvsKvZe7VaXXBijFt3DZ5/CQ/+D7Q==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-attributes": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.18.0.tgz", + "integrity": "sha512-nLzix1wrMnojE0RPGhk4iKqSRwHKjie2EPzgKT7CDzfqN+Ref03E5Q19x3cQTLgxvq3C3CnvCQBfnhoS3Eakug==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-breakpoint": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.18.0.tgz", + "integrity": "sha512-k6rwff+7i+vTQYJ/CjBfE20qNqPaW60IRH2x2oEPuCzmwDmoVWOcplJIuotSqIAdfwF9hLkICknisp1BpczVlQ==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-font": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.18.0.tgz", + "integrity": "sha512-ao8HB5nf+Dmxw4GO6lMMOlnj1lNZONai0GC9RobrZgPlghZw6hpURWGpkON7pQcy6XnOHwYwkV7Go/npzA2i7w==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-html-attributes": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.18.0.tgz", + "integrity": "sha512-xaQE1rthe0RrNotwEr71X1tE+QQ489Yc0ynMm3oNMrohDI/TaCeazx8GAHPMM7VLduDA8D4A5wkZ6PuEvlJu4w==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-preview": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.18.0.tgz", + "integrity": "sha512-2JvYqhbLyU/+Te6/1AXxzTNoHYCDYhXOVZP7wMvU4t7K34pXqyRUNO405atyHUY1MRafrl6RJ8cIx0x5vUX7PA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-style": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.18.0.tgz", + "integrity": "sha512-nEwDHkAqY3Fm7QWeAZc/a7MakZpXh6THfrE8/AWrfpgzTHrD/wihNUc09ztNpr6z/K1+JWgQfSF2BRc+X3P46g==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-head-title": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.18.0.tgz", + "integrity": "sha512-0Hm8o50rPMUQLSCOOa4D4pz9NajmCDccLvBYE4fwKdeUXjSJ6bwAYeMpveel8oNZMDUVJ4Hx+PskisEGHMHM2w==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-hero": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.18.0.tgz", + "integrity": "sha512-rujm0ROM4QGWw77vnl3NaVaCKXrT4xTSHeAnkHKiY5AuRf6HPTgEtutq5pdel/y6Q9GrmxvN3HRESum7tpJCJw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-image": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.18.0.tgz", + "integrity": "sha512-e09NkoYwvzMcTv7V6H5doWD6Te2E1y2EvOLQJoXKVdQpDwyBWGdfnZke0scJGdA58HLAB+0mLYogpLwmfLaP5Q==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-migrate": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.18.0.tgz", + "integrity": "sha512-qfNCgW9zhJIsbPyXFA5RT/WY4mlje3N0WhHHOsHc0nY89Q01DenyslUy9nLLGXwi4K5FHS58oCjwWbMhwDcj1w==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "mjml-core": "4.18.0", + "mjml-parser-xml": "4.18.0", + "yargs": "^17.7.2" + }, + "bin": { + "migrate": "lib/cli.js" + } + }, + "node_modules/mjml-navbar": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.18.0.tgz", + "integrity": "sha512-uho/MS2tfNAe+V9u2X7NoCco34MDbdp30ETA8009Qo1VCP/D8lZ+s69WGRPu6hvN/Y2pzBgZly++CMg3qFZqBQ==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-parser-xml": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.18.0.tgz", + "integrity": "sha512-sHSsZg4afY1heThuJzxa1Kvfh/QzB7/9P5fFUHeVnnxb07ZTXnhXWA6YbobdND5/l9+5yjN5/UgqDZm3tIT4Uw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "detect-node": "2.1.0", + "htmlparser2": "^9.1.0", + "lodash": "^4.17.21" + } + }, + "node_modules/mjml-parser-xml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/mjml-preset-core": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.18.0.tgz", + "integrity": "sha512-x3l8vMVtsaqM/jauMeZIN7HFD2t5A28J4U0o4849yIlRxiWguLFV5l3BL8Byol+YLkoLuT9PjaZs9RYv+FGfeg==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "mjml-accordion": "4.18.0", + "mjml-body": "4.18.0", + "mjml-button": "4.18.0", + "mjml-carousel": "4.18.0", + "mjml-column": "4.18.0", + "mjml-divider": "4.18.0", + "mjml-group": "4.18.0", + "mjml-head": "4.18.0", + "mjml-head-attributes": "4.18.0", + "mjml-head-breakpoint": "4.18.0", + "mjml-head-font": "4.18.0", + "mjml-head-html-attributes": "4.18.0", + "mjml-head-preview": "4.18.0", + "mjml-head-style": "4.18.0", + "mjml-head-title": "4.18.0", + "mjml-hero": "4.18.0", + "mjml-image": "4.18.0", + "mjml-navbar": "4.18.0", + "mjml-raw": "4.18.0", + "mjml-section": "4.18.0", + "mjml-social": "4.18.0", + "mjml-spacer": "4.18.0", + "mjml-table": "4.18.0", + "mjml-text": "4.18.0", + "mjml-wrapper": "4.18.0" + } + }, + "node_modules/mjml-raw": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.18.0.tgz", + "integrity": "sha512-F/kViAwXm3ccPP52kw++/mHQbcYbYYxC8JH15TZxH8GLVZkX5CGKgcBrHhDK7WoIlfEIsVRZ6IZdlHjH8vgyxw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-section": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.18.0.tgz", + "integrity": "sha512-bB8My9zvIEkTOxej+TrjEeaeRT0lsypGeRADtdrRZXeqUClkkuCnCXlsNKSLGT8ZRqjUqWRc5z8ubDOvGk2+Gg==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-social": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.18.0.tgz", + "integrity": "sha512-iAQc9g59L6L3VHDd55BxeIvk/zHkxflxmvuyYyOOvpmmKAvUBC//ULfpxiiM4yupofsThqFfrO+wc8d4kTRkbQ==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-spacer": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.18.0.tgz", + "integrity": "sha512-FK/0f5IBiONgaRpwNBs7G8EbLdAbmYqcIfHR8O8tP4LipAChLQKHO9vX3vrRMGLBZZNTESLObcFSVWmA40Mfpw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-table": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.18.0.tgz", + "integrity": "sha512-vJysCPUL3CHcsQDAFpW+skzBtY0RYsmMBYswI4WX0B05GLKlOjXqpYOwcmAupWeGoBVL5r/t28ynu2PqnOlN3w==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-text": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.18.0.tgz", + "integrity": "sha512-hBLmF3JgveUKktKQFWHqHAr7qr92j1CxAvq7mtpDUgiWgyPFzqRX8mUsFYgZ7DmRxG4UE+Kzpt8/YFd9+E98lw==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0" + } + }, + "node_modules/mjml-validator": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.18.0.tgz", + "integrity": "sha512-JmpWAsNTUlAxJOz2zHYfF8Vod8OzM3Qp5JXtrVw5tivZQzq88ZfqVGuqsas51z0pp1/ilfD4lC17YGfGwKGyhA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4" + } + }, + "node_modules/mjml-wrapper": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.18.0.tgz", + "integrity": "sha512-TZeOvLjIhXEK60rjWNiYhEYNlv5GKYahE+96ifcT5OGkWkRA0DsQDfp+6VI32OS5VxsfKq2h/UdERPlQijjpAQ==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.18.0", + "mjml-section": "4.18.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nan": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/nest-winston": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/nest-winston/-/nest-winston-1.10.2.tgz", + "integrity": "sha512-Z9IzL/nekBOF/TEwBHUJDiDPMaXUcFquUQOFavIRet6xF0EbuWnOzslyN/ksgzG+fITNgXhMdrL/POp9SdaFxA==", + "dependencies": { + "fast-safe-stringify": "^2.1.1" + }, + "peerDependencies": { + "@nestjs/common": "^5.0.0 || ^6.6.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "winston": "^3.0.0" + } + }, + "node_modules/newrelic": { + "version": "12.25.0", + "resolved": "https://registry.npmjs.org/newrelic/-/newrelic-12.25.0.tgz", + "integrity": "sha512-WqYv7EvOcOQhvnMbPvjhZzcihO36qj2iNOO6jTfaixku+f558KFawXdNjw3Gl3kL1ycrTfO7N0ywrIZ1ggKGbA==", + "dependencies": { + "@grpc/grpc-js": "^1.13.2", + "@grpc/proto-loader": "^0.7.5", + "@newrelic/security-agent": "^2.4.2", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.201.1", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@opentelemetry/sdk-trace-base": "^2.0.0", + "@tyriar/fibonacci-heap": "^2.0.7", + "concat-stream": "^2.0.0", + "https-proxy-agent": "^7.0.1", + "import-in-the-middle": "^1.13.0", + "json-bigint": "^1.0.0", + "json-stringify-safe": "^5.0.0", + "module-details-from-path": "^1.0.3", + "readable-stream": "^3.6.1", + "require-in-the-middle": "^7.4.0", + "semver": "^7.5.2", + "winston-transport": "^4.5.0" + }, + "bin": { + "newrelic-naming-rules": "bin/test-naming-rules.js" + }, + "engines": { + "node": ">=18", + "npm": ">=6.0.0" + }, + "optionalDependencies": { + "@newrelic/fn-inspect": "^4.4.0", + "@newrelic/native-metrics": "^11.1.0", + "@prisma/prisma-fmt-wasm": "^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" + } + }, + "node_modules/newrelic/node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/newrelic/node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/newrelic/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "optional": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "optional": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-abi": { + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", + "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "optional": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true + }, + "node_modules/nodeify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz", + "integrity": "sha512-n7C2NyEze8GCo/z73KdbjRsBiLbv6eBn1FxwYKQ23IqGo7pQY3mhQan61Sv7eEDJCiyUjTVrVkXTzJCo1dW7Aw==", + "dependencies": { + "is-promise": "~1.0.0", + "promise": "~1.3.0" + } + }, + "node_modules/nodemailer": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.12.tgz", + "integrity": "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "optional": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-to-words-ru": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/number-to-words-ru/-/number-to-words-ru-2.4.1.tgz", + "integrity": "sha512-NrjvYLonGUptrU6eVm/G3rwY2hnd4NTE/K25Sx+sbifZ2EGSUPda6c+3iueqmfpPadNhQhyHJ5qFIDGRhNSXIQ==", + "dependencies": { + "@ungap/structured-clone": "^1.2.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "optional": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/oxc-resolver": { + "version": "11.16.2", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.16.2.tgz", + "integrity": "sha512-Uy76u47vwhhF7VAmVY61Srn+ouiOobf45MU9vGct9GD2ARy6hKoqEElyHDB0L+4JOM6VLuZ431KiLwyjI/A21g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.16.2", + "@oxc-resolver/binding-android-arm64": "11.16.2", + "@oxc-resolver/binding-darwin-arm64": "11.16.2", + "@oxc-resolver/binding-darwin-x64": "11.16.2", + "@oxc-resolver/binding-freebsd-x64": "11.16.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.16.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.16.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.16.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.16.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.16.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.16.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.16.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.16.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.16.2", + "@oxc-resolver/binding-linux-x64-musl": "11.16.2", + "@oxc-resolver/binding-openharmony-arm64": "11.16.2", + "@oxc-resolver/binding-wasm32-wasi": "11.16.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.16.2", + "@oxc-resolver/binding-win32-ia32-msvc": "11.16.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.16.2" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "optional": true, + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "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==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "optional": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-wait-for": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", + "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "optional": true, + "dependencies": { + "p-timeout": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "optional": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "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==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "optional": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "optional": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "optional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "devOptional": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pino": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", + "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" + }, + "node_modules/piscina": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.9.2.tgz", + "integrity": "sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==", + "dev": true, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pizzip": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pizzip/-/pizzip-3.2.0.tgz", + "integrity": "sha512-X4NPNICxCfIK8VYhF6wbksn81vTiziyLbvKuORVAmolvnUzl1A1xmz9DAWKxPRq9lZg84pJOOAMq3OE61bD8IQ==", + "dependencies": { + "pako": "^2.1.0" + } + }, + "node_modules/pizzip/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prebuildify": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.1.tgz", + "integrity": "sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw==", + "dependencies": { + "minimist": "^1.2.5", + "mkdirp-classic": "^0.5.3", + "node-abi": "^3.3.0", + "npm-run-path": "^3.1.0", + "pump": "^3.0.0", + "tar-fs": "^2.1.0" + }, + "bin": { + "prebuildify": "bin.js" + } + }, + "node_modules/prebuildify/node_modules/npm-run-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", + "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/preview-email": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.1.0.tgz", + "integrity": "sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw==", + "optional": true, + "dependencies": { + "ci-info": "^3.8.0", + "display-notification": "2.0.0", + "fixpack": "^4.0.0", + "get-port": "5.1.1", + "mailparser": "^3.7.1", + "nodemailer": "^6.9.13", + "open": "7", + "p-event": "4.2.0", + "p-wait-for": "3.2.0", + "pug": "^3.0.3", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/preview-email/node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/preview-email/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-1.3.0.tgz", + "integrity": "sha512-R9WrbTF3EPkVtWjp7B7umQGVndpsi+rsDAfrR4xAALQpFLa/+2OriecLhawxzvii2gd9+DZFwROWDuUUaqS5yA==", + "dependencies": { + "is-promise": "~1" + } + }, + "node_modules/promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha512-StEy2osPr28o17bIW776GtwO6+Q+M9zPiZkYfosciUUMYqjhU/ffwRAH0zN2+uvGyUsn8/YICIHRzLbPacpZGw==", + "dependencies": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==", + "engines": { + "node": "*" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "optional": true + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/pug": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", + "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", + "optional": true, + "dependencies": { + "pug-code-gen": "^3.0.3", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz", + "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "optional": true + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "optional": true, + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "optional": true, + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "optional": true, + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "optional": true, + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "optional": true + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "optional": true, + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "optional": true + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quoted-printable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/quoted-printable/-/quoted-printable-1.0.1.tgz", + "integrity": "sha512-cihC68OcGiQOjGiXuo5Jk6XHANTHl1K4JLk/xlEJRTIXfy19Sg6XzB95XonYgr+1rB88bCpr7WZE7D7AlZow4g==", + "dependencies": { + "utf8": "^2.1.0" + }, + "bin": { + "quoted-printable": "bin/quoted-printable" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request-ip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", + "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, + "node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "dependencies": { + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ringbufferjs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ringbufferjs/-/ringbufferjs-2.0.0.tgz", + "integrity": "sha512-GCOqTzUsTHF7nrqcgtNGAFotXztLgiePpIDpyWZ7R5I02tmfJWV+/yuJc//Hlsd8G+WzI1t/dc2y/w2imDZdog==" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "node_modules/run-applescript": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-3.2.0.tgz", + "integrity": "sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==", + "optional": true, + "dependencies": { + "execa": "^0.10.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "optional": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "optional": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "optional": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-applescript/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/run-applescript/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "optional": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/run-applescript/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "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==", + "dev": true + }, + "node_modules/scmp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", + "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==", + "deprecated": "Just use Node.js's crypto.timingSafeEqual()" + }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, + "node_modules/seek-bzip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-2.0.0.tgz", + "integrity": "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==", + "dev": true, + "dependencies": { + "commander": "^6.0.0" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-truncate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", + "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slick": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/smol-toml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.18.3" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "dev": true, + "dependencies": { + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "optional": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], + "engines": { + "node": ">=14" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.fromcodepoint": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz", + "integrity": "sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg==" + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-3.0.0.tgz", + "integrity": "sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==", + "dev": true, + "dependencies": { + "inspect-with-kind": "^1.0.5", + "is-plain-obj": "^1.1.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stripe": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-17.7.0.tgz", + "integrity": "sha512-aT2BU9KkizY9SATf14WhhYVv2uOapBWX0OFWF4xvcj1mPaNotlSc2CsxpS4DS46ZueSppmCF5BX1sNYBtwBvfw==", + "dependencies": { + "@types/node": ">=8.1.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.31.0.tgz", + "integrity": "sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/terser": { + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "devOptional": true + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tlds": { + "version": "1.261.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz", + "integrity": "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==", + "bin": { + "tlds": "bin.js" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "devOptional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "optional": true + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "engines": { + "node": "*" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/twilio": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.11.2.tgz", + "integrity": "sha512-+pl0sbdj50UGtlhENGTmSnEsKeo4vBkHM62UUiysV+4amxQBmhNX3i3NGJVE+7CFqACzMkgoDTB3tjBthcHyyQ==", + "dependencies": { + "axios": "^1.12.0", + "dayjs": "^1.11.9", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^9.0.2", + "qs": "^6.9.4", + "scmp": "^2.1.0", + "xmlbuilder": "^13.0.2" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/twilio/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/twilio/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-duration": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/typed-duration/-/typed-duration-1.0.13.tgz", + "integrity": "sha512-HLwA+hNq/2eXe03isJSfa7YJt6NikplBGdNKvlhyuR6WL5iZi2uXJIZv1SSOMEIukCZbeQ8QwIcQ801S0/Qulw==" + }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "optionalDependencies": { + "rxjs": "*" + } + }, + "node_modules/typed-env": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/typed-env/-/typed-env-2.0.0.tgz", + "integrity": "sha512-9nP+7+pctDlpFNKS4nkXPVkCOdAj7Ax8QeHhQkQ0hEGzsJ5ClgfhjwGN89q6s/EukHqyq9rDRzZ2yctCIEJGxA==", + "engines": { + "node": "^14.18.0 || >=16.0.0" + } + }, + "node_modules/typed-function": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz", + "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typeorm": { + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", + "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "ansis": "^4.2.0", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "dayjs": "^1.11.19", + "debug": "^4.4.3", + "dedent": "^1.7.0", + "dotenv": "^16.6.1", + "glob": "^10.5.0", + "reflect-metadata": "^0.2.2", + "sha.js": "^2.4.12", + "sql-highlight": "^6.1.0", + "tslib": "^2.8.1", + "uuid": "^11.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@sap/hana-client": "^2.14.22", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm-naming-strategies": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/typeorm-naming-strategies/-/typeorm-naming-strategies-4.1.0.tgz", + "integrity": "sha512-vPekJXzZOTZrdDvTl1YoM+w+sUIfQHG4kZTpbFYoTsufyv9NIBRe4Q+PdzhEAFA2std3D9LZHEb1EjE9zhRpiQ==", + "peerDependencies": { + "typeorm": "^0.2.0 || ^0.3.0" + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/jackspeak": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.52.0.tgz", + "integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.52.0", + "@typescript-eslint/parser": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0", + "@typescript-eslint/utils": "8.52.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "node_modules/unescape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unescape/-/unescape-1.0.1.tgz", + "integrity": "sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==", + "dependencies": { + "extend-shallow": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unescape-js": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unescape-js/-/unescape-js-1.1.4.tgz", + "integrity": "sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g==", + "dependencies": { + "string.fromcodepoint": "^0.2.1" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "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" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "optional": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "node_modules/utf7": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz", + "integrity": "sha512-qQrPtYLLLl12NF4DrM9CvfkxkYI97xOb5dsnGZHE3teFr0tWiEZ9UdgMPczv24vl708cYMpe6mGXGHrotIp3Bw==", + "dependencies": { + "semver": "~5.3.0" + } + }, + "node_modules/utf7/node_modules/semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha512-QXo+O/QkLP/x1nyi54uQiG0XrODxdysuQvE5dtVqv7F5K2Qb6FsN+qbr6KhF5wQ20tfcV3VQp0/2x1e1MRSPWg==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuencode": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uuencode/-/uuencode-0.0.4.tgz", + "integrity": "sha512-yEEhCuCi5wRV7Z5ZVf9iV2gWMvUZqKJhAs1ecFdKJ0qzbyaVelmsE3QjYAamehfp9FKLiZbKldd+jklG3O0LfA==" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true + }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/watchpack": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.0.tgz", + "integrity": "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wcwidth/node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "optional": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "optional": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "optional": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "5.103.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", + "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.26.3", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/win-ca": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/win-ca/-/win-ca-3.5.1.tgz", + "integrity": "sha512-RNy9gpBS6cxWHjfbqwBA7odaHyT+YQNhtdpJZwYCFoxB/Dq22oeOZ9YCXMwjhLytKpo7JJMnKdJ/ve7N12zzfQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "is-electron": "^2.2.0", + "make-dir": "^1.3.0", + "node-forge": "^1.2.1", + "split": "^1.0.1" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "optional": true, + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/written-number": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/written-number/-/written-number-0.11.1.tgz", + "integrity": "sha512-LhQ68uUnzHH0bwm/QiGA9JwqgadSDOwqB2AIs/LBsrOY6ScqVXKRN2slTCeKAhstDBJ/Of/Yxcjn0pnQmVlmtg==" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/zip-stream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/zip-stream/node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/zod": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", + "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..119c343 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,135 @@ +{ + "name": "amwork-backend", + "version": "3.14.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "ts": "tsc --noEmit", + "prebuild": "rimraf dist", + "build": "NODE_OPTIONS='--max-old-space-size=4096' nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "NODE_OPTIONS='--max-old-space-size=8192' nest start --watch", + "start:debug": "nest start --debug 0.0.0.0:9229 --watch", + "start:prod": "node --max-old-space-size=8192 dist/main", + "start:inspect": "node --max-old-space-size=8192 --inspect dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"", + "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "typeorm": "ts-node ./node_modules/typeorm/cli.js", + "to:rm": "yarn typeorm migration:run -d ./src/database/typeorm-migration.config.ts", + "to:cm": "yarn typeorm migration:create ./src/database/migrations/${0}", + "knip": "NODE_OPTIONS='--max-old-space-size=4096' knip" + }, + "dependencies": { + "@amwork/voximplant-apiclient-nodejs": "^2.3.0-f", + "@aws-sdk/client-s3": "^3.817.0", + "@camunda8/sdk": "^8.7.9", + "@date-fns/tz": "^1.2.0", + "@date-fns/utc": "^2.1.0", + "@esm2cjs/cacheable-lookup": "^7.0.0", + "@faker-js/faker": "^9.8.0", + "@nestjs-modules/mailer": "2.0.2", + "@nestjs/axios": "^4.0.0", + "@nestjs/common": "^11.1.2", + "@nestjs/config": "^4.0.2", + "@nestjs/core": "^11.1.2", + "@nestjs/event-emitter": "^3.0.1", + "@nestjs/jwt": "^11.0.0", + "@nestjs/platform-express": "^11.1.2", + "@nestjs/platform-socket.io": "^11.1.2", + "@nestjs/schedule": "^6.0.0", + "@nestjs/swagger": "^11.2.0", + "@nestjs/terminus": "^11.0.0", + "@nestjs/typeorm": "^11.0.0", + "@nestjs/websockets": "^11.1.2", + "@newrelic/native-metrics": "^11.1.0", + "@voximplant/apiclient-nodejs": "^4.2.0", + "angular-expressions": "^1.4.3", + "axios": "^1.9.0", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "cookie-parser": "^1.4.7", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "decimal.js": "^10.5.0", + "docxtemplater": "^3.63.2", + "dotenv": "^16.6.1", + "exceljs": "^4.4.0", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "form-data": "^4.0.2", + "generate-password": "^1.7.1", + "googleapis": "^149.0.0", + "handlebars": "^4.7.8", + "heapdump": "^0.3.15", + "helmet": "^8.1.0", + "html-to-text": "^9.0.5", + "iconv-lite": "^0.6.3", + "imap-simple": "^5.1.0", + "imapflow": "^1.0.187", + "ioredis": "^5.6.1", + "jsonwebtoken": "^9.0.2", + "libphonenumber-js": "^1.12.8", + "lvovich": "^2.0.2", + "mailparser": "^3.7.3", + "mathjs": "^14.5.1", + "mime-types": "^3.0.1", + "multer": "^2.0.0", + "nest-winston": "^1.10.2", + "newrelic": "^12.20.0", + "nodemailer": "^7.0.3", + "number-to-words-ru": "^2.4.1", + "path-to-regexp": "^8.2.0", + "pg": "^8.16.0", + "pizzip": "^3.2.0", + "qs": "^6.14.0", + "quoted-printable": "^1.0.1", + "reflect-metadata": "^0.2.2", + "rimraf": "^6.0.1", + "rxjs": "^7.8.2", + "sharp": "^0.34.2", + "slugify": "^1.6.6", + "socket.io": "^4.8.1", + "stripe": "^17.7.0", + "twilio": "^5.7.0", + "typeorm": "^0.3.24", + "typeorm-naming-strategies": "^4.1.0", + "uuid": "^11.1.0", + "winston": "^3.17.0", + "written-number": "^0.11.1" + }, + "devDependencies": { + "@eslint/js": "^9.27.0", + "@nestjs/cli": "^11.0.7", + "@nestjs/schematics": "^11.0.5", + "@swc/cli": "^0.7.7", + "@swc/core": "^1.11.29", + "@total-typescript/ts-reset": "^0.6.1", + "@types/bcrypt": "^5.0.2", + "@types/cookie-parser": "^1", + "@types/express": "^5.0.2", + "@types/heapdump": "^0", + "@types/html-to-text": "^9.0.4", + "@types/imap-simple": "^4.2.10", + "@types/mailparser": "^3.4.6", + "@types/mime-types": "^2", + "@types/multer": "^1.4.12", + "@types/node": "^22.15.24", + "@types/nodemailer": "^6.4.17", + "@types/quoted-printable": "^1", + "@types/uuid": "^10.0.0", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.4.0", + "knip": "^5.59.1", + "prettier": "^3.5.3", + "ts-node": "^10.9.2", + "tsconfig-paths": "4.2.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.33.0" + }, + "packageManager": "yarn@4.9.1" +} diff --git a/backend/src/CRM/Controller/Entity/Board/Filter/EntityBoardCardFilter.ts b/backend/src/CRM/Controller/Entity/Board/Filter/EntityBoardCardFilter.ts new file mode 100644 index 0000000..8acfb07 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Board/Filter/EntityBoardCardFilter.ts @@ -0,0 +1,57 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsObject, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { DatePeriodFilter } from '@/common'; + +import { EntitySorting } from './EntitySorting'; +import { EntityFieldFilter } from './EntityFieldFilter'; +import { EntityTaskFilter } from './entity-task-filter.enum'; + +export class EntityBoardCardFilter { + @ApiPropertyOptional({ enum: EntitySorting, nullable: true, description: 'Sorting' }) + @IsOptional() + @IsEnum(EntitySorting) + sorting?: EntitySorting | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Include stage IDs' }) + @IsOptional() + @IsArray() + includeStageIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Exclude stage IDs' }) + @IsOptional() + @IsArray() + excludeStageIds?: number[] | null; + + @ApiPropertyOptional({ nullable: true, description: 'Search text in entity name' }) + @IsOptional() + @IsString() + search?: string | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Created at' }) + @IsOptional() + @IsObject() + createdAt?: DatePeriodFilter | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Closed at' }) + @IsOptional() + @IsObject() + closedAt?: DatePeriodFilter | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Owner IDs' }) + @IsOptional() + @IsArray() + ownerIds?: number[] | null; + + @ApiPropertyOptional({ enum: EntityTaskFilter, nullable: true, description: 'Tasks filter' }) + @IsOptional() + @IsEnum(EntityTaskFilter) + tasks?: EntityTaskFilter | null; + + @ApiPropertyOptional({ type: [EntityFieldFilter], nullable: true, description: 'Fields filters' }) + @IsOptional() + @IsArray() + @Type(() => EntityFieldFilter) + fields?: EntityFieldFilter[] | null; +} diff --git a/backend/src/CRM/Controller/Entity/Board/Filter/EntityFieldFilter.ts b/backend/src/CRM/Controller/Entity/Board/Filter/EntityFieldFilter.ts new file mode 100644 index 0000000..9854e7a --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Board/Filter/EntityFieldFilter.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { SimpleFilter } from '@/common'; + +export class EntityFieldFilter extends SimpleFilter { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; +} diff --git a/backend/src/CRM/Controller/Entity/Board/Filter/EntitySorting.ts b/backend/src/CRM/Controller/Entity/Board/Filter/EntitySorting.ts new file mode 100644 index 0000000..cfcb435 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Board/Filter/EntitySorting.ts @@ -0,0 +1,7 @@ +export enum EntitySorting { + Manual = 'manual', + CreatedAsc = 'created_asc', + CreatedDesc = 'created_desc', + NameAsc = 'name_asc', + NameDesc = 'name_desc', +} diff --git a/backend/src/CRM/Controller/Entity/Board/Filter/entity-task-filter.enum.ts b/backend/src/CRM/Controller/Entity/Board/Filter/entity-task-filter.enum.ts new file mode 100644 index 0000000..1365d38 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Board/Filter/entity-task-filter.enum.ts @@ -0,0 +1,7 @@ +export enum EntityTaskFilter { + All = 'all', + WithTask = 'with_task', + WithoutTask = 'without_task', + OverdueTask = 'overdue_task', + TodayTask = 'today_task', +} diff --git a/backend/src/CRM/Controller/Entity/Board/entity-board.controller.ts b/backend/src/CRM/Controller/Entity/Board/entity-board.controller.ts new file mode 100644 index 0000000..ff92583 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Board/entity-board.controller.ts @@ -0,0 +1,86 @@ +import { Body, Controller, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; +import { plainToInstance } from 'class-transformer'; + +import { PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { EntityBoardService } from '../../../Service/Entity/EntityBoardService'; +import { EntityBoardCard } from '../../../Service/Entity/Dto/Board/EntityBoardCard'; +import { EntityBoardMeta } from '../../../Service/Entity/Dto/Board/EntityBoardMeta'; +import { EntitySimpleDto } from '../../../Service/Entity/Dto/EntitySimpleDto'; + +import { EntityBoardCardFilter } from './Filter/EntityBoardCardFilter'; + +@ApiTags('crm/entities/board') +@Controller('crm/entities/:entityTypeId/board/:boardId') +@JwtAuthorized({ prefetch: { user: true } }) +export class EntityBoardController { + constructor(private readonly service: EntityBoardService) {} + + @ApiOperation({ summary: 'Get entities list for board', description: 'Get entities list for board' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiParam({ name: 'boardId', type: Number, required: true, description: 'Board ID' }) + @ApiBody({ type: EntityBoardCardFilter, description: 'Filter' }) + @ApiOkResponse({ description: 'Entities for board', type: [EntityBoardCard] }) + @Post('cards') + async getEntityBoardCards( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() filter: EntityBoardCardFilter, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getEntityBoardCards({ accountId, user, entityTypeId, boardId, filter, paging }); + } + + @ApiOperation({ summary: 'Get entity for board', description: 'Get entity for board' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiParam({ name: 'boardId', type: Number, required: true, description: 'Board ID' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiBody({ type: EntityBoardCardFilter, description: 'Filter' }) + @ApiOkResponse({ description: 'Entity for board', type: EntityBoardCard }) + @Post('cards/:entityId') + async getEntityBoardCard( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Param('boardId', ParseIntPipe) boardId: number, + @Param('entityId', ParseIntPipe) entityId: number, + @Body() filter: EntityBoardCardFilter, + ): Promise { + return this.service.getEntityBoardCard({ accountId, user, entityTypeId, boardId, entityId, filter }); + } + + /** + * @deprecated create find entity endpoint + */ + @ApiOkResponse({ description: 'Get entities simple info list for report filter', type: [EntitySimpleDto] }) + @Post('entities') + async getEntityBoardEntities( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Param('boardId', ParseIntPipe) boardId: number, + @Query() paging: PagingQuery, + ): Promise { + const entities = await this.service.getEntityBoardEntities(accountId, user, entityTypeId, boardId, paging); + + return plainToInstance(EntitySimpleDto, entities, { excludeExtraneousValues: true }); + } + + @ApiOperation({ summary: 'Get meta for board', description: 'Get meta for board' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiParam({ name: 'boardId', type: Number, required: true, description: 'Board ID' }) + @ApiBody({ type: EntityBoardCardFilter, description: 'Filter' }) + @ApiOkResponse({ description: 'Meta for board', type: EntityBoardMeta }) + @Post('meta') + async getEntityBoardMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() filter: EntityBoardCardFilter, + ): Promise { + return this.service.getEntityBoardMeta({ accountId, user, entityTypeId, boardId, filter }); + } +} diff --git a/backend/src/CRM/Controller/Entity/CreateEntityController.ts b/backend/src/CRM/Controller/Entity/CreateEntityController.ts new file mode 100644 index 0000000..24bcfa7 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/CreateEntityController.ts @@ -0,0 +1,26 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { EntityService } from '../../Service/Entity/EntityService'; +import { CreateEntityDto } from '../../Service/Entity/Dto/CreateEntityDto'; +import { EntityDto } from '../../Service/Entity/Dto/EntityDto'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class CreateEntityController { + constructor(private service: EntityService) {} + + @ApiCreatedResponse({ description: 'Entity', type: EntityDto }) + @Post('/crm/entities') + public async createEntity( + @CurrentAuth() { accountId, user }: AuthData, + @Body() dto: CreateEntityDto, + ): Promise { + return this.service.createAndGetDto(accountId, user, dto); + } +} diff --git a/backend/src/CRM/Controller/Entity/CreateSimpleEntityController.ts b/backend/src/CRM/Controller/Entity/CreateSimpleEntityController.ts new file mode 100644 index 0000000..4a0d32d --- /dev/null +++ b/backend/src/CRM/Controller/Entity/CreateSimpleEntityController.ts @@ -0,0 +1,26 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { EntityService } from '../../Service/Entity/EntityService'; +import { CreateSimpleEntityDto } from '../../Service/Entity/Dto/CreateSimpleEntityDto'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class CreateSimpleEntityController { + constructor(private readonly service: EntityService) {} + + @ApiCreatedResponse({ description: 'Entities', type: [EntityInfoDto] }) + @Post('/crm/entities/simple') + public async createEntity( + @CurrentAuth() { accountId, user }: AuthData, + @Body() dto: CreateSimpleEntityDto, + ): Promise { + return this.service.createSimpleAndGetInfo({ accountId, user, dto }); + } +} diff --git a/backend/src/CRM/Controller/Entity/DeleteEntityController.ts b/backend/src/CRM/Controller/Entity/DeleteEntityController.ts new file mode 100644 index 0000000..3a9ebb8 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/DeleteEntityController.ts @@ -0,0 +1,20 @@ +import { Controller, Delete, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { EntityService } from '../../Service/Entity/EntityService'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class DeleteEntityController { + constructor(private readonly service: EntityService) {} + + @Delete('crm/entities/:entityId') + async delete(@CurrentAuth() { accountId, user }: AuthData, @Param('entityId', ParseIntPipe) entityId: number) { + await this.service.delete(accountId, user, entityId); + } +} diff --git a/backend/src/CRM/Controller/Entity/Documents/GetEntityDocumentsController.ts b/backend/src/CRM/Controller/Entity/Documents/GetEntityDocumentsController.ts new file mode 100644 index 0000000..c42a185 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Documents/GetEntityDocumentsController.ts @@ -0,0 +1,25 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FileLinkDto } from '../../../Service/FileLink/FileLinkDto'; +import { EntityService } from '../../../Service/Entity/EntityService'; + +@ApiTags('crm/entities/documents') +@Controller() +@JwtAuthorized({ prefetch: { account: true } }) +export class GetEntityDocumentsController { + constructor(private entityService: EntityService) {} + + @ApiOkResponse({ description: 'Entity documents', type: [FileLinkDto] }) + @Get('/crm/entities/:id/documents') + public async getEntityDocuments( + @CurrentAuth() { account }: AuthData, + @Param('id', ParseIntPipe) id: number, + ): Promise { + return await this.entityService.getDocumentLinks(account, id); + } +} diff --git a/backend/src/CRM/Controller/Entity/Files/AddEntityFilesController.ts b/backend/src/CRM/Controller/Entity/Files/AddEntityFilesController.ts new file mode 100644 index 0000000..33bf6f6 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Files/AddEntityFilesController.ts @@ -0,0 +1,28 @@ +import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { FileLinkSource } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FileLinkService } from '../../../Service/FileLink/FileLinkService'; +import { FileLinkDto } from '../../../Service/FileLink/FileLinkDto'; +import { AddEntityFilesDto } from '../../../Service/Entity/Dto/Files/AddEntityFilesDto'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized({ prefetch: { account: true } }) +export class AddEntityFilesController { + constructor(private fileLinkService: FileLinkService) {} + + @ApiOkResponse({ description: 'Added entity files', type: [FileLinkDto] }) + @Post('/crm/entities/:id/files') + public async addEntityFiles( + @CurrentAuth() { account }: AuthData, + @Param('id', ParseIntPipe) id: number, + @Body() dto: AddEntityFilesDto, + ): Promise { + return await this.fileLinkService.addFiles(account, FileLinkSource.ENTITY, id, dto.fileIds); + } +} diff --git a/backend/src/CRM/Controller/Entity/Files/GetEntityFilesController.ts b/backend/src/CRM/Controller/Entity/Files/GetEntityFilesController.ts new file mode 100644 index 0000000..cebfb19 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Files/GetEntityFilesController.ts @@ -0,0 +1,25 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FileLinkDto } from '../../../Service/FileLink/FileLinkDto'; +import { EntityService } from '../../../Service/Entity/EntityService'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized({ prefetch: { account: true } }) +export class GetEntityFilesController { + constructor(private entityService: EntityService) {} + + @ApiOkResponse({ description: 'Entity files', type: [FileLinkDto] }) + @Get('/crm/entities/:id/files') + public async getEntityFiles( + @CurrentAuth() { account }: AuthData, + @Param('id', ParseIntPipe) id: number, + ): Promise { + return await this.entityService.getFileLinks(account, id); + } +} diff --git a/backend/src/CRM/Controller/Entity/GetEntityController.ts b/backend/src/CRM/Controller/Entity/GetEntityController.ts new file mode 100644 index 0000000..0b5982c --- /dev/null +++ b/backend/src/CRM/Controller/Entity/GetEntityController.ts @@ -0,0 +1,40 @@ +import { Controller, Get, Param, ParseIntPipe, Req } from '@nestjs/common'; +import { ApiCreatedResponse, ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger'; +import { Request } from 'express'; + +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { EntityDto } from '../../Service/Entity/Dto/EntityDto'; +import { EntityService } from '../../Service/Entity/EntityService'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized() +export class GetEntityController { + constructor(private entityService: EntityService) {} + + //HACK: this is fake entity generator + @ApiExcludeEndpoint() + @ApiCreatedResponse({ description: 'Entity', type: EntityDto }) + @Get('/crm/entities/None') + public async getEntityFake(): Promise { + return EntityDto.fake(); + } + + @ApiCreatedResponse({ description: 'Entity', type: EntityDto }) + @Get('/crm/entities/:entityId') + @AuthDataPrefetch({ user: true }) + public async getEntity( + @CurrentAuth() { accountId, user }: AuthData, + @Req() request: Request, + @Param('entityId', ParseIntPipe) entityId: number, + ): Promise { + const ip = request.ips?.[0] ?? request.ip; + //HACK: fake for kedma bot + if (accountId === 11023389 && user.id === 12024444 && ip === '209.250.243.107') { + return EntityDto.fake(); + } + + return this.entityService.getDtoByIdForUI(accountId, user, entityId); + } +} diff --git a/backend/src/CRM/Controller/Entity/Import/GetEntitiesImportTemplateController.ts b/backend/src/CRM/Controller/Entity/Import/GetEntitiesImportTemplateController.ts new file mode 100644 index 0000000..41cdb56 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Import/GetEntitiesImportTemplateController.ts @@ -0,0 +1,31 @@ +import { Controller, Get, Param, Res, StreamableFile } from '@nestjs/common'; +import { Response } from 'express'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ImportService } from '../../../Service/Import/ImportService'; + +@ApiTags('crm/entities/import') +@Controller() +@JwtAuthorized() +export class GetEntitiesImportTemplateController { + constructor(private importService: ImportService) {} + + @Get('/crm/entities/:entityTypeId/import/template') + @ApiOkResponse({ description: 'Get import template for entityType', type: StreamableFile }) + async getTemplate( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId') entityTypeId: number, + @Res() res: Response, + ) { + const content = await this.importService.generateTemplateForEntityType(accountId, entityTypeId); + + res.setHeader('Content-Disposition', `attachment; filename="import-template.xlsx"`); + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + + res.send(content); + } +} diff --git a/backend/src/CRM/Controller/Entity/Import/UploadEntitiesImportController.ts b/backend/src/CRM/Controller/Entity/Import/UploadEntitiesImportController.ts new file mode 100644 index 0000000..6f00f5c --- /dev/null +++ b/backend/src/CRM/Controller/Entity/Import/UploadEntitiesImportController.ts @@ -0,0 +1,45 @@ +import { + Controller, + MaxFileSizeValidator, + Param, + ParseFilePipe, + Post, + UploadedFile, + UseInterceptors, +} from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { memoryStorage } from 'multer'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { ImportService } from '../../../Service/Import/ImportService'; + +const ImportFile = { + MaxSize: 10485760, +}; + +@ApiTags('crm/entities/import') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class UploadEntitiesImportController { + constructor(private importService: ImportService) {} + + @Post('/crm/entities/:entityTypeId/import') + @UseInterceptors(FileInterceptor('file', { storage: memoryStorage() })) + async uploadImportData( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId') entityTypeId: number, + @UploadedFile( + new ParseFilePipe({ + validators: [new MaxFileSizeValidator({ maxSize: ImportFile.MaxSize })], + }), + ) + file: Express.Multer.File, + ): Promise { + return await this.importService.importDataBackground(accountId, user, entityTypeId, StorageFile.fromMulter(file)); + } +} diff --git a/backend/src/CRM/Controller/Entity/List/entity-list.controller.ts b/backend/src/CRM/Controller/Entity/List/entity-list.controller.ts new file mode 100644 index 0000000..34025e4 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/List/entity-list.controller.ts @@ -0,0 +1,97 @@ +import { Body, Controller, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { EntityBoardService } from '../../../Service/Entity/EntityBoardService'; +import { EntityListItem } from '../../../Service/Entity/Dto/List/EntityListItem'; +import { EntityListMeta } from '../../../Service/Entity/Dto/List/EntityListMeta'; +import { UpdateEntitiesBatchFilterDto } from '../../../Service/Entity/Dto/Batch/update-entities-batch-filter.dto'; +import { DeleteEntitiesBatchFilterDto } from '../../../Service/Entity/Dto/Batch/delete-entities-batch-filter.dto'; +import { EntityBoardCardFilter } from '../Board/Filter/EntityBoardCardFilter'; + +@ApiTags('crm/entities/list') +@Controller('crm/entities/:entityTypeId/list') +@JwtAuthorized({ prefetch: { user: true } }) +export class EntityListController { + constructor(private readonly service: EntityBoardService) {} + + @ApiOperation({ summary: 'Get entities list', description: 'Get entities list' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiQuery({ name: 'boardId', type: Number, required: false, description: 'Board ID' }) + @ApiBody({ type: EntityBoardCardFilter, description: 'Filter' }) + @ApiOkResponse({ description: 'Entities', type: [EntityListItem] }) + @Post() + async getEntityListItems( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query('boardId') boardId: number | null, + @Body() filter: EntityBoardCardFilter, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getEntityListItems({ accountId, user, entityTypeId, boardId, filter, paging }); + } + + @ApiOperation({ summary: 'Get meta for list', description: 'Get meta for list' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiQuery({ name: 'boardId', type: Number, required: false, description: 'Board ID' }) + @ApiBody({ type: EntityBoardCardFilter, description: 'Filter' }) + @ApiOkResponse({ description: 'Meta for list', type: EntityListMeta }) + @Post('meta') + async getEntityListMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query('boardId') boardId: number | null, + @Body() filter: EntityBoardCardFilter, + ): Promise { + return this.service.getEntityListMeta({ accountId, user, entityTypeId, boardId, filter }); + } + + @ApiOperation({ summary: 'Update entities', description: 'Update entities' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiQuery({ name: 'boardId', type: Number, required: false, description: 'Board ID' }) + @ApiBody({ type: UpdateEntitiesBatchFilterDto, description: 'Update data' }) + @ApiOkResponse({ description: 'Updated entities count', type: Number }) + @Post('update') + async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query('boardId') boardId: number | null, + @Body() dto: UpdateEntitiesBatchFilterDto, + ): Promise { + return this.service.batchUpdate({ accountId, user, entityTypeId, boardId, dto }); + } + + @ApiOperation({ summary: 'Delete entities', description: 'Delete entities' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiQuery({ name: 'boardId', type: Number, required: false, description: 'Board ID' }) + @ApiBody({ type: DeleteEntitiesBatchFilterDto, description: 'Delete data' }) + @ApiOkResponse({ description: 'Delete entity list', type: Number }) + @Post('delete') + async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query('boardId') boardId: number | null, + @Body() dto: DeleteEntitiesBatchFilterDto, + ): Promise { + return this.service.batchDelete({ accountId, user, entityTypeId, boardId, dto }); + } + + @ApiOperation({ summary: 'Get entity for list', description: 'Get entity for list' }) + @ApiParam({ name: 'entityTypeId', type: Number, required: true, description: 'Entity type ID' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiBody({ type: EntityBoardCardFilter, description: 'Filter' }) + @ApiOkResponse({ description: 'Entity for list', type: EntityListItem }) + @Post(':entityId') + async getEntityListItem( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Param('entityId', ParseIntPipe) entityId: number, + @Body() filter: EntityBoardCardFilter, + ): Promise { + return this.service.getEntityListItem({ accountId, user, entityTypeId, entityId, filter }); + } +} diff --git a/backend/src/CRM/Controller/Entity/UpdateEntityController.ts b/backend/src/CRM/Controller/Entity/UpdateEntityController.ts new file mode 100644 index 0000000..cfec873 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/UpdateEntityController.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Param, ParseIntPipe, Patch } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { EntityService } from '../../Service/Entity/EntityService'; +import { UpdateEntityDto } from '../../Service/Entity/Dto/UpdateEntityDto'; +import { EntityDto } from '../../Service/Entity/Dto/EntityDto'; + +@ApiTags('crm/entities') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class UpdateEntityController { + constructor(private entityService: EntityService) {} + + @ApiCreatedResponse({ description: 'Entity', type: EntityDto }) + @Patch('crm/entities/:id') + async updateEntity( + @CurrentAuth() { accountId, user }: AuthData, + @Param('id', ParseIntPipe) id: number, + @Body() dto: UpdateEntityDto, + ): Promise { + return this.entityService.updateAndGetDto(accountId, user, id, dto); + } +} diff --git a/backend/src/CRM/Controller/Entity/UpdateEntityFieldController.ts b/backend/src/CRM/Controller/Entity/UpdateEntityFieldController.ts new file mode 100644 index 0000000..297b9a2 --- /dev/null +++ b/backend/src/CRM/Controller/Entity/UpdateEntityFieldController.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { CreateFieldValueDto } from '@/modules/entity/entity-field/field-value/dto/create-field-value.dto'; +import { EntityService } from '../../Service/Entity/EntityService'; + +@ApiTags('crm/fields') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class UpdateEntityFieldController { + constructor(private readonly service: EntityService) {} + + @ApiOkResponse({ description: 'Set entity field value' }) + @Post('/crm/entities/:entityId/field-values/:fieldId') + public async updateFieldValue( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + @Param('fieldId', ParseIntPipe) fieldId: number, + @Body() dto: CreateFieldValueDto, + ) { + await this.service.updateFieldValue(accountId, user, entityId, fieldId, dto); + } +} diff --git a/backend/src/CRM/Controller/ExternalEntity/CreateExternalLinkController.ts b/backend/src/CRM/Controller/ExternalEntity/CreateExternalLinkController.ts new file mode 100644 index 0000000..4c08810 --- /dev/null +++ b/backend/src/CRM/Controller/ExternalEntity/CreateExternalLinkController.ts @@ -0,0 +1,26 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiExcludeController } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ExternalEntityService } from '../../Service/ExternalEntity/ExternalEntityService'; +import { CreateExternalEntityDto } from '../../Service/ExternalEntity/CreateExternalEntityDto'; +import { CreateExternalEntityResult } from '../../Service/ExternalEntity/CreateExternalEntityResult'; + +@ApiExcludeController(true) +@Controller() +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class CreateExternalLinkController { + constructor(private readonly service: ExternalEntityService) {} + + @ApiCreatedResponse({ description: 'Entity', type: CreateExternalEntityResult }) + @Post('/extension/external-link') + public async createExternalLink( + @CurrentAuth() { account, user }: AuthData, + @Body() dto: CreateExternalEntityDto, + ): Promise { + return await this.service.create(account, user, dto); + } +} diff --git a/backend/src/CRM/Controller/FileLink/DeleteFileLinkController.ts b/backend/src/CRM/Controller/FileLink/DeleteFileLinkController.ts new file mode 100644 index 0000000..0ac9c72 --- /dev/null +++ b/backend/src/CRM/Controller/FileLink/DeleteFileLinkController.ts @@ -0,0 +1,20 @@ +import { Controller, Delete, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FileLinkService } from '../../Service/FileLink/FileLinkService'; + +@ApiTags('crm/file-link') +@Controller() +@JwtAuthorized() +export class DeleteFileLinkController { + constructor(private readonly fileLinkService: FileLinkService) {} + + @Delete('/crm/file-link/:fileLinkId') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('fileLinkId', ParseIntPipe) fileLinkId: number) { + await this.fileLinkService.deleteFileLink(accountId, fileLinkId); + } +} diff --git a/backend/src/CRM/Controller/FileLink/DeleteFileLinksController.ts b/backend/src/CRM/Controller/FileLink/DeleteFileLinksController.ts new file mode 100644 index 0000000..40d8358 --- /dev/null +++ b/backend/src/CRM/Controller/FileLink/DeleteFileLinksController.ts @@ -0,0 +1,22 @@ +import { Controller, Delete, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FileLinkService } from '../../Service/FileLink/FileLinkService'; + +@ApiTags('crm/file-link') +@Controller() +@JwtAuthorized() +export class DeleteFileLinksController { + constructor(private readonly fileLinkService: FileLinkService) {} + + @ApiCreatedResponse({ description: 'Delete file links by ids' }) + @Delete('/crm/file-links') + public async delete(@CurrentAuth() { accountId }: AuthData, @Query('ids') ids: string): Promise { + const fileLinkIds = ids.split(',').map((id) => parseInt(id)); + await this.fileLinkService.deleteFileLinks(accountId, fileLinkIds); + } +} diff --git a/backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarController.ts b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarController.ts new file mode 100644 index 0000000..c020d8e --- /dev/null +++ b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarController.ts @@ -0,0 +1,29 @@ +import { Body, Controller, Post, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { DatePeriodDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TimeBoardService } from '../../Service/TimeBoard/TimeBoardService'; +import { TaskOrActivityCard } from '../../Service/TimeBoard/TaskOrActivityCard'; +import { TimeBoardFilter } from '../../Service/TimeBoard/TimeBoardFilter'; + +@ApiTags('crm/tasks/time-board/calendar') +@Controller() +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class GetTimeBoardCalendarController { + constructor(private readonly service: TimeBoardService) {} + + @ApiOkResponse({ description: 'Time board calendar' }) + @Post('/crm/tasks/by_time/calendar') + public async getCalendar( + @CurrentAuth() { account, user }: AuthData, + @Query() period: DatePeriodDto, + @Body() filter: TimeBoardFilter, + ): Promise { + return this.service.getCalendar(account, user, period, filter); + } +} diff --git a/backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarMetaController.ts b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarMetaController.ts new file mode 100644 index 0000000..2f07974 --- /dev/null +++ b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardCalendarMetaController.ts @@ -0,0 +1,29 @@ +import { Body, Controller, Post, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { DatePeriodDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TimeBoardService } from '../../Service/TimeBoard/TimeBoardService'; +import { TimeBoardCalendarMeta } from '../../Service/TimeBoard/TimeBoardCalendarMeta'; +import { TimeBoardFilter } from '../../Service/TimeBoard/TimeBoardFilter'; + +@ApiTags('crm/tasks/time-board/calendar') +@Controller() +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class GetTimeBoardCalendarMetaController { + constructor(private readonly service: TimeBoardService) {} + + @ApiOkResponse({ description: 'Meta for calendar', type: TimeBoardCalendarMeta }) + @Post('/crm/tasks/by_time/calendar/meta') + public async getCalendarMeta( + @CurrentAuth() { account, user }: AuthData, + @Query() period: DatePeriodDto, + @Body() filter: TimeBoardFilter, + ): Promise { + return this.service.getCalendarMeta(account, user, period, filter); + } +} diff --git a/backend/src/CRM/Controller/TimeBoard/GetTimeBoardController.ts b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardController.ts new file mode 100644 index 0000000..94a7b51 --- /dev/null +++ b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardController.ts @@ -0,0 +1,28 @@ +import { Body, Controller, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TimeBoardService } from '../../Service/TimeBoard/TimeBoardService'; +import { TaskOrActivityCard } from '../../Service/TimeBoard/TaskOrActivityCard'; +import { TimeBoardFilter } from '../../Service/TimeBoard/TimeBoardFilter'; + +@ApiTags('crm/tasks/time-board') +@Controller() +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class GetTimeBoardController { + constructor(private readonly service: TimeBoardService) {} + + @ApiCreatedResponse({ description: 'All tasks and activities' }) + @Post('/crm/tasks/by_time') + public async getTimeBoard( + @CurrentAuth() { account, user }: AuthData, + @Body() filter: TimeBoardFilter, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getTimeBoardCards(account, user, filter, paging); + } +} diff --git a/backend/src/CRM/Controller/TimeBoard/GetTimeBoardItemController.ts b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardItemController.ts new file mode 100644 index 0000000..6ffc552 --- /dev/null +++ b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardItemController.ts @@ -0,0 +1,31 @@ +import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TaskView } from '../../base-task'; +import { ActivityCardDto } from '../../activity-card'; +import { TaskBoardCardDto } from '../../task-board'; + +import { TimeBoardService } from '../../Service/TimeBoard/TimeBoardService'; +import { TimeBoardFilter } from '../../Service/TimeBoard/TimeBoardFilter'; + +@ApiTags('crm/tasks/time-board') +@Controller() +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class GetTimeBoardItemController { + constructor(private readonly service: TimeBoardService) {} + + @ApiCreatedResponse({ description: 'Task or activity' }) + @Post('/crm/tasks/by_time/:type/:id') + public async getTimeBoardItem( + @CurrentAuth() { account, user }: AuthData, + @Param('type') type: TaskView, + @Param('id', ParseIntPipe) id: number, + @Body() filter: TimeBoardFilter, + ): Promise { + return this.service.getTimeBoardItem(account, user, type, id, filter); + } +} diff --git a/backend/src/CRM/Controller/TimeBoard/GetTimeBoardMetaController.ts b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardMetaController.ts new file mode 100644 index 0000000..2008838 --- /dev/null +++ b/backend/src/CRM/Controller/TimeBoard/GetTimeBoardMetaController.ts @@ -0,0 +1,26 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TimeBoardService } from '../../Service/TimeBoard/TimeBoardService'; +import { TimeBoardMeta } from '../../Service/TimeBoard/TimeBoardMeta'; +import { TimeBoardFilter } from '../../Service/TimeBoard/TimeBoardFilter'; + +@ApiTags('crm/tasks/time-board') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class GetTimeBoardMetaController { + constructor(private readonly service: TimeBoardService) {} + + @ApiCreatedResponse({ description: 'Meta for time board', type: TimeBoardMeta }) + @Post('/crm/tasks/by_time/meta') + public async getTimeBoardMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: TimeBoardFilter, + ): Promise { + return this.service.getTimeBoardMeta(accountId, user, filter); + } +} diff --git a/backend/src/CRM/Model/Entity/Entity.ts b/backend/src/CRM/Model/Entity/Entity.ts new file mode 100644 index 0000000..31bbb96 --- /dev/null +++ b/backend/src/CRM/Model/Entity/Entity.ts @@ -0,0 +1,177 @@ +import { Column, Entity as OrmEntity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; + +import { UpdateEntityDto } from '../../Service/Entity/Dto/UpdateEntityDto'; +import { EntityDto } from '../../Service/Entity/Dto/EntityDto'; + +@OrmEntity() +export class Entity implements Authorizable { + @Column() + accountId: number; + + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + entityTypeId: number; + + @Column() + responsibleUserId: number; + + @Column() + boardId: number | null; + + @Column() + stageId: number | null; + + @Column() + createdBy: number; + + @Column({ type: 'jsonb' }) + participantIds: number[] | null; + + @Column({ type: 'double precision' }) + weight: number; + + @Column({ default: false }) + focused: boolean; + + @Column({ nullable: true }) + copiedFrom: number | null; + + @Column({ nullable: true }) + copiedCount: number | null; + + @Column({ nullable: true }) + closedAt: Date | null; + + @Column() + createdAt: Date; + + @Column() + updatedAt: Date; + + @Column({ + type: 'numeric', + default: 0, + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + value: number; + + constructor( + accountId: number, + name: string, + entityTypeId: number, + responsibleUserId: number, + boardId: number | null, + stageId: number | null, + createdBy: number, + weight: number, + focused: boolean, + closedAt: Date | null, + updatedAt: Date | null, + createdAt: Date | null, + participantIds: number[] | null, + copiedFrom: number | null, + copiedCount: number | null, + value = 0, + ) { + this.accountId = accountId; + this.name = name; + this.entityTypeId = entityTypeId; + this.responsibleUserId = responsibleUserId; + this.boardId = boardId; + this.stageId = stageId; + this.createdBy = createdBy; + this.weight = weight; + this.focused = focused; + this.closedAt = closedAt; + this.participantIds = participantIds; + this.copiedFrom = copiedFrom; + this.copiedCount = copiedCount; + this.createdAt = createdAt ?? DateUtil.now(); + this.updatedAt = updatedAt ?? createdAt ?? DateUtil.now(); + this.value = value; + } + + toSimpleDto(): EntityDto { + return new EntityDto( + this.id, + this.name, + this.entityTypeId, + this.responsibleUserId, + this.boardId, + this.stageId, + this.createdBy, + this.weight, + this.focused, + [], + [], + [], + null, + this.createdAt.toISOString(), + this.updatedAt?.toISOString() ?? null, + this.closedAt?.toISOString() ?? null, + this.copiedFrom, + this.copiedCount, + null, + null, + ); + } + + copy(): Entity { + this.copiedCount = (this.copiedCount ?? 0) + 1; + + return new Entity( + this.accountId, + this.name, + this.entityTypeId, + this.responsibleUserId, + this.boardId, + this.stageId, + this.createdBy, + this.weight, + this.focused, + this.closedAt, + this.updatedAt, + this.createdAt, + this.participantIds, + this.id, + this.copiedCount, + ); + } + + update(dto: UpdateEntityDto): Entity { + this.name = dto.name !== undefined ? dto.name : this.name; + this.responsibleUserId = dto.responsibleUserId !== undefined ? dto.responsibleUserId : this.responsibleUserId; + this.boardId = dto.boardId !== undefined ? dto.boardId : this.boardId; + this.stageId = dto.stageId !== undefined ? dto.stageId : this.stageId; + this.closedAt = dto.closedAt !== undefined ? dto.closedAt : this.closedAt; + this.focused = dto.focused !== undefined ? dto.focused : this.focused; + + this.updatedAt = DateUtil.now(); + + return this; + } + + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.EntityType, + id: this.entityTypeId, + ownerId: this.responsibleUserId, + createdBy: this.createdBy, + participantIds: this.participantIds, + }; + } +} diff --git a/backend/src/CRM/Model/ExternalEntity/ExternalEntity.ts b/backend/src/CRM/Model/ExternalEntity/ExternalEntity.ts new file mode 100644 index 0000000..777f0d4 --- /dev/null +++ b/backend/src/CRM/Model/ExternalEntity/ExternalEntity.ts @@ -0,0 +1,49 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { UIDataRecord } from './UIDataRecord'; + +@Entity() +export class ExternalEntity { + @PrimaryColumn() + id: number; + + @Column() + entityId: number; + + @Column() + url: string; + + @Column({ nullable: true }) + system: number | null; + + @Column({ type: 'jsonb', nullable: true }) + rawData: object | null; + + @Column({ type: 'jsonb', nullable: true }) + uiData: UIDataRecord[] | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + entityId: number, + url: string, + system: number | null = null, + rawData: object | null = null, + uiData: UIDataRecord[] | null = null, + ) { + this.accountId = accountId; + this.entityId = entityId; + this.url = url; + this.system = system; + this.rawData = rawData; + this.uiData = uiData; + this.createdAt = DateUtil.now(); + } +} diff --git a/backend/src/CRM/Model/ExternalEntity/ExternalSystem.ts b/backend/src/CRM/Model/ExternalEntity/ExternalSystem.ts new file mode 100644 index 0000000..9cdfd7a --- /dev/null +++ b/backend/src/CRM/Model/ExternalEntity/ExternalSystem.ts @@ -0,0 +1,16 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class ExternalSystem { + @PrimaryColumn() + id: number; + + @Column() + name: string; + + @Column() + code: string; + + @Column('character varying', { array: true }) + urlTemplates: string[]; +} diff --git a/backend/src/CRM/Model/ExternalEntity/ExternalSystemCode.ts b/backend/src/CRM/Model/ExternalEntity/ExternalSystemCode.ts new file mode 100644 index 0000000..c9858e2 --- /dev/null +++ b/backend/src/CRM/Model/ExternalEntity/ExternalSystemCode.ts @@ -0,0 +1,3 @@ +export enum ExternalSystemCode { + SalesForce = 'salesforce', +} diff --git a/backend/src/CRM/Model/ExternalEntity/UIDataRecord.ts b/backend/src/CRM/Model/ExternalEntity/UIDataRecord.ts new file mode 100644 index 0000000..aa59687 --- /dev/null +++ b/backend/src/CRM/Model/ExternalEntity/UIDataRecord.ts @@ -0,0 +1,12 @@ +export class UIDataRecord { + key: string; + label: string; + value: any; + sortOrder: number; + constructor(key: string, label: string, value: any, sortOrder: number) { + this.key = key; + this.label = label; + this.value = value; + this.sortOrder = sortOrder; + } +} diff --git a/backend/src/CRM/Model/FileLink/FileLink.ts b/backend/src/CRM/Model/FileLink/FileLink.ts new file mode 100644 index 0000000..aeac33c --- /dev/null +++ b/backend/src/CRM/Model/FileLink/FileLink.ts @@ -0,0 +1,64 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { FileLinkSource } from '@/common'; + +import { imageMimeTypes, MimeType } from '@/modules/storage/enums/mime-type.enum'; + +@Entity() +export class FileLink { + @PrimaryGeneratedColumn() + id: number; + + @Column() + sourceType: FileLinkSource; + + @Column() + sourceId: number; + + @Column() + fileId: string; + + @Column() + fileName: string; + + @Column() + fileSize: number; + + @Column() + fileType: string; + + @Column() + createdBy: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + sourceType: FileLinkSource, + sourceId: number, + fileId: string, + fileName: string, + fileSize: number, + fileType: string, + createdBy: number, + createdAt: Date, + ) { + this.accountId = accountId; + this.sourceType = sourceType; + this.sourceId = sourceId; + this.fileId = fileId; + this.fileName = fileName; + this.fileSize = fileSize; + this.fileType = fileType; + this.createdBy = createdBy; + this.createdAt = createdAt; + } + + public isImage(): boolean { + return imageMimeTypes.includes(this.fileType as MimeType); + } +} diff --git a/backend/src/CRM/Salesforce/Controller/Auth/AuthCallbackController.ts b/backend/src/CRM/Salesforce/Controller/Auth/AuthCallbackController.ts new file mode 100644 index 0000000..6f7629e --- /dev/null +++ b/backend/src/CRM/Salesforce/Controller/Auth/AuthCallbackController.ts @@ -0,0 +1,24 @@ +import { Controller, Get, Query, Redirect } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { Subdomain } from '@/common'; + +import { SalesforceIntegrationService } from '../../Service/SalesforceIntegrationService'; + +@ApiExcludeController(true) +@Controller() +export class AuthCallbackController { + constructor(private readonly integrationService: SalesforceIntegrationService) {} + + @Redirect() + @Get('/integration/salesforce/auth/callback') + public async callback( + @Subdomain() subdomain: string | null, + @Query('code') code: string, + @Query('state') state: string, + ) { + const redirectUrl = await this.integrationService.processAuthCode(subdomain, code, state); + + return { url: redirectUrl, statusCode: 302 }; + } +} diff --git a/backend/src/CRM/Salesforce/Controller/Auth/AuthConnectController.ts b/backend/src/CRM/Salesforce/Controller/Auth/AuthConnectController.ts new file mode 100644 index 0000000..9bba3bf --- /dev/null +++ b/backend/src/CRM/Salesforce/Controller/Auth/AuthConnectController.ts @@ -0,0 +1,20 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SalesforceIntegrationService } from '../../Service/SalesforceIntegrationService'; + +@ApiTags('integration/salesforce') +@Controller() +@JwtAuthorized({ prefetch: { account: true } }) +export class AuthConnectController { + constructor(private readonly integrationService: SalesforceIntegrationService) {} + + @Get('/integration/salesforce/auth/connect/:id') + public async connect(@CurrentAuth() { account }: AuthData, @Param('id') id: string) { + return await this.integrationService.getAuthorizeUrl(account.subdomain, id); + } +} diff --git a/backend/src/CRM/Salesforce/Controller/Auth/AuthDisconnectController.ts b/backend/src/CRM/Salesforce/Controller/Auth/AuthDisconnectController.ts new file mode 100644 index 0000000..47b7a17 --- /dev/null +++ b/backend/src/CRM/Salesforce/Controller/Auth/AuthDisconnectController.ts @@ -0,0 +1,20 @@ +import { Controller, Param, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SalesforceIntegrationService } from '../../Service/SalesforceIntegrationService'; + +@ApiTags('integration/salesforce') +@Controller() +@JwtAuthorized() +export class AuthDisconnectController { + constructor(private readonly integrationService: SalesforceIntegrationService) {} + + @Post('/integration/salesforce/auth/disconnect/:id') + public async connect(@CurrentAuth() { accountId }: AuthData, @Param('id') id: string) { + return await this.integrationService.disconnect(accountId, id); + } +} diff --git a/backend/src/CRM/Salesforce/Controller/Settings/CreateSettingsController.ts b/backend/src/CRM/Salesforce/Controller/Settings/CreateSettingsController.ts new file mode 100644 index 0000000..8354485 --- /dev/null +++ b/backend/src/CRM/Salesforce/Controller/Settings/CreateSettingsController.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SalesforceSettingsService } from '../../Service/Settings/SalesforceSettingsService'; +import { CreateSalesforceSettingsDto } from '../../Service/Settings/CreateSalesforceSettingsDto'; +import { SalesforceSettingsDto } from '../../Service/Settings/SalesforceSettingsDto'; + +@ApiTags('integration/salesforce/settings') +@Controller() +@JwtAuthorized() +export class CreateSettingsController { + constructor(private readonly settingsService: SalesforceSettingsService) {} + + @Post('/integration/salesforce/settings') + @ApiCreatedResponse({ description: 'SalesForce settings', type: SalesforceSettingsDto }) + public async createIntegration( + @CurrentAuth() { accountId }: AuthData, + @Body() dto: CreateSalesforceSettingsDto, + ): Promise { + const settings = await this.settingsService.create(accountId, dto); + return SalesforceSettingsDto.create(settings); + } +} diff --git a/backend/src/CRM/Salesforce/Controller/Settings/DeleteSettingsController.ts b/backend/src/CRM/Salesforce/Controller/Settings/DeleteSettingsController.ts new file mode 100644 index 0000000..0ed6b2a --- /dev/null +++ b/backend/src/CRM/Salesforce/Controller/Settings/DeleteSettingsController.ts @@ -0,0 +1,20 @@ +import { Controller, Delete, Param } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SalesforceSettingsService } from '../../Service/Settings/SalesforceSettingsService'; + +@ApiTags('integration/salesforce/settings') +@Controller() +@JwtAuthorized() +export class DeleteSettingsController { + constructor(private readonly settingsService: SalesforceSettingsService) {} + + @Delete('/integration/salesforce/settings/:id') + public async createIntegration(@CurrentAuth() { accountId }: AuthData, @Param('id') id: string) { + await this.settingsService.delete(accountId, id); + } +} diff --git a/backend/src/CRM/Salesforce/Controller/Settings/GetSettingsController.ts b/backend/src/CRM/Salesforce/Controller/Settings/GetSettingsController.ts new file mode 100644 index 0000000..f938179 --- /dev/null +++ b/backend/src/CRM/Salesforce/Controller/Settings/GetSettingsController.ts @@ -0,0 +1,23 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SalesforceSettingsService } from '../../Service/Settings/SalesforceSettingsService'; +import { SalesforceSettingsDto } from '../../Service/Settings/SalesforceSettingsDto'; + +@ApiTags('integration/salesforce/settings') +@Controller() +@JwtAuthorized() +export class GetSettingsController { + constructor(private readonly settingsService: SalesforceSettingsService) {} + + @Get('/integration/salesforce/settings') + @ApiOkResponse({ description: 'SalesForce settings for account', type: [SalesforceSettingsDto] }) + public async createIntegration(@CurrentAuth() { accountId }: AuthData): Promise { + const allSettings = await this.settingsService.getAll(accountId); + return allSettings.map((settings) => SalesforceSettingsDto.create(settings)); + } +} diff --git a/backend/src/CRM/Salesforce/Model/IntegrationData.ts b/backend/src/CRM/Salesforce/Model/IntegrationData.ts new file mode 100644 index 0000000..a385293 --- /dev/null +++ b/backend/src/CRM/Salesforce/Model/IntegrationData.ts @@ -0,0 +1,11 @@ +import { UIDataRecord } from '../../Model/ExternalEntity/UIDataRecord'; + +export class IntegrationData { + rawData: object; + uiData: UIDataRecord[]; + + constructor(rawData: object, uiData: UIDataRecord[]) { + this.rawData = rawData; + this.uiData = uiData; + } +} diff --git a/backend/src/CRM/Salesforce/Model/Settings/SalesforceSettings.ts b/backend/src/CRM/Salesforce/Model/Settings/SalesforceSettings.ts new file mode 100644 index 0000000..8a7c137 --- /dev/null +++ b/backend/src/CRM/Salesforce/Model/Settings/SalesforceSettings.ts @@ -0,0 +1,35 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +@Entity('salesforce_settings') +export class SalesforceSettings { + @PrimaryGeneratedColumn() + id: string; + + @Column() + domain: string; + + @Column() + key: string; + + @Column() + secret: string; + + @Column({ nullable: true }) + refreshToken: string | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, domain: string, key: string, secret: string) { + this.accountId = accountId; + this.domain = domain; + this.key = key; + this.secret = secret; + this.createdAt = DateUtil.now(); + } +} diff --git a/backend/src/CRM/Salesforce/SalesforceModule.ts b/backend/src/CRM/Salesforce/SalesforceModule.ts new file mode 100644 index 0000000..208c31c --- /dev/null +++ b/backend/src/CRM/Salesforce/SalesforceModule.ts @@ -0,0 +1,29 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { SalesforceSettings } from './Model/Settings/SalesforceSettings'; +import { SalesforceIntegrationService } from './Service/SalesforceIntegrationService'; +import { SalesforceSettingsService } from './Service/Settings/SalesforceSettingsService'; +import { CreateSettingsController } from './Controller/Settings/CreateSettingsController'; +import { GetSettingsController } from './Controller/Settings/GetSettingsController'; +import { DeleteSettingsController } from './Controller/Settings/DeleteSettingsController'; +import { AuthCallbackController } from './Controller/Auth/AuthCallbackController'; +import { AuthConnectController } from './Controller/Auth/AuthConnectController'; +import { AuthDisconnectController } from './Controller/Auth/AuthDisconnectController'; + +@Module({ + imports: [TypeOrmModule.forFeature([SalesforceSettings]), IAMModule], + providers: [SalesforceIntegrationService, SalesforceSettingsService], + controllers: [ + CreateSettingsController, + GetSettingsController, + DeleteSettingsController, + AuthConnectController, + AuthCallbackController, + AuthDisconnectController, + ], + exports: [SalesforceIntegrationService], +}) +export class SalesforceModule {} diff --git a/backend/src/CRM/Salesforce/Service/SalesforceIntegrationService.ts b/backend/src/CRM/Salesforce/Service/SalesforceIntegrationService.ts new file mode 100644 index 0000000..275aa20 --- /dev/null +++ b/backend/src/CRM/Salesforce/Service/SalesforceIntegrationService.ts @@ -0,0 +1,222 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { InjectRepository } from '@nestjs/typeorm'; +import { IsNull, Not, Repository } from 'typeorm'; +import { lastValueFrom } from 'rxjs'; +import * as qs from 'qs'; + +import { UrlGeneratorService, FrontendRoute, NotFoundError, formatUrlQuery } from '@/common'; + +import { UIDataRecord } from '../../Model/ExternalEntity/UIDataRecord'; + +import { SalesforceUIFields } from '../Util/SalesforceUIFields'; +import { SalesforceCodeToType } from '../Util/SalesforceCodeToType'; +import { SalesforceSettings } from '../Model/Settings/SalesforceSettings'; +import { IntegrationData } from '../Model/IntegrationData'; + +const CALLBACK_PATH = '/api/integration/salesforce/auth/callback'; +const SaleForceUrls = { + services(domain: string) { + return `https://${domain}.my.salesforce.com/services`; + }, + auth(domain: string) { + return `${this.services(domain)}/oauth2`; + }, + token(domain: string) { + return `${this.auth(domain)}/token`; + }, + authorize(domain: string) { + return `${this.auth(domain)}/authorize`; + }, + sobjects(domain: string, type: string, id: string) { + return `${this.services(domain)}/data/v56.0/sobjects/${type}/${id}`; + }, +} as const; + +interface SalesforceObject { + type: string; + id: string; +} +interface ConnectionSettings extends SalesforceSettings { + accessToken: string; +} + +@Injectable() +export class SalesforceIntegrationService { + private readonly logger = new Logger(SalesforceIntegrationService.name); + constructor( + private readonly httpService: HttpService, + @InjectRepository(SalesforceSettings) + private readonly repository: Repository, + private readonly urlGenerator: UrlGeneratorService, + ) {} + + public async getAuthorizeUrl(subdomain: string, id: string) { + const settings = await this.repository.findOneBy({ id }); + if (!settings) { + throw NotFoundError.withId(SalesforceSettings, id); + } + + return formatUrlQuery(SaleForceUrls.authorize(settings.domain), { + client_id: settings.key, + redirect_uri: this.getCallbackUrl(subdomain), + state: settings.id, + response_type: 'code', + }); + } + + public async disconnect(accountId: number, id: string) { + await this.repository.update({ accountId, id }, { refreshToken: null }); + } + + public async processAuthCode(subdomain: string, code: string, state: string): Promise { + if (!state) { + return this.getAuthRedirectUrl(subdomain, false, `SalesForce authentication callback state doesn't set`); + } + const settings = await this.repository.findOneBy({ id: state }); + if (!settings) { + return this.getAuthRedirectUrl(subdomain, false, `SalesForce settings with id=${state} don't founded`); + } + + const response = await this.callWithCatch(async () => { + const { data } = await lastValueFrom( + this.httpService.post( + SaleForceUrls.token(settings.domain), + qs.stringify({ + grant_type: 'authorization_code', + code: code, + client_id: settings.key, + client_secret: settings.secret, + redirect_uri: this.getCallbackUrl(subdomain), + }), + ), + ); + return data; + }); + + if (response?.refresh_token) { + await this.repository.update(settings.id, { refreshToken: response.refresh_token }); + return this.getAuthRedirectUrl(subdomain, true); + } + + return this.getAuthRedirectUrl(subdomain, false, `Can't get Salesforce refresh token`); + } + + private getAuthRedirectUrl(subdomain: string, result: boolean, message?: string): string { + return this.urlGenerator.createUrl({ + route: FrontendRoute.settings.salesforce(), + subdomain, + query: { result: String(result), message }, + }); + } + + public async getDataFromUrl(accountId: number, url: string, entityUrl?: string): Promise { + const settings = await this.getConnectionSettings(accountId); + const urlParam = this.parseUrl(url); + if (urlParam && settings) { + const objectData = await this.getDataForObject(settings, urlParam.type, urlParam.id); + const valuableData = Object.fromEntries(Object.entries(objectData).filter(([, value]) => value !== null)); + const uiData = Object.entries(valuableData) + .filter(([key]) => SalesforceUIFields.get(urlParam.type).includes(key)) + .map( + ([key, value]) => + new UIDataRecord( + key, + key.replace(/([A-Z])/g, ' $1'), + value, + SalesforceUIFields.get(urlParam.type).findIndex((field) => key === field), + ), + ); + uiData.push(new UIDataRecord('SalesforceType', 'Salesforce Type', urlParam.type, -1)); + const ownerId = uiData.find((item) => item.key === 'OwnerId'); + const ownerData = ownerId ? await this.getDataForObject(settings, 'user', ownerId.value) : null; + if (ownerData) { + uiData.push(new UIDataRecord('OwnerName', 'Owner Name', ownerData['Name'], ownerId.sortOrder)); + const ownerIdIdx = uiData.findIndex((item) => item.key === 'OwnerId'); + uiData.splice(ownerIdIdx, 1); + } + if (entityUrl) { + await this.setDataForObject(settings, urlParam.type, urlParam.id, { AmworkEntityUrl__c: entityUrl }); + } + return new IntegrationData(valuableData, uiData); + } + return null; + } + + private getCallbackUrl(subdomain: string) { + return this.urlGenerator.createUrl({ route: CALLBACK_PATH, subdomain }); + } + + private parseUrl(originalUrl: string): SalesforceObject | null { + const { pathname } = new URL(originalUrl); + const match = pathname.match(/\/lightning\/r\/([^/]*)\/([a-zA-Z0-9]{15,18})\/view/); + if (match) { + return { type: match[1], id: match[2] }; + } else { + const match = pathname.match(/\/([a-zA-Z0-9]{15,18})$/); + if (match) { + const type = this.findObjectType(match[1].substring(0, 3)); + return { type, id: match[1] }; + } + } + return null; + } + + private findObjectType(typeCode: string): string { + return SalesforceCodeToType.get(typeCode); + } + + private getDataForObject(settings: ConnectionSettings, type: string, id: string): Promise { + return this.callWithCatch(async () => { + const { data } = await lastValueFrom( + this.httpService.get(SaleForceUrls.sobjects(settings.domain, type, id), { + headers: { Authorization: `Bearer ${settings.accessToken}` }, + }), + ); + return data; + }); + } + + private setDataForObject(settings: ConnectionSettings, type: string, id: string, data: object) { + this.callWithCatch(async () => { + await lastValueFrom( + this.httpService.patch(SaleForceUrls.sobjects(settings.domain, type, id), data, { + headers: { Authorization: `Bearer ${settings.accessToken}` }, + }), + ); + }); + } + + private async getConnectionSettings(accountId: number, domain?: string): Promise { + const settingsList = await this.repository.find({ where: { accountId, domain, refreshToken: Not(IsNull()) } }); + for (const settings of settingsList) { + const response = await this.callWithCatch(async () => { + const { data } = await lastValueFrom( + this.httpService.post( + SaleForceUrls.token(settings.domain), + qs.stringify({ + grant_type: 'refresh_token', + refresh_token: settings.refreshToken, + client_id: settings.key, + client_secret: settings.secret, + }), + ), + ); + return data; + }); + if (response?.access_token) { + return { ...settings, accessToken: response.access_token }; + } + } + return null; + } + + private async callWithCatch(func: () => Promise): Promise { + try { + return await func(); + } catch (e) { + this.logger.error(`Error in SalesforceIntegration`, (e as Error)?.stack); + return {}; + } + } +} diff --git a/backend/src/CRM/Salesforce/Service/Settings/CreateSalesforceSettingsDto.ts b/backend/src/CRM/Salesforce/Service/Settings/CreateSalesforceSettingsDto.ts new file mode 100644 index 0000000..2549c81 --- /dev/null +++ b/backend/src/CRM/Salesforce/Service/Settings/CreateSalesforceSettingsDto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class CreateSalesforceSettingsDto { + @ApiProperty() + @IsString() + domain: string; + + @ApiProperty() + @IsString() + key: string; + + @ApiProperty() + @IsString() + secret: string; +} diff --git a/backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsDto.ts b/backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsDto.ts new file mode 100644 index 0000000..6d941d1 --- /dev/null +++ b/backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsDto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { SalesforceSettings } from '../../Model/Settings/SalesforceSettings'; + +export class SalesforceSettingsDto { + @ApiProperty() + id: string; + + @ApiProperty() + domain: string; + + @ApiProperty() + key: string; + + @ApiProperty() + isConnected: boolean; + + constructor(id: string, domain: string, key: string, isConnected: boolean) { + this.id = id; + this.domain = domain; + this.key = key; + this.isConnected = isConnected; + } + + public static create(settings: SalesforceSettings) { + return new SalesforceSettingsDto(settings.id, settings.domain, settings.key, !!settings.refreshToken); + } +} diff --git a/backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsService.ts b/backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsService.ts new file mode 100644 index 0000000..dc97e82 --- /dev/null +++ b/backend/src/CRM/Salesforce/Service/Settings/SalesforceSettingsService.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { SalesforceSettings } from '../../Model/Settings/SalesforceSettings'; +import { CreateSalesforceSettingsDto } from './CreateSalesforceSettingsDto'; + +@Injectable() +export class SalesforceSettingsService { + constructor( + @InjectRepository(SalesforceSettings) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, dto: CreateSalesforceSettingsDto): Promise { + return await this.repository.save(new SalesforceSettings(accountId, dto.domain, dto.key, dto.secret)); + } + + public async getAll(accountId: number): Promise { + return await this.repository.findBy({ accountId }); + } + + public async delete(accountId: number, id: string) { + await this.repository.delete({ accountId, id }); + } +} diff --git a/backend/src/CRM/Salesforce/Util/SalesforceCodeToType.ts b/backend/src/CRM/Salesforce/Util/SalesforceCodeToType.ts new file mode 100644 index 0000000..ec72e87 --- /dev/null +++ b/backend/src/CRM/Salesforce/Util/SalesforceCodeToType.ts @@ -0,0 +1,34 @@ +export const SalesforceCodeToType = new Map([ + ['001', 'Account'], + ['007', 'Activity'], + ['806', 'Approval'], + ['00P', 'Attachment'], + ['00v', 'CampaignMember'], + ['701', 'Campaigns'], + ['500', 'Case'], + ['003', 'Contact'], + ['800', 'Contract'], + ['01Z', 'Dashboard'], + ['01a', 'DashboardComponent'], + ['00X', 'EmailTemplate'], + ['00U', 'Event'], + ['00l', 'Folder'], + ['00G', 'Group'], + ['00h', 'PageLayout'], + ['00Q', 'Lead'], + ['00B', 'ListView'], + ['006', 'Opportunity'], + ['00k', 'OpportunityLineItem'], + ['801', 'Order'], + ['802', 'OrderItem'], + ['00i', 'Pricebook'], + ['01s', 'Pricebook2'], + ['00j', 'Product'], + ['01t', 'Product2'], + ['00e', 'Profile'], + ['00O', 'Report'], + ['02c', 'SharingRule'], + ['00T', 'Task'], + ['005', 'User'], + ['00E', 'UserRole'], +]); diff --git a/backend/src/CRM/Salesforce/Util/SalesforceUIFields.ts b/backend/src/CRM/Salesforce/Util/SalesforceUIFields.ts new file mode 100644 index 0000000..25157f4 --- /dev/null +++ b/backend/src/CRM/Salesforce/Util/SalesforceUIFields.ts @@ -0,0 +1,36 @@ +const DefaultUIFields = ['OwnerId', 'Name', 'Description']; + +export const SalesforceUIFields = new Map([ + ['Account', [...DefaultUIFields, 'Phone', 'Website', 'Industry', 'Type']], + ['Activity', [...DefaultUIFields, 'Subject', 'Status', 'ActivityDate', 'StartDateTime', 'EndDateTime']], + ['Approval', DefaultUIFields], + ['Attachment', DefaultUIFields], + ['CampaignMember', DefaultUIFields], + ['Campaigns', DefaultUIFields], + ['Case', DefaultUIFields], + ['Contact', [...DefaultUIFields, 'Email', 'Phone', 'Title']], + ['Contract', DefaultUIFields], + ['Dashboard', DefaultUIFields], + ['DashboardComponent', DefaultUIFields], + ['EmailTemplate', DefaultUIFields], + ['Event', DefaultUIFields], + ['Folder', DefaultUIFields], + ['Group', DefaultUIFields], + ['PageLayout', DefaultUIFields], + ['Lead', [...DefaultUIFields, 'Email', 'Phone', 'Company', 'Title', 'Status', 'Rating']], + ['ListView', DefaultUIFields], + ['Opportunity', [...DefaultUIFields, 'StageName', 'Amount', 'Probability', 'Type', 'ForecastCategory', 'CloseDate']], + ['OpportunityLineItem', DefaultUIFields], + ['Order', DefaultUIFields], + ['OrderItem', DefaultUIFields], + ['Pricebook', DefaultUIFields], + ['Pricebook2', DefaultUIFields], + ['Product', DefaultUIFields], + ['Product2', DefaultUIFields], + ['Profile', DefaultUIFields], + ['Report', DefaultUIFields], + ['SharingRule', DefaultUIFields], + ['Task', [...DefaultUIFields, 'Subject', 'Status', 'Priority', 'DueDate', 'IsClosed']], + ['User', [...DefaultUIFields, 'Email', 'Phone', 'Company', 'Title', 'Username', 'IsActive']], + ['UserRole', DefaultUIFields], +]); diff --git a/backend/src/CRM/Service/BaseTaskBoard/BaseTaskBoardFilter.ts b/backend/src/CRM/Service/BaseTaskBoard/BaseTaskBoardFilter.ts new file mode 100644 index 0000000..fc58e22 --- /dev/null +++ b/backend/src/CRM/Service/BaseTaskBoard/BaseTaskBoardFilter.ts @@ -0,0 +1,58 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsObject, IsOptional, IsString } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +import { TaskSorting } from './TaskSorting'; + +export class BaseTaskBoardFilter { + @ApiPropertyOptional({ description: 'Sorting', enum: TaskSorting }) + @IsOptional() + @IsEnum(TaskSorting) + sorting?: TaskSorting | null; + + @ApiPropertyOptional({ description: 'Search text in task' }) + @IsOptional() + @IsString() + search?: string | null; + + @ApiPropertyOptional({ description: 'Show resolved tasks' }) + @IsOptional() + @IsBoolean() + showResolved?: boolean | null; + + @ApiPropertyOptional({ description: 'Created by User IDs', type: [Number] }) + @IsOptional() + @IsArray() + createdBy?: number[] | null; + + @ApiPropertyOptional({ description: 'Owner (Responsible) by User IDs', type: [Number] }) + @IsOptional() + @IsArray() + ownerIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Entity ids', type: [Number] }) + @IsOptional() + @IsArray() + entityIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Created at', type: DatePeriodFilter }) + @IsOptional() + @IsObject() + createdAt?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Task start date', type: DatePeriodFilter }) + @IsOptional() + @IsObject() + startDate?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Task end date', type: DatePeriodFilter }) + @IsOptional() + @IsObject() + endDate?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Task resolved date', type: DatePeriodFilter }) + @IsOptional() + @IsObject() + resolvedDate?: DatePeriodFilter; +} diff --git a/backend/src/CRM/Service/BaseTaskBoard/TaskBoardQueryHelper.ts b/backend/src/CRM/Service/BaseTaskBoard/TaskBoardQueryHelper.ts new file mode 100644 index 0000000..98a6f31 --- /dev/null +++ b/backend/src/CRM/Service/BaseTaskBoard/TaskBoardQueryHelper.ts @@ -0,0 +1,106 @@ +import { Brackets, type Repository, type SelectQueryBuilder } from 'typeorm'; + +import { DatePeriod, DatePeriodFilter, intersection } from '@/common'; + +import { TaskSorting } from './TaskSorting'; +import { BaseTaskBoardFilter } from './BaseTaskBoardFilter'; + +export class TaskBoardQueryHelper { + public static createBoardQueryBuilder( + accountId: number, + userId: number, + repository: Repository, + filter: BaseTaskBoardFilter, + responsibles: number[], + withOrder: boolean, + titleField: string, + ): SelectQueryBuilder { + const qb = repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + const users = intersection(filter.ownerIds, responsibles); + if (users) { + if (users.length > 0) { + qb.andWhere( + new Brackets((qb1) => { + qb1.where('responsible_user_id IN (:...users)', { users }); + if (!filter.ownerIds) { + qb1.orWhere('created_by = :userId', { userId }); + } + }), + ); + } else { + qb.andWhere( + new Brackets((qb1) => { + qb1.where('responsible_user_id IS NULL'); + if (!filter.ownerIds) { + qb1.orWhere('created_by = :userId', { userId }); + } + }), + ); + } + } + + if (filter.createdBy?.length) { + qb.andWhere('created_by IN (:...createdBy)', { createdBy: filter.createdBy }); + } + + if (filter.search) { + qb.andWhere(`${titleField} ILIKE :search`, { search: `%${filter.search}%` }); + } + + if (filter.entityIds) { + qb.andWhere('entity_id IN (:...entityIds)', { entityIds: filter.entityIds }); + } + + if (filter.createdAt) { + TaskBoardQueryHelper.addDateCondition(qb, filter.createdAt, 'created_at'); + } + + if (filter.startDate) { + TaskBoardQueryHelper.addDateCondition(qb, filter.startDate, 'start_date'); + } + + if (filter.endDate) { + TaskBoardQueryHelper.addDateCondition(qb, filter.endDate, 'end_date'); + } + + if (filter.resolvedDate) { + TaskBoardQueryHelper.addDateCondition(qb, filter.resolvedDate, 'resolved_date'); + } + + if (withOrder) { + TaskBoardQueryHelper.addBoardOrderBy(qb, filter.sorting); + } + + return qb; + } + + private static addDateCondition(qb: SelectQueryBuilder, dateFilter: DatePeriodFilter, fieldName: string) { + const dates = DatePeriod.fromFilter(dateFilter); + if (dates.from) { + qb.andWhere(`${fieldName} >= :${fieldName}from`, { [`${fieldName}from`]: dates.from }); + } + if (dates.to) { + qb.andWhere(`${fieldName} <= :${fieldName}to`, { [`${fieldName}to`]: dates.to }); + } + return qb; + } + + public static addBoardOrderBy(qb: SelectQueryBuilder, sorting: TaskSorting | null | undefined) { + switch (sorting) { + case TaskSorting.MANUAL: + qb.orderBy('weight', 'ASC'); + break; + case TaskSorting.CREATED_ASC: + qb.orderBy('created_at', 'ASC'); + break; + case TaskSorting.CREATED_DESC: + qb.orderBy('created_at', 'DESC'); + break; + default: + qb.orderBy('weight', 'ASC'); + break; + } + return qb.addOrderBy('id', 'DESC'); + } +} diff --git a/backend/src/CRM/Service/BaseTaskBoard/TaskSorting.ts b/backend/src/CRM/Service/BaseTaskBoard/TaskSorting.ts new file mode 100644 index 0000000..767544d --- /dev/null +++ b/backend/src/CRM/Service/BaseTaskBoard/TaskSorting.ts @@ -0,0 +1,5 @@ +export enum TaskSorting { + MANUAL = 'manual', + CREATED_ASC = 'created_asc', + CREATED_DESC = 'created_desc', +} diff --git a/backend/src/CRM/Service/BaseTaskBoard/UserTimeAllocation.ts b/backend/src/CRM/Service/BaseTaskBoard/UserTimeAllocation.ts new file mode 100644 index 0000000..88fd7ed --- /dev/null +++ b/backend/src/CRM/Service/BaseTaskBoard/UserTimeAllocation.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UserTimeAllocation { + @ApiProperty() + userId: number; + + @ApiProperty() + plannedTime: number; + + constructor(userId: number, plannedTime: number) { + this.userId = userId; + this.plannedTime = plannedTime; + } +} diff --git a/backend/src/CRM/Service/Entity/CommonEntityBoardCardService.ts b/backend/src/CRM/Service/Entity/CommonEntityBoardCardService.ts new file mode 100644 index 0000000..b1f87fb --- /dev/null +++ b/backend/src/CRM/Service/Entity/CommonEntityBoardCardService.ts @@ -0,0 +1,267 @@ +import { Injectable } from '@nestjs/common'; + +import { isUnique } from '@/common'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; + +import { EntityCategory } from '../../common'; +import { Activity } from '../../activity/entities'; +import { ActivityService } from '../../activity/activity.service'; +import { BaseTask } from '../../base-task'; +import { EntityLink } from '../../entity-link/entities'; +import { EntityLinkService } from '../../entity-link/entity-link.service'; +import { EntityType } from '../../entity-type/entities'; +import { EntityTypeLinkService } from '../../entity-type-link/entity-type-link.service'; +import { Task } from '../../task/entities'; +import { TaskService } from '../../task/task.service'; + +import { Entity } from '../../Model/Entity/Entity'; + +import { TaskIndicator } from './enums/task-indicator.enum'; +import { EntityBoardCard } from './Dto/Board/EntityBoardCard'; +import { CommonEntityCard } from './Dto/Board/CommonEntityCard'; +import { EntityService } from './EntityService'; + +@Injectable() +export class CommonEntityBoardCardService { + constructor( + private readonly authService: AuthorizationService, + private readonly entityService: EntityService, + private readonly entityLinkService: EntityLinkService, + private readonly entityTypeLinkService: EntityTypeLinkService, + private readonly fieldService: FieldService, + private readonly fieldValueService: FieldValueService, + private readonly taskService: TaskService, + private readonly activityService: ActivityService, + ) {} + + public async getBoardCard( + accountId: number, + user: User, + entity: Entity, + entityCategory: EntityCategory, + ): Promise { + const links = await this.entityLinkService.findMany({ accountId, sourceId: entity.id }); + const linkedIds = links.map((l) => l.targetId).filter(isUnique); + const linkedEntities = linkedIds.length + ? await this.entityService.findMany(accountId, { entityId: linkedIds }) + : []; + + const linkedEntityTypes = await this.entityTypeLinkService.findMany({ accountId, sourceId: entity.entityTypeId }); + const allowedEntityTypeLinks = ( + await Promise.all( + linkedEntityTypes.map(async (linked) => { + const hasAccess = await this.authService.check({ + action: 'view', + user, + authorizable: EntityType.getAuthorizable(linked.targetId), + }); + return hasAccess ? linked : null; + }), + ) + ).filter(Boolean); + + const sortOrderMap: Record = {}; + allowedEntityTypeLinks.forEach((link) => { + sortOrderMap[link.targetId] = link.sortOrder; + }); + + const filteredLinkedEntities = linkedEntities.filter((e) => + allowedEntityTypeLinks.some((etl) => etl.targetId === e.entityTypeId), + ); + + const priceField = await this.fieldService.findOne({ + accountId, + entityTypeId: entity.entityTypeId, + type: FieldType.Value, + }); + + const taskResponsibles = await this.authService.whoCanView({ user, authorizable: Task.getAuthorizable() }); + const activityResponsibles = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + return this.createBoardCard({ + accountId, + user, + entity, + entityCategory, + sortOrderMap, + linksCache: links, + entityCache: filteredLinkedEntities, + priceFieldId: priceField?.id, + taskResponsibles, + activityResponsibles, + }); + } + + public async getBoardCards( + accountId: number, + user: User, + entityTypeId: number, + entities: Entity[], + entityCategory: EntityCategory, + ): Promise { + const links = await this.entityLinkService.findMany({ accountId, sourceId: entities.map((e) => e.id) }); + const linkedIds = links.map((l) => l.targetId).filter(isUnique); + const linkedEntities = linkedIds.length + ? await this.entityService.findMany(accountId, { entityId: linkedIds }) + : []; + + const linkedEntityTypes = await this.entityTypeLinkService.findMany({ accountId, sourceId: entityTypeId }); + const allowedEntityTypeLinks = ( + await Promise.all( + linkedEntityTypes.map(async (linked) => { + const hasAccess = await this.authService.check({ + action: 'view', + user, + authorizable: EntityType.getAuthorizable(linked.targetId), + }); + return hasAccess ? linked : null; + }), + ) + ).filter(Boolean); + const sortOrderMap: Record = {}; + allowedEntityTypeLinks.forEach((link) => { + sortOrderMap[link.targetId] = link.sortOrder; + }); + const filteredLinkedEntities = linkedEntities.filter((e) => + allowedEntityTypeLinks.some((etl) => etl.targetId === e.entityTypeId), + ); + + const priceField = await this.fieldService.findOne({ accountId, entityTypeId, type: FieldType.Value }); + + const taskResponsibles = await this.authService.whoCanView({ user, authorizable: Task.getAuthorizable() }); + const activityResponsibles = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + const cards = await Promise.all( + entities.map(async (entity, idx) => { + const card = await this.createBoardCard({ + accountId, + user, + entity, + entityCategory, + sortOrderMap, + linksCache: links, + entityCache: filteredLinkedEntities, + priceFieldId: priceField?.id, + taskResponsibles, + activityResponsibles, + }); + return { idx, card }; + }), + ); + + return cards.sort((a, b) => a.idx - b.idx).map((c) => c.card); + } + + private async createBoardCard({ + accountId, + user, + entity, + entityCategory, + sortOrderMap, + linksCache, + entityCache, + priceFieldId, + taskResponsibles, + activityResponsibles, + }: { + accountId: number; + user: User; + entity: Entity; + entityCategory: EntityCategory; + sortOrderMap: Record; + linksCache: EntityLink[]; + entityCache: Entity[]; + priceFieldId: number | null | undefined; + taskResponsibles: number[] | undefined; + activityResponsibles: number[] | undefined; + }): Promise { + const targets = linksCache.filter((l) => l.sourceId === entity.id).map((l) => l.targetId); + const linkedEntities = entityCache + .filter((ec) => targets.includes(ec.id)) + .sort((a, b) => (sortOrderMap[a.entityTypeId] || 0) - (sortOrderMap[b.entityTypeId] || 0)); + const price = await this.getPrice(accountId, entity, priceFieldId); + const taskIndicatorColor = await this.getTaskIndicatorColor({ + accountId, + entityId: entity.id, + taskResponsibles, + activityResponsibles, + }); + const userRights = await this.authService.getUserRights({ user, authorizable: entity }); + return new EntityBoardCard({ + id: entity.id, + entityCategory, + boardId: entity.boardId, + stageId: entity.stageId, + price, + weight: entity.weight, + focused: entity.focused, + closedAt: entity.closedAt?.toISOString() ?? null, + data: CommonEntityCard.create( + entity, + linkedEntities.map((le) => le.name), + taskIndicatorColor, + userRights, + ), + }); + } + + private async getPrice( + accountId: number, + entity: Entity, + priceFieldId: number | null | undefined, + ): Promise { + if (!priceFieldId) return null; + + const fieldValue = await this.fieldValueService.findOne({ accountId, entityId: entity.id, fieldId: priceFieldId }); + if (!fieldValue) return null; + + const value = fieldValue.getValue(); + return isNaN(value) ? null : value; + } + + private async getTaskIndicatorColor({ + accountId, + entityId, + taskResponsibles, + activityResponsibles, + }: { + accountId: number; + entityId: number; + taskResponsibles: number[] | undefined; + activityResponsibles: number[] | undefined; + }): Promise { + const tasks: BaseTask[] = + !taskResponsibles || taskResponsibles.length + ? await this.taskService.findMany({ + accountId, + entityId, + responsibles: taskResponsibles, + isResolved: false, + }) + : []; + const activities: BaseTask[] = + !activityResponsibles || activityResponsibles.length + ? await this.activityService.findMany({ + accountId, + entityId, + responsibles: activityResponsibles, + isResolved: false, + }) + : []; + const allTasks = [...tasks, ...activities]; + + if (allTasks.length === 0) return TaskIndicator.Empty; + + const expiredTask = allTasks.find((task) => task.isTaskExpired()); + if (expiredTask) return TaskIndicator.Overdue; + + const todayTask = allTasks.find((task) => task.isTaskToday()); + if (todayTask) return TaskIndicator.Today; + + return TaskIndicator.Upcoming; + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/Batch/delete-entities-batch-filter.dto.ts b/backend/src/CRM/Service/Entity/Dto/Batch/delete-entities-batch-filter.dto.ts new file mode 100644 index 0000000..c809767 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Batch/delete-entities-batch-filter.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { EntityBoardCardFilter } from '../../../../Controller/Entity/Board/Filter/EntityBoardCardFilter'; + +export class DeleteEntitiesBatchFilterDto extends EntityBoardCardFilter { + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + entityIds?: number[]; +} diff --git a/backend/src/CRM/Service/Entity/Dto/Batch/index.ts b/backend/src/CRM/Service/Entity/Dto/Batch/index.ts new file mode 100644 index 0000000..ccdbb5a --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Batch/index.ts @@ -0,0 +1,2 @@ +export * from './delete-entities-batch-filter.dto'; +export * from './update-entities-batch-filter.dto'; diff --git a/backend/src/CRM/Service/Entity/Dto/Batch/update-entities-batch-filter.dto.ts b/backend/src/CRM/Service/Entity/Dto/Batch/update-entities-batch-filter.dto.ts new file mode 100644 index 0000000..2df979c --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Batch/update-entities-batch-filter.dto.ts @@ -0,0 +1,33 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { EntityBoardCardFilter } from '../../../../Controller/Entity/Board/Filter/EntityBoardCardFilter'; + +export class UpdateEntitiesBatchFilterDto extends EntityBoardCardFilter { + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + entityIds?: number[]; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + responsibleUserId?: number | null; + + @ApiPropertyOptional({ type: [Number] }) + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + responsibleEntityTypeIds?: number[] | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + boardId?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/CommonEntityCard.ts b/backend/src/CRM/Service/Entity/Dto/Board/CommonEntityCard.ts new file mode 100644 index 0000000..8e4d0a2 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/CommonEntityCard.ts @@ -0,0 +1,107 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; + +import { Entity } from '../../../../Model/Entity/Entity'; + +export class CommonEntityCard { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsNumber() + userId: number; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiProperty() + @IsNumber() + weight: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + closedAt?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedFrom?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedCount?: number | null; + + @ApiProperty({ type: [String] }) + linkedEntityNames: string[]; + + @ApiProperty() + @IsString() + taskIndicatorColor: string; + + @ApiProperty({ type: () => UserRights }) + userRights: UserRights; + + constructor({ + id, + entityTypeId, + name, + createdAt, + userId, + weight, + closedAt, + copiedFrom, + copiedCount, + linkedEntityNames, + taskIndicatorColor, + userRights, + }: CommonEntityCard) { + this.id = id; + this.entityTypeId = entityTypeId; + this.name = name; + this.createdAt = createdAt; + this.userId = userId; + this.weight = weight; + this.closedAt = closedAt; + this.copiedFrom = copiedFrom; + this.copiedCount = copiedCount; + this.linkedEntityNames = linkedEntityNames; + this.taskIndicatorColor = taskIndicatorColor; + this.userRights = userRights; + } + + public static create( + entity: Entity, + linkedEntityNames: string[], + taskIndicatorColor: string, + userRights: UserRights, + ) { + return new CommonEntityCard({ + id: entity.id, + entityTypeId: entity.entityTypeId, + name: entity.name, + createdAt: entity.createdAt.toISOString(), + userId: entity.responsibleUserId, + weight: entity.weight, + closedAt: entity.closedAt?.toISOString(), + copiedFrom: entity.copiedFrom, + copiedCount: entity.copiedCount, + linkedEntityNames, + taskIndicatorColor, + userRights, + }); + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardCard.ts b/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardCard.ts new file mode 100644 index 0000000..581ed10 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardCard.ts @@ -0,0 +1,59 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { EntityCategory } from '../../../../common'; + +import { CommonEntityCard } from './CommonEntityCard'; +import { ProjectEntityCard } from './ProjectEntityCard'; + +export class EntityBoardCard { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty({ enum: EntityCategory }) + @IsEnum(EntityCategory) + entityCategory: EntityCategory; + + @ApiProperty() + @IsNumber() + boardId: number; + + @ApiProperty() + @IsNumber() + stageId: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + price: number | null; + + @ApiProperty() + @IsNumber() + weight: number; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused: boolean; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + closedAt?: string | null; + + @ApiProperty() + data: CommonEntityCard | ProjectEntityCard; + + constructor({ id, entityCategory, boardId, stageId, price, weight, focused, closedAt, data }: EntityBoardCard) { + this.id = id; + this.entityCategory = entityCategory; + this.boardId = boardId; + this.stageId = stageId; + this.price = price; + this.weight = weight; + this.focused = focused; + this.closedAt = closedAt; + this.data = data; + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardMeta.ts b/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardMeta.ts new file mode 100644 index 0000000..a60108d --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardMeta.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { EntityBoardStageMeta } from './EntityBoardStageMeta'; + +export class EntityBoardMeta { + @ApiProperty() + totalCount: number; + + @ApiProperty() + hasPrice: boolean; + + @ApiProperty({ nullable: true }) + totalPrice: number | null; + + @ApiProperty({ type: [EntityBoardStageMeta] }) + stages: EntityBoardStageMeta[]; +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardStageMeta.ts b/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardStageMeta.ts new file mode 100644 index 0000000..3ac5659 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/EntityBoardStageMeta.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class EntityBoardStageMeta { + @ApiProperty() + id: number; + + @ApiProperty() + totalCount: number; + + @ApiProperty({ nullable: true }) + totalPrice: number | null; +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/ProjectEntityCard.ts b/backend/src/CRM/Service/Entity/Dto/Board/ProjectEntityCard.ts new file mode 100644 index 0000000..0bbc920 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/ProjectEntityCard.ts @@ -0,0 +1,125 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; + +import { Entity } from '../../../../Model/Entity/Entity'; +import { TasksCount } from './TasksCount'; + +export class ProjectEntityCard { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsNumber() + ownerId: number; + + @ApiProperty({ type: [Number] }) + @IsNumber({}, { each: true }) + participantIds: number[]; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + startDate: string | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + endDate: string | null; + + @ApiProperty({ type: TasksCount }) + tasksCount: TasksCount; + + @ApiProperty({ type: () => UserRights }) + userRights: UserRights; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiProperty() + @IsNumber() + weight: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + closedAt?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedFrom?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedCount?: number | null; + + constructor({ + id, + entityTypeId, + name, + ownerId, + participantIds, + startDate, + endDate, + tasksCount, + userRights, + createdAt, + weight, + closedAt, + copiedFrom, + copiedCount, + }: ProjectEntityCard) { + this.id = id; + this.entityTypeId = entityTypeId; + this.name = name; + this.ownerId = ownerId; + this.participantIds = participantIds; + this.startDate = startDate; + this.endDate = endDate; + this.tasksCount = tasksCount; + this.userRights = userRights; + this.createdAt = createdAt; + this.weight = weight; + this.closedAt = closedAt; + this.copiedFrom = copiedFrom; + this.copiedCount = copiedCount; + } + + public static create( + entity: Entity, + startDate: string | null, + endDate: string | null, + tasksCount: TasksCount, + userRights: UserRights, + ): ProjectEntityCard { + return new ProjectEntityCard({ + id: entity.id, + entityTypeId: entity.entityTypeId, + name: entity.name, + ownerId: entity.responsibleUserId, + participantIds: entity.participantIds ?? [], + startDate, + endDate, + tasksCount, + userRights, + createdAt: entity.createdAt.toISOString(), + weight: entity.weight, + closedAt: entity.closedAt?.toISOString(), + copiedFrom: entity.copiedFrom, + copiedCount: entity.copiedCount, + }); + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/TasksCount.ts b/backend/src/CRM/Service/Entity/Dto/Board/TasksCount.ts new file mode 100644 index 0000000..02177f7 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/TasksCount.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TasksCount { + @ApiProperty() + notResolved: number; + + @ApiProperty() + overdue: number; + + @ApiProperty() + today: number; + + @ApiProperty() + resolved: number; + + constructor(notResolved: number, overdue: number, today: number, resolved: number) { + this.notResolved = notResolved; + this.overdue = overdue; + this.today = today; + this.resolved = resolved; + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/Board/index.ts b/backend/src/CRM/Service/Entity/Dto/Board/index.ts new file mode 100644 index 0000000..3c68424 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Board/index.ts @@ -0,0 +1,6 @@ +export * from './CommonEntityCard'; +export * from './EntityBoardCard'; +export * from './EntityBoardMeta'; +export * from './EntityBoardStageMeta'; +export * from './ProjectEntityCard'; +export * from './TasksCount'; diff --git a/backend/src/CRM/Service/Entity/Dto/CreateEntityDto.ts b/backend/src/CRM/Service/Entity/Dto/CreateEntityDto.ts new file mode 100644 index 0000000..448db2f --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/CreateEntityDto.ts @@ -0,0 +1,48 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ManualSorting } from '@/common'; +import { UpdateFieldValueDto } from '@/modules/entity/entity-field/field-value/dto/update-field-value.dto'; + +import { EntityLinkDto } from '../../../entity-link/dto'; + +export class CreateEntityDto { + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsNumber() + responsibleUserId: number; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + boardId?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; + + @ApiPropertyOptional({ type: [UpdateFieldValueDto] }) + fieldValues?: UpdateFieldValueDto[]; + + @ApiPropertyOptional({ type: [EntityLinkDto] }) + entityLinks?: EntityLinkDto[]; + + @ApiPropertyOptional({ type: ManualSorting }) + @IsOptional() + sorting?: ManualSorting; + + closedAt?: Date | null; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused?: boolean; +} diff --git a/backend/src/CRM/Service/Entity/Dto/CreateSimpleEntityDto.ts b/backend/src/CRM/Service/Entity/Dto/CreateSimpleEntityDto.ts new file mode 100644 index 0000000..8601871 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/CreateSimpleEntityDto.ts @@ -0,0 +1,51 @@ +import { ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { SimpleFieldValueDto } from '@/modules/entity/entity-field/field-value/dto/simple-field-value.dto'; + +type LinkedEntity = CreateSimpleEntityDto | number; + +export class CreateSimpleEntityDto { + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + boardId?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused?: boolean; + + @ApiPropertyOptional({ nullable: true, type: [SimpleFieldValueDto] }) + @IsOptional() + @IsArray() + fieldValues?: SimpleFieldValueDto[] | null; + + @ApiPropertyOptional({ + nullable: true, + type: 'array', + items: { oneOf: [{ $ref: getSchemaPath(CreateSimpleEntityDto) }, { type: 'number' }] }, + }) + @IsOptional() + @IsArray() + linkedEntities?: LinkedEntity[] | null; +} diff --git a/backend/src/CRM/Service/Entity/Dto/EntityDto.ts b/backend/src/CRM/Service/Entity/Dto/EntityDto.ts new file mode 100644 index 0000000..36aa5e0 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/EntityDto.ts @@ -0,0 +1,210 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; +import { faker } from '@faker-js/faker'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { ChatDto } from '@/modules/multichat/chat/dto/chat.dto'; +import { FieldValue } from '@/modules/entity/entity-field/field-value/entities/field-value.entity'; +import { FieldValueDto } from '@/modules/entity/entity-field/field-value/dto/field-value.dto'; + +import { EntityLinkDto } from '../../../entity-link/dto'; +import { Entity } from '../../../Model/Entity/Entity'; +import { ExternalEntityDto } from '../../ExternalEntity/ExternalEntityDto'; +import { ExternalEntity } from '../../../Model/ExternalEntity/ExternalEntity'; +import { ExternalSystem } from '../../../Model/ExternalEntity/ExternalSystem'; + +export class EntityDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty() + @IsNumber() + responsibleUserId: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + boardId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + stageId: number | null; + + @ApiProperty() + @IsNumber() + createdBy: number; + + @ApiProperty() + @IsNumber() + weight: number; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused?: boolean; + + @ApiProperty({ type: [FieldValueDto] }) + @IsArray() + fieldValues: FieldValueDto[]; + + @ApiProperty({ type: [EntityLinkDto] }) + @IsArray() + entityLinks: EntityLinkDto[]; + + @ApiProperty({ type: [ExternalEntityDto] }) + @IsArray() + externalEntities: ExternalEntityDto[]; + + @ApiProperty({ type: UserRights }) + @IsObject() + userRights: UserRights; + + @ApiPropertyOptional() + @IsString() + createdAt: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + updatedAt?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + closedAt?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedFrom?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedCount?: number | null; + + @ApiPropertyOptional({ type: [ChatDto], nullable: true }) + @IsOptional() + chats?: ChatDto[] | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + lastShipmentDate?: string | null; + + constructor( + id: number, + name: string, + entityTypeId: number, + responsibleUserId: number, + boardId: number | null, + stageId: number | null, + createdBy: number, + weight: number, + focused: boolean, + fieldValues: FieldValueDto[], + entityLinks: EntityLinkDto[], + externalEntities: ExternalEntityDto[], + userRights: UserRights, + createdAt: string, + updatedAt: string | null, + closedAt: string | null, + copiedFrom: number | null, + copiedCount: number | null, + chats: ChatDto[] | null, + lastShipmentDate: string | null, + ) { + this.id = id; + this.name = name; + this.entityTypeId = entityTypeId; + this.responsibleUserId = responsibleUserId; + this.boardId = boardId; + this.stageId = stageId; + this.createdBy = createdBy; + this.weight = weight; + this.focused = focused; + this.fieldValues = fieldValues; + this.entityLinks = entityLinks; + this.externalEntities = externalEntities; + this.userRights = userRights; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.closedAt = closedAt; + this.copiedFrom = copiedFrom; + this.copiedCount = copiedCount; + this.chats = chats; + this.lastShipmentDate = lastShipmentDate; + } + + public static fromEntity( + entity: Entity, + fieldValues: FieldValue[], + entityLinks: EntityLinkDto[], + externalEntities: { externalEntity: ExternalEntity; type: ExternalSystem }[], + userRights: UserRights, + chats: ChatDto[] | null, + lastShipmentDate: string | null, + ) { + const fieldValueDtos = fieldValues.map((fieldValue) => fieldValue.toDto()); + const externalEntitiesDtos = externalEntities.map((ee) => ExternalEntityDto.create(ee.externalEntity, ee.type)); + + return new EntityDto( + entity.id, + entity.name, + entity.entityTypeId, + entity.responsibleUserId, + entity.boardId, + entity.stageId, + entity.createdBy, + entity.weight, + entity.focused, + fieldValueDtos, + entityLinks, + externalEntitiesDtos, + userRights, + entity.createdAt.toISOString(), + entity.updatedAt.toISOString(), + entity.closedAt?.toISOString() ?? null, + entity.copiedFrom, + entity.copiedCount, + chats, + lastShipmentDate, + ); + } + + public static fake(): EntityDto { + const closedAt = faker.date.past(); + return new EntityDto( + faker.number.int({ min: 10000000, max: 99999999 }), + faker.person.fullName(), + faker.number.int({ min: 10000000, max: 99999999 }), + faker.number.int({ min: 10000000, max: 99999999 }), + faker.number.int({ min: 10000000, max: 99999999 }), + faker.number.int({ min: 10000000, max: 99999999 }), + faker.number.int({ min: 10000000, max: 99999999 }), + faker.number.float({ min: -1000000, max: 1000000 }), + false, + [], + [], + [], + UserRights.full(), + faker.date.past({ refDate: closedAt }).toISOString(), + faker.date.past({ refDate: closedAt }).toISOString(), + closedAt.toISOString(), + faker.number.int({ min: 10000000, max: 99999999 }), + faker.number.int({ min: 10000000, max: 99999999 }), + [], + faker.date.past({ refDate: closedAt }).toISOString(), + ); + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/EntitySimpleDto.ts b/backend/src/CRM/Service/Entity/Dto/EntitySimpleDto.ts new file mode 100644 index 0000000..9a5bb6b --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/EntitySimpleDto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Expose } from 'class-transformer'; +import { IsNumber, IsString } from 'class-validator'; + +export class EntitySimpleDto { + @ApiProperty() + @IsNumber() + @Expose() + id: number; + + @ApiProperty() + @IsString() + @Expose() + name: string; +} diff --git a/backend/src/CRM/Service/Entity/Dto/Files/AddEntityFilesDto.ts b/backend/src/CRM/Service/Entity/Dto/Files/AddEntityFilesDto.ts new file mode 100644 index 0000000..037c3d7 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Files/AddEntityFilesDto.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class AddEntityFilesDto { + @ApiProperty() + fileIds: string[]; +} diff --git a/backend/src/CRM/Service/Entity/Dto/Files/index.ts b/backend/src/CRM/Service/Entity/Dto/Files/index.ts new file mode 100644 index 0000000..b1293dc --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/Files/index.ts @@ -0,0 +1 @@ +export * from './AddEntityFilesDto'; diff --git a/backend/src/CRM/Service/Entity/Dto/List/EntityListItem.ts b/backend/src/CRM/Service/Entity/Dto/List/EntityListItem.ts new file mode 100644 index 0000000..29d6661 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/List/EntityListItem.ts @@ -0,0 +1,120 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { FieldValueDto } from '@/modules/entity/entity-field/field-value/dto/field-value.dto'; + +import { Entity } from '../../../../Model/Entity/Entity'; + +export class EntityListItem { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsNumber() + responsibleUserId: number; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + boardId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + stageId: number | null; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiProperty() + @IsNumber() + weight: number; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused: boolean; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + closedAt?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedFrom?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + copiedCount?: number | null; + + @ApiProperty({ type: [FieldValueDto] }) + fieldValues: FieldValueDto[]; + + @ApiProperty({ type: () => UserRights }) + userRights: UserRights; + + constructor({ + id, + name, + createdAt, + responsibleUserId, + entityTypeId, + boardId, + stageId, + weight, + focused, + closedAt, + copiedFrom, + copiedCount, + fieldValues, + userRights, + }: EntityListItem) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + this.responsibleUserId = responsibleUserId; + this.entityTypeId = entityTypeId; + this.boardId = boardId; + this.stageId = stageId; + this.weight = weight; + this.focused = focused; + this.closedAt = closedAt; + this.copiedFrom = copiedFrom; + this.copiedCount = copiedCount; + this.fieldValues = fieldValues; + this.userRights = userRights; + } + + static create(entity: Entity, fieldValues: FieldValueDto[], userRights: UserRights) { + return new EntityListItem({ + id: entity.id, + name: entity.name, + createdAt: entity.createdAt.toISOString(), + responsibleUserId: entity.responsibleUserId, + entityTypeId: entity.entityTypeId, + boardId: entity.boardId, + stageId: entity.stageId, + weight: entity.weight, + focused: entity.focused, + closedAt: entity.closedAt?.toISOString(), + copiedFrom: entity.copiedFrom, + copiedCount: entity.copiedCount, + fieldValues, + userRights, + }); + } +} diff --git a/backend/src/CRM/Service/Entity/Dto/List/EntityListMeta.ts b/backend/src/CRM/Service/Entity/Dto/List/EntityListMeta.ts new file mode 100644 index 0000000..c5d0a96 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/List/EntityListMeta.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class EntityListMeta { + @ApiProperty() + totalCount: number; + + @ApiProperty() + hasPrice: boolean; + + @ApiProperty({ nullable: true }) + totalPrice: number | null; +} diff --git a/backend/src/CRM/Service/Entity/Dto/List/index.ts b/backend/src/CRM/Service/Entity/Dto/List/index.ts new file mode 100644 index 0000000..8684254 --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/List/index.ts @@ -0,0 +1,2 @@ +export * from './EntityListItem'; +export * from './EntityListMeta'; diff --git a/backend/src/CRM/Service/Entity/Dto/UpdateEntityDto.ts b/backend/src/CRM/Service/Entity/Dto/UpdateEntityDto.ts new file mode 100644 index 0000000..89e934c --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/UpdateEntityDto.ts @@ -0,0 +1,49 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ManualSorting } from '@/common'; +import { UpdateFieldValueDto } from '@/modules/entity/entity-field/field-value/dto/update-field-value.dto'; +import { EntityLinkDto } from '../../../entity-link/dto'; + +export class UpdateEntityDto { + @ApiPropertyOptional() + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + responsibleUserId?: number; + + @ApiPropertyOptional({ type: [UpdateFieldValueDto] }) + @IsOptional() + @IsArray() + fieldValues?: UpdateFieldValueDto[]; + + @ApiPropertyOptional({ type: [EntityLinkDto] }) + @IsOptional() + @IsArray() + entityLinks?: EntityLinkDto[]; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + boardId?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; + + @ApiPropertyOptional({ type: ManualSorting }) + @IsOptional() + sorting?: ManualSorting; + + closedAt?: Date | null; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused?: boolean; +} diff --git a/backend/src/CRM/Service/Entity/Dto/index.ts b/backend/src/CRM/Service/Entity/Dto/index.ts new file mode 100644 index 0000000..7ef54ff --- /dev/null +++ b/backend/src/CRM/Service/Entity/Dto/index.ts @@ -0,0 +1,9 @@ +export * from './Batch'; +export * from './Board'; +export * from './CreateEntityDto'; +export * from './CreateSimpleEntityDto'; +export * from './EntityDto'; +export * from './EntitySimpleDto'; +export * from './Files'; +export * from './List'; +export * from './UpdateEntityDto'; diff --git a/backend/src/CRM/Service/Entity/EntityBoardService.ts b/backend/src/CRM/Service/Entity/EntityBoardService.ts new file mode 100644 index 0000000..3c7c101 --- /dev/null +++ b/backend/src/CRM/Service/Entity/EntityBoardService.ts @@ -0,0 +1,874 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, DataSource, Repository, SelectQueryBuilder } from 'typeorm'; + +import { + BooleanFilter, + DateFilter, + DatePeriod, + DateUtil, + ExistsFilter, + ExistsFilterType, + intersection, + NumberFilter, + PagingQuery, + SelectFilter, + SimpleFilterType, + StringFilter, + StringFilterType, + UserNotification, +} from '@/common'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldValue } from '@/modules/entity/entity-field/field-value/entities/field-value.entity'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; +import { Field } from '@/modules/entity/entity-field/field/entities/field.entity'; + +import { EntityCategory } from '../../common'; +import { BoardStageService } from '../../board-stage'; +import { EntityType } from '../../entity-type/entities/entity-type.entity'; +import { EntityTypeService } from '../../entity-type/entity-type.service'; + +import { Entity } from '../../Model/Entity/Entity'; + +import { ProjectEntityBoardCardService } from './ProjectEntityBoardCardService'; +import { CommonEntityBoardCardService } from './CommonEntityBoardCardService'; +import { EntityService } from './EntityService'; + +import { EntityBoardCardFilter } from '../../Controller/Entity/Board/Filter/EntityBoardCardFilter'; +import { EntitySorting } from '../../Controller/Entity/Board/Filter/EntitySorting'; +import { EntityTaskFilter } from '../../Controller/Entity/Board/Filter/entity-task-filter.enum'; +import { EntityFieldFilter } from '../../Controller/Entity/Board/Filter/EntityFieldFilter'; +import { + EntityBoardCard, + EntityBoardMeta, + EntityBoardStageMeta, + EntityListItem, + EntityListMeta, + UpdateEntitiesBatchFilterDto, + DeleteEntitiesBatchFilterDto, +} from './Dto'; + +const EntityBatchLimit = 50; + +@Injectable() +export class EntityBoardService { + constructor( + @InjectRepository(Entity) + private readonly repository: Repository, + private readonly dataSource: DataSource, + private readonly authService: AuthorizationService, + private readonly fieldValueService: FieldValueService, + private readonly entityTypeService: EntityTypeService, + private readonly stageService: BoardStageService, + private readonly fieldService: FieldService, + private readonly entityService: EntityService, + private readonly commonBoardCardService: CommonEntityBoardCardService, + private readonly projectBoardCardService: ProjectEntityBoardCardService, + ) {} + + async getEntityBoardCards({ + accountId, + user, + entityTypeId, + boardId, + filter, + paging, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number; + filter: EntityBoardCardFilter; + paging: PagingQuery; + }): Promise { + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: filter.includeStageIds, + excludeStageIds: filter.excludeStageIds, + }); + if (!stageIds?.length) { + return []; + } + + const qb = await this.createFilteredQb({ + accountId, + user, + entityTypeId, + filter, + sorting: filter.sorting ?? EntitySorting.Manual, + }); + + const entityIds = ( + await Promise.all( + stageIds.map(async (stageId) => { + return ( + await qb + .clone() + .andWhere('e.stage_id = :stageId', { stageId }) + .offset(paging.skip) + .limit(paging.take) + .getRawMany<{ id: number }>() + ).map((i) => i.id); + }), + ) + ).flat(); + + if (entityIds.length) { + const entities = await this.entityService.getByIdsOrdered({ accountId, entityIds }); + const entityType = await this.entityTypeService.findOne(accountId, { id: entityTypeId }); + return entityType.entityCategory === EntityCategory.PROJECT + ? this.projectBoardCardService.getBoardCards(accountId, user, entityTypeId, entities, entityType.entityCategory) + : this.commonBoardCardService.getBoardCards(accountId, user, entityTypeId, entities, entityType.entityCategory); + } else { + return []; + } + } + + async getEntityBoardCard({ + accountId, + user, + entityTypeId, + boardId, + entityId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number; + entityId: number; + filter: EntityBoardCardFilter; + }): Promise { + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: filter.includeStageIds, + excludeStageIds: filter.excludeStageIds, + }); + if (!stageIds?.length) { + return null; + } + + const qb = await this.createFilteredQb({ accountId, user, entityTypeId, stageIds, filter }); + + const filteredEntityId = await qb.andWhere('e.id = :entityId', { entityId }).getRawOne<{ id: number }>(); + + if (!filteredEntityId?.id) { + return null; + } + + const entity = await this.entityService.findOne(accountId, { entityId }); + const entityType = await this.entityTypeService.findOne(accountId, { id: entityTypeId }); + return entityType.entityCategory === EntityCategory.PROJECT + ? await this.projectBoardCardService.getBoardCard(accountId, user, entity, entityType.entityCategory) + : await this.commonBoardCardService.getBoardCard(accountId, user, entity, entityType.entityCategory); + } + + async getEntityBoardMeta({ + accountId, + user, + entityTypeId, + boardId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number; + filter: EntityBoardCardFilter; + }): Promise { + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: filter.includeStageIds, + excludeStageIds: filter.excludeStageIds, + }); + const hasPrice = await this.fieldService.hasPriceField({ accountId, entityTypeId }); + if (!stageIds?.length) { + return { totalCount: 0, hasPrice, totalPrice: hasPrice ? 0 : null, stages: [] }; + } + + const qb = await this.createFilteredQb({ accountId, user, entityTypeId, filter }); + + const stageMetas: EntityBoardStageMeta[] = await Promise.all( + stageIds.map(async (stageId) => { + const stageQb = qb.clone().andWhere('e.stage_id = :stageId', { stageId }); + + const count = await stageQb.getCount(); + const price = hasPrice ? await this.getTotalPrice(stageQb) : null; + + return { id: stageId, totalCount: count, totalPrice: price }; + }), + ); + + const totalCount = stageMetas.reduce((acc, cur) => acc + cur.totalCount, 0); + const totalPrice = hasPrice ? stageMetas.reduce((acc, cur) => acc + cur.totalPrice, 0) : null; + + return { totalCount, hasPrice, totalPrice, stages: stageMetas }; + } + + /** + * @deprecated Use @see EntityService findMany + */ + async getEntityBoardEntities( + accountId: number, + user: User, + entityTypeId: number, + boardId: number, + paging: PagingQuery, + ): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + throwError: true, + }); + + return this.entityService.findMany(accountId, { boardId }, paging); + } + + async getEntityListItems({ + accountId, + user, + entityTypeId, + boardId, + filter, + paging, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number | null; + filter: EntityBoardCardFilter; + paging: PagingQuery; + }): Promise { + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: filter.includeStageIds, + excludeStageIds: filter.excludeStageIds, + }); + if (boardId && stageIds?.length === 0) { + return []; + } + + const qb = await this.createFilteredQb({ + accountId, + user, + entityTypeId, + stageIds: boardId ? stageIds : undefined, + filter, + sorting: filter.sorting ?? EntitySorting.Manual, + }); + + const entityIds = (await qb.offset(paging.skip).limit(paging.take).getRawMany<{ id: number }>()).map((e) => e.id); + + return entityIds.length ? this.createListItems({ accountId, user, entityIds }) : []; + } + + async getEntityListItem({ + accountId, + user, + entityTypeId, + entityId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + entityId: number; + filter: EntityBoardCardFilter; + }): Promise { + const qb = await this.createFilteredQb({ + accountId, + user, + entityTypeId, + stageIds: filter.includeStageIds?.length ? filter.includeStageIds : undefined, + filter, + }); + + const filteredEntityId = await qb.andWhere('e.id = :entityId', { entityId }).getRawOne<{ id: number }>(); + return filteredEntityId?.id + ? (await this.createListItems({ accountId, user, entityIds: [filteredEntityId.id] }))[0] + : null; + } + + async createListItems({ accountId, user, entityIds }: { accountId: number; user: User; entityIds: number[] }) { + const entities = await this.entityService.getByIdsOrdered({ accountId, entityIds }); + + const items = await Promise.all( + entities.map(async (entity, idx) => { + const fieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + const values = fieldValues.map((value) => value.toDto()); + const userRights = await this.authService.getUserRights({ user, authorizable: entity }); + return { idx, item: EntityListItem.create(entity, values, userRights) }; + }), + ); + + return items.sort((a, b) => a.idx - b.idx).map((i) => i.item); + } + + async getEntityListMeta({ + accountId, + user, + entityTypeId, + boardId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number | null; + filter: EntityBoardCardFilter; + }): Promise { + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: filter.includeStageIds, + excludeStageIds: filter.excludeStageIds, + }); + const hasPrice = await this.fieldService.hasPriceField({ accountId, entityTypeId }); + if (boardId && stageIds?.length === 0) { + return { totalCount: 0, hasPrice, totalPrice: hasPrice ? 0 : null }; + } + + const qb = await this.createFilteredQb({ + accountId, + user, + entityTypeId, + stageIds: boardId ? stageIds : undefined, + filter, + }); + + const totalCount = await qb.getCount(); + const totalPrice = hasPrice ? await this.getTotalPrice(qb) : null; + + return { totalCount, hasPrice, totalPrice }; + } + + async batchUpdate({ + accountId, + user, + entityTypeId, + boardId, + dto, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number | null; + dto: UpdateEntitiesBatchFilterDto; + }): Promise { + if (dto.entityIds) { + return this.entityService.batchUpdateEntityIds(accountId, user, dto.entityIds, dto, UserNotification.Suppressed); + } + + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: dto.includeStageIds, + excludeStageIds: dto.excludeStageIds, + }); + if (boardId && stageIds?.length === 0) { + return 0; + } + + const qb = await this.createFilteredQb({ + accountId, + user, + entityTypeId, + stageIds: boardId ? stageIds : undefined, + filter: dto, + }); + qb.orderBy('e.id', 'ASC'); + + let updated = 0; + let offset = 0; + let entityIds: number[] = []; + let baseCount = await qb.getCount(); + do { + const count = await qb.getCount(); + if (count !== baseCount) { + baseCount = count; + offset = 0; + } + entityIds = (await qb.clone().offset(offset).limit(EntityBatchLimit).getRawMany<{ id: number }>()).map( + (e) => e.id, + ); + if (entityIds.length > 0) { + const pageUpdated = await this.entityService.batchUpdateEntityIds( + accountId, + user, + entityIds, + dto, + UserNotification.Suppressed, + ); + updated += pageUpdated; + offset += entityIds.length; + } + } while (entityIds.length > 0); + + return updated; + } + + async batchDelete({ + accountId, + user, + entityTypeId, + boardId, + dto, + }: { + accountId: number; + user: User; + entityTypeId: number; + boardId: number | null; + dto: DeleteEntitiesBatchFilterDto; + }): Promise { + if (dto.entityIds) { + return this.entityService.deleteMany(accountId, user, dto.entityIds, UserNotification.Suppressed); + } + + const stageIds = await this.getStageIds({ + accountId, + boardId, + includeStageIds: dto.includeStageIds, + excludeStageIds: dto.excludeStageIds, + }); + if (boardId && stageIds?.length === 0) { + return 0; + } + + const qb = await this.createFilteredQb({ + accountId, + user, + entityTypeId, + stageIds: boardId ? stageIds : undefined, + filter: dto, + }); + qb.orderBy('e.id', 'ASC'); + + let deleted = 0; + let offset = 0; + let entityIds: number[] = []; + do { + entityIds = (await qb.clone().offset(offset).limit(EntityBatchLimit).getRawMany<{ id: number }>()).map( + (e) => e.id, + ); + if (entityIds.length > 0) { + const pageDeleted = await this.entityService.deleteMany( + accountId, + user, + entityIds, + UserNotification.Suppressed, + ); + deleted += pageDeleted; + offset += entityIds.length - pageDeleted; + } + } while (entityIds.length > 0); + + return deleted; + } + + private async getStageIds({ + accountId, + boardId, + includeStageIds, + excludeStageIds, + }: { + accountId: number; + boardId: number | null | undefined; + includeStageIds: number[] | null | undefined; + excludeStageIds: number[] | null | undefined; + }): Promise { + const stageIds = includeStageIds?.length + ? includeStageIds + : boardId + ? await this.stageService.findManyIds({ accountId, boardId }) + : []; + + return excludeStageIds?.length ? stageIds.filter((id) => !excludeStageIds.includes(id)) : stageIds; + } + + private async createFilteredQb({ + accountId, + user, + entityTypeId, + stageIds, + filter, + sorting, + }: { + accountId: number; + user: User; + entityTypeId: number; + stageIds?: number[]; + filter: EntityBoardCardFilter; + sorting?: EntitySorting; + }) { + const responsibles = await this.authService.whoCanView({ + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + + const fieldIds = filter.fields?.map((f) => f.fieldId); + const fields = fieldIds?.length + ? await this.fieldService.findMany({ accountId, entityTypeId, id: fieldIds }) + : undefined; + + return this.createQb({ + accountId, + userId: user.id, + responsibles, + entityTypeId, + stageId: stageIds, + name: filter.search, + createdAt: filter.createdAt ? DatePeriod.fromFilter(filter.createdAt) : undefined, + closedAt: filter.closedAt ? DatePeriod.fromFilter(filter.closedAt) : undefined, + ownerIds: filter.ownerIds, + tasks: filter.tasks, + fieldFilters: filter.fields, + fields, + sorting, + }); + } + + private createQb({ + accountId, + userId, + responsibles, + entityTypeId, + stageId, + name, + createdAt, + closedAt, + ownerIds, + tasks, + fieldFilters, + fields, + sorting, + }: { + accountId: number; + userId: number; + responsibles: number[]; + entityTypeId: number; + stageId?: number | number[]; + name?: string | null; + createdAt?: DatePeriod; + closedAt?: DatePeriod; + ownerIds?: number[]; + tasks?: EntityTaskFilter; + fieldFilters?: EntityFieldFilter[]; + fields?: Field[]; + sorting?: EntitySorting; + }) { + const qb = this.repository + .createQueryBuilder('e') + .select('e.id', 'id') + .where('e.account_id = :accountId', { accountId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + + if (stageId) { + if (Array.isArray(stageId)) { + qb.andWhere('e.stage_id IN (:...stageId)', { stageId }); + } else { + qb.andWhere('e.stage_id = :stageId', { stageId }); + } + } + + if (name) { + qb.andWhere('e.name ILIKE :name', { name: `%${name}%` }); + } + + if (createdAt) { + if (createdAt.from) { + qb.andWhere('e.created_at >= :createdAtFrom', { createdAtFrom: createdAt.from }); + } + if (createdAt.to) { + qb.andWhere('e.created_at <= :createdAtTo', { createdAtTo: createdAt.to }); + } + } + if (closedAt) { + if (closedAt.from) { + qb.andWhere('e.closed_at >= :closedAtFrom', { closedAtFrom: closedAt.from }); + } + if (closedAt.to) { + qb.andWhere('e.closed_at <= :closedAtTo', { closedAtTo: closedAt.to }); + } + } + + const users = intersection(ownerIds, responsibles); + if (users) { + qb.andWhere( + new Brackets((qb1) => { + if (users.length > 0) { + qb1.where('e.responsible_user_id in (:...responsibles)', { responsibles: users }); + } else { + qb1.where('e.responsible_user_id IS NULL'); + } + if (!ownerIds) { + qb1.orWhere(`e.created_by = ${userId}`).orWhere(`e.participant_ids @> '${userId}'`); + } + }), + ); + } + + if (tasks) { + const taskQb = this.dataSource + .createQueryBuilder() + .select('at.id') + .from('all_tasks', 'at') + .where('at.entity_id = e.id') + .andWhere('at.is_resolved = FALSE'); + if (tasks === EntityTaskFilter.WithoutTask) { + qb.andWhere(`NOT EXISTS (${taskQb.getQuery()})`); + } else if (tasks === EntityTaskFilter.WithTask) { + qb.andWhere(`EXISTS (${taskQb.getQuery()})`); + } else if (tasks === EntityTaskFilter.OverdueTask) { + taskQb.andWhere('at.end_date < NOW()'); + qb.andWhere(`EXISTS (${taskQb.getQuery()})`); + } else if (tasks === EntityTaskFilter.TodayTask) { + taskQb.andWhere( + new Brackets((todayQb) => { + todayQb + .where('at.start_date < NOW() AND at.end_date > NOW()') + .orWhere('at.start_date IS NOT NULL AND at.end_date IS NULL AND at.start_date < NOW()') + .orWhere('at.start_date IS NULL AND at.end_date IS NOT NULL AND at.end_date > NOW()'); + }), + ); + qb.andWhere(`EXISTS (${taskQb.getQuery()})`); + } + } + + if (fieldFilters && fields) { + this.addBoardFieldsCondition({ qb, fieldFilters, fields }); + } + + if (sorting) { + switch (sorting) { + case EntitySorting.Manual: + qb.addSelect('e.weight', 'weight').orderBy('e.weight', 'ASC'); + break; + case EntitySorting.CreatedAsc: + qb.addSelect('e.created_at', 'created_at').orderBy('e.created_at', 'ASC'); + break; + case EntitySorting.CreatedDesc: + qb.addSelect('e.created_at', 'created_at').orderBy('e.created_at', 'DESC'); + break; + case EntitySorting.NameAsc: + qb.addSelect('e.name', 'name').orderBy('e.name', 'ASC'); + break; + case EntitySorting.NameDesc: + qb.addSelect('e.name', 'name').orderBy('e.name', 'DESC'); + break; + } + } + qb.addOrderBy('e.id', 'DESC'); + + return qb; + } + + private addBoardFieldsCondition({ + qb, + fieldFilters, + fields, + }: { + qb: SelectQueryBuilder; + fieldFilters: EntityFieldFilter[]; + fields: Field[]; + }) { + for (const fieldFilter of fieldFilters) { + const field = fields.find((f) => f.id === fieldFilter.fieldId); + if (!field) continue; + + const fieldAlias = `fv_${field.id}`; + qb.leftJoin(FieldValue, fieldAlias, `${fieldAlias}.entity_id = e.id AND ${fieldAlias}.field_id = ${field.id}`); + + switch (field.type) { + case FieldType.Text: + case FieldType.RichText: + case FieldType.Link: + case FieldType.Checklist: + if (fieldFilter.type === SimpleFilterType.String) { + this.addStringFieldCondition(qb, fieldAlias, 'value', fieldFilter.filter as StringFilter); + } + break; + case FieldType.MultiText: + case FieldType.Email: + case FieldType.Phone: + if (fieldFilter.type === SimpleFilterType.String) { + this.addStringFieldCondition(qb, fieldAlias, 'values', fieldFilter.filter as StringFilter); + } + break; + case FieldType.Number: + case FieldType.Value: + case FieldType.Formula: + if (fieldFilter.type === SimpleFilterType.Number) { + this.addNumberFieldCondition(qb, fieldAlias, fieldFilter.filter as NumberFilter); + } + break; + case FieldType.Date: + if (fieldFilter.type === SimpleFilterType.Date) { + this.addDateFieldCondition(qb, fieldAlias, fieldFilter.filter as DateFilter); + } + break; + case FieldType.Switch: + if (fieldFilter.type === SimpleFilterType.Boolean) { + this.addBooleanFieldCondition(qb, fieldAlias, fieldFilter.filter as BooleanFilter); + } + break; + case FieldType.Select: + case FieldType.ColoredSelect: + if (fieldFilter.type === SimpleFilterType.Select) { + this.addSelectFieldCondition(qb, fieldAlias, 'optionId', fieldFilter.filter as SelectFilter); + } + break; + case FieldType.MultiSelect: + case FieldType.ColoredMultiSelect: + case FieldType.CheckedMultiSelect: + if (fieldFilter.type === SimpleFilterType.Select) { + this.addMultiSelectFieldCondition(qb, fieldAlias, 'optionIds', fieldFilter.filter as SelectFilter); + } + break; + case FieldType.Participant: + if (fieldFilter.type === SimpleFilterType.Select) { + this.addSelectFieldCondition(qb, fieldAlias, 'value', fieldFilter.filter as SelectFilter); + } + break; + case FieldType.Participants: + if (fieldFilter.type === SimpleFilterType.Select) { + this.addMultiSelectFieldCondition(qb, fieldAlias, 'userIds', fieldFilter.filter as SelectFilter); + } + break; + case FieldType.File: + if (fieldFilter.type === SimpleFilterType.Exists) { + this.addExistsFieldCondition(qb, fieldAlias, 'value', fieldFilter.filter as ExistsFilter); + } + break; + } + } + } + private addExistsFieldCondition( + qb: SelectQueryBuilder, + fieldAlias: string, + valueAlias: string, + filter: ExistsFilter, + ) { + switch (filter.type) { + case ExistsFilterType.Empty: + qb.andWhere( + new Brackets((qb1) => + qb1.where(`${fieldAlias}.payload is null`).orWhere(`${fieldAlias}.payload->>'${valueAlias}' = ''`), + ), + ); + break; + case ExistsFilterType.NotEmpty: + qb.andWhere(`${fieldAlias}.payload->>'${valueAlias}' != ''`); + break; + } + } + private addStringFieldCondition( + qb: SelectQueryBuilder, + fieldAlias: string, + valueAlias: string, + filter: StringFilter, + ) { + switch (filter.type) { + case StringFilterType.Empty: + qb.andWhere( + new Brackets((qb1) => + qb1.where(`${fieldAlias}.payload is null`).orWhere(`${fieldAlias}.payload->>'${valueAlias}' = ''`), + ), + ); + break; + case StringFilterType.NotEmpty: + qb.andWhere(`${fieldAlias}.payload->>'${valueAlias}' != ''`); + break; + default: + if (filter.text) { + qb.andWhere(`${fieldAlias}.payload->>'${valueAlias}' ilike :${fieldAlias}_value`, { + [`${fieldAlias}_value`]: `%${filter.text}%`, + }); + } + } + } + private addNumberFieldCondition(qb: SelectQueryBuilder, fieldAlias: string, filter: NumberFilter) { + if (filter.min && !isNaN(Number(filter.min))) { + qb.andWhere(`(${fieldAlias}.payload->>'value')::numeric >= :${fieldAlias}_from`, { + [`${fieldAlias}_from`]: filter.min, + }); + } + if (filter.max && !isNaN(Number(filter.max))) { + qb.andWhere(`(${fieldAlias}.payload->>'value')::numeric <= :${fieldAlias}_to`, { + [`${fieldAlias}_to`]: filter.max, + }); + } + } + private addDateFieldCondition(qb: SelectQueryBuilder, fieldAlias: string, filter: DateFilter) { + if (filter.from) { + const fromDate = DateUtil.fromISOString(filter.from); + qb.andWhere(`(${fieldAlias}.payload->>'value')::timestamp >= '${fromDate.toISOString()}'::timestamp`); + } + if (filter.to) { + const toDate = DateUtil.fromISOString(filter.to); + qb.andWhere(`(${fieldAlias}.payload->>'value')::timestamp <= '${toDate.toISOString()}'::timestamp`); + } + } + private addBooleanFieldCondition(qb: SelectQueryBuilder, fieldAlias: string, filter: BooleanFilter) { + if (filter.value !== undefined && filter.value !== null) { + if (filter.value) { + qb.andWhere(`(${fieldAlias}.payload->>'value')::boolean = true`); + } else { + qb.andWhere( + new Brackets((qb1) => + qb1.where(`${fieldAlias}.payload is null`).orWhere(`(${fieldAlias}.payload->>'value')::boolean = false`), + ), + ); + } + } + } + private addSelectFieldCondition( + qb: SelectQueryBuilder, + fieldAlias: string, + valueAlias: string, + filter: SelectFilter, + ) { + if (filter.optionIds?.length) { + qb.andWhere(`COALESCE((${fieldAlias}.payload->>'${valueAlias}')::integer, 0) IN (:...${fieldAlias}_value)`, { + [`${fieldAlias}_value`]: filter.optionIds, + }); + } + } + private addMultiSelectFieldCondition( + qb: SelectQueryBuilder, + fieldAlias: string, + valueAlias: string, + filter: SelectFilter, + ) { + if (filter.optionIds?.length > 0) { + qb.leftJoin( + '(select 1)', + `dummy_${fieldAlias}`, + `true cross join jsonb_array_elements(${fieldAlias}.payload->'${valueAlias}') a(opt_id_${fieldAlias})`, + ); + qb.andWhere(`opt_id_${fieldAlias}::text::integer IN (:...${fieldAlias}_value)`, { + [`${fieldAlias}_value`]: filter.optionIds, + }); + } + } + + private async getTotalPrice(qb: SelectQueryBuilder): Promise { + const { sum } = await this.dataSource + .createQueryBuilder() + .select(`sum(cast(fv.payload::jsonb->>'value' as decimal))::decimal`, 'sum') + .from('field_value', 'fv') + .where(`fv.field_type = '${FieldType.Value}'`) + .andWhere(`fv.entity_id in (${qb.getQuery()})`) + .setParameters(qb.getParameters()) + .getRawOne<{ sum: number }>(); + + return sum ? Number(sum) : 0; + } +} diff --git a/backend/src/CRM/Service/Entity/EntityService.ts b/backend/src/CRM/Service/Entity/EntityService.ts new file mode 100644 index 0000000..d1487f2 --- /dev/null +++ b/backend/src/CRM/Service/Entity/EntityService.ts @@ -0,0 +1,1212 @@ +import { ForbiddenException, forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { + DateUtil, + FileLinkSource, + ManualSorting, + NotFoundError, + ObjectState, + PagingQuery, + UserNotification, +} from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { ChatService } from '@/modules/multichat/chat/services/chat.service'; +import { ShipmentService } from '@/modules/inventory/shipment/shipment.service'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldAccess } from '@/modules/entity/entity-field/field-settings/enums/field-access.enum'; +import { UpdateFieldValueDto } from '@/modules/entity/entity-field/field-value/dto/update-field-value.dto'; +import { FieldSettingsService } from '@/modules/entity/entity-field/field-settings/field-settings.service'; +import { FieldValue } from '@/modules/entity/entity-field/field-value/entities/field-value.entity'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; +import { EntityInfoDto, EntityInfoService } from '@/modules/entity/entity-info'; + +import { EntityCategory } from '../../common'; +import { BoardService } from '../../board/board.service'; +import { BoardStage, BoardStageService } from '../../board-stage'; +import { EntityLink } from '../../entity-link/entities'; +import { EntityLinkService } from '../../entity-link/entity-link.service'; +import { EntityTypeService } from '../../entity-type/entity-type.service'; +import { EntitySearchService } from '../../entity-search/entity-search.service'; + +import { Entity } from '../../Model/Entity/Entity'; +import { ExternalEntityService } from '../ExternalEntity/ExternalEntityService'; +import { FileLinkService } from '../FileLink/FileLinkService'; + +import { EntityServiceEmitter } from './entity-service.emitter'; +import { ReadonlyFieldChangedError } from './errors/readonly-field-changed.error'; +import { RequiredFieldEmptyError } from './errors/required-field-empty.error'; +import { FileLinkDto } from '../FileLink/FileLinkDto'; +import { EntityDto } from './Dto/EntityDto'; +import { CreateEntityDto } from './Dto/CreateEntityDto'; +import { UpdateEntityDto } from './Dto/UpdateEntityDto'; +import { CreateSimpleEntityDto } from './Dto/CreateSimpleEntityDto'; + +const Weight = { + min: 100.0, + step: 100.0, +}; +const BatchProcessLimit = 100; + +interface CreateSimpleOptions { + linkedEntities?: number[]; + createdAt?: Date; + userNotification?: UserNotification; + checkActiveLead?: boolean; + checkActiveLeadContactEntityId?: number; + checkDuplicate?: boolean; +} +type EntityWithOrder = Record; + +interface FindFilter { + entityId?: number | number[]; + entityTypeId?: number | number[]; + boardId?: number | number[]; + stageId?: number | number[]; + fieldValue?: { type: FieldType; value: string }; + exclude?: { entityId?: number | number[]; entityTypeId?: number | number[] }; +} +interface CalculateEntity { + entityTypeId: number; + entityId: number; + recalculate?: boolean; + children?: CalculateEntity[]; +} + +//TODO: need to refactor all class +@Injectable() +export class EntityService { + constructor( + @InjectRepository(Entity) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly fieldValueService: FieldValueService, + private readonly fieldService: FieldService, + private readonly entityLinkService: EntityLinkService, + @Inject(forwardRef(() => ExternalEntityService)) + private readonly externalEntityService: ExternalEntityService, + private readonly entityTypeService: EntityTypeService, + private readonly fileLinkService: FileLinkService, + @Inject(forwardRef(() => BoardStageService)) + private readonly stageService: BoardStageService, + @Inject(forwardRef(() => BoardService)) + private readonly boardService: BoardService, + private readonly fieldSettingsService: FieldSettingsService, + @Inject(forwardRef(() => ChatService)) + private readonly chatService: ChatService, + private readonly shipmentService: ShipmentService, + private readonly entityEmitter: EntityServiceEmitter, + private readonly entityInfoService: EntityInfoService, + @Inject(forwardRef(() => EntitySearchService)) + private readonly entitySearchService: EntitySearchService, + ) {} + + ////////////////// + // Create + ////////////////// + //TODO: remove this + /** + * @deprecated + */ + async save(entity: Entity): Promise { + return this.repository.save(entity); + } + + async createAndGetDto(accountId: number, user: User, dto: CreateEntityDto): Promise { + const entity = await this.create(accountId, user, dto); + + return this.getDtoForEntity(accountId, user, entity); + } + + async create( + accountId: number, + user: User, + dto: CreateEntityDto, + userNotification = UserNotification.Default, + ): Promise { + const entity = await this.createEntity({ + accountId, + user, + data: { + entityTypeId: dto.entityTypeId, + name: dto.name, + boardId: dto.boardId, + stageId: dto.stageId, + ownerId: dto.responsibleUserId, + sorting: dto.sorting, + focused: dto.focused, + }, + }); + + if (dto.entityLinks) { + await this.entityLinkService.processMany({ accountId, links: dto.entityLinks }); + } + + if (dto.fieldValues?.length) { + const { updateParticipants, value } = await this.fieldValueService.processBatch({ + accountId, + entityId: entity.id, + dtos: dto.fieldValues, + }); + await this.postProcessFieldValues(accountId, entity, updateParticipants, value, true); + } + + this.entityEmitter.emitCreatedEvent(accountId, entity, { userNotification }); + + return entity; + } + + async createSimpleAndGetInfo({ + accountId, + user, + dto, + options, + }: { + accountId: number; + user: User; + dto: CreateSimpleEntityDto; + options?: CreateSimpleOptions; + }): Promise { + const entities = await this.createSimple({ accountId, user, dto, options }); + + return Promise.all(entities.map((entity) => this.entityInfoService.getEntityInfo({ user, entity, access: true }))); + } + + async createSimple({ + accountId, + user, + dto, + options, + }: { + accountId: number; + user: User; + dto: CreateSimpleEntityDto; + options?: CreateSimpleOptions; + }): Promise { + let entity = options?.checkDuplicate + ? await this.findDuplicateEntity({ + accountId, + user, + dto, + checkActive: options?.checkActiveLead, + checkActiveLeadContactEntityId: options?.checkActiveLeadContactEntityId, + }) + : null; + const entityExists = !!entity; + + let checkActiveLeadContactEntityId: number | undefined = undefined; + if (entityExists && options?.checkActiveLead) { + const entityType = await this.entityTypeService.findOne(accountId, { id: entity.entityTypeId }); + if (entityType.entityCategory === EntityCategory.CONTACT) { + checkActiveLeadContactEntityId = entity.id; + } + } + + if (!entityExists) { + entity = await this.createEntity({ + accountId, + user, + data: { + entityTypeId: dto.entityTypeId, + name: dto.name, + boardId: dto.boardId, + stageId: dto.stageId, + createdAt: options?.createdAt, + ownerId: dto.ownerId, + focused: dto.focused, + }, + }); + } + + if (options?.linkedEntities?.length) { + await Promise.all( + options.linkedEntities.map((targetId) => + this.entityLinkService.create({ accountId, sourceId: entity.id, targetId }), + ), + ); + } + if (dto.linkedEntities?.length) { + await Promise.all( + dto.linkedEntities + .filter((linkedEntity): linkedEntity is number => typeof linkedEntity === 'number') + .map((targetId) => this.entityLinkService.create({ accountId, sourceId: entity.id, targetId })), + ); + } + + if (dto.fieldValues?.length) { + const { updateParticipants, value } = await this.fieldValueService.createManySimple({ + accountId, + entityTypeId: entity.entityTypeId, + entityId: entity.id, + dtos: dto.fieldValues, + }); + await this.postProcessFieldValues(accountId, entity, updateParticipants, value, true); + } + + const entities = [entity]; + if (dto.linkedEntities?.length) { + entities.push( + ...( + await Promise.all( + dto.linkedEntities + .filter((linkedEntity): linkedEntity is CreateSimpleEntityDto => typeof linkedEntity !== 'number') + .map((dto) => + this.createSimple({ + accountId, + user, + dto, + options: { ...options, linkedEntities: [entity.id], checkActiveLeadContactEntityId }, + }), + ), + ) + ).flat(), + ); + } + + if (!entityExists) { + this.entityEmitter.emitCreatedEvent(accountId, entity, { userNotification: options?.userNotification }); + } + + return entities; + } + + private async createEntity({ + accountId, + user, + data, + }: { + accountId: number; + user: User; + data: { + entityTypeId: number; + name?: string | null; + boardId?: number | null; + stageId?: number | null; + createdAt?: Date | null; + ownerId?: number | null; + sorting?: ManualSorting | null; + focused?: boolean | null; + }; + }): Promise { + const entityType = await this.entityTypeService.findOne(accountId, { id: data.entityTypeId }); + await this.authService.check({ action: 'create', user, authorizable: entityType, throwError: true }); + + const name = + data.name && data.name.trim() !== '' + ? data.name + : `${entityType.name} ${Math.trunc(new Date().getTime() / 1000)}`; + + const stage = await this.findStage({ + accountId, + entityTypeId: data.entityTypeId, + boardId: data.boardId, + stageId: data.stageId, + }); + + const weight = await this.calculateWeight({ + accountId, + entityTypeId: entityType.id, + boardId: stage?.boardId, + afterId: data.sorting?.afterId, + beforeId: data.sorting?.beforeId, + }); + return this.repository.save( + new Entity( + accountId, + name, + entityType.id, + data.ownerId ?? user.id, + stage?.boardId ?? null, + stage?.id ?? null, + user.id, + weight, + data.focused ?? false, + this.getClosedAt(null, stage), + null, + data.createdAt, + null, + null, + null, + ), + ); + } + + private async findDuplicateEntity({ + accountId, + user, + dto, + checkActive, + checkActiveLeadContactEntityId, + }: { + accountId: number; + user: User; + dto: CreateSimpleEntityDto; + checkActive?: boolean | null; + checkActiveLeadContactEntityId?: number | null; + }): Promise { + const findByField = async ({ + accountId, + user, + dto, + fieldType, + checkActive, + }: { + accountId: number; + user: User; + dto: CreateSimpleEntityDto; + fieldType: FieldType; + checkActive?: boolean | null; + }): Promise => { + const fieldValue = dto.fieldValues.find((fv) => fv.fieldType === fieldType); + if (fieldValue) { + const { entities } = await this.entitySearchService.search({ + accountId, + user, + filter: { entityTypeId: dto.entityTypeId, fieldType, fieldValue: fieldValue.value as string }, + paging: new PagingQuery(0, checkActive ? 20 : 1), + }); + if (entities.length) { + return checkActive ? entities.find((e) => !e.closedAt) : entities[0]; + } + } + return null; + }; + + let entity = await findByField({ accountId, user, dto, fieldType: FieldType.Phone, checkActive }); + if (!entity) entity = await findByField({ accountId, user, dto, fieldType: FieldType.Email, checkActive }); + + if (!entity && checkActiveLeadContactEntityId) { + const linked = await this.findLinkedEntities({ accountId, entityId: checkActiveLeadContactEntityId }); + entity = linked + .filter((l) => l.entityTypeId === dto.entityTypeId) + .sort((a, b) => DateUtil.sort(b.createdAt, a.createdAt)) + .find((e) => !e.closedAt); + } + + return entity; + } + + async copyToStage( + accountId: number, + entityId: number, + stageId: number, + copyOriginal: boolean, + ): Promise { + const sourceEntity = await this.findOne(accountId, { entityId }); + const stage = await this.stageService.findOne({ accountId, stageId }); + + if (sourceEntity && stage) { + let copiedEntity = sourceEntity.copy(); + if (!copyOriginal) { + copiedEntity.update({ + stageId: stage.id, + boardId: stage.boardId, + closedAt: this.getClosedAt(sourceEntity.closedAt, stage), + }); + } + copiedEntity = await this.repository.save(copiedEntity); + await this.entityLinkService.copyEntityLinks({ + accountId, + sourceId: sourceEntity.id, + targetId: copiedEntity.id, + }); + await this.fieldValueService.copyEntityFieldValues({ + accountId, + sourceEntityId: sourceEntity.id, + targetEntityId: copiedEntity.id, + }); + this.entityEmitter.emitCreatedEvent(accountId, copiedEntity, { + copiedFrom: copyOriginal ? sourceEntity.id : undefined, + }); + + if (copyOriginal) { + sourceEntity.update({ + stageId: stage.id, + boardId: stage.boardId, + closedAt: this.getClosedAt(sourceEntity.closedAt, stage), + }); + this.repository.save(sourceEntity); + this.entityEmitter.emitUpdatedEvent(accountId, sourceEntity); + this.entityEmitter.emitStageChangedEvent(accountId, sourceEntity); + } + + return copiedEntity; + } + return null; + } + + private async findStage({ + accountId, + entityTypeId, + boardId, + stageId, + }: { + accountId: number; + entityTypeId: number; + boardId?: number | null; + stageId?: number | null; + }) { + const stage = + stageId || boardId + ? await this.stageService.findOne({ accountId, boardId: boardId ?? undefined, stageId: stageId ?? undefined }) + : null; + if (stage) return stage; + + const board = await this.boardService.findOneId({ accountId, recordId: entityTypeId }); + return board ? this.stageService.findOne({ accountId, boardId: board }) : null; + } + ////////////////// + + ////////////////// + // Get + ////////////////// + async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + async findMany(accountId: number, filter?: FindFilter, paging?: PagingQuery): Promise { + return this.createFindQb(accountId, filter).skip(paging?.skip).take(paging?.take).orderBy('e.id').getMany(); + } + + async getByIdsOrdered({ accountId, entityIds }: { accountId: number; entityIds: number[] }): Promise { + const entities = await this.createFindQb(accountId, { entityId: entityIds }).getMany(); + + const entityMap = new Map(entities.map((entity) => [entity.id, entity])); + return entityIds.map((id) => entityMap.get(id)).filter(Boolean); + } + + async ensureExistId(accountId: number, id: number): Promise { + const entity = await this.repository.findOneBy({ accountId, id }); + return entity ? id : null; + } + + async getDtoByIdForUI(accountId: number, user: User, entityId: number, checkPermission = true): Promise { + const entity = await this.findOne(accountId, { entityId }); + if (!entity) { + throw NotFoundError.withId(Entity, entityId); + } + + return this.getDtoForEntity(accountId, user, entity, checkPermission); + } + + async getDtoForEntity(accountId: number, user: User, entity: Entity, checkPermission = true): Promise { + if (checkPermission) { + await this.authService.check({ action: 'view', user, authorizable: entity, throwError: true }); + } + + const allFieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + const fieldValues = await this.excludeHiddenFields(accountId, user.id, entity, allFieldValues); + const entityLinks = await this.entityLinkService.findMany({ accountId, sourceId: entity.id }); + const allowedLinks: EntityLink[] = []; + for (const link of entityLinks) { + const linkedEntity = await this.repository.findOneBy({ id: link.targetId }); + if (await this.authService.check({ action: 'view', user, authorizable: linkedEntity })) { + allowedLinks.push(link); + } + } + const externalEntities = await this.externalEntityService.getExternalEntitiesWithType(entity.id); + const userRights = await this.authService.getUserRights({ user, authorizable: entity }); + const chats = await this.chatService.findMany({ + accountId, + filter: { entityId: entity.id }, + accessUserId: user.id, + }); + const shipment = await this.shipmentService.findOne({ accountId, user, filter: { entityId: entity.id } }); + + return EntityDto.fromEntity( + entity, + fieldValues, + allowedLinks?.map((l) => l.toDto()), + externalEntities, + userRights, + chats.map((chat) => chat.toDto()), + shipment?.shippedAt?.toISOString() ?? null, + ); + } + + async getFileLinks(account: Account, entityId: number): Promise { + return this.fileLinkService.getFileLinkDtos(account, FileLinkSource.ENTITY, entityId); + } + + async getDocumentLinks(account: Account, entityId: number): Promise { + return this.fileLinkService.getFileLinkDtos(account, FileLinkSource.ENTITY_DOCUMENT, entityId, 'DESC'); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('e').where('e.account_id = :accountId', { accountId }); + + if (filter?.entityId) { + if (Array.isArray(filter.entityId)) { + qb.andWhere('e.id IN (:...entityIds)', { entityIds: filter.entityId }); + } else { + qb.andWhere('e.id = :entityId', { entityId: filter.entityId }); + } + } + if (filter?.entityTypeId) { + if (Array.isArray(filter.entityTypeId)) { + qb.andWhere('e.entity_type_id IN (:...entityTypeIds)', { entityTypeIds: filter.entityTypeId }); + } else { + qb.andWhere('e.entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + } + if (filter?.exclude?.entityId) { + if (Array.isArray(filter.exclude.entityId)) { + if (filter.exclude.entityId.length) { + qb.andWhere('e.id NOT IN (:...exEntityIds)', { exEntityIds: filter.exclude.entityId }); + } + } else { + qb.andWhere('e.id != :exEntityId', { exEntityId: filter.exclude.entityId }); + } + } + if (filter?.exclude?.entityTypeId) { + if (Array.isArray(filter.exclude.entityTypeId)) { + if (filter.exclude.entityTypeId.length) { + qb.andWhere('e.entity_type_id NOT IN (:...exEntityTypeIds)', { + exEntityTypeIds: filter.exclude.entityTypeId, + }); + } + } else { + qb.andWhere('e.entity_type_id != :exEntityTypeId', { exEntityTypeId: filter.exclude.entityTypeId }); + } + } + if (filter?.boardId) { + if (Array.isArray(filter.boardId)) { + qb.andWhere('e.board_id IN (:...boardIds)', { boardIds: filter.boardId }); + } else { + qb.andWhere('e.board_id = :boardId', { boardId: filter.boardId }); + } + } + if (filter?.stageId) { + if (Array.isArray(filter.stageId)) { + qb.andWhere('e.stage_id IN (:...stageIds)', { stageIds: filter.stageId }); + } else { + qb.andWhere('e.stage_id = :stageId', { stageId: filter.stageId }); + } + } + if (filter?.fieldValue) { + qb.leftJoin('field_value', 'fv', 'fv.entity_id = e.id AND fv.field_type = :fieldType', { + fieldType: filter.fieldValue.type, + }).andWhere(`(fv.payload::jsonb::text ilike :fieldValue)`, { fieldValue: `%${filter.fieldValue.value}%` }); + } + + return qb; + } + ////////////////// + + ////////////////// + // Update + ////////////////// + async updateAndGetDto(accountId: number, user: User, id: number, dto: UpdateEntityDto): Promise { + const entity = await this.update(accountId, user, id, dto); + + return this.getDtoForEntity(accountId, user, entity); + } + + async update( + accountId: number, + user: User | null, + entityId: number, + dto: UpdateEntityDto, + { + userNotification = UserNotification.Default, + skipCheckRestrictions = false, + skipCheckPermissions = false, + }: { + userNotification?: UserNotification; + skipCheckRestrictions?: boolean; + skipCheckPermissions?: boolean; + } = {}, + ): Promise { + const entity = await this.repository.findOneBy({ accountId, id: entityId }); + if (!entity) { + throw NotFoundError.withId(Entity, entityId); + } + + if (!skipCheckPermissions && user) { + await this.authService.check({ action: 'edit', user, authorizable: entity, throwError: true }); + } + + const ownerChanged = dto.responsibleUserId && entity.responsibleUserId !== dto.responsibleUserId; + const stageChanged = dto.stageId && entity.stageId !== dto.stageId; + const stage = entity.stageId + ? await this.stageService.findOne({ accountId, stageId: dto.stageId ?? entity.stageId }) + : null; + if (entity.stageId && !stage) { + throw NotFoundError.withId(BoardStage, dto.stageId ?? entity.stageId); + } + + if (!skipCheckRestrictions && stageChanged && user) { + await this.checkFieldsRestriction(accountId, user.id, entity, stage.id, dto); + } + + dto.closedAt = this.getClosedAt(entity.closedAt, stage); + if (stage && !dto.boardId) dto.boardId = stage.boardId; + entity.update(dto); + if (dto.sorting) { + entity.weight = await this.calculateWeight({ + accountId, + entityTypeId: entity.entityTypeId, + boardId: stage ? stage.boardId : null, + afterId: dto.sorting.afterId, + beforeId: dto.sorting.beforeId, + }); + } + await this.repository.save(entity); + + let linksChanged = false; + if (dto.entityLinks) { + linksChanged = await this.entityLinkService.processMany({ accountId, links: dto.entityLinks }); + } + if (dto.fieldValues?.length) { + const { recalculate, updateParticipants, value } = await this.fieldValueService.processBatch({ + accountId, + entityId: entity.id, + dtos: dto.fieldValues, + }); + await this.postProcessFieldValues(accountId, entity, updateParticipants, value, recalculate || linksChanged); + } else if (linksChanged) { + await this.calculateFormulas(accountId, entity); + } + + this.entityEmitter.emitUpdatedEvent(accountId, entity, { userNotification }); + if (stageChanged) { + this.entityEmitter.emitStageChangedEvent(accountId, entity, { userNotification }); + } + if (ownerChanged && user) { + this.entityEmitter.emitOwnerChangedEvent(accountId, user.id, entity, { userNotification }); + } + + return entity; + } + + async updateFieldValue( + accountId: number, + user: User, + entityId: number, + fieldId: number, + dto: UpdateFieldValueDto, + ): Promise { + dto.fieldId = dto.fieldId || fieldId; + await this.update(accountId, user, entityId, { fieldValues: [dto] }); + } + + async updateValue(accountId: number, entityId: number, price: number): Promise { + const entity = await this.findOne(accountId, { entityId }); + + if (entity) { + const field = await this.fieldService.findOne({ + accountId, + entityTypeId: entity.entityTypeId, + type: FieldType.Value, + }); + + if (field && !field.value) { + await this.fieldValueService.setValue({ + accountId, + entityId, + fieldId: field.id, + dto: { + fieldId: field.id, + fieldType: field.type, + payload: { value: price }, + }, + }); + + entity.value = price; + await this.repository.save(entity); + + await this.calculateFormulas(accountId, entity); + + this.entityEmitter.emitUpdatedEvent(accountId, entity); + } + } + } + + async batchUpdateEntityIds( + accountId: number, + user: User, + entityIds: number[], + { + responsibleUserId, + boardId, + stageId, + responsibleEntityTypeIds, + }: { responsibleUserId?: number; boardId?: number; stageId?: number; responsibleEntityTypeIds?: number[] }, + userNotification = UserNotification.Default, + ): Promise { + let updated = 0; + for (const entityId of entityIds) { + try { + await this.update(accountId, user, entityId, { responsibleUserId, boardId, stageId }, { userNotification }); + updated++; + + if (responsibleEntityTypeIds?.length) { + const links = await this.entityLinkService.findMany({ accountId, sourceId: entityId }); + for (const link of links) { + const linkedEntity = await this.repository.findOneBy({ id: link.targetId }); + if (responsibleEntityTypeIds.includes(linkedEntity.entityTypeId)) { + await this.update(accountId, user, linkedEntity.id, { responsibleUserId }, { userNotification }); + } + } + } + } catch (e) { + if (!(e instanceof ForbiddenException)) throw e; + } + } + return updated; + } + + async removeUser({ accountId, userId, newUserId }: { accountId: number; userId: number; newUserId?: number | null }) { + if (newUserId) { + await this.repository.update( + { accountId, responsibleUserId: userId }, + { responsibleUserId: newUserId, updatedAt: DateUtil.now() }, + ); + + await this.repository + .createQueryBuilder() + .update() + .set({ + participantIds: () => + // eslint-disable-next-line max-len + `(SELECT jsonb_agg(DISTINCT CASE WHEN elem::integer = ${userId} THEN ${newUserId} ELSE elem::integer END) FROM jsonb_array_elements(participant_ids) AS elem)`, + }) + .where('account_id = :accountId', { accountId }) + .andWhere('participant_ids IS NOT NULL') + .andWhere(`participant_ids @> jsonb_build_array(${userId})`) + .execute(); + } else { + await this.repository + .createQueryBuilder() + .update() + .set({ + participantIds: () => + // eslint-disable-next-line max-len + `(SELECT jsonb_agg(elem) FROM jsonb_array_elements(participant_ids) AS elem WHERE elem::integer != ${userId})`, + }) + .where('account_id = :accountId', { accountId }) + .andWhere('participant_ids IS NOT NULL') + .andWhere(`participant_ids @> jsonb_build_array(${userId})`) + .execute(); + } + } + + async changeStageForAll({ + accountId, + boardId, + stageId, + newStageId, + }: { + accountId: number; + boardId: number; + stageId: number; + newStageId: number; + }) { + const qb = this.repository + .createQueryBuilder('entity') + .select('entity.id', 'id') + .where('entity.account_id = :accountId', { accountId }) + .andWhere('entity.board_id = :boardId', { boardId }) + .andWhere('entity.stage_id = :stageId', { stageId }) + .limit(BatchProcessLimit); + let entities: { id: number }[] = []; + do { + entities = await qb.getRawMany<{ id: number }>(); + for (const entity of entities) { + await this.update( + accountId, + null, + entity.id, + { boardId: boardId, stageId: newStageId }, + { skipCheckRestrictions: true, skipCheckPermissions: true }, + ); + } + } while (entities.length); + } + ////////////////// + + ////////////////// + // Delete + ////////////////// + async delete(accountId: number, user: User, entityId: number, userNotification = UserNotification.Default) { + const entity = await this.repository.findOneBy({ accountId, id: entityId }); + if (entity) { + await this.authService.check({ action: 'delete', user, authorizable: entity, throwError: true }); + + const links = await this.entityLinkService.findMany({ accountId, sourceId: entityId }); + + await this.repository.delete({ accountId, id: entityId }); + this.fileLinkService.processFiles(accountId, FileLinkSource.ENTITY, entityId, []); + this.fileLinkService.processFiles(accountId, FileLinkSource.ENTITY_DOCUMENT, entityId, []); + + Promise.all( + links.map(async (link) => { + const linked = await this.findOne(accountId, { entityId: link.targetId }); + await this.calculateFormulas(accountId, linked); + }), + ); + + this.entityEmitter.emitDeletedEvent(accountId, entity, { userNotification }); + } + } + + async deleteMany( + accountId: number, + user: User, + entityIds: number[], + userNotification = UserNotification.Default, + ): Promise { + let deleted = 0; + for (const id of entityIds) { + try { + await this.delete(accountId, user, id, userNotification); + deleted++; + } catch (e) { + if (!(e instanceof ForbiddenException)) throw e; + } + } + return deleted; + } + ////////////////// + + ////////////////// + // Linked entities + ////////////////// + async findLinkedEntities({ + accountId, + entityId, + entityTypeId, + }: { + accountId: number; + entityId: number; + entityTypeId?: number; + }): Promise { + const qb = this.repository + .createQueryBuilder('e') + .innerJoin(EntityLink, 'el', 'el.target_id = e.id and el.source_id = :entityId', { entityId }) + .where('e.account_id = :accountId', { accountId }) + .orderBy('el.sort_order'); + if (entityTypeId) { + qb.andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + } + + return qb.getMany(); + } + + async findFirstLinkedEntityByType(accountId: number, entityId: number): Promise { + const links = await this.entityLinkService.findMany({ accountId, sourceId: entityId }); + const entities = await this.repository.findBy({ id: In(links.map((l) => l.targetId)) }); + const groupedEntities = entities.reduce((acc, entity) => { + const link = links.find((l) => l.targetId === entity.id); + if (!acc[entity.entityTypeId]) { + acc[entity.entityTypeId] = { entity, order: link.sortOrder }; + } else if (acc[entity.entityTypeId].order > link.sortOrder) { + acc[entity.entityTypeId] = { entity, order: link.sortOrder }; + } + return acc; + }, {}); + return Object.values(groupedEntities).map((g) => g.entity); + } + + async findLastLinkedEntity({ + accountId, + entityId, + category, + }: { + accountId: number; + entityId: number; + category: EntityCategory; + }): Promise { + return this.repository + .createQueryBuilder('e') + .innerJoin('entity_link', 'el', 'el.target_id = e.id and el.source_id = :entityId', { entityId }) + .innerJoin('entity_type', 'et', 'et.id = e.entity_type_id and et.entity_category = :category', { category }) + .where('e.account_id = :accountId', { accountId }) + .orderBy('e.created_at', 'DESC') + .getOne(); + } + ////////////////// + + ////////////////// + // Entity values helpers + ////////////////// + private getClosedAt(closedAt: Date | null, stage: BoardStage | null | undefined): Date | null { + return stage?.isSystem ? (closedAt ?? DateUtil.now()) : null; + } + ////////////////// + + ////////////////// + // Weight helpers + ////////////////// + private async calculateWeight({ + accountId, + entityTypeId, + boardId, + afterId, + beforeId, + }: { + accountId: number; + entityTypeId: number; + boardId: number | null; + afterId?: number | null; + beforeId?: number | null; + }): Promise { + let afterWeight = afterId ? await this.getWeightById(accountId, afterId) : null; + let beforeWeight = beforeId ? await this.getWeightById(accountId, beforeId) : null; + if (afterWeight === null && beforeWeight === null) { + const minWeight = await this.getMinWeight(accountId, entityTypeId, boardId); + return minWeight - Weight.step; + } else if (afterWeight !== null && beforeWeight === null) { + beforeWeight = await this.getMinWeightMoreThan(accountId, entityTypeId, boardId, afterWeight); + if (beforeWeight === null) { + return afterWeight + Weight.step; + } + } else if (afterWeight === null && beforeWeight !== null) { + afterWeight = await this.getMaxWeightLessThan(accountId, entityTypeId, boardId, beforeWeight); + if (afterWeight === null) { + return beforeWeight - Weight.step; + } + } + return (afterWeight + beforeWeight) / 2.0; + } + + private async getWeightById(accountId: number, id: number): Promise { + const result = await this.repository.findOne({ where: { accountId, id }, select: { weight: true } }); + return result ? result.weight : null; + } + + private async getMinWeight(accountId: number, entityTypeId: number, boardId: number | null): Promise { + const query = this.repository + .createQueryBuilder('e') + .select('min(e.weight)', 'weight') + .where('e.account_id = :accountId', { accountId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + if (boardId) { + query.andWhere('e.board_id = :boardId', { boardId }); + } + const { weight } = await query.getRawOne<{ weight: number | null }>(); + return weight ?? Weight.min; + } + + private async getMinWeightMoreThan( + accountId: number, + entityTypeId: number, + boardId: number | null, + limitWeight: number, + ): Promise { + const query = this.repository + .createQueryBuilder('e') + .select('min(e.weight)', 'weight') + .where('e.account_id = :accountId', { accountId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }) + .andWhere('e.weight > :limitWeight', { limitWeight }); + if (boardId) { + query.andWhere('e.board_id = :boardId', { boardId }); + } + const { weight } = await query.getRawOne(); + return weight; + } + + private async getMaxWeightLessThan( + accountId: number, + entityTypeId: number, + boardId: number | null, + limitWeight: number, + ): Promise { + const query = this.repository + .createQueryBuilder('e') + .select('max(e.weight)', 'weight') + .where('e.account_id = :accountId', { accountId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }) + .andWhere('e.weight < :limitWeight', { limitWeight }); + if (boardId) { + query.andWhere('e.board_id = :boardId', { boardId }); + } + const { weight } = await query.getRawOne(); + return weight; + } + ////////////////// + + ////////////////// + // Field values + ////////////////// + private async postProcessFieldValues( + accountId: number, + entity: Entity, + updateParticipants: boolean, + value: number | null | undefined, + calculateFormulas: boolean, + ) { + if (updateParticipants) { + entity.participantIds = await this.fieldValueService.getParticipantIds({ accountId, entityId: entity.id }); + await this.repository.update({ accountId, id: entity.id }, { participantIds: entity.participantIds }); + } + if (value !== undefined && value !== null) { + entity.value = value; + await this.repository.update({ accountId, id: entity.id }, { value }); + } + + if (calculateFormulas) { + await this.calculateFormulas(accountId, entity); + } + } + + async recalculateFormulas({ accountId, entityTypeId }: { accountId: number; entityTypeId: number }) { + const batchSize = 100; + let page = 0; + let entities: Entity[]; + + do { + entities = await this.findMany(accountId, { entityTypeId }, new PagingQuery(page * batchSize, batchSize)); + + for (const entity of entities) { + await this.calculateFormulas(accountId, entity); + + const fieldValue = await this.fieldValueService.findOne({ + accountId, + entityId: entity.id, + type: FieldType.Value, + }); + if (fieldValue) { + entity.value = fieldValue.getValue() ?? entity.value; + await this.repository.update({ accountId, id: entity.id }, { value: entity.value }); + } + } + + page++; + } while (entities.length === batchSize); + } + + private async calculateFormulas(accountId: number, entity: Entity) { + const entityHierarchy = await this.buildCalculationHierarchy({ accountId, entity }); + await this.fieldValueService.calculateFormulas({ + accountId, + calcEntity: entityHierarchy, + previousEntityIds: [], + hasUpdates: true, + }); + } + + private async buildCalculationHierarchy({ + accountId, + entity, + previous = [], + }: { + accountId: number; + entity: Entity; + previous?: CalculateEntity[]; + }): Promise { + const children = previous.length ? [previous[previous.length - 1]] : []; + const calcEntity = { entityId: entity.id, entityTypeId: entity.entityTypeId, recalculate: true, children }; + + const links = await this.entityLinkService.findMany({ accountId, sourceId: entity.id }); + if (links.length) { + const linkedEntities = await this.findMany(accountId, { + entityId: links.map((l) => l.targetId), + exclude: { entityId: previous.map((prev) => prev.entityId) }, + }); + if (linkedEntities.length) { + const prevEntityTypeIds = previous.map((prev) => prev.entityTypeId); + calcEntity.children.push( + ...(await Promise.all( + linkedEntities.map((linked) => + prevEntityTypeIds.includes(linked.entityTypeId) + ? { entityId: linked.id, entityTypeId: linked.entityTypeId } + : this.buildCalculationHierarchy({ + accountId, + entity: linked, + previous: [...previous, { entityId: entity.id, entityTypeId: entity.entityTypeId }], + }), + ), + )), + ); + } + } + + return calcEntity; + } + ////////////////// + + ////////////////// + // Field settings + ////////////////// + private async checkFieldsRestriction( + accountId: number, + userId: number, + entity: Entity, + stageId: number | null, + dto?: UpdateEntityDto, + ) { + if (dto) { + const updatedFieldIds = dto.fieldValues?.filter((f) => f.state !== ObjectState.Unchanged)?.map((f) => f.fieldId); + if (updatedFieldIds?.length > 0) { + const readonlyFieldIds = await this.fieldSettingsService.getRestrictedFields({ + accountId, + entityTypeId: entity.entityTypeId, + access: FieldAccess.READONLY, + userId, + }); + if (updatedFieldIds.some((id) => readonlyFieldIds.includes(id))) { + throw new ReadonlyFieldChangedError(); + } + } + } + if (stageId) { + const requiredFieldIds = await this.fieldSettingsService.getRestrictedFields({ + accountId, + entityTypeId: entity.entityTypeId, + access: FieldAccess.REQUIRED, + userId, + stageId, + }); + if (requiredFieldIds.length > 0) { + const fieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + for (const requiredFieldId of requiredFieldIds) { + const dtoValue = dto?.fieldValues?.find((f) => f.fieldId === requiredFieldId); + if ( + (dtoValue && dtoValue.state === ObjectState.Deleted) || + (!dtoValue && !fieldValues.find((fv) => fv.fieldId === requiredFieldId)) + ) { + throw new RequiredFieldEmptyError(); + } + } + } + } + } + + private async excludeHiddenFields(accountId: number, userId: number, entity: Entity, fieldValues: FieldValue[]) { + const hiddenFieldIds = await this.fieldSettingsService.getRestrictedFields({ + accountId, + entityTypeId: entity.entityTypeId, + access: FieldAccess.HIDDEN, + userId, + stageId: entity.stageId, + }); + return hiddenFieldIds.length > 0 ? fieldValues.filter((f) => !hiddenFieldIds.includes(f.fieldId)) : fieldValues; + } + ////////////////// + + ////////////////// + // Automation helpers + ////////////////// + async applyAutomation({ + accountId, + processId, + entityTypeId, + stageId, + }: { + accountId: number; + processId: number; + entityTypeId: number; + stageId?: number | null; + }) { + const entities = await this.findMany(accountId, { entityTypeId, stageId }); + entities.forEach((entity) => this.entityEmitter.emitProcessStart({ accountId, entity, processId })); + } + ////////////////// +} diff --git a/backend/src/CRM/Service/Entity/ProjectEntityBoardCardService.ts b/backend/src/CRM/Service/Entity/ProjectEntityBoardCardService.ts new file mode 100644 index 0000000..2b3439f --- /dev/null +++ b/backend/src/CRM/Service/Entity/ProjectEntityBoardCardService.ts @@ -0,0 +1,151 @@ +import { Injectable } from '@nestjs/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { FieldCode } from '@/modules/entity/entity-field/field/enums/field-code.enum'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; + +import { EntityCategory } from '../../common'; + +import { Entity } from '../../Model/Entity/Entity'; +import { Task } from '../../task/entities'; +import { TaskService } from '../../task/task.service'; + +import { EntityBoardCard } from './Dto/Board/EntityBoardCard'; +import { ProjectEntityCard } from './Dto/Board/ProjectEntityCard'; +import { TasksCount } from './Dto/Board/TasksCount'; + +@Injectable() +export class ProjectEntityBoardCardService { + constructor( + private readonly authService: AuthorizationService, + private readonly fieldService: FieldService, + private readonly fieldValueService: FieldValueService, + private readonly taskService: TaskService, + ) {} + + public async getBoardCards( + accountId: number, + user: User, + entityTypeId: number, + entities: Entity[], + entityCategory: EntityCategory, + ): Promise { + const startDateField = await this.fieldService.findOne({ accountId, entityTypeId, code: FieldCode.StartDate }); + const endDateField = await this.fieldService.findOne({ accountId, entityTypeId, code: FieldCode.EndDate }); + + const taskResponsibles = await this.authService.whoCanView({ user, authorizable: Task.getAuthorizable() }); + + const cards = await Promise.all( + entities.map(async (entity, idx) => { + const card = await this.createBoardCard({ + accountId, + user, + entity, + entityCategory, + startDateFieldId: startDateField?.id, + endDateFieldId: endDateField?.id, + taskResponsibles, + }); + return { idx, card }; + }), + ); + + return cards.sort((a, b) => a.idx - b.idx).map((c) => c.card); + } + + public async getBoardCard( + accountId: number, + user: User, + entity: Entity, + entityCategory: EntityCategory, + ): Promise { + const startDateField = await this.fieldService.findOne({ + accountId, + entityTypeId: entity.entityTypeId, + code: FieldCode.StartDate, + }); + const endDateField = await this.fieldService.findOne({ + accountId, + entityTypeId: entity.entityTypeId, + code: FieldCode.EndDate, + }); + const taskResponsibles = await this.authService.whoCanView({ user, authorizable: Task.getAuthorizable() }); + + return this.createBoardCard({ + accountId, + user, + entity, + entityCategory, + startDateFieldId: startDateField?.id, + endDateFieldId: endDateField?.id, + taskResponsibles, + }); + } + + public async createBoardCard({ + accountId, + user, + entity, + entityCategory, + startDateFieldId, + endDateFieldId, + taskResponsibles, + }: { + accountId: number; + user: User; + entity: Entity; + entityCategory: EntityCategory; + startDateFieldId: number | null | undefined; + endDateFieldId: number | null | undefined; + taskResponsibles: number[] | undefined; + }): Promise { + const startDateFV = startDateFieldId + ? await this.fieldValueService.findOne({ accountId, entityId: entity.id, fieldId: startDateFieldId }) + : null; + const endDateFV = endDateFieldId + ? await this.fieldValueService.findOne({ accountId, entityId: entity.id, fieldId: endDateFieldId }) + : null; + + const startDate = startDateFV ? startDateFV.getValue() : null; + const endDate = endDateFV ? endDateFV.getValue() : null; + + const tasksCount = await this.getTasksCount({ accountId, entityId: entity.id, taskResponsibles }); + const userRights = await this.authService.getUserRights({ user, authorizable: entity }); + + return new EntityBoardCard({ + id: entity.id, + entityCategory, + boardId: entity.boardId, + stageId: entity.stageId, + price: null, + weight: entity.weight, + focused: entity.focused, + closedAt: entity.closedAt?.toISOString() ?? null, + data: ProjectEntityCard.create(entity, startDate, endDate, tasksCount, userRights), + }); + } + + private async getTasksCount({ + accountId, + entityId, + taskResponsibles, + }: { + accountId: number; + entityId: number; + taskResponsibles: number[] | undefined; + }): Promise { + const tasks = + !taskResponsibles || taskResponsibles.length + ? await this.taskService.findMany({ accountId, entityId, responsibles: taskResponsibles }) + : []; + + const notResolved = tasks.filter((task) => !task.isResolved); + const overdue = tasks.filter((task) => !task.isResolved && task.isTaskExpired()); + const today = tasks.filter((task) => !task.isResolved && task.isTaskToday()); + const resolved = tasks.filter((task) => task.isResolved); + + return new TasksCount(notResolved.length, overdue.length, today.length, resolved.length); + } +} diff --git a/backend/src/CRM/Service/Entity/entity-service.emitter.ts b/backend/src/CRM/Service/Entity/entity-service.emitter.ts new file mode 100644 index 0000000..a1f2408 --- /dev/null +++ b/backend/src/CRM/Service/Entity/entity-service.emitter.ts @@ -0,0 +1,238 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; + +import { UserNotification } from '@/common'; + +import { AutomationEventType, ProcessStartEvent, SendMessageEvent } from '@/modules/automation'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value'; +import { FieldType } from '@/modules/entity/entity-field/common'; + +import { CrmEventType, EntityCreatedEvent, EntityEvent, EntityOwnerChangedEvent } from '../../common'; +import { Entity } from '../../Model/Entity/Entity'; + +@Injectable() +export class EntityServiceEmitter { + constructor( + private readonly eventEmitter: EventEmitter2, + private readonly fieldValueService: FieldValueService, + ) {} + + public async emitCreatedEvent( + accountId: number, + entity: Entity, + options?: { + copiedFrom?: number; + userNotification?: UserNotification; + }, + ) { + this.eventEmitter.emit( + CrmEventType.EntityCreated, + new EntityCreatedEvent({ + accountId, + entityId: entity.id, + entityName: entity.name, + boardId: entity.boardId, + stageId: entity.stageId, + createdBy: entity.createdBy, + ownerId: entity.responsibleUserId, + entityTypeId: entity.entityTypeId, + copiedFrom: options?.copiedFrom, + userNotification: options?.userNotification ?? UserNotification.Default, + }), + ); + + if (!options?.copiedFrom) { + this.sendAutomationMessage(accountId, entity, CrmEventType.EntityCreated); + } + } + + public async emitUpdatedEvent( + accountId: number, + entity: Entity, + options?: { + userNotification?: UserNotification; + }, + ) { + this.eventEmitter.emit( + CrmEventType.EntityUpdated, + new EntityEvent({ + accountId, + entityId: entity.id, + entityTypeId: entity.entityTypeId, + boardId: entity.boardId, + stageId: entity.stageId, + ownerId: entity.responsibleUserId, + userNotification: options?.userNotification, + }), + ); + } + + public async emitStageChangedEvent( + accountId: number, + entity: Entity, + options?: { + userNotification?: UserNotification; + }, + ) { + this.eventEmitter.emit( + CrmEventType.EntityStageChanged, + new EntityEvent({ + accountId, + entityId: entity.id, + entityTypeId: entity.entityTypeId, + boardId: entity.boardId, + stageId: entity.stageId, + ownerId: entity.responsibleUserId, + userNotification: options?.userNotification, + }), + ); + + this.sendAutomationMessage(accountId, entity, CrmEventType.EntityStageChanged); + } + + public async emitOwnerChangedEvent( + accountId: number, + changedBy: number, + entity: Entity, + options?: { + copiedFrom?: number; + userNotification?: UserNotification; + }, + ) { + this.eventEmitter.emit( + CrmEventType.EntityOwnerChanged, + new EntityOwnerChangedEvent({ + accountId, + entityId: entity.id, + entityName: entity.name, + boardId: entity.boardId, + stageId: entity.stageId, + changedBy, + ownerId: entity.responsibleUserId, + entityTypeId: entity.entityTypeId, + userNotification: options?.userNotification, + }), + ); + + this.sendAutomationMessage(accountId, entity, CrmEventType.EntityOwnerChanged); + } + + public async emitDeletedEvent( + accountId: number, + entity: Entity, + options?: { + userNotification?: UserNotification; + }, + ) { + this.eventEmitter.emit( + CrmEventType.EntityDeleted, + new EntityEvent({ + accountId, + entityId: entity.id, + entityTypeId: entity.entityTypeId, + boardId: entity.boardId, + stageId: entity.stageId, + ownerId: entity.responsibleUserId, + userNotification: options?.userNotification, + }), + ); + } + + private async sendAutomationMessage(accountId: number, entity: Entity, eventType: CrmEventType) { + const entityVar = await this.createEntityVariable({ accountId, entity }); + + this.eventEmitter.emit( + AutomationEventType.SendMessage, + new SendMessageEvent({ + accountId, + message: { + name: [accountId, entity.entityTypeId, eventType], + variables: { accountId, entity: entityVar }, + }, + }), + ); + } + + public async emitProcessStart({ + accountId, + entity, + processId, + }: { + accountId: number; + entity: Entity; + processId: number; + }) { + const entityVar = await this.createEntityVariable({ accountId, entity }); + + this.eventEmitter.emit( + AutomationEventType.ProcessStart, + new ProcessStartEvent({ accountId, processId, variables: { accountId, entity: entityVar } }), + ); + } + + public async createEntityVariable({ accountId, entity }: { accountId: number; entity: Entity }) { + const fields = await this.getFieldValues({ accountId, entity }); + + return { + id: entity.id, + name: entity.name, + entityTypeId: entity.entityTypeId, + responsibleUserId: entity.responsibleUserId, + boardId: entity.boardId, + stageId: entity.stageId, + createdBy: entity.createdBy, + participantIds: entity.participantIds, + copiedFrom: entity.copiedFrom, + copiedCount: entity.copiedCount, + closedAt: entity.closedAt?.toISOString(), + createdAt: entity.createdAt.toISOString(), + updatedAt: entity.updatedAt.toISOString(), + fields, + }; + } + + private async getFieldValues({ + accountId, + entity, + }: { + accountId: number; + entity: Entity; + }): Promise> { + const fields = {}; + const fieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + fieldValues.forEach((fieldValue) => { + let value = fieldValue.getValue(); + if (value !== null && value !== undefined) { + switch (fieldValue.fieldType) { + case FieldType.Email: + case FieldType.Phone: + case FieldType.MultiText: + value = value + ? Array.isArray(value) + ? value + .filter((v) => v) + .map((v) => encodeURIComponent(v.toString())) + .join('\n') + : encodeURIComponent(value.toString()) + : null; + break; + case FieldType.Select: + case FieldType.ColoredSelect: + value = value ? [value] : null; + break; + case FieldType.Link: + case FieldType.Text: + case FieldType.RichText: + value = value ? encodeURIComponent(value.toString()) : null; + break; + case FieldType.Checklist: + value = null; //skip checklist fields + } + if (value !== null && value !== undefined) { + fields[`f_${fieldValue.fieldId}`] = value; + } + } + }); + return fields; + } +} diff --git a/backend/src/CRM/Service/Entity/entity-service.handler.ts b/backend/src/CRM/Service/Entity/entity-service.handler.ts new file mode 100644 index 0000000..e96235f --- /dev/null +++ b/backend/src/CRM/Service/Entity/entity-service.handler.ts @@ -0,0 +1,251 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; +import { UserService } from '@/modules/iam/user/user.service'; +import { FieldEvent, FieldEventType, FieldTypes } from '@/modules/entity/entity-field/common'; +import { + ActionEntityCreateSettings, + ActionEntityStageChangeSettings, + AutomationEventType, + EntityTypeApplyEvent, + OnAutomationJob, + EntityTypeActionType, + AutomationJob, + ChangeStageType, + ActionEntityResponsibleChangeSettings, + ActionEntityLinkedStageChangeSettings, +} from '@/modules/automation'; + +import { CrmEventType, EntityPriceUpdateEvent } from '../../common'; +import { Entity } from '../../Model/Entity/Entity'; +import { EntityService } from './EntityService'; +import { EntityServiceEmitter } from './entity-service.emitter'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; + entityTypeId?: number | null; + responsibleUserId?: number | null; +} + +interface EntityCreateVariables { + accountId: number; + entity?: EntityVariables | null; + entitySettings?: ActionEntityCreateSettings | null; +} + +interface EntityChangeStageVariables { + accountId: number; + entity?: EntityVariables | null; + entitySettings?: ActionEntityStageChangeSettings | null; +} + +interface EntityLinkedChangeStageVariables { + accountId: number; + entity?: EntityVariables | null; + entitySettings?: ActionEntityLinkedStageChangeSettings | null; +} + +interface EntityResponsibleChangeVariables { + accountId: number; + entity?: EntityVariables | null; + entitySettings?: ActionEntityResponsibleChangeSettings | null; +} + +@Injectable() +export class EntityServiceHandler { + private readonly logger = new Logger(EntityServiceHandler.name); + constructor( + private readonly userService: UserService, + private readonly entityService: EntityService, + private readonly entityEmitter: EntityServiceEmitter, + ) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.entityService.removeUser(event); + } + + @OnEvent(CrmEventType.EntityPriceUpdate, { async: true }) + public async onEntityPriceUpdate(event: EntityPriceUpdateEvent) { + this.entityService.updateValue(event.accountId, event.entityId, event.price); + } + + @OnEvent(FieldEventType.FieldCreated, { async: true }) + public async onFieldCreated(event: FieldEvent) { + if (FieldTypes.formula.includes(event.type)) { + this.entityService.recalculateFormulas(event); + } + } + + @OnEvent(FieldEventType.FieldUpdated, { async: true }) + public async onFieldUpdated(event: FieldEvent) { + if (FieldTypes.formula.includes(event.type)) { + this.entityService.recalculateFormulas(event); + } + } + + @OnEvent(AutomationEventType.EntityTypeApply, { async: true }) + public async onAutomationApply(event: EntityTypeApplyEvent) { + this.entityService.applyAutomation({ + accountId: event.accountId, + processId: event.processId, + entityTypeId: event.entityTypeId, + stageId: event.stageId, + }); + } + + /** + * @deprecated use new @see handleStageChangeJob instead + */ + @OnAutomationJob(EntityTypeActionType.ChangeStage) + async handleStageChangeJobOld(job: AutomationJob): Promise<{ variables?: unknown }> { + return this.handleStageChangeJob(job); + } + @OnAutomationJob(EntityTypeActionType.EntityStageChange) + async handleStageChangeJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.entitySettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, entitySettings } = job.variables; + + const current = await this.entityService.findOne(accountId, { entityId: entity.id }); + if (current && (!current.stageId || entitySettings.allowAnyStage || current.stageId === entity.stageId)) { + const user = await this.userService.findOne({ accountId, id: entity.responsibleUserId }); + let updated: Entity = null; + switch (entitySettings.operationType) { + case ChangeStageType.Move: + updated = await this.entityService.update(accountId, user, entity.id, { stageId: entitySettings.stageId }); + break; + case ChangeStageType.CopyOriginal: + updated = await this.entityService.copyToStage(accountId, entity.id, entitySettings.stageId, true); + break; + case ChangeStageType.CopyNew: + updated = await this.entityService.copyToStage(accountId, entity.id, entitySettings.stageId, false); + break; + } + const updatedVar = updated + ? await this.entityEmitter.createEntityVariable({ accountId, entity: updated }) + : null; + return { variables: { ...job.variables, entity: updatedVar ?? entity } }; + } + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + } + return { variables: job.variables }; + } + + @OnAutomationJob(EntityTypeActionType.EntityLinkedStageChange) + async handleLinkedStageChangeJob( + job: AutomationJob, + ): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.entitySettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, entitySettings } = job.variables; + + const linkedEntities = await this.entityService.findLinkedEntities({ + accountId, + entityId: entity.id, + entityTypeId: entitySettings.entityTypeId, + }); + if (linkedEntities.length) { + const current = linkedEntities[0]; + const user = await this.userService.findOne({ accountId, id: current.responsibleUserId }); + let updated: Entity = null; + switch (entitySettings.operationType) { + case ChangeStageType.Move: + updated = await this.entityService.update(accountId, user, current.id, { + stageId: entitySettings.stageId, + }); + break; + case ChangeStageType.CopyOriginal: + updated = await this.entityService.copyToStage(accountId, current.id, entitySettings.stageId, true); + break; + case ChangeStageType.CopyNew: + updated = await this.entityService.copyToStage(accountId, current.id, entitySettings.stageId, false); + break; + } + const updatedVar = updated + ? await this.entityEmitter.createEntityVariable({ accountId, entity: updated }) + : null; + return { variables: { ...job.variables, entity: updatedVar ?? entity } }; + } + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + } + return { variables: job.variables }; + } + + @OnAutomationJob(EntityTypeActionType.EntityResponsibleChange) + async handleResponsibleChangeJob( + job: AutomationJob, + ): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.entitySettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, entitySettings } = job.variables; + + const current = await this.entityService.findOne(accountId, { entityId: entity.id }); + if (current && (!current.stageId || entitySettings.allowAnyStage || current.stageId === entity.stageId)) { + const user = await this.userService.findOne({ accountId, id: entity.responsibleUserId }); + + const updated = await this.entityService.update(accountId, user, entity.id, { + responsibleUserId: entitySettings.responsibleUserId, + }); + + const updatedVar = await this.entityEmitter.createEntityVariable({ accountId, entity: updated }); + return { variables: { ...job.variables, entity: updatedVar } }; + } + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + } + + return { variables: job.variables }; + } + + @OnAutomationJob(EntityTypeActionType.EntityCreate) + async handleCreateJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.entitySettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, entitySettings } = job.variables; + + const current = await this.entityService.findOne(accountId, { entityId: entity.id }); + if (current && (!current.stageId || entitySettings.allowAnyStage || current.stageId === entity.stageId)) { + const user = await this.userService.findOne({ accountId, id: entity.responsibleUserId }); + const [created] = await this.entityService.createSimple({ + accountId, + user, + dto: { + entityTypeId: entitySettings.entityTypeId, + boardId: entitySettings.boardId, + stageId: entitySettings.stageId, + ownerId: entitySettings.ownerId, + name: entitySettings.name, + }, + options: { linkedEntities: [entity.id] }, + }); + + const createdVar = await this.entityEmitter.createEntityVariable({ accountId, entity: created }); + return { variables: { ...job.variables, entity: createdVar } }; + } + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + } + return { variables: job.variables }; + } +} diff --git a/backend/src/CRM/Service/Entity/enums/task-indicator.enum.ts b/backend/src/CRM/Service/Entity/enums/task-indicator.enum.ts new file mode 100644 index 0000000..1d822ca --- /dev/null +++ b/backend/src/CRM/Service/Entity/enums/task-indicator.enum.ts @@ -0,0 +1,6 @@ +export enum TaskIndicator { + Empty = 'empty', + Overdue = 'overdue', + Today = 'today', + Upcoming = 'upcoming', +} diff --git a/backend/src/CRM/Service/Entity/errors/index.ts b/backend/src/CRM/Service/Entity/errors/index.ts new file mode 100644 index 0000000..98682d8 --- /dev/null +++ b/backend/src/CRM/Service/Entity/errors/index.ts @@ -0,0 +1,2 @@ +export * from './readonly-field-changed.error'; +export * from './required-field-empty.error'; diff --git a/backend/src/CRM/Service/Entity/errors/readonly-field-changed.error.ts b/backend/src/CRM/Service/Entity/errors/readonly-field-changed.error.ts new file mode 100644 index 0000000..1ed9d11 --- /dev/null +++ b/backend/src/CRM/Service/Entity/errors/readonly-field-changed.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class ReadonlyFieldChangedError extends ServiceError { + constructor(message = 'Readonly field changed') { + super({ errorCode: 'entity.readonly_field_changed', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/CRM/Service/Entity/errors/required-field-empty.error.ts b/backend/src/CRM/Service/Entity/errors/required-field-empty.error.ts new file mode 100644 index 0000000..7fe0d83 --- /dev/null +++ b/backend/src/CRM/Service/Entity/errors/required-field-empty.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class RequiredFieldEmptyError extends ServiceError { + constructor(message = 'Required field do not set') { + super({ errorCode: 'required_field_empty', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/CRM/Service/ExternalEntity/CreateExternalEntityDto.ts b/backend/src/CRM/Service/ExternalEntity/CreateExternalEntityDto.ts new file mode 100644 index 0000000..c620097 --- /dev/null +++ b/backend/src/CRM/Service/ExternalEntity/CreateExternalEntityDto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsUrl } from 'class-validator'; + +import { CreateSimpleEntityDto } from '../Entity/Dto'; + +export class CreateExternalEntityDto extends CreateSimpleEntityDto { + @ApiProperty() + @IsUrl() + @IsNotEmpty() + url: string; +} diff --git a/backend/src/CRM/Service/ExternalEntity/CreateExternalEntityResult.ts b/backend/src/CRM/Service/ExternalEntity/CreateExternalEntityResult.ts new file mode 100644 index 0000000..10a9804 --- /dev/null +++ b/backend/src/CRM/Service/ExternalEntity/CreateExternalEntityResult.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateExternalEntityResult { + @ApiProperty() + entityCardUrl: string; + + constructor(crmCardUrl: string) { + this.entityCardUrl = crmCardUrl; + } +} diff --git a/backend/src/CRM/Service/ExternalEntity/ExternalEntityDto.ts b/backend/src/CRM/Service/ExternalEntity/ExternalEntityDto.ts new file mode 100644 index 0000000..7f47bc7 --- /dev/null +++ b/backend/src/CRM/Service/ExternalEntity/ExternalEntityDto.ts @@ -0,0 +1,47 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { UIDataRecord } from '../../Model/ExternalEntity/UIDataRecord'; +import { ExternalEntity } from '../../Model/ExternalEntity/ExternalEntity'; +import { ExternalSystem } from '../../Model/ExternalEntity/ExternalSystem'; +import { ExternalSystemDto } from './ExternalSystemDto'; + +export class ExternalEntityDto { + @ApiProperty() + id: number; + + @ApiProperty() + url: string; + + @ApiProperty() + entityId: number; + + @ApiProperty() + uiData: UIDataRecord[]; + + @ApiProperty({ nullable: true }) + system: ExternalSystemDto | null; + + private constructor( + id: number, + url: string, + entityId: number, + system: ExternalSystemDto | null, + uiData: UIDataRecord[] | null, + ) { + this.id = id; + this.url = url; + this.entityId = entityId; + this.system = system; + this.uiData = uiData; + } + + public static create(entity: ExternalEntity, system: ExternalSystem) { + return new ExternalEntityDto( + entity.id, + entity.url, + entity.entityId, + system ? ExternalSystemDto.create(system) : null, + entity.uiData, + ); + } +} diff --git a/backend/src/CRM/Service/ExternalEntity/ExternalEntityService.ts b/backend/src/CRM/Service/ExternalEntity/ExternalEntityService.ts new file mode 100644 index 0000000..30b06f2 --- /dev/null +++ b/backend/src/CRM/Service/ExternalEntity/ExternalEntityService.ts @@ -0,0 +1,65 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { FrontendRoute, UrlGeneratorService } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { EntityService } from '../Entity/EntityService'; +import { ExternalSystemService } from './ExternalSystemService'; + +import { ExternalEntity } from '../../Model/ExternalEntity/ExternalEntity'; +import { ExternalSystem } from '../../Model/ExternalEntity/ExternalSystem'; +import { ExternalSystemCode } from '../../Model/ExternalEntity/ExternalSystemCode'; + +import { SalesforceIntegrationService } from '../../Salesforce/Service/SalesforceIntegrationService'; + +import { CreateExternalEntityDto } from './CreateExternalEntityDto'; +import { CreateExternalEntityResult } from './CreateExternalEntityResult'; + +@Injectable() +export class ExternalEntityService { + constructor( + @InjectRepository(ExternalEntity) + private readonly repository: Repository, + @Inject(forwardRef(() => EntityService)) + private readonly entityService: EntityService, + private readonly externalSystemService: ExternalSystemService, + private readonly urlGenerator: UrlGeneratorService, + private readonly salesforceIntegrationService: SalesforceIntegrationService, + ) {} + + public async create(account: Account, user: User, dto: CreateExternalEntityDto): Promise { + const [entity] = await this.entityService.createSimple({ accountId: account.id, user, dto }); + + const entityUrl = this.urlGenerator.createUrl({ + route: FrontendRoute.entity.card({ entityTypeId: dto.entityTypeId, entityId: entity.id }), + subdomain: account.subdomain, + }); + + const externalSystem = await this.externalSystemService.getMatched(dto.url); + const data = + externalSystem?.code === ExternalSystemCode.SalesForce + ? await this.salesforceIntegrationService.getDataFromUrl(account.id, dto.url, entityUrl) + : null; + await this.repository.save( + new ExternalEntity(account.id, entity.id, dto.url, externalSystem?.id, data?.rawData, data?.uiData), + ); + + return new CreateExternalEntityResult(entityUrl); + } + + public async getExternalEntitiesWithType( + entityId: number, + ): Promise<{ externalEntity: ExternalEntity; type: ExternalSystem }[]> { + const entities = await this.repository.findBy({ entityId }); + const result = []; + for (const entity of entities) { + const type = entity.system ? await this.externalSystemService.getById(entity.system) : null; + result.push({ externalEntity: entity, type }); + } + return result; + } +} diff --git a/backend/src/CRM/Service/ExternalEntity/ExternalSystemDto.ts b/backend/src/CRM/Service/ExternalEntity/ExternalSystemDto.ts new file mode 100644 index 0000000..f33848e --- /dev/null +++ b/backend/src/CRM/Service/ExternalEntity/ExternalSystemDto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ExternalSystem } from '../../Model/ExternalEntity/ExternalSystem'; + +export class ExternalSystemDto { + @ApiProperty() + id: number; + + @ApiProperty() + name: string; + + @ApiProperty() + code: string; + + private constructor(id: number, name: string, code: string) { + this.id = id; + this.name = name; + this.code = code; + } + + public static create(type: ExternalSystem) { + return new ExternalSystemDto(type.id, type.name, type.code); + } +} diff --git a/backend/src/CRM/Service/ExternalEntity/ExternalSystemService.ts b/backend/src/CRM/Service/ExternalEntity/ExternalSystemService.ts new file mode 100644 index 0000000..576854a --- /dev/null +++ b/backend/src/CRM/Service/ExternalEntity/ExternalSystemService.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { ArrayContains, Repository } from 'typeorm'; + +import { ExternalSystem } from '../../Model/ExternalEntity/ExternalSystem'; + +@Injectable() +export class ExternalSystemService { + constructor( + @InjectRepository(ExternalSystem) + private readonly repository: Repository, + ) {} + + public async getById(id: number): Promise { + return await this.repository.findOneBy({ id }); + } + + public async getMatched(url: string): Promise { + const { hostname } = new URL(url); + const host = hostname.split('.').slice(-2).join('.'); + return await this.repository.findOneBy({ urlTemplates: ArrayContains([host]) }); + } +} diff --git a/backend/src/CRM/Service/FileLink/FileLinkDto.ts b/backend/src/CRM/Service/FileLink/FileLinkDto.ts new file mode 100644 index 0000000..421c719 --- /dev/null +++ b/backend/src/CRM/Service/FileLink/FileLinkDto.ts @@ -0,0 +1,67 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { FileLink } from '../../Model/FileLink/FileLink'; + +export class FileLinkDto { + @ApiProperty() + id: number; + + @ApiProperty() + fileId: string; + + @ApiProperty() + fileName: string; + + @ApiProperty() + fileSize: number; + + @ApiProperty() + fileType: string; + + @ApiProperty() + downloadUrl: string; + + @ApiProperty() + previewUrl: string | null; + + @ApiProperty() + createdBy: number; + + @ApiProperty() + createdAt: string; + + private constructor( + id: number, + fileId: string, + fileName: string, + fileSize: number, + fileType: string, + createdAt: string, + createdBy: number, + downloadUrl: string, + previewUrl: string | null, + ) { + this.id = id; + this.fileId = fileId; + this.fileName = fileName; + this.fileSize = fileSize; + this.fileType = fileType; + this.createdAt = createdAt; + this.createdBy = createdBy; + this.downloadUrl = downloadUrl; + this.previewUrl = previewUrl; + } + + public static create(fileLink: FileLink, downloadUrl: string, previewUrl: string | null) { + return new FileLinkDto( + fileLink.id, + fileLink.fileId, + fileLink.fileName, + fileLink.fileSize, + fileLink.fileType, + fileLink.createdAt.toISOString(), + fileLink.createdBy, + downloadUrl, + previewUrl, + ); + } +} diff --git a/backend/src/CRM/Service/FileLink/FileLinkService.ts b/backend/src/CRM/Service/FileLink/FileLinkService.ts new file mode 100644 index 0000000..74c27ac --- /dev/null +++ b/backend/src/CRM/Service/FileLink/FileLinkService.ts @@ -0,0 +1,163 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { FindOptionsOrderValue, Repository } from 'typeorm'; + +import { FileLinkSource, NotFoundError } from '@/common'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { StorageUrlService } from '@/modules/storage/storage-url.service'; +import { StorageService } from '@/modules/storage/storage.service'; + +import { CrmEventType, FileLinkCreatedEvent, FileLinkEvent } from '../../common'; + +import { FileLink } from '../../Model/FileLink/FileLink'; +import { FileLinkDto } from './FileLinkDto'; + +@Injectable() +export class FileLinkService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(FileLink) + private readonly repository: Repository, + private readonly storageService: StorageService, + private readonly storageUrlService: StorageUrlService, + ) {} + + private async create( + accountId: number, + sourceType: FileLinkSource, + sourceId: number, + fileInfoId: string, + ): Promise { + const fileInfo = await this.storageService.markUsed({ accountId, id: fileInfoId }); + if (!fileInfo) { + return null; + } + const fileLink = await this.repository.save( + new FileLink( + accountId, + sourceType, + sourceId, + fileInfoId, + fileInfo.originalName, + fileInfo.size, + fileInfo.mimeType, + fileInfo.createdBy, + fileInfo.createdAt, + ), + ); + + this.eventEmitter.emit( + CrmEventType.FileLinkCreated, + new FileLinkCreatedEvent({ + accountId, + sourceType: fileLink.sourceType, + sourceId: fileLink.sourceId, + fileLinkId: fileLink.id, + createdAt: fileLink.createdAt.toISOString(), + }), + ); + + return fileLink; + } + public async addFile( + account: Account, + sourceType: FileLinkSource, + sourceId: number, + fileId: string, + ): Promise { + const link = await this.create(account.id, sourceType, sourceId, fileId); + return link ? this.getFileLinkDto(account, link) : null; + } + + public async addFiles( + account: Account, + sourceType: FileLinkSource, + sourceId: number, + fileIds: string[], + ): Promise { + const linkDtos: FileLinkDto[] = []; + for (const file of fileIds) { + const linkDto = await this.addFile(account, sourceType, sourceId, file); + if (linkDto) linkDtos.push(linkDto); + } + return linkDtos; + } + + public async findFileLinks(accountId: number, sourceType: FileLinkSource, sourceId: number): Promise { + return await this.repository.find({ where: { accountId, sourceType, sourceId } }); + } + + public async findDtoById(account: Account, fileLinkId: number): Promise { + const link = await this.repository.findOneBy({ id: fileLinkId, accountId: account.id }); + return link ? this.getFileLinkDto(account, link) : null; + } + + public async getFileLinkDtos( + account: Account, + sourceType: FileLinkSource, + sourceId: number, + order: FindOptionsOrderValue = 'ASC', + ) { + const links = await this.repository.find({ + where: { accountId: account.id, sourceType, sourceId }, + order: { id: order }, + }); + + return links.map((link) => this.getFileLinkDto(account, link)); + } + + public async processFiles(accountId: number, sourceType: FileLinkSource, sourceId: number, fileIds: string[]) { + const currentLinks = await this.repository.findBy({ accountId, sourceType, sourceId }); + const currentFiles = currentLinks ? currentLinks.map((link) => link.fileId) : []; + + const createdFiles = fileIds.filter((file) => !currentFiles.includes(file)); + for (const file of createdFiles) { + await this.create(accountId, sourceType, sourceId, file); + } + + const deletedFiles = currentFiles.filter((file) => !fileIds.includes(file)); + for (const file of deletedFiles) { + await this.delete(accountId, sourceType, sourceId, file); + } + } + + public async deleteFileLink(accountId: number, id: number) { + const link = await this.repository.findOneBy({ id, accountId }); + if (!link) { + throw NotFoundError.withId(FileLink, id); + } + return await this.delete(accountId, link.sourceType, link.sourceId, link.fileId); + } + + public async deleteFileLinks(accountId: number, ids: number[]): Promise { + await Promise.all(ids.map((id) => this.deleteFileLink(accountId, id))); + } + + private getFileLinkDto(account: Account, link: FileLink): FileLinkDto { + const downloadUrl = this.storageUrlService.getDownloadUrl(account.subdomain, link.fileId); + const previewUrl = link.isImage() + ? this.storageUrlService.getImageUrl(account.id, account.subdomain, link.fileId) + : null; + return FileLinkDto.create(link, downloadUrl, previewUrl); + } + + private async delete(accountId: number, sourceType: FileLinkSource, sourceId: number, fileId: string) { + const deleted = await this.storageService.delete({ accountId, id: fileId }); + if (deleted) { + const fileLink = await this.repository.findOneBy({ accountId, sourceType, sourceId, fileId }); + await this.repository.delete({ accountId, sourceType, sourceId, fileId }); + if (fileLink) { + this.eventEmitter.emit( + CrmEventType.FileLinkDeleted, + new FileLinkEvent({ + accountId, + sourceType: fileLink.sourceType, + sourceId: fileLink.sourceId, + fileLinkId: fileLink.id, + }), + ); + } + } + } +} diff --git a/backend/src/CRM/Service/Import/ImportService.ts b/backend/src/CRM/Service/Import/ImportService.ts new file mode 100644 index 0000000..91a73af --- /dev/null +++ b/backend/src/CRM/Service/Import/ImportService.ts @@ -0,0 +1,470 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Cell, CellRichTextValue, CellValue, Row, Workbook, Worksheet } from 'exceljs'; + +import { DateUtil, isUnique, UserNotification } from '@/common'; +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldOption } from '@/modules/entity/entity-field/field-option/entities/field-option.entity'; +import { FieldOptionService } from '@/modules/entity/entity-field/field-option/field-option.service'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; +import { Field } from '@/modules/entity/entity-field/field/entities/field.entity'; +import { SimpleFieldValueDto } from '@/modules/entity/entity-field/field-value/dto'; + +import { CrmEventType, EntityImportEvent } from '../../common'; +import { Board } from '../../board'; +import { BoardService } from '../../board/board.service'; +import { BoardStage, BoardStageService } from '../../board-stage'; +import { EntityType } from '../../entity-type/entities/entity-type.entity'; +import { EntityTypeService } from '../../entity-type/entity-type.service'; + +import { EntityService } from '../Entity/EntityService'; + +type SystemFieldName = 'Board' | 'Stage' | 'Name' | 'Owner'; +type SystemFieldType = Record; +const SystemField: SystemFieldType = { + Board: 'Board', + Stage: 'Stage', + Name: 'Name', + Owner: 'Owner', +}; +const SystemFields = [SystemField.Board, SystemField.Stage, SystemField.Name, SystemField.Owner]; + +interface ImportInfo { + key: string | null; + entityTypeId: number; + fields: { fieldId: number | SystemFieldName; colNumber: number }[]; +} + +interface EntityIdWithKey { + key: string; + entityId: number; +} + +@Injectable() +export class ImportService { + constructor( + private readonly eventEmitter: EventEmitter2, + private readonly userService: UserService, + private readonly entityTypeService: EntityTypeService, + private readonly fieldService: FieldService, + private readonly entityService: EntityService, + private readonly boardService: BoardService, + private readonly stageService: BoardStageService, + private readonly fieldOptionService: FieldOptionService, + ) {} + + public async generateTemplateForEntityType(accountId: number, entityTypeId: number) { + const entityType = await this.entityTypeService.getById(accountId, entityTypeId); + const linkedEntityTypes = await this.entityTypeService.findLinkedTypes(accountId, entityType.id); + const entityTypes = [entityType, ...linkedEntityTypes]; + + const fieldCodes: string[] = []; + for (const et of entityTypes) { + const prefix = et.id !== entityType.id ? '1//' : ''; + const boards = await this.boardService.findMany({ filter: { accountId, recordId: et.id } }); + if (boards && boards.length > 0) { + fieldCodes.push(`${prefix}${et.name}//${SystemField.Board} {{${et.id}/${SystemField.Board}}}`); + fieldCodes.push(`${prefix}${et.name}//${SystemField.Stage} {{${et.id}/${SystemField.Stage}}}`); + } + fieldCodes.push(`${prefix}${et.name}//${SystemField.Name} {{${et.id}/${SystemField.Name}}}`); + fieldCodes.push(`${prefix}${et.name}//${SystemField.Owner} {{${et.id}/${SystemField.Owner}}}`); + const fields = await this.fieldService.findMany({ accountId, entityTypeId: et.id }); + fieldCodes.push(...fields.map((field) => `${prefix}${et.name}//${field.name} {{${et.id}/${field.id}}}`)); + } + + const workbook = new Workbook(); + const worksheet = workbook.addWorksheet(entityType.sectionView); + worksheet.addRow(fieldCodes); + + // Set column widths to fit column name + const numberOfColumns = worksheet.columns.length; + for (let columnIndex = 1; columnIndex <= numberOfColumns; columnIndex++) { + const columnWidth = this.calculateColumnWidth(worksheet, columnIndex); + worksheet.getColumn(columnIndex).width = columnWidth; + } + + const buffer = await workbook.xlsx.writeBuffer(); + + return buffer; + } + + private calculateColumnWidth(worksheet: Worksheet, columnIndex: number) { + let maxWidth = 0; + worksheet.eachRow((row) => { + const cellValue = row.getCell(columnIndex).value; + const cellWidth = typeof cellValue === 'string' ? cellValue.length : String(cellValue).length; + maxWidth = Math.max(maxWidth, cellWidth); + }); + + return maxWidth; + } + + public async importDataBackground(accountId: number, user: User, entityTypeId: number, file: StorageFile) { + this.importDataForEntityType(accountId, user, entityTypeId, file); + } + + public async importDataForEntityType(accountId: number, user: User, entityTypeId: number, file: StorageFile) { + const workbook = new Workbook(); + await workbook.xlsx.load(file.buffer as any); + const worksheet = workbook.worksheets[0]; + + const importInfos = this.processHeaderRow(worksheet.getRow(1)); + const mainEntityInfo = importInfos.find((ii) => ii.entityTypeId === entityTypeId); + + let totalCount = 0; + let entityTypeName: string | null = null; + + if (mainEntityInfo) { + const entityTypeIds = importInfos.map((ii) => ii.entityTypeId).filter(isUnique); + const entityTypeCache = await this.getEntityTypeCache(accountId, entityTypeIds); + const boardCache = await this.getBoardCache(accountId, entityTypeIds); + const stageCache = await this.getStageCache( + accountId, + boardCache.map((b) => b.id), + ); + const fieldCache = await this.getFieldCache(accountId, importInfos); + const fieldOptionCache = await this.getFieldOptionCache(accountId, fieldCache); + const userCache: User[] = [user]; + const linkedEntitiesInfo = importInfos.filter((ii) => ii.entityTypeId !== entityTypeId); + + for (let rowNumber = 2; rowNumber <= worksheet.lastRow.number; rowNumber++) { + const result = await this.processDataRow( + accountId, + user, + mainEntityInfo, + linkedEntitiesInfo, + worksheet.getRow(rowNumber), + boardCache, + stageCache, + fieldCache, + fieldOptionCache, + userCache, + ); + if (result) totalCount++; + } + + entityTypeName = entityTypeCache.find((et) => et.id === entityTypeId)?.sectionName ?? null; + } + + this.eventEmitter.emit( + CrmEventType.EntityImportCompleted, + new EntityImportEvent({ + accountId, + userId: user.id, + fileName: decodeURIComponent(file.originalName), + entityTypeId, + entityTypeName, + totalCount, + }), + ); + } + + private processHeaderRow(row: Row): ImportInfo[] { + const importInfo: ImportInfo[] = []; + const regex = /(([^/]+)\/\/)?(?:.+)\/\/(?:.+)\s+{{(\d+)\/(\d+|Board|Stage|Name|Owner)}}/; + + row.eachCell({ includeEmpty: false }, (cell, colNumber) => { + const cellValue = this.getCellValueAsString(cell.value); + const match = cellValue.match(regex); + if (match) { + const [, , key, entityTypeIdStr, fieldIdStr] = match; + const entityTypeId = parseInt(entityTypeIdStr); + const fieldId = SystemFields.includes(fieldIdStr as SystemFieldName) + ? (fieldIdStr as SystemFieldName) + : parseInt(fieldIdStr); + const entityType = importInfo.find((ii) => ii.key === key && ii.entityTypeId === entityTypeId); + if (entityType) { + if (!entityType.fields) entityType.fields = []; + entityType.fields.push({ fieldId, colNumber }); + } else { + importInfo.push({ key, entityTypeId, fields: [{ fieldId, colNumber }] }); + } + } + }); + return importInfo; + } + + private async processDataRow( + accountId: number, + user: User, + mainEntityInfo: ImportInfo, + allEntityInfo: ImportInfo[], + row: Row, + boardCache: Board[], + stageCache: BoardStage[], + fieldCache: Field[], + fieldOptionCache: FieldOption[], + userCache: User[], + ): Promise { + const mainEntity = await this.createEntity( + accountId, + user, + mainEntityInfo, + row, + boardCache, + stageCache, + fieldCache, + fieldOptionCache, + userCache, + ); + if (mainEntity) { + const entitiesToLink: EntityIdWithKey[] = []; + for (const entityInfo of allEntityInfo) { + const linkedEntity = await this.createEntity( + accountId, + user, + entityInfo, + row, + boardCache, + stageCache, + fieldCache, + fieldOptionCache, + userCache, + mainEntity.id, + entitiesToLink, + ); + if (linkedEntity) entitiesToLink.push({ key: entityInfo.key, entityId: linkedEntity.id }); + } + } + return !!mainEntity; + } + + private async createEntity( + accountId: number, + user: User, + importInfo: ImportInfo, + row: Row, + boardCache: Board[], + stageCache: BoardStage[], + fieldCache: Field[], + fieldOptionCache: FieldOption[], + userCache: User[], + mainEntityId?: number, + entitiesToLink?: EntityIdWithKey[], + ) { + const fieldValues: SimpleFieldValueDto[] = []; + let boardName: string | null = null; + let stageName: string | null = null; + let name: string | null = null; + let owner: string | null = null; + for (const fieldInfo of importInfo.fields) { + if (fieldInfo.fieldId === SystemField.Board) { + boardName = this.getCellValueAsString(row.getCell(fieldInfo.colNumber).value); + } else if (fieldInfo.fieldId === SystemField.Stage) { + stageName = this.getCellValueAsString(row.getCell(fieldInfo.colNumber).value); + } else if (fieldInfo.fieldId === SystemField.Name) { + name = this.getCellValueAsString(row.getCell(fieldInfo.colNumber).value); + } else if (fieldInfo.fieldId === SystemField.Owner) { + owner = this.getCellValueAsString(row.getCell(fieldInfo.colNumber).value); + } else { + const fieldId = Number(fieldInfo.fieldId); + const field = fieldCache.find((f) => f.id === fieldId); + if (field) { + const fieldValue = await this.getFieldValue(field, row.getCell(fieldInfo.colNumber), fieldOptionCache); + if (fieldValue) { + fieldValues.push({ fieldId, payload: fieldValue }); + } + } + } + } + if (!name && !owner && !boardName && !stageName && fieldValues.length === 0) { + return null; + } + + const { boardId, stageId } = await this.getBoardAndStageIds( + importInfo.entityTypeId, + boardName, + stageName, + boardCache, + stageCache, + ); + + const ownerId = await this.getResponsibleUserId(accountId, owner, user, userCache); + const linkedEntities = mainEntityId ? [mainEntityId] : []; + if (entitiesToLink && importInfo.key) { + linkedEntities.push( + ...entitiesToLink.filter((entity) => entity.key === importInfo.key).map((entity) => entity.entityId), + ); + } + + const [entity] = await this.entityService.createSimple({ + accountId, + user, + dto: { entityTypeId: importInfo.entityTypeId, name, ownerId, boardId, stageId, fieldValues }, + options: { linkedEntities, userNotification: UserNotification.Suppressed }, + }); + + return entity; + } + + private async getEntityTypeCache(accountId: number, entityTypeIds: number[]): Promise { + return await this.entityTypeService.findMany(accountId, { id: entityTypeIds }); + } + + private async getBoardCache(accountId: number, entityTypeIds: number[]): Promise { + const boards: Board[] = []; + for (const entityTypeId of entityTypeIds) { + const etBoards = await this.boardService.findMany({ filter: { accountId, recordId: entityTypeId } }); + etBoards.forEach((b) => { + if (b.sortOrder === 0) b['_first'] = true; + }); + boards.push(...etBoards); + } + return boards; + } + + private async getStageCache(accountId: number, boardIds: number[]): Promise { + const stages: BoardStage[] = []; + for (const boardId of boardIds) { + const boardStages = await this.stageService.findMany({ accountId, boardId }); + boardStages.forEach((s) => { + if (s.sortOrder === 0) s['_first'] = true; + }); + stages.push(...boardStages); + } + return stages; + } + + private async getFieldCache(accountId: number, importInfos: ImportInfo[]): Promise { + const fieldIds = importInfos + .map((ii) => ii.fields) + .flat() + .filter((fi) => typeof fi.fieldId === 'number') + .map((fi) => fi.fieldId as number) + .filter(isUnique); + return this.fieldService.findMany({ accountId, id: fieldIds }); + } + + private async getFieldOptionCache(accountId: number, fieldCache: Field[]): Promise { + const cache: FieldOption[] = []; + for (const field of fieldCache) { + cache.push(...(await this.fieldOptionService.findMany({ accountId, fieldId: field.id }))); + } + return cache; + } + + private async getBoardAndStageIds( + entityTypeId: number, + boardName: string | null, + stageName: string | null, + boardCache: Board[], + stageCache: BoardStage[], + ): Promise<{ boardId: number | null; stageId: number | null }> { + const boards = boardCache.filter((b) => b.recordId === entityTypeId); + if (boards?.length > 0) { + const board = boardName + ? boards.find((b) => b.name.toLowerCase() === boardName.toLowerCase()) + : (boards.find((b) => b['_first']) ?? boards[0]); + if (board) { + const stages = stageCache.filter((s) => s.boardId === board.id); + const stage = stageName + ? stages.find((s) => s.name.toLowerCase() === stageName.toLowerCase()) + : (stages.find((s) => s['_first']) ?? null); + if (stage) return { boardId: board.id, stageId: stage.id }; + } + return { boardId: board.id, stageId: null }; + } + return { boardId: null, stageId: null }; + } + + private async getResponsibleUserId( + accountId: number, + owner: string | null, + user: User, + userCache: User[], + ): Promise { + if (!owner) { + return user.id; + } + + const cachedUser = userCache.find((u) => u.fullName.toLowerCase() === owner.toLowerCase()); + if (cachedUser) { + return cachedUser.id; + } + + const dbUser = await this.userService.findOne({ accountId, fullName: owner }); + if (dbUser) { + userCache.push(dbUser); + + return dbUser.id; + } + + return user.id; + } + + private async getFieldValue(field: Field, cell: Cell, fieldOptionCache: FieldOption[]): Promise { + const cellValue = cell.value; + if (!cellValue) return null; + const valueAsString = this.getCellValueAsString(cellValue); + switch (field.type) { + case FieldType.Text: + case FieldType.RichText: + case FieldType.Link: + return { value: valueAsString }; + case FieldType.Number: + case FieldType.Value: + case FieldType.Formula: + return { value: Number(cellValue) }; + case FieldType.MultiText: + case FieldType.Phone: + case FieldType.Email: + return { values: valueAsString.split(/[,;]+/).map((v) => v.trim()) }; + case FieldType.Switch: + return { value: Boolean(cellValue) }; + case FieldType.Date: { + const date = cellValue ? new Date(cellValue as string | number | Date) : null; + return { value: date && DateUtil.isValid(date) ? date.toISOString() : null }; + } + case FieldType.Select: + case FieldType.ColoredSelect: { + const options = fieldOptionCache.filter((fo) => fo.fieldId === field.id); + const option = options.find((o) => o.label === valueAsString); + return { optionId: option ? option.id : null }; + } + case FieldType.MultiSelect: + case FieldType.ColoredMultiSelect: + case FieldType.CheckedMultiSelect: { + const multiOptions = fieldOptionCache.filter((fo) => fo.fieldId === field.id); + const values = valueAsString.split(/[,;]+/).map((v) => v.trim()); + const fieldOptionIds = multiOptions + .filter((o) => values.includes(o.label)) + .map((fo) => fo.id) + .filter(isUnique); + return { optionIds: fieldOptionIds?.length > 0 ? fieldOptionIds : [] }; + } + case FieldType.Participant: + return { value: Number(cellValue) }; + case FieldType.Participants: + //TODO: parse participants + return { userIds: [] }; + case FieldType.File: + return { value: null }; + case FieldType.Checklist: + return valueAsString + .split(/[,;]+/) + .map((v) => v.trim()) + .filter((v) => v.length) + .map((v) => ({ text: v, checked: false })); + } + } + + private getCellValueAsString(cellValue: CellValue): string { + if (!cellValue) return ''; + if (cellValue instanceof Date) { + return DateUtil.formatPreset(cellValue, 'dateAndTime'); + } + if (cellValue instanceof Object) { + const anyValue = cellValue as any; + if (anyValue.richText) { + const value = anyValue as CellRichTextValue; + return value.richText.map((i) => this.getCellValueAsString(i.text)).join(''); + } + return this.getCellValueAsString(anyValue.error || anyValue.text || anyValue.result); + } + return String(cellValue).trim(); + } +} diff --git a/backend/src/CRM/Service/TimeBoard/TaskGroupByTime.ts b/backend/src/CRM/Service/TimeBoard/TaskGroupByTime.ts new file mode 100644 index 0000000..ad2a18b --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TaskGroupByTime.ts @@ -0,0 +1,8 @@ +export enum TaskGroupByTime { + Unallocated = 'unallocated', + Overdue = 'overdue', + Today = 'today', + Tomorrow = 'tomorrow', + Upcoming = 'upcoming', + Resolved = 'resolved', +} diff --git a/backend/src/CRM/Service/TimeBoard/TaskOrActivityCard.ts b/backend/src/CRM/Service/TimeBoard/TaskOrActivityCard.ts new file mode 100644 index 0000000..b848ff8 --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TaskOrActivityCard.ts @@ -0,0 +1,4 @@ +import { ActivityCardDto } from '../../activity-card'; +import { TaskBoardCardDto } from '../../task-board'; + +export type TaskOrActivityCard = TaskBoardCardDto | ActivityCardDto; diff --git a/backend/src/CRM/Service/TimeBoard/TimeBoardCalendarMeta.ts b/backend/src/CRM/Service/TimeBoard/TimeBoardCalendarMeta.ts new file mode 100644 index 0000000..8ac78bb --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TimeBoardCalendarMeta.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TimeBoardCalendarMeta { + @ApiProperty() + total: number; + + constructor({ total }: TimeBoardCalendarMeta) { + this.total = total; + } +} diff --git a/backend/src/CRM/Service/TimeBoard/TimeBoardFilter.ts b/backend/src/CRM/Service/TimeBoard/TimeBoardFilter.ts new file mode 100644 index 0000000..602fd33 --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TimeBoardFilter.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { BaseTaskBoardFilter } from '../../Service/BaseTaskBoard/BaseTaskBoardFilter'; +import { TaskGroupByTime } from './TaskGroupByTime'; + +export class TimeBoardFilter extends BaseTaskBoardFilter { + @ApiPropertyOptional({ enum: TaskGroupByTime }) + @IsOptional() + @IsArray() + groups?: TaskGroupByTime[] | null; +} diff --git a/backend/src/CRM/Service/TimeBoard/TimeBoardMeta.ts b/backend/src/CRM/Service/TimeBoard/TimeBoardMeta.ts new file mode 100644 index 0000000..3fbee1a --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TimeBoardMeta.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { UserTimeAllocation } from '../BaseTaskBoard/UserTimeAllocation'; +import { TimeBoardStageMeta } from './TimeBoardStageMeta'; + +export class TimeBoardMeta { + @ApiProperty() + total: number; + + @ApiProperty({ nullable: true }) + unallocated: TimeBoardStageMeta | null = null; + + @ApiProperty({ nullable: true }) + overdue: TimeBoardStageMeta | null = null; + + @ApiProperty({ nullable: true }) + today: TimeBoardStageMeta | null = null; + + @ApiProperty({ nullable: true }) + tomorrow: TimeBoardStageMeta | null = null; + + @ApiProperty({ nullable: true }) + upcoming: TimeBoardStageMeta | null = null; + + @ApiProperty({ nullable: true }) + resolved: TimeBoardStageMeta | null = null; + + @ApiProperty({ type: [UserTimeAllocation] }) + timeAllocation: UserTimeAllocation[]; +} diff --git a/backend/src/CRM/Service/TimeBoard/TimeBoardService.ts b/backend/src/CRM/Service/TimeBoard/TimeBoardService.ts new file mode 100644 index 0000000..a2da93f --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TimeBoardService.ts @@ -0,0 +1,627 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Brackets, DataSource, SelectQueryBuilder } from 'typeorm'; + +import { DatePeriod, DatePeriodDto, DateUtil, ForbiddenError, intersection, isUnique, PagingQuery } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoDto, EntityInfoService } from '@/modules/entity/entity-info'; + +import { Activity } from '../../activity'; +import { ActivityCardDto, ActivityCardService } from '../../activity-card'; +import { TaskView } from '../../base-task'; +import { BoardService } from '../../board/board.service'; +import { Task } from '../../task'; +import { TaskBoardCardDto, TaskBoardService } from '../../task-board'; + +import { TaskBoardQueryHelper } from '../BaseTaskBoard/TaskBoardQueryHelper'; +import { TaskSorting } from '../BaseTaskBoard/TaskSorting'; +import { UserTimeAllocation } from '../BaseTaskBoard/UserTimeAllocation'; +import { TimeBoardFilter } from './TimeBoardFilter'; +import { TimeBoardMeta } from './TimeBoardMeta'; +import { TaskOrActivityCard } from './TaskOrActivityCard'; +import { TimeBoardStageMeta } from './TimeBoardStageMeta'; +import { TimeBoardCalendarMeta } from './TimeBoardCalendarMeta'; +import { TaskGroupByTime } from './TaskGroupByTime'; + +@Injectable() +export class TimeBoardService { + private readonly logger = new Logger(TimeBoardService.name); + constructor( + private readonly dataSource: DataSource, + private readonly authService: AuthorizationService, + private readonly activityCardService: ActivityCardService, + private readonly taskBoardService: TaskBoardService, + private readonly boardService: BoardService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async getTimeBoardCards( + account: Account, + user: User, + filter: TimeBoardFilter, + paging: PagingQuery, + ): Promise { + const activityPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Activity.getAuthorizable(), + }); + const taskPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Task.getAuthorizable(), + }); + if (!activityPerm.allow && !taskPerm.allow) { + throw new ForbiddenError(); + } + + const allowedBoardIds = await this.boardService.getAllowedTaskBoardIds({ accountId: account.id, userId: user.id }); + + const allowedTypes = []; + if (activityPerm.allow) { + allowedTypes.push(TaskView.Activity); + } + if (taskPerm.allow) { + allowedTypes.push(TaskView.Task); + } + + const activityUsers = intersection(filter.ownerIds, activityPerm.userIds); + const taskUsers = intersection(filter.ownerIds, taskPerm.userIds); + + const now = DateUtil.now(); + const tasks = []; + const qb = this.createQueryBuilder( + account.id, + user.id, + allowedBoardIds, + allowedTypes, + activityUsers, + taskUsers, + filter, + ); + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Unallocated)) { + const unallocatedQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere('at.start_date is null') + .andWhere('at.end_date is null'); + + tasks.push(...(await this.getTasks(unallocatedQb.clone(), filter.sorting, paging))); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Overdue)) { + const expiredQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbE) => { + qbE.where('at.end_date < :now', { now }).orWhere('at.end_date is null AND at.start_date < :now', { now }); + }), + ); + + tasks.push(...(await this.getTasks(expiredQb.clone(), filter.sorting, paging))); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Today)) { + const endOfTheDay = DateUtil.endOf(now, 'day'); + const todayQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbT) => { + qbT + .where('at.start_date <= :endOfTheDay AND at.end_date > :now', { endOfTheDay, now }) + .orWhere('at.end_date is null AND at.start_date >= :now AND at.start_date <= :endOfTheDay', { + endOfTheDay, + now, + }) + .orWhere('at.start_date is null AND at.end_date >= :now AND at.end_date <= :endOfTheDay', { + endOfTheDay, + now, + }); + }), + ); + + tasks.push(...(await this.getTasks(todayQb.clone(), filter.sorting, paging))); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Tomorrow)) { + const tomorrowStart = DateUtil.add(DateUtil.startOf(now, 'day'), { days: 1 }); + const tomorrowEnd = DateUtil.add(DateUtil.endOf(now, 'day'), { days: 1 }); + const tomorrowQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbTm) => { + qbTm + .where('at.start_date >= :tomorrowStart AND at.start_date <= :tomorrowEnd', { + tomorrowStart, + tomorrowEnd, + }) + .orWhere('at.start_date is null AND at.end_date >= :tomorrowStart AND at.end_date <= :tomorrowEnd', { + tomorrowStart, + tomorrowEnd, + }); + }), + ); + + tasks.push(...(await this.getTasks(tomorrowQb.clone(), filter.sorting, paging))); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Upcoming)) { + const tomorrowEnd = DateUtil.add(DateUtil.startOf(now, 'day'), { days: 2 }); + const upcomingQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbU) => { + qbU + .where('at.start_date > :tomorrowEnd', { tomorrowEnd }) + .orWhere('at.start_date is null AND at.end_date > :tomorrowEnd', { tomorrowEnd }); + }), + ); + + tasks.push(...(await this.getTasks(upcomingQb.clone(), filter.sorting, paging))); + } + if ((!filter.groups && filter.showResolved !== false) || filter.groups?.includes(TaskGroupByTime.Resolved)) { + const resolvedQb = qb.clone().andWhere('at.is_resolved = true'); + + tasks.push(...(await this.getTasks(resolvedQb.clone(), filter.sorting, paging))); + } + + return this.createItemsFromRaw(account, user, tasks); + } + + public async getTimeBoardMeta(accountId: number, user: User, filter: TimeBoardFilter): Promise { + const activityPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Activity.getAuthorizable(), + }); + const taskPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Task.getAuthorizable(), + }); + if (!activityPerm.allow && !taskPerm.allow) { + throw new ForbiddenError(); + } + + const allowedBoardIds = await this.boardService.getAllowedTaskBoardIds({ accountId, userId: user.id }); + + const allowedTypes = []; + if (activityPerm.allow) { + allowedTypes.push(TaskView.Activity); + } + if (taskPerm.allow) { + allowedTypes.push(TaskView.Task); + } + + const activityUsers = intersection(filter.ownerIds, activityPerm.userIds); + const taskUsers = intersection(filter.ownerIds, taskPerm.userIds); + + const now = DateUtil.now(); + const meta = new TimeBoardMeta(); + const qb = this.createQueryBuilder( + accountId, + user.id, + allowedBoardIds, + allowedTypes, + activityUsers, + taskUsers, + filter, + ); + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Unallocated)) { + const unallocatedQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere('at.start_date is null') + .andWhere('at.end_date is null'); + + meta.unallocated = await this.getStageMeta(unallocatedQb); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Overdue)) { + const overdueQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbE) => { + qbE.where('at.end_date < :now', { now }).orWhere('at.end_date is null AND at.start_date < :now', { now }); + }), + ); + + meta.overdue = await this.getStageMeta(overdueQb); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Today)) { + const endOfTheDay = DateUtil.endOf(now, 'day'); + const todayQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbT) => { + qbT + .where('at.start_date <= :endOfTheDay AND at.end_date > :now', { endOfTheDay, now }) + .orWhere('at.end_date is null AND at.start_date >= :now AND at.start_date <= :endOfTheDay', { + endOfTheDay, + now, + }) + .orWhere('at.start_date is null AND at.end_date >= :now AND at.end_date <= :endOfTheDay', { + endOfTheDay, + now, + }); + }), + ); + + meta.today = await this.getStageMeta(todayQb); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Tomorrow)) { + const tomorrowStart = DateUtil.add(DateUtil.startOf(now, 'day'), { days: 1 }); + const tomorrowEnd = DateUtil.add(DateUtil.endOf(now, 'day'), { days: 1 }); + const tomorrowQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbTm) => { + qbTm + .where('at.start_date >= :tomorrowStart AND at.start_date <= :tomorrowEnd', { + tomorrowStart, + tomorrowEnd, + }) + .orWhere('at.start_date is null AND at.end_date >= :tomorrowStart AND at.end_date <= :tomorrowEnd', { + tomorrowStart, + tomorrowEnd, + }); + }), + ); + + meta.tomorrow = await this.getStageMeta(tomorrowQb); + } + if (!filter.groups || filter.groups.includes(TaskGroupByTime.Upcoming)) { + const tomorrowEnd = DateUtil.add(DateUtil.startOf(now, 'day'), { days: 2 }); + const upcomingQb = qb + .clone() + .andWhere('at.is_resolved = false') + .andWhere( + new Brackets((qbU) => { + qbU + .where('at.start_date > :tomorrowEnd', { tomorrowEnd }) + .orWhere('at.start_date is null AND at.end_date > :tomorrowEnd', { tomorrowEnd }); + }), + ); + + meta.upcoming = await this.getStageMeta(upcomingQb); + } + if ((!filter.groups && filter.showResolved !== false) || filter.groups?.includes(TaskGroupByTime.Resolved)) { + const resolvedQb = qb.clone().andWhere('at.is_resolved = true'); + + meta.resolved = await this.getStageMeta(resolvedQb); + } + + const boardQb = qb.clone(); + if (filter.showResolved === false) { + boardQb.andWhere(`at.is_resolved = false`); + } + meta.total = await this.getCount(boardQb.clone()); + meta.timeAllocation = await this.getUserTimeAllocation(boardQb.clone()); + + return meta; + } + + public async getTimeBoardItem( + account: Account, + user: User, + type: TaskView, + id: number, + filter: TimeBoardFilter, + ): Promise { + switch (type) { + case TaskView.Activity: + return await this.activityCardService.getActivityCard(account.id, user, id, filter); + case TaskView.Task: + return await this.taskBoardService.getTaskBoardCard(account, user, id, filter); + } + } + + public async getCalendar( + account: Account, + user: User, + periodDto: DatePeriodDto, + filter: TimeBoardFilter, + ): Promise { + const activityPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Activity.getAuthorizable(), + }); + const taskPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Task.getAuthorizable(), + }); + if (!activityPerm.allow && !taskPerm.allow) { + throw new ForbiddenError(); + } + + const allowedBoardIds = await this.boardService.getAllowedTaskBoardIds({ accountId: account.id, userId: user.id }); + + const allowedTypes = []; + if (activityPerm.allow) { + allowedTypes.push(TaskView.Activity); + } + if (taskPerm.allow) { + allowedTypes.push(TaskView.Task); + } + + const activityUsers = intersection(filter.ownerIds, activityPerm.userIds); + const taskUsers = intersection(filter.ownerIds, taskPerm.userIds); + + const qb = this.createQueryBuilder( + account.id, + user.id, + allowedBoardIds, + allowedTypes, + activityUsers, + taskUsers, + filter, + ); + if (filter.showResolved === false) { + qb.andWhere('at.is_resolved = :isResolved', { isResolved: false }); + } + + const period = DatePeriod.fromDto(periodDto); + if (period.from && period.to) { + qb.andWhere('start_date < :to', { to: period.to }).andWhere('end_date > :from', { from: period.from }); + } + + const tasks = await qb.getRawMany(); + + return this.createItemsFromRaw(account, user, tasks); + } + + public async getCalendarMeta( + account: Account, + user: User, + periodDto: DatePeriodDto, + filter: TimeBoardFilter, + ): Promise { + const activityPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Activity.getAuthorizable(), + }); + const taskPerm = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Task.getAuthorizable(), + }); + if (!activityPerm.allow && !taskPerm.allow) { + throw new ForbiddenError(); + } + + const allowedBoardIds = await this.boardService.getAllowedTaskBoardIds({ accountId: account.id, userId: user.id }); + + const allowedTypes = []; + if (activityPerm.allow) { + allowedTypes.push(TaskView.Activity); + } + if (taskPerm.allow) { + allowedTypes.push(TaskView.Task); + } + + const activityUsers = intersection(filter.ownerIds, activityPerm.userIds); + const taskUsers = intersection(filter.ownerIds, taskPerm.userIds); + + const qb = this.createQueryBuilder( + account.id, + user.id, + allowedBoardIds, + allowedTypes, + activityUsers, + taskUsers, + filter, + ); + + qb.select('count(*)', 'total'); + + if (filter.showResolved === false) { + qb.andWhere('at.is_resolved = :isResolved', { isResolved: false }); + } + + const period = DatePeriod.fromDto(periodDto); + if (period.from && period.to) { + qb.andWhere('start_date < :to', { to: period.to }).andWhere('end_date > :from', { from: period.from }); + } + const { total } = await qb.getRawOne<{ total: number }>(); + + return new TimeBoardCalendarMeta({ total }); + } + + private createQueryBuilder( + accountId: number, + userId: number, + _allowedBoardIds: number[], + allowedTypes: string[], + activityUsers: number[] | null | undefined, + taskUsers: number[] | null | undefined, + filter: TimeBoardFilter, + ) { + const qb = this.dataSource + .createQueryBuilder() + .select('at.*') + .from('all_tasks', 'at') + .where('at.account_id = :accountId', { accountId }) + .andWhere('at.type IN (:...allowedTypes)', { allowedTypes }); + //HACK: skip board check + //.andWhere('(at.board_id is null or at.board_id IN (:...allowedBoardIds))', { allowedBoardIds }); + + if (activityUsers || taskUsers) { + qb.andWhere( + new Brackets((qbP) => { + qbP + .where( + new Brackets((qbA) => { + qbA.where(`at.type = 'activity'`); + if (activityUsers && activityUsers.length > 0) { + qbA.andWhere(`at.responsible_user_id IN (:...activityUsers)`, { activityUsers }); + } else if (activityUsers) { + qbA.andWhere(`at.responsible_user_id IS NULL`); + } + }), + ) + .orWhere( + new Brackets((qbT) => { + qbT.where(`at.type = 'task'`); + if (taskUsers && taskUsers.length > 0) { + qbT.andWhere(`at.responsible_user_id IN (:...taskUsers)`, { taskUsers }); + } else if (taskUsers) { + qbT.andWhere(`at.responsible_user_id IS NULL`); + } + }), + ); + if (!filter.ownerIds) { + qbP.orWhere('at.created_by = :userId', { userId }); + } + }), + ); + } + + if (filter.entityIds) { + qb.andWhere(`at.entity_id IN (:...entityIds)`, { entityIds: filter.entityIds }); + } + + if (filter.search) { + qb.andWhere( + new Brackets((qbS) => { + qbS + .where(`at.text ILIKE :searchText`, { searchText: `%${filter.search}%` }) + .orWhere(`at.title ILIKE :searchTitle`, { searchTitle: `%${filter.search}%` }); + }), + ); + } + + return qb; + } + + private async getTasks( + qb: SelectQueryBuilder, + sorting: TaskSorting | null | undefined, + paging: PagingQuery, + ): Promise { + try { + return await TaskBoardQueryHelper.addBoardOrderBy(qb, sorting) + .offset(paging.skip) + .limit(paging.take) + .getRawMany(); + } catch (e) { + this.logger.error(`getTasks query: ${qb.getQuery()}`); + this.logger.error(`getTasks error`, (e as Error)?.stack); + throw e; + } + } + + private async getStageMeta(qb: SelectQueryBuilder): Promise { + const unallocatedCount = await this.getCount(qb.clone()); + const allocations = await this.getUserTimeAllocation(qb.clone()); + + return new TimeBoardStageMeta(unallocatedCount, allocations); + } + + private async getCount(qb: SelectQueryBuilder): Promise { + const { count } = await qb.select('COUNT(at.id)', 'count').getRawOne(); + return Number(count); + } + + private async getUserTimeAllocation(qb: SelectQueryBuilder): Promise { + const allocations = await qb + .groupBy('at.responsible_user_id') + .select('at.responsible_user_id, SUM(at.planned_time) AS planned_time') + .getRawMany(); + + return allocations.map( + (a) => new UserTimeAllocation(a.responsible_user_id, a.planned_time ? Number(a.planned_time) : 0), + ); + } + + private async createItemsFromRaw(account: Account, user: User, items: any[]): Promise { + const entityIds = items + .map((item) => item.entity_id) + .filter((id) => !!id) + .filter(isUnique); + const entityInfoCache = entityIds.length + ? await this.entityInfoService.findMany({ accountId: account.id, user, entityIds }) + : []; + + const cards: TaskOrActivityCard[] = []; + for (const item of items) { + const entityInfo = item.entity_id ? entityInfoCache.find((ei) => ei.id === item.entity_id) : null; + cards.push(await this.createItemFromRaw(account, user, item, entityInfo)); + } + return cards; + } + + private async createItemFromRaw( + account: Account, + user: User, + item: any, + entityInfo: EntityInfoDto | null, + ): Promise { + if (item.type === TaskView.Activity) { + return await this.createActivityCardItem(user, item, entityInfo); + } else if (item.type === TaskView.Task) { + return await this.createTaskCardItem(account, user, item, entityInfo); + } else { + return null; + } + } + + private async createActivityCardItem( + user: User, + item: any, + entityInfo: EntityInfoDto | null, + ): Promise { + const activity = new Activity( + item.account_id, + item.entity_id, + item.created_by, + item.responsible_user_id, + item.text, + item.activity_type_id, + item.is_resolved, + item.start_date ? new Date(item.start_date) : null, + item.end_date ? new Date(item.end_date) : null, + item.resolved_date ? new Date(item.resolved_date) : null, + item.weight, + item.result, + new Date(item.created_at), + ); + activity.id = item.id; + return await this.activityCardService.createActivityCard(user, activity, entityInfo); + } + + private async createTaskCardItem( + account: Account, + user: User, + item: any, + entityInfo: EntityInfoDto | null, + ): Promise { + const task = new Task( + item.account_id, + item.entity_id, + item.created_by, + item.responsible_user_id, + item.text, + item.title, + item.planned_time, + item.is_resolved, + item.start_date ? new Date(item.start_date) : null, + item.end_date ? new Date(item.end_date) : null, + item.resolved_date ? new Date(item.resolved_date) : null, + item.weight, + item.board_id, + item.stage_id, + item.settings_id, + item.external_id, + new Date(item.created_at), + ); + task.id = item.id; + return await this.taskBoardService.createTaskBoardCard({ account, user, task, entityInfo }); + } +} diff --git a/backend/src/CRM/Service/TimeBoard/TimeBoardStageMeta.ts b/backend/src/CRM/Service/TimeBoard/TimeBoardStageMeta.ts new file mode 100644 index 0000000..3be3384 --- /dev/null +++ b/backend/src/CRM/Service/TimeBoard/TimeBoardStageMeta.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { UserTimeAllocation } from '../BaseTaskBoard/UserTimeAllocation'; + +export class TimeBoardStageMeta { + @ApiProperty() + total: number; + + @ApiProperty() + timeAllocation: UserTimeAllocation[]; + + constructor(total: number, timeAllocation: UserTimeAllocation[]) { + this.total = total; + this.timeAllocation = timeAllocation; + } +} diff --git a/backend/src/CRM/activity-card/activity-card.controller.ts b/backend/src/CRM/activity-card/activity-card.controller.ts new file mode 100644 index 0000000..bd208d9 --- /dev/null +++ b/backend/src/CRM/activity-card/activity-card.controller.ts @@ -0,0 +1,66 @@ +import { Body, Controller, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { DatePeriodDto, PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ActivityCalendarMetaDto, ActivityCardDto, ActivityCardFilterDto, ActivityCardMetaDto } from './dto'; +import { ActivityCardService } from './activity-card.service'; + +@ApiTags('crm/activity/card') +@Controller('/crm/activities') +@JwtAuthorized({ prefetch: { user: true } }) +export class ActivityCardController { + constructor(private readonly service: ActivityCardService) {} + + @ApiOkResponse({ description: 'Activities for board', type: [ActivityCardDto] }) + @Post('cards') + public async getActivityCards( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ActivityCardFilterDto, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getActivityCards(accountId, user, filter, paging); + } + + @ApiOkResponse({ description: 'Meta for activities board', type: ActivityCardMetaDto }) + @Post('cards/meta') + public async getActivityCardsMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ActivityCardFilterDto, + ): Promise { + return this.service.getActivityCardsMeta(accountId, user, filter); + } + + @ApiOkResponse({ description: 'Activity for board', type: ActivityCardDto }) + @Post('cards/:activityId') + public async getActivityCard( + @CurrentAuth() { accountId, user }: AuthData, + @Param('activityId', ParseIntPipe) activityId: number, + @Body() filter: ActivityCardFilterDto, + ): Promise { + return this.service.getActivityCard(accountId, user, activityId, filter); + } + + @ApiOkResponse({ description: 'Activities calendar', type: [ActivityCardDto] }) + @Post('calendar') + public async getActivityCalendar( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ActivityCardFilterDto, + @Query() period: DatePeriodDto, + ): Promise { + return this.service.getActivityCalendar(accountId, user, period, filter); + } + + @ApiOkResponse({ description: 'Meta for activities calendar', type: ActivityCalendarMetaDto }) + @Post('calendar/meta') + public async getActivityCalendarMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ActivityCardFilterDto, + @Query() period: DatePeriodDto, + ): Promise { + return this.service.getActivityCalendarMeta(accountId, user, period, filter); + } +} diff --git a/backend/src/CRM/activity-card/activity-card.service.ts b/backend/src/CRM/activity-card/activity-card.service.ts new file mode 100644 index 0000000..bd60584 --- /dev/null +++ b/backend/src/CRM/activity-card/activity-card.service.ts @@ -0,0 +1,244 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DatePeriod, DatePeriodDto, isUnique, PagingQuery } from '@/common'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoDto, EntityInfoService } from '@/modules/entity/entity-info'; + +import { Activity } from '../activity'; +import { ActivityTypeService } from '../activity-type'; +import { TaskBoardQueryHelper } from '../Service/BaseTaskBoard/TaskBoardQueryHelper'; + +import { + ActivityCalendarMetaDto, + ActivityCardDto, + ActivityCardFilterDto, + ActivityCardMetaDto, + ActivityCardTypeMetaDto, +} from './dto'; + +@Injectable() +export class ActivityCardService { + constructor( + @InjectRepository(Activity) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly activityTypeService: ActivityTypeService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async getActivityCards( + accountId: number, + user: User, + filter: ActivityCardFilterDto, + paging: PagingQuery, + ): Promise { + const userIds = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + const typeIds = filter.typeIds ?? (await this.activityTypeService.findManyIds({ accountId })); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + userIds, + true, + 'text', + ); + const activities: Activity[] = []; + for (const typeId of typeIds) { + const typedActivities = await qb + .clone() + .andWhere('activity_type_id = :typeId', { typeId }) + .andWhere('is_resolved = :isResolved', { isResolved: false }) + .offset(paging.skip) + .limit(paging.take) + .getMany(); + activities.push(...typedActivities); + } + if (filter.showResolved !== false) { + const resolvedActivities = await qb + .clone() + .andWhere('is_resolved = :isResolved', { isResolved: true }) + .offset(paging.skip) + .limit(paging.take) + .getMany(); + activities.push(...resolvedActivities); + } + const entityIds = activities.map((activity) => activity.entityId).filter(isUnique); + const entityInfoCache = entityIds.length + ? await this.entityInfoService.findMany({ accountId, user, entityIds }) + : []; + + const activityCards: ActivityCardDto[] = []; + for (const activity of activities) { + const entityInfo = entityInfoCache.find((ei) => ei.id === activity.entityId); + activityCards.push(await this.createActivityCard(user, activity, entityInfo)); + } + return activityCards; + } + + public async getActivityCard( + accountId: number, + user: User, + activityId: number, + filter: ActivityCardFilterDto, + ): Promise { + const userIds = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + userIds, + false, + 'text', + ); + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const activity = await qb.andWhere('id = :id', { id: activityId }).getOne(); + + if (!activity) return null; + + const entityInfo = activity.entityId + ? await this.entityInfoService.findOne({ accountId, user, entityId: activity.entityId }) + : null; + + return await this.createActivityCard(user, activity, entityInfo); + } + + public async getActivityCardsMeta( + accountId: number, + user: User, + filter: ActivityCardFilterDto, + ): Promise { + const userIds = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + const typeIds = filter.typeIds ?? (await this.activityTypeService.findManyIds({ accountId })); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + userIds, + false, + 'text', + ); + let totalCount = 0; + const typeMeta: ActivityCardTypeMetaDto[] = []; + for (const typeId of typeIds) { + const count = await qb + .clone() + .andWhere('activity_type_id = :typeId', { typeId }) + .andWhere('is_resolved = :isResolved', { isResolved: false }) + .getCount(); + typeMeta.push(new ActivityCardTypeMetaDto(typeId, count)); + totalCount += count; + } + let resolvedCount = 0; + if (filter.showResolved !== false) { + resolvedCount = await qb.clone().andWhere('is_resolved = :isResolved', { isResolved: true }).getCount(); + totalCount += resolvedCount; + } + + return new ActivityCardMetaDto(totalCount, typeMeta, resolvedCount); + } + + public async createActivityCard( + user: User, + activity: Activity, + entityInfo: EntityInfoDto | null, + ): Promise { + const userRights = await this.authService.getUserRights({ user, authorizable: activity }); + + return new ActivityCardDto(activity, entityInfo, [], userRights); + } + + public async getActivityCalendar( + accountId: number, + user: User, + periodDto: DatePeriodDto, + filter: ActivityCardFilterDto, + ): Promise { + const userIds = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + userIds, + true, + 'text', + ); + + const typeIds = filter.typeIds ?? (await this.activityTypeService.findManyIds({ accountId })); + if (typeIds) { + qb.andWhere('activity_type_id IN (:...typeIds)', { typeIds }); + } + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const period = DatePeriod.fromDto(periodDto); + if (period.from && period.to) { + qb.andWhere('start_date < :to', { to: period.to }).andWhere('end_date > :from', { from: period.from }); + } + + const activities = await qb.getMany(); + + const entityIds = activities.map((activity) => activity.entityId).filter(isUnique); + const entityInfoCache = entityIds.length + ? await this.entityInfoService.findMany({ accountId, user, entityIds }) + : []; + + const activityCards: ActivityCardDto[] = []; + for (const activity of activities) { + const entityInfo = entityInfoCache.find((ei) => ei.id === activity.entityId); + activityCards.push(await this.createActivityCard(user, activity, entityInfo)); + } + return activityCards; + } + + public async getActivityCalendarMeta( + accountId: number, + user: User, + periodDto: DatePeriodDto, + filter: ActivityCardFilterDto, + ): Promise { + const userIds = await this.authService.whoCanView({ user, authorizable: Activity.getAuthorizable() }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + userIds, + true, + 'text', + ); + + const typeIds = filter.typeIds ?? (await this.activityTypeService.findManyIds({ accountId })); + if (typeIds) { + qb.andWhere('activity_type_id IN (:...typeIds)', { typeIds }); + } + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const period = DatePeriod.fromDto(periodDto); + if (period.from && period.to) { + qb.andWhere('start_date < :to', { to: period.to }).andWhere('end_date > :from', { from: period.from }); + } + const total = await qb.getCount(); + + return new ActivityCalendarMetaDto({ total }); + } +} diff --git a/backend/src/CRM/activity-card/dto/activity-calendar-meta.dto.ts b/backend/src/CRM/activity-card/dto/activity-calendar-meta.dto.ts new file mode 100644 index 0000000..599feb8 --- /dev/null +++ b/backend/src/CRM/activity-card/dto/activity-calendar-meta.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class ActivityCalendarMetaDto { + @ApiProperty() + @IsNumber() + total: number; + + constructor({ total }: ActivityCalendarMetaDto) { + this.total = total; + } +} diff --git a/backend/src/CRM/activity-card/dto/activity-card-filter.dto.ts b/backend/src/CRM/activity-card/dto/activity-card-filter.dto.ts new file mode 100644 index 0000000..94a6e94 --- /dev/null +++ b/backend/src/CRM/activity-card/dto/activity-card-filter.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { BaseTaskBoardFilter } from '../../Service/BaseTaskBoard/BaseTaskBoardFilter'; + +export class ActivityCardFilterDto extends BaseTaskBoardFilter { + @ApiPropertyOptional({ type: [Number] }) + @IsOptional() + @IsArray() + typeIds?: number[] | null; +} diff --git a/backend/src/CRM/activity-card/dto/activity-card-meta.dto.ts b/backend/src/CRM/activity-card/dto/activity-card-meta.dto.ts new file mode 100644 index 0000000..fa8d76a --- /dev/null +++ b/backend/src/CRM/activity-card/dto/activity-card-meta.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ActivityCardTypeMetaDto } from './activity-card-type-meta.dto'; + +export class ActivityCardMetaDto { + @ApiProperty() + total: number; + + @ApiProperty({ type: [ActivityCardTypeMetaDto] }) + types: ActivityCardTypeMetaDto[]; + + @ApiProperty() + resolvedTotal: number; + + constructor(total: number, types: ActivityCardTypeMetaDto[], resolvedTotal: number) { + this.total = total; + this.types = types; + this.resolvedTotal = resolvedTotal; + } +} diff --git a/backend/src/CRM/activity-card/dto/activity-card-type-meta.dto.ts b/backend/src/CRM/activity-card/dto/activity-card-type-meta.dto.ts new file mode 100644 index 0000000..d6ae47d --- /dev/null +++ b/backend/src/CRM/activity-card/dto/activity-card-type-meta.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ActivityCardTypeMetaDto { + @ApiProperty() + id: number; + + @ApiProperty() + totalCount: number; + + constructor(id: number, totalCount: number) { + this.id = id; + this.totalCount = totalCount; + } +} diff --git a/backend/src/CRM/activity-card/dto/activity-card.dto.ts b/backend/src/CRM/activity-card/dto/activity-card.dto.ts new file mode 100644 index 0000000..6839fc7 --- /dev/null +++ b/backend/src/CRM/activity-card/dto/activity-card.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { BaseTaskDto } from '../../base-task'; +import { Activity } from '../../activity'; +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +export class ActivityCardDto extends BaseTaskDto { + @ApiProperty() + activityTypeId: number; + + constructor(activity: Activity, entityInfo: EntityInfoDto | null, fileLinks: FileLinkDto[], userRights: UserRights) { + super(activity, entityInfo, fileLinks, userRights); + + this.activityTypeId = activity.activityTypeId; + } +} diff --git a/backend/src/CRM/activity-card/dto/index.ts b/backend/src/CRM/activity-card/dto/index.ts new file mode 100644 index 0000000..ad0e855 --- /dev/null +++ b/backend/src/CRM/activity-card/dto/index.ts @@ -0,0 +1,5 @@ +export * from './activity-calendar-meta.dto'; +export * from './activity-card-filter.dto'; +export * from './activity-card-meta.dto'; +export * from './activity-card-type-meta.dto'; +export * from './activity-card.dto'; diff --git a/backend/src/CRM/activity-card/index.ts b/backend/src/CRM/activity-card/index.ts new file mode 100644 index 0000000..6419861 --- /dev/null +++ b/backend/src/CRM/activity-card/index.ts @@ -0,0 +1,3 @@ +export * from './activity-card.controller'; +export * from './activity-card.service'; +export * from './dto'; diff --git a/backend/src/CRM/activity-type/activity-type.controller.ts b/backend/src/CRM/activity-type/activity-type.controller.ts new file mode 100644 index 0000000..59405ec --- /dev/null +++ b/backend/src/CRM/activity-type/activity-type.controller.ts @@ -0,0 +1,54 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ActivityTypeDto, CreateActivityTypeDto, UpdateActivityTypeDto } from './dto'; +import { ActivityTypeService } from './activity-type.service'; + +@ApiTags('crm/activity-type') +@Controller('crm/activity-types') +@JwtAuthorized() +@TransformToDto() +export class ActivityTypeController { + constructor(private readonly service: ActivityTypeService) {} + + @ApiOperation({ summary: 'Create activity type', description: 'Create activity type' }) + @ApiBody({ type: CreateActivityTypeDto, required: true, description: 'Data to create activity type' }) + @ApiCreatedResponse({ description: 'Activity type', type: ActivityTypeDto }) + @Post() + async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateActivityTypeDto) { + return this.service.create({ accountId, dto }); + } + + @ApiOperation({ summary: 'Get all activity types', description: 'Get all activity types for account' }) + @ApiOkResponse({ description: 'Activity types', type: [ActivityTypeDto] }) + @Get() + async findMany(@CurrentAuth() { accountId }: AuthData) { + return this.service.findMany({ accountId }); + } + + @ApiOperation({ summary: 'Update activity type', description: 'Update activity type' }) + @ApiParam({ name: 'activityTypeId', type: Number, required: true, description: 'Activity type ID' }) + @ApiBody({ type: UpdateActivityTypeDto, required: true, description: 'Data to update activity type' }) + @ApiOkResponse({ description: 'Activity type', type: ActivityTypeDto }) + @Patch(':activityTypeId') + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('activityTypeId', ParseIntPipe) activityTypeId: number, + @Body() dto: UpdateActivityTypeDto, + ) { + return this.service.update({ accountId, activityTypeId, dto }); + } + + @ApiOperation({ summary: 'Delete activity type', description: 'Delete activity type' }) + @ApiParam({ name: 'activityTypeId', type: Number, required: true, description: 'Activity type ID' }) + @ApiOkResponse() + @Delete(':activityTypeId') + async delete(@CurrentAuth() { accountId }: AuthData, @Param('activityTypeId', ParseIntPipe) activityTypeId: number) { + await this.service.setActive({ accountId, activityTypeId, isActive: false }); + } +} diff --git a/backend/src/CRM/activity-type/activity-type.service.ts b/backend/src/CRM/activity-type/activity-type.service.ts new file mode 100644 index 0000000..c47e40e --- /dev/null +++ b/backend/src/CRM/activity-type/activity-type.service.ts @@ -0,0 +1,102 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { CreateActivityTypeDto, UpdateActivityTypeDto } from './dto'; +import { ActivityType } from './entities'; +import { NotFoundError } from '@/common'; + +interface FindFilter { + accountId: number; + id?: number | number[]; + name?: string; +} + +@Injectable() +export class ActivityTypeService { + constructor( + @InjectRepository(ActivityType) + private readonly repository: Repository, + ) {} + + async create({ accountId, dto }: { accountId: number; dto: CreateActivityTypeDto }): Promise { + let activityType = await this.findOne({ accountId, name: dto.name }); + if (activityType && activityType.isActive) { + return activityType; + } + if (activityType && !activityType.isActive) { + activityType.isActive = true; + } else { + activityType = new ActivityType(accountId, dto.name); + } + return this.repository.save(activityType); + } + + async findOne(filter: FindFilter): Promise { + return await this.createFindQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + return await this.createFindQb(filter).orderBy('at.created_at', 'ASC').getMany(); + } + + async findManyIds(filter: FindFilter): Promise { + const result = await this.createFindQb(filter).select('at.id', 'id').getRawMany(); + + return result.map((r) => r.id); + } + + async update({ + accountId, + activityTypeId, + dto, + }: { + accountId: number; + activityTypeId: number; + dto: UpdateActivityTypeDto; + }): Promise { + const at = await this.findOne({ accountId, id: activityTypeId }); + if (!at) { + throw NotFoundError.withId(ActivityType, activityTypeId); + } + + if (dto.name) { + at.name = dto.name; + } + + await this.repository.save(at); + + return at; + } + + async setActive({ + accountId, + activityTypeId, + isActive, + }: { + accountId: number; + activityTypeId: number; + isActive: boolean; + }): Promise { + await this.repository.update({ accountId, id: activityTypeId }, { isActive }); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('at') + .where('at.account_id = :accountId', { accountId: filter.accountId }); + + if (filter?.id) { + if (Array.isArray(filter.id)) { + qb.andWhere('at.id IN (:...ids)', { ids: filter.id }); + } else { + qb.andWhere('at.id = :id', { id: filter.id }); + } + } + + if (filter?.name) { + qb.andWhere('at.name = :name', { name: filter.name }); + } + + return qb; + } +} diff --git a/backend/src/CRM/activity-type/dto/activity-type.dto.ts b/backend/src/CRM/activity-type/dto/activity-type.dto.ts new file mode 100644 index 0000000..9a96468 --- /dev/null +++ b/backend/src/CRM/activity-type/dto/activity-type.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsString } from 'class-validator'; + +export class ActivityTypeDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsBoolean() + isActive: boolean; + + @ApiProperty() + @IsString() + createdAt: string; +} diff --git a/backend/src/CRM/activity-type/dto/create-activity-type.dto.ts b/backend/src/CRM/activity-type/dto/create-activity-type.dto.ts new file mode 100644 index 0000000..bd4c772 --- /dev/null +++ b/backend/src/CRM/activity-type/dto/create-activity-type.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { ActivityTypeDto } from './activity-type.dto'; + +export class CreateActivityTypeDto extends PickType(ActivityTypeDto, ['name'] as const) {} diff --git a/backend/src/CRM/activity-type/dto/index.ts b/backend/src/CRM/activity-type/dto/index.ts new file mode 100644 index 0000000..4c2f91c --- /dev/null +++ b/backend/src/CRM/activity-type/dto/index.ts @@ -0,0 +1,3 @@ +export * from './activity-type.dto'; +export * from './create-activity-type.dto'; +export * from './update-activity-type.dto'; diff --git a/backend/src/CRM/activity-type/dto/update-activity-type.dto.ts b/backend/src/CRM/activity-type/dto/update-activity-type.dto.ts new file mode 100644 index 0000000..2998272 --- /dev/null +++ b/backend/src/CRM/activity-type/dto/update-activity-type.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { ActivityTypeDto } from './activity-type.dto'; + +export class UpdateActivityTypeDto extends PickType(ActivityTypeDto, ['name'] as const) {} diff --git a/backend/src/CRM/activity-type/entities/activity-type.entity.ts b/backend/src/CRM/activity-type/entities/activity-type.entity.ts new file mode 100644 index 0000000..7d3b445 --- /dev/null +++ b/backend/src/CRM/activity-type/entities/activity-type.entity.ts @@ -0,0 +1,39 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ActivityTypeDto } from '../dto/activity-type.dto'; + +@Entity('activity_type') +export class ActivityType { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + accountId: number; + + @Column() + isActive: boolean; + + @Column() + createdAt: Date; + + constructor(accountId: number, name: string, isActive?: boolean, createdAt?: Date) { + this.accountId = accountId; + this.name = name; + this.isActive = isActive ?? true; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public toDto(): ActivityTypeDto { + return { + id: this.id, + name: this.name, + isActive: this.isActive, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/backend/src/CRM/activity-type/entities/index.ts b/backend/src/CRM/activity-type/entities/index.ts new file mode 100644 index 0000000..16cc30b --- /dev/null +++ b/backend/src/CRM/activity-type/entities/index.ts @@ -0,0 +1 @@ +export * from './activity-type.entity'; diff --git a/backend/src/CRM/activity-type/index.ts b/backend/src/CRM/activity-type/index.ts new file mode 100644 index 0000000..201789b --- /dev/null +++ b/backend/src/CRM/activity-type/index.ts @@ -0,0 +1,4 @@ +export * from './activity-type.controller'; +export * from './activity-type.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/CRM/activity/activity.controller.ts b/backend/src/CRM/activity/activity.controller.ts new file mode 100644 index 0000000..9f3f911 --- /dev/null +++ b/backend/src/CRM/activity/activity.controller.ts @@ -0,0 +1,38 @@ +import { Body, Controller, Delete, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ActivityDto, CreateActivityDto, UpdateActivityDto } from './dto'; +import { ActivityService } from './activity.service'; + +@ApiTags('crm/activity') +@Controller('crm/activities') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class ActivityController { + constructor(private readonly service: ActivityService) {} + + @ApiCreatedResponse({ description: 'Create activity', type: ActivityDto }) + @Post() + async create(@CurrentAuth() { account, user }: AuthData, @Body() dto: CreateActivityDto): Promise { + return this.service.createAndGetDto(account, user, dto); + } + + @ApiCreatedResponse({ description: 'Update activity', type: ActivityDto }) + @Patch(':activityId') + async update( + @CurrentAuth() { account, user }: AuthData, + @Param('activityId', ParseIntPipe) activityId: number, + @Body() dto: UpdateActivityDto, + ): Promise { + return this.service.update(account, user, activityId, dto); + } + + @ApiOkResponse({ description: 'Delete activity' }) + @Delete(':activityId') + async delete(@CurrentAuth() { accountId, user }: AuthData, @Param('activityId', ParseIntPipe) activityId: number) { + await this.service.delete(accountId, user, activityId); + } +} diff --git a/backend/src/CRM/activity/activity.handler.ts b/backend/src/CRM/activity/activity.handler.ts new file mode 100644 index 0000000..0202961 --- /dev/null +++ b/backend/src/CRM/activity/activity.handler.ts @@ -0,0 +1,71 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; +import { + ActionActivityCreateSettings, + AutomationJob, + EntityTypeActionType, + OnAutomationJob, +} from '@/modules/automation'; + +import { ActivityService } from './activity.service'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; + responsibleUserId?: number | null; +} + +interface CreateActivityVariables { + accountId: number; + entity?: EntityVariables | null; + activitySettings?: ActionActivityCreateSettings | null; +} + +@Injectable() +export class ActivityHandler { + private readonly logger = new Logger(ActivityHandler.name); + constructor(private readonly service: ActivityService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + async onUserDeleted(event: UserDeletedEvent) { + if (event.newUserId) { + await this.service.changeResponsible({ + accountId: event.accountId, + currentUserId: event.userId, + newUserId: event.newUserId, + }); + } + } + + /** + * @deprecated use new @see handleCreateJob instead + */ + @OnAutomationJob(EntityTypeActionType.CreateActivity) + async handleCreateJobOld(job: AutomationJob): Promise<{ variables?: unknown }> { + return this.handleCreateJob(job); + } + @OnAutomationJob(EntityTypeActionType.ActivityCreate) + async handleCreateJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.activitySettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, activitySettings } = job.variables; + const activity = await this.service.processAutomation({ + accountId, + entityId: entity.id, + entityOwnerId: entity.responsibleUserId, + entityStageId: entity.stageId, + settings: activitySettings, + }); + return { variables: { ...job.variables, activity: activity?.toSimpleDto() } }; + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + return { variables: job.variables }; + } + } +} diff --git a/backend/src/CRM/activity/activity.service.ts b/backend/src/CRM/activity/activity.service.ts new file mode 100644 index 0000000..67a9274 --- /dev/null +++ b/backend/src/CRM/activity/activity.service.ts @@ -0,0 +1,325 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Repository } from 'typeorm'; +import { convert } from 'html-to-text'; + +import { DateUtil, FileLinkSource, NotFoundError } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { ActionActivityCreateSettings, ActionHelper } from '@/modules/automation'; +import { EntityInfoService } from '@/modules/entity/entity-info'; +import { NotificationType } from '@/modules/notification/notification/enums/notification-type.enum'; +import { CreateNotificationDto } from '@/modules/notification/notification/dto/create-notification.dto'; + +import { ActivityCreatedEvent, ActivityEvent, CrmEventType } from '../common'; +import { ActivityTypeService } from '../activity-type/activity-type.service'; +import { BaseTaskService } from '../base-task'; +import { FileLinkService } from '../Service/FileLink/FileLinkService'; + +import { Activity } from './entities'; +import { ActivityDto, CreateActivityDto, UpdateActivityDto } from './dto'; + +interface FindFilter { + accountId: number; + activityId?: number | number; + entityId?: number; + isResolved?: boolean; + responsibles?: number | number[]; +} + +@Injectable() +export class ActivityService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Activity) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly userService: UserService, + private readonly activityTypeService: ActivityTypeService, + private readonly fileLinkService: FileLinkService, + private readonly baseTaskService: BaseTaskService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async create( + accountId: number, + user: User, + dto: CreateActivityDto, + options?: { skipPermissionCheck?: boolean }, + ): Promise { + if (!options?.skipPermissionCheck) { + await this.authService.check({ + action: 'create', + user, + authorizable: Activity.getAuthorizable(), + throwError: true, + }); + } + + dto.weight = dto.weight ?? (await this.baseTaskService.calculateWeight(accountId, dto.afterId, dto.beforeId)); + const activity = await this.repository.save(Activity.create(accountId, user.id, dto)); + + if (dto.fileIds) { + await this.fileLinkService.processFiles(accountId, FileLinkSource.ACTIVITY, activity.id, dto.fileIds); + } + + const activityType = await this.activityTypeService.findOne({ accountId, id: dto.activityTypeId }); + + this.eventEmitter.emit( + CrmEventType.ActivityCreated, + new ActivityCreatedEvent({ + accountId, + activityId: activity.id, + ownerId: activity.responsibleUserId, + entityId: activity.entityId, + createdBy: activity.createdBy, + activityText: activity.text, + activityTypeName: activityType.name, + createdAt: activity.createdAt.toISOString(), + }), + ); + + return activity; + } + + public async createAndGetDto(account: Account, user: User, dto: CreateActivityDto): Promise { + const activity = await this.create(account.id, user, dto); + + return await this.createDtoForActivity(account, user, activity); + } + + public async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + public async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).orderBy('activity.id', 'DESC').getMany(); + } + + public async update(account: Account, user: User, id: number, dto: UpdateActivityDto): Promise { + let activity = await this.repository.findOneBy({ id }); + if (!activity) { + throw NotFoundError.withId(Activity, id); + } + + await this.authService.check({ action: 'edit', user, authorizable: activity, throwError: true }); + + if (dto.sorting) { + activity.weight = await this.baseTaskService.calculateWeight( + account.id, + dto.sorting.afterId, + dto.sorting.beforeId, + ); + } + activity = await this.repository.save(activity.update(dto)); + + if (dto.fileIds !== undefined) { + await this.fileLinkService.processFiles(account.id, FileLinkSource.ACTIVITY, id, dto.fileIds ?? []); + } + + this.eventEmitter.emit( + CrmEventType.ActivityUpdated, + new ActivityEvent({ accountId: account.id, entityId: activity.entityId, activityId: id }), + ); + + return await this.createDtoForActivity(account, user, activity); + } + + public async delete(accountId: number, user: User, id: number) { + const activity = await this.repository.findOneBy({ id }); + await this.authService.check({ action: 'delete', user, authorizable: activity, throwError: true }); + + await this.fileLinkService.processFiles(accountId, FileLinkSource.ACTIVITY, id, []); + const result = await this.repository.delete({ id }); + if (result.affected === 0) { + throw NotFoundError.withId(Activity, id); + } + + this.eventEmitter.emit( + CrmEventType.ActivityDeleted, + new ActivityEvent({ accountId, entityId: activity.entityId, activityId: id }), + ); + } + + public async findDtoForId(account: Account, user: User, id: number): Promise { + const activity = await this.repository.findOneBy({ id }); + return activity ? await this.createDtoForActivity(account, user, activity) : null; + } + + public async createDtoForActivity(account: Account, user: User, activity: Activity): Promise { + if (!(await this.authService.check({ action: 'view', user, authorizable: activity }))) { + return null; + } + + const entityInfo = activity.entityId + ? await this.entityInfoService.findOne({ + accountId: account.id, + user, + entityId: activity.entityId, + }) + : null; + const fileLinks = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.ACTIVITY, activity.id); + const userRights = await this.authService.getUserRights({ user, authorizable: activity }); + + return new ActivityDto(activity, entityInfo, fileLinks, userRights); + } + + public async changeResponsible({ + accountId, + currentUserId, + newUserId, + }: { + accountId: number; + currentUserId: number; + newUserId: number; + }) { + await this.repository.update({ accountId, responsibleUserId: currentUserId }, { responsibleUserId: newUserId }); + } + + public async processAutomation({ + accountId, + entityId, + entityOwnerId, + entityStageId, + settings, + }: { + accountId: number; + entityId: number; + entityOwnerId: number; + entityStageId: number | null | undefined; + settings: ActionActivityCreateSettings; + }): Promise { + const entity = await this.entityInfoService.findOne({ accountId, entityId }); + if (entity && (!entity.stageId || settings.allowAnyStage || entity.stageId === entityStageId)) { + const user = await this.userService.findOne({ accountId, id: entityOwnerId }); + const ownerId = settings.responsibleUserId ?? entityOwnerId; + const startDate = DateUtil.add(DateUtil.now(), { seconds: settings.deferStart ?? 0 }); + const endDate = ActionHelper.getEndDate({ + startDate, + deadlineType: settings.deadlineType, + deadlineTime: settings.deadlineTime, + }); + return this.create( + accountId, + user, + { + responsibleUserId: ownerId, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + activityTypeId: settings.activityTypeId, + text: settings.text, + entityId, + }, + { skipPermissionCheck: true }, + ); + } + return null; + } + + public async getOverdueNotifications(from: Date, to: Date): Promise { + const activities = await this.repository + .createQueryBuilder('activity') + .where('activity.is_resolved = false') + .andWhere('activity.end_date > :from', { from }) + .andWhere('activity.end_date <= :to', { to }) + .getMany(); + return activities.map( + (activity) => + new CreateNotificationDto( + activity.accountId, + activity.responsibleUserId, + NotificationType.ACTIVITY_OVERDUE, + activity.id, + activity.entityId, + activity.createdBy, + null, + convert(activity.text), + ), + ); + } + + public async getOverdueForFollowNotifications( + notifyUserId: number, + from: Date, + to: Date, + followUserIds: number[], + ): Promise { + const activities = await this.repository + .createQueryBuilder('activity') + .where('activity.is_resolved = false') + .andWhere('activity.end_date > :from', { from }) + .andWhere('activity.end_date <= :to', { to }) + .andWhere('activity.responsible_user_id in (:...userIds)', { userIds: followUserIds }) + .getMany(); + return activities.map( + (activity) => + new CreateNotificationDto( + activity.accountId, + notifyUserId, + NotificationType.ACTIVITY_OVERDUE_EMPLOYEE, + activity.id, + activity.entityId, + activity.responsibleUserId, + null, + convert(activity.text), + ), + ); + } + + public async getBeforeStartNotifications(userId: number, from: Date, to: Date) { + const activities = await this.repository + .createQueryBuilder('activity') + .where('activity.is_resolved = false') + .andWhere('activity.responsible_user_id = :userId', { userId }) + .andWhere('activity.start_date > :from', { from }) + .andWhere('activity.start_date <= :to', { to }) + .getMany(); + return activities.map( + (activity) => + new CreateNotificationDto( + activity.accountId, + activity.responsibleUserId, + NotificationType.ACTIVITY_BEFORE_START, + activity.id, + activity.entityId, + activity.createdBy, + null, + convert(activity.text), + ), + ); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('activity') + .where('activity.accountId = :accountId', { accountId: filter.accountId }); + if (filter.activityId) { + if (Array.isArray(filter.activityId)) { + qb.andWhere('activity.id IN (:...activityIds)', { activityIds: filter.activityId }); + } else { + qb.andWhere('activity.id = :activityId', { activityId: filter.activityId }); + } + } + if (filter.entityId) { + qb.andWhere('activity.entity_id = :entityId', { entityId: filter.entityId }); + } + if (filter.isResolved !== undefined) { + qb.andWhere('activity.is_resolved = :isResolved', { isResolved: filter.isResolved }); + } + if (filter.responsibles) { + if (Array.isArray(filter.responsibles)) { + if (filter.responsibles.length === 0) { + return qb.where('1 = 0'); + } + qb.andWhere('activity.responsible_user_id IN (:...responsibles)', { responsibles: filter.responsibles }); + } else { + qb.andWhere('activity.responsible_user_id = :responsible', { responsible: filter.responsibles }); + } + } + + return qb; + } +} diff --git a/backend/src/CRM/activity/dto/activity.dto.ts b/backend/src/CRM/activity/dto/activity.dto.ts new file mode 100644 index 0000000..8bcaf85 --- /dev/null +++ b/backend/src/CRM/activity/dto/activity.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +import { BaseTaskDto } from '../../base-task'; +import { Activity } from '../entities'; + +export class ActivityDto extends BaseTaskDto { + @ApiProperty() + activityTypeId: number; + + @ApiProperty({ required: false }) + result?: string; + + constructor(activity: Activity, entityInfo: EntityInfoDto | null, fileLinks: FileLinkDto[], userRights: UserRights) { + super(activity, entityInfo, fileLinks, userRights); + this.activityTypeId = activity.activityTypeId; + this.result = activity.result; + } +} diff --git a/backend/src/CRM/activity/dto/create-activity.dto.ts b/backend/src/CRM/activity/dto/create-activity.dto.ts new file mode 100644 index 0000000..6e337ef --- /dev/null +++ b/backend/src/CRM/activity/dto/create-activity.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { CreateBaseTaskDto } from '../../base-task'; + +export class CreateActivityDto extends CreateBaseTaskDto { + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty() + @IsNumber() + activityTypeId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + fileIds?: string[] | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + result?: string | null; +} diff --git a/backend/src/CRM/activity/dto/index.ts b/backend/src/CRM/activity/dto/index.ts new file mode 100644 index 0000000..51002bc --- /dev/null +++ b/backend/src/CRM/activity/dto/index.ts @@ -0,0 +1,3 @@ +export * from './activity.dto'; +export * from './create-activity.dto'; +export * from './update-activity.dto'; diff --git a/backend/src/CRM/activity/dto/update-activity.dto.ts b/backend/src/CRM/activity/dto/update-activity.dto.ts new file mode 100644 index 0000000..23091d2 --- /dev/null +++ b/backend/src/CRM/activity/dto/update-activity.dto.ts @@ -0,0 +1,26 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UpdateBaseTaskDto } from '../../base-task'; + +export class UpdateActivityDto extends UpdateBaseTaskDto { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + entityId?: number; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + activityTypeId?: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsArray() + fileIds?: string[] | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + result?: string | null; +} diff --git a/backend/src/CRM/activity/entities/activity.entity.ts b/backend/src/CRM/activity/entities/activity.entity.ts new file mode 100644 index 0000000..a421df3 --- /dev/null +++ b/backend/src/CRM/activity/entities/activity.entity.ts @@ -0,0 +1,107 @@ +import { Column, Entity } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; +import { BaseTask, TaskView } from '../../base-task'; +import { ActivityDto, CreateActivityDto, UpdateActivityDto } from '../dto'; + +@Entity('activity') +export class Activity extends BaseTask implements Authorizable { + @Column() + entityId: number; + + @Column() + activityTypeId: number; + + @Column({ nullable: true }) + result?: string; + + constructor( + accountId: number, + entityId: number, + createdBy: number, + responsibleUserId: number, + text: string, + activityTypeId: number, + isResolved: boolean, + startDate: Date, + endDate: Date, + resolvedDate: Date | null, + weight: number, + result?: string, + createdAt?: Date, + ) { + super( + accountId, + createdBy, + responsibleUserId, + text, + isResolved, + startDate, + endDate, + resolvedDate, + weight, + createdAt, + ); + this.entityId = entityId; + this.activityTypeId = activityTypeId; + this.result = result; + } + + public static create(accountId: number, createdBy: number, dto: CreateActivityDto): Activity { + return new Activity( + accountId, + dto.entityId, + createdBy, + dto.responsibleUserId, + dto.text, + dto.activityTypeId, + !!dto.isResolved, + dto.startDate ? DateUtil.fromISOString(dto.startDate) : DateUtil.now(), + dto.endDate ? DateUtil.fromISOString(dto.endDate) : DateUtil.now(), + dto.resolvedDate ? DateUtil.fromISOString(dto.resolvedDate) : null, + dto.weight, + dto.result, + ); + } + + public update(dto: UpdateActivityDto): Activity { + this.entityId = dto.entityId !== undefined ? dto.entityId : this.entityId; + this.responsibleUserId = dto.responsibleUserId !== undefined ? dto.responsibleUserId : this.responsibleUserId; + this.text = dto.text !== undefined ? dto.text : this.text; + this.activityTypeId = dto.activityTypeId !== undefined ? dto.activityTypeId : this.activityTypeId; + this.result = dto.result !== undefined ? dto.result : this.result; + this.startDate = dto.startDate !== undefined ? DateUtil.fromISOString(dto.startDate) : this.startDate; + this.endDate = dto.endDate !== undefined ? DateUtil.fromISOString(dto.endDate) : this.endDate; + if (dto.isResolved !== undefined) { + if (!this.isResolved && dto.isResolved) this.resolvedDate = DateUtil.now(); + if (this.isResolved && !dto.isResolved) this.resolvedDate = null; + this.isResolved = dto.isResolved; + } + return this; + } + + public view(): TaskView { + return TaskView.Activity; + } + + public toSimpleDto(): ActivityDto { + return new ActivityDto(this, null, [], null); + } + + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Activity, + id: null, + ownerId: this.responsibleUserId, + createdBy: this.createdBy, + }; + } + + static getAuthorizable(): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Activity, id: null }); + } +} diff --git a/backend/src/CRM/activity/entities/index.ts b/backend/src/CRM/activity/entities/index.ts new file mode 100644 index 0000000..97645a8 --- /dev/null +++ b/backend/src/CRM/activity/entities/index.ts @@ -0,0 +1 @@ +export * from './activity.entity'; diff --git a/backend/src/CRM/activity/index.ts b/backend/src/CRM/activity/index.ts new file mode 100644 index 0000000..190678d --- /dev/null +++ b/backend/src/CRM/activity/index.ts @@ -0,0 +1,5 @@ +export * from './activity.controller'; +export * from './activity.handler'; +export * from './activity.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/CRM/base-task/base-task.service.ts b/backend/src/CRM/base-task/base-task.service.ts new file mode 100644 index 0000000..9beb906 --- /dev/null +++ b/backend/src/CRM/base-task/base-task.service.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; + +const WEIGHT_MIN = 100.0; +const WEIGHT_STEP = 100.0; + +@Injectable() +export class BaseTaskService { + constructor(private dataSource: DataSource) {} + + public async calculateWeight( + accountId: number, + afterId: number | null | undefined, + beforeId: number | null | undefined, + ): Promise { + let afterWeight = afterId ? await this.getWeightById(accountId, afterId) : null; + let beforeWeight = beforeId ? await this.getWeightById(accountId, beforeId) : null; + if (afterWeight === null && beforeWeight === null) { + const minWeight = await this.getMinWeightForAccount(accountId); + return minWeight === null ? WEIGHT_MIN : minWeight - WEIGHT_STEP; + } else if (afterWeight !== null && beforeWeight === null) { + beforeWeight = await this.getMinWeightMoreThan(accountId, afterWeight); + if (beforeWeight === null) { + return afterWeight + WEIGHT_STEP; + } + } else if (afterWeight === null && beforeWeight !== null) { + afterWeight = await this.getMaxWeightLessThan(accountId, beforeWeight); + if (afterWeight === null) { + return beforeWeight - WEIGHT_STEP; + } + } + return (afterWeight + beforeWeight) / 2.0; + } + + private async getMinWeightForAccount(accountId: number): Promise { + const result = await this.dataSource.query( + `select min(at.weight) as weight from all_tasks at where at.account_id = ${accountId}`, + ); + return result && result.length > 0 ? result[0].weight : null; + } + + private async getWeightById(accountId: number, id: number): Promise { + const result = await this.dataSource.query( + `select at.weight from all_tasks at where at.account_id = ${accountId} and at.id = ${id};`, + ); + return result && result.length > 0 ? result[0].weight : null; + } + + private async getMinWeightMoreThan(accountId: number, limitWeight: number): Promise { + const result = await this.dataSource.query( + // eslint-disable-next-line max-len + `select min(at.weight) as weight from all_tasks at where at.account_id = ${accountId} and at.weight > ${limitWeight}`, + ); + return result && result.length > 0 ? result[0].weight : null; + } + + private async getMaxWeightLessThan(accountId: number, limitWeight: number): Promise { + const result = await this.dataSource.query( + // eslint-disable-next-line max-len + `select max(at.weight) as weight from all_tasks at where at.account_id = ${accountId} and at.weight < ${limitWeight}`, + ); + return result && result.length > 0 ? result[0].weight : null; + } +} diff --git a/backend/src/CRM/base-task/dto/base-task.dto.ts b/backend/src/CRM/base-task/dto/base-task.dto.ts new file mode 100644 index 0000000..ffecf87 --- /dev/null +++ b/backend/src/CRM/base-task/dto/base-task.dto.ts @@ -0,0 +1,70 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +import { TaskView } from '../enums'; +import { BaseTask } from '../entities'; + +export abstract class BaseTaskDto { + @ApiProperty() + id: number; + + @ApiProperty() + responsibleUserId: number; + + @ApiProperty({ nullable: true }) + startDate: string | null; + + @ApiProperty({ nullable: true }) + endDate: string | null; + + @ApiProperty() + text: string; + + @ApiProperty() + isResolved: boolean; + + @ApiProperty({ nullable: true }) + resolvedDate: string | null; + + @ApiProperty() + createdBy: number; + + @ApiProperty() + weight: number; + + @ApiProperty() + createdAt: string; + + @ApiProperty() + view: TaskView; + + @ApiProperty({ type: [FileLinkDto] }) + fileLinks: FileLinkDto[]; + + @ApiProperty({ nullable: true }) + entityInfo: EntityInfoDto | null; + + @ApiProperty({ type: () => UserRights }) + userRights: UserRights; + + constructor(baseTask: BaseTask, entityInfo: EntityInfoDto | null, fileLinks: FileLinkDto[], userRights: UserRights) { + this.id = baseTask.id; + this.responsibleUserId = baseTask.responsibleUserId; + this.startDate = baseTask.startDate ? baseTask.startDate.toISOString() : null; + this.endDate = baseTask.endDate ? baseTask.endDate.toISOString() : null; + this.text = baseTask.text; + this.isResolved = baseTask.isResolved; + this.createdBy = baseTask.createdBy; + this.weight = baseTask.weight; + this.createdAt = baseTask.createdAt.toISOString(); + this.view = baseTask.view(); + this.fileLinks = fileLinks; + this.resolvedDate = baseTask.resolvedDate ? baseTask.resolvedDate.toISOString() : null; + this.entityInfo = entityInfo; + this.userRights = userRights; + } +} diff --git a/backend/src/CRM/base-task/dto/create-base-task.dto.ts b/backend/src/CRM/base-task/dto/create-base-task.dto.ts new file mode 100644 index 0000000..45e621f --- /dev/null +++ b/backend/src/CRM/base-task/dto/create-base-task.dto.ts @@ -0,0 +1,48 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export abstract class CreateBaseTaskDto { + @ApiProperty({ description: 'Responsible user ID' }) + @IsNumber() + responsibleUserId: number; + + @ApiPropertyOptional({ nullable: true, description: 'Start date' }) + @IsOptional() + @IsString() + startDate?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'End date' }) + @IsOptional() + @IsString() + endDate?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Text' }) + @IsOptional() + @IsString() + text?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Is resolved' }) + @IsOptional() + @IsBoolean() + isResolved?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Resolved date' }) + @IsOptional() + @IsString() + resolvedDate?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Weight' }) + @IsOptional() + @IsNumber() + weight?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Task ID to place after on weight' }) + @IsOptional() + @IsNumber() + afterId?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Task ID to place before on weight' }) + @IsOptional() + @IsNumber() + beforeId?: number | null; +} diff --git a/backend/src/CRM/base-task/dto/index.ts b/backend/src/CRM/base-task/dto/index.ts new file mode 100644 index 0000000..0ebce87 --- /dev/null +++ b/backend/src/CRM/base-task/dto/index.ts @@ -0,0 +1,3 @@ +export * from './base-task.dto'; +export * from './create-base-task.dto'; +export * from './update-base-task.dto'; diff --git a/backend/src/CRM/base-task/dto/update-base-task.dto.ts b/backend/src/CRM/base-task/dto/update-base-task.dto.ts new file mode 100644 index 0000000..230493c --- /dev/null +++ b/backend/src/CRM/base-task/dto/update-base-task.dto.ts @@ -0,0 +1,35 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ManualSorting } from '@/common'; + +export abstract class UpdateBaseTaskDto { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + responsibleUserId?: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + startDate?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + endDate?: string | null; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + text?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsBoolean() + isResolved?: boolean; + + @ApiPropertyOptional({ type: ManualSorting, nullable: true }) + @IsOptional() + sorting?: ManualSorting | null; +} diff --git a/backend/src/CRM/base-task/entities/base-task.entity.ts b/backend/src/CRM/base-task/entities/base-task.entity.ts new file mode 100644 index 0000000..c07d7a0 --- /dev/null +++ b/backend/src/CRM/base-task/entities/base-task.entity.ts @@ -0,0 +1,89 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { TaskView } from '../enums'; + +@Entity() +export abstract class BaseTask { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + responsibleUserId: number; + + @Column({ nullable: true }) + startDate: Date | null; + + @Column({ nullable: true }) + endDate: Date | null; + + @Column() + text: string; + + @Column() + createdBy: number; + + @Column() + isResolved: boolean; + + @Column({ nullable: true }) + resolvedDate: Date | null; + + @Column({ type: 'double precision' }) + weight: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + createdBy: number, + responsibleUserId: number, + text: string, + isResolved: boolean, + startDate: Date | null, + endDate: Date | null, + resolvedDate: Date | null, + weight: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.createdBy = createdBy; + this.responsibleUserId = responsibleUserId; + this.text = text; + this.isResolved = isResolved; + this.startDate = startDate; + this.endDate = endDate; + this.resolvedDate = resolvedDate; + this.weight = weight; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public isTaskExpired(): boolean { + if (this.endDate) { + return DateUtil.isPast(this.endDate); + } + + return false; + } + + public isTaskToday(): boolean { + const now = DateUtil.now(); + + if (this.startDate && this.endDate) { + return this.startDate <= now && this.endDate >= now; + } else if (this.startDate) { + return DateUtil.isToday(this.startDate); + } else if (this.endDate) { + return DateUtil.isToday(this.endDate); + } + + return false; + } + + public abstract view(): TaskView; +} diff --git a/backend/src/CRM/base-task/entities/index.ts b/backend/src/CRM/base-task/entities/index.ts new file mode 100644 index 0000000..62c3a22 --- /dev/null +++ b/backend/src/CRM/base-task/entities/index.ts @@ -0,0 +1 @@ +export * from './base-task.entity'; diff --git a/backend/src/CRM/base-task/enums/index.ts b/backend/src/CRM/base-task/enums/index.ts new file mode 100644 index 0000000..51c69e6 --- /dev/null +++ b/backend/src/CRM/base-task/enums/index.ts @@ -0,0 +1 @@ +export * from './task-view.enum'; diff --git a/backend/src/CRM/base-task/enums/task-view.enum.ts b/backend/src/CRM/base-task/enums/task-view.enum.ts new file mode 100644 index 0000000..9f1deb3 --- /dev/null +++ b/backend/src/CRM/base-task/enums/task-view.enum.ts @@ -0,0 +1,4 @@ +export enum TaskView { + Activity = 'activity', + Task = 'task', +} diff --git a/backend/src/CRM/base-task/index.ts b/backend/src/CRM/base-task/index.ts new file mode 100644 index 0000000..d37410d --- /dev/null +++ b/backend/src/CRM/base-task/index.ts @@ -0,0 +1,4 @@ +export * from './base-task.service'; +export * from './dto'; +export * from './entities'; +export * from './enums'; diff --git a/backend/src/CRM/board-stage/board-stage.controller.ts b/backend/src/CRM/board-stage/board-stage.controller.ts new file mode 100644 index 0000000..0b45ea8 --- /dev/null +++ b/backend/src/CRM/board-stage/board-stage.controller.ts @@ -0,0 +1,120 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized, UserAccess } from '@/modules/iam/common'; + +import { + BoardStageDto, + CreateBoardStageDto, + ProcessBoardStageDto, + UpdateBoardStageDto, + UpdateBoardStagesDto, +} from './dto'; +import { BoardStageService } from './board-stage.service'; + +@ApiTags('crm/boards/stages') +@Controller('crm/boards/:boardId/stages') +@JwtAuthorized() +@TransformToDto() +export class BoardStageController { + constructor(private readonly service: BoardStageService) {} + + @ApiOperation({ summary: 'Create board stage', description: 'Create board stage' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ type: CreateBoardStageDto, description: 'Data for creating board stage', required: true }) + @ApiCreatedResponse({ description: 'Board stage', type: BoardStageDto }) + @Post() + @UserAccess({ adminOnly: true }) + async create( + @CurrentAuth() { accountId }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() dto: CreateBoardStageDto, + ) { + return this.service.create({ accountId, boardId, dto }); + } + + @ApiOperation({ + summary: 'Get board stages', + description: `Get board stages. Use 'all' for boardId to get all stages for account.`, + }) + @ApiParam({ name: 'boardId', description: `Board ID`, type: Number, required: true }) + @ApiOkResponse({ description: 'Board stages', type: [BoardStageDto] }) + @Get() + async findMany(@CurrentAuth() { accountId }: AuthData, @Param('boardId', ParseIntPipe) boardId: number) { + return this.service.findMany({ accountId, boardId }); + } + + @ApiOperation({ summary: 'Get board stage', description: 'Get board stage.' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiParam({ name: 'stageId', description: 'Stage ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Board stage', type: BoardStageDto }) + @Get(':stageId') + async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Param('stageId', ParseIntPipe) stageId: number, + ) { + return this.service.findOne({ accountId, boardId, stageId }); + } + + @ApiOperation({ summary: 'Update board stages', description: 'Update board stages' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ type: UpdateBoardStagesDto, description: 'Data for updating board stages', required: true }) + @ApiOkResponse({ description: 'Board stage', type: BoardStageDto }) + @Patch() + @UserAccess({ adminOnly: true }) + async updateMany( + @CurrentAuth() { accountId }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() dto: UpdateBoardStagesDto, + ) { + return this.service.updateMany({ accountId, boardId, dtos: dto.stages }); + } + + @ApiOperation({ summary: 'Update board stage', description: 'Update board stage.' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiParam({ name: 'stageId', description: 'Stage ID', type: Number, required: true }) + @ApiBody({ type: UpdateBoardStageDto, description: 'Data for updating board stage', required: true }) + @ApiOkResponse({ description: 'Board stage', type: BoardStageDto }) + @Patch(':stageId') + @UserAccess({ adminOnly: true }) + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Param('stageId', ParseIntPipe) stageId: number, + @Body() dto: UpdateBoardStageDto, + ) { + return this.service.update({ accountId, boardId, stageId, dto }); + } + + @ApiOperation({ summary: 'Process board stages', description: 'Process board stages. Create or update stages.' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ type: ProcessBoardStageDto, description: 'Data for processing board stages', required: true }) + @ApiCreatedResponse({ description: 'Board stages', type: [BoardStageDto] }) + @Post('batch') + @UserAccess({ adminOnly: true }) + async processBatch( + @CurrentAuth() { accountId }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() dto: ProcessBoardStageDto, + ) { + return this.service.processBatch({ accountId, boardId, dtos: dto?.stages }); + } + + @ApiOperation({ summary: 'Delete board stage', description: 'Delete board stage and move related to new stage.' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiParam({ name: 'stageId', description: 'Stage ID', type: Number, required: true }) + @ApiQuery({ name: 'newStageId', description: 'New stage ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Deleted stage ID', type: Number }) + @Delete(':stageId') + @UserAccess({ adminOnly: true }) + async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Param('stageId', ParseIntPipe) stageId: number, + @Query('newStageId', ParseIntPipe) newStageId: number, + ) { + return this.service.delete({ accountId, boardId, stageId, newStageId }); + } +} diff --git a/backend/src/CRM/board-stage/board-stage.service.ts b/backend/src/CRM/board-stage/board-stage.service.ts new file mode 100644 index 0000000..9f9eb55 --- /dev/null +++ b/backend/src/CRM/board-stage/board-stage.service.ts @@ -0,0 +1,331 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; +import { SequenceIdService } from '@/database'; + +import { BoardStageDeletedEvent, BoardStageEvent, CrmEventType, SequenceName } from '../common'; +import { TaskService } from '../task'; +import { EntityService } from '../Service/Entity/EntityService'; + +import { CreateBoardStageDto, UpdateBoardStageDto } from './dto'; +import { BoardStage } from './entities'; +import { BoardStageCode, BoardStageCodes, BoardStageType } from './enums'; +import { GroupedStages } from './types'; + +interface FindFilter { + accountId: number; + stageId?: number; + boardId?: number | number[]; + includeCodes?: BoardStageCode[]; + excludeCodes?: BoardStageCode[]; + afterStageId?: number; +} + +@Injectable() +export class BoardStageService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(BoardStage) + private readonly repository: Repository, + private readonly sequenceIdService: SequenceIdService, + @Inject(forwardRef(() => EntityService)) + private readonly entityService: EntityService, + @Inject(forwardRef(() => TaskService)) + private readonly taskService: TaskService, + ) {} + + /** + * @deprecated + */ + private async nextIdentity(): Promise { + return this.sequenceIdService.nextIdentity(SequenceName.Stage); + } + + async create({ + accountId, + boardId, + dto, + }: { + accountId: number; + boardId: number; + dto: CreateBoardStageDto; + }): Promise { + dto.id ??= await this.nextIdentity(); + if (dto.sortOrder === undefined) { + const maxSortOrder = await this.createFindQb({ accountId, boardId }) + .select('MAX(stage.sort_order)', 'max') + .orderBy() + .getRawOne<{ max: number }>(); + + dto.sortOrder = (maxSortOrder?.max ?? 0) + 1; + } + const stage = await this.repository.save(BoardStage.fromDto(accountId, boardId, dto)); + + this.eventEmitter.emit( + CrmEventType.BoardStageCreated, + new BoardStageEvent({ accountId, boardId, stageId: stage.id }), + ); + + return stage; + } + async createMany({ + accountId, + boardId, + dtos, + }: { + accountId: number; + boardId: number; + dtos: CreateBoardStageDto[]; + }): Promise { + return Promise.all(dtos.map((dto) => this.create({ accountId, boardId, dto }))); + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).getMany(); + } + + async findOneId(filter: FindFilter): Promise { + const stage = await this.createFindQb(filter).select('stage.id', 'id').getRawOne<{ id: number }>(); + + return stage?.id ?? null; + } + async findManyIds(filter: FindFilter): Promise { + const stages = await this.createFindQb(filter).select('stage.id', 'id').getRawMany<{ id: number }>(); + + return stages.map((stage) => stage.id); + } + + async exists(filter: FindFilter): Promise { + return (await this.createFindQb(filter).getCount()) > 0; + } + + async getGroupedByType({ + accountId, + entityTypeId, + boardId, + type, + }: { + accountId: number; + entityTypeId?: number | number[] | null; + boardId?: number | number[] | null; + type?: BoardStageType | null; + }): Promise { + const qb = this.repository.createQueryBuilder('stage').where('stage.account_id = :accountId', { accountId }); + if (boardId) { + if (Array.isArray(boardId)) { + qb.andWhere('stage.board_id IN (:...boardId)', { boardId }); + } else { + qb.andWhere('stage.board_id = :boardId', { boardId }); + } + } + if (entityTypeId) { + qb.innerJoin('board', 'board', 'board.id = stage.board_id'); + if (Array.isArray(entityTypeId)) { + qb.andWhere('board.record_id IN (:...entityTypeId)', { entityTypeId }); + } else { + qb.andWhere('board.record_id = :entityTypeId', { entityTypeId }); + } + } + const closed = [...BoardStageCodes.won, ...BoardStageCodes.lost]; + if (type) { + if (type === BoardStageType.Open) { + qb.andWhere('(stage.code NOT IN (:...codes) OR stage.code IS NULL)', { codes: closed }); + } else if (type === BoardStageType.Won) { + qb.andWhere('stage.code IN (:...codes)', { codes: BoardStageCodes.won }); + } else if (type === BoardStageType.Lost) { + qb.andWhere('stage.code IN (:...codes)', { codes: BoardStageCodes.lost }); + } + } + qb.select(`array_agg(stage.id)::int[]`, 'all'); + qb.addSelect( + `array_agg(stage.id) filter (where stage.code IN (${BoardStageCodes.won.map((c) => `'${c}'`).join(',')}))::int[]`, + 'won', + ); + qb.addSelect( + // eslint-disable-next-line max-len + `array_agg(stage.id) filter (where stage.code IN (${BoardStageCodes.lost.map((c) => `'${c}'`).join(',')}))::int[]`, + 'lost', + ); + qb.addSelect( + // eslint-disable-next-line max-len + `array_agg(stage.id) filter (where stage.code NOT IN (${closed.map((c) => `'${c}'`).join(',')}) OR stage.code IS NULL)::int[]`, + 'open', + ); + + const result = await qb.getRawOne(); + return result; + } + + async update({ + accountId, + boardId, + stageId, + dto, + }: { + accountId: number; + boardId: number; + stageId: number; + dto: UpdateBoardStageDto; + }): Promise { + const stage = await this.findOne({ accountId, boardId, stageId }); + if (!stage) { + throw NotFoundError.withId(BoardStage, stageId); + } + + await this.repository.save(stage.update(dto)); + + this.eventEmitter.emit( + CrmEventType.BoardStageUpdated, + new BoardStageEvent({ accountId, boardId, stageId: stage.id }), + ); + + return stage; + } + + async updateMany({ + accountId, + boardId, + dtos, + }: { + accountId: number; + boardId: number; + dtos: UpdateBoardStageDto[]; + }): Promise { + return Promise.all(dtos.map((dto) => this.update({ accountId, boardId, stageId: dto.id, dto }))); + } + + async processBatch({ + accountId, + boardId, + dtos, + }: { + accountId: number; + boardId: number; + dtos: (CreateBoardStageDto | UpdateBoardStageDto)[]; + }): Promise { + //TODO: This is right way to check for created or updated stages. Use this after Frontend refactor. + // const created = dtos.filter((dto) => !dto['id']).map((dto) => dto as CreateBoardStageDto); + // const updated = dtos.filter((dto) => dto['id']).map((dto) => dto as UpdateBoardStageDto); + const created: CreateBoardStageDto[] = []; + const updated: UpdateBoardStageDto[] = []; + for (const dto of dtos) { + if (!dto['id'] || !(await this.exists({ accountId, boardId, stageId: dto['id'] }))) { + created.push(dto as CreateBoardStageDto); + } else { + updated.push(dto as UpdateBoardStageDto); + } + } + + const result: BoardStage[] = []; + + result.push(...(await this.createMany({ accountId, boardId, dtos: created }))); + result.push(...(await this.updateMany({ accountId, boardId, dtos: updated }))); + + return result; + } + + async delete({ + accountId, + boardId, + stageId, + newStageId, + }: { + accountId: number; + boardId: number; + stageId: number; + newStageId?: number | null; + }) { + const stage = await this.findOne({ accountId, boardId, stageId }); + if (!stage) { + throw NotFoundError.withId(BoardStage, stageId); + } + + return await this.deleteStage({ stage, newStageId }); + } + + async deleteMany(filter: FindFilter) { + const stages = await this.findMany(filter); + for (const stage of stages) { + await this.deleteStage({ stage }); + } + } + + private async deleteStage({ stage, newStageId }: { stage: BoardStage; newStageId?: number | null }) { + if (newStageId) { + await Promise.all([ + this.entityService.changeStageForAll({ + accountId: stage.accountId, + boardId: stage.boardId, + stageId: stage.id, + newStageId, + }), + this.taskService.changeStageForAll({ + accountId: stage.accountId, + boardId: stage.boardId, + stageId: stage.id, + newStageId: newStageId, + }), + ]); + } + + await this.repository.delete(stage.id); + + this.eventEmitter.emit( + CrmEventType.BoardStageDeleted, + new BoardStageDeletedEvent({ accountId: stage.accountId, boardId: stage.boardId, stageId: stage.id, newStageId }), + ); + + return stage.id; + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('stage') + .where('stage.account_id = :accountId', { accountId: filter.accountId }) + .orderBy('stage.sort_order', 'ASC'); + + if (filter.stageId) { + qb.andWhere('stage.id = :stageId', { stageId: filter.stageId }); + } + if (filter.boardId) { + if (Array.isArray(filter.boardId)) { + qb.andWhere('stage.board_id IN (:...boardIds)', { boardIds: filter.boardId }); + } else { + qb.andWhere('stage.board_id = :boardId', { boardId: filter.boardId }); + } + } + if (filter.includeCodes) { + qb.andWhere('stage.code IN (:...includeCodes)', { includeCodes: filter.includeCodes }); + } + if (filter.excludeCodes) { + qb.andWhere( + new Brackets((qb) => { + qb.where('stage.code NOT IN (:...excludeCodes)', { excludeCodes: filter.excludeCodes }).orWhere( + 'code IS NULL', + ); + }), + ); + } + if (filter.afterStageId) { + qb.innerJoin( + (subQuery) => { + return subQuery + .select('s.id', 'id') + .addSelect('s.sort_order', 'sort_order') + .from('stage', 's') + .where('s.id = :afterStageId', { afterStageId: filter.afterStageId }); + }, + 'after_stage', + 'stage.sort_order > after_stage.sort_order', + ); + } + + return qb; + } +} diff --git a/backend/src/CRM/board-stage/dto/board-stage.dto.ts b/backend/src/CRM/board-stage/dto/board-stage.dto.ts new file mode 100644 index 0000000..acded72 --- /dev/null +++ b/backend/src/CRM/board-stage/dto/board-stage.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { BoardStageCode } from '../enums'; + +export class BoardStageDto { + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Board ID' }) + @IsNumber() + boardId: number; + + @ApiProperty({ description: 'Stage name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Stage color' }) + @IsString() + color: string; + + @ApiProperty({ enum: BoardStageCode, nullable: true, description: 'Stage code' }) + @IsOptional() + @IsEnum(BoardStageCode) + code?: BoardStageCode | null; + + @ApiProperty({ description: 'Is system stage' }) + @IsBoolean() + isSystem: boolean; + + @ApiProperty({ description: 'Sort order' }) + @IsNumber() + sortOrder: number; +} diff --git a/backend/src/CRM/board-stage/dto/create-board-stage.dto.ts b/backend/src/CRM/board-stage/dto/create-board-stage.dto.ts new file mode 100644 index 0000000..b277944 --- /dev/null +++ b/backend/src/CRM/board-stage/dto/create-board-stage.dto.ts @@ -0,0 +1,21 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; + +import { BoardStageDto } from './board-stage.dto'; + +export class CreateBoardStageDto extends PickType(BoardStageDto, ['name', 'color', 'code'] as const) { + @ApiPropertyOptional({ description: 'Stage ID' }) + @IsOptional() + @IsNumber() + id?: number; + + @ApiPropertyOptional({ description: 'Sort order' }) + @IsOptional() + @IsNumber() + sortOrder?: number; + + @ApiPropertyOptional({ description: 'Is system' }) + @IsOptional() + @IsBoolean() + isSystem?: boolean; +} diff --git a/backend/src/CRM/board-stage/dto/index.ts b/backend/src/CRM/board-stage/dto/index.ts new file mode 100644 index 0000000..7299e03 --- /dev/null +++ b/backend/src/CRM/board-stage/dto/index.ts @@ -0,0 +1,5 @@ +export * from './board-stage.dto'; +export * from './create-board-stage.dto'; +export * from './process-board-stage.dto'; +export * from './update-board-stage.dto'; +export * from './update-board-stages.dto'; diff --git a/backend/src/CRM/board-stage/dto/process-board-stage.dto.ts b/backend/src/CRM/board-stage/dto/process-board-stage.dto.ts new file mode 100644 index 0000000..1732597 --- /dev/null +++ b/backend/src/CRM/board-stage/dto/process-board-stage.dto.ts @@ -0,0 +1,18 @@ +import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { CreateBoardStageDto } from './create-board-stage.dto'; +import { UpdateBoardStageDto } from './update-board-stage.dto'; + +@ApiExtraModels(CreateBoardStageDto) +@ApiExtraModels(UpdateBoardStageDto) +export class ProcessBoardStageDto { + @ApiProperty({ + description: 'Array of stages', + type: 'array', + items: { oneOf: [{ $ref: getSchemaPath(CreateBoardStageDto) }, { $ref: getSchemaPath(UpdateBoardStageDto) }] }, + }) + @IsOptional() + @IsArray() + stages: (CreateBoardStageDto | UpdateBoardStageDto)[]; +} diff --git a/backend/src/CRM/board-stage/dto/update-board-stage.dto.ts b/backend/src/CRM/board-stage/dto/update-board-stage.dto.ts new file mode 100644 index 0000000..ec533e4 --- /dev/null +++ b/backend/src/CRM/board-stage/dto/update-board-stage.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, PartialType, PickType } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { BoardStageDto } from './board-stage.dto'; + +export class UpdateBoardStageDto extends PartialType( + PickType(BoardStageDto, ['name', 'color', 'code', 'isSystem', 'sortOrder'] as const), +) { + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + id: number; +} diff --git a/backend/src/CRM/board-stage/dto/update-board-stages.dto.ts b/backend/src/CRM/board-stage/dto/update-board-stages.dto.ts new file mode 100644 index 0000000..3f5a541 --- /dev/null +++ b/backend/src/CRM/board-stage/dto/update-board-stages.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { UpdateBoardStageDto } from './update-board-stage.dto'; + +export class UpdateBoardStagesDto { + @ApiProperty({ type: [UpdateBoardStageDto], description: 'Stage dtos' }) + @IsArray() + stages: UpdateBoardStageDto[]; +} diff --git a/backend/src/CRM/board-stage/entities/board-stage.entity.ts b/backend/src/CRM/board-stage/entities/board-stage.entity.ts new file mode 100644 index 0000000..b475abe --- /dev/null +++ b/backend/src/CRM/board-stage/entities/board-stage.entity.ts @@ -0,0 +1,91 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { BoardStageCode } from '../enums'; +import { CreateBoardStageDto, BoardStageDto, UpdateBoardStageDto } from '../dto'; + +@Entity() +export class BoardStage { + @PrimaryColumn() + id: number; + + @Column() + accountId: number; + + @Column() + boardId: number; + + @Column() + createdAt: Date; + + @Column() + name: string; + + @Column() + color: string; + + @Column() + code: BoardStageCode | null; + + @Column() + isSystem: boolean; + + @Column() + sortOrder: number; + + constructor( + accountId: number, + id: number, + boardId: number, + name: string, + color: string, + code: BoardStageCode | null, + isSystem: boolean, + sortOrder: number, + ) { + this.accountId = accountId; + this.id = id; + this.boardId = boardId; + this.name = name; + this.color = color; + this.code = code; + this.isSystem = isSystem; + this.sortOrder = sortOrder; + this.createdAt = DateUtil.now(); + } + + public static fromDto(accountId: number, boardId: number, dto: CreateBoardStageDto): BoardStage { + return new BoardStage( + accountId, + dto.id, + boardId, + dto.name, + dto.color, + dto.code, + dto.isSystem ?? false, + dto.sortOrder ?? 0, + ); + } + + public update(dto: Omit): BoardStage { + this.name = dto.name !== undefined ? dto.name : this.name; + this.color = dto.color !== undefined ? dto.color : this.color; + this.code = dto.code !== undefined ? dto.code : this.code; + this.isSystem = dto.isSystem !== undefined ? dto.isSystem : this.isSystem; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + + return this; + } + + public toDto(): BoardStageDto { + return { + id: this.id, + boardId: this.boardId, + name: this.name, + color: this.color, + code: this.code, + isSystem: this.isSystem, + sortOrder: this.sortOrder, + }; + } +} diff --git a/backend/src/CRM/board-stage/entities/index.ts b/backend/src/CRM/board-stage/entities/index.ts new file mode 100644 index 0000000..50a5a94 --- /dev/null +++ b/backend/src/CRM/board-stage/entities/index.ts @@ -0,0 +1 @@ +export * from './board-stage.entity'; diff --git a/backend/src/CRM/board-stage/enums/board-stage-code.enum.ts b/backend/src/CRM/board-stage/enums/board-stage-code.enum.ts new file mode 100644 index 0000000..846a336 --- /dev/null +++ b/backend/src/CRM/board-stage/enums/board-stage-code.enum.ts @@ -0,0 +1,24 @@ +import { BoardStageType } from './board-stage-type.enum'; + +export enum BoardStageCode { + Win = 'win', + Lost = 'lost', + Hired = 'hired', + Rejected = 'rejected', + Fired = 'fired', + Done = 'done', + NotDone = 'not_done', + Completed = 'completed', + NotSatisfied = 'not_satisfied', +} + +export const BoardStageCodes = { + [BoardStageType.Won]: [BoardStageCode.Win, BoardStageCode.Hired, BoardStageCode.Done, BoardStageCode.Completed], + [BoardStageType.Lost]: [ + BoardStageCode.Lost, + BoardStageCode.Rejected, + BoardStageCode.Fired, + BoardStageCode.NotDone, + BoardStageCode.NotSatisfied, + ], +}; diff --git a/backend/src/CRM/board-stage/enums/board-stage-type.enum.ts b/backend/src/CRM/board-stage/enums/board-stage-type.enum.ts new file mode 100644 index 0000000..038b23a --- /dev/null +++ b/backend/src/CRM/board-stage/enums/board-stage-type.enum.ts @@ -0,0 +1,6 @@ +export enum BoardStageType { + All = 'all', + Open = 'open', + Won = 'won', + Lost = 'lost', +} diff --git a/backend/src/CRM/board-stage/enums/index.ts b/backend/src/CRM/board-stage/enums/index.ts new file mode 100644 index 0000000..9daa0c4 --- /dev/null +++ b/backend/src/CRM/board-stage/enums/index.ts @@ -0,0 +1,2 @@ +export * from './board-stage-code.enum'; +export * from './board-stage-type.enum'; diff --git a/backend/src/CRM/board-stage/index.ts b/backend/src/CRM/board-stage/index.ts new file mode 100644 index 0000000..f175d19 --- /dev/null +++ b/backend/src/CRM/board-stage/index.ts @@ -0,0 +1,6 @@ +export * from './board-stage.controller'; +export * from './board-stage.service'; +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './types'; diff --git a/backend/src/CRM/board-stage/types/grouped-stages.ts b/backend/src/CRM/board-stage/types/grouped-stages.ts new file mode 100644 index 0000000..bb4c8f4 --- /dev/null +++ b/backend/src/CRM/board-stage/types/grouped-stages.ts @@ -0,0 +1,3 @@ +import { BoardStageType } from '../enums'; + +export type GroupedStages = Record; diff --git a/backend/src/CRM/board-stage/types/index.ts b/backend/src/CRM/board-stage/types/index.ts new file mode 100644 index 0000000..97c518d --- /dev/null +++ b/backend/src/CRM/board-stage/types/index.ts @@ -0,0 +1 @@ +export * from './grouped-stages'; diff --git a/backend/src/CRM/board/board.controller.ts b/backend/src/CRM/board/board.controller.ts new file mode 100644 index 0000000..68f99f4 --- /dev/null +++ b/backend/src/CRM/board/board.controller.ts @@ -0,0 +1,60 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { BoardDto, BoardFilterDto, CreateBoardDto, UpdateBoardDto } from './dto'; +import { BoardService } from './board.service'; + +@ApiTags('crm/boards') +@Controller('crm/boards') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class BoardController { + constructor(private readonly service: BoardService) {} + + @ApiOperation({ summary: 'Create board', description: 'Create board' }) + @ApiBody({ type: CreateBoardDto, required: true, description: 'Data for creating board' }) + @ApiCreatedResponse({ type: BoardDto, description: 'Created board' }) + @Post() + public async create(@CurrentAuth() { accountId, user }: AuthData, @Body() dto: CreateBoardDto) { + return await this.service.create({ accountId, user, dto }); + } + + @ApiOperation({ summary: 'Get boards', description: 'Get boards' }) + @ApiOkResponse({ description: 'Boards', type: [BoardDto] }) + @Get() + public async findMany(@CurrentAuth() { accountId, user }: AuthData, @Query() filter: BoardFilterDto) { + return await this.service.findMany({ user, filter: { accountId, ...filter } }); + } + + @ApiOperation({ summary: 'Get board', description: 'Get board' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Board', type: BoardDto }) + @Get(':boardId') + public async findOne(@CurrentAuth() { accountId, user }: AuthData, @Param('boardId', ParseIntPipe) boardId: number) { + return await this.service.findOne({ user, filter: { accountId, boardId } }); + } + + @ApiOperation({ summary: 'Update board', description: 'Update board' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ type: UpdateBoardDto, required: true, description: 'Data for updating board' }) + @ApiOkResponse({ description: 'Updated board', type: BoardDto }) + @Patch(':boardId') + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() dto: UpdateBoardDto, + ) { + return await this.service.update({ accountId, user, boardId, dto }); + } + + @ApiOperation({ summary: 'Delete board', description: 'Delete board' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiOkResponse() + @Delete(':boardId') + public async delete(@CurrentAuth() { accountId, userId }: AuthData, @Param('boardId', ParseIntPipe) boardId: number) { + await this.service.delete({ accountId, userId, boardId, preserveLast: true }); + } +} diff --git a/backend/src/CRM/board/board.handler.ts b/backend/src/CRM/board/board.handler.ts new file mode 100644 index 0000000..61ee9c1 --- /dev/null +++ b/backend/src/CRM/board/board.handler.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; + +import { CrmEventType, EntityTypeEvent } from '../common'; +import { BoardService } from './board.service'; + +@Injectable() +export class BoardHandler { + constructor(private readonly service: BoardService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.service.changeUser({ accountId: event.accountId, userId: event.userId, newUserId: event.newUserId }); + } + + @OnEvent(CrmEventType.EntityTypeDeleted, { async: true }) + public async onEntityTypeDeleted(event: EntityTypeEvent): Promise { + this.service.deleteMany({ + userId: event.userId, + filter: { accountId: event.accountId, recordId: event.entityTypeId }, + }); + } +} diff --git a/backend/src/CRM/board/board.service.ts b/backend/src/CRM/board/board.service.ts new file mode 100644 index 0000000..4c39516 --- /dev/null +++ b/backend/src/CRM/board/board.service.ts @@ -0,0 +1,428 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { BadRequestError, isUnique, NotFoundError } from '@/common'; + +import { UserRights } from '@/modules/iam/common'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { BoardEvent, CrmEventType, EntityCategory } from '../common'; +import { BoardStageCode, BoardStageService } from '../board-stage'; +import { EntityTypeService } from '../entity-type/entity-type.service'; + +import { CreateBoardDto, UpdateBoardDto } from './dto'; +import { Board } from './entities'; +import { BoardType } from './enums'; + +interface FindFilter { + accountId: number; + boardId?: number; + isSystem?: boolean; + type?: BoardType; + recordId?: number; +} + +@Injectable() +export class BoardService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Board) + private readonly repository: Repository, + @Inject(forwardRef(() => EntityTypeService)) + private readonly entityTypeService: EntityTypeService, + @Inject(forwardRef(() => BoardStageService)) + private readonly stageService: BoardStageService, + ) {} + + //TODO: split user and owner!!! + public async create({ + accountId, + user, + dto, + options: { ownerId = null, isSystem = false, taskBoardId = null, createDefaultStages = true } = {}, + }: { + accountId: number; + user: User | null; + dto: CreateBoardDto; + options?: { + ownerId?: number | null; + isSystem?: boolean; + taskBoardId?: number | null; + createDefaultStages?: boolean; + }; + }): Promise { + let ownerIdCurrent = ownerId; + if (!ownerIdCurrent) { + ownerIdCurrent = isSystem || dto.type === BoardType.EntityType ? null : user?.id; + } + let taskBoardIdCurrent = taskBoardId; + if (!taskBoardIdCurrent && dto.type === BoardType.EntityType && dto.recordId) { + const taskBoard = await this.createLinkedTaskBoard({ + accountId, + user, + name: dto.name, + entityTypeId: dto.recordId, + }); + taskBoardIdCurrent = taskBoard?.id; + } + if (dto.sortOrder === undefined) { + const maxSortOrder = await this.createFindQb({ accountId, type: dto.type, recordId: dto.recordId }) + .select('MAX(board.sort_order)', 'max') + .orderBy() + .getRawOne<{ max: number }>(); + + dto.sortOrder = (maxSortOrder?.max ?? 0) + 1; + } + const board = await this.repository.save( + Board.fromDto({ accountId, dto, isSystem, ownerId: ownerIdCurrent, taskBoardId: taskBoardIdCurrent }), + ); + if (createDefaultStages) { + await this.createDefaultStages({ accountId, board }); + } + + if (user) { + board.userRights = this.getUserRights({ user, board }); + } + + this.eventEmitter.emit( + CrmEventType.BoardCreated, + new BoardEvent({ accountId, userId: ownerIdCurrent, boardId: board.id }), + ); + return board; + } + + public async findOne({ user = null, filter }: { user?: User | null; filter: FindFilter }): Promise { + const board = await this.createFindQb(filter).getOne(); + + if (board && user) { + board.userRights = this.getUserRights({ user, board }); + } + + return board; + } + public async findMany({ user = null, filter }: { user?: User | null; filter: FindFilter }): Promise { + const boards = await this.createFindQb(filter).getMany(); + + if (user) { + for (const board of boards) { + board.userRights = this.getUserRights({ user, board }); + } + + return boards.filter((board) => board.userRights.canView); + } + + return boards; + } + + public async findOneId(filter: FindFilter): Promise { + const result = await this.createFindQb(filter).select('board.id', 'id').getRawOne(); + + return result?.id ?? null; + } + public async findManyIds(filter: FindFilter): Promise { + const result = await this.createFindQb(filter).select('board.id', 'id').getRawMany(); + + return result.map((r) => r.id); + } + + public async count(filter: FindFilter): Promise { + return await this.createFindQb(filter).getCount(); + } + + public async getAllowedTaskBoardIds({ accountId, userId }: { accountId: number; userId: number }): Promise { + const ids = await this.repository + .createQueryBuilder('b') + .select('b.id', 'id') + .where('b.account_id = :accountId', { accountId }) + .andWhere('b.type = :boardType', { boardType: BoardType.Task }) + .andWhere( + new Brackets((qb) => + qb + .where('b.owner_id = :ownerId', { ownerId: userId }) + .orWhere(`b.participant_ids @> '[${userId}]'`) + .orWhere('b.is_system = true'), + ), + ) + .getRawMany(); + return ids.map((i) => i.id); + } + + public async update({ + accountId, + user, + boardId, + dto, + }: { + accountId: number; + user: User; + boardId: number; + dto: UpdateBoardDto; + }): Promise { + const board = await this.findOne({ user, filter: { accountId, boardId } }); + if (!board) { + throw NotFoundError.withId(Board, boardId); + } + + await this.repository.save(board.update(dto)); + + if (dto.name && board.type === BoardType.EntityType && board.taskBoardId !== null) { + this.update({ accountId, user, boardId: board.taskBoardId, dto: { name: dto.name } }); + } + + board.userRights = this.getUserRights({ user, board }); + + this.eventEmitter.emit( + CrmEventType.BoardUpdated, + new BoardEvent({ accountId, userId: user.id, boardId: board.id }), + ); + + return board; + } + + public async changeUser({ + accountId, + userId, + newUserId, + }: { + accountId: number; + userId: number; + newUserId?: number | null; + }) { + if (newUserId) { + await this.repository.update({ accountId, ownerId: userId }, { ownerId: newUserId }); + } else { + await this.repository.delete({ accountId, ownerId: userId }); + } + + const boards = await this.repository + .createQueryBuilder('board') + .where(`board.account_id = :accountId`, { accountId }) + .andWhere(`board.participant_ids @> :userId`, { userId }) + .getMany(); + for (const board of boards) { + board.participantIds = newUserId + ? board.participantIds.map((id) => (id === userId ? newUserId : id)).filter(isUnique) + : board.participantIds.filter((id) => id !== userId); + await this.repository.save(board); + } + } + + public async delete({ + accountId, + userId, + boardId, + preserveLast, + }: { + accountId: number; + userId: number; + boardId: number; + preserveLast?: boolean; + }) { + const board = await this.findOne({ filter: { accountId, boardId } }); + if (board) { + if (board.isSystem) { + throw new BadRequestError('Cannot delete system board'); + } + if (board.type === BoardType.EntityType) { + if (preserveLast && board.recordId) { + const count = await this.count({ accountId, type: BoardType.EntityType, recordId: board.recordId }); + if (count <= 1) { + throw new BadRequestError('Cannot delete last entity board'); + } + } + if (board.taskBoardId) { + await this.delete({ accountId, userId, boardId: board.taskBoardId }); + } + } + await this.stageService.deleteMany({ accountId: accountId, boardId: board.id }); + await this.repository.delete({ id: board.id }); + this.eventEmitter.emit( + CrmEventType.BoardDeleted, + new BoardEvent({ accountId: accountId, userId, boardId: board.id }), + ); + } + } + + public async deleteMany({ userId, filter }: { userId: number; filter: FindFilter }) { + const boardIds = await this.findManyIds(filter); + await Promise.all(boardIds.map((boardId) => this.delete({ accountId: filter.accountId, userId, boardId }))); + } + + private getUserRights({ user, board }: { user: User | null; board: Board }): UserRights { + if (user === null) { + return UserRights.none(); + } else if (board.type === BoardType.EntityType) { + return UserRights.full(); + } else if (board.type === BoardType.Task) { + const canView = + board.isSystem || + board.ownerId === user.id || + (board.participantIds !== null && + (board.participantIds.length === 0 || board.participantIds.includes(user.id))); + const canEdit = board.ownerId === user.id || user.isAdmin; + const canDelete = !board.isSystem && (board.ownerId === user.id || user.isAdmin); + return { canView, canEdit, canDelete }; + } + + return UserRights.none(); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('board') + .where('board.account_id = :accountId', { accountId: filter.accountId }) + .orderBy('board.sort_order', 'ASC'); + + if (filter?.boardId) { + qb.andWhere('board.id = :boardId', { boardId: filter.boardId }); + } + if (filter?.type) { + qb.andWhere('board.type = :type', { type: filter.type }); + } + if (filter?.recordId) { + qb.andWhere('board.record_id = :recordId', { recordId: filter.recordId }); + } + + if (filter?.isSystem !== undefined) { + qb.andWhere('board.is_system = :isSystem', { isSystem: filter.isSystem }); + } + + return qb; + } + + private async createLinkedTaskBoard({ + accountId, + user, + name, + entityTypeId, + }: { + accountId: number; + user: User; + name: string; + entityTypeId: number; + }): Promise { + const entityType = await this.entityTypeService.getById(accountId, entityTypeId); + + return entityType.entityCategory === EntityCategory.PROJECT + ? this.create({ accountId, user, dto: { name, type: BoardType.Task, recordId: null, sortOrder: 1 } }) + : null; + } + + private async createDefaultStages({ accountId, board }: { accountId: number; board: Board }) { + if (board.type === BoardType.Task) { + return this.createTasksStages({ accountId, boardId: board.id }); + } else if (board.type === BoardType.EntityType && board.recordId) { + const entityType = await this.entityTypeService.getById(accountId, board.recordId); + + switch (entityType.entityCategory) { + case EntityCategory.DEAL: + case EntityCategory.CUSTOMER: + return this.createDealsStages({ accountId, boardId: board.id }); + case EntityCategory.PROJECT: + return this.createProjectsStages({ accountId, boardId: board.id }); + case EntityCategory.SUPPLIER: + return this.createSuppliersStages({ accountId, boardId: board.id }); + case EntityCategory.CONTRACTOR: + return this.createContractorsStages({ accountId, boardId: board.id }); + case EntityCategory.HR: + return this.createApplicantsStages({ accountId, boardId: board.id }); + default: + return this.createEntityTypeStages({ accountId, boardId: board.id }); + } + } else { + return null; + } + } + private async createTasksStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'To Do', color: '#555', sortOrder: 0, code: null, isSystem: false }, + { name: 'In Progress', color: '#555', sortOrder: 1, code: null, isSystem: false }, + { name: 'Done', color: '#555', sortOrder: 2, code: BoardStageCode.Done, isSystem: true }, + ], + }); + } + private async createDealsStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'Qualified Lead', color: 'rgb(168, 227, 121)', sortOrder: 0, isSystem: false, code: null }, + { name: 'Contact Made', color: 'rgb(255, 227, 133)', sortOrder: 1, isSystem: false, code: null }, + { name: 'Proposal Made', color: 'rgb(255, 158, 158)', sortOrder: 2, isSystem: false, code: null }, + { name: 'Objections Identified', color: 'rgb(202, 183, 239)', sortOrder: 3, isSystem: false, code: null }, + { name: 'Invoice Sent', color: 'rgb(152, 215, 255)', sortOrder: 4, isSystem: false, code: null }, + { name: 'Closed Won', color: '#50b810', sortOrder: 5, isSystem: true, code: BoardStageCode.Win }, + { name: 'Closed Lost', color: '#ff5a71', sortOrder: 6, isSystem: true, code: BoardStageCode.Lost }, + ], + }); + } + private async createProjectsStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'To Do', color: 'rgb(255, 227, 133)', sortOrder: 0, isSystem: false, code: null }, + { name: 'In progress', color: 'rgb(202, 183, 239)', sortOrder: 1, isSystem: false, code: null }, + { name: 'Completed', color: '#50b810', sortOrder: 2, isSystem: true, code: BoardStageCode.Completed }, + ], + }); + } + private async createSuppliersStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'To Do', color: 'rgb(255, 227, 133)', sortOrder: 0, isSystem: false, code: null }, + { name: 'Request Supplier', color: 'rgb(202, 183, 239)', sortOrder: 1, isSystem: false, code: null }, + { name: 'Negotiate', color: 'rgb(168, 227, 121)', sortOrder: 2, isSystem: false, code: null }, + { name: 'Paid', color: 'rgb(152, 215, 255)', sortOrder: 3, isSystem: false, code: null }, + { name: 'Completed', color: '#50b810', sortOrder: 4, isSystem: true, code: BoardStageCode.Completed }, + { name: 'Not satisfied', color: '#ff5a71', sortOrder: 5, isSystem: true, code: BoardStageCode.NotSatisfied }, + ], + }); + } + private async createContractorsStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'To Do', color: 'rgb(255, 227, 133)', sortOrder: 0, isSystem: false, code: null }, + { name: 'Request Contractor', color: 'rgb(202, 183, 239)', sortOrder: 1, isSystem: false, code: null }, + { name: 'Negotiate', color: 'rgb(168, 227, 121)', sortOrder: 2, isSystem: false, code: null }, + { name: 'Paid', color: 'rgb(152, 215, 255)', sortOrder: 3, isSystem: false, code: null }, + { name: 'Completed', color: '#50b810', sortOrder: 4, isSystem: true, code: BoardStageCode.Completed }, + { name: 'Not satisfied', color: '#ff5a71', sortOrder: 5, isSystem: true, code: BoardStageCode.NotSatisfied }, + ], + }); + } + private async createApplicantsStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'Interview', color: 'rgb(255, 227, 133)', sortOrder: 0, isSystem: false, code: null }, + { name: 'Approved', color: 'rgb(202, 183, 239)', sortOrder: 1, isSystem: false, code: null }, + { name: 'Hired', color: '#50b810', sortOrder: 2, isSystem: true, code: BoardStageCode.Hired }, + { name: 'Rejected', color: '#ff5a71', sortOrder: 3, isSystem: true, code: BoardStageCode.Rejected }, + ], + }); + } + public async createEntityTypeStages({ accountId, boardId }: { accountId: number; boardId: number }) { + return this.stageService.createMany({ + accountId, + boardId, + dtos: [ + { name: 'Name your status', color: '#A8E379', sortOrder: 0, isSystem: false, code: null }, + { name: 'Name your status', color: '#FFE385', sortOrder: 1, isSystem: false, code: null }, + { name: 'Name your status', color: '#CAB7EF', sortOrder: 2, isSystem: false, code: null }, + { name: 'Done', color: '#50b810', sortOrder: 3, isSystem: true, code: BoardStageCode.Done }, + { name: 'Not done', color: '#ff5a71', sortOrder: 3, isSystem: true, code: BoardStageCode.NotDone }, + ], + }); + } +} diff --git a/backend/src/CRM/board/dto/board-filter.dto.ts b/backend/src/CRM/board/dto/board-filter.dto.ts new file mode 100644 index 0000000..18d8891 --- /dev/null +++ b/backend/src/CRM/board/dto/board-filter.dto.ts @@ -0,0 +1,16 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { BoardType } from '../enums'; + +export class BoardFilterDto { + @ApiPropertyOptional({ enum: BoardType, description: 'Board type' }) + @IsOptional() + @IsEnum(BoardType) + type?: BoardType; + + @ApiPropertyOptional({ description: 'Object ID associated with board' }) + @IsOptional() + @IsNumber() + recordId?: number; +} diff --git a/backend/src/CRM/board/dto/board.dto.ts b/backend/src/CRM/board/dto/board.dto.ts new file mode 100644 index 0000000..8db3e7c --- /dev/null +++ b/backend/src/CRM/board/dto/board.dto.ts @@ -0,0 +1,52 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common'; + +import { BoardType } from '../enums'; + +export class BoardDto { + @ApiProperty({ description: 'Board ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Board name' }) + @IsString() + name: string; + + @ApiProperty({ enum: BoardType, description: 'Board type' }) + @IsEnum(BoardType) + type: BoardType; + + @ApiProperty({ nullable: true, description: 'Object ID associated with board' }) + @IsOptional() + @IsNumber() + recordId: number | null; + + @ApiProperty({ description: 'Indicates if the board is a system board' }) + @IsBoolean() + isSystem: boolean; + + @ApiProperty({ nullable: true, description: 'Owner ID of the board' }) + @IsOptional() + @IsNumber() + ownerId: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Array of participant IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + participantIds?: number[] | null; + + @ApiProperty({ description: 'Sort order of the board' }) + @IsNumber() + sortOrder: number; + + @ApiPropertyOptional({ nullable: true, description: 'Task board ID associated with this board' }) + @IsOptional() + @IsNumber() + taskBoardId: number | null; + + @ApiProperty({ type: () => UserRights, nullable: true, description: 'User rights for the board' }) + @IsOptional() + userRights: UserRights | null; +} diff --git a/backend/src/CRM/board/dto/create-board.dto.ts b/backend/src/CRM/board/dto/create-board.dto.ts new file mode 100644 index 0000000..84b8c2b --- /dev/null +++ b/backend/src/CRM/board/dto/create-board.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { BoardDto } from './board.dto'; + +export class CreateBoardDto extends PickType(BoardDto, ['name', 'type', 'recordId', 'participantIds'] as const) { + @ApiPropertyOptional({ description: 'Sort order' }) + @IsOptional() + @IsNumber() + sortOrder?: number; +} diff --git a/backend/src/CRM/board/dto/index.ts b/backend/src/CRM/board/dto/index.ts new file mode 100644 index 0000000..7008ced --- /dev/null +++ b/backend/src/CRM/board/dto/index.ts @@ -0,0 +1,4 @@ +export * from './board-filter.dto'; +export * from './board.dto'; +export * from './create-board.dto'; +export * from './update-board.dto'; diff --git a/backend/src/CRM/board/dto/update-board.dto.ts b/backend/src/CRM/board/dto/update-board.dto.ts new file mode 100644 index 0000000..90249df --- /dev/null +++ b/backend/src/CRM/board/dto/update-board.dto.ts @@ -0,0 +1,5 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { BoardDto } from './board.dto'; + +export class UpdateBoardDto extends PartialType(PickType(BoardDto, ['name', 'sortOrder', 'participantIds'] as const)) {} diff --git a/backend/src/CRM/board/entities/board.entity.ts b/backend/src/CRM/board/entities/board.entity.ts new file mode 100644 index 0000000..247db9c --- /dev/null +++ b/backend/src/CRM/board/entities/board.entity.ts @@ -0,0 +1,123 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { UserRights } from '@/modules/iam/common'; + +import { BoardDto, CreateBoardDto, UpdateBoardDto } from '../dto'; +import { BoardType } from '../enums'; + +@Entity() +export class Board { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + type: BoardType; + + @Column({ nullable: true }) + recordId: number | null; + + @Column() + isSystem: boolean; + + @Column() + ownerId: number | null; + + @Column({ type: 'jsonb' }) + participantIds: number[] | null; + + @Column() + sortOrder: number; + + @Column() + taskBoardId: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + name: string, + type: BoardType, + recordId: number | null, + isSystem: boolean, + ownerId: number | null, + participantIds: number[] | null, + sortOrder: number, + taskBoardId: number | null, + ) { + this.accountId = accountId; + this.name = name; + this.type = type; + this.recordId = recordId; + this.isSystem = isSystem; + this.ownerId = ownerId; + this.participantIds = participantIds; + this.sortOrder = sortOrder; + this.taskBoardId = taskBoardId; + this.createdAt = DateUtil.now(); + } + + private _userRights: UserRights | null; + public get userRights(): UserRights { + return this._userRights; + } + public set userRights(value: UserRights | null) { + this._userRights = value; + } + + public static fromDto({ + accountId, + isSystem = false, + ownerId = null, + taskBoardId = null, + dto, + }: { + accountId: number; + isSystem?: boolean; + ownerId?: number | null; + taskBoardId?: number | null; + dto: CreateBoardDto; + }): Board { + return new Board( + accountId, + dto.name, + dto.type, + dto.recordId, + isSystem, + ownerId, + dto.participantIds, + dto.sortOrder, + taskBoardId, + ); + } + + public update(dto: UpdateBoardDto): Board { + this.name = dto.name !== undefined ? dto.name : this.name; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + this.participantIds = dto.participantIds !== undefined ? dto.participantIds : this.participantIds; + + return this; + } + + public toDto(): BoardDto { + return { + id: this.id, + name: this.name, + type: this.type, + recordId: this.recordId, + isSystem: this.isSystem, + ownerId: this.ownerId, + participantIds: this.participantIds, + sortOrder: this.sortOrder, + taskBoardId: this.taskBoardId, + userRights: this.userRights, + }; + } +} diff --git a/backend/src/CRM/board/entities/index.ts b/backend/src/CRM/board/entities/index.ts new file mode 100644 index 0000000..73f4c10 --- /dev/null +++ b/backend/src/CRM/board/entities/index.ts @@ -0,0 +1 @@ +export * from './board.entity'; diff --git a/backend/src/CRM/board/enums/board-type.enum.ts b/backend/src/CRM/board/enums/board-type.enum.ts new file mode 100644 index 0000000..d147054 --- /dev/null +++ b/backend/src/CRM/board/enums/board-type.enum.ts @@ -0,0 +1,4 @@ +export enum BoardType { + EntityType = 'entity_type', + Task = 'task', +} diff --git a/backend/src/CRM/board/enums/index.ts b/backend/src/CRM/board/enums/index.ts new file mode 100644 index 0000000..4adfb14 --- /dev/null +++ b/backend/src/CRM/board/enums/index.ts @@ -0,0 +1 @@ +export * from './board-type.enum'; diff --git a/backend/src/CRM/board/index.ts b/backend/src/CRM/board/index.ts new file mode 100644 index 0000000..f0e6538 --- /dev/null +++ b/backend/src/CRM/board/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; diff --git a/backend/src/CRM/common/enums/entity-category.enum.ts b/backend/src/CRM/common/enums/entity-category.enum.ts new file mode 100644 index 0000000..242226a --- /dev/null +++ b/backend/src/CRM/common/enums/entity-category.enum.ts @@ -0,0 +1,15 @@ +export enum EntityCategory { + UNIVERSAL = 'universal', + PARTNER = 'partner', + CUSTOMER = 'customer', + LOGISTICS = 'logistics', + PRODUCT = 'product', + REAL_ESTATE_PROPERTY = 'real_estate_property', + DEAL = 'deal', + CONTACT = 'contact', + COMPANY = 'company', + SUPPLIER = 'supplier', + CONTRACTOR = 'contractor', + HR = 'hr', + PROJECT = 'project', +} diff --git a/backend/src/CRM/common/enums/index.ts b/backend/src/CRM/common/enums/index.ts new file mode 100644 index 0000000..d818964 --- /dev/null +++ b/backend/src/CRM/common/enums/index.ts @@ -0,0 +1,4 @@ +export * from './entity-category.enum'; +export * from './permission-object-type.enum'; +export * from './sequence-name.enum'; +export * from './sort-order.enum'; diff --git a/backend/src/CRM/common/enums/permission-object-type.enum.ts b/backend/src/CRM/common/enums/permission-object-type.enum.ts new file mode 100644 index 0000000..50a0f39 --- /dev/null +++ b/backend/src/CRM/common/enums/permission-object-type.enum.ts @@ -0,0 +1,5 @@ +export enum PermissionObjectType { + EntityType = 'entity_type', + Task = 'task', + Activity = 'activity', +} diff --git a/backend/src/CRM/common/enums/sequence-name.enum.ts b/backend/src/CRM/common/enums/sequence-name.enum.ts new file mode 100644 index 0000000..81b8e38 --- /dev/null +++ b/backend/src/CRM/common/enums/sequence-name.enum.ts @@ -0,0 +1,9 @@ +/** + * @deprecated + */ +export enum SequenceName { + Field = 'field_id_seq', + FieldGroup = 'field_group_id_seq', + FieldOption = 'field_option_id_seq', + Stage = 'stage_id_seq', +} diff --git a/backend/src/CRM/common/enums/sort-order.enum.ts b/backend/src/CRM/common/enums/sort-order.enum.ts new file mode 100644 index 0000000..5881975 --- /dev/null +++ b/backend/src/CRM/common/enums/sort-order.enum.ts @@ -0,0 +1,4 @@ +export enum SortOrder { + First = 0, + Last = 32767, +} diff --git a/backend/src/CRM/common/events/activity/activity-created.event.ts b/backend/src/CRM/common/events/activity/activity-created.event.ts new file mode 100644 index 0000000..7943fbc --- /dev/null +++ b/backend/src/CRM/common/events/activity/activity-created.event.ts @@ -0,0 +1,28 @@ +import { ActivityEvent } from './activity.event'; + +export class ActivityCreatedEvent extends ActivityEvent { + ownerId: number; + createdBy: number; + activityText: string; + activityTypeName: string; + createdAt: string; + + constructor({ + accountId, + activityId, + ownerId, + entityId, + createdBy, + activityText, + activityTypeName, + createdAt, + }: ActivityCreatedEvent) { + super({ accountId, entityId, activityId }); + + this.ownerId = ownerId; + this.createdBy = createdBy; + this.activityText = activityText; + this.activityTypeName = activityTypeName; + this.createdAt = createdAt; + } +} diff --git a/backend/src/CRM/common/events/activity/activity.event.ts b/backend/src/CRM/common/events/activity/activity.event.ts new file mode 100644 index 0000000..f63c087 --- /dev/null +++ b/backend/src/CRM/common/events/activity/activity.event.ts @@ -0,0 +1,11 @@ +export class ActivityEvent { + accountId: number; + entityId: number; + activityId: number; + + constructor({ accountId, entityId, activityId }: ActivityEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.activityId = activityId; + } +} diff --git a/backend/src/CRM/common/events/activity/index.ts b/backend/src/CRM/common/events/activity/index.ts new file mode 100644 index 0000000..d94999c --- /dev/null +++ b/backend/src/CRM/common/events/activity/index.ts @@ -0,0 +1,2 @@ +export * from './activity-created.event'; +export * from './activity.event'; diff --git a/backend/src/CRM/common/events/board-stage/board-stage-deleted.event.ts b/backend/src/CRM/common/events/board-stage/board-stage-deleted.event.ts new file mode 100644 index 0000000..c8e09f2 --- /dev/null +++ b/backend/src/CRM/common/events/board-stage/board-stage-deleted.event.ts @@ -0,0 +1,11 @@ +import { BoardStageEvent } from './board-stage.event'; + +export class BoardStageDeletedEvent extends BoardStageEvent { + newStageId?: number | null; + + constructor({ accountId, boardId, stageId, newStageId }: BoardStageDeletedEvent) { + super({ accountId, boardId, stageId }); + + this.newStageId = newStageId; + } +} diff --git a/backend/src/CRM/common/events/board-stage/board-stage.event.ts b/backend/src/CRM/common/events/board-stage/board-stage.event.ts new file mode 100644 index 0000000..1923e83 --- /dev/null +++ b/backend/src/CRM/common/events/board-stage/board-stage.event.ts @@ -0,0 +1,11 @@ +export class BoardStageEvent { + accountId: number; + boardId: number; + stageId: number; + + constructor({ accountId, boardId, stageId }: BoardStageEvent) { + this.accountId = accountId; + this.boardId = boardId; + this.stageId = stageId; + } +} diff --git a/backend/src/CRM/common/events/board-stage/index.ts b/backend/src/CRM/common/events/board-stage/index.ts new file mode 100644 index 0000000..8bf42c9 --- /dev/null +++ b/backend/src/CRM/common/events/board-stage/index.ts @@ -0,0 +1,2 @@ +export * from './board-stage-deleted.event'; +export * from './board-stage.event'; diff --git a/backend/src/CRM/common/events/board/board.event.ts b/backend/src/CRM/common/events/board/board.event.ts new file mode 100644 index 0000000..5edea62 --- /dev/null +++ b/backend/src/CRM/common/events/board/board.event.ts @@ -0,0 +1,11 @@ +export class BoardEvent { + accountId: number; + userId: number; + boardId: number; + + constructor({ accountId, userId, boardId }: BoardEvent) { + this.accountId = accountId; + this.userId = userId; + this.boardId = boardId; + } +} diff --git a/backend/src/CRM/common/events/board/index.ts b/backend/src/CRM/common/events/board/index.ts new file mode 100644 index 0000000..e03a21e --- /dev/null +++ b/backend/src/CRM/common/events/board/index.ts @@ -0,0 +1 @@ +export * from './board.event'; diff --git a/backend/src/CRM/common/events/crm-event-type.enum.ts b/backend/src/CRM/common/events/crm-event-type.enum.ts new file mode 100644 index 0000000..d047e7a --- /dev/null +++ b/backend/src/CRM/common/events/crm-event-type.enum.ts @@ -0,0 +1,29 @@ +export enum CrmEventType { + ActivityCreated = 'activity:created', + ActivityUpdated = 'activity:updated', + ActivityDeleted = 'activity:deleted', + BoardCreated = 'board:created', + BoardUpdated = 'board:updated', + BoardDeleted = 'board:deleted', + BoardStageCreated = 'board:stage:created', + BoardStageUpdated = 'board:stage:updated', + BoardStageDeleted = 'board:stage:deleted', + EntityCreated = 'entity:created', + EntityUpdated = 'entity:updated', + EntityOwnerChanged = 'entity:owner_changed', + EntityStageChanged = 'entity:stage_changed', + EntityImportCompleted = 'entity:import_completed', + EntityDeleted = 'entity:deleted', + EntityPriceUpdate = 'entity:price:update', + EntityTypeDeleted = 'entity_type:deleted', + FileLinkCreated = 'file_link:created', + FileLinkDeleted = 'file_link:deleted', + NoteCreated = 'note:created', + NoteDeleted = 'note:deleted', + TaskCreated = 'task:created', + TaskDeleted = 'task:deleted', + TaskDeleteExt = 'task:delete-ext', + TaskUpdated = 'task:updated', + TaskUpsertExt = 'task:upsert-ext', + TaskCommentCreated = 'task:comment:created', +} diff --git a/backend/src/CRM/common/events/entity-type/entity-type.event.ts b/backend/src/CRM/common/events/entity-type/entity-type.event.ts new file mode 100644 index 0000000..4bcf148 --- /dev/null +++ b/backend/src/CRM/common/events/entity-type/entity-type.event.ts @@ -0,0 +1,11 @@ +export class EntityTypeEvent { + accountId: number; + userId: number; + entityTypeId: number; + + constructor({ accountId, userId, entityTypeId }: EntityTypeEvent) { + this.accountId = accountId; + this.userId = userId; + this.entityTypeId = entityTypeId; + } +} diff --git a/backend/src/CRM/common/events/entity-type/index.ts b/backend/src/CRM/common/events/entity-type/index.ts new file mode 100644 index 0000000..ae61275 --- /dev/null +++ b/backend/src/CRM/common/events/entity-type/index.ts @@ -0,0 +1 @@ +export * from './entity-type.event'; diff --git a/backend/src/CRM/common/events/entity/entity-created.event.ts b/backend/src/CRM/common/events/entity/entity-created.event.ts new file mode 100644 index 0000000..1cd6757 --- /dev/null +++ b/backend/src/CRM/common/events/entity/entity-created.event.ts @@ -0,0 +1,29 @@ +import { UserNotification } from '@/common/enums'; + +import { EntityEvent } from './entity.event'; + +export class EntityCreatedEvent extends EntityEvent { + entityName: string; + createdBy: number; + copiedFrom?: number | null; + + constructor({ + accountId, + entityId, + entityName, + boardId, + stageId, + createdBy, + ownerId, + entityTypeId, + userNotification = UserNotification.Default, + copiedFrom = undefined, + }: EntityCreatedEvent) { + super({ accountId, entityId, entityTypeId, boardId, stageId, ownerId, userNotification }); + + this.entityName = entityName; + this.stageId = stageId; + this.createdBy = createdBy; + this.copiedFrom = copiedFrom; + } +} diff --git a/backend/src/CRM/common/events/entity/entity-import.event.ts b/backend/src/CRM/common/events/entity/entity-import.event.ts new file mode 100644 index 0000000..ee52ae7 --- /dev/null +++ b/backend/src/CRM/common/events/entity/entity-import.event.ts @@ -0,0 +1,17 @@ +export class EntityImportEvent { + accountId: number; + userId: number; + fileName: string; + entityTypeId: number; + entityTypeName: string | null; + totalCount: number; + + constructor({ accountId, userId, fileName, entityTypeId, entityTypeName, totalCount }: EntityImportEvent) { + this.accountId = accountId; + this.userId = userId; + this.fileName = fileName; + this.entityTypeId = entityTypeId; + this.entityTypeName = entityTypeName; + this.totalCount = totalCount; + } +} diff --git a/backend/src/CRM/common/events/entity/entity-owner-changed.event.ts b/backend/src/CRM/common/events/entity/entity-owner-changed.event.ts new file mode 100644 index 0000000..0645c84 --- /dev/null +++ b/backend/src/CRM/common/events/entity/entity-owner-changed.event.ts @@ -0,0 +1,25 @@ +import { UserNotification } from '@/common/enums'; +import { EntityEvent } from './entity.event'; + +export class EntityOwnerChangedEvent extends EntityEvent { + entityName: string; + changedBy: number; + + constructor({ + accountId, + entityId, + entityName, + boardId, + stageId, + changedBy, + ownerId, + entityTypeId, + userNotification = UserNotification.Default, + }: EntityOwnerChangedEvent) { + super({ accountId, entityId, entityTypeId, boardId, stageId, ownerId, userNotification }); + + this.entityName = entityName; + this.changedBy = changedBy; + this.ownerId = ownerId; + } +} diff --git a/backend/src/CRM/common/events/entity/entity-price.update.event.ts b/backend/src/CRM/common/events/entity/entity-price.update.event.ts new file mode 100644 index 0000000..00c48aa --- /dev/null +++ b/backend/src/CRM/common/events/entity/entity-price.update.event.ts @@ -0,0 +1,11 @@ +export class EntityPriceUpdateEvent { + accountId: number; + entityId: number; + price: number; + + constructor({ accountId, entityId, price }: EntityPriceUpdateEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.price = price; + } +} diff --git a/backend/src/CRM/common/events/entity/entity.event.ts b/backend/src/CRM/common/events/entity/entity.event.ts new file mode 100644 index 0000000..10e31d0 --- /dev/null +++ b/backend/src/CRM/common/events/entity/entity.event.ts @@ -0,0 +1,29 @@ +import { UserNotification } from '@/common/enums'; + +export class EntityEvent { + accountId: number; + entityId: number; + entityTypeId: number; + boardId: number | null; + stageId: number | null; + ownerId: number; + userNotification: UserNotification; + + constructor({ + accountId, + entityId, + entityTypeId, + boardId, + stageId, + ownerId, + userNotification = UserNotification.Default, + }: EntityEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.entityTypeId = entityTypeId; + this.boardId = boardId; + this.stageId = stageId; + this.ownerId = ownerId; + this.userNotification = userNotification; + } +} diff --git a/backend/src/CRM/common/events/entity/index.ts b/backend/src/CRM/common/events/entity/index.ts new file mode 100644 index 0000000..ccb9847 --- /dev/null +++ b/backend/src/CRM/common/events/entity/index.ts @@ -0,0 +1,5 @@ +export * from './entity-created.event'; +export * from './entity-import.event'; +export * from './entity-owner-changed.event'; +export * from './entity-price.update.event'; +export * from './entity.event'; diff --git a/backend/src/CRM/common/events/file-link/file-link-created.event.ts b/backend/src/CRM/common/events/file-link/file-link-created.event.ts new file mode 100644 index 0000000..5c15ac7 --- /dev/null +++ b/backend/src/CRM/common/events/file-link/file-link-created.event.ts @@ -0,0 +1,11 @@ +import { FileLinkEvent } from './file-link.event'; + +export class FileLinkCreatedEvent extends FileLinkEvent { + createdAt: string; + + constructor({ accountId, sourceType, sourceId, fileLinkId, createdAt }: FileLinkCreatedEvent) { + super({ accountId, sourceType, sourceId, fileLinkId }); + + this.createdAt = createdAt; + } +} diff --git a/backend/src/CRM/common/events/file-link/file-link.event.ts b/backend/src/CRM/common/events/file-link/file-link.event.ts new file mode 100644 index 0000000..725604e --- /dev/null +++ b/backend/src/CRM/common/events/file-link/file-link.event.ts @@ -0,0 +1,15 @@ +import { type FileLinkSource } from '@/common/enums'; + +export class FileLinkEvent { + accountId: number; + sourceType: FileLinkSource; + sourceId: number | null; + fileLinkId: number; + + constructor({ accountId, sourceType, sourceId, fileLinkId }: FileLinkEvent) { + this.accountId = accountId; + this.sourceType = sourceType; + this.sourceId = sourceId; + this.fileLinkId = fileLinkId; + } +} diff --git a/backend/src/CRM/common/events/file-link/index.ts b/backend/src/CRM/common/events/file-link/index.ts new file mode 100644 index 0000000..1777198 --- /dev/null +++ b/backend/src/CRM/common/events/file-link/index.ts @@ -0,0 +1,2 @@ +export * from './file-link-created.event'; +export * from './file-link.event'; diff --git a/backend/src/CRM/common/events/index.ts b/backend/src/CRM/common/events/index.ts new file mode 100644 index 0000000..50d2e63 --- /dev/null +++ b/backend/src/CRM/common/events/index.ts @@ -0,0 +1,9 @@ +export * from './activity'; +export * from './board'; +export * from './board-stage'; +export * from './crm-event-type.enum'; +export * from './entity'; +export * from './entity-type'; +export * from './file-link'; +export * from './note'; +export * from './task'; diff --git a/backend/src/CRM/common/events/note/index.ts b/backend/src/CRM/common/events/note/index.ts new file mode 100644 index 0000000..1fd88a5 --- /dev/null +++ b/backend/src/CRM/common/events/note/index.ts @@ -0,0 +1,2 @@ +export * from './note-created.event'; +export * from './note.event'; diff --git a/backend/src/CRM/common/events/note/note-created.event.ts b/backend/src/CRM/common/events/note/note-created.event.ts new file mode 100644 index 0000000..972f530 --- /dev/null +++ b/backend/src/CRM/common/events/note/note-created.event.ts @@ -0,0 +1,18 @@ +import { NoteEvent } from './note.event'; + +export class NoteCreatedEvent extends NoteEvent { + entityName: string; + createdBy: number; + ownerId: number; + noteText: string; + createdAt: string; + + constructor({ accountId, entityId, entityName, createdBy, ownerId, noteId, noteText, createdAt }: NoteCreatedEvent) { + super({ accountId, entityId, noteId }); + this.entityName = entityName; + this.createdBy = createdBy; + this.ownerId = ownerId; + this.noteText = noteText; + this.createdAt = createdAt; + } +} diff --git a/backend/src/CRM/common/events/note/note.event.ts b/backend/src/CRM/common/events/note/note.event.ts new file mode 100644 index 0000000..b70387f --- /dev/null +++ b/backend/src/CRM/common/events/note/note.event.ts @@ -0,0 +1,11 @@ +export class NoteEvent { + accountId: number; + entityId: number | null; + noteId: number; + + constructor({ accountId, entityId, noteId }: NoteEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.noteId = noteId; + } +} diff --git a/backend/src/CRM/common/events/task/index.ts b/backend/src/CRM/common/events/task/index.ts new file mode 100644 index 0000000..18f0e23 --- /dev/null +++ b/backend/src/CRM/common/events/task/index.ts @@ -0,0 +1,6 @@ +export * from './task-comment-created.event'; +export * from './task-created.event'; +export * from './task-ext-upsert.event'; +export * from './task-ext.event'; +export * from './task-updated.event'; +export * from './task.event'; diff --git a/backend/src/CRM/common/events/task/task-comment-created.event.ts b/backend/src/CRM/common/events/task/task-comment-created.event.ts new file mode 100644 index 0000000..18fbe31 --- /dev/null +++ b/backend/src/CRM/common/events/task/task-comment-created.event.ts @@ -0,0 +1,11 @@ +import { TaskCreatedEvent } from './task-created.event'; + +export class TaskCommentCreatedEvent extends TaskCreatedEvent { + taskComment: string; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.taskComment = data.taskComment; + } +} diff --git a/backend/src/CRM/common/events/task/task-created.event.ts b/backend/src/CRM/common/events/task/task-created.event.ts new file mode 100644 index 0000000..1518f3e --- /dev/null +++ b/backend/src/CRM/common/events/task/task-created.event.ts @@ -0,0 +1,23 @@ +import { TaskEvent } from './task.event'; + +export class TaskCreatedEvent extends TaskEvent { + ownerId: number; + createdBy: number; + taskTitle: string; + taskText: string; + createdAt: Date; + startDate: Date | null; + endDate: Date | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.ownerId = data.ownerId; + this.createdBy = data.createdBy; + this.taskTitle = data.taskTitle; + this.taskText = data.taskText; + this.createdAt = data.createdAt; + this.startDate = data.startDate; + this.endDate = data.endDate; + } +} diff --git a/backend/src/CRM/common/events/task/task-ext-upsert.event.ts b/backend/src/CRM/common/events/task/task-ext-upsert.event.ts new file mode 100644 index 0000000..f536b5a --- /dev/null +++ b/backend/src/CRM/common/events/task/task-ext-upsert.event.ts @@ -0,0 +1,19 @@ +import { TaskExtEvent } from './task-ext.event'; + +export class TaskExtUpsertEvent extends TaskExtEvent { + ownerId: number; + title: string; + text?: string | null; + startDate?: Date | null; + endDate?: Date | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.ownerId = data.ownerId; + this.title = data.title; + this.text = data.text; + this.startDate = data.startDate; + this.endDate = data.endDate; + } +} diff --git a/backend/src/CRM/common/events/task/task-ext.event.ts b/backend/src/CRM/common/events/task/task-ext.event.ts new file mode 100644 index 0000000..c307fbb --- /dev/null +++ b/backend/src/CRM/common/events/task/task-ext.event.ts @@ -0,0 +1,17 @@ +import { ServiceEvent } from '@/common'; + +export class TaskExtEvent extends ServiceEvent { + externalId?: string | null; + accountId: number; + boardId: number; + taskId?: number | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.externalId = data.externalId; + this.accountId = data.accountId; + this.boardId = data.boardId; + this.taskId = data.taskId; + } +} diff --git a/backend/src/CRM/common/events/task/task-updated.event.ts b/backend/src/CRM/common/events/task/task-updated.event.ts new file mode 100644 index 0000000..0cc3d50 --- /dev/null +++ b/backend/src/CRM/common/events/task/task-updated.event.ts @@ -0,0 +1,11 @@ +import { TaskCreatedEvent } from './task-created.event'; + +export class TaskUpdatedEvent extends TaskCreatedEvent { + prevEntityId: number | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.prevEntityId = data.prevEntityId; + } +} diff --git a/backend/src/CRM/common/events/task/task.event.ts b/backend/src/CRM/common/events/task/task.event.ts new file mode 100644 index 0000000..387d52f --- /dev/null +++ b/backend/src/CRM/common/events/task/task.event.ts @@ -0,0 +1,19 @@ +import { ServiceEvent } from '@/common'; + +export class TaskEvent extends ServiceEvent { + accountId: number; + taskId: number; + boardId: number; + entityId?: number | null; + externalId?: string | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.accountId = data.accountId; + this.taskId = data.taskId; + this.boardId = data.boardId; + this.entityId = data.entityId; + this.externalId = data.externalId; + } +} diff --git a/backend/src/CRM/common/index.ts b/backend/src/CRM/common/index.ts new file mode 100644 index 0000000..df1eda9 --- /dev/null +++ b/backend/src/CRM/common/index.ts @@ -0,0 +1,2 @@ +export * from './enums'; +export * from './events'; diff --git a/backend/src/CRM/crm.module.ts b/backend/src/CRM/crm.module.ts new file mode 100644 index 0000000..b961db1 --- /dev/null +++ b/backend/src/CRM/crm.module.ts @@ -0,0 +1,216 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; +import { InventoryModule } from '@/modules/inventory/inventory.module'; +import { SchedulerModule } from '@/modules/scheduler/scheduler.module'; +import { TelephonyModule } from '@/modules/telephony/telephony.module'; +import { MultichatModule } from '@/modules/multichat/multichat.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { EntityFieldModule } from '@/modules/entity/entity-field/entity-field.module'; + +import { MailingModule } from '@/Mailing/MailingModule'; + +import { CrmReportingModule } from './reporting/crm-reporting.module'; +import { SalesPlanModule } from './sales-plan/sales-plan.module'; +import { TaskSettingsModule } from './task-settings/task-settings.module'; +import { SalesforceModule } from './Salesforce/SalesforceModule'; + +/// Entity +import { Entity } from './Model/Entity/Entity'; +import { EntityService } from './Service/Entity/EntityService'; +import { EntityServiceEmitter } from './Service/Entity/entity-service.emitter'; +import { EntityServiceHandler } from './Service/Entity/entity-service.handler'; +import { EntityBoardService } from './Service/Entity/EntityBoardService'; +import { CreateEntityController } from './Controller/Entity/CreateEntityController'; +import { CreateSimpleEntityController } from './Controller/Entity/CreateSimpleEntityController'; +import { UpdateEntityController } from './Controller/Entity/UpdateEntityController'; +import { UpdateEntityFieldController } from './Controller/Entity/UpdateEntityFieldController'; +import { GetEntityController } from './Controller/Entity/GetEntityController'; +import { GetEntityFilesController } from './Controller/Entity/Files/GetEntityFilesController'; +import { AddEntityFilesController } from './Controller/Entity/Files/AddEntityFilesController'; +import { GetTimeBoardController } from './Controller/TimeBoard/GetTimeBoardController'; +import { GetTimeBoardMetaController } from './Controller/TimeBoard/GetTimeBoardMetaController'; +import { GetTimeBoardCalendarController } from './Controller/TimeBoard/GetTimeBoardCalendarController'; +import { GetTimeBoardCalendarMetaController } from './Controller/TimeBoard/GetTimeBoardCalendarMetaController'; +import { GetTimeBoardItemController } from './Controller/TimeBoard/GetTimeBoardItemController'; +import { TimeBoardService } from './Service/TimeBoard/TimeBoardService'; +import { DeleteEntityController } from './Controller/Entity/DeleteEntityController'; +import { ExternalEntity } from './Model/ExternalEntity/ExternalEntity'; +import { ExternalEntityService } from './Service/ExternalEntity/ExternalEntityService'; +import { CreateExternalLinkController } from './Controller/ExternalEntity/CreateExternalLinkController'; +import { ExternalSystem } from './Model/ExternalEntity/ExternalSystem'; +import { ExternalSystemService } from './Service/ExternalEntity/ExternalSystemService'; +import { FileLink } from './Model/FileLink/FileLink'; +import { FileLinkService } from './Service/FileLink/FileLinkService'; +import { DeleteFileLinkController } from './Controller/FileLink/DeleteFileLinkController'; +import { ProjectEntityBoardCardService } from './Service/Entity/ProjectEntityBoardCardService'; +import { CommonEntityBoardCardService } from './Service/Entity/CommonEntityBoardCardService'; +import { ImportService } from './Service/Import/ImportService'; +import { GetEntitiesImportTemplateController } from './Controller/Entity/Import/GetEntitiesImportTemplateController'; +import { UploadEntitiesImportController } from './Controller/Entity/Import/UploadEntitiesImportController'; +import { GetEntityDocumentsController } from './Controller/Entity/Documents/GetEntityDocumentsController'; +import { DeleteFileLinksController } from './Controller/FileLink/DeleteFileLinksController'; +import { EntityBoardController } from './Controller/Entity/Board/entity-board.controller'; +import { EntityListController } from './Controller/Entity/List/entity-list.controller'; + +import { Activity, ActivityController, ActivityHandler, ActivityService } from './activity'; +import { ActivityCardController, ActivityCardService } from './activity-card'; +import { ActivityType, ActivityTypeController, ActivityTypeService } from './activity-type'; +import { BaseTaskService } from './base-task'; +import { Board } from './board/entities'; +import { BoardController } from './board/board.controller'; +import { BoardService } from './board/board.service'; +import { BoardHandler } from './board/board.handler'; +import { BoardStage, BoardStageController, BoardStageService } from './board-stage'; +import { EntityLink } from './entity-link/entities'; +import { EntityLinkService } from './entity-link/entity-link.service'; +import { EntitySearchController, EntitySearchService } from './entity-search'; +import { EntityType } from './entity-type/entities'; +import { EntityTypeController } from './entity-type/entity-type.controller'; +import { EntityTypeService } from './entity-type/entity-type.service'; +import { EntityTypeLink } from './entity-type-link/entities'; +import { EntityTypeLinkService } from './entity-type-link/entity-type-link.service'; +import { EntityTypeFeature, EntityTypeFeatureService, Feature, FeatureController, FeatureService } from './feature'; +import { Task, TaskController, TaskHandler, TaskService } from './task'; +import { TaskBoardController, TaskBoardService } from './task-board'; +import { TaskComment, TaskCommentController, TaskCommentService } from './task-comment'; +import { TaskCommentLike, TaskCommentLikeController, TaskCommentLikeService } from './task-comment-like'; +import { TaskSubtask, TaskSubtaskController, TaskSubtaskService } from './task-subtask'; +import { IdentityController } from './identity/identity.controller'; +import { IdentityService } from './identity/identity.service'; + +import { Note, NoteController, NoteHandler, NoteService } from './note'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + Activity, + ActivityType, + Board, + BoardStage, + Entity, + EntityLink, + EntityType, + Feature, + EntityTypeFeature, + EntityTypeLink, + ExternalEntity, + ExternalSystem, + FileLink, + Note, + TaskSubtask, + Task, + TaskComment, + TaskCommentLike, + ]), + IAMModule, + forwardRef(() => MailingModule), + forwardRef(() => InventoryModule), + forwardRef(() => CrmReportingModule), + forwardRef(() => SchedulerModule), + forwardRef(() => SalesPlanModule), + forwardRef(() => TelephonyModule), + forwardRef(() => MultichatModule), + SalesforceModule, + StorageModule, + TaskSettingsModule, + EntityFieldModule, + EntityInfoModule, + ], + controllers: [ + IdentityController, + EntityTypeController, + CreateEntityController, + CreateSimpleEntityController, + UpdateEntityController, + UpdateEntityFieldController, + DeleteEntityController, + EntitySearchController, + GetEntityController, + EntityBoardController, + EntityListController, + GetEntityFilesController, + AddEntityFilesController, + NoteController, + BoardController, + BoardStageController, + ActivityTypeController, + ActivityController, + TaskController, + GetTimeBoardController, + GetTimeBoardMetaController, + GetTimeBoardCalendarController, + GetTimeBoardCalendarMetaController, + GetTimeBoardItemController, + FeatureController, + CreateExternalLinkController, + TaskBoardController, + ActivityCardController, + DeleteFileLinkController, + DeleteFileLinksController, + TaskSubtaskController, + TaskCommentController, + TaskCommentLikeController, + GetEntitiesImportTemplateController, + UploadEntitiesImportController, + GetEntityDocumentsController, + ], + providers: [ + IdentityService, + ActivityCardService, + ActivityService, + ActivityHandler, + ActivityTypeService, + BaseTaskService, + BoardService, + BoardHandler, + BoardStageService, + CommonEntityBoardCardService, + EntityBoardService, + EntityLinkService, + EntitySearchService, + EntityService, + EntityServiceEmitter, + EntityServiceHandler, + EntityTypeFeatureService, + EntityTypeLinkService, + EntityTypeService, + ExternalEntityService, + ExternalSystemService, + FeatureService, + FileLinkService, + ImportService, + NoteService, + NoteHandler, + ProjectEntityBoardCardService, + TaskSubtaskService, + TaskBoardService, + TaskCommentService, + TaskCommentLikeService, + TaskService, + TaskHandler, + TimeBoardService, + ], + exports: [ + EntityService, + EntitySearchService, + EntityTypeService, + EntityLinkService, + EntityBoardService, + EntityTypeFeatureService, + BoardStageService, + ActivityService, + TaskService, + FileLinkService, + ActivityTypeService, + BoardService, + EntityTypeLinkService, + NoteService, + TaskSubtaskService, + CrmReportingModule, + EntityFieldModule, + ], +}) +export class CrmModule {} diff --git a/backend/src/CRM/entity-link/dto/entity-link.dto.ts b/backend/src/CRM/entity-link/dto/entity-link.dto.ts new file mode 100644 index 0000000..6b9ec18 --- /dev/null +++ b/backend/src/CRM/entity-link/dto/entity-link.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { ObjectState } from '@/common'; + +export class EntityLinkDto { + @ApiProperty() + @IsNumber() + sourceId: number; + + @ApiProperty() + @IsNumber() + targetId: number; + + @ApiProperty() + @IsNumber() + sortOrder: number; + + @ApiProperty({ enum: ObjectState }) + @IsEnum(ObjectState) + state: ObjectState; +} diff --git a/backend/src/CRM/entity-link/dto/index.ts b/backend/src/CRM/entity-link/dto/index.ts new file mode 100644 index 0000000..216fc12 --- /dev/null +++ b/backend/src/CRM/entity-link/dto/index.ts @@ -0,0 +1 @@ +export * from './entity-link.dto'; diff --git a/backend/src/CRM/entity-link/entities/entity-link.entity.ts b/backend/src/CRM/entity-link/entities/entity-link.entity.ts new file mode 100644 index 0000000..16cfffc --- /dev/null +++ b/backend/src/CRM/entity-link/entities/entity-link.entity.ts @@ -0,0 +1,36 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ObjectState } from '@/common'; + +import { EntityLinkDto } from '../dto'; + +@Entity() +export class EntityLink { + @PrimaryColumn() + sourceId: number; + + @PrimaryColumn() + targetId: number; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + constructor(accountId: number, sourceId: number, targetId: number, sortOrder: number) { + this.accountId = accountId; + this.sourceId = sourceId; + this.targetId = targetId; + this.sortOrder = sortOrder; + } + + public toDto(): EntityLinkDto { + return { + sourceId: this.sourceId, + targetId: this.targetId, + sortOrder: this.sortOrder, + state: ObjectState.Unchanged, + }; + } +} diff --git a/backend/src/CRM/entity-link/entities/index.ts b/backend/src/CRM/entity-link/entities/index.ts new file mode 100644 index 0000000..7f33a02 --- /dev/null +++ b/backend/src/CRM/entity-link/entities/index.ts @@ -0,0 +1 @@ +export * from './entity-link.entity'; diff --git a/backend/src/CRM/entity-link/entity-link.service.ts b/backend/src/CRM/entity-link/entity-link.service.ts new file mode 100644 index 0000000..507e669 --- /dev/null +++ b/backend/src/CRM/entity-link/entity-link.service.ts @@ -0,0 +1,151 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ObjectState } from '@/common'; + +import { EntityLinkDto } from './dto'; +import { EntityLink } from './entities'; + +interface FindFilter { + accountId: number; + linkId?: number; + sourceId?: number | number[]; + targetId?: number | number[]; +} + +@Injectable() +export class EntityLinkService { + private readonly logger = new Logger(EntityLinkService.name); + constructor( + @InjectRepository(EntityLink) + private readonly repository: Repository, + ) {} + + async create({ + accountId, + sourceId, + targetId, + sortOrder, + createBackLink = true, + }: { + accountId: number; + sourceId: number; + targetId: number; + sortOrder?: number; + createBackLink?: boolean; + }): Promise { + sortOrder ??= (await this.getMaxSortOrder({ accountId, sourceId })) + 1; + const link = await this.repository.save(new EntityLink(accountId, sourceId, targetId, sortOrder)); + if (createBackLink) { + await this.create({ accountId, sourceId: targetId, targetId: sourceId, createBackLink: false }); + } + return link; + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).getMany(); + } + + async processMany({ accountId, links }: { accountId: number; links: EntityLinkDto[] }): Promise { + let changed = false; + for (const link of links) { + try { + if (link.state === ObjectState.Created) { + await this.create({ accountId, sourceId: link.sourceId, targetId: link.targetId, sortOrder: link.sortOrder }); + changed = true; + } else if (link.state === ObjectState.Updated) { + await this.update({ accountId, sourceId: link.sourceId, targetId: link.targetId, sortOrder: link.sortOrder }); + } else if (link.state === ObjectState.Deleted) { + await this.delete({ accountId, sourceId: link.sourceId, targetId: link.targetId }); + changed = true; + } + } catch (error) { + this.logger.warn(`Failed to process link: ${JSON.stringify(link)}. Error: ${error.toString()}`); + } + } + return changed; + } + + async update({ + accountId, + sourceId, + targetId, + sortOrder, + }: { + accountId: number; + sourceId: number; + targetId: number; + sortOrder: number; + }) { + await this.repository.update({ accountId, sourceId, targetId }, { sortOrder }); + } + + async delete({ accountId, sourceId, targetId }: { accountId: number; sourceId: number; targetId: number }) { + await this.repository.delete({ accountId, sourceId, targetId }); + await this.repository.delete({ accountId, sourceId: targetId, targetId: sourceId }); + } + + async copyEntityLinks({ + accountId, + sourceId, + targetId, + }: { + accountId: number; + sourceId: number; + targetId: number; + }): Promise { + const links = await this.findMany({ accountId, sourceId }); + return Promise.all( + links.map((link) => + this.create({ accountId, sourceId: targetId, targetId: link.targetId, sortOrder: link.sortOrder }), + ), + ); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('el') + .where('el.account_id = :accountId', { accountId: filter.accountId }) + .orderBy('el.sort_order', 'ASC'); + if (filter.linkId) { + qb.andWhere('el.id = :linkId', { linkId: filter.linkId }); + } + if (filter.sourceId) { + if (Array.isArray(filter.sourceId)) { + if (filter.sourceId.length) { + qb.andWhere('el.source_id IN (:...sourceIds)', { sourceIds: filter.sourceId }); + } else { + qb.andWhere('1 = 0'); + } + } else { + qb.andWhere('el.source_id = :sourceId', { sourceId: filter.sourceId }); + } + } + if (filter.targetId) { + if (Array.isArray(filter.targetId)) { + if (filter.targetId.length) { + qb.andWhere('el.target_id IN (:...targetIds)', { targetIds: filter.targetId }); + } else { + qb.andWhere('1 = 0'); + } + } else { + qb.andWhere('el.target_id = :targetId', { targetId: filter.targetId }); + } + } + return qb; + } + + private async getMaxSortOrder({ accountId, sourceId }: { accountId: number; sourceId: number }): Promise { + const result = await this.repository + .createQueryBuilder('el') + .select('max(el.sort_order)', 'max') + .where('el.account_id = :accountId', { accountId }) + .andWhere('el.source_id = :sourceId', { sourceId }) + .getRawOne<{ max: number }>(); + return result?.max ?? 0; + } +} diff --git a/backend/src/CRM/entity-search/dto/entity-search-filter.dto.ts b/backend/src/CRM/entity-search/dto/entity-search-filter.dto.ts new file mode 100644 index 0000000..87d5651 --- /dev/null +++ b/backend/src/CRM/entity-search/dto/entity-search-filter.dto.ts @@ -0,0 +1,38 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +import { FieldType } from '@/modules/entity/entity-field/common'; + +export class EntitySearchFilterDto { + @ApiPropertyOptional({ description: 'Filter by entity type ID.', nullable: true, type: [Number] }) + @IsOptional() + entityTypeId?: number | number[] | null; + + @ApiPropertyOptional({ description: 'Filter by entity board ID.', nullable: true, type: [Number] }) + @IsOptional() + boardId?: number | number[] | null; + + @ApiPropertyOptional({ description: 'Filter by entity name.', nullable: true }) + @IsOptional() + @IsString() + name?: string | null; + + @ApiPropertyOptional({ description: 'Exclude entity id from search', nullable: true, type: [Number] }) + @IsOptional() + excludeEntityId?: number | number[] | null; + + @ApiPropertyOptional({ description: 'Filter by field value.', nullable: true }) + @IsOptional() + @IsString() + fieldValue?: string | null; + + @ApiPropertyOptional({ enum: FieldType, description: 'Filter by field type for field value.', nullable: true }) + @IsOptional() + @IsString() + fieldType?: FieldType | null; + + @ApiPropertyOptional({ description: 'Search in linked entities', nullable: true }) + @IsOptional() + @IsBoolean() + searchInLinked?: boolean | null; +} diff --git a/backend/src/CRM/entity-search/dto/entity-search-for-call-result.dto.ts b/backend/src/CRM/entity-search/dto/entity-search-for-call-result.dto.ts new file mode 100644 index 0000000..7d89872 --- /dev/null +++ b/backend/src/CRM/entity-search/dto/entity-search-for-call-result.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; + +import { EntityInfoDto } from '@/modules/entity/entity-info'; +import { IsOptional } from 'class-validator'; + +export class EntitySearchForCallResultDto { + @ApiProperty({ description: 'Entity info', type: EntityInfoDto, nullable: true }) + @IsOptional() + entity: EntityInfoDto | null; + + @ApiPropertyOptional({ description: 'Entity info', type: EntityInfoDto, nullable: true }) + @IsOptional() + linked?: EntityInfoDto | null; +} diff --git a/backend/src/CRM/entity-search/dto/entity-search-full-result.dto.ts b/backend/src/CRM/entity-search/dto/entity-search-full-result.dto.ts new file mode 100644 index 0000000..d3fa47a --- /dev/null +++ b/backend/src/CRM/entity-search/dto/entity-search-full-result.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { EntityDto } from '../../Service/Entity/Dto/EntityDto'; + +export class EntitySearchFullResultDto { + @ApiProperty({ description: 'List of entities', type: [EntityDto], nullable: true }) + entities: EntityDto[] | null; + + @ApiProperty({ description: 'Paging metadata', type: PagingMeta }) + meta: PagingMeta; +} diff --git a/backend/src/CRM/entity-search/dto/entity-search-result.dto.ts b/backend/src/CRM/entity-search/dto/entity-search-result.dto.ts new file mode 100644 index 0000000..3b19784 --- /dev/null +++ b/backend/src/CRM/entity-search/dto/entity-search-result.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +export class EntitySearchResultDto { + @ApiProperty({ description: 'List of entities', type: [EntityInfoDto], nullable: true }) + entities: EntityInfoDto[] | null; + + @ApiProperty({ description: 'Paging metadata', type: PagingMeta }) + meta: PagingMeta; +} diff --git a/backend/src/CRM/entity-search/dto/index.ts b/backend/src/CRM/entity-search/dto/index.ts new file mode 100644 index 0000000..097f5c6 --- /dev/null +++ b/backend/src/CRM/entity-search/dto/index.ts @@ -0,0 +1,4 @@ +export * from './entity-search-filter.dto'; +export * from './entity-search-for-call-result.dto'; +export * from './entity-search-full-result.dto'; +export * from './entity-search-result.dto'; diff --git a/backend/src/CRM/entity-search/entity-search.controller.ts b/backend/src/CRM/entity-search/entity-search.controller.ts new file mode 100644 index 0000000..c1d7b58 --- /dev/null +++ b/backend/src/CRM/entity-search/entity-search.controller.ts @@ -0,0 +1,60 @@ +import { Body, Controller, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { + EntitySearchFilterDto, + EntitySearchForCallResultDto, + EntitySearchFullResultDto, + EntitySearchResultDto, +} from './dto'; +import { EntitySearchService } from './entity-search.service'; + +@ApiTags('crm/entities/search') +@Controller('crm/entities/search') +@JwtAuthorized({ prefetch: { user: true } }) +export class EntitySearchController { + constructor(private readonly service: EntitySearchService) {} + + @ApiOperation({ + summary: 'Search entities with full information', + description: 'Search entities with full information by name according permissions', + }) + @ApiBody({ type: EntitySearchFilterDto, required: true, description: 'Search filter' }) + @ApiOkResponse({ description: 'Entity search result', type: EntitySearchFullResultDto }) + @Post('full') + async searchFull( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: EntitySearchFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.searchFull({ accountId, user, filter, paging }); + } + + @ApiOperation({ summary: 'Search entities', description: 'Search entities by name according permissions' }) + @ApiBody({ type: EntitySearchFilterDto, required: true, description: 'Search filter' }) + @ApiOkResponse({ description: 'Entity search result', type: EntitySearchResultDto }) + @Post() + async search( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: EntitySearchFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.searchInfo({ accountId, user, filter, paging }); + } + + @ApiOperation({ + summary: 'Search entity for call info', + description: 'Search entities for call information with last deal', + }) + @ApiBody({ type: EntitySearchFilterDto, required: true, description: 'Search filter' }) + @ApiOkResponse({ description: 'Entity search result', type: EntitySearchForCallResultDto }) + @Post('for-call') + async searchForCall(@CurrentAuth() { accountId, user }: AuthData, @Body() filter: EntitySearchFilterDto) { + return this.service.searchForCall({ accountId, user, filter }); + } +} diff --git a/backend/src/CRM/entity-search/entity-search.service.ts b/backend/src/CRM/entity-search/entity-search.service.ts new file mode 100644 index 0000000..e87040d --- /dev/null +++ b/backend/src/CRM/entity-search/entity-search.service.ts @@ -0,0 +1,258 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, DataSource, Repository } from 'typeorm'; + +import { PagingMeta, PagingQuery } from '@/common'; +import { buildSearchParams } from '@/database/common'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities'; +import { EntityInfoService } from '@/modules/entity/entity-info'; +import { FieldType } from '@/modules/entity/entity-field/common'; + +import { EntityCategory } from '../common'; +import { Entity } from '../Model/Entity/Entity'; +import { EntityService } from '../Service/Entity/EntityService'; +import { EntityTypeService } from '../entity-type/entity-type.service'; + +import { EntitySearchForCallResultDto, EntitySearchFullResultDto, EntitySearchResultDto } from './dto'; + +interface SearchFilter { + entityTypeId?: number | number[] | null; + boardId?: number | number[] | null; + name?: string | null; + excludeEntityId?: number | number[] | null; + fieldValue?: string | null; + fieldType?: FieldType | null; + searchInLinked?: boolean | null; +} + +@Injectable() +export class EntitySearchService { + constructor( + private readonly dataSource: DataSource, + @InjectRepository(Entity) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly entityTypeService: EntityTypeService, + @Inject(forwardRef(() => EntityService)) + private readonly entityService: EntityService, + private readonly entityInfoService: EntityInfoService, + ) {} + + async searchFull({ + accountId, + user, + filter, + paging, + }: { + accountId: number; + user: User; + filter: SearchFilter; + paging: PagingQuery; + }): Promise { + const { entities, total } = await this.search({ accountId, user, filter, paging }); + + return { + entities: await Promise.all( + entities.map(async (entity) => await this.entityService.getDtoForEntity(accountId, user, entity, false)), + ), + meta: new PagingMeta(paging.skip + paging.take, total), + }; + } + + async searchInfo({ + accountId, + user, + filter, + paging, + }: { + accountId: number; + user: User; + filter: SearchFilter; + paging: PagingQuery; + }): Promise { + const { entities, total } = await this.search({ accountId, user, filter, paging }); + + return { + entities: await Promise.all( + entities.map(async (entity) => await this.entityInfoService.getEntityInfo({ user, entity, access: true })), + ), + meta: new PagingMeta(paging.skip + paging.take, total), + }; + } + + async searchForCall({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: SearchFilter; + }): Promise { + const { entities } = await this.search({ accountId, user, filter, paging: { skip: 0, take: 1 } }); + const entity = entities.length + ? await this.entityInfoService.getEntityInfo({ user, entity: entities[0], access: true }) + : null; + + if (entity) { + const linked = await this.getLinkedForCall({ accountId, user, entityId: entity.id }); + + return { entity, linked }; + } + + return { entity }; + } + + async search({ + accountId, + user, + filter, + paging, + }: { + accountId: number; + user: User; + filter: SearchFilter; + paging: PagingQuery; + }): Promise<{ entities: Entity[]; total: number }> { + const qb = this.repository.createQueryBuilder('entity').where('entity.account_id = :accountId', { accountId }); + + const accessibleEntityTypes = await this.entityTypeService.getAccessibleForUser(accountId, user); + const entityTypeIds = filter.entityTypeId + ? Array.isArray(filter.entityTypeId) + ? filter.entityTypeId + : [filter.entityTypeId] + : null; + const entityTypes = entityTypeIds + ? accessibleEntityTypes.filter((et) => entityTypeIds.includes(et.id)) + : accessibleEntityTypes; + + if (entityTypes.length) { + const etWithResponsible = await Promise.all( + entityTypes.map(async (entityType) => ({ + entityType, + responsibles: await this.authService.whoCanView({ user, authorizable: entityType }), + })), + ); + qb.andWhere( + new Brackets((qb1) => { + etWithResponsible.forEach((et) => + qb1.orWhere( + new Brackets((qb2) => { + qb2.where(`entity.entity_type_id = ${et.entityType.id}`); + if (et.responsibles) { + qb2.andWhere( + new Brackets((qb3) => { + qb3.where(`entity.created_by = ${user.id}`); + if (et.responsibles.length) { + qb3.orWhere(`entity.responsible_user_id IN (${et.responsibles.join(',')})`); + } + }), + ); + } + }), + ), + ); + }), + ); + } else { + return { entities: [], total: 0 }; + } + + const nameFilter = buildSearchParams(filter.name); + if (nameFilter?.length && filter.searchInLinked) { + qb.addCommonTableExpression( + this.dataSource + .createQueryBuilder() + .select('link.source_id', 'entity_id') + .from('entity_link', 'link') + .leftJoin('entity', 'linked', 'linked.id = link.target_id') + .where(`linked.name_tsv @@ to_tsquery('${nameFilter}')`), + 'linked', + ); + } + + const fieldFilter = buildSearchParams(filter.fieldValue); + if (fieldFilter?.length) { + const fqb = this.dataSource + .createQueryBuilder() + .select('fv.entity_id') + .from('field_value', 'fv') + .where(`fv.payload_tsv @@ to_tsquery('${fieldFilter}')`); + if (filter.fieldType) { + fqb.andWhere(`fv.field_type = '${filter.fieldType}'`); + } + qb.addCommonTableExpression(fqb, 'field_values'); + if (filter.searchInLinked) { + qb.addCommonTableExpression( + this.dataSource + .createQueryBuilder() + .select('link.source_id', 'entity_id') + .from('entity_link', 'link') + .leftJoin('entity', 'linked', 'linked.id = link.target_id') + .where(`link.target_id IN (SELECT entity_id FROM field_values)`), + 'linked_field_values', + ); + } + } + + if (nameFilter?.length || fieldFilter?.length) { + qb.andWhere( + new Brackets((qb1) => { + if (nameFilter?.length) { + qb1.where('entity.name_tsv @@ to_tsquery(:name)', { name: nameFilter }); + } + if (fieldFilter?.length) { + qb1.orWhere('entity.id IN (SELECT entity_id FROM field_values)'); + } + if (filter.searchInLinked) { + if (nameFilter?.length) { + qb1.orWhere('entity.id IN (SELECT entity_id FROM linked)'); + } + if (fieldFilter?.length) { + qb1.orWhere('entity.id IN (SELECT entity_id FROM linked_field_values)'); + } + } + }), + ); + } else if (filter.name?.length || filter.fieldValue?.length) { + qb.andWhere('1 = 0'); + } + + if (filter.excludeEntityId) { + if (Array.isArray(filter.excludeEntityId)) { + qb.andWhere('entity.id NOT IN (:...excludeEntityIds)', { excludeEntityIds: filter.excludeEntityId }); + } else { + qb.andWhere('entity.id != :excludeEntityId', { excludeEntityId: filter.excludeEntityId }); + } + } + + if (filter.boardId) { + if (Array.isArray(filter.boardId)) { + qb.andWhere('entity.board_id IN (:...boardIds)', { boardIds: filter.boardId }); + } else { + qb.andWhere('entity.board_id = :boardId', { boardId: filter.boardId }); + } + } + + const total = await qb.getCount(); + const entities = await qb + .offset(paging.skip) + .limit(paging.take) + .orderBy('entity.created_at', 'DESC') + .addOrderBy('entity.id', 'DESC') + .getMany(); + + return { entities, total }; + } + + private async getLinkedForCall({ accountId, user, entityId }: { accountId: number; user: User; entityId: number }) { + const entity = await this.entityService.findLastLinkedEntity({ + accountId, + entityId: entityId, + category: EntityCategory.DEAL, + }); + + return entity ? this.entityInfoService.getEntityInfo({ user, entity, access: true }) : null; + } +} diff --git a/backend/src/CRM/entity-search/index.ts b/backend/src/CRM/entity-search/index.ts new file mode 100644 index 0000000..0c7a0f3 --- /dev/null +++ b/backend/src/CRM/entity-search/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './entity-search.controller'; +export * from './entity-search.service'; diff --git a/backend/src/CRM/entity-type-link/dto/create-entity-type-link.dto.ts b/backend/src/CRM/entity-type-link/dto/create-entity-type-link.dto.ts new file mode 100644 index 0000000..ca8479a --- /dev/null +++ b/backend/src/CRM/entity-type-link/dto/create-entity-type-link.dto.ts @@ -0,0 +1,10 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; +import { EntityTypeLinkDto } from './entity-type-link.dto'; + +export class CreateEntityTypeLinkDto extends PickType(EntityTypeLinkDto, ['targetId'] as const) { + @ApiPropertyOptional({ description: 'Sort order' }) + @IsOptional() + @IsNumber() + sortOrder?: number; +} diff --git a/backend/src/CRM/entity-type-link/dto/entity-type-link.dto.ts b/backend/src/CRM/entity-type-link/dto/entity-type-link.dto.ts new file mode 100644 index 0000000..f65bb14 --- /dev/null +++ b/backend/src/CRM/entity-type-link/dto/entity-type-link.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class EntityTypeLinkDto { + @ApiProperty({ description: 'Source entity type ID' }) + @IsNumber() + sourceId: number; + + @ApiProperty({ description: 'Target entity type ID' }) + @IsNumber() + targetId: number; + + @ApiProperty({ description: 'Sort order' }) + @IsNumber() + sortOrder: number; +} diff --git a/backend/src/CRM/entity-type-link/dto/index.ts b/backend/src/CRM/entity-type-link/dto/index.ts new file mode 100644 index 0000000..9403b0f --- /dev/null +++ b/backend/src/CRM/entity-type-link/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-entity-type-link.dto'; +export * from './entity-type-link.dto'; +export * from './update-entity-type-link.dto'; diff --git a/backend/src/CRM/entity-type-link/dto/update-entity-type-link.dto.ts b/backend/src/CRM/entity-type-link/dto/update-entity-type-link.dto.ts new file mode 100644 index 0000000..689248c --- /dev/null +++ b/backend/src/CRM/entity-type-link/dto/update-entity-type-link.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { EntityTypeLinkDto } from './entity-type-link.dto'; + +export class UpdateEntityTypeLinkDto extends PickType(EntityTypeLinkDto, ['targetId', 'sortOrder'] as const) {} diff --git a/backend/src/CRM/entity-type-link/entities/entity-type-link.entity.ts b/backend/src/CRM/entity-type-link/entities/entity-type-link.entity.ts new file mode 100644 index 0000000..37cf617 --- /dev/null +++ b/backend/src/CRM/entity-type-link/entities/entity-type-link.entity.ts @@ -0,0 +1,41 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { CreateEntityTypeLinkDto, EntityTypeLinkDto } from '../dto'; + +@Entity() +export class EntityTypeLink { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sourceId: number; + + @Column() + targetId: number; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + constructor(accountId: number, sourceId: number, targetId: number, sortOrder: number) { + this.accountId = accountId; + this.sourceId = sourceId; + this.targetId = targetId; + this.sortOrder = sortOrder; + } + + public static fromDto(accountId: number, sourceId: number, dto: CreateEntityTypeLinkDto): EntityTypeLink { + return new EntityTypeLink(accountId, sourceId, dto.targetId, dto.sortOrder); + } + + public update({ sortOrder }: { sortOrder?: number }): EntityTypeLink { + this.sortOrder = sortOrder !== undefined ? sortOrder : this.sortOrder; + + return this; + } + + public toDto(): EntityTypeLinkDto { + return { sourceId: this.sourceId, targetId: this.targetId, sortOrder: this.sortOrder }; + } +} diff --git a/backend/src/CRM/entity-type-link/entities/index.ts b/backend/src/CRM/entity-type-link/entities/index.ts new file mode 100644 index 0000000..e281727 --- /dev/null +++ b/backend/src/CRM/entity-type-link/entities/index.ts @@ -0,0 +1 @@ +export * from './entity-type-link.entity'; diff --git a/backend/src/CRM/entity-type-link/entity-type-link.service.ts b/backend/src/CRM/entity-type-link/entity-type-link.service.ts new file mode 100644 index 0000000..9a58d4b --- /dev/null +++ b/backend/src/CRM/entity-type-link/entity-type-link.service.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { CreateEntityTypeLinkDto } from './dto'; +import { EntityTypeLink } from './entities'; + +interface FindFilter { + accountId: number; + sourceId?: number; + targetId?: number; +} + +@Injectable() +export class EntityTypeLinkService { + constructor( + @InjectRepository(EntityTypeLink) + private repository: Repository, + ) {} + + public async create({ + accountId, + sourceId, + dto, + createBackLink = true, + }: { + accountId: number; + sourceId: number; + dto: CreateEntityTypeLinkDto; + createBackLink?: boolean; + }): Promise { + dto.sortOrder ??= (await this.getMaxSortOrder({ accountId, sourceId })) + 1; + + const link = await this.repository.save(EntityTypeLink.fromDto(accountId, sourceId, dto)); + if (createBackLink) { + await this.create({ accountId, sourceId: dto.targetId, dto: { targetId: sourceId }, createBackLink: false }); + } + return link; + } + + public async findMany(filter: FindFilter): Promise { + return this.repository.find({ where: filter, order: { sortOrder: 'ASC' } }); + } + + public async processMany({ + accountId, + sourceId, + dtos, + }: { + accountId: number; + sourceId: number; + dtos: CreateEntityTypeLinkDto[]; + }): Promise { + const existingLinks = await this.findMany({ accountId, sourceId }); + + const deleted = existingLinks.filter((link) => !dtos.some((dto) => dto.targetId === link.targetId)); + if (deleted.length) { + await this.repository.delete(deleted.map((d) => d.id)); + await Promise.all( + deleted.map(async (link) => { + await this.repository.delete({ accountId, sourceId: link.targetId, targetId: sourceId }); + }), + ); + } + + return Promise.all( + dtos.map(async (dto) => { + const existingLink = existingLinks.find((link) => link.targetId === dto.targetId); + if (existingLink) { + return this.repository.save(existingLink.update({ sortOrder: dto.sortOrder })); + } else { + return this.create({ accountId, sourceId, dto }); + } + }), + ); + } + + private async getMaxSortOrder({ accountId, sourceId }: { accountId: number; sourceId: number }): Promise { + const result = await this.repository + .createQueryBuilder('etl') + .select('max(etl.sort_order)', 'max') + .where('etl.account_id = :accountId', { accountId }) + .andWhere('etl.source_id = :sourceId', { sourceId }) + .getRawOne<{ max: number }>(); + return result?.max ?? 0; + } +} diff --git a/backend/src/CRM/entity-type/dto/create-entity-type.dto.ts b/backend/src/CRM/entity-type/dto/create-entity-type.dto.ts new file mode 100644 index 0000000..8cecc24 --- /dev/null +++ b/backend/src/CRM/entity-type/dto/create-entity-type.dto.ts @@ -0,0 +1,68 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; + +import { UpdateFieldGroupDto } from '@/modules/entity/entity-field/field-group/dto/update-field-group.dto'; +import { FieldsSettingsDto } from '@/modules/entity/entity-field/field/dto/fields-settings.dto'; +import { CreateFieldDto } from '@/modules/entity/entity-field/field/dto/create-field.dto'; + +import { EntityCategory } from '../../common'; +import { CreateEntityTypeLinkDto } from '../../entity-type-link/dto'; +import { FeatureCode } from '../../feature'; +import { TaskFieldCode } from '../../task-settings/enums/task-field-code.enum'; + +import { EntityTypeSectionDto } from './entity-type-section.dto'; + +export class CreateEntityTypeDto { + @ApiProperty({ description: 'Entity type name' }) + @IsString() + name: string; + + @ApiProperty({ enum: EntityCategory, description: 'Entity type category' }) + @IsEnum(EntityCategory) + entityCategory: EntityCategory; + + @ApiProperty({ type: EntityTypeSectionDto, description: 'Entity type section info' }) + @IsObject() + section: EntityTypeSectionDto; + + @ApiProperty({ type: [UpdateFieldGroupDto], description: 'Entity type field groups' }) + @IsArray() + fieldGroups: UpdateFieldGroupDto[]; + + @ApiProperty({ type: [CreateFieldDto], description: 'Entity type fields' }) + @IsArray() + fields: CreateFieldDto[]; + + @ApiProperty({ type: [CreateEntityTypeLinkDto], description: 'Linked entity types' }) + @IsArray() + linkedEntityTypes: CreateEntityTypeLinkDto[]; + + @ApiProperty({ enum: FeatureCode, isArray: true, description: 'Feature codes' }) + @IsArray() + featureCodes: FeatureCode[]; + + @ApiPropertyOptional({ enum: TaskFieldCode, isArray: true, description: 'Task settings active fields' }) + @IsOptional() + @IsArray() + taskSettingsActiveFields: TaskFieldCode[]; + + @ApiPropertyOptional({ type: FieldsSettingsDto, description: 'Fields settings' }) + @IsOptional() + @IsObject() + fieldsSettings?: FieldsSettingsDto; + + @ApiPropertyOptional({ required: false, nullable: true, description: 'Sort order' }) + @IsOptional() + @IsNumber() + sortOrder?: number | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Linked products section IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + linkedProductsSectionIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Linked scheduler IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + linkedSchedulerIds?: number[] | null; +} diff --git a/backend/src/CRM/entity-type/dto/entity-type-section.dto.ts b/backend/src/CRM/entity-type/dto/entity-type-section.dto.ts new file mode 100644 index 0000000..556cbe2 --- /dev/null +++ b/backend/src/CRM/entity-type/dto/entity-type-section.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; + +import { SectionView } from '../enums'; + +export class EntityTypeSectionDto { + @ApiProperty({ description: 'Section name' }) + @IsString() + name: string; + + @ApiProperty({ enum: SectionView, description: 'Section view' }) + @IsEnum(SectionView) + view: SectionView; + + @ApiProperty({ description: 'Section icon' }) + @IsString() + icon: string; +} diff --git a/backend/src/CRM/entity-type/dto/entity-type.dto.ts b/backend/src/CRM/entity-type/dto/entity-type.dto.ts new file mode 100644 index 0000000..75c7c98 --- /dev/null +++ b/backend/src/CRM/entity-type/dto/entity-type.dto.ts @@ -0,0 +1,91 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { FieldDto } from '@/modules/entity/entity-field/field/dto/field.dto'; +import { FieldGroupDto } from '@/modules/entity/entity-field/field-group/dto/field-group.dto'; + +import { EntityCategory } from '../../common'; +import { EntityTypeLinkDto } from '../../entity-type-link/dto'; +import { FeatureCode } from '../../feature'; + +import { EntityTypeSectionDto } from './entity-type-section.dto'; + +export class EntityTypeDto { + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Entity type name' }) + @IsString() + name: string; + + @ApiProperty({ enum: EntityCategory, description: 'Entity type category' }) + @IsEnum(EntityCategory) + entityCategory: EntityCategory; + + @ApiProperty({ type: EntityTypeSectionDto, description: 'Entity type section info' }) + section: EntityTypeSectionDto; + + @ApiProperty({ type: [FieldGroupDto], description: 'Entity type field groups' }) + @IsArray() + fieldGroups: FieldGroupDto[]; + + @ApiProperty({ type: [FieldDto], description: 'Entity type fields' }) + @IsArray() + fields: FieldDto[]; + + @ApiProperty({ type: [EntityTypeLinkDto], description: 'Linked entity types' }) + @IsArray() + linkedEntityTypes: EntityTypeLinkDto[]; + + @ApiProperty({ enum: FeatureCode, isArray: true, description: 'Feature codes' }) + @IsArray() + featureCodes: FeatureCode[]; + + @ApiPropertyOptional({ description: 'Sort order' }) + @IsOptional() + @IsNumber() + sortOrder?: number | null; + + @ApiProperty({ description: 'Created at in ISO format' }) + @IsString() + createdAt: string; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Linked products section IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + linkedProductsSectionIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Linked scheduler IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + linkedSchedulerIds?: number[] | null; + + constructor({ + id, + name, + entityCategory, + section, + fieldGroups, + fields, + linkedEntityTypes, + featureCodes, + sortOrder, + createdAt, + linkedProductsSectionIds, + linkedSchedulerIds, + }: EntityTypeDto) { + this.id = id; + this.name = name; + this.entityCategory = entityCategory; + this.section = section; + this.fieldGroups = fieldGroups; + this.fields = fields; + this.linkedEntityTypes = linkedEntityTypes; + this.featureCodes = featureCodes; + this.sortOrder = sortOrder; + this.createdAt = createdAt; + this.linkedProductsSectionIds = linkedProductsSectionIds; + this.linkedSchedulerIds = linkedSchedulerIds; + } +} diff --git a/backend/src/CRM/entity-type/dto/index.ts b/backend/src/CRM/entity-type/dto/index.ts new file mode 100644 index 0000000..bf7eabd --- /dev/null +++ b/backend/src/CRM/entity-type/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-entity-type.dto'; +export * from './entity-type-section.dto'; +export * from './entity-type.dto'; +export * from './update-entity-type-fields.dto'; +export * from './update-entity-type.dto'; diff --git a/backend/src/CRM/entity-type/dto/update-entity-type-fields.dto.ts b/backend/src/CRM/entity-type/dto/update-entity-type-fields.dto.ts new file mode 100644 index 0000000..b6cb9a9 --- /dev/null +++ b/backend/src/CRM/entity-type/dto/update-entity-type-fields.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { UpdateFieldGroupDto } from '@/modules/entity/entity-field/field-group/dto/update-field-group.dto'; +import { UpdateFieldDto } from '@/modules/entity/entity-field/field/dto/update-field.dto'; + +export class UpdateEntityTypeFieldsDto { + @ApiProperty({ type: [UpdateFieldGroupDto], description: 'Field groups' }) + @IsArray() + fieldGroups: UpdateFieldGroupDto[]; + + @ApiProperty({ type: [UpdateFieldDto], description: 'Fields' }) + @IsArray() + fields: UpdateFieldDto[]; +} diff --git a/backend/src/CRM/entity-type/dto/update-entity-type.dto.ts b/backend/src/CRM/entity-type/dto/update-entity-type.dto.ts new file mode 100644 index 0000000..43625ba --- /dev/null +++ b/backend/src/CRM/entity-type/dto/update-entity-type.dto.ts @@ -0,0 +1,71 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; + +import { UpdateFieldGroupDto } from '@/modules/entity/entity-field/field-group/dto/update-field-group.dto'; +import { FieldsSettingsDto } from '@/modules/entity/entity-field/field/dto/fields-settings.dto'; +import { UpdateFieldDto } from '@/modules/entity/entity-field/field/dto/update-field.dto'; + +import { EntityCategory } from '../../common'; +import { CreateEntityTypeLinkDto } from '../../entity-type-link/dto'; +import { FeatureCode } from '../../feature'; +import { TaskFieldCode } from '../../task-settings/enums/task-field-code.enum'; + +import { EntityTypeSectionDto } from './entity-type-section.dto'; + +export class UpdateEntityTypeDto { + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Entity type name' }) + @IsString() + name: string; + + @ApiProperty({ enum: EntityCategory, description: 'Entity type category' }) + @IsEnum(EntityCategory) + entityCategory: EntityCategory; + + @ApiProperty({ type: EntityTypeSectionDto, description: 'Entity type section info' }) + section: EntityTypeSectionDto; + + @ApiProperty({ type: [UpdateFieldGroupDto], description: 'Entity type field groups' }) + @IsArray() + fieldGroups: UpdateFieldGroupDto[]; + + @ApiProperty({ type: [UpdateFieldDto], description: 'Entity type fields' }) + @IsArray() + fields: UpdateFieldDto[]; + + @ApiProperty({ type: [CreateEntityTypeLinkDto], description: 'Linked entity types' }) + @IsArray() + linkedEntityTypes: CreateEntityTypeLinkDto[]; + + @ApiProperty({ enum: FeatureCode, isArray: true, description: 'Feature codes' }) + @IsArray() + featureCodes: FeatureCode[]; + + @ApiPropertyOptional({ enum: TaskFieldCode, isArray: true, description: 'Task settings active fields' }) + @IsOptional() + @IsArray() + taskSettingsActiveFields: TaskFieldCode[]; + + @ApiPropertyOptional({ type: FieldsSettingsDto, description: 'Fields settings' }) + @IsOptional() + @IsObject() + fieldsSettings: FieldsSettingsDto; + + @ApiPropertyOptional({ description: 'Sort order' }) + @IsOptional() + @IsNumber() + sortOrder?: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Linked products section IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + linkedProductsSectionIds: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Linked scheduler IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + linkedSchedulerIds?: number[] | null; +} diff --git a/backend/src/CRM/entity-type/entities/entity-type.entity.ts b/backend/src/CRM/entity-type/entities/entity-type.entity.ts new file mode 100644 index 0000000..ab39e24 --- /dev/null +++ b/backend/src/CRM/entity-type/entities/entity-type.entity.ts @@ -0,0 +1,85 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { EntityCategory, PermissionObjectType } from '../../common'; +import { SectionView } from '../enums'; + +@Entity() +export class EntityType implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + name: string; + + @Column() + entityCategory: EntityCategory; + + @Column() + sectionName: string; + + @Column() + sectionView: SectionView; + + @Column() + sectionIcon: string; + + @Column() + sortOrder: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + name: string, + entityCategory: EntityCategory, + sectionName: string, + sectionView: SectionView, + sectionIcon: string, + sortOrder: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.name = name; + this.entityCategory = entityCategory; + this.sectionName = sectionName; + this.sectionView = sectionView; + this.sectionIcon = sectionIcon; + this.sortOrder = sortOrder; + this.createdAt = createdAt ?? DateUtil.now(); + } + + hasBoardSectionView(): boolean { + return this.sectionView === SectionView.BOARD; + } + + getAuthorizableObject(): AuthorizableObject { + return { type: PermissionObjectType.EntityType, id: this.id }; + } + static getAuthorizable(entityTypeId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.EntityType, id: entityTypeId }); + } + + public isProject() { + return this.entityCategory === EntityCategory.PROJECT; + } + + public static copy(accountId: number, et: EntityType): EntityType { + return new EntityType( + accountId, + et.name, + et.entityCategory, + et.sectionName, + et.sectionView, + et.sectionIcon, + et.sortOrder, + ); + } +} diff --git a/backend/src/CRM/entity-type/entities/index.ts b/backend/src/CRM/entity-type/entities/index.ts new file mode 100644 index 0000000..b714563 --- /dev/null +++ b/backend/src/CRM/entity-type/entities/index.ts @@ -0,0 +1 @@ +export * from './entity-type.entity'; diff --git a/backend/src/CRM/entity-type/entity-type.controller.ts b/backend/src/CRM/entity-type/entity-type.controller.ts new file mode 100644 index 0000000..2f357c5 --- /dev/null +++ b/backend/src/CRM/entity-type/entity-type.controller.ts @@ -0,0 +1,90 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { UserAccess } from '@/modules/iam/common/decorators/user-access.decorator'; + +import { EntityTypeDto, CreateEntityTypeDto, UpdateEntityTypeDto, UpdateEntityTypeFieldsDto } from './dto'; +import { EntityTypeService } from './entity-type.service'; + +@ApiTags('crm/entity-types') +@Controller('crm/entity-types') +@JwtAuthorized({ prefetch: { user: true } }) +export class EntityTypeController { + constructor(private readonly service: EntityTypeService) {} + + @ApiOperation({ summary: 'Create entity type', description: 'Create entity type' }) + @ApiBody({ type: CreateEntityTypeDto, description: 'Data for creating entity type' }) + @ApiCreatedResponse({ description: 'Created entity type', type: EntityTypeDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create( + @CurrentAuth() { accountId, user }: AuthData, + @Body() dto: CreateEntityTypeDto, + ): Promise { + return this.service.create(accountId, user, dto); + } + + @ApiOperation({ summary: 'Get entity types', description: 'Get all entity types' }) + @ApiOkResponse({ description: 'Entity types', type: [EntityTypeDto] }) + @Get() + public async getEntityTypes(@CurrentAuth() { accountId, user }: AuthData): Promise { + return this.service.getDtosByAccountId(accountId, user); + } + + @ApiOperation({ summary: 'Get entity type', description: 'Get entity type by id' }) + @ApiParam({ name: 'entityTypeId', description: 'Entity type ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Entity type', type: EntityTypeDto }) + @Get(':entityTypeId') + public async getEntityType( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + ): Promise { + return this.service.getDtoById(accountId, user, entityTypeId); + } + + @ApiOperation({ summary: 'Update entity type', description: 'Update entity type' }) + @ApiParam({ name: 'entityTypeId', description: 'Entity type ID', type: Number, required: true }) + @ApiBody({ type: UpdateEntityTypeDto, description: 'Data for updating entity type' }) + @ApiOkResponse({ description: 'Updated entity type', type: EntityTypeDto }) + @Put(':entityTypeId') + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() dto: UpdateEntityTypeDto, + ): Promise { + return this.service.update(accountId, user, entityTypeId, dto); + } + + @ApiOperation({ + summary: 'Update entity type fields and groups', + description: 'Update entity type fields and groups', + }) + @ApiParam({ name: 'entityTypeId', description: 'Entity type ID', type: Number, required: true }) + @ApiBody({ type: UpdateEntityTypeFieldsDto, description: 'Data for updating entity type fields and groups' }) + @ApiOkResponse({ description: 'Updated entity type', type: EntityTypeDto }) + @Put(':entityTypeId/fields') + @UserAccess({ adminOnly: true }) + public async updateEntityTypeFields( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() dto: UpdateEntityTypeFieldsDto, + ): Promise { + return this.service.updateFields(accountId, user, entityTypeId, dto); + } + + @ApiOperation({ summary: 'Delete entity type', description: 'Delete entity type' }) + @ApiParam({ name: 'entityTypeId', description: 'Entity type ID', type: Number, required: true }) + @ApiOkResponse() + @Delete(':entityTypeId') + @UserAccess({ adminOnly: true }) + public async delete( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + ) { + await this.service.delete(accountId, userId, entityTypeId); + } +} diff --git a/backend/src/CRM/entity-type/entity-type.service.ts b/backend/src/CRM/entity-type/entity-type.service.ts new file mode 100644 index 0000000..e20c4e0 --- /dev/null +++ b/backend/src/CRM/entity-type/entity-type.service.ts @@ -0,0 +1,403 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { ObjectPermissionService } from '@/modules/iam/object-permission/object-permission.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { ProductsSectionService } from '@/modules/inventory/products-section/services/products-section.service'; +import { ScheduleService } from '@/modules/scheduler/schedule/services/schedule.service'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldGroupService } from '@/modules/entity/entity-field/field-group/field-group.service'; +import { FieldCode } from '@/modules/entity/entity-field/field/enums/field-code.enum'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; + +import { CrmEventType, EntityCategory, EntityTypeEvent, PermissionObjectType, SortOrder } from '../common'; + +import { BoardService } from '../board/board.service'; +import { BoardType } from '../board'; +import { EntityTypeLinkService } from '../entity-type-link/entity-type-link.service'; +import { EntityTypeFeatureService, FeatureCode } from '../feature'; +import { TaskSettingsService } from '../task-settings/task-settings.service'; + +import { CreateEntityTypeDto, EntityTypeDto, UpdateEntityTypeDto, UpdateEntityTypeFieldsDto } from './dto'; +import { EntityType } from './entities'; +import { SectionView } from './enums'; +import { EntityTypeUsedInFormulaError } from './errors'; + +interface FindFilter { + id?: number | number[]; + name?: string; + category?: EntityCategory; +} + +@Injectable() +export class EntityTypeService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(EntityType) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly objectPermissionService: ObjectPermissionService, + private readonly fieldGroupService: FieldGroupService, + private readonly fieldService: FieldService, + private readonly entityTypeLinkService: EntityTypeLinkService, + private readonly entityTypeFeatureService: EntityTypeFeatureService, + private readonly taskSettingsService: TaskSettingsService, + @Inject(forwardRef(() => BoardService)) + private readonly boardService: BoardService, + private readonly productsSectionService: ProductsSectionService, + private readonly scheduleService: ScheduleService, + ) {} + + public async save(entityType: EntityType): Promise { + return await this.repository.save(entityType); + } + + public async create(accountId: number, user: User, dto: CreateEntityTypeDto): Promise { + const sectionView = dto.entityCategory === EntityCategory.PROJECT ? SectionView.BOARD : dto.section.view; + + const entityType = await this.repository.save( + new EntityType( + accountId, + dto.name, + dto.entityCategory, + dto.section.name, + sectionView, + dto.section.icon, + dto.sortOrder ?? (await this.getMaxSortOrder(accountId)), + ), + ); + + if (entityType.entityCategory === EntityCategory.PROJECT) { + await this.createProjectSystemFields(entityType.accountId, entityType); + } + + if (entityType.sectionView === SectionView.BOARD) { + await this.boardService.create({ + accountId, + user, + dto: { + name: entityType.name, + type: BoardType.EntityType, + recordId: entityType.id, + sortOrder: 0, + }, + }); + } + + await this.fieldGroupService.saveBatch({ accountId, entityTypeId: entityType.id, dtos: dto.fieldGroups }); + await this.fieldService.createMany({ accountId, entityTypeId: entityType.id, dtos: dto.fields }); + + await this.entityTypeLinkService.processMany({ accountId, sourceId: entityType.id, dtos: dto.linkedEntityTypes }); + await this.entityTypeFeatureService.setFeaturesForEntityType(accountId, entityType.id, dto.featureCodes); + + if (dto.linkedProductsSectionIds) { + await this.productsSectionService.linkSections(accountId, entityType.id, dto.linkedProductsSectionIds); + } + if (dto.linkedSchedulerIds) { + await this.scheduleService.linkEntityType(accountId, dto.linkedSchedulerIds, entityType.id); + } + + await this.createTaskSettingsIfNeeded(accountId, entityType.id, dto); + + if (entityType.entityCategory === EntityCategory.PROJECT) { + await this.fieldService.updateFieldsSettings({ + accountId, + entityTypeId: entityType.id, + activeFieldCodes: dto.fieldsSettings.activeFieldCodes, + }); + } + + return this.getDtoById(accountId, user, entityType.id); + } + + private async getMaxSortOrder(accountId: number): Promise { + const result = await this.repository + .createQueryBuilder('et') + .select('MAX(et.sortOrder)', 'max') + .where('et.accountId = :accountId', { accountId }) + .getRawOne(); + + return Math.min(Number(result.max ?? 0) + 1, SortOrder.Last); + } + + private async createTaskSettingsIfNeeded(accountId: number, entityTypeId: number, dto: CreateEntityTypeDto) { + if (dto.featureCodes.includes(FeatureCode.TASK)) { + await this.taskSettingsService.setTaskSettingsForEntityType( + accountId, + entityTypeId, + dto.taskSettingsActiveFields, + ); + } + } + + public async getById(accountId: number, id: number): Promise { + const entityType = await this.findOne(accountId, { id }); + if (!entityType) { + throw NotFoundError.withId(EntityType, id); + } + + return entityType; + } + + public async getDtoById(accountId: number, user: User, id: number): Promise { + const entityType = await this.getById(accountId, id); + + return this.createDto(accountId, user, entityType); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).orderBy('et.sort_order', 'ASC').getMany(); + } + + public async getDtosByAccountId(accountId: number, user: User): Promise { + const entityTypes = await this.findMany(accountId); + const dtos = []; + + for (const entityType of entityTypes) { + dtos.push(await this.getDtoById(accountId, user, entityType.id)); + } + + return dtos; + } + + public async getAccessibleForUser(accountId: number, user: User): Promise { + const entityTypes = await this.findMany(accountId); + const result: EntityType[] = []; + for (const et of entityTypes) { + if (await this.authService.check({ action: 'view', user, authorizable: et })) { + result.push(et); + } + } + return result; + } + + public async findLinkedTypes(accountId: number, entityTypeId: number): Promise { + const links = await this.entityTypeLinkService.findMany({ accountId, sourceId: entityTypeId }); + return this.repository.findBy({ id: In(links.map((l) => l.targetId)) }); + } + + public async update( + accountId: number, + user: User, + entityTypeId: number, + dto: UpdateEntityTypeDto, + ): Promise { + const links = await this.entityTypeLinkService.findMany({ accountId, sourceId: entityTypeId }); + const usedInFormula = await Promise.all( + links.map(async (link) => + !dto.linkedEntityTypes.some((linked) => linked.targetId === link.targetId) + ? await this.fieldService.checkFormulaUsageEntityType({ + accountId, + entityTypeId, + checkEntityTypeId: link.targetId, + }) + : false, + ), + ); + if (usedInFormula.some((usage) => usage)) { + throw new EntityTypeUsedInFormulaError(); + } + + await this.repository.update( + { id: entityTypeId }, + { + name: dto.name, + entityCategory: dto.entityCategory, + sectionName: dto.section.name, + sectionView: dto.section.view, + sectionIcon: dto.section.icon, + sortOrder: dto.sortOrder, + }, + ); + + await this.fieldGroupService.saveBatch({ accountId, entityTypeId, dtos: dto.fieldGroups }); + await this.fieldService.updateBatch({ accountId, entityTypeId, dtos: dto.fields }); + + await this.entityTypeLinkService.processMany({ accountId, sourceId: entityTypeId, dtos: dto.linkedEntityTypes }); + await this.entityTypeFeatureService.setFeaturesForEntityType(accountId, entityTypeId, dto.featureCodes); + + if (dto.linkedProductsSectionIds) { + await this.productsSectionService.linkSections(accountId, entityTypeId, dto.linkedProductsSectionIds); + } + if (dto.linkedSchedulerIds) { + await this.scheduleService.linkEntityType(accountId, dto.linkedSchedulerIds, entityTypeId); + } + + if (dto.featureCodes.includes(FeatureCode.TASK)) { + await this.taskSettingsService.setTaskSettingsForEntityType( + accountId, + entityTypeId, + dto.taskSettingsActiveFields, + ); + } + if (dto.entityCategory === EntityCategory.PROJECT && dto.fieldsSettings) { + await this.fieldService.updateFieldsSettings({ + accountId, + entityTypeId, + activeFieldCodes: dto.fieldsSettings.activeFieldCodes, + }); + } + + return this.getDtoById(accountId, user, entityTypeId); + } + + public async updateFields( + accountId: number, + user: User, + entityTypeId: number, + dto: UpdateEntityTypeFieldsDto, + ): Promise { + await this.fieldGroupService.saveBatch({ accountId, entityTypeId, dtos: dto.fieldGroups }); + await this.fieldService.updateBatch({ accountId, entityTypeId, dtos: dto.fields }); + + return this.getDtoById(accountId, user, entityTypeId); + } + + public async delete(accountId: number, userId: number, entityTypeId: number): Promise { + if ( + await this.fieldService.checkFormulaUsageEntityType({ + accountId, + excludeEntityTypeId: entityTypeId, + checkEntityTypeId: entityTypeId, + }) + ) { + throw new EntityTypeUsedInFormulaError(); + } + + await this.repository.delete({ id: entityTypeId, accountId }); + await this.objectPermissionService.delete({ + accountId, + objectType: PermissionObjectType.EntityType, + objectId: entityTypeId, + }); + this.eventEmitter.emit(CrmEventType.EntityTypeDeleted, new EntityTypeEvent({ accountId, userId, entityTypeId })); + } + + private async createDto(accountId: number, user: User, entityType: EntityType) { + const fieldGroups = await this.fieldGroupService.findMany({ accountId, entityTypeId: entityType.id }); + const fields = await this.fieldService.findMany( + { accountId, entityTypeId: entityType.id }, + { expand: ['options'] }, + ); + const links = await this.entityTypeLinkService.findMany({ accountId, sourceId: entityType.id }); + const featureCodes = await this.entityTypeFeatureService.getFeatureCodesForEntityType(accountId, entityType.id); + const sectionIds = await this.productsSectionService.getLinkedSectionIds(accountId, user, entityType.id); + const schedulerIds = await this.scheduleService.getLinkedSchedulerIds(accountId, { entityTypeId: entityType.id }); + + return new EntityTypeDto({ + id: entityType.id, + name: entityType.name, + entityCategory: entityType.entityCategory, + createdAt: entityType.createdAt.toISOString(), + section: { + name: entityType.sectionName, + view: entityType.sectionView, + icon: entityType.sectionIcon, + }, + sortOrder: entityType.sortOrder, + fieldGroups: fieldGroups.map((fieldGroup) => fieldGroup.toDto()), + fields: fields.map((field) => field.toDto()), + featureCodes: featureCodes, + linkedEntityTypes: links.map((link) => link.toDto()), + linkedProductsSectionIds: sectionIds, + linkedSchedulerIds: schedulerIds, + }); + } + + private async createProjectSystemFields(accountId: number, entityType: EntityType) { + await this.fieldService.create({ + accountId, + entityTypeId: entityType.id, + dto: { + name: 'Value', + type: FieldType.Value, + code: FieldCode.Value, + active: true, + sortOrder: -1, + entityTypeId: entityType.id, + fieldGroupId: null, + }, + }); + await this.fieldService.create({ + accountId, + entityTypeId: entityType.id, + dto: { + name: 'Start date', + type: FieldType.Date, + code: FieldCode.StartDate, + active: true, + sortOrder: -1, + entityTypeId: entityType.id, + fieldGroupId: null, + }, + }); + await this.fieldService.create({ + accountId, + entityTypeId: entityType.id, + dto: { + name: 'End date', + type: FieldType.Date, + code: FieldCode.EndDate, + active: true, + sortOrder: -1, + entityTypeId: entityType.id, + fieldGroupId: null, + }, + }); + await this.fieldService.create({ + accountId, + entityTypeId: entityType.id, + dto: { + name: 'Participants', + type: FieldType.Participants, + code: FieldCode.Participants, + active: true, + sortOrder: -1, + entityTypeId: entityType.id, + fieldGroupId: null, + }, + }); + await this.fieldService.create({ + accountId, + entityTypeId: entityType.id, + dto: { + name: 'Description', + type: FieldType.Text, + code: FieldCode.Description, + active: true, + sortOrder: -1, + entityTypeId: entityType.id, + fieldGroupId: null, + }, + }); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('et').where('et.account_id = :accountId', { accountId }); + + if (filter?.id) { + if (Array.isArray(filter.id)) { + qb.andWhere('et.id IN (:...ids)', { ids: filter.id }); + } else { + qb.andWhere('et.id = :id', { id: filter.id }); + } + } + if (filter?.name) { + qb.andWhere('et.name = :name', { name: filter.name }); + } + if (filter?.category) { + qb.andWhere('et.entity_category = :category', { category: filter.category }); + } + + return qb; + } +} diff --git a/backend/src/CRM/entity-type/enums/index.ts b/backend/src/CRM/entity-type/enums/index.ts new file mode 100644 index 0000000..e75e9ea --- /dev/null +++ b/backend/src/CRM/entity-type/enums/index.ts @@ -0,0 +1 @@ +export * from './section-view.enum'; diff --git a/backend/src/CRM/entity-type/enums/section-view.enum.ts b/backend/src/CRM/entity-type/enums/section-view.enum.ts new file mode 100644 index 0000000..3c98232 --- /dev/null +++ b/backend/src/CRM/entity-type/enums/section-view.enum.ts @@ -0,0 +1,5 @@ +export enum SectionView { + PIPELINE = 'pipeline', + LIST = 'list', + BOARD = 'board', +} diff --git a/backend/src/CRM/entity-type/errors/entity-type-used-in-formula.error.ts b/backend/src/CRM/entity-type/errors/entity-type-used-in-formula.error.ts new file mode 100644 index 0000000..70268cf --- /dev/null +++ b/backend/src/CRM/entity-type/errors/entity-type-used-in-formula.error.ts @@ -0,0 +1,12 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class EntityTypeUsedInFormulaError extends ServiceError { + constructor(message = 'Entity type used in formula') { + super({ + errorCode: 'entity_type.used_in_formula', + status: HttpStatus.BAD_REQUEST, + message, + }); + } +} diff --git a/backend/src/CRM/entity-type/errors/index.ts b/backend/src/CRM/entity-type/errors/index.ts new file mode 100644 index 0000000..0894482 --- /dev/null +++ b/backend/src/CRM/entity-type/errors/index.ts @@ -0,0 +1 @@ +export * from './entity-type-used-in-formula.error'; diff --git a/backend/src/CRM/entity-type/index.ts b/backend/src/CRM/entity-type/index.ts new file mode 100644 index 0000000..61e2dd1 --- /dev/null +++ b/backend/src/CRM/entity-type/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './errors'; diff --git a/backend/src/CRM/feature/dto/feature.dto.ts b/backend/src/CRM/feature/dto/feature.dto.ts new file mode 100644 index 0000000..06b14a1 --- /dev/null +++ b/backend/src/CRM/feature/dto/feature.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsString } from 'class-validator'; + +export class FeatureDto { + @ApiProperty({ description: 'Feature ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Feature name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Feature code' }) + @IsString() + code: string; + + @ApiProperty({ description: 'Is feature enabled' }) + @IsBoolean() + isEnabled: boolean; + + constructor(id: number, name: string, code: string, isEnabled: boolean) { + this.id = id; + this.name = name; + this.code = code; + this.isEnabled = isEnabled; + } +} diff --git a/backend/src/CRM/feature/dto/index.ts b/backend/src/CRM/feature/dto/index.ts new file mode 100644 index 0000000..ad3e3d4 --- /dev/null +++ b/backend/src/CRM/feature/dto/index.ts @@ -0,0 +1 @@ +export * from './feature.dto'; diff --git a/backend/src/CRM/feature/entities/entity-type-feature.entity.ts b/backend/src/CRM/feature/entities/entity-type-feature.entity.ts new file mode 100644 index 0000000..63fd1f1 --- /dev/null +++ b/backend/src/CRM/feature/entities/entity-type-feature.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class EntityTypeFeature { + @PrimaryColumn() + entityTypeId: number; + + @PrimaryColumn() + featureId: number; + + @Column() + accountId: number; + + constructor(entityTypeId: number, featureId: number, accountId: number) { + this.entityTypeId = entityTypeId; + this.featureId = featureId; + this.accountId = accountId; + } +} diff --git a/backend/src/CRM/feature/entities/feature.entity.ts b/backend/src/CRM/feature/entities/feature.entity.ts new file mode 100644 index 0000000..ab663a0 --- /dev/null +++ b/backend/src/CRM/feature/entities/feature.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { FeatureDto } from '../dto'; + +@Entity() +export class Feature { + @PrimaryColumn() + id: number; + + @Column() + name: string; + + @Column() + code: string; + + @Column() + isEnabled: boolean; + + constructor(id: number, name: string, code: string, isEnabled: boolean) { + this.id = id; + this.name = name; + this.code = code; + this.isEnabled = isEnabled; + } + + public toDto(): FeatureDto { + return new FeatureDto(this.id, this.name, this.code, this.isEnabled); + } +} diff --git a/backend/src/CRM/feature/entities/index.ts b/backend/src/CRM/feature/entities/index.ts new file mode 100644 index 0000000..3a2df5e --- /dev/null +++ b/backend/src/CRM/feature/entities/index.ts @@ -0,0 +1,2 @@ +export * from './entity-type-feature.entity'; +export * from './feature.entity'; diff --git a/backend/src/CRM/feature/entity-type-feature.service.ts b/backend/src/CRM/feature/entity-type-feature.service.ts new file mode 100644 index 0000000..aec9aea --- /dev/null +++ b/backend/src/CRM/feature/entity-type-feature.service.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { EntityTypeFeature } from './entities'; +import { FeatureCode } from './enums'; +import { FeatureService } from './feature.service'; + +@Injectable() +export class EntityTypeFeatureService { + constructor( + @InjectRepository(EntityTypeFeature) + private repository: Repository, + private featureService: FeatureService, + ) {} + + public async findByEntityTypeId(accountId: number, entityTypeId: number): Promise { + return await this.repository.findBy({ accountId, entityTypeId }); + } + + public async getFeatureCodesForEntityType(accountId: number, entityTypeId: number): Promise { + const etfs = await this.findByEntityTypeId(accountId, entityTypeId); + const features = await this.featureService.getByIds(etfs.map((etf) => etf.featureId)); + + return features.map((feature) => feature.code as FeatureCode); + } + + public async setEntityTypeFeatures(accountId: number, entityTypeId: number, featureIds: number[]): Promise { + await this.repository.delete({ entityTypeId }); + await this.repository.insert( + featureIds.map((featureId) => new EntityTypeFeature(entityTypeId, featureId, accountId)), + ); + return featureIds; + } + + public async setFeaturesForEntityType( + accountId: number, + entityTypeId: number, + featureCodes: FeatureCode[], + ): Promise { + const features = await this.featureService.getByCodes(featureCodes); + const featureIds = features.map((feature) => feature.id); + + return await this.setEntityTypeFeatures(accountId, entityTypeId, featureIds); + } + + public async setFeatureForEntityTypes( + accountId: number, + entityTypes: number[], + featureCode: FeatureCode, + ): Promise { + const feature = await this.featureService.getByCode(featureCode); + + await this.repository.delete({ featureId: feature.id }); + + const features = await this.repository.save( + entityTypes.map((entityTypeId) => new EntityTypeFeature(entityTypeId, feature.id, accountId)), + ); + + return features.length > 0; + } + + public async getEntityTypeIdsWithFeature(accountId: number, featureCode: FeatureCode): Promise { + const feature = await this.featureService.getByCode(featureCode); + + return (await this.repository.findBy({ accountId, featureId: feature.id })).map((etf) => etf.entityTypeId); + } +} diff --git a/backend/src/CRM/feature/enums/feature-code.enum.ts b/backend/src/CRM/feature/enums/feature-code.enum.ts new file mode 100644 index 0000000..7d080c8 --- /dev/null +++ b/backend/src/CRM/feature/enums/feature-code.enum.ts @@ -0,0 +1,9 @@ +export enum FeatureCode { + ACTIVITY = 'activities', + TASK = 'tasks', + NOTE = 'notes', + CHAT = 'chat', + ENTITY_FILES = 'saveFiles', + ENTITY_DOCUMENTS = 'documents', + PRODUCTS = 'products', +} diff --git a/backend/src/CRM/feature/enums/index.ts b/backend/src/CRM/feature/enums/index.ts new file mode 100644 index 0000000..ef7a5c2 --- /dev/null +++ b/backend/src/CRM/feature/enums/index.ts @@ -0,0 +1 @@ +export * from './feature-code.enum'; diff --git a/backend/src/CRM/feature/feature.controller.ts b/backend/src/CRM/feature/feature.controller.ts new file mode 100644 index 0000000..900615c --- /dev/null +++ b/backend/src/CRM/feature/feature.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FeatureDto } from './dto'; +import { FeatureService } from './feature.service'; + +@ApiTags('crm/features') +@Controller('crm/features') +@JwtAuthorized() +@TransformToDto() +export class FeatureController { + constructor(private readonly service: FeatureService) {} + + @ApiOperation({ summary: 'Get features', description: 'Get all features' }) + @ApiOkResponse({ description: 'Features', type: [FeatureDto] }) + @Get() + public async getEnabled() { + return this.service.getEnabled(); + } +} diff --git a/backend/src/CRM/feature/feature.service.ts b/backend/src/CRM/feature/feature.service.ts new file mode 100644 index 0000000..41e1501 --- /dev/null +++ b/backend/src/CRM/feature/feature.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { Feature } from './entities'; +import { FeatureCode } from './enums'; + +@Injectable() +export class FeatureService { + constructor( + @InjectRepository(Feature) + private featureRepository: Repository, + ) {} + + public async getEnabled(): Promise { + return await this.featureRepository.find({ where: { isEnabled: true }, order: { id: 'ASC' } }); + } + + public async getByCode(code: FeatureCode): Promise { + const feature = this.featureRepository.findOneBy({ code }); + if (!feature) { + throw NotFoundError.withMessage(Feature, `with code '${code}' is not found`); + } + + return feature; + } + + public async getByCodes(codes: FeatureCode[]): Promise { + return await this.featureRepository.findBy({ code: In(codes) }); + } + + public async getByIds(ids: number[]): Promise { + return await this.featureRepository.findBy({ id: In(ids) }); + } +} diff --git a/backend/src/CRM/feature/index.ts b/backend/src/CRM/feature/index.ts new file mode 100644 index 0000000..8b54001 --- /dev/null +++ b/backend/src/CRM/feature/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './entity-type-feature.service'; +export * from './enums'; +export * from './feature.controller'; +export * from './feature.service'; diff --git a/backend/src/CRM/identity/dto/identity-pool.dto.ts b/backend/src/CRM/identity/dto/identity-pool.dto.ts new file mode 100644 index 0000000..204bbd8 --- /dev/null +++ b/backend/src/CRM/identity/dto/identity-pool.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +import { SequenceName } from '../../common'; + +export class IdentityPoolDto { + @ApiProperty({ enum: SequenceName, description: 'Identity pool name' }) + @IsString() + name: SequenceName; + + @ApiProperty({ description: 'Identity pool values', type: [Number] }) + @IsNumber({}, { each: true }) + values: number[]; +} diff --git a/backend/src/CRM/identity/dto/index.ts b/backend/src/CRM/identity/dto/index.ts new file mode 100644 index 0000000..a9f0011 --- /dev/null +++ b/backend/src/CRM/identity/dto/index.ts @@ -0,0 +1 @@ +export * from './identity-pool.dto'; diff --git a/backend/src/CRM/identity/identity.controller.ts b/backend/src/CRM/identity/identity.controller.ts new file mode 100644 index 0000000..0e864bf --- /dev/null +++ b/backend/src/CRM/identity/identity.controller.ts @@ -0,0 +1,32 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SequenceName } from '../common'; +import { IdentityPoolDto } from './dto'; +import { IdentityService } from './identity.service'; + +@ApiTags('crm/identity') +@Controller('crm/identities') +@JwtAuthorized() +@TransformToDto() +export class IdentityController { + constructor(private readonly service: IdentityService) {} + + @ApiOperation({ summary: 'Get all available identity pools', description: 'Get all available identity pools' }) + @ApiOkResponse({ type: [IdentityPoolDto], description: 'All available identity pools' }) + @Get('all') + public async getAllIdentityPools(): Promise { + return await this.service.getMany(); + } + + @ApiOperation({ summary: 'Get identity pool for sequence name', description: 'Get identity pool for sequence name' }) + @ApiParam({ name: 'name', enum: SequenceName, description: 'Sequence name', required: true }) + @ApiOkResponse({ type: [Number], description: 'Identity pool values' }) + @Get(':name') + public async getIdentityPool(@Param('name') name: SequenceName): Promise { + return await this.service.getOne(name); + } +} diff --git a/backend/src/CRM/identity/identity.service.ts b/backend/src/CRM/identity/identity.service.ts new file mode 100644 index 0000000..861a462 --- /dev/null +++ b/backend/src/CRM/identity/identity.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@nestjs/common'; + +import { NotFoundError } from '@/common'; +import { SequenceIdService } from '@/database'; + +import { SequenceName } from '../common'; +import { IdentityPool } from './types'; + +const DefaultPoolSize = 20; + +@Injectable() +export class IdentityService { + constructor(private readonly sequenceIdService: SequenceIdService) {} + + public async getOne(sequence: SequenceName): Promise { + if (!Object.values(SequenceName).includes(sequence)) { + throw new NotFoundError(`Sequence with name '${sequence}' is not found`); + } else { + return await this.sequenceIdService.getIdentityPool(sequence, DefaultPoolSize); + } + } + + public async getMany(): Promise { + const sequences = Object.values(SequenceName); + const pool: IdentityPool[] = []; + for (const sequence of sequences) { + const values = await this.getOne(sequence); + pool.push(new IdentityPool({ name: sequence, values })); + } + return pool; + } +} diff --git a/backend/src/CRM/identity/types/identity-pool.ts b/backend/src/CRM/identity/types/identity-pool.ts new file mode 100644 index 0000000..6f087c8 --- /dev/null +++ b/backend/src/CRM/identity/types/identity-pool.ts @@ -0,0 +1,16 @@ +import { SequenceName } from '../../common'; +import { IdentityPoolDto } from '../dto'; + +export class IdentityPool { + name: SequenceName; + values: number[]; + + constructor({ name, values }: { name: SequenceName; values: number[] }) { + this.name = name; + this.values = values; + } + + public toDto(): IdentityPoolDto { + return { name: this.name, values: this.values }; + } +} diff --git a/backend/src/CRM/identity/types/index.ts b/backend/src/CRM/identity/types/index.ts new file mode 100644 index 0000000..2fa162a --- /dev/null +++ b/backend/src/CRM/identity/types/index.ts @@ -0,0 +1 @@ +export * from './identity-pool'; diff --git a/backend/src/CRM/note/dto/create-note.dto.ts b/backend/src/CRM/note/dto/create-note.dto.ts new file mode 100644 index 0000000..57ba1a0 --- /dev/null +++ b/backend/src/CRM/note/dto/create-note.dto.ts @@ -0,0 +1,10 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; + +import { NoteDto } from './note.dto'; + +export class CreateNoteDto extends PickType(NoteDto, ['text']) { + @ApiPropertyOptional({ type: [String], nullable: true, description: 'File IDs' }) + @IsOptional() + fileIds?: string[] | null; +} diff --git a/backend/src/CRM/note/dto/index.ts b/backend/src/CRM/note/dto/index.ts new file mode 100644 index 0000000..db33b34 --- /dev/null +++ b/backend/src/CRM/note/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-note.dto'; +export * from './note.dto'; +export * from './update-note.dto'; diff --git a/backend/src/CRM/note/dto/note.dto.ts b/backend/src/CRM/note/dto/note.dto.ts new file mode 100644 index 0000000..9b48051 --- /dev/null +++ b/backend/src/CRM/note/dto/note.dto.ts @@ -0,0 +1,30 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +export class NoteDto { + @ApiProperty({ description: 'Note ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Entity ID' }) + @IsNumber() + entityId: number; + + @ApiProperty({ description: 'User ID' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'Note text' }) + @IsString() + text: string; + + @ApiProperty({ description: 'Note creation date' }) + @IsDateString() + createdAt: string; + + @ApiPropertyOptional({ type: [FileLinkDto], nullable: true, description: 'File links' }) + @IsOptional() + fileLinks?: FileLinkDto[] | null; +} diff --git a/backend/src/CRM/note/dto/update-note.dto.ts b/backend/src/CRM/note/dto/update-note.dto.ts new file mode 100644 index 0000000..b22973a --- /dev/null +++ b/backend/src/CRM/note/dto/update-note.dto.ts @@ -0,0 +1,10 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; + +import { NoteDto } from './note.dto'; + +export class UpdateNoteDto extends PickType(NoteDto, ['text']) { + @ApiPropertyOptional({ type: [String], nullable: true, description: 'File IDs' }) + @IsOptional() + fileIds?: string[] | null; +} diff --git a/backend/src/CRM/note/entities/index.ts b/backend/src/CRM/note/entities/index.ts new file mode 100644 index 0000000..511e546 --- /dev/null +++ b/backend/src/CRM/note/entities/index.ts @@ -0,0 +1 @@ +export * from './note.entity'; diff --git a/backend/src/CRM/note/entities/note.entity.ts b/backend/src/CRM/note/entities/note.entity.ts new file mode 100644 index 0000000..56f25c5 --- /dev/null +++ b/backend/src/CRM/note/entities/note.entity.ts @@ -0,0 +1,52 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; +import { NoteDto, UpdateNoteDto } from '../dto'; + +@Entity('note') +export class Note { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + entityId: number; + + @Column() + createdBy: number; + + @Column() + text: string; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, entityId: number, userId: number, text: string, createdAt?: Date) { + this.accountId = accountId; + this.entityId = entityId; + this.createdBy = userId; + this.text = text; + this.createdAt = createdAt ?? DateUtil.now(); + } + + update(dto: UpdateNoteDto): Note { + this.text = dto.text !== undefined ? dto.text : this.text; + + return this; + } + + toDto(fileLinks?: FileLinkDto[] | null): NoteDto { + return { + id: this.id, + entityId: this.entityId, + createdBy: this.createdBy, + text: this.text, + createdAt: this.createdAt.toISOString(), + fileLinks: fileLinks, + }; + } +} diff --git a/backend/src/CRM/note/index.ts b/backend/src/CRM/note/index.ts new file mode 100644 index 0000000..4001c9f --- /dev/null +++ b/backend/src/CRM/note/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './note.controller'; +export * from './note.handler'; +export * from './note.service'; diff --git a/backend/src/CRM/note/note.controller.ts b/backend/src/CRM/note/note.controller.ts new file mode 100644 index 0000000..74d0e0c --- /dev/null +++ b/backend/src/CRM/note/note.controller.ts @@ -0,0 +1,96 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Put } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CreateNoteDto, NoteDto, UpdateNoteDto } from './dto'; +import { NoteService } from './note.service'; + +@ApiTags('crm/entities/notes') +@Controller('crm/entities/:entityId/notes') +@JwtAuthorized({ prefetch: { account: true } }) +@TransformToDto() +export class NoteController { + constructor(private readonly service: NoteService) {} + + @ApiOperation({ summary: 'Create entity note', description: 'Create entity note' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiBody({ type: CreateNoteDto, required: true, description: 'Entity note data' }) + @ApiCreatedResponse({ description: 'Entity note', type: NoteDto }) + @Post() + async create( + @CurrentAuth() { account, userId }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + @Body() dto: CreateNoteDto, + ): Promise { + return this.service.createAndGetDto({ account, userId, entityId, dto }); + } + + @ApiOperation({ summary: 'Get entity note', description: 'Get entity note' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiParam({ name: 'noteId', type: Number, required: true, description: 'Entity note ID' }) + @ApiOkResponse({ description: 'Entity note', type: NoteDto }) + @Get(':noteId') + async findOne( + @CurrentAuth() { account }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + @Param('noteId', ParseIntPipe) noteId: number, + ): Promise { + return this.service.findOneDto({ account, filter: { entityId, noteId } }); + } + + @ApiOperation({ summary: 'Get entity notes', description: 'Get entity notes' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiOkResponse({ description: 'Entity notes', type: NoteDto }) + @Get() + async findMany( + @CurrentAuth() { account }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + ): Promise { + return this.service.findManyDto({ account, filter: { entityId } }); + } + + @ApiOperation({ summary: 'Update entity note', description: 'Update entity note' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiParam({ name: 'noteId', type: Number, required: true, description: 'Entity note ID' }) + @ApiBody({ type: UpdateNoteDto, required: true, description: 'Entity note data' }) + @ApiOkResponse({ description: 'Entity note', type: NoteDto }) + @Put(':noteId') + async updatePut( + @CurrentAuth() { account }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + @Param('noteId', ParseIntPipe) noteId: number, + @Body() dto: UpdateNoteDto, + ): Promise { + return this.service.updateAndGetDto({ account, entityId, noteId, dto }); + } + + @ApiOperation({ summary: 'Update entity note', description: 'Update entity note' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiParam({ name: 'noteId', type: Number, required: true, description: 'Entity note ID' }) + @ApiBody({ type: UpdateNoteDto, required: true, description: 'Entity note data' }) + @ApiOkResponse({ description: 'Entity note', type: NoteDto }) + @Patch(':noteId') + async updatePatch( + @CurrentAuth() { account }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + @Param('noteId', ParseIntPipe) noteId: number, + @Body() dto: UpdateNoteDto, + ): Promise { + return this.service.updateAndGetDto({ account, entityId, noteId, dto }); + } + + @ApiOperation({ summary: 'Delete entity note', description: 'Delete entity note' }) + @ApiParam({ name: 'entityId', type: Number, required: true, description: 'Entity ID' }) + @ApiParam({ name: 'noteId', type: Number, required: true, description: 'Entity note ID' }) + @ApiOkResponse() + @Delete(':noteId') + async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + @Param('noteId', ParseIntPipe) noteId: number, + ) { + await this.service.delete({ accountId, entityId, noteId }); + } +} diff --git a/backend/src/CRM/note/note.handler.ts b/backend/src/CRM/note/note.handler.ts new file mode 100644 index 0000000..610ff68 --- /dev/null +++ b/backend/src/CRM/note/note.handler.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { CrmEventType, EntityCreatedEvent } from '../common'; +import { NoteService } from './note.service'; + +@Injectable() +export class NoteHandler { + constructor(private readonly service: NoteService) {} + + @OnEvent(CrmEventType.EntityCreated, { async: true }) + async onEntityCreated(event: EntityCreatedEvent): Promise { + if (event.copiedFrom) { + await this.service.copyEntityNotes({ + accountId: event.accountId, + sourceEntityId: event.copiedFrom, + targetEntityId: event.entityId, + }); + } + } +} diff --git a/backend/src/CRM/note/note.service.ts b/backend/src/CRM/note/note.service.ts new file mode 100644 index 0000000..7d708c5 --- /dev/null +++ b/backend/src/CRM/note/note.service.ts @@ -0,0 +1,206 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NotFoundError, FileLinkSource } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; + +import { CrmEventType, NoteCreatedEvent, NoteEvent } from '../common'; +import { FileLinkService } from '../Service/FileLink/FileLinkService'; + +import { Note } from './entities'; +import { CreateNoteDto, NoteDto, UpdateNoteDto } from './dto'; + +interface CreateOptions { + createdAt?: Date; +} +interface FindFilterDto { + noteId?: number; + entityId?: number; +} +interface FindFilter extends FindFilterDto { + accountId: number; +} + +@Injectable() +export class NoteService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Note) + private readonly repository: Repository, + private readonly fileLinkService: FileLinkService, + private readonly entityInfoService: EntityInfoService, + ) {} + + async create({ + accountId, + userId, + entityId, + dto, + options, + }: { + accountId: number; + userId: number; + entityId: number; + dto: CreateNoteDto; + options?: CreateOptions; + }): Promise { + const note = await this.repository.save(new Note(accountId, entityId, userId, dto.text, options?.createdAt)); + if (dto.fileIds) { + await this.fileLinkService.processFiles(accountId, FileLinkSource.NOTE, note.id, dto.fileIds); + } + + const entityInfo = await this.entityInfoService.findOne({ accountId, entityId }); + this.eventEmitter.emit( + CrmEventType.NoteCreated, + new NoteCreatedEvent({ + accountId, + entityId: entityInfo.id, + entityName: entityInfo.name, + createdBy: userId, + ownerId: entityInfo.ownerId, + noteId: note.id, + noteText: note.text, + createdAt: note.createdAt.toISOString(), + }), + ); + + return note; + } + + async createAndGetDto({ + account, + userId, + entityId, + dto, + }: { + account: Account; + userId: number; + entityId: number; + dto: CreateNoteDto; + }): Promise { + const note = await this.create({ accountId: account.id, userId, entityId, dto }); + + return this.createDtoForNote({ account, note }); + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).orderBy('note.created_at', 'DESC').getMany(); + } + + async findOneDto({ account, filter }: { account: Account; filter: FindFilterDto }): Promise { + const note = await this.findOne({ accountId: account.id, ...filter }); + + return note ? this.createDtoForNote({ account, note }) : null; + } + async findManyDto({ account, filter }: { account: Account; filter: FindFilterDto }): Promise { + const notes = await this.findMany({ accountId: account.id, ...filter }); + + return Promise.all(notes.map((note) => this.createDtoForNote({ account, note }))); + } + + async update({ + accountId, + entityId, + noteId, + dto, + }: { + accountId: number; + entityId: number; + noteId: number; + dto: UpdateNoteDto; + }): Promise { + const note = await this.findOne({ accountId, entityId, noteId }); + if (!note) { + throw NotFoundError.withId(Note, noteId); + } + await this.repository.save(note.update(dto)); + + if (dto.fileIds) { + await this.fileLinkService.processFiles(accountId, FileLinkSource.NOTE, noteId, dto.fileIds); + } + + return note; + } + + async updateAndGetDto({ + account, + entityId, + noteId, + dto, + }: { + account: Account; + entityId: number; + noteId: number; + dto: UpdateNoteDto; + }): Promise { + const note = await this.update({ accountId: account.id, entityId, noteId, dto }); + + return this.createDtoForNote({ account, note }); + } + + async delete({ accountId, entityId, noteId }: { accountId: number; entityId: number; noteId: number }) { + const note = await this.findOne({ accountId, entityId, noteId }); + if (!note) { + throw NotFoundError.withId(Note, noteId); + } + + await this.fileLinkService.processFiles(accountId, FileLinkSource.NOTE, noteId, []); + await this.repository.delete({ accountId, entityId, id: noteId }); + + this.eventEmitter.emit( + CrmEventType.NoteDeleted, + new NoteEvent({ accountId: accountId, entityId: entityId, noteId: note.id }), + ); + } + + async copyEntityNotes({ + accountId, + sourceEntityId, + targetEntityId, + }: { + accountId: number; + sourceEntityId: number; + targetEntityId: number; + }) { + const notes = await this.findMany({ accountId, entityId: sourceEntityId }); + await Promise.all( + notes.map((note) => + this.create({ + accountId, + userId: note.createdBy, + entityId: targetEntityId, + dto: { text: note.text }, + options: { createdAt: note.createdAt }, + }), + ), + ); + } + + private async createDtoForNote({ account, note }: { account: Account; note: Note }): Promise { + const links = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.NOTE, note.id); + + return note.toDto(links); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('note') + .where('note.accountId = :accountId', { accountId: filter.accountId }); + + if (filter?.noteId) { + qb.andWhere('note.id = :noteId', { noteId: filter.noteId }); + } + if (filter?.entityId) { + qb.andWhere('note.entityId = :entityId', { entityId: filter.entityId }); + } + + return qb; + } +} diff --git a/backend/src/CRM/reporting/common/enums/index.ts b/backend/src/CRM/reporting/common/enums/index.ts new file mode 100644 index 0000000..0fd046b --- /dev/null +++ b/backend/src/CRM/reporting/common/enums/index.ts @@ -0,0 +1,2 @@ +export * from './report-user-type.enum'; +export * from './sales-pipeline-type.enum'; diff --git a/backend/src/CRM/reporting/common/enums/report-user-type.enum.ts b/backend/src/CRM/reporting/common/enums/report-user-type.enum.ts new file mode 100644 index 0000000..6785d34 --- /dev/null +++ b/backend/src/CRM/reporting/common/enums/report-user-type.enum.ts @@ -0,0 +1,4 @@ +export enum ReportUserType { + Owner = 'owner', + Participant = 'participant', +} diff --git a/backend/src/CRM/reporting/common/enums/sales-pipeline-type.enum.ts b/backend/src/CRM/reporting/common/enums/sales-pipeline-type.enum.ts new file mode 100644 index 0000000..5e2f6b1 --- /dev/null +++ b/backend/src/CRM/reporting/common/enums/sales-pipeline-type.enum.ts @@ -0,0 +1,16 @@ +export enum SalesPipelineType { + All = 'all', + AllActive = 'all_active', + Open = 'open', + OpenActive = 'open_active', + Closed = 'closed', + Created = 'created', +} + +export const SalesPipelineTypes = { + All: [SalesPipelineType.All, SalesPipelineType.AllActive], + Open: [SalesPipelineType.Open, SalesPipelineType.OpenActive], + Closed: [SalesPipelineType.Closed], + Created: [SalesPipelineType.Created], + Active: [SalesPipelineType.AllActive, SalesPipelineType.OpenActive], +}; diff --git a/backend/src/CRM/reporting/common/helpers/entity-query-builder.helper.ts b/backend/src/CRM/reporting/common/helpers/entity-query-builder.helper.ts new file mode 100644 index 0000000..205778b --- /dev/null +++ b/backend/src/CRM/reporting/common/helpers/entity-query-builder.helper.ts @@ -0,0 +1,65 @@ +import { SelectQueryBuilder } from 'typeorm'; + +import { DatePeriod, PagingQuery } from '@/common'; +import { Entity } from '../../../Model/Entity/Entity'; + +interface Conditions { + accountId?: number | null; + entityTypeIds?: number[] | null; + stageIds?: number[] | null; + search?: string | null; + createdAt?: DatePeriod | null; + closedAt?: DatePeriod | null; + ownerIds?: number[] | null; +} + +export class EntityQueryBuilderHelper { + public static addConditions(qb: SelectQueryBuilder, conditions?: Conditions, paging?: PagingQuery) { + if (conditions.accountId) { + qb.andWhere('e.account_id = :accountId', { accountId: conditions.accountId }); + } + + if (conditions.entityTypeIds?.length) { + qb.andWhere(`e.entity_type_id IN (:...entityTypeIds)`, { entityTypeIds: conditions.entityTypeIds }); + } + + if (conditions.stageIds?.length) { + qb.andWhere(`e.stage_id IN (:...stageIds)`, { stageIds: conditions.stageIds }); + } + + if (conditions.search) { + qb.andWhere(`e.name ILIKE :search`, { search: `%${conditions.search}%` }); + } + + if (conditions.createdAt) { + EntityQueryBuilderHelper.addDateCondition(qb, conditions.createdAt, 'created_at'); + } + + if (conditions.closedAt) { + EntityQueryBuilderHelper.addDateCondition(qb, conditions.closedAt, 'closed_at'); + } + + if (conditions.ownerIds) { + if (conditions.ownerIds.length) { + qb.andWhere(`e.responsible_user_id in (:...ownerIds)`, { ownerIds: conditions.ownerIds }); + } else { + qb.andWhere(`e.responsible_user_id is null`); + } + } + + if (paging) { + qb.offset(paging.skip).limit(paging.take); + } + + return qb; + } + + private static addDateCondition(qb: SelectQueryBuilder, dates: DatePeriod, fieldName: string) { + if (dates.from) { + qb.andWhere(`e.${fieldName} >= :${fieldName}_from`, { [`${fieldName}_from`]: dates.from }); + } + if (dates.to) { + qb.andWhere(`e.${fieldName} <= :${fieldName}_to`, { [`${fieldName}_to`]: dates.to }); + } + } +} diff --git a/backend/src/CRM/reporting/common/helpers/index.ts b/backend/src/CRM/reporting/common/helpers/index.ts new file mode 100644 index 0000000..9d3531e --- /dev/null +++ b/backend/src/CRM/reporting/common/helpers/index.ts @@ -0,0 +1 @@ +export * from './entity-query-builder.helper'; diff --git a/backend/src/CRM/reporting/common/index.ts b/backend/src/CRM/reporting/common/index.ts new file mode 100644 index 0000000..5ef24a3 --- /dev/null +++ b/backend/src/CRM/reporting/common/index.ts @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './helpers'; +export * from './types'; diff --git a/backend/src/CRM/reporting/common/types/index.ts b/backend/src/CRM/reporting/common/types/index.ts new file mode 100644 index 0000000..5a88621 --- /dev/null +++ b/backend/src/CRM/reporting/common/types/index.ts @@ -0,0 +1,2 @@ +export * from './owner-date-value'; +export * from './report-row-owner'; diff --git a/backend/src/CRM/reporting/common/types/owner-date-value.ts b/backend/src/CRM/reporting/common/types/owner-date-value.ts new file mode 100644 index 0000000..60f103c --- /dev/null +++ b/backend/src/CRM/reporting/common/types/owner-date-value.ts @@ -0,0 +1,5 @@ +export interface OwnerDateValue { + ownerId: number | null; + value: number; + date: string | null | undefined; +} diff --git a/backend/src/CRM/reporting/common/types/report-row-owner.ts b/backend/src/CRM/reporting/common/types/report-row-owner.ts new file mode 100644 index 0000000..fa9301e --- /dev/null +++ b/backend/src/CRM/reporting/common/types/report-row-owner.ts @@ -0,0 +1 @@ +export type ReportRowOwner = 'total' | 'user' | 'department'; diff --git a/backend/src/CRM/reporting/comparative/comparative-report.controller.ts b/backend/src/CRM/reporting/comparative/comparative-report.controller.ts new file mode 100644 index 0000000..493ea6e --- /dev/null +++ b/backend/src/CRM/reporting/comparative/comparative-report.controller.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { ComparativeReportDto, ComparativeReportFilterDto } from './dto'; +import { ComparativeReportService } from './comparative-report.service'; + +@ApiTags('crm/reporting') +@Controller('crm/reporting/comparative') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ComparativeReportController { + constructor(private readonly service: ComparativeReportService) {} + + @ApiOperation({ summary: 'Get comparative report', description: 'Get comparative report' }) + @ApiBody({ type: ComparativeReportFilterDto, required: true, description: 'Comparative report filter' }) + @ApiOkResponse({ description: 'Comparative report', type: ComparativeReportDto }) + @Post() + public async getComparativeReport( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ComparativeReportFilterDto, + ) { + return this.service.getReport({ accountId, user, filter }); + } +} diff --git a/backend/src/CRM/reporting/comparative/comparative-report.service.ts b/backend/src/CRM/reporting/comparative/comparative-report.service.ts new file mode 100644 index 0000000..02c42ff --- /dev/null +++ b/backend/src/CRM/reporting/comparative/comparative-report.service.ts @@ -0,0 +1,360 @@ +import { Injectable } from '@nestjs/common'; + +import { + GroupByDate, + QuantityAmount, + DatePeriod, + DateUtil, + ForbiddenError, + propagateData, + intersection, +} from '@/common'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { DepartmentService } from '@/modules/iam/department/department.service'; +import { User } from '@/modules/iam/user/entities'; + +import { GroupedStages } from '../../board-stage/types'; +import { BoardStageService } from '../../board-stage/board-stage.service'; +import { EntityType } from '../../entity-type/entities'; + +import { ReportRowOwner } from '../common'; +import { CrmReportingService } from '../crm-reporting.service'; + +import { ComparativeReportFilterDto } from './dto'; +import { ComparativeReport, ComparativeReportRow, ComparativeReportCell } from './types'; + +interface Filter { + type: GroupByDate; + stages: GroupedStages; + userIds?: number[] | null; + period?: DatePeriod | null; +} + +@Injectable() +export class ComparativeReportService { + constructor( + private readonly authService: AuthorizationService, + private readonly departmentService: DepartmentService, + private readonly stageService: BoardStageService, + private readonly reportingService: CrmReportingService, + ) {} + + public async getReport({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: ComparativeReportFilterDto; + }): Promise { + const { allow, userIds: allowedUserIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: EntityType.getAuthorizable(filter.entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + const userIds = filter.userIds?.length ? intersection(filter.userIds, allowedUserIds) : allowedUserIds; + + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId: filter.entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + type: filter.stageType, + }); + + let period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + if (filter.period?.from) { + switch (filter.type) { + case GroupByDate.Day: + period = new DatePeriod(DateUtil.sub(period.from, { weeks: 1 }), period.to); + break; + case GroupByDate.Week: + period = new DatePeriod(DateUtil.sub(period.from, { weeks: 1 }), period.to); + break; + case GroupByDate.Month: + period = new DatePeriod(DateUtil.sub(period.from, { months: 1 }), period.to); + break; + case GroupByDate.Quarter: + period = new DatePeriod(DateUtil.sub(period.from, { months: 3 }), period.to); + break; + case GroupByDate.Year: + period = new DatePeriod(DateUtil.sub(period.from, { years: 1 }), period.to); + break; + } + } + + const [users, departments, total] = await Promise.all([ + this.getGroupBy({ + accountId, + owner: 'user', + userOwnerFieldId: filter.ownerFieldId, + filter: { type: filter.type, stages, userIds, period }, + }), + this.getGroupBy({ + accountId, + owner: 'department', + userOwnerFieldId: filter.ownerFieldId, + filter: { type: filter.type, stages, userIds, period }, + }), + this.getGroupBy({ + accountId, + owner: 'total', + userOwnerFieldId: filter.ownerFieldId, + filter: { type: filter.type, stages, userIds, period }, + }), + ]); + + if (departments.size) { + const hierarchy = await this.departmentService.getHierarchy({ accountId }); + + if (hierarchy.length) { + propagateData(hierarchy, departments, (ownerId: number) => { + return ComparativeReport.createEmptyRow(ownerId); + }); + } + } + + return new ComparativeReport(users, departments, total.values().next().value); + } + + private async getGroupBy({ + accountId, + owner, + userOwnerFieldId, + filter, + }: { + accountId: number; + owner: ReportRowOwner; + userOwnerFieldId: number | undefined; + filter: Filter; + }): Promise> { + const rowMap = new Map(); + + if (filter.stages?.open?.length) { + await this.processEntitiesOpen({ accountId, owner, userOwnerFieldId, filter, rowMap }); + } + if (filter.stages?.lost?.length) { + await this.processEntitiesLost({ accountId, owner, userOwnerFieldId, filter, rowMap }); + } + if (filter.stages?.won?.length) { + await this.processEntitiesWon({ accountId, owner, userOwnerFieldId, filter, rowMap }); + } + + for (const row of rowMap.values()) { + const newCells: ComparativeReportCell[] = []; + for (const cell of row.cells.values()) { + const [prevDate, nextDate] = this.getPrevAndNextDates({ date: cell.date, type: filter.type }); + const prevCell = row.cells.get(prevDate); + if (prevCell) { + this.setCellPrevious({ cell, previous: prevCell }); + } else { + const newCell = ComparativeReportCell.empty(prevDate); + this.setCellPrevious({ cell, previous: newCell }); + } + const nextCell = row.cells.get(nextDate); + if (!nextCell) { + const newCell = ComparativeReportCell.empty(nextDate); + this.setCellPrevious({ cell: newCell, previous: cell }); + newCells.push(newCell); + } + } + newCells.forEach((c) => row.cells.set(c.date, c)); + } + + return rowMap; + } + + private async processEntitiesOpen({ + accountId, + owner, + userOwnerFieldId, + filter, + rowMap, + }: { + accountId: number; + owner: ReportRowOwner; + userOwnerFieldId: number | undefined; + filter: Filter; + rowMap: Map; + }) { + const result = await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.open, + { owner, userOwnerFieldId, date: { type: filter.type, fieldName: 'created_at' } }, + { amount: true, quantity: true }, + { createdAt: filter.period, userIds: filter.userIds }, + ); + for (const { ownerId, value, date } of result.quantity) { + const row = rowMap.get(ownerId) ?? ComparativeReportRow.empty(ownerId); + const cell = row.cells.get(date) ?? ComparativeReportCell.empty(date); + cell.all.current.quantity += value; + cell.open.current.quantity = value; + row.cells.set(date, cell); + rowMap.set(ownerId, row); + } + for (const { ownerId, value, date } of result.amount) { + const row = rowMap.get(ownerId) ?? ComparativeReportRow.empty(ownerId); + const cell = row.cells.get(date) ?? ComparativeReportCell.empty(date); + cell.all.current.amount += value; + cell.open.current.amount = value; + row.cells.set(date, cell); + rowMap.set(ownerId, row); + } + } + + private async processEntitiesLost({ + accountId, + owner, + userOwnerFieldId, + filter, + rowMap, + }: { + accountId: number; + owner: ReportRowOwner; + userOwnerFieldId: number | undefined; + filter: Filter; + rowMap: Map; + }) { + const result = await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.lost, + { owner, userOwnerFieldId, date: { type: filter.type, fieldName: 'closed_at' } }, + { amount: true, quantity: true }, + { closedAt: filter.period, userIds: filter.userIds }, + ); + for (const { ownerId, value, date } of result.quantity) { + const row = rowMap.get(ownerId) ?? ComparativeReportRow.empty(ownerId); + const cell = row.cells.get(date) ?? ComparativeReportCell.empty(date); + cell.all.current.quantity += value; + cell.lost.current.quantity = value; + row.cells.set(date, cell); + rowMap.set(ownerId, row); + } + for (const { ownerId, value, date } of result.amount) { + const row = rowMap.get(ownerId) ?? ComparativeReportRow.empty(ownerId); + const cell = row.cells.get(date) ?? ComparativeReportCell.empty(date); + cell.all.current.amount += value; + cell.lost.current.amount = value; + row.cells.set(date, cell); + rowMap.set(ownerId, row); + } + } + + private async processEntitiesWon({ + accountId, + owner, + userOwnerFieldId, + filter, + rowMap, + }: { + accountId: number; + owner: ReportRowOwner; + userOwnerFieldId: number | undefined; + filter: Filter; + rowMap: Map; + }) { + const result = await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.won, + { owner, userOwnerFieldId, date: { type: filter.type, fieldName: 'closed_at' } }, + { amount: true, quantity: true }, + { closedAt: filter.period, userIds: filter.userIds }, + ); + for (const { ownerId, value, date } of result.quantity) { + const row = rowMap.get(ownerId) ?? ComparativeReportRow.empty(ownerId); + const cell = row.cells.get(date) ?? ComparativeReportCell.empty(date); + cell.all.current.quantity += value; + cell.won.current.quantity = value; + row.cells.set(date, cell); + rowMap.set(ownerId, row); + } + for (const { ownerId, value, date } of result.amount) { + const row = rowMap.get(ownerId) ?? ComparativeReportRow.empty(ownerId); + const cell = row.cells.get(date) ?? ComparativeReportCell.empty(date); + cell.all.current.amount += value; + cell.won.current.amount = value; + row.cells.set(date, cell); + rowMap.set(ownerId, row); + } + } + + private getPrevAndNextDates({ date, type }: { date: string; type: GroupByDate }): [string, string] { + const current = this.parseDate({ date, type }); + switch (type) { + case GroupByDate.Day: { + const prev = DateUtil.format(DateUtil.sub(current, { weeks: 1 }), 'yyyy-MM-dd'); + const next = DateUtil.format(DateUtil.add(current, { weeks: 1 }), 'yyyy-MM-dd'); + return [prev, next]; + } + case GroupByDate.Week: { + const prev = DateUtil.format(DateUtil.sub(current, { weeks: 1 }), 'yyyy-ww'); + const next = DateUtil.format(DateUtil.add(current, { weeks: 1 }), 'yyyy-ww'); + return [prev, next]; + } + case GroupByDate.Month: { + const prev = DateUtil.format(DateUtil.sub(current, { months: 1 }), 'yyyy-MM'); + const next = DateUtil.format(DateUtil.add(current, { months: 1 }), 'yyyy-MM'); + return [prev, next]; + } + case GroupByDate.Quarter: { + const prev = DateUtil.format(DateUtil.sub(current, { months: 3 }), 'yyyy-Q'); + const next = DateUtil.format(DateUtil.add(current, { months: 3 }), 'yyyy-Q'); + return [prev, next]; + } + case GroupByDate.Year: { + const prev = DateUtil.format(DateUtil.sub(current, { years: 1 }), 'yyyy'); + const next = DateUtil.format(DateUtil.add(current, { years: 1 }), 'yyyy'); + return [prev, next]; + } + } + } + + private parseDate({ date, type }: { date: string; type: GroupByDate }): Date { + switch (type) { + case GroupByDate.Day: + return DateUtil.parse(date, 'yyyy-MM-dd'); + case GroupByDate.Week: { + const [yearStr, weekStr] = date.split('-'); + const weeks = parseInt(weekStr, 10) - 1; + return DateUtil.add(DateUtil.parse(yearStr, 'yyyy'), { weeks }); + } + case GroupByDate.Month: + return DateUtil.parse(date, 'yyyy-MM'); + case GroupByDate.Quarter: + return DateUtil.parse(date, 'yyyy-Q'); + case GroupByDate.Year: + return DateUtil.parse(date, 'yyyy'); + } + } + + private setCellPrevious({ cell, previous }: { cell: ComparativeReportCell; previous: ComparativeReportCell }) { + cell.all.previous = new QuantityAmount(previous.all.current.quantity, previous.all.current.amount); + cell.open.previous = new QuantityAmount(previous.open.current.quantity, previous.open.current.amount); + cell.lost.previous = new QuantityAmount(previous.lost.current.quantity, previous.lost.current.amount); + cell.won.previous = new QuantityAmount(previous.won.current.quantity, previous.won.current.amount); + + cell.all.difference = new QuantityAmount( + this.difference(cell.all.current.quantity, cell.all.previous.quantity), + this.difference(cell.all.current.amount, cell.all.previous.amount), + ); + cell.open.difference = new QuantityAmount( + this.difference(cell.open.current.quantity, cell.open.previous.quantity), + this.difference(cell.open.current.amount, cell.open.previous.amount), + ); + cell.lost.difference = new QuantityAmount( + this.difference(cell.lost.current.quantity, cell.lost.previous.quantity), + this.difference(cell.lost.current.amount, cell.lost.previous.amount), + ); + cell.won.difference = new QuantityAmount( + this.difference(cell.won.current.quantity, cell.won.previous.quantity), + this.difference(cell.won.current.amount, cell.won.previous.amount), + ); + } + + private difference(current: number, previous: number): number { + return previous ? (current - previous) / previous : current ? 1 : 0; + } +} diff --git a/backend/src/CRM/reporting/comparative/dto/comparative-report-cell.dto.ts b/backend/src/CRM/reporting/comparative/dto/comparative-report-cell.dto.ts new file mode 100644 index 0000000..2c43458 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/dto/comparative-report-cell.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { ComparativeReportValueDto } from './comparative-report-value.dto'; + +export class ComparativeReportCellDto { + @ApiProperty({ description: 'Date depending on report type' }) + @IsString() + date: string; + + @ApiProperty({ type: ComparativeReportValueDto, description: 'All' }) + all: ComparativeReportValueDto; + + @ApiProperty({ type: ComparativeReportValueDto, description: 'Open' }) + open: ComparativeReportValueDto; + + @ApiProperty({ type: ComparativeReportValueDto, description: 'Lost' }) + lost: ComparativeReportValueDto; + + @ApiProperty({ type: ComparativeReportValueDto, description: 'Won' }) + won: ComparativeReportValueDto; +} diff --git a/backend/src/CRM/reporting/comparative/dto/comparative-report-filter.dto.ts b/backend/src/CRM/reporting/comparative/dto/comparative-report-filter.dto.ts new file mode 100644 index 0000000..459da59 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/dto/comparative-report-filter.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { GroupByDate, DatePeriodFilter } from '@/common'; + +import { BoardStageType } from '../../../board-stage'; + +export class ComparativeReportFilterDto { + @ApiProperty({ enum: GroupByDate, description: 'Type of grouping by date' }) + @IsEnum(GroupByDate) + type: GroupByDate; + + @ApiPropertyOptional({ description: 'Field ID for owner instead of responsibleUserId' }) + @IsOptional() + ownerFieldId?: number; + + @ApiPropertyOptional({ type: Number, description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Board IDs' }) + @IsOptional() + @IsArray() + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs' }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ enum: BoardStageType, nullable: true, description: 'Stage type' }) + @IsOptional() + @IsEnum(BoardStageType) + stageType?: BoardStageType | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/CRM/reporting/comparative/dto/comparative-report-row.dto.ts b/backend/src/CRM/reporting/comparative/dto/comparative-report-row.dto.ts new file mode 100644 index 0000000..d691937 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/dto/comparative-report-row.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber } from 'class-validator'; + +import { ComparativeReportCellDto } from './comparative-report-cell.dto'; + +export class ComparativeReportRowDto { + @ApiProperty({ description: 'Owner ID' }) + @IsNumber() + ownerId: number; + + @ApiProperty({ type: [ComparativeReportCellDto], description: 'Cells' }) + @IsArray() + cells: ComparativeReportCellDto[]; +} diff --git a/backend/src/CRM/reporting/comparative/dto/comparative-report-value.dto.ts b/backend/src/CRM/reporting/comparative/dto/comparative-report-value.dto.ts new file mode 100644 index 0000000..057c481 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/dto/comparative-report-value.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { QuantityAmountDto } from '@/common'; + +export class ComparativeReportValueDto { + @ApiProperty({ type: QuantityAmountDto, description: 'Current value' }) + current: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Previous value' }) + previous: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Difference' }) + difference: QuantityAmountDto; +} diff --git a/backend/src/CRM/reporting/comparative/dto/comparative-report.dto.ts b/backend/src/CRM/reporting/comparative/dto/comparative-report.dto.ts new file mode 100644 index 0000000..282143a --- /dev/null +++ b/backend/src/CRM/reporting/comparative/dto/comparative-report.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ComparativeReportRowDto } from './comparative-report-row.dto'; + +export class ComparativeReportDto { + @ApiProperty({ type: [ComparativeReportRowDto], description: 'Users' }) + users: ComparativeReportRowDto[]; + + @ApiProperty({ type: [ComparativeReportRowDto], description: 'Departments' }) + departments: ComparativeReportRowDto[]; + + @ApiProperty({ type: ComparativeReportRowDto, description: 'Total' }) + total: ComparativeReportRowDto; +} diff --git a/backend/src/CRM/reporting/comparative/dto/index.ts b/backend/src/CRM/reporting/comparative/dto/index.ts new file mode 100644 index 0000000..5958d60 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/dto/index.ts @@ -0,0 +1,5 @@ +export * from './comparative-report-cell.dto'; +export * from './comparative-report-filter.dto'; +export * from './comparative-report-row.dto'; +export * from './comparative-report-value.dto'; +export * from './comparative-report.dto'; diff --git a/backend/src/CRM/reporting/comparative/types/comparative-report-cell.ts b/backend/src/CRM/reporting/comparative/types/comparative-report-cell.ts new file mode 100644 index 0000000..0326fcf --- /dev/null +++ b/backend/src/CRM/reporting/comparative/types/comparative-report-cell.ts @@ -0,0 +1,53 @@ +import { ComparativeReportCellDto } from '../dto'; +import { ComparativeReportValue } from './comparative-report-value'; + +export class ComparativeReportCell { + date: string; + all: ComparativeReportValue; + open: ComparativeReportValue; + lost: ComparativeReportValue; + won: ComparativeReportValue; + + constructor( + date: string, + all: ComparativeReportValue, + open: ComparativeReportValue, + lost: ComparativeReportValue, + won: ComparativeReportValue, + ) { + this.date = date; + this.all = all; + this.open = open; + this.lost = lost; + this.won = won; + } + + public static empty(date: string): ComparativeReportCell { + return new ComparativeReportCell( + date, + ComparativeReportValue.empty(), + ComparativeReportValue.empty(), + ComparativeReportValue.empty(), + ComparativeReportValue.empty(), + ); + } + + public toDto(): ComparativeReportCellDto { + return { + date: this.date, + all: this.all.toDto(), + open: this.open.toDto(), + lost: this.lost.toDto(), + won: this.won.toDto(), + }; + } + + public add(cell: ComparativeReportCell): ComparativeReportCell { + this.all.add(cell.all); + this.open.add(cell.open); + this.lost.add(cell.lost); + this.won.add(cell.won); + + return this; + } +} diff --git a/backend/src/CRM/reporting/comparative/types/comparative-report-row.ts b/backend/src/CRM/reporting/comparative/types/comparative-report-row.ts new file mode 100644 index 0000000..a28160b --- /dev/null +++ b/backend/src/CRM/reporting/comparative/types/comparative-report-row.ts @@ -0,0 +1,36 @@ +import { ComparativeReportRowDto } from '../dto'; +import { ComparativeReportCell } from './comparative-report-cell'; + +export class ComparativeReportRow { + ownerId: number; + cells: Map; + + constructor(ownerId: number, cells: Map) { + this.ownerId = ownerId; + this.cells = cells; + } + + public static empty(ownerId: number): ComparativeReportRow { + return new ComparativeReportRow(ownerId, new Map()); + } + + public toDto(): ComparativeReportRowDto { + return { + ownerId: this.ownerId, + cells: Array.from(this.cells.values()).map((v) => v.toDto()), + }; + } + + public add(row: ComparativeReportRow): ComparativeReportRow { + for (const [rowCellId, rowCell] of row.cells) { + let cell = this.cells.get(rowCellId); + if (!cell) { + cell = ComparativeReportCell.empty(rowCellId); + this.cells.set(rowCellId, cell); + } + cell.add(rowCell); + } + + return this; + } +} diff --git a/backend/src/CRM/reporting/comparative/types/comparative-report-value.ts b/backend/src/CRM/reporting/comparative/types/comparative-report-value.ts new file mode 100644 index 0000000..2b5382c --- /dev/null +++ b/backend/src/CRM/reporting/comparative/types/comparative-report-value.ts @@ -0,0 +1,31 @@ +import { QuantityAmount } from '@/common'; + +import { ComparativeReportValueDto } from '../dto/comparative-report-value.dto'; + +export class ComparativeReportValue { + current: QuantityAmount; + previous: QuantityAmount; + difference: QuantityAmount; + + constructor(current: QuantityAmount, previous: QuantityAmount, difference: QuantityAmount) { + this.current = current; + this.previous = previous; + this.difference = difference; + } + + public static empty(): ComparativeReportValue { + return new ComparativeReportValue(QuantityAmount.empty(), QuantityAmount.empty(), QuantityAmount.empty()); + } + + public toDto(): ComparativeReportValueDto { + return { current: this.current.toDto(), previous: this.previous.toDto(), difference: this.difference.toDto() }; + } + + public add(cell: ComparativeReportValue): ComparativeReportValue { + this.current.add(cell.current); + this.previous.add(cell.previous); + this.difference.add(cell.difference); + + return this; + } +} diff --git a/backend/src/CRM/reporting/comparative/types/comparative-report.ts b/backend/src/CRM/reporting/comparative/types/comparative-report.ts new file mode 100644 index 0000000..5c813e8 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/types/comparative-report.ts @@ -0,0 +1,38 @@ +import { ComparativeReportDto } from '../dto/comparative-report.dto'; +import { ComparativeReportRow } from './comparative-report-row'; + +export class ComparativeReport { + users: Map; + departments: Map; + total: ComparativeReportRow | null; + + constructor( + users: Map, + departments: Map, + total: ComparativeReportRow | null, + ) { + this.users = users; + this.departments = departments; + this.total = total; + } + + public static empty(): ComparativeReport { + return new ComparativeReport( + new Map(), + new Map(), + ComparativeReportRow.empty(-1), + ); + } + + public static createEmptyRow(ownerId: number): ComparativeReportRow { + return ComparativeReportRow.empty(ownerId); + } + + public toDto(): ComparativeReportDto { + return { + users: Array.from(this.users.values()).map((u) => u.toDto()), + departments: Array.from(this.departments.values()).map((u) => u.toDto()), + total: this.total?.toDto(), + }; + } +} diff --git a/backend/src/CRM/reporting/comparative/types/index.ts b/backend/src/CRM/reporting/comparative/types/index.ts new file mode 100644 index 0000000..5553603 --- /dev/null +++ b/backend/src/CRM/reporting/comparative/types/index.ts @@ -0,0 +1,4 @@ +export * from './comparative-report-cell'; +export * from './comparative-report-row'; +export * from './comparative-report-value'; +export * from './comparative-report'; diff --git a/backend/src/CRM/reporting/crm-reporting.module.ts b/backend/src/CRM/reporting/crm-reporting.module.ts new file mode 100644 index 0000000..3ceb46c --- /dev/null +++ b/backend/src/CRM/reporting/crm-reporting.module.ts @@ -0,0 +1,49 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { TelephonyModule } from '@/modules/telephony/telephony.module'; +import { CrmModule } from '../crm.module'; +import { SalesPlanModule } from '../sales-plan/sales-plan.module'; + +import { Entity } from '../Model/Entity/Entity'; +import { Task } from '../task'; + +import { CrmReportingService } from './crm-reporting.service'; +import { ComparativeReportService } from './comparative/comparative-report.service'; +import { ComparativeReportController } from './comparative/comparative-report.controller'; +import { CustomerReportService } from './customer/customer-report.service'; +import { CustomerReportController } from './customer/customer-report.controller'; +import { DashboardController } from './dashboard/dashboard.controller'; +import { DashboardService } from './dashboard/dashboard.service'; +import { GeneralReportService } from './general/general-report.service'; +import { GeneralReportController } from './general/general-report.controller'; +import { ProjectReportService } from './project/project-report.service'; +import { ProjectReportController } from './project/project-report.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Entity, Task]), + IAMModule, + forwardRef(() => CrmModule), + forwardRef(() => SalesPlanModule), + forwardRef(() => TelephonyModule), + ], + controllers: [ + ComparativeReportController, + CustomerReportController, + GeneralReportController, + DashboardController, + ProjectReportController, + ], + providers: [ + ComparativeReportService, + CrmReportingService, + CustomerReportService, + DashboardService, + GeneralReportService, + ProjectReportService, + ], + exports: [CrmReportingService], +}) +export class CrmReportingModule {} diff --git a/backend/src/CRM/reporting/crm-reporting.service.ts b/backend/src/CRM/reporting/crm-reporting.service.ts new file mode 100644 index 0000000..9a56d9a --- /dev/null +++ b/backend/src/CRM/reporting/crm-reporting.service.ts @@ -0,0 +1,259 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { GroupByDate, NumberUtil, DateUtil, DatePeriod } from '@/common'; + +import { Entity } from '../Model/Entity/Entity'; + +import { OwnerDateValue, ReportRowOwner } from './common'; + +interface EntityFieldFilter { + fieldId: number; + optionId?: number; + optionsId?: number; + switch?: boolean; +} +interface EntityTotalFilter { + createdAt?: DatePeriod; + closedAt?: DatePeriod; + userIds?: number[]; + field?: EntityFieldFilter; +} +interface GroupBy { + owner?: ReportRowOwner; + userOwnerFieldId?: number; + date?: { type: GroupByDate; fieldName: 'created_at' | 'closed_at' }; +} +interface EntityTotal { + quantity: OwnerDateValue[]; + amount: OwnerDateValue[]; + close: OwnerDateValue[]; + fieldQuantity: OwnerDateValue[]; + fieldAmount: OwnerDateValue[]; +} + +@Injectable() +export class CrmReportingService { + constructor( + private readonly dataSource: DataSource, + @InjectRepository(Entity) + private readonly entityRepository: Repository, + ) {} + + public async getEntityGroupBy( + accountId: number, + stageIds: number[], + groupBy: GroupBy, + include: { quantity?: boolean; amount?: boolean; close?: boolean; fieldQuantity?: number; fieldAmount?: number }, + filter: EntityTotalFilter, + ): Promise { + const qb = this.entityRepository.createQueryBuilder('e').where('e.account_id = :accountId', { accountId }); + if (stageIds.length) { + qb.andWhere(`e.stage_id IN (:...stageIds)`, { stageIds: stageIds }); + } + if (filter.createdAt) { + if (filter.createdAt.from) { + qb.andWhere(`e.created_at >= :createdAtFrom`, { createdAtFrom: filter.createdAt.from }); + } + if (filter.createdAt.to) { + qb.andWhere(`e.created_at <= :createdAtTo`, { createdAtTo: filter.createdAt.to }); + } + } + if (filter.closedAt) { + if (filter.closedAt.from) { + qb.andWhere(`e.closed_at >= :closedAtFrom`, { closedAtFrom: filter.closedAt.from }); + } + if (filter.closedAt.to) { + qb.andWhere(`e.closed_at <= :closedAtTo`, { closedAtTo: filter.closedAt.to }); + } + } + if (filter.userIds) { + if (filter.userIds.length) { + qb.andWhere(`u.id IN (:...userIds)`, { userIds: filter.userIds }); + } else { + qb.andWhere(`e.responsible_user_id IS NULL`); + } + } + + if (groupBy.owner) { + if (groupBy.userOwnerFieldId) { + qb.innerJoin('field_value', 'fvuo', `fvuo.entity_id = e.id and fvuo.field_id = ${groupBy.userOwnerFieldId}`); + qb.leftJoin('users', 'u', `u.id = (fvuo.payload->>'value')::integer`); + } else { + qb.leftJoin('users', 'u', 'e.responsible_user_id = u.id'); + } + switch (groupBy.owner) { + case 'user': + qb.select('u.id', 'owner_id').groupBy('u.id'); + break; + case 'department': + qb.select('u.department_id', 'owner_id').groupBy('u.department_id'); + break; + case 'total': + qb.select('0::int', 'owner_id'); + break; + } + } + if (groupBy.date) { + const groupByDate = `TO_CHAR(DATE(e.${groupBy.date.fieldName}), '${this.getDatePattern(groupBy.date.type)}')`; + qb.addSelect(groupByDate, 'date').addGroupBy(groupByDate); + } + + if (filter.field?.fieldId && filter.field?.optionId) { + // eslint-disable-next-line prettier/prettier + qb.leftJoin('field_value', 'fvt', `fvt.entity_id = e.id and fvt.field_id = ${filter.field.fieldId}`) + .andWhere(`fvt.payload @> '{"optionId": ${filter.field.optionId}}'`); + } else if (filter.field?.fieldId && filter.field?.optionsId) { + // eslint-disable-next-line prettier/prettier + qb.leftJoin('field_value', 'fvt', `fvt.entity_id = e.id and fvt.field_id = ${filter.field.fieldId}`) + .andWhere(`fvt.payload @> '{"optionIds": [${filter.field.optionsId}]}'`); + } else if (filter.field?.fieldId && filter.field?.switch !== undefined) { + qb.leftJoin('field_value', 'fvs', 'fvs.entity_id = e.id and fvs.field_id = :fvsId', { + fvsId: filter.field.fieldId, + }); + if (filter.field.switch) { + qb.andWhere(`fvs.payload @> '{"value": true}'::jsonb`); + } else { + qb.andWhere(`(fvs.payload @> '{"value": true}'::jsonb OR fvs.id IS NULL)`); + } + } + + const [countByUser, countByField, amountByUser, amountByField, avgCloseByUser] = await Promise.all([ + include.quantity ? qb.clone().addSelect('count(e.id)', 'cnt').getRawMany() : Promise.resolve([]), + include.fieldQuantity + ? qb + .clone() + .addSelect(`count(e.id)`, 'field_cnt') + .innerJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_id = :fieldQuantityId`, { + fieldQuantityId: include.fieldQuantity, + }) + .getRawMany() + : Promise.resolve([]), + include.amount + ? qb + .clone() + .addSelect(`sum(cast(av.payload::json->>'value' as decimal))`, 'amount') + .innerJoin('field_value', 'av', `av.entity_id = e.id and av.field_type = 'value'`) + .getRawMany() + : Promise.resolve([]), + include.fieldAmount + ? qb + .clone() + .addSelect(`sum(cast(fv.payload::json->>'value' as decimal))`, 'field_amount') + .innerJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_id = :fieldAmountId`, { + fieldAmountId: include.fieldAmount, + }) + .getRawMany() + : Promise.resolve([]), + include.close + ? qb + .clone() + .addSelect(`sum(extract(epoch from (e.closed_at - e.created_at)))`, 'close') + .andWhere('e.closed_at is not null') + .getRawMany() + : Promise.resolve([]), + ]); + + return { + quantity: countByUser.map((c) => { + return { ownerId: NumberUtil.toNumber(c.owner_id), value: NumberUtil.toNumber(c.cnt), date: c.date }; + }), + amount: amountByUser.map((c) => { + return { ownerId: NumberUtil.toNumber(c.owner_id), value: NumberUtil.toNumber(c.amount), date: c.date }; + }), + close: avgCloseByUser.map((c) => { + return { ownerId: NumberUtil.toNumber(c.owner_id), value: NumberUtil.toNumber(c.close), date: c.date }; + }), + fieldQuantity: countByField.map((c) => { + return { ownerId: NumberUtil.toNumber(c.owner_id), value: NumberUtil.toNumber(c.field_cnt), date: c.date }; + }), + fieldAmount: amountByField.map((c) => { + return { ownerId: NumberUtil.toNumber(c.owner_id), value: NumberUtil.toNumber(c.field_amount), date: c.date }; + }), + }; + } + + public async getTaskOrActivityGroupBy( + accountId: number, + source: 'task' | 'activity', + stageIds: number[], + groupBy: GroupBy, + options?: { + resolved?: boolean; + expired?: boolean; + dateField?: string; + period?: DatePeriod | null; + ownerIds?: number[]; + departmentIds?: number[]; + }, + ): Promise { + const qb = this.dataSource + .createQueryBuilder() + .select('count(s.*)', 'cnt') + .from(source, 's') + .leftJoin('users', 'u', 's.responsible_user_id = u.id') + .innerJoin('entity', 'e', 'e.id = s.entity_id') + .where('s.account_id = :accountId', { accountId }) + .andWhere('e.stage_id IN (:...stageIds)', { stageIds }); + + if (groupBy.owner) { + switch (groupBy.owner) { + case 'user': + qb.addSelect('u.id', 'owner_id').groupBy('u.id'); + break; + case 'department': + qb.addSelect('u.department_id', 'owner_id').groupBy('u.department_id'); + break; + } + } + if (groupBy.date) { + const groupByDate = `TO_CHAR(DATE(s.${groupBy.date.fieldName}), '${this.getDatePattern(groupBy.date.type)}')`; + qb.addSelect(groupByDate, 'date').addGroupBy(groupByDate); + } + + if (options?.period && options?.dateField) { + if (options.period.from) { + qb.andWhere(`s.${options.dateField} >= :from`, { from: options.period.from }); + } + if (options.period.to) { + qb.andWhere(`s.${options.dateField} <= :to`, { to: options.period.to }); + } + } + + if (options?.resolved === true) { + qb.andWhere('s.is_resolved = true'); + } else if (options?.resolved === false) { + qb.andWhere('s.is_resolved = false'); + } + + if (options?.expired) { + qb.andWhere('s.end_date < :now', { now: DateUtil.now() }); + } + + if (options?.ownerIds?.length > 0) { + qb.andWhere('s.responsible_user_id IN (:...ownerIds)', { ownerIds: options.ownerIds }); + } + + const countByUser = await qb.getRawMany(); + + return countByUser.map((c) => { + return { ownerId: NumberUtil.toNumber(c.owner_id), value: NumberUtil.toNumber(c.cnt), date: c.date }; + }); + } + + private getDatePattern(type: GroupByDate): string { + switch (type) { + case GroupByDate.Day: + return 'YYYY-MM-DD'; + case GroupByDate.Week: + return 'YYYY-WW'; + case GroupByDate.Month: + return 'YYYY-MM'; + case GroupByDate.Quarter: + return 'YYYY-Q'; + case GroupByDate.Year: + return 'YYYY'; + } + } +} diff --git a/backend/src/CRM/reporting/customer/customer-report.controller.ts b/backend/src/CRM/reporting/customer/customer-report.controller.ts new file mode 100644 index 0000000..11449e2 --- /dev/null +++ b/backend/src/CRM/reporting/customer/customer-report.controller.ts @@ -0,0 +1,28 @@ +import { Body, Controller, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CustomerReportDto, CustomerReportFilterDto } from './dto'; +import { CustomerReportService } from './customer-report.service'; + +@ApiTags('crm/reporting') +@Controller('crm/reporting/customer') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class CustomerReportController { + constructor(private readonly service: CustomerReportService) {} + + @ApiOperation({ summary: 'Get customer report', description: 'Get customer report' }) + @ApiBody({ type: CustomerReportFilterDto, required: true, description: 'Customer report filter' }) + @ApiOkResponse({ description: 'Customer report', type: CustomerReportDto }) + @Post() + async getCustomerReport( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: CustomerReportFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.getReport({ accountId, user, filter, paging }); + } +} diff --git a/backend/src/CRM/reporting/customer/customer-report.service.ts b/backend/src/CRM/reporting/customer/customer-report.service.ts new file mode 100644 index 0000000..bc32173 --- /dev/null +++ b/backend/src/CRM/reporting/customer/customer-report.service.ts @@ -0,0 +1,305 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { DatePeriod, ForbiddenError, intersection, NumberUtil, PagingQuery, QuantityAmount } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; + +import { EntityCategory } from '../../common'; +import { GroupedStages } from '../../board-stage/types'; +import { BoardStageService } from '../../board-stage/board-stage.service'; +import { EntityType } from '../../entity-type/entities'; +import { Entity } from '../../Model/Entity/Entity'; + +import { CustomerReportFilterDto } from './dto'; +import { CustomerReportType } from './enums'; +import { + CustomerReport, + CustomerReportRow, + CustomerReportMeta, + CustomerReportFieldMeta, + CustomerReportField, +} from './types'; + +interface Filter { + entityTypeId: number; + type?: CustomerReportType | null; + userIds?: number[] | null; + period?: DatePeriod; +} + +@Injectable() +export class CustomerReportService { + constructor( + @InjectRepository(Entity) + private readonly repository: Repository, + private readonly dataSource: DataSource, + private readonly authService: AuthorizationService, + private readonly stageService: BoardStageService, + private readonly fieldService: FieldService, + ) {} + + public async getReport({ + accountId, + user, + filter, + paging, + }: { + accountId: number; + user: User; + filter: CustomerReportFilterDto; + paging: PagingQuery; + }) { + const { allow, userIds: allowedUserIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: EntityType.getAuthorizable(filter.entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + const userIds = filter.ownerIds?.length ? intersection(filter.ownerIds, allowedUserIds) : allowedUserIds; + + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId: filter.entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }); + + const fieldsMeta = await this.getFieldsMeta({ accountId, entityTypeId: filter.entityTypeId }); + + //HACK: for overall report + if (filter.type === CustomerReportType.ContactCompany) { + filter.type = null; + } + + const groupFilter: Filter = { + entityTypeId: filter.entityTypeId, + type: filter.type, + userIds, + period: filter.period ? DatePeriod.fromFilter(filter.period) : undefined, + }; + const rows = await this.getGroupBy({ accountId, stages, isTotal: false, fieldsMeta, filter: groupFilter, paging }); + // const totalRow = filter.type + // ? await this.getGroupBy({ accountId, stages, isTotal: true, fieldsMeta, filter: groupFilter }) + // : null; + const total = await this.createQb({ accountId, stages, isTotal: false, filter: groupFilter, fieldsMeta }) + .select('count(distinct(te.id))::integer', 'cnt') + .orderBy() + .groupBy() + .getRawOne(); + + return new CustomerReport( + Array.from(rows.values()), + // eslint-disable-next-line max-len + CustomerReportRow.empty(0, 0, null), //totalRow ? totalRow.values().next().value : CustomerReportRow.empty(0, 0, null), + new CustomerReportMeta({ offset: paging.skip + paging.take, total: total.cnt, fields: fieldsMeta }), + ); + } + + private async getGroupBy({ + accountId, + stages, + isTotal, + filter, + fieldsMeta, + paging, + }: { + accountId: number; + stages: GroupedStages; + isTotal: boolean; + filter: Filter; + fieldsMeta: CustomerReportFieldMeta[]; + paging?: PagingQuery; + }): Promise> { + const rowMap = new Map(); + const qb = this.createQb({ accountId, stages, isTotal, filter, fieldsMeta }); + if (paging) { + qb.offset(paging.skip).limit(paging.take); + } + const rows = await qb.getRawMany(); + for (const row of rows) { + const fields = new Map(); + for (const fieldMeta of fieldsMeta) { + const value = NumberUtil.toNumber(row[`fv_${fieldMeta.fieldId}_amount`]); + if (value) { + fields.set(fieldMeta.fieldId, new CustomerReportField(fieldMeta.fieldId, fieldMeta.fieldName, value)); + } + } + rowMap.set( + row.owner_id, + new CustomerReportRow( + row.owner_id, + row.owner_entity_type_id, + row.owner_name ?? null, + NumberUtil.toNumber(row.won_product_quantity), + new QuantityAmount(NumberUtil.toNumber(row.won_quantity), NumberUtil.toNumber(row.won_amount)), + new QuantityAmount(NumberUtil.toNumber(row.open_quantity), NumberUtil.toNumber(row.open_amount)), + new QuantityAmount(NumberUtil.toNumber(row.lost_quantity), NumberUtil.toNumber(row.lost_amount)), + new QuantityAmount(NumberUtil.toNumber(row.all_quantity), NumberUtil.toNumber(row.all_amount)), + NumberUtil.toNumber(row.won_avg_quantity), + NumberUtil.toNumber(row.won_avg_amount), + NumberUtil.toNumber(row.won_avg_time), + fields, + ), + ); + } + return rowMap; + } + + private createQb({ + accountId, + stages, + isTotal, + filter, + fieldsMeta, + }: { + accountId: number; + stages: GroupedStages; + isTotal: boolean; + filter: Filter; + fieldsMeta: CustomerReportFieldMeta[]; + }) { + const qb = this.repository + .createQueryBuilder('se') + .select(`count(se.id)::integer`, 'all_quantity') + .addSelect(`coalesce(sum(cast(se_fv.payload::json->>'value' as decimal)), 0)::decimal`, 'all_amount') + .innerJoin('entity_link', 'el', 'el.target_id = se.id') + .innerJoin('entity', 'te', 'te.id = el.source_id') + .innerJoin('entity_type', 'te_et', 'te_et.id = te.entity_type_id') + .leftJoin('orders', 'se_o', 'se_o.entity_id = se.id') + .leftJoin('order_item', 'se_oi', 'se_oi.order_id = se_o.id') + .leftJoin('field_value', 'se_fv', `se_fv.entity_id = se.id and se_fv.field_type = '${FieldType.Value}'`) + .where('se.account_id = :accountId', { accountId }) + .andWhere('se.entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }) + .andWhere('te_et.entity_category in (:...types)', { + types: filter.type ? [filter.type] : [EntityCategory.COMPANY, EntityCategory.CONTACT], + }); + if (isTotal) { + qb.addSelect('0::integer', 'owner_id'); + } else { + qb.addSelect('te.entity_type_id', 'owner_entity_type_id') + .addSelect('te.id', 'owner_id') + .addSelect('te.name', 'owner_name') + .groupBy('te.entity_type_id') + .addGroupBy('te.id') + .addGroupBy('te.name'); + } + if (stages.all?.length > 0) { + qb.andWhere('se.stage_id in (:...stageIds)', { stageIds: stages.all }); + } + if (stages.open?.length > 0) { + qb.addSelect( + `count(se.id) filter (where se.stage_id in (${stages.open.join(',')}))::integer`, + 'open_quantity', + ).addSelect( + // eslint-disable-next-line max-len + `coalesce(sum(cast(se_fv.payload::json->>'value' as decimal)) filter (where se.stage_id in (${stages.open.join(',')})), 0)::decimal`, + 'open_amount', + ); + } + if (stages.won?.length > 0) { + qb.addSelect(`count(se.id) filter (where se.stage_id in (${stages.won.join(',')}))::integer`, 'won_quantity') + .addSelect( + // eslint-disable-next-line max-len + `coalesce(sum(cast(se_fv.payload::json->>'value' as decimal)) filter (where se.stage_id in (${stages.won.join(',')})), 0)::decimal`, + 'won_amount', + ) + .addSelect( + // eslint-disable-next-line max-len + `coalesce(avg(cast(se_fv.payload::json->>'value' as decimal)) filter (where se.stage_id in (${stages.won.join(',')})), 0)::decimal`, + 'won_avg_amount', + ) + .addSelect( + // eslint-disable-next-line max-len + `coalesce(avg(extract(EPOCH from (se.closed_at - se.created_at))) filter (where se.stage_id in (${stages.won.join(',')})), 0)::decimal`, + 'won_avg_time', + ) + .addSelect( + `coalesce(sum(se_oi.quantity) filter (where se.stage_id in (${stages.won.join(',')})), 0)::decimal`, + 'won_product_quantity', + ) + .orderBy('won_amount', 'DESC') + .addOrderBy('won_quantity', 'DESC') + .addOrderBy('all_amount', 'DESC') + .addOrderBy('all_quantity', 'DESC'); + + qb.addCommonTableExpression( + this.dataSource + .createQueryBuilder() + .select('el.source_id', 'owner_id') + .addSelect(`date_trunc('month', se.created_at)`, 'month') + .addSelect('COUNT(se.id)', 'monthly_deal_count') + .from('entity', 'se') + .innerJoin('entity_link', 'el', 'el.target_id = se.id') + .where(`se.account_id = ${accountId}`) + .andWhere(`se.entity_type_id = ${filter.entityTypeId}`) + .groupBy('el.source_id') + .addGroupBy(`date_trunc('month', se.created_at)`), + 'se_month_counts', + ).leftJoin( + // eslint-disable-next-line max-len + '(SELECT owner_id, AVG(monthly_deal_count)::integer AS won_avg_quantity FROM se_month_counts GROUP BY owner_id)', + 'se_month_avg', + 'se_month_avg.owner_id = te.id', + ); + if (isTotal) { + qb.addSelect('AVG(se_month_avg.won_avg_quantity)', 'won_avg_quantity'); + } else { + qb.addSelect('se_month_avg.won_avg_quantity', 'won_avg_quantity').addGroupBy('se_month_avg.won_avg_quantity'); + } + } else { + qb.orderBy('all_amount', 'DESC').addOrderBy('all_quantity', 'DESC'); + } + if (stages.lost?.length > 0) { + qb.addSelect( + `count(se.id) filter (where se.stage_id in (${stages.lost.join(',')}))::integer`, + 'lost_quantity', + ).addSelect( + // eslint-disable-next-line max-len + `coalesce(sum(cast(se_fv.payload::json->>'value' as decimal)) filter (where se.stage_id in (${stages.lost.join(',')})), 0)::decimal`, + 'lost_amount', + ); + } + if (filter.userIds?.length) { + qb.andWhere('se.responsible_user_id in (:...userIds)', { userIds: filter.userIds }); + } + if (filter.period?.from) { + qb.andWhere('se.created_at >= :from', { from: filter.period.from }); + } + if (filter.period?.to) { + qb.andWhere('se.created_at <= :to', { to: filter.period.to }); + } + for (const fieldMeta of fieldsMeta) { + const fieldKey = `fv_${fieldMeta.fieldId}`; + qb.leftJoin( + // eslint-disable-next-line max-len + `(select entity_id, sum(cast(payload::json->>'value' as decimal)) as amount from field_value where field_id = ${fieldMeta.fieldId} group by entity_id)`, + fieldKey, + `${fieldKey}.entity_id = se.id`, + ).addSelect(`sum(${fieldKey}.amount)`, `${fieldKey}_amount`); + } + return qb; + } + + private async getFieldsMeta({ + accountId, + entityTypeId, + }: { + accountId: number; + entityTypeId: number; + }): Promise { + const fieldsMeta: CustomerReportFieldMeta[] = []; + + const formulaFields = await this.fieldService.findMany({ accountId, entityTypeId, type: FieldType.Formula }); + for (const field of formulaFields) { + fieldsMeta.push(new CustomerReportFieldMeta(field.id, field.name)); + } + + return fieldsMeta; + } +} diff --git a/backend/src/CRM/reporting/customer/dto/customer-report-field-meta.dto.ts b/backend/src/CRM/reporting/customer/dto/customer-report-field-meta.dto.ts new file mode 100644 index 0000000..5f965d9 --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/customer-report-field-meta.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class CustomerReportFieldMetaDto { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; + + @ApiProperty({ description: 'Field name' }) + @IsString() + fieldName: string; +} diff --git a/backend/src/CRM/reporting/customer/dto/customer-report-field.dto.ts b/backend/src/CRM/reporting/customer/dto/customer-report-field.dto.ts new file mode 100644 index 0000000..8694ace --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/customer-report-field.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class CustomerReportFieldDto { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; + + @ApiProperty({ description: 'Field name' }) + @IsString() + fieldName: string; + + @ApiProperty({ description: 'Field value' }) + @IsNumber() + value: number; +} diff --git a/backend/src/CRM/reporting/customer/dto/customer-report-filter.dto.ts b/backend/src/CRM/reporting/customer/dto/customer-report-filter.dto.ts new file mode 100644 index 0000000..87be20f --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/customer-report-filter.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +import { CustomerReportType } from '../enums'; + +export class CustomerReportFilterDto { + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional({ enum: CustomerReportType, nullable: true, description: 'Report type' }) + @IsOptional() + @IsEnum(CustomerReportType) + type?: CustomerReportType | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Board IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Owner IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + ownerIds?: number[] | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/CRM/reporting/customer/dto/customer-report-meta.dto.ts b/backend/src/CRM/reporting/customer/dto/customer-report-meta.dto.ts new file mode 100644 index 0000000..57d0b6e --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/customer-report-meta.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { CustomerReportFieldMetaDto } from './customer-report-field-meta.dto'; + +export class CustomerReportMetaDto extends PagingMeta { + @ApiProperty({ type: [CustomerReportFieldMetaDto], description: 'Fields meta' }) + fields: CustomerReportFieldMetaDto[]; + + constructor({ offset, total, fields }: CustomerReportMetaDto) { + super(offset, total); + + this.fields = fields; + } +} diff --git a/backend/src/CRM/reporting/customer/dto/customer-report-row.dto.ts b/backend/src/CRM/reporting/customer/dto/customer-report-row.dto.ts new file mode 100644 index 0000000..b114974 --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/customer-report-row.dto.ts @@ -0,0 +1,51 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { QuantityAmountDto } from '@/common'; +import { CustomerReportFieldDto } from './customer-report-field.dto'; + +export class CustomerReportRowDto { + @ApiProperty({ description: 'Owner ID' }) + @IsNumber() + ownerId: number; + + @ApiProperty({ description: 'Owner entity type ID' }) + @IsNumber() + ownerEntityTypeId: number; + + @ApiProperty({ description: 'Owner name' }) + @IsString() + ownerName: string; + + @ApiProperty({ description: 'Won product quantity' }) + @IsNumber() + wonProductQuantity: number; + + @ApiProperty({ type: QuantityAmountDto, description: 'Won quantity and amount' }) + won: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Open quantity and amount' }) + open: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Lost quantity and amount' }) + lost: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'All quantity and amount' }) + all: QuantityAmountDto; + + @ApiProperty({ description: 'Average won deal quantity' }) + @IsNumber() + avgWonDealQuantity: number; + + @ApiProperty({ description: 'Average won deal budget' }) + @IsNumber() + avgWonDealBudget: number; + + @ApiProperty({ description: 'Average won deal time' }) + @IsNumber() + avgWonDealTime: number; + + @ApiPropertyOptional({ type: [CustomerReportFieldDto], nullable: true, description: 'Fields' }) + @IsOptional() + fields?: CustomerReportFieldDto[] | null; +} diff --git a/backend/src/CRM/reporting/customer/dto/customer-report.dto.ts b/backend/src/CRM/reporting/customer/dto/customer-report.dto.ts new file mode 100644 index 0000000..d0da241 --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/customer-report.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; + +import { CustomerReportRowDto } from './customer-report-row.dto'; +import { CustomerReportMetaDto } from './customer-report-meta.dto'; + +export class CustomerReportDto { + @ApiProperty({ type: [CustomerReportRowDto], description: 'Rows' }) + rows: CustomerReportRowDto[]; + + @ApiPropertyOptional({ type: CustomerReportRowDto, nullable: true, description: 'Total' }) + total?: CustomerReportRowDto | null; + + @ApiProperty({ type: CustomerReportMetaDto, description: 'Meta' }) + meta: CustomerReportMetaDto; +} diff --git a/backend/src/CRM/reporting/customer/dto/index.ts b/backend/src/CRM/reporting/customer/dto/index.ts new file mode 100644 index 0000000..131a1a5 --- /dev/null +++ b/backend/src/CRM/reporting/customer/dto/index.ts @@ -0,0 +1,6 @@ +export * from './customer-report-field-meta.dto'; +export * from './customer-report-field.dto'; +export * from './customer-report-filter.dto'; +export * from './customer-report-meta.dto'; +export * from './customer-report-row.dto'; +export * from './customer-report.dto'; diff --git a/backend/src/CRM/reporting/customer/enums/customer-report-type.enum.ts b/backend/src/CRM/reporting/customer/enums/customer-report-type.enum.ts new file mode 100644 index 0000000..40438bc --- /dev/null +++ b/backend/src/CRM/reporting/customer/enums/customer-report-type.enum.ts @@ -0,0 +1,5 @@ +export enum CustomerReportType { + Contact = 'contact', + Company = 'company', + ContactCompany = 'contactAndCompany', +} diff --git a/backend/src/CRM/reporting/customer/enums/index.ts b/backend/src/CRM/reporting/customer/enums/index.ts new file mode 100644 index 0000000..e3b15cc --- /dev/null +++ b/backend/src/CRM/reporting/customer/enums/index.ts @@ -0,0 +1 @@ +export * from './customer-report-type.enum'; diff --git a/backend/src/CRM/reporting/customer/types/customer-report-field-meta.ts b/backend/src/CRM/reporting/customer/types/customer-report-field-meta.ts new file mode 100644 index 0000000..f971a4a --- /dev/null +++ b/backend/src/CRM/reporting/customer/types/customer-report-field-meta.ts @@ -0,0 +1,15 @@ +import { CustomerReportFieldMetaDto } from '../dto/customer-report-field-meta.dto'; + +export class CustomerReportFieldMeta { + fieldId: number; + fieldName: string; + + constructor(fieldId: number, fieldName: string) { + this.fieldId = fieldId; + this.fieldName = fieldName; + } + + public toDto(): CustomerReportFieldMetaDto { + return { fieldId: this.fieldId, fieldName: this.fieldName }; + } +} diff --git a/backend/src/CRM/reporting/customer/types/customer-report-field.ts b/backend/src/CRM/reporting/customer/types/customer-report-field.ts new file mode 100644 index 0000000..ed7d369 --- /dev/null +++ b/backend/src/CRM/reporting/customer/types/customer-report-field.ts @@ -0,0 +1,17 @@ +import { CustomerReportFieldDto } from '../dto/customer-report-field.dto'; + +export class CustomerReportField { + fieldId: number; + fieldName: string; + value: number; + + constructor(fieldId: number, fieldName: string, value: number) { + this.fieldId = fieldId; + this.fieldName = fieldName; + this.value = value; + } + + public toDto(): CustomerReportFieldDto { + return { fieldId: this.fieldId, fieldName: this.fieldName, value: this.value }; + } +} diff --git a/backend/src/CRM/reporting/customer/types/customer-report-meta.ts b/backend/src/CRM/reporting/customer/types/customer-report-meta.ts new file mode 100644 index 0000000..67512f5 --- /dev/null +++ b/backend/src/CRM/reporting/customer/types/customer-report-meta.ts @@ -0,0 +1,22 @@ +import { CustomerReportMetaDto } from '../dto/customer-report-meta.dto'; +import { type CustomerReportFieldMeta } from './customer-report-field-meta'; + +export class CustomerReportMeta { + fields: CustomerReportFieldMeta[]; + offset: number; + total: number; + + constructor({ offset, total, fields }: { offset: number; total: number; fields: CustomerReportFieldMeta[] }) { + this.offset = offset; + this.total = total; + this.fields = fields; + } + + public toDto(): CustomerReportMetaDto { + return new CustomerReportMetaDto({ + offset: this.offset, + total: this.total, + fields: this.fields?.map((f) => f.toDto()), + }); + } +} diff --git a/backend/src/CRM/reporting/customer/types/customer-report-row.ts b/backend/src/CRM/reporting/customer/types/customer-report-row.ts new file mode 100644 index 0000000..a0877f3 --- /dev/null +++ b/backend/src/CRM/reporting/customer/types/customer-report-row.ts @@ -0,0 +1,81 @@ +import { QuantityAmount } from '@/common'; + +import { CustomerReportRowDto } from '../dto'; +import { type CustomerReportField } from './customer-report-field'; + +export class CustomerReportRow { + ownerId: number; + ownerEntityTypeId: number; + ownerName: string; + wonProductQuantity: number; + won: QuantityAmount; + open: QuantityAmount; + lost: QuantityAmount; + all: QuantityAmount; + avgWonDealQuantity: number; + avgWonDealBudget: number; + avgWonDealTime: number; + fields: Map | null; + + constructor( + ownerId: number, + ownerEntityTypeId: number, + ownerName: string, + wonProductQuantity: number, + won: QuantityAmount, + open: QuantityAmount, + lost: QuantityAmount, + all: QuantityAmount, + avgWonDealQuantity: number, + avgWonDealBudget: number, + avgWonDealTime: number, + fields: Map | null, + ) { + this.ownerId = ownerId; + this.ownerEntityTypeId = ownerEntityTypeId; + this.ownerName = ownerName; + this.wonProductQuantity = wonProductQuantity; + this.won = won; + this.open = open; + this.lost = lost; + this.all = all; + this.avgWonDealQuantity = avgWonDealQuantity; + this.avgWonDealBudget = avgWonDealBudget; + this.avgWonDealTime = avgWonDealTime; + this.fields = fields; + } + + public static empty(ownerId: number, ownerEntityTypeId: number, ownerName: string): CustomerReportRow { + return new CustomerReportRow( + ownerId, + ownerEntityTypeId, + ownerName, + 0, + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + 0, + 0, + 0, + null, + ); + } + + public toDto(): CustomerReportRowDto { + return { + ownerId: this.ownerId, + ownerEntityTypeId: this.ownerEntityTypeId, + ownerName: this.ownerName, + wonProductQuantity: this.wonProductQuantity, + won: this.won.toDto(), + open: this.open.toDto(), + lost: this.lost.toDto(), + all: this.all.toDto(), + avgWonDealQuantity: this.avgWonDealQuantity, + avgWonDealBudget: this.avgWonDealBudget, + avgWonDealTime: this.avgWonDealTime, + fields: this.fields ? Array.from(this.fields.values()).map((v) => v.toDto()) : null, + }; + } +} diff --git a/backend/src/CRM/reporting/customer/types/customer-report.ts b/backend/src/CRM/reporting/customer/types/customer-report.ts new file mode 100644 index 0000000..2a10f19 --- /dev/null +++ b/backend/src/CRM/reporting/customer/types/customer-report.ts @@ -0,0 +1,24 @@ +import { CustomerReportDto } from '../dto'; + +import { type CustomerReportRow } from './customer-report-row'; +import { type CustomerReportMeta } from './customer-report-meta'; + +export class CustomerReport { + rows: CustomerReportRow[]; + total: CustomerReportRow | null; + meta: CustomerReportMeta; + + constructor(rows: CustomerReportRow[], total: CustomerReportRow | null, meta: CustomerReportMeta) { + this.rows = rows; + this.total = total; + this.meta = meta; + } + + public toDto(): CustomerReportDto { + return { + rows: this.rows.map((r) => r.toDto()), + total: this.total?.toDto(), + meta: this.meta.toDto(), + }; + } +} diff --git a/backend/src/CRM/reporting/customer/types/index.ts b/backend/src/CRM/reporting/customer/types/index.ts new file mode 100644 index 0000000..cbc0785 --- /dev/null +++ b/backend/src/CRM/reporting/customer/types/index.ts @@ -0,0 +1,5 @@ +export * from './customer-report-field-meta'; +export * from './customer-report-field'; +export * from './customer-report-meta'; +export * from './customer-report-row'; +export * from './customer-report'; diff --git a/backend/src/CRM/reporting/dashboard/dashboard.controller.ts b/backend/src/CRM/reporting/dashboard/dashboard.controller.ts new file mode 100644 index 0000000..d802917 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dashboard.controller.ts @@ -0,0 +1,126 @@ +import { Body, Controller, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { + SellersRatingReportDto, + DashboardFilterDto, + TopSellersReportDto, + SalesPlanReportDto, + EntitySummaryReportDto, + TaskSummaryReportDto, + SalesPipelineReportDto, + SalesPipelineFilterDto, +} from './dto'; +import { DashboardService } from './dashboard.service'; + +@ApiTags('crm/reporting/dashboard') +@Controller('crm/entity-types/:entityTypeId/dashboard') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class DashboardController { + constructor(private readonly service: DashboardService) {} + + @ApiOperation({ summary: 'Get sellers rating report', description: 'Get sellers rating report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: DashboardFilterDto, required: true, description: 'Dashboard filter' }) + @ApiOkResponse({ description: 'Sellers report', type: SellersRatingReportDto }) + @Post('rating') + public async getSellersRating( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: DashboardFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.getSellersRating({ accountId, user, entityTypeId, filter, paging }); + } + + @ApiOperation({ summary: 'Get top sellers report', description: 'Get top sellers report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: DashboardFilterDto, required: true, description: 'Dashboard filter' }) + @ApiOkResponse({ description: 'Top sellers report', type: TopSellersReportDto }) + @Post('top-sellers') + public async getTopSellers( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: DashboardFilterDto, + @Query('limit') limit?: string, + ) { + return this.service.getTopSellers({ + accountId, + user, + entityTypeId, + filter, + limit: limit ? Number(limit) : undefined, + }); + } + + @ApiOperation({ summary: 'Get sales plan report', description: 'Get sales plan report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: DashboardFilterDto, required: true, description: 'Dashboard filter' }) + @ApiOkResponse({ description: 'Sales plan report', type: SalesPlanReportDto }) + @Post('sales-plan') + public async getSalesPlanSummary( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: DashboardFilterDto, + ) { + return this.service.getSalesPlanSummary({ accountId, user, entityTypeId, filter }); + } + + @ApiOperation({ summary: 'Get entity type summary report', description: 'Get entity type summary report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: DashboardFilterDto, required: true, description: 'Dashboard filter' }) + @ApiOkResponse({ description: 'Entity type summary report', type: EntitySummaryReportDto }) + @Post('summary/entities') + public async getEntitiesSummary( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: DashboardFilterDto, + ) { + return this.service.getEntitiesSummary({ accountId, user, entityTypeId, filter }); + } + + @ApiOperation({ summary: 'Get tasks summary report', description: 'Get tasks summary report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: DashboardFilterDto, required: true, description: 'Dashboard filter' }) + @ApiOkResponse({ description: 'Tasks summary report', type: TaskSummaryReportDto }) + @Post('summary/tasks') + public async getTasksSummary( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: DashboardFilterDto, + ) { + return this.service.getTasksSummary({ accountId, user, entityTypeId, filter }); + } + + @ApiOperation({ summary: 'Get activities summary report', description: 'Get activities summary report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: DashboardFilterDto, required: true, description: 'Dashboard filter' }) + @ApiOkResponse({ description: 'Get activities summary report', type: TaskSummaryReportDto }) + @Post('summary/activities') + public async getActivitiesSummary( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: DashboardFilterDto, + ) { + return this.service.getActivitiesSummary({ accountId, user, entityTypeId, filter }); + } + + @ApiOperation({ summary: 'Get sales pipeline report', description: 'Get sales pipeline report' }) + @ApiParam({ name: 'entityTypeId', description: 'EntityType ID', type: Number, required: true }) + @ApiBody({ type: SalesPipelineFilterDto, required: true, description: 'Sales pipeline filter' }) + @ApiOkResponse({ description: 'Get top sales pipeline report', type: SalesPipelineReportDto }) + @Post('pipeline') + public async getSalesPipeline( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() filter: SalesPipelineFilterDto, + ) { + return this.service.getSalesPipeline({ accountId, user, entityTypeId, filter }); + } +} diff --git a/backend/src/CRM/reporting/dashboard/dashboard.service.ts b/backend/src/CRM/reporting/dashboard/dashboard.service.ts new file mode 100644 index 0000000..7acb5e7 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dashboard.service.ts @@ -0,0 +1,935 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, DataSource, Repository } from 'typeorm'; + +import { + DatePeriod, + DateUtil, + ForbiddenError, + intersection, + NumberUtil, + PagingMeta, + PagingQuery, + QuantityAmountDto, +} from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; + +import { SalesPlanService } from '../../sales-plan/sales-plan.service'; +import { BoardStage, BoardStageCodes, BoardStageService } from '../../board-stage'; +import { EntityType } from '../../entity-type/entities'; +import { Entity } from '../../Model/Entity/Entity'; + +import { EntityQueryBuilderHelper, SalesPipelineType, SalesPipelineTypes } from '../common'; +import { + DashboardFilterDto, + SellersRatingReportDto, + TopSellersReportDto, + SalesPlanReportDto, + EntitySummaryReportDto, + TaskSummaryReportDto, + SalesPipelineFilterDto, + SalesPipelineReportDto, + SalesPipelineReportRowDto, +} from './dto'; + +interface SalePipelineStageDuration { + stageId: number; + duration: number; +} +interface SalePipelineRowData { + quantity: number; + amount: number; +} +interface EntityTotalFilter { + createdAt?: DatePeriod; + closedAt?: DatePeriod; + userIds?: number[]; +} +interface TaskFilter { + userIds?: number[] | null; + period?: DatePeriod | null; +} + +@Injectable() +export class DashboardService { + constructor( + private readonly dataSource: DataSource, + @InjectRepository(Entity) + private readonly entityRepository: Repository, + private readonly authService: AuthorizationService, + @Inject(forwardRef(() => SalesPlanService)) + private readonly salesPlanService: SalesPlanService, + private readonly stageService: BoardStageService, + ) {} + + public async getSellersRating({ + accountId, + user, + entityTypeId, + filter, + paging, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: DashboardFilterDto; + paging: PagingQuery; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }); + if (!stages.won.length) { + return { users: [], meta: PagingMeta.empty() }; + } + + const closedAt = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const qb = this.createSellersRatingQb(); + EntityQueryBuilderHelper.addConditions( + qb, + { accountId, stageIds: stages.won, closedAt, ownerIds: filter.userIds }, + paging, + ); + const users = await qb.getRawMany(); + + const cntQb = this.entityRepository + .createQueryBuilder('e') + .select('count(distinct(e.responsible_user_id))', 'cnt') + .leftJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_type = 'value'`); + EntityQueryBuilderHelper.addConditions(cntQb, { + accountId, + stageIds: stages.won, + closedAt, + ownerIds: filter.userIds, + }); + const { cnt } = await cntQb.getRawOne(); + + return { + users: users.map((r) => ({ + userId: r.ownerId, + quantity: NumberUtil.toNumber(r.cnt), + amount: NumberUtil.toNumber(r.amount), + })), + meta: new PagingMeta(paging.skip + paging.take, NumberUtil.toNumber(cnt)), + }; + } + + public async getTopSellers({ + accountId, + user, + entityTypeId, + filter, + limit = 5, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: DashboardFilterDto; + limit?: number; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }); + if (!stages.won.length) { + return { users: [], others: QuantityAmountDto.empty(), total: QuantityAmountDto.empty() }; + } + + const closedAt = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const qb = this.createSellersRatingQb(); + EntityQueryBuilderHelper.addConditions(qb, { + accountId, + stageIds: stages.won, + closedAt, + ownerIds: filter.userIds, + }); + qb.limit(limit); + const rawUsers = await qb.getRawMany(); + const users = rawUsers.map((r) => ({ + userId: r.ownerId, + quantity: NumberUtil.toNumber(r.cnt), + amount: NumberUtil.toNumber(r.amount), + })); + + const [totalQuantity, totalAmount] = await this.getEntityTotal(accountId, stages.won, { + closedAt, + userIds: filter.userIds, + }); + + const othersQuantity = users.reduce((acc, cur) => acc - cur.quantity, totalQuantity); + const othersAmount = users.reduce((acc, cur) => acc - cur.amount, totalAmount); + + return { + users, + others: new QuantityAmountDto(othersQuantity, othersAmount), + total: new QuantityAmountDto(totalQuantity, totalAmount), + }; + } + + public async getSalesPlanSummary({ + accountId, + user, + entityTypeId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: DashboardFilterDto; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const salesPlans = await this.salesPlanService.getActualPlans( + accountId, + entityTypeId, + filter.userIds, + filter.period, + ); + + if (salesPlans.length === 0) { + return { + amount: { current: 0, plannedToday: 0, plannedTotal: 0 }, + quantity: { current: 0, plannedToday: 0, plannedTotal: 0 }, + }; + } + + const quantityPlanned = salesPlans.reduce((acc, cur) => acc + cur.quantity, 0); + const amountPlanned = salesPlans.reduce((acc, cur) => acc + cur.amount, 0); + + const { startDate, endDate } = salesPlans[0]; + + const planDays = DateUtil.diff({ startDate, endDate, unit: 'day' }); + const todayDays = DateUtil.diff({ startDate, endDate: DateUtil.now(), unit: 'day' }); + const quantityToday = quantityPlanned * (todayDays / planDays); + const amountToday = amountPlanned * (todayDays / planDays); + + const { quantity, amount } = await this.salesPlanService.getSalesPlanProgress( + accountId, + entityTypeId, + startDate, + endDate, + salesPlans.map((sp) => sp.userId), + filter.boardIds, + ); + const quantityCurrent = quantity.reduce((acc, cur) => acc + cur.value, 0); + const amountCurrent = amount.reduce((acc, cur) => acc + cur.value, 0); + + return { + amount: { current: amountCurrent, plannedToday: amountToday, plannedTotal: amountPlanned }, + quantity: { current: quantityCurrent, plannedToday: quantityToday, plannedTotal: quantityPlanned }, + }; + } + + public async getEntitiesSummary({ + accountId, + user, + entityTypeId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: DashboardFilterDto; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }); + + const [totalQuantity, totalAmount] = await this.getEntityTotal(accountId, stages.open, { + userIds: filter.userIds, + }); + const [winQuantity, winAmount] = await this.getEntityTotal(accountId, stages.won, { + closedAt: period, + userIds: filter.userIds, + }); + const [lostQuantity, lostAmount] = await this.getEntityTotal(accountId, stages.lost, { + closedAt: period, + userIds: filter.userIds, + }); + const [newQuantity, newAmount] = await this.getEntityTotal(accountId, stages.all, { + createdAt: period, + userIds: filter.userIds, + }); + + return { + total: { quantity: totalQuantity, amount: totalAmount }, + win: { quantity: winQuantity, amount: winAmount }, + lost: { quantity: lostQuantity, amount: lostAmount }, + new: { quantity: newQuantity, amount: newAmount }, + }; + } + + public async getTasksSummary({ + accountId, + user, + entityTypeId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: DashboardFilterDto; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }); + if (!stages.all.length) { + return { total: 0, completed: 0, expired: 0, noTask: 0 }; + } + + const period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const total = await this.getTasksOrActivityTotal( + accountId, + 'task', + stages.all, + { userIds: filter.userIds }, + { resolved: false }, + ); + const completed = await this.getTasksOrActivityTotal( + accountId, + 'task', + stages.all, + { userIds: filter.userIds, period }, + { resolved: true, dateField: 'resolved_date' }, + ); + const expired = await this.getTasksOrActivityTotal( + accountId, + 'task', + stages.all, + { userIds: filter.userIds }, + { resolved: false, expired: true }, + ); + const noTask = await this.getEntityWithoutTasksCount(accountId, 'task', stages.all, { + userIds: filter.userIds, + period, + }); + + return { total, completed, expired, noTask }; + } + + public async getActivitiesSummary({ + accountId, + user, + entityTypeId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: DashboardFilterDto; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }); + if (!stages.all.length) { + return { total: 0, completed: 0, expired: 0, noTask: 0 }; + } + + const period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const total = await this.getTasksOrActivityTotal( + accountId, + 'activity', + stages.all, + { userIds: filter.userIds }, + { resolved: false }, + ); + const completed = await this.getTasksOrActivityTotal( + accountId, + 'activity', + stages.all, + { userIds: filter.userIds, period }, + { resolved: true, dateField: 'resolved_date' }, + ); + const expired = await this.getTasksOrActivityTotal( + accountId, + 'activity', + stages.all, + { userIds: filter.userIds }, + { resolved: false, expired: true }, + ); + const noTask = await this.getEntityWithoutTasksCount(accountId, 'activity', stages.all, { + userIds: filter.userIds, + period, + }); + + return { total, completed, expired, noTask }; + } + + public async getSalesPipeline({ + accountId, + user, + entityTypeId, + filter, + }: { + accountId: number; + user: User; + entityTypeId: number; + filter: SalesPipelineFilterDto; + }): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'dashboard', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + filter.userIds = filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds; + const stages = await this.stageService.findMany({ accountId, boardId: filter.boardId }); + if (stages.length === 0) { + return { rows: [], totalSales: null, conversionToSale: null, averageAmount: null, averageTerm: null }; + } + const entityStageIds = this.getSalesPipelineEntityStageIds({ type: filter.type, stages }); + const period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const durations = await this.getSalesPipelineStageDuration({ + accountId, + entityTypeId, + stageIds: stages.map((s) => s.id), + entityStageIds, + type: filter.type, + period, + userIds: filter.userIds, + }); + let quantity = 0; + let isFirstStage = true; + let totalSales: number | null = null; + let conversionToSale: number | null = null; + let averageAmount: number | null = null; + const rows: SalesPipelineReportRowDto[] = []; + const rowStages = SalesPipelineTypes.Open.includes(filter.type) + ? stages.filter((s) => !s.isSystem) + : stages.filter((s) => !BoardStageCodes.lost.includes(s.code)); + const rowStageIds = rowStages.map((s) => s.id); + while (rowStages.length > 0) { + const stageIds = rowStages.map((s) => s.id); + const data = await this.getSalesPipelineRowData({ + accountId, + entityTypeId, + stageIds, + entityStageIds, + type: filter.type, + period, + userIds: filter.userIds, + }); + if (isFirstStage) { + quantity = data.quantity; + isFirstStage = false; + } + const stage = rowStages.shift(); + const duration = durations.find((d) => d.stageId === stage.id)?.duration ?? 0; + rows.push({ + stageId: stage.id, + stageName: stage.name, + stageColor: stage.color, + stageOrder: stage.sortOrder, + daysCount: !stage.isSystem ? duration : null, + percent: quantity ? data.quantity / quantity : 1, + value: data.amount, + count: data.quantity, + }); + if (BoardStageCodes.won.includes(stage.code)) { + totalSales = data.amount; + conversionToSale = quantity ? data.quantity / quantity : 1; + averageAmount = data.quantity ? data.amount / data.quantity : 0; + } + } + let averageTerm: number | null = null; + if (!SalesPipelineTypes.Open.includes(filter.type)) { + const lostStages = stages.filter((s) => BoardStageCodes.lost.includes(s.code)); + const stage = lostStages[0]; + const data = await this.getSalesPipelineRowData({ + accountId, + entityTypeId, + stageIds: [stage.id], + entityStageIds, + type: filter.type, + period, + userIds: filter.userIds, + }); + rows.push({ + stageId: stage.id, + stageName: stage.name, + stageColor: stage.color, + stageOrder: stage.sortOrder, + daysCount: null, + percent: quantity ? data.quantity / quantity : 1, + value: data.amount, + count: data.quantity, + }); + + const wonStages = stages.filter((s) => BoardStageCodes.won.includes(s.code)); + const wonStage = wonStages[0]; + averageTerm = await this.getSalesPipelineAverageTerm({ + accountId, + entityTypeId, + stageIds: rowStageIds, + entityStageIds: [wonStage.id], + type: filter.type, + period, + userIds: filter.userIds, + }); + } + + return { totalSales, conversionToSale, averageAmount, averageTerm, rows }; + } + + private async getEntityTotal( + accountId: number, + stageIds: number[], + filter?: EntityTotalFilter, + ): Promise<[number, number]> { + if (!stageIds?.length) { + return [0, 0]; + } + + const qb = this.entityRepository.createQueryBuilder('e').select('count(e.id)', 'cnt'); + + EntityQueryBuilderHelper.addConditions(qb, { + accountId, + stageIds, + createdAt: filter?.createdAt, + closedAt: filter?.closedAt, + ownerIds: filter?.userIds, + }); + + const { cnt } = await qb.getRawOne(); + const { amount } = await qb + .clone() + .select(`sum(cast(fv.payload::json->>'value' as decimal))`, 'amount') + .innerJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_type = 'value'`) + .getRawOne(); + + return [NumberUtil.toNumber(cnt), NumberUtil.toNumber(amount)]; + } + + private async getTasksOrActivityTotal( + accountId: number, + source: 'task' | 'activity', + stageIds: number[], + filter: TaskFilter, + options?: { resolved?: boolean; expired?: boolean; dateField?: string }, + ): Promise { + const qb = this.dataSource + .createQueryBuilder() + .select('count(s.*)', 'cnt') + .from(source, 's') + .innerJoin('entity', 'e', 'e.id = s.entity_id') + .where('s.account_id = :accountId', { accountId }) + .andWhere('e.stage_id IN (:...stageIds)', { stageIds }); + + if (filter.userIds?.length) { + qb.andWhere('s.responsible_user_id IN (:...userIds)', { userIds: filter.userIds }); + } + + if (filter.period && options?.dateField) { + if (filter.period.from) { + qb.andWhere(`s.${options.dateField} >= :from`, { from: filter.period.from }); + } + if (filter.period.to) { + qb.andWhere(`s.${options.dateField} <= :to`, { to: filter.period.to }); + } + } + + if (options?.resolved === true) { + qb.andWhere('s.is_resolved = true'); + } else if (options?.resolved === false) { + qb.andWhere('s.is_resolved = false'); + } + + if (options?.expired) { + qb.andWhere('s.end_date < :now', { now: DateUtil.now() }); + } + + const { cnt } = await qb.getRawOne(); + + return NumberUtil.toNumber(cnt); + } + + private async getEntityWithoutTasksCount( + accountId: number, + source: 'task' | 'activity', + stageIds: number[], + filter: TaskFilter, + ): Promise { + const qb = this.entityRepository + .createQueryBuilder('e') + .select('count(distinct(e.id))', 'cnt') + .leftJoin(source, 's', 's.entity_id = e.id') + .where('s.id IS NULL'); + + EntityQueryBuilderHelper.addConditions(qb, { + accountId, + stageIds, + createdAt: filter.period, + ownerIds: filter.userIds, + }); + + const { cnt } = await qb.getRawOne(); + return NumberUtil.toNumber(cnt); + } + + private getSalesPipelineEntityStageIds({ + type, + stages, + }: { + type: SalesPipelineType; + stages: BoardStage[]; + }): number[] | null { + if (SalesPipelineTypes.Open.includes(type)) { + return stages.filter((s) => !s.isSystem).map((s) => s.id); + } else if (SalesPipelineTypes.Closed.includes(type)) { + return stages.filter((s) => s.isSystem).map((s) => s.id); + } else { + return null; + } + } + + private async getSalesPipelineStageDuration({ + accountId, + entityTypeId, + stageIds, + entityStageIds, + type, + period, + userIds, + }: { + accountId: number; + entityTypeId: number; + stageIds: number[]; + entityStageIds: number[] | null; + type: SalesPipelineType; + period: DatePeriod | null | undefined; + userIds: number[] | null | undefined; + }): Promise { + const qb = this.dataSource + .createQueryBuilder() + .select('esh.stage_id', 'stage_id') + .addSelect('esh.entity_id', 'entity_id') + .addSelect( + 'lead(esh.created_at) over (partition by esh.entity_id order by esh.created_at) - esh.created_at', + 'duration', + ) + .from('entity_stage_history', 'esh') + .innerJoin('entity', 'e', 'e.id = esh.entity_id') + .where(`esh.account_id = ${accountId}`) + .andWhere(`esh.stage_id in (${stageIds.join(', ')})`) + .andWhere(`e.entity_type_id = ${entityTypeId}`); + if (entityStageIds) { + qb.andWhere(`e.stage_id in (${entityStageIds.join(',')})`); + } + if (userIds?.length) { + qb.andWhere(`e.responsible_user_id in (${userIds.join(',')})`); + } + if (period) { + if (SalesPipelineTypes.Active.includes(type)) { + if (period.from) { + qb.andWhere(`esh.created_at >= :eshFrom`, { eshFrom: period.from }); + } + if (period.to) { + qb.andWhere(`esh.created_at <= :eshTo`, { eshTo: period.to }); + } + } + if (SalesPipelineTypes.All.includes(type)) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('e.closed_at is null') + .orWhere('e.closed_at > :closedLimit', { closedLimit: period.from || period.to }), + ), + ); + } else if (SalesPipelineTypes.Open.includes(type)) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('e.closed_at is null') + .orWhere('e.closed_at > :closedLimit', { closedLimit: period.to || period.from }), + ), + ); + } else if (SalesPipelineTypes.Closed.includes(type)) { + if (period.from) { + qb.andWhere(`e.closed_at >= :closedFrom`, { closedFrom: period.from }); + } + if (period.to) { + qb.andWhere(`e.closed_at <= :closedTo`, { closedTo: period.to }); + } + } else if (SalesPipelineTypes.Created.includes(type)) { + if (period.from) { + qb.andWhere(`e.created_at >= :createdFrom`, { createdFrom: period.from }); + } + if (period.to) { + qb.andWhere(`e.created_at <= :createdTo`, { createdTo: period.to }); + } + } + } + + const result = await this.dataSource + .createQueryBuilder() + .select('durations.stage_id', 'stage_id') + .addSelect('avg(extract(epoch from durations.duration))', 'duration') + .from(`(${qb.getQuery()})`, 'durations') + .where('durations.duration is not null') + .groupBy('durations.stage_id') + .setParameters(qb.getParameters()) + .getRawMany(); + + return result.map((r) => ({ stageId: r.stage_id, duration: NumberUtil.toNumber(r.duration) })); + } + + private async getSalesPipelineRowData({ + accountId, + entityTypeId, + stageIds, + entityStageIds, + type, + period, + userIds, + }: { + accountId: number; + entityTypeId: number; + stageIds: number[]; + entityStageIds: number[] | null; + type: SalesPipelineType; + period: DatePeriod | null | undefined; + userIds: number[] | null | undefined; + }): Promise { + const historyQb = this.createSalesPipelineHistoryQb({ accountId, stageIds, type, period }); + + const qb = this.entityRepository + .createQueryBuilder('e') + .select('count(e.id)::integer', 'quantity') + .addSelect(`coalesce(sum(cast(fv.payload::json->>'value' as decimal)), 0)::decimal`, 'amount') + .innerJoin(`(${historyQb.getQuery()})`, 'history', 'history.entity_id = e.id') + .leftJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_type = '${FieldType.Value}'`) + .where('e.account_id = :accountId', { accountId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + if (entityStageIds) { + qb.andWhere('e.stage_id in (:...stageIds)', { stageIds: entityStageIds }); + } + if (userIds?.length) { + qb.andWhere('e.responsible_user_id in (:...userIds)', { userIds }); + } + if (period) { + if (SalesPipelineTypes.All.includes(type)) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('e.closed_at is null') + .orWhere('e.closed_at > :closedLimit', { closedLimit: period.from || period.to }), + ), + ); + } else if (SalesPipelineTypes.Open.includes(type)) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('e.closed_at is null') + .orWhere('e.closed_at > :closedLimit', { closedLimit: period.to || period.from }), + ), + ); + } else if (SalesPipelineTypes.Closed.includes(type)) { + if (period.from) { + qb.andWhere(`e.closed_at >= :closedFrom`, { closedFrom: period.from }); + } + if (period.to) { + qb.andWhere(`e.closed_at <= :closedTo`, { closedTo: period.to }); + } + } else if (SalesPipelineTypes.Created.includes(type)) { + if (period.from) { + qb.andWhere(`e.created_at >= :createdFrom`, { createdFrom: period.from }); + } + if (period.to) { + qb.andWhere(`e.created_at <= :createdTo`, { createdTo: period.to }); + } + } + } + + qb.setParameters({ ...historyQb.getParameters(), ...qb.getParameters() }); + + const result = await qb.getRawOne(); + + return { quantity: result.quantity, amount: NumberUtil.toNumber(result.amount) }; + } + + private async getSalesPipelineAverageTerm({ + accountId, + entityTypeId, + stageIds, + entityStageIds, + type, + period, + userIds, + }: { + accountId: number; + entityTypeId: number; + stageIds: number[]; + entityStageIds: number[] | null; + type: SalesPipelineType; + period: DatePeriod | null | undefined; + userIds: number[] | null | undefined; + }): Promise { + const historyQb = this.createSalesPipelineHistoryQb({ accountId, stageIds, type, period }); + + const qb = this.entityRepository + .createQueryBuilder('e') + .select('avg(extract(epoch from (e.closed_at - e.created_at)))', 'term') + .innerJoin(`(${historyQb.getQuery()})`, 'history', 'history.entity_id = e.id') + .where('e.account_id = :accountId', { accountId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + if (entityStageIds) { + qb.andWhere('e.stage_id in (:...stageIds)', { stageIds: entityStageIds }); + } + if (userIds?.length) { + qb.andWhere('e.responsible_user_id in (:...userIds)', { userIds }); + } + if (period) { + if (SalesPipelineTypes.All.includes(type)) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('e.closed_at is null') + .orWhere('e.closed_at > :closedLimit', { closedLimit: period.from || period.to }), + ), + ); + } else if (SalesPipelineTypes.Open.includes(type)) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('e.closed_at is null') + .orWhere('e.closed_at > :closedLimit', { closedLimit: period.to || period.from }), + ), + ); + } else if (SalesPipelineTypes.Closed.includes(type)) { + if (period.from) { + qb.andWhere(`e.closed_at >= :closedFrom`, { closedFrom: period.from }); + } + if (period.to) { + qb.andWhere(`e.closed_at <= :closedTo`, { closedTo: period.to }); + } + } else if (SalesPipelineTypes.Created.includes(type)) { + if (period.from) { + qb.andWhere(`e.created_at >= :createdFrom`, { createdFrom: period.from }); + } + if (period.to) { + qb.andWhere(`e.created_at <= :createdTo`, { createdTo: period.to }); + } + } + } + + qb.setParameters({ ...historyQb.getParameters(), ...qb.getParameters() }); + + const result = await qb.getRawOne(); + + return NumberUtil.toNumber(result.term); + } + + private createSalesPipelineHistoryQb({ + accountId, + stageIds, + type, + period, + }: { + accountId: number; + stageIds: number[]; + type: SalesPipelineType; + period: DatePeriod | null | undefined; + }) { + const historyQb = this.dataSource + .createQueryBuilder() + .select('distinct esh.entity_id', 'entity_id') + .from('entity_stage_history', 'esh') + .where(`esh.account_id = ${accountId}`) + .andWhere(`esh.stage_id in (${stageIds.join(',')})`); + if (period && SalesPipelineTypes.Active.includes(type)) { + if (period.from) { + historyQb.andWhere(`esh.created_at >= :eshFrom`, { eshFrom: period.from }); + } + if (period.to) { + historyQb.andWhere(`esh.created_at <= :eshTo`, { eshTo: period.to }); + } + } + + return historyQb; + } + + private createSellersRatingQb() { + return this.entityRepository + .createQueryBuilder('e') + .select('e.responsible_user_id', 'ownerId') + .addSelect('count(e.id)', 'cnt') + .addSelect(`sum(cast(fv.payload::json->>'value' as decimal))`, 'amount') + .leftJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_type = 'value'`) + .groupBy('e.responsible_user_id') + .orderBy('amount', 'DESC', 'NULLS LAST') + .addOrderBy('cnt', 'DESC', 'NULLS LAST'); + } +} diff --git a/backend/src/CRM/reporting/dashboard/dto/dashboard-filter.dto.ts b/backend/src/CRM/reporting/dashboard/dto/dashboard-filter.dto.ts new file mode 100644 index 0000000..5aa0a64 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/dashboard-filter.dto.ts @@ -0,0 +1,20 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +export class DashboardFilterDto { + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs' }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Board IDs' }) + @IsOptional() + @IsArray() + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/entity-summary-report.dto.ts b/backend/src/CRM/reporting/dashboard/dto/entity-summary-report.dto.ts new file mode 100644 index 0000000..e755df7 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/entity-summary-report.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { QuantityAmountDto } from '@/common'; + +export class EntitySummaryReportDto { + @ApiProperty({ type: QuantityAmountDto, description: 'Total quantity and amount' }) + total: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Won quantity and amount' }) + win: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Lost quantity and amount' }) + lost: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'New quantity and amount' }) + new: QuantityAmountDto; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/index.ts b/backend/src/CRM/reporting/dashboard/dto/index.ts new file mode 100644 index 0000000..061ca7d --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/index.ts @@ -0,0 +1,11 @@ +export * from './dashboard-filter.dto'; +export * from './entity-summary-report.dto'; +export * from './sales-pipeline-filter.dto'; +export * from './sales-pipeline-report-row.dto'; +export * from './sales-pipeline-report.dto'; +export * from './sales-plan-report.dto'; +export * from './sales-plan-value.dto'; +export * from './sellers-rating-report.dto'; +export * from './task-summary-report.dto'; +export * from './top-sellers-report.dto'; +export * from './user-quantity-amount.dto'; diff --git a/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-filter.dto.ts b/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-filter.dto.ts new file mode 100644 index 0000000..e71fcd9 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-filter.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +import { SalesPipelineType } from '../../common'; + +export class SalesPipelineFilterDto { + @ApiProperty({ enum: SalesPipelineType, description: 'Sales pipeline type' }) + @IsEnum(SalesPipelineType) + type: SalesPipelineType; + + @ApiProperty({ description: 'Board ID' }) + @IsNumber() + boardId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs' }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report-row.dto.ts b/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report-row.dto.ts new file mode 100644 index 0000000..c040e77 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report-row.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class SalesPipelineReportRowDto { + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + stageId: number; + + @ApiProperty({ description: 'Stage name' }) + @IsString() + stageName: string; + + @ApiProperty({ description: 'Stage color' }) + @IsString() + stageColor: string; + + @ApiProperty({ description: 'Stage order' }) + @IsNumber() + stageOrder: number; + + @ApiProperty({ nullable: true, description: 'Days count' }) + @IsOptional() + @IsNumber() + daysCount: number | null; + + @ApiProperty({ description: 'Percent' }) + @IsNumber() + percent: number; + + @ApiProperty({ description: 'Value' }) + @IsNumber() + value: number; + + @ApiProperty({ description: 'Count' }) + @IsNumber() + count: number; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report.dto.ts b/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report.dto.ts new file mode 100644 index 0000000..e91cd1b --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/sales-pipeline-report.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { SalesPipelineReportRowDto } from './sales-pipeline-report-row.dto'; + +export class SalesPipelineReportDto { + @ApiProperty({ nullable: true, description: 'Total sales' }) + @IsOptional() + @IsNumber() + totalSales: number | null; + + @ApiProperty({ nullable: true, description: 'Conversion to sale' }) + @IsOptional() + @IsNumber() + conversionToSale: number | null; + + @ApiProperty({ nullable: true, description: 'Average amount' }) + @IsOptional() + @IsNumber() + averageAmount: number | null; + + @ApiProperty({ nullable: true, description: 'Average term' }) + @IsOptional() + @IsNumber() + averageTerm: number | null; + + @ApiProperty({ type: [SalesPipelineReportRowDto], description: 'Rows' }) + rows: SalesPipelineReportRowDto[]; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/sales-plan-report.dto.ts b/backend/src/CRM/reporting/dashboard/dto/sales-plan-report.dto.ts new file mode 100644 index 0000000..e0d8a63 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/sales-plan-report.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { SalesPlanValueDto } from './sales-plan-value.dto'; + +export class SalesPlanReportDto { + @ApiProperty({ type: SalesPlanValueDto, description: 'Sales plan amount' }) + amount: SalesPlanValueDto; + + @ApiProperty({ type: SalesPlanValueDto, description: 'Sales plan quantity' }) + quantity: SalesPlanValueDto; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/sales-plan-value.dto.ts b/backend/src/CRM/reporting/dashboard/dto/sales-plan-value.dto.ts new file mode 100644 index 0000000..e99e48d --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/sales-plan-value.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class SalesPlanValueDto { + @ApiProperty({ description: 'The current value of the sales plan' }) + @IsNumber() + current: number; + + @ApiProperty({ description: 'The planned value for today' }) + @IsNumber() + plannedToday: number; + + @ApiProperty({ description: 'The planned total value' }) + @IsNumber() + plannedTotal: number; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/sellers-rating-report.dto.ts b/backend/src/CRM/reporting/dashboard/dto/sellers-rating-report.dto.ts new file mode 100644 index 0000000..a95800e --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/sellers-rating-report.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; + +import { UserQuantityAmountDto } from './user-quantity-amount.dto'; + +export class SellersRatingReportDto { + @ApiProperty({ type: [UserQuantityAmountDto], description: 'Sellers rating' }) + users: UserQuantityAmountDto[]; + + @ApiProperty({ type: PagingMeta, description: 'Paging metadata' }) + meta: PagingMeta; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/task-summary-report.dto.ts b/backend/src/CRM/reporting/dashboard/dto/task-summary-report.dto.ts new file mode 100644 index 0000000..7a83076 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/task-summary-report.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class TaskSummaryReportDto { + @ApiProperty({ description: 'Total tasks' }) + @IsNumber() + total: number; + + @ApiProperty({ description: 'Completed tasks' }) + @IsNumber() + completed: number; + + @ApiProperty({ description: 'Expired tasks' }) + @IsNumber() + expired: number; + + @ApiProperty({ description: 'No task' }) + @IsNumber() + noTask: number; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/top-sellers-report.dto.ts b/backend/src/CRM/reporting/dashboard/dto/top-sellers-report.dto.ts new file mode 100644 index 0000000..4c42cdf --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/top-sellers-report.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { QuantityAmountDto } from '@/common'; + +import { UserQuantityAmountDto } from './user-quantity-amount.dto'; + +export class TopSellersReportDto { + @ApiProperty({ type: [UserQuantityAmountDto], description: 'List of users with quantity and amount' }) + users: UserQuantityAmountDto[]; + + @ApiProperty({ type: QuantityAmountDto, description: 'Other users with quantity and amount' }) + others: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Total quantity and amount' }) + total: QuantityAmountDto; +} diff --git a/backend/src/CRM/reporting/dashboard/dto/user-quantity-amount.dto.ts b/backend/src/CRM/reporting/dashboard/dto/user-quantity-amount.dto.ts new file mode 100644 index 0000000..c889078 --- /dev/null +++ b/backend/src/CRM/reporting/dashboard/dto/user-quantity-amount.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { QuantityAmountDto } from '@/common'; + +export class UserQuantityAmountDto extends QuantityAmountDto { + @ApiProperty({ description: 'User ID' }) + @IsNumber() + userId: number; +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-entity.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-entity.dto.ts new file mode 100644 index 0000000..fcf4dda --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-entity.dto.ts @@ -0,0 +1,42 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { QuantityAmountDto } from '@/common'; + +export class CrmGeneralReportEntityDto { + @ApiProperty({ type: QuantityAmountDto }) + all: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto }) + open: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto }) + lost: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto }) + won: QuantityAmountDto; + + @ApiProperty() + @IsNumber() + avgAmount: number; + + @ApiProperty() + @IsNumber() + avgClose: number; + + constructor( + all: QuantityAmountDto, + open: QuantityAmountDto, + lost: QuantityAmountDto, + won: QuantityAmountDto, + avgAmount: number, + avgClose: number, + ) { + this.all = all; + this.open = open; + this.lost = lost; + this.won = won; + this.avgAmount = avgAmount; + this.avgClose = avgClose; + } +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-field-meta.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-field-meta.dto.ts new file mode 100644 index 0000000..c697d39 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-field-meta.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { CrmGeneralReportFieldOptionMetaDto } from './crm-general-report-field-option-meta.dto'; + +export class CrmGeneralReportFieldMetaDto { + @ApiProperty() + @IsNumber() + fieldId: number; + + @ApiProperty() + @IsString() + fieldName: string; + + @ApiPropertyOptional({ type: [CrmGeneralReportFieldOptionMetaDto], nullable: true }) + @IsOptional() + @IsArray() + values?: CrmGeneralReportFieldOptionMetaDto[] | null; +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-field-option-meta.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-field-option-meta.dto.ts new file mode 100644 index 0000000..64cd8ac --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-field-option-meta.dto.ts @@ -0,0 +1,21 @@ +import { FieldFormat } from '@/modules/entity/entity-field/field'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +export class CrmGeneralReportFieldOptionMetaDto { + @ApiProperty() + @IsNumber() + optionId: number; + + @ApiProperty() + optionLabel: string | boolean; + + @ApiPropertyOptional({ + enum: FieldFormat, + nullable: true, + description: 'Field format of display for specific field types', + }) + @IsOptional() + @IsEnum(FieldFormat) + format?: FieldFormat | null; +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-field-value.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-field-value.dto.ts new file mode 100644 index 0000000..a1831c9 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-field-value.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class CrmGeneralReportFieldValueDto { + @ApiProperty() + @IsNumber() + optionId: number; + + @ApiProperty() + optionLabel: string | boolean; + + @ApiProperty() + @IsNumber() + quantity: number; + + @ApiProperty() + @IsNumber() + amount: number; + + constructor(optionId: number, optionLabel: string | boolean, quantity: number, amount: number) { + this.optionId = optionId; + this.optionLabel = optionLabel; + this.quantity = quantity; + this.amount = amount; + } +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-field.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-field.dto.ts new file mode 100644 index 0000000..36bff67 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-field.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsString } from 'class-validator'; + +import { CrmGeneralReportFieldValueDto } from './crm-general-report-field-value.dto'; + +export class CrmGeneralReportFieldDto { + @ApiProperty() + @IsNumber() + fieldId: number; + + @ApiProperty() + @IsString() + fieldName: string; + + @ApiProperty({ type: [CrmGeneralReportFieldValueDto] }) + @IsArray() + values: CrmGeneralReportFieldValueDto[]; + + constructor(fieldId: number, fieldName: string, values: CrmGeneralReportFieldValueDto[]) { + this.fieldId = fieldId; + this.fieldName = fieldName; + this.values = values; + } +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-meta.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-meta.dto.ts new file mode 100644 index 0000000..579066f --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-meta.dto.ts @@ -0,0 +1,7 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { CrmGeneralReportFieldMetaDto } from './crm-general-report-field-meta.dto'; + +export class CrmGeneralReportMetaDto { + @ApiProperty({ type: [CrmGeneralReportFieldMetaDto] }) + fields: CrmGeneralReportFieldMetaDto[]; +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-row.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-row.dto.ts new file mode 100644 index 0000000..a0a596e --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-row.dto.ts @@ -0,0 +1,38 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { CrmGeneralReportEntityDto } from './crm-general-report-entity.dto'; +import { CrmGeneralReportTaskDto } from './crm-general-report-task.dto'; +import { CrmGeneralReportFieldDto } from './crm-general-report-field.dto'; + +export class CrmGeneralReportRowDto { + @ApiProperty() + @IsNumber() + ownerId: number; + + @ApiProperty({ type: CrmGeneralReportEntityDto }) + entity: CrmGeneralReportEntityDto; + + @ApiProperty({ type: CrmGeneralReportTaskDto }) + task: CrmGeneralReportTaskDto; + + @ApiProperty({ type: CrmGeneralReportTaskDto }) + activity: CrmGeneralReportTaskDto; + + @ApiProperty({ type: [CrmGeneralReportFieldDto] }) + fields: CrmGeneralReportFieldDto[]; + + constructor( + ownerId: number, + entity: CrmGeneralReportEntityDto, + task: CrmGeneralReportTaskDto, + activity: CrmGeneralReportTaskDto, + fields: CrmGeneralReportFieldDto[], + ) { + this.ownerId = ownerId; + this.entity = entity; + this.task = task; + this.activity = activity; + this.fields = fields; + } +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report-task.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report-task.dto.ts new file mode 100644 index 0000000..55eb520 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report-task.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class CrmGeneralReportTaskDto { + @ApiProperty() + @IsNumber() + all: number; + + @ApiProperty() + @IsNumber() + open: number; + + @ApiProperty() + @IsNumber() + expired: number; + + @ApiProperty() + @IsNumber() + resolved: number; + + constructor(all: number, open: number, expired: number, resolved: number) { + this.all = all; + this.open = open; + this.expired = expired; + this.resolved = resolved; + } +} diff --git a/backend/src/CRM/reporting/general/dto/crm-general-report.dto.ts b/backend/src/CRM/reporting/general/dto/crm-general-report.dto.ts new file mode 100644 index 0000000..2850d81 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/crm-general-report.dto.ts @@ -0,0 +1,30 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { CrmGeneralReportMetaDto } from './crm-general-report-meta.dto'; +import { CrmGeneralReportRowDto } from './crm-general-report-row.dto'; + +export class CrmGeneralReportDto { + @ApiProperty({ type: [CrmGeneralReportRowDto] }) + users: CrmGeneralReportRowDto[]; + + @ApiProperty({ type: [CrmGeneralReportRowDto] }) + departments: CrmGeneralReportRowDto[]; + + @ApiProperty({ type: CrmGeneralReportRowDto }) + total: CrmGeneralReportRowDto; + + @ApiProperty({ type: CrmGeneralReportMetaDto }) + meta: CrmGeneralReportMetaDto; + + constructor( + users: CrmGeneralReportRowDto[], + departments: CrmGeneralReportRowDto[], + total: CrmGeneralReportRowDto, + meta: CrmGeneralReportMetaDto, + ) { + this.users = users; + this.departments = departments; + this.total = total; + this.meta = meta; + } +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-call.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-call.dto.ts new file mode 100644 index 0000000..bdfdcc5 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-call.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class GeneralReportFilterVisibilityCallDto { + @ApiPropertyOptional({ nullable: true, description: 'Exclude calls block' }) + @IsOptional() + @IsBoolean() + exclude?: boolean | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-entity.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-entity.dto.ts new file mode 100644 index 0000000..ce21989 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-entity.dto.ts @@ -0,0 +1,24 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class GeneralReportFilterVisibilityEntityDto { + @ApiPropertyOptional({ nullable: true, description: 'Exclude entities block' }) + @IsOptional() + @IsBoolean() + exclude?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude open entities' }) + @IsOptional() + @IsBoolean() + excludeOpen?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude lost entities' }) + @IsOptional() + @IsBoolean() + excludeLost?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude won entities' }) + @IsOptional() + @IsBoolean() + excludeWon?: boolean | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field-option.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field-option.dto.ts new file mode 100644 index 0000000..2dae7cb --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field-option.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; + +export class GeneralReportFilterVisibilityFieldOptionDto { + @ApiProperty({ nullable: true, description: 'Option ID' }) + @IsNumber() + optionId: number; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude option' }) + @IsOptional() + @IsBoolean() + exclude?: boolean | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field.dto.ts new file mode 100644 index 0000000..516a336 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-field.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; +import { GeneralReportFilterVisibilityFieldOptionDto } from './general-report-filter-visibility-field-option.dto'; + +export class GeneralReportFilterVisibilityFieldDto { + @ApiProperty({ nullable: true, description: 'Field ID' }) + @IsNumber() + fieldId: number; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude field' }) + @IsOptional() + @IsBoolean() + exclude?: boolean | null; + + @ApiPropertyOptional({ + type: GeneralReportFilterVisibilityFieldOptionDto, + nullable: true, + description: 'Field options', + }) + @IsOptional() + options?: GeneralReportFilterVisibilityFieldOptionDto[] | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-fields.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-fields.dto.ts new file mode 100644 index 0000000..3bf9a4a --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-fields.dto.ts @@ -0,0 +1,15 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +import { GeneralReportFilterVisibilityFieldDto } from './general-report-filter-visibility-field.dto'; + +export class GeneralReportFilterVisibilityFieldsDto { + @ApiPropertyOptional({ nullable: true, description: 'Exclude fields block' }) + @IsOptional() + @IsBoolean() + exclude?: boolean | null; + + @ApiPropertyOptional({ type: [GeneralReportFilterVisibilityFieldDto], nullable: true, description: 'Fields' }) + @IsOptional() + fields?: GeneralReportFilterVisibilityFieldDto[] | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-task.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-task.dto.ts new file mode 100644 index 0000000..630b156 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility-task.dto.ts @@ -0,0 +1,24 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class GeneralReportFilterVisibilityTaskDto { + @ApiPropertyOptional({ nullable: true, description: 'Exclude tasks block' }) + @IsOptional() + @IsBoolean() + exclude?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude open tasks' }) + @IsOptional() + @IsBoolean() + excludeOpen?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude expired tasks' }) + @IsOptional() + @IsBoolean() + excludeExpired?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Exclude resolved tasks' }) + @IsOptional() + @IsBoolean() + excludeResolved?: boolean | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter-visibility.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility.dto.ts new file mode 100644 index 0000000..5f4d4b6 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter-visibility.dto.ts @@ -0,0 +1,41 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; + +import { GeneralReportFilterVisibilityEntityDto } from './general-report-filter-visibility-entity.dto'; +import { GeneralReportFilterVisibilityTaskDto } from './general-report-filter-visibility-task.dto'; +import { GeneralReportFilterVisibilityFieldsDto } from './general-report-filter-visibility-fields.dto'; +import { GeneralReportFilterVisibilityCallDto } from './general-report-filter-visibility-call.dto'; + +export class GeneralReportFilterVisibilityDto { + @ApiPropertyOptional({ + type: GeneralReportFilterVisibilityEntityDto, + nullable: true, + description: 'Entities visibility', + }) + @IsOptional() + entity?: GeneralReportFilterVisibilityEntityDto | null; + + @ApiPropertyOptional({ type: GeneralReportFilterVisibilityTaskDto, nullable: true, description: 'Tasks visibility' }) + @IsOptional() + task?: GeneralReportFilterVisibilityTaskDto | null; + + @ApiPropertyOptional({ + type: GeneralReportFilterVisibilityTaskDto, + nullable: true, + description: 'Activities visibility', + }) + @IsOptional() + activity?: GeneralReportFilterVisibilityTaskDto | null; + + @ApiPropertyOptional({ + type: GeneralReportFilterVisibilityFieldsDto, + nullable: true, + description: 'Fields visibility', + }) + @IsOptional() + fields?: GeneralReportFilterVisibilityFieldsDto | null; + + @ApiPropertyOptional({ type: GeneralReportFilterVisibilityCallDto, nullable: true, description: 'Calls visibility' }) + @IsOptional() + call?: GeneralReportFilterVisibilityCallDto | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-filter.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-filter.dto.ts new file mode 100644 index 0000000..53c2dfe --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-filter.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +import { BoardStageType } from '../../../board-stage'; +import { GeneralReportType } from '../enums'; +import { GeneralReportFilterVisibilityDto } from './general-report-filter-visibility.dto'; + +export class GeneralReportFilterDto { + @ApiProperty({ enum: GeneralReportType, description: 'Report type' }) + @IsEnum(GeneralReportType) + type: GeneralReportType; + + @ApiPropertyOptional({ description: 'Field ID for owner instead of responsibleUserId' }) + @IsOptional() + ownerFieldId?: number; + + @ApiPropertyOptional({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Board IDs' }) + @IsOptional() + @IsArray() + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs' }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ enum: BoardStageType, nullable: true, description: 'Stage type' }) + @IsOptional() + @IsEnum(BoardStageType) + stageType?: BoardStageType | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; + + @ApiPropertyOptional({ type: GeneralReportFilterVisibilityDto, nullable: true, description: 'Visibility' }) + @IsOptional() + visibility?: GeneralReportFilterVisibilityDto | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report-row.dto.ts b/backend/src/CRM/reporting/general/dto/general-report-row.dto.ts new file mode 100644 index 0000000..bf9649a --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report-row.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { CallReportBlockDto } from '@/modules/telephony/voximplant/voximplant-reporting/dto/call-report-block.dto'; +import { CrmGeneralReportEntityDto } from '@/CRM/reporting/general/dto/crm-general-report-entity.dto'; +import { CrmGeneralReportFieldDto } from '@/CRM/reporting/general/dto/crm-general-report-field.dto'; +import { CrmGeneralReportTaskDto } from '@/CRM/reporting/general/dto/crm-general-report-task.dto'; + +export class GeneralReportRowDto { + @ApiProperty({ description: 'Owner ID' }) + @IsNumber() + ownerId: number; + + @ApiPropertyOptional({ nullable: true, type: CrmGeneralReportEntityDto, description: 'Entities report block' }) + @IsOptional() + entity?: CrmGeneralReportEntityDto | null; + + @ApiPropertyOptional({ nullable: true, type: CrmGeneralReportTaskDto, description: 'Tasks report block' }) + @IsOptional() + task?: CrmGeneralReportTaskDto | null; + + @ApiPropertyOptional({ nullable: true, type: CrmGeneralReportTaskDto, description: 'Activities report block' }) + @IsOptional() + activity?: CrmGeneralReportTaskDto | null; + + @ApiPropertyOptional({ nullable: true, type: [CrmGeneralReportFieldDto], description: 'Fields report block' }) + @IsOptional() + fields?: CrmGeneralReportFieldDto[] | null; + + @ApiPropertyOptional({ nullable: true, type: CallReportBlockDto, description: 'Calls report block' }) + @IsOptional() + call?: CallReportBlockDto | null; +} diff --git a/backend/src/CRM/reporting/general/dto/general-report.dto.ts b/backend/src/CRM/reporting/general/dto/general-report.dto.ts new file mode 100644 index 0000000..249c737 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/general-report.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { CrmGeneralReportMetaDto } from '@/CRM/reporting/general/dto/crm-general-report-meta.dto'; +import { GeneralReportRowDto } from './general-report-row.dto'; + +export class GeneralReportDto { + @ApiProperty({ type: [GeneralReportRowDto], description: 'Users' }) + users: GeneralReportRowDto[]; + + @ApiProperty({ type: [GeneralReportRowDto], description: 'Departments' }) + departments: GeneralReportRowDto[]; + + @ApiProperty({ type: GeneralReportRowDto, description: 'Total' }) + total: GeneralReportRowDto; + + @ApiProperty({ type: CrmGeneralReportMetaDto, description: 'Meta' }) + meta: CrmGeneralReportMetaDto; +} diff --git a/backend/src/CRM/reporting/general/dto/index.ts b/backend/src/CRM/reporting/general/dto/index.ts new file mode 100644 index 0000000..8b1d705 --- /dev/null +++ b/backend/src/CRM/reporting/general/dto/index.ts @@ -0,0 +1,19 @@ +export * from './crm-general-report-entity.dto'; +export * from './crm-general-report-field-meta.dto'; +export * from './crm-general-report-field-option-meta.dto'; +export * from './crm-general-report-field-value.dto'; +export * from './crm-general-report-field.dto'; +export * from './crm-general-report-meta.dto'; +export * from './crm-general-report-row.dto'; +export * from './crm-general-report-task.dto'; +export * from './crm-general-report.dto'; +export * from './general-report-filter-visibility-call.dto'; +export * from './general-report-filter-visibility-entity.dto'; +export * from './general-report-filter-visibility-field-option.dto'; +export * from './general-report-filter-visibility-field.dto'; +export * from './general-report-filter-visibility-fields.dto'; +export * from './general-report-filter-visibility-task.dto'; +export * from './general-report-filter-visibility.dto'; +export * from './general-report-filter.dto'; +export * from './general-report-row.dto'; +export * from './general-report.dto'; diff --git a/backend/src/CRM/reporting/general/enums/general-report-type.enum.ts b/backend/src/CRM/reporting/general/enums/general-report-type.enum.ts new file mode 100644 index 0000000..219501a --- /dev/null +++ b/backend/src/CRM/reporting/general/enums/general-report-type.enum.ts @@ -0,0 +1,5 @@ +export enum GeneralReportType { + User = 'user', + Rating = 'rating', + Department = 'department', +} diff --git a/backend/src/CRM/reporting/general/enums/index.ts b/backend/src/CRM/reporting/general/enums/index.ts new file mode 100644 index 0000000..03aed19 --- /dev/null +++ b/backend/src/CRM/reporting/general/enums/index.ts @@ -0,0 +1 @@ +export * from './general-report-type.enum'; diff --git a/backend/src/CRM/reporting/general/general-report.controller.ts b/backend/src/CRM/reporting/general/general-report.controller.ts new file mode 100644 index 0000000..c3b4e20 --- /dev/null +++ b/backend/src/CRM/reporting/general/general-report.controller.ts @@ -0,0 +1,24 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { GeneralReportService } from './general-report.service'; +import { GeneralReportFilterDto, GeneralReportDto } from './dto'; + +@ApiTags('crm/reporting') +@Controller('crm/reporting/general') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class GeneralReportController { + constructor(private readonly service: GeneralReportService) {} + + @ApiOperation({ summary: 'Get general report', description: 'Get general report' }) + @ApiBody({ type: GeneralReportFilterDto, required: true, description: 'General report filter' }) + @ApiOkResponse({ description: 'General report', type: GeneralReportDto }) + @Post() + public async getGeneralReport(@CurrentAuth() { accountId, user }: AuthData, @Body() filter: GeneralReportFilterDto) { + return this.service.getGeneralReport({ accountId, user, filter }); + } +} diff --git a/backend/src/CRM/reporting/general/general-report.service.ts b/backend/src/CRM/reporting/general/general-report.service.ts new file mode 100644 index 0000000..7f54a46 --- /dev/null +++ b/backend/src/CRM/reporting/general/general-report.service.ts @@ -0,0 +1,823 @@ +import { Injectable } from '@nestjs/common'; + +import { DatePeriod, ForbiddenError, intersection, propagateData } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization'; +import { DepartmentService } from '@/modules/iam/department'; +import { User } from '@/modules/iam/user/entities'; +import { FieldType, FieldTypes } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; +import { TelephonyReportType } from '@/modules/telephony/voximplant/voximplant-reporting/enums'; +import { CallReportRow } from '@/modules/telephony/voximplant/voximplant-reporting/types'; +// eslint-disable-next-line max-len +import { VoximplantReportingService } from '@/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.service'; + +import { BoardStageService, GroupedStages } from '../../board-stage'; +import { EntityType } from '../../entity-type'; + +import { OwnerDateValue, ReportRowOwner } from '../common'; +import { CrmReportingService } from '../crm-reporting.service'; + +import { GeneralReportFilterDto } from './dto'; +import { + CrmGeneralReport, + CrmGeneralReportField, + CrmGeneralReportFieldMeta, + CrmGeneralReportFieldOptionMeta, + CrmGeneralReportFieldValue, + CrmGeneralReportMeta, + CrmGeneralReportRow, + GeneralReport, + GeneralReportRow, +} from './types'; +import { GeneralReportType } from './enums'; + +interface VisibilityEntity { + exclude?: boolean | null; + excludeOpen?: boolean | null; + excludeLost?: boolean | null; + excludeWon?: boolean | null; +} +interface VisibilityTask { + exclude?: boolean | null; + excludeOpen?: boolean | null; + excludeExpired?: boolean | null; + excludeResolved?: boolean | null; +} +interface VisibilityFieldOption { + optionId: number; + exclude?: boolean | null; +} +interface VisibilityField { + fieldId: number; + exclude?: boolean | null; + options?: VisibilityFieldOption[] | null; +} +interface VisibilityFields { + exclude?: boolean | null; + fields?: VisibilityField[] | null; +} +interface VisibilityCall { + exclude?: boolean | null; +} +interface Visibility { + entity?: VisibilityEntity | null; + task?: VisibilityTask | null; + activity?: VisibilityTask | null; + fields?: VisibilityFields | null; + call?: VisibilityCall | null; +} + +interface Filter { + include: { users: boolean; departments: boolean }; + stages: GroupedStages; + period?: DatePeriod; + userIds?: number[]; + visibility?: Visibility | null; +} + +@Injectable() +export class GeneralReportService { + constructor( + private readonly authService: AuthorizationService, + private readonly departmentService: DepartmentService, + private readonly stageService: BoardStageService, + private readonly reportingService: CrmReportingService, + private readonly fieldService: FieldService, + private readonly telephonyReporting: VoximplantReportingService, + ) {} + + public async getGeneralReport({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: GeneralReportFilterDto; + }): Promise { + const crmReport = await this.getCrmGeneralReport(accountId, user, filter); + const callReport = !filter.visibility?.call?.exclude + ? await this.telephonyReporting.getCallReport({ + accountId, + user, + filter: { ...filter, type: this.toTelephoneReportType(filter.type) }, + }) + : null; + + const users = this.combineGeneralReportRows(crmReport.users, callReport?.users); + const departments = this.combineGeneralReportRows(crmReport.departments, callReport?.departments); + + const total = new GeneralReportRow( + crmReport.total?.ownerId, + crmReport.total?.entity, + crmReport.total?.task, + crmReport.total?.activity, + crmReport.total?.field, + callReport?.total?.call, + ); + + return new GeneralReport(users, departments, total, crmReport.meta); + } + + private combineGeneralReportRows( + crmRows: Map | null | undefined, + callRows: Map | null | undefined, + ): Map { + const rows: Map = new Map(); + if (crmRows) { + for (const [key, crmRow] of crmRows) { + const callRaw = callRows?.get(key); + rows.set( + key, + new GeneralReportRow(key, crmRow.entity, crmRow.task, crmRow.activity, crmRow.field, callRaw?.call), + ); + } + } + if (callRows) { + for (const [key, callRaw] of callRows) { + if (!rows.has(key)) { + rows.set(key, new GeneralReportRow(key, null, null, null, null, callRaw?.call)); + } + } + } + return rows; + } + private toTelephoneReportType(type: GeneralReportType): TelephonyReportType { + switch (type) { + case GeneralReportType.Department: + return TelephonyReportType.Department; + case GeneralReportType.Rating: + return TelephonyReportType.Rating; + case GeneralReportType.User: + return TelephonyReportType.User; + } + } + + private async getCrmGeneralReport( + accountId: number, + user: User, + filter: GeneralReportFilterDto, + ): Promise { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: EntityType.getAuthorizable(filter.entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId: filter.entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + type: filter.stageType, + }); + const include = { + users: filter.type !== GeneralReportType.Department, + departments: filter.type !== GeneralReportType.Rating, + }; + + const report = await this.getReport( + accountId, + filter.entityTypeId, + { + include, + stages, + period: filter.period ? DatePeriod.fromFilter(filter.period) : undefined, + userIds: filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds, + visibility: filter.visibility, + }, + filter.ownerFieldId, + filter.type === GeneralReportType.Rating, + ); + + if (report.departments.size) { + const useWon = stages.won?.length > 0; + const hierarchy = await this.departmentService.getHierarchy({ accountId }); + + if (hierarchy.length) { + propagateData(hierarchy, report.departments, (ownerId: number) => { + return CrmGeneralReport.createEmptyRow(ownerId, useWon); + }); + } + } + + return report; + } + + private async getReport( + accountId: number, + entityTypeId: number, + filter: Filter, + ownerFieldId: number | undefined, + sort: boolean, + ): Promise { + const useWon = filter.stages.won?.length > 0; + const [meta, users, departments, total] = await Promise.all([ + this.getReportMeta(accountId, entityTypeId), + filter.include.users + ? this.getReportGroupBy(accountId, entityTypeId, 'user', ownerFieldId, useWon, sort, filter) + : Promise.resolve(new Map()), + filter.include.departments + ? await this.getReportGroupBy(accountId, entityTypeId, 'department', ownerFieldId, useWon, sort, filter) + : Promise.resolve(new Map()), + this.getReportGroupBy(accountId, entityTypeId, 'total', ownerFieldId, useWon, false, filter), + ]); + + return new CrmGeneralReport(users, departments, total.values().next().value, meta); + } + + private async getReportMeta(accountId: number, entityTypeId: number): Promise { + const fieldsMeta: CrmGeneralReportFieldMeta[] = ( + await Promise.all([ + this.getSelectFieldsMeta({ accountId, entityTypeId }), + this.getSwitchFieldsMeta({ accountId, entityTypeId }), + this.getFormulaFieldsMeta({ accountId, entityTypeId }), + ]) + ).flat(); + return new CrmGeneralReportMeta(fieldsMeta); + } + private async getSelectFieldsMeta({ + accountId, + entityTypeId, + }: { + accountId: number; + entityTypeId: number; + }): Promise { + const fields = await this.fieldService.findMany( + { + accountId, + entityTypeId, + type: [...FieldTypes.select, ...FieldTypes.multiSelect], + }, + { expand: ['options'] }, + ); + return fields.map( + (field) => + new CrmGeneralReportFieldMeta( + field.id, + field.name, + field.options.map((o) => new CrmGeneralReportFieldOptionMeta(o.id, o.label, field.format)), + ), + ); + } + private async getSwitchFieldsMeta({ + accountId, + entityTypeId, + }: { + accountId: number; + entityTypeId: number; + }): Promise { + const fields = await this.fieldService.findMany({ accountId, entityTypeId, type: FieldType.Switch }); + return fields.map( + (field) => + new CrmGeneralReportFieldMeta(field.id, field.name, [ + new CrmGeneralReportFieldOptionMeta(0, false, field.format), + new CrmGeneralReportFieldOptionMeta(1, true, field.format), + ]), + ); + } + private async getFormulaFieldsMeta({ + accountId, + entityTypeId, + }: { + accountId: number; + entityTypeId: number; + }): Promise { + const fields = await this.fieldService.findMany({ + accountId, + entityTypeId, + type: [FieldType.Formula, FieldType.Number], + }); + return fields.map( + (field) => + new CrmGeneralReportFieldMeta(field.id, field.name, [new CrmGeneralReportFieldOptionMeta(0, '', field.format)]), + ); + } + + private async getReportGroupBy( + accountId: number, + entityTypeId: number, + owner: ReportRowOwner, + ownerFieldId: number | undefined, + useWon: boolean, + sort: boolean, + filter: Filter, + ): Promise> { + const rowMap = new Map(); + + if (!filter.visibility?.entity?.exclude) + await this.processEntities(accountId, owner, ownerFieldId, useWon, filter, rowMap); + if (!filter.visibility?.task?.exclude) + await this.processTasks(accountId, filter.stages.all, useWon, owner, filter, rowMap); + if (!filter.visibility?.activity?.exclude) + await this.processActivities(accountId, filter.stages.all, useWon, owner, filter, rowMap); + + if (!filter.visibility?.fields?.exclude) { + const excludeFields = filter.visibility?.fields?.fields?.filter((f) => f.exclude).map((f) => f.fieldId); + await this.processSelectField( + accountId, + entityTypeId, + owner, + ownerFieldId, + useWon, + filter, + excludeFields, + rowMap, + ); + await this.processMultiSelectField( + accountId, + entityTypeId, + owner, + ownerFieldId, + useWon, + filter, + excludeFields, + rowMap, + ); + await this.processSwitchField( + accountId, + entityTypeId, + owner, + ownerFieldId, + useWon, + filter, + excludeFields, + rowMap, + ); + await this.processNumberField( + accountId, + entityTypeId, + owner, + ownerFieldId, + useWon, + filter, + excludeFields, + rowMap, + ); + } + + if (sort) { + const rows = Array.from(rowMap.values()); + + const sorted = rows.sort((rowA, rowB) => + useWon ? rowB.entity.won.amount - rowA.entity.won.amount : rowB.entity.all.amount - rowA.entity.all.amount, + ); + + return new Map(sorted.map((row) => [row.ownerId, row])); + } + + return rowMap; + } + + private async processEntities( + accountId: number, + owner: ReportRowOwner, + userOwnerFieldId: number | undefined, + useWon: boolean, + filter: Filter, + rowMap: Map, + ) { + const [open, lost, won] = await Promise.all([ + filter.stages.open?.length && !filter.visibility?.entity?.excludeOpen + ? this.reportingService.getEntityGroupBy( + accountId, + filter.stages.open, + { owner, userOwnerFieldId }, + { amount: true, quantity: true }, + { createdAt: filter.period, userIds: filter.userIds }, + ) + : Promise.resolve({ quantity: [], amount: [] }), + filter.stages.lost?.length && !filter.visibility?.entity?.excludeLost + ? this.reportingService.getEntityGroupBy( + accountId, + filter.stages.lost, + { owner, userOwnerFieldId }, + { amount: true, quantity: true, close: !useWon }, + { closedAt: filter.period, userIds: filter.userIds }, + ) + : Promise.resolve({ quantity: [], amount: [], close: [] }), + filter.stages.won?.length && !filter.visibility?.entity?.excludeWon + ? this.reportingService.getEntityGroupBy( + accountId, + filter.stages.won, + { owner, userOwnerFieldId }, + { amount: true, quantity: true, close: true }, + { closedAt: filter.period, userIds: filter.userIds }, + ) + : Promise.resolve({ quantity: [], amount: [], close: [] }), + ]); + for (const { ownerId, value } of open.quantity) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.all.quantity += value; + values.entity.open.quantity = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of open.amount) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.all.amount += value; + values.entity.open.amount = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of lost.quantity) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.all.quantity += value; + values.entity.lost.quantity = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of lost.amount) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.all.amount += value; + values.entity.lost.amount = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of lost.close) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.close = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of won.quantity) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.all.quantity += value; + values.entity.won.quantity = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of won.amount) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.all.amount += value; + values.entity.won.amount = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of won.close) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.entity.close = value; + rowMap.set(ownerId, values); + } + } + private async processTasks( + accountId: number, + stageIds: number[], + useWon: boolean, + owner: ReportRowOwner, + filter: Filter, + rowMap: Map, + ) { + const [open, expired, resolved] = await this.getTasksOrActivity(accountId, 'task', stageIds, owner, filter); + + for (const { ownerId, value } of open) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.task.all += value; + values.task.open = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of expired) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.task.expired = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of resolved) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.task.all += value; + values.task.resolved = value; + rowMap.set(ownerId, values); + } + } + private async processActivities( + accountId: number, + stageIds: number[], + useWon: boolean, + owner: ReportRowOwner, + filter: Filter, + rowMap: Map, + ) { + const [open, expired, resolved] = await this.getTasksOrActivity(accountId, 'activity', stageIds, owner, filter); + + for (const { ownerId, value } of open) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.activity.all += value; + values.activity.open = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of expired) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.activity.expired = value; + rowMap.set(ownerId, values); + } + for (const { ownerId, value } of resolved) { + const values = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + values.activity.all += value; + values.activity.resolved = value; + rowMap.set(ownerId, values); + } + } + private async getTasksOrActivity( + accountId: number, + source: 'task' | 'activity', + stageIds: number[], + owner: ReportRowOwner, + filter: Filter, + ): Promise<[OwnerDateValue[], OwnerDateValue[], OwnerDateValue[]]> { + return Promise.all([ + !filter.visibility?.task?.excludeOpen + ? this.reportingService.getTaskOrActivityGroupBy( + accountId, + source, + stageIds, + { owner }, + { resolved: false, period: filter.period, dateField: 'created_at', ownerIds: filter.userIds }, + ) + : Promise.resolve([] as OwnerDateValue[]), + !filter.visibility?.task?.excludeExpired + ? this.reportingService.getTaskOrActivityGroupBy( + accountId, + source, + stageIds, + { owner }, + { + expired: true, + resolved: false, + period: filter.period, + dateField: 'created_at', + ownerIds: filter.userIds, + }, + ) + : Promise.resolve([] as OwnerDateValue[]), + !filter.visibility?.task?.excludeResolved + ? this.reportingService.getTaskOrActivityGroupBy( + accountId, + source, + stageIds, + { owner }, + { resolved: true, period: filter.period, dateField: 'created_at', ownerIds: filter.userIds }, + ) + : Promise.resolve([] as OwnerDateValue[]), + ]); + } + private async processSelectField( + accountId: number, + entityTypeId: number, + owner: ReportRowOwner, + userOwnerFieldId: number | undefined, + useWon: boolean, + filter: Filter, + excludeFieldIds: number[] | null | undefined, + rowMap: Map, + ) { + const fields = await this.fieldService.findMany( + { + accountId, + entityTypeId, + type: FieldTypes.select, + excludeId: excludeFieldIds, + }, + { expand: ['options'] }, + ); + const results = await Promise.all( + fields + .map((field) => { + // const excludeIds = filter.visibility?.fields?.fields + // ?.find((f) => f.fieldId === field.id) + // ?.options?.filter((o) => o.exclude) + // .map((o) => o.optionId); + // eslint-disable-next-line max-len + // const options = excludeIds?.length ? field.options.filter((o) => !excludeIds.includes(o.id)) : field.options; + const options = field.options; + return options.map(async (option) => ({ + fieldId: field.id, + fieldName: field.name, + optionId: option.id, + optionLabel: option.label, + result: await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.all, + { owner, userOwnerFieldId }, + { amount: true, quantity: true }, + { createdAt: filter.period, userIds: filter.userIds, field: { fieldId: field.id, optionId: option.id } }, + ), + })); + }) + .flat(), + ); + for (const { fieldId, fieldName, optionId, optionLabel, result } of results) { + for (const { ownerId, value } of result.quantity) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(optionId) ?? CrmGeneralReportFieldValue.empty(optionId, optionLabel); + grFieldValues.quantity = value; + grField.values.set(optionId, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + for (const { ownerId, value } of result.amount) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(optionId) ?? CrmGeneralReportFieldValue.empty(optionId, optionLabel); + grFieldValues.amount = value; + grField.values.set(optionId, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + } + } + private async processMultiSelectField( + accountId: number, + entityTypeId: number, + owner: ReportRowOwner, + userOwnerFieldId: number | undefined, + useWon: boolean, + filter: Filter, + excludeFieldIds: number[] | null | undefined, + rowMap: Map, + ) { + const fields = await this.fieldService.findMany( + { + accountId, + entityTypeId, + type: FieldTypes.multiSelect, + excludeId: excludeFieldIds, + }, + { expand: ['options'] }, + ); + const results = await Promise.all( + fields + .map((field) => { + // const excludeIds = filter.visibility?.fields?.fields + // ?.find((f) => f.fieldId === field.id) + // ?.options?.filter((o) => o.exclude) + // .map((o) => o.optionId); + // eslint-disable-next-line max-len + // const options = excludeIds?.length ? field.options.filter((o) => !excludeIds.includes(o.id)) : field.options; + const options = field.options; + return options.map(async (option) => ({ + fieldId: field.id, + fieldName: field.name, + optionId: option.id, + optionLabel: option.label, + result: await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.all, + { owner, userOwnerFieldId }, + { amount: true, quantity: true }, + { + createdAt: filter.period, + userIds: filter.userIds, + field: { fieldId: field.id, optionsId: option.id }, + }, + ), + })); + }) + .flat(), + ); + for (const { fieldId, fieldName, optionId, optionLabel, result } of results) { + for (const { ownerId, value } of result.quantity) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(optionId) ?? CrmGeneralReportFieldValue.empty(optionId, optionLabel); + grFieldValues.quantity = value; + grField.values.set(optionId, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + for (const { ownerId, value } of result.amount) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(optionId) ?? CrmGeneralReportFieldValue.empty(optionId, optionLabel); + grFieldValues.amount = value; + grField.values.set(optionId, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + } + } + private async processSwitchField( + accountId: number, + entityTypeId: number, + owner: ReportRowOwner, + userOwnerFieldId: number | undefined, + useWon: boolean, + filter: Filter, + excludeFieldIds: number[] | null | undefined, + rowMap: Map, + ) { + const fields = await this.fieldService.findMany({ + accountId, + entityTypeId, + type: FieldType.Switch, + excludeId: excludeFieldIds, + }); + const results = ( + await Promise.all( + fields.map(async (field) => [ + { + fieldId: field.id, + fieldName: field.name, + optionId: 1, + optionLabel: true, + result: await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.all, + { owner, userOwnerFieldId }, + { amount: true, quantity: true }, + { + createdAt: filter.period, + userIds: filter.userIds, + field: { fieldId: field.id, switch: true }, + }, + ), + }, + { + fieldId: field.id, + fieldName: field.name, + optionId: 0, + optionLabel: false, + result: await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.all, + { owner, userOwnerFieldId }, + { amount: true, quantity: true }, + { + createdAt: filter.period, + userIds: filter.userIds, + field: { fieldId: field.id, switch: false }, + }, + ), + }, + ]), + ) + ).flat(); + for (const { fieldId, fieldName, optionId, optionLabel, result } of results) { + for (const { ownerId, value } of result.quantity) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(optionId) ?? CrmGeneralReportFieldValue.empty(optionId, optionLabel); + grFieldValues.quantity = value; + grField.values.set(optionId, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + for (const { ownerId, value } of result.amount) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(optionId) ?? CrmGeneralReportFieldValue.empty(optionId, optionLabel); + grFieldValues.amount = value; + grField.values.set(optionId, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + } + } + private async processNumberField( + accountId: number, + entityTypeId: number, + owner: ReportRowOwner, + userOwnerFieldId: number | undefined, + useWon: boolean, + filter: Filter, + excludeFieldIds: number[] | null | undefined, + rowMap: Map, + ) { + const fields = await this.fieldService.findMany({ + accountId, + entityTypeId, + type: [FieldType.Number, FieldType.Formula], + excludeId: excludeFieldIds, + }); + const results = ( + await Promise.all( + fields.map(async (field) => ({ + fieldId: field.id, + fieldName: field.name, + result: await this.reportingService.getEntityGroupBy( + accountId, + filter.stages.all, + { owner, userOwnerFieldId }, + { fieldAmount: field.id, fieldQuantity: field.id }, + { createdAt: filter.period, userIds: filter.userIds }, + ), + })), + ) + ).flat(); + for (const { fieldId, fieldName, result } of results) { + for (const { ownerId, value } of result.fieldQuantity) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(0) ?? CrmGeneralReportFieldValue.empty(0, ''); + grFieldValues.quantity = value; + grField.values.set(0, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + for (const { ownerId, value } of result.fieldAmount) { + const grUser = rowMap.get(ownerId) ?? CrmGeneralReportRow.empty(ownerId, useWon); + const grField = grUser.field.get(fieldId) ?? CrmGeneralReportField.empty(fieldId, fieldName); + const grFieldValues = grField.values.get(0) ?? CrmGeneralReportFieldValue.empty(0, ''); + grFieldValues.amount = value; + grField.values.set(0, grFieldValues); + grUser.field.set(fieldId, grField); + rowMap.set(ownerId, grUser); + } + } + } +} diff --git a/backend/src/CRM/reporting/general/index.ts b/backend/src/CRM/reporting/general/index.ts new file mode 100644 index 0000000..e05c43a --- /dev/null +++ b/backend/src/CRM/reporting/general/index.ts @@ -0,0 +1,2 @@ +export * from './dto'; +export * from './types'; diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-entity.ts b/backend/src/CRM/reporting/general/types/crm-general-report-entity.ts new file mode 100644 index 0000000..428b7c8 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-entity.ts @@ -0,0 +1,70 @@ +import { QuantityAmount } from '@/common'; + +import { CrmGeneralReportEntityDto } from '../dto/crm-general-report-entity.dto'; + +export class CrmGeneralReportEntity { + useWon: boolean; + all: QuantityAmount; + open: QuantityAmount; + lost: QuantityAmount; + won: QuantityAmount; + close: number; + + constructor( + useWon: boolean, + all: QuantityAmount, + open: QuantityAmount, + lost: QuantityAmount, + won: QuantityAmount, + close: number, + ) { + this.useWon = useWon; + this.all = all; + this.open = open; + this.lost = lost; + this.won = won; + this.close = close; + } + + public static empty(useWon: boolean): CrmGeneralReportEntity { + return new CrmGeneralReportEntity( + useWon, + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + 0, + ); + } + + public get avgAmount(): number { + const avg = this.useWon ? this.won.amount / this.won.quantity : this.all.amount / this.all.quantity; + + return Number.isNaN(avg) ? 0 : avg; + } + + public get avgClose(): number { + const avg = this.useWon ? this.close / this.won.quantity : this.close / this.lost.quantity; + + return Number.isNaN(avg) ? 0 : avg; + } + + public toDto(): CrmGeneralReportEntityDto { + return new CrmGeneralReportEntityDto( + this.all.toDto(), + this.open.toDto(), + this.lost.toDto(), + this.won.toDto(), + this.avgAmount, + this.avgClose, + ); + } + + public add(entity: CrmGeneralReportEntity) { + this.all.add(entity.all); + this.open.add(entity.open); + this.won.add(entity.won); + this.lost.add(entity.lost); + this.close += entity.close; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-field-meta.ts b/backend/src/CRM/reporting/general/types/crm-general-report-field-meta.ts new file mode 100644 index 0000000..8e47476 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-field-meta.ts @@ -0,0 +1,23 @@ +import { CrmGeneralReportFieldMetaDto } from '../dto'; + +import { CrmGeneralReportFieldOptionMeta } from './crm-general-report-field-option-meta'; + +export class CrmGeneralReportFieldMeta { + fieldId: number; + fieldName: string; + options?: CrmGeneralReportFieldOptionMeta[] | null; + + constructor(fieldId: number, fieldName: string, options?: CrmGeneralReportFieldOptionMeta[] | null) { + this.fieldId = fieldId; + this.fieldName = fieldName; + this.options = options; + } + + public toDto(): CrmGeneralReportFieldMetaDto { + return { + fieldId: this.fieldId, + fieldName: this.fieldName, + values: this.options?.map((o) => o.toDto()), + }; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-field-option-meta.ts b/backend/src/CRM/reporting/general/types/crm-general-report-field-option-meta.ts new file mode 100644 index 0000000..efa3e05 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-field-option-meta.ts @@ -0,0 +1,18 @@ +import type { FieldFormat } from '@/modules/entity/entity-field/field'; +import { CrmGeneralReportFieldOptionMetaDto } from '../dto/crm-general-report-field-option-meta.dto'; + +export class CrmGeneralReportFieldOptionMeta { + optionId: number; + optionLabel: string | boolean; + format?: FieldFormat | null; + + constructor(optionId: number, optionLabel: string | boolean, format?: FieldFormat | null) { + this.optionId = optionId; + this.optionLabel = optionLabel; + this.format = format; + } + + public toDto(): CrmGeneralReportFieldOptionMetaDto { + return { optionId: this.optionId, optionLabel: this.optionLabel, format: this.format }; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-field-value.ts b/backend/src/CRM/reporting/general/types/crm-general-report-field-value.ts new file mode 100644 index 0000000..d8fe114 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-field-value.ts @@ -0,0 +1,28 @@ +import { CrmGeneralReportFieldValueDto } from '../dto/crm-general-report-field-value.dto'; + +export class CrmGeneralReportFieldValue { + optionId: number; + optionLabel: string | boolean; + quantity: number; + amount: number; + + constructor(optionId: number, optionLabel: string | boolean, quantity: number, amount: number) { + this.optionId = optionId; + this.optionLabel = optionLabel; + this.quantity = quantity; + this.amount = amount; + } + + public static empty(optionId: number, optionLabel: string | boolean): CrmGeneralReportFieldValue { + return new CrmGeneralReportFieldValue(optionId, optionLabel, 0, 0); + } + + public toDto(): CrmGeneralReportFieldValueDto { + return new CrmGeneralReportFieldValueDto(this.optionId, this.optionLabel, this.quantity, this.amount); + } + + public add(value: CrmGeneralReportFieldValue) { + this.quantity += value.quantity; + this.amount += value.amount; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-field.ts b/backend/src/CRM/reporting/general/types/crm-general-report-field.ts new file mode 100644 index 0000000..6ec60a8 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-field.ts @@ -0,0 +1,37 @@ +import { CrmGeneralReportFieldDto } from '../dto/crm-general-report-field.dto'; +import { CrmGeneralReportFieldValue } from './crm-general-report-field-value'; + +export class CrmGeneralReportField { + fieldId: number; + fieldName: string; + values: Map; + + constructor(fieldId: number, fieldName: string, values: Map) { + this.fieldId = fieldId; + this.fieldName = fieldName; + this.values = values; + } + + public static empty(fieldId: number, fieldName: string): CrmGeneralReportField { + return new CrmGeneralReportField(fieldId, fieldName, new Map()); + } + + public toDto(): CrmGeneralReportFieldDto { + return new CrmGeneralReportFieldDto( + this.fieldId, + this.fieldName, + Array.from(this.values.values()).map((v) => v.toDto()), + ); + } + + public add(field: CrmGeneralReportField) { + for (const [fieldValueId, fieldValue] of field.values) { + let value = this.values.get(fieldValueId); + if (!value) { + value = CrmGeneralReportFieldValue.empty(fieldValueId, fieldValue.optionLabel); + this.values.set(fieldValueId, value); + } + value.add(fieldValue); + } + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-meta.ts b/backend/src/CRM/reporting/general/types/crm-general-report-meta.ts new file mode 100644 index 0000000..40aa842 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-meta.ts @@ -0,0 +1,14 @@ +import { CrmGeneralReportMetaDto } from '../dto/crm-general-report-meta.dto'; +import { type CrmGeneralReportFieldMeta } from './crm-general-report-field-meta'; + +export class CrmGeneralReportMeta { + fields: CrmGeneralReportFieldMeta[]; + + constructor(fields: CrmGeneralReportFieldMeta[]) { + this.fields = fields; + } + + public toDto(): CrmGeneralReportMetaDto { + return { fields: this.fields.map((u) => u.toDto()) }; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-row.ts b/backend/src/CRM/reporting/general/types/crm-general-report-row.ts new file mode 100644 index 0000000..b22e4ea --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-row.ts @@ -0,0 +1,65 @@ +import { CrmGeneralReportRowDto } from '../dto'; + +import { CrmGeneralReportEntity } from './crm-general-report-entity'; +import { CrmGeneralReportField } from './crm-general-report-field'; +import { CrmGeneralReportTask } from './crm-general-report-task'; + +export class CrmGeneralReportRow { + ownerId: number; + entity: CrmGeneralReportEntity; + task: CrmGeneralReportTask; + activity: CrmGeneralReportTask; + field: Map; + + constructor( + ownerId: number, + entity: CrmGeneralReportEntity, + task: CrmGeneralReportTask, + activity: CrmGeneralReportTask, + field: Map, + ) { + this.ownerId = ownerId; + this.entity = entity; + this.task = task; + this.activity = activity; + this.field = field; + } + + public static empty(ownerId: number, useWon: boolean): CrmGeneralReportRow { + return new CrmGeneralReportRow( + ownerId, + CrmGeneralReportEntity.empty(useWon), + CrmGeneralReportTask.empty(), + CrmGeneralReportTask.empty(), + new Map(), + ); + } + + public toDto(): CrmGeneralReportRowDto { + return new CrmGeneralReportRowDto( + this.ownerId, + this.entity.toDto(), + this.task.toDto(), + this.activity.toDto(), + Array.from(this.field.values()).map((v) => v.toDto()), + ); + } + + public add(row: CrmGeneralReportRow): CrmGeneralReportRow { + this.entity.add(row.entity); + + this.task.add(row.task); + this.activity.add(row.activity); + + for (const [rowFieldId, rowField] of row.field) { + let field = this.field.get(rowFieldId); + if (!field) { + field = CrmGeneralReportField.empty(rowFieldId, rowField.fieldName); + this.field.set(rowFieldId, field); + } + field.add(rowField); + } + + return this; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report-task.ts b/backend/src/CRM/reporting/general/types/crm-general-report-task.ts new file mode 100644 index 0000000..13a61e1 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report-task.ts @@ -0,0 +1,30 @@ +import { CrmGeneralReportTaskDto } from '../dto/crm-general-report-task.dto'; + +export class CrmGeneralReportTask { + all: number; + open: number; + expired: number; + resolved: number; + + constructor(all: number, open: number, expired: number, resolved: number) { + this.all = all; + this.open = open; + this.expired = expired; + this.resolved = resolved; + } + + public static empty(): CrmGeneralReportTask { + return new CrmGeneralReportTask(0, 0, 0, 0); + } + + public toDto(): CrmGeneralReportTaskDto { + return new CrmGeneralReportTaskDto(this.all, this.open, this.expired, this.resolved); + } + + public add(task: CrmGeneralReportTask) { + this.all += task.all; + this.open += task.open; + this.expired += task.expired; + this.resolved += task.resolved; + } +} diff --git a/backend/src/CRM/reporting/general/types/crm-general-report.ts b/backend/src/CRM/reporting/general/types/crm-general-report.ts new file mode 100644 index 0000000..51b59d8 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/crm-general-report.ts @@ -0,0 +1,35 @@ +import { CrmGeneralReportDto } from '../dto/crm-general-report.dto'; +import { type CrmGeneralReportMeta } from './crm-general-report-meta'; +import { CrmGeneralReportRow } from './crm-general-report-row'; + +export class CrmGeneralReport { + users: Map; + departments: Map; + total: CrmGeneralReportRow | null; + meta: CrmGeneralReportMeta; + + constructor( + users: Map, + departments: Map, + total: CrmGeneralReportRow | null, + meta: CrmGeneralReportMeta, + ) { + this.users = users; + this.departments = departments; + this.total = total; + this.meta = meta; + } + + public static createEmptyRow(ownerId: number, useWon: boolean): CrmGeneralReportRow { + return CrmGeneralReportRow.empty(ownerId, useWon); + } + + public toDto(): CrmGeneralReportDto { + return new CrmGeneralReportDto( + Array.from(this.users.values()).map((u) => u.toDto()), + Array.from(this.departments.values()).map((u) => u.toDto()), + this.total?.toDto(), + this.meta.toDto(), + ); + } +} diff --git a/backend/src/CRM/reporting/general/types/general-report-row.ts b/backend/src/CRM/reporting/general/types/general-report-row.ts new file mode 100644 index 0000000..23015d4 --- /dev/null +++ b/backend/src/CRM/reporting/general/types/general-report-row.ts @@ -0,0 +1,83 @@ +import { CallReportBlock } from '@/modules/telephony/voximplant/voximplant-reporting/types/call-report-block'; + +import { GeneralReportRowDto } from '../dto'; +import { CrmGeneralReportEntity } from './crm-general-report-entity'; +import { CrmGeneralReportField } from './crm-general-report-field'; +import { CrmGeneralReportTask } from './crm-general-report-task'; + +export class GeneralReportRow { + ownerId: number; + entity: CrmGeneralReportEntity | null | undefined; + task: CrmGeneralReportTask | null | undefined; + activity: CrmGeneralReportTask | null | undefined; + field: Map | null | undefined; + call: CallReportBlock | null | undefined; + + constructor( + ownerId: number, + entity: CrmGeneralReportEntity | null | undefined, + task: CrmGeneralReportTask | null | undefined, + activity: CrmGeneralReportTask | null | undefined, + field: Map | null | undefined, + call: CallReportBlock | null | undefined, + ) { + this.ownerId = ownerId; + this.entity = entity; + this.task = task; + this.activity = activity; + this.field = field; + this.call = call; + } + + public toDto(): GeneralReportRowDto { + return { + ownerId: this.ownerId, + entity: this.entity?.toDto(), + task: this.task?.toDto(), + activity: this.activity?.toDto(), + fields: this.field ? Array.from(this.field.values()).map((v) => v.toDto()) : undefined, + call: this.call?.toDto(), + }; + } + + public add(row: GeneralReportRow): GeneralReportRow { + if (this.entity && row.entity) { + this.entity.add(row.entity); + } else if (row.entity) { + this.entity = row.entity; + } + + if (this.task && row.task) { + this.task.add(row.task); + } else if (row.task) { + this.task = row.task; + } + + if (this.activity && row.activity) { + this.activity.add(row.activity); + } else if (row.activity) { + this.activity = row.activity; + } + + if (this.field && row.field) { + for (const [rowFieldId, rowField] of row.field) { + let field = this.field.get(rowFieldId); + if (!field) { + field = CrmGeneralReportField.empty(rowFieldId, rowField.fieldName); + this.field.set(rowFieldId, field); + } + field.add(rowField); + } + } else if (row.field) { + this.field = row.field; + } + + if (this.call && row.call) { + this.call.add(row.call); + } else if (row.call) { + this.call = row.call; + } + + return this; + } +} diff --git a/backend/src/CRM/reporting/general/types/general-report.ts b/backend/src/CRM/reporting/general/types/general-report.ts new file mode 100644 index 0000000..9f02a8d --- /dev/null +++ b/backend/src/CRM/reporting/general/types/general-report.ts @@ -0,0 +1,31 @@ +import { GeneralReportDto } from '../dto'; +import { CrmGeneralReportMeta } from './crm-general-report-meta'; +import { GeneralReportRow } from './general-report-row'; + +export class GeneralReport { + users: Map; + departments: Map; + total: GeneralReportRow; + meta: CrmGeneralReportMeta; + + constructor( + users: Map, + departments: Map, + total: GeneralReportRow, + meta: CrmGeneralReportMeta, + ) { + this.users = users; + this.departments = departments; + this.total = total; + this.meta = meta; + } + + public toDto(): GeneralReportDto { + return { + users: Array.from(this.users.values()).map((u) => u.toDto()), + departments: Array.from(this.departments.values()).map((u) => u.toDto()), + total: this.total.toDto(), + meta: this.meta.toDto(), + }; + } +} diff --git a/backend/src/CRM/reporting/general/types/index.ts b/backend/src/CRM/reporting/general/types/index.ts new file mode 100644 index 0000000..8303b6d --- /dev/null +++ b/backend/src/CRM/reporting/general/types/index.ts @@ -0,0 +1,11 @@ +export * from './crm-general-report-entity'; +export * from './crm-general-report-field-meta'; +export * from './crm-general-report-field-option-meta'; +export * from './crm-general-report-field-value'; +export * from './crm-general-report-field'; +export * from './crm-general-report-meta'; +export * from './crm-general-report-row'; +export * from './crm-general-report-task'; +export * from './crm-general-report'; +export * from './general-report-row'; +export * from './general-report'; diff --git a/backend/src/CRM/reporting/project/dto/index.ts b/backend/src/CRM/reporting/project/dto/index.ts new file mode 100644 index 0000000..a8fa737 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/index.ts @@ -0,0 +1,12 @@ +export * from './project-entities-report-filter.dto'; +export * from './project-entities-report-meta.dto'; +export * from './project-entities-report-row.dto'; +export * from './project-entities-report.dto'; +export * from './project-report-field-meta.dto'; +export * from './project-report-field.dto'; +export * from './project-report-item.dto'; +export * from './project-stage-item.dto'; +export * from './project-task-user-report-filter.dto'; +export * from './project-task-user-report-row.dto'; +export * from './project-task-user-report-total-row.dto'; +export * from './project-task-user-report.dto'; diff --git a/backend/src/CRM/reporting/project/dto/project-entities-report-filter.dto.ts b/backend/src/CRM/reporting/project/dto/project-entities-report-filter.dto.ts new file mode 100644 index 0000000..4bc50cf --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-entities-report-filter.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +export class ProjectEntitiesReportFilterDto { + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiProperty({ description: 'Board ID' }) + @IsNumber() + boardId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Project user IDs' }) + @IsOptional() + @IsArray() + ownerIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Project stage IDs' }) + @IsOptional() + @IsArray() + taskBoardStageIds?: number[] | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/CRM/reporting/project/dto/project-entities-report-meta.dto.ts b/backend/src/CRM/reporting/project/dto/project-entities-report-meta.dto.ts new file mode 100644 index 0000000..e204432 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-entities-report-meta.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ProjectReportFieldMetaDto } from './project-report-field-meta.dto'; + +export class ProjectEntitiesReportMetaDto { + @ApiProperty({ type: [ProjectReportFieldMetaDto], description: 'Fields meta' }) + fields: ProjectReportFieldMetaDto[]; +} diff --git a/backend/src/CRM/reporting/project/dto/project-entities-report-row.dto.ts b/backend/src/CRM/reporting/project/dto/project-entities-report-row.dto.ts new file mode 100644 index 0000000..e3296a3 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-entities-report-row.dto.ts @@ -0,0 +1,41 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ProjectReportItemDto } from './project-report-item.dto'; +import { ProjectStageItemDto } from './project-stage-item.dto'; +import { ProjectReportFieldDto } from './project-report-field.dto'; + +export class ProjectEntitiesReportRowDto { + @ApiProperty({ description: 'Entity ID' }) + @IsNumber() + entityId: number; + + @ApiProperty({ description: 'Entity name' }) + @IsString() + entityName: string; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'All tasks' }) + all: ProjectReportItemDto; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Done tasks' }) + done: ProjectReportItemDto; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Overdue tasks' }) + overdue: ProjectReportItemDto; + + @ApiProperty({ type: [ProjectStageItemDto], description: 'Tasks by project stages' }) + stages: ProjectStageItemDto[]; + + @ApiProperty({ nullable: true, description: 'Project stage ID' }) + @IsOptional() + @IsNumber() + projectStageId: number | null; + + @ApiProperty({ description: 'Completion percent' }) + @IsNumber() + completionPercent: number; + + @ApiPropertyOptional({ type: [ProjectReportFieldDto], nullable: true, description: 'Fields' }) + @IsOptional() + fields: ProjectReportFieldDto[] | null; +} diff --git a/backend/src/CRM/reporting/project/dto/project-entities-report.dto.ts b/backend/src/CRM/reporting/project/dto/project-entities-report.dto.ts new file mode 100644 index 0000000..f97ed44 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-entities-report.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ProjectEntitiesReportRowDto } from './project-entities-report-row.dto'; +import { ProjectEntitiesReportMetaDto } from './project-entities-report-meta.dto'; + +export class ProjectEntitiesReportDto { + @ApiProperty({ type: [ProjectEntitiesReportRowDto], description: 'Rows' }) + rows: ProjectEntitiesReportRowDto[]; + + @ApiProperty({ type: ProjectEntitiesReportRowDto, description: 'Total' }) + total: ProjectEntitiesReportRowDto; + + @ApiProperty({ type: ProjectEntitiesReportMetaDto, description: 'Meta' }) + meta: ProjectEntitiesReportMetaDto; +} diff --git a/backend/src/CRM/reporting/project/dto/project-report-field-meta.dto.ts b/backend/src/CRM/reporting/project/dto/project-report-field-meta.dto.ts new file mode 100644 index 0000000..f782887 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-report-field-meta.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class ProjectReportFieldMetaDto { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; + + @ApiProperty({ description: 'Field name' }) + @IsString() + fieldName: string; +} diff --git a/backend/src/CRM/reporting/project/dto/project-report-field.dto.ts b/backend/src/CRM/reporting/project/dto/project-report-field.dto.ts new file mode 100644 index 0000000..41003ec --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-report-field.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class ProjectReportFieldDto { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; + + @ApiProperty({ description: 'Field name' }) + @IsString() + fieldName: string; + + @ApiProperty({ description: 'Field value' }) + @IsNumber() + value: number; +} diff --git a/backend/src/CRM/reporting/project/dto/project-report-item.dto.ts b/backend/src/CRM/reporting/project/dto/project-report-item.dto.ts new file mode 100644 index 0000000..3dbac85 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-report-item.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class ProjectReportItemDto { + @ApiProperty({ description: 'Task count' }) + @IsNumber() + taskCount: number; + + @ApiProperty({ nullable: true, description: 'Planned time' }) + @IsNumber() + plannedTime: number | null; +} diff --git a/backend/src/CRM/reporting/project/dto/project-stage-item.dto.ts b/backend/src/CRM/reporting/project/dto/project-stage-item.dto.ts new file mode 100644 index 0000000..09ca6e5 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-stage-item.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { ProjectReportItemDto } from './project-report-item.dto'; + +export class ProjectStageItemDto { + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + stageId: number; + + @ApiProperty({ type: ProjectReportItemDto, description: 'Project report item' }) + item: ProjectReportItemDto; +} diff --git a/backend/src/CRM/reporting/project/dto/project-task-user-report-filter.dto.ts b/backend/src/CRM/reporting/project/dto/project-task-user-report-filter.dto.ts new file mode 100644 index 0000000..9e80216 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-task-user-report-filter.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +export class ProjectTaskUserReportFilterDto { + @ApiProperty({ description: 'Board ID' }) + @IsNumber() + boardId: number; + + @ApiPropertyOptional({ description: 'Entity ID' }) + @IsNumber() + entityId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Task user IDs' }) + @IsOptional() + @IsArray() + taskUserIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Task board stage IDs' }) + @IsOptional() + @IsArray() + taskBoardStageIds?: number[] | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/CRM/reporting/project/dto/project-task-user-report-row.dto.ts b/backend/src/CRM/reporting/project/dto/project-task-user-report-row.dto.ts new file mode 100644 index 0000000..3dc8799 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-task-user-report-row.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { ProjectReportItemDto } from './project-report-item.dto'; +import { ProjectStageItemDto } from './project-stage-item.dto'; + +export class ProjectTaskUserReportRowDto { + @ApiProperty({ description: 'User ID' }) + @IsNumber() + userId: number; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Opened tasks' }) + opened: ProjectReportItemDto; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Done tasks' }) + done: ProjectReportItemDto; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Overdue tasks' }) + overdue: ProjectReportItemDto; + + @ApiProperty({ type: [ProjectStageItemDto], description: 'Tasks by stages' }) + stages: ProjectStageItemDto[]; + + @ApiProperty({ description: 'Planned time' }) + @IsNumber() + planedTime: number; + + @ApiProperty({ description: 'Completion percent' }) + @IsNumber() + completionPercent: number; +} diff --git a/backend/src/CRM/reporting/project/dto/project-task-user-report-total-row.dto.ts b/backend/src/CRM/reporting/project/dto/project-task-user-report-total-row.dto.ts new file mode 100644 index 0000000..f4e0b47 --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-task-user-report-total-row.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { ProjectReportItemDto } from './project-report-item.dto'; +import { ProjectStageItemDto } from './project-stage-item.dto'; + +export class ProjectTaskUserReportTotalRowDto { + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Open tasks' }) + opened: ProjectReportItemDto; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Done tasks' }) + done: ProjectReportItemDto; + + @ApiProperty({ type: () => ProjectReportItemDto, description: 'Overdue tasks' }) + overdue: ProjectReportItemDto; + + @ApiProperty({ type: [ProjectStageItemDto], description: 'Tasks by project stages' }) + stages: ProjectStageItemDto[]; + + @ApiProperty({ description: 'Planned time' }) + @IsNumber() + planedTime: number; + + @ApiProperty({ description: 'Completion percent' }) + @IsNumber() + completionPercent: number; +} diff --git a/backend/src/CRM/reporting/project/dto/project-task-user-report.dto.ts b/backend/src/CRM/reporting/project/dto/project-task-user-report.dto.ts new file mode 100644 index 0000000..056df8e --- /dev/null +++ b/backend/src/CRM/reporting/project/dto/project-task-user-report.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ProjectTaskUserReportRowDto } from './project-task-user-report-row.dto'; +import { ProjectTaskUserReportTotalRowDto } from './project-task-user-report-total-row.dto'; + +export class ProjectTaskUserReportDto { + @ApiProperty({ type: [ProjectTaskUserReportRowDto], description: 'Project task user report rows' }) + rows: ProjectTaskUserReportRowDto[]; + + @ApiProperty({ type: ProjectTaskUserReportTotalRowDto, description: 'Project task user report total row' }) + total: ProjectTaskUserReportTotalRowDto; +} diff --git a/backend/src/CRM/reporting/project/project-report.controller.ts b/backend/src/CRM/reporting/project/project-report.controller.ts new file mode 100644 index 0000000..2261af4 --- /dev/null +++ b/backend/src/CRM/reporting/project/project-report.controller.ts @@ -0,0 +1,46 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { + ProjectEntitiesReportDto, + ProjectEntitiesReportFilterDto, + ProjectTaskUserReportDto, + ProjectTaskUserReportFilterDto, +} from './dto'; +import { ProjectReportService } from './project-report.service'; + +@ApiTags('crm/reporting') +@Controller('crm/reporting/project') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ProjectReportController { + constructor(private readonly service: ProjectReportService) {} + + @ApiOperation({ summary: 'Get project entities report', description: 'Get project entities report' }) + @ApiBody({ type: ProjectEntitiesReportFilterDto, required: true, description: 'Project report filter' }) + @ApiOkResponse({ description: 'Get project entities report', type: ProjectEntitiesReportDto }) + @Post('entities') + public async getProjectEntitiesReport( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ProjectEntitiesReportFilterDto, + ) { + return this.service.getEntitiesReport({ accountId, user, filter }); + } + + @ApiOperation({ + summary: 'Get project report by task responsible users', + description: 'Get project report by task responsible users', + }) + @ApiBody({ type: ProjectTaskUserReportFilterDto, required: true, description: 'Project report filter' }) + @ApiOkResponse({ description: 'Project report by task responsible users', type: ProjectTaskUserReportDto }) + @Post('users') + public async getProjectTaskUserReport( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: ProjectTaskUserReportFilterDto, + ) { + return this.service.getTaskUserReport({ accountId, user, filter }); + } +} diff --git a/backend/src/CRM/reporting/project/project-report.service.ts b/backend/src/CRM/reporting/project/project-report.service.ts new file mode 100644 index 0000000..29b1e19 --- /dev/null +++ b/backend/src/CRM/reporting/project/project-report.service.ts @@ -0,0 +1,350 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, SelectQueryBuilder } from 'typeorm'; + +import { DatePeriod, ForbiddenError, intersection, NumberUtil } from '@/common'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; + +import { BoardService } from '../../board/board.service'; +import { BoardStageService } from '../../board-stage/board-stage.service'; +import { EntityType } from '../../entity-type/entities'; +import { Entity } from '../../Model/Entity/Entity'; +import { EntityService } from '../../Service/Entity/EntityService'; +import { Task } from '../../task/entities'; + +import { ProjectEntitiesReportFilterDto, ProjectTaskUserReportFilterDto } from './dto'; +import { + ProjectEntitiesReport, + ProjectEntitiesReportMeta, + ProjectEntitiesReportRow, + ProjectReportField, + ProjectReportFieldMeta, + ProjectReportItem, + ProjectStageItem, + ProjectTaskUserReport, + ProjectTaskUserReportRow, + ProjectTaskUserReportTotalRow, +} from './types'; + +interface Filter { + userIds?: number[]; + projectStageIds?: number[]; + taskUserIds?: number[]; + taskBoardStageIds?: number[]; + period?: DatePeriod; +} + +@Injectable() +export class ProjectReportService { + constructor( + @InjectRepository(Task) + private readonly taskRepository: Repository, + @InjectRepository(Entity) + private readonly entityRepository: Repository, + private readonly authService: AuthorizationService, + private readonly boardService: BoardService, + private readonly stageService: BoardStageService, + private readonly entityService: EntityService, + private readonly fieldService: FieldService, + ) {} + + public async getEntitiesReport({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: ProjectEntitiesReportFilterDto; + }): Promise { + const { entityTypeId, boardId, taskBoardStageIds } = filter; + const { allow, userIds: allowedUserIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: EntityType.getAuthorizable(entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + const userIds = filter.ownerIds?.length ? intersection(filter.ownerIds, allowedUserIds) : allowedUserIds; + + const period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const fieldsMeta = await this.getFieldsMeta({ accountId, entityTypeId: entityTypeId }); + const projectStageIds = await this.stageService.findManyIds({ accountId, boardId }); + const stageIds = await this.getTaskBoardStageIds({ accountId, boardId, taskBoardStageIds }); + const groupFilter: Filter = { userIds, projectStageIds, taskBoardStageIds: stageIds, period }; + + const rows = await this.getEntitiesGroupBy({ accountId, isTotal: false, filter: groupFilter, fieldsMeta }); + const [total] = await this.getEntitiesGroupBy({ accountId, isTotal: true, filter: groupFilter, fieldsMeta }); + + return new ProjectEntitiesReport(rows, total, new ProjectEntitiesReportMeta({ fields: fieldsMeta })); + } + + public async getTaskUserReport({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: ProjectTaskUserReportFilterDto; + }): Promise { + const { entityId, boardId, taskBoardStageIds } = filter; + const entity = await this.entityService.findOne(accountId, { entityId }); + const { allow, userIds: allowedUserIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: entity, + }); + if (!allow) { + throw new ForbiddenError(); + } + const userIds = filter.taskUserIds?.length ? intersection(filter.taskUserIds, allowedUserIds) : allowedUserIds; + + const period = filter.period ? DatePeriod.fromFilter(filter.period) : undefined; + const stageIds = await this.getTaskBoardStageIds({ accountId, boardId, taskBoardStageIds }); + + const taskFilter: Filter = { taskUserIds: userIds, taskBoardStageIds: stageIds, period }; + + const taskUserQb = this.getTaskUserReportQb(accountId, entityId, false, taskFilter); + const taskUserRawData = await taskUserQb.getRawMany(); + let taskUserReportRows: ProjectTaskUserReportRow[] = []; + if (taskUserRawData.length > 0) { + taskUserReportRows = taskUserRawData.map( + (d) => + new ProjectTaskUserReportRow( + d?.user_id, + new ProjectReportItem(d?.opened?.task_count, d?.opened?.planned_time), + new ProjectReportItem(d?.done?.task_count, d?.done?.planned_time), + new ProjectReportItem(d?.overdue?.task_count, d?.overdue?.planned_time), + stageIds.map( + (id) => new ProjectStageItem(id, new ProjectReportItem(d[id]['task_count'], d[id]['planned_time'])), + ), + d?.total_planned_time, + +d?.completion_percent, + ), + ); + } + + const totalTaskUserQb = this.getTaskUserReportQb(accountId, entityId, true, taskFilter); + const totalTaskUserRawData = await totalTaskUserQb.getRawOne(); + let taskUserReportTotalRow = ProjectTaskUserReportTotalRow.empty(); + if (totalTaskUserRawData !== undefined) { + taskUserReportTotalRow = new ProjectTaskUserReportTotalRow( + new ProjectReportItem(totalTaskUserRawData?.opened?.task_count, totalTaskUserRawData?.opened?.planned_time), + new ProjectReportItem(totalTaskUserRawData?.done?.task_count, totalTaskUserRawData?.done?.planned_time), + new ProjectReportItem(totalTaskUserRawData?.overdue?.task_count, totalTaskUserRawData?.overdue?.planned_time), + taskBoardStageIds.map( + (id) => + new ProjectStageItem( + id, + new ProjectReportItem(totalTaskUserRawData[id]['task_count'], totalTaskUserRawData[id]['planned_time']), + ), + ), + totalTaskUserRawData?.total_planned_time, + +totalTaskUserRawData?.completion_percent, + ); + } + return new ProjectTaskUserReport(taskUserReportRows, taskUserReportTotalRow); + } + + private async getEntitiesGroupBy({ + accountId, + isTotal, + filter, + fieldsMeta, + }: { + accountId: number; + isTotal: boolean; + filter: Filter; + fieldsMeta: ProjectReportFieldMeta[]; + }): Promise { + const rowArray: ProjectEntitiesReportRow[] = []; + const fieldRows = await this.getFieldsReportQb(accountId, isTotal, filter, fieldsMeta).getRawMany(); + const rows = await this.getEntitiesReportQb(accountId, isTotal, filter).getRawMany(); + for (const row of rows) { + const ownerId = row.entity_id || 0; + const fieldRow = fieldRows.find((r) => r.entity_id === ownerId); + const fields: ProjectReportField[] = []; + if (fieldRow) { + for (const fieldMeta of fieldsMeta) { + const value = NumberUtil.toNumber(fieldRow[`fv_${fieldMeta.fieldId}_amount`]); + if (value) { + fields.push(new ProjectReportField(fieldMeta.fieldId, fieldMeta.fieldName, value)); + } + } + } + rowArray.push( + new ProjectEntitiesReportRow( + ownerId, + row.entity_name || '', + new ProjectReportItem(row.all?.task_count, row.all?.planned_time), + new ProjectReportItem(row.done?.task_count, row.done?.planned_time), + new ProjectReportItem(row.overdue?.task_count, row.overdue?.planned_time), + filter.taskBoardStageIds.map( + (id) => new ProjectStageItem(id, new ProjectReportItem(row[id]['task_count'], row[id]['planned_time'])), + ), + row.project_stage_id || null, + row.completion_percent, + fields, + ), + ); + } + + return rowArray; + } + + private async getFieldsMeta({ + accountId, + entityTypeId, + }: { + accountId: number; + entityTypeId: number; + }): Promise { + const fieldsMeta: ProjectReportFieldMeta[] = []; + + const formulaFields = await this.fieldService.findMany({ accountId, entityTypeId, type: FieldType.Formula }); + for (const field of formulaFields) { + fieldsMeta.push(new ProjectReportFieldMeta(field.id, field.name)); + } + + return fieldsMeta; + } + + private async getTaskBoardStageIds({ + accountId, + boardId, + taskBoardStageIds, + }: { + accountId: number; + boardId: number; + taskBoardStageIds?: number[]; + }) { + const board = await this.boardService.findOne({ filter: { accountId, boardId } }); + const stageIds = board.taskBoardId + ? await this.stageService.findManyIds({ accountId, boardId: board.taskBoardId }) + : []; + + return taskBoardStageIds?.length ? stageIds.filter((s) => taskBoardStageIds.includes(s)) : stageIds; + } + + private getEntitiesReportQb(accountId: number, isTotal: boolean, filter: Filter) { + const qb = this.entityRepository + .createQueryBuilder('entity') + .where('entity.account_id = :accountId', { accountId }) + .andWhere('entity.stage_id in (:...stageIds)', { stageIds: filter?.projectStageIds }); + + if (filter.userIds?.length) { + qb.andWhere(`entity.responsible_user_id in (:...ownerIds)`, { ownerIds: filter.userIds }); + } + + if (isTotal) { + qb.select('0::integer', 'entity_id'); + } else { + qb.select('entity.id', 'entity_id') + .addSelect('entity.stage_id', 'project_stage_id') + .addSelect('entity.name', 'entity_name') + .groupBy('entity.id') + .addGroupBy('entity.name'); + } + qb.leftJoin(Task, 'task', 'task.entity_id = entity.id').addSelect( + "json_build_object('task_count', count(task.id), 'planned_time', coalesce(sum(task.planned_time), 0))", + 'all', + ); + this.specifyBaseFieldsQb(qb, filter); + + return qb; + } + + private getFieldsReportQb(accountId: number, isTotal: boolean, filter: Filter, fieldsMeta: ProjectReportFieldMeta[]) { + const qb = this.entityRepository + .createQueryBuilder('entity') + .where('entity.account_id = :accountId', { accountId }) + .andWhere('entity.stage_id in (:...stageIds)', { stageIds: filter?.projectStageIds }); + + if (filter.userIds?.length) { + qb.andWhere(`entity.responsible_user_id in (:...ownerIds)`, { ownerIds: filter.userIds }); + } + if (isTotal) { + qb.select('0::integer', 'entity_id'); + } else { + qb.select('entity.id', 'entity_id').groupBy('entity.id'); + } + + for (const fieldMeta of fieldsMeta) { + const fieldKey = `fv_${fieldMeta.fieldId}`; + qb.leftJoin( + // eslint-disable-next-line max-len + `(select entity_id, sum(cast(payload::json->>'value' as decimal)) as amount from field_value where field_id = ${fieldMeta.fieldId} group by entity_id)`, + fieldKey, + `${fieldKey}.entity_id = entity.id`, + ).addSelect(`sum(${fieldKey}.amount)`, `${fieldKey}_amount`); + } + + return qb; + } + + private getTaskUserReportQb(accountId: number, entityId: number, isTotal: boolean, filter: Filter) { + const qb = this.taskRepository.createQueryBuilder('task'); + if (isTotal) { + qb.select('task.entity_id', 'entity_id').groupBy('task.entity_id'); + } else { + qb.select('task.responsible_user_id', 'user_id').groupBy('task.responsible_user_id'); + } + this.specifyBaseFieldsQb(qb, filter); + qb.addSelect('coalesce(sum(task.planned_time), 0)::integer', 'total_planned_time') + .addSelect( + 'json_build_object(' + + "'task_count', count(task.id) filter (where task.is_resolved=false), " + + "'planned_time', coalesce(sum(task.planned_time) filter (where task.is_resolved=false), 0))", + 'opened', + ) + .andWhere('task.account_id = :accountId', { accountId }) + .andWhere('task.entity_id = :entityId', { entityId: entityId }); + + if (filter.taskUserIds?.length) { + qb.andWhere('task.responsible_user_id in (:...userIds)', { userIds: filter.taskUserIds }); + } + return qb; + } + + private specifyBaseFieldsQb(qb: SelectQueryBuilder, filter: Filter) { + qb.addSelect( + 'json_build_object(' + + "'task_count', count(task.id) filter (where task.is_resolved=true), " + + "'planned_time', coalesce(sum(task.planned_time) filter (where task.is_resolved=true), 0))", + 'done', + ) + .addSelect( + 'json_build_object(' + + "'task_count', count(task.id) filter (where task.end_date < now() and task.is_resolved=false), " + + // eslint-disable-next-line max-len + "'planned_time', coalesce(sum(task.planned_time) filter (where task.end_date < now() and task.is_resolved=false), 0))", + 'overdue', + ) + .addSelect( + 'coalesce(count(task.id) filter (where task.is_resolved = true)::float / nullif(count(task.id), 0), 0)::float', + 'completion_percent', + ); + + if (filter.taskBoardStageIds?.length) { + for (const stageId of filter.taskBoardStageIds) { + qb.addSelect( + `json_build_object('task_count', count(task.id) filter (where task.stage_id = ${stageId}), ` + + `'planned_time', coalesce(sum(task.planned_time) filter (where task.stage_id = ${stageId}), 0))`, + `${stageId}`, + ); + } + } + + if (filter.period?.from) { + qb.andWhere('task.created_at >= :from', { from: filter.period.from }); + } + if (filter.period?.to) { + qb.andWhere('task.created_at <= :to', { to: filter.period.to }); + } + } +} diff --git a/backend/src/CRM/reporting/project/types/index.ts b/backend/src/CRM/reporting/project/types/index.ts new file mode 100644 index 0000000..00fcb2a --- /dev/null +++ b/backend/src/CRM/reporting/project/types/index.ts @@ -0,0 +1,10 @@ +export * from './project-entities-report-meta'; +export * from './project-entities-report-row'; +export * from './project-entities-report'; +export * from './project-report-field-meta'; +export * from './project-report-field'; +export * from './project-report-item'; +export * from './project-stage-item'; +export * from './project-task-user-report-row'; +export * from './project-task-user-report-total-row'; +export * from './project-task-user-report'; diff --git a/backend/src/CRM/reporting/project/types/project-entities-report-meta.ts b/backend/src/CRM/reporting/project/types/project-entities-report-meta.ts new file mode 100644 index 0000000..c07f7e6 --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-entities-report-meta.ts @@ -0,0 +1,14 @@ +import { ProjectEntitiesReportMetaDto } from '../dto'; +import { type ProjectReportFieldMeta } from './project-report-field-meta'; + +export class ProjectEntitiesReportMeta { + fields: ProjectReportFieldMeta[]; + + constructor({ fields }: { fields: ProjectReportFieldMeta[] }) { + this.fields = fields; + } + + public toDto(): ProjectEntitiesReportMetaDto { + return { fields: this.fields?.map((f) => f.toDto()) }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-entities-report-row.ts b/backend/src/CRM/reporting/project/types/project-entities-report-row.ts new file mode 100644 index 0000000..276653a --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-entities-report-row.ts @@ -0,0 +1,52 @@ +import { ProjectEntitiesReportRowDto } from '../dto'; +import { type ProjectReportField } from './project-report-field'; +import { type ProjectReportItem } from './project-report-item'; +import { type ProjectStageItem } from './project-stage-item'; + +export class ProjectEntitiesReportRow { + entityId: number; + entityName: string; + all: ProjectReportItem; + done: ProjectReportItem; + overdue: ProjectReportItem; + stages: ProjectStageItem[]; + projectStageId: number | null; + completionPercent: number; + fields: ProjectReportField[]; + + constructor( + entityId: number, + entityName: string, + all: ProjectReportItem, + done: ProjectReportItem, + overdue: ProjectReportItem, + stages: ProjectStageItem[], + projectStageId: number | null, + completionPercent: number, + fields: ProjectReportField[], + ) { + this.entityId = entityId; + this.entityName = entityName; + this.all = all; + this.done = done; + this.overdue = overdue; + this.stages = stages; + this.projectStageId = projectStageId; + this.completionPercent = completionPercent; + this.fields = fields; + } + + public toDto(): ProjectEntitiesReportRowDto { + return { + entityId: this.entityId, + entityName: this.entityName, + all: this.all.toDto(), + done: this.done.toDto(), + overdue: this.overdue.toDto(), + stages: this.stages.map((stage) => stage.toDto()), + projectStageId: this.projectStageId, + completionPercent: this.completionPercent, + fields: this.fields?.map((field) => field.toDto()), + }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-entities-report.ts b/backend/src/CRM/reporting/project/types/project-entities-report.ts new file mode 100644 index 0000000..760e534 --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-entities-report.ts @@ -0,0 +1,19 @@ +import { ProjectEntitiesReportDto } from '../dto'; +import { type ProjectEntitiesReportMeta } from './project-entities-report-meta'; +import { type ProjectEntitiesReportRow } from './project-entities-report-row'; + +export class ProjectEntitiesReport { + rows: ProjectEntitiesReportRow[]; + total: ProjectEntitiesReportRow; + meta: ProjectEntitiesReportMeta; + + constructor(rows: ProjectEntitiesReportRow[], total: ProjectEntitiesReportRow, meta: ProjectEntitiesReportMeta) { + this.rows = rows; + this.total = total; + this.meta = meta; + } + + public toDto(): ProjectEntitiesReportDto { + return { rows: this.rows.map((d) => d.toDto()), total: this.total.toDto(), meta: this.meta.toDto() }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-report-field-meta.ts b/backend/src/CRM/reporting/project/types/project-report-field-meta.ts new file mode 100644 index 0000000..a5ba8e6 --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-report-field-meta.ts @@ -0,0 +1,15 @@ +import { ProjectReportFieldMetaDto } from '../dto'; + +export class ProjectReportFieldMeta { + fieldId: number; + fieldName: string; + + constructor(fieldId: number, fieldName: string) { + this.fieldId = fieldId; + this.fieldName = fieldName; + } + + public toDto(): ProjectReportFieldMetaDto { + return { fieldId: this.fieldId, fieldName: this.fieldName }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-report-field.ts b/backend/src/CRM/reporting/project/types/project-report-field.ts new file mode 100644 index 0000000..9252af1 --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-report-field.ts @@ -0,0 +1,17 @@ +import { ProjectReportFieldDto } from '../dto'; + +export class ProjectReportField { + fieldId: number; + fieldName: string; + value: number; + + constructor(fieldId: number, fieldName: string, value: number) { + this.fieldId = fieldId; + this.fieldName = fieldName; + this.value = value; + } + + public toDto(): ProjectReportFieldDto { + return { fieldId: this.fieldId, fieldName: this.fieldName, value: this.value }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-report-item.ts b/backend/src/CRM/reporting/project/types/project-report-item.ts new file mode 100644 index 0000000..ef572eb --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-report-item.ts @@ -0,0 +1,19 @@ +import { ProjectReportItemDto } from '../dto'; + +export class ProjectReportItem { + taskCount: number; + plannedTime: number | null; + + constructor(taskCount: number, plannedTime: number | null) { + this.taskCount = taskCount; + this.plannedTime = plannedTime; + } + + public static empty(): ProjectReportItem { + return new ProjectReportItem(0, 0); + } + + public toDto(): ProjectReportItemDto { + return { taskCount: this.taskCount, plannedTime: this.plannedTime }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-stage-item.ts b/backend/src/CRM/reporting/project/types/project-stage-item.ts new file mode 100644 index 0000000..5c6abc1 --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-stage-item.ts @@ -0,0 +1,16 @@ +import { ProjectStageItemDto } from '../dto'; +import { type ProjectReportItem } from './project-report-item'; + +export class ProjectStageItem { + stageId: number; + item: ProjectReportItem; + + constructor(stageId: number, item: ProjectReportItem) { + this.stageId = stageId; + this.item = item; + } + + public toDto(): ProjectStageItemDto { + return { stageId: this.stageId, item: this.item.toDto() }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-task-user-report-row.ts b/backend/src/CRM/reporting/project/types/project-task-user-report-row.ts new file mode 100644 index 0000000..cda19ec --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-task-user-report-row.ts @@ -0,0 +1,43 @@ +import { ProjectTaskUserReportRowDto } from '../dto'; +import { type ProjectReportItem } from './project-report-item'; +import { type ProjectStageItem } from './project-stage-item'; + +export class ProjectTaskUserReportRow { + userId: number; + opened: ProjectReportItem; + done: ProjectReportItem; + overdue: ProjectReportItem; + stages: ProjectStageItem[]; + plannedTime: number; + completionPercent: number; + + constructor( + userId: number, + opened: ProjectReportItem, + done: ProjectReportItem, + overdue: ProjectReportItem, + stages: ProjectStageItem[], + plannedTime: number, + completionPercent: number, + ) { + this.userId = userId; + this.opened = opened; + this.done = done; + this.overdue = overdue; + this.stages = stages; + this.plannedTime = plannedTime; + this.completionPercent = completionPercent; + } + + public toDto(): ProjectTaskUserReportRowDto { + return { + userId: this.userId, + opened: this.opened.toDto(), + done: this.done.toDto(), + overdue: this.overdue.toDto(), + stages: this.stages.map((s) => s.toDto()), + planedTime: this.plannedTime, + completionPercent: this.completionPercent, + }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-task-user-report-total-row.ts b/backend/src/CRM/reporting/project/types/project-task-user-report-total-row.ts new file mode 100644 index 0000000..4365289 --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-task-user-report-total-row.ts @@ -0,0 +1,50 @@ +import { ProjectTaskUserReportTotalRowDto } from '../dto'; +import { ProjectReportItem } from './project-report-item'; +import { type ProjectStageItem } from './project-stage-item'; + +export class ProjectTaskUserReportTotalRow { + opened: ProjectReportItem; + done: ProjectReportItem; + overdue: ProjectReportItem; + stages: ProjectStageItem[]; + plannedTime: number; + completionPercent: number; + + constructor( + opened: ProjectReportItem, + done: ProjectReportItem, + overdue: ProjectReportItem, + stages: ProjectStageItem[], + plannedTime: number, + completionPercent: number, + ) { + this.opened = opened; + this.done = done; + this.overdue = overdue; + this.stages = stages; + this.plannedTime = plannedTime; + this.completionPercent = completionPercent; + } + + public static empty(): ProjectTaskUserReportTotalRow { + return new ProjectTaskUserReportTotalRow( + ProjectReportItem.empty(), + ProjectReportItem.empty(), + ProjectReportItem.empty(), + [], + 0, + 0, + ); + } + + public toDto(): ProjectTaskUserReportTotalRowDto { + return { + opened: this.opened.toDto(), + done: this.done.toDto(), + overdue: this.overdue.toDto(), + stages: this.stages.map((s) => s.toDto()), + planedTime: this.plannedTime, + completionPercent: this.completionPercent, + }; + } +} diff --git a/backend/src/CRM/reporting/project/types/project-task-user-report.ts b/backend/src/CRM/reporting/project/types/project-task-user-report.ts new file mode 100644 index 0000000..47e634a --- /dev/null +++ b/backend/src/CRM/reporting/project/types/project-task-user-report.ts @@ -0,0 +1,17 @@ +import { ProjectTaskUserReportDto } from '../dto'; +import { type ProjectTaskUserReportRow } from './project-task-user-report-row'; +import { type ProjectTaskUserReportTotalRow } from './project-task-user-report-total-row'; + +export class ProjectTaskUserReport { + rows: ProjectTaskUserReportRow[]; + total: ProjectTaskUserReportTotalRow; + + constructor(rows: ProjectTaskUserReportRow[], total: ProjectTaskUserReportTotalRow) { + this.rows = rows; + this.total = total; + } + + public toDto(): ProjectTaskUserReportDto { + return { rows: this.rows.map((r) => r.toDto()), total: this.total.toDto() }; + } +} diff --git a/backend/src/CRM/sales-plan/dto/index.ts b/backend/src/CRM/sales-plan/dto/index.ts new file mode 100644 index 0000000..3818c78 --- /dev/null +++ b/backend/src/CRM/sales-plan/dto/index.ts @@ -0,0 +1,2 @@ +export * from './sales-plan-progress.dto'; +export * from './sales-plan.dto'; diff --git a/backend/src/CRM/sales-plan/dto/sales-plan-progress.dto.ts b/backend/src/CRM/sales-plan/dto/sales-plan-progress.dto.ts new file mode 100644 index 0000000..3970701 --- /dev/null +++ b/backend/src/CRM/sales-plan/dto/sales-plan-progress.dto.ts @@ -0,0 +1,42 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class SalesPlanProgressDto { + @ApiProperty() + @IsNumber() + userId: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + currentQuantity: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + currentAmount: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + plannedQuantity: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + plannedAmount: number | null; + + constructor( + userId: number, + currentQuantity: number | null, + currentAmount: number | null, + plannedQuantity: number | null, + plannedAmount: number | null, + ) { + this.userId = userId; + this.currentQuantity = currentQuantity; + this.currentAmount = currentAmount; + this.plannedQuantity = plannedQuantity; + this.plannedAmount = plannedAmount; + } +} diff --git a/backend/src/CRM/sales-plan/dto/sales-plan.dto.ts b/backend/src/CRM/sales-plan/dto/sales-plan.dto.ts new file mode 100644 index 0000000..4a4bc36 --- /dev/null +++ b/backend/src/CRM/sales-plan/dto/sales-plan.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsObject, IsOptional } from 'class-validator'; + +import { DatePeriodDto } from '@/common'; + +export class SalesPlanDto { + @ApiProperty() + @IsNumber() + userId: number; + + @ApiProperty({ type: DatePeriodDto }) + @IsObject() + period: DatePeriodDto; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + quantity: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + amount: number | null; + + constructor(userId: number, period: DatePeriodDto, quantity: number | null, amount: number | null) { + this.userId = userId; + this.period = period; + this.quantity = quantity; + this.amount = amount; + } +} diff --git a/backend/src/CRM/sales-plan/entities/index.ts b/backend/src/CRM/sales-plan/entities/index.ts new file mode 100644 index 0000000..0c4ac40 --- /dev/null +++ b/backend/src/CRM/sales-plan/entities/index.ts @@ -0,0 +1 @@ +export * from './sales-plan.entity'; diff --git a/backend/src/CRM/sales-plan/entities/sales-plan.entity.ts b/backend/src/CRM/sales-plan/entities/sales-plan.entity.ts new file mode 100644 index 0000000..07d3d91 --- /dev/null +++ b/backend/src/CRM/sales-plan/entities/sales-plan.entity.ts @@ -0,0 +1,76 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DatePeriod, DateUtil } from '@/common'; + +import { SalesPlanDto } from '../dto/sales-plan.dto'; + +@Entity() +export class SalesPlan { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + entityTypeId: number; + + @Column() + userId: number; + + @Column() + startDate: Date; + + @Column() + endDate: Date; + + @Column({ nullable: true }) + quantity: number | null; + + @Column({ nullable: true }) + amount: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + entityTypeId: number, + userId: number, + startDate: Date, + endDate: Date, + quantity: number | null, + amount: number | null, + accountId: number, + createdAt?: Date, + ) { + this.entityTypeId = entityTypeId; + this.userId = userId; + this.startDate = startDate; + this.endDate = endDate; + this.quantity = quantity; + this.amount = amount; + this.accountId = accountId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto(accountId: number, entityTypeId: number, dto: SalesPlanDto): SalesPlan { + const period = DatePeriod.fromDto(dto.period); + return new SalesPlan(entityTypeId, dto.userId, period.from, period.to, dto.quantity, dto.amount, accountId); + } + + public update(dto: SalesPlanDto): SalesPlan { + this.amount = dto.amount; + this.quantity = dto.quantity; + + return this; + } + + public toDto(): SalesPlanDto { + return new SalesPlanDto( + this.userId, + new DatePeriod(this.startDate, this.endDate).toDto(), + this.quantity, + this.amount, + ); + } +} diff --git a/backend/src/CRM/sales-plan/sales-plan.controller.ts b/backend/src/CRM/sales-plan/sales-plan.controller.ts new file mode 100644 index 0000000..7837d41 --- /dev/null +++ b/backend/src/CRM/sales-plan/sales-plan.controller.ts @@ -0,0 +1,80 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { DatePeriodDto, TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { SalesPlanProgressDto } from './dto/sales-plan-progress.dto'; +import { SalesPlanDto } from './dto/sales-plan.dto'; +import { SalesPlanService } from './sales-plan.service'; + +@ApiTags('crm/sales-plans') +@Controller('/crm/entity-types/:entityTypeId/sales-plans') +@JwtAuthorized() +@TransformToDto() +export class SalesPlanController { + constructor(private readonly service: SalesPlanService) {} + + @ApiCreatedResponse({ type: [SalesPlanDto] }) + @Post('settings') + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() dtos: SalesPlanDto[], + ) { + return await this.service.upsertMany(accountId, entityTypeId, dtos); + } + + @ApiCreatedResponse({ type: [SalesPlanDto] }) + @Get('settings') + public async getMany( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query() period: DatePeriodDto, + ) { + return await this.service.getMany(accountId, entityTypeId, period); + } + + @ApiCreatedResponse({ type: [SalesPlanProgressDto] }) + @Get() + public async getProgress( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query() period: DatePeriodDto, + ) { + return await this.service.getProgress(accountId, entityTypeId, period); + } + + @ApiCreatedResponse({ type: [SalesPlanDto] }) + @Put('settings') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() dtos: SalesPlanDto[], + ) { + return await this.service.upsertMany(accountId, entityTypeId, dtos); + } + + @ApiCreatedResponse({ type: [SalesPlanDto] }) + @Delete('settings') + public async deleteAll( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Query() period: DatePeriodDto, + ) { + return await this.service.deleteAll(accountId, entityTypeId, period); + } + + @ApiCreatedResponse({ type: [SalesPlanDto] }) + @Delete('settings/users/:userId') + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Param('userId', ParseIntPipe) userId: number, + @Query() period: DatePeriodDto, + ) { + return await this.service.delete(accountId, entityTypeId, userId, period); + } +} diff --git a/backend/src/CRM/sales-plan/sales-plan.module.ts b/backend/src/CRM/sales-plan/sales-plan.module.ts new file mode 100644 index 0000000..926d7a6 --- /dev/null +++ b/backend/src/CRM/sales-plan/sales-plan.module.ts @@ -0,0 +1,23 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { CrmModule } from '../crm.module'; + +import { CrmReportingModule } from '../reporting/crm-reporting.module'; +import { SalesPlan } from './entities/sales-plan.entity'; +import { SalesPlanService } from './sales-plan.service'; +import { SalesPlanController } from './sales-plan.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([SalesPlan]), + IAMModule, + forwardRef(() => CrmModule), + forwardRef(() => CrmReportingModule), + ], + controllers: [SalesPlanController], + providers: [SalesPlanService], + exports: [SalesPlanService], +}) +export class SalesPlanModule {} diff --git a/backend/src/CRM/sales-plan/sales-plan.service.ts b/backend/src/CRM/sales-plan/sales-plan.service.ts new file mode 100644 index 0000000..4973d17 --- /dev/null +++ b/backend/src/CRM/sales-plan/sales-plan.service.ts @@ -0,0 +1,177 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DatePeriod, DatePeriodDto, DatePeriodFilter, DatePeriodFilterType, DateUtil } from '@/common'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; + +import { BoardStageService } from '../board-stage'; +import { OwnerDateValue } from '../reporting/common'; +import { CrmReportingService } from '../reporting/crm-reporting.service'; + +import { SalesPlanDto, SalesPlanProgressDto } from './dto'; +import { SalesPlan } from './entities'; + +@Injectable() +export class SalesPlanService { + constructor( + @InjectRepository(SalesPlan) + private readonly repository: Repository, + private readonly stageService: BoardStageService, + @Inject(forwardRef(() => CrmReportingService)) + private readonly reportingService: CrmReportingService, + ) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.deleteForUser(event.accountId, event.userId); + } + + public async upsertMany(accountId: number, entityTypeId: number, dtos: SalesPlanDto[]): Promise { + return await Promise.all(dtos.map((dto) => this.upsertOne(accountId, entityTypeId, dto))); + } + + public async upsertOne(accountId: number, entityTypeId: number, dto: SalesPlanDto): Promise { + const period = DatePeriod.fromDto(dto.period); + const sp = await this.createQb(accountId, { entityTypeId, period, userId: dto.userId }).getOne(); + + if (sp) { + return await this.repository.save(sp.update(dto)); + } else { + return await this.repository.save(SalesPlan.fromDto(accountId, entityTypeId, dto)); + } + } + + public async getMany(accountId: number, entityTypeId: number, periodDto: DatePeriodDto): Promise { + const period = DatePeriod.fromDto(periodDto); + + return await this.createQb(accountId, { entityTypeId, period }).getMany(); + } + + public async getProgress( + accountId: number, + entityTypeId: number, + periodDto: DatePeriodDto, + ): Promise { + const period = DatePeriod.fromDto(periodDto); + const salesPlans = await this.createQb(accountId, { entityTypeId, period }).getMany(); + + if (salesPlans.length === 0) { + return []; + } + + const { quantity, amount } = await this.getSalesPlanProgress( + accountId, + entityTypeId, + salesPlans[0].startDate, + salesPlans[0].endDate, + salesPlans.map((sp) => sp.userId), + ); + + const result: SalesPlanProgressDto[] = []; + for (const salePlan of salesPlans) { + const currentCount = quantity.find((q) => q.ownerId === salePlan.userId)?.value ?? 0; + const currentAmount = amount.find((a) => a.ownerId === salePlan.userId)?.value ?? 0; + result.push( + new SalesPlanProgressDto(salePlan.userId, currentCount, currentAmount, salePlan.quantity, salePlan.amount), + ); + } + + return result; + } + + public async getSalesPlanProgress( + accountId: number, + entityTypeId: number, + startDate: Date, + endDate: Date, + userIds: number[], + boardIds?: number[], + ): Promise<{ quantity: OwnerDateValue[]; amount: OwnerDateValue[] }> { + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: boardIds?.length ? boardIds : undefined, + }); + + return this.reportingService.getEntityGroupBy( + accountId, + stages.won, + { owner: 'user' }, + { amount: true, quantity: true }, + { closedAt: new DatePeriod(startDate, endDate), userIds }, + ); + } + + public async getActualPlans( + accountId: number, + entityTypeId: number, + userIds: number[] | null | undefined, + period: DatePeriodFilter | null | undefined, + ): Promise { + const dates = period + ? DatePeriod.fromFilter(period) + : DatePeriod.fromFilter({ type: DatePeriodFilterType.CurrentMonth }); + + const qb = this.repository + .createQueryBuilder() + .where('account_id = :accountId', { accountId }) + .andWhere('entity_type_id = :entityTypeId', { entityTypeId }); + if (userIds?.length > 0) { + qb.andWhere('user_id IN (:...userIds)', { userIds }); + } + if (dates.from && dates.to) { + dates.to.setMilliseconds(0); + // eslint-disable-next-line prettier/prettier + qb.andWhere('start_date <= :startDate', { startDate: dates.from }) + .andWhere('end_date >= :endDate', { endDate: dates.to }); + } else { + const now = DateUtil.now(); + // eslint-disable-next-line prettier/prettier + qb.andWhere('start_date < :startDate', { startDate: now }) + .andWhere('end_date > :endDate', { endDate: now }); + } + + return await qb.getMany(); + } + + public async deleteAll(accountId: number, entityTypeId: number, periodDto: DatePeriodDto): Promise { + const period = DatePeriod.fromDto(periodDto); + await this.createQb(accountId, { entityTypeId, period }).delete().execute(); + } + + public async delete( + accountId: number, + entityTypeId: number, + userId: number, + periodDto: DatePeriodDto, + ): Promise { + const period = DatePeriod.fromDto(periodDto); + await this.createQb(accountId, { entityTypeId, period, userId }).delete().execute(); + } + + private async deleteForUser(accountId: number, userId: number) { + await this.repository.delete({ accountId, userId }); + } + + private createQb(accountId: number, filter: { entityTypeId: number; period?: DatePeriod; userId?: number }) { + const qb = this.repository + .createQueryBuilder() + .where('account_id = :accountId', { accountId }) + .andWhere('entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + if (filter.period?.from) { + qb.andWhere('start_date >= :startDate', { startDate: filter.period.from }); + } + if (filter.period?.to) { + qb.andWhere('end_date <= :endDate', { endDate: filter.period.to }); + } + + if (filter.userId) { + qb.andWhere('user_id = :userId', { userId: filter.userId }); + } + + return qb; + } +} diff --git a/backend/src/CRM/task-board/dto/index.ts b/backend/src/CRM/task-board/dto/index.ts new file mode 100644 index 0000000..cce2ded --- /dev/null +++ b/backend/src/CRM/task-board/dto/index.ts @@ -0,0 +1,6 @@ +export * from './task-board-card.dto'; +export * from './task-board-filter.dto'; +export * from './task-board-meta.dto'; +export * from './task-board-stage-meta.dto'; +export * from './task-calendar-meta.dto'; +export * from './task-list-meta.dto'; diff --git a/backend/src/CRM/task-board/dto/task-board-card.dto.ts b/backend/src/CRM/task-board/dto/task-board-card.dto.ts new file mode 100644 index 0000000..e7b7e37 --- /dev/null +++ b/backend/src/CRM/task-board/dto/task-board-card.dto.ts @@ -0,0 +1,51 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { BaseTaskDto } from '../../base-task'; +import { Task } from '../../task'; +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +export class TaskBoardCardDto extends BaseTaskDto { + @ApiProperty({ description: 'Title' }) + @IsString() + title: string; + + @ApiProperty({ description: 'Planned time', nullable: true }) + @IsOptional() + @IsNumber() + plannedTime: number | null; + + @ApiProperty({ description: 'Stage ID', nullable: true }) + @IsOptional() + @IsNumber() + stageId: number | null; + + @ApiProperty({ description: 'Settings ID', nullable: true }) + @IsOptional() + @IsNumber() + settingsId: number | null; + + @ApiProperty({ description: 'Subtask count', nullable: true }) + @IsOptional() + @IsNumber() + subtaskCount: number | null; + + constructor( + task: Task, + entityInfo: EntityInfoDto | null, + fileLinks: FileLinkDto[], + userRights: UserRights, + subtaskCount: number | null, + ) { + super(task, entityInfo, fileLinks, userRights); + + this.title = task.title; + this.plannedTime = task.plannedTime; + this.stageId = task.stageId; + this.settingsId = task.settingsId; + this.subtaskCount = subtaskCount; + } +} diff --git a/backend/src/CRM/task-board/dto/task-board-filter.dto.ts b/backend/src/CRM/task-board/dto/task-board-filter.dto.ts new file mode 100644 index 0000000..5cbb270 --- /dev/null +++ b/backend/src/CRM/task-board/dto/task-board-filter.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { BaseTaskBoardFilter } from '../../Service/BaseTaskBoard/BaseTaskBoardFilter'; + +export class TaskBoardFilterDto extends BaseTaskBoardFilter { + @ApiPropertyOptional({ description: 'Stage ids', type: [Number] }) + @IsOptional() + @IsArray() + stageIds?: number[] | null; +} diff --git a/backend/src/CRM/task-board/dto/task-board-meta.dto.ts b/backend/src/CRM/task-board/dto/task-board-meta.dto.ts new file mode 100644 index 0000000..2cd0763 --- /dev/null +++ b/backend/src/CRM/task-board/dto/task-board-meta.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { UserTimeAllocation } from '../../Service/BaseTaskBoard/UserTimeAllocation'; +import { TaskBoardStageMeta } from './task-board-stage-meta.dto'; + +export class TaskBoardMeta { + @ApiProperty({ description: 'Total tasks count' }) + @IsNumber() + total: number; + + @ApiProperty({ description: 'Task board stages meta', type: [TaskBoardStageMeta] }) + stages: TaskBoardStageMeta[]; + + @ApiProperty({ description: 'User time allocation', type: [UserTimeAllocation] }) + timeAllocation: UserTimeAllocation[]; +} diff --git a/backend/src/CRM/task-board/dto/task-board-stage-meta.dto.ts b/backend/src/CRM/task-board/dto/task-board-stage-meta.dto.ts new file mode 100644 index 0000000..97d49ef --- /dev/null +++ b/backend/src/CRM/task-board/dto/task-board-stage-meta.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { UserTimeAllocation } from '../../Service/BaseTaskBoard/UserTimeAllocation'; + +export class TaskBoardStageMeta { + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Total tasks count' }) + @IsNumber() + total: number; + + @ApiProperty({ description: 'User time allocation', type: [UserTimeAllocation] }) + timeAllocation: UserTimeAllocation[]; +} diff --git a/backend/src/CRM/task-board/dto/task-calendar-meta.dto.ts b/backend/src/CRM/task-board/dto/task-calendar-meta.dto.ts new file mode 100644 index 0000000..f62f771 --- /dev/null +++ b/backend/src/CRM/task-board/dto/task-calendar-meta.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class TaskCalendarMeta { + @ApiProperty({ description: 'Total tasks count' }) + @IsNumber() + total: number; +} diff --git a/backend/src/CRM/task-board/dto/task-list-meta.dto.ts b/backend/src/CRM/task-board/dto/task-list-meta.dto.ts new file mode 100644 index 0000000..7ee1d27 --- /dev/null +++ b/backend/src/CRM/task-board/dto/task-list-meta.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { UserTimeAllocation } from '../../Service/BaseTaskBoard/UserTimeAllocation'; + +export class TaskListMeta { + @ApiProperty({ description: 'Total tasks count' }) + @IsNumber() + total: number; + + @ApiProperty({ description: 'User time allocation', type: [UserTimeAllocation] }) + timeAllocation: UserTimeAllocation[]; +} diff --git a/backend/src/CRM/task-board/index.ts b/backend/src/CRM/task-board/index.ts new file mode 100644 index 0000000..447b999 --- /dev/null +++ b/backend/src/CRM/task-board/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './task-board.controller'; +export * from './task-board.service'; diff --git a/backend/src/CRM/task-board/task-board.controller.ts b/backend/src/CRM/task-board/task-board.controller.ts new file mode 100644 index 0000000..7dc6843 --- /dev/null +++ b/backend/src/CRM/task-board/task-board.controller.ts @@ -0,0 +1,113 @@ +import { Body, Controller, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { DatePeriodDto, PagingQuery } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TaskBoardCardDto, TaskBoardFilterDto, TaskBoardMeta, TaskCalendarMeta, TaskListMeta } from './dto'; +import { TaskBoardService } from './task-board.service'; + +@ApiTags('crm/tasks/board') +@Controller('/crm/tasks') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class TaskBoardController { + constructor(private readonly service: TaskBoardService) {} + + @ApiOperation({ summary: 'Get list of tasks', description: 'Get list of tasks' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for list of tasks', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Task cards', type: [TaskBoardCardDto] }) + @Post('list/:boardId') + public async getTaskList( + @CurrentAuth() { account, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() filter: TaskBoardFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.getTaskList(account, user, boardId, filter, paging); + } + + @ApiOperation({ summary: 'Get meta for list of tasks', description: 'Get meta for list of tasks' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for list of tasks', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Meta for list of tasks', type: TaskListMeta }) + @Post('list/:boardId/meta') + public async getTaskListMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() filter: TaskBoardFilterDto, + ) { + return this.service.getTaskListMeta(accountId, user, boardId, filter); + } + + @ApiOperation({ summary: 'Get tasks for board', description: 'Get tasks for board' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for board', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Task cards', type: [TaskBoardCardDto] }) + @Post('boards/:boardId') + public async getTaskBoardCards( + @CurrentAuth() { account, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() filter: TaskBoardFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.getTaskBoardCards(account, user, boardId, filter, paging); + } + + @ApiOperation({ summary: 'Get meta for board', description: 'Get meta for board' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for board', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Meta for board', type: TaskBoardMeta }) + @Post('boards/:boardId/meta') + public async getTaskBoardMeta( + @CurrentAuth() { accountId, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Body() filter: TaskBoardFilterDto, + ) { + return this.service.getTaskBoardMeta(accountId, user, boardId, filter); + } + + @ApiOperation({ summary: 'Get calendar for tasks', description: 'Get calendar for tasks' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for calendar', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Task cards', type: [TaskBoardCardDto] }) + @Post('calendar/:boardId') + public async getTaskCalendar( + @CurrentAuth() { account, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Query() period: DatePeriodDto, + @Body() filter: TaskBoardFilterDto, + ) { + return this.service.getTaskCalendar(account, user, boardId, period, filter); + } + + @ApiOperation({ summary: 'Get meta for calendar', description: 'Get meta for calendar' }) + @ApiParam({ name: 'boardId', description: 'Board ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for calendar', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Meta for calendar', type: TaskCalendarMeta }) + @Post('calendar/:boardId/meta') + public async getTaskCalendarMeta( + @CurrentAuth() { account, user }: AuthData, + @Param('boardId', ParseIntPipe) boardId: number, + @Query() period: DatePeriodDto, + @Body() filter: TaskBoardFilterDto, + ) { + return this.service.getTaskCalendarMeta(account, user, boardId, period, filter); + } + + @ApiOperation({ summary: 'Get task card', description: 'Get task card' }) + @ApiParam({ name: 'taskId', description: 'Task ID', type: Number, required: true }) + @ApiBody({ description: 'Filter data for task card', type: TaskBoardFilterDto }) + @ApiOkResponse({ description: 'Task card', type: TaskBoardCardDto }) + @Post('cards/:taskId') + public async getTaskBoardCard( + @CurrentAuth() { account, user }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Body() filter: TaskBoardFilterDto, + @Query('boardId') boardId?: number, + ) { + return this.service.getTaskBoardCard(account, user, taskId, filter, boardId); + } +} diff --git a/backend/src/CRM/task-board/task-board.service.ts b/backend/src/CRM/task-board/task-board.service.ts new file mode 100644 index 0000000..4d1278c --- /dev/null +++ b/backend/src/CRM/task-board/task-board.service.ts @@ -0,0 +1,384 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DatePeriod, DatePeriodDto, FileLinkSource, isUnique, NumberUtil, PagingQuery } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoDto, EntityInfoService } from '@/modules/entity/entity-info'; + +import { BoardStageService } from '../board-stage'; +import { Task } from '../task'; +import { TaskSubtaskService } from '../task-subtask/task-subtask.service'; + +import { FileLinkService } from '../Service/FileLink/FileLinkService'; + +import { TaskBoardQueryHelper } from '../Service/BaseTaskBoard/TaskBoardQueryHelper'; +import { UserTimeAllocation } from '../Service/BaseTaskBoard/UserTimeAllocation'; +import { + TaskBoardCardDto, + TaskBoardFilterDto, + TaskBoardMeta, + TaskBoardStageMeta, + TaskCalendarMeta, + TaskListMeta, +} from './dto'; + +@Injectable() +export class TaskBoardService { + constructor( + @InjectRepository(Task) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + @Inject(forwardRef(() => BoardStageService)) + private readonly stageService: BoardStageService, + private readonly fileLinkService: FileLinkService, + private readonly subtaskService: TaskSubtaskService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async getTaskList( + account: Account, + user: User, + boardId: number, + filter: TaskBoardFilterDto, + paging: PagingQuery, + ): Promise { + const responsibles = await this.getResponsibles({ accountId: account.id, user, entityIds: filter?.entityIds }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + account.id, + user.id, + this.repository, + filter, + responsibles, + true, + 'title', + ); + + const stageIds = filter.stageIds ?? (await this.stageService.findManyIds({ accountId: account.id, boardId })); + if (stageIds) { + qb.andWhere('stage_id IN (:...stageIds)', { stageIds }); + } + + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const tasks = await qb.offset(paging.skip).limit(paging.take).getMany(); + + return this.createTaskBoardCards({ account, user, tasks }); + } + + public async getTaskListMeta( + accountId: number, + user: User, + boardId: number, + filter: TaskBoardFilterDto, + ): Promise { + const responsibles = await this.getResponsibles({ accountId, user, entityIds: filter?.entityIds }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + responsibles, + false, + 'title', + ); + + const stageIds = filter.stageIds ?? (await this.stageService.findManyIds({ accountId, boardId })); + if (stageIds) { + qb.andWhere('stage_id IN (:...stageIds)', { stageIds }); + } + + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const total = await qb.getCount(); + + const boardAllocations = await qb + .groupBy('responsible_user_id') + .select('responsible_user_id, SUM(planned_time) AS planned_time') + .getRawMany(); + const timeAllocation = boardAllocations.map( + (a) => new UserTimeAllocation(a.responsible_user_id, NumberUtil.toNumber(a.planned_time)), + ); + + return { total, timeAllocation }; + } + + public async getTaskBoardCards( + account: Account, + user: User, + boardId: number, + filter: TaskBoardFilterDto, + paging: PagingQuery, + ): Promise { + const responsibles = await this.getResponsibles({ accountId: account.id, user, entityIds: filter?.entityIds }); + + const stageIds = filter.stageIds ?? (await this.stageService.findManyIds({ accountId: account.id, boardId })); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + account.id, + user.id, + this.repository, + filter, + responsibles, + true, + 'title', + ); + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const tasks = ( + await Promise.all( + stageIds.map(async (stageId) => + qb.clone().andWhere('stage_id = :stageId', { stageId }).offset(paging.skip).limit(paging.take).getMany(), + ), + ) + ).flat(); + + return this.createTaskBoardCards({ account, user, tasks }); + } + + public async getTaskBoardCard( + account: Account, + user: User, + taskId: number, + filter: TaskBoardFilterDto, + boardId?: number, + ): Promise { + const responsibles = await this.getResponsibles({ accountId: account.id, user, entityIds: filter?.entityIds }); + + const stageIds = + filter.stageIds ?? (boardId ? await this.stageService.findManyIds({ accountId: account.id, boardId }) : null); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + account.id, + user.id, + this.repository, + filter, + responsibles, + false, + 'title', + ); + + if (stageIds) { + qb.andWhere('stage_id IN (:...stageIds)', { stageIds }); + } + + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const task = await qb.andWhere('id = :id', { id: taskId }).getOne(); + + return task ? this.createTaskBoardCard({ account, user, task }) : null; + } + + public async getTaskBoardMeta( + accountId: number, + user: User, + boardId: number, + filter: TaskBoardFilterDto, + ): Promise { + const responsibles = await this.getResponsibles({ accountId, user, entityIds: filter?.entityIds }); + + const stageIds = filter.stageIds ?? (await this.stageService.findManyIds({ accountId, boardId })); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + accountId, + user.id, + this.repository, + filter, + responsibles, + false, + 'title', + ); + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const stages: TaskBoardStageMeta[] = ( + await Promise.all( + stageIds.map(async (stageId) => { + const stagedQb = qb.clone().andWhere('stage_id = :stageId', { stageId }); + const total = await stagedQb.getCount(); + const allocations = await stagedQb + .groupBy('responsible_user_id') + .select('responsible_user_id', 'user_id') + .addSelect('COALESCE(SUM(planned_time), 0)::int', 'planned_time') + .getRawMany<{ user_id: number; planned_time: number }>(); + + return { + id: stageId, + total, + timeAllocation: allocations.map((a) => new UserTimeAllocation(a.user_id, a.planned_time)), + }; + }), + ) + ).flat(); + + const boardQb = qb.clone().andWhere('stage_id IN (:...stageIds)', { stageIds }); + if (filter.showResolved === false) { + boardQb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + const total = await boardQb.getCount(); + const boardAllocations = await boardQb + .groupBy('responsible_user_id') + .select('responsible_user_id', 'user_id') + .addSelect('COALESCE(SUM(planned_time), 0)::int', 'planned_time') + .getRawMany<{ user_id: number; planned_time: number }>(); + const timeAllocation = boardAllocations.map( + (a) => new UserTimeAllocation(a.user_id, NumberUtil.toNumber(a.planned_time)), + ); + + return { total, stages, timeAllocation }; + } + + public async getTaskCalendar( + account: Account, + user: User, + boardId: number, + periodDto: DatePeriodDto, + filter: TaskBoardFilterDto, + ): Promise { + const responsibles = await this.getResponsibles({ accountId: account.id, user, entityIds: filter?.entityIds }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + account.id, + user.id, + this.repository, + filter, + responsibles, + true, + 'title', + ); + + const stageIds = filter.stageIds ?? (await this.stageService.findManyIds({ accountId: account.id, boardId })); + if (stageIds) { + qb.andWhere('stage_id IN (:...stageIds)', { stageIds }); + } + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const period = DatePeriod.fromDto(periodDto); + if (period.from && period.to) { + qb.andWhere('start_date < :to', { to: period.to }).andWhere('end_date > :from', { from: period.from }); + } + + const tasks = await qb.getMany(); + + return this.createTaskBoardCards({ account, user, tasks }); + } + + public async getTaskCalendarMeta( + account: Account, + user: User, + boardId: number, + periodDto: DatePeriodDto, + filter: TaskBoardFilterDto, + ): Promise { + const responsibles = await this.getResponsibles({ accountId: account.id, user, entityIds: filter?.entityIds }); + + const qb = TaskBoardQueryHelper.createBoardQueryBuilder( + account.id, + user.id, + this.repository, + filter, + responsibles, + true, + 'title', + ); + + const stageIds = filter.stageIds ?? (await this.stageService.findManyIds({ accountId: account.id, boardId })); + if (stageIds) { + qb.andWhere('stage_id IN (:...stageIds)', { stageIds }); + } + if (filter.showResolved === false) { + qb.andWhere('is_resolved = :isResolved', { isResolved: false }); + } + + const period = DatePeriod.fromDto(periodDto); + if (period.from && period.to) { + qb.andWhere('start_date < :to', { to: period.to }).andWhere('end_date > :from', { from: period.from }); + } + const total = await qb.getCount(); + + return { total }; + } + + public async createTaskBoardCards({ + account, + user, + tasks, + }: { + account: Account; + user: User; + tasks: Task[]; + }): Promise { + const entityIds = tasks + .map((task) => task.entityId) + .filter((id) => !!id) + .filter(isUnique); + const entityInfoCache = entityIds.length + ? await this.entityInfoService.findMany({ accountId: account.id, user, entityIds }) + : []; + + const cards = await Promise.all( + tasks.map(async (task, idx) => { + const entityInfo = task.entityId ? entityInfoCache.find((ei) => ei.id === task.entityId) : null; + const card = await this.createTaskBoardCard({ account, user, task, entityInfo }); + return { idx, card }; + }), + ); + return cards.sort((a, b) => a.idx - b.idx).map((c) => c.card); + } + + public async createTaskBoardCard({ + account, + user, + task, + entityInfo = null, + }: { + account: Account; + user: User; + task: Task; + entityInfo?: EntityInfoDto | null; + }): Promise { + const entityInfoCurrent = + !entityInfo && task.entityId + ? await this.entityInfoService.findOne({ accountId: account.id, user, entityId: task.entityId }) + : entityInfo; + const userRights = await this.authService.getUserRights({ user, authorizable: task }); + const fileLinks = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.TASK, task.id); + const subtaskCount = await this.subtaskService.getCount(account.id, { taskId: task.id }); + + return new TaskBoardCardDto(task, entityInfoCurrent, fileLinks, userRights, subtaskCount); + } + + private async getResponsibles({ + accountId, + user, + entityIds, + }: { + accountId: number; + user: User; + entityIds?: number[] | null; + }): Promise { + if (entityIds?.length === 1) { + const entityInfo = await this.entityInfoService.findOne({ accountId, entityId: entityIds[0] }); + if (entityInfo.participantIds?.length > 0) { + return entityInfo.participantIds.includes(user.id) ? null : [user.id]; + } + } + return await this.authService.whoCanView({ user, authorizable: Task.getAuthorizable() }); + } +} diff --git a/backend/src/CRM/task-comment-like/dto/index.ts b/backend/src/CRM/task-comment-like/dto/index.ts new file mode 100644 index 0000000..7358713 --- /dev/null +++ b/backend/src/CRM/task-comment-like/dto/index.ts @@ -0,0 +1 @@ +export * from './task-comment-like.dto'; diff --git a/backend/src/CRM/task-comment-like/dto/task-comment-like.dto.ts b/backend/src/CRM/task-comment-like/dto/task-comment-like.dto.ts new file mode 100644 index 0000000..c83fa09 --- /dev/null +++ b/backend/src/CRM/task-comment-like/dto/task-comment-like.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class TaskCommentLikeDto { + @ApiProperty({ description: 'Task comment ID' }) + @IsNumber() + commentId: number; + + @ApiProperty({ description: 'User ID who liked the comment' }) + @IsNumber() + userId: number; +} diff --git a/backend/src/CRM/task-comment-like/entities/index.ts b/backend/src/CRM/task-comment-like/entities/index.ts new file mode 100644 index 0000000..65399de --- /dev/null +++ b/backend/src/CRM/task-comment-like/entities/index.ts @@ -0,0 +1 @@ +export * from './task-comment-like.entity'; diff --git a/backend/src/CRM/task-comment-like/entities/task-comment-like.entity.ts b/backend/src/CRM/task-comment-like/entities/task-comment-like.entity.ts new file mode 100644 index 0000000..d66eb30 --- /dev/null +++ b/backend/src/CRM/task-comment-like/entities/task-comment-like.entity.ts @@ -0,0 +1,24 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { TaskCommentLikeDto } from '../dto'; + +@Entity() +export class TaskCommentLike { + @PrimaryColumn() + commentId: number; + + @PrimaryColumn() + userId: number; + + @Column() + accountId: number; + + constructor(commentId: number, userId: number, accountId: number) { + this.commentId = commentId; + this.userId = userId; + this.accountId = accountId; + } + + public toDto(): TaskCommentLikeDto { + return { commentId: this.commentId, userId: this.userId }; + } +} diff --git a/backend/src/CRM/task-comment-like/index.ts b/backend/src/CRM/task-comment-like/index.ts new file mode 100644 index 0000000..2d21bab --- /dev/null +++ b/backend/src/CRM/task-comment-like/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './task-comment-like.controller'; +export * from './task-comment-like.service'; diff --git a/backend/src/CRM/task-comment-like/task-comment-like.controller.ts b/backend/src/CRM/task-comment-like/task-comment-like.controller.ts new file mode 100644 index 0000000..7083831 --- /dev/null +++ b/backend/src/CRM/task-comment-like/task-comment-like.controller.ts @@ -0,0 +1,43 @@ +import { Controller, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TaskCommentLikeDto } from './dto'; +import { TaskCommentLikeService } from './task-comment-like.service'; + +@ApiTags('crm/task-comments') +@Controller('crm/tasks/:taskId/comments/:commentId') +@JwtAuthorized() +@TransformToDto() +export class TaskCommentLikeController { + constructor(private readonly service: TaskCommentLikeService) {} + + @ApiOperation({ summary: 'Like task comment', description: 'Like task comment for current user' }) + @ApiParam({ name: 'taskId', type: Number, required: true, description: 'Task ID' }) + @ApiParam({ name: 'commentId', type: Number, required: true, description: 'Comment ID' }) + @ApiCreatedResponse({ type: TaskCommentLikeDto, description: 'Task comment like' }) + @Post('like') + public async like( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('commentId', ParseIntPipe) commentId: number, + ) { + return await this.service.like({ accountId, userId, commentId }); + } + + @ApiOperation({ summary: 'Unlike task comment', description: 'Unlike task comment for current user' }) + @ApiParam({ name: 'taskId', type: Number, required: true, description: 'Task ID' }) + @ApiParam({ name: 'commentId', type: Number, required: true, description: 'Comment ID' }) + @ApiOkResponse() + @Post('unlike') + public async unlike( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('commentId', ParseIntPipe) commentId: number, + ) { + return await this.service.unlike({ accountId, userId, commentId }); + } +} diff --git a/backend/src/CRM/task-comment-like/task-comment-like.service.ts b/backend/src/CRM/task-comment-like/task-comment-like.service.ts new file mode 100644 index 0000000..8654b69 --- /dev/null +++ b/backend/src/CRM/task-comment-like/task-comment-like.service.ts @@ -0,0 +1,46 @@ +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { TaskCommentLike } from './entities'; + +interface FindFilter { + accountId: number; + commentId?: number; + userId?: number; +} + +export class TaskCommentLikeService { + constructor( + @InjectRepository(TaskCommentLike) + private readonly repository: Repository, + ) {} + + public async like({ + accountId, + commentId, + userId, + }: { + accountId: number; + commentId: number; + userId: number; + }): Promise { + return this.repository.save(new TaskCommentLike(commentId, userId, accountId)); + } + + public async unlike({ accountId, commentId, userId }: { accountId: number; commentId: number; userId: number }) { + await this.repository.delete({ accountId, commentId, userId }); + } + + public async findMany({ accountId, commentId, userId }: FindFilter): Promise { + const qb = this.repository.createQueryBuilder('tcl').where('tcl.account_id = :accountId', { accountId }); + + if (commentId) { + qb.andWhere('tcl.comment_id = :commentId', { commentId }); + } + if (userId) { + qb.andWhere('tcl.user_id = :userId', { userId }); + } + + return qb.getMany(); + } +} diff --git a/backend/src/CRM/task-comment/dto/create-task-comment.dto.ts b/backend/src/CRM/task-comment/dto/create-task-comment.dto.ts new file mode 100644 index 0000000..ee99e4b --- /dev/null +++ b/backend/src/CRM/task-comment/dto/create-task-comment.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional, IsString } from 'class-validator'; + +export class CreateTaskCommentDto { + @ApiProperty({ description: 'Text of comment' }) + @IsString() + text: string; + + @ApiPropertyOptional({ type: [String], nullable: true, description: 'Task comment attached file IDs' }) + @IsOptional() + @IsArray() + fileIds: string[] | null; +} diff --git a/backend/src/CRM/task-comment/dto/index.ts b/backend/src/CRM/task-comment/dto/index.ts new file mode 100644 index 0000000..3a3fb0e --- /dev/null +++ b/backend/src/CRM/task-comment/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-task-comment.dto'; +export * from './task-comment-result.dto'; +export * from './task-comment.dto'; +export * from './update-task-comment.dto'; diff --git a/backend/src/CRM/task-comment/dto/task-comment-result.dto.ts b/backend/src/CRM/task-comment/dto/task-comment-result.dto.ts new file mode 100644 index 0000000..019b28c --- /dev/null +++ b/backend/src/CRM/task-comment/dto/task-comment-result.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { TaskCommentDto } from './task-comment.dto'; + +export class TaskCommentResultDto { + @ApiProperty({ type: [TaskCommentDto], description: 'Task comment list' }) + result: TaskCommentDto[]; + + @ApiProperty({ type: PagingMeta, description: 'Paging meta' }) + meta: PagingMeta; +} diff --git a/backend/src/CRM/task-comment/dto/task-comment.dto.ts b/backend/src/CRM/task-comment/dto/task-comment.dto.ts new file mode 100644 index 0000000..d5c1ab7 --- /dev/null +++ b/backend/src/CRM/task-comment/dto/task-comment.dto.ts @@ -0,0 +1,32 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +export class TaskCommentDto { + @ApiProperty({ description: 'Task comment ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Task ID of comment' }) + @IsNumber() + taskId: number; + + @ApiProperty({ description: 'User ID of comment' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'Text of comment' }) + @IsString() + text: string; + + @ApiProperty({ type: [FileLinkDto], description: 'Task comment attached file IDs' }) + fileLinks: FileLinkDto[]; + + @ApiProperty({ type: [Number], description: 'User IDs who liked the comment' }) + likedUserIds: number[]; + + @ApiProperty({ description: 'Date and time when the comment was created in ISO format' }) + @IsString() + createdAt: string; +} diff --git a/backend/src/CRM/task-comment/dto/update-task-comment.dto.ts b/backend/src/CRM/task-comment/dto/update-task-comment.dto.ts new file mode 100644 index 0000000..5f6497f --- /dev/null +++ b/backend/src/CRM/task-comment/dto/update-task-comment.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class UpdateTaskCommentDto { + @ApiProperty({ description: 'Text of comment' }) + @IsString() + text: string; +} diff --git a/backend/src/CRM/task-comment/entities/index.ts b/backend/src/CRM/task-comment/entities/index.ts new file mode 100644 index 0000000..f4e9be4 --- /dev/null +++ b/backend/src/CRM/task-comment/entities/index.ts @@ -0,0 +1 @@ +export * from './task-comment.entity'; diff --git a/backend/src/CRM/task-comment/entities/task-comment.entity.ts b/backend/src/CRM/task-comment/entities/task-comment.entity.ts new file mode 100644 index 0000000..9bb45d2 --- /dev/null +++ b/backend/src/CRM/task-comment/entities/task-comment.entity.ts @@ -0,0 +1,63 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; +import { TaskCommentLike } from '../../task-comment-like'; +import { TaskCommentDto } from '../dto'; + +@Entity() +export class TaskComment { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + text: string; + + @Column() + taskId: number; + + @Column() + createdBy: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, createdBy: number, text: string, taskId: number, createdAt?: Date) { + this.accountId = accountId; + this.createdBy = createdBy; + this.text = text; + this.taskId = taskId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _fileLinks: FileLinkDto[] | null; + public get fileLinks(): FileLinkDto[] | null { + return this._fileLinks; + } + public set fileLinks(value: FileLinkDto[] | null) { + this._fileLinks = value; + } + private _likes: TaskCommentLike[] | null; + public get likes(): TaskCommentLike[] | null { + return this._likes; + } + public set likes(value: TaskCommentLike[] | null) { + this._likes = value; + } + + public toDto(): TaskCommentDto { + return { + id: this.id, + taskId: this.taskId, + createdBy: this.createdBy, + text: this.text, + createdAt: this.createdAt.toISOString(), + fileLinks: this.fileLinks ?? [], + likedUserIds: this.likes ? this.likes.map((l) => l.userId) : [], + }; + } +} diff --git a/backend/src/CRM/task-comment/index.ts b/backend/src/CRM/task-comment/index.ts new file mode 100644 index 0000000..b44b5e4 --- /dev/null +++ b/backend/src/CRM/task-comment/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './task-comment.controller'; +export * from './task-comment.service'; diff --git a/backend/src/CRM/task-comment/task-comment.controller.ts b/backend/src/CRM/task-comment/task-comment.controller.ts new file mode 100644 index 0000000..a6b48a9 --- /dev/null +++ b/backend/src/CRM/task-comment/task-comment.controller.ts @@ -0,0 +1,66 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { CreateTaskCommentDto, TaskCommentDto, TaskCommentResultDto, UpdateTaskCommentDto } from './dto'; +import { TaskCommentService } from './task-comment.service'; + +@ApiTags('crm/task-comments') +@Controller('crm/tasks/:taskId/comments') +@JwtAuthorized({ prefetch: { account: true } }) +@TransformToDto() +export class TaskCommentController { + constructor(private readonly service: TaskCommentService) {} + + @ApiOperation({ summary: 'Create task comment', description: 'Create task comment' }) + @ApiParam({ name: 'taskId', type: Number, required: true, description: 'Task ID' }) + @ApiBody({ type: CreateTaskCommentDto, required: true, description: 'Data for creating task comment' }) + @ApiCreatedResponse({ description: 'Task comment', type: TaskCommentDto }) + @Post() + public async create( + @CurrentAuth() { account, userId }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Body() dto: CreateTaskCommentDto, + ) { + return this.service.create({ account, taskId, userId, dto }); + } + + @ApiOperation({ summary: 'Get task comments', description: 'Get task comments' }) + @ApiParam({ name: 'taskId', type: Number, required: true, description: 'Task ID' }) + @ApiOkResponse({ description: 'Task comments', type: TaskCommentResultDto }) + @Get() + public async getComments( + @CurrentAuth() { account }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getComments({ account, taskId, paging }); + } + + @ApiOperation({ summary: 'Update task comment', description: 'Update task comment' }) + @ApiParam({ name: 'taskId', type: Number, required: true, description: 'Task ID' }) + @ApiParam({ name: 'commentId', type: Number, required: true, description: 'Comment ID' }) + @ApiBody({ type: UpdateTaskCommentDto, required: true, description: 'Data for updating task comment' }) + @ApiOkResponse({ description: 'Task comment', type: TaskCommentDto }) + @Put(':commentId') + public async update( + @CurrentAuth() { account }: AuthData, + @Param('commentId', ParseIntPipe) commentId: number, + @Body() dto: UpdateTaskCommentDto, + ) { + return this.service.update({ account, commentId, dto }); + } + + @ApiOperation({ summary: 'Delete task comment', description: 'Delete task comment' }) + @ApiParam({ name: 'taskId', type: Number, required: true, description: 'Task ID' }) + @ApiParam({ name: 'commentId', type: Number, required: true, description: 'Comment ID' }) + @ApiOkResponse() + @Delete(':commentId') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('commentId', ParseIntPipe) commentId: number) { + return this.service.delete({ accountId, commentId }); + } +} diff --git a/backend/src/CRM/task-comment/task-comment.service.ts b/backend/src/CRM/task-comment/task-comment.service.ts new file mode 100644 index 0000000..b0e9346 --- /dev/null +++ b/backend/src/CRM/task-comment/task-comment.service.ts @@ -0,0 +1,152 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Repository } from 'typeorm'; + +import { FileLinkSource, PagingQuery, PagingMeta } from '@/common'; +import { Account } from '@/modules/iam/account/entities/account.entity'; + +import { CrmEventType, TaskCommentCreatedEvent } from '../common'; +import { TaskService } from '../task'; +import { TaskCommentLikeService } from '../task-comment-like'; + +import { FileLinkService } from '../Service/FileLink/FileLinkService'; + +import { CreateTaskCommentDto, TaskCommentResultDto, UpdateTaskCommentDto } from './dto'; +import { TaskComment } from './entities'; + +interface FindFilter { + accountId: number; + commentId?: number; + taskId?: number; +} + +@Injectable() +export class TaskCommentService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(TaskComment) + private readonly repository: Repository, + private readonly fileLinkService: FileLinkService, + private readonly likeService: TaskCommentLikeService, + private readonly taskService: TaskService, + ) {} + + public async create({ + account, + userId, + taskId, + dto, + }: { + account: Account; + userId: number; + taskId: number; + dto: CreateTaskCommentDto; + }): Promise { + const comment = await this.repository.save(new TaskComment(account.id, userId, dto.text, taskId)); + + if (dto.fileIds) { + await this.fileLinkService.processFiles(account.id, FileLinkSource.TASK_COMMENT, comment.id, dto.fileIds); + } + + await this.expandOne({ account, comment }); + + const task = await this.taskService.findOne({ accountId: account.id, taskId }); + this.eventEmitter.emit( + CrmEventType.TaskCommentCreated, + new TaskCommentCreatedEvent({ + source: TaskCommentService.name, + accountId: account.id, + taskId: task.id, + boardId: task.boardId, + ownerId: task.responsibleUserId, + entityId: task.entityId ?? null, + createdBy: userId, + taskTitle: task.title, + taskText: task.text, + createdAt: comment.createdAt, + startDate: task.startDate, + endDate: task.endDate, + taskComment: comment.text, + }), + ); + + return comment; + } + + public async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + public async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).orderBy('tc.created_at', 'DESC').getMany(); + } + + public async getComments({ + account, + taskId, + paging, + }: { + account: Account; + taskId: number; + paging: PagingQuery; + }): Promise { + const qb = this.createFindQb({ accountId: account.id, taskId }); + const comments = await qb.orderBy('tc.created_at', 'DESC').offset(paging.skip).limit(paging.take).getMany(); + await this.expandMany({ account, comments }); + const total = await qb.getCount(); + + return { result: comments.map((r) => r.toDto()), meta: new PagingMeta(paging.offset + paging.limit, total) }; + } + + public async update({ + account, + commentId, + dto, + }: { + account: Account; + commentId: number; + dto: UpdateTaskCommentDto; + }): Promise { + await this.repository.update({ accountId: account.id, id: commentId }, { text: dto.text }); + + const comment = await this.findOne({ accountId: account.id, commentId }); + await this.expandOne({ account, comment }); + + return comment; + } + + public async delete({ accountId, commentId }: { accountId: number; commentId: number }): Promise { + await this.repository.delete({ accountId, id: commentId }); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('tc') + .where('tc.account_id = :accountId', { accountId: filter.accountId }); + + if (filter.commentId) { + qb.andWhere('tc.id = :commentId', { commentId: filter.commentId }); + } + if (filter.taskId) { + qb.andWhere('tc.task_id = :taskId', { taskId: filter.taskId }); + } + + return qb; + } + + private async expandOne({ account, comment }: { account: Account; comment: TaskComment }): Promise { + comment.fileLinks = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.TASK_COMMENT, comment.id); + comment.likes = await this.likeService.findMany({ accountId: account.id, commentId: comment.id }); + + return comment; + } + private async expandMany({ + account, + comments, + }: { + account: Account; + comments: TaskComment[]; + }): Promise { + return await Promise.all(comments.map((comment) => this.expandOne({ account, comment }))); + } +} diff --git a/backend/src/CRM/task-settings/dto/create-task-settings.dto.ts b/backend/src/CRM/task-settings/dto/create-task-settings.dto.ts new file mode 100644 index 0000000..d2a3979 --- /dev/null +++ b/backend/src/CRM/task-settings/dto/create-task-settings.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from '@nestjs/swagger'; + +import { TaskSettingsDto } from './task-settings.dto'; + +export class CreateTaskSettingsDto extends PickType(TaskSettingsDto, ['type', 'recordId', 'activeFields'] as const) {} diff --git a/backend/src/CRM/task-settings/dto/index.ts b/backend/src/CRM/task-settings/dto/index.ts new file mode 100644 index 0000000..0d1c871 --- /dev/null +++ b/backend/src/CRM/task-settings/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-task-settings.dto'; +export * from './task-settings.dto'; +export * from './update-task-settings.dto'; diff --git a/backend/src/CRM/task-settings/dto/task-settings.dto.ts b/backend/src/CRM/task-settings/dto/task-settings.dto.ts new file mode 100644 index 0000000..18574d8 --- /dev/null +++ b/backend/src/CRM/task-settings/dto/task-settings.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { TaskFieldCode, TaskSettingsType } from '../enums'; + +export class TaskSettingsDto { + @ApiProperty({ description: 'Task settings id' }) + @IsNumber() + id: number; + + @ApiProperty({ enum: TaskSettingsType, description: 'Task settings connected object type' }) + @IsEnum(TaskSettingsType) + type: TaskSettingsType; + + @ApiProperty({ nullable: true, description: 'Connected object id' }) + @IsOptional() + @IsNumber() + recordId: number | null; + + @ApiProperty({ enum: TaskFieldCode, isArray: true, description: 'Task settings active fields' }) + @IsEnum(TaskFieldCode, { each: true }) + activeFields: TaskFieldCode[]; +} diff --git a/backend/src/CRM/task-settings/dto/update-task-settings.dto.ts b/backend/src/CRM/task-settings/dto/update-task-settings.dto.ts new file mode 100644 index 0000000..77ee9c5 --- /dev/null +++ b/backend/src/CRM/task-settings/dto/update-task-settings.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from '@nestjs/swagger'; + +import { TaskSettingsDto } from './task-settings.dto'; + +export class UpdateTaskSettingsDto extends PickType(TaskSettingsDto, ['activeFields'] as const) {} diff --git a/backend/src/CRM/task-settings/entities/index.ts b/backend/src/CRM/task-settings/entities/index.ts new file mode 100644 index 0000000..a295a1f --- /dev/null +++ b/backend/src/CRM/task-settings/entities/index.ts @@ -0,0 +1 @@ +export * from './task-settings.entity'; diff --git a/backend/src/CRM/task-settings/entities/task-settings.entity.ts b/backend/src/CRM/task-settings/entities/task-settings.entity.ts new file mode 100644 index 0000000..eed8888 --- /dev/null +++ b/backend/src/CRM/task-settings/entities/task-settings.entity.ts @@ -0,0 +1,39 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { TaskFieldCode } from '../enums/task-field-code.enum'; +import { TaskSettingsType } from '../enums/task-settings-type.enum'; +import { CreateTaskSettingsDto } from '../dto/create-task-settings.dto'; +import { TaskSettingsDto } from '../dto/task-settings.dto'; + +@Entity() +export class TaskSettings { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column({ type: 'jsonb' }) + activeFields: TaskFieldCode[]; + + @Column() + type: TaskSettingsType; + + @Column({ nullable: true }) + recordId: number | null; + + constructor(accountId: number, type: TaskSettingsType, recordId: number | null, activeFields: TaskFieldCode[]) { + this.accountId = accountId; + this.type = type; + this.recordId = recordId; + this.activeFields = activeFields; + } + + public static fromDto(accountId: number, dto: CreateTaskSettingsDto) { + return new TaskSettings(accountId, dto.type, dto.recordId, dto.activeFields); + } + + public toDto(): TaskSettingsDto { + return { id: this.id, type: this.type, recordId: this.recordId, activeFields: this.activeFields }; + } +} diff --git a/backend/src/CRM/task-settings/enums/index.ts b/backend/src/CRM/task-settings/enums/index.ts new file mode 100644 index 0000000..996f7c9 --- /dev/null +++ b/backend/src/CRM/task-settings/enums/index.ts @@ -0,0 +1,2 @@ +export * from './task-field-code.enum'; +export * from './task-settings-type.enum'; diff --git a/backend/src/CRM/task-settings/enums/task-field-code.enum.ts b/backend/src/CRM/task-settings/enums/task-field-code.enum.ts new file mode 100644 index 0000000..69ab10b --- /dev/null +++ b/backend/src/CRM/task-settings/enums/task-field-code.enum.ts @@ -0,0 +1,8 @@ +export enum TaskFieldCode { + PlannedTime = 'planned_time', + BoardName = 'board_name', + StartDate = 'start_date', + EndDate = 'end_date', + Description = 'description', + Subtasks = 'subtasks', +} diff --git a/backend/src/CRM/task-settings/enums/task-settings-type.enum.ts b/backend/src/CRM/task-settings/enums/task-settings-type.enum.ts new file mode 100644 index 0000000..09a916c --- /dev/null +++ b/backend/src/CRM/task-settings/enums/task-settings-type.enum.ts @@ -0,0 +1,5 @@ +export enum TaskSettingsType { + EntityType = 'entity_type', + TaskBoard = 'task_board', + TimeBoard = 'time_board', +} diff --git a/backend/src/CRM/task-settings/task-settings.controller.ts b/backend/src/CRM/task-settings/task-settings.controller.ts new file mode 100644 index 0000000..d765ee4 --- /dev/null +++ b/backend/src/CRM/task-settings/task-settings.controller.ts @@ -0,0 +1,40 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TaskSettingsDto, CreateTaskSettingsDto, UpdateTaskSettingsDto } from './dto'; +import { TaskSettingsService } from './task-settings.service'; + +@ApiTags('crm/task-settings') +@Controller('/crm/task-settings') +@JwtAuthorized() +@TransformToDto() +export class TaskSettingsController { + constructor(private readonly service: TaskSettingsService) {} + + @ApiCreatedResponse({ description: 'Create task settings', type: TaskSettingsDto }) + @Post() + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateTaskSettingsDto) { + return await this.service.create(accountId, dto); + } + + @ApiCreatedResponse({ description: 'Task settings list', type: [TaskSettingsDto] }) + @Get() + public async findMany(@CurrentAuth() { accountId }: AuthData) { + return await this.service.findMany(accountId); + } + + @ApiCreatedResponse({ description: 'Update task settings', type: TaskSettingsDto }) + @Put(':id') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('id', ParseIntPipe) id: number, + @Body() dto: UpdateTaskSettingsDto, + ) { + return await this.service.update(accountId, id, dto); + } +} diff --git a/backend/src/CRM/task-settings/task-settings.handler.ts b/backend/src/CRM/task-settings/task-settings.handler.ts new file mode 100644 index 0000000..ddaff77 --- /dev/null +++ b/backend/src/CRM/task-settings/task-settings.handler.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { BoardEvent, CrmEventType, EntityTypeEvent } from '@/CRM/common'; + +import { TaskSettingsType } from './enums/task-settings-type.enum'; +import { TaskSettingsService } from './task-settings.service'; + +@Injectable() +export class TaskSettingsHandler { + constructor(private readonly service: TaskSettingsService) {} + + @OnEvent(CrmEventType.EntityTypeDeleted, { async: true }) + public async onEntityTypeDeleted(event: EntityTypeEvent) { + await this.service.delete(event.accountId, event.entityTypeId, TaskSettingsType.EntityType); + } + + @OnEvent(CrmEventType.BoardDeleted, { async: true }) + public async onBoardDeleted(event: BoardEvent) { + await this.service.delete(event.accountId, event.boardId, TaskSettingsType.TaskBoard); + } +} diff --git a/backend/src/CRM/task-settings/task-settings.module.ts b/backend/src/CRM/task-settings/task-settings.module.ts new file mode 100644 index 0000000..317c687 --- /dev/null +++ b/backend/src/CRM/task-settings/task-settings.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { TaskSettings } from './entities'; +import { TaskSettingsService } from './task-settings.service'; +import { TaskSettingsHandler } from './task-settings.handler'; +import { TaskSettingsController } from './task-settings.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([TaskSettings]), IAMModule], + controllers: [TaskSettingsController], + providers: [TaskSettingsHandler, TaskSettingsService], + exports: [TaskSettingsService], +}) +export class TaskSettingsModule {} diff --git a/backend/src/CRM/task-settings/task-settings.service.ts b/backend/src/CRM/task-settings/task-settings.service.ts new file mode 100644 index 0000000..b9dd4ca --- /dev/null +++ b/backend/src/CRM/task-settings/task-settings.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { CreateTaskSettingsDto, UpdateTaskSettingsDto } from './dto'; +import { TaskSettings } from './entities'; +import { TaskFieldCode, TaskSettingsType } from './enums'; + +@Injectable() +export class TaskSettingsService { + constructor( + @InjectRepository(TaskSettings) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, dto: CreateTaskSettingsDto): Promise { + return this.repository.save(TaskSettings.fromDto(accountId, dto)); + } + + public async findOne(accountId: number, settingsId: number): Promise { + return await this.repository.findOne({ where: { accountId, id: settingsId } }); + } + + public async findMany(accountId: number): Promise { + return await this.repository.find({ where: { accountId } }); + } + + public async setTaskSettingsForEntityType( + accountId: number, + entityTypeId: number, + activeFields: TaskFieldCode[] | null, + ) { + if (activeFields) { + await this.repository.delete({ accountId, type: TaskSettingsType.EntityType, recordId: entityTypeId }); + await this.create(accountId, { type: TaskSettingsType.EntityType, recordId: entityTypeId, activeFields }); + } + } + + public async update(accountId: number, settingsId: number, dto: UpdateTaskSettingsDto): Promise { + await this.repository.update({ accountId, id: settingsId }, { activeFields: dto.activeFields }); + + return this.findOne(accountId, settingsId); + } + + public async delete(accountId: number, recordId: number, type: TaskSettingsType): Promise { + await this.repository.delete({ accountId, recordId, type }); + } +} diff --git a/backend/src/CRM/task-subtask/dto/create-task-subtask.dto.ts b/backend/src/CRM/task-subtask/dto/create-task-subtask.dto.ts new file mode 100644 index 0000000..7c98814 --- /dev/null +++ b/backend/src/CRM/task-subtask/dto/create-task-subtask.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { TaskSubtaskDto } from './task-subtask.dto'; + +export class CreateTaskSubtaskDto extends PickType(TaskSubtaskDto, ['text', 'resolved', 'sortOrder'] as const) {} diff --git a/backend/src/CRM/task-subtask/dto/index.ts b/backend/src/CRM/task-subtask/dto/index.ts new file mode 100644 index 0000000..7f49914 --- /dev/null +++ b/backend/src/CRM/task-subtask/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-task-subtask.dto'; +export * from './task-subtask.dto'; +export * from './update-task-subtask.dto'; diff --git a/backend/src/CRM/task-subtask/dto/task-subtask.dto.ts b/backend/src/CRM/task-subtask/dto/task-subtask.dto.ts new file mode 100644 index 0000000..58d7519 --- /dev/null +++ b/backend/src/CRM/task-subtask/dto/task-subtask.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class TaskSubtaskDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + text: string; + + @ApiProperty() + @IsBoolean() + resolved: boolean; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + sortOrder?: number; + + constructor({ id, text, resolved, sortOrder }: TaskSubtaskDto) { + this.id = id; + this.text = text; + this.resolved = resolved; + this.sortOrder = sortOrder; + } +} diff --git a/backend/src/CRM/task-subtask/dto/update-task-subtask.dto.ts b/backend/src/CRM/task-subtask/dto/update-task-subtask.dto.ts new file mode 100644 index 0000000..5815960 --- /dev/null +++ b/backend/src/CRM/task-subtask/dto/update-task-subtask.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, PartialType, PickType } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { TaskSubtaskDto } from './task-subtask.dto'; + +export class UpdateTaskSubtaskDto extends PartialType( + PickType(TaskSubtaskDto, ['text', 'resolved', 'sortOrder'] as const), +) { + @ApiProperty() + @IsNumber() + id: number; +} diff --git a/backend/src/CRM/task-subtask/entities/index.ts b/backend/src/CRM/task-subtask/entities/index.ts new file mode 100644 index 0000000..10da639 --- /dev/null +++ b/backend/src/CRM/task-subtask/entities/index.ts @@ -0,0 +1 @@ +export * from './task-subtask.entity'; diff --git a/backend/src/CRM/task-subtask/entities/task-subtask.entity.ts b/backend/src/CRM/task-subtask/entities/task-subtask.entity.ts new file mode 100644 index 0000000..b2f04b7 --- /dev/null +++ b/backend/src/CRM/task-subtask/entities/task-subtask.entity.ts @@ -0,0 +1,48 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { CreateTaskSubtaskDto, TaskSubtaskDto, UpdateTaskSubtaskDto } from '../dto'; + +@Entity() +export class TaskSubtask { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + text: string; + + @Column() + resolved: boolean; + + @Column() + taskId: number; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + constructor(accountId: number, taskId: number, text: string, resolved: boolean, sortOrder: number) { + this.accountId = accountId; + this.text = text; + this.resolved = resolved; + this.taskId = taskId; + this.sortOrder = sortOrder; + } + + public static fromDto(accountId: number, taskId: number, dto: CreateTaskSubtaskDto): TaskSubtask { + return new TaskSubtask(accountId, taskId, dto.text, dto.resolved, dto.sortOrder || 0); + } + + public update(dto: UpdateTaskSubtaskDto): TaskSubtask { + this.text = dto.text !== undefined ? dto.text : this.text; + this.resolved = dto.resolved !== undefined ? dto.resolved : this.resolved; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + + return this; + } + + public toDto(): TaskSubtaskDto { + return new TaskSubtaskDto(this); + } +} diff --git a/backend/src/CRM/task-subtask/index.ts b/backend/src/CRM/task-subtask/index.ts new file mode 100644 index 0000000..bb1b49c --- /dev/null +++ b/backend/src/CRM/task-subtask/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './task-subtask.controller'; +export * from './task-subtask.service'; diff --git a/backend/src/CRM/task-subtask/task-subtask.controller.ts b/backend/src/CRM/task-subtask/task-subtask.controller.ts new file mode 100644 index 0000000..434532f --- /dev/null +++ b/backend/src/CRM/task-subtask/task-subtask.controller.ts @@ -0,0 +1,49 @@ +import { Body, Controller, Delete, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { CreateTaskSubtaskDto, TaskSubtaskDto, UpdateTaskSubtaskDto } from './dto'; +import { TaskSubtaskService } from './task-subtask.service'; + +@ApiTags('crm/tasks/subtasks') +@Controller('/crm/tasks/:taskId/subtasks') +@JwtAuthorized() +@TransformToDto() +export class TaskSubtaskController { + constructor(private readonly service: TaskSubtaskService) {} + + @ApiCreatedResponse({ description: 'Create subtask', type: TaskSubtaskDto }) + @Post() + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Body() dto: CreateTaskSubtaskDto, + ) { + return this.service.create(accountId, taskId, dto); + } + + @ApiOkResponse({ description: 'Update subtask', type: TaskSubtaskDto }) + @Put(':subtaskId') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Param('subtaskId', ParseIntPipe) subtaskId: number, + @Body() dto: UpdateTaskSubtaskDto, + ) { + return this.service.update(accountId, taskId, subtaskId, dto); + } + + @ApiOkResponse({ description: 'Delete subtask' }) + @Delete(':subtaskId') + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Param('subtaskId', ParseIntPipe) subtaskId: number, + ) { + await this.service.delete(accountId, taskId, subtaskId); + } +} diff --git a/backend/src/CRM/task-subtask/task-subtask.service.ts b/backend/src/CRM/task-subtask/task-subtask.service.ts new file mode 100644 index 0000000..507c80f --- /dev/null +++ b/backend/src/CRM/task-subtask/task-subtask.service.ts @@ -0,0 +1,104 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { TaskSubtask } from './entities'; +import { CreateTaskSubtaskDto, UpdateTaskSubtaskDto } from './dto'; + +interface FindFilter { + taskId?: number; + subtaskId?: number | number[]; +} + +@Injectable() +export class TaskSubtaskService { + constructor( + @InjectRepository(TaskSubtask) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, taskId: number, dto: CreateTaskSubtaskDto): Promise { + return this.repository.save(TaskSubtask.fromDto(accountId, taskId, dto)); + } + + public async createMany(accountId: number, taskId: number, dtos: CreateTaskSubtaskDto[]): Promise { + return Promise.all(dtos.map((dto) => this.create(accountId, taskId, dto))); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).orderBy('sort_order', 'ASC').getMany(); + } + + public async getCount(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getCount(); + } + + public async update( + accountId: number, + taskId: number, + subtaskId: number, + dto: UpdateTaskSubtaskDto, + ): Promise { + const subtask = await this.findOne(accountId, { taskId, subtaskId }); + if (!subtask) { + throw NotFoundError.withId(TaskSubtask, subtaskId); + } + + return this.repository.save(subtask.update(dto)); + } + + public async updateMany(accountId: number, taskId: number, dtos: UpdateTaskSubtaskDto[]): Promise { + return Promise.all(dtos.map((dto) => this.update(accountId, taskId, dto.id, dto))); + } + + public async processBatch( + accountId: number, + taskId: number, + dtos: (CreateTaskSubtaskDto | UpdateTaskSubtaskDto)[], + ): Promise { + const subtasks = await this.findMany(accountId, { taskId }); + + const created = dtos.filter((dto) => !dto['id']).map((dto) => dto as CreateTaskSubtaskDto); + const updated = dtos.filter((dto) => dto['id']).map((dto) => dto as UpdateTaskSubtaskDto); + const deleted = subtasks.filter((f) => !updated.some((dto) => dto.id === f.id)).map((f) => f.id); + + const result: TaskSubtask[] = []; + + result.push(...(await this.createMany(accountId, taskId, created))); + result.push(...(await this.updateMany(accountId, taskId, updated))); + + if (deleted.length) { + await this.delete(accountId, taskId, deleted); + } + + return result; + } + + public async delete(accountId: number, taskId: number, subtaskId: number | number[]): Promise { + await this.createFindQb(accountId, { taskId, subtaskId }).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.taskId) { + qb.andWhere('task_id = :taskId', { taskId: filter.taskId }); + } + + if (filter?.subtaskId) { + if (Array.isArray(filter.subtaskId)) { + qb.andWhere('id IN (:...ids)', { ids: filter.subtaskId }); + } else { + qb.andWhere('id = :id', { id: filter.subtaskId }); + } + } + + return qb; + } +} diff --git a/backend/src/CRM/task/dto/create-task.dto.ts b/backend/src/CRM/task/dto/create-task.dto.ts new file mode 100644 index 0000000..a9ed5cb --- /dev/null +++ b/backend/src/CRM/task/dto/create-task.dto.ts @@ -0,0 +1,53 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { CreateBaseTaskDto } from '../../base-task'; +import { CreateTaskSubtaskDto } from '../../task-subtask/dto'; + +export class CreateTaskDto extends CreateBaseTaskDto { + @ApiPropertyOptional({ nullable: true, description: 'Entity ID' }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiProperty({ description: 'Title' }) + @IsString() + title: string; + + @ApiPropertyOptional({ nullable: true, description: 'Planned time' }) + @IsOptional() + @IsNumber() + plannedTime?: number | null; + + @ApiPropertyOptional({ description: 'Board ID' }) + @IsOptional() + @IsNumber() + boardId?: number; + + @ApiPropertyOptional({ description: 'Stage ID' }) + @IsOptional() + @IsNumber() + stageId?: number; + + @ApiProperty({ nullable: true, description: 'Settings ID' }) + @IsOptional() + @IsNumber() + settingsId?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'External ID' }) + @IsOptional() + @IsString() + externalId?: string | null; + + @ApiPropertyOptional({ nullable: true, type: [String], description: 'File IDs' }) + @IsOptional() + @IsString({ each: true }) + fileIds?: string[] | null; + + @ApiPropertyOptional({ type: [CreateTaskSubtaskDto], description: 'Subtasks' }) + @IsOptional() + @IsArray() + @Type(() => CreateTaskSubtaskDto) + subtasks?: CreateTaskSubtaskDto[]; +} diff --git a/backend/src/CRM/task/dto/index.ts b/backend/src/CRM/task/dto/index.ts new file mode 100644 index 0000000..5bda572 --- /dev/null +++ b/backend/src/CRM/task/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-task.dto'; +export * from './task.dto'; +export * from './update-task.dto'; diff --git a/backend/src/CRM/task/dto/task.dto.ts b/backend/src/CRM/task/dto/task.dto.ts new file mode 100644 index 0000000..bac991e --- /dev/null +++ b/backend/src/CRM/task/dto/task.dto.ts @@ -0,0 +1,61 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { BaseTaskDto } from '../../base-task'; +import { TaskSubtaskDto } from '../../task-subtask/dto/task-subtask.dto'; +import { FileLinkDto } from '../../Service/FileLink/FileLinkDto'; + +import { Task } from '../entities'; + +export class TaskDto extends BaseTaskDto { + @ApiProperty() + @IsString() + title: string; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + plannedTime: number | null; + + @ApiProperty() + @IsNumber() + boardId: number; + + @ApiProperty() + @IsNumber() + stageId: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + settingsId: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'External ID' }) + @IsOptional() + @IsString() + externalId?: string | null; + + @ApiProperty({ type: [TaskSubtaskDto] }) + subtasks: TaskSubtaskDto[]; + + constructor( + task: Task, + entityInfo: EntityInfoDto | null, + fileLinks: FileLinkDto[], + subtasks: TaskSubtaskDto[], + userRights: UserRights, + ) { + super(task, entityInfo, fileLinks, userRights); + + this.title = task.title; + this.plannedTime = task.plannedTime; + this.boardId = task.boardId; + this.stageId = task.stageId; + this.settingsId = task.settingsId; + this.externalId = task.externalId; + this.subtasks = subtasks; + } +} diff --git a/backend/src/CRM/task/dto/update-task.dto.ts b/backend/src/CRM/task/dto/update-task.dto.ts new file mode 100644 index 0000000..ac69a79 --- /dev/null +++ b/backend/src/CRM/task/dto/update-task.dto.ts @@ -0,0 +1,55 @@ +import { ApiExtraModels, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { UpdateBaseTaskDto } from '../../base-task'; +import { CreateTaskSubtaskDto, UpdateTaskSubtaskDto } from '../../task-subtask/dto'; + +@ApiExtraModels(CreateTaskSubtaskDto) +@ApiExtraModels(UpdateTaskSubtaskDto) +export class UpdateTaskDto extends UpdateBaseTaskDto { + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + title?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + plannedTime?: number | null; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + boardId?: number; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + stageId?: number; + + @ApiPropertyOptional({ nullable: true, description: 'External ID' }) + @IsOptional() + @IsString() + externalId?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsArray() + fileIds?: string[] | null; + + @ApiPropertyOptional({ + description: 'Array of subtasks', + type: 'array', + items: { oneOf: [{ $ref: getSchemaPath(CreateTaskSubtaskDto) }, { $ref: getSchemaPath(UpdateTaskSubtaskDto) }] }, + }) + @IsOptional() + @IsArray() + @Type(() => Object) //HACK: Nest make items array not object?!?! + subtasks?: (CreateTaskSubtaskDto | UpdateTaskSubtaskDto)[] | null; +} diff --git a/backend/src/CRM/task/entities/index.ts b/backend/src/CRM/task/entities/index.ts new file mode 100644 index 0000000..1ab88d2 --- /dev/null +++ b/backend/src/CRM/task/entities/index.ts @@ -0,0 +1 @@ +export * from './task.entity'; diff --git a/backend/src/CRM/task/entities/task.entity.ts b/backend/src/CRM/task/entities/task.entity.ts new file mode 100644 index 0000000..f1d0574 --- /dev/null +++ b/backend/src/CRM/task/entities/task.entity.ts @@ -0,0 +1,152 @@ +import { Column, Entity } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; +import { BaseTask, TaskView } from '../../base-task'; + +import { CreateTaskDto, TaskDto, UpdateTaskDto } from '../dto'; + +@Entity('task') +export class Task extends BaseTask implements Authorizable { + @Column({ nullable: true }) + entityId: number | null; + + @Column() + title: string; + + @Column() + plannedTime: number; // interval in seconds + + @Column() + boardId: number; + + @Column() + stageId: number; + + @Column() + settingsId: number | null; + + @Column() + externalId: string | null; + + constructor( + accountId: number, + entityId: number | null, + createdBy: number, + responsibleUserId: number, + text: string, + title: string, + plannedTime: number | null, + isResolved: boolean, + startDate: Date, + endDate: Date, + resolvedDate: Date | null, + weight: number, + boardId: number, + stageId: number, + settingsId: number | null, + externalId: string | null, + createdAt?: Date, + ) { + super( + accountId, + createdBy, + responsibleUserId, + text, + isResolved, + startDate, + endDate, + resolvedDate, + weight, + createdAt, + ); + this.entityId = entityId; + this.title = title; + this.plannedTime = plannedTime; + this.boardId = boardId; + this.stageId = stageId; + this.settingsId = settingsId; + this.externalId = externalId; + } + + public static create(accountId: number, createdBy: number, dto: CreateTaskDto): Task { + return new Task( + accountId, + dto.entityId, + createdBy, + dto.responsibleUserId, + dto.text ?? '', + dto.title, + dto.plannedTime, + false, + dto.startDate ? DateUtil.fromISOString(dto.startDate) : null, + dto.endDate ? DateUtil.fromISOString(dto.endDate) : null, + dto.resolvedDate ? DateUtil.fromISOString(dto.resolvedDate) : null, + dto.weight, + dto.boardId, + dto.stageId, + dto.settingsId, + dto.externalId, + ); + } + + public update(dto: UpdateTaskDto): Task { + this.entityId = dto.entityId !== undefined ? dto.entityId : this.entityId; + this.responsibleUserId = dto.responsibleUserId !== undefined ? dto.responsibleUserId : this.responsibleUserId; + this.text = dto.text !== undefined ? dto.text : this.text; + this.title = dto.title !== undefined ? dto.title : this.title; + this.plannedTime = dto.plannedTime !== undefined ? dto.plannedTime : this.plannedTime; + this.startDate = dto.startDate !== undefined ? DateUtil.fromISOString(dto.startDate) : this.startDate; + this.endDate = dto.endDate !== undefined ? DateUtil.fromISOString(dto.endDate) : this.endDate; + this.boardId = dto.boardId ? dto.boardId : this.boardId; + this.stageId = dto.stageId ? dto.stageId : this.stageId; + this.externalId = dto.externalId !== undefined ? dto.externalId : this.externalId; + + if (dto.isResolved !== undefined) { + if (!this.isResolved && dto.isResolved) this.resolvedDate = DateUtil.now(); + if (this.isResolved && !dto.isResolved) this.resolvedDate = null; + this.isResolved = dto.isResolved; + } + + return this; + } + + public hasChanges(dto: UpdateTaskDto): boolean { + return ( + (dto.entityId !== undefined && dto.entityId !== this.entityId) || + (dto.responsibleUserId !== undefined && dto.responsibleUserId !== this.responsibleUserId) || + (dto.text !== undefined && dto.text !== this.text) || + (dto.title !== undefined && dto.title !== this.title) || + (dto.plannedTime !== undefined && dto.plannedTime !== this.plannedTime) || + (dto.startDate !== undefined && DateUtil.fromISOString(dto.startDate) !== this.startDate) || + (dto.endDate !== undefined && DateUtil.fromISOString(dto.endDate) !== this.endDate) || + (dto.boardId !== undefined && dto.boardId !== this.boardId) || + (dto.stageId !== undefined && dto.stageId !== this.stageId) || + (dto.externalId !== undefined && dto.externalId !== this.externalId) || + (dto.isResolved !== undefined && dto.isResolved !== this.isResolved) + ); + } + + public view(): TaskView { + return TaskView.Task; + } + + public toSimpleDto(): TaskDto { + return new TaskDto(this, null, [], [], null); + } + + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Task, + id: null, + ownerId: this.responsibleUserId, + createdBy: this.createdBy, + }; + } + static getAuthorizable(): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Task, id: null }); + } +} diff --git a/backend/src/CRM/task/index.ts b/backend/src/CRM/task/index.ts new file mode 100644 index 0000000..375c931 --- /dev/null +++ b/backend/src/CRM/task/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './task.controller'; +export * from './task.handler'; +export * from './task.service'; diff --git a/backend/src/CRM/task/task.controller.ts b/backend/src/CRM/task/task.controller.ts new file mode 100644 index 0000000..644e8dc --- /dev/null +++ b/backend/src/CRM/task/task.controller.ts @@ -0,0 +1,47 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { CreateTaskDto, TaskDto, UpdateTaskDto } from './dto'; +import { TaskService } from './task.service'; + +@ApiTags('crm/tasks') +@Controller('/crm/tasks') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class TaskController { + constructor(private readonly service: TaskService) {} + + @ApiCreatedResponse({ description: 'Create task', type: TaskDto }) + @Post() + public async create(@CurrentAuth() { account, user }: AuthData, @Body() dto: CreateTaskDto): Promise { + return await this.service.createAndGetDto(account, user, dto); + } + + @ApiCreatedResponse({ description: 'Get task', type: TaskDto }) + @Get(':taskId') + public async findOne( + @CurrentAuth() { account, user }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + ): Promise { + return await this.service.findDtoById(account, user, taskId); + } + + @ApiCreatedResponse({ description: 'Update task', type: TaskDto }) + @Patch(':taskId') + public async update( + @CurrentAuth() { account, user }: AuthData, + @Param('taskId', ParseIntPipe) taskId: number, + @Body() dto: UpdateTaskDto, + ): Promise { + return await this.service.updateAndGetDto(account, user, taskId, dto); + } + + @ApiOkResponse({ description: 'Delete task' }) + @Delete(':taskId') + public async delete(@CurrentAuth() { accountId, user }: AuthData, @Param('taskId', ParseIntPipe) taskId: number) { + await this.service.delete({ user, filter: { accountId, taskId } }); + } +} diff --git a/backend/src/CRM/task/task.handler.ts b/backend/src/CRM/task/task.handler.ts new file mode 100644 index 0000000..bc76655 --- /dev/null +++ b/backend/src/CRM/task/task.handler.ts @@ -0,0 +1,83 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; +import { ActionTaskCreateSettings, AutomationJob, EntityTypeActionType, OnAutomationJob } from '@/modules/automation'; + +import { CrmEventType, TaskExtEvent, TaskExtUpsertEvent } from '../common'; +import { TaskService } from './task.service'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; + responsibleUserId?: number | null; +} + +interface CreateTaskVariables { + accountId: number; + entity?: EntityVariables | null; + taskSettings?: ActionTaskCreateSettings | null; +} + +@Injectable() +export class TaskHandler { + private readonly logger = new Logger(TaskHandler.name); + constructor(private readonly service: TaskService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + if (event.newUserId) { + await this.service.changeResponsible({ + accountId: event.accountId, + currentUserId: event.userId, + newUserId: event.newUserId, + }); + } + } + + @OnEvent(CrmEventType.TaskUpsertExt, { async: true }) + public async onTaskUpsertExt(event: TaskExtUpsertEvent) { + await this.service.handleUpsertExt(event); + } + + @OnEvent(CrmEventType.TaskDeleteExt, { async: true }) + public async onTaskDeleteExt(event: TaskExtEvent) { + const { accountId, boardId, taskId, externalId } = event; + if (accountId && boardId && externalId) { + await this.service.delete({ user: null, filter: { accountId, boardId, externalId }, event }); + } + if (accountId && boardId && taskId) { + await this.service.delete({ user: null, filter: { accountId, boardId, taskId }, event }); + } + } + + /** + * @deprecated use new @see handleCreateJob instead + */ + @OnAutomationJob(EntityTypeActionType.CreateTask) + async handleCreateJobOld(job: AutomationJob): Promise<{ variables?: unknown }> { + return this.handleCreateJob(job); + } + @OnAutomationJob(EntityTypeActionType.TaskCreate) + async handleCreateJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.taskSettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, taskSettings } = job.variables; + const task = await this.service.processAutomation({ + accountId, + entityId: entity.id, + entityOwnerId: entity.responsibleUserId, + entityStageId: entity.stageId, + settings: taskSettings, + }); + return { variables: { ...job.variables, task: task?.toSimpleDto() } }; + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + return { variables: job.variables }; + } + } +} diff --git a/backend/src/CRM/task/task.service.ts b/backend/src/CRM/task/task.service.ts new file mode 100644 index 0000000..b7d2ab0 --- /dev/null +++ b/backend/src/CRM/task/task.service.ts @@ -0,0 +1,584 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Repository } from 'typeorm'; +import { convert } from 'html-to-text'; + +import { DateUtil, FileLinkSource, NotFoundError, ServiceEvent } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { ActionHelper, ActionTaskCreateSettings } from '@/modules/automation'; +import { EntityInfoService } from '@/modules/entity/entity-info'; +import { NotificationType } from '@/modules/notification/notification/enums/notification-type.enum'; +import { CreateNotificationDto } from '@/modules/notification/notification/dto/create-notification.dto'; + +import { CrmEventType, TaskCreatedEvent, TaskEvent, TaskExtUpsertEvent, TaskUpdatedEvent } from '../common'; +import { BaseTaskService } from '../base-task'; +import { BoardService } from '../board/board.service'; +import { BoardType } from '../board/enums'; +import { BoardStageCode, BoardStageService } from '../board-stage'; +import { TaskSubtaskService } from '../task-subtask/task-subtask.service'; + +import { EntityService } from '../Service/Entity/EntityService'; +import { FileLinkService } from '../Service/FileLink/FileLinkService'; + +import { CreateTaskDto, TaskDto, UpdateTaskDto } from './dto'; +import { Task } from './entities'; + +const BatchProcessLimit = 100; + +interface FindFilter { + accountId: number; + boardId?: number; + taskId?: number | number[]; + entityId?: number; + isResolved?: boolean; + responsibles?: number | number[]; + externalId?: string; +} + +@Injectable() +export class TaskService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Task) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly userService: UserService, + @Inject(forwardRef(() => EntityService)) + private readonly entityService: EntityService, + private readonly fileLinkService: FileLinkService, + private readonly subtaskService: TaskSubtaskService, + private readonly baseTaskService: BaseTaskService, + @Inject(forwardRef(() => BoardStageService)) + private readonly stageService: BoardStageService, + @Inject(forwardRef(() => BoardService)) + private readonly boardService: BoardService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async create({ + accountId, + user, + dto, + skipPermissionCheck, + event, + }: { + accountId: number; + user: User; + dto: CreateTaskDto; + skipPermissionCheck?: boolean; + event?: ServiceEvent; + }): Promise { + if (!skipPermissionCheck) { + await this.authService.check({ + action: 'create', + user, + authorizable: Task.getAuthorizable(), + throwError: true, + }); + } + + if (!dto.boardId) { + dto.boardId = await this.boardService.findOneId({ accountId, isSystem: true, type: BoardType.Task }); + } + + if (dto.isResolved || !dto.stageId) { + dto.stageId = await this.stageService.findOneId({ + accountId, + boardId: dto.boardId, + includeCodes: dto.isResolved ? [BoardStageCode.Done] : undefined, + }); + } + + dto.weight = dto.weight ?? (await this.baseTaskService.calculateWeight(accountId, dto.afterId, dto.beforeId)); + const task = await this.repository.save(Task.create(accountId, user.id, dto)); + + if (dto.fileIds) { + await this.fileLinkService.processFiles(accountId, FileLinkSource.TASK, task.id, dto.fileIds); + } + + if (dto.subtasks) { + await this.subtaskService.createMany(accountId, task.id, dto.subtasks); + } + + this.eventEmitter.emit( + CrmEventType.TaskCreated, + new TaskCreatedEvent({ + source: Task.name, + accountId, + taskId: task.id, + boardId: task.boardId, + externalId: task.externalId, + ownerId: task.responsibleUserId, + entityId: task.entityId ?? null, + createdBy: task.createdBy, + taskTitle: task.title, + taskText: task.text, + createdAt: task.createdAt, + startDate: task.startDate, + endDate: task.endDate, + prevEvent: event, + }), + ); + + return task; + } + + public async createAndGetDto(account: Account, user: User, dto: CreateTaskDto): Promise { + const task = await this.create({ accountId: account.id, user, dto }); + + return await this.createDtoForTask(account, user, task); + } + + public async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + public async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).orderBy('task.id', 'DESC').getMany(); + } + + public async findDtoById(account: Account, user: User, id: number): Promise { + const task = await this.findOne({ accountId: account.id, taskId: id }); + return task ? await this.createDtoForTask(account, user, task) : null; + } + + public async update({ + accountId, + user, + taskId, + dto, + skipPermissionCheck, + event, + }: { + accountId: number; + user: User | null; + taskId: number; + dto: UpdateTaskDto; + skipPermissionCheck?: boolean; + event?: ServiceEvent; + }): Promise { + const task = await this.repository.findOneBy({ id: taskId }); + if (!task) { + throw NotFoundError.withId(Task, taskId); + } + return this.updateTask({ accountId, user, task, dto, skipPermissionCheck, event }); + } + + private async updateTask({ + accountId, + user, + task, + dto, + skipPermissionCheck, + event, + }: { + accountId: number; + user: User | null; + task: Task; + dto: UpdateTaskDto; + skipPermissionCheck?: boolean; + event?: ServiceEvent; + }): Promise { + if (!skipPermissionCheck && user) { + await this.authService.check({ action: 'edit', user, authorizable: task, throwError: true }); + } + + if (!task.hasChanges(dto) && !dto.sorting && !dto.fileIds && !dto.subtasks) { + return task; + } + + if (dto.fileIds !== undefined) { + await this.fileLinkService.processFiles(accountId, FileLinkSource.TASK, task.id, dto.fileIds ?? []); + } + + if (dto.subtasks !== undefined) { + await this.subtaskService.processBatch(accountId, task.id, dto.subtasks); + } + + const { entityId } = task; + + if (dto.sorting) { + task.weight = await this.baseTaskService.calculateWeight(accountId, dto.sorting.afterId, dto.sorting.beforeId); + } + + if (dto.entityId !== undefined && dto.entityId !== task.entityId) { + const taskBoardId = dto.entityId ? await this.getProjectTaskBoardId(accountId, dto.entityId) : undefined; + if (taskBoardId && taskBoardId !== task.boardId) { + dto.boardId = taskBoardId; + dto.stageId = await this.stageService.findOneId({ + accountId, + boardId: dto.boardId ?? task.boardId, + includeCodes: (dto.isResolved ?? task.isResolved) ? [BoardStageCode.Done] : undefined, + }); + } else if (!taskBoardId && task.boardId) { + dto.boardId = await this.boardService.findOneId({ accountId, isSystem: true, type: BoardType.Task }); + dto.stageId = await this.stageService.findOneId({ + accountId, + boardId: dto.boardId ?? task.boardId, + includeCodes: (dto.isResolved ?? task.isResolved) ? [BoardStageCode.Done] : undefined, + }); + } + } else if (dto.isResolved !== undefined && !dto.stageId && dto.isResolved !== task.isResolved) { + dto.stageId = await this.stageService.findOneId({ + accountId, + boardId: dto.boardId ?? task.boardId, + includeCodes: dto.isResolved ? [BoardStageCode.Done] : undefined, + }); + } else if (dto.isResolved === undefined) { + const stageChanged = dto.stageId && dto.stageId !== task.stageId; + const boardChanged = dto.boardId && dto.boardId !== task.boardId; + if (stageChanged || boardChanged) { + const stage = await this.stageService.findOne({ + accountId, + stageId: stageChanged ? dto.stageId : undefined, + boardId: boardChanged ? dto.boardId : undefined, + includeCodes: !stageChanged && boardChanged && task.isResolved ? [BoardStageCode.Done] : undefined, + }); + dto.isResolved = stage?.code === BoardStageCode.Done; + dto.stageId = stage?.id; + dto.boardId = stage?.boardId; + } + } + + await this.repository.save(task.update(dto)); + + this.eventEmitter.emit( + CrmEventType.TaskUpdated, + new TaskUpdatedEvent({ + source: Task.name, + accountId, + taskId: task.id, + boardId: task.boardId, + externalId: task.externalId, + ownerId: task.responsibleUserId, + entityId: task.entityId ?? null, + createdBy: task.createdBy, + taskTitle: task.title, + taskText: task.text, + createdAt: task.createdAt, + startDate: task.startDate, + endDate: task.endDate, + prevEntityId: entityId, + prevEvent: event, + }), + ); + + return task; + } + + public async updateAndGetDto(account: Account, user: User, taskId: number, dto: UpdateTaskDto): Promise { + const task = await this.update({ accountId: account.id, user, taskId, dto }); + + return await this.createDtoForTask(account, user, task); + } + + private async getProjectTaskBoardId(accountId: number, entityId: number): Promise { + const entity = await this.entityService.findOne(accountId, { entityId }); + if (entity?.boardId) { + const board = await this.boardService.findOne({ filter: { accountId, boardId: entity.boardId } }); + + return board?.taskBoardId; + } + + return undefined; + } + + public async delete({ user, filter, event }: { user: User | null; filter: FindFilter; event?: ServiceEvent | null }) { + const tasks = await this.findMany(filter); + await Promise.all( + tasks.map(async (task) => { + if (user) { + await this.authService.check({ action: 'delete', user, authorizable: task, throwError: true }); + } + + await this.fileLinkService.processFiles(task.accountId, FileLinkSource.TASK, task.id, []); + await this.repository.delete({ id: task.id }); + this.eventEmitter.emit( + CrmEventType.TaskDeleted, + new TaskEvent({ + source: Task.name, + accountId: task.accountId, + taskId: task.id, + boardId: task.boardId, + externalId: task.externalId, + entityId: task.entityId, + prevEvent: event, + }), + ); + }), + ); + } + + public async changeResponsible({ + accountId, + currentUserId, + newUserId, + }: { + accountId: number; + currentUserId: number; + newUserId: number; + }) { + await this.repository.update({ accountId, responsibleUserId: currentUserId }, { responsibleUserId: newUserId }); + } + + public async changeStageForAll({ + accountId, + boardId, + stageId, + newStageId, + }: { + accountId: number; + boardId: number; + stageId: number; + newStageId: number; + }) { + const qb = this.repository + .createQueryBuilder('task') + .select('task.id', 'id') + .where('task.account_id = :accountId', { accountId }) + .andWhere('task.board_id = :boardId', { boardId }) + .andWhere('task.stage_id = :stageId', { stageId }) + .limit(BatchProcessLimit); + let tasks: { id: number }[] = []; + do { + tasks = await qb.getRawMany<{ id: number }>(); + for (const task of tasks) { + await this.update({ + accountId, + user: null, + taskId: task.id, + dto: { boardId: boardId, stageId: newStageId }, + skipPermissionCheck: true, + }); + } + } while (tasks.length); + } + + public async handleUpsertExt(event: TaskExtUpsertEvent): Promise { + const user = await this.userService.findOne({ accountId: event.accountId, id: event.ownerId }); + if (user) { + const { accountId, boardId, taskId, externalId } = event; + let task = event.externalId ? await this.findOne({ accountId, boardId, externalId }) : undefined; + if (!task && event.taskId) { + task = await this.findOne({ accountId, boardId, taskId }); + } + + if (task) { + return this.updateTask({ + accountId, + user, + task, + dto: { + title: event.title, + text: event.text, + startDate: event.startDate.toISOString(), + endDate: event.endDate.toISOString(), + externalId, + }, + skipPermissionCheck: true, + event, + }); + } else { + return this.create({ + accountId, + user, + dto: { + boardId, + responsibleUserId: event.ownerId, + title: event.title, + text: event.text, + startDate: event.startDate.toISOString(), + endDate: event.endDate.toISOString(), + externalId, + }, + skipPermissionCheck: true, + event, + }); + } + } + return null; + } + + public async processAutomation({ + accountId, + entityId, + entityOwnerId, + entityStageId, + settings, + }: { + accountId: number; + entityId: number; + entityOwnerId: number; + entityStageId: number | null | undefined; + settings: ActionTaskCreateSettings; + }): Promise { + const entity = await this.entityInfoService.findOne({ accountId, entityId }); + if (entity && (!entity.stageId || settings.allowAnyStage || entity.stageId === entityStageId)) { + const user = await this.userService.findOne({ accountId, id: entityOwnerId }); + const ownerId = settings.responsibleUserId ?? entityOwnerId; + const startDate = DateUtil.add(DateUtil.now(), { seconds: settings.deferStart ?? 0 }); + const endDate = ActionHelper.getEndDate({ + startDate, + deadlineType: settings.deadlineType, + deadlineTime: settings.deadlineTime, + }); + return this.create({ + accountId, + user, + dto: { + responsibleUserId: ownerId, + startDate: startDate.toISOString(), + endDate: endDate.toISOString(), + title: settings.title, + text: settings.text, + entityId: entityId, + boardId: null, + stageId: null, + settingsId: null, + plannedTime: null, + }, + skipPermissionCheck: true, + }); + } + return null; + } + + public async getOverdueNotifications(from: Date, to: Date): Promise { + const tasks = await this.repository + .createQueryBuilder('task') + .where('task.is_resolved = false') + .andWhere('task.end_date > :from', { from }) + .andWhere('task.end_date <= :to', { to }) + .getMany(); + return tasks.map( + (task) => + new CreateNotificationDto( + task.accountId, + task.responsibleUserId, + NotificationType.TASK_OVERDUE, + task.id, + task.entityId, + task.createdBy, + task.title, + convert(task.text), + ), + ); + } + + public async getOverdueForFollowNotifications( + notifyUserId: number, + from: Date, + to: Date, + followUserIds: number[], + ): Promise { + const tasks = await this.repository + .createQueryBuilder('task') + .where('task.is_resolved = false') + .andWhere('task.end_date > :from', { from }) + .andWhere('task.end_date <= :to', { to }) + .andWhere('task.responsible_user_id in (:...userIds)', { userIds: followUserIds }) + .getMany(); + return tasks.map( + (task) => + new CreateNotificationDto( + task.accountId, + notifyUserId, + NotificationType.TASK_OVERDUE_EMPLOYEE, + task.id, + task.entityId, + task.responsibleUserId, + task.title, + convert(task.text), + ), + ); + } + + public async getBeforeStartNotifications(userId: number, from: Date, to: Date) { + const tasks = await this.repository + .createQueryBuilder('task') + .where('task.is_resolved = false') + .andWhere('task.responsible_user_id = :userId', { userId }) + .andWhere('task.start_date > :from', { from }) + .andWhere('task.start_date <= :to', { to }) + .getMany(); + return tasks.map( + (task) => + new CreateNotificationDto( + task.accountId, + task.responsibleUserId, + NotificationType.TASK_BEFORE_START, + task.id, + task.entityId, + task.createdBy, + task.title, + convert(task.text), + ), + ); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('task') + .where('task.accountId = :accountId', { accountId: filter.accountId }); + if (filter.boardId) { + qb.andWhere('task.board_id = :boardId', { boardId: filter.boardId }); + } + if (filter.taskId) { + if (Array.isArray(filter.taskId)) { + qb.andWhere('task.id IN (:...taskIds)', { taskIds: filter.taskId }); + } else { + qb.andWhere('task.id = :taskId', { taskId: filter.taskId }); + } + } + if (filter.entityId) { + qb.andWhere('task.entity_id = :entityId', { entityId: filter.entityId }); + } + if (filter.isResolved !== undefined) { + qb.andWhere('task.is_resolved = :isResolved', { isResolved: filter.isResolved }); + } + if (filter.responsibles) { + if (Array.isArray(filter.responsibles)) { + if (filter.responsibles.length === 0) { + return qb.where('1 = 0'); + } + qb.andWhere('task.responsible_user_id IN (:...responsibles)', { responsibles: filter.responsibles }); + } else { + qb.andWhere('task.responsible_user_id = :responsible', { responsible: filter.responsibles }); + } + } + if (filter.externalId) { + qb.andWhere('task.external_id = :externalId', { externalId: filter.externalId }); + } + + return qb; + } + + private async createDtoForTask(account: Account, user: User, task: Task): Promise { + if (!(await this.authService.check({ action: 'view', user, authorizable: task }))) { + return null; + } + + const fileLinks = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.TASK, task.id); + const subtasks = await this.subtaskService.findMany(task.accountId, { taskId: task.id }); + const entityInfo = task.entityId + ? await this.entityInfoService.findOne({ + accountId: account.id, + user, + entityId: task.entityId, + }) + : null; + const userRights = await this.authService.getUserRights({ user, authorizable: task }); + + return new TaskDto( + task, + entityInfo, + fileLinks, + subtasks.map((s) => s.toDto()), + userRights, + ); + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/CreateContactAndLeadController.ts b/backend/src/Mailing/Controller/MailMessage/CreateContactAndLeadController.ts new file mode 100644 index 0000000..4cf2c52 --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/CreateContactAndLeadController.ts @@ -0,0 +1,28 @@ +import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; +import { CreateContactLeadDto } from '../../Service/MailMessage/Dto/CreateContactLeadDto'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class CreateContactAndLeadController { + constructor(private mailMessageService: MailMessageService) {} + + @Post('/mailing/mailboxes/:mailboxId/messages/:messageId/contact') + @ApiOkResponse({ description: 'Created contact and lead', type: EntityInfoDto }) + async createContactAndLead( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + @Body() dto: CreateContactLeadDto, + ): Promise { + return await this.mailMessageService.createContact(accountId, user, mailboxId, messageId, dto); + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetAttachmentController.ts b/backend/src/Mailing/Controller/MailMessage/GetAttachmentController.ts new file mode 100644 index 0000000..c3c7612 --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetAttachmentController.ts @@ -0,0 +1,44 @@ +import { Controller, Get, HttpStatus, Param, ParseIntPipe, Res, StreamableFile } from '@nestjs/common'; +import { Response } from 'express'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized() +export class GetAttachmentController { + constructor(private mailMessageService: MailMessageService) {} + + @Get('/mailing/mailboxes/:mailboxId/messages/:messageId/attachments/:payloadId') + @ApiOkResponse({ description: 'Get attachment for mail message', type: StreamableFile }) + async getAttachment( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + @Param('payloadId', ParseIntPipe) payloadId: number, + @Res({ passthrough: true }) res: Response, + ): Promise { + const attachment = await this.mailMessageService.getMessageAttachment( + accountId, + userId, + mailboxId, + messageId, + payloadId, + ); + if (attachment) { + res.set({ + 'Content-Type': attachment.mimeType, + 'Content-Disposition': `attachment; filename="${encodeURI(attachment.filename)}"`, + }); + return new StreamableFile(attachment.content); + } else { + res.sendStatus(HttpStatus.NOT_FOUND); + return null; + } + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetMailMessageController.ts b/backend/src/Mailing/Controller/MailMessage/GetMailMessageController.ts new file mode 100644 index 0000000..a5012e3 --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetMailMessageController.ts @@ -0,0 +1,26 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; +import { MailMessageDto } from '../../Service/MailMessage/Dto/MailMessageDto'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class GetMailMessageController { + constructor(private mailMessageService: MailMessageService) {} + + @Get('/mailing/mailboxes/:mailboxId/messages/:messageId') + @ApiOkResponse({ description: 'Mail', type: MailMessageDto }) + async getMessage( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ): Promise { + return await this.mailMessageService.getMessageWithPayload(accountId, user, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetMailThreadController.ts b/backend/src/Mailing/Controller/MailMessage/GetMailThreadController.ts new file mode 100644 index 0000000..4f0e220 --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetMailThreadController.ts @@ -0,0 +1,26 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; +import { MailMessageDto } from '../../Service/MailMessage/Dto/MailMessageDto'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class GetMailThreadController { + constructor(private mailMessageService: MailMessageService) {} + + @Get('/mailing/mailboxes/:mailboxId/threads/:messageId') + @ApiOkResponse({ description: 'Mail thread', type: [MailMessageDto] }) + async getThread( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ): Promise { + return await this.mailMessageService.getThreadWithPayload(accountId, user, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesController.ts b/backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesController.ts new file mode 100644 index 0000000..297c812 --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesController.ts @@ -0,0 +1,27 @@ +import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; +import { MailThreadResult } from '../../Service/MailMessage/MailThreadResult'; +import { GetMailboxMessagesFilter } from './GetMailboxMessagesFilter'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class GetMailboxMessagesController { + constructor(private mailMessageService: MailMessageService) {} + + @Get('/mailing/mailboxes/:mailboxId/messages') + @ApiOkResponse({ description: 'Mail thread list', type: MailThreadResult }) + async getMessages( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Query() filter: GetMailboxMessagesFilter, + ): Promise { + return await this.mailMessageService.getMessagesForMailbox(accountId, user, mailboxId, filter); + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesFilter.ts b/backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesFilter.ts new file mode 100644 index 0000000..c72df5b --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetMailboxMessagesFilter.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { PagingQuery } from '@/common'; + +export class GetMailboxMessagesFilter extends PagingQuery { + @ApiProperty() + @IsOptional() + @IsNumber() + folderId?: number; + + @ApiProperty() + @IsOptional() + @IsString() + search?: string; +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetSectionMessagesController.ts b/backend/src/Mailing/Controller/MailMessage/GetSectionMessagesController.ts new file mode 100644 index 0000000..9a5b99c --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetSectionMessagesController.ts @@ -0,0 +1,28 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxFolderType } from '../../common'; +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; +import { MailThreadResult } from '../../Service/MailMessage/MailThreadResult'; +import { GetSectionMessagesFilter } from './GetSectionMessagesFilter'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized({ prefetch: { user: true } }) +export class GetSectionMessagesController { + constructor(private mailMessageService: MailMessageService) {} + + @Get('/mailing/section/:type/messages') + @ApiOkResponse({ description: 'Mail list', type: MailThreadResult }) + async getMessages( + @CurrentAuth() { accountId, user }: AuthData, + @Param('type') type: MailboxFolderType, + @Query() filter: GetSectionMessagesFilter, + ): Promise { + return await this.mailMessageService.getMessagesForSection(accountId, user, type, filter); + } +} diff --git a/backend/src/Mailing/Controller/MailMessage/GetSectionMessagesFilter.ts b/backend/src/Mailing/Controller/MailMessage/GetSectionMessagesFilter.ts new file mode 100644 index 0000000..d31e1bb --- /dev/null +++ b/backend/src/Mailing/Controller/MailMessage/GetSectionMessagesFilter.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { PagingQuery } from '@/common'; + +export class GetSectionMessagesFilter extends PagingQuery { + @ApiProperty() + @IsOptional() + @IsNumber() + mailboxId?: number; + + @ApiProperty() + @IsOptional() + @IsString() + search?: string; +} diff --git a/backend/src/Mailing/Controller/Mailbox/GetMailboxesInfoController.ts b/backend/src/Mailing/Controller/Mailbox/GetMailboxesInfoController.ts new file mode 100644 index 0000000..41b0614 --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/GetMailboxesInfoController.ts @@ -0,0 +1,22 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; +import { MailboxesInfoDto } from '../../Service/Mailbox/Dto/mailboxes-info.dto'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class GetMailboxesInfoController { + constructor(private mailboxService: MailboxService) {} + + @ApiOkResponse({ description: 'Mailboxes', type: MailboxesInfoDto }) + @Get('mailing/mailboxes') + public async getMailboxes(@CurrentAuth() { accountId, userId }: AuthData): Promise { + return await this.mailboxService.getMailboxesForInfo(accountId, userId); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/SeenMailThreadController.ts b/backend/src/Mailing/Controller/Mailbox/SeenMailThreadController.ts new file mode 100644 index 0000000..88005f8 --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/SeenMailThreadController.ts @@ -0,0 +1,24 @@ +import { Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class SeenMailThreadController { + constructor(private mailboxService: MailboxService) {} + + @Put('/mailing/mailboxes/:mailboxId/threads/:messageId/seen') + public async markSeenMailThread( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.mailboxService.markSeenThread(accountId, userId, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/SendMailMessageController.ts b/backend/src/Mailing/Controller/Mailbox/SendMailMessageController.ts new file mode 100644 index 0000000..c3ef807 --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/SendMailMessageController.ts @@ -0,0 +1,32 @@ +import { Body, Controller, Param, ParseIntPipe, Post, UploadedFiles, UseInterceptors } from '@nestjs/common'; +import { FilesInterceptor } from '@nestjs/platform-express'; +import { memoryStorage } from 'multer'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { SendMailMessageDto } from '../../common'; +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/messages') +@Controller() +@JwtAuthorized() +export class SendMailMessageController { + constructor(private mailboxService: MailboxService) {} + + @Post('/mailing/mailboxes/:mailboxId/send') + @UseInterceptors(FilesInterceptor('attachment', 100, { storage: memoryStorage() })) + async sendMessage( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Body('message') message: string, + @UploadedFiles() files: Express.Multer.File[], + ): Promise { + const dto = JSON.parse(message) as SendMailMessageDto; + const attachments = files ? files.map((file) => StorageFile.fromMulter(file)) : []; + return await this.mailboxService.sendMessage(accountId, userId, mailboxId, dto, attachments); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/SpamMailThreadController.ts b/backend/src/Mailing/Controller/Mailbox/SpamMailThreadController.ts new file mode 100644 index 0000000..33b28ad --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/SpamMailThreadController.ts @@ -0,0 +1,24 @@ +import { Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class SpamMailThreadController { + constructor(private mailboxService: MailboxService) {} + + @Put('/mailing/mailboxes/:mailboxId/threads/:messageId/spam') + public async spamMailThread( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.mailboxService.spamThread(accountId, userId, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/TrashMailThreadController.ts b/backend/src/Mailing/Controller/Mailbox/TrashMailThreadController.ts new file mode 100644 index 0000000..0303b68 --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/TrashMailThreadController.ts @@ -0,0 +1,24 @@ +import { Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class TrashMailThreadController { + constructor(private mailboxService: MailboxService) {} + + @Put('/mailing/mailboxes/:mailboxId/threads/:messageId/trash') + public async trashMailThread( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.mailboxService.trashThread(accountId, userId, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/UnseenMailThreadController.ts b/backend/src/Mailing/Controller/Mailbox/UnseenMailThreadController.ts new file mode 100644 index 0000000..d11dd1c --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/UnseenMailThreadController.ts @@ -0,0 +1,24 @@ +import { Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class UnseenMailThreadController { + constructor(private mailboxService: MailboxService) {} + + @Put('/mailing/mailboxes/:mailboxId/threads/:messageId/unseen') + public async markUnseenMailThread( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.mailboxService.markUnseenThread(accountId, userId, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/UnspamMailThreadController.ts b/backend/src/Mailing/Controller/Mailbox/UnspamMailThreadController.ts new file mode 100644 index 0000000..74a779d --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/UnspamMailThreadController.ts @@ -0,0 +1,24 @@ +import { Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class UnspamMailThreadController { + constructor(private mailboxService: MailboxService) {} + + @Put('/mailing/mailboxes/:mailboxId/threads/:messageId/unspam') + public async unspamMailThread( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.mailboxService.unspamThread(accountId, userId, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/Mailbox/UntrashMailThreadController.ts b/backend/src/Mailing/Controller/Mailbox/UntrashMailThreadController.ts new file mode 100644 index 0000000..1c72bff --- /dev/null +++ b/backend/src/Mailing/Controller/Mailbox/UntrashMailThreadController.ts @@ -0,0 +1,24 @@ +import { Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxService } from '../../Service/Mailbox/MailboxService'; + +@ApiTags('mailing/mailbox') +@Controller() +@JwtAuthorized() +export class UntrashMailThreadController { + constructor(private mailboxService: MailboxService) {} + + @Put('/mailing/mailboxes/:mailboxId/threads/:messageId/untrash') + async untrashMailThread( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.mailboxService.untrashThread(accountId, userId, mailboxId, messageId); + } +} diff --git a/backend/src/Mailing/Controller/MailboxGmail/GmailAuthCallbackController.ts b/backend/src/Mailing/Controller/MailboxGmail/GmailAuthCallbackController.ts new file mode 100644 index 0000000..760f917 --- /dev/null +++ b/backend/src/Mailing/Controller/MailboxGmail/GmailAuthCallbackController.ts @@ -0,0 +1,18 @@ +import { Controller, Get, Query, Redirect } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { MailboxGmailService } from '../../Service/MailboxGmail/MailboxGmailService'; + +@ApiExcludeController(true) +@Controller() +export class GmailAuthCallbackController { + constructor(private gmailService: MailboxGmailService) {} + + @Redirect() + @Get('/mailing/settings/mailboxes/gmail/callback') + public async callback(@Query('code') code: string, @Query('state') state: string) { + const redirectUrl = await this.gmailService.processAuthCode({ code, state }); + + return { url: redirectUrl, statusCode: 302 }; + } +} diff --git a/backend/src/Mailing/Controller/MailboxGmail/GmailAuthConnectController.ts b/backend/src/Mailing/Controller/MailboxGmail/GmailAuthConnectController.ts new file mode 100644 index 0000000..824e266 --- /dev/null +++ b/backend/src/Mailing/Controller/MailboxGmail/GmailAuthConnectController.ts @@ -0,0 +1,22 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { MailboxGmailService } from '../../Service/MailboxGmail/MailboxGmailService'; + +@ApiTags('mailing/settings/mailbox') +@Controller() +@JwtAuthorized() +export class GmailAuthConnectController { + constructor(private readonly gmailService: MailboxGmailService) {} + + @Get('/mailing/settings/mailboxes/gmail/connect/:mailboxId') + @ApiOkResponse({ description: 'Gmail connection url', type: String }) + async connect( + @CurrentAuth() { accountId }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + ): Promise { + return await this.gmailService.getAuthorizeUrl({ accountId, mailboxId }); + } +} diff --git a/backend/src/Mailing/Controller/MailboxManual/GetMailboxSettingsManualController.ts b/backend/src/Mailing/Controller/MailboxManual/GetMailboxSettingsManualController.ts new file mode 100644 index 0000000..3565a15 --- /dev/null +++ b/backend/src/Mailing/Controller/MailboxManual/GetMailboxSettingsManualController.ts @@ -0,0 +1,25 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxManualService } from '../../Service/MailboxManual/MailboxManualService'; +import { MailboxSettingsManualDto } from '../../Service/MailboxManual/Dto/MailboxSettingsManualDto'; + +@ApiTags('mailing/settings/mailbox') +@Controller() +@JwtAuthorized() +export class GetMailboxSettingsManualController { + constructor(private mailboxManualService: MailboxManualService) {} + + @ApiOkResponse({ description: 'Mailbox manual settings', type: MailboxSettingsManualDto }) + @Get('/mailing/settings/mailboxes/:id/manual') + public async getMailboxSettings( + @CurrentAuth() { accountId }: AuthData, + @Param('id', ParseIntPipe) id: number, + ): Promise { + return await this.mailboxManualService.getManualSettings(accountId, id); + } +} diff --git a/backend/src/Mailing/Controller/MailboxManual/UpdateMailboxSettingsManualController.ts b/backend/src/Mailing/Controller/MailboxManual/UpdateMailboxSettingsManualController.ts new file mode 100644 index 0000000..9100a2f --- /dev/null +++ b/backend/src/Mailing/Controller/MailboxManual/UpdateMailboxSettingsManualController.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MailboxManualService } from '../../Service/MailboxManual/MailboxManualService'; +import { MailboxSettingsManualDto } from '../../Service/MailboxManual/Dto/MailboxSettingsManualDto'; +import { UpdateMailboxSettingsManualDto } from '../../Service/MailboxManual/Dto/UpdateMailboxSettingsManualDto'; + +@ApiTags('mailing/settings/mailbox') +@Controller() +@JwtAuthorized() +export class UpdateMailboxSettingsManualController { + constructor(private mailboxManualService: MailboxManualService) {} + + @Post('mailing/settings/mailboxes/:id/manual') + @ApiCreatedResponse({ description: 'Mailbox', type: MailboxSettingsManualDto }) + public async updateMailboxSettings( + @CurrentAuth() { accountId }: AuthData, + @Param('id', ParseIntPipe) id: number, + @Body() dto: UpdateMailboxSettingsManualDto, + ) { + return await this.mailboxManualService.saveManualSettings(accountId, id, dto); + } +} diff --git a/backend/src/Mailing/MailingModule.ts b/backend/src/Mailing/MailingModule.ts new file mode 100644 index 0000000..199f8d5 --- /dev/null +++ b/backend/src/Mailing/MailingModule.ts @@ -0,0 +1,170 @@ +/* eslint-disable max-len */ +import { forwardRef, Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { DiscoveryModule } from '@nestjs/core'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { MailerModule } from '@nestjs-modules/mailer'; +import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; +import { join } from 'path'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import mailConfig, { MailConfig } from './config/mail.config'; + +import { MailMessageBuilderService } from './mail-message-builder'; +import { MailMessagePayload, MailMessagePayloadService } from './mail-message-payload'; +import { Mailbox, MailboxAccessibleUser, MailboxEntitySettings } from './mailbox/entities'; +import { + MailboxAccessibleUserService, + MailboxEntitySettingsService, + MailboxHandler, + MailboxLockService, + MailboxService as MailboxSettingsService, +} from './mailbox/services'; +import { MailboxController } from './mailbox/mailbox.controller'; +import { MailboxFolder, MailboxFolderService } from './mailbox-folder'; +import { + MailboxSignature, + MailboxSignatureController, + MailboxSignatureMailbox, + MailboxSignatureService, +} from './mailbox-signature'; +import { MailProviderRegistry } from './mail-provider'; +import { UnsubscribeController } from './subscription'; +import { PublicSystemMailingController, SystemMailingController, SystemMailingService } from './system-mailing'; + +import { MailboxSettingsManual } from './Model/MailboxManual/MailboxSettingsManual'; +import { MailboxSettingsGmail } from './Model/MailboxGmail/MailboxSettingsGmail'; +import { MailMessage } from './Model/MailMessage/MailMessage'; +import { MailMessageFolder } from './Model/MailMessage/MailMessageFolder'; + +import { MailboxService } from './Service/Mailbox/MailboxService'; +import { MailboxManualService } from './Service/MailboxManual/MailboxManualService'; +import { MailboxGmailService } from './Service/MailboxGmail/MailboxGmailService'; +import { MailMessageService } from './Service/MailMessage/MailMessageService'; + +import { GetMailboxSettingsManualController } from './Controller/MailboxManual/GetMailboxSettingsManualController'; +import { UpdateMailboxSettingsManualController } from './Controller/MailboxManual/UpdateMailboxSettingsManualController'; +import { GmailAuthConnectController } from './Controller/MailboxGmail/GmailAuthConnectController'; +import { GmailAuthCallbackController } from './Controller/MailboxGmail/GmailAuthCallbackController'; +import { GetAttachmentController } from './Controller/MailMessage/GetAttachmentController'; +import { GetMailboxesInfoController } from './Controller/Mailbox/GetMailboxesInfoController'; +import { GetSectionMessagesController } from './Controller/MailMessage/GetSectionMessagesController'; +import { GetMailboxMessagesController } from './Controller/MailMessage/GetMailboxMessagesController'; +import { GetMailMessageController } from './Controller/MailMessage/GetMailMessageController'; +import { GetMailThreadController } from './Controller/MailMessage/GetMailThreadController'; +import { SendMailMessageController } from './Controller/Mailbox/SendMailMessageController'; +import { TrashMailThreadController } from './Controller/Mailbox/TrashMailThreadController'; +import { UntrashMailThreadController } from './Controller/Mailbox/UntrashMailThreadController'; +import { SpamMailThreadController } from './Controller/Mailbox/SpamMailThreadController'; +import { UnspamMailThreadController } from './Controller/Mailbox/UnspamMailThreadController'; +import { SeenMailThreadController } from './Controller/Mailbox/SeenMailThreadController'; +import { UnseenMailThreadController } from './Controller/Mailbox/UnseenMailThreadController'; +import { CreateContactAndLeadController } from './Controller/MailMessage/CreateContactAndLeadController'; + +@Module({ + imports: [ + ConfigModule.forFeature(mailConfig), + DiscoveryModule, + MailerModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async (configService: ConfigService) => { + const config = configService.get('mail'); + return { + transport: { + host: config.mailing.host, + port: config.mailing.port, + secure: config.mailing.secure, + auth: { + user: config.mailing.user, + pass: config.mailing.password, + }, + }, + defaults: { + from: config.mailing.from, + replyTo: config.mailing.replyTo, + }, + template: { + dir: join(__dirname, './system-mailing/templates'), + adapter: new HandlebarsAdapter(), + options: { + strict: true, + }, + }, + }; + }, + }), + TypeOrmModule.forFeature([ + Mailbox, + MailboxAccessibleUser, + MailboxEntitySettings, + MailboxSettingsManual, + MailboxSettingsGmail, + MailboxFolder, + MailMessage, + MailMessagePayload, + MailMessageFolder, + MailboxSignature, + MailboxSignatureMailbox, + ]), + IAMModule, + StorageModule, + forwardRef(() => CrmModule), + EntityInfoModule, + ], + providers: [ + MailboxSettingsService, + MailboxHandler, + MailboxLockService, + MailboxAccessibleUserService, + MailboxEntitySettingsService, + SystemMailingService, + MailboxService, + MailboxManualService, + MailboxGmailService, + MailboxFolderService, + MailMessageService, + MailMessagePayloadService, + MailMessageBuilderService, + MailboxSignatureService, + MailProviderRegistry, + ], + controllers: [ + MailboxController, + SystemMailingController, + PublicSystemMailingController, + GetMailboxSettingsManualController, + UpdateMailboxSettingsManualController, + GmailAuthConnectController, + GmailAuthCallbackController, + GetAttachmentController, + GetMailboxesInfoController, + GetSectionMessagesController, + GetMailboxMessagesController, + GetMailMessageController, + GetMailThreadController, + SendMailMessageController, + TrashMailThreadController, + UntrashMailThreadController, + SpamMailThreadController, + UnspamMailThreadController, + SeenMailThreadController, + UnseenMailThreadController, + CreateContactAndLeadController, + MailboxSignatureController, + UnsubscribeController, + ], + exports: [ + SystemMailingService, + MailMessageService, + MailboxService, + MailboxSettingsService, + MailboxFolderService, + MailMessageBuilderService, + ], +}) +export class MailingModule {} diff --git a/backend/src/Mailing/Model/MailMessage/MailMessage.ts b/backend/src/Mailing/Model/MailMessage/MailMessage.ts new file mode 100644 index 0000000..47c3070 --- /dev/null +++ b/backend/src/Mailing/Model/MailMessage/MailMessage.ts @@ -0,0 +1,141 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { MailMessageExternal } from '../../common'; + +@Entity() +export class MailMessage { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + mailboxId: number; + + @Column() + externalId: string; + + @Column() + threadId: string; + + @Column({ nullable: true }) + snippet: string | null; + + @Column({ nullable: true }) + sentFrom: string | null; + + @Column({ nullable: true }) + sentTo: string | null; + + @Column({ nullable: true }) + replyTo: string | null; + + @Column({ nullable: true }) + cc: string | null; + + @Column({ nullable: true }) + subject: string | null; + + @Column() + date: Date; + + @Column() + hasAttachment: boolean; + + @Column({ nullable: true }) + messageId: string | null; + + @Column('simple-array', { nullable: true }) + referencesTo: string[] | null; + + @Column({ nullable: true }) + inReplyTo: string | null; + + @Column({ nullable: true }) + entityId: number | null; + + @Column() + isSeen: boolean; + + @Column() + accountId: number; + + constructor( + accountId: number, + mailboxId: number, + externalId: string, + threadId: string, + snippet: string | null, + sentFrom: string | null, + sentTo: string | null, + replyTo: string | null, + cc: string | null, + subject: string | null, + date: Date, + hasAttachment: boolean, + messageId: string | null, + referencesTo: string[] | null, + inReplyTo: string | null, + entityId: number | null, + isSeen: boolean, + ) { + this.accountId = accountId; + this.mailboxId = mailboxId; + this.externalId = externalId; + this.threadId = threadId; + this.snippet = snippet; + this.sentFrom = sentFrom; + this.sentTo = sentTo; + this.replyTo = replyTo; + this.cc = cc; + this.subject = subject; + this.date = date; + this.hasAttachment = hasAttachment; + this.messageId = messageId; + this.referencesTo = referencesTo; + this.inReplyTo = inReplyTo; + this.entityId = entityId; + this.isSeen = isSeen; + } + + public static create(accountId: number, mailboxId: number, entityId: number | null, message: MailMessageExternal) { + return new MailMessage( + accountId, + mailboxId, + message.id, + message.threadId, + message.snippet, + message.sentFrom?.text ?? null, + message.sentTo?.text ?? null, + message.replyTo?.text ?? null, + message.cc?.text ?? null, + message.subject, + message.date, + message.hasAttachment, + message.messageId, + message.references, + message.inReplyTo, + entityId, + message.isSeen, + ); + } + + public update(message: MailMessageExternal): MailMessage { + this.externalId = message.id; + if (message.threadId) { + this.threadId = message.threadId; + } + this.snippet = message.snippet; + this.sentFrom = message.sentFrom?.text ?? null; + this.sentTo = message.sentTo?.text ?? null; + this.replyTo = message.replyTo?.text ?? null; + this.cc = message.cc?.text ?? null; + this.subject = message.subject; + this.date = message.date; + this.hasAttachment = message.hasAttachment; + this.messageId = message.messageId; + this.referencesTo = message.references; + this.inReplyTo = message.inReplyTo; + this.isSeen = message.isSeen; + this.entityId = message.entityId; + return this; + } +} diff --git a/backend/src/Mailing/Model/MailMessage/MailMessageFolder.ts b/backend/src/Mailing/Model/MailMessage/MailMessageFolder.ts new file mode 100644 index 0000000..731d850 --- /dev/null +++ b/backend/src/Mailing/Model/MailMessage/MailMessageFolder.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class MailMessageFolder { + @PrimaryColumn() + messageId: number; + + @PrimaryColumn() + folderId: number; + + constructor(messageId: number, folderId: number) { + this.messageId = messageId; + this.folderId = folderId; + } +} diff --git a/backend/src/Mailing/Model/MailMessage/MailMessageWithFolders.ts b/backend/src/Mailing/Model/MailMessage/MailMessageWithFolders.ts new file mode 100644 index 0000000..6e68c5d --- /dev/null +++ b/backend/src/Mailing/Model/MailMessage/MailMessageWithFolders.ts @@ -0,0 +1,12 @@ +import { MailboxFolder } from '../../mailbox-folder'; +import { type MailMessage } from './MailMessage'; + +export class MailMessageWithFolders { + message: MailMessage; + folders: MailboxFolder[]; + + constructor(message: MailMessage, folders: MailboxFolder[]) { + this.message = message; + this.folders = folders; + } +} diff --git a/backend/src/Mailing/Model/MailboxGmail/MailboxSettingsGmail.ts b/backend/src/Mailing/Model/MailboxGmail/MailboxSettingsGmail.ts new file mode 100644 index 0000000..28d5346 --- /dev/null +++ b/backend/src/Mailing/Model/MailboxGmail/MailboxSettingsGmail.ts @@ -0,0 +1,24 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { Auth } from 'googleapis'; + +@Entity() +export class MailboxSettingsGmail { + @PrimaryColumn() + mailboxId: number; + + @Column({ type: 'jsonb' }) + tokens: Auth.Credentials; + + @Column() + historyId: string | null; + + @Column() + accountId: number; + + constructor(mailboxId: number, accountId: number, tokens: Auth.Credentials, historyId: string | null = null) { + this.mailboxId = mailboxId; + this.accountId = accountId; + this.tokens = tokens; + this.historyId = historyId; + } +} diff --git a/backend/src/Mailing/Model/MailboxManual/MailboxSettingsManual.ts b/backend/src/Mailing/Model/MailboxManual/MailboxSettingsManual.ts new file mode 100644 index 0000000..ef05428 --- /dev/null +++ b/backend/src/Mailing/Model/MailboxManual/MailboxSettingsManual.ts @@ -0,0 +1,93 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ImapSyncInfo } from '../../common'; +import { UpdateMailboxSettingsManualDto } from '../../Service/MailboxManual/Dto/UpdateMailboxSettingsManualDto'; + +@Entity() +export class MailboxSettingsManual { + @PrimaryColumn() + mailboxId: number; + + @Column() + password: string; + + @Column() + imapServer: string; + + @Column() + imapPort: number; + + @Column() + imapSecure: boolean; + + @Column() + smtpServer: string; + + @Column() + smtpPort: number; + + @Column() + smtpSecure: boolean; + + @Column({ type: 'jsonb', nullable: true }) + imapSync: ImapSyncInfo[] | null; + + @Column() + accountId: number; + + constructor( + mailboxId: number, + accountId: number, + password: string, + imapServer: string, + imapPort: number, + imapSecure: boolean, + smtpServer: string, + smtpPort: number, + smtpSecure: boolean, + imapSync: ImapSyncInfo[] | null = null, + ) { + this.mailboxId = mailboxId; + this.accountId = accountId; + this.password = password; + this.imapServer = imapServer; + this.imapPort = imapPort; + this.imapSecure = imapSecure; + this.smtpServer = smtpServer; + this.smtpPort = smtpPort; + this.smtpSecure = smtpSecure; + this.imapSync = imapSync; + } + + public static create(mailboxId: number, accountId: number, dto: UpdateMailboxSettingsManualDto) { + return new MailboxSettingsManual( + mailboxId, + accountId, + dto.password, + dto.imapServer, + dto.imapPort, + dto.imapSecure, + dto.smtpServer, + dto.smtpPort, + dto.smtpSecure, + ); + } + + public update(dto: UpdateMailboxSettingsManualDto): MailboxSettingsManual { + if (dto.password) { + this.password = dto.password; + } + this.imapServer = dto.imapServer; + this.imapPort = dto.imapPort; + this.imapSecure = dto.imapSecure; + this.smtpServer = dto.smtpServer; + this.smtpPort = dto.smtpPort; + this.smtpSecure = dto.smtpSecure; + return this; + } + + public updateImapSync(imapSync: ImapSyncInfo[] | null): MailboxSettingsManual { + this.imapSync = imapSync; + return this; + } +} diff --git a/backend/src/Mailing/Service/MailMessage/Dto/CreateContactLeadDto.ts b/backend/src/Mailing/Service/MailMessage/Dto/CreateContactLeadDto.ts new file mode 100644 index 0000000..a691cd1 --- /dev/null +++ b/backend/src/Mailing/Service/MailMessage/Dto/CreateContactLeadDto.ts @@ -0,0 +1,44 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class CreateContactLeadDto { + @ApiPropertyOptional({ description: 'Contact entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + contactTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + leadTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead board ID', nullable: true }) + @IsOptional() + @IsNumber() + leadBoardId?: number | null; + + @ApiPropertyOptional({ description: 'Lead stage ID', nullable: true }) + @IsOptional() + @IsNumber() + leadStageId?: number | null; + + @ApiPropertyOptional({ description: 'Lead name', nullable: true }) + @IsOptional() + @IsString() + leadName?: string | null; + + @ApiPropertyOptional({ description: 'Lead and Contact responsible user ID', nullable: true }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ description: 'Do not create lead if active lead exists', nullable: true }) + @IsOptional() + @IsBoolean() + checkActiveLead?: boolean; + + @ApiPropertyOptional({ description: 'Do not create duplicate contact', nullable: true }) + @IsOptional() + @IsBoolean() + checkDuplicate?: boolean; +} diff --git a/backend/src/Mailing/Service/MailMessage/Dto/MailMessageDto.ts b/backend/src/Mailing/Service/MailMessage/Dto/MailMessageDto.ts new file mode 100644 index 0000000..da1cb39 --- /dev/null +++ b/backend/src/Mailing/Service/MailMessage/Dto/MailMessageDto.ts @@ -0,0 +1,105 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { MailMessagePayloadDto } from '../../../mail-message-payload'; +import { MailMessage } from '../../../Model/MailMessage/MailMessage'; + +export class MailMessageDto { + @ApiProperty() + id: number; + + @ApiProperty() + mailboxId: number; + + @ApiProperty() + threadId: string; + + @ApiProperty() + snippet: string | null; + + @ApiProperty() + sentFrom: string | null; + + @ApiProperty() + sentTo: string | null; + + @ApiProperty() + replyTo: string | null; + + @ApiProperty() + cc: string | null; + + @ApiProperty() + subject: string | null; + + @ApiProperty() + date: string; + + @ApiProperty() + hasAttachment: boolean; + + @ApiProperty() + isSeen: boolean; + + @ApiProperty() + payloads: MailMessagePayloadDto[]; + + @ApiProperty({ nullable: true }) + entityInfo: EntityInfoDto | null; + + constructor( + id: number, + mailboxId: number, + threadId: string, + snippet: string | null, + sentFrom: string, + sentTo: string | null, + replyTo: string | null, + cc: string | null, + subject: string | null, + date: string, + hasAttachment: boolean, + isSeen: boolean, + payloads: MailMessagePayloadDto[], + entityInfo: EntityInfoDto | null, + ) { + this.id = id; + this.mailboxId = mailboxId; + this.threadId = threadId; + this.snippet = snippet; + this.sentFrom = sentFrom; + this.sentTo = sentTo; + this.replyTo = replyTo; + this.cc = cc; + this.subject = subject; + this.date = date; + this.hasAttachment = hasAttachment; + this.isSeen = isSeen; + this.payloads = payloads; + this.entityInfo = entityInfo; + } + + static create( + message: MailMessage, + payloads: MailMessagePayloadDto[], + entityInfo: EntityInfoDto | null, + ): MailMessageDto { + return new MailMessageDto( + message.id, + message.mailboxId, + message.threadId, + message.snippet, + message.sentFrom, + message.sentTo, + message.replyTo, + message.cc, + message.subject, + message.date.toISOString(), + message.hasAttachment, + message.isSeen, + payloads, + entityInfo, + ); + } +} diff --git a/backend/src/Mailing/Service/MailMessage/MailMessageInfo.ts b/backend/src/Mailing/Service/MailMessage/MailMessageInfo.ts new file mode 100644 index 0000000..9eb1e8d --- /dev/null +++ b/backend/src/Mailing/Service/MailMessage/MailMessageInfo.ts @@ -0,0 +1,88 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { MailMessage } from '../../Model/MailMessage/MailMessage'; + +export class MailMessageInfo { + @ApiProperty() + id: number; + + @ApiProperty() + mailboxId: number; + + @ApiProperty() + threadId: string | null; + + @ApiProperty() + snippet: string | null; + + @ApiProperty() + sentFrom: string | null; + + @ApiProperty() + sentTo: string | null; + + @ApiProperty() + subject: string | null; + + @ApiProperty() + date: string; + + @ApiProperty() + hasAttachment: boolean; + + @ApiProperty() + isSeen: boolean; + + @ApiProperty() + folders: string[]; + + @ApiProperty({ nullable: true, type: EntityInfoDto }) + entityInfo: EntityInfoDto | null; + + constructor( + id: number, + mailboxId: number, + threadId: string | null, + snippet: string | null, + sentFrom: string | null, + sentTo: string | null, + subject: string | null, + date: string, + hasAttachment: boolean, + isSeen: boolean, + folders: string[], + entityInfo: EntityInfoDto | null, + ) { + this.id = id; + this.mailboxId = mailboxId; + this.threadId = threadId; + this.snippet = snippet; + this.sentFrom = sentFrom; + this.sentTo = sentTo; + this.subject = subject; + this.date = date; + this.hasAttachment = hasAttachment; + this.isSeen = isSeen; + this.folders = folders; + this.entityInfo = entityInfo; + } + + public static create(message: MailMessage, folders: string[], entityInfo: EntityInfoDto | null): MailMessageInfo { + return new MailMessageInfo( + message.id, + message.mailboxId, + message.threadId, + message.snippet, + message.sentFrom, + message.sentTo, + message.subject, + message.date.toISOString(), + message.hasAttachment, + message.isSeen, + folders, + entityInfo, + ); + } +} diff --git a/backend/src/Mailing/Service/MailMessage/MailMessageService.ts b/backend/src/Mailing/Service/MailMessage/MailMessageService.ts new file mode 100644 index 0000000..967d528 --- /dev/null +++ b/backend/src/Mailing/Service/MailMessage/MailMessageService.ts @@ -0,0 +1,675 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Brackets, In, Repository, WhereExpressionBuilder } from 'typeorm'; +import { v4 as uuidv4 } from 'uuid'; +import addressparser from 'nodemailer/lib/addressparser'; + +import { PagingMeta, NotFoundError } from '@/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { Entity } from '@/CRM/Model/Entity/Entity'; + +import { + EmailAddressValue, + FolderMessages, + MailboxFolderType, + MailboxState, + MailEventType, + MailMessageAttachment, + MailMessageEvent, + MailMessageExternal, + MailMessageReceivedEvent, +} from '../../common'; +import { Mailbox } from '../../mailbox/entities'; +import { MailboxService } from '../../mailbox/services'; +import { MailboxFolder, MailboxFolderService } from '../../mailbox-folder'; +import { MailMessagePayloadService } from '../../mail-message-payload'; + +import { MailMessage } from '../../Model/MailMessage/MailMessage'; +import { MailMessageFolder } from '../../Model/MailMessage/MailMessageFolder'; +import { MailMessageWithFolders } from '../../Model/MailMessage/MailMessageWithFolders'; + +import { MailThreadInfo } from './MailThreadInfo'; +import { MailMessageInfo } from './MailMessageInfo'; +import { MailThreadResult } from './MailThreadResult'; + +import { MailMessageDto } from './Dto/MailMessageDto'; +import { CreateContactLeadDto } from './Dto/CreateContactLeadDto'; + +import { GetSectionMessagesFilter } from '../../Controller/MailMessage/GetSectionMessagesFilter'; +import { GetMailboxMessagesFilter } from '../../Controller/MailMessage/GetMailboxMessagesFilter'; + +@Injectable() +export class MailMessageService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(MailMessage) + private readonly repository: Repository, + @InjectRepository(MailMessageFolder) + private readonly repositoryFolderLink: Repository, + @Inject(forwardRef(() => MailboxService)) + private readonly mailboxService: MailboxService, + private readonly mailboxFolderService: MailboxFolderService, + private readonly mailMessagePayloadService: MailMessagePayloadService, + private readonly userService: UserService, + private readonly entityService: EntityService, + private readonly entityInfoService: EntityInfoService, + ) {} + + async getById(accountId: number, mailboxId: number, messageId: number): Promise { + const message = await this.findById(accountId, mailboxId, messageId); + if (!message) { + throw NotFoundError.withId(MailMessage, messageId); + } + return message; + } + + async findById(accountId: number, mailboxId: number, messageId: number): Promise { + return this.repository.findOneBy({ accountId, mailboxId, id: messageId }); + } + + async getByThreadIdGroupByFolder(accountId: number, mailboxId: number, threadId: string): Promise { + const links = await this.repository + .createQueryBuilder('mm') + .select('mm.external_id', 'messageId') + .addSelect('mf.external_id', 'folderId') + .leftJoin('mail_message_folder', 'mmf', 'mmf.message_id = mm.id') + .leftJoin('mailbox_folder', 'mf', 'mf.id = mmf.folder_id') + .where('mm.account_id = :accountId', { accountId }) + .andWhere('mm.mailbox_id = :mailboxId', { mailboxId }) + .andWhere('mm.thread_id = :threadId', { threadId }) + .getRawMany<{ messageId: string; folderId: string }>(); + + const groupByFolder: FolderMessages[] = links.reduce((groups, link) => { + let group = groups.find((g) => g.folderId === link.folderId); + if (!group) { + group = { folderId: link.folderId, messageIds: [] }; + groups.push(group); + } + group.messageIds.push(link.messageId); + return groups; + }, [] as FolderMessages[]); + + return groupByFolder; + } + + async getMessagesForMailbox( + accountId: number, + user: User, + mailboxId: number, + filter: GetMailboxMessagesFilter, + ): Promise { + const { folderId, search, skip, take } = filter; + const mailbox = await this.mailboxService.findOne({ accountId, mailboxId, accessibleUserId: user.id }); + + if (!mailbox) { + return new MailThreadResult([], PagingMeta.empty()); + } + + const threadsQuery = this.repository + .createQueryBuilder('message') + .select('message.thread_id', 'threadId') + .where('message.account_id = :accountId', { accountId }) + .andWhere('message.mailbox_id = :mailboxId', { mailboxId: mailbox.id }); + if (folderId) { + threadsQuery + .leftJoin(MailMessageFolder, 'mmf', 'mmf.message_id = message.id') + .andWhere('mmf.folder_id = :folderId', { folderId }); + } + if (search) { + threadsQuery.andWhere(new Brackets((qb) => this.createSearchQuery(qb, search))); + } + + const { count } = await threadsQuery.clone().select('COUNT(DISTINCT(message.thread_id))').getRawOne(); + const threads = await threadsQuery + .groupBy('message.thread_id') + .orderBy('MAX(message.date)', 'DESC') + .offset(skip) + .limit(take) + .getRawMany(); + + const threadInfos = await this.getMessagesForThreads( + accountId, + user, + [mailbox.id], + threads.map((thread) => thread.threadId), + ); + + return new MailThreadResult(threadInfos, new PagingMeta(skip + threads.length, Number(count))); + } + + async getMessagesForSection( + accountId: number, + user: User, + type: MailboxFolderType, + filter: GetSectionMessagesFilter, + ): Promise { + const { mailboxId, search, skip, take } = filter; + + const mailboxes = await this.mailboxService.findMany({ accountId, mailboxId, accessibleUserId: user.id }); + + const mailboxesIds = mailboxes + .filter((mailbox) => mailbox.state !== MailboxState.Deleted) + .map((mailbox) => mailbox.id); + + if (mailboxesIds.length === 0) { + return new MailThreadResult([], PagingMeta.empty()); + } + + const threadsQuery = this.repository + .createQueryBuilder('message') + .select('message.thread_id', 'threadId') + .leftJoin(MailMessageFolder, 'mmf', 'mmf.message_id = message.id') + .leftJoin(MailboxFolder, 'folder', 'folder.id = mmf.folder_id') + .where('message.account_id = :accountId', { accountId }) + .andWhere('message.mailbox_id IN (:...mailboxIds)', { mailboxIds: mailboxesIds }) + .andWhere('folder.type = :type', { type }); + if (search) { + threadsQuery.andWhere(new Brackets((qb) => this.createSearchQuery(qb, search))); + } + + const { count } = await threadsQuery.clone().select('COUNT(DISTINCT(message.thread_id))').getRawOne(); + const threads = await threadsQuery + .groupBy('message.thread_id') + .orderBy('MAX(message.date)', 'DESC') + .offset(skip) + .limit(take) + .getRawMany(); + + const threadInfos = await this.getMessagesForThreads( + accountId, + user, + mailboxesIds, + threads.map((thread) => thread.threadId), + ); + + return new MailThreadResult(threadInfos, new PagingMeta(skip + threads.length, Number(count))); + } + + private createSearchQuery(qb: WhereExpressionBuilder, search: string): void { + qb.where(`message.sent_from ilike '%${search}%'`) + .orWhere(`message.sent_to ilike '%${search}%'`) + .orWhere(`message.cc ilike '%${search}%'`) + .orWhere(`message.subject ilike '%${search}%'`) + .orWhere(`message.snippet ilike '%${search}%'`); + } + + async getThreadForMessageId(accountId: number, user: User, messageId: number): Promise { + const message = await this.repository.findOneBy({ accountId, id: messageId }); + if (message) { + const threads = await this.getMessagesForThreads(accountId, user, [message.mailboxId], [message.threadId]); + + return threads?.length ? threads[0] : null; + } + + return null; + } + + private async getMessagesForThreads( + accountId: number, + user: User, + mailboxIds: number[], + threadIds: string[], + ): Promise { + const messages = await this.repository.find({ + where: { accountId, mailboxId: In(mailboxIds), threadId: In(threadIds) }, + order: { date: 'DESC' }, + }); + + const messagesWithFolders: MailMessageWithFolders[] = []; + for (const msg of messages) { + const folders = await this.mailboxFolderService.findMany({ accountId, messageId: msg.id }); + messagesWithFolders.push(new MailMessageWithFolders(msg, folders)); + } + + const entityInfoCache: EntityInfoDto[] = []; + const threads: MailThreadInfo[] = []; + for (const threadId of threadIds) { + const messages: MailMessageInfo[] = []; + const threadMessages = messagesWithFolders.filter((mwf) => mwf.message.threadId === threadId); + for (const tm of threadMessages) { + const entityInfo = tm.message.entityId + ? await this.getEntityInfoCached(accountId, user, tm.message.entityId, entityInfoCache) + : null; + messages.push( + MailMessageInfo.create( + tm.message, + tm.folders.map((f) => f.name), + entityInfo, + ), + ); + } + threads.push(new MailThreadInfo(threadId, messages)); + } + return threads; + } + + private async getEntityInfoCached(accountId: number, user: User, entityId: number, entityInfoCache: EntityInfoDto[]) { + let entityInfo: EntityInfoDto | null = entityInfoCache.find((e) => e.id === entityId); + if (!entityInfo) { + entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId }); + if (entityInfo) { + entityInfoCache.push(entityInfo); + } + } + return entityInfo ?? null; + } + + async getThreadWithPayload( + accountId: number, + user: User, + mailboxId: number, + messageId: number, + ): Promise { + const mailbox = await this.mailboxService.findOne({ accountId, mailboxId, accessibleUserId: user.id }); + const { threadId } = await this.getById(accountId, mailboxId, messageId); + const messages = await this.repository.find({ + where: { accountId, mailboxId: mailbox.id, threadId }, + order: { date: 'DESC' }, + }); + + const payloads = await this.mailMessagePayloadService.findByMessageIds( + accountId, + messages.map((message) => message.id), + ); + + const entityInfoCache: EntityInfoDto[] = []; + const messageDtos: MailMessageDto[] = []; + for (const message of messages) { + const payload = payloads.filter((p) => p.messageId === message.id).map((p) => p.toDto()); + const entityInfo = message.entityId + ? await this.getEntityInfoCached(accountId, user, message.entityId, entityInfoCache) + : null; + messageDtos.push(MailMessageDto.create(message, payload, entityInfo)); + } + return messageDtos; + } + + async getMessageWithPayload( + accountId: number, + user: User, + mailboxId: number, + messageId: number, + ): Promise { + const mailbox = await this.mailboxService.findOne({ accountId, mailboxId, accessibleUserId: user.id }); + if (mailbox) { + const message = await this.repository.findOneBy({ accountId, mailboxId: mailbox.id, id: messageId }); + if (message) { + const payloads = await this.mailMessagePayloadService.findByMessageId(accountId, message.id); + + const entityInfo = message.entityId + ? await this.entityInfoService.findOne({ accountId, user, entityId: message.entityId }) + : null; + + return MailMessageDto.create( + message, + payloads.map((p) => p.toDto()), + entityInfo, + ); + } + } + + throw NotFoundError.withId(MailMessage, messageId); + } + + async getMessageAttachment( + accountId: number, + userId: number, + mailboxId: number, + messageId: number, + payloadId: number, + ): Promise { + const mailbox = await this.mailboxService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.repository.findOneBy({ accountId, mailboxId, id: messageId }); + return this.mailMessagePayloadService.getAttachment(accountId, mailbox, message, payloadId); + } + + async moveThreadToSpecialFolder( + accountId: number, + mailboxId: number, + threadId: string, + folderType: MailboxFolderType, + ) { + const messages = await this.repository.findBy({ accountId, mailboxId, threadId }); + if (messages) { + await this.moveMessagesToSpecialFolder( + accountId, + mailboxId, + messages.map((m) => m.id), + folderType, + ); + } + } + + async moveMessagesToSpecialFolder( + accountId: number, + mailboxId: number, + messageIds: number[], + type: MailboxFolderType, + ) { + const folder = await this.mailboxFolderService.findOne({ accountId, mailboxId, type }); + if (folder) { + await this.repositoryFolderLink.delete({ messageId: In(messageIds) }); + await this.repositoryFolderLink.insert(messageIds.map((m) => new MailMessageFolder(m, folder.id))); + } + } + + async markSeenThread(accountId: number, mailboxId: number, threadId: string, isSeen: boolean) { + await this.repository.update({ accountId, mailboxId, threadId }, { isSeen }); + } + + async createContact( + accountId: number, + user: User, + mailboxId: number, + messageId: number, + dto: CreateContactLeadDto, + ): Promise { + const message = await this.repository.findOneBy({ accountId, mailboxId, id: messageId }); + if (!message || !message.sentFrom) { + return null; + } + + const email = addressparser(message.sentFrom, { flatten: true })[0]; + const entities = await this.createEntities({ + accountId, + ownerId: user.id, + email, + subject: message.subject, + settings: dto, + }); + + if (entities?.length) { + message.entityId = entities[0].id; + await this.repository.save(message); + + this.eventEmitter.emit( + MailEventType.MailMessageLinked, + new MailMessageEvent({ + accountId: message.accountId, + entityId: message.entityId, + messageId: message.id, + messageDate: message.date.toISOString(), + }), + ); + } + + const entity = entities?.length > 1 ? entities[1] : entities?.[0]; + + return entity ? this.entityInfoService.getEntityInfo({ user, entity, access: true }) : null; + } + + async processExternalMessages({ + accountId, + mailbox, + added, + updated, + deleted, + }: { + accountId: number; + mailbox: Mailbox; + added?: MailMessageExternal[]; + updated?: MailMessageExternal[]; + deleted?: string[]; + }) { + if (added?.length) { + await this.upsertMessage({ accountId, mailbox, messages: added }); + } + if (updated?.length) { + await this.upsertMessage({ accountId, mailbox, messages: updated }); + } + if (deleted?.length) { + await this.deleteMessages(deleted); + } + + if (added?.length || updated?.length || deleted?.length) { + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + } + + private async upsertMessage({ + accountId, + mailbox, + messages, + }: { + accountId: number; + mailbox: Mailbox; + messages: MailMessageExternal[]; + }) { + for (const message of messages) { + //TODO: check folder because messageId could be in many folders like Sent and Inbox then it message to yourself + const current = await this.repository + .createQueryBuilder('message') + .where('message.account_id = :accountId', { accountId: mailbox.accountId }) + .andWhere('message.mailbox_id = :mailboxId', { mailboxId: mailbox.id }) + .andWhere( + new Brackets((qb) => + qb + .where('message.external_id = :externalId', { externalId: message.id }) + .orWhere('message.message_id = :messageId', { messageId: message.messageId }), + ), + ) + .getOne(); + if (current) { + await this.updateMessage({ accountId, mailbox, current, message }); + } else { + await this.addMessage({ accountId, mailbox, extMessage: message }); + } + } + } + + private async addMessage({ + accountId, + mailbox, + extMessage, + }: { + accountId: number; + mailbox: Mailbox; + extMessage: MailMessageExternal; + }) { + if (!extMessage.id) { + extMessage.id = uuidv4(); + } + const prevMessage = await this.findPreviousMessage(accountId, mailbox.id, extMessage); + if (!extMessage.threadId) { + extMessage.threadId = prevMessage ? prevMessage.threadId : uuidv4(); + } + const folders = await this.mailboxFolderService.findMany({ + accountId, + mailboxId: mailbox.id, + externalId: extMessage.folders, + }); + + const isInbox = folders.some((f) => f.type === MailboxFolderType.Inbox); + const entityId = await this.findEntityId({ accountId, message: extMessage, mailbox, prevMessage, isInbox }); + + const message = await this.repository.save(MailMessage.create(accountId, mailbox.id, entityId, extMessage)); + if (folders) { + await this.repositoryFolderLink.insert(folders.map((f) => new MailMessageFolder(message.id, f.id))); + } + if (extMessage.payloads && extMessage.payloads.length > 0) { + await this.mailMessagePayloadService.processExternalPayloads(accountId, message.id, extMessage.payloads); + } + + this.eventEmitter.emit( + MailEventType.MailMessageReceived, + new MailMessageReceivedEvent({ + accountId: accountId, + ownerId: mailbox.ownerId, + entityId: entityId, + messageId: message.id, + messageSubject: message.subject, + messageSnippet: message.snippet, + messageDate: message.date.toISOString(), + isInbox, + }), + ); + } + + private async updateMessage({ + accountId, + mailbox, + current, + message, + }: { + accountId: number; + mailbox: Mailbox; + current: MailMessage; + message: MailMessageExternal; + }) { + message.entityId = message.entityId + ? await this.entityService.ensureExistId(accountId, message.entityId) + : current.entityId; + await this.repository.update(current.id, current.update(message)); + await this.repositoryFolderLink.delete({ messageId: current.id }); + const folders = await this.mailboxFolderService.findMany({ + accountId, + mailboxId: mailbox.id, + externalId: message.folders, + }); + if (folders) { + await this.repositoryFolderLink.insert(folders.map((f) => new MailMessageFolder(current.id, f.id))); + } + } + + private async deleteMessages(messagesDeleted: string[]) { + const messages = await this.repository.findBy({ externalId: In(messagesDeleted) }); + const result = await this.repository.delete({ externalId: In(messagesDeleted) }); + if (!result.affected && !messages) { + return; + } + for (const message of messages) { + this.eventEmitter.emit( + MailEventType.MailMessageDeleted, + new MailMessageEvent({ + accountId: message.accountId, + entityId: message.entityId, + messageId: message.id, + messageDate: message.date.toISOString(), + }), + ); + } + } + + private async findPreviousMessage( + accountId: number, + mailboxId: number, + message: MailMessageExternal, + ): Promise { + if (message.inReplyTo) { + const prevMessage = await this.repository.findOneBy({ accountId, mailboxId, messageId: message.inReplyTo }); + if (prevMessage) { + return prevMessage; + } + } + if (message.references) { + const refMessages = await this.repository.find({ + where: { accountId, mailboxId, messageId: In(message.references) }, + order: { date: 'desc' }, + take: 1, + }); + if (refMessages && refMessages.length > 0) { + return refMessages[0]; + } + } + return null; + } + + private async findEntityId({ + accountId, + message, + mailbox, + prevMessage, + isInbox, + }: { + accountId: number; + message: MailMessageExternal; + mailbox: Mailbox; + prevMessage: MailMessage | null; + isInbox: boolean; + }): Promise { + if (message.entityId) { + return this.entityService.ensureExistId(accountId, message.entityId); + } + if (prevMessage?.entityId) { + return this.entityService.ensureExistId(accountId, prevMessage.entityId); + } + if (isInbox && mailbox.entitySettings) { + const entities = await this.createEntities({ + accountId, + ownerId: mailbox.ownerId, + email: message.sentFrom.values[0], + subject: message.subject, + settings: { + contactTypeId: mailbox.entitySettings.contactEntityTypeId, + leadTypeId: mailbox.entitySettings.leadEntityTypeId, + leadBoardId: mailbox.entitySettings.leadBoardId, + leadStageId: mailbox.entitySettings.leadStageId, + leadName: mailbox.entitySettings.leadName, + ownerId: mailbox.entitySettings.ownerId, + checkActiveLead: mailbox.entitySettings.checkActiveLead, + checkDuplicate: mailbox.entitySettings.checkDuplicate, + }, + }); + return entities.length ? entities[0].id : null; + } + return null; + } + + private async createEntities({ + accountId, + ownerId, + email, + subject, + settings, + }: { + accountId: number; + ownerId: number; + email: EmailAddressValue; + subject: string; + settings: CreateContactLeadDto; + }): Promise { + if (!settings.leadTypeId && !settings.contactTypeId) { + return null; + } + + const fieldValues = email?.address ? [{ fieldType: FieldType.Email, value: email.address }] : []; + const lead = settings.leadTypeId + ? { + ownerId: settings.ownerId, + entityTypeId: settings.leadTypeId, + boardId: settings.leadBoardId, + stageId: settings.leadStageId, + name: settings.leadName || subject || email?.name || email?.address, + fieldValues, + } + : null; + const contact = settings.contactTypeId + ? { + ownerId: settings.ownerId, + entityTypeId: settings.contactTypeId, + name: email?.name, + fieldValues, + linkedEntities: lead ? [lead] : undefined, + } + : null; + + if (contact || lead) { + const user = await this.userService.findOne({ accountId, id: settings.ownerId ?? ownerId }); + return this.entityService.createSimple({ + accountId, + user, + dto: contact ?? lead, + options: { checkActiveLead: settings.checkActiveLead, checkDuplicate: settings.checkDuplicate }, + }); + } + + return []; + } +} diff --git a/backend/src/Mailing/Service/MailMessage/MailThreadInfo.ts b/backend/src/Mailing/Service/MailMessage/MailThreadInfo.ts new file mode 100644 index 0000000..6e48a64 --- /dev/null +++ b/backend/src/Mailing/Service/MailMessage/MailThreadInfo.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { MailMessageInfo } from './MailMessageInfo'; + +export class MailThreadInfo { + @ApiProperty() + id: string; + + @ApiProperty() + messages: MailMessageInfo[]; + + constructor(id: string, messages: MailMessageInfo[]) { + this.id = id; + this.messages = messages; + } +} diff --git a/backend/src/Mailing/Service/MailMessage/MailThreadResult.ts b/backend/src/Mailing/Service/MailMessage/MailThreadResult.ts new file mode 100644 index 0000000..ae855c8 --- /dev/null +++ b/backend/src/Mailing/Service/MailMessage/MailThreadResult.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { MailThreadInfo } from './MailThreadInfo'; + +export class MailThreadResult { + @ApiProperty() + threads: MailThreadInfo[]; + + @ApiProperty() + meta: PagingMeta; + + constructor(threads: MailThreadInfo[], meta: PagingMeta) { + this.threads = threads; + this.meta = meta; + } +} diff --git a/backend/src/Mailing/Service/Mailbox/Dto/mailbox-full-info.dto.ts b/backend/src/Mailing/Service/Mailbox/Dto/mailbox-full-info.dto.ts new file mode 100644 index 0000000..8a3b386 --- /dev/null +++ b/backend/src/Mailing/Service/Mailbox/Dto/mailbox-full-info.dto.ts @@ -0,0 +1,59 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsString } from 'class-validator'; + +import { MailboxState } from '../../../common'; +import { Mailbox } from '../../../mailbox/entities'; +import { MailboxFolderDto } from '../../../mailbox-folder'; + +export class MailboxFullInfoDto { + @ApiProperty({ description: 'Mailbox ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Mailbox name (email' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Owner ID' }) + @IsNumber() + ownerId: number; + + @ApiProperty({ description: 'Unread count' }) + @IsNumber() + unread: number; + + @ApiProperty({ description: 'Total count' }) + @IsNumber() + total: number; + + @ApiProperty({ enum: MailboxState, description: 'Mailbox state' }) + @IsEnum(MailboxState) + state: MailboxState; + + @ApiProperty({ type: [MailboxFolderDto], description: 'Mailbox folders' }) + folders: MailboxFolderDto[]; + + constructor( + id: number, + name: string, + ownerId: number, + unread: number, + total: number, + state: MailboxState, + folders: MailboxFolderDto[], + ) { + this.id = id; + this.name = name; + this.ownerId = ownerId; + this.unread = unread; + this.total = total; + this.state = state; + this.folders = folders; + } + + static create(mailbox: Mailbox, folders: MailboxFolderDto[]): MailboxFullInfoDto { + const unread = folders.reduce((acc, cur) => acc + cur.unread, 0); + const total = folders.reduce((acc, cur) => acc + cur.total, 0); + return new MailboxFullInfoDto(mailbox.id, mailbox.email, mailbox.ownerId, unread, total, mailbox.state, folders); + } +} diff --git a/backend/src/Mailing/Service/Mailbox/Dto/mailbox-section.dto.ts b/backend/src/Mailing/Service/Mailbox/Dto/mailbox-section.dto.ts new file mode 100644 index 0000000..a57f95c --- /dev/null +++ b/backend/src/Mailing/Service/Mailbox/Dto/mailbox-section.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { MailboxFolderType } from '../../../common'; +import { MailboxShortInfoDto } from './mailbox-short-info.dto'; + +export class MailboxSectionDto { + @ApiProperty({ enum: MailboxFolderType, description: 'Folder type' }) + @IsEnum(MailboxFolderType) + type: MailboxFolderType; + + @ApiProperty({ description: 'Unread count' }) + @IsNumber() + unread: number; + + @ApiProperty({ description: 'Total count' }) + @IsNumber() + total: number; + + @ApiProperty({ type: [MailboxShortInfoDto], description: 'Mailboxes' }) + mailboxes: MailboxShortInfoDto[]; + + constructor(type: MailboxFolderType) { + this.type = type; + this.unread = 0; + this.total = 0; + this.mailboxes = []; + } + + addMailboxInfo(mailbox: MailboxShortInfoDto) { + this.unread += mailbox.unread; + this.total += mailbox.total; + this.mailboxes.push(mailbox); + } +} diff --git a/backend/src/Mailing/Service/Mailbox/Dto/mailbox-short-info.dto.ts b/backend/src/Mailing/Service/Mailbox/Dto/mailbox-short-info.dto.ts new file mode 100644 index 0000000..8e80674 --- /dev/null +++ b/backend/src/Mailing/Service/Mailbox/Dto/mailbox-short-info.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsString } from 'class-validator'; + +import { MailboxState } from '../../../common'; + +export class MailboxShortInfoDto { + @ApiProperty({ description: 'Mailbox ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Mailbox name (email' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Unread count' }) + @IsNumber() + unread: number; + + @ApiProperty({ description: 'Total count' }) + @IsNumber() + total: number; + + @ApiProperty({ enum: MailboxState, description: 'Mailbox state' }) + @IsEnum(MailboxState) + state: MailboxState; +} diff --git a/backend/src/Mailing/Service/Mailbox/Dto/mailboxes-info.dto.ts b/backend/src/Mailing/Service/Mailbox/Dto/mailboxes-info.dto.ts new file mode 100644 index 0000000..1afdcea --- /dev/null +++ b/backend/src/Mailing/Service/Mailbox/Dto/mailboxes-info.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { MailboxSectionDto } from './mailbox-section.dto'; +import { MailboxFullInfoDto } from './mailbox-full-info.dto'; + +export class MailboxesInfoDto { + @ApiProperty({ type: [MailboxSectionDto], description: 'Mailboxes by sections' }) + sections: MailboxSectionDto[]; + + @ApiProperty({ type: [MailboxFullInfoDto], description: 'Mailboxes' }) + mailboxes: MailboxFullInfoDto[]; +} diff --git a/backend/src/Mailing/Service/Mailbox/MailboxService.ts b/backend/src/Mailing/Service/Mailbox/MailboxService.ts new file mode 100644 index 0000000..a0a7a93 --- /dev/null +++ b/backend/src/Mailing/Service/Mailbox/MailboxService.ts @@ -0,0 +1,281 @@ +import { Injectable } from '@nestjs/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { MailboxFolderType, MailboxState, SendMailMessageDto } from '../../common'; +import { Mailbox } from '../../mailbox/entities'; +import { MailboxService as MailboxSettingsService } from '../../mailbox/services'; +import { MailboxFolderService } from '../../mailbox-folder'; +import { MailProviderRegistry } from '../../mail-provider'; + +import { MailMessageService } from '../MailMessage/MailMessageService'; + +import { MailboxesInfoDto } from './Dto/mailboxes-info.dto'; +import { MailboxSectionDto } from './Dto/mailbox-section.dto'; +import { MailboxFullInfoDto } from './Dto/mailbox-full-info.dto'; + +@Injectable() +export class MailboxService { + constructor( + private readonly storageService: StorageService, + private readonly mailboxSettingsService: MailboxSettingsService, + private readonly mailboxFolderService: MailboxFolderService, + private readonly mailMessageService: MailMessageService, + private readonly mailProviderRegistry: MailProviderRegistry, + private readonly userService: UserService, + ) {} + + async getMailboxesForInfo(accountId: number, userId: number): Promise { + const accessibleMailboxes = await this.mailboxSettingsService.findMany({ + accountId, + accessibleUserId: userId, + state: [MailboxState.Active, MailboxState.Inactive], + }); + + const folderTypes = Object.values(MailboxFolderType); + const sections = folderTypes.map((type) => new MailboxSectionDto(type)); + const mailboxes: MailboxFullInfoDto[] = []; + for (const mailbox of accessibleMailboxes.sort((mb1, mb2) => mb1.id - mb2.id)) { + const hierarchy = await this.mailboxFolderService.getHierarchy({ accountId, mailboxId: mailbox.id }); + mailboxes.push( + MailboxFullInfoDto.create( + mailbox, + hierarchy.map((f) => f.toDto()), + ), + ); + + const typed = await this.mailboxFolderService.findMany({ accountId, mailboxId: mailbox.id, type: folderTypes }); + for (const folder of typed) { + const section = sections.find((ms) => ms.type === folder.type); + if (section) { + section.addMailboxInfo({ + id: mailbox.id, + name: mailbox.email, + unread: folder.unread, + total: folder.total, + state: mailbox.state, + }); + } + } + } + return { sections, mailboxes }; + } + + async sendMessage( + accountId: number, + userId: number, + mailboxId: number, + dto: SendMailMessageDto, + attachments?: StorageFile[] | null, + senderUserId?: number, + ): Promise { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + + return mailbox ? this.sendMessageForMailbox(accountId, mailbox, dto, attachments, senderUserId) : false; + } + + async sendMessageForMailbox( + accountId: number, + mailbox: Mailbox, + dto: SendMailMessageDto, + attachments?: StorageFile[] | null, + senderUserId?: number, + ): Promise { + const replyToMessage = dto.replyToMessageId + ? await this.mailMessageService.findById(accountId, mailbox.id, dto.replyToMessageId) + : null; + + const files = dto.fileIds?.length > 0 ? await this.getMessageFiles(accountId, dto.fileIds) : []; + const allAttachments = [...files, ...(attachments || [])]; + + const sendFrom = await this.userService.findOne({ + accountId: mailbox.accountId, + id: senderUserId ?? mailbox.ownerId, + }); + + const provider = this.mailProviderRegistry.get(mailbox.provider); + const message = await provider.send({ + accountId, + mailbox, + userName: sendFrom.fullName, + dto, + replyToMessage, + attachments: allAttachments, + }); + + if (message) { + await this.mailMessageService.processExternalMessages({ accountId, mailbox, added: [message] }); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + + return true; + } + + return false; + } + + private async getMessageFiles(accountId: number, fileIds: string[]): Promise { + const attachments: StorageFile[] = []; + + for (const fileId of fileIds) { + const { file, content } = await this.storageService.getFile({ fileId, accountId }); + const buffer = Buffer.from(content.buffer); + attachments.push(StorageFile.fromFileInfo(file, buffer)); + } + + return attachments; + } + + async trashThread(accountId: number, userId: number, mailboxId: number, messageId: number): Promise { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.mailMessageService.getById(accountId, mailbox.id, messageId); + const provider = this.mailProviderRegistry.get(mailbox.provider); + let result = false; + if (provider.isCapable('thread')) { + result = await provider.trash({ mailbox, messages: { threadId: message.threadId } }); + } else { + const messages = await this.mailMessageService.getByThreadIdGroupByFolder( + accountId, + mailbox.id, + message.threadId, + ); + result = await provider.trash({ mailbox, messages }); + } + if (result) { + await this.mailMessageService.moveThreadToSpecialFolder( + accountId, + mailbox.id, + message.threadId, + MailboxFolderType.Trash, + ); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + return result; + } + + async untrashThread(accountId: number, userId: number, mailboxId: number, messageId: number): Promise { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.mailMessageService.getById(accountId, mailbox.id, messageId); + const provider = this.mailProviderRegistry.get(mailbox.provider); + let result = false; + if (provider.isCapable('thread')) { + result = await provider.untrash({ mailbox, messages: { threadId: message.threadId } }); + } else { + const messages = await this.mailMessageService.getByThreadIdGroupByFolder( + accountId, + mailbox.id, + message.threadId, + ); + result = await provider.untrash({ mailbox, messages }); + } + if (result) { + await this.mailMessageService.moveThreadToSpecialFolder( + accountId, + mailbox.id, + message.threadId, + MailboxFolderType.Inbox, + ); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + return result; + } + + async spamThread(accountId: number, userId: number, mailboxId: number, messageId: number): Promise { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.mailMessageService.getById(accountId, mailbox.id, messageId); + const provider = this.mailProviderRegistry.get(mailbox.provider); + let result = false; + if (provider.isCapable('thread')) { + result = await provider.spam({ mailbox, messages: { threadId: message.threadId } }); + } else { + const messages = await this.mailMessageService.getByThreadIdGroupByFolder( + accountId, + mailbox.id, + message.threadId, + ); + result = await provider.spam({ mailbox, messages }); + } + if (result) { + await this.mailMessageService.moveThreadToSpecialFolder( + accountId, + mailbox.id, + message.threadId, + MailboxFolderType.Junk, + ); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + return result; + } + + async unspamThread(accountId: number, userId: number, mailboxId: number, messageId: number): Promise { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.mailMessageService.getById(accountId, mailbox.id, messageId); + const provider = this.mailProviderRegistry.get(mailbox.provider); + let result = false; + if (provider.isCapable('thread')) { + result = await provider.unspam({ mailbox, messages: { threadId: message.threadId } }); + } else { + const messages = await this.mailMessageService.getByThreadIdGroupByFolder( + accountId, + mailbox.id, + message.threadId, + ); + result = await provider.unspam({ mailbox, messages }); + } + if (result) { + await this.mailMessageService.moveThreadToSpecialFolder( + accountId, + mailbox.id, + message.threadId, + MailboxFolderType.Inbox, + ); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + return result; + } + + async markSeenThread(accountId: number, userId: number, mailboxId: number, messageId: number) { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.mailMessageService.getById(accountId, mailbox.id, messageId); + const provider = this.mailProviderRegistry.get(mailbox.provider); + let result = false; + if (provider.isCapable('thread')) { + result = await provider.setSeen({ mailbox, seen: true, messages: { threadId: message.threadId } }); + } else { + const messages = await this.mailMessageService.getByThreadIdGroupByFolder( + accountId, + mailbox.id, + message.threadId, + ); + result = await provider.setSeen({ mailbox, seen: true, messages }); + } + if (result) { + await this.mailMessageService.markSeenThread(accountId, mailbox.id, message.threadId, true); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + return result; + } + + async markUnseenThread(accountId: number, userId: number, mailboxId: number, messageId: number) { + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId, accessibleUserId: userId }); + const message = await this.mailMessageService.getById(accountId, mailbox.id, messageId); + const provider = this.mailProviderRegistry.get(mailbox.provider); + let result = false; + if (provider.isCapable('thread')) { + result = await provider.setSeen({ mailbox, seen: false, messages: { threadId: message.threadId } }); + } else { + const messages = await this.mailMessageService.getByThreadIdGroupByFolder( + accountId, + mailbox.id, + message.threadId, + ); + result = await provider.setSeen({ mailbox, seen: false, messages }); + } + if (result) { + await this.mailMessageService.markSeenThread(accountId, mailbox.id, message.threadId, true); + await this.mailboxFolderService.actualizeCounters({ accountId, mailboxId: mailbox.id }); + } + return result; + } +} diff --git a/backend/src/Mailing/Service/MailboxGmail/MailboxGmailService.ts b/backend/src/Mailing/Service/MailboxGmail/MailboxGmailService.ts new file mode 100644 index 0000000..4fcfb1f --- /dev/null +++ b/backend/src/Mailing/Service/MailboxGmail/MailboxGmailService.ts @@ -0,0 +1,724 @@ +import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { google, Auth, gmail_v1 } from 'googleapis'; +import addressparser from 'nodemailer/lib/addressparser'; + +import { formatState, FrontendRoute, isUnique, parseState, StringUtil, UrlGeneratorService } from '@/common'; +import { AccountService } from '@/modules/iam/account/account.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { MailConfig } from '../../config'; +import { + detectMailboxFolderType, + FolderMessages, + MailboxFolderExternal, + MailboxFolderType, + MailboxSyncResult, + MailMessageAttachment, + MailMessageExternal, + MailMessagePayloadExternal, + SendMailMessageDto, +} from '../../common'; +import { Mailbox } from '../../mailbox/entities'; +import { MailboxService } from '../../mailbox/services'; +import { MailboxFolderService } from '../../mailbox-folder'; +import { MailIntegration, MailProvider, MailProviderCapability } from '../../mail-provider'; +import { MailMessagePayload } from '../../mail-message-payload'; +import { HEADER_ENTITY_ID, MailMessageBuilderService } from '../../mail-message-builder'; + +import { MailboxSettingsGmail } from '../../Model/MailboxGmail/MailboxSettingsGmail'; +import { MailMessage } from '../../Model/MailMessage/MailMessage'; + +import { MailMessageService } from '../MailMessage/MailMessageService'; + +const CALLBACK_PATH = '/api/mailing/settings/mailboxes/gmail/callback'; + +const GmailSystemLabels = ['INBOX', 'SENT', 'IMPORTANT', 'STARRED', 'TRASH', 'DRAFT', 'SPAM']; +const IgnoreMimeType = [ + 'message/delivery-status', + 'message/global-delivery-status', + 'message/disposition-notification', +]; + +const ProviderName = 'gmail'; + +@Injectable() +@MailIntegration(ProviderName) +export class MailboxGmailService implements MailProvider { + private readonly logger = new Logger(MailboxGmailService.name); + private readonly _config: MailConfig; + private oAuth2Client: Auth.OAuth2Client; + + constructor( + private readonly urlGenerator: UrlGeneratorService, + private readonly configService: ConfigService, + @InjectRepository(MailboxSettingsGmail) + private readonly repository: Repository, + private readonly accountService: AccountService, + @Inject(forwardRef(() => MailboxService)) + private readonly mailboxService: MailboxService, + private readonly mailboxFolderService: MailboxFolderService, + @Inject(forwardRef(() => MailMessageService)) + private readonly mailMessageService: MailMessageService, + private readonly mailMessageBuilder: MailMessageBuilderService, + ) { + this._config = this.configService.get('mail'); + this.oAuth2Client = this.getOAuth2Client(); + } + + isCapable(capability: MailProviderCapability): boolean { + if (capability === 'thread') { + return true; + } + + return false; + } + + async getAuthorizeUrl({ accountId, mailboxId }: { accountId: number; mailboxId: number }): Promise { + const authUrl = this.oAuth2Client.generateAuthUrl({ + access_type: 'offline', + prompt: 'consent', + scope: ['https://www.googleapis.com/auth/gmail.modify'], + redirect_uri: this.getRedirectUrl(), + state: formatState(accountId, mailboxId), + }); + return authUrl as string; + } + + async processAuthCode({ code, state }: { code: string; state: string }): Promise { + const { tokens } = await this.oAuth2Client.getToken({ + code, + redirect_uri: this.getRedirectUrl(), + }); + const [accountId, mailboxId] = parseState(state, Number); + const mailbox = await this.mailboxService.findOne({ accountId, mailboxId }); + const profile = await this.getProfile(tokens); + await this.repository.save(new MailboxSettingsGmail(mailbox.id, mailbox.accountId, tokens, profile.historyId)); + if (profile.emailAddress && profile.emailAddress !== mailbox.email) { + await this.mailboxService.update({ + accountId: mailbox.accountId, + mailboxId: mailbox.id, + dto: { email: profile.emailAddress }, + }); + } + const { subdomain } = await this.accountService.findOne({ accountId: mailbox.accountId }); + + return this.urlGenerator.createUrl({ + route: FrontendRoute.settings.mailing(), + subdomain, + query: { mailboxId: String(mailbox.id) }, + }); + } + + private getRedirectUrl() { + return this.urlGenerator.createUrl({ route: CALLBACK_PATH }); + } + + private getOAuth2Client(tokens?: Auth.Credentials): Auth.OAuth2Client { + const client = new google.auth.OAuth2(this._config.gmail.apiClientId, this._config.gmail.apiClientSecret); + if (tokens) { + client.setCredentials(tokens); + } + return client; + } + + private async getProfile(tokens: Auth.Credentials) { + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(tokens) }); + const { data } = await gmail.users.getProfile({ userId: 'me' }); + return data; + } + + async sync({ + mailbox, + syncFull, + syncDate, + }: { + mailbox: Mailbox; + syncFull?: boolean; + syncDate?: Date; + }): Promise { + return syncFull ? this.syncFull(mailbox, syncDate) : this.syncPartial(mailbox); + } + + async syncFull(mailbox: Mailbox, syncDate: Date | null): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await this.updateFolders(mailbox, gmail); + + if (syncDate) { + const added: string[] = []; + let historyId: string = undefined; + let nextPageToken: string = undefined; + let listMessages = true; + while (listMessages) { + const { data } = await gmail.users.messages.list({ + userId: 'me', + pageToken: nextPageToken, + includeSpamTrash: true, + }); + if (!historyId && data.messages.length > 0) { + const message = await gmail.users.messages.get({ userId: 'me', id: data.messages[0].id }); + historyId = message.data.historyId; + } + + nextPageToken = data.nextPageToken; + listMessages = !!nextPageToken; + + added.push(...data.messages.map((m) => m.id)); + } + + if (added.length) { + await this.updateMessages({ accountId: mailbox.accountId, mailbox, gmail, added }); + } + + if (historyId) { + await this.repository.update(mailbox.id, { historyId }); + } + } + + return { result: true, message: null }; + } catch (e) { + this.logger.warn(`Gmail full synchronization error for mailbox ${mailbox.id}. ${e.toString()}`); + if (e instanceof Error) { + return { result: false, message: e.message }; + } else { + return { result: false, message: e.toString() }; + } + } + } + + async syncPartial(mailbox: Mailbox): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await this.updateFolders(mailbox, gmail); + + let added: string[] = []; + let updated: string[] = []; + let deleted: string[] = []; + let historyId = settings.historyId; + let nextPageToken: string = undefined; + let checkHistory = true; + while (checkHistory) { + const { data } = await gmail.users.history.list({ + userId: 'me', + startHistoryId: settings.historyId, + pageToken: nextPageToken, + }); + if (!nextPageToken) { + historyId = data.historyId; + } + + nextPageToken = data.nextPageToken; + checkHistory = !!nextPageToken; + + if (data.history) { + for (const record of data.history) { + let recordMessagesAdded: string[] = []; + let recordMessagesUpdated: string[] = []; + let recordMessagesDeleted: string[] = []; + if (record.messagesAdded) { + recordMessagesAdded = record.messagesAdded.map((m) => m.message.id).filter(isUnique); + } + if (record.messages) { + recordMessagesUpdated = record.messages.map((m) => m.id).filter(isUnique); + } + if (record.messagesDeleted) { + recordMessagesDeleted = record.messagesDeleted.map((m) => m.message.id).filter(isUnique); + } + if (recordMessagesUpdated.length > 0) { + recordMessagesUpdated = recordMessagesUpdated.filter((m) => !added.includes(m)); + updated.push(...recordMessagesUpdated); + deleted = deleted.filter((m) => !recordMessagesUpdated.includes(m)); + } + if (recordMessagesDeleted.length > 0) { + added = added.filter((m) => !recordMessagesDeleted.includes(m)); + updated = updated.filter((m) => !recordMessagesDeleted.includes(m)); + deleted.push(...recordMessagesDeleted); + } + if (recordMessagesAdded.length > 0) { + added.push(...recordMessagesAdded); + updated = updated.filter((m) => !recordMessagesAdded.includes(m)); + deleted = deleted.filter((m) => !recordMessagesAdded.includes(m)); + } + } + } + } + + added = added.filter(isUnique); + updated = updated.filter(isUnique); + deleted = deleted.filter(isUnique); + + if (added.length || updated.length || deleted.length) { + await this.updateMessages({ accountId: mailbox.accountId, mailbox, gmail, added, updated, deleted }); + } + + if (historyId) { + await this.repository.update(settings.mailboxId, { historyId }); + } + + return { result: true, message: null }; + } catch (e) { + this.logger.warn(`Gmail partial synchronization error for mailbox ${mailbox.id}. ${e.toString()}`); + if (e instanceof Error) { + return { result: false, message: e.message }; + } else { + return { result: false, message: e.toString() }; + } + } + } + + async getAttachment({ + mailbox, + message, + payload, + }: { + mailbox: Mailbox; + message: MailMessage; + payload: MailMessagePayload; + }): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + const { data } = await gmail.users.messages.attachments.get({ + userId: 'me', + messageId: message.externalId, + id: payload.attachment, + }); + + return { + mimeType: payload.mimeType, + filename: payload.filename, + content: new Uint8Array(Buffer.from(data.data, 'base64')), + }; + } catch (e) { + this.logger.error(`Gmail get attachment error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return null; + } + } + + private async updateFolders(mailbox: Mailbox, gmail: gmail_v1.Gmail) { + const { data } = await gmail.users.labels.list({ userId: 'me' }); + const folders = data.labels + .filter((l) => l.type === 'user' || GmailSystemLabels.includes(l.id)) + .map((label) => this.convertToExternalFolder(label)); + if (folders.length) { + await this.mailboxFolderService.processExternal({ + accountId: mailbox.accountId, + mailboxId: mailbox.id, + extFolders: folders, + }); + } + } + + private convertToExternalFolder(label: gmail_v1.Schema$Label): MailboxFolderExternal { + return { + id: label.id, + name: label.name, + type: detectMailboxFolderType({ name: label.id }), + }; + } + + private async updateMessages({ + accountId, + mailbox, + gmail, + added, + updated, + deleted, + }: { + accountId: number; + mailbox: Mailbox; + gmail: gmail_v1.Gmail; + added?: string[]; + updated?: string[]; + deleted?: string[]; + }) { + const addedMsgs = added?.length + ? (await Promise.all(added.map((messageId) => this.findMessage(mailbox.id, gmail, messageId)))).filter(Boolean) + : undefined; + + const updatedMsgs = updated?.length + ? (await Promise.all(updated.map((messageId) => this.findMessage(mailbox.id, gmail, messageId)))).filter(Boolean) + : undefined; + + await this.mailMessageService.processExternalMessages({ + accountId, + mailbox, + added: addedMsgs, + updated: updatedMsgs, + deleted, + }); + } + + private async findMessage( + mailboxId: number, + gmail: gmail_v1.Gmail, + messageId: string, + ): Promise { + try { + const { data } = await gmail.users.messages.get({ userId: 'me', id: messageId }); + const from = this.getMessageHeadersValue(data.payload.headers, 'From'); + if (!from) { + this.logger.warn( + `Null 'From' value! Mailbox: ${mailboxId}. Message payload header: ${JSON.stringify(data.payload.headers)}`, + ); + return null; + } + const payloads = this.getMessagePayloads(data.payload); + const hasAttachment = payloads.some((payload) => payload.attachmentId); + const to = this.getMessageHeadersValue(data.payload.headers, 'To'); + const replyTo = this.getMessageHeadersValue(data.payload.headers, 'Reply-To'); + const cc = this.getMessageHeadersValue(data.payload.headers, 'Cc'); + const isUnread = data.labelIds.includes('UNREAD'); + const entityId = this.getMessageHeadersValue(data.payload.headers, HEADER_ENTITY_ID); + return { + id: data.id, + threadId: data.threadId, + snippet: data.snippet, + sentFrom: { text: from, values: addressparser(from, { flatten: true }) }, + sentTo: to ? { text: to, values: addressparser(to, { flatten: true }) } : null, + replyTo: replyTo ? { text: replyTo, values: addressparser(replyTo, { flatten: true }) } : null, + cc: cc ? { text: cc, values: addressparser(cc, { flatten: true }) } : null, + subject: this.getMessageHeadersValue(data.payload.headers, 'Subject'), + date: new Date(parseInt(data.internalDate)), + hasAttachment, + messageId: this.getMessageHeadersValue(data.payload.headers, 'Message-Id'), + inReplyTo: this.getMessageHeadersValue(data.payload.headers, 'In-Reply-To'), + references: this.getMessageHeadersValue(data.payload.headers, 'References') + ?.split(',') + ?.map((i) => i.trim()), + isSeen: !isUnread, + entityId: entityId ? parseInt(entityId) : null, + folders: data.labelIds, + payloads, + }; + } catch { + return null; + } + } + + private getMessageHeadersValue(headers: gmail_v1.Schema$MessagePartHeader[], name: string): string { + return headers.find((header) => header.name.toLowerCase() === name.toLowerCase())?.value; + } + + private getMessagePayloads(part: gmail_v1.Schema$MessagePart): MailMessagePayloadExternal[] { + if (part.mimeType.startsWith('text') || part.filename || part.body?.attachmentId) { + const attachmentId = part.body?.attachmentId ?? null; + const content = part.body?.data ? StringUtil.decode(part.body.data, 'base64', 'utf-8') : null; + const size = part.body?.size ?? null; + return [{ id: part.partId, mimeType: part.mimeType, filename: part.filename, attachmentId, content, size }]; + } else if (part.mimeType.startsWith('multipart') || part.mimeType === 'message/rfc822') { + const payloads: MailMessagePayloadExternal[] = []; + for (const nestedPart of part.parts) { + const nestedPayloads = this.getMessagePayloads(nestedPart); + if (nestedPayloads.length > 0) { + payloads.push(...nestedPayloads); + } + } + return payloads; + } else if (IgnoreMimeType.includes(part.mimeType)) { + //TODO: process delivery status + return []; + } else if (part.mimeType.startsWith('image')) { + //TODO: process inline images + return []; + } else { + this.logger.error(`Gmail synchronization. Unknown message part mime type '${part.mimeType}' or empty file name`); + return []; + } + } + + async send({ + accountId, + mailbox, + userName, + dto, + replyToMessage, + attachments, + }: { + accountId: number; + mailbox: Mailbox; + userName: string; + dto: SendMailMessageDto; + replyToMessage?: MailMessage | null; + attachments: StorageFile[]; + }): Promise { + try { + const settings = await this.repository.findOneBy({ accountId, mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + const mail = await this.mailMessageBuilder.createNodemailerMessage( + mailbox.email, + userName, + dto, + replyToMessage, + attachments, + ); + const request = { + threadId: replyToMessage ? replyToMessage.threadId : null, + raw: await this.mailMessageBuilder.createRawMessage(mail), + }; + + const { data } = await gmail.users.messages.send({ userId: 'me', requestBody: request }); + + if (data?.id) { + return await this.findMessage(mailbox.id, gmail, data.id); + } + } catch (e) { + const error = e as Error; + this.logger.error(`SMTP send message error for mailbox ${mailbox.id}: ${error?.message}`, error?.stack); + } + + return null; + } + + async setSeen({ + mailbox, + seen, + messages, + }: { + accountId: number; + mailbox: Mailbox; + seen: boolean; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + const messageIds = messages.map((m) => m.messageIds).flat(); + return seen + ? this.removeLabelFromMessages(mailbox, messageIds, ['UNREAD']) + : this.addLabelToMessages(mailbox, messageIds, ['UNREAD']); + } else { + return seen + ? this.removeLabelFromThread(mailbox, messages.threadId, ['UNREAD']) + : this.addLabelToThread(mailbox, messages.threadId, ['UNREAD']); + } + } + private async addLabelToThread(mailbox: Mailbox, threadId: string, labels: string[]): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await gmail.users.threads.modify({ + userId: 'me', + id: threadId, + requestBody: { addLabelIds: labels }, + }); + + return true; + } catch (e) { + this.logger.error(`Gmail add label ${labels} to thread error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + private async removeLabelFromThread(mailbox: Mailbox, threadId: string, labels: string[]): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await gmail.users.threads.modify({ + userId: 'me', + id: threadId, + requestBody: { removeLabelIds: labels }, + }); + + return true; + } catch (e) { + this.logger.error(`Gmail remove label ${labels} to thread error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + private async addLabelToMessages(mailbox: Mailbox, messageExtIds: string[], labels: string[]): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await gmail.users.messages.batchModify({ + userId: 'me', + requestBody: { ids: messageExtIds, addLabelIds: labels }, + }); + + return true; + } catch (e) { + this.logger.error(`Gmail add label ${labels} to messages error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + private async removeLabelFromMessages(mailbox: Mailbox, messageExtIds: string[], labels: string[]): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await gmail.users.messages.batchModify({ + userId: 'me', + requestBody: { ids: messageExtIds, removeLabelIds: labels }, + }); + + return true; + } catch (e) { + this.logger.error( + `Gmail remove label ${labels} to messages error for mailbox ${mailbox.id}`, + (e as Error)?.stack, + ); + return false; + } + } + + async trash({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + const messageIds = messages.map((m) => m.messageIds).flat(); + return this.trashMessages(mailbox, messageIds); + } else { + return this.trashThread(mailbox, messages.threadId); + } + } + private async trashMessages(mailbox: Mailbox, messageExtIds: string[]): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + for (const messageExtId of messageExtIds) { + await gmail.users.messages.trash({ userId: 'me', id: messageExtId }); + } + + return true; + } catch (e) { + this.logger.error(`Gmail trash message error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + private async trashThread(mailbox: Mailbox, threadId: string): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await gmail.users.threads.trash({ userId: 'me', id: threadId }); + + return true; + } catch (e) { + this.logger.error(`Gmail trash thread error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + + async untrash({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + const messageIds = messages.map((m) => m.messageIds).flat(); + return this.untrashMessages(mailbox, messageIds); + } else { + return this.untrashThread(mailbox, messages.threadId); + } + } + private async untrashMessages(mailbox: Mailbox, messageExtIds: string[]): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + for (const messageExtId of messageExtIds) { + await gmail.users.messages.untrash({ userId: 'me', id: messageExtId }); + } + + return true; + } catch (e) { + this.logger.error(`Gmail untrash message error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + private async untrashThread(mailbox: Mailbox, threadId: string): Promise { + try { + const settings = await this.repository.findOneBy({ mailboxId: mailbox.id }); + const gmail = google.gmail({ version: 'v1', auth: this.getOAuth2Client(settings.tokens) }); + + await gmail.users.threads.untrash({ userId: 'me', id: threadId }); + + return true; + } catch (e) { + this.logger.error(`Gmail untrash thread error for mailbox ${mailbox.id}`, (e as Error)?.stack); + return false; + } + } + + async spam({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + const messageIds = messages.map((m) => m.messageIds).flat(); + return this.addMessagesToFolder(mailbox.accountId, mailbox, messageIds, MailboxFolderType.Junk); + } else { + return this.addThreadToFolder(mailbox.accountId, mailbox, messages.threadId, MailboxFolderType.Junk); + } + } + private async addThreadToFolder( + accountId: number, + mailbox: Mailbox, + threadId: string, + type: MailboxFolderType, + ): Promise { + const toFolder = await this.mailboxFolderService.findOne({ accountId, mailboxId: mailbox.id, type }); + return await this.addLabelToThread(mailbox, threadId, [toFolder.externalId]); + } + private async addMessagesToFolder( + accountId: number, + mailbox: Mailbox, + messageExtIds: string[], + type: MailboxFolderType, + ): Promise { + const toFolder = await this.mailboxFolderService.findOne({ accountId, mailboxId: mailbox.id, type }); + return await this.addLabelToMessages(mailbox, messageExtIds, [toFolder.externalId]); + } + + async unspam({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + const messageIds = messages.map((m) => m.messageIds).flat(); + return this.removeMessagesFromFolder(mailbox.accountId, mailbox, messageIds, MailboxFolderType.Junk); + } else { + return this.removeThreadFromFolder(mailbox.accountId, mailbox, messages.threadId, MailboxFolderType.Junk); + } + } + private async removeThreadFromFolder( + accountId: number, + mailbox: Mailbox, + threadId: string, + type: MailboxFolderType, + ): Promise { + const fromFolder = await this.mailboxFolderService.findOne({ accountId, mailboxId: mailbox.id, type }); + return await this.removeLabelFromThread(mailbox, threadId, [fromFolder.externalId]); + } + private async removeMessagesFromFolder( + accountId: number, + mailbox: Mailbox, + messageExtIds: string[], + type: MailboxFolderType, + ): Promise { + const fromFolder = await this.mailboxFolderService.findOne({ accountId, mailboxId: mailbox.id, type }); + return await this.removeLabelFromMessages(mailbox, messageExtIds, [fromFolder.externalId]); + } +} diff --git a/backend/src/Mailing/Service/MailboxManual/Dto/MailboxSettingsManualDto.ts b/backend/src/Mailing/Service/MailboxManual/Dto/MailboxSettingsManualDto.ts new file mode 100644 index 0000000..762c130 --- /dev/null +++ b/backend/src/Mailing/Service/MailboxManual/Dto/MailboxSettingsManualDto.ts @@ -0,0 +1,61 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsString } from 'class-validator'; + +import { MailboxSettingsManual } from '../../../Model/MailboxManual/MailboxSettingsManual'; + +export class MailboxSettingsManualDto { + @ApiProperty() + @IsString() + imapServer: string; + + @ApiProperty() + @IsNumber() + imapPort: number; + + @ApiProperty() + @IsBoolean() + imapSecure: boolean; + + @ApiProperty() + @IsString() + smtpServer: string; + + @ApiProperty() + @IsNumber() + smtpPort: number; + + @ApiProperty() + @IsBoolean() + smtpSecure: boolean; + + private constructor( + imapServer: string, + imapPort: number, + imapSecure: boolean, + smtpServer: string, + smtpPort: number, + smtpSecure: boolean, + ) { + this.imapServer = imapServer; + this.imapPort = imapPort; + this.imapSecure = imapSecure; + this.smtpServer = smtpServer; + this.smtpPort = smtpPort; + this.smtpSecure = smtpSecure; + } + + public static create(settings: MailboxSettingsManual): MailboxSettingsManualDto { + return new MailboxSettingsManualDto( + settings.imapServer, + settings.imapPort, + settings.imapSecure, + settings.smtpServer, + settings.smtpPort, + settings.smtpSecure, + ); + } + + public static createDefault(): MailboxSettingsManualDto { + return new MailboxSettingsManualDto('', 0, true, '', 0, true); + } +} diff --git a/backend/src/Mailing/Service/MailboxManual/Dto/UpdateMailboxSettingsManualDto.ts b/backend/src/Mailing/Service/MailboxManual/Dto/UpdateMailboxSettingsManualDto.ts new file mode 100644 index 0000000..64c6bff --- /dev/null +++ b/backend/src/Mailing/Service/MailboxManual/Dto/UpdateMailboxSettingsManualDto.ts @@ -0,0 +1,38 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class UpdateMailboxSettingsManualDto { + @ApiProperty() + @IsOptional() + @IsString() + email: string; + + @ApiProperty() + @IsOptional() + @IsString() + password: string | null; + + @ApiProperty() + @IsString() + imapServer: string; + + @ApiProperty() + @IsNumber() + imapPort: number; + + @ApiProperty() + @IsBoolean() + imapSecure: boolean; + + @ApiProperty() + @IsString() + smtpServer: string; + + @ApiProperty() + @IsNumber() + smtpPort: number; + + @ApiProperty() + @IsBoolean() + smtpSecure: boolean; +} diff --git a/backend/src/Mailing/Service/MailboxManual/MailboxManualService.ts b/backend/src/Mailing/Service/MailboxManual/MailboxManualService.ts new file mode 100644 index 0000000..f5adeca --- /dev/null +++ b/backend/src/Mailing/Service/MailboxManual/MailboxManualService.ts @@ -0,0 +1,803 @@ +import { forwardRef, Inject, Injectable, Logger, NotImplementedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { connect, getParts, ImapSimple, Message } from 'imap-simple'; +import * as Imap from 'imap'; +import nodemailer from 'nodemailer'; +import Mail from 'nodemailer/lib/mailer'; +import addressparser from 'nodemailer/lib/addressparser'; +import { AddressObject, simpleParser, Source } from 'mailparser'; +import { v4 as uuidv4 } from 'uuid'; +import { convert } from 'html-to-text'; + +import { capitalizeFirst, DateUtil, StringUtil, withTimeout } from '@/common'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { MailConfig } from '../../config'; +import { + detectMailboxFolderType, + EmailAddress, + EmailAddressValue, + FolderMessages, + ImapSyncInfo, + MailboxFolderExternal, + MailboxFolderType, + MailboxSyncResult, + MailMessageAttachment, + MailMessageExternal, + MailMessagePayloadExternal, + SendMailMessageDto, +} from '../../common'; +import { Mailbox } from '../../mailbox/entities'; +import { MailboxFolderService } from '../../mailbox-folder'; +import { MailIntegration, MailProvider, MailProviderCapability } from '../../mail-provider'; +import { MailMessagePayload } from '../../mail-message-payload'; +import { HEADER_ENTITY_ID, MailMessageBuilderService } from '../../mail-message-builder'; + +import { MailboxSettingsManual } from '../../Model/MailboxManual/MailboxSettingsManual'; +import { MailMessage } from '../../Model/MailMessage/MailMessage'; + +import { MailMessageService } from '../MailMessage/MailMessageService'; + +import { MailboxSettingsManualDto } from './Dto/MailboxSettingsManualDto'; +import { UpdateMailboxSettingsManualDto } from './Dto/UpdateMailboxSettingsManualDto'; + +// eslint-disable-next-line no-control-regex +const cleanString = (str: string) => str?.replace(/[\u0000\f]/g, '')?.trim(); + +const ProviderName = 'manual'; + +@Injectable() +@MailIntegration(ProviderName) +export class MailboxManualService implements MailProvider { + private readonly logger = new Logger(MailboxManualService.name); + private readonly _config: MailConfig; + + constructor( + private readonly configService: ConfigService, + @InjectRepository(MailboxSettingsManual) + private readonly repository: Repository, + private readonly mailboxFolderService: MailboxFolderService, + @Inject(forwardRef(() => MailMessageService)) + private readonly mailMessageService: MailMessageService, + private readonly mailMessageBuilder: MailMessageBuilderService, + ) { + this._config = this.configService.get('mail'); + } + + isCapable(capability: MailProviderCapability): boolean { + if (capability === 'thread') { + return false; + } + + return false; + } + + async getManualSettings(accountId: number, mailboxId: number): Promise { + const settings = await this.repository.findOneBy({ accountId, mailboxId }); + if (!settings) { + return MailboxSettingsManualDto.createDefault(); + } + return MailboxSettingsManualDto.create(settings); + } + + async saveManualSettings( + accountId: number, + mailboxId: number, + dto: UpdateMailboxSettingsManualDto, + ): Promise<{ result: boolean; state: MailboxSettingsManualDto | string }> { + let settings = await this.repository.findOneBy({ accountId, mailboxId }); + settings = settings ? settings.update(dto) : MailboxSettingsManual.create(mailboxId, accountId, dto); + const { result, message } = await this.checkSettings(dto.email, settings); + if (result) { + await this.repository.save(settings); + return { result, state: MailboxSettingsManualDto.create(settings) }; + } else { + return { result, state: message }; + } + } + + private async withConnection({ + email, + mailboxId, + settings, + action, + onError, + }: { + email: string; + mailboxId?: number; + settings?: MailboxSettingsManual; + action: (connection: ImapSimple, settings: MailboxSettingsManual) => Promise; + onError?: (error: unknown) => E; + }) { + if (!mailboxId && !settings) { + throw new Error('No mailboxId or settings provided'); + } + const localSettings = settings ?? (await this.repository.findOneBy({ mailboxId })); + let connection: ImapSimple; + try { + connection = await connect(this.getSimpleImapConfig(email, localSettings)); + // connection.on('error', (error) => { + // this.logger.error(`Connection onError for mailbox ${localSettings.mailboxId}`, error.stack); + // }); + return await action(connection, localSettings); + } catch (e) { + this.logger.warn(`Connection error for mailbox ${localSettings.mailboxId}. ${e.toString()}`); + return onError ? onError(e as Error) : null; + } finally { + connection?.end(); + } + } + + private getSimpleImapConfig(user: string, settings: MailboxSettingsManual) { + return { + imap: { + user: user, + password: settings.password, + host: settings.imapServer, + port: settings.imapPort, + tls: settings.imapSecure, + tlsOptions: { servername: settings.imapServer }, + authTimeout: 3000, + }, + }; + } + + private async withBox({ + mailboxId, + connection, + boxName, + autoExpunge, + action, + onError, + }: { + mailboxId: number; + connection: ImapSimple; + boxName: string; + autoExpunge: boolean; + action: (box: Imap.Box) => Promise; + onError?: (error: unknown) => E; + }) { + let box: Imap.Box; + try { + box = (await connection.openBox(boxName)) as unknown as Imap.Box; + return await action(box); + } catch (e) { + this.logger.warn(`Box <${boxName}> error for mailbox ${mailboxId}. ${e.toString()}`); + return onError ? onError(e) : null; + } finally { + try { + await connection.closeBox(autoExpunge); + } catch (closeError) { + this.logger.warn(`Failed to close box <${boxName}> for mailbox ${mailboxId}. ${closeError.toString()}`); + } + } + } + + private async checkSettings(email: string, settings: MailboxSettingsManual): Promise { + return await this.withConnection({ + email, + settings, + action: async () => { + return { result: true, message: null }; + }, + onError: (error) => { + return { result: false, message: (error as Error)?.message }; + }, + }); + } + + async sync({ + mailbox, + syncFull, + syncDate, + }: { + mailbox: Mailbox; + syncFull?: boolean; + syncDate?: Date; + }): Promise { + return syncFull ? this.syncFull(mailbox, syncDate) : this.syncPartial(mailbox); + } + + async syncFull(mailbox: Mailbox, syncDate: Date | null): Promise { + return await this.withConnection({ + mailboxId: mailbox.id, + email: mailbox.email, + action: async (connection) => { + const syncInfo = await this.processMailbox({ + connection, + mailbox, + syncInfo: [], + fromDate: syncDate ?? DateUtil.now(), + }); + await this.repository.update(mailbox.id, { imapSync: syncInfo }); + return { result: true, message: null }; + }, + onError: (error) => { + return { result: false, message: (error as Error)?.message }; + }, + }); + } + + async syncPartial(mailbox: Mailbox): Promise { + return await this.withConnection({ + mailboxId: mailbox.id, + email: mailbox.email, + action: async (connection, settings) => { + const syncInfo = await this.processMailbox({ + connection, + mailbox, + syncInfo: settings.imapSync ?? [], + fromDate: DateUtil.now(), + }); + await this.repository.update(mailbox.id, { imapSync: syncInfo }); + return { result: true, message: null }; + }, + onError: (error) => { + return { result: false, message: (error as Error)?.message }; + }, + }); + } + + async getAttachment({ + mailbox, + message, + payload, + }: { + mailbox: Mailbox; + message: MailMessage; + payload: MailMessagePayload; + }): Promise { + const content: Uint8Array | null = await this.withConnection({ + mailboxId: mailbox.id, + email: mailbox.email, + action: async (connection) => { + const folders = await this.mailboxFolderService.findMany({ + accountId: message.accountId, + messageId: message.id, + }); + if (folders && folders.length > 0) { + return await this.withBox({ + mailboxId: mailbox.id, + connection, + boxName: folders[0].externalId, + autoExpunge: false, + action: async () => { + const uid = this.parseMessageId(message.externalId); + const mails = await connection.search(['ALL', ['UID', `${uid}`]], { bodies: ['HEADER'], struct: true }); + let data = null; + if (mails && mails.length > 0) { + const parts = getParts(mails[0].attributes.struct); + const part = parts.find((p) => p.partID === payload.externalId); + if (part) { + data = await connection.getPartData(mails[0], part); + } + } + return data ? new Uint8Array(data) : null; + }, + onError: () => { + return null; + }, + }); + } else { + return null; + } + }, + }); + + return content ? { mimeType: payload.mimeType, filename: payload.filename, content } : null; + } + + async send({ + accountId, + mailbox, + userName, + dto, + replyToMessage, + attachments, + }: { + accountId: number; + mailbox: Mailbox; + userName: string; + dto: SendMailMessageDto; + replyToMessage?: MailMessage | null; + attachments: StorageFile[]; + }): Promise { + try { + const settings = await this.repository.findOneBy({ accountId, mailboxId: mailbox.id }); + const transporter = nodemailer.createTransport({ + host: settings.smtpServer, + port: settings.smtpPort, + secure: settings.smtpSecure, + auth: { + user: mailbox.email, + pass: settings.password, + }, + }); + + const mail = await this.mailMessageBuilder.createNodemailerMessage( + mailbox.email, + userName, + dto, + replyToMessage, + attachments, + ); + const { messageId } = await transporter.sendMail(mail); + if (messageId) { + mail.messageId = messageId; + const appended = await this.appendToFolder(accountId, mailbox, settings, MailboxFolderType.Sent, mail); + if (appended) { + return await this.createExternalMessage(uuidv4(), appended.message, appended.folderExternalId, true); + } + } + } catch (e) { + const error = e as Error; + this.logger.error(`SMTP send message error for mailbox ${mailbox.id}: ${error?.message}`, error?.stack); + } + + return null; + } + + private async processMailbox({ + connection, + mailbox, + syncInfo, + fromDate, + }: { + connection: ImapSimple; + mailbox: Mailbox; + syncInfo: ImapSyncInfo[]; + fromDate: Date; + }): Promise { + const sync: ImapSyncInfo[] = []; + const boxes = await connection.getBoxes(); + const boxesNames = Object.getOwnPropertyNames(boxes); + const folders: MailboxFolderExternal[] = []; + const messages: MailMessageExternal[] = []; + + for (const boxName of boxesNames) { + await this.withBox({ + mailboxId: mailbox.id, + connection, + boxName, + autoExpunge: true, + action: async (box) => { + folders.push(this.convertToExternalFolder(box)); + const search: unknown[] = ['ALL']; + let uidTo: number | undefined = undefined; + const boxSyncInfo = syncInfo.find((si) => si.boxName === box.name); + if (boxSyncInfo) { + if (boxSyncInfo.uidnext >= box.uidnext) { + // No new mail in box + sync.push({ boxName: box.name, uidnext: box.uidnext }); + return; + } + uidTo = Math.min(boxSyncInfo.uidnext + this._config.manual.searchBatchSize, box.uidnext); + search.push(['UID', `${boxSyncInfo.uidnext}:${uidTo}`]); + } else { + search.push(['SINCE', fromDate]); + } + const mails = await withTimeout( + connection.search(search, { size: true, struct: true, bodies: ['HEADER'] }), + this._config.manual.searchTimeout, + [], + ); + for (const mail of mails) { + if (!boxSyncInfo || boxSyncInfo.uidnext <= mail.attributes.uid) { + const message = await this.processMessage({ connection, folderName: box.name, message: mail }); + if (message) messages.push(message); + } + } + sync.push({ boxName: box.name, uidnext: Math.min((uidTo ?? 0) + 1, box.uidnext) }); + }, + }); + } + + await this.mailboxFolderService.processExternal({ + accountId: mailbox.accountId, + mailboxId: mailbox.id, + extFolders: folders, + }); + await this.mailMessageService.processExternalMessages({ accountId: mailbox.accountId, mailbox, added: messages }); + + return sync; + } + + private async processMessage({ + connection, + folderName, + message, + }: { + connection: ImapSimple; + folderName: string; + message: Message; + }): Promise { + const header = message.parts.find((p) => p.which === 'HEADER'); + if (!header || !header.body) return null; + + const from = cleanString((header.body['from'] as string[])?.join(', ')); + const to = cleanString((header.body['to'] as string[])?.join(', ')); + const replyTo = cleanString((header.body['reply-to'] as string[])?.join(', ')); + const cc = cleanString((header.body['cc'] as string[])?.join(', ')); + const subject = cleanString((header.body['subject'] as string[])?.join(', ')); + let date = new Date(header.body['date']?.[0] as string); + if (!date || isNaN(date.getTime())) { + date = new Date(); + } + const messageId = header.body['message-id']?.[0] as string; + const inReplyTo = header.body['in-reply-to']?.[0] as string; + const references = (header.body['references'] as string[]) + ?.join(',') + ?.split(',') + ?.map((i) => i.trim()); + const isSeen = message.attributes.flags.includes(`\\Seen`); + const entityId = header.body[HEADER_ENTITY_ID] ? parseInt(header.body[HEADER_ENTITY_ID][0] as string) : null; + + const parts = getParts(message.attributes.struct); + const { hasAttachment, payloads } = await this.getMessagePayloads({ connection, message, parts }); + const snippet = this.getMessageSnippet(payloads); + + return { + id: this.formatExternalId(folderName, message.attributes.uid), + threadId: null, + snippet: snippet, + sentFrom: { text: from, values: addressparser(from, { flatten: true }) }, + sentTo: to ? { text: to, values: addressparser(to, { flatten: true }) } : null, + replyTo: replyTo ? { text: replyTo, values: addressparser(replyTo, { flatten: true }) } : null, + cc: cc ? { text: cc, values: addressparser(cc, { flatten: true }) } : null, + subject: subject, + date: date, + hasAttachment: hasAttachment, + messageId: messageId, + inReplyTo: inReplyTo, + references: references, + isSeen: isSeen, + entityId: entityId, + folders: [folderName], + payloads: payloads, + }; + } + + private async getMessagePayloads({ + connection, + message, + parts, + }: { + connection: ImapSimple; + message: Message; + parts: any[]; + }): Promise<{ + hasAttachment: boolean; + payloads: MailMessagePayloadExternal[]; + }> { + let hasAttachment = false; + const payloads: MailMessagePayloadExternal[] = []; + for (const part of parts) { + hasAttachment ||= part.type !== 'text'; + const mimeType = `${part.type}/${part.subtype}`; + if (part.type === 'text') { + const partData = part.size + ? await withTimeout(connection.getPartData(message, part), this._config.manual.partLoadTimeout, null) + : null; + const content = partData ? cleanString(partData.toString()) : null; + payloads.push({ id: part.partID, mimeType, filename: null, attachmentId: part.id, content, size: part.size }); + } else { + let fileName = null; + if (part.params?.name) { + fileName = StringUtil.decodeMimeWord(part.params.name); + } + if (!fileName && part.disposition?.params?.filename) { + fileName = StringUtil.decodeRFC5987(part.disposition.params.filename); + } + payloads.push({ + id: part.partID, + mimeType, + filename: fileName, + attachmentId: part.id, + content: null, + size: part.size, + }); + } + } + + return { hasAttachment, payloads }; + } + + private getMessageSnippet(payloads: MailMessagePayloadExternal[]): string { + const plain = payloads.find((p) => p.mimeType === 'text/plain'); + if (plain && plain.content) { + return plain.content.trim().substring(0, 150).trim(); + } + + const html = payloads.find((p) => p.mimeType === 'text/html'); + if (html && html.content) { + return convert(html.content)?.trim()?.substring(0, 150)?.trim(); + } + + return ''; + } + + private convertToExternalFolder(box: Imap.Box): MailboxFolderExternal { + return { + id: box.name, + name: capitalizeFirst(box.name), + uidValidity: box.uidvalidity, + type: detectMailboxFolderType({ name: box.name }), + }; + } + + private async createExternalMessage( + id: number | string, + raw: Source, + boxName: string, + isSeen: boolean, + ): Promise { + const parsed = await simpleParser(raw); + if (!parsed) { + return null; + } + + const { from, to, replyTo, cc, subject, date, messageId, inReplyTo, references, text, html, attachments, headers } = + parsed; + const messageDate = date ?? DateUtil.now(); + const snippet = text ? text.substring(0, 150).trim() : ''; + const entityId: number | null = headers.has(HEADER_ENTITY_ID) + ? parseInt(headers.get(HEADER_ENTITY_ID) as string) + : null; + const payloads = this.formatAsExternalPayload(text, html, attachments); + return { + id: this.formatExternalId(boxName, id), + threadId: null, + snippet, + sentFrom: this.convertAddress(from), + sentTo: this.convertAddress(to), + replyTo: this.convertAddress(replyTo), + cc: this.convertAddress(cc), + subject, + date: messageDate, + hasAttachment: attachments && attachments.length > 0, + messageId, + inReplyTo: inReplyTo, + references: typeof references === 'string' ? [references] : references, + isSeen, + entityId, + folders: [boxName], + payloads, + }; + } + + private convertAddress(addresses: AddressObject | AddressObject[] | null | undefined): EmailAddress | null { + if (!addresses) { + return null; + } + + if (Array.isArray(addresses)) { + const texts: string[] = []; + const values: EmailAddressValue[] = []; + for (const address of addresses) { + if (address) { + texts.push(address.text); + values.push(...address.value.filter((v) => v?.address).map((v) => ({ address: v.address, name: v.name }))); + } + } + return { text: texts.join(', '), values }; + } + + return { + text: addresses.text, + values: addresses.value.filter((v) => v?.address).map((v) => ({ address: v.address, name: v.name })), + }; + } + + private formatAsExternalPayload( + text: string, + html: string | boolean, + attachments: any[], + ): MailMessagePayloadExternal[] { + const payloads: MailMessagePayloadExternal[] = []; + if (text && text.trim()) { + payloads.push({ + id: null, + mimeType: 'text/plain', + filename: null, + attachmentId: null, + content: text, + size: text.length, + }); + } + if (html && typeof html === 'string') { + payloads.push({ + id: null, + mimeType: 'text/html', + filename: null, + attachmentId: null, + content: html.trim(), + size: html.trim().length, + }); + } + if (attachments && attachments.length > 0) { + attachments.forEach((a) => + payloads.push({ + id: a.partId, + mimeType: a.contentType, + filename: a.filename, + attachmentId: a.id, + content: null, + size: a.size, + }), + ); + } + return payloads; + } + + private formatExternalId(boxName: string, id: number | string): string { + return `${boxName}-${id}`; + } + private parseMessageId(externalId: string): number { + return Number(externalId.slice(externalId.lastIndexOf('-') + 1)); + } + + private async appendToFolder( + accountId: number, + mailbox: Mailbox, + settings: MailboxSettingsManual, + type: MailboxFolderType, + mail: Mail.Options, + ): Promise<{ message: string; folderExternalId: string } | null> { + return await this.withConnection({ + email: mailbox.email, + settings: settings, + action: async (connection) => { + const folder = await this.mailboxFolderService.findOne({ accountId, mailboxId: mailbox.id, type }); + if (folder) { + const message = await this.mailMessageBuilder.createRawMessage(mail, 'utf-8'); + connection.append(message, { mailbox: folder.externalId }); + return { message, folderExternalId: folder.externalId }; + } + return null; + }, + }); + } + + async setSeen({ + mailbox, + seen, + messages, + }: { + accountId: number; + mailbox: Mailbox; + seen: boolean; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.changeFlags(mailbox, messages, ['\\Seen'], seen ? 'add' : 'del'); + } else { + throw new NotImplementedException(); + } + } + + private async changeFlags( + mailbox: Mailbox, + groupedMessages: FolderMessages[], + flags: string[], + action: 'add' | 'del', + ): Promise { + return await this.withConnection({ + email: mailbox.email, + mailboxId: mailbox.id, + action: async (connection) => { + for (const { folderId, messageIds } of groupedMessages) { + await this.withBox({ + mailboxId: mailbox.id, + connection, + boxName: folderId, + autoExpunge: true, + action: async () => { + const parsedIds = messageIds.map((m) => this.parseMessageId(m)); + switch (action) { + case 'add': + await connection.addFlags(parsedIds, flags); + break; + case 'del': + await connection.delFlags(parsedIds, flags); + break; + } + }, + }); + } + return true; + }, + onError: () => { + return false; + }, + }); + } + + async trash({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox.accountId, mailbox, messages, MailboxFolderType.Trash); + } else { + throw new NotImplementedException(); + } + } + async untrash({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox.accountId, mailbox, messages, MailboxFolderType.Inbox); + } else { + throw new NotImplementedException(); + } + } + + async spam({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox.accountId, mailbox, messages, MailboxFolderType.Junk); + } else { + throw new NotImplementedException(); + } + } + async unspam({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox.accountId, mailbox, messages, MailboxFolderType.Inbox); + } else { + throw new NotImplementedException(); + } + } + + private async moveMessages( + accountId: number, + mailbox: Mailbox, + groupedMessages: FolderMessages[], + type: MailboxFolderType, + ): Promise { + return await this.withConnection({ + email: mailbox.email, + mailboxId: mailbox.id, + action: async (connection) => { + const toFolder = await this.mailboxFolderService.findOne({ accountId, mailboxId: mailbox.id, type }); + if (toFolder) { + for (const { folderId, messageIds } of groupedMessages) { + const parsedIds = messageIds.map((m) => this.parseMessageId(m).toString()); + await this.withBox({ + mailboxId: mailbox.id, + connection, + boxName: folderId, + autoExpunge: true, + action: async () => { + await connection.moveMessage(parsedIds, toFolder.externalId); + }, + }); + } + } + return true; + }, + onError: () => { + return false; + }, + }); + } +} diff --git a/backend/src/Mailing/common/dto/delete-mailbox-query.ts b/backend/src/Mailing/common/dto/delete-mailbox-query.ts new file mode 100644 index 0000000..3f996b0 --- /dev/null +++ b/backend/src/Mailing/common/dto/delete-mailbox-query.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class DeleteMailboxQuery { + @ApiPropertyOptional({ description: 'Save mailbox mail' }) + @IsOptional() + @IsBoolean() + save?: boolean; +} diff --git a/backend/src/Mailing/common/dto/index.ts b/backend/src/Mailing/common/dto/index.ts new file mode 100644 index 0000000..3115ed1 --- /dev/null +++ b/backend/src/Mailing/common/dto/index.ts @@ -0,0 +1,2 @@ +export * from './delete-mailbox-query'; +export * from './send-mail-message.dto'; diff --git a/backend/src/Mailing/common/dto/send-mail-message.dto.ts b/backend/src/Mailing/common/dto/send-mail-message.dto.ts new file mode 100644 index 0000000..1fcc638 --- /dev/null +++ b/backend/src/Mailing/common/dto/send-mail-message.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +export class SendMailMessageDto { + @ApiProperty() + sentTo: string[]; + + @ApiProperty() + cc: string[] | null; + + @ApiProperty() + bcc: string[] | null; + + @ApiProperty() + replyTo: string | null; + + @ApiProperty() + subject: string | null; + + @ApiProperty() + contentText: string | null; + + @ApiProperty() + contentHtml: string | null; + + @ApiProperty() + replyToMessageId: number | null; + + @ApiProperty() + entityId: number | null; + + @ApiProperty() + @IsOptional() + @IsArray() + fileIds: string[] | null | undefined; +} diff --git a/backend/src/Mailing/common/enums/index.ts b/backend/src/Mailing/common/enums/index.ts new file mode 100644 index 0000000..2b431ee --- /dev/null +++ b/backend/src/Mailing/common/enums/index.ts @@ -0,0 +1,3 @@ +export * from './mailbox-folder-type.enum'; +export * from './mailbox-provider.enum'; +export * from './mailbox-state.enum'; diff --git a/backend/src/Mailing/common/enums/mailbox-folder-type.enum.ts b/backend/src/Mailing/common/enums/mailbox-folder-type.enum.ts new file mode 100644 index 0000000..d76608b --- /dev/null +++ b/backend/src/Mailing/common/enums/mailbox-folder-type.enum.ts @@ -0,0 +1,10 @@ +export enum MailboxFolderType { + Inbox = 'inbox', + Sent = 'sent', + Trash = 'trash', + Drafts = 'drafts', + Junk = 'junk', + Archive = 'archive', + Flagged = 'flagged', + All = 'all', +} diff --git a/backend/src/Mailing/common/enums/mailbox-provider.enum.ts b/backend/src/Mailing/common/enums/mailbox-provider.enum.ts new file mode 100644 index 0000000..9a4e17f --- /dev/null +++ b/backend/src/Mailing/common/enums/mailbox-provider.enum.ts @@ -0,0 +1,4 @@ +export enum MailboxProvider { + Manual = 'manual', + GMail = 'gmail', +} diff --git a/backend/src/Mailing/common/enums/mailbox-state.enum.ts b/backend/src/Mailing/common/enums/mailbox-state.enum.ts new file mode 100644 index 0000000..7440469 --- /dev/null +++ b/backend/src/Mailing/common/enums/mailbox-state.enum.ts @@ -0,0 +1,8 @@ +export enum MailboxState { + Draft = 'draft', + Init = 'init', + Active = 'active', + Inactive = 'inactive', + Deleted = 'deleted', + Sync = 'sync', +} diff --git a/backend/src/Mailing/common/events/index.ts b/backend/src/Mailing/common/events/index.ts new file mode 100644 index 0000000..603e1d9 --- /dev/null +++ b/backend/src/Mailing/common/events/index.ts @@ -0,0 +1,3 @@ +export * from './mail-event-type.enum'; +export * from './mail-message'; +export * from './mailbox'; diff --git a/backend/src/Mailing/common/events/mail-event-type.enum.ts b/backend/src/Mailing/common/events/mail-event-type.enum.ts new file mode 100644 index 0000000..e321820 --- /dev/null +++ b/backend/src/Mailing/common/events/mail-event-type.enum.ts @@ -0,0 +1,6 @@ +export enum MailEventType { + MailboxDeleted = 'mail:mailbox:deleted', + MailMessageDeleted = 'mail:message:deleted', + MailMessageLinked = 'mail:message:linked', + MailMessageReceived = 'mail:message:received', +} diff --git a/backend/src/Mailing/common/events/mail-message/index.ts b/backend/src/Mailing/common/events/mail-message/index.ts new file mode 100644 index 0000000..548d2cf --- /dev/null +++ b/backend/src/Mailing/common/events/mail-message/index.ts @@ -0,0 +1,2 @@ +export * from './mail-message-received.event'; +export * from './mail-message.event'; diff --git a/backend/src/Mailing/common/events/mail-message/mail-message-received.event.ts b/backend/src/Mailing/common/events/mail-message/mail-message-received.event.ts new file mode 100644 index 0000000..7db6a81 --- /dev/null +++ b/backend/src/Mailing/common/events/mail-message/mail-message-received.event.ts @@ -0,0 +1,26 @@ +import { MailMessageEvent } from './mail-message.event'; + +export class MailMessageReceivedEvent extends MailMessageEvent { + isInbox: boolean; + ownerId: number; + messageSubject: string; + messageSnippet: string; + + constructor({ + accountId, + ownerId, + entityId, + messageId, + messageSubject, + messageSnippet, + messageDate, + isInbox, + }: MailMessageReceivedEvent) { + super({ accountId, entityId, messageId, messageDate }); + + this.ownerId = ownerId; + this.messageSubject = messageSubject; + this.messageSnippet = messageSnippet; + this.isInbox = isInbox; + } +} diff --git a/backend/src/Mailing/common/events/mail-message/mail-message.event.ts b/backend/src/Mailing/common/events/mail-message/mail-message.event.ts new file mode 100644 index 0000000..892be47 --- /dev/null +++ b/backend/src/Mailing/common/events/mail-message/mail-message.event.ts @@ -0,0 +1,13 @@ +export class MailMessageEvent { + accountId: number; + entityId: number | null; + messageId: number; + messageDate: string; + + constructor({ accountId, entityId, messageId, messageDate }: MailMessageEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.messageId = messageId; + this.messageDate = messageDate; + } +} diff --git a/backend/src/Mailing/common/events/mailbox/index.ts b/backend/src/Mailing/common/events/mailbox/index.ts new file mode 100644 index 0000000..cf0aa44 --- /dev/null +++ b/backend/src/Mailing/common/events/mailbox/index.ts @@ -0,0 +1 @@ +export * from './mailbox.event'; diff --git a/backend/src/Mailing/common/events/mailbox/mailbox.event.ts b/backend/src/Mailing/common/events/mailbox/mailbox.event.ts new file mode 100644 index 0000000..ab714b0 --- /dev/null +++ b/backend/src/Mailing/common/events/mailbox/mailbox.event.ts @@ -0,0 +1,9 @@ +export class MailboxEvent { + accountId: number; + mailboxId: number; + + constructor({ accountId, mailboxId }: MailboxEvent) { + this.accountId = accountId; + this.mailboxId = mailboxId; + } +} diff --git a/backend/src/Mailing/common/index.ts b/backend/src/Mailing/common/index.ts new file mode 100644 index 0000000..5f7094d --- /dev/null +++ b/backend/src/Mailing/common/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './enums'; +export * from './events'; +export * from './types'; +export * from './utils'; diff --git a/backend/src/Mailing/common/types/email-address.ts b/backend/src/Mailing/common/types/email-address.ts new file mode 100644 index 0000000..4cec2d1 --- /dev/null +++ b/backend/src/Mailing/common/types/email-address.ts @@ -0,0 +1,9 @@ +export interface EmailAddressValue { + address?: string; + name?: string; +} + +export interface EmailAddress { + text: string; + values: EmailAddressValue[]; +} diff --git a/backend/src/Mailing/common/types/folder-messages.ts b/backend/src/Mailing/common/types/folder-messages.ts new file mode 100644 index 0000000..51111cd --- /dev/null +++ b/backend/src/Mailing/common/types/folder-messages.ts @@ -0,0 +1,4 @@ +export interface FolderMessages { + folderId: string; + messageIds: string[]; +} diff --git a/backend/src/Mailing/common/types/imap-sync-info.ts b/backend/src/Mailing/common/types/imap-sync-info.ts new file mode 100644 index 0000000..048730c --- /dev/null +++ b/backend/src/Mailing/common/types/imap-sync-info.ts @@ -0,0 +1,4 @@ +export class ImapSyncInfo { + boxName: string; + uidnext: number; +} diff --git a/backend/src/Mailing/common/types/index.ts b/backend/src/Mailing/common/types/index.ts new file mode 100644 index 0000000..824e8de --- /dev/null +++ b/backend/src/Mailing/common/types/index.ts @@ -0,0 +1,9 @@ +export * from './email-address'; +export * from './folder-messages'; +export * from './imap-sync-info'; +export * from './mail-message-attachment.dto'; +export * from './mail-message-external'; +export * from './mail-message-payload-external'; +export * from './mailbox-folder-external'; +export * from './mailbox-sync-messages'; +export * from './mailbox-sync-result'; diff --git a/backend/src/Mailing/common/types/mail-message-attachment.dto.ts b/backend/src/Mailing/common/types/mail-message-attachment.dto.ts new file mode 100644 index 0000000..116c8df --- /dev/null +++ b/backend/src/Mailing/common/types/mail-message-attachment.dto.ts @@ -0,0 +1,5 @@ +export interface MailMessageAttachment { + mimeType: string; + filename: string; + content: Uint8Array | Buffer; +} diff --git a/backend/src/Mailing/common/types/mail-message-external.ts b/backend/src/Mailing/common/types/mail-message-external.ts new file mode 100644 index 0000000..058f674 --- /dev/null +++ b/backend/src/Mailing/common/types/mail-message-external.ts @@ -0,0 +1,22 @@ +import { EmailAddress } from './email-address'; +import { MailMessagePayloadExternal } from './mail-message-payload-external'; + +export interface MailMessageExternal { + id: string; + threadId: string | null; + snippet: string | null; + sentFrom: EmailAddress | null; + sentTo: EmailAddress | null; + replyTo: EmailAddress | null; + cc: EmailAddress | null; + subject: string | null; + date: Date; + hasAttachment: boolean; + messageId: string | null; + inReplyTo: string | null; + references: string[] | null; + isSeen: boolean; + entityId: number | null; + folders: string[]; + payloads: MailMessagePayloadExternal[]; +} diff --git a/backend/src/Mailing/common/types/mail-message-payload-external.ts b/backend/src/Mailing/common/types/mail-message-payload-external.ts new file mode 100644 index 0000000..aa237f4 --- /dev/null +++ b/backend/src/Mailing/common/types/mail-message-payload-external.ts @@ -0,0 +1,8 @@ +export class MailMessagePayloadExternal { + id: string | null; + mimeType: string; + filename: string | null; + attachmentId: string | null; + content: string | null; + size: number | null; +} diff --git a/backend/src/Mailing/common/types/mailbox-folder-external.ts b/backend/src/Mailing/common/types/mailbox-folder-external.ts new file mode 100644 index 0000000..67c71e0 --- /dev/null +++ b/backend/src/Mailing/common/types/mailbox-folder-external.ts @@ -0,0 +1,10 @@ +import { MailboxFolderType } from '../enums'; + +export interface MailboxFolderExternal { + id: string; + uidValidity?: number | null; + uidNext?: number | null; + name: string; + type?: MailboxFolderType | null; + folders?: MailboxFolderExternal[] | null; +} diff --git a/backend/src/Mailing/common/types/mailbox-sync-messages.ts b/backend/src/Mailing/common/types/mailbox-sync-messages.ts new file mode 100644 index 0000000..1407839 --- /dev/null +++ b/backend/src/Mailing/common/types/mailbox-sync-messages.ts @@ -0,0 +1,7 @@ +import { MailMessageExternal } from './mail-message-external'; + +export interface MailboxSyncMessages { + added?: MailMessageExternal[] | null; + updated?: MailMessageExternal[] | null; + deleted?: string[] | null; +} diff --git a/backend/src/Mailing/common/types/mailbox-sync-result.ts b/backend/src/Mailing/common/types/mailbox-sync-result.ts new file mode 100644 index 0000000..a711305 --- /dev/null +++ b/backend/src/Mailing/common/types/mailbox-sync-result.ts @@ -0,0 +1,9 @@ +import { MailboxFolderExternal } from './mailbox-folder-external'; +import { MailboxSyncMessages } from './mailbox-sync-messages'; + +export interface MailboxSyncResult { + result: boolean; + message?: string | null; + folders?: MailboxFolderExternal[] | null; + messages?: MailboxSyncMessages | null; +} diff --git a/backend/src/Mailing/common/utils/index.ts b/backend/src/Mailing/common/utils/index.ts new file mode 100644 index 0000000..cebf0a2 --- /dev/null +++ b/backend/src/Mailing/common/utils/index.ts @@ -0,0 +1 @@ +export * from './mailbox-folder-type.util'; diff --git a/backend/src/Mailing/common/utils/mailbox-folder-type.util.ts b/backend/src/Mailing/common/utils/mailbox-folder-type.util.ts new file mode 100644 index 0000000..c135870 --- /dev/null +++ b/backend/src/Mailing/common/utils/mailbox-folder-type.util.ts @@ -0,0 +1,78 @@ +/* eslint-disable prettier/prettier */ +import { MailboxFolderType } from '../enums'; + +const SpecialUseToFolderType: Record = { + '\\Inbox': MailboxFolderType.Inbox, + '\\Sent': MailboxFolderType.Sent, + '\\Trash': MailboxFolderType.Trash, + '\\Drafts': MailboxFolderType.Drafts, + '\\Junk': MailboxFolderType.Junk, + '\\Archive': MailboxFolderType.Archive, + '\\All': MailboxFolderType.All, + '\\Flagged': MailboxFolderType.Flagged, +}; + +const CommonNamesMap: Record = { + // Inbox + 'inbox': MailboxFolderType.Inbox, + 'входящие': MailboxFolderType.Inbox, + 'boîte de réception': MailboxFolderType.Inbox, + + // Sent + 'sent': MailboxFolderType.Sent, + 'отправленные': MailboxFolderType.Sent, + 'envoyés': MailboxFolderType.Sent, + + // Trash + 'trash': MailboxFolderType.Trash, + 'корзина': MailboxFolderType.Trash, + 'corbeille': MailboxFolderType.Trash, + + // Drafts + 'draft': MailboxFolderType.Drafts, + 'drafts': MailboxFolderType.Drafts, + 'черновики': MailboxFolderType.Drafts, + 'brouillons': MailboxFolderType.Drafts, + + // Junk + 'junk': MailboxFolderType.Junk, + 'spam': MailboxFolderType.Junk, + 'спам': MailboxFolderType.Junk, + 'courrier indésirable': MailboxFolderType.Junk, + + // Archive + 'archive': MailboxFolderType.Archive, + 'архив': MailboxFolderType.Archive, + 'archives': MailboxFolderType.Archive, + + // All + 'all mail': MailboxFolderType.All, + '[gmail]/all mail': MailboxFolderType.All, + + // Flagged + 'flagged': MailboxFolderType.Flagged, + 'important': MailboxFolderType.Flagged, + 'starred': MailboxFolderType.Flagged, +}; + +export const detectMailboxFolderType = ({ + specialUse, + name, +}: { + specialUse?: string | null; + name?: string; +}): MailboxFolderType | null => { + if (specialUse) { + const normalizedSpecialUse = specialUse.trim(); + const mapped = SpecialUseToFolderType[normalizedSpecialUse]; + if (mapped) return mapped; + } + + if (name) { + const normalizedName = name.trim().toLowerCase(); + const mapped = CommonNamesMap[normalizedName]; + if (mapped) return mapped; + } + + return undefined; +}; diff --git a/backend/src/Mailing/config/index.ts b/backend/src/Mailing/config/index.ts new file mode 100644 index 0000000..e98842c --- /dev/null +++ b/backend/src/Mailing/config/index.ts @@ -0,0 +1 @@ +export * from './mail.config'; diff --git a/backend/src/Mailing/config/mail.config.ts b/backend/src/Mailing/config/mail.config.ts new file mode 100644 index 0000000..2f8f5c6 --- /dev/null +++ b/backend/src/Mailing/config/mail.config.ts @@ -0,0 +1,52 @@ +import { registerAs } from '@nestjs/config'; + +interface Mailing { + host: string; + port: number; + secure: boolean; + user: string; + password: string; + from: string; + replyTo: string; +} + +interface GMail { + apiClientId: string; + apiClientSecret: string; +} + +interface Manual { + searchTimeout: number; + searchBatchSize: number; + partLoadTimeout: number; +} + +export interface MailConfig { + mailing: Mailing; + gmail: GMail; + manual: Manual; +} + +export default registerAs( + 'mail', + (): MailConfig => ({ + mailing: { + host: process.env.MAILING_HOST, + port: parseInt(process.env.MAILING_PORT, 10) || 465, + secure: process.env.MAILING_SECURE === 'on', + user: process.env.MAILING_USER, + password: process.env.MAILING_PASSWORD, + from: process.env.MAILING_FROM, + replyTo: process.env.MAILING_REPLY_TO, + }, + gmail: { + apiClientId: process.env.GMAIL_API_CLIENT_ID, + apiClientSecret: process.env.GMAIL_API_CLIENT_SECRET, + }, + manual: { + searchTimeout: parseInt(process.env.MAIL_MANUAL_SEARCH_TIMEOUT, 10) || 30000, + searchBatchSize: parseInt(process.env.MAIL_MANUAL_SEARCH_BATCH_SIZE, 10) || 100, + partLoadTimeout: parseInt(process.env.MAIL_MANUAL_PART_LOAD_TIMEOUT, 10) || 15000, + }, + }), +); diff --git a/backend/src/Mailing/mail-message-builder/index.ts b/backend/src/Mailing/mail-message-builder/index.ts new file mode 100644 index 0000000..4e9eceb --- /dev/null +++ b/backend/src/Mailing/mail-message-builder/index.ts @@ -0,0 +1 @@ +export * from './mail-message-builder.service'; diff --git a/backend/src/Mailing/mail-message-builder/mail-message-builder.service.ts b/backend/src/Mailing/mail-message-builder/mail-message-builder.service.ts new file mode 100644 index 0000000..2a3bdbd --- /dev/null +++ b/backend/src/Mailing/mail-message-builder/mail-message-builder.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@nestjs/common'; +import Mail, { TextEncoding } from 'nodemailer/lib/mailer'; +import MailComposer from 'nodemailer/lib/mail-composer'; + +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { SendMailMessageDto } from '../common'; +import { MailMessage } from '../Model/MailMessage/MailMessage'; + +export const HEADER_ENTITY_ID = 'x-amwork-entityid'; + +@Injectable() +export class MailMessageBuilderService { + async createNodemailerMessage( + fromEmail: string, + fromName: string, + dto: SendMailMessageDto, + replyToMessage: MailMessage | null, + files: StorageFile[] | null, + ): Promise { + const attachments = files + ? files.map((file) => { + return { + filename: file.originalName, + encoding: file.encoding, + contentType: file.mimeType, + content: file.buffer, + }; + }) + : undefined; + const references = replyToMessage?.referencesTo ? replyToMessage.referencesTo : []; + if (replyToMessage?.messageId) { + references.push(replyToMessage.messageId); + } + const headers = dto.entityId ? [{ key: HEADER_ENTITY_ID, value: dto.entityId.toString() }] : undefined; + const options = { + from: { name: fromName, address: fromEmail }, + to: dto.sentTo.join(','), + cc: dto.cc?.join(','), + bcc: dto.bcc?.join(','), + replyTo: dto.replyTo, + inReplyTo: replyToMessage?.messageId, + references: references && references.length > 0 ? references : undefined, + subject: dto.subject, + text: dto.contentText, + html: dto.contentHtml, + textEncoding: 'base64' as TextEncoding, + headers: headers, + attachments, + }; + return options; + } + + async createRawMessage(options: Mail.Options, encoding: BufferEncoding = 'base64url') { + const mail = new MailComposer(options).compile(); + mail['keepBcc'] = true; + return (await mail.build()).toString(encoding); + } +} diff --git a/backend/src/Mailing/mail-message-payload/dto/index.ts b/backend/src/Mailing/mail-message-payload/dto/index.ts new file mode 100644 index 0000000..5f4e2df --- /dev/null +++ b/backend/src/Mailing/mail-message-payload/dto/index.ts @@ -0,0 +1 @@ +export * from './mail-message-payload.dto'; diff --git a/backend/src/Mailing/mail-message-payload/dto/mail-message-payload.dto.ts b/backend/src/Mailing/mail-message-payload/dto/mail-message-payload.dto.ts new file mode 100644 index 0000000..588f56a --- /dev/null +++ b/backend/src/Mailing/mail-message-payload/dto/mail-message-payload.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class MailMessagePayloadDto { + @ApiProperty() + id: number; + + @ApiProperty() + mimeType: string; + + @ApiProperty() + filename: string | null; + + @ApiProperty() + content: string | null; + + @ApiProperty() + size: number | null; + + @ApiProperty() + sortOrder: number; +} diff --git a/backend/src/Mailing/mail-message-payload/entities/index.ts b/backend/src/Mailing/mail-message-payload/entities/index.ts new file mode 100644 index 0000000..eecd98b --- /dev/null +++ b/backend/src/Mailing/mail-message-payload/entities/index.ts @@ -0,0 +1 @@ +export * from './mail-message-payload.entity'; diff --git a/backend/src/Mailing/mail-message-payload/entities/mail-message-payload.entity.ts b/backend/src/Mailing/mail-message-payload/entities/mail-message-payload.entity.ts new file mode 100644 index 0000000..7e16cbb --- /dev/null +++ b/backend/src/Mailing/mail-message-payload/entities/mail-message-payload.entity.ts @@ -0,0 +1,89 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { MailMessagePayloadExternal } from '../../common'; +import { MailMessagePayloadDto } from '../dto'; + +@Entity() +export class MailMessagePayload { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + messageId: number; + + @Column({ nullable: true }) + externalId: string | null; + + @Column() + mimeType: string; + + @Column({ nullable: true }) + filename: string | null; + + @Column({ nullable: true }) + attachment: string | null; + + @Column({ nullable: true }) + content: string | null; + + @Column({ nullable: true }) + size: number | null; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + constructor( + accountId: number, + messageId: number, + externalId: string | null, + mimeType: string, + filename: string | null, + attachment: string | null, + content: string | null, + size: number | null, + sortOrder: number, + ) { + this.accountId = accountId; + this.messageId = messageId; + this.externalId = externalId; + this.mimeType = mimeType; + this.filename = filename; + this.attachment = attachment; + this.content = content; + this.size = size; + this.sortOrder = sortOrder; + } + + static fromExternal( + accountId: number, + messageId: number, + sortOrder: number, + payload: MailMessagePayloadExternal, + ): MailMessagePayload { + return new MailMessagePayload( + accountId, + messageId, + payload.id, + payload.mimeType, + payload.filename, + payload.attachmentId, + payload.content, + payload.size, + sortOrder, + ); + } + + toDto(): MailMessagePayloadDto { + return { + id: this.id, + mimeType: this.mimeType, + filename: this.filename, + content: this.content, + size: this.size, + sortOrder: this.sortOrder, + }; + } +} diff --git a/backend/src/Mailing/mail-message-payload/index.ts b/backend/src/Mailing/mail-message-payload/index.ts new file mode 100644 index 0000000..5c51fcc --- /dev/null +++ b/backend/src/Mailing/mail-message-payload/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './entities'; +export * from './mail-message-payload.service'; diff --git a/backend/src/Mailing/mail-message-payload/mail-message-payload.service.ts b/backend/src/Mailing/mail-message-payload/mail-message-payload.service.ts new file mode 100644 index 0000000..8e3b0eb --- /dev/null +++ b/backend/src/Mailing/mail-message-payload/mail-message-payload.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { MailMessageAttachment, MailMessagePayloadExternal } from '../common'; +import { MailProviderRegistry } from '../mail-provider'; +import { Mailbox } from '../mailbox'; +import { MailMessage } from '../Model/MailMessage/MailMessage'; + +import { MailMessagePayload } from './entities'; + +@Injectable() +export class MailMessagePayloadService { + constructor( + @InjectRepository(MailMessagePayload) + private readonly repository: Repository, + private readonly mailProviderRegistry: MailProviderRegistry, + ) {} + + async findByMessageId(accountId: number, messageId: number): Promise { + return this.repository.findBy({ accountId, messageId }); + } + + async findByMessageIds(accountId: number, messageIds: number[]): Promise { + return this.repository.findBy({ accountId, messageId: In(messageIds) }); + } + + async getAttachment( + accountId: number, + mailbox: Mailbox, + message: MailMessage, + payloadId: number, + ): Promise { + const payload = await this.repository.findOneBy({ accountId, messageId: message.id, id: payloadId }); + if (payload) { + const provider = this.mailProviderRegistry.get(mailbox.provider); + return provider.getAttachment({ mailbox, message, payload }); + } + + return null; + } + + async processExternalPayloads(accountId: number, messageId: number, payloads: MailMessagePayloadExternal[]) { + let sortOrder = 0; + for (const payload of payloads) { + await this.repository.insert(MailMessagePayload.fromExternal(accountId, messageId, sortOrder, payload)); + ++sortOrder; + } + } +} diff --git a/backend/src/Mailing/mail-provider/decorators/index.ts b/backend/src/Mailing/mail-provider/decorators/index.ts new file mode 100644 index 0000000..dc28767 --- /dev/null +++ b/backend/src/Mailing/mail-provider/decorators/index.ts @@ -0,0 +1 @@ +export * from './mail-integration.decorator'; diff --git a/backend/src/Mailing/mail-provider/decorators/mail-integration.decorator.ts b/backend/src/Mailing/mail-provider/decorators/mail-integration.decorator.ts new file mode 100644 index 0000000..d77eb53 --- /dev/null +++ b/backend/src/Mailing/mail-provider/decorators/mail-integration.decorator.ts @@ -0,0 +1,5 @@ +import { SetMetadata } from '@nestjs/common'; + +export const MAIL_PROVIDER_KEY = 'mail:provider'; + +export const MailIntegration = (provider: string): ClassDecorator => SetMetadata(MAIL_PROVIDER_KEY, provider); diff --git a/backend/src/Mailing/mail-provider/index.ts b/backend/src/Mailing/mail-provider/index.ts new file mode 100644 index 0000000..8a0185a --- /dev/null +++ b/backend/src/Mailing/mail-provider/index.ts @@ -0,0 +1,3 @@ +export * from './decorators'; +export * from './mail-provider.registry'; +export * from './types'; diff --git a/backend/src/Mailing/mail-provider/mail-provider.registry.ts b/backend/src/Mailing/mail-provider/mail-provider.registry.ts new file mode 100644 index 0000000..9a70634 --- /dev/null +++ b/backend/src/Mailing/mail-provider/mail-provider.registry.ts @@ -0,0 +1,36 @@ +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { DiscoveryService } from '@nestjs/core'; + +import { MAIL_PROVIDER_KEY } from './decorators'; +import { MailProvider } from './types'; + +@Injectable() +export class MailProviderRegistry implements OnModuleInit { + private readonly logger = new Logger(MailProviderRegistry.name); + private readonly providers = new Map(); + + constructor(private readonly discoveryService: DiscoveryService) {} + + onModuleInit() { + const providers = this.discoveryService + .getProviders() + .filter((provider) => provider.metatype && Reflect.getMetadata(MAIL_PROVIDER_KEY, provider.metatype)) + .map((provider) => ({ + instance: provider.instance as MailProvider, + type: Reflect.getMetadata(MAIL_PROVIDER_KEY, provider.metatype) as string, + })); + + for (const provider of providers) { + this.providers.set(provider.type, provider.instance); + this.logger.log(`Registered mail provider: ${provider.type}`); + } + } + + get(type: string): MailProvider { + const impl = this.providers.get(type); + if (!impl) { + throw new Error(`Mail provider not found for: ${type}`); + } + return impl; + } +} diff --git a/backend/src/Mailing/mail-provider/types/index.ts b/backend/src/Mailing/mail-provider/types/index.ts new file mode 100644 index 0000000..4f0409b --- /dev/null +++ b/backend/src/Mailing/mail-provider/types/index.ts @@ -0,0 +1 @@ +export * from './mail-provider'; diff --git a/backend/src/Mailing/mail-provider/types/mail-provider.ts b/backend/src/Mailing/mail-provider/types/mail-provider.ts new file mode 100644 index 0000000..9ac83d7 --- /dev/null +++ b/backend/src/Mailing/mail-provider/types/mail-provider.ts @@ -0,0 +1,45 @@ +import { StorageFile } from '@/modules/storage/types'; + +import { + FolderMessages, + MailboxSyncResult, + MailMessageAttachment, + MailMessageExternal, + SendMailMessageDto, +} from '../../common'; +import { Mailbox } from '../../mailbox'; +import { MailMessagePayload } from '../../mail-message-payload'; +import { MailMessage } from '../../Model/MailMessage/MailMessage'; + +export type MailProviderCapability = 'thread'; + +type ActionMessageId = { threadId: string } | FolderMessages[]; + +export interface MailProvider { + isCapable(capability: MailProviderCapability): boolean; + + sync(params: { mailbox: Mailbox; syncFull?: boolean; syncDate?: Date }): Promise; + + getAttachment(params: { + mailbox: Mailbox; + message: MailMessage; + payload: MailMessagePayload; + }): Promise; + + send(params: { + accountId: number; + mailbox: Mailbox; + userName: string; + dto: SendMailMessageDto; + replyToMessage?: MailMessage | null; + attachments: StorageFile[]; + }): Promise; + + setSeen(params: { mailbox: Mailbox; seen: boolean; messages: ActionMessageId }): Promise; + + trash(params: { mailbox: Mailbox; messages: ActionMessageId }): Promise; + untrash(params: { mailbox: Mailbox; messages: ActionMessageId }): Promise; + + spam(params: { mailbox: Mailbox; messages: ActionMessageId }): Promise; + unspam(params: { mailbox: Mailbox; messages: ActionMessageId }): Promise; +} diff --git a/backend/src/Mailing/mailbox-folder/dto/index.ts b/backend/src/Mailing/mailbox-folder/dto/index.ts new file mode 100644 index 0000000..abbb4ca --- /dev/null +++ b/backend/src/Mailing/mailbox-folder/dto/index.ts @@ -0,0 +1 @@ +export * from './mailbox-folder.dto'; diff --git a/backend/src/Mailing/mailbox-folder/dto/mailbox-folder.dto.ts b/backend/src/Mailing/mailbox-folder/dto/mailbox-folder.dto.ts new file mode 100644 index 0000000..10975e1 --- /dev/null +++ b/backend/src/Mailing/mailbox-folder/dto/mailbox-folder.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { MailboxFolderType } from '../../common'; + +export class MailboxFolderDto { + @ApiProperty({ description: 'Mailbox folder ID' }) + @IsNumber() + id: number; + + @ApiProperty({ enum: MailboxFolderType, nullable: true, description: 'Mailbox folder type' }) + @IsOptional() + @IsEnum(MailboxFolderType) + type: MailboxFolderType | null; + + @ApiProperty({ description: 'Mailbox folder name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Unread mail count', nullable: true }) + @IsOptional() + @IsNumber() + unread: number | null; + + @ApiProperty({ description: 'Total mail count', nullable: true }) + @IsOptional() + @IsNumber() + total: number | null; + + @ApiPropertyOptional({ description: 'Child folders', type: [MailboxFolderDto], nullable: true }) + @IsOptional() + folders?: MailboxFolderDto[] | null; +} diff --git a/backend/src/Mailing/mailbox-folder/entities/index.ts b/backend/src/Mailing/mailbox-folder/entities/index.ts new file mode 100644 index 0000000..8d1c94e --- /dev/null +++ b/backend/src/Mailing/mailbox-folder/entities/index.ts @@ -0,0 +1 @@ +export * from './mailbox-folder.entity'; diff --git a/backend/src/Mailing/mailbox-folder/entities/mailbox-folder.entity.ts b/backend/src/Mailing/mailbox-folder/entities/mailbox-folder.entity.ts new file mode 100644 index 0000000..491b7f8 --- /dev/null +++ b/backend/src/Mailing/mailbox-folder/entities/mailbox-folder.entity.ts @@ -0,0 +1,130 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { MailboxFolderExternal, MailboxFolderType } from '../../common'; +import { MailboxFolderDto } from '../dto'; + +@Entity() +export class MailboxFolder { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + mailboxId: number; + + @Column({ nullable: true }) + parentId: number | null; + + @Column() + externalId: string; + + @Column({ nullable: true }) + uidValidity: number | null; + + @Column() + name: string; + + @Column({ nullable: true }) + type: MailboxFolderType | null; + + @Column({ nullable: true }) + unread: number | null; + + @Column({ nullable: true }) + total: number | null; + + constructor( + accountId: number, + mailboxId: number, + parentId: number | null, + externalId: string, + uidValidity: number | null, + name: string, + type: MailboxFolderType | null, + unread?: number | null, + total?: number | null, + ) { + this.accountId = accountId; + this.mailboxId = mailboxId; + this.parentId = parentId; + this.externalId = externalId; + this.uidValidity = uidValidity; + this.name = name; + this.type = type; + this.unread = unread; + this.total = total; + } + + private _folders: MailboxFolder[]; + get folders(): MailboxFolder[] { + return this._folders; + } + set folders(value: MailboxFolder[]) { + this._folders = value; + } + + static fromExternal({ + accountId, + mailboxId, + parentId, + external, + }: { + accountId: number; + mailboxId: number; + parentId?: number | null; + external: MailboxFolderExternal; + }): MailboxFolder { + return new MailboxFolder( + accountId, + mailboxId, + parentId ?? null, + external.id, + external.uidValidity, + external.name, + external.type, + ); + } + + hasChanges(data: { parentId?: number | null } & Partial): boolean { + return ( + (data.parentId !== undefined && data.parentId !== this.parentId) || + (data.id !== undefined && data.id !== this.externalId) || + (data.uidValidity !== undefined && data.uidValidity !== this.uidValidity) || + (data.name !== undefined && data.name !== this.name) || + (data.type !== undefined && data.type !== this.type) + ); + } + + update(data: { parentId?: number | null } & Partial): MailboxFolder { + this.parentId = data.parentId !== undefined ? data.parentId : this.parentId; + this.externalId = data.id !== undefined ? data.id : this.externalId; + this.uidValidity = data.uidValidity !== undefined ? data.uidValidity : this.uidValidity; + this.name = data.name !== undefined ? data.name : this.name; + this.type = data.type !== undefined ? data.type : this.type; + + return this; + } + + toUpdate(): Pick { + return { + parentId: this.parentId, + externalId: this.externalId, + uidValidity: this.uidValidity, + name: this.name, + type: this.type, + }; + } + + toDto(): MailboxFolderDto { + return { + id: this.id, + name: this.name, + type: this.type, + unread: this.unread, + total: this.total, + folders: this.folders?.map((f) => f.toDto()), + }; + } +} diff --git a/backend/src/Mailing/mailbox-folder/index.ts b/backend/src/Mailing/mailbox-folder/index.ts new file mode 100644 index 0000000..ad9a70f --- /dev/null +++ b/backend/src/Mailing/mailbox-folder/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './entities'; +export * from './mailbox-folder.service'; diff --git a/backend/src/Mailing/mailbox-folder/mailbox-folder.service.ts b/backend/src/Mailing/mailbox-folder/mailbox-folder.service.ts new file mode 100644 index 0000000..e58ecd3 --- /dev/null +++ b/backend/src/Mailing/mailbox-folder/mailbox-folder.service.ts @@ -0,0 +1,223 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Not, Repository } from 'typeorm'; + +import { flattenTree } from '@/common'; + +import { detectMailboxFolderType, MailboxFolderExternal, MailboxFolderType } from '../common'; +import { MailMessage } from '../Model/MailMessage/MailMessage'; +import { MailMessageFolder } from '../Model/MailMessage/MailMessageFolder'; + +import { MailboxFolder } from './entities'; + +interface FindFilter { + accountId: number; + mailboxId?: number; + folderId?: number; + type?: MailboxFolderType | MailboxFolderType[]; + externalId?: string | string[]; + messageId?: number; + parentId?: number | null; +} + +@Injectable() +export class MailboxFolderService { + constructor( + @InjectRepository(MailboxFolder) + private readonly repository: Repository, + ) {} + + async findOne(filter: FindFilter): Promise { + return this.createQb(filter).getOne(); + } + + async findMany(filter: FindFilter): Promise { + return this.createQb(filter).orderBy('mf.id', 'ASC').getMany(); + } + + async getHierarchy({ + accountId, + mailboxId, + folderId, + }: { + accountId: number; + mailboxId: number; + folderId?: number | null; + }): Promise { + const folders = await this.findMany({ accountId, mailboxId, parentId: folderId ?? null }); + for (const folder of folders) { + const children = await this.getHierarchy({ accountId, mailboxId, folderId: folder.id }); + folder.folders = children?.length ? children : undefined; + } + return folders; + } + + async processExternal({ + accountId, + mailboxId, + extFolders, + }: { + accountId: number; + mailboxId: number; + extFolders: MailboxFolderExternal[]; + }) { + const extFlatten = flattenTree(extFolders, (item) => item.folders); + this.postprocessTypes(extFlatten); + + const currentFolders = await this.findMany({ accountId, mailboxId }); + const currentFoldersMap = new Map(currentFolders.map((f) => [f.externalId, f])); + + const processedIds = await this.processHierarchy({ + accountId, + mailboxId, + currentFolders, + currentFoldersMap, + extFolders, + }); + + await this.repository.delete({ accountId, mailboxId, id: processedIds.length ? Not(In(processedIds)) : undefined }); + } + + async actualizeCounters({ accountId, mailboxId }: { accountId: number; mailboxId: number }) { + const folders = await this.repository + .createQueryBuilder('mf') + .select('mf.id', 'id') + .addSelect('count(mm.id)', 'total') + .addSelect('count(mm.id) filter (where mm.is_seen = false)', 'unread') + .leftJoin(MailMessageFolder, 'mmf', 'mmf.folder_id = mf.id') + .leftJoin(MailMessage, 'mm', 'mm.id = mmf.message_id') + .where('mf.account_id = :accountId', { accountId }) + .andWhere('mf.mailbox_id = :mailboxId', { mailboxId }) + .groupBy('mf.id') + .getRawMany<{ id: number; total: number; unread: number }>(); + + await Promise.all( + folders.map((folder) => + this.repository.update({ accountId, id: folder.id }, { total: folder.total, unread: folder.unread }), + ), + ); + } + + private createQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('mf') + .where('mf.account_id = :accountId', { accountId: filter.accountId }); + if (filter.folderId) { + qb.andWhere('mf.id = :folderId', { folderId: filter.folderId }); + } + if (filter.mailboxId) { + qb.andWhere('mf.mailbox_id = :mailboxId', { mailboxId: filter.mailboxId }); + } + if (filter.type) { + if (Array.isArray(filter.type)) { + qb.andWhere('mf.type IN (:...types)', { types: filter.type }); + } else { + qb.andWhere('mf.type = :type', { type: filter.type }); + } + } + if (filter.externalId) { + if (Array.isArray(filter.externalId)) { + qb.andWhere('mf.external_id IN (:...externalIds)', { externalIds: filter.externalId }); + } else { + qb.andWhere('mf.external_id = :externalId', { externalId: filter.externalId }); + } + } + if (filter.messageId) { + qb.leftJoin(MailMessageFolder, 'mmf', 'mmf.folder_id = mf.id'); + qb.andWhere('mmf.message_id = :messageId', { messageId: filter.messageId }); + } + if (filter.parentId !== undefined) { + if (filter.parentId === null) { + qb.andWhere('mf.parent_id IS NULL'); + } else { + qb.andWhere('mf.parent_id = :parentId', { parentId: filter.parentId }); + } + } + return qb; + } + + private postprocessTypes(flatten: MailboxFolderExternal[]) { + const types = new Set(flatten.map((f) => f.type).filter(Boolean)); + if (!types.size) { + for (const folder of flatten) { + if (!folder.type) { + const type = detectMailboxFolderType({ name: folder.name }); + if (type && !types.has(type)) { + folder.type = type; + types.add(type); + } + } + } + } + } + + private async processHierarchy({ + accountId, + mailboxId, + currentFolders, + currentFoldersMap, + extFolders, + parentId = null, + }: { + accountId: number; + mailboxId: number; + currentFolders: MailboxFolder[]; + currentFoldersMap: Map; + extFolders: MailboxFolderExternal[]; + parentId?: number | null; + }): Promise { + const processedIds: number[] = []; + for (const extFolder of extFolders) { + let folder = this.findCurrentFolder({ extFolder, currentFolders, currentFoldersMap }); + if (folder) { + if (folder.hasChanges({ parentId, ...extFolder })) { + await this.repository.update( + { accountId, mailboxId, id: folder.id }, + folder.update({ parentId, ...extFolder }).toUpdate(), + ); + } + } else { + folder = await this.repository.save( + MailboxFolder.fromExternal({ accountId, mailboxId, parentId, external: extFolder }), + ); + } + processedIds.push(folder.id); + if (extFolder.folders?.length) { + const childrenIds = await this.processHierarchy({ + accountId, + mailboxId, + currentFolders, + currentFoldersMap, + extFolders: extFolder.folders, + parentId: folder.id, + }); + processedIds.push(...childrenIds); + } + } + return processedIds; + } + + private findCurrentFolder({ + extFolder, + currentFoldersMap, + currentFolders, + }: { + extFolder: MailboxFolderExternal; + currentFoldersMap: Map; + currentFolders: MailboxFolder[]; + }): MailboxFolder | undefined { + const folderById = currentFoldersMap.get(extFolder.id); + + if (!extFolder.uidValidity || (folderById && !folderById.uidValidity)) { + return folderById; + } + + if (folderById?.uidValidity === extFolder.uidValidity) { + return folderById; + } + + const foldersByUidValidity = currentFolders.filter((f) => f.uidValidity === extFolder.uidValidity); + + return foldersByUidValidity.length === 1 ? foldersByUidValidity[0] : null; + } +} diff --git a/backend/src/Mailing/mailbox-signature/dto/create-mailbox-signature.dto.ts b/backend/src/Mailing/mailbox-signature/dto/create-mailbox-signature.dto.ts new file mode 100644 index 0000000..3f7133d --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/dto/create-mailbox-signature.dto.ts @@ -0,0 +1,15 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +import { MailboxSignatureDto } from './mailbox-signature.dto'; + +export class CreateMailboxSignatureDto extends PickType(MailboxSignatureDto, [ + 'name', + 'text', + 'linkedMailboxes', +] as const) { + @ApiPropertyOptional({ description: 'Signature text is HTML' }) + @IsOptional() + @IsBoolean() + isHtml?: boolean; +} diff --git a/backend/src/Mailing/mailbox-signature/dto/index.ts b/backend/src/Mailing/mailbox-signature/dto/index.ts new file mode 100644 index 0000000..0f54272 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-mailbox-signature.dto'; +export * from './mailbox-signature-filter.dto'; +export * from './mailbox-signature.dto'; +export * from './update-mailbox-signature.dto'; diff --git a/backend/src/Mailing/mailbox-signature/dto/mailbox-signature-filter.dto.ts b/backend/src/Mailing/mailbox-signature/dto/mailbox-signature-filter.dto.ts new file mode 100644 index 0000000..92b7038 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/dto/mailbox-signature-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class MailboxSignatureFilterDto { + @ApiPropertyOptional({ nullable: true, description: 'Mailbox ID' }) + @IsOptional() + @IsNumber() + mailboxId?: number | null; +} diff --git a/backend/src/Mailing/mailbox-signature/dto/mailbox-signature.dto.ts b/backend/src/Mailing/mailbox-signature/dto/mailbox-signature.dto.ts new file mode 100644 index 0000000..cf9dce1 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/dto/mailbox-signature.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsString } from 'class-validator'; + +export class MailboxSignatureDto { + @ApiProperty({ description: 'Signature ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Signature created by user ID' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'Signature name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Signature text' }) + @IsString() + text: string; + + @ApiProperty({ description: 'Signature text is HTML' }) + @IsBoolean() + isHtml: boolean; + + @ApiProperty({ description: 'Signature linked mailboxes' }) + @IsArray() + @IsNumber({}, { each: true }) + linkedMailboxes: number[]; +} diff --git a/backend/src/Mailing/mailbox-signature/dto/update-mailbox-signature.dto.ts b/backend/src/Mailing/mailbox-signature/dto/update-mailbox-signature.dto.ts new file mode 100644 index 0000000..488ae7d --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/dto/update-mailbox-signature.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { MailboxSignatureDto } from './mailbox-signature.dto'; + +export class UpdateMailboxSignatureDto extends PartialType( + PickType(MailboxSignatureDto, ['name', 'text', 'isHtml', 'linkedMailboxes'] as const), +) {} diff --git a/backend/src/Mailing/mailbox-signature/entities/index.ts b/backend/src/Mailing/mailbox-signature/entities/index.ts new file mode 100644 index 0000000..ec1840f --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/entities/index.ts @@ -0,0 +1,2 @@ +export * from './mailbox-signature-mailbox.entity'; +export * from './mailbox-signature.entity'; diff --git a/backend/src/Mailing/mailbox-signature/entities/mailbox-signature-mailbox.entity.ts b/backend/src/Mailing/mailbox-signature/entities/mailbox-signature-mailbox.entity.ts new file mode 100644 index 0000000..aacd12b --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/entities/mailbox-signature-mailbox.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class MailboxSignatureMailbox { + @PrimaryColumn() + signatureId: number; + + @PrimaryColumn() + mailboxId: number; + + @Column() + accountId: number; + + constructor(accountId: number, signatureId: number, mailboxId: number) { + this.mailboxId = mailboxId; + this.signatureId = signatureId; + this.accountId = accountId; + } +} diff --git a/backend/src/Mailing/mailbox-signature/entities/mailbox-signature.entity.ts b/backend/src/Mailing/mailbox-signature/entities/mailbox-signature.entity.ts new file mode 100644 index 0000000..7acfa29 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/entities/mailbox-signature.entity.ts @@ -0,0 +1,69 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { CreateMailboxSignatureDto, MailboxSignatureDto, UpdateMailboxSignatureDto } from '../dto'; +import { MailboxSignatureMailbox } from './mailbox-signature-mailbox.entity'; + +@Entity() +export class MailboxSignature { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + createdBy: number; + + @Column() + name: string; + + @Column() + text: string; + + @Column({ default: false }) + isHtml: boolean; + + @Column() + createdAt: Date; + + constructor(accountId: number, createdBy: number, name: string, text: string, isHtml: boolean, createdAt?: Date) { + this.accountId = accountId; + this.createdBy = createdBy; + this.name = name; + this.text = text; + this.isHtml = isHtml; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _mailboxes: MailboxSignatureMailbox[] = []; + get mailboxes(): MailboxSignatureMailbox[] { + return this._mailboxes; + } + set mailboxes(value: MailboxSignatureMailbox[]) { + this._mailboxes = value; + } + + static create(accountId: number, createdBy: number, dto: CreateMailboxSignatureDto): MailboxSignature { + return new MailboxSignature(accountId, createdBy, dto.name, dto.text, dto.isHtml ?? false); + } + + update(dto: UpdateMailboxSignatureDto): MailboxSignature { + this.name = dto.name ?? this.name; + this.text = dto.text ?? this.text; + this.isHtml = dto.isHtml ?? this.isHtml; + + return this; + } + + toDto(): MailboxSignatureDto { + return { + id: this.id, + createdBy: this.createdBy, + name: this.name, + text: this.text, + isHtml: this.isHtml, + linkedMailboxes: this.mailboxes?.map((m) => m.mailboxId) ?? [], + }; + } +} diff --git a/backend/src/Mailing/mailbox-signature/index.ts b/backend/src/Mailing/mailbox-signature/index.ts new file mode 100644 index 0000000..2968b86 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './mailbox-signature.controller'; +export * from './mailbox-signature.service'; diff --git a/backend/src/Mailing/mailbox-signature/mailbox-signature.controller.ts b/backend/src/Mailing/mailbox-signature/mailbox-signature.controller.ts new file mode 100644 index 0000000..eb2e919 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/mailbox-signature.controller.ts @@ -0,0 +1,83 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Put, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { + CreateMailboxSignatureDto, + MailboxSignatureDto, + MailboxSignatureFilterDto, + UpdateMailboxSignatureDto, +} from './dto'; +import { MailboxSignatureService } from './mailbox-signature.service'; + +@ApiTags('mailing/signature') +@Controller('mailing/signatures') +@JwtAuthorized() +@TransformToDto() +export class MailboxSignatureController { + constructor(private readonly service: MailboxSignatureService) {} + + @ApiOperation({ summary: 'Create signature for mail', description: 'Create signature for mail' }) + @ApiBody({ type: CreateMailboxSignatureDto, required: true, description: 'Signature data' }) + @ApiCreatedResponse({ description: 'Signature', type: MailboxSignatureDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateMailboxSignatureDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Get signatures for mail', description: 'Get signatures for accessible mailboxes' }) + @ApiQuery({ name: 'mailboxId', type: Number, required: false, description: 'Mailbox ID' }) + @ApiOkResponse({ description: 'Signatures', type: [MailboxSignatureDto] }) + @Get() + async findMany(@CurrentAuth() { accountId, userId }: AuthData, @Query() filter: MailboxSignatureFilterDto) { + return this.service.findMany({ + accountId, + accessibleUserId: userId, + mailboxId: filter?.mailboxId, + }); + } + + @ApiOperation({ summary: 'Get signature', description: 'Get signature' }) + @ApiParam({ name: 'signatureId', type: Number, required: true, description: 'Signature ID' }) + @ApiOkResponse({ description: 'Signature', type: MailboxSignatureDto }) + @Get(':signatureId') + async findOne(@CurrentAuth() { accountId }: AuthData, @Param('signatureId', ParseIntPipe) signatureId: number) { + return this.service.findOne({ accountId, signatureId }); + } + + @ApiOperation({ summary: 'Update signature for mail', description: 'Update signature for mail' }) + @ApiParam({ name: 'signatureId', type: Number, required: true, description: 'Signature ID' }) + @ApiBody({ type: UpdateMailboxSignatureDto, required: true, description: 'Signature data' }) + @ApiOkResponse({ description: 'Signature', type: MailboxSignatureDto }) + @Put(':signatureId') + async updatePut( + @CurrentAuth() { accountId }: AuthData, + @Param('signatureId', ParseIntPipe) signatureId: number, + @Body() dto: UpdateMailboxSignatureDto, + ) { + return this.service.update({ accountId, signatureId, dto }); + } + + @ApiOperation({ summary: 'Update signature for mail', description: 'Update signature for mail' }) + @ApiParam({ name: 'signatureId', type: Number, required: true, description: 'Signature ID' }) + @ApiBody({ type: UpdateMailboxSignatureDto, required: true, description: 'Signature data' }) + @ApiOkResponse({ description: 'Signature', type: MailboxSignatureDto }) + @Patch(':signatureId') + async updatePatch( + @CurrentAuth() { accountId }: AuthData, + @Param('signatureId', ParseIntPipe) signatureId: number, + @Body() dto: UpdateMailboxSignatureDto, + ) { + return this.service.update({ accountId, signatureId, dto }); + } + + @ApiOperation({ summary: 'Delete signature for mail', description: 'Delete signature for mail' }) + @ApiParam({ name: 'signatureId', type: Number, required: true, description: 'Signature ID' }) + @ApiOkResponse() + @Delete(':signatureId') + async delete(@CurrentAuth() { accountId }: AuthData, @Param('signatureId', ParseIntPipe) signatureId: number) { + return this.service.delete({ accountId, signatureId }); + } +} diff --git a/backend/src/Mailing/mailbox-signature/mailbox-signature.service.ts b/backend/src/Mailing/mailbox-signature/mailbox-signature.service.ts new file mode 100644 index 0000000..cf6f690 --- /dev/null +++ b/backend/src/Mailing/mailbox-signature/mailbox-signature.service.ts @@ -0,0 +1,130 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { MailboxService } from '../mailbox/services'; + +import { CreateMailboxSignatureDto, UpdateMailboxSignatureDto } from './dto'; +import { MailboxSignature, MailboxSignatureMailbox } from './entities'; + +interface FindFilter { + accountId: number; + signatureId?: number; + accessibleUserId?: number; + mailboxId?: number | number[]; +} + +@Injectable() +export class MailboxSignatureService { + constructor( + @InjectRepository(MailboxSignature) + private readonly repositorySignature: Repository, + @InjectRepository(MailboxSignatureMailbox) + private readonly repositoryLink: Repository, + private readonly mailboxService: MailboxService, + ) {} + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateMailboxSignatureDto; + }): Promise { + const signature = await this.repositorySignature.save(MailboxSignature.create(accountId, userId, dto)); + + signature.mailboxes = await this.processLinks({ + accountId, + signatureId: signature.id, + mailboxIds: dto.linkedMailboxes, + }); + + return signature; + } + + async findOne(filter: FindFilter): Promise { + return this.createQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + if (!filter.mailboxId && filter.accessibleUserId) { + const mailboxes = await this.mailboxService.findMany({ + accountId: filter.accountId, + accessibleUserId: filter.accessibleUserId, + }); + filter.mailboxId = mailboxes.map((m) => m.id); + } + return this.createQb(filter).orderBy('ms.created_at', 'ASC').getMany(); + } + + async update({ + accountId, + signatureId, + dto, + }: { + accountId: number; + signatureId: number; + dto: UpdateMailboxSignatureDto; + }): Promise { + const signature = await this.findOne({ accountId, signatureId }); + if (!signature) { + throw NotFoundError.withId(MailboxSignature, signatureId); + } + + await this.repositorySignature.save(signature.update(dto)); + + signature.mailboxes = await this.processLinks({ + accountId, + signatureId: signature.id, + mailboxIds: dto.linkedMailboxes, + }); + + return signature; + } + + async delete({ accountId, signatureId }: { accountId: number; signatureId: number }) { + await this.repositorySignature.delete({ accountId, id: signatureId }); + } + + private async processLinks({ + accountId, + signatureId, + mailboxIds, + }: { + accountId: number; + signatureId: number; + mailboxIds: number[] | null; + }): Promise { + await this.repositoryLink.delete({ accountId, signatureId }); + return mailboxIds?.length + ? this.repositoryLink.save( + mailboxIds.map((mailboxId) => new MailboxSignatureMailbox(accountId, signatureId, mailboxId)), + ) + : []; + } + + private createQb(filter: FindFilter) { + const qb = this.repositorySignature + .createQueryBuilder('ms') + .leftJoinAndMapMany('ms.mailboxes', MailboxSignatureMailbox, 'msm', 'msm.signature_id = ms.id') + .where('ms.account_id = :accountId', { accountId: filter.accountId }); + if (filter.signatureId) { + qb.andWhere('ms.id = :signatureId', { signatureId: filter.signatureId }); + } + if (filter.mailboxId) { + if (Array.isArray(filter.mailboxId)) { + if (filter.mailboxId.length) { + qb.andWhere('msm.mailbox_id IN (:...mailboxIds)', { mailboxIds: filter.mailboxId }); + } else { + qb.andWhere('1 = 0'); + } + } else { + qb.andWhere('msm.mailbox_id = :mailboxId', { mailboxId: filter.mailboxId }); + } + } + return qb; + } +} diff --git a/backend/src/Mailing/mailbox/dto/create-mailbox.dto.ts b/backend/src/Mailing/mailbox/dto/create-mailbox.dto.ts new file mode 100644 index 0000000..3debcf6 --- /dev/null +++ b/backend/src/Mailing/mailbox/dto/create-mailbox.dto.ts @@ -0,0 +1,18 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { MailboxDto } from './mailbox.dto'; + +export class CreateMailboxDto extends PickType(MailboxDto, [ + 'ownerId', + 'provider', + 'email', + 'accessibleUserIds', + 'emailsPerDay', + 'entitySettings', +] as const) { + @ApiPropertyOptional({ nullable: true, description: 'Sync days' }) + @IsOptional() + @IsNumber() + syncDays?: number | null; +} diff --git a/backend/src/Mailing/mailbox/dto/index.ts b/backend/src/Mailing/mailbox/dto/index.ts new file mode 100644 index 0000000..ee73217 --- /dev/null +++ b/backend/src/Mailing/mailbox/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-mailbox.dto'; +export * from './mailbox-entity-settings.dto'; +export * from './mailbox.dto'; +export * from './update-mailbox.dto'; diff --git a/backend/src/Mailing/mailbox/dto/mailbox-entity-settings.dto.ts b/backend/src/Mailing/mailbox/dto/mailbox-entity-settings.dto.ts new file mode 100644 index 0000000..323463e --- /dev/null +++ b/backend/src/Mailing/mailbox/dto/mailbox-entity-settings.dto.ts @@ -0,0 +1,44 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class MailboxEntitySettingsDto { + @ApiPropertyOptional({ description: 'Contact entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + contactEntityTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + leadEntityTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead board ID', nullable: true }) + @IsOptional() + @IsNumber() + leadBoardId?: number | null; + + @ApiPropertyOptional({ description: 'Lead stage ID', nullable: true }) + @IsOptional() + @IsNumber() + leadStageId?: number | null; + + @ApiPropertyOptional({ description: 'Lead name', nullable: true }) + @IsOptional() + @IsString() + leadName: string | null; + + @ApiPropertyOptional({ description: 'Lead and Contact responsible user ID', nullable: true }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ description: 'Do not create lead if active lead exists', nullable: true }) + @IsOptional() + @IsBoolean() + checkActiveLead?: boolean; + + @ApiPropertyOptional({ description: 'Do not create duplicate contact', nullable: true }) + @IsOptional() + @IsBoolean() + checkDuplicate?: boolean; +} diff --git a/backend/src/Mailing/mailbox/dto/mailbox.dto.ts b/backend/src/Mailing/mailbox/dto/mailbox.dto.ts new file mode 100644 index 0000000..648ee9b --- /dev/null +++ b/backend/src/Mailing/mailbox/dto/mailbox.dto.ts @@ -0,0 +1,47 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEmail, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { MailboxState } from '../../common'; +import { MailboxEntitySettingsDto } from './mailbox-entity-settings.dto'; + +export class MailboxDto { + @ApiProperty({ description: 'Mailbox ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Mailbox email' }) + @IsEmail() + email: string; + + @ApiProperty({ description: 'Mailbox provider' }) + @IsString() + provider: string; + + @ApiPropertyOptional({ nullable: true, description: 'Owner ID' }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ type: [Number], description: 'Accessible user IDs', nullable: true }) + @IsOptional() + @IsNumber({}, { each: true }) + accessibleUserIds?: number[] | null; + + @ApiProperty({ enum: MailboxState, description: 'Mailbox state' }) + @IsEnum(MailboxState) + state: MailboxState; + + @ApiProperty({ nullable: true, description: 'Error message' }) + @IsOptional() + @IsString() + errorMessage: string | null; + + @ApiProperty({ nullable: true, description: 'Emails per day' }) + @IsOptional() + @IsNumber() + emailsPerDay?: number | null; + + @ApiPropertyOptional({ type: MailboxEntitySettingsDto, description: 'Entity settings', nullable: true }) + @IsOptional() + entitySettings?: MailboxEntitySettingsDto | null; +} diff --git a/backend/src/Mailing/mailbox/dto/update-mailbox.dto.ts b/backend/src/Mailing/mailbox/dto/update-mailbox.dto.ts new file mode 100644 index 0000000..e7d45d7 --- /dev/null +++ b/backend/src/Mailing/mailbox/dto/update-mailbox.dto.ts @@ -0,0 +1,13 @@ +import { ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { MailboxDto } from './mailbox.dto'; + +export class UpdateMailboxDto extends PartialType( + PickType(MailboxDto, ['ownerId', 'email', 'accessibleUserIds', 'emailsPerDay', 'entitySettings'] as const), +) { + @ApiPropertyOptional({ nullable: true, description: 'Sync days' }) + @IsOptional() + @IsNumber() + syncDays?: number | null; +} diff --git a/backend/src/Mailing/mailbox/entities/index.ts b/backend/src/Mailing/mailbox/entities/index.ts new file mode 100644 index 0000000..568d574 --- /dev/null +++ b/backend/src/Mailing/mailbox/entities/index.ts @@ -0,0 +1,3 @@ +export * from './mailbox-accessible-user.entity'; +export * from './mailbox-entity-settings.entity'; +export * from './mailbox.entity'; diff --git a/backend/src/Mailing/mailbox/entities/mailbox-accessible-user.entity.ts b/backend/src/Mailing/mailbox/entities/mailbox-accessible-user.entity.ts new file mode 100644 index 0000000..a313942 --- /dev/null +++ b/backend/src/Mailing/mailbox/entities/mailbox-accessible-user.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class MailboxAccessibleUser { + @PrimaryColumn() + mailboxId: number; + + @PrimaryColumn() + userId: number; + + @Column() + accountId: number; + + constructor(accountId: number, mailboxId: number, userId: number) { + this.mailboxId = mailboxId; + this.userId = userId; + this.accountId = accountId; + } +} diff --git a/backend/src/Mailing/mailbox/entities/mailbox-entity-settings.entity.ts b/backend/src/Mailing/mailbox/entities/mailbox-entity-settings.entity.ts new file mode 100644 index 0000000..354d8f9 --- /dev/null +++ b/backend/src/Mailing/mailbox/entities/mailbox-entity-settings.entity.ts @@ -0,0 +1,110 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { MailboxEntitySettingsDto } from '../dto'; + +@Entity() +export class MailboxEntitySettings { + @Column() + accountId: number; + + @PrimaryColumn() + mailboxId: number; + + @Column({ nullable: true }) + contactEntityTypeId: number | null; + + @Column({ nullable: true }) + leadEntityTypeId: number | null; + + @Column({ nullable: true }) + leadBoardId: number | null; + + @Column({ nullable: true }) + leadStageId: number | null; + + @Column({ nullable: true }) + leadName: string | null; + + @Column({ nullable: true }) + ownerId: number | null; + + @Column({ default: false }) + checkActiveLead: boolean; + + @Column({ default: false }) + checkDuplicate: boolean; + + constructor( + accountId: number, + mailboxId: number, + contactEntityTypeId: number | null, + leadEntityTypeId: number | null, + leadBoardId: number | null, + leadStageId: number | null, + leadName: string | null, + ownerId: number | null, + checkActiveLead: boolean, + checkDuplicate: boolean, + ) { + this.accountId = accountId; + this.mailboxId = mailboxId; + this.contactEntityTypeId = contactEntityTypeId; + this.leadEntityTypeId = leadEntityTypeId; + this.leadBoardId = leadBoardId; + this.leadStageId = leadStageId; + this.leadName = leadName; + this.ownerId = ownerId; + this.checkActiveLead = checkActiveLead; + this.checkDuplicate = checkDuplicate; + } + + static fromDto({ + accountId, + mailboxId, + dto, + }: { + accountId: number; + mailboxId: number; + dto: MailboxEntitySettingsDto; + }): MailboxEntitySettings { + return new MailboxEntitySettings( + accountId, + mailboxId, + dto.contactEntityTypeId, + dto.leadEntityTypeId, + dto.leadBoardId, + dto.leadStageId, + dto.leadName, + dto.ownerId, + dto.checkActiveLead ?? false, + dto.checkDuplicate ?? false, + ); + } + + update(dto: MailboxEntitySettingsDto): MailboxEntitySettings { + this.contactEntityTypeId = + dto.contactEntityTypeId !== undefined ? dto.contactEntityTypeId : this.contactEntityTypeId; + this.leadEntityTypeId = dto.leadEntityTypeId !== undefined ? dto.leadEntityTypeId : this.leadEntityTypeId; + this.leadBoardId = dto.leadBoardId !== undefined ? dto.leadBoardId : this.leadBoardId; + this.leadStageId = dto.leadStageId !== undefined ? dto.leadStageId : this.leadStageId; + this.leadName = dto.leadName !== undefined ? dto.leadName : this.leadName; + this.ownerId = dto.ownerId !== undefined ? dto.ownerId : this.ownerId; + this.checkActiveLead = dto.checkActiveLead !== undefined ? dto.checkActiveLead : this.checkActiveLead; + this.checkDuplicate = dto.checkDuplicate !== undefined ? dto.checkDuplicate : this.checkDuplicate; + + return this; + } + + toDto(): MailboxEntitySettingsDto { + return { + contactEntityTypeId: this.contactEntityTypeId, + leadEntityTypeId: this.leadEntityTypeId, + leadBoardId: this.leadBoardId, + leadStageId: this.leadStageId, + leadName: this.leadName, + ownerId: this.ownerId, + checkActiveLead: this.checkActiveLead, + checkDuplicate: this.checkDuplicate, + }; + } +} diff --git a/backend/src/Mailing/mailbox/entities/mailbox.entity.ts b/backend/src/Mailing/mailbox/entities/mailbox.entity.ts new file mode 100644 index 0000000..8e43089 --- /dev/null +++ b/backend/src/Mailing/mailbox/entities/mailbox.entity.ts @@ -0,0 +1,126 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { MailboxProvider, MailboxState } from '../../common'; + +import { CreateMailboxDto, MailboxDto, UpdateMailboxDto } from '../dto'; +import { MailboxAccessibleUser } from './mailbox-accessible-user.entity'; +import { MailboxEntitySettings } from './mailbox-entity-settings.entity'; + +const Default = { + emailsPerDay: 100, +}; + +@Entity() +export class Mailbox { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + email: string; + + @Column() + provider: string; + + @Column() + ownerId: number | null; + + @Column() + state: MailboxState; + + @Column({ nullable: true }) + errorMessage: string | null; + + @Column() + emailsPerDay: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + @Column({ nullable: true }) + lastActiveAt: Date | null; + + constructor( + accountId: number, + email: string, + provider: string, + ownerId: number | null, + state: MailboxState, + emailsPerDay: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.email = email; + this.provider = provider; + this.ownerId = ownerId; + this.state = state; + this.emailsPerDay = emailsPerDay; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _accessibleUsers: MailboxAccessibleUser[]; + get accessibleUsers(): MailboxAccessibleUser[] { + return this._accessibleUsers; + } + set accessibleUsers(value: MailboxAccessibleUser[]) { + this._accessibleUsers = value; + } + + private _entitySettings: MailboxEntitySettings | null; + get entitySettings(): MailboxEntitySettings | null { + return this._entitySettings; + } + set entitySettings(value: MailboxEntitySettings | null) { + this._entitySettings = value; + } + + static fromDto({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateMailboxDto & { state?: MailboxState }; + }): Mailbox { + return new Mailbox( + accountId, + dto.email, + dto.provider ?? Mailbox.getMailboxProvider(dto.email), + dto.ownerId ?? userId, + dto.state ?? MailboxState.Draft, + dto.emailsPerDay ?? Default.emailsPerDay, + ); + } + + update(data: UpdateMailboxDto & { state?: MailboxState }): Mailbox { + this.email = data.email ?? this.email; + this.ownerId = data.ownerId ?? this.ownerId; + this.state = data.state ?? this.state; + this.emailsPerDay = data.emailsPerDay ?? this.emailsPerDay; + + return this; + } + + toDto(): MailboxDto { + return { + id: this.id, + email: this.email, + provider: this.provider, + ownerId: this.ownerId, + state: this.state, + errorMessage: this.errorMessage, + emailsPerDay: this.emailsPerDay, + accessibleUserIds: this.accessibleUsers?.map((user) => user.userId), + entitySettings: this.entitySettings?.toDto(), + }; + } + + private static getMailboxProvider(email: string): MailboxProvider { + return email.includes('gmail.com') ? MailboxProvider.GMail : MailboxProvider.Manual; + } +} diff --git a/backend/src/Mailing/mailbox/index.ts b/backend/src/Mailing/mailbox/index.ts new file mode 100644 index 0000000..f1cea2f --- /dev/null +++ b/backend/src/Mailing/mailbox/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './mailbox.controller'; +export * from './services'; diff --git a/backend/src/Mailing/mailbox/mailbox.controller.ts b/backend/src/Mailing/mailbox/mailbox.controller.ts new file mode 100644 index 0000000..091b9bc --- /dev/null +++ b/backend/src/Mailing/mailbox/mailbox.controller.ts @@ -0,0 +1,65 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CreateMailboxDto, MailboxDto, UpdateMailboxDto } from './dto'; +import { MailboxService } from './services'; + +@ApiTags('mailing/mailbox/settings') +@Controller('mailing/settings/mailboxes') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class MailboxController { + constructor(private readonly service: MailboxService) {} + + @Post() + @ApiCreatedResponse({ description: 'Mailbox', type: MailboxDto }) + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateMailboxDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOkResponse({ description: 'Mailboxes', type: [MailboxDto] }) + @AuthDataPrefetch({ user: true }) + @Get() + async findMany(@CurrentAuth() { accountId, user }: AuthData) { + return this.service.findMany({ accountId, ownerId: user.isAdmin ? undefined : user.id }); + } + + @ApiOkResponse({ description: 'Mailbox', type: MailboxDto }) + @AuthDataPrefetch({ user: true }) + @Get(':mailboxId') + async findOne(@CurrentAuth() { accountId, user }: AuthData, @Param('mailboxId', ParseIntPipe) mailboxId: number) { + return this.service.findOne({ accountId, mailboxId, ownerId: user.isAdmin ? undefined : user.id }); + } + + @ApiOkResponse({ description: 'Mailbox', type: MailboxDto }) + @Put(':mailboxId') + async updatePut( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Body() dto: UpdateMailboxDto, + ) { + return this.service.update({ accountId, user, mailboxId, dto }); + } + + @ApiOkResponse({ description: 'Mailbox', type: MailboxDto }) + @Patch(':mailboxId') + async updatePatch( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Body() dto: UpdateMailboxDto, + ) { + return this.service.update({ accountId, user, mailboxId, dto }); + } + + @Delete(':mailboxId') + async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Query('save') save: string, + ) { + await this.service.delete({ accountId, user, mailboxId, softDelete: save === 'true' }); + } +} diff --git a/backend/src/Mailing/mailbox/services/index.ts b/backend/src/Mailing/mailbox/services/index.ts new file mode 100644 index 0000000..2215f67 --- /dev/null +++ b/backend/src/Mailing/mailbox/services/index.ts @@ -0,0 +1,5 @@ +export * from './mailbox-accessible-user.service'; +export * from './mailbox-entity-settings.service'; +export * from './mailbox-lock.service'; +export * from './mailbox.handler'; +export * from './mailbox.service'; diff --git a/backend/src/Mailing/mailbox/services/mailbox-accessible-user.service.ts b/backend/src/Mailing/mailbox/services/mailbox-accessible-user.service.ts new file mode 100644 index 0000000..aa4d337 --- /dev/null +++ b/backend/src/Mailing/mailbox/services/mailbox-accessible-user.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { MailboxAccessibleUser } from '../entities'; + +@Injectable() +export class MailboxAccessibleUserService { + constructor( + @InjectRepository(MailboxAccessibleUser) + private readonly repository: Repository, + ) {} + + async create({ + accountId, + mailboxId, + userIds, + }: { + accountId: number; + mailboxId: number; + userIds: number[]; + }): Promise { + return this.repository.save(userIds.map((userId) => new MailboxAccessibleUser(accountId, mailboxId, userId))); + } + + async findMany({ accountId, mailboxId }: { accountId: number; mailboxId: number }): Promise { + return this.repository + .createQueryBuilder('mau') + .where('mau.account_id = :accountId', { accountId }) + .andWhere('mau.mailbox_id = :mailboxId', { mailboxId }) + .getMany(); + } + + async update({ + accountId, + mailboxId, + currentUsers, + userIds, + }: { + accountId: number; + mailboxId: number; + currentUsers: MailboxAccessibleUser[]; + userIds: number[]; + }): Promise { + const addUsers = userIds.filter((id) => !currentUsers.some((user) => user.userId === id)); + const removeUsers = currentUsers.filter((user) => !userIds.some((id) => id === user.userId)); + + currentUsers.push(...(await this.create({ accountId, mailboxId, userIds: addUsers }))); + + if (removeUsers.length > 0) { + await this.repository.remove(removeUsers); + } + + return currentUsers.filter((user) => !removeUsers.some((u) => u.userId === user.userId)); + } +} diff --git a/backend/src/Mailing/mailbox/services/mailbox-entity-settings.service.ts b/backend/src/Mailing/mailbox/services/mailbox-entity-settings.service.ts new file mode 100644 index 0000000..d4587a5 --- /dev/null +++ b/backend/src/Mailing/mailbox/services/mailbox-entity-settings.service.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { MailboxEntitySettingsDto } from '../dto'; +import { MailboxEntitySettings } from '../entities'; + +@Injectable() +export class MailboxEntitySettingsService { + constructor( + @InjectRepository(MailboxEntitySettings) + private readonly repository: Repository, + ) {} + + async create({ + accountId, + mailboxId, + dto, + }: { + accountId: number; + mailboxId: number; + dto: MailboxEntitySettingsDto; + }): Promise { + return this.repository.save(MailboxEntitySettings.fromDto({ accountId, mailboxId, dto })); + } + + async findOne({ + accountId, + mailboxId, + }: { + accountId: number; + mailboxId: number; + }): Promise { + return this.repository.findOneBy({ accountId, mailboxId }); + } + + async update({ + accountId, + mailboxId, + dto, + }: { + accountId: number; + mailboxId: number; + dto: MailboxEntitySettingsDto; + }): Promise { + const settings = await this.findOne({ accountId, mailboxId }); + if (settings) { + await this.repository.save(settings.update(dto)); + return settings; + } else { + return this.create({ accountId, mailboxId, dto }); + } + } + + async updateUser({ + accountId, + userId, + newUserId, + }: { + accountId: number; + userId: number; + newUserId?: number | null; + }): Promise { + await this.repository.update({ accountId, ownerId: userId }, { ownerId: newUserId ?? null }); + } + + async delete({ accountId, mailboxId }: { accountId: number; mailboxId: number }): Promise { + await this.repository.delete({ accountId, mailboxId }); + } +} diff --git a/backend/src/Mailing/mailbox/services/mailbox-lock.service.ts b/backend/src/Mailing/mailbox/services/mailbox-lock.service.ts new file mode 100644 index 0000000..5ead771 --- /dev/null +++ b/backend/src/Mailing/mailbox/services/mailbox-lock.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter } from 'events'; + +@Injectable() +export class MailboxLockService { + private mailboxLocks = new Map(); + private mailboxEvents = new Map(); + + lock(mailboxId: number): boolean { + if (this.mailboxLocks.get(mailboxId)) { + return false; + } + this.mailboxLocks.set(mailboxId, true); + return true; + } + + unlock(mailboxId: number): void { + this.mailboxLocks.set(mailboxId, false); + this.emitUnlockEvent(mailboxId); + } + + private emitUnlockEvent(mailboxId: number): void { + const eventEmitter = this.mailboxEvents.get(mailboxId); + if (eventEmitter) { + eventEmitter.emit('unlock'); + this.mailboxEvents.delete(mailboxId); + } + } + + async waitForUnlock(mailboxId: number): Promise { + if (!this.mailboxLocks.get(mailboxId)) { + return; + } + + return new Promise((resolve) => { + const eventEmitter = this.mailboxEvents.get(mailboxId) ?? new EventEmitter(); + eventEmitter.once('unlock', resolve); + this.mailboxEvents.set(mailboxId, eventEmitter); + }); + } +} diff --git a/backend/src/Mailing/mailbox/services/mailbox.handler.ts b/backend/src/Mailing/mailbox/services/mailbox.handler.ts new file mode 100644 index 0000000..92ad009 --- /dev/null +++ b/backend/src/Mailing/mailbox/services/mailbox.handler.ts @@ -0,0 +1,43 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { Cron, CronExpression } from '@nestjs/schedule'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; + +import { MailboxState } from '../../common'; +import { MailboxService } from './mailbox.service'; +import { MailboxEntitySettingsService } from './mailbox-entity-settings.service'; + +@Injectable() +export class MailboxHandler { + private readonly logger = new Logger(MailboxHandler.name); + constructor( + private readonly mailboxService: MailboxService, + private readonly mailboxEntitySettingsService: MailboxEntitySettingsService, + ) {} + + @Cron(CronExpression.EVERY_30_SECONDS) + async synchronizeActive() { + if (process.env.SCHEDULE_MAIL_ACTIVE_DISABLE === 'true') return; + this.logger.log('Before: Synchronize active mailboxes'); + const count = await this.mailboxService.synchronize(MailboxState.Active); + this.logger.log(`After: Synchronize active mailboxes. Processed: ${count}`); + } + + @Cron(CronExpression.EVERY_5_MINUTES) + async synchronizeInactive() { + if (process.env.SCHEDULE_MAIL_INACTIVE_DISABLE === 'true') return; + this.logger.log('Before: Synchronize inactive mailboxes'); + const count = await this.mailboxService.synchronize(MailboxState.Inactive); + this.logger.log(`After: Synchronize inactive mailboxes. Processed: ${count}`); + } + + @OnEvent(IamEventType.UserDeleted, { async: true }) + async onUserDeleted(event: UserDeletedEvent) { + await this.mailboxEntitySettingsService.updateUser({ + accountId: event.accountId, + userId: event.userId, + newUserId: event.newUserId, + }); + } +} diff --git a/backend/src/Mailing/mailbox/services/mailbox.service.ts b/backend/src/Mailing/mailbox/services/mailbox.service.ts new file mode 100644 index 0000000..9b6253e --- /dev/null +++ b/backend/src/Mailing/mailbox/services/mailbox.service.ts @@ -0,0 +1,296 @@ +import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DateUtil, ForbiddenError, NotFoundError } from '@/common'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { MailboxEvent, MailboxState, MailboxSyncResult, MailEventType } from '../../common'; +import { MailProviderRegistry } from '../../mail-provider'; +import { MailboxFolderService } from '../../mailbox-folder'; +import { MailMessageService } from '../../Service/MailMessage/MailMessageService'; + +import { CreateMailboxDto, UpdateMailboxDto } from '../dto'; +import { Mailbox, MailboxAccessibleUser, MailboxEntitySettings } from '../entities'; +import { MailboxLockService } from './mailbox-lock.service'; +import { MailboxAccessibleUserService } from './mailbox-accessible-user.service'; +import { MailboxEntitySettingsService } from './mailbox-entity-settings.service'; + +interface FindFilter { + accountId: number; + mailboxId?: number; + ownerId?: number; + state?: MailboxState | MailboxState[]; + accessibleUserId?: number; + provider?: string; +} + +@Injectable() +export class MailboxService { + private readonly logger = new Logger(MailboxService.name); + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Mailbox) + private readonly repository: Repository, + private readonly mailboxLock: MailboxLockService, + private readonly mailboxAccessibleUserService: MailboxAccessibleUserService, + private readonly mailboxEntitySettingsService: MailboxEntitySettingsService, + private readonly mailProviderRegistry: MailProviderRegistry, + private readonly mailboxFolderService: MailboxFolderService, + @Inject(forwardRef(() => MailMessageService)) + private readonly mailMessageService: MailMessageService, + ) {} + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateMailboxDto & { state?: MailboxState }; + }): Promise { + const mailbox = await this.repository.save(Mailbox.fromDto({ accountId, userId, dto })); + + if (mailbox.state !== MailboxState.Draft) { + this.syncMailboxWithLock({ + accountId, + mailboxId: mailbox.id, + syncFull: mailbox.state === MailboxState.Init, + syncDays: dto.syncDays, + }); + } + + return mailbox; + } + + async findOne(filter: FindFilter): Promise { + const mailbox = await this.createQb(filter).getOne(); + + if (mailbox && filter.accessibleUserId) { + return mailbox.ownerId === filter.accessibleUserId || + mailbox.accessibleUsers?.some((au) => au.userId === filter.accessibleUserId) + ? mailbox + : null; + } else { + return mailbox; + } + } + async findMany(filter: FindFilter): Promise { + const mailboxes = await this.createQb(filter).orderBy('mailbox.created_at').getMany(); + return filter.accessibleUserId + ? mailboxes.filter( + (mb) => + mb.ownerId === filter.accessibleUserId || + mb.accessibleUsers?.some((au) => au.userId === filter.accessibleUserId), + ) + : mailboxes; + } + + async update({ + accountId, + user, + mailboxId, + dto, + }: { + accountId: number; + user?: User; + mailboxId: number; + dto: UpdateMailboxDto; + }): Promise { + const mailbox = await this.findOne({ accountId, mailboxId }); + if (!mailbox) { + throw NotFoundError.withId(Mailbox, mailboxId); + } + if (user && !user.isAdmin && mailbox.ownerId !== user.id) { + throw new ForbiddenError(); + } + + if (mailbox.state === MailboxState.Draft) { + mailbox.update({ state: MailboxState.Init }); + } + await this.repository.save(mailbox.update(dto)); + + if (dto.accessibleUserIds) { + mailbox.accessibleUsers = await this.mailboxAccessibleUserService.update({ + accountId, + mailboxId, + currentUsers: mailbox.accessibleUsers ?? [], + userIds: dto.accessibleUserIds, + }); + } + if (dto.entitySettings === null) { + await this.mailboxEntitySettingsService.delete({ accountId, mailboxId: mailbox.id }); + mailbox.entitySettings = null; + } else if (dto.entitySettings) { + mailbox.entitySettings = await this.mailboxEntitySettingsService.update({ + accountId, + mailboxId: mailbox.id, + dto: dto.entitySettings, + }); + } + + this.syncMailboxWithLock({ + accountId, + mailboxId: mailbox.id, + syncFull: mailbox.state === MailboxState.Init, + syncDays: dto.syncDays, + }); + + return mailbox; + } + + async delete({ + accountId, + user, + mailboxId, + softDelete, + }: { + accountId: number; + user: User; + mailboxId: number; + softDelete: boolean; + }) { + if (!user.isAdmin) { + const mailbox = await this.findOne({ accountId, mailboxId }); + if (!mailbox || mailbox.ownerId !== user.id) { + throw new ForbiddenError(); + } + } + if (softDelete) { + await this.repository.update({ id: mailboxId, accountId }, { state: MailboxState.Deleted }); + } else { + await this.mailboxLock.waitForUnlock(mailboxId); + await this.repository.delete({ id: mailboxId, accountId }); + } + this.eventEmitter.emit(MailEventType.MailboxDeleted, new MailboxEvent({ accountId, mailboxId })); + } + + async synchronize(state: MailboxState): Promise { + const mailboxes = await this.findForSync(state); + mailboxes.forEach(async ({ accountId, mailboxId }) => { + this.syncMailboxWithLock({ accountId, mailboxId }); + }); + return mailboxes.length; + } + + private createQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('mailbox') + .leftJoinAndMapMany('mailbox.accessibleUsers', MailboxAccessibleUser, 'mau', 'mau.mailbox_id = mailbox.id') + .leftJoinAndMapOne('mailbox.entitySettings', MailboxEntitySettings, 'mes', 'mes.mailbox_id = mailbox.id') + .where('mailbox.account_id = :accountId', { accountId: filter.accountId }); + + if (filter.mailboxId) { + qb.andWhere('mailbox.id = :mailboxId', { mailboxId: filter.mailboxId }); + } + if (filter.ownerId) { + qb.andWhere('mailbox.owner_id = :ownerId', { ownerId: filter.ownerId }); + } + if (filter.state) { + if (Array.isArray(filter.state)) { + qb.andWhere('mailbox.state IN (:...states)', { states: filter.state }); + } else { + qb.andWhere('mailbox.state = :state', { state: filter.state }); + } + } + if (filter.provider) { + qb.andWhere('mailbox.provider = :provider', { provider: filter.provider }); + } + + return qb; + } + + private async updateState({ + accountId, + mailboxId, + state, + errorMessage, + lastActiveAt, + }: { + accountId: number; + mailboxId: number; + state: MailboxState; + errorMessage?: string; + lastActiveAt?: Date; + }) { + await this.repository.update( + { accountId, id: mailboxId }, + { state, errorMessage: errorMessage ?? null, lastActiveAt }, + ); + } + + private async findForSync(state: MailboxState): Promise<{ accountId: number; mailboxId: number }[]> { + return this.repository + .createQueryBuilder('mailbox') + .select('mailbox.id', 'mailboxId') + .addSelect('mailbox.account_id', 'accountId') + .where('mailbox.state = :state', { state }) + .getRawMany<{ accountId: number; mailboxId: number }>(); + } + + private async syncMailboxWithLock({ + accountId, + mailboxId, + syncFull = false, + syncDays = null, + }: { + accountId: number; + mailboxId: number; + syncFull?: boolean | null; + syncDays?: number | null; + }) { + if (!this.mailboxLock.lock(mailboxId)) { + return; + } + + try { + const mailbox = await this.findOne({ accountId, mailboxId }); + const { result, message, folders, messages } = await this.syncMailbox({ mailbox, syncFull, syncDays }); + if (result && folders?.length) + await this.mailboxFolderService.processExternal({ accountId, mailboxId, extFolders: folders }); + if (result && (messages?.added?.length || messages?.updated?.length || messages?.deleted?.length)) + await this.mailMessageService.processExternalMessages({ + accountId, + mailbox, + added: messages.added, + updated: messages.updated, + deleted: messages.deleted, + }); + await this.updateState({ + accountId, + mailboxId, + state: result ? MailboxState.Active : MailboxState.Inactive, + errorMessage: message, + lastActiveAt: result ? DateUtil.now() : undefined, + }); + } catch (e) { + const error = e as Error; + await this.updateState({ + accountId, + mailboxId, + state: MailboxState.Inactive, + errorMessage: error?.message, + }); + this.logger.error(`Mailbox ${mailboxId} sync error: ${error?.message}`, error?.stack); + } finally { + this.mailboxLock.unlock(mailboxId); + } + } + + private async syncMailbox({ + mailbox, + syncFull = false, + syncDays = null, + }: { + mailbox: Mailbox; + syncFull?: boolean | null; + syncDays?: number | null; + }): Promise { + const provider = this.mailProviderRegistry.get(mailbox.provider); + const syncDate = syncDays ? DateUtil.sub(DateUtil.now(), { days: syncDays }) : null; + + return provider.sync({ mailbox, syncFull, syncDate }); + } +} diff --git a/backend/src/Mailing/subscription/dto/index.ts b/backend/src/Mailing/subscription/dto/index.ts new file mode 100644 index 0000000..4a3cfad --- /dev/null +++ b/backend/src/Mailing/subscription/dto/index.ts @@ -0,0 +1 @@ +export * from './unsubscribe.dto'; diff --git a/backend/src/Mailing/subscription/dto/unsubscribe.dto.ts b/backend/src/Mailing/subscription/dto/unsubscribe.dto.ts new file mode 100644 index 0000000..f27867c --- /dev/null +++ b/backend/src/Mailing/subscription/dto/unsubscribe.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class UnsubscribeDto { + @ApiProperty({ description: 'Entity ID' }) + @IsNumber() + entityId: number; +} diff --git a/backend/src/Mailing/subscription/index.ts b/backend/src/Mailing/subscription/index.ts new file mode 100644 index 0000000..d6c2b61 --- /dev/null +++ b/backend/src/Mailing/subscription/index.ts @@ -0,0 +1,2 @@ +export * from './dto'; +export * from './unsubscribe.controller'; diff --git a/backend/src/Mailing/subscription/unsubscribe.controller.ts b/backend/src/Mailing/subscription/unsubscribe.controller.ts new file mode 100644 index 0000000..6eb0686 --- /dev/null +++ b/backend/src/Mailing/subscription/unsubscribe.controller.ts @@ -0,0 +1,15 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { ApiAccessRequired } from '@/modules/iam/common/decorators/api-access-required.decorator'; +import { UnsubscribeDto } from './dto'; + +@ApiExcludeController(true) +@Controller() +@ApiAccessRequired() +export class UnsubscribeController { + @Post('mailing/subscription/unsubscribe') + async unsubscribe(@Body() dto: UnsubscribeDto): Promise { + return !!dto.entityId; + } +} diff --git a/backend/src/Mailing/system-mailing/dto/become-partner-feedback.dto.ts b/backend/src/Mailing/system-mailing/dto/become-partner-feedback.dto.ts new file mode 100644 index 0000000..150ddf7 --- /dev/null +++ b/backend/src/Mailing/system-mailing/dto/become-partner-feedback.dto.ts @@ -0,0 +1,11 @@ +export class BecomePartnerFeedback { + firstName: string; + lastName: string; + phone: string; + email: string; + company: string; + country: string; + website: string; + employees: string; + comment: string; +} diff --git a/backend/src/Mailing/system-mailing/dto/contact-us-feedback.dto.ts b/backend/src/Mailing/system-mailing/dto/contact-us-feedback.dto.ts new file mode 100644 index 0000000..4752495 --- /dev/null +++ b/backend/src/Mailing/system-mailing/dto/contact-us-feedback.dto.ts @@ -0,0 +1,6 @@ +export class ContactUsFeedback { + name: string; + phone: string; + email: string; + comment: string; +} diff --git a/backend/src/Mailing/system-mailing/dto/index.ts b/backend/src/Mailing/system-mailing/dto/index.ts new file mode 100644 index 0000000..24a8acc --- /dev/null +++ b/backend/src/Mailing/system-mailing/dto/index.ts @@ -0,0 +1,5 @@ +export * from './become-partner-feedback.dto'; +export * from './contact-us-feedback.dto'; +export * from './send-feedback.dto'; +export * from './trial-expired-feedback.dto'; +export * from './user-limit-feedback.dto'; diff --git a/backend/src/Mailing/system-mailing/dto/send-feedback.dto.ts b/backend/src/Mailing/system-mailing/dto/send-feedback.dto.ts new file mode 100644 index 0000000..f5ad434 --- /dev/null +++ b/backend/src/Mailing/system-mailing/dto/send-feedback.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { FeedbackType } from '../enums'; +import { TrialExpiredFeedback } from './trial-expired-feedback.dto'; +import { UserLimitFeedback } from './user-limit-feedback.dto'; +import { BecomePartnerFeedback } from './become-partner-feedback.dto'; +import { ContactUsFeedback } from './contact-us-feedback.dto'; + +type FeedbackPayload = TrialExpiredFeedback | UserLimitFeedback | BecomePartnerFeedback | ContactUsFeedback; + +export class SendFeedbackDto { + @ApiProperty() + type: FeedbackType; + + @ApiProperty() + payload: FeedbackPayload; +} diff --git a/backend/src/Mailing/system-mailing/dto/trial-expired-feedback.dto.ts b/backend/src/Mailing/system-mailing/dto/trial-expired-feedback.dto.ts new file mode 100644 index 0000000..25d0284 --- /dev/null +++ b/backend/src/Mailing/system-mailing/dto/trial-expired-feedback.dto.ts @@ -0,0 +1,8 @@ +export class TrialExpiredFeedback { + name: string; + phone: string; + email: string; + userNumber: string; + subscribe: string; + plan: string; +} diff --git a/backend/src/Mailing/system-mailing/dto/user-limit-feedback.dto.ts b/backend/src/Mailing/system-mailing/dto/user-limit-feedback.dto.ts new file mode 100644 index 0000000..8679899 --- /dev/null +++ b/backend/src/Mailing/system-mailing/dto/user-limit-feedback.dto.ts @@ -0,0 +1,6 @@ +export class UserLimitFeedback { + name: string; + phone: string; + email: string; + userNumber: string; +} diff --git a/backend/src/Mailing/system-mailing/enums/feedback-type.enum.ts b/backend/src/Mailing/system-mailing/enums/feedback-type.enum.ts new file mode 100644 index 0000000..e9131f2 --- /dev/null +++ b/backend/src/Mailing/system-mailing/enums/feedback-type.enum.ts @@ -0,0 +1,6 @@ +export enum FeedbackType { + TrialExpired = 'trial_expired', + UserLimit = 'user_limit', + ContactUs = 'contact_us', + BecomePartner = 'become_partner', +} diff --git a/backend/src/Mailing/system-mailing/enums/index.ts b/backend/src/Mailing/system-mailing/enums/index.ts new file mode 100644 index 0000000..7fa65a6 --- /dev/null +++ b/backend/src/Mailing/system-mailing/enums/index.ts @@ -0,0 +1 @@ +export * from './feedback-type.enum'; diff --git a/backend/src/Mailing/system-mailing/index.ts b/backend/src/Mailing/system-mailing/index.ts new file mode 100644 index 0000000..f871bcf --- /dev/null +++ b/backend/src/Mailing/system-mailing/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './enums'; +export * from './public-system-mailing.controller.ts'; +export * from './system-mailing.controller'; +export * from './system-mailing.service'; diff --git a/backend/src/Mailing/system-mailing/public-system-mailing.controller.ts.ts b/backend/src/Mailing/system-mailing/public-system-mailing.controller.ts.ts new file mode 100644 index 0000000..7cca0ea --- /dev/null +++ b/backend/src/Mailing/system-mailing/public-system-mailing.controller.ts.ts @@ -0,0 +1,19 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { ApiAccessRequired } from '@/modules/iam/common'; + +import { SendFeedbackDto } from './dto'; +import { SystemMailingService } from './system-mailing.service'; + +@ApiExcludeController(true) +@Controller('mailing/feedback/public') +@ApiAccessRequired() +export class PublicSystemMailingController { + constructor(private readonly service: SystemMailingService) {} + + @Post() + async sendFeedback(@Body() dto: SendFeedbackDto) { + await this.service.sendFeedback(null, null, dto); + } +} diff --git a/backend/src/Mailing/system-mailing/system-mailing.controller.ts b/backend/src/Mailing/system-mailing/system-mailing.controller.ts new file mode 100644 index 0000000..c285e15 --- /dev/null +++ b/backend/src/Mailing/system-mailing/system-mailing.controller.ts @@ -0,0 +1,19 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { SendFeedbackDto } from './dto'; +import { SystemMailingService } from './system-mailing.service'; + +@ApiTags('mailing/feedback') +@Controller('mailing') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class SystemMailingController { + constructor(private readonly service: SystemMailingService) {} + + @Post('feedback') + async sendFeedback(@CurrentAuth() { account, user }: AuthData, @Body() dto: SendFeedbackDto) { + await this.service.sendFeedback(account, user, dto); + } +} diff --git a/backend/src/Mailing/system-mailing/system-mailing.service.ts b/backend/src/Mailing/system-mailing/system-mailing.service.ts new file mode 100644 index 0000000..c1bcb62 --- /dev/null +++ b/backend/src/Mailing/system-mailing/system-mailing.service.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { OnEvent } from '@nestjs/event-emitter'; +import { MailerService } from '@nestjs-modules/mailer'; + +import { UrlGeneratorService } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { IamEventType, UserPasswordRecoveryEvent } from '@/modules/iam/common'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { SendFeedbackDto } from './dto'; + +@Injectable() +export class SystemMailingService { + private _appConfig: ApplicationConfig; + + constructor( + private readonly configService: ConfigService, + private readonly mailerService: MailerService, + private readonly urlGeneratorService: UrlGeneratorService, + ) { + this._appConfig = this.configService.get('application'); + } + + @OnEvent(IamEventType.UserPasswordRecovery, { async: true }) + async sendPasswordRecoveryEmail(event: UserPasswordRecoveryEvent) { + const domain = this.urlGeneratorService.createUrl(); + + await this.mailerService.sendMail({ + to: event.userEmail, + subject: `${this._appConfig.name} password recovery`, + template: `./auth/password_recovery.html`, + context: { + appName: this._appConfig.name, + appNameLower: this._appConfig.name.toLowerCase(), + userName: event.userFullName, + recoveryLink: `${domain}/recovery?token=${event.recoveryToken}`, + supportEmail: this._appConfig.supportEmail, + domain: domain, + }, + }); + } + + async sendFeedback(account: Account | null, user: User | null, dto: SendFeedbackDto) { + const accountName = account?.companyName ?? 'Unregistered'; + const userName = user?.fullName ?? null; + const userEmail = user?.email ?? null; + + await this.mailerService.sendMail({ + to: this._appConfig.feedbackEmail, + subject: `${this._appConfig.name} feedback (${dto.type}) from ${accountName}`, + template: `./feedback/${dto.type}`.toLowerCase(), + context: { ...dto.payload, accountId: account.id, accountName, userName, userEmail }, + }); + } +} diff --git a/backend/src/Mailing/system-mailing/templates/auth/password_recovery.html b/backend/src/Mailing/system-mailing/templates/auth/password_recovery.html new file mode 100644 index 0000000..42f8503 --- /dev/null +++ b/backend/src/Mailing/system-mailing/templates/auth/password_recovery.html @@ -0,0 +1,222 @@ + + + + + + + + {{appName}} password recovery + + + + + + + + + + + +
+

+ Recover your password in order to gain access to your account. +

+
+ + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ {{appName}} Logo +
+ We have received a request to reset the password for the account. +
+ {{appName}} password recovery +
+ To set a new password for the account of + {{userName}}, please proceed by + clicking on the link below. +
+ Reset +
+ + + + + + + + + + + + +
+ If this request was not initiated by you, please notify us of the receipt of + this email at + {{supportEmail}}. +
+ Yours sincerely, The {{appName}} Team! +
+
+
+ + + \ No newline at end of file diff --git a/backend/src/Mailing/system-mailing/templates/feedback/become_partner.html b/backend/src/Mailing/system-mailing/templates/feedback/become_partner.html new file mode 100644 index 0000000..1f7e0d0 --- /dev/null +++ b/backend/src/Mailing/system-mailing/templates/feedback/become_partner.html @@ -0,0 +1,51 @@ + + + + + + + + Become Partner Request + + + +

Become Partner Request

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
First name{{firstName}}
Last name{{lastName}}
Phone{{phone}}
Email{{email}}
Company{{company}}
Country{{country}}
Website{{website}}
Employees{{employees}}
Comment{{comment}}
+ \ No newline at end of file diff --git a/backend/src/Mailing/system-mailing/templates/feedback/contact_us.html b/backend/src/Mailing/system-mailing/templates/feedback/contact_us.html new file mode 100644 index 0000000..5f44610 --- /dev/null +++ b/backend/src/Mailing/system-mailing/templates/feedback/contact_us.html @@ -0,0 +1,31 @@ + + + + + + + + Contact Us Request + + + +

Contact Us Request

+ + + + + + + + + + + + + + + + + +
Name{{name}}
Phone{{phone}}
Email{{email}}
Comment{{comment}}
+ \ No newline at end of file diff --git a/backend/src/Mailing/system-mailing/templates/feedback/trial_expired.html b/backend/src/Mailing/system-mailing/templates/feedback/trial_expired.html new file mode 100644 index 0000000..545cda6 --- /dev/null +++ b/backend/src/Mailing/system-mailing/templates/feedback/trial_expired.html @@ -0,0 +1,63 @@ + + + + + + + + End Trial Request + + + +

End Trial Request

+ + + + + + + + + + + + + + + + + + + + +
Registration data
Account Id{{accountId}}
Account Name{{accountName}}
User Name{{userName}}
User Email{{userEmail}}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Request data
Name{{name}}
Phone{{phone}}
Email{{email}}
Number of users{{userNumber}}
Subscribe{{subscribe}}
Plan{{plan}}
+ \ No newline at end of file diff --git a/backend/src/Mailing/system-mailing/templates/feedback/user_limit.html b/backend/src/Mailing/system-mailing/templates/feedback/user_limit.html new file mode 100644 index 0000000..08dae2c --- /dev/null +++ b/backend/src/Mailing/system-mailing/templates/feedback/user_limit.html @@ -0,0 +1,55 @@ + + + + + + + + Additional User Request + + + +

Additional User Request

+ + + + + + + + + + + + + + + + + + + + +
Registration data
Account Id{{accountId}}
Account Name{{accountName}}
User Name{{userName}}
User Email{{userEmail}}
+ + + + + + + + + + + + + + + + + + + + +
Request data
Name{{name}}
Phone{{phone}}
Email{{email}}
Number of users{{userNumber}}
+ \ No newline at end of file diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts new file mode 100644 index 0000000..94b8b55 --- /dev/null +++ b/backend/src/app.module.ts @@ -0,0 +1,83 @@ +import { Module } from '@nestjs/common'; +import { RouterModule } from '@nestjs/core'; +import { ConditionalModule, ConfigModule } from '@nestjs/config'; +import { EventEmitterModule } from '@nestjs/event-emitter'; +import { ScheduleModule } from '@nestjs/schedule'; + +import applicationConfig from './config/application.config'; + +import { ApiDocumentation } from './documentation'; +import { CommonModule } from './common/common.module'; +import { DatabaseModule } from './database/database.module'; +import { SupportModule } from './support/support.module'; +import { IAMModule } from './modules/iam/iam.module'; +import { FrontendEventModule } from './modules/frontend-event/frontend-event.module'; +import { StorageModule } from './modules/storage/storage.module'; +import { NotificationModule } from './modules/notification/notification.module'; +import { InventoryModule } from './modules/inventory/inventory.module'; +import { SchedulerModule } from './modules/scheduler/scheduler.module'; +import { MultichatModule } from './modules/multichat/multichat.module'; +import { TelephonyModule } from './modules/telephony/telephony.module'; +import { FormsModule } from './modules/forms/forms.module'; +import { AnalyticsModule } from './modules/analytics/analytics.module'; +import { EntityModule } from './modules/entity/entity.module'; +import { TutorialModule } from './modules/tutorial/tutorial.module'; +import { DocumentsModule } from './modules/documents/documents.module'; +import { DataEnrichmentModule } from './modules/data-enrichment/data-enrichment.module'; +import { SetupModule } from './modules/setup/setup.module'; +import { PartnerModule } from './modules/partner/partner.module'; +import { AutomationModule } from './modules/automation/automation.module'; +import { IntegrationModule } from './modules/integration/integration.module'; +import { MailModule } from './modules/mail/mail.module'; +import { FrontendObjectModule } from './modules/frontend-object/frontend-object.module'; + +import { CrmModule } from './CRM/crm.module'; +import { MailingModule } from './Mailing/MailingModule'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + cache: true, + envFilePath: ['.env.local', '.env'], + load: [applicationConfig], + }), + ConditionalModule.registerWhen(ScheduleModule.forRoot(), 'SCHEDULE_ENABLED'), + EventEmitterModule.forRoot({ delimiter: ':', maxListeners: 100, wildcard: true }), + ApiDocumentation, + DatabaseModule, + CommonModule, + IAMModule, + FrontendEventModule, + StorageModule, + NotificationModule, + MailingModule, + MultichatModule, + EntityModule, + CrmModule, + InventoryModule, + SchedulerModule, + TelephonyModule, + FormsModule, + AnalyticsModule, + TutorialModule, + DocumentsModule, + DataEnrichmentModule, + SetupModule, + PartnerModule, + ConditionalModule.registerWhen(AutomationModule, 'AUTOMATION_ENABLED'), + IntegrationModule, + SupportModule, + MailModule, + FrontendObjectModule, + RouterModule.register([ + { path: 'automation', module: AutomationModule }, + { path: 'iam', module: IAMModule }, + { path: 'site-forms', module: FormsModule }, + { path: 'scheduler', module: SchedulerModule }, + { path: 'telephony', module: TelephonyModule }, + { path: 'tutorial', module: TutorialModule }, + ]), + ], +}) +export class AppModule {} diff --git a/backend/src/common/common.module.ts b/backend/src/common/common.module.ts new file mode 100644 index 0000000..1868f76 --- /dev/null +++ b/backend/src/common/common.module.ts @@ -0,0 +1,49 @@ +import { Global, Module, ValidationPipe } from '@nestjs/common'; +import { APP_FILTER, APP_PIPE } from '@nestjs/core'; + +import commonConfig, { CommonConfig } from './config/common.config'; +import { CacheModule, GlobalHttpModule, TokenModule, UrlGeneratorModule } from './modules'; + +import { AllExceptionsFilter } from './filters'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +@Global() +@Module({ + imports: [ + ConfigModule.forFeature(commonConfig), + CacheModule.forRootAsync({ + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + const config = configService.get('common'); + return { + type: config.cache?.type, + options: + config.cache?.type === 'ioredis' + ? { host: config.cache?.ioredis?.host, port: config.cache?.ioredis?.port } + : undefined, + }; + }, + }), + GlobalHttpModule, + TokenModule, + UrlGeneratorModule, + ], + providers: [ + { + provide: APP_PIPE, + useFactory: () => { + return new ValidationPipe({ + transform: true, + transformOptions: { enableImplicitConversion: true }, + whitelist: true, + }); + }, + }, + { + provide: APP_FILTER, + useClass: AllExceptionsFilter, + }, + ], + exports: [CacheModule, TokenModule, UrlGeneratorModule], +}) +export class CommonModule {} diff --git a/backend/src/common/config/common.config.ts b/backend/src/common/config/common.config.ts new file mode 100644 index 0000000..947ced8 --- /dev/null +++ b/backend/src/common/config/common.config.ts @@ -0,0 +1,27 @@ +import { registerAs } from '@nestjs/config'; +import { CacheProviderType } from '../modules/cache/types'; + +interface IORedisConfig { + host: string; + port: number; +} +interface CacheConfig { + type?: CacheProviderType; + ioredis?: IORedisConfig; +} +export interface CommonConfig { + cache?: CacheConfig; +} + +export default registerAs( + 'common', + (): CommonConfig => ({ + cache: { + type: process.env.CACHE_TYPE as CacheProviderType, + ioredis: { + host: process.env.CACHE_IOREDIS_HOST, + port: +process.env.CACHE_IOREDIS_PORT, + }, + }, + }), +); diff --git a/backend/src/common/config/index.ts b/backend/src/common/config/index.ts new file mode 100644 index 0000000..33ec909 --- /dev/null +++ b/backend/src/common/config/index.ts @@ -0,0 +1 @@ +export * from './common.config'; diff --git a/backend/src/common/constants/frontend-route.ts b/backend/src/common/constants/frontend-route.ts new file mode 100644 index 0000000..dcde2e5 --- /dev/null +++ b/backend/src/common/constants/frontend-route.ts @@ -0,0 +1,23 @@ +export const FrontendRoute = { + signup: '/signup', + + entity: { + card: ({ entityTypeId, entityId }: { entityTypeId: number; entityId: number }) => + `/et/${entityTypeId}/card/${entityId}/overview`, + } as const, + + settings: { + base: '/settings', + mailing: () => `${FrontendRoute.settings.base}/mailing`, + stripe: () => `${FrontendRoute.settings.base}/billing/stripe`, + integration: () => `${FrontendRoute.settings.base}/integrations`, + salesforce: () => FrontendRoute.settings.integration(), + facebook: { + deleteVerify: () => `/facebook/auth/delete-verify`, + messenger: () => FrontendRoute.settings.integration(), + } as const, + google: { + calendar: () => `${FrontendRoute.settings.integration()}/google-calendar`, + } as const, + } as const, +} as const; diff --git a/backend/src/common/constants/index.ts b/backend/src/common/constants/index.ts new file mode 100644 index 0000000..c2ef8bb --- /dev/null +++ b/backend/src/common/constants/index.ts @@ -0,0 +1,2 @@ +export * from './frontend-route'; +export * from './paging-default'; diff --git a/backend/src/common/constants/paging-default.ts b/backend/src/common/constants/paging-default.ts new file mode 100644 index 0000000..f2b4f2c --- /dev/null +++ b/backend/src/common/constants/paging-default.ts @@ -0,0 +1,4 @@ +export const PagingDefault = { + offset: 0, + limit: 20, +} as const; diff --git a/backend/src/common/decorators/index.ts b/backend/src/common/decorators/index.ts new file mode 100644 index 0000000..12ee43f --- /dev/null +++ b/backend/src/common/decorators/index.ts @@ -0,0 +1,2 @@ +export * from './subdomain.decorator'; +export * from './transform-to-dto.decorator'; diff --git a/backend/src/common/decorators/subdomain.decorator.ts b/backend/src/common/decorators/subdomain.decorator.ts new file mode 100644 index 0000000..bdaf494 --- /dev/null +++ b/backend/src/common/decorators/subdomain.decorator.ts @@ -0,0 +1,8 @@ +import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; +import { Request } from 'express'; + +export const Subdomain = createParamDecorator((_data: unknown, context: ExecutionContext): string | null => { + const request = context.switchToHttp().getRequest(); + + return request.subdomain; +}); diff --git a/backend/src/common/decorators/transform-to-dto.decorator.ts b/backend/src/common/decorators/transform-to-dto.decorator.ts new file mode 100644 index 0000000..72e56e2 --- /dev/null +++ b/backend/src/common/decorators/transform-to-dto.decorator.ts @@ -0,0 +1,4 @@ +import { UseInterceptors } from '@nestjs/common'; +import { TransformToDtoInterceptor } from '../interceptors'; + +export const TransformToDto = () => UseInterceptors(TransformToDtoInterceptor); diff --git a/backend/src/common/dto/date/date-period.dto.ts b/backend/src/common/dto/date/date-period.dto.ts new file mode 100644 index 0000000..a746eac --- /dev/null +++ b/backend/src/common/dto/date/date-period.dto.ts @@ -0,0 +1,14 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class DatePeriodDto { + @ApiPropertyOptional({ description: 'Start date', nullable: true }) + @IsOptional() + @IsString() + startDate?: string | null; + + @ApiPropertyOptional({ description: 'End date', nullable: true }) + @IsOptional() + @IsString() + endDate?: string | null; +} diff --git a/backend/src/common/dto/date/index.ts b/backend/src/common/dto/date/index.ts new file mode 100644 index 0000000..adf964c --- /dev/null +++ b/backend/src/common/dto/date/index.ts @@ -0,0 +1 @@ +export * from './date-period.dto'; diff --git a/backend/src/common/dto/expand/expand-query.dto.ts b/backend/src/common/dto/expand/expand-query.dto.ts new file mode 100644 index 0000000..ba9fb12 --- /dev/null +++ b/backend/src/common/dto/expand/expand-query.dto.ts @@ -0,0 +1,10 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; + +export class ExpandQuery { + @ApiPropertyOptional({ type: String, description: 'Expand fields' }) + expand?: string; + + get fields(): T[] { + return this.expand?.split(',') as T[]; + } +} diff --git a/backend/src/common/dto/expand/index.ts b/backend/src/common/dto/expand/index.ts new file mode 100644 index 0000000..9b9ef3e --- /dev/null +++ b/backend/src/common/dto/expand/index.ts @@ -0,0 +1 @@ +export * from './expand-query.dto'; diff --git a/backend/src/common/dto/filter/boolean-filter.ts b/backend/src/common/dto/filter/boolean-filter.ts new file mode 100644 index 0000000..5519549 --- /dev/null +++ b/backend/src/common/dto/filter/boolean-filter.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean } from 'class-validator'; + +export class BooleanFilter { + @ApiProperty({ description: 'Filter value' }) + @IsBoolean() + value: boolean; +} diff --git a/backend/src/common/dto/filter/date-filter.ts b/backend/src/common/dto/filter/date-filter.ts new file mode 100644 index 0000000..2822548 --- /dev/null +++ b/backend/src/common/dto/filter/date-filter.ts @@ -0,0 +1,14 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsOptional } from 'class-validator'; + +export class DateFilter { + @ApiPropertyOptional({ description: 'From date' }) + @IsOptional() + @IsDateString() + from?: string; + + @ApiPropertyOptional({ description: 'To date' }) + @IsOptional() + @IsDateString() + to?: string; +} diff --git a/backend/src/common/dto/filter/date-period-filter.ts b/backend/src/common/dto/filter/date-period-filter.ts new file mode 100644 index 0000000..603d9ba --- /dev/null +++ b/backend/src/common/dto/filter/date-period-filter.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsDateString, IsEnum, IsOptional } from 'class-validator'; + +import { DatePeriodFilterType } from '../../enums'; + +export class DatePeriodFilter { + @ApiProperty({ description: 'Type of date period', required: false, enum: DatePeriodFilterType }) + @IsOptional() + @IsEnum(DatePeriodFilterType) + type?: DatePeriodFilterType; + + @ApiProperty({ description: 'From date', required: false }) + @IsOptional() + @IsDateString() + from?: string; + + @ApiProperty({ description: 'To date', required: false }) + @IsOptional() + @IsDateString() + to?: string; +} diff --git a/backend/src/common/dto/filter/exists-filter.ts b/backend/src/common/dto/filter/exists-filter.ts new file mode 100644 index 0000000..8de8b05 --- /dev/null +++ b/backend/src/common/dto/filter/exists-filter.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum } from 'class-validator'; + +import { ExistsFilterType } from '../../enums'; + +export class ExistsFilter { + @ApiProperty({ description: 'Filter type', enum: ExistsFilterType }) + @IsEnum(ExistsFilterType) + type: ExistsFilterType; +} diff --git a/backend/src/common/dto/filter/index.ts b/backend/src/common/dto/filter/index.ts new file mode 100644 index 0000000..7d6a363 --- /dev/null +++ b/backend/src/common/dto/filter/index.ts @@ -0,0 +1,8 @@ +export * from './boolean-filter'; +export * from './date-filter'; +export * from './date-period-filter'; +export * from './exists-filter'; +export * from './number-filter'; +export * from './select-filter'; +export * from './simple-filter'; +export * from './string-filter'; diff --git a/backend/src/common/dto/filter/number-filter.ts b/backend/src/common/dto/filter/number-filter.ts new file mode 100644 index 0000000..ac62cc2 --- /dev/null +++ b/backend/src/common/dto/filter/number-filter.ts @@ -0,0 +1,14 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class NumberFilter { + @ApiPropertyOptional({ description: 'Minimum value' }) + @IsOptional() + @IsNumber() + min?: number; + + @ApiPropertyOptional({ description: 'Maximum value' }) + @IsOptional() + @IsNumber() + max?: number; +} diff --git a/backend/src/common/dto/filter/select-filter.ts b/backend/src/common/dto/filter/select-filter.ts new file mode 100644 index 0000000..46b6193 --- /dev/null +++ b/backend/src/common/dto/filter/select-filter.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber } from 'class-validator'; + +export class SelectFilter { + @ApiProperty({ description: 'List of option IDs', type: [Number] }) + @IsArray() + @IsNumber({}, { each: true }) + optionIds: number[]; +} diff --git a/backend/src/common/dto/filter/simple-filter.ts b/backend/src/common/dto/filter/simple-filter.ts new file mode 100644 index 0000000..75a23be --- /dev/null +++ b/backend/src/common/dto/filter/simple-filter.ts @@ -0,0 +1,28 @@ +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; +import { IsEnum, IsObject } from 'class-validator'; + +import { SimpleFilterType } from '../../enums'; +import { BooleanFilter, DateFilter, ExistsFilter, NumberFilter, SelectFilter, StringFilter } from '../../dto'; + +export class SimpleFilter { + @ApiProperty({ description: 'Filter type', enum: SimpleFilterType }) + @IsEnum(SimpleFilterType) + type: SimpleFilterType; + + @ApiProperty({ + description: 'Filter value', + type: 'array', + items: { + oneOf: [ + { $ref: getSchemaPath(BooleanFilter) }, + { $ref: getSchemaPath(DateFilter) }, + { $ref: getSchemaPath(ExistsFilter) }, + { $ref: getSchemaPath(NumberFilter) }, + { $ref: getSchemaPath(SelectFilter) }, + { $ref: getSchemaPath(StringFilter) }, + ], + }, + }) + @IsObject() + filter: BooleanFilter | DateFilter | ExistsFilter | NumberFilter | SelectFilter | StringFilter; +} diff --git a/backend/src/common/dto/filter/string-filter.ts b/backend/src/common/dto/filter/string-filter.ts new file mode 100644 index 0000000..2bf5265 --- /dev/null +++ b/backend/src/common/dto/filter/string-filter.ts @@ -0,0 +1,15 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsOptional, IsString } from 'class-validator'; + +import { StringFilterType } from '../../enums'; + +export class StringFilter { + @ApiProperty({ description: 'Filter type', enum: StringFilterType }) + @IsEnum(StringFilterType) + type: StringFilterType; + + @ApiPropertyOptional({ description: 'Filter value' }) + @IsOptional() + @IsString() + text?: string | null; +} diff --git a/backend/src/common/dto/index.ts b/backend/src/common/dto/index.ts new file mode 100644 index 0000000..6b3bd70 --- /dev/null +++ b/backend/src/common/dto/index.ts @@ -0,0 +1,6 @@ +export * from './date'; +export * from './expand'; +export * from './filter'; +export * from './paging'; +export * from './quantity-amount.dto'; +export * from './sorting'; diff --git a/backend/src/common/dto/paging/chat-paging-query.dto.ts b/backend/src/common/dto/paging/chat-paging-query.dto.ts new file mode 100644 index 0000000..5022e79 --- /dev/null +++ b/backend/src/common/dto/paging/chat-paging-query.dto.ts @@ -0,0 +1,39 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { PagingDefault } from '../../constants'; + +export class ChatPagingQuery { + @ApiPropertyOptional({ description: 'Offset', example: 0 }) + @IsOptional() + @IsNumber() + offset?: number; + + @ApiPropertyOptional({ description: 'Limit', example: 10 }) + @IsOptional() + @IsNumber() + limit?: number; + + @ApiPropertyOptional({ description: 'Cursor position for pagination' }) + @IsOptional() + @IsNumber() + cursor?: number; + + constructor(offset: number | undefined, limit: number | undefined, cursor: number | undefined) { + this.offset = offset; + this.limit = limit; + this.cursor = cursor; + } + + static default(): ChatPagingQuery { + return new ChatPagingQuery(undefined, undefined, undefined); + } + + get skip(): number { + return this.offset ?? PagingDefault.offset; + } + + get take(): number { + return this.limit ?? PagingDefault.limit; + } +} diff --git a/backend/src/common/dto/paging/cursor-paging-query.dto.ts b/backend/src/common/dto/paging/cursor-paging-query.dto.ts new file mode 100644 index 0000000..9f9f8fc --- /dev/null +++ b/backend/src/common/dto/paging/cursor-paging-query.dto.ts @@ -0,0 +1,20 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { PagingDefault } from '../../constants'; + +export class CursorPagingQuery { + @ApiPropertyOptional({ description: 'Cursor position for pagination' }) + @IsOptional() + @IsNumber() + cursor?: number; + + @ApiPropertyOptional({ description: 'Limit for pagination' }) + @IsOptional() + @IsNumber() + limit?: number; + + get take(): number { + return this.limit ?? PagingDefault.limit; + } +} diff --git a/backend/src/common/dto/paging/index.ts b/backend/src/common/dto/paging/index.ts new file mode 100644 index 0000000..2b16bd7 --- /dev/null +++ b/backend/src/common/dto/paging/index.ts @@ -0,0 +1,3 @@ +export * from './cursor-paging-query.dto'; +export * from './paging-meta.dto'; +export * from './paging-query.dto'; diff --git a/backend/src/common/dto/paging/paging-meta.dto.ts b/backend/src/common/dto/paging/paging-meta.dto.ts new file mode 100644 index 0000000..f0ee47c --- /dev/null +++ b/backend/src/common/dto/paging/paging-meta.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class PagingMeta { + @ApiProperty({ description: 'Offset for pagination' }) + @IsNumber() + offset: number; + + @ApiProperty({ description: 'Total number of items' }) + @IsNumber() + total: number; + + constructor(offset: number, total: number) { + this.offset = Math.min(offset, total); + this.total = total; + } + + static empty(): PagingMeta { + return new PagingMeta(0, 0); + } +} diff --git a/backend/src/common/dto/paging/paging-query.dto.ts b/backend/src/common/dto/paging/paging-query.dto.ts new file mode 100644 index 0000000..152095d --- /dev/null +++ b/backend/src/common/dto/paging/paging-query.dto.ts @@ -0,0 +1,33 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { PagingDefault } from '../../constants'; + +export class PagingQuery { + @ApiPropertyOptional({ description: 'Offset', example: 0 }) + @IsOptional() + @IsNumber() + offset?: number; + + @ApiPropertyOptional({ description: 'Limit', example: 10 }) + @IsOptional() + @IsNumber() + limit?: number; + + constructor(offset: number | undefined, limit: number | undefined) { + this.offset = offset; + this.limit = limit; + } + + static default(): PagingQuery { + return new PagingQuery(undefined, undefined); + } + + get skip(): number { + return this.offset ?? PagingDefault.offset; + } + + get take(): number { + return this.limit ?? PagingDefault.limit; + } +} diff --git a/backend/src/common/dto/quantity-amount.dto.ts b/backend/src/common/dto/quantity-amount.dto.ts new file mode 100644 index 0000000..4a80288 --- /dev/null +++ b/backend/src/common/dto/quantity-amount.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class QuantityAmountDto { + @ApiProperty({ description: 'Quantity' }) + @IsNumber() + quantity: number; + + @ApiProperty({ description: 'Amount' }) + @IsNumber() + amount: number; + + constructor(quantity: number, amount: number) { + this.quantity = quantity; + this.amount = amount; + } + + public static empty(): QuantityAmountDto { + return new QuantityAmountDto(0, 0); + } +} diff --git a/backend/src/common/dto/sorting/index.ts b/backend/src/common/dto/sorting/index.ts new file mode 100644 index 0000000..5e677c8 --- /dev/null +++ b/backend/src/common/dto/sorting/index.ts @@ -0,0 +1,3 @@ +export * from './manual-sorting.dto'; +export * from './sort-order-list.dto'; +export * from './sort-order.dto'; diff --git a/backend/src/common/dto/sorting/manual-sorting.dto.ts b/backend/src/common/dto/sorting/manual-sorting.dto.ts new file mode 100644 index 0000000..ec7afc3 --- /dev/null +++ b/backend/src/common/dto/sorting/manual-sorting.dto.ts @@ -0,0 +1,14 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class ManualSorting { + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + afterId?: number | null | undefined; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + beforeId?: number | null | undefined; +} diff --git a/backend/src/common/dto/sorting/sort-order-list.dto.ts b/backend/src/common/dto/sorting/sort-order-list.dto.ts new file mode 100644 index 0000000..d244ab9 --- /dev/null +++ b/backend/src/common/dto/sorting/sort-order-list.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; +import { SortOrderDto } from './sort-order.dto'; + +export class SortOrderListDto { + @ApiProperty({ type: [SortOrderDto] }) + @IsArray() + items: SortOrderDto[]; +} diff --git a/backend/src/common/dto/sorting/sort-order.dto.ts b/backend/src/common/dto/sorting/sort-order.dto.ts new file mode 100644 index 0000000..4f574af --- /dev/null +++ b/backend/src/common/dto/sorting/sort-order.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class SortOrderDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + sortOrder: number; +} diff --git a/backend/src/common/enums/currency.enum.ts b/backend/src/common/enums/currency.enum.ts new file mode 100644 index 0000000..d0c5fbe --- /dev/null +++ b/backend/src/common/enums/currency.enum.ts @@ -0,0 +1,55 @@ +export enum Currency { + USD = 'USD', + EUR = 'EUR', + GBP = 'GBP', + JPY = 'JPY', + CNY = 'CNY', + INR = 'INR', + RUB = 'RUB', + MXN = 'MXN', + BRL = 'BRL', + ZAR = 'ZAR', + AUD = 'AUD', + CAD = 'CAD', + AED = 'AED', + CHF = 'CHF', + TRY = 'TRY', + UAH = 'UAH', + KRW = 'KRW', + NZD = 'NZD', + NOK = 'NOK', + SEK = 'SEK', + DKK = 'DKK', + PLN = 'PLN', + CZK = 'CZK', + HUF = 'HUF', + IDR = 'IDR', + ILS = 'ILS', + MYR = 'MYR', + PHP = 'PHP', + SGD = 'SGD', + THB = 'THB', + KZT = 'KZT', + CLP = 'CLP', + CRC = 'CRC', + COP = 'COP', + BOB = 'BOB', + HKD = 'HKD', + SAR = 'SAR', + VND = 'VND', + EGP = 'EGP', + KWD = 'KWD', + PKR = 'PKR', + LKR = 'LKR', + BDT = 'BDT', + NGN = 'NGN', + GHS = 'GHS', + TWD = 'TWD', + MAD = 'MAD', + ARS = 'ARS', + PEN = 'PEN', + UYU = 'UYU', + BGN = 'BGN', + RON = 'RON', + LBP = 'LBP', +} diff --git a/backend/src/common/enums/date-format.enum.ts b/backend/src/common/enums/date-format.enum.ts new file mode 100644 index 0000000..4cd403b --- /dev/null +++ b/backend/src/common/enums/date-format.enum.ts @@ -0,0 +1,8 @@ +export enum DateFormat { + ISO8601 = 'YYYY-MM-DD', + EUSlash = 'DD/MM/YYYY', + EUDot = 'DD.MM.YYYY', + EUDash = 'DD-MM-YYYY', + US = 'MM/DD/YYYY', + Asia = 'YYYY/MM/DD', +} diff --git a/backend/src/common/enums/date-period-filter-type.enum.ts b/backend/src/common/enums/date-period-filter-type.enum.ts new file mode 100644 index 0000000..f937ee4 --- /dev/null +++ b/backend/src/common/enums/date-period-filter-type.enum.ts @@ -0,0 +1,12 @@ +export enum DatePeriodFilterType { + All = 'all', + Today = 'today', + Yesterday = 'yesterday', + CurrentWeek = 'current_week', + LastWeek = 'last_week', + CurrentMonth = 'current_month', + LastMonth = 'last_month', + CurrentQuarter = 'current_quarter', + LastQuarter = 'last_quarter', + Period = 'period', +} diff --git a/backend/src/common/enums/exists-filter-type.enum.ts b/backend/src/common/enums/exists-filter-type.enum.ts new file mode 100644 index 0000000..b2dc9f6 --- /dev/null +++ b/backend/src/common/enums/exists-filter-type.enum.ts @@ -0,0 +1,4 @@ +export enum ExistsFilterType { + Empty = 'empty', + NotEmpty = 'not_empty', +} diff --git a/backend/src/common/enums/file-link-source.enum.ts b/backend/src/common/enums/file-link-source.enum.ts new file mode 100644 index 0000000..86efae8 --- /dev/null +++ b/backend/src/common/enums/file-link-source.enum.ts @@ -0,0 +1,15 @@ +//TODO: remove from common +/** + * @deprecated + */ +export enum FileLinkSource { + NOTE = 'note', + TASK = 'task', + TASK_COMMENT = 'task_comment', + ACTIVITY = 'activity', + ENTITY = 'entity', + EMAIL_ACTION_SETTINGS = 'email_action_settings', + DOCUMENT_TEMPLATE = 'document_template', + ENTITY_DOCUMENT = 'entity_document', + PRODUCT_PHOTO = 'product_photo', +} diff --git a/backend/src/common/enums/group-by-date.enum.ts b/backend/src/common/enums/group-by-date.enum.ts new file mode 100644 index 0000000..f8547ab --- /dev/null +++ b/backend/src/common/enums/group-by-date.enum.ts @@ -0,0 +1,7 @@ +export enum GroupByDate { + Day = 'day', + Week = 'week', + Month = 'month', + Quarter = 'quarter', + Year = 'year', +} diff --git a/backend/src/common/enums/http-body-type.enum.ts b/backend/src/common/enums/http-body-type.enum.ts new file mode 100644 index 0000000..a1cef85 --- /dev/null +++ b/backend/src/common/enums/http-body-type.enum.ts @@ -0,0 +1,5 @@ +export enum HttpBodyType { + Raw = 'RAW', + FormUrlEncoded = 'FORM_URL_ENCODED', + FormData = 'FORM_DATA', +} diff --git a/backend/src/common/enums/http-method.enum.ts b/backend/src/common/enums/http-method.enum.ts new file mode 100644 index 0000000..3407ed7 --- /dev/null +++ b/backend/src/common/enums/http-method.enum.ts @@ -0,0 +1,9 @@ +export enum HttpMethod { + Get = 'GET', + Post = 'POST', + Put = 'PUT', + Delete = 'DELETE', + Patch = 'PATCH', + Options = 'OPTIONS', + Head = 'HEAD', +} diff --git a/backend/src/common/enums/index.ts b/backend/src/common/enums/index.ts new file mode 100644 index 0000000..b1fa1bc --- /dev/null +++ b/backend/src/common/enums/index.ts @@ -0,0 +1,11 @@ +export * from './currency.enum'; +export * from './date-format.enum'; +export * from './date-period-filter-type.enum'; +export * from './exists-filter-type.enum'; +export * from './file-link-source.enum'; +export * from './group-by-date.enum'; +export * from './http-method.enum'; +export * from './object-state.enum'; +export * from './simple-filter-type.enum'; +export * from './string-filter-type.enum'; +export * from './user-notification.enum'; diff --git a/backend/src/common/enums/object-state.enum.ts b/backend/src/common/enums/object-state.enum.ts new file mode 100644 index 0000000..7613ca2 --- /dev/null +++ b/backend/src/common/enums/object-state.enum.ts @@ -0,0 +1,6 @@ +export enum ObjectState { + Created = 'created', + Updated = 'updated', + Deleted = 'deleted', + Unchanged = 'unchanged', +} diff --git a/backend/src/common/enums/simple-filter-type.enum.ts b/backend/src/common/enums/simple-filter-type.enum.ts new file mode 100644 index 0000000..fbc3b2d --- /dev/null +++ b/backend/src/common/enums/simple-filter-type.enum.ts @@ -0,0 +1,8 @@ +export enum SimpleFilterType { + Boolean = 'boolean', + Date = 'date', + Number = 'number', + Select = 'select', + String = 'string', + Exists = 'exists', +} diff --git a/backend/src/common/enums/string-filter-type.enum.ts b/backend/src/common/enums/string-filter-type.enum.ts new file mode 100644 index 0000000..61e8520 --- /dev/null +++ b/backend/src/common/enums/string-filter-type.enum.ts @@ -0,0 +1,5 @@ +export enum StringFilterType { + Empty = 'empty', + NotEmpty = 'not_empty', + Contains = 'contains', +} diff --git a/backend/src/common/enums/user-notification.enum.ts b/backend/src/common/enums/user-notification.enum.ts new file mode 100644 index 0000000..c31a090 --- /dev/null +++ b/backend/src/common/enums/user-notification.enum.ts @@ -0,0 +1,5 @@ +export enum UserNotification { + Suppressed = 'suppressed', + Default = 'default', + Forced = 'forced', +} diff --git a/backend/src/common/errors/bad-request.error.ts b/backend/src/common/errors/bad-request.error.ts new file mode 100644 index 0000000..166beef --- /dev/null +++ b/backend/src/common/errors/bad-request.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from './service.error'; + +export class BadRequestError extends ServiceError { + constructor(message = 'Bad Request') { + super({ errorCode: 'bad_request', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/common/errors/forbidden.error.ts b/backend/src/common/errors/forbidden.error.ts new file mode 100644 index 0000000..489e3de --- /dev/null +++ b/backend/src/common/errors/forbidden.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from './service.error'; + +export class ForbiddenError extends ServiceError { + constructor(message = 'Item is forbidden') { + super({ errorCode: 'forbidden', status: HttpStatus.FORBIDDEN, message }); + } +} diff --git a/backend/src/common/errors/index.ts b/backend/src/common/errors/index.ts new file mode 100644 index 0000000..d3b34ff --- /dev/null +++ b/backend/src/common/errors/index.ts @@ -0,0 +1,5 @@ +export * from './bad-request.error'; +export * from './forbidden.error'; +export * from './invalid-phone.error'; +export * from './not-found.error'; +export * from './service.error'; diff --git a/backend/src/common/errors/invalid-phone.error.ts b/backend/src/common/errors/invalid-phone.error.ts new file mode 100644 index 0000000..10b9859 --- /dev/null +++ b/backend/src/common/errors/invalid-phone.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from './service.error'; + +export class InvalidPhoneError extends ServiceError { + constructor(message = 'Invalid phone number') { + super({ errorCode: 'invalid_phone', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/common/errors/not-found.error.ts b/backend/src/common/errors/not-found.error.ts new file mode 100644 index 0000000..bb8b2a1 --- /dev/null +++ b/backend/src/common/errors/not-found.error.ts @@ -0,0 +1,21 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from './service.error'; + +export class NotFoundError extends ServiceError { + constructor(message: string) { + super({ errorCode: 'not_found', status: HttpStatus.NOT_FOUND, message }); + } + + static fromNamed(named: T, message = 'is not found') { + return new NotFoundError(`${named.name} ${message}`); + } + + static withMessage(named: T, message?: string) { + return NotFoundError.fromNamed(named, message); + } + + static withId(named: T, id: number | string) { + return NotFoundError.withMessage(named, `with id ${id} is not found`); + } +} diff --git a/backend/src/common/errors/service.error.ts b/backend/src/common/errors/service.error.ts new file mode 100644 index 0000000..a7eca82 --- /dev/null +++ b/backend/src/common/errors/service.error.ts @@ -0,0 +1,22 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; + +interface ServiceErrorOptions { + message: string; + errorCode: string; + status: HttpStatus; + details?: object; + cause?: unknown; + description?: string; +} + +export class ServiceError extends HttpException { + errorCode: string; + details?: object; + + constructor({ errorCode, status, message, details, cause, description }: ServiceErrorOptions) { + super(message, status, { cause, description }); + + this.errorCode = errorCode ?? 'internal_server_error'; + this.details = details; + } +} diff --git a/backend/src/common/filters/all-exceptions.filter.ts b/backend/src/common/filters/all-exceptions.filter.ts new file mode 100644 index 0000000..9227b53 --- /dev/null +++ b/backend/src/common/filters/all-exceptions.filter.ts @@ -0,0 +1,57 @@ +import { ArgumentsHost, Catch, HttpException, HttpStatus, Logger } from '@nestjs/common'; +import { BaseExceptionFilter } from '@nestjs/core'; +import { Request, Response } from 'express'; +import { QueryFailedError } from 'typeorm'; + +import { ServiceError } from '../errors'; + +@Catch() +export class AllExceptionsFilter extends BaseExceptionFilter { + private readonly logger = new Logger('Exception'); + + override catch(exception: unknown, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const request = ctx.getRequest(); + const response = ctx.getResponse(); + + const requestStr = `${request.id ?? ''} ${request.method} ${request.hostname}${request.originalUrl}`; + const user = `User: <${request.accountId ?? ''}; ${request.userId ?? ''}; ${request.ips?.[0] ?? request.ip}>`; + const body = `Body: ${JSON.stringify(request.body)}`; + const msg = `${requestStr}\t${body}\t${user}`; + + const statusCode = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; + let errorCode: string | undefined = 'INTERNAL_SERVER_ERROR'; + let message: string | undefined = undefined; + let details: unknown | undefined = undefined; + + if (exception instanceof ServiceError) { + this.logger.warn(`${msg}\tError: ${JSON.stringify(exception)}`, 'Request'); + errorCode = exception.errorCode; + message = exception.message; + details = exception.details; + } else if (exception instanceof QueryFailedError) { + this.logger.error( + `${msg}\tQuery: ${exception.query} -- PARAMETERS: [${exception.parameters}]`, + exception.stack, + 'SQL', + ); + errorCode = 'SQL_ERROR'; + message = exception.message; + details = exception.driverError?.detail ?? exception.driverError?.message ?? exception.driverError?.toString(); + } else if (exception instanceof HttpException) { + this.logger.error(`${msg}\tResponse: ${JSON.stringify(exception.getResponse())}`, exception.stack); + message = exception.message; + details = exception.getResponse(); + } else if (exception instanceof Error) { + this.logger.error(`${msg}\tMessage: ${exception.message}`, exception.stack); + message = exception.message; + details = exception.stack; + } else { + this.logger.error(`${msg}`, exception['stack']); + } + + response + .status(statusCode) + .json({ statusCode, errorCode, message, details, timestamp: new Date().toISOString(), path: request.url }); + } +} diff --git a/backend/src/common/filters/index.ts b/backend/src/common/filters/index.ts new file mode 100644 index 0000000..03c9c30 --- /dev/null +++ b/backend/src/common/filters/index.ts @@ -0,0 +1 @@ +export * from './all-exceptions.filter'; diff --git a/backend/src/common/index.ts b/backend/src/common/index.ts new file mode 100644 index 0000000..2b979cb --- /dev/null +++ b/backend/src/common/index.ts @@ -0,0 +1,12 @@ +export * from './common.module'; +export * from './constants'; +export * from './decorators'; +export * from './dto'; +export * from './enums'; +export * from './errors'; +export * from './filters'; +export * from './interceptors'; +export * from './middleware'; +export * from './modules'; +export * from './types'; +export * from './utils'; diff --git a/backend/src/common/interceptors/index.ts b/backend/src/common/interceptors/index.ts new file mode 100644 index 0000000..d6f3e48 --- /dev/null +++ b/backend/src/common/interceptors/index.ts @@ -0,0 +1,2 @@ +export * from './logging.interceptor'; +export * from './transform-to-dto.interceptor'; diff --git a/backend/src/common/interceptors/logging.interceptor.ts b/backend/src/common/interceptors/logging.interceptor.ts new file mode 100644 index 0000000..44889a8 --- /dev/null +++ b/backend/src/common/interceptors/logging.interceptor.ts @@ -0,0 +1,21 @@ +import { Logger, Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; +import { Request } from 'express'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +@Injectable() +export class LoggingInterceptor implements NestInterceptor { + private readonly logger = new Logger('Request'); + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + const now = Date.now(); + request.id = now.toString(); + const msg = `${request.id} ${request.method} ${request.hostname}${request.originalUrl}`; + const user = `User: <${request.accountId ?? ''}; ${request.userId ?? ''}; ${request.ips?.[0] ?? request.ip}>`; + + this.logger.log(`${msg}\t${user}`); + + return next.handle().pipe(tap(() => this.logger.log(`${msg}\t<${Date.now() - now}ms>`, 'Response'))); + } +} diff --git a/backend/src/common/interceptors/transform-to-dto.interceptor.ts b/backend/src/common/interceptors/transform-to-dto.interceptor.ts new file mode 100644 index 0000000..b8bd6bd --- /dev/null +++ b/backend/src/common/interceptors/transform-to-dto.interceptor.ts @@ -0,0 +1,22 @@ +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Injectable() +export class TransformToDtoInterceptor implements NestInterceptor { + private transformData(data: any) { + return data?.toDto ? data.toDto() : data; + } + + intercept(_context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + map((data) => { + if (Array.isArray(data)) { + return data.map(this.transformData); + } + + return this.transformData(data); + }), + ); + } +} diff --git a/backend/src/common/middleware/extract-subdomain.middleware.ts b/backend/src/common/middleware/extract-subdomain.middleware.ts new file mode 100644 index 0000000..c300a90 --- /dev/null +++ b/backend/src/common/middleware/extract-subdomain.middleware.ts @@ -0,0 +1,8 @@ +import { Request, Response, NextFunction } from 'express'; + +export const extractSubdomain = (request: Request, _response: Response, next: NextFunction) => { + const parts = request.hostname.split('.'); + request.subdomain = parts.length >= 4 ? parts[0] : null; + + next(); +}; diff --git a/backend/src/common/middleware/index.ts b/backend/src/common/middleware/index.ts new file mode 100644 index 0000000..e9f92ee --- /dev/null +++ b/backend/src/common/middleware/index.ts @@ -0,0 +1 @@ +export * from './extract-subdomain.middleware'; diff --git a/backend/src/common/modules/cache/cache.constants.ts b/backend/src/common/modules/cache/cache.constants.ts new file mode 100644 index 0000000..858a457 --- /dev/null +++ b/backend/src/common/modules/cache/cache.constants.ts @@ -0,0 +1,3 @@ +import { MODULE_OPTIONS_TOKEN } from './cache.module-definition'; + +export const CACHE_MODULE_OPTIONS = MODULE_OPTIONS_TOKEN; diff --git a/backend/src/common/modules/cache/cache.module-definition.ts b/backend/src/common/modules/cache/cache.module-definition.ts new file mode 100644 index 0000000..f5f1232 --- /dev/null +++ b/backend/src/common/modules/cache/cache.module-definition.ts @@ -0,0 +1,6 @@ +import { ConfigurableModuleBuilder } from '@nestjs/common'; +import { CacheModuleOptions } from './interfaces'; + +export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new ConfigurableModuleBuilder() + .setClassMethodName('forRoot') + .build(); diff --git a/backend/src/common/modules/cache/cache.module.ts b/backend/src/common/modules/cache/cache.module.ts new file mode 100644 index 0000000..8872d4e --- /dev/null +++ b/backend/src/common/modules/cache/cache.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { CacheService } from './cache.service'; +import { ConfigurableModuleClass } from './cache.module-definition'; + +@Module({ + providers: [CacheService], + exports: [CacheService], +}) +export class CacheModule extends ConfigurableModuleClass {} diff --git a/backend/src/common/modules/cache/cache.service.ts b/backend/src/common/modules/cache/cache.service.ts new file mode 100644 index 0000000..5e0f081 --- /dev/null +++ b/backend/src/common/modules/cache/cache.service.ts @@ -0,0 +1,36 @@ +import { Inject } from '@nestjs/common'; +import { CacheServiceOptions, IORedisProviderOptions, Store } from './interfaces'; +import { IORedisProvider, StubProvider } from './providers'; +import { MODULE_OPTIONS_TOKEN } from './cache.module-definition'; + +export class CacheService implements Store { + private readonly store: Store; + + constructor(@Inject(MODULE_OPTIONS_TOKEN) private readonly options?: CacheServiceOptions) { + const type = this.options?.type || 'stub'; + switch (type) { + case 'ioredis': + this.store = new IORedisProvider(options?.options as IORedisProviderOptions); + break; + default: + this.store = new StubProvider(); + break; + } + } + + async get(key: string): Promise { + return this.store.get(key); + } + async set(key: string, value: T, seconds?: number): Promise { + return this.store.set(key, value, seconds); + } + async del(key: string): Promise { + return this.store.del(key); + } + async clear(): Promise { + return this.store.clear(); + } + async wrap(key: string, fn: () => Promise, seconds?: number): Promise { + return this.store.wrap(key, fn, seconds); + } +} diff --git a/backend/src/common/modules/cache/index.ts b/backend/src/common/modules/cache/index.ts new file mode 100644 index 0000000..aee8067 --- /dev/null +++ b/backend/src/common/modules/cache/index.ts @@ -0,0 +1,4 @@ +export * from './cache.module'; +export * from './cache.service'; +export * from './interfaces'; +export * from './providers'; diff --git a/backend/src/common/modules/cache/interfaces/cache-module.interface.ts b/backend/src/common/modules/cache/interfaces/cache-module.interface.ts new file mode 100644 index 0000000..e8b5f92 --- /dev/null +++ b/backend/src/common/modules/cache/interfaces/cache-module.interface.ts @@ -0,0 +1,4 @@ +import { CacheServiceOptions } from './cache-service.interface'; + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface CacheModuleOptions extends CacheServiceOptions {} diff --git a/backend/src/common/modules/cache/interfaces/cache-service.interface.ts b/backend/src/common/modules/cache/interfaces/cache-service.interface.ts new file mode 100644 index 0000000..44960b5 --- /dev/null +++ b/backend/src/common/modules/cache/interfaces/cache-service.interface.ts @@ -0,0 +1,8 @@ +import { CacheProviderType } from '../types'; +import { IORedisProviderOptions } from './ioredis-provider.interface'; + +export interface CacheServiceOptions { + type: CacheProviderType; + + options?: IORedisProviderOptions | null; +} diff --git a/backend/src/common/modules/cache/interfaces/index.ts b/backend/src/common/modules/cache/interfaces/index.ts new file mode 100644 index 0000000..9bb0281 --- /dev/null +++ b/backend/src/common/modules/cache/interfaces/index.ts @@ -0,0 +1,4 @@ +export * from './cache-module.interface'; +export * from './cache-service.interface'; +export * from './ioredis-provider.interface'; +export * from './store.interface'; diff --git a/backend/src/common/modules/cache/interfaces/ioredis-provider.interface.ts b/backend/src/common/modules/cache/interfaces/ioredis-provider.interface.ts new file mode 100644 index 0000000..bd360e0 --- /dev/null +++ b/backend/src/common/modules/cache/interfaces/ioredis-provider.interface.ts @@ -0,0 +1,3 @@ +import { RedisOptions } from 'ioredis'; + +export type IORedisProviderOptions = RedisOptions; diff --git a/backend/src/common/modules/cache/interfaces/store.interface.ts b/backend/src/common/modules/cache/interfaces/store.interface.ts new file mode 100644 index 0000000..24a7683 --- /dev/null +++ b/backend/src/common/modules/cache/interfaces/store.interface.ts @@ -0,0 +1,7 @@ +export interface Store { + get: (key: string) => Promise; + set: (key: string, value: T, seconds?: number) => Promise; + del: (key: string) => Promise; + clear: () => Promise; + wrap: (key: string, fn: () => Promise, seconds?: number) => Promise; +} diff --git a/backend/src/common/modules/cache/providers/index.ts b/backend/src/common/modules/cache/providers/index.ts new file mode 100644 index 0000000..0478b45 --- /dev/null +++ b/backend/src/common/modules/cache/providers/index.ts @@ -0,0 +1,2 @@ +export * from './ioredis.provider'; +export * from './stub.provider'; diff --git a/backend/src/common/modules/cache/providers/ioredis.provider.ts b/backend/src/common/modules/cache/providers/ioredis.provider.ts new file mode 100644 index 0000000..016d4f2 --- /dev/null +++ b/backend/src/common/modules/cache/providers/ioredis.provider.ts @@ -0,0 +1,52 @@ +import Redis from 'ioredis'; +import { IORedisProviderOptions, Store } from '../interfaces'; + +export class IORedisProvider implements Store { + private redis: Redis; + + constructor(options?: IORedisProviderOptions) { + this.redis = new Redis(options); + } + + async get(key: string): Promise { + const value = await this.redis.get(key); + if (value !== undefined && value !== null) { + return JSON.parse(value) as T; + } + + return undefined; + } + + async set(key: string, value: T, seconds?: number): Promise { + const valueStr = JSON.stringify(value); + if (seconds) { + await this.redis.setex(key, seconds, valueStr); + } else { + await this.redis.set(key, valueStr); + } + } + + async del(key: string): Promise { + const result = await this.redis.del(key); + + return result > 0; + } + + async clear(): Promise { + const result = await this.redis.flushdb(); + + return result === 'OK'; + } + + async wrap(key: string, fn: () => Promise, seconds?: number): Promise { + const value = await this.get(key); + if (value !== undefined) { + return value; + } + + const result = await fn(); + await this.set(key, result, seconds); + + return result; + } +} diff --git a/backend/src/common/modules/cache/providers/stub.provider.ts b/backend/src/common/modules/cache/providers/stub.provider.ts new file mode 100644 index 0000000..35207cf --- /dev/null +++ b/backend/src/common/modules/cache/providers/stub.provider.ts @@ -0,0 +1,9 @@ +import { Store } from '../interfaces'; + +export class StubProvider implements Store { + get = async () => undefined; + set = async () => null; + del = async () => true; + clear = async () => true; + wrap = async (_: string, fn: () => Promise) => fn(); +} diff --git a/backend/src/common/modules/cache/types/cache-provider.type.ts b/backend/src/common/modules/cache/types/cache-provider.type.ts new file mode 100644 index 0000000..226f4aa --- /dev/null +++ b/backend/src/common/modules/cache/types/cache-provider.type.ts @@ -0,0 +1 @@ +export type CacheProviderType = 'stub' | 'ioredis'; diff --git a/backend/src/common/modules/cache/types/index.ts b/backend/src/common/modules/cache/types/index.ts new file mode 100644 index 0000000..c2b58b9 --- /dev/null +++ b/backend/src/common/modules/cache/types/index.ts @@ -0,0 +1 @@ +export * from './cache-provider.type'; diff --git a/backend/src/common/modules/global-http/dns-cache.service.ts b/backend/src/common/modules/global-http/dns-cache.service.ts new file mode 100644 index 0000000..a377c73 --- /dev/null +++ b/backend/src/common/modules/global-http/dns-cache.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import CacheableLookup, { LookupOptions as CacheableLookupOptions } from '@esm2cjs/cacheable-lookup'; +import { LookupFunction } from 'net'; + +@Injectable() +export class DnsCacheService { + private cacheableLookup: CacheableLookup; + + constructor() { + this.cacheableLookup = new CacheableLookup({ + maxTtl: 300, // Cache DNS records for 5 minutes + errorTtl: 30, // Cache DNS errors for 30 seconds + fallbackDuration: 600, // Use outdated entries for 10 minutes if DNS is down + }); + } + + get lookupFunction(): LookupFunction { + return (hostname, options, callback) => { + if (typeof options === 'function') { + callback = options; + options = {}; + } + const cacheableLookupOptions = options as unknown as CacheableLookupOptions; + return this.cacheableLookup.lookup(hostname, cacheableLookupOptions, callback); + }; + } +} diff --git a/backend/src/common/modules/global-http/global-http.module.ts b/backend/src/common/modules/global-http/global-http.module.ts new file mode 100644 index 0000000..d157945 --- /dev/null +++ b/backend/src/common/modules/global-http/global-http.module.ts @@ -0,0 +1,21 @@ +import { Global, Module } from '@nestjs/common'; +import { HttpModule } from '@nestjs/axios'; +import * as http from 'http'; +import * as https from 'https'; +import { DnsCacheService } from './dns-cache.service'; + +@Global() +@Module({ + imports: [ + HttpModule.registerAsync({ + useFactory: (dnsCacheService: DnsCacheService) => ({ + httpAgent: new http.Agent({ lookup: dnsCacheService.lookupFunction }), + httpsAgent: new https.Agent({ lookup: dnsCacheService.lookupFunction }), + }), + inject: [DnsCacheService], + }), + ], + providers: [DnsCacheService], + exports: [HttpModule, DnsCacheService], +}) +export class GlobalHttpModule {} diff --git a/backend/src/common/modules/global-http/index.ts b/backend/src/common/modules/global-http/index.ts new file mode 100644 index 0000000..372d9c4 --- /dev/null +++ b/backend/src/common/modules/global-http/index.ts @@ -0,0 +1,2 @@ +export * from './dns-cache.service'; +export * from './global-http.module'; diff --git a/backend/src/common/modules/index.ts b/backend/src/common/modules/index.ts new file mode 100644 index 0000000..8c82ea9 --- /dev/null +++ b/backend/src/common/modules/index.ts @@ -0,0 +1,4 @@ +export * from './cache'; +export * from './global-http'; +export * from './token'; +export * from './url-generator'; diff --git a/backend/src/common/modules/token/errors/index.ts b/backend/src/common/modules/token/errors/index.ts new file mode 100644 index 0000000..3e0186d --- /dev/null +++ b/backend/src/common/modules/token/errors/index.ts @@ -0,0 +1 @@ +export * from './invalid-token.error'; diff --git a/backend/src/common/modules/token/errors/invalid-token.error.ts b/backend/src/common/modules/token/errors/invalid-token.error.ts new file mode 100644 index 0000000..f00c6d3 --- /dev/null +++ b/backend/src/common/modules/token/errors/invalid-token.error.ts @@ -0,0 +1,8 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '../../../errors'; + +export class InvalidTokenError extends ServiceError { + constructor(message = 'Invalid token') { + super({ errorCode: 'invalid_token', status: HttpStatus.UNAUTHORIZED, message }); + } +} diff --git a/backend/src/common/modules/token/index.ts b/backend/src/common/modules/token/index.ts new file mode 100644 index 0000000..f6d0f63 --- /dev/null +++ b/backend/src/common/modules/token/index.ts @@ -0,0 +1,2 @@ +export * from './token.module'; +export * from './token.service'; diff --git a/backend/src/common/modules/token/token.module.ts b/backend/src/common/modules/token/token.module.ts new file mode 100644 index 0000000..c923900 --- /dev/null +++ b/backend/src/common/modules/token/token.module.ts @@ -0,0 +1,17 @@ +import { Global, Module } from '@nestjs/common'; +import { TokenService } from './token.service'; +import { JwtModule } from '@nestjs/jwt'; + +@Global() +@Module({ + imports: [ + JwtModule.register({ + global: true, + privateKey: `${process.cwd()}/var/jwt/private.pem`, + publicKey: `${process.cwd()}/var/jwt/private.pem`, + }), + ], + providers: [TokenService], + exports: [TokenService], +}) +export class TokenModule {} diff --git a/backend/src/common/modules/token/token.service.ts b/backend/src/common/modules/token/token.service.ts new file mode 100644 index 0000000..4cdb69a --- /dev/null +++ b/backend/src/common/modules/token/token.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { JwtService, JwtSignOptions } from '@nestjs/jwt'; +import { JsonWebTokenError } from 'jsonwebtoken'; + +import { InvalidTokenError } from './errors'; + +@Injectable() +export class TokenService { + constructor(private readonly jwtService: JwtService) {} + + create(payload: Buffer | object, options?: JwtSignOptions): string { + return this.jwtService.sign(payload, options); + } + + verify(token: string): Payload { + try { + return this.jwtService.verify(token); + } catch (e) { + if (e instanceof JsonWebTokenError) { + throw new InvalidTokenError(e.message); + } + throw e; + } + } +} diff --git a/backend/src/common/modules/url-generator/index.ts b/backend/src/common/modules/url-generator/index.ts new file mode 100644 index 0000000..d9c1124 --- /dev/null +++ b/backend/src/common/modules/url-generator/index.ts @@ -0,0 +1,2 @@ +export * from './url-generator.module'; +export * from './url-generator.service'; diff --git a/backend/src/common/modules/url-generator/url-generator.module.ts b/backend/src/common/modules/url-generator/url-generator.module.ts new file mode 100644 index 0000000..babff04 --- /dev/null +++ b/backend/src/common/modules/url-generator/url-generator.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { UrlGeneratorService } from './url-generator.service'; + +@Module({ + providers: [UrlGeneratorService], + exports: [UrlGeneratorService], +}) +export class UrlGeneratorModule {} diff --git a/backend/src/common/modules/url-generator/url-generator.service.ts b/backend/src/common/modules/url-generator/url-generator.service.ts new file mode 100644 index 0000000..5e3ab39 --- /dev/null +++ b/backend/src/common/modules/url-generator/url-generator.service.ts @@ -0,0 +1,34 @@ +import { ConfigService } from '@nestjs/config'; +import { Injectable } from '@nestjs/common'; + +import { ApplicationConfig } from '@/config'; + +import { FormatUrlOptions, formatUrlPath, formatUrlQuery } from '../../utils'; + +type CreateUrlParams = { route?: string; subdomain?: string } & FormatUrlOptions; + +@Injectable() +export class UrlGeneratorService { + private _appConfig: ApplicationConfig; + + constructor(private readonly configService: ConfigService) { + this._appConfig = this.configService.get('application'); + } + + private baseUrl(subdomain?: string) { + return subdomain ? `${this._appConfig.baseUrlTemplate.replace('{subdomain}', subdomain)}` : this._appConfig.baseUrl; + } + + createUrl(params?: CreateUrlParams): string { + if (!params) { + return this.baseUrl(); + } + + let formattedRoute = params.path ? formatUrlPath(params.route, params.path) : params.route; + if (formattedRoute.length && !formattedRoute.startsWith('/')) { + formattedRoute = '/' + formattedRoute; + } + + return formatUrlQuery(`${this.baseUrl(params.subdomain)}${formattedRoute}`, params.query); + } +} diff --git a/backend/src/common/types/date/date-period.ts b/backend/src/common/types/date/date-period.ts new file mode 100644 index 0000000..5df79d1 --- /dev/null +++ b/backend/src/common/types/date/date-period.ts @@ -0,0 +1,69 @@ +import { DatePeriodDto, DatePeriodFilter } from '../../dto'; +import { DatePeriodFilterType } from '../../enums'; +import { DateUtil } from '../../utils'; + +export class DatePeriod { + public from?: Date | null; + public to?: Date | null; + + constructor(from: Date | null | undefined, to: Date | null | undefined) { + this.from = from; + this.to = to; + } + + public static fromDto(dto: DatePeriodDto, isISO = true): DatePeriod { + return new DatePeriod( + dto.startDate ? (isISO ? DateUtil.fromISOString(dto.startDate) : new Date(dto.startDate)) : undefined, + dto.endDate ? (isISO ? DateUtil.fromISOString(dto.endDate) : new Date(dto.endDate)) : undefined, + ); + } + + public static fromFilter(filter: DatePeriodFilter): DatePeriod { + const now = DateUtil.now(); + let from: Date | null = null; + let to: Date | null = null; + switch (filter.type) { + case DatePeriodFilterType.Today: + from = DateUtil.startOf(now, 'day'); + to = DateUtil.endOf(now, 'day'); + break; + case DatePeriodFilterType.Yesterday: + from = DateUtil.sub(DateUtil.startOf(now, 'day'), { days: 1 }); + to = DateUtil.sub(DateUtil.endOf(now, 'day'), { days: 1 }); + break; + case DatePeriodFilterType.CurrentWeek: + from = DateUtil.startOf(now, 'week'); + to = DateUtil.endOf(now, 'week'); + break; + case DatePeriodFilterType.LastWeek: + from = DateUtil.sub(DateUtil.startOf(now, 'week'), { weeks: 1 }); + to = DateUtil.sub(DateUtil.endOf(now, 'week'), { weeks: 1 }); + break; + case DatePeriodFilterType.CurrentMonth: + from = DateUtil.startOf(now, 'month'); + to = DateUtil.endOf(now, 'month'); + break; + case DatePeriodFilterType.LastMonth: + from = DateUtil.sub(DateUtil.startOf(now, 'month'), { months: 1 }); + to = DateUtil.sub(DateUtil.endOf(now, 'month'), { months: 1 }); + break; + case DatePeriodFilterType.CurrentQuarter: + from = DateUtil.startOf(now, 'quarter'); + to = DateUtil.endOf(now, 'quarter'); + break; + case DatePeriodFilterType.LastQuarter: + from = DateUtil.sub(DateUtil.startOf(now, 'quarter'), { months: 3 }); + to = DateUtil.sub(DateUtil.endOf(now, 'quarter'), { months: 3 }); + break; + case DatePeriodFilterType.Period: + from = filter.from ? DateUtil.fromISOString(filter.from) : null; + to = filter.to ? DateUtil.fromISOString(filter.to) : null; + break; + } + return new DatePeriod(from, to); + } + + public toDto(): DatePeriodDto { + return { startDate: this.from?.toISOString(), endDate: this.to?.toISOString() }; + } +} diff --git a/backend/src/common/types/date/index.ts b/backend/src/common/types/date/index.ts new file mode 100644 index 0000000..8114e31 --- /dev/null +++ b/backend/src/common/types/date/index.ts @@ -0,0 +1 @@ +export * from './date-period'; diff --git a/backend/src/common/types/events/index.ts b/backend/src/common/types/events/index.ts new file mode 100644 index 0000000..dfc1ba6 --- /dev/null +++ b/backend/src/common/types/events/index.ts @@ -0,0 +1 @@ +export * from './service.event'; diff --git a/backend/src/common/types/events/service.event.ts b/backend/src/common/types/events/service.event.ts new file mode 100644 index 0000000..c4920db --- /dev/null +++ b/backend/src/common/types/events/service.event.ts @@ -0,0 +1,34 @@ +import { v4 as uuidv4 } from 'uuid'; + +export class ServiceEvent { + source: string; + key: string; + prevEvent?: ServiceEvent | null; + + constructor(data: { source: string; key?: string; prevEvent?: ServiceEvent | null }) { + this.source = data.source; + this.key = data.key ?? uuidv4(); + this.prevEvent = data.prevEvent; + } + + checkHistory({ + source, + key, + checked = [], + }: { + source?: string; + key?: string; + checked?: { source: string; key: string }[]; + }): T | null { + if (checked.find((item) => item.source === this.source && item.key === this.key)) { + return null; + } + checked.push({ source: this.source, key: this.key }); + + if ((!source || this.source === source) && (!key || this.key === key)) { + return this as unknown as T; + } + + return this.prevEvent ? this.prevEvent.checkHistory({ source, key }) : null; + } +} diff --git a/backend/src/common/types/index.ts b/backend/src/common/types/index.ts new file mode 100644 index 0000000..320657c --- /dev/null +++ b/backend/src/common/types/index.ts @@ -0,0 +1,5 @@ +export * from './date'; +export * from './events'; +export * from './quantity-amount'; +export * from './sort-order'; +export * from './task-queue'; diff --git a/backend/src/common/types/quantity-amount.ts b/backend/src/common/types/quantity-amount.ts new file mode 100644 index 0000000..1c5da43 --- /dev/null +++ b/backend/src/common/types/quantity-amount.ts @@ -0,0 +1,26 @@ +import { QuantityAmountDto } from '../dto'; + +export class QuantityAmount { + quantity: number; + amount: number; + + constructor(quantity: number, amount: number) { + this.quantity = quantity; + this.amount = amount; + } + + public static empty(): QuantityAmount { + return new QuantityAmount(0, 0); + } + + public toDto(): QuantityAmountDto { + return new QuantityAmountDto(this.quantity, this.amount); + } + + public add(value?: QuantityAmount): QuantityAmount { + this.quantity += value?.quantity ?? 0; + this.amount += value?.amount ?? 0; + + return this; + } +} diff --git a/backend/src/common/types/sort-order.ts b/backend/src/common/types/sort-order.ts new file mode 100644 index 0000000..65d9433 --- /dev/null +++ b/backend/src/common/types/sort-order.ts @@ -0,0 +1 @@ +export type SortOrder = 'ASC' | 'DESC'; diff --git a/backend/src/common/types/task-queue.ts b/backend/src/common/types/task-queue.ts new file mode 100644 index 0000000..5e4d704 --- /dev/null +++ b/backend/src/common/types/task-queue.ts @@ -0,0 +1,31 @@ +import { Logger } from '@nestjs/common'; + +type Task = () => Promise; + +export class TaskQueue { + private readonly logger = new Logger(TaskQueue.name); + private readonly queue: Task[] = []; + private isProcessing = false; + + enqueue(task: Task) { + this.queue.push(task); + this.process(); + } + + private async process() { + if (this.isProcessing) return; + this.isProcessing = true; + + while (this.queue.length > 0) { + const task = this.queue.shift(); + try { + await task(); + } catch (e) { + const error = e as Error; + this.logger.error(`Process task failed: ${error?.message}`, error?.stack); + } + } + + this.isProcessing = false; + } +} diff --git a/backend/src/common/utils/array.util.ts b/backend/src/common/utils/array.util.ts new file mode 100644 index 0000000..e6eb659 --- /dev/null +++ b/backend/src/common/utils/array.util.ts @@ -0,0 +1,7 @@ +export const isUnique = (value: T, index: number, self: T[]): boolean => { + return self.indexOf(value) === index; +}; + +export const intersection = (arr1: T[] | null | undefined, arr2: T[] | null | undefined): T[] | undefined => { + return arr1 && arr2 ? arr1.filter((id) => arr2.includes(id)) : (arr1 ?? arr2 ?? undefined); +}; diff --git a/backend/src/common/utils/date.util.ts b/backend/src/common/utils/date.util.ts new file mode 100644 index 0000000..2c08b9c --- /dev/null +++ b/backend/src/common/utils/date.util.ts @@ -0,0 +1,189 @@ +import * as FNS from 'date-fns'; + +import { isUnique } from './array.util'; + +type UnitOfTime = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'; + +const WeekDaysNames = { + en: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'], +}; + +type FormatPresets = 'date' | 'time' | 'dateAndTime'; +const Formats: Record = { + date: 'dd.MM.yyyy', + time: 'HH:mm:ss', + dateAndTime: 'dd.MM.yyyy HH:mm:ss', +}; + +export class DateUtil { + static now(): Date { + return new Date(); + } + + static fromISOString(dateStr: string | null): Date | null { + return dateStr ? new Date(dateStr.endsWith('Z') ? dateStr : `${dateStr}Z`) : null; + } + + static parse(date: string, format: string): Date { + return FNS.parse(date, format, DateUtil.now()); + } + + static format(date: Date, formatStr: string, options?: FNS.FormatOptions): string { + return FNS.format(date, formatStr, options); + } + static formatPreset(date: Date, preset: FormatPresets): string { + return FNS.format(date, Formats[preset]); + } + + static add(date: Date, duration: FNS.Duration): Date { + return FNS.add(date, duration); + } + static sub(date: Date, duration: FNS.Duration): Date { + return FNS.sub(date, duration); + } + + static startOf(date: Date, unit: UnitOfTime): Date { + const isoCorrection = date.getTimezoneOffset(); + switch (unit) { + case 'second': + return FNS.startOfSecond(date); + case 'minute': + return FNS.startOfMinute(date); + case 'hour': + return FNS.startOfHour(date); + case 'day': + return FNS.subMinutes(FNS.startOfDay(date), isoCorrection); + case 'week': + //TODO: add settings for week start + return FNS.subMinutes(FNS.startOfWeek(date, { weekStartsOn: 1 }), isoCorrection); + case 'month': + return FNS.subMinutes(FNS.startOfMonth(date), isoCorrection); + case 'quarter': + return FNS.subMinutes(FNS.startOfQuarter(date), isoCorrection); + case 'year': + return FNS.subMinutes(FNS.startOfYear(date), isoCorrection); + } + } + static endOf(date: Date, unit: UnitOfTime): Date { + const isoCorrection = date.getTimezoneOffset(); + switch (unit) { + case 'second': + return FNS.endOfSecond(date); + case 'minute': + return FNS.endOfMinute(date); + case 'hour': + return FNS.endOfHour(date); + case 'day': + return FNS.subMinutes(FNS.endOfDay(date), isoCorrection); + case 'week': + //TODO: add settings for week start + return FNS.subMinutes(FNS.endOfWeek(date, { weekStartsOn: 1 }), isoCorrection); + case 'month': + return FNS.subMinutes(FNS.endOfMonth(date), isoCorrection); + case 'quarter': + return FNS.subMinutes(FNS.endOfQuarter(date), isoCorrection); + case 'year': + return FNS.subMinutes(FNS.endOfYear(date), isoCorrection); + } + } + + static diff({ + startDate, + endDate, + unit, + abs = true, + }: { + startDate: Date; + endDate: Date; + unit: UnitOfTime; + abs?: boolean; + }): number { + let diff = 0; + switch (unit) { + case 'second': + diff = FNS.differenceInSeconds(endDate, startDate); + break; + case 'minute': + diff = FNS.differenceInMinutes(endDate, startDate); + break; + case 'hour': + diff = FNS.differenceInHours(endDate, startDate); + break; + case 'day': + diff = FNS.differenceInDays(endDate, startDate); + break; + case 'week': + diff = FNS.differenceInWeeks(endDate, startDate); + break; + case 'month': + diff = FNS.differenceInMonths(endDate, startDate); + break; + case 'quarter': + diff = FNS.differenceInQuarters(endDate, startDate); + break; + case 'year': + diff = FNS.differenceInYears(endDate, startDate); + break; + } + return abs ? Math.abs(diff) : diff; + } + + static isToday(date: Date): boolean { + return FNS.isToday(date); + } + static isPast(date: Date): boolean { + return FNS.isPast(date); + } + static isFuture(date: Date): boolean { + return FNS.isFuture(date); + } + + static sort(a: Date, b: Date): number { + return a.getTime() - b.getTime(); + } + + static isValid(date: Date): boolean { + return FNS.isValid(date); + } + + static workingDaysBetween(from: Date, to: Date, workingWeekDays: string[]): number { + const workingDays = workingWeekDays + .map((day) => day.toLowerCase()) + .filter(isUnique) + .filter((d) => WeekDaysNames.en.includes(d)); + + const calendarDifference = FNS.differenceInCalendarDays(to, from); + const sign = calendarDifference < 0 ? -1 : 1; + const weeks = sign < 0 ? Math.ceil(calendarDifference / 7) : Math.floor(calendarDifference / 7); + let result = weeks * workingDays.length; + let dateFrom = FNS.add(from, { weeks }); + const nonWorkingWeekDays = WeekDaysNames.en + .filter((day) => !workingDays.includes(day)) + .map((day) => WeekDaysNames.en.indexOf(day)); + while (!FNS.isSameDay(to, dateFrom)) { + result += nonWorkingWeekDays.includes(FNS.getDay(dateFrom)) ? 0 : sign; + dateFrom = FNS.add(dateFrom, { days: sign }); + } + return result; + } + + /** + * Get time offset from UTC string + * @param utcStringChunk in "UTC+0200" format + * @returns time offset in hours + */ + static extractOffsetFromUTCString(utcStringChunk: string): number | null { + const match = utcStringChunk.match(/UTC([+-])(\d{2})(\d{2})/); + + if (!match) { + return null; + } + + const sign = match[1] === '+' ? 1 : -1; + const hours = parseInt(match[2], 10); + const minutes = parseInt(match[3], 10); + const totalOffsetInHours = sign * (hours + minutes / 60); + + return totalOffsetInHours; + } +} diff --git a/backend/src/common/utils/index.ts b/backend/src/common/utils/index.ts new file mode 100644 index 0000000..448e4dd --- /dev/null +++ b/backend/src/common/utils/index.ts @@ -0,0 +1,11 @@ +export * from './array.util'; +export * from './date.util'; +export * from './number.util'; +export * from './object.util'; +export * from './password.util'; +export * from './phone.util'; +export * from './promise.util'; +export * from './propagation.util'; +export * from './string.util'; +export * from './tree.util'; +export * from './url.util'; diff --git a/backend/src/common/utils/number.util.ts b/backend/src/common/utils/number.util.ts new file mode 100644 index 0000000..1c86a8f --- /dev/null +++ b/backend/src/common/utils/number.util.ts @@ -0,0 +1,18 @@ +import writtenNumber from 'written-number'; +import { convert as convertRu } from 'number-to-words-ru'; + +export class NumberUtil { + static toWord(value: number, options?: { language?: string; currency?: string }): string { + return options?.language === 'ru' + ? convertRu(value, { + currency: options?.currency as 'rub' | 'usd' | 'eur', + showNumberParts: { fractional: false }, + showCurrency: { integer: !!options.currency, fractional: !!options.currency }, + }).toLowerCase() + : writtenNumber(value, { lang: options?.language }); + } + + static toNumber(value: unknown): number { + return value ? Number(value) : 0; + } +} diff --git a/backend/src/common/utils/object.util.ts b/backend/src/common/utils/object.util.ts new file mode 100644 index 0000000..497076e --- /dev/null +++ b/backend/src/common/utils/object.util.ts @@ -0,0 +1,11 @@ +export class ObjectUtil { + public static assign(target: T, source: U): T & U { + Object.keys(source).forEach((field) => { + if (source[field] !== undefined) { + target[field] = source[field]; + } + }); + + return target as T & U; + } +} diff --git a/backend/src/common/utils/password.util.ts b/backend/src/common/utils/password.util.ts new file mode 100644 index 0000000..e6f1adb --- /dev/null +++ b/backend/src/common/utils/password.util.ts @@ -0,0 +1,92 @@ +import * as bcrypt from 'bcrypt'; +import { generate } from 'generate-password'; + +const rounds = 12; + +interface GenerateOptions { + /** + * Length of the generated password. + * @default 10 + */ + length?: number; + /** + * Should the password include numbers + * @default false + */ + numbers?: boolean; + /** + * Should the password include symbols, or symbols to include + * @default false + */ + symbols?: boolean | string; + /** + * Should the password include lowercase characters + * @default true + */ + lowercase?: boolean; + /** + * Should the password include uppercase characters + * @default true + */ + uppercase?: boolean; + /** + * Should exclude visually similar characters like 'i' and 'I' + * @default false + */ + excludeSimilarCharacters?: boolean; + /** + * List of characters to be excluded from the password + * @default "" + */ + exclude?: string; + /** + * Password should include at least one character from each pool + * @default false + */ + strict?: boolean; +} +export class PasswordUtil { + static generate(options?: GenerateOptions): string { + return generate(options); + } + + static generateSecure( + options: GenerateOptions = { + length: 12, + numbers: true, + symbols: false, + lowercase: true, + uppercase: true, + excludeSimilarCharacters: true, + }, + ): string { + return generate(options); + } + + static hash(plainPassword: string): string { + return bcrypt.hashSync(plainPassword, rounds); + } + + static verify(password: string, hash: string): boolean { + return bcrypt.compareSync(password, hash); + } + + static isStrong(password: string): boolean { + // Minimum 8 characters + if (password.length < 8) return false; + + // At least one uppercase letter + if (!/[A-Z]/.test(password)) return false; + + // At least one lowercase letter + if (!/[a-z]/.test(password)) return false; + + // At least one number + if (!/[0-9]/.test(password)) return false; + + // At least one special character + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) return false; + + return true; + } +} diff --git a/backend/src/common/utils/phone.util.ts b/backend/src/common/utils/phone.util.ts new file mode 100644 index 0000000..02fa157 --- /dev/null +++ b/backend/src/common/utils/phone.util.ts @@ -0,0 +1,16 @@ +import parsePhoneNumberWithError, { ParseError } from 'libphonenumber-js'; +import { InvalidPhoneError } from '../errors'; + +export class PhoneUtil { + static normalize(phone: string): string { + try { + return parsePhoneNumberWithError(phone).number; + } catch (e) { + if (e instanceof ParseError) { + throw new InvalidPhoneError(e.message); + } else { + throw new InvalidPhoneError(); + } + } + } +} diff --git a/backend/src/common/utils/promise.util.ts b/backend/src/common/utils/promise.util.ts new file mode 100644 index 0000000..2f2bb71 --- /dev/null +++ b/backend/src/common/utils/promise.util.ts @@ -0,0 +1,9 @@ +export const withTimeout = (promise: Promise, ms: number, timeoutResult?: T): Promise => { + const timeout = new Promise((resolve) => setTimeout(() => resolve(timeoutResult), ms)); + + return Promise.race([promise, timeout]); +}; + +export const sleep = (ms: number): Promise => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; diff --git a/backend/src/common/utils/propagation.util.ts b/backend/src/common/utils/propagation.util.ts new file mode 100644 index 0000000..890a484 --- /dev/null +++ b/backend/src/common/utils/propagation.util.ts @@ -0,0 +1,26 @@ +export const propagateData = R }>( + hierarchy: T[], + rowsMap: Map, + initializer: (ownerId: number) => R, +) => { + const propagateSubordinates = (node: T): R | null => { + let aggregatedRow = rowsMap.get(node.id) || initializer(node.id); + + let hasValue = rowsMap.has(node.id); + node.subordinates.forEach((subordinate) => { + const subordinateRow = propagateSubordinates(subordinate); + if (subordinateRow) { + aggregatedRow = aggregatedRow.add(subordinateRow); + hasValue = true; + } + }); + return hasValue ? aggregatedRow : null; + }; + + hierarchy.forEach((node) => { + const aggregatedRow = propagateSubordinates(node); + if (aggregatedRow) { + rowsMap.set(node.id, aggregatedRow); + } + }); +}; diff --git a/backend/src/common/utils/string.util.ts b/backend/src/common/utils/string.util.ts new file mode 100644 index 0000000..492546b --- /dev/null +++ b/backend/src/common/utils/string.util.ts @@ -0,0 +1,62 @@ +import { decode } from 'iconv-lite'; + +export const formatState = (...params: unknown[]): string => params?.join(':'); + +export const parseState = (state: string, parser: (value: string) => T): T[] => + state.split(':').map((part) => parser(part)); + +export const splitByFirstSpace = (input: string): [string, string] => { + const parts = input.split(/\s/, 2); + + return parts.length === 1 ? [parts[0], ''] : [parts[0], input.substring(parts[0].length).trim()]; +}; + +export const capitalizeFirst = (str: string): string => { + return str.length === 0 ? str : str.charAt(0).toUpperCase() + str.slice(1); +}; + +export class StringUtil { + static decode(str: string, from: BufferEncoding, to: BufferEncoding): string { + return Buffer.from(str, from).toString(to); + } + + /** + * Decode MIME "encoded-word" format + * @param encodedStr encoded string + * @returns decoded string + */ + static decodeMimeWord(encodedStr: string): string { + const match = encodedStr.match(/=\?([^?]+)\?([BQ])\?([^?]*)\?=/i); + if (!match) return encodedStr; + + const charset = match[1].toLowerCase(); + const encoding = match[2].toUpperCase(); + const text = match[3]; + + let buffer: Buffer; + if (encoding === 'B') { + buffer = Buffer.from(text, 'base64'); + } else if (encoding === 'Q') { + // Replace underscore with space and decode quoted-printable + buffer = Buffer.from( + text.replace(/_/g, ' ').replace(/=([0-9A-F]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16))), + 'binary', + ); + } else { + return encodedStr; // Unsupported encoding + } + + return decode(buffer, charset); + } + + /** + * Decode RFC 5987 format + * @param encodedStr encoded string + * @returns decoded string + */ + static decodeRFC5987(encodedStr: string): string { + const parts = encodedStr.split("''"); + + return parts.length === 2 ? decodeURIComponent(parts[1].replace(/%20/g, ' ')) : encodedStr; + } +} diff --git a/backend/src/common/utils/tree.util.ts b/backend/src/common/utils/tree.util.ts new file mode 100644 index 0000000..c01b378 --- /dev/null +++ b/backend/src/common/utils/tree.util.ts @@ -0,0 +1,25 @@ +export const flattenTree = ( + root: T | T[], + accessor: (item: T) => T | T[] | null | undefined, + depthFirst = true, +): T[] => { + const result: T[] = []; + const queue: T[] = Array.isArray(root) ? [...root] : [root]; + + while (queue.length > 0) { + const current = depthFirst ? queue.pop() : queue.shift(); + result.push(current); + + const children = accessor(current); + if (children) { + const items = Array.isArray(children) ? children : [children]; + if (depthFirst) { + queue.push(...items.reverse()); + } else { + queue.unshift(...items); + } + } + } + + return result; +}; diff --git a/backend/src/common/utils/url.util.ts b/backend/src/common/utils/url.util.ts new file mode 100644 index 0000000..2af0513 --- /dev/null +++ b/backend/src/common/utils/url.util.ts @@ -0,0 +1,37 @@ +import { compile, ParseOptions } from 'path-to-regexp'; + +type UrlParams = Record; +export interface FormatUrlOptions { + path?: UrlParams; + query?: UrlParams; +} + +export const formatUrl = (url: string, options?: FormatUrlOptions): string => { + return formatUrlQuery(formatUrlPath(url, options?.path), options?.query); +}; + +export const formatUrlPath = (url: string, params?: UrlParams | null): string => { + if (!params) { + return url; + } + + const toPath = compile(url, { encode: encodeURIComponent } as ParseOptions); + return toPath(params); +}; + +export const formatUrlQuery = (url: string, params?: UrlParams | null): string => { + if (!params) { + return url; + } + + const urlObj = new URL(url); + const searchParams = urlObj.searchParams; + + for (const key in params) { + if (params[key] !== undefined) { + searchParams.set(key, params[key].toString()); + } + } + + return String(urlObj); +}; diff --git a/backend/src/config/application.config.ts b/backend/src/config/application.config.ts new file mode 100644 index 0000000..54c32ec --- /dev/null +++ b/backend/src/config/application.config.ts @@ -0,0 +1,30 @@ +import { registerAs } from '@nestjs/config'; + +export interface ApplicationConfig { + baseUrl: string; + baseUrlTemplate: string; + port: number; + name: string; + apiKeyRequired: boolean; + feedbackEmail: string; + supportEmail: string; + verificationToken: string; + skeletonKey?: string; + subdomainPrefix: string; +} + +export default registerAs( + 'application', + (): ApplicationConfig => ({ + baseUrl: process.env.APPLICATION_BASE_URL, + baseUrlTemplate: process.env.APPLICATION_BASE_URL_TEMPLATE, + port: parseInt(process.env.APPLICATION_PORT, 10) || 8000, + name: process.env.APPLICATION_NAME, + apiKeyRequired: process.env.APPLICATION_API_KEY_REQUIRED === 'true', + feedbackEmail: process.env.APPLICATION_FEEDBACK_EMAIL, + supportEmail: process.env.APPLICATION_SUPPORT_EMAIL, + verificationToken: process.env.APPLICATION_VERIFICATION_TOKEN, + skeletonKey: process.env.APPLICATION_SKELETON_KEY, + subdomainPrefix: process.env.APPLICATION_SUBDOMAIN_PREFIX, + }), +); diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts new file mode 100644 index 0000000..253b86d --- /dev/null +++ b/backend/src/config/index.ts @@ -0,0 +1 @@ +export * from './application.config'; diff --git a/backend/src/database/backup/empty.2024-07-23.sql b/backend/src/database/backup/empty.2024-07-23.sql new file mode 100644 index 0000000..d4f7761 --- /dev/null +++ b/backend/src/database/backup/empty.2024-07-23.sql @@ -0,0 +1,9734 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 14.12 (Ubuntu 14.12-1.pgdg22.04+1ubuntu4) +-- Dumped by pg_dump version 14.9 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: chat_provider_user_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.chat_provider_user_type AS ENUM ( + 'accessible', + 'responsible', + 'supervisor' +); + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: account; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.account ( + id integer NOT NULL, + company_name character varying(255) NOT NULL, + subdomain character varying(255) NOT NULL, + created_at timestamp without time zone NOT NULL, + logo_id uuid +); + + +-- +-- Name: account_api_access; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.account_api_access ( + account_id integer NOT NULL, + api_key character varying NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: account_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.account_id_seq + AS integer + START WITH 11023201 + INCREMENT BY 1 + MINVALUE 11023201 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: account_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.account_settings ( + account_id integer NOT NULL, + language character varying NOT NULL, + working_days character varying, + working_time_from time without time zone, + working_time_to time without time zone, + time_zone character varying, + currency character varying(3) NOT NULL, + number_format character varying, + phone_format character varying DEFAULT 'international'::character varying NOT NULL, + allow_duplicates boolean DEFAULT false NOT NULL +); + + +-- +-- Name: account_subscription; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.account_subscription ( + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + expired_at timestamp without time zone, + is_trial boolean NOT NULL, + user_limit integer NOT NULL, + plan_name character varying NOT NULL, + external_customer_id character varying +); + + +-- +-- Name: action; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.action ( + id integer NOT NULL, + type character varying(100) NOT NULL, + delay integer, + account_id integer NOT NULL +); + + +-- +-- Name: action_activity_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.action_activity_settings ( + action_id integer NOT NULL, + responsible_user_type character varying(100) NOT NULL, + responsible_user_id integer, + activity_type_id integer NOT NULL, + text character varying NOT NULL, + deadline_type character varying(100) NOT NULL, + deadline_time integer, + account_id integer NOT NULL +); + + +-- +-- Name: action_email_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.action_email_settings ( + action_id integer NOT NULL, + subject character varying NOT NULL, + content character varying, + send_as_html boolean NOT NULL, + mailbox_id integer, + user_id integer, + account_id integer NOT NULL, + signature character varying +); + + +-- +-- Name: action_entity_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.action_entity_settings ( + action_id integer NOT NULL, + stage_id integer NOT NULL, + account_id integer NOT NULL, + operation_type character varying DEFAULT 'move'::character varying NOT NULL +); + + +-- +-- Name: action_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.action_id_seq + AS integer + START WITH 51011001 + INCREMENT BY 1 + MINVALUE 51011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: action_scheduled; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.action_scheduled ( + id integer NOT NULL, + action_id integer NOT NULL, + entity_id integer NOT NULL, + scheduled_time timestamp without time zone NOT NULL, + completed boolean NOT NULL, + account_id integer NOT NULL, + created_by integer NOT NULL +); + + +-- +-- Name: action_task_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.action_task_settings ( + action_id integer NOT NULL, + responsible_user_type character varying(100) NOT NULL, + responsible_user_id integer, + title character varying NOT NULL, + text character varying NOT NULL, + deadline_type character varying(100) NOT NULL, + deadline_time integer, + account_id integer NOT NULL +); + + +-- +-- Name: activity; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.activity ( + id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + account_id integer NOT NULL, + created_by integer NOT NULL, + responsible_user_id integer NOT NULL, + text character varying NOT NULL, + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone NOT NULL, + is_resolved boolean NOT NULL, + result character varying, + activity_type_id integer NOT NULL, + entity_id integer NOT NULL, + resolved_date timestamp without time zone, + weight double precision NOT NULL +); + + +-- +-- Name: activity_type; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.activity_type ( + id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + account_id integer NOT NULL, + name character varying(128) NOT NULL, + is_active boolean DEFAULT true NOT NULL +); + + +-- +-- Name: activity_type_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.activity_type_id_seq + AS integer + START WITH 25022001 + INCREMENT BY 1 + MINVALUE 25022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.task ( + id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + account_id integer NOT NULL, + created_by integer NOT NULL, + responsible_user_id integer NOT NULL, + text character varying NOT NULL, + start_date timestamp without time zone, + end_date timestamp without time zone, + is_resolved boolean NOT NULL, + title character varying NOT NULL, + entity_id integer, + stage_id integer, + planned_time integer, + settings_id integer, + resolved_date timestamp without time zone, + weight double precision NOT NULL, + board_id integer +); + + +-- +-- Name: all_tasks; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.all_tasks AS + SELECT activity.id, + activity.created_at, + activity.account_id, + activity.created_by, + activity.responsible_user_id, + activity.text, + activity.start_date, + activity.end_date, + activity.is_resolved, + activity.resolved_date, + activity.result, + activity.entity_id, + activity.weight, + activity.activity_type_id, + NULL::character varying AS title, + NULL::integer AS planned_time, + NULL::integer AS settings_id, + NULL::integer AS board_id, + NULL::integer AS stage_id, + 'activity'::text AS type + FROM public.activity +UNION + SELECT task.id, + task.created_at, + task.account_id, + task.created_by, + task.responsible_user_id, + task.text, + task.start_date, + task.end_date, + task.is_resolved, + task.resolved_date, + NULL::character varying AS result, + task.entity_id, + task.weight, + NULL::integer AS activity_type_id, + task.title, + task.planned_time, + task.settings_id, + task.board_id, + task.stage_id, + 'task'::text AS type + FROM public.task + ORDER BY 1; + + +-- +-- Name: appsumo_license; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.appsumo_license ( + id integer NOT NULL, + license_key text NOT NULL, + license_status text NOT NULL, + plan_id text NOT NULL, + tier integer NOT NULL, + account_id integer, + created_at timestamp without time zone DEFAULT now() NOT NULL, + prev_license_key text +); + + +-- +-- Name: app_sumo_license_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.appsumo_license ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.app_sumo_license_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: appsumo_tier; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.appsumo_tier ( + id integer NOT NULL, + tier integer NOT NULL, + user_limit integer NOT NULL, + term_in_days integer NOT NULL, + plan_name text NOT NULL +); + + +-- +-- Name: app_sumo_preset_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.appsumo_tier ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.app_sumo_preset_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: automation; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.automation ( + id integer NOT NULL, + trigger_id integer NOT NULL, + action_id integer NOT NULL, + created_by integer NOT NULL, + is_active boolean NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: automation_condition; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.automation_condition ( + automation_id integer NOT NULL, + condition_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: automation_entity_type; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.automation_entity_type ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + created_by integer NOT NULL, + name text NOT NULL, + triggers text NOT NULL, + entity_type_id integer NOT NULL, + board_id integer, + stage_id integer, + conditions jsonb, + actions jsonb, + process_id integer +); + + +-- +-- Name: automation_entity_type_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.automation_entity_type ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.automation_entity_type_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: automation_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.automation_id_seq + AS integer + START WITH 51011001 + INCREMENT BY 1 + MINVALUE 51011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: automation_process; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.automation_process ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + resource_key text, + bpmn_file text, + bpmn_process_id text +); + + +-- +-- Name: automation_process_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.automation_process ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.automation_process_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: automation_stage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.automation_stage ( + automation_id integer NOT NULL, + stage_id integer NOT NULL +); + + +-- +-- Name: board; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.board ( + id integer NOT NULL, + name character varying(100) NOT NULL, + type character varying(50) NOT NULL, + record_id integer, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + sort_order smallint DEFAULT 0 NOT NULL, + is_system boolean DEFAULT false NOT NULL, + code character varying(255), + need_migration boolean DEFAULT false, + task_board_id integer, + owner_id integer, + participant_ids jsonb +); + + +-- +-- Name: board_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.board_id_seq + AS integer + START WITH 14022001 + INCREMENT BY 1 + MINVALUE 14022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chat; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat ( + id integer NOT NULL, + provider_id integer NOT NULL, + created_by integer, + external_id character varying, + type character varying NOT NULL, + title character varying, + entity_id integer, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: chat_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chat_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chat_message; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_message ( + id integer NOT NULL, + chat_id integer NOT NULL, + chat_user_id integer NOT NULL, + external_id character varying, + text character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + reply_to_id integer +); + + +-- +-- Name: chat_message_file; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_message_file ( + id integer NOT NULL, + message_id integer NOT NULL, + external_id character varying, + account_id integer NOT NULL, + file_id uuid, + name character varying NOT NULL, + mime_type character varying NOT NULL, + size integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: chat_message_file_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chat_message_file_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chat_message_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chat_message_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chat_message_reaction; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_message_reaction ( + id integer NOT NULL, + message_id integer NOT NULL, + chat_user_id integer NOT NULL, + reaction character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: chat_message_reaction_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chat_message_reaction_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chat_message_user_status; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_message_user_status ( + chat_id integer NOT NULL, + message_id integer NOT NULL, + chat_user_id integer NOT NULL, + status character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: chat_pinned_message; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_pinned_message ( + chat_id integer NOT NULL, + message_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: chat_provider; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_provider ( + id integer NOT NULL, + created_by integer NOT NULL, + type character varying NOT NULL, + title character varying, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + status character varying DEFAULT 'draft'::character varying NOT NULL, + transport character varying NOT NULL +); + + +-- +-- Name: chat_provider_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chat_provider_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chat_provider_messenger; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_provider_messenger ( + provider_id integer NOT NULL, + page_id character varying NOT NULL, + page_access_token character varying NOT NULL, + account_id integer NOT NULL, + user_id character varying NOT NULL, + user_access_token character varying NOT NULL +); + + +-- +-- Name: chat_provider_twilio; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_provider_twilio ( + provider_id integer NOT NULL, + account_sid character varying NOT NULL, + auth_token character varying NOT NULL, + phone_number character varying NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: chat_provider_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_provider_user ( + provider_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer NOT NULL, + type public.chat_provider_user_type DEFAULT 'accessible'::public.chat_provider_user_type NOT NULL +); + + +-- +-- Name: chat_provider_wazzup; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_provider_wazzup ( + provider_id integer NOT NULL, + account_id integer NOT NULL, + api_key character varying NOT NULL, + channel_id character varying NOT NULL, + plain_id character varying NOT NULL, + transport character varying NOT NULL +); + + +-- +-- Name: chat_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_user ( + id integer NOT NULL, + chat_id integer NOT NULL, + user_id integer, + role character varying NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: chat_user_external; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chat_user_external ( + account_id integer NOT NULL, + external_id character varying NOT NULL, + first_name character varying, + last_name character varying, + avatar_url character varying, + phone character varying, + email character varying, + link character varying, + chat_user_id integer NOT NULL +); + + +-- +-- Name: chat_user_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chat_user_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: condition; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.condition ( + id integer NOT NULL, + type character varying(100) NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: condition_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.condition_id_seq + AS integer + START WITH 51011001 + INCREMENT BY 1 + MINVALUE 51011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: demo_data; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.demo_data ( + id integer NOT NULL, + account_id integer NOT NULL, + type character varying NOT NULL, + ids character varying NOT NULL +); + + +-- +-- Name: demo_data_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.demo_data ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.demo_data_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: department; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.department ( + id integer NOT NULL, + name character varying NOT NULL, + parent_id integer, + account_id integer NOT NULL +); + + +-- +-- Name: department_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.department_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: document_template; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_template ( + id integer NOT NULL, + name character varying NOT NULL, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + created_count integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: document_template_access; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_template_access ( + document_template_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: document_template_entity_type; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_template_entity_type ( + document_template_id integer NOT NULL, + entity_type_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: document_template_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.document_template_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: entity; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity ( + id integer NOT NULL, + name character varying(255) NOT NULL, + entity_type_id integer NOT NULL, + responsible_user_id integer NOT NULL, + stage_id integer, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + participant_ids jsonb, + weight double precision NOT NULL, + closed_at timestamp without time zone, + copied_from integer, + copied_count integer +); + + +-- +-- Name: entity_event; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_event ( + id integer NOT NULL, + account_id integer NOT NULL, + entity_id integer NOT NULL, + object_id integer NOT NULL, + type character varying NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: entity_event_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.entity_event ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.entity_event_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: entity_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.entity_id_seq + AS integer + START WITH 14022001 + INCREMENT BY 1 + MINVALUE 14022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: entity_link; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_link ( + id integer NOT NULL, + source_id integer NOT NULL, + target_id integer NOT NULL, + sort_order smallint NOT NULL, + back_link_id integer, + account_id integer NOT NULL +); + + +-- +-- Name: entity_link_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.entity_link_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: entity_list_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_list_settings ( + id integer NOT NULL, + entity_type_id integer NOT NULL, + board_id integer, + settings jsonb NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: entity_list_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.entity_list_settings_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: entity_stage_history; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_stage_history ( + id integer NOT NULL, + account_id integer NOT NULL, + entity_id integer NOT NULL, + board_id integer NOT NULL, + stage_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: entity_stage_history_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.entity_stage_history ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.entity_stage_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: entity_type; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_type ( + id integer NOT NULL, + name character varying(255) NOT NULL, + entity_category character varying(50) NOT NULL, + section_name character varying(100) NOT NULL, + section_view character varying(50) NOT NULL, + section_icon character varying(50) NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + sort_order smallint DEFAULT 0 NOT NULL +); + + +-- +-- Name: entity_type_feature; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_type_feature ( + entity_type_id integer NOT NULL, + feature_id smallint NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: entity_type_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.entity_type_id_seq + AS integer + START WITH 13022001 + INCREMENT BY 1 + MINVALUE 13022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: entity_type_link_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.entity_type_link_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: entity_type_link; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entity_type_link ( + id integer DEFAULT nextval('public.entity_type_link_id_seq'::regclass) NOT NULL, + source_id integer NOT NULL, + target_id integer NOT NULL, + sort_order smallint NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: exact_time_trigger_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.exact_time_trigger_settings ( + trigger_id integer NOT NULL, + date timestamp without time zone NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: external_entity_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.external_entity_id_seq + START WITH 30022001 + INCREMENT BY 1 + MINVALUE 30022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: external_entity; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.external_entity ( + id integer DEFAULT nextval('public.external_entity_id_seq'::regclass) NOT NULL, + entity_id integer NOT NULL, + url character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + system smallint, + raw_data jsonb, + ui_data jsonb +); + + +-- +-- Name: external_system; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.external_system ( + id smallint NOT NULL, + name character varying NOT NULL, + code character varying NOT NULL, + url_templates character varying[] NOT NULL +); + + +-- +-- Name: external_system_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.external_system ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.external_system_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: feature; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.feature ( + id smallint NOT NULL, + name character varying NOT NULL, + code character varying NOT NULL, + is_enabled boolean NOT NULL +); + + +-- +-- Name: feature_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.feature ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.feature_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: feed_item_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.feed_item_id_seq + AS integer + START WITH 22022001 + INCREMENT BY 1 + MINVALUE 22022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: file_link; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.file_link ( + id integer NOT NULL, + account_id integer NOT NULL, + source_type character varying NOT NULL, + source_id integer NOT NULL, + file_id uuid NOT NULL, + file_name character varying NOT NULL, + file_size integer NOT NULL, + file_type character varying NOT NULL, + created_at timestamp without time zone NOT NULL, + created_by integer NOT NULL +); + + +-- +-- Name: mail_message; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mail_message ( + id integer NOT NULL, + mailbox_id integer NOT NULL, + external_id character varying NOT NULL, + thread_id character varying NOT NULL, + snippet character varying, + sent_from character varying NOT NULL, + sent_to character varying, + subject character varying, + date timestamp without time zone NOT NULL, + account_id integer NOT NULL, + reply_to character varying, + cc character varying, + has_attachment boolean DEFAULT false NOT NULL, + message_id character varying, + references_to character varying, + in_reply_to character varying, + entity_id integer, + is_seen boolean DEFAULT false NOT NULL +); + + +-- +-- Name: note; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.note ( + id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + text character varying NOT NULL, + entity_id integer NOT NULL, + created_by integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: feed_items; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.feed_items AS + SELECT note.id, + note.created_at, + note.entity_id, + 'note'::text AS type + FROM public.note +UNION + SELECT task.id, + task.created_at, + task.entity_id, + 'task'::text AS type + FROM public.task +UNION + SELECT activity.id, + activity.created_at, + activity.entity_id, + 'activity'::text AS type + FROM public.activity +UNION +( SELECT max(message.id) AS id, + max(message.date) AS created_at, + message.entity_id, + 'mail'::text AS type + FROM public.mail_message message + WHERE (message.entity_id IS NOT NULL) + GROUP BY message.thread_id, message.entity_id + ORDER BY (max(message.date)) DESC) +UNION +( SELECT max(message.id) AS id, + max(message.date) AS created_at, + el.source_id AS entity_id, + 'mail'::text AS type + FROM (public.mail_message message + RIGHT JOIN public.entity_link el ON ((el.target_id = message.entity_id))) + WHERE (message.entity_id IS NOT NULL) + GROUP BY message.thread_id, el.source_id + ORDER BY (max(message.date)) DESC) +UNION + SELECT fl.id, + fl.created_at, + fl.source_id AS entity_id, + 'document'::text AS type + FROM public.file_link fl + WHERE ((fl.source_type)::text = 'entity_document'::text) + ORDER BY 2 DESC; + + +-- +-- Name: field; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field ( + id integer NOT NULL, + name character varying(100) NOT NULL, + type character varying(50) NOT NULL, + sort_order smallint NOT NULL, + entity_type_id integer NOT NULL, + field_group_id integer, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + code character varying(255), + active boolean DEFAULT true NOT NULL, + value character varying +); + + +-- +-- Name: field_condition; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field_condition ( + condition_id integer NOT NULL, + field_id integer NOT NULL, + field_type character varying(50) NOT NULL, + payload jsonb NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: field_group; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field_group ( + id integer NOT NULL, + name character varying(100) NOT NULL, + sort_order smallint NOT NULL, + entity_type_id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: field_group_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.field_group_id_seq + AS integer + START WITH 41022001 + INCREMENT BY 1 + MINVALUE 41022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: field_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.field_id_seq + AS integer + START WITH 42022001 + INCREMENT BY 1 + MINVALUE 42022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: field_option; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field_option ( + id integer NOT NULL, + label character varying(255) NOT NULL, + sort_order smallint NOT NULL, + field_id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + color character varying(50) +); + + +-- +-- Name: field_option_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.field_option_id_seq + AS integer + START WITH 43022001 + INCREMENT BY 1 + MINVALUE 43022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: field_stage_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field_stage_settings ( + id integer NOT NULL, + account_id integer NOT NULL, + field_id integer NOT NULL, + stage_id integer NOT NULL, + access character varying NOT NULL, + exclude_user_ids integer[] +); + + +-- +-- Name: field_stage_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.field_stage_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.field_stage_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: field_user_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field_user_settings ( + id integer NOT NULL, + account_id integer NOT NULL, + field_id integer NOT NULL, + user_id integer NOT NULL, + access character varying NOT NULL +); + + +-- +-- Name: field_user_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.field_user_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.field_user_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: field_value; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.field_value ( + id integer NOT NULL, + field_id integer NOT NULL, + payload jsonb NOT NULL, + entity_id integer NOT NULL, + account_id integer NOT NULL, + field_type character varying(50) NOT NULL +); + + +-- +-- Name: field_value_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.field_value_id_seq + AS integer + START WITH 44022001 + INCREMENT BY 1 + MINVALUE 44022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: file_info; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.file_info ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + created_by integer, + original_name character varying NOT NULL, + mime_type character varying NOT NULL, + size integer NOT NULL, + store_path character varying NOT NULL, + is_used boolean DEFAULT false NOT NULL, + hash_sha256 character varying +); + + +-- +-- Name: file_link_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.file_link ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.file_link_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: industry; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.industry ( + code character varying NOT NULL, + name character varying NOT NULL, + color character varying NOT NULL, + sort_order smallint NOT NULL, + active boolean NOT NULL +); + + +-- +-- Name: mail_message_folder; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mail_message_folder ( + message_id integer NOT NULL, + folder_id integer NOT NULL +); + + +-- +-- Name: mail_message_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.mail_message_id_seq + AS integer + START WITH 28023001 + INCREMENT BY 1 + MINVALUE 28023001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: mail_message_payload; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mail_message_payload ( + id integer NOT NULL, + message_id integer NOT NULL, + mime_type character varying NOT NULL, + filename character varying, + attachment character varying, + content character varying, + account_id integer NOT NULL, + sort_order smallint NOT NULL, + size integer, + external_id character varying +); + + +-- +-- Name: mail_message_payload_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.mail_message_payload_id_seq + AS integer + START WITH 29023001 + INCREMENT BY 1 + MINVALUE 29023001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: mailbox; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + email character varying NOT NULL, + provider character varying NOT NULL, + owner_id integer, + create_contact boolean NOT NULL, + state character varying NOT NULL, + contact_entity_type_id integer, + lead_entity_type_id integer, + lead_board_id integer, + error_message character varying, + emails_per_day smallint, + create_lead boolean DEFAULT false NOT NULL +); + + +-- +-- Name: mailbox_accessible_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox_accessible_user ( + mailbox_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer +); + + +-- +-- Name: mailbox_folder; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox_folder ( + id integer NOT NULL, + mailbox_id integer NOT NULL, + external_id character varying NOT NULL, + name character varying NOT NULL, + type character varying, + account_id integer NOT NULL, + messages_total integer, + messages_unread integer +); + + +-- +-- Name: mailbox_folder_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.mailbox_folder_id_seq + AS integer + START WITH 31023001 + INCREMENT BY 1 + MINVALUE 31023001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: mailbox_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.mailbox_id_seq + AS integer + START WITH 27023001 + INCREMENT BY 1 + MINVALUE 27023001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: mailbox_settings_gmail; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox_settings_gmail ( + mailbox_id integer NOT NULL, + account_id integer NOT NULL, + tokens jsonb NOT NULL, + history_id character varying +); + + +-- +-- Name: mailbox_settings_manual; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox_settings_manual ( + mailbox_id integer NOT NULL, + account_id integer NOT NULL, + password character varying NOT NULL, + imap_server character varying NOT NULL, + imap_port smallint NOT NULL, + imap_secure boolean NOT NULL, + smtp_server character varying NOT NULL, + smtp_port smallint NOT NULL, + smtp_secure boolean NOT NULL, + imap_sync jsonb +); + + +-- +-- Name: mailbox_signature; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox_signature ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + name character varying NOT NULL, + text character varying NOT NULL, + created_by integer NOT NULL +); + + +-- +-- Name: mailbox_signature_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.mailbox_signature_id_seq + AS integer + START WITH 27023001 + INCREMENT BY 1 + MINVALUE 27023001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: mailbox_signature_link; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.mailbox_signature_link ( + account_id integer NOT NULL, + signature_id integer NOT NULL, + mailbox_id integer NOT NULL +); + + +-- +-- Name: migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.migrations ( + id integer NOT NULL, + "timestamp" bigint NOT NULL, + name character varying NOT NULL +); + + +-- +-- Name: migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.migrations_id_seq OWNED BY public.migrations.id; + + +-- +-- Name: notification; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + type character varying NOT NULL, + object_id integer NOT NULL, + entity_id integer, + from_user integer, + title character varying, + description character varying, + is_seen boolean DEFAULT false NOT NULL, + user_id integer NOT NULL, + starts_in integer +); + + +-- +-- Name: notification_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.notification_id_seq + AS integer + START WITH 61011001 + INCREMENT BY 1 + MINVALUE 61011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: notification_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_settings ( + id integer NOT NULL, + account_id integer NOT NULL, + user_id integer NOT NULL, + enable_popup boolean DEFAULT true NOT NULL +); + + +-- +-- Name: notification_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.notification_settings_id_seq + AS integer + START WITH 62011001 + INCREMENT BY 1 + MINVALUE 62011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: notification_type_follow_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_type_follow_user ( + type_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: notification_type_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_type_settings ( + id integer NOT NULL, + account_id integer NOT NULL, + settings_id integer NOT NULL, + type character varying NOT NULL, + is_enabled boolean DEFAULT true NOT NULL, + object_id integer, + before integer +); + + +-- +-- Name: notification_type_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.notification_type_settings_id_seq + AS integer + START WITH 63011001 + INCREMENT BY 1 + MINVALUE 63011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: object_permission; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.object_permission ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + object_type character varying NOT NULL, + object_id integer, + create_permission character varying NOT NULL, + view_permission character varying NOT NULL, + edit_permission character varying NOT NULL, + delete_permission character varying NOT NULL +); + + +-- +-- Name: object_permission_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.object_permission ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.object_permission_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: order_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.order_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: order_item; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.order_item ( + id integer NOT NULL, + unit_price numeric(15,2) NOT NULL, + quantity integer NOT NULL, + tax numeric(5,2) NOT NULL, + discount numeric(5,2) NOT NULL, + product_id integer NOT NULL, + order_id integer NOT NULL, + sort_order smallint NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: order_item_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.order_item_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: order_status; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.order_status ( + id integer NOT NULL, + name character varying(255) NOT NULL, + color character varying(50) NOT NULL, + code character varying(50), + sort_order smallint NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: order_status_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.order_status_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: orders; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.orders ( + id integer NOT NULL, + total_amount numeric(15,2) NOT NULL, + currency character varying(3) NOT NULL, + entity_id integer NOT NULL, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + tax_included boolean DEFAULT true NOT NULL, + status_id integer, + warehouse_id integer, + section_id integer NOT NULL, + order_number integer NOT NULL, + updated_at timestamp without time zone NOT NULL, + cancel_after integer +); + + +-- +-- Name: product; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.product ( + id integer NOT NULL, + name character varying NOT NULL, + description character varying, + sku character varying, + unit character varying, + tax smallint, + is_deleted boolean NOT NULL, + category_id integer, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + type character varying(50) DEFAULT 'product'::character varying NOT NULL, + updated_at timestamp without time zone DEFAULT now(), + section_id integer NOT NULL +); + + +-- +-- Name: product_category; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.product_category ( + id integer NOT NULL, + name character varying NOT NULL, + parent_id integer, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + section_id integer NOT NULL +); + + +-- +-- Name: product_category_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.product_category_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: product_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.product_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: products_section; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.products_section ( + id integer NOT NULL, + name character varying NOT NULL, + icon character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + type character varying DEFAULT 'sale'::character varying NOT NULL, + enable_warehouse boolean NOT NULL, + enable_barcode boolean DEFAULT true NOT NULL, + cancel_after integer +); + + +-- +-- Name: product_module_id_seq1; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.products_section ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.product_module_id_seq1 + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: product_price; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.product_price ( + id integer NOT NULL, + name character varying, + unit_price numeric(15,2), + currency character varying(3) NOT NULL, + product_id integer NOT NULL, + account_id integer NOT NULL, + max_discount integer +); + + +-- +-- Name: product_price_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.product_price_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: product_stock; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.product_stock ( + product_id integer NOT NULL, + warehouse_id integer NOT NULL, + stock_quantity integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: products_section_entity_type; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.products_section_entity_type ( + section_id integer NOT NULL, + entity_type_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: ready_made_solution; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ready_made_solution ( + code character varying NOT NULL, + name character varying NOT NULL, + subdomain character varying NOT NULL, + sort_order smallint NOT NULL, + active boolean NOT NULL, + industry_code character varying, + account_id integer +); + + +-- +-- Name: rental_event; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.rental_event ( + id integer NOT NULL, + product_id integer NOT NULL, + order_item_id integer NOT NULL, + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone NOT NULL, + status character varying NOT NULL, + account_id integer NOT NULL, + section_id integer NOT NULL +); + + +-- +-- Name: rental_interval; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.rental_interval ( + id integer NOT NULL, + section_id integer NOT NULL, + type character varying NOT NULL, + start_time time without time zone, + account_id integer NOT NULL +); + + +-- +-- Name: rental_interval_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.rental_interval ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.rental_interval_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: rental_order; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.rental_order ( + id integer NOT NULL, + section_id integer NOT NULL, + warehouse_id integer, + entity_id integer NOT NULL, + created_by integer NOT NULL, + status character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + currency character varying NOT NULL, + tax_included boolean DEFAULT true NOT NULL, + order_number integer NOT NULL +); + + +-- +-- Name: rental_order_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.rental_order ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.rental_order_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: rental_order_item; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.rental_order_item ( + id integer NOT NULL, + order_id integer NOT NULL, + product_id integer NOT NULL, + sort_order integer NOT NULL, + account_id integer NOT NULL, + unit_price numeric(15,2) NOT NULL, + tax numeric(5,2) NOT NULL, + discount numeric(5,2) NOT NULL +); + + +-- +-- Name: rental_order_item_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.rental_order_item ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.rental_order_item_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: rental_order_period; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.rental_order_period ( + id integer NOT NULL, + order_id integer NOT NULL, + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: rental_order_period_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.rental_order_period ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.rental_order_period_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: rental_schedule_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.rental_event ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.rental_schedule_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: reservation; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.reservation ( + id integer NOT NULL, + order_id integer NOT NULL, + order_item_id integer NOT NULL, + product_id integer NOT NULL, + warehouse_id integer NOT NULL, + quantity integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: reservation_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.reservation_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: sales_plan; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.sales_plan ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + entity_type_id integer NOT NULL, + user_id integer NOT NULL, + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone NOT NULL, + quantity integer, + amount integer +); + + +-- +-- Name: sales_plan_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.sales_plan ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.sales_plan_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: salesforce_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.salesforce_settings ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + domain character varying NOT NULL, + key character varying NOT NULL, + secret character varying NOT NULL, + refresh_token character varying +); + + +-- +-- Name: schedule; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.schedule ( + id integer NOT NULL, + name character varying NOT NULL, + entity_type_id integer, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + products_section_id integer, + icon character varying DEFAULT ''::character varying NOT NULL, + time_period integer, + appointment_limit integer, + type character varying DEFAULT 'schedule'::character varying NOT NULL +); + + +-- +-- Name: schedule_appointment; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.schedule_appointment ( + id integer NOT NULL, + schedule_id integer NOT NULL, + start_date timestamp without time zone NOT NULL, + end_date timestamp without time zone NOT NULL, + status character varying NOT NULL, + comment character varying, + owner_id integer NOT NULL, + entity_id integer, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + order_id integer, + title character varying, + performer_id integer NOT NULL +); + + +-- +-- Name: schedule_event_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.schedule_appointment ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.schedule_event_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: schedule_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.schedule ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.schedule_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: schedule_performer; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.schedule_performer ( + schedule_id integer NOT NULL, + user_id integer, + account_id integer NOT NULL, + id integer NOT NULL, + department_id integer, + type character varying DEFAULT 'user'::character varying NOT NULL +); + + +-- +-- Name: schedule_performer_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.schedule_performer ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.schedule_performer_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: scheduled_action_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.scheduled_action_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: scheduled_mail_message; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.scheduled_mail_message ( + id integer NOT NULL, + send_to jsonb NOT NULL, + subject character varying NOT NULL, + content character varying, + send_as_html boolean NOT NULL, + file_ids jsonb NOT NULL, + sent_at timestamp without time zone, + mailbox_id integer NOT NULL, + user_id integer NOT NULL, + entity_id integer, + action_id integer, + account_id integer NOT NULL +); + + +-- +-- Name: scheduled_mail_message_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.scheduled_mail_message_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: shipment; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.shipment ( + id integer NOT NULL, + name character varying(255) NOT NULL, + warehouse_id integer NOT NULL, + order_id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + shipped_at timestamp without time zone, + status_id integer NOT NULL, + section_id integer NOT NULL, + entity_id integer NOT NULL, + order_number integer NOT NULL +); + + +-- +-- Name: shipment_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.shipment_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: shipment_item; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.shipment_item ( + id integer NOT NULL, + shipment_id integer NOT NULL, + product_id integer NOT NULL, + quantity integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: shipment_item_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.shipment_item_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: site_form; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form ( + id integer NOT NULL, + account_id integer NOT NULL, + name text NOT NULL, + code text NOT NULL, + is_active boolean NOT NULL, + title text, + responsible_id integer, + design jsonb, + field_label_enabled boolean DEFAULT false NOT NULL, + field_placeholder_enabled boolean DEFAULT true NOT NULL, + created_by integer NOT NULL +); + + +-- +-- Name: site_form_consent; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_consent ( + form_id integer NOT NULL, + account_id integer, + is_enabled boolean DEFAULT false NOT NULL, + text text, + link_url text, + link_text text, + default_value boolean DEFAULT false NOT NULL +); + + +-- +-- Name: site_form_entity_type; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_entity_type ( + form_id integer NOT NULL, + entity_type_id integer NOT NULL, + account_id integer NOT NULL, + board_id integer, + is_main boolean NOT NULL +); + + +-- +-- Name: site_form_field; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_field ( + id integer NOT NULL, + account_id integer NOT NULL, + page_id integer NOT NULL, + label text, + type text NOT NULL, + is_required boolean, + sort_order integer NOT NULL, + placeholder text +); + + +-- +-- Name: site_form_field_entity_field; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_field_entity_field ( + form_field_id integer NOT NULL, + field_id integer NOT NULL, + entity_type_id integer NOT NULL, + is_validation_required boolean, + meta jsonb +); + + +-- +-- Name: site_form_field_entity_name; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_field_entity_name ( + form_field_id integer NOT NULL, + entity_type_id integer NOT NULL +); + + +-- +-- Name: site_form_field_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.site_form_field ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.site_form_field_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: site_form_gratitude; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_gratitude ( + form_id integer NOT NULL, + account_id integer, + is_enabled boolean DEFAULT false NOT NULL, + header text, + text text +); + + +-- +-- Name: site_form_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.site_form ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.site_form_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: site_form_page; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.site_form_page ( + id integer NOT NULL, + account_id integer NOT NULL, + form_id integer NOT NULL, + title text, + sort_order integer NOT NULL +); + + +-- +-- Name: site_form_page_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.site_form_page ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.site_form_page_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: stage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.stage ( + id integer NOT NULL, + name character varying(100) NOT NULL, + color character varying(50) NOT NULL, + code character varying(50), + is_system boolean NOT NULL, + sort_order smallint NOT NULL, + board_id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: stage_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.stage_id_seq + AS integer + START WITH 15022001 + INCREMENT BY 1 + MINVALUE 15022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: subtask_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.subtask_id_seq + AS integer + START WITH 46022001 + INCREMENT BY 1 + MINVALUE 46022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_comment; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.task_comment ( + id integer NOT NULL, + text character varying NOT NULL, + task_id integer NOT NULL, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL +); + + +-- +-- Name: task_comment_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.task_comment_id_seq + AS integer + START WITH 47022001 + INCREMENT BY 1 + MINVALUE 47022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_comment_like; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.task_comment_like ( + comment_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer +); + + +-- +-- Name: task_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.task_settings ( + id integer NOT NULL, + active_fields jsonb NOT NULL, + type character varying(100) NOT NULL, + record_id integer, + account_id integer +); + + +-- +-- Name: task_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.task_settings_id_seq + AS integer + START WITH 45022001 + INCREMENT BY 1 + MINVALUE 45022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_subtask; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.task_subtask ( + id integer NOT NULL, + text character varying NOT NULL, + resolved boolean NOT NULL, + task_id integer NOT NULL, + account_id integer NOT NULL, + sort_order integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: test_account; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.test_account ( + account_id integer NOT NULL +); + + +-- +-- Name: trigger; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.trigger ( + id integer NOT NULL, + type character varying(100) NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: trigger_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.trigger_id_seq + AS integer + START WITH 51011001 + INCREMENT BY 1 + MINVALUE 51011001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: tutorial_group; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tutorial_group ( + id integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + name character varying NOT NULL, + sort_order integer NOT NULL +); + + +-- +-- Name: tutorial_group_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.tutorial_group ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.tutorial_group_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: tutorial_item; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tutorial_item ( + id integer NOT NULL, + account_id integer NOT NULL, + group_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + name character varying NOT NULL, + link character varying NOT NULL, + sort_order integer NOT NULL +); + + +-- +-- Name: tutorial_item_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.tutorial_item ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.tutorial_item_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: tutorial_item_product; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tutorial_item_product ( + id integer NOT NULL, + account_id integer NOT NULL, + item_id integer NOT NULL, + type character varying NOT NULL, + object_id integer +); + + +-- +-- Name: tutorial_item_product_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.tutorial_item_product ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.tutorial_item_product_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: tutorial_item_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tutorial_item_user ( + item_id integer NOT NULL, + user_id integer NOT NULL +); + + +-- +-- Name: user_condition; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_condition ( + condition_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.user_id_seq + AS integer + START WITH 12022001 + INCREMENT BY 1 + MINVALUE 12022001 + NO MAXVALUE + CACHE 1; + + +-- +-- Name: user_object_permission; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_object_permission ( + user_id integer NOT NULL, + object_permission_id integer NOT NULL +); + + +-- +-- Name: user_profile; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_profile ( + created_at timestamp without time zone NOT NULL, + user_id integer NOT NULL, + birth_date timestamp without time zone, + employment_date timestamp without time zone, + account_id integer NOT NULL +); + + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users ( + id integer NOT NULL, + email character varying(254) NOT NULL, + password character varying(100) NOT NULL, + created_at timestamp without time zone NOT NULL, + first_name character varying(255) NOT NULL, + last_name character varying(255), + phone character varying(32), + account_id integer NOT NULL, + role character varying(100) NOT NULL, + is_active boolean DEFAULT true NOT NULL, + department_id integer, + avatar_id uuid, + "position" character varying, + analytics_id uuid NOT NULL +); + + +-- +-- Name: voximplant_account; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_account ( + account_id integer NOT NULL, + account_name character varying NOT NULL, + application_id integer NOT NULL, + application_name character varying NOT NULL, + external_id integer NOT NULL, + api_key character varying NOT NULL, + password character varying NOT NULL, + billing_account_id integer NOT NULL, + is_active boolean DEFAULT false NOT NULL, + account_email character varying NOT NULL, + key_id character varying NOT NULL, + private_key character varying NOT NULL +); + + +-- +-- Name: voximplant_call; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_call ( + id integer NOT NULL, + user_id integer NOT NULL, + entity_id integer, + direction character varying NOT NULL, + phone_number character varying NOT NULL, + duration integer, + status character varying, + failure_reason character varying, + record_url character varying, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + session_id character varying NOT NULL, + call_id character varying NOT NULL, + comment character varying, + number_id integer +); + + +-- +-- Name: voximplant_call_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.voximplant_call ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.voximplant_call_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: voximplant_number; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_number ( + id integer NOT NULL, + account_id integer NOT NULL, + phone_number character varying NOT NULL, + external_id character varying +); + + +-- +-- Name: voximplant_number_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.voximplant_number ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.voximplant_number_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: voximplant_number_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_number_user ( + number_id integer NOT NULL, + user_id integer NOT NULL, + account_id integer NOT NULL +); + + +-- +-- Name: voximplant_scenario_entity; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_scenario_entity ( + id integer NOT NULL, + account_id integer NOT NULL, + scenario_type character varying NOT NULL, + contact_id integer, + deal_id integer, + board_id integer, + owner_id integer +); + + +-- +-- Name: voximplant_scenario_entity_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.voximplant_scenario_entity ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.voximplant_scenario_entity_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: voximplant_scenario_note; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_scenario_note ( + id integer NOT NULL, + account_id integer NOT NULL, + scenario_type character varying NOT NULL, + note_text character varying NOT NULL +); + + +-- +-- Name: voximplant_scenario_note_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.voximplant_scenario_note ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.voximplant_scenario_note_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: voximplant_scenario_task; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_scenario_task ( + id integer NOT NULL, + account_id integer NOT NULL, + scenario_type character varying NOT NULL, + create_activity boolean, + activity_type_id integer, + activity_text character varying, + activity_duration integer, + activity_owner_id integer, + create_task boolean, + task_title character varying, + task_text character varying, + task_duration integer, + task_owner_id integer +); + + +-- +-- Name: voximplant_scenario_task_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +ALTER TABLE public.voximplant_scenario_task ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.voximplant_scenario_task_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: voximplant_user; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.voximplant_user ( + user_id integer NOT NULL, + external_id integer NOT NULL, + user_name character varying NOT NULL, + account_id integer NOT NULL, + password character varying NOT NULL, + is_active boolean DEFAULT true NOT NULL +); + + +-- +-- Name: warehouse; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.warehouse ( + id integer NOT NULL, + name character varying, + created_by integer NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + is_deleted boolean DEFAULT false NOT NULL, + section_id integer NOT NULL +); + + +-- +-- Name: warehouse_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.warehouse_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: migrations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.migrations ALTER COLUMN id SET DEFAULT nextval('public.migrations_id_seq'::regclass); + + +-- +-- Data for Name: account; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.account (id, company_name, subdomain, created_at, logo_id) FROM stdin; +\. + + +-- +-- Data for Name: account_api_access; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.account_api_access (account_id, api_key, created_at) FROM stdin; +\. + + +-- +-- Data for Name: account_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.account_settings (account_id, language, working_days, working_time_from, working_time_to, time_zone, currency, number_format, phone_format, allow_duplicates) FROM stdin; +\. + + +-- +-- Data for Name: account_subscription; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.account_subscription (account_id, created_at, expired_at, is_trial, user_limit, plan_name, external_customer_id) FROM stdin; +\. + + +-- +-- Data for Name: action; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.action (id, type, delay, account_id) FROM stdin; +\. + + +-- +-- Data for Name: action_activity_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.action_activity_settings (action_id, responsible_user_type, responsible_user_id, activity_type_id, text, deadline_type, deadline_time, account_id) FROM stdin; +\. + + +-- +-- Data for Name: action_email_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.action_email_settings (action_id, subject, content, send_as_html, mailbox_id, user_id, account_id, signature) FROM stdin; +\. + + +-- +-- Data for Name: action_entity_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.action_entity_settings (action_id, stage_id, account_id, operation_type) FROM stdin; +\. + + +-- +-- Data for Name: action_scheduled; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.action_scheduled (id, action_id, entity_id, scheduled_time, completed, account_id, created_by) FROM stdin; +\. + + +-- +-- Data for Name: action_task_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.action_task_settings (action_id, responsible_user_type, responsible_user_id, title, text, deadline_type, deadline_time, account_id) FROM stdin; +\. + + +-- +-- Data for Name: activity; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.activity (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, activity_type_id, entity_id, resolved_date, weight) FROM stdin; +\. + + +-- +-- Data for Name: activity_type; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.activity_type (id, created_at, account_id, name, is_active) FROM stdin; +\. + + +-- +-- Data for Name: appsumo_license; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.appsumo_license (id, license_key, license_status, plan_id, tier, account_id, created_at, prev_license_key) FROM stdin; +\. + + +-- +-- Data for Name: appsumo_tier; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.appsumo_tier (id, tier, user_limit, term_in_days, plan_name) FROM stdin; +1 1 1 36500 AppSumo Tier 1 +2 2 5 36500 AppSumo Tier 2 +3 3 15 36500 AppSumo Tier 3 +4 4 30 36500 AppSumo Tier 4 +5 5 50 36500 AppSumo Tier 5 +\. + + +-- +-- Data for Name: automation; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.automation (id, trigger_id, action_id, created_by, is_active, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: automation_condition; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.automation_condition (automation_id, condition_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: automation_entity_type; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.automation_entity_type (id, account_id, created_at, created_by, name, triggers, entity_type_id, board_id, stage_id, conditions, actions, process_id) FROM stdin; +\. + + +-- +-- Data for Name: automation_process; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.automation_process (id, account_id, created_at, resource_key, bpmn_file, bpmn_process_id) FROM stdin; +\. + + +-- +-- Data for Name: automation_stage; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.automation_stage (automation_id, stage_id) FROM stdin; +\. + + +-- +-- Data for Name: board; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.board (id, name, type, record_id, account_id, created_at, sort_order, is_system, code, need_migration, task_board_id, owner_id, participant_ids) FROM stdin; +\. + + +-- +-- Data for Name: chat; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat (id, provider_id, created_by, external_id, type, title, entity_id, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: chat_message; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_message (id, chat_id, chat_user_id, external_id, text, account_id, created_at, reply_to_id) FROM stdin; +\. + + +-- +-- Data for Name: chat_message_file; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_message_file (id, message_id, external_id, account_id, file_id, name, mime_type, size, created_at) FROM stdin; +\. + + +-- +-- Data for Name: chat_message_reaction; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_message_reaction (id, message_id, chat_user_id, reaction, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: chat_message_user_status; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_message_user_status (chat_id, message_id, chat_user_id, status, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: chat_pinned_message; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_pinned_message (chat_id, message_id, created_at, account_id) FROM stdin; +\. + + +-- +-- Data for Name: chat_provider; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_provider (id, created_by, type, title, account_id, created_at, status, transport) FROM stdin; +\. + + +-- +-- Data for Name: chat_provider_messenger; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_provider_messenger (provider_id, page_id, page_access_token, account_id, user_id, user_access_token) FROM stdin; +\. + + +-- +-- Data for Name: chat_provider_twilio; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_provider_twilio (provider_id, account_sid, auth_token, phone_number, account_id) FROM stdin; +\. + + +-- +-- Data for Name: chat_provider_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_provider_user (provider_id, user_id, account_id, type) FROM stdin; +\. + + +-- +-- Data for Name: chat_provider_wazzup; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_provider_wazzup (provider_id, account_id, api_key, channel_id, plain_id, transport) FROM stdin; +\. + + +-- +-- Data for Name: chat_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_user (id, chat_id, user_id, role, account_id) FROM stdin; +\. + + +-- +-- Data for Name: chat_user_external; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.chat_user_external (account_id, external_id, first_name, last_name, avatar_url, phone, email, link, chat_user_id) FROM stdin; +\. + + +-- +-- Data for Name: condition; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.condition (id, type, account_id) FROM stdin; +\. + + +-- +-- Data for Name: demo_data; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.demo_data (id, account_id, type, ids) FROM stdin; +\. + + +-- +-- Data for Name: department; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.department (id, name, parent_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: document_template; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.document_template (id, name, created_by, account_id, created_at, created_count) FROM stdin; +\. + + +-- +-- Data for Name: document_template_access; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.document_template_access (document_template_id, user_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: document_template_entity_type; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.document_template_entity_type (document_template_id, entity_type_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: entity; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity (id, name, entity_type_id, responsible_user_id, stage_id, created_by, account_id, created_at, participant_ids, weight, closed_at, copied_from, copied_count) FROM stdin; +\. + + +-- +-- Data for Name: entity_event; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_event (id, account_id, entity_id, object_id, type, created_at) FROM stdin; +\. + + +-- +-- Data for Name: entity_link; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_link (id, source_id, target_id, sort_order, back_link_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: entity_list_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_list_settings (id, entity_type_id, board_id, settings, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: entity_stage_history; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_stage_history (id, account_id, entity_id, board_id, stage_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: entity_type; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_type (id, name, entity_category, section_name, section_view, section_icon, account_id, created_at, sort_order) FROM stdin; +\. + + +-- +-- Data for Name: entity_type_feature; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_type_feature (entity_type_id, feature_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: entity_type_link; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entity_type_link (id, source_id, target_id, sort_order, account_id) FROM stdin; +\. + + +-- +-- Data for Name: exact_time_trigger_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.exact_time_trigger_settings (trigger_id, date, account_id) FROM stdin; +\. + + +-- +-- Data for Name: external_entity; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.external_entity (id, entity_id, url, account_id, created_at, system, raw_data, ui_data) FROM stdin; +\. + + +-- +-- Data for Name: external_system; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.external_system (id, name, code, url_templates) FROM stdin; +1 SalesForce salesforce {salesforce.com,force.com} +2 LinkedIn linkedin {linkedin.com} +3 Facebook facebook {facebook.com,fb.com} +4 Pipedrive pipedrive {pipedrive.com} +5 HubSpot hubspot {hubspot.com} +6 Freshsales freshsales {freshworks.com,myfreshworks.com} +7 Zoho zoho {zoho.com} +8 Twitter twitter {twitter.com} +9 Instagram instagram {instagram.com} +10 Notion notion {notion.so} +11 Zendesk zendesk {zendesk.com} +12 SugarCRM sugarcrm {sugarcrm.com} +13 Monday monday {monday.com} +14 amoCRM amocrm {amocrm.ru,kommo.com} +15 Bitrix24 bitrix {bitrix24.ru,bitrix24.com,bitrix24.es,bitrix24.eu,bitrix24.de,bitrix24.fr,bitrix24.pl,bitrix24.it,bitrix24.uk} +\. + + +-- +-- Data for Name: feature; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.feature (id, name, code, is_enabled) FROM stdin; +1 Activities activities t +2 Tasks tasks t +6 File storage disk diskForFiles f +7 Avatar avatar f +8 Photo/pictures (several) photos f +3 Notes notes t +5 File storage saveFiles t +4 Chat chat t +9 Create Documents documents t +10 Products products f +\. + + +-- +-- Data for Name: field; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, active, value) FROM stdin; +\. + + +-- +-- Data for Name: field_condition; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field_condition (condition_id, field_id, field_type, payload, account_id) FROM stdin; +\. + + +-- +-- Data for Name: field_group; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field_group (id, name, sort_order, entity_type_id, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: field_option; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field_option (id, label, sort_order, field_id, account_id, created_at, color) FROM stdin; +\. + + +-- +-- Data for Name: field_stage_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field_stage_settings (id, account_id, field_id, stage_id, access, exclude_user_ids) FROM stdin; +\. + + +-- +-- Data for Name: field_user_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field_user_settings (id, account_id, field_id, user_id, access) FROM stdin; +\. + + +-- +-- Data for Name: field_value; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.field_value (id, field_id, payload, entity_id, account_id, field_type) FROM stdin; +\. + + +-- +-- Data for Name: file_info; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.file_info (id, account_id, created_at, created_by, original_name, mime_type, size, store_path, is_used, hash_sha256) FROM stdin; +\. + + +-- +-- Data for Name: file_link; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.file_link (id, account_id, source_type, source_id, file_id, file_name, file_size, file_type, created_at, created_by) FROM stdin; +\. + + +-- +-- Data for Name: industry; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.industry (code, name, color, sort_order, active) FROM stdin; +it_and_development IT & Development #FF8D07 0 t +construction_and_engineering Construction and engineering #A33CAB 1 t +advertising_and_marketing Advertising & Marketing #67E2F9 2 t +consulting_and_outsourcing Consulting and outsourcing #8AF039 3 t +manufacturing Manufacturing #EC008C 4 t +education Education #3D8FEC 5 t +\. + + +-- +-- Data for Name: mail_message; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mail_message (id, mailbox_id, external_id, thread_id, snippet, sent_from, sent_to, subject, date, account_id, reply_to, cc, has_attachment, message_id, references_to, in_reply_to, entity_id, is_seen) FROM stdin; +\. + + +-- +-- Data for Name: mail_message_folder; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mail_message_folder (message_id, folder_id) FROM stdin; +\. + + +-- +-- Data for Name: mail_message_payload; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mail_message_payload (id, message_id, mime_type, filename, attachment, content, account_id, sort_order, size, external_id) FROM stdin; +\. + + +-- +-- Data for Name: mailbox; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox (id, account_id, created_at, email, provider, owner_id, create_contact, state, contact_entity_type_id, lead_entity_type_id, lead_board_id, error_message, emails_per_day, create_lead) FROM stdin; +\. + + +-- +-- Data for Name: mailbox_accessible_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox_accessible_user (mailbox_id, user_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: mailbox_folder; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox_folder (id, mailbox_id, external_id, name, type, account_id, messages_total, messages_unread) FROM stdin; +\. + + +-- +-- Data for Name: mailbox_settings_gmail; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox_settings_gmail (mailbox_id, account_id, tokens, history_id) FROM stdin; +\. + + +-- +-- Data for Name: mailbox_settings_manual; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox_settings_manual (mailbox_id, account_id, password, imap_server, imap_port, imap_secure, smtp_server, smtp_port, smtp_secure, imap_sync) FROM stdin; +\. + + +-- +-- Data for Name: mailbox_signature; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox_signature (id, account_id, created_at, name, text, created_by) FROM stdin; +\. + + +-- +-- Data for Name: mailbox_signature_link; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.mailbox_signature_link (account_id, signature_id, mailbox_id) FROM stdin; +\. + + +-- +-- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.migrations (id, "timestamp", name) FROM stdin; +1 1667672722508 AddAccount1667672722508 +2 1667743637921 AddUser1667743637921 +3 1667755763616 AddUserAccount1667755763616 +4 1668609239887 AddEntityType1668609239887 +5 1668679480636 AddBoard1668679480636 +6 1668679994594 Note1668679994594 +7 1668680006964 AddStage1668680006964 +8 1668693976313 FeedsView1668693976313 +9 1668759141152 AddEntity1668759141152 +10 1668769413399 AddFeedItemsSequence1668769413399 +11 1668770161278 AddTaskType1668770161278 +12 1668957885188 AddSortOrderToBoard1668957885188 +13 1669035936818 AddActivity1669035936818 +14 1669035943607 AddTask1669035943607 +15 1669126197933 AddFieldGroup1669126197933 +16 1669127718002 AddField1669127718002 +17 1669130700529 AddTaskToFeed1669130700529 +18 1669192730867 AddNoteToEntityFK1669192730867 +19 1669197308978 AddFieldOption1669197308978 +20 1669210346088 AddTasksView1669210346088 +21 1669279080953 FixActivityConstraints1669279080953 +22 1669370353662 SplitUserName1669370353662 +23 1669372200327 AddUserPhone1669372200327 +24 1669535731810 UserProfile1669535731810 +25 1669545773479 EntityTypeLink1669545773479 +26 1669565827895 EntityLink1669565827895 +27 1669629567395 RemoveUserAccount1669629567395 +28 1669706986248 AddFeature1669706986248 +29 1669707814502 AddEntityTypeToFeature1669707814502 +30 1669711256398 AddDefaultFeatures1669711256398 +31 1669732421801 AddFieldValue1669732421801 +32 1669799906268 FixEntityLinkColumns1669799906268 +33 1669880194355 ChangeFeatureNameNotes1669880194355 +34 1669966161211 BigintToInteger1669966161211 +35 1669973222725 ForeignKeyToInteger1669973222725 +36 1669991106433 AddFieldTypeColumnToFieldValue1669991106433 +37 1670576080218 AddExternalEntity1670576080218 +38 1670840721696 ExternalSystem1670840721696 +39 1670846440118 AddExternalSystems1670846440118 +40 1670936878487 AddFileInfo1670936878487 +41 1670939571185 AddFileStorePath1670939571185 +42 1671006792499 AddStageIdToTask1671006792499 +43 1671030345135 ChangeFileInfoIdType1671030345135 +44 1671089754889 AddEntityTypeSortOrder1671089754889 +45 1671096971264 RenameTaskType1671096971264 +46 1671108685879 NoteFiles1671108685879 +47 1671196063822 FileInfoCascade1671196063822 +48 1671196394406 NoteFileRemoveFileKey1671196394406 +49 1671203263368 TaskAndActivityFiles1671203263368 +50 1671438176416 RemoveKeyFromFileInfo1671438176416 +51 1671439005617 AddIsUsedToFileInfo1671439005617 +52 1671440838639 DropNoteFileLink1671440838639 +53 1671521628139 BoardRecordIdNull1671521628139 +54 1671533557027 UserActive1671533557027 +55 1671619787598 UserObjectPermission1671619787598 +56 1671914250074 ChangeCardView1671914250074 +57 1672064100473 EntityIdCascade1672064100473 +58 1672154773200 AddSalesforceSettings1672154773200 +59 1672224404851 AddRefreshTokenToSalesforce1672224404851 +60 1672242451242 ExternalSystemUrlTemplates1672242451242 +61 1672306523845 AddDataToExternalEntity1672306523845 +62 1672386954959 AddUIDataToExternalEntity1672386954959 +63 1672928537580 CascadeStageIdInTask1672928537580 +64 1673251726994 AddStageToAllTasks1673251726994 +65 1673339773307 EnableFileStorageFeature1673339773307 +66 1673417366210 RenameCardViewToEntityCategory1673417366210 +67 1673447442894 AddSubscription1673447442894 +68 1673514341599 AddCreatedAtToFileLink1673514341599 +69 1673515827427 AddSequenceToSubscriptionId1673515827427 +70 1673969196807 AddMailboxAndProviderSettings1673969196807 +71 1674130775550 AddMailboxState1674130775550 +72 1674138959333 AddPlannedTimeToTask1674138959333 +73 1674203801914 ChangeMailboxSettingsGmail1674203801914 +74 1674492197867 AddHistoryIdToGmailSettings1674492197867 +75 1674546589359 AddLastSyncToManualSettings1674546589359 +76 1674560582007 AddMailMessageBase1674560582007 +77 1674572622632 AddTaskSettings1674572622632 +78 1674637903317 ChangeMailbox1674637903317 +79 1674638261068 AddMailboxAccessibleUser1674638261068 +80 1674648741806 AddMailboxFolderInfo1674648741806 +81 1674660419230 AddMailMessageReplyTo1674660419230 +82 1674661928759 AddMailMessageCc1674661928759 +83 1674732906697 AddMailMessagePayloadSortOrder1674732906697 +84 1674741639286 MakeStartEndDateNullable1674741639286 +85 1674742927551 AddSubtaskTable1674742927551 +86 1674747324757 AddMailMessagePayloadSize1674747324757 +87 1674822646459 AddTaskCommentTable1674822646459 +88 1674823506430 DeleteSyncDaysFromMailbox1674823506430 +89 1674831146207 AddTaskCommentLikeTable1674831146207 +90 1675076470234 ImapSync1675076470234 +91 1675080091774 AddSystemColumnToBoard1675080091774 +92 1675086921624 AddMailMessagePayloadExternalId1675086921624 +93 1675090441216 AddTaskSettingsIdToTask1675090441216 +94 1675176424905 AddMailMessageHasAttachment1675176424905 +95 1675178566416 MailboxFolderTypeNullable1675178566416 +96 1675259924922 AddMailMessageMessageId1675259924922 +97 1675340097111 DropGroupMessageFromMailbox1675340097111 +98 1675349215128 AddCodeColumn1675349215128 +99 1675350490867 AddCodeToBoard1675350490867 +100 1675412897506 AddMailMessageReferences1675412897506 +101 1675858313415 AddContactEntityTypeToMailbox1675858313415 +102 1675858907059 AddLeadEntityTypeToMailbox1675858907059 +103 1675932022996 AddLeadBoardIdToMailbox1675932022996 +104 1675933206380 AddContactIdToMailMessage1675933206380 +105 1675939938059 MailMessageThreadIdNotNull1675939938059 +106 1675949522751 AddErrorToMailbox1675949522751 +107 1675958088984 AddSeenToMailMessage1675958088984 +108 1676281117335 AddSetNullOnDeleteToMailbox1676281117335 +109 1676283110539 AlterMailMessageSentToNull1676283110539 +110 1676299431922 AddRecipientToNote1676299431922 +111 1676370561831 AddMailToFeedItem1676370561831 +112 1676383947580 TurnOnChatFeature1676383947580 +113 1676385805343 AddChatFeedItemType1676385805343 +114 1676823429937 AddAutomationTables1676823429937 +115 1676866745580 AddAutomationStageTable1676866745580 +116 1676872004900 AddTaskActionSettingsTable1676872004900 +117 1676884562402 RemoveResultFromTask1676884562402 +118 1676895093533 AddChangeStageActionSettingsTable1676895093533 +119 1676992655279 AddAutomationConditions1676992655279 +120 1676994397217 AddNotification1676994397217 +121 1677079993131 AddUserIdToNotification1677079993131 +122 1677581691766 AddMailboxSignature1677581691766 +123 1677744619246 AddNotificationHighlight1677744619246 +124 1677752600091 AlterNotificationSetDescriptionNullable1677752600091 +125 1677765215861 AddNotificationSettings1677765215861 +126 1678093755605 AddExternalSystems1678093755605 +127 1678096716231 AddAccountSettings1678096716231 +128 1678113775031 AddExactTimeTriggerSettings1678113775031 +129 1678194605837 AddDefaultNotificationSettings1678194605837 +130 1678285724304 AddResolveDateToTaskAndActivity1678285724304 +131 1678286876322 AddResolveDateToAllTasks1678286876322 +132 1678347902775 AlterNotificationHighlightToTagName1678347902775 +133 1678367411991 FillResolvedDateForTaskAndAction1678367411991 +134 1678696011650 AddPlanNameToSubscription1678696011650 +135 1678697720944 UpdateSubscriptionTo50Users1678697720944 +136 1678720323399 AddCodeToField1678720323399 +137 1678891987277 AddDepartment1678891987277 +138 1678963689149 AddStartsInToNotification1678963689149 +139 1679291439089 AddScheduledAction1679291439089 +140 1679494426598 MakeFieldGroupOptional1679494426598 +141 1679578329175 AddColorToFieldOption1679578329175 +142 1679583365997 AlterMailMessageContactEntityId1679583365997 +143 1679929245833 AddUserAvatarId1679929245833 +144 1679931642588 AddActiveToField1679931642588 +145 1679931904542 AddAccountLogo1679931904542 +146 1680499051021 AddParticipantsToEntity1680499051021 +147 1680763220747 MigrateProjects1680763220747 +148 1681141545739 AddEmailActionSettings1681141545739 +149 1681224301468 AddProjectFields1681224301468 +150 1681289039535 AddEntityListSettings1681289039535 +151 1681483040117 AddScheduledMailMessage1681483040117 +152 1681732037710 AddEmailsPerDayColumnToMailbox1681732037710 +153 1681828967422 AddWeightToTaskAndActivity1681828967422 +154 1681832187113 AddWeightToAllTasks1681832187113 +155 1681900142878 ResetWeightForTasksAndActivities1681900142878 +156 1682002593036 AddEntityWeight1682002593036 +157 1682083589639 AddAccountIdTETFeature1682083589639 +158 1682348048312 AddRMS1682348048312 +159 1682350735553 AddIndustries1682350735553 +160 1682433824946 AddDemoMarker1682433824946 +161 1682507153940 AddDocumentTemplate1682507153940 +162 1682518277268 DeleteFileInfoWithUser1682518277268 +163 1682589692981 AddIndexes1682589692981 +164 1683016482581 AddIndexes1683016482581 +165 1683205194466 AddTaskBoardIdToBoard1683205194466 +166 1683517583707 DeleteProjectEntityBoards1683517583707 +167 1683731671898 AddFeatureDocuments1683731671898 +168 1683797890507 AddDocumentTemplateGenerated1683797890507 +169 1683802969000 AddDocumentInFeed1683802969000 +170 1683875863921 TrimUsersNames1683875863921 +171 1684249775346 AddAllTasksStageId1684249775346 +172 1684317847183 addCreatedByToFileLink1684317847183 +173 1685001497108 ChangeFileInfoHash1685001497108 +174 1685595302584 AddParticipantsToBoard1685595302584 +175 1685604837960 AddChatModel1685604837960 +176 1685689401123 AddDefaultChatProvider1685689401123 +177 1686048795624 AddBoardIdToTask1686048795624 +178 1686061937533 AlterChatMEssageFile1686061937533 +179 1686297344564 AlterMailMessageEntityId1686297344564 +180 1686310775887 AddSignatureToEmailActionSettings1686310775887 +181 1686643536303 AddReplayToInChatMessage1686643536303 +182 1686736715335 AddChatPinnedMessage1686736715335 +183 1686816157824 AddChatPinnedMessage1686816157824 +184 1686824143539 AddChatMessageReaction1686824143539 +185 1686840724427 AddProducts1686840724427 +186 1686904432256 AddChatIdToStatus1686904432256 +187 1686930758334 RenameProductField1686930758334 +188 1687015795997 AlterChatMessageReply1687015795997 +189 1687350416742 RemoveIdFromSubscription1687350416742 +190 1687351857599 AddSubscriptionExternalCustomerId1687351857599 +191 1687790975332 AddOrder1687790975332 +192 1687793191931 FixOrder1687793191931 +193 1687877020115 AddTaxIncluded1687877020115 +194 1687943824933 AddChatProviderTwilio1687943824933 +195 1687954149882 AlterChatProviderUser1687954149882 +196 1687962117509 AddWarehouse1687962117509 +197 1687965328992 AddIsDeletedToWarehouse1687965328992 +198 1688025794222 AlterChatUser1688025794222 +199 1688044274695 AddStocks1688044274695 +200 1688053486248 AddChatUserExternalName1688053486248 +201 1688112039219 AlterChatProviderTwilio1688112039219 +202 1688130606571 AlterChatProviderUser1688130606571 +203 1688136613049 AlterChat1688136613049 +204 1688138872050 AddOrderStatus1688138872050 +205 1688139271540 AddShipment1688139271540 +206 1688140521166 AddStatusIdToOrder1688140521166 +207 1688388514670 AddReservation1688388514670 +208 1688390259595 AlterFileInfoCreatedBy1688390259595 +209 1688394200229 FixStatusIdInOrder1688394200229 +210 1688472386401 AddShipmentDate1688472386401 +211 1688543908016 AddChatProviderMessenger1688543908016 +212 1688567846856 AlterChatUser1688567846856 +213 1688996628275 FixCategoryIdConstraint1688996628275 +214 1689059395581 AddChatProviderStatus1689059395581 +215 1689068374394 AddUserIdToMessengerProvider1689068374394 +216 1689081064483 AddWarehouseIdToOrder1689081064483 +217 1689087134759 AddModules1689087134759 +218 1689170448447 AddProductType1689170448447 +219 1689243925753 FixOrderStatuses1689243925753 +220 1689259268310 DeleteShipmentStatus1689259268310 +221 1689337185167 AddProductsFeature1689337185167 +222 1689508562776 AddIsActiveToReservation1689508562776 +223 1689763128902 RemoveNoteRecipientId1689763128902 +224 1689774963182 AddUpdatedAtToProduct1689774963182 +225 1689860682052 AddSchedule1689860682052 +226 1689933154489 AlterSchedulePerformer1689933154489 +227 1690208012261 AddProductModule1690208012261 +228 1690456178510 MigrateModuleToProductModule1690456178510 +229 1690467527775 RemoveModule1690467527775 +230 1690469860109 AddProductPermissions1690469860109 +231 1690543599386 DropProductPermissions1690543599386 +232 1690817128717 AlterProductModule1690817128717 +233 1690973831680 AddSectionIdToProducts1690973831680 +234 1691056504886 AddSectionIdToOrderAndShipment1691056504886 +235 1691061102493 DeleteShipmentStatus1691061102493 +236 1691061408646 AlterTableStockToProductStock1691061408646 +237 1691139996885 AddProductsSectionEntityType1691139996885 +238 1691155049107 AddRentalInterval1691155049107 +239 1691397636905 AlterProductsSectionType1691397636905 +240 1691411118591 AddRentalOrder1691411118591 +241 1691414938591 AddRentalSchedule1691414938591 +242 1691657890280 AddRentalOrderPeriod1691657890280 +243 1691678125349 AddRentalOrderPeriodAccountId1691678125349 +244 1691754596482 AlterRentalOrder1691754596482 +245 1691755141714 AlterRentalOrderItem1691755141714 +246 1692002092660 RentalScheduleAddSectionId1692002092660 +247 1692014115943 RenameRentalScheduleToRentalEvent1692014115943 +248 1692170842159 AddProductsSectionEnableWarehouse1692170842159 +249 1692172254434 AddOrdersOrderNumber1692172254434 +250 1692172318353 AddRentalOrderOrderNumber1692172318353 +251 1692283603851 AlterOrderItemCascadeDelete1692283603851 +252 1692343747646 AlterProductStock1692343747646 +253 1692354371998 FixFieldValueType1692354371998 +254 1692604044210 ProductsSectionEnableBarcode1692604044210 +255 1692708295281 AlterOrderStatusNull1692708295281 +256 1692885285551 RefactorScheduler1692885285551 +257 1692890628636 RenameScheduleEvent1692890628636 +258 1692975377102 AlterSchedule1692975377102 +259 1693218884137 UserPosition1693218884137 +260 1693232990040 ScheduleAppointmentOrderId1693232990040 +261 1693485238189 OrderStatusColor1693485238189 +262 1693556962547 CascadeDeleteShipment1693556962547 +263 1694085886365 SchedulerIcon1694085886365 +264 1694166234404 BoardCleanProjectParticipants1694166234404 +265 1695040324876 AppointmentTitle1695040324876 +266 1695046445852 EntityClosedAt1695046445852 +267 1695201739381 SalesPlan1695201739381 +268 1695287859742 ChatDeleteCascadeEntity1695287859742 +269 1695382850916 SalesPlanAlterAmount1695382850916 +270 1695742049917 VoximplantUser1695742049917 +271 1695743140564 VoximplantUserPrimaryColumn1695743140564 +272 1695808984339 VoximplantUserPassword1695808984339 +273 1695810676148 VoximplantAccount1695810676148 +274 1695820387969 AlterVoximplantUser1695820387969 +275 1696500815450 DemoData1696500815450 +276 1697019761609 VoximplantCall1697019761609 +277 1697028866185 AlterVoximplantUser1697028866185 +278 1697115016543 RefactorDemoData1697115016543 +279 1697440579544 SchedulerEventPerformerCascade1697440579544 +280 1697452411558 VoximplantUserActive1697452411558 +281 1697541300418 VoximplantAccount1697541300418 +282 1697541767120 VoximplantAccountAlter1697541767120 +283 1697543064652 VoximplantAccountEmail1697543064652 +284 1697556130148 VoximplantAccountKey1697556130148 +285 1697702819805 VoximplantCallAlterExternalId1697702819805 +286 1698135013349 ReportingOptimization1698135013349 +287 1698421539770 VoximplantScenarios1698421539770 +288 1698663785490 OrderStatusColors1698663785490 +289 1699457673085 AddEntityEventModel1699457673085 +290 1700060543771 AddSchedulePerformerId1700060543771 +291 1700060859571 AlterSchedulePerformer1700060859571 +292 1700230572219 AlterMailbox1700230572219 +293 1700395051542 AlterExternalEntity1700395051542 +294 1700475817946 AllEventsMigrationScript1700475817946 +295 1700591906266 TelephonyCallsToEntityEvent1700591906266 +296 1700663233866 AlterSchedulerAppointment1700663233866 +297 1700729760783 AlterSchedule1700729760783 +298 1700733045104 AlterSchedule1700733045104 +299 1700735236205 AlterSchedule1700735236205 +300 1700741072037 ProductOrdersToEntityEvents1700741072037 +301 1700820935837 AlterSchedulerAppointment1700820935837 +302 1700836699189 ShipmentsToEntityEvents1700836699189 +303 1701264274255 CallsToEntityEventsFix1701264274255 +304 1701437712747 DeleteEntityEventTelephonyCalls1701437712747 +305 1701701891843 AlterVoximplantCall1701701891843 +306 1702542289418 UpdateModulesIcons1702542289418 +307 1702637665853 AlterActivityType1702637665853 +308 1702970939958 AlterAccountSettings1702970939958 +309 1703085253100 AlterProductPrice1703085253100 +310 1703488850551 AlterAccountSettings1703488850551 +311 1703502036545 FieldSettings1703502036545 +312 1703761495779 AlterFieldStageSettings1703761495779 +313 1703850851646 AlterShipment1703850851646 +314 1703857140848 AlterReservation1703857140848 +315 1703876929122 AlterChangeStageActionSettings1703876929122 +316 1704282894747 AlterEntity1704282894747 +317 1706795467082 TestAccount1706795467082 +318 1708088397272 EntityStageChange1708088397272 +319 1708433846254 EntityChangeHistoryInit1708433846254 +320 1708589222946 UserAnalyticsId1708589222946 +321 1708952321460 OptimizeNotificationIndexes1708952321460 +322 1709047301377 DBOptimizationIndex1709047301377 +323 1709048922111 StageAccountIdIndex1709048922111 +324 1709110989045 ScheduledMailMessageIndex1709110989045 +325 1709111575857 AddIndexes1709111575857 +326 1709280253891 ChatProviderCascadeDelete1709280253891 +327 1709736232826 ChatEntityRemoveCascade1709736232826 +328 1709805560320 ChatUserExternal1709805560320 +329 1710162901881 EntityCopiedCount1710162901881 +330 1710758264055 OrderCancelAfter1710758264055 +331 1710759144910 ProductSectionCancelAfter1710759144910 +332 1710864090375 ScheduledActionCreatedBy1710864090375 +333 1710927112868 TutorialGroup1710927112868 +334 1710929893275 TutorialItem1710929893275 +335 1710939538331 TutorialItemUser1710939538331 +336 1711033243401 TutorialItemProduct1711033243401 +337 1711087326245 MailMessageIndexes1711087326245 +338 1711540999340 EntityActionSettings1711540999340 +339 1711541635402 ActionSettingsRename1711541635402 +340 1711706670268 ScheduledAction1711706670268 +341 1711962655915 AutomationActionSettings1711962655915 +342 1712575547663 FieldValue1712575547663 +343 1713167989297 DeleteAllFormulaFields1713167989297 +344 1713257835467 FixEntityTypeSortOrder1713257835467 +345 1713258622876 FixEntityTypeLinkSortOrder1713258622876 +346 1713971186799 ChatProviderTransport1713971186799 +347 1714382561376 WazzupProvider1714382561376 +348 1714557065128 ProductPricePrecision1714557065128 +349 1714663341741 WazzupProviderRemoveChatType1714663341741 +350 1714730508391 WazzupProviderTransport1714730508391 +351 1714732587962 RemoveWazzupProviders1714732587962 +352 1714734600334 ChatProviderTypeRename1714734600334 +353 1715602468891 ChatProviderUserType1715602468891 +354 1715610371002 AlterChatUserExternal1715610371002 +355 1715856544173 AccountApiAccess1715856544173 +356 1715856948928 AlterAccountApiAccess1715856948928 +357 1716299180820 VoximplantNumber1716299180820 +358 1716384743872 VoximplantPhoneNumber1716384743872 +359 1716465152984 VoximplantNumberDefaults1716465152984 +360 1716466922606 VoximplantCallNumber1716466922606 +361 1716469802549 VoximplantCallNumberDefault1716469802549 +362 1717599382958 AddForms1717599382958 +363 1717746424353 SiteFormPageSortOrder1717746424353 +364 1717773341006 FormSiteLink1717773341006 +365 1718030543491 SiteFormEntityType1718030543491 +366 1718098299378 SiteFormRemoveConsent1718098299378 +367 1718098642901 SiteFormConsent1718098642901 +368 1718115282972 SiteFormGratitude1718115282972 +369 1718118622975 AlterSiteForm1718118622975 +370 1718120948980 AlterSiteFormField1718120948980 +371 1718266194827 NotificationRemoveTag1718266194827 +372 1718613418648 AlterSiteForm1718613418648 +373 1718715571983 AlterSiteFormField1718715571983 +374 1718724129043 AutomationProcess1718724129043 +375 1718793150525 AlterSiteFormField1718793150525 +376 1718793757895 AlterSiteFormField1718793757895 +377 1718798058177 AlterAutomationProcess1718798058177 +378 1718801950089 AlterAutomationProcess1718801950089 +379 1718880290844 AlterSiteFormField1718880290844 +380 1719213418299 AlterSiteFormField1719213418299 +381 1719302707526 AlterSiteFormField1719302707526 +382 1719324782700 RenameSubtask1719324782700 +383 1719325474889 AlterTaskSubtaskSortOrder1719325474889 +384 1719393706903 AlterAutomationProcess1719393706903 +385 1719396909800 AutomationEntityType1719396909800 +386 1719404585119 AlterAutomationEntityType1719404585119 +387 1719411072571 AlterAutomationEntityType1719411072571 +388 1719414260257 AlterAutomationEntityType1719414260257 +389 1719502764234 AlterAutomationEntityType1719502764234 +390 1719502897605 AlterAutomationEntityType1719502897605 +391 1719833217147 AlterEntityType1719833217147 +392 1719995612500 UpdateCopiesCreatedAt1719995612500 +393 1720076313493 AlterSiteForm1720076313493 +394 1720077438733 AlterSiteFormEntityType1720077438733 +395 1720194016236 AppSumoLicense1720194016236 +396 1720194705296 AppSumoPreset1720194705296 +397 1720423072348 AppSumoPresets1720423072348 +398 1720423711324 RenameAppSumo1720423711324 +399 1720434041376 AppsumoLicenseUnique1720434041376 +400 1720446828855 AlterReadyMadeSolution1720446828855 +401 1720530507278 RenameAppsumoPreset1720530507278 +402 1720530714621 AlterAppsumoTier1720530714621 +403 1720531175011 RenameSubscription1720531175011 +404 1720596831169 AlterAppsumoTier1720596831169 +405 1720597683965 UpdateAccountSubscription1720597683965 +406 1720610809785 AlterAppsumoLicense1720610809785 +407 1720619947686 AlterAppsumoTier1720619947686 +408 1720778334472 AlterAutomationEntityType1720778334472 +\. + + +-- +-- Data for Name: note; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.note (id, created_at, text, entity_id, created_by, account_id) FROM stdin; +\. + + +-- +-- Data for Name: notification; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification (id, account_id, created_at, type, object_id, entity_id, from_user, title, description, is_seen, user_id, starts_in) FROM stdin; +\. + + +-- +-- Data for Name: notification_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_settings (id, account_id, user_id, enable_popup) FROM stdin; +\. + + +-- +-- Data for Name: notification_type_follow_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_type_follow_user (type_id, user_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: notification_type_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_type_settings (id, account_id, settings_id, type, is_enabled, object_id, before) FROM stdin; +\. + + +-- +-- Data for Name: object_permission; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.object_permission (id, account_id, created_at, object_type, object_id, create_permission, view_permission, edit_permission, delete_permission) FROM stdin; +\. + + +-- +-- Data for Name: order_item; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.order_item (id, unit_price, quantity, tax, discount, product_id, order_id, sort_order, account_id) FROM stdin; +\. + + +-- +-- Data for Name: order_status; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.order_status (id, name, color, code, sort_order, account_id) FROM stdin; +\. + + +-- +-- Data for Name: orders; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.orders (id, total_amount, currency, entity_id, created_by, account_id, created_at, tax_included, status_id, warehouse_id, section_id, order_number, updated_at, cancel_after) FROM stdin; +\. + + +-- +-- Data for Name: product; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.product (id, name, description, sku, unit, tax, is_deleted, category_id, created_by, account_id, created_at, type, updated_at, section_id) FROM stdin; +\. + + +-- +-- Data for Name: product_category; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.product_category (id, name, parent_id, created_by, account_id, created_at, section_id) FROM stdin; +\. + + +-- +-- Data for Name: product_price; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.product_price (id, name, unit_price, currency, product_id, account_id, max_discount) FROM stdin; +\. + + +-- +-- Data for Name: product_stock; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.product_stock (product_id, warehouse_id, stock_quantity, account_id) FROM stdin; +\. + + +-- +-- Data for Name: products_section; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.products_section (id, name, icon, account_id, created_at, type, enable_warehouse, enable_barcode, cancel_after) FROM stdin; +\. + + +-- +-- Data for Name: products_section_entity_type; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.products_section_entity_type (section_id, entity_type_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: ready_made_solution; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.ready_made_solution (code, name, subdomain, sort_order, active, industry_code, account_id) FROM stdin; +\. + + +-- +-- Data for Name: rental_event; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.rental_event (id, product_id, order_item_id, start_date, end_date, status, account_id, section_id) FROM stdin; +\. + + +-- +-- Data for Name: rental_interval; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.rental_interval (id, section_id, type, start_time, account_id) FROM stdin; +\. + + +-- +-- Data for Name: rental_order; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.rental_order (id, section_id, warehouse_id, entity_id, created_by, status, account_id, created_at, currency, tax_included, order_number) FROM stdin; +\. + + +-- +-- Data for Name: rental_order_item; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.rental_order_item (id, order_id, product_id, sort_order, account_id, unit_price, tax, discount) FROM stdin; +\. + + +-- +-- Data for Name: rental_order_period; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.rental_order_period (id, order_id, start_date, end_date, account_id) FROM stdin; +\. + + +-- +-- Data for Name: reservation; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.reservation (id, order_id, order_item_id, product_id, warehouse_id, quantity, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: sales_plan; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.sales_plan (id, account_id, created_at, entity_type_id, user_id, start_date, end_date, quantity, amount) FROM stdin; +\. + + +-- +-- Data for Name: salesforce_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.salesforce_settings (id, account_id, created_at, domain, key, secret, refresh_token) FROM stdin; +\. + + +-- +-- Data for Name: schedule; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.schedule (id, name, entity_type_id, account_id, created_at, products_section_id, icon, time_period, appointment_limit, type) FROM stdin; +\. + + +-- +-- Data for Name: schedule_appointment; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.schedule_appointment (id, schedule_id, start_date, end_date, status, comment, owner_id, entity_id, account_id, created_at, order_id, title, performer_id) FROM stdin; +\. + + +-- +-- Data for Name: schedule_performer; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.schedule_performer (schedule_id, user_id, account_id, id, department_id, type) FROM stdin; +\. + + +-- +-- Data for Name: scheduled_mail_message; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.scheduled_mail_message (id, send_to, subject, content, send_as_html, file_ids, sent_at, mailbox_id, user_id, entity_id, action_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: shipment; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.shipment (id, name, warehouse_id, order_id, account_id, created_at, shipped_at, status_id, section_id, entity_id, order_number) FROM stdin; +\. + + +-- +-- Data for Name: shipment_item; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.shipment_item (id, shipment_id, product_id, quantity, account_id) FROM stdin; +\. + + +-- +-- Data for Name: site_form; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form (id, account_id, name, code, is_active, title, responsible_id, design, field_label_enabled, field_placeholder_enabled, created_by) FROM stdin; +\. + + +-- +-- Data for Name: site_form_consent; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_consent (form_id, account_id, is_enabled, text, link_url, link_text, default_value) FROM stdin; +\. + + +-- +-- Data for Name: site_form_entity_type; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_entity_type (form_id, entity_type_id, account_id, board_id, is_main) FROM stdin; +\. + + +-- +-- Data for Name: site_form_field; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_field (id, account_id, page_id, label, type, is_required, sort_order, placeholder) FROM stdin; +\. + + +-- +-- Data for Name: site_form_field_entity_field; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_field_entity_field (form_field_id, field_id, entity_type_id, is_validation_required, meta) FROM stdin; +\. + + +-- +-- Data for Name: site_form_field_entity_name; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_field_entity_name (form_field_id, entity_type_id) FROM stdin; +\. + + +-- +-- Data for Name: site_form_gratitude; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_gratitude (form_id, account_id, is_enabled, header, text) FROM stdin; +\. + + +-- +-- Data for Name: site_form_page; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.site_form_page (id, account_id, form_id, title, sort_order) FROM stdin; +\. + + +-- +-- Data for Name: stage; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.stage (id, name, color, code, is_system, sort_order, board_id, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: task; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.task (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, title, entity_id, stage_id, planned_time, settings_id, resolved_date, weight, board_id) FROM stdin; +\. + + +-- +-- Data for Name: task_comment; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.task_comment (id, text, task_id, created_by, account_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: task_comment_like; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.task_comment_like (comment_id, user_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: task_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.task_settings (id, active_fields, type, record_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: task_subtask; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.task_subtask (id, text, resolved, task_id, account_id, sort_order) FROM stdin; +\. + + +-- +-- Data for Name: test_account; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.test_account (account_id) FROM stdin; +\. + + +-- +-- Data for Name: trigger; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.trigger (id, type, account_id) FROM stdin; +\. + + +-- +-- Data for Name: tutorial_group; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tutorial_group (id, account_id, created_at, name, sort_order) FROM stdin; +\. + + +-- +-- Data for Name: tutorial_item; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tutorial_item (id, account_id, group_id, created_at, name, link, sort_order) FROM stdin; +\. + + +-- +-- Data for Name: tutorial_item_product; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tutorial_item_product (id, account_id, item_id, type, object_id) FROM stdin; +\. + + +-- +-- Data for Name: tutorial_item_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tutorial_item_user (item_id, user_id) FROM stdin; +\. + + +-- +-- Data for Name: user_condition; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_condition (condition_id, user_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: user_object_permission; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_object_permission (user_id, object_permission_id) FROM stdin; +\. + + +-- +-- Data for Name: user_profile; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_profile (created_at, user_id, birth_date, employment_date, account_id) FROM stdin; +\. + + +-- +-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.users (id, email, password, created_at, first_name, last_name, phone, account_id, role, is_active, department_id, avatar_id, "position", analytics_id) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_account; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_account (account_id, account_name, application_id, application_name, external_id, api_key, password, billing_account_id, is_active, account_email, key_id, private_key) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_call; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_call (id, user_id, entity_id, direction, phone_number, duration, status, failure_reason, record_url, account_id, created_at, session_id, call_id, comment, number_id) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_number; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_number (id, account_id, phone_number, external_id) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_number_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_number_user (number_id, user_id, account_id) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_scenario_entity; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_scenario_entity (id, account_id, scenario_type, contact_id, deal_id, board_id, owner_id) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_scenario_note; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_scenario_note (id, account_id, scenario_type, note_text) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_scenario_task; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_scenario_task (id, account_id, scenario_type, create_activity, activity_type_id, activity_text, activity_duration, activity_owner_id, create_task, task_title, task_text, task_duration, task_owner_id) FROM stdin; +\. + + +-- +-- Data for Name: voximplant_user; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.voximplant_user (user_id, external_id, user_name, account_id, password, is_active) FROM stdin; +\. + + +-- +-- Data for Name: warehouse; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.warehouse (id, name, created_by, account_id, created_at, is_deleted, section_id) FROM stdin; +\. + + +-- +-- Name: account_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.account_id_seq', 11023201, false); + + +-- +-- Name: action_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.action_id_seq', 51011001, false); + + +-- +-- Name: activity_type_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.activity_type_id_seq', 25022001, false); + + +-- +-- Name: app_sumo_license_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.app_sumo_license_id_seq', 1, false); + + +-- +-- Name: app_sumo_preset_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.app_sumo_preset_id_seq', 5, true); + + +-- +-- Name: automation_entity_type_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.automation_entity_type_id_seq', 1, false); + + +-- +-- Name: automation_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.automation_id_seq', 51011001, false); + + +-- +-- Name: automation_process_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.automation_process_id_seq', 1, false); + + +-- +-- Name: board_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.board_id_seq', 14022001, false); + + +-- +-- Name: chat_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.chat_id_seq', 1, false); + + +-- +-- Name: chat_message_file_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.chat_message_file_id_seq', 1, false); + + +-- +-- Name: chat_message_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.chat_message_id_seq', 1, false); + + +-- +-- Name: chat_message_reaction_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.chat_message_reaction_id_seq', 1, false); + + +-- +-- Name: chat_provider_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.chat_provider_id_seq', 1, false); + + +-- +-- Name: chat_user_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.chat_user_id_seq', 1, false); + + +-- +-- Name: condition_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.condition_id_seq', 51011001, false); + + +-- +-- Name: demo_data_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.demo_data_id_seq', 1, false); + + +-- +-- Name: department_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.department_id_seq', 1, false); + + +-- +-- Name: document_template_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.document_template_id_seq', 1, false); + + +-- +-- Name: entity_event_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_event_id_seq', 1, false); + + +-- +-- Name: entity_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_id_seq', 14022001, false); + + +-- +-- Name: entity_link_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_link_id_seq', 1, false); + + +-- +-- Name: entity_list_settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_list_settings_id_seq', 1, false); + + +-- +-- Name: entity_stage_history_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_stage_history_id_seq', 1, false); + + +-- +-- Name: entity_type_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_type_id_seq', 13022001, false); + + +-- +-- Name: entity_type_link_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.entity_type_link_id_seq', 1, false); + + +-- +-- Name: external_entity_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.external_entity_id_seq', 30022001, false); + + +-- +-- Name: external_system_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.external_system_id_seq', 3, true); + + +-- +-- Name: feature_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.feature_id_seq', 10, true); + + +-- +-- Name: feed_item_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.feed_item_id_seq', 22022001, false); + + +-- +-- Name: field_group_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.field_group_id_seq', 41022001, false); + + +-- +-- Name: field_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.field_id_seq', 42022001, false); + + +-- +-- Name: field_option_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.field_option_id_seq', 43022001, false); + + +-- +-- Name: field_stage_settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.field_stage_settings_id_seq', 1, false); + + +-- +-- Name: field_user_settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.field_user_settings_id_seq', 1, false); + + +-- +-- Name: field_value_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.field_value_id_seq', 44022001, false); + + +-- +-- Name: file_link_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.file_link_id_seq', 1, false); + + +-- +-- Name: mail_message_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.mail_message_id_seq', 28023001, false); + + +-- +-- Name: mail_message_payload_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.mail_message_payload_id_seq', 29023001, false); + + +-- +-- Name: mailbox_folder_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.mailbox_folder_id_seq', 31023001, false); + + +-- +-- Name: mailbox_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.mailbox_id_seq', 27023001, false); + + +-- +-- Name: mailbox_signature_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.mailbox_signature_id_seq', 27023001, false); + + +-- +-- Name: migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.migrations_id_seq', 408, true); + + +-- +-- Name: notification_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.notification_id_seq', 61011001, false); + + +-- +-- Name: notification_settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.notification_settings_id_seq', 62011001, false); + + +-- +-- Name: notification_type_settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.notification_type_settings_id_seq', 63011001, false); + + +-- +-- Name: object_permission_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.object_permission_id_seq', 1, false); + + +-- +-- Name: order_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.order_id_seq', 1, false); + + +-- +-- Name: order_item_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.order_item_id_seq', 1, false); + + +-- +-- Name: order_status_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.order_status_id_seq', 1, false); + + +-- +-- Name: product_category_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.product_category_id_seq', 1, false); + + +-- +-- Name: product_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.product_id_seq', 1, false); + + +-- +-- Name: product_module_id_seq1; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.product_module_id_seq1', 1, false); + + +-- +-- Name: product_price_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.product_price_id_seq', 1, false); + + +-- +-- Name: rental_interval_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.rental_interval_id_seq', 1, false); + + +-- +-- Name: rental_order_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.rental_order_id_seq', 1, false); + + +-- +-- Name: rental_order_item_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.rental_order_item_id_seq', 1, false); + + +-- +-- Name: rental_order_period_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.rental_order_period_id_seq', 1, false); + + +-- +-- Name: rental_schedule_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.rental_schedule_id_seq', 1, false); + + +-- +-- Name: reservation_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.reservation_id_seq', 1, false); + + +-- +-- Name: sales_plan_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.sales_plan_id_seq', 1, false); + + +-- +-- Name: schedule_event_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.schedule_event_id_seq', 1, false); + + +-- +-- Name: schedule_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.schedule_id_seq', 1, false); + + +-- +-- Name: schedule_performer_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.schedule_performer_id_seq', 1, false); + + +-- +-- Name: scheduled_action_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.scheduled_action_id_seq', 1, false); + + +-- +-- Name: scheduled_mail_message_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.scheduled_mail_message_id_seq', 1, false); + + +-- +-- Name: shipment_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.shipment_id_seq', 1, false); + + +-- +-- Name: shipment_item_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.shipment_item_id_seq', 1, false); + + +-- +-- Name: site_form_field_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.site_form_field_id_seq', 1, false); + + +-- +-- Name: site_form_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.site_form_id_seq', 1, false); + + +-- +-- Name: site_form_page_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.site_form_page_id_seq', 1, false); + + +-- +-- Name: stage_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.stage_id_seq', 15022001, false); + + +-- +-- Name: subtask_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.subtask_id_seq', 46022001, false); + + +-- +-- Name: task_comment_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.task_comment_id_seq', 47022001, false); + + +-- +-- Name: task_settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.task_settings_id_seq', 45022001, false); + + +-- +-- Name: trigger_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.trigger_id_seq', 51011001, false); + + +-- +-- Name: tutorial_group_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.tutorial_group_id_seq', 1, false); + + +-- +-- Name: tutorial_item_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.tutorial_item_id_seq', 1, false); + + +-- +-- Name: tutorial_item_product_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.tutorial_item_product_id_seq', 1, false); + + +-- +-- Name: user_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.user_id_seq', 12022001, false); + + +-- +-- Name: voximplant_call_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.voximplant_call_id_seq', 1, false); + + +-- +-- Name: voximplant_number_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.voximplant_number_id_seq', 1, false); + + +-- +-- Name: voximplant_scenario_entity_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.voximplant_scenario_entity_id_seq', 1, false); + + +-- +-- Name: voximplant_scenario_note_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.voximplant_scenario_note_id_seq', 1, false); + + +-- +-- Name: voximplant_scenario_task_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.voximplant_scenario_task_id_seq', 1, false); + + +-- +-- Name: warehouse_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.warehouse_id_seq', 1, false); + + +-- +-- Name: migrations PK_8c82d7f526340ab734260ea46be; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.migrations + ADD CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY (id); + + +-- +-- Name: account_api_access account_api_access_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account_api_access + ADD CONSTRAINT account_api_access_pkey PRIMARY KEY (account_id); + + +-- +-- Name: account_settings account_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account_settings + ADD CONSTRAINT account_settings_pkey PRIMARY KEY (account_id); + + +-- +-- Name: account accounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account + ADD CONSTRAINT accounts_pkey PRIMARY KEY (id); + + +-- +-- Name: account accounts_subdomain_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account + ADD CONSTRAINT accounts_subdomain_key UNIQUE (subdomain); + + +-- +-- Name: action action_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action + ADD CONSTRAINT action_pkey PRIMARY KEY (id); + + +-- +-- Name: action_activity_settings activity_action_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_activity_settings + ADD CONSTRAINT activity_action_settings_pkey PRIMARY KEY (action_id); + + +-- +-- Name: activity activity_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity + ADD CONSTRAINT activity_pkey PRIMARY KEY (id); + + +-- +-- Name: appsumo_license app_sumo_license_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.appsumo_license + ADD CONSTRAINT app_sumo_license_pkey PRIMARY KEY (id); + + +-- +-- Name: appsumo_tier app_sumo_preset_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.appsumo_tier + ADD CONSTRAINT app_sumo_preset_pkey PRIMARY KEY (id); + + +-- +-- Name: appsumo_license appsumo_license_license_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.appsumo_license + ADD CONSTRAINT appsumo_license_license_key_key UNIQUE (license_key); + + +-- +-- Name: automation_condition automation_condition_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_condition + ADD CONSTRAINT automation_condition_pkey PRIMARY KEY (automation_id, condition_id); + + +-- +-- Name: automation_entity_type automation_entity_type_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_pkey PRIMARY KEY (id); + + +-- +-- Name: automation automation_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation + ADD CONSTRAINT automation_pkey PRIMARY KEY (id); + + +-- +-- Name: automation_process automation_process_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_process + ADD CONSTRAINT automation_process_pkey PRIMARY KEY (id); + + +-- +-- Name: automation_stage automation_stage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_stage + ADD CONSTRAINT automation_stage_pkey PRIMARY KEY (automation_id, stage_id); + + +-- +-- Name: board board__code__account_id__uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.board + ADD CONSTRAINT board__code__account_id__uniq UNIQUE (code, account_id); + + +-- +-- Name: board board_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.board + ADD CONSTRAINT board_pkey PRIMARY KEY (id); + + +-- +-- Name: chat_message_file chat_message_file_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_file + ADD CONSTRAINT chat_message_file_pkey PRIMARY KEY (id); + + +-- +-- Name: chat_message chat_message_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message + ADD CONSTRAINT chat_message_pkey PRIMARY KEY (id); + + +-- +-- Name: chat_message_reaction chat_message_reaction_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_reaction + ADD CONSTRAINT chat_message_reaction_pkey PRIMARY KEY (id); + + +-- +-- Name: chat_message_user_status chat_message_user_status_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_user_status + ADD CONSTRAINT chat_message_user_status_pkey PRIMARY KEY (chat_id, message_id, chat_user_id); + + +-- +-- Name: chat_pinned_message chat_pinned_message_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_pinned_message + ADD CONSTRAINT chat_pinned_message_pkey PRIMARY KEY (chat_id, message_id); + + +-- +-- Name: chat chat_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat + ADD CONSTRAINT chat_pkey PRIMARY KEY (id); + + +-- +-- Name: chat_provider_messenger chat_provider_messenger_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_messenger + ADD CONSTRAINT chat_provider_messenger_pkey PRIMARY KEY (provider_id); + + +-- +-- Name: chat_provider chat_provider_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider + ADD CONSTRAINT chat_provider_pkey PRIMARY KEY (id); + + +-- +-- Name: chat_provider_twilio chat_provider_twilio_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_twilio + ADD CONSTRAINT chat_provider_twilio_pkey PRIMARY KEY (provider_id); + + +-- +-- Name: chat_provider_user chat_provider_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_user + ADD CONSTRAINT chat_provider_user_pkey PRIMARY KEY (provider_id, user_id, type); + + +-- +-- Name: chat_provider_wazzup chat_provider_wazzup_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_wazzup + ADD CONSTRAINT chat_provider_wazzup_pkey PRIMARY KEY (provider_id); + + +-- +-- Name: chat_user_external chat_user_external_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user_external + ADD CONSTRAINT chat_user_external_pkey PRIMARY KEY (chat_user_id); + + +-- +-- Name: chat_user chat_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user + ADD CONSTRAINT chat_user_pkey PRIMARY KEY (id); + + +-- +-- Name: condition condition_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.condition + ADD CONSTRAINT condition_pkey PRIMARY KEY (id); + + +-- +-- Name: demo_data demo_data_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.demo_data + ADD CONSTRAINT demo_data_pkey PRIMARY KEY (id); + + +-- +-- Name: department department_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.department + ADD CONSTRAINT department_pkey PRIMARY KEY (id); + + +-- +-- Name: document_template_access document_template_access_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_access + ADD CONSTRAINT document_template_access_pkey PRIMARY KEY (document_template_id, user_id); + + +-- +-- Name: document_template_entity_type document_template_entity_type_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_entity_type + ADD CONSTRAINT document_template_entity_type_pkey PRIMARY KEY (document_template_id, entity_type_id); + + +-- +-- Name: document_template document_template_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template + ADD CONSTRAINT document_template_pkey PRIMARY KEY (id); + + +-- +-- Name: action_email_settings email_action_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_email_settings + ADD CONSTRAINT email_action_settings_pkey PRIMARY KEY (action_id); + + +-- +-- Name: action_entity_settings entity_action_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_entity_settings + ADD CONSTRAINT entity_action_settings_pkey PRIMARY KEY (action_id); + + +-- +-- Name: entity_event entity_event_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_event + ADD CONSTRAINT entity_event_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_link entity_link_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_link + ADD CONSTRAINT entity_link_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_list_settings entity_list_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_list_settings + ADD CONSTRAINT entity_list_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: entity entity_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity + ADD CONSTRAINT entity_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_stage_history entity_stage_history_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_stage_history + ADD CONSTRAINT entity_stage_history_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_type_feature entity_type_feature_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_feature + ADD CONSTRAINT entity_type_feature_pkey PRIMARY KEY (entity_type_id, feature_id); + + +-- +-- Name: entity_type_link entity_type_link_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_link + ADD CONSTRAINT entity_type_link_pkey PRIMARY KEY (id); + + +-- +-- Name: entity_type entity_type_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type + ADD CONSTRAINT entity_type_pkey PRIMARY KEY (id); + + +-- +-- Name: exact_time_trigger_settings exact_time_trigger_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.exact_time_trigger_settings + ADD CONSTRAINT exact_time_trigger_settings_pkey PRIMARY KEY (trigger_id); + + +-- +-- Name: external_entity external_entity_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_entity + ADD CONSTRAINT external_entity_pkey PRIMARY KEY (id); + + +-- +-- Name: external_system external_system_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_system + ADD CONSTRAINT external_system_pkey PRIMARY KEY (id); + + +-- +-- Name: feature feature_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feature + ADD CONSTRAINT feature_pkey PRIMARY KEY (id); + + +-- +-- Name: field field__code__entity_type_id__uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field + ADD CONSTRAINT field__code__entity_type_id__uniq UNIQUE (code, entity_type_id); + + +-- +-- Name: field_condition field_condition_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_condition + ADD CONSTRAINT field_condition_pkey PRIMARY KEY (condition_id); + + +-- +-- Name: field_group field_group_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_group + ADD CONSTRAINT field_group_pkey PRIMARY KEY (id); + + +-- +-- Name: field_value field_id__entity_id__uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_value + ADD CONSTRAINT field_id__entity_id__uniq UNIQUE (field_id, entity_id); + + +-- +-- Name: field_option field_option_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_option + ADD CONSTRAINT field_option_pkey PRIMARY KEY (id); + + +-- +-- Name: field field_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field + ADD CONSTRAINT field_pkey PRIMARY KEY (id); + + +-- +-- Name: field_stage_settings field_stage_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_stage_settings + ADD CONSTRAINT field_stage_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: field_user_settings field_user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_user_settings + ADD CONSTRAINT field_user_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: field_value field_value_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_value + ADD CONSTRAINT field_value_pkey PRIMARY KEY (id); + + +-- +-- Name: file_info file_info_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.file_info + ADD CONSTRAINT file_info_pkey PRIMARY KEY (id); + + +-- +-- Name: file_link file_link_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.file_link + ADD CONSTRAINT file_link_pkey PRIMARY KEY (id); + + +-- +-- Name: industry industry_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.industry + ADD CONSTRAINT industry_pkey PRIMARY KEY (code); + + +-- +-- Name: mail_message_folder mail_message_folder_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message_folder + ADD CONSTRAINT mail_message_folder_pkey PRIMARY KEY (message_id, folder_id); + + +-- +-- Name: mail_message_payload mail_message_payload_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message_payload + ADD CONSTRAINT mail_message_payload_pkey PRIMARY KEY (id); + + +-- +-- Name: mail_message mail_message_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message + ADD CONSTRAINT mail_message_pkey PRIMARY KEY (id); + + +-- +-- Name: mailbox_accessible_user mailbox_accessible_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_accessible_user + ADD CONSTRAINT mailbox_accessible_user_pkey PRIMARY KEY (mailbox_id, user_id); + + +-- +-- Name: mailbox_folder mailbox_folder_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_folder + ADD CONSTRAINT mailbox_folder_pkey PRIMARY KEY (id); + + +-- +-- Name: mailbox mailbox_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox + ADD CONSTRAINT mailbox_pkey PRIMARY KEY (id); + + +-- +-- Name: mailbox_settings_gmail mailbox_settings_gmail_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_settings_gmail + ADD CONSTRAINT mailbox_settings_gmail_pkey PRIMARY KEY (mailbox_id); + + +-- +-- Name: mailbox_settings_manual mailbox_settings_manual_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_settings_manual + ADD CONSTRAINT mailbox_settings_manual_pkey PRIMARY KEY (mailbox_id); + + +-- +-- Name: mailbox_signature_link mailbox_signature_link_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature_link + ADD CONSTRAINT mailbox_signature_link_pkey PRIMARY KEY (signature_id, mailbox_id); + + +-- +-- Name: mailbox_signature mailbox_signature_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature + ADD CONSTRAINT mailbox_signature_pkey PRIMARY KEY (id); + + +-- +-- Name: note note_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.note + ADD CONSTRAINT note_pkey PRIMARY KEY (id); + + +-- +-- Name: notification notification_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification + ADD CONSTRAINT notification_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_settings notification_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_settings + ADD CONSTRAINT notification_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_type_follow_user notification_type_follow_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_follow_user + ADD CONSTRAINT notification_type_follow_user_pkey PRIMARY KEY (type_id, user_id); + + +-- +-- Name: notification_type_settings notification_type_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_settings + ADD CONSTRAINT notification_type_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: object_permission object_permission_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.object_permission + ADD CONSTRAINT object_permission_pkey PRIMARY KEY (id); + + +-- +-- Name: order_item order_item_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.order_item + ADD CONSTRAINT order_item_pkey PRIMARY KEY (id); + + +-- +-- Name: order_status order_status_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.order_status + ADD CONSTRAINT order_status_pkey PRIMARY KEY (id); + + +-- +-- Name: orders orders_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_pkey PRIMARY KEY (id); + + +-- +-- Name: product_category product_category_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_category + ADD CONSTRAINT product_category_pkey PRIMARY KEY (id); + + +-- +-- Name: products_section product_module_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.products_section + ADD CONSTRAINT product_module_pkey PRIMARY KEY (id); + + +-- +-- Name: product product_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product + ADD CONSTRAINT product_pkey PRIMARY KEY (id); + + +-- +-- Name: product_price product_price_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_price + ADD CONSTRAINT product_price_pkey PRIMARY KEY (id); + + +-- +-- Name: products_section_entity_type products_section_entity_type_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.products_section_entity_type + ADD CONSTRAINT products_section_entity_type_pkey PRIMARY KEY (section_id, entity_type_id); + + +-- +-- Name: ready_made_solution ready_made_solution_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ready_made_solution + ADD CONSTRAINT ready_made_solution_pkey PRIMARY KEY (code); + + +-- +-- Name: rental_interval rental_interval_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_interval + ADD CONSTRAINT rental_interval_pkey PRIMARY KEY (id); + + +-- +-- Name: rental_order_item rental_order_item_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_item + ADD CONSTRAINT rental_order_item_pkey PRIMARY KEY (id); + + +-- +-- Name: rental_order_period rental_order_period_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_period + ADD CONSTRAINT rental_order_period_pkey PRIMARY KEY (id); + + +-- +-- Name: rental_order rental_order_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order + ADD CONSTRAINT rental_order_pkey PRIMARY KEY (id); + + +-- +-- Name: rental_event rental_schedule_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_event + ADD CONSTRAINT rental_schedule_pkey PRIMARY KEY (id); + + +-- +-- Name: reservation reservation_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.reservation + ADD CONSTRAINT reservation_pkey PRIMARY KEY (id); + + +-- +-- Name: sales_plan sales_plan_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.sales_plan + ADD CONSTRAINT sales_plan_pkey PRIMARY KEY (id); + + +-- +-- Name: salesforce_settings salesforce_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.salesforce_settings + ADD CONSTRAINT salesforce_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: schedule_appointment schedule_event_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_event_pkey PRIMARY KEY (id); + + +-- +-- Name: schedule_performer schedule_performer_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_performer + ADD CONSTRAINT schedule_performer_pkey PRIMARY KEY (id); + + +-- +-- Name: schedule schedule_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule + ADD CONSTRAINT schedule_pkey PRIMARY KEY (id); + + +-- +-- Name: action_scheduled scheduled_action_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_scheduled + ADD CONSTRAINT scheduled_action_pkey PRIMARY KEY (id); + + +-- +-- Name: scheduled_mail_message scheduled_mail_message_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.scheduled_mail_message + ADD CONSTRAINT scheduled_mail_message_pkey PRIMARY KEY (id); + + +-- +-- Name: shipment_item shipment_item_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment_item + ADD CONSTRAINT shipment_item_pkey PRIMARY KEY (id); + + +-- +-- Name: shipment shipment_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_pkey PRIMARY KEY (id); + + +-- +-- Name: site_form site_form_code_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form + ADD CONSTRAINT site_form_code_key UNIQUE (code); + + +-- +-- Name: site_form_consent site_form_consent_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_consent + ADD CONSTRAINT site_form_consent_pkey PRIMARY KEY (form_id); + + +-- +-- Name: site_form_entity_type site_form_entity_type_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_entity_type + ADD CONSTRAINT site_form_entity_type_pkey PRIMARY KEY (form_id, entity_type_id); + + +-- +-- Name: site_form_field_entity_field site_form_field_entity_field_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_field + ADD CONSTRAINT site_form_field_entity_field_pkey PRIMARY KEY (form_field_id); + + +-- +-- Name: site_form_field_entity_name site_form_field_entity_name_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_name + ADD CONSTRAINT site_form_field_entity_name_pkey PRIMARY KEY (form_field_id); + + +-- +-- Name: site_form_field site_form_field_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field + ADD CONSTRAINT site_form_field_pkey PRIMARY KEY (id); + + +-- +-- Name: site_form_gratitude site_form_gratitude_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_gratitude + ADD CONSTRAINT site_form_gratitude_pkey PRIMARY KEY (form_id); + + +-- +-- Name: site_form_page site_form_page_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_page + ADD CONSTRAINT site_form_page_pkey PRIMARY KEY (id); + + +-- +-- Name: site_form site_form_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form + ADD CONSTRAINT site_form_pkey PRIMARY KEY (id); + + +-- +-- Name: stage stage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.stage + ADD CONSTRAINT stage_pkey PRIMARY KEY (id); + + +-- +-- Name: product_stock stock_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_stock + ADD CONSTRAINT stock_pkey PRIMARY KEY (product_id, warehouse_id); + + +-- +-- Name: account_subscription subscription_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account_subscription + ADD CONSTRAINT subscription_pkey PRIMARY KEY (account_id); + + +-- +-- Name: task_subtask subtask_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_subtask + ADD CONSTRAINT subtask_pkey PRIMARY KEY (id); + + +-- +-- Name: action_task_settings task_action_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_task_settings + ADD CONSTRAINT task_action_settings_pkey PRIMARY KEY (action_id); + + +-- +-- Name: task_comment_like task_comment_like_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment_like + ADD CONSTRAINT task_comment_like_pkey PRIMARY KEY (comment_id, user_id); + + +-- +-- Name: task_comment task_comment_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment + ADD CONSTRAINT task_comment_pkey PRIMARY KEY (id); + + +-- +-- Name: task task_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_pkey PRIMARY KEY (id); + + +-- +-- Name: task_settings task_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_settings + ADD CONSTRAINT task_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: activity_type task_type_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity_type + ADD CONSTRAINT task_type_pkey PRIMARY KEY (id); + + +-- +-- Name: test_account test_account_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.test_account + ADD CONSTRAINT test_account_pkey PRIMARY KEY (account_id); + + +-- +-- Name: trigger trigger_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.trigger + ADD CONSTRAINT trigger_pkey PRIMARY KEY (id); + + +-- +-- Name: tutorial_group tutorial_group_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_group + ADD CONSTRAINT tutorial_group_pkey PRIMARY KEY (id); + + +-- +-- Name: tutorial_item tutorial_item_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item + ADD CONSTRAINT tutorial_item_pkey PRIMARY KEY (id); + + +-- +-- Name: tutorial_item_product tutorial_item_product_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item_product + ADD CONSTRAINT tutorial_item_product_pkey PRIMARY KEY (id); + + +-- +-- Name: tutorial_item_user tutorial_item_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item_user + ADD CONSTRAINT tutorial_item_user_pkey PRIMARY KEY (item_id, user_id); + + +-- +-- Name: user_condition user_condition_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_condition + ADD CONSTRAINT user_condition_pkey PRIMARY KEY (condition_id, user_id); + + +-- +-- Name: user_object_permission user_object_permission_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_object_permission + ADD CONSTRAINT user_object_permission_pkey PRIMARY KEY (user_id, object_permission_id); + + +-- +-- Name: user_profile user_profile_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_profile + ADD CONSTRAINT user_profile_pkey PRIMARY KEY (user_id); + + +-- +-- Name: users users_email_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_email_key UNIQUE (email); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: voximplant_account voximplant_account_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_account + ADD CONSTRAINT voximplant_account_pkey PRIMARY KEY (account_id); + + +-- +-- Name: voximplant_call voximplant_call_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_call + ADD CONSTRAINT voximplant_call_pkey PRIMARY KEY (id); + + +-- +-- Name: voximplant_number voximplant_number_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_number + ADD CONSTRAINT voximplant_number_pkey PRIMARY KEY (id); + + +-- +-- Name: voximplant_number_user voximplant_number_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_number_user + ADD CONSTRAINT voximplant_number_user_pkey PRIMARY KEY (number_id, user_id); + + +-- +-- Name: voximplant_scenario_entity voximplant_scenario_entity_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_entity + ADD CONSTRAINT voximplant_scenario_entity_pkey PRIMARY KEY (id); + + +-- +-- Name: voximplant_scenario_note voximplant_scenario_note_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_note + ADD CONSTRAINT voximplant_scenario_note_pkey PRIMARY KEY (id); + + +-- +-- Name: voximplant_scenario_task voximplant_scenario_task_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_task + ADD CONSTRAINT voximplant_scenario_task_pkey PRIMARY KEY (id); + + +-- +-- Name: voximplant_user voximplant_user_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_user + ADD CONSTRAINT voximplant_user_pkey PRIMARY KEY (user_id); + + +-- +-- Name: warehouse warehouse_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.warehouse + ADD CONSTRAINT warehouse_pkey PRIMARY KEY (id); + + +-- +-- Name: activity_entity_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX activity_entity_id_idx ON public.activity USING btree (entity_id); + + +-- +-- Name: activity_is_resolved_end_date_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX activity_is_resolved_end_date_idx ON public.activity USING btree (is_resolved, end_date); + + +-- +-- Name: activity_is_resolved_responsible_user_id_start_date_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX activity_is_resolved_responsible_user_id_start_date_idx ON public.activity USING btree (is_resolved, responsible_user_id, start_date); + + +-- +-- Name: board_account_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX board_account_id_idx ON public.board USING btree (account_id); + + +-- +-- Name: board_type_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX board_type_idx ON public.board USING btree (type); + + +-- +-- Name: department_account_id_parent_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX department_account_id_parent_id_idx ON public.department USING btree (account_id, parent_id); + + +-- +-- Name: entity_entity_type_id_stage_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX entity_entity_type_id_stage_id_idx ON public.entity USING btree (entity_type_id, stage_id); + + +-- +-- Name: entity_link_source_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX entity_link_source_id_idx ON public.entity_link USING btree (source_id); + + +-- +-- Name: entity_link_source_id_sort_order_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX entity_link_source_id_sort_order_id_idx ON public.entity_link USING btree (source_id, sort_order, id); + + +-- +-- Name: entity_link_target_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX entity_link_target_id_idx ON public.entity_link USING btree (target_id); + + +-- +-- Name: entity_type_link_account_id_source_id_sort_order_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX entity_type_link_account_id_source_id_sort_order_id_idx ON public.entity_type_link USING btree (account_id, source_id, sort_order, id); + + +-- +-- Name: exact_time_trigger_settings_date_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX exact_time_trigger_settings_date_idx ON public.exact_time_trigger_settings USING btree (date); + + +-- +-- Name: field_entity_type_id_type_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX field_entity_type_id_type_idx ON public.field USING btree (entity_type_id, type); + + +-- +-- Name: field_group_account_id_entity_type_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX field_group_account_id_entity_type_id_idx ON public.field_group USING btree (account_id, entity_type_id); + + +-- +-- Name: field_option_field_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX field_option_field_id_idx ON public.field_option USING btree (field_id); + + +-- +-- Name: field_value_account_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX field_value_account_id_idx ON public.field_value USING btree (account_id); + + +-- +-- Name: field_value_entity_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX field_value_entity_id_idx ON public.field_value USING btree (entity_id); + + +-- +-- Name: file_link_account_id_source_type_source_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX file_link_account_id_source_type_source_id_idx ON public.file_link USING btree (account_id, source_type, source_id); + + +-- +-- Name: idx_chat_on_account_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chat_on_account_id ON public.chat USING btree (account_id); + + +-- +-- Name: idx_chat_user_on_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chat_user_on_user_id ON public.chat_user USING btree (user_id); + + +-- +-- Name: idx_chat_user_on_user_id_chat_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chat_user_on_user_id_chat_id ON public.chat_user USING btree (user_id, chat_id); + + +-- +-- Name: idx_cmus_on_message_id_chat_id_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_cmus_on_message_id_chat_id_status ON public.chat_message_user_status USING btree (message_id, chat_id, status); + + +-- +-- Name: idx_cmus_on_message_id_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_cmus_on_message_id_status ON public.chat_message_user_status USING btree (message_id, status); + + +-- +-- Name: idx_entity_account_id_stage_id_closed_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_entity_account_id_stage_id_closed_at ON public.entity USING btree (account_id, stage_id, closed_at); + + +-- +-- Name: idx_field_value_entity_id_field_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_field_value_entity_id_field_type ON public.field_value USING btree (entity_id, field_type); + + +-- +-- Name: idx_mail_message_account_mailbox; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_mail_message_account_mailbox ON public.mail_message USING btree (account_id, mailbox_id); + + +-- +-- Name: idx_mail_message_external_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_mail_message_external_id ON public.mail_message USING btree (external_id); + + +-- +-- Name: idx_mail_message_message_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_mail_message_message_id ON public.mail_message USING btree (message_id); + + +-- +-- Name: idx_mailbox_folder_account_mailbox; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_mailbox_folder_account_mailbox ON public.mailbox_folder USING btree (account_id, mailbox_id); + + +-- +-- Name: mail_message_entity_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX mail_message_entity_id_idx ON public.mail_message USING btree (entity_id); + + +-- +-- Name: mailbox_folder_account_id_mailbox_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX mailbox_folder_account_id_mailbox_id_idx ON public.mailbox_folder USING btree (account_id, mailbox_id); + + +-- +-- Name: note_entity_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX note_entity_id_idx ON public.note USING btree (entity_id); + + +-- +-- Name: notification_account_user_seen_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notification_account_user_seen_idx ON public.notification USING btree (account_id, user_id, is_seen); + + +-- +-- Name: notification_settings_user_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notification_settings_user_id_idx ON public.notification_settings USING btree (user_id); + + +-- +-- Name: notification_type_settings_on_type_enabled_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notification_type_settings_on_type_enabled_idx ON public.notification_type_settings USING btree (type) WHERE (is_enabled = true); + + +-- +-- Name: object_permission_object_type_object_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX object_permission_object_type_object_id_idx ON public.object_permission USING btree (object_type, object_id); + + +-- +-- Name: scheduled_action_scheduled_time_completed_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX scheduled_action_scheduled_time_completed_idx ON public.action_scheduled USING btree (scheduled_time, completed); + + +-- +-- Name: scheduled_mail_message_mailbox_id_sent_at_null_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX scheduled_mail_message_mailbox_id_sent_at_null_idx ON public.scheduled_mail_message USING btree (mailbox_id) WHERE (sent_at IS NULL); + + +-- +-- Name: stage_account_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX stage_account_id_idx ON public.stage USING btree (account_id); + + +-- +-- Name: stage_board_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX stage_board_id_idx ON public.stage USING btree (board_id); + + +-- +-- Name: subtask_account_id_task_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subtask_account_id_task_id_idx ON public.task_subtask USING btree (account_id, task_id); + + +-- +-- Name: task_account_id_responsible_user_id_stage_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX task_account_id_responsible_user_id_stage_id_idx ON public.task USING btree (account_id, responsible_user_id, stage_id); + + +-- +-- Name: task_entity_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX task_entity_id_idx ON public.task USING btree (entity_id); + + +-- +-- Name: task_entity_id_is_resolved_created_by_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX task_entity_id_is_resolved_created_by_idx ON public.task USING btree (entity_id, is_resolved, created_by); + + +-- +-- Name: task_is_resolved_end_date_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX task_is_resolved_end_date_idx ON public.task USING btree (is_resolved, end_date); + + +-- +-- Name: task_is_resolved_responsible_user_id_start_date_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX task_is_resolved_responsible_user_id_start_date_idx ON public.task USING btree (is_resolved, responsible_user_id, start_date); + + +-- +-- Name: user_object_permission_user_id_object_permission_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX user_object_permission_user_id_object_permission_id_idx ON public.user_object_permission USING btree (user_id, object_permission_id); + + +-- +-- Name: users_account_id_department_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX users_account_id_department_id_idx ON public.users USING btree (account_id, department_id); + + +-- +-- Name: users_account_id_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX users_account_id_id_idx ON public.users USING btree (account_id, id); + + +-- +-- Name: account_api_access account_api_access_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account_api_access + ADD CONSTRAINT account_api_access_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: account_settings account_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account_settings + ADD CONSTRAINT account_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action action_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action + ADD CONSTRAINT action_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: activity activity_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity + ADD CONSTRAINT activity_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_activity_settings activity_action_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_activity_settings + ADD CONSTRAINT activity_action_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_activity_settings activity_action_settings_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_activity_settings + ADD CONSTRAINT activity_action_settings_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id) ON DELETE CASCADE; + + +-- +-- Name: action_activity_settings activity_action_settings_activity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_activity_settings + ADD CONSTRAINT activity_action_settings_activity_type_id_fkey FOREIGN KEY (activity_type_id) REFERENCES public.activity_type(id) ON DELETE CASCADE; + + +-- +-- Name: action_activity_settings activity_action_settings_responsible_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_activity_settings + ADD CONSTRAINT activity_action_settings_responsible_user_id_fkey FOREIGN KEY (responsible_user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: activity activity_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity + ADD CONSTRAINT activity_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: activity activity_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity + ADD CONSTRAINT activity_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: activity activity_responsible_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity + ADD CONSTRAINT activity_responsible_user_id_fkey FOREIGN KEY (responsible_user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: activity activity_task_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity + ADD CONSTRAINT activity_task_type_id_fkey FOREIGN KEY (activity_type_id) REFERENCES public.activity_type(id) ON DELETE CASCADE; + + +-- +-- Name: appsumo_license app_sumo_license_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.appsumo_license + ADD CONSTRAINT app_sumo_license_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE SET NULL; + + +-- +-- Name: automation automation_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation + ADD CONSTRAINT automation_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: automation automation_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation + ADD CONSTRAINT automation_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id); + + +-- +-- Name: automation_condition automation_condition_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_condition + ADD CONSTRAINT automation_condition_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: automation_condition automation_condition_automation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_condition + ADD CONSTRAINT automation_condition_automation_id_fkey FOREIGN KEY (automation_id) REFERENCES public.automation(id) ON DELETE CASCADE; + + +-- +-- Name: automation_condition automation_condition_condition_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_condition + ADD CONSTRAINT automation_condition_condition_id_fkey FOREIGN KEY (condition_id) REFERENCES public.condition(id) ON DELETE CASCADE; + + +-- +-- Name: automation automation_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation + ADD CONSTRAINT automation_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: automation_entity_type automation_entity_type_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: automation_entity_type automation_entity_type_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id); + + +-- +-- Name: automation_entity_type automation_entity_type_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: automation_entity_type automation_entity_type_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id); + + +-- +-- Name: automation_entity_type automation_entity_type_process_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_process_id_fkey FOREIGN KEY (process_id) REFERENCES public.automation_process(id) ON DELETE CASCADE; + + +-- +-- Name: automation_entity_type automation_entity_type_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_entity_type + ADD CONSTRAINT automation_entity_type_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id); + + +-- +-- Name: automation_process automation_process_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_process + ADD CONSTRAINT automation_process_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: automation_stage automation_stage_automation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_stage + ADD CONSTRAINT automation_stage_automation_id_fkey FOREIGN KEY (automation_id) REFERENCES public.automation(id) ON DELETE CASCADE; + + +-- +-- Name: automation_stage automation_stage_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_stage + ADD CONSTRAINT automation_stage_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id) ON DELETE CASCADE; + + +-- +-- Name: automation automation_trigger_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation + ADD CONSTRAINT automation_trigger_id_fkey FOREIGN KEY (trigger_id) REFERENCES public.trigger(id); + + +-- +-- Name: board board_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.board + ADD CONSTRAINT board_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: board board_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.board + ADD CONSTRAINT board_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: board board_task_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.board + ADD CONSTRAINT board_task_board_id_fkey FOREIGN KEY (task_board_id) REFERENCES public.board(id) ON DELETE SET NULL; + + +-- +-- Name: action_entity_settings change_stage_action_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_entity_settings + ADD CONSTRAINT change_stage_action_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_entity_settings change_stage_action_settings_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_entity_settings + ADD CONSTRAINT change_stage_action_settings_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id) ON DELETE CASCADE; + + +-- +-- Name: action_entity_settings change_stage_action_settings_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_entity_settings + ADD CONSTRAINT change_stage_action_settings_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id) ON DELETE CASCADE; + + +-- +-- Name: chat chat_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat + ADD CONSTRAINT chat_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat chat_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat + ADD CONSTRAINT chat_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: chat chat_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat + ADD CONSTRAINT chat_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE SET NULL; + + +-- +-- Name: chat_message chat_message_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message + ADD CONSTRAINT chat_message_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message chat_message_chat_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message + ADD CONSTRAINT chat_message_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES public.chat(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message chat_message_chat_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message + ADD CONSTRAINT chat_message_chat_user_id_fkey FOREIGN KEY (chat_user_id) REFERENCES public.chat_user(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_file chat_message_file_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_file + ADD CONSTRAINT chat_message_file_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_file chat_message_file_message_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_file + ADD CONSTRAINT chat_message_file_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.chat_message(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_reaction chat_message_reaction_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_reaction + ADD CONSTRAINT chat_message_reaction_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_reaction chat_message_reaction_chat_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_reaction + ADD CONSTRAINT chat_message_reaction_chat_user_id_fkey FOREIGN KEY (chat_user_id) REFERENCES public.chat_user(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_reaction chat_message_reaction_message_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_reaction + ADD CONSTRAINT chat_message_reaction_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.chat_message(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message chat_message_reply_to_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message + ADD CONSTRAINT chat_message_reply_to_id_fkey FOREIGN KEY (reply_to_id) REFERENCES public.chat_message(id) ON DELETE SET NULL; + + +-- +-- Name: chat_message_user_status chat_message_user_status_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_user_status + ADD CONSTRAINT chat_message_user_status_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_user_status chat_message_user_status_chat_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_user_status + ADD CONSTRAINT chat_message_user_status_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES public.chat(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_user_status chat_message_user_status_chat_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_user_status + ADD CONSTRAINT chat_message_user_status_chat_user_id_fkey FOREIGN KEY (chat_user_id) REFERENCES public.chat_user(id) ON DELETE CASCADE; + + +-- +-- Name: chat_message_user_status chat_message_user_status_message_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_message_user_status + ADD CONSTRAINT chat_message_user_status_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.chat_message(id) ON DELETE CASCADE; + + +-- +-- Name: chat_pinned_message chat_pinned_message_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_pinned_message + ADD CONSTRAINT chat_pinned_message_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_pinned_message chat_pinned_message_chat_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_pinned_message + ADD CONSTRAINT chat_pinned_message_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES public.chat(id) ON DELETE CASCADE; + + +-- +-- Name: chat_pinned_message chat_pinned_message_message_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_pinned_message + ADD CONSTRAINT chat_pinned_message_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.chat_message(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider chat_provider_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider + ADD CONSTRAINT chat_provider_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider chat_provider_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider + ADD CONSTRAINT chat_provider_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: chat chat_provider_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat + ADD CONSTRAINT chat_provider_id_fkey FOREIGN KEY (provider_id) REFERENCES public.chat_provider(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_messenger chat_provider_messenger_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_messenger + ADD CONSTRAINT chat_provider_messenger_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_messenger chat_provider_messenger_provider_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_messenger + ADD CONSTRAINT chat_provider_messenger_provider_id_fkey FOREIGN KEY (provider_id) REFERENCES public.chat_provider(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_twilio chat_provider_twilio_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_twilio + ADD CONSTRAINT chat_provider_twilio_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_twilio chat_provider_twilio_provider_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_twilio + ADD CONSTRAINT chat_provider_twilio_provider_id_fkey FOREIGN KEY (provider_id) REFERENCES public.chat_provider(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_user chat_provider_user_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_user + ADD CONSTRAINT chat_provider_user_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_user chat_provider_user_provider_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_user + ADD CONSTRAINT chat_provider_user_provider_id_fkey FOREIGN KEY (provider_id) REFERENCES public.chat_provider(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_user chat_provider_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_user + ADD CONSTRAINT chat_provider_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_wazzup chat_provider_wazzup_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_wazzup + ADD CONSTRAINT chat_provider_wazzup_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_provider_wazzup chat_provider_wazzup_provider_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_provider_wazzup + ADD CONSTRAINT chat_provider_wazzup_provider_id_fkey FOREIGN KEY (provider_id) REFERENCES public.chat_provider(id) ON DELETE CASCADE; + + +-- +-- Name: chat_user chat_user_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user + ADD CONSTRAINT chat_user_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_user chat_user_chat_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user + ADD CONSTRAINT chat_user_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES public.chat(id) ON DELETE CASCADE; + + +-- +-- Name: chat_user_external chat_user_external_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user_external + ADD CONSTRAINT chat_user_external_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: chat_user_external chat_user_external_chat_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user_external + ADD CONSTRAINT chat_user_external_chat_user_id_fkey FOREIGN KEY (chat_user_id) REFERENCES public.chat_user(id) ON DELETE CASCADE; + + +-- +-- Name: chat_user chat_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chat_user + ADD CONSTRAINT chat_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: condition condition_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.condition + ADD CONSTRAINT condition_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: demo_data demo_data_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.demo_data + ADD CONSTRAINT demo_data_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: department department_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.department + ADD CONSTRAINT department_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: department department_parent_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.department + ADD CONSTRAINT department_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES public.department(id) ON DELETE CASCADE; + + +-- +-- Name: document_template_access document_template_access_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_access + ADD CONSTRAINT document_template_access_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: document_template_access document_template_access_document_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_access + ADD CONSTRAINT document_template_access_document_template_id_fkey FOREIGN KEY (document_template_id) REFERENCES public.document_template(id) ON DELETE CASCADE; + + +-- +-- Name: document_template_access document_template_access_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_access + ADD CONSTRAINT document_template_access_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: document_template document_template_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template + ADD CONSTRAINT document_template_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: document_template document_template_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template + ADD CONSTRAINT document_template_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: document_template_entity_type document_template_entity_type_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_entity_type + ADD CONSTRAINT document_template_entity_type_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: document_template_entity_type document_template_entity_type_document_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_entity_type + ADD CONSTRAINT document_template_entity_type_document_template_id_fkey FOREIGN KEY (document_template_id) REFERENCES public.document_template(id) ON DELETE CASCADE; + + +-- +-- Name: document_template_entity_type document_template_entity_type_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_template_entity_type + ADD CONSTRAINT document_template_entity_type_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: action_email_settings email_action_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_email_settings + ADD CONSTRAINT email_action_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_email_settings email_action_settings_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_email_settings + ADD CONSTRAINT email_action_settings_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id) ON DELETE CASCADE; + + +-- +-- Name: action_email_settings email_action_settings_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_email_settings + ADD CONSTRAINT email_action_settings_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: action_email_settings email_action_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_email_settings + ADD CONSTRAINT email_action_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: entity entity_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity + ADD CONSTRAINT entity_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity entity_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity + ADD CONSTRAINT entity_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: entity entity_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity + ADD CONSTRAINT entity_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: entity_event entity_event_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_event + ADD CONSTRAINT entity_event_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_event entity_event_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_event + ADD CONSTRAINT entity_event_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: entity_link entity_link_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_link + ADD CONSTRAINT entity_link_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_link entity_link_back_link_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_link + ADD CONSTRAINT entity_link_back_link_id_fkey FOREIGN KEY (back_link_id) REFERENCES public.entity_link(id) ON DELETE CASCADE; + + +-- +-- Name: entity_link entity_link_source_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_link + ADD CONSTRAINT entity_link_source_id_fkey FOREIGN KEY (source_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: entity_link entity_link_target_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_link + ADD CONSTRAINT entity_link_target_id_fkey FOREIGN KEY (target_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: entity_list_settings entity_list_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_list_settings + ADD CONSTRAINT entity_list_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_list_settings entity_list_settings_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_list_settings + ADD CONSTRAINT entity_list_settings_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id) ON DELETE CASCADE; + + +-- +-- Name: entity_list_settings entity_list_settings_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_list_settings + ADD CONSTRAINT entity_list_settings_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: entity entity_responsible_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity + ADD CONSTRAINT entity_responsible_user_id_fkey FOREIGN KEY (responsible_user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: entity_stage_history entity_stage_history_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_stage_history + ADD CONSTRAINT entity_stage_history_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_stage_history entity_stage_history_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_stage_history + ADD CONSTRAINT entity_stage_history_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id) ON DELETE CASCADE; + + +-- +-- Name: entity_stage_history entity_stage_history_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_stage_history + ADD CONSTRAINT entity_stage_history_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: entity_stage_history entity_stage_history_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_stage_history + ADD CONSTRAINT entity_stage_history_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id) ON DELETE CASCADE; + + +-- +-- Name: entity entity_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity + ADD CONSTRAINT entity_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type entity_type_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type + ADD CONSTRAINT entity_type_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type_feature entity_type_feature_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_feature + ADD CONSTRAINT entity_type_feature_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type_feature entity_type_feature_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_feature + ADD CONSTRAINT entity_type_feature_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type_feature entity_type_feature_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_feature + ADD CONSTRAINT entity_type_feature_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.feature(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type_link entity_type_link_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_link + ADD CONSTRAINT entity_type_link_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type_link entity_type_link_source_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_link + ADD CONSTRAINT entity_type_link_source_id_fkey FOREIGN KEY (source_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: entity_type_link entity_type_link_target_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entity_type_link + ADD CONSTRAINT entity_type_link_target_id_fkey FOREIGN KEY (target_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: exact_time_trigger_settings exact_time_trigger_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.exact_time_trigger_settings + ADD CONSTRAINT exact_time_trigger_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: exact_time_trigger_settings exact_time_trigger_settings_trigger_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.exact_time_trigger_settings + ADD CONSTRAINT exact_time_trigger_settings_trigger_id_fkey FOREIGN KEY (trigger_id) REFERENCES public.trigger(id) ON DELETE CASCADE; + + +-- +-- Name: external_entity external_entity_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_entity + ADD CONSTRAINT external_entity_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: external_entity external_entity_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_entity + ADD CONSTRAINT external_entity_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: external_entity external_entity_system_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_entity + ADD CONSTRAINT external_entity_system_fkey FOREIGN KEY (system) REFERENCES public.external_system(id); + + +-- +-- Name: field field_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field + ADD CONSTRAINT field_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_condition field_condition_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_condition + ADD CONSTRAINT field_condition_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_condition field_condition_condition_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_condition + ADD CONSTRAINT field_condition_condition_id_fkey FOREIGN KEY (condition_id) REFERENCES public.condition(id) ON DELETE CASCADE; + + +-- +-- Name: field_condition field_condition_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_condition + ADD CONSTRAINT field_condition_field_id_fkey FOREIGN KEY (field_id) REFERENCES public.field(id) ON DELETE CASCADE; + + +-- +-- Name: field field_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field + ADD CONSTRAINT field_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: field field_field_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field + ADD CONSTRAINT field_field_group_id_fkey FOREIGN KEY (field_group_id) REFERENCES public.field_group(id) ON DELETE CASCADE; + + +-- +-- Name: field_group field_group_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_group + ADD CONSTRAINT field_group_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_group field_group_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_group + ADD CONSTRAINT field_group_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: field_option field_option_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_option + ADD CONSTRAINT field_option_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_option field_option_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_option + ADD CONSTRAINT field_option_field_id_fkey FOREIGN KEY (field_id) REFERENCES public.field(id) ON DELETE CASCADE; + + +-- +-- Name: field_stage_settings field_stage_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_stage_settings + ADD CONSTRAINT field_stage_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_stage_settings field_stage_settings_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_stage_settings + ADD CONSTRAINT field_stage_settings_field_id_fkey FOREIGN KEY (field_id) REFERENCES public.field(id) ON DELETE CASCADE; + + +-- +-- Name: field_stage_settings field_stage_settings_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_stage_settings + ADD CONSTRAINT field_stage_settings_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id) ON DELETE CASCADE; + + +-- +-- Name: field_user_settings field_user_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_user_settings + ADD CONSTRAINT field_user_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_user_settings field_user_settings_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_user_settings + ADD CONSTRAINT field_user_settings_field_id_fkey FOREIGN KEY (field_id) REFERENCES public.field(id) ON DELETE CASCADE; + + +-- +-- Name: field_user_settings field_user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_user_settings + ADD CONSTRAINT field_user_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: field_value field_value_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_value + ADD CONSTRAINT field_value_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: field_value field_value_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_value + ADD CONSTRAINT field_value_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: field_value field_value_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.field_value + ADD CONSTRAINT field_value_field_id_fkey FOREIGN KEY (field_id) REFERENCES public.field(id) ON DELETE CASCADE; + + +-- +-- Name: file_info file_info_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.file_info + ADD CONSTRAINT file_info_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: file_info file_info_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.file_info + ADD CONSTRAINT file_info_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: file_link file_link_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.file_link + ADD CONSTRAINT file_link_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: file_link file_link_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.file_link + ADD CONSTRAINT file_link_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: mail_message mail_message_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message + ADD CONSTRAINT mail_message_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mail_message mail_message_contact_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message + ADD CONSTRAINT mail_message_contact_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE SET NULL; + + +-- +-- Name: mail_message_folder mail_message_folder_folder_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message_folder + ADD CONSTRAINT mail_message_folder_folder_id_fkey FOREIGN KEY (folder_id) REFERENCES public.mailbox_folder(id) ON DELETE CASCADE; + + +-- +-- Name: mail_message_folder mail_message_folder_message_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message_folder + ADD CONSTRAINT mail_message_folder_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.mail_message(id) ON DELETE CASCADE; + + +-- +-- Name: mail_message mail_message_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message + ADD CONSTRAINT mail_message_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: mail_message_payload mail_message_payload_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message_payload + ADD CONSTRAINT mail_message_payload_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mail_message_payload mail_message_payload_message_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mail_message_payload + ADD CONSTRAINT mail_message_payload_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.mail_message(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_accessible_user mailbox_accessible_user_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_accessible_user + ADD CONSTRAINT mailbox_accessible_user_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_accessible_user mailbox_accessible_user_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_accessible_user + ADD CONSTRAINT mailbox_accessible_user_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_accessible_user mailbox_accessible_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_accessible_user + ADD CONSTRAINT mailbox_accessible_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox mailbox_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox + ADD CONSTRAINT mailbox_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox mailbox_contact_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox + ADD CONSTRAINT mailbox_contact_entity_type_id_fkey FOREIGN KEY (contact_entity_type_id) REFERENCES public.entity_type(id) ON DELETE SET NULL; + + +-- +-- Name: mailbox_folder mailbox_folder_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_folder + ADD CONSTRAINT mailbox_folder_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_folder mailbox_folder_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_folder + ADD CONSTRAINT mailbox_folder_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox mailbox_lead_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox + ADD CONSTRAINT mailbox_lead_board_id_fkey FOREIGN KEY (lead_board_id) REFERENCES public.board(id) ON DELETE SET NULL; + + +-- +-- Name: mailbox mailbox_lead_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox + ADD CONSTRAINT mailbox_lead_entity_type_id_fkey FOREIGN KEY (lead_entity_type_id) REFERENCES public.entity_type(id) ON DELETE SET NULL; + + +-- +-- Name: mailbox mailbox_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox + ADD CONSTRAINT mailbox_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.users(id); + + +-- +-- Name: mailbox_settings_gmail mailbox_settings_gmail_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_settings_gmail + ADD CONSTRAINT mailbox_settings_gmail_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_settings_gmail mailbox_settings_gmail_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_settings_gmail + ADD CONSTRAINT mailbox_settings_gmail_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_settings_manual mailbox_settings_manual_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_settings_manual + ADD CONSTRAINT mailbox_settings_manual_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_settings_manual mailbox_settings_manual_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_settings_manual + ADD CONSTRAINT mailbox_settings_manual_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_signature mailbox_signature_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature + ADD CONSTRAINT mailbox_signature_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_signature mailbox_signature_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature + ADD CONSTRAINT mailbox_signature_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_signature_link mailbox_signature_link_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature_link + ADD CONSTRAINT mailbox_signature_link_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_signature_link mailbox_signature_link_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature_link + ADD CONSTRAINT mailbox_signature_link_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: mailbox_signature_link mailbox_signature_link_signature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.mailbox_signature_link + ADD CONSTRAINT mailbox_signature_link_signature_id_fkey FOREIGN KEY (signature_id) REFERENCES public.mailbox_signature(id) ON DELETE CASCADE; + + +-- +-- Name: note note_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.note + ADD CONSTRAINT note_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: note note_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.note + ADD CONSTRAINT note_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: note note_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.note + ADD CONSTRAINT note_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: notification notification_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification + ADD CONSTRAINT notification_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: notification notification_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification + ADD CONSTRAINT notification_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE SET NULL; + + +-- +-- Name: notification notification_from_user_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification + ADD CONSTRAINT notification_from_user_fkey FOREIGN KEY (from_user) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: notification_settings notification_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_settings + ADD CONSTRAINT notification_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: notification_settings notification_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_settings + ADD CONSTRAINT notification_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_type_follow_user notification_type_follow_user_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_follow_user + ADD CONSTRAINT notification_type_follow_user_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: notification_type_follow_user notification_type_follow_user_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_follow_user + ADD CONSTRAINT notification_type_follow_user_type_id_fkey FOREIGN KEY (type_id) REFERENCES public.notification_type_settings(id) ON DELETE CASCADE; + + +-- +-- Name: notification_type_follow_user notification_type_follow_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_follow_user + ADD CONSTRAINT notification_type_follow_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_type_settings notification_type_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_settings + ADD CONSTRAINT notification_type_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: notification_type_settings notification_type_settings_settings_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_type_settings + ADD CONSTRAINT notification_type_settings_settings_id_fkey FOREIGN KEY (settings_id) REFERENCES public.notification_settings(id) ON DELETE CASCADE; + + +-- +-- Name: notification notification_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification + ADD CONSTRAINT notification_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: object_permission object_permission_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.object_permission + ADD CONSTRAINT object_permission_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: order_item order_item_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.order_item + ADD CONSTRAINT order_item_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: order_item order_item_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.order_item + ADD CONSTRAINT order_item_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id) ON DELETE CASCADE; + + +-- +-- Name: order_item order_item_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.order_item + ADD CONSTRAINT order_item_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id); + + +-- +-- Name: order_status order_status_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.order_status + ADD CONSTRAINT order_status_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: orders orders_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: orders orders_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: orders orders_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: orders orders_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: orders orders_status_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_status_id_fkey FOREIGN KEY (status_id) REFERENCES public.order_status(id); + + +-- +-- Name: orders orders_warehouse_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES public.warehouse(id); + + +-- +-- Name: product product_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product + ADD CONSTRAINT product_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: product_category product_category_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_category + ADD CONSTRAINT product_category_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: product_category product_category_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_category + ADD CONSTRAINT product_category_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: product product_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product + ADD CONSTRAINT product_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.product_category(id) ON DELETE SET NULL; + + +-- +-- Name: product_category product_category_parent_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_category + ADD CONSTRAINT product_category_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES public.product_category(id); + + +-- +-- Name: product_category product_category_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_category + ADD CONSTRAINT product_category_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: product product_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product + ADD CONSTRAINT product_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: products_section product_module_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.products_section + ADD CONSTRAINT product_module_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: product_price product_price_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_price + ADD CONSTRAINT product_price_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: product_price product_price_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_price + ADD CONSTRAINT product_price_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id) ON DELETE CASCADE; + + +-- +-- Name: product product_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product + ADD CONSTRAINT product_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: products_section_entity_type products_section_entity_type_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.products_section_entity_type + ADD CONSTRAINT products_section_entity_type_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: products_section_entity_type products_section_entity_type_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.products_section_entity_type + ADD CONSTRAINT products_section_entity_type_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: products_section_entity_type products_section_entity_type_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.products_section_entity_type + ADD CONSTRAINT products_section_entity_type_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: ready_made_solution ready_made_solution_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ready_made_solution + ADD CONSTRAINT ready_made_solution_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id); + + +-- +-- Name: ready_made_solution ready_made_solution_industry_code_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ready_made_solution + ADD CONSTRAINT ready_made_solution_industry_code_fkey FOREIGN KEY (industry_code) REFERENCES public.industry(code); + + +-- +-- Name: rental_interval rental_interval_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_interval + ADD CONSTRAINT rental_interval_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: rental_interval rental_interval_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_interval + ADD CONSTRAINT rental_interval_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order rental_order_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order + ADD CONSTRAINT rental_order_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order rental_order_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order + ADD CONSTRAINT rental_order_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: rental_order rental_order_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order + ADD CONSTRAINT rental_order_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order_item rental_order_item_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_item + ADD CONSTRAINT rental_order_item_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order_item rental_order_item_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_item + ADD CONSTRAINT rental_order_item_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.rental_order(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order_item rental_order_item_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_item + ADD CONSTRAINT rental_order_item_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order_period rental_order_period_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_period + ADD CONSTRAINT rental_order_period_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order_period rental_order_period_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order_period + ADD CONSTRAINT rental_order_period_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.rental_order(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order rental_order_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order + ADD CONSTRAINT rental_order_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: rental_order rental_order_warehouse_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_order + ADD CONSTRAINT rental_order_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES public.warehouse(id); + + +-- +-- Name: rental_event rental_schedule_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_event + ADD CONSTRAINT rental_schedule_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: rental_event rental_schedule_order_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_event + ADD CONSTRAINT rental_schedule_order_item_id_fkey FOREIGN KEY (order_item_id) REFERENCES public.rental_order_item(id) ON DELETE CASCADE; + + +-- +-- Name: rental_event rental_schedule_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_event + ADD CONSTRAINT rental_schedule_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id) ON DELETE CASCADE; + + +-- +-- Name: rental_event rental_schedule_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rental_event + ADD CONSTRAINT rental_schedule_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: reservation reservation_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.reservation + ADD CONSTRAINT reservation_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: reservation reservation_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.reservation + ADD CONSTRAINT reservation_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id) ON DELETE CASCADE; + + +-- +-- Name: reservation reservation_order_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.reservation + ADD CONSTRAINT reservation_order_item_id_fkey FOREIGN KEY (order_item_id) REFERENCES public.order_item(id) ON DELETE CASCADE; + + +-- +-- Name: reservation reservation_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.reservation + ADD CONSTRAINT reservation_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id); + + +-- +-- Name: reservation reservation_warehouse_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.reservation + ADD CONSTRAINT reservation_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES public.warehouse(id); + + +-- +-- Name: sales_plan sales_plan_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.sales_plan + ADD CONSTRAINT sales_plan_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: sales_plan sales_plan_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.sales_plan + ADD CONSTRAINT sales_plan_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: sales_plan sales_plan_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.sales_plan + ADD CONSTRAINT sales_plan_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: salesforce_settings salesforce_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.salesforce_settings + ADD CONSTRAINT salesforce_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: schedule schedule_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule + ADD CONSTRAINT schedule_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: schedule_appointment schedule_appointment_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_appointment_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id) ON DELETE SET NULL; + + +-- +-- Name: schedule_appointment schedule_appointment_performer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_appointment_performer_id_fkey FOREIGN KEY (performer_id) REFERENCES public.schedule_performer(id) ON DELETE CASCADE; + + +-- +-- Name: schedule schedule_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule + ADD CONSTRAINT schedule_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE SET NULL; + + +-- +-- Name: schedule_appointment schedule_event_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_event_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: schedule_appointment schedule_event_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_event_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE SET NULL; + + +-- +-- Name: schedule_appointment schedule_event_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_event_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.users(id); + + +-- +-- Name: schedule_appointment schedule_event_schedule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_appointment + ADD CONSTRAINT schedule_event_schedule_id_fkey FOREIGN KEY (schedule_id) REFERENCES public.schedule(id) ON DELETE CASCADE; + + +-- +-- Name: schedule_performer schedule_performer_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_performer + ADD CONSTRAINT schedule_performer_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: schedule_performer schedule_performer_department_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_performer + ADD CONSTRAINT schedule_performer_department_id_fkey FOREIGN KEY (department_id) REFERENCES public.department(id) ON DELETE CASCADE; + + +-- +-- Name: schedule_performer schedule_performer_schedule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_performer + ADD CONSTRAINT schedule_performer_schedule_id_fkey FOREIGN KEY (schedule_id) REFERENCES public.schedule(id) ON DELETE CASCADE; + + +-- +-- Name: schedule_performer schedule_performer_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule_performer + ADD CONSTRAINT schedule_performer_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: schedule schedule_products_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schedule + ADD CONSTRAINT schedule_products_section_id_fkey FOREIGN KEY (products_section_id) REFERENCES public.products_section(id) ON DELETE SET NULL; + + +-- +-- Name: action_scheduled scheduled_action_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_scheduled + ADD CONSTRAINT scheduled_action_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_scheduled scheduled_action_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_scheduled + ADD CONSTRAINT scheduled_action_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id) ON DELETE CASCADE; + + +-- +-- Name: action_scheduled scheduled_action_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_scheduled + ADD CONSTRAINT scheduled_action_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: action_scheduled scheduled_action_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_scheduled + ADD CONSTRAINT scheduled_action_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: scheduled_mail_message scheduled_mail_message_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.scheduled_mail_message + ADD CONSTRAINT scheduled_mail_message_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: scheduled_mail_message scheduled_mail_message_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.scheduled_mail_message + ADD CONSTRAINT scheduled_mail_message_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id) ON DELETE SET NULL; + + +-- +-- Name: scheduled_mail_message scheduled_mail_message_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.scheduled_mail_message + ADD CONSTRAINT scheduled_mail_message_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE SET NULL; + + +-- +-- Name: scheduled_mail_message scheduled_mail_message_mailbox_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.scheduled_mail_message + ADD CONSTRAINT scheduled_mail_message_mailbox_id_fkey FOREIGN KEY (mailbox_id) REFERENCES public.mailbox(id) ON DELETE CASCADE; + + +-- +-- Name: scheduled_mail_message scheduled_mail_message_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.scheduled_mail_message + ADD CONSTRAINT scheduled_mail_message_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: shipment shipment_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: shipment shipment_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: shipment_item shipment_item_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment_item + ADD CONSTRAINT shipment_item_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: shipment_item shipment_item_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment_item + ADD CONSTRAINT shipment_item_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id); + + +-- +-- Name: shipment_item shipment_item_shipment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment_item + ADD CONSTRAINT shipment_item_shipment_id_fkey FOREIGN KEY (shipment_id) REFERENCES public.shipment(id) ON DELETE CASCADE; + + +-- +-- Name: shipment shipment_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id) ON DELETE CASCADE; + + +-- +-- Name: shipment shipment_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- Name: shipment shipment_status_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_status_id_fkey FOREIGN KEY (status_id) REFERENCES public.order_status(id); + + +-- +-- Name: shipment shipment_warehouse_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.shipment + ADD CONSTRAINT shipment_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES public.warehouse(id) ON DELETE CASCADE; + + +-- +-- Name: site_form site_form_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form + ADD CONSTRAINT site_form_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_consent site_form_consent_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_consent + ADD CONSTRAINT site_form_consent_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_consent site_form_consent_form_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_consent + ADD CONSTRAINT site_form_consent_form_id_fkey FOREIGN KEY (form_id) REFERENCES public.site_form(id) ON DELETE CASCADE; + + +-- +-- Name: site_form site_form_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form + ADD CONSTRAINT site_form_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: site_form_entity_type site_form_entity_type_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_entity_type + ADD CONSTRAINT site_form_entity_type_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_entity_type site_form_entity_type_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_entity_type + ADD CONSTRAINT site_form_entity_type_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id) ON DELETE SET NULL; + + +-- +-- Name: site_form_entity_type site_form_entity_type_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_entity_type + ADD CONSTRAINT site_form_entity_type_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_entity_type site_form_entity_type_form_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_entity_type + ADD CONSTRAINT site_form_entity_type_form_id_fkey FOREIGN KEY (form_id) REFERENCES public.site_form(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field site_form_field_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field + ADD CONSTRAINT site_form_field_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field_entity_field site_form_field_entity_field_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_field + ADD CONSTRAINT site_form_field_entity_field_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field_entity_field site_form_field_entity_field_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_field + ADD CONSTRAINT site_form_field_entity_field_field_id_fkey FOREIGN KEY (field_id) REFERENCES public.field(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field_entity_field site_form_field_entity_field_form_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_field + ADD CONSTRAINT site_form_field_entity_field_form_field_id_fkey FOREIGN KEY (form_field_id) REFERENCES public.site_form_field(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field_entity_name site_form_field_entity_name_entity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_name + ADD CONSTRAINT site_form_field_entity_name_entity_type_id_fkey FOREIGN KEY (entity_type_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field_entity_name site_form_field_entity_name_form_field_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field_entity_name + ADD CONSTRAINT site_form_field_entity_name_form_field_id_fkey FOREIGN KEY (form_field_id) REFERENCES public.site_form_field(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_field site_form_field_page_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_field + ADD CONSTRAINT site_form_field_page_id_fkey FOREIGN KEY (page_id) REFERENCES public.site_form_page(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_gratitude site_form_gratitude_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_gratitude + ADD CONSTRAINT site_form_gratitude_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_gratitude site_form_gratitude_form_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_gratitude + ADD CONSTRAINT site_form_gratitude_form_id_fkey FOREIGN KEY (form_id) REFERENCES public.site_form(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_page site_form_page_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_page + ADD CONSTRAINT site_form_page_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: site_form_page site_form_page_form_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form_page + ADD CONSTRAINT site_form_page_form_id_fkey FOREIGN KEY (form_id) REFERENCES public.site_form(id) ON DELETE CASCADE; + + +-- +-- Name: site_form site_form_responsible_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.site_form + ADD CONSTRAINT site_form_responsible_id_fkey FOREIGN KEY (responsible_id) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: stage stage_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.stage + ADD CONSTRAINT stage_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: stage stage_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.stage + ADD CONSTRAINT stage_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id) ON DELETE CASCADE; + + +-- +-- Name: product_stock stock_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_stock + ADD CONSTRAINT stock_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: product_stock stock_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_stock + ADD CONSTRAINT stock_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id) ON DELETE CASCADE; + + +-- +-- Name: product_stock stock_warehouse_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.product_stock + ADD CONSTRAINT stock_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES public.warehouse(id) ON DELETE CASCADE; + + +-- +-- Name: account_subscription subscription_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.account_subscription + ADD CONSTRAINT subscription_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: task_subtask subtask_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_subtask + ADD CONSTRAINT subtask_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: task_subtask subtask_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_subtask + ADD CONSTRAINT subtask_task_id_fkey FOREIGN KEY (task_id) REFERENCES public.task(id) ON DELETE CASCADE; + + +-- +-- Name: task task_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_task_settings task_action_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_task_settings + ADD CONSTRAINT task_action_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: action_task_settings task_action_settings_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_task_settings + ADD CONSTRAINT task_action_settings_action_id_fkey FOREIGN KEY (action_id) REFERENCES public.action(id) ON DELETE CASCADE; + + +-- +-- Name: action_task_settings task_action_settings_responsible_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.action_task_settings + ADD CONSTRAINT task_action_settings_responsible_user_id_fkey FOREIGN KEY (responsible_user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: task task_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id) ON DELETE CASCADE; + + +-- +-- Name: task_comment task_comment_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment + ADD CONSTRAINT task_comment_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: task_comment task_comment_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment + ADD CONSTRAINT task_comment_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: task_comment_like task_comment_like_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment_like + ADD CONSTRAINT task_comment_like_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: task_comment_like task_comment_like_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment_like + ADD CONSTRAINT task_comment_like_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES public.task_comment(id) ON DELETE CASCADE; + + +-- +-- Name: task_comment_like task_comment_like_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment_like + ADD CONSTRAINT task_comment_like_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: task_comment task_comment_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_comment + ADD CONSTRAINT task_comment_task_id_fkey FOREIGN KEY (task_id) REFERENCES public.task(id) ON DELETE CASCADE; + + +-- +-- Name: task task_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: task task_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE CASCADE; + + +-- +-- Name: task task_responsible_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_responsible_user_id_fkey FOREIGN KEY (responsible_user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: task_settings task_settings_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task_settings + ADD CONSTRAINT task_settings_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: task task_settings_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_settings_id_fkey FOREIGN KEY (settings_id) REFERENCES public.task_settings(id) ON DELETE SET NULL; + + +-- +-- Name: task task_stage_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.task + ADD CONSTRAINT task_stage_id_fkey FOREIGN KEY (stage_id) REFERENCES public.stage(id) ON DELETE CASCADE; + + +-- +-- Name: activity_type task_type_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.activity_type + ADD CONSTRAINT task_type_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: test_account test_account_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.test_account + ADD CONSTRAINT test_account_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: trigger trigger_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.trigger + ADD CONSTRAINT trigger_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_group tutorial_group_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_group + ADD CONSTRAINT tutorial_group_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_item tutorial_item_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item + ADD CONSTRAINT tutorial_item_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_item tutorial_item_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item + ADD CONSTRAINT tutorial_item_group_id_fkey FOREIGN KEY (group_id) REFERENCES public.tutorial_group(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_item_product tutorial_item_product_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item_product + ADD CONSTRAINT tutorial_item_product_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_item_product tutorial_item_product_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item_product + ADD CONSTRAINT tutorial_item_product_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.tutorial_item(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_item_user tutorial_item_user_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item_user + ADD CONSTRAINT tutorial_item_user_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.tutorial_item(id) ON DELETE CASCADE; + + +-- +-- Name: tutorial_item_user tutorial_item_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tutorial_item_user + ADD CONSTRAINT tutorial_item_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: user_condition user_condition_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_condition + ADD CONSTRAINT user_condition_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: user_condition user_condition_condition_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_condition + ADD CONSTRAINT user_condition_condition_id_fkey FOREIGN KEY (condition_id) REFERENCES public.condition(id) ON DELETE CASCADE; + + +-- +-- Name: user_condition user_condition_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_condition + ADD CONSTRAINT user_condition_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: user_object_permission user_object_permission_object_permission_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_object_permission + ADD CONSTRAINT user_object_permission_object_permission_id_fkey FOREIGN KEY (object_permission_id) REFERENCES public.object_permission(id) ON DELETE CASCADE; + + +-- +-- Name: user_object_permission user_object_permission_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_object_permission + ADD CONSTRAINT user_object_permission_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: user_profile user_profile_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_profile + ADD CONSTRAINT user_profile_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: user_profile user_profile_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_profile + ADD CONSTRAINT user_profile_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: users users_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: users users_department_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_department_id_fkey FOREIGN KEY (department_id) REFERENCES public.department(id); + + +-- +-- Name: voximplant_account voximplant_account_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_account + ADD CONSTRAINT voximplant_account_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_call voximplant_call_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_call + ADD CONSTRAINT voximplant_call_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_call voximplant_call_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_call + ADD CONSTRAINT voximplant_call_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES public.entity(id) ON DELETE SET NULL; + + +-- +-- Name: voximplant_call voximplant_call_number_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_call + ADD CONSTRAINT voximplant_call_number_id_fkey FOREIGN KEY (number_id) REFERENCES public.voximplant_number(id) ON DELETE SET NULL; + + +-- +-- Name: voximplant_call voximplant_call_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_call + ADD CONSTRAINT voximplant_call_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_number voximplant_number_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_number + ADD CONSTRAINT voximplant_number_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_number_user voximplant_number_user_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_number_user + ADD CONSTRAINT voximplant_number_user_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_number_user voximplant_number_user_number_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_number_user + ADD CONSTRAINT voximplant_number_user_number_id_fkey FOREIGN KEY (number_id) REFERENCES public.voximplant_number(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_number_user voximplant_number_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_number_user + ADD CONSTRAINT voximplant_number_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_entity voximplant_scenario_entity_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_entity + ADD CONSTRAINT voximplant_scenario_entity_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_entity voximplant_scenario_entity_board_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_entity + ADD CONSTRAINT voximplant_scenario_entity_board_id_fkey FOREIGN KEY (board_id) REFERENCES public.board(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_entity voximplant_scenario_entity_contact_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_entity + ADD CONSTRAINT voximplant_scenario_entity_contact_id_fkey FOREIGN KEY (contact_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_entity voximplant_scenario_entity_deal_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_entity + ADD CONSTRAINT voximplant_scenario_entity_deal_id_fkey FOREIGN KEY (deal_id) REFERENCES public.entity_type(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_entity voximplant_scenario_entity_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_entity + ADD CONSTRAINT voximplant_scenario_entity_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_note voximplant_scenario_note_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_note + ADD CONSTRAINT voximplant_scenario_note_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_task voximplant_scenario_task_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_task + ADD CONSTRAINT voximplant_scenario_task_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_task voximplant_scenario_task_activity_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_task + ADD CONSTRAINT voximplant_scenario_task_activity_owner_id_fkey FOREIGN KEY (activity_owner_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_task voximplant_scenario_task_activity_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_task + ADD CONSTRAINT voximplant_scenario_task_activity_type_id_fkey FOREIGN KEY (activity_type_id) REFERENCES public.activity_type(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_scenario_task voximplant_scenario_task_task_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_scenario_task + ADD CONSTRAINT voximplant_scenario_task_task_owner_id_fkey FOREIGN KEY (task_owner_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_user voximplant_user_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_user + ADD CONSTRAINT voximplant_user_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: voximplant_user voximplant_user_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.voximplant_user + ADD CONSTRAINT voximplant_user_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: warehouse warehouse_account_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.warehouse + ADD CONSTRAINT warehouse_account_id_fkey FOREIGN KEY (account_id) REFERENCES public.account(id) ON DELETE CASCADE; + + +-- +-- Name: warehouse warehouse_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.warehouse + ADD CONSTRAINT warehouse_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: warehouse warehouse_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.warehouse + ADD CONSTRAINT warehouse_section_id_fkey FOREIGN KEY (section_id) REFERENCES public.products_section(id) ON DELETE CASCADE; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/backend/src/database/common/index.ts b/backend/src/database/common/index.ts new file mode 100644 index 0000000..04bca77 --- /dev/null +++ b/backend/src/database/common/index.ts @@ -0,0 +1 @@ +export * from './utils'; diff --git a/backend/src/database/common/utils/index.ts b/backend/src/database/common/utils/index.ts new file mode 100644 index 0000000..96aebfb --- /dev/null +++ b/backend/src/database/common/utils/index.ts @@ -0,0 +1 @@ +export * from './tsquery.util'; diff --git a/backend/src/database/common/utils/tsquery.util.ts b/backend/src/database/common/utils/tsquery.util.ts new file mode 100644 index 0000000..141139b --- /dev/null +++ b/backend/src/database/common/utils/tsquery.util.ts @@ -0,0 +1,28 @@ +export const buildSearchParams = (input: string): string => { + if (!input) return undefined; + + const phoneRegex = /(?:\+?\d[\d\s().-]{4,}\d)/g; + + // Extract and normalize phone numbers + const phoneNumbers = Array.from(input.matchAll(phoneRegex), (match) => match[0].replace(/\D/g, '')); + + // Remove phones from text + const cleanedInput = input.replace(phoneRegex, ' '); + + // Split into tokens + const tokens = cleanedInput + .trim() + .replace(/[:&`'!()*]+/g, '') // safely remove tsquery special symbols + .split(/\s+/) + .map( + (token) => + /^[\w@.]+$/.test(token) + ? token.toLowerCase() // keep as-is if safe (email/domain/etc.) + : token.toLowerCase().replace(/^[^\p{L}\p{N}@]+|[^\p{L}\p{N}@]+$/gu, ''), // trim edges + ) + .filter((token) => token.length >= 3); // remove short words + + const allTerms = [...tokens, ...phoneNumbers, ...phoneNumbers.map((n) => '+' + n)]; + + return allTerms.map((t) => `${t}:*`).join(' | '); +}; diff --git a/backend/src/database/config/database.config.ts b/backend/src/database/config/database.config.ts new file mode 100644 index 0000000..2c00786 --- /dev/null +++ b/backend/src/database/config/database.config.ts @@ -0,0 +1,37 @@ +import { registerAs } from '@nestjs/config'; +import { LoggerOptions, LogLevel } from 'typeorm'; + +export interface DatabaseConfig { + host: string; + port: number; + username: string; + password: string; + database: string; + logging: LoggerOptions; + cache: { type: string | undefined; duration: number | undefined }; +} + +const parseQueryLogging = (value: string | null | undefined): LoggerOptions | undefined => { + if (!value) return undefined; + if (value === 'true') return true; + if (value === 'false') return false; + if (value === 'all') return 'all'; + + return value.split(',').map((v) => v as LogLevel); +}; + +export default registerAs( + 'database', + (): DatabaseConfig => ({ + host: process.env.POSTGRES_HOST, + port: +process.env.POSTGRES_PORT, + username: process.env.POSTGRES_USER, + password: process.env.POSTGRES_PASSWORD, + database: process.env.POSTGRES_DB, + logging: parseQueryLogging(process.env.POSTGRES_QUERY_LOGGING), + cache: { + type: process.env.TYPEORM_CACHE_TYPE, + duration: +process.env.TYPEORM_CACHE_DURATION, + }, + }), +); diff --git a/backend/src/database/config/index.ts b/backend/src/database/config/index.ts new file mode 100644 index 0000000..64341a6 --- /dev/null +++ b/backend/src/database/config/index.ts @@ -0,0 +1 @@ +export * from './database.config'; diff --git a/backend/src/database/database.module.ts b/backend/src/database/database.module.ts new file mode 100644 index 0000000..7dfa9ca --- /dev/null +++ b/backend/src/database/database.module.ts @@ -0,0 +1,40 @@ +import { Global, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; + +import databaseConfig, { DatabaseConfig } from './config/database.config'; +import { SequenceIdService } from './services'; +import { NestjsLogger } from './nestjs-logger'; + +@Global() +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + imports: [ConfigModule.forFeature(databaseConfig)], + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + const config = configService.get('database'); + + return { + type: 'postgres', + host: config.host, + port: config.port, + username: config.username, + password: config.password, + database: config.database, + namingStrategy: new SnakeNamingStrategy(), + entities: [__dirname + '/../**/Model/**/*.{js,ts}', __dirname + '/../**/entities/*.entity.{js,ts}'], + maxQueryExecutionTime: 1000, + logging: config.logging, + logger: config.logging ? new NestjsLogger() : undefined, + synchronize: false, + migrationsRun: false, + }; + }, + }), + ], + providers: [SequenceIdService], + exports: [SequenceIdService], +}) +export class DatabaseModule {} diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts new file mode 100644 index 0000000..3902db8 --- /dev/null +++ b/backend/src/database/index.ts @@ -0,0 +1,6 @@ +export * from './common'; +export * from './config'; +export * from './database.module'; +export * from './nestjs-logger'; +export * from './services'; +export * from './typeorm-migration.config'; diff --git a/backend/src/database/migrations/1737731260000-AddLoginSecurityFields.ts b/backend/src/database/migrations/1737731260000-AddLoginSecurityFields.ts new file mode 100644 index 0000000..ffb4b36 --- /dev/null +++ b/backend/src/database/migrations/1737731260000-AddLoginSecurityFields.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddLoginSecurityFields1737731260000 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE users ADD COLUMN login_attempts INTEGER NOT NULL DEFAULT 0; + ALTER TABLE users ADD COLUMN lock_until TIMESTAMP WITH TIME ZONE NULL; + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE users DROP COLUMN lock_until; + ALTER TABLE users DROP COLUMN login_attempts; + `); + } +} diff --git a/backend/src/database/migrations/1744637701399-AddMailboxSettingsImapflow.ts b/backend/src/database/migrations/1744637701399-AddMailboxSettingsImapflow.ts new file mode 100644 index 0000000..a8f36b8 --- /dev/null +++ b/backend/src/database/migrations/1744637701399-AddMailboxSettingsImapflow.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxSettingsImapflow1744637701399 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table mailbox_settings_imapflow ( + mailbox_id integer, + account_id integer not null, + password text not null, + imap_server text not null, + imap_port smallint not null, + imap_secure boolean not null, + smtp_server text not null, + smtp_port smallint not null, + smtp_secure boolean not null, + sync_info jsonb, + primary key (mailbox_id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/1744982861922-AlterMailboxFolder.ts b/backend/src/database/migrations/1744982861922-AlterMailboxFolder.ts new file mode 100644 index 0000000..5072f86 --- /dev/null +++ b/backend/src/database/migrations/1744982861922-AlterMailboxFolder.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailboxFolder1744982861922 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_folder + add column parent_id integer, + add foreign key (parent_id) references mailbox_folder(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/1744990391092-AlterMailboxFolder.ts b/backend/src/database/migrations/1744990391092-AlterMailboxFolder.ts new file mode 100644 index 0000000..b74f9b7 --- /dev/null +++ b/backend/src/database/migrations/1744990391092-AlterMailboxFolder.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailboxFolder1744990391092 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_folder add column if not exists uid_validity integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/1745220421131-AlterMailboxFolder.ts b/backend/src/database/migrations/1745220421131-AlterMailboxFolder.ts new file mode 100644 index 0000000..d5023a8 --- /dev/null +++ b/backend/src/database/migrations/1745220421131-AlterMailboxFolder.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailboxFolder1745220421131 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_folder add column if not exists uid_validity integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/1745245997361-AlterUser.ts b/backend/src/database/migrations/1745245997361-AlterUser.ts new file mode 100644 index 0000000..2e9f07e --- /dev/null +++ b/backend/src/database/migrations/1745245997361-AlterUser.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterUser1745245997361 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users add column is_platform_admin boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1667672722508-AddAccount.ts b/backend/src/database/migrations/archive/1667672722508-AddAccount.ts new file mode 100644 index 0000000..0ad257e --- /dev/null +++ b/backend/src/database/migrations/archive/1667672722508-AddAccount.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAccount1667672722508 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table account + ( + id integer not null + constraint accounts_pkey primary key, + company_name varchar(255) not null, + subdomain varchar(255) not null + constraint accounts_subdomain_key unique, + created_at timestamp not null + ); + `); + queryRunner.query(`create sequence account_id_seq as integer minvalue 11023201;`); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1667743637921-AddUser.ts b/backend/src/database/migrations/archive/1667743637921-AddUser.ts new file mode 100644 index 0000000..0bc159e --- /dev/null +++ b/backend/src/database/migrations/archive/1667743637921-AddUser.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUser1667743637921 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table users + ( + id integer not null + primary key, + name varchar(255) not null, + email varchar(254) not null + unique, + password varchar(100) not null, + created_at timestamp not null + ); + `); + queryRunner.query(`create sequence user_id_seq as integer minvalue 12022001;`); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1667755763616-AddUserAccount.ts b/backend/src/database/migrations/archive/1667755763616-AddUserAccount.ts new file mode 100644 index 0000000..3bb8980 --- /dev/null +++ b/backend/src/database/migrations/archive/1667755763616-AddUserAccount.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserAccount1667755763616 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table user_account + ( + id integer not null + primary key, + user_id integer not null + references users on delete cascade, + account_id integer not null + references account on delete cascade, + role varchar(100) not null, + created_at timestamp not null, + constraint user_account__user_id__account_id__uniq + unique (user_id, account_id) + ); + `); + queryRunner.query(`create sequence user_account_id_seq as integer minvalue 12022001;`); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668609239887-AddEntityType.ts b/backend/src/database/migrations/archive/1668609239887-AddEntityType.ts new file mode 100644 index 0000000..9ce8022 --- /dev/null +++ b/backend/src/database/migrations/archive/1668609239887-AddEntityType.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntityType1668609239887 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table entity_type + ( + id integer not null + primary key, + name varchar(255) not null, + card_view varchar(50) not null, + section_name varchar(100) not null, + section_view varchar(50) not null, + section_icon varchar(50) not null, + account_id integer not null + references account on delete cascade, + created_at timestamp not null + ); + `); + queryRunner.query(`create sequence entity_type_id_seq as integer minvalue 13022001;`); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668679480636-AddBoard.ts b/backend/src/database/migrations/archive/1668679480636-AddBoard.ts new file mode 100644 index 0000000..33e1fd9 --- /dev/null +++ b/backend/src/database/migrations/archive/1668679480636-AddBoard.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddBoard1668679480636 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table board + ( + id integer not null + primary key, + name varchar(100) not null, + type varchar(50) not null, + record_id bigint not null, + account_id integer not null + references account on delete cascade, + created_at timestamp not null + ); + `); + queryRunner.query(`create sequence board_id_seq as integer minvalue 14022001;`); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668679994594-Note.ts b/backend/src/database/migrations/archive/1668679994594-Note.ts new file mode 100644 index 0000000..fce5976 --- /dev/null +++ b/backend/src/database/migrations/archive/1668679994594-Note.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class Note1668679994594 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table note ( + id bigint, + created_at timestamp without time zone not null, + text character varying not null, + entity_id bigint not null, + created_by integer not null, + account_id integer not null, + primary key (id), + foreign key (created_by) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + create sequence note_id_seq as bigint minvalue 22022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668680006964-AddStage.ts b/backend/src/database/migrations/archive/1668680006964-AddStage.ts new file mode 100644 index 0000000..e06f1e5 --- /dev/null +++ b/backend/src/database/migrations/archive/1668680006964-AddStage.ts @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddStage1668680006964 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table stage + ( + id integer not null + primary key, + name varchar(100) not null, + color varchar(50) not null, + code varchar(50), + is_system boolean not null, + sort_order smallint not null, + board_id integer not null + references board on delete cascade, + account_id integer not null + references account on delete cascade, + created_at timestamp not null + ); + `); + queryRunner.query(`create sequence stage_id_seq as integer minvalue 15022001;`); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668693976313-FeedsView.ts b/backend/src/database/migrations/archive/1668693976313-FeedsView.ts new file mode 100644 index 0000000..5af0f30 --- /dev/null +++ b/backend/src/database/migrations/archive/1668693976313-FeedsView.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FeedsView1668693976313 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create or replace view feed_items (id, created_at, entity_id, type) as + select id, created_at, entity_id, 'note' as type from note + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668759141152-AddEntity.ts b/backend/src/database/migrations/archive/1668759141152-AddEntity.ts new file mode 100644 index 0000000..99d56d7 --- /dev/null +++ b/backend/src/database/migrations/archive/1668759141152-AddEntity.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntity1668759141152 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table entity + ( + id bigint not null + primary key, + name varchar(255) not null, + entity_type_id integer not null + references entity_type on delete cascade, + responsible_user_id integer not null + references users on delete cascade, + stage_id integer + references stage on delete cascade, + created_by integer not null + references users on delete cascade, + account_id integer not null + references account on delete cascade, + created_at timestamp without time zone not null + ); + create sequence entity_id_seq as integer minvalue 14022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668769413399-AddFeedItemsSequence.ts b/backend/src/database/migrations/archive/1668769413399-AddFeedItemsSequence.ts new file mode 100644 index 0000000..1e457a1 --- /dev/null +++ b/backend/src/database/migrations/archive/1668769413399-AddFeedItemsSequence.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFeedItemsSequence1668769413399 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop sequence note_id_seq; + create sequence feed_item_id_seq as bigint minvalue 22022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668770161278-AddTaskType.ts b/backend/src/database/migrations/archive/1668770161278-AddTaskType.ts new file mode 100644 index 0000000..481ed3a --- /dev/null +++ b/backend/src/database/migrations/archive/1668770161278-AddTaskType.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskType1668770161278 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table task_type ( + id integer, + created_at timestamp without time zone not null, + account_id integer not null, + name character varying(128) not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence task_type_id_seq as bigint minvalue 25022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1668957885188-AddSortOrderToBoard.ts b/backend/src/database/migrations/archive/1668957885188-AddSortOrderToBoard.ts new file mode 100644 index 0000000..c5ff510 --- /dev/null +++ b/backend/src/database/migrations/archive/1668957885188-AddSortOrderToBoard.ts @@ -0,0 +1,11 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSortOrderToBoard1668957885188 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('alter table board add column sort_order smallint not null default 0;'); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669035936818-AddActivity.ts b/backend/src/database/migrations/archive/1669035936818-AddActivity.ts new file mode 100644 index 0000000..33624d2 --- /dev/null +++ b/backend/src/database/migrations/archive/1669035936818-AddActivity.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddActivity1669035936818 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table activity ( + id bigint, + created_at timestamp without time zone not null, + account_id integer not null, + created_by integer not null, + responsible_user_id integer not null, + text character varying not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + is_resolved boolean not null, + result character varying, + task_type_id integer not null, + entity_id bigint not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (created_by) references users(id) on delete cascade, + foreign key (responsible_user_id) references users(id) on delete cascade, + foreign key (entity_id) references entity(id), + foreign key (task_type_id) references task_type(id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669035943607-AddTask.ts b/backend/src/database/migrations/archive/1669035943607-AddTask.ts new file mode 100644 index 0000000..5ca862c --- /dev/null +++ b/backend/src/database/migrations/archive/1669035943607-AddTask.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTask1669035943607 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table task ( + id bigint, + created_at timestamp without time zone not null, + account_id integer not null, + created_by integer not null, + responsible_user_id integer not null, + text character varying not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + is_resolved boolean not null, + result character varying, + title character varying not null, + entity_id bigint, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (created_by) references users(id) on delete cascade, + foreign key (responsible_user_id) references users(id) on delete cascade, + foreign key (entity_id) references entity(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669126197933-AddFieldGroup.ts b/backend/src/database/migrations/archive/1669126197933-AddFieldGroup.ts new file mode 100644 index 0000000..69dad3c --- /dev/null +++ b/backend/src/database/migrations/archive/1669126197933-AddFieldGroup.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFieldGroup1669126197933 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table field_group + ( + id integer not null + primary key, + name varchar(100) not null, + sort_order smallint not null, + entity_type_id integer not null + references entity_type (id) on delete cascade, + account_id integer not null + references account (id) on delete cascade, + created_at timestamp without time zone not null + ); + create sequence field_group_id_seq as integer minvalue 41022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669127718002-AddField.ts b/backend/src/database/migrations/archive/1669127718002-AddField.ts new file mode 100644 index 0000000..d95ceb9 --- /dev/null +++ b/backend/src/database/migrations/archive/1669127718002-AddField.ts @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddField1669127718002 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table field + ( + id integer not null + primary key, + name varchar(100) not null, + type varchar(50) not null, + sort_order smallint not null, + entity_type_id integer not null + references entity_type (id) on delete cascade, + field_group_id integer not null + references field_group (id) on delete cascade, + account_id integer not null + references account (id) on delete cascade, + created_at timestamp without time zone not null + ); + create sequence field_id_seq as integer minvalue 42022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669130700529-AddTaskToFeed.ts b/backend/src/database/migrations/archive/1669130700529-AddTaskToFeed.ts new file mode 100644 index 0000000..52c3cc4 --- /dev/null +++ b/backend/src/database/migrations/archive/1669130700529-AddTaskToFeed.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskToFeed1669130700529 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create or replace view feed_items (id, created_at, entity_id, type) as + select id, created_at, entity_id, 'note' as type from note + union + select id, created_at, entity_id, 'task' as type from task + union + select id, created_at, entity_id, 'activity' as type from activity + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669192730867-AddNoteToEntityFK.ts b/backend/src/database/migrations/archive/1669192730867-AddNoteToEntityFK.ts new file mode 100644 index 0000000..31bf313 --- /dev/null +++ b/backend/src/database/migrations/archive/1669192730867-AddNoteToEntityFK.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddNoteToEntityFK1669192730867 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table note add foreign key (entity_id) references entity(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669197308978-AddFieldOption.ts b/backend/src/database/migrations/archive/1669197308978-AddFieldOption.ts new file mode 100644 index 0000000..bed26dc --- /dev/null +++ b/backend/src/database/migrations/archive/1669197308978-AddFieldOption.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFieldOption1669197308978 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table field_option + ( + id integer not null + primary key, + label varchar(255) not null, + sort_order smallint not null, + field_id integer not null + references field (id) on delete cascade, + account_id integer not null + references account (id) on delete cascade, + created_at timestamp without time zone not null + ); + create sequence field_option_id_seq as integer minvalue 43022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669210346088-AddTasksView.ts b/backend/src/database/migrations/archive/1669210346088-AddTasksView.ts new file mode 100644 index 0000000..b144499 --- /dev/null +++ b/backend/src/database/migrations/archive/1669210346088-AddTasksView.ts @@ -0,0 +1,18 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTasksView1669210346088 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create or replace view all_tasks (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, task_type_id, title, type) as + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, task_type_id, null as title, 'activity' as type from activity + union + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, null as task_type_id, title, 'task' as type from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669279080953-FixActivityConstraints.ts b/backend/src/database/migrations/archive/1669279080953-FixActivityConstraints.ts new file mode 100644 index 0000000..8ac0d9e --- /dev/null +++ b/backend/src/database/migrations/archive/1669279080953-FixActivityConstraints.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixActivityConstraints1669279080953 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table activity drop constraint activity_entity_id_fkey; + alter table activity + add foreign key (entity_id) references entity (id) + on delete cascade; + + alter table activity drop constraint activity_task_type_id_fkey; + alter table activity + add foreign key (task_type_id) references task_type (id) + on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669370353662-SplitUserName.ts b/backend/src/database/migrations/archive/1669370353662-SplitUserName.ts new file mode 100644 index 0000000..f04e6c5 --- /dev/null +++ b/backend/src/database/migrations/archive/1669370353662-SplitUserName.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SplitUserName1669370353662 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users + add column first_name character varying(255), + add column last_name character varying(255); + + update users set first_name = split_part(name, ' ', 1), last_name = split_part(name, ' ', 2); + + alter table users + alter column first_name set not null, + alter column last_name set not null; + + alter table users drop column name; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669372200327-AddUserPhone.ts b/backend/src/database/migrations/archive/1669372200327-AddUserPhone.ts new file mode 100644 index 0000000..cee870e --- /dev/null +++ b/backend/src/database/migrations/archive/1669372200327-AddUserPhone.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserPhone1669372200327 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users + add column phone character varying(32); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669535731810-UserProfile.ts b/backend/src/database/migrations/archive/1669535731810-UserProfile.ts new file mode 100644 index 0000000..476c8a7 --- /dev/null +++ b/backend/src/database/migrations/archive/1669535731810-UserProfile.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserProfile1669535731810 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table user_profile ( + created_at timestamp without time zone not null, + user_id integer not null, + birth_date timestamp without time zone, + employment_date timestamp without time zone, + account_id integer not null, + primary key (user_id), + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669545773479-EntityTypeLink.ts b/backend/src/database/migrations/archive/1669545773479-EntityTypeLink.ts new file mode 100644 index 0000000..01c1f25 --- /dev/null +++ b/backend/src/database/migrations/archive/1669545773479-EntityTypeLink.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityTypeLink1669545773479 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence entity_type_link_id_seq as bigint; + + create table entity_type_link ( + id bigint default nextval('entity_type_link_id_seq'::regclass), + source_id integer not null, + target_id integer not null, + sort_order smallint not null, + account_id integer not null, + primary key (id), + foreign key (source_id) references entity_type(id) on delete cascade, + foreign key (target_id) references entity_type(id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669565827895-EntityLink.ts b/backend/src/database/migrations/archive/1669565827895-EntityLink.ts new file mode 100644 index 0000000..de0f951 --- /dev/null +++ b/backend/src/database/migrations/archive/1669565827895-EntityLink.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityLink1669565827895 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence entity_link_id_seq as bigint; + + create table entity_link ( + id bigint, + source_id integer not null, + target_id integer not null, + sort_order smallint not null, + back_link_id bigint, + account_id integer not null, + primary key (id), + foreign key (source_id) references entity(id) on delete cascade, + foreign key (target_id) references entity(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + foreign key (back_link_id) references entity_link(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669629567395-RemoveUserAccount.ts b/backend/src/database/migrations/archive/1669629567395-RemoveUserAccount.ts new file mode 100644 index 0000000..5ef4f43 --- /dev/null +++ b/backend/src/database/migrations/archive/1669629567395-RemoveUserAccount.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveUserAccount1669629567395 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users + add column account_id integer, + add column role character varying(100), + add column avatar_url character varying(256), + add foreign key (account_id) references account(id) on delete cascade; + + update users set (account_id, role) = + (select account_id, role from user_account where user_account.user_id = users.id); + + alter table users + alter column account_id set not null, + alter column role set not null; + + alter table users + alter column last_name drop not null; + + drop table user_account; + drop sequence user_account_id_seq; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669706986248-AddFeature.ts b/backend/src/database/migrations/archive/1669706986248-AddFeature.ts new file mode 100644 index 0000000..8c5744e --- /dev/null +++ b/backend/src/database/migrations/archive/1669706986248-AddFeature.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFeature1669706986248 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table feature ( + id smallint generated by default as identity, + name character varying not null, + code character varying not null, + is_enabled boolean not null, + primary key (id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669707814502-AddEntityTypeToFeature.ts b/backend/src/database/migrations/archive/1669707814502-AddEntityTypeToFeature.ts new file mode 100644 index 0000000..3fc0fe6 --- /dev/null +++ b/backend/src/database/migrations/archive/1669707814502-AddEntityTypeToFeature.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntityTypeToFeature1669707814502 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table entity_type_feature ( + entity_type_id integer not null, + feature_id smallint not null, + primary key (entity_type_id, feature_id), + foreign key (entity_type_id) references entity_type(id) on delete cascade, + foreign key (feature_id) references feature(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669711256398-AddDefaultFeatures.ts b/backend/src/database/migrations/archive/1669711256398-AddDefaultFeatures.ts new file mode 100644 index 0000000..68177d7 --- /dev/null +++ b/backend/src/database/migrations/archive/1669711256398-AddDefaultFeatures.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDefaultFeatures1669711256398 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into feature(name, code, is_enabled) values('Activities', 'activities', true); + insert into feature(name, code, is_enabled) values('Tasks', 'tasks', true); + insert into feature(name, code, is_enabled) values('Comments', 'comments', false); + insert into feature(name, code, is_enabled) values('Chat', 'chat', false); + insert into feature(name, code, is_enabled) values('File storage', 'saveFiles', false); + insert into feature(name, code, is_enabled) values('File storage disk', 'diskForFiles', false); + insert into feature(name, code, is_enabled) values('Avatar', 'avatar', false); + insert into feature(name, code, is_enabled) values('Photo/pictures (several)', 'photos', false); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669732421801-AddFieldValue.ts b/backend/src/database/migrations/archive/1669732421801-AddFieldValue.ts new file mode 100644 index 0000000..32eaa12 --- /dev/null +++ b/backend/src/database/migrations/archive/1669732421801-AddFieldValue.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFieldValue1669732421801 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table field_value + ( + id bigint not null, + field_id integer not null, + payload jsonb not null, + entity_id bigint not null, + account_id integer not null, + primary key (id), + foreign key (field_id) references field (id) on delete cascade, + foreign key (entity_id) references entity (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade, + constraint field_id__entity_id__uniq + unique (field_id, entity_id) + ); + create sequence field_value_id_seq as bigint minvalue 44022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669799906268-FixEntityLinkColumns.ts b/backend/src/database/migrations/archive/1669799906268-FixEntityLinkColumns.ts new file mode 100644 index 0000000..2635ce8 --- /dev/null +++ b/backend/src/database/migrations/archive/1669799906268-FixEntityLinkColumns.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixEntityLinkColumns1669799906268 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + queryRunner.query(` + alter table entity_link + alter column source_id type bigint using source_id::bigint, + alter column target_id type bigint using target_id::bigint + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669880194355-ChangeFeatureName_Notes.ts b/backend/src/database/migrations/archive/1669880194355-ChangeFeatureName_Notes.ts new file mode 100644 index 0000000..e955d72 --- /dev/null +++ b/backend/src/database/migrations/archive/1669880194355-ChangeFeatureName_Notes.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChangeFeatureNameNotes1669880194355 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update feature set name = 'Notes', code = 'notes', is_enabled = true where code = 'comments'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669966161211-BigintToInteger.ts b/backend/src/database/migrations/archive/1669966161211-BigintToInteger.ts new file mode 100644 index 0000000..4994fb0 --- /dev/null +++ b/backend/src/database/migrations/archive/1669966161211-BigintToInteger.ts @@ -0,0 +1,43 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class BigintToInteger1669966161211 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + drop view feed_items; + + alter sequence feed_item_id_seq as integer; + alter sequence task_type_id_seq as integer; + alter sequence entity_link_id_seq as integer; + alter sequence entity_type_link_id_seq as integer; + alter sequence field_value_id_seq as integer; + + alter table activity alter column id type integer; + alter table entity alter column id type integer; + alter table entity_link alter column id type integer; + alter table entity_type_link alter column id type integer; + alter table field_value alter column id type integer; + alter table note alter column id type integer; + alter table task alter column id type integer; + + create or replace view all_tasks (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, task_type_id, title, type) as + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, task_type_id, null as title, 'activity' as type from activity + union + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, null as task_type_id, title, 'task' as type from task + order by id; + + create or replace view feed_items (id, created_at, entity_id, type) as + select id, created_at, entity_id, 'note' as type from note + union + select id, created_at, entity_id, 'task' as type from task + union + select id, created_at, entity_id, 'activity' as type from activity + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669973222725-ForeignKeyToInteger.ts b/backend/src/database/migrations/archive/1669973222725-ForeignKeyToInteger.ts new file mode 100644 index 0000000..b9925a0 --- /dev/null +++ b/backend/src/database/migrations/archive/1669973222725-ForeignKeyToInteger.ts @@ -0,0 +1,38 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ForeignKeyToInteger1669973222725 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + drop view feed_items; + + alter table activity alter column entity_id type integer; + alter table board alter column record_id type integer; + alter table entity_link alter column source_id type integer; + alter table entity_link alter column target_id type integer; + alter table entity_link alter column back_link_id type integer; + alter table field_value alter column entity_id type integer; + alter table note alter column entity_id type integer; + alter table task alter column entity_id type integer; + + create or replace view all_tasks (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, task_type_id, title, type) as + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, task_type_id, null as title, 'activity' as type from activity + union + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, null as task_type_id, title, 'task' as type from task + order by id; + + create or replace view feed_items (id, created_at, entity_id, type) as + select id, created_at, entity_id, 'note' as type from note + union + select id, created_at, entity_id, 'task' as type from task + union + select id, created_at, entity_id, 'activity' as type from activity + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1669991106433-AddFieldTypeColumnToFieldValue.ts b/backend/src/database/migrations/archive/1669991106433-AddFieldTypeColumnToFieldValue.ts new file mode 100644 index 0000000..b6cc8a9 --- /dev/null +++ b/backend/src/database/migrations/archive/1669991106433-AddFieldTypeColumnToFieldValue.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFieldTypeColumnToFieldValue1669991106433 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field_value + add column field_type varchar(50); + + update field_value + set (field_type) = (select type from field where field.id = field_value.field_id); + + alter table field_value + alter column field_type set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1670576080218-AddExternalEntity.ts b/backend/src/database/migrations/archive/1670576080218-AddExternalEntity.ts new file mode 100644 index 0000000..53acce2 --- /dev/null +++ b/backend/src/database/migrations/archive/1670576080218-AddExternalEntity.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddExternalEntity1670576080218 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists external_entity_id_seq as bigint minvalue 30022001; + + create table external_entity ( + id integer DEFAULT nextval('external_entity_id_seq'::regclass), + entity_id integer NOT NULL, + url character varying NOT NULL, + account_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + primary key (id), + foreign key (entity_id) references entity(id), + foreign key (account_id) references account(id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1670840721696-ExternalSystem.ts b/backend/src/database/migrations/archive/1670840721696-ExternalSystem.ts new file mode 100644 index 0000000..f9959f9 --- /dev/null +++ b/backend/src/database/migrations/archive/1670840721696-ExternalSystem.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ExternalSystem1670840721696 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table external_system ( + id smallint generated by default as identity, + name character varying not null, + code character varying not null, + url_template character varying not null, + primary key (id) + ); + + alter table external_entity + add column system smallint, + add foreign key (system) references external_system(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1670846440118-AddExternalSystems.ts b/backend/src/database/migrations/archive/1670846440118-AddExternalSystems.ts new file mode 100644 index 0000000..201b2f3 --- /dev/null +++ b/backend/src/database/migrations/archive/1670846440118-AddExternalSystems.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddExternalSystems1670846440118 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into external_system(name, code, url_template) values('SalesForce', 'salesforce', 'salesforce.com'); + insert into external_system(name, code, url_template) values('LinkedIn', 'linkedin', 'www.linkedin.com'); + insert into external_system(name, code, url_template) values('Facebook', 'facebook', 'facebook.com'); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1670936878487-AddFileInfo.ts b/backend/src/database/migrations/archive/1670936878487-AddFileInfo.ts new file mode 100644 index 0000000..fae06e6 --- /dev/null +++ b/backend/src/database/migrations/archive/1670936878487-AddFileInfo.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFileInfo1670936878487 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists file_info_seq as integer minvalue 0; + + create table file_info ( + id integer default nextval('file_info_seq'::regclass), + account_id integer not null, + created_at timestamp without time zone not null, + created_by integer not null, + key character varying not null, + original_name character varying not null, + mime_type character varying not null, + size integer not null, + primary key (id), + foreign key (account_id) references account(id), + foreign key (created_by) references users(id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1670939571185-AddFileStorePath.ts b/backend/src/database/migrations/archive/1670939571185-AddFileStorePath.ts new file mode 100644 index 0000000..453e4b6 --- /dev/null +++ b/backend/src/database/migrations/archive/1670939571185-AddFileStorePath.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFileStorePath1670939571185 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info add column store_path character varying not null; + alter table file_info add column hash_md5 character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671006792499-AddStageIdToTask.ts b/backend/src/database/migrations/archive/1671006792499-AddStageIdToTask.ts new file mode 100644 index 0000000..f7b9bd4 --- /dev/null +++ b/backend/src/database/migrations/archive/1671006792499-AddStageIdToTask.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddStageIdToTask1671006792499 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task + add column stage_id integer, + add foreign key(stage_id) references stage(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671030345135-ChangeFileInfoIdType.ts b/backend/src/database/migrations/archive/1671030345135-ChangeFileInfoIdType.ts new file mode 100644 index 0000000..0319ac0 --- /dev/null +++ b/backend/src/database/migrations/archive/1671030345135-ChangeFileInfoIdType.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChangeFileInfoIdType1671030345135 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from file_info; + drop table file_info; + + drop sequence file_info_seq; + + create table file_info ( + id uuid default gen_random_uuid(), + account_id integer not null, + created_at timestamp without time zone not null, + created_by integer not null, + key character varying not null, + original_name character varying not null, + mime_type character varying not null, + size integer not null, + hash_md5 character varying not null, + store_path character varying not null, + primary key (id), + foreign key (account_id) references account(id), + foreign key (created_by) references users(id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671089754889-AddEntityTypeSortOrder.ts b/backend/src/database/migrations/archive/1671089754889-AddEntityTypeSortOrder.ts new file mode 100644 index 0000000..6e7395a --- /dev/null +++ b/backend/src/database/migrations/archive/1671089754889-AddEntityTypeSortOrder.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntityTypeSortOrder1671089754889 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity_type add column sort_order smallint not null default 0; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671096971264-RenameTaskType.ts b/backend/src/database/migrations/archive/1671096971264-RenameTaskType.ts new file mode 100644 index 0000000..69a15d7 --- /dev/null +++ b/backend/src/database/migrations/archive/1671096971264-RenameTaskType.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameTaskType1671096971264 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter sequence task_type_id_seq rename to activity_type_id_seq; + alter table task_type rename to activity_type; + alter table activity rename column task_type_id to activity_type_id; + drop view all_tasks; + create or replace view all_tasks (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, activity_type_id, title, type) as + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, activity_type_id, null as title, 'activity' as type from activity + union + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, null as activity_type_id, title, 'task' as type from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671108685879-NoteFiles.ts b/backend/src/database/migrations/archive/1671108685879-NoteFiles.ts new file mode 100644 index 0000000..47fcaab --- /dev/null +++ b/backend/src/database/migrations/archive/1671108685879-NoteFiles.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NoteFiles1671108685879 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table note_file ( + note_id integer NOT NULL, + file_info_id uuid NOT NULL, + primary key (note_id, file_info_id), + foreign key (note_id) references note(id), + foreign key (file_info_id) references file_info(id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671196063822-FileInfoCascade.ts b/backend/src/database/migrations/archive/1671196063822-FileInfoCascade.ts new file mode 100644 index 0000000..3467a1b --- /dev/null +++ b/backend/src/database/migrations/archive/1671196063822-FileInfoCascade.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FileInfoCascade1671196063822 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info + drop constraint file_info_account_id_fkey, + add constraint file_info_account_id_fkey foreign key (account_id) references account(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671196394406-NoteFileRemoveFileKey.ts b/backend/src/database/migrations/archive/1671196394406-NoteFileRemoveFileKey.ts new file mode 100644 index 0000000..7bb2cb9 --- /dev/null +++ b/backend/src/database/migrations/archive/1671196394406-NoteFileRemoveFileKey.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NoteFileRemoveFileKey1671196394406 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table note_file drop constraint note_file_file_info_id_fkey; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671203263368-TaskAndActivityFiles.ts b/backend/src/database/migrations/archive/1671203263368-TaskAndActivityFiles.ts new file mode 100644 index 0000000..eeb1ab1 --- /dev/null +++ b/backend/src/database/migrations/archive/1671203263368-TaskAndActivityFiles.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TaskAndActivityFiles1671203263368 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table file_link ( + id integer generated by default as identity, + account_id integer not null, + source_type character varying not null, + source_id integer not null, + file_id uuid not null, + file_name character varying not null, + file_size integer not null, + file_type character varying not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671438176416-RemoveKeyFromFileInfo.ts b/backend/src/database/migrations/archive/1671438176416-RemoveKeyFromFileInfo.ts new file mode 100644 index 0000000..77e4150 --- /dev/null +++ b/backend/src/database/migrations/archive/1671438176416-RemoveKeyFromFileInfo.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveKeyFromFileInfo1671438176416 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info drop column key; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671439005617-AddIsUsedToFileInfo.ts b/backend/src/database/migrations/archive/1671439005617-AddIsUsedToFileInfo.ts new file mode 100644 index 0000000..13bfce9 --- /dev/null +++ b/backend/src/database/migrations/archive/1671439005617-AddIsUsedToFileInfo.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIsUsedToFileInfo1671439005617 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info add column is_used boolean default false not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671440838639-DropNoteFileLink.ts b/backend/src/database/migrations/archive/1671440838639-DropNoteFileLink.ts new file mode 100644 index 0000000..149f0fc --- /dev/null +++ b/backend/src/database/migrations/archive/1671440838639-DropNoteFileLink.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DropNoteFileLink1671440838639 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table note_file; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671521628139-BoardRecordIdNull.ts b/backend/src/database/migrations/archive/1671521628139-BoardRecordIdNull.ts new file mode 100644 index 0000000..6502ec4 --- /dev/null +++ b/backend/src/database/migrations/archive/1671521628139-BoardRecordIdNull.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class BoardRecordIdNull1671521628139 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board alter column record_id drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671533557027-UserActive.ts b/backend/src/database/migrations/archive/1671533557027-UserActive.ts new file mode 100644 index 0000000..5079b2a --- /dev/null +++ b/backend/src/database/migrations/archive/1671533557027-UserActive.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserActive1671533557027 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users add column is_active boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671619787598-UserObjectPermission.ts b/backend/src/database/migrations/archive/1671619787598-UserObjectPermission.ts new file mode 100644 index 0000000..e2817ac --- /dev/null +++ b/backend/src/database/migrations/archive/1671619787598-UserObjectPermission.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserObjectPermission1671619787598 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table object_permission ( + id integer generated by default as identity, + account_id integer not null, + created_at timestamp without time zone not null, + object_type character varying not null, + object_id integer, + create_permission character varying not null, + view_permission character varying not null, + edit_permission character varying not null, + delete_permission character varying not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + create table user_object_permission ( + user_id integer not null, + object_permission_id integer not null, + primary key (user_id, object_permission_id), + foreign key (user_id) references users(id) on delete cascade, + foreign key (object_permission_id) references object_permission(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1671914250074-ChangeCardView.ts b/backend/src/database/migrations/archive/1671914250074-ChangeCardView.ts new file mode 100644 index 0000000..e80ebea --- /dev/null +++ b/backend/src/database/migrations/archive/1671914250074-ChangeCardView.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChangeCardView1671914250074 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + queryRunner.query(` + update entity_type set card_view = 'deal' + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672064100473-EntityIdCascade.ts b/backend/src/database/migrations/archive/1672064100473-EntityIdCascade.ts new file mode 100644 index 0000000..7fd22b9 --- /dev/null +++ b/backend/src/database/migrations/archive/1672064100473-EntityIdCascade.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityIdCascade1672064100473 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table external_entity + drop constraint external_entity_entity_id_fkey, + add constraint external_entity_entity_id_fkey foreign key (entity_id) references entity(id) + on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672154773200-AddSalesforceSettings.ts b/backend/src/database/migrations/archive/1672154773200-AddSalesforceSettings.ts new file mode 100644 index 0000000..25deffe --- /dev/null +++ b/backend/src/database/migrations/archive/1672154773200-AddSalesforceSettings.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSalesforceSettings1672154773200 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table salesforce_settings ( + id uuid default gen_random_uuid(), + account_id integer not null, + created_at timestamp without time zone not null, + domain character varying not null, + key character varying not null, + secret character varying not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672224404851-AddRefreshTokenToSalesforce.ts b/backend/src/database/migrations/archive/1672224404851-AddRefreshTokenToSalesforce.ts new file mode 100644 index 0000000..9aa3d86 --- /dev/null +++ b/backend/src/database/migrations/archive/1672224404851-AddRefreshTokenToSalesforce.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRefreshTokenToSalesforce1672224404851 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table salesforce_settings add column refresh_token character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672242451242-ExternalSystemUrlTemplates.ts b/backend/src/database/migrations/archive/1672242451242-ExternalSystemUrlTemplates.ts new file mode 100644 index 0000000..808d5ba --- /dev/null +++ b/backend/src/database/migrations/archive/1672242451242-ExternalSystemUrlTemplates.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ExternalSystemUrlTemplates1672242451242 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table external_system add column url_templates character varying[]; + + update external_system set url_templates='{salesforce.com,force.com}' where id=1; + update external_system set url_templates='{linkedin.com}' where id=2; + update external_system set url_templates='{facebook.com, fb.com}' where id=3; + + alter table external_system alter column url_templates set not null; + + alter table external_system drop column url_template; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672306523845-AddDataToExternalEntity.ts b/backend/src/database/migrations/archive/1672306523845-AddDataToExternalEntity.ts new file mode 100644 index 0000000..0b8bed2 --- /dev/null +++ b/backend/src/database/migrations/archive/1672306523845-AddDataToExternalEntity.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDataToExternalEntity1672306523845 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table external_entity add column data jsonb; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672386954959-AddUIDataToExternalEntity.ts b/backend/src/database/migrations/archive/1672386954959-AddUIDataToExternalEntity.ts new file mode 100644 index 0000000..d6a6826 --- /dev/null +++ b/backend/src/database/migrations/archive/1672386954959-AddUIDataToExternalEntity.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUIDataToExternalEntity1672386954959 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table external_entity rename column data to raw_data; + alter table external_entity add column ui_data jsonb; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1672928537580-CascadeStageIdInTask.ts b/backend/src/database/migrations/archive/1672928537580-CascadeStageIdInTask.ts new file mode 100644 index 0000000..fe90256 --- /dev/null +++ b/backend/src/database/migrations/archive/1672928537580-CascadeStageIdInTask.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class CascadeStageIdInTask1672928537580 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task + drop constraint task_stage_id_fkey; + + alter table task + add foreign key (stage_id) references stage (id) + on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673251726994-AddStageToAllTasks.ts b/backend/src/database/migrations/archive/1673251726994-AddStageToAllTasks.ts new file mode 100644 index 0000000..7e0ce94 --- /dev/null +++ b/backend/src/database/migrations/archive/1673251726994-AddStageToAllTasks.ts @@ -0,0 +1,19 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddStageToAllTasks1673251726994 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + create or replace view all_tasks (id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, activity_type_id, title, stage_id, type) as + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, activity_type_id, null as title, null as stage_id, 'activity' as type from activity + union + select id, created_at, account_id, created_by, responsible_user_id, text, start_date, end_date, is_resolved, result, entity_id, null as activity_type_id, title, stage_id, 'task' as type from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673339773307-EnableFileStorageFeature.ts b/backend/src/database/migrations/archive/1673339773307-EnableFileStorageFeature.ts new file mode 100644 index 0000000..e8af2a6 --- /dev/null +++ b/backend/src/database/migrations/archive/1673339773307-EnableFileStorageFeature.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EnableFileStorageFeature1673339773307 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update feature set is_enabled=true where code='saveFiles'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673417366210-RenameCardViewToEntityCategory.ts b/backend/src/database/migrations/archive/1673417366210-RenameCardViewToEntityCategory.ts new file mode 100644 index 0000000..44df32d --- /dev/null +++ b/backend/src/database/migrations/archive/1673417366210-RenameCardViewToEntityCategory.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameCardViewToEntityCategory1673417366210 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity_type rename column card_view to entity_category; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673447442894-AddSubscription.ts b/backend/src/database/migrations/archive/1673447442894-AddSubscription.ts new file mode 100644 index 0000000..7029d14 --- /dev/null +++ b/backend/src/database/migrations/archive/1673447442894-AddSubscription.ts @@ -0,0 +1,26 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSubscription1673447442894 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table subscription ( + id integer generated by default as identity, + account_id integer not null, + created_at timestamp without time zone not null, + expired_at timestamp without time zone, + is_trial boolean not null, + user_limit integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + insert into subscription(account_id, created_at, expired_at, is_trial, user_limit) + select id as account_id, created_at, created_at + INTERVAL '14 day' as expired_at, true as is_trial, 5 as user_limit from account; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673514341599-AddCreatedAtToFileLink.ts b/backend/src/database/migrations/archive/1673514341599-AddCreatedAtToFileLink.ts new file mode 100644 index 0000000..3674849 --- /dev/null +++ b/backend/src/database/migrations/archive/1673514341599-AddCreatedAtToFileLink.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddCreatedAtToFileLink1673514341599 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_link add column created_at timestamp without time zone; + update file_link set created_at = file_info.created_at from file_info where file_link.file_id = file_info.id; + alter table file_link alter column created_at set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673515827427-AddSequenceToSubscriptionId.ts b/backend/src/database/migrations/archive/1673515827427-AddSequenceToSubscriptionId.ts new file mode 100644 index 0000000..90f2938 --- /dev/null +++ b/backend/src/database/migrations/archive/1673515827427-AddSequenceToSubscriptionId.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSequenceToSubscriptionId1673515827427 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subscription alter column id drop identity; + drop sequence if exists subscription_id_seq; + create sequence if not exists subscription_id_seq as integer minvalue 16022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1673969196807-AddMailboxAndProviderSettings.ts b/backend/src/database/migrations/archive/1673969196807-AddMailboxAndProviderSettings.ts new file mode 100644 index 0000000..fa3b145 --- /dev/null +++ b/backend/src/database/migrations/archive/1673969196807-AddMailboxAndProviderSettings.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxAndProviderSettings1673969196807 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists mailbox_id_seq as integer minvalue 27023001; + + create table mailbox ( + id integer, + account_id integer not null, + created_at timestamp without time zone not null, + email character varying not null, + provider character varying not null, + type character varying not null, + owner_id integer, + group_message boolean not null, + create_contact boolean not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (owner_id) references users(id) + ); + + create table mailbox_settings_gmail ( + mailbox_id integer, + account_id integer not null, + access_token character varying not null, + refresh_token character varying not null, + primary key (mailbox_id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create table mailbox_settings_manual ( + "mailbox_id" integer, + "account_id" integer not null, + "password" character varying not null, + "imap_server" character varying not null, + "imap_port" smallint not null, + "imap_secure" boolean not null, + "smtp_server" character varying not null, + "smtp_port" smallint not null, + "smtp_secure" boolean not null, + primary key (mailbox_id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674130775550-AddMailboxState.ts b/backend/src/database/migrations/archive/1674130775550-AddMailboxState.ts new file mode 100644 index 0000000..ec465c8 --- /dev/null +++ b/backend/src/database/migrations/archive/1674130775550-AddMailboxState.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxState1674130775550 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox add column state character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674138959333-AddPlannedTimeToTask.ts b/backend/src/database/migrations/archive/1674138959333-AddPlannedTimeToTask.ts new file mode 100644 index 0000000..93ed0ef --- /dev/null +++ b/backend/src/database/migrations/archive/1674138959333-AddPlannedTimeToTask.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddPlannedTimeToTask1674138959333 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task + add column planned_time integer; + + drop view all_tasks; + create + or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + activity_type_id, + title, + planned_time, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + activity_type_id, + null as title, + null as planned_time, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + null as activity_type_id, + title, + planned_time, + 'task' as type + from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674203801914-ChangeMailboxSettingsGmail.ts b/backend/src/database/migrations/archive/1674203801914-ChangeMailboxSettingsGmail.ts new file mode 100644 index 0000000..065abfc --- /dev/null +++ b/backend/src/database/migrations/archive/1674203801914-ChangeMailboxSettingsGmail.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChangeMailboxSettingsGmail1674203801914 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_settings_gmail + drop column access_token, + drop column refresh_token, + add column tokens jsonb not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674492197867-AddHistoryIdToGmailSettings.ts b/backend/src/database/migrations/archive/1674492197867-AddHistoryIdToGmailSettings.ts new file mode 100644 index 0000000..397beaf --- /dev/null +++ b/backend/src/database/migrations/archive/1674492197867-AddHistoryIdToGmailSettings.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddHistoryIdToGmailSettings1674492197867 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_settings_gmail + add column history_id character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674546589359-AddLastSyncToManualSettings.ts b/backend/src/database/migrations/archive/1674546589359-AddLastSyncToManualSettings.ts new file mode 100644 index 0000000..702b09a --- /dev/null +++ b/backend/src/database/migrations/archive/1674546589359-AddLastSyncToManualSettings.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddLastSyncToManualSettings1674546589359 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_settings_manual + add column last_sync timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674560582007-AddMailMessageBase.ts b/backend/src/database/migrations/archive/1674560582007-AddMailMessageBase.ts new file mode 100644 index 0000000..d7a6082 --- /dev/null +++ b/backend/src/database/migrations/archive/1674560582007-AddMailMessageBase.ts @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageBase1674560582007 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists mail_message_id_seq as integer minvalue 28023001; + create table mail_message ( + id integer, + mailbox_id integer not null, + external_id character varying not null, + thread_id character varying, + snippet character varying, + sent_from character varying not null, + sent_to character varying not null, + subject character varying, + date timestamp without time zone not null, + account_id integer not null, + primary key (id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists mail_message_payload_id_seq as integer minvalue 29023001; + create table mail_message_payload ( + id integer, + message_id integer not null, + mime_type character varying not null, + filename character varying, + attachment character varying, + content character varying, + account_id integer not null, + primary key (id), + foreign key (message_id) references mail_message(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists mailbox_folder_id_seq as integer minvalue 31023001; + create table mailbox_folder ( + id integer, + mailbox_id integer not null, + external_id character varying not null, + name character varying not null, + type character varying not null, + account_id integer not null, + primary key (id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create table mail_message_folder ( + message_id integer, + folder_id integer, + foreign key (message_id) references mail_message(id) on delete cascade, + foreign key (folder_id) references mailbox_folder(id) on delete cascade, + primary key (message_id, folder_id) + ); + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674572622632-AddTaskSettings.ts b/backend/src/database/migrations/archive/1674572622632-AddTaskSettings.ts new file mode 100644 index 0000000..7db77b1 --- /dev/null +++ b/backend/src/database/migrations/archive/1674572622632-AddTaskSettings.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskSettings1674572622632 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table task_settings + ( + id integer not null, + active_fields jsonb not null, + type varchar(100) not null, + record_id integer, + account_id integer, + primary key (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence task_settings_id_seq as integer minvalue 45022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674637903317-ChangeMailbox.ts b/backend/src/database/migrations/archive/1674637903317-ChangeMailbox.ts new file mode 100644 index 0000000..a50c892 --- /dev/null +++ b/backend/src/database/migrations/archive/1674637903317-ChangeMailbox.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChangeMailbox1674637903317 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + add column sync_days smallint, + drop column type + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674638261068-AddMailboxAccessibleUser.ts b/backend/src/database/migrations/archive/1674638261068-AddMailboxAccessibleUser.ts new file mode 100644 index 0000000..b7dd50a --- /dev/null +++ b/backend/src/database/migrations/archive/1674638261068-AddMailboxAccessibleUser.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxAccessibleUser1674638261068 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table mailbox_accessible_user ( + mailbox_id integer, + user_id integer, + account_id integer, + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + primary key (mailbox_id, user_id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674648741806-AddMailboxFolderInfo.ts b/backend/src/database/migrations/archive/1674648741806-AddMailboxFolderInfo.ts new file mode 100644 index 0000000..3417d8c --- /dev/null +++ b/backend/src/database/migrations/archive/1674648741806-AddMailboxFolderInfo.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxFolderInfo1674648741806 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_folder + add column messages_total integer, + add column messages_unread integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674660419230-AddMailMessageReplyTo.ts b/backend/src/database/migrations/archive/1674660419230-AddMailMessageReplyTo.ts new file mode 100644 index 0000000..3c3a224 --- /dev/null +++ b/backend/src/database/migrations/archive/1674660419230-AddMailMessageReplyTo.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageReplyTo1674660419230 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + add column reply_to character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674661928759-AddMailMessageCc.ts b/backend/src/database/migrations/archive/1674661928759-AddMailMessageCc.ts new file mode 100644 index 0000000..26d8c7e --- /dev/null +++ b/backend/src/database/migrations/archive/1674661928759-AddMailMessageCc.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageCc1674661928759 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + add column cc character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674732906697-AddMailMessagePayloadSortOrder.ts b/backend/src/database/migrations/archive/1674732906697-AddMailMessagePayloadSortOrder.ts new file mode 100644 index 0000000..b00510a --- /dev/null +++ b/backend/src/database/migrations/archive/1674732906697-AddMailMessagePayloadSortOrder.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessagePayloadSortOrder1674732906697 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message_payload + add column sort_order smallint not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674741639286-MakeStartEndDateNullable.ts b/backend/src/database/migrations/archive/1674741639286-MakeStartEndDateNullable.ts new file mode 100644 index 0000000..1f70eda --- /dev/null +++ b/backend/src/database/migrations/archive/1674741639286-MakeStartEndDateNullable.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MakeStartEndDateNullable1674741639286 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task + alter column start_date drop not null; + alter table task + alter column end_date drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674742927551-AddSubtaskTable.ts b/backend/src/database/migrations/archive/1674742927551-AddSubtaskTable.ts new file mode 100644 index 0000000..89507fb --- /dev/null +++ b/backend/src/database/migrations/archive/1674742927551-AddSubtaskTable.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSubtaskTable1674742927551 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table subtask + ( + id integer not null, + text character varying not null, + resolved boolean not null, + task_id integer not null, + account_id integer not null, + primary key (id), + foreign key (task_id) references task (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + create sequence subtask_id_seq as integer minvalue 46022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674747324757-AddMailMessagePayloadSize.ts b/backend/src/database/migrations/archive/1674747324757-AddMailMessagePayloadSize.ts new file mode 100644 index 0000000..a04085f --- /dev/null +++ b/backend/src/database/migrations/archive/1674747324757-AddMailMessagePayloadSize.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessagePayloadSize1674747324757 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message_payload + add column size integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674822646459-AddTaskCommentTable.ts b/backend/src/database/migrations/archive/1674822646459-AddTaskCommentTable.ts new file mode 100644 index 0000000..f8d2291 --- /dev/null +++ b/backend/src/database/migrations/archive/1674822646459-AddTaskCommentTable.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskCommentTable1674822646459 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table task_comment + ( + id integer, + text character varying not null, + task_id integer not null, + created_by integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (task_id) references task (id) on delete cascade, + foreign key (created_by) references users (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + create sequence task_comment_id_seq as integer minvalue 47022001; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674823506430-DeleteSyncDaysFromMailbox.ts b/backend/src/database/migrations/archive/1674823506430-DeleteSyncDaysFromMailbox.ts new file mode 100644 index 0000000..b53474e --- /dev/null +++ b/backend/src/database/migrations/archive/1674823506430-DeleteSyncDaysFromMailbox.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteSyncDaysFromMailbox1674823506430 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + drop column sync_days; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1674831146207-AddTaskCommentLikeTable.ts b/backend/src/database/migrations/archive/1674831146207-AddTaskCommentLikeTable.ts new file mode 100644 index 0000000..42fe633 --- /dev/null +++ b/backend/src/database/migrations/archive/1674831146207-AddTaskCommentLikeTable.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskCommentLikeTable1674831146207 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table task_comment_like + ( + comment_id integer, + user_id integer, + account_id integer, + foreign key (comment_id) references task_comment (id) on delete cascade, + foreign key (user_id) references users (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade, + primary key (comment_id, user_id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675076470234-ImapSync.ts b/backend/src/database/migrations/archive/1675076470234-ImapSync.ts new file mode 100644 index 0000000..ab04ce9 --- /dev/null +++ b/backend/src/database/migrations/archive/1675076470234-ImapSync.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ImapSync1675076470234 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_settings_manual drop column last_sync; + alter table mailbox_settings_manual add column imap_sync jsonb; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675080091774-AddSystemColumnToBoard.ts b/backend/src/database/migrations/archive/1675080091774-AddSystemColumnToBoard.ts new file mode 100644 index 0000000..8ef4657 --- /dev/null +++ b/backend/src/database/migrations/archive/1675080091774-AddSystemColumnToBoard.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSystemColumnToBoard1675080091774 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board + add is_system boolean default false not null; + + update board + set is_system = true + where name = 'Tasks board'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675086921624-AddMailMessagePayloadExternalId.ts b/backend/src/database/migrations/archive/1675086921624-AddMailMessagePayloadExternalId.ts new file mode 100644 index 0000000..53745ec --- /dev/null +++ b/backend/src/database/migrations/archive/1675086921624-AddMailMessagePayloadExternalId.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessagePayloadExternalId1675086921624 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message_payload + add column external_id character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675090441216-AddTaskSettingsIdToTask.ts b/backend/src/database/migrations/archive/1675090441216-AddTaskSettingsIdToTask.ts new file mode 100644 index 0000000..5a20b7b --- /dev/null +++ b/backend/src/database/migrations/archive/1675090441216-AddTaskSettingsIdToTask.ts @@ -0,0 +1,74 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskSettingsIdToTask1675090441216 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task + add column settings_id integer; + alter table task + add foreign key (settings_id) references task_settings (id) + on delete set null; + + drop view all_tasks; + create + or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + activity_type_id, + title, + planned_time, + settings_id, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + activity_type_id, + null as title, + null as planned_time, + null as settings_id, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + null as activity_type_id, + title, + planned_time, + settings_id, + 'task' as type + from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675176424905-AddMailMessageHasAttachment.ts b/backend/src/database/migrations/archive/1675176424905-AddMailMessageHasAttachment.ts new file mode 100644 index 0000000..9f20ceb --- /dev/null +++ b/backend/src/database/migrations/archive/1675176424905-AddMailMessageHasAttachment.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageHasAttachment1675176424905 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + add column has_attachment boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675178566416-MailboxFolderTypeNullable.ts b/backend/src/database/migrations/archive/1675178566416-MailboxFolderTypeNullable.ts new file mode 100644 index 0000000..db6b38b --- /dev/null +++ b/backend/src/database/migrations/archive/1675178566416-MailboxFolderTypeNullable.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailboxFolderTypeNullable1675178566416 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_folder alter column type drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675259924922-AddMailMessageMessageId.ts b/backend/src/database/migrations/archive/1675259924922-AddMailMessageMessageId.ts new file mode 100644 index 0000000..33f2fe3 --- /dev/null +++ b/backend/src/database/migrations/archive/1675259924922-AddMailMessageMessageId.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageMessageId1675259924922 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message add column message_id character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675340097111-DropGroupMessageFromMailbox.ts b/backend/src/database/migrations/archive/1675340097111-DropGroupMessageFromMailbox.ts new file mode 100644 index 0000000..16bc73c --- /dev/null +++ b/backend/src/database/migrations/archive/1675340097111-DropGroupMessageFromMailbox.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DropGroupMessageFromMailbox1675340097111 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox drop column group_message; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675349215128-AddCodeToEntityType.ts b/backend/src/database/migrations/archive/1675349215128-AddCodeToEntityType.ts new file mode 100644 index 0000000..782c971 --- /dev/null +++ b/backend/src/database/migrations/archive/1675349215128-AddCodeToEntityType.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddCodeColumn1675349215128 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity_type + add code varchar(255); + + alter table entity_type + add constraint entity_type__code__account_id__uniq + unique (code, account_id); + + update entity_type + set code = 'deal' + where name = 'Deal'; + + update entity_type + set code = 'contact' + where name = 'Contact'; + + update entity_type + set code = 'company' + where name = 'Company'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675350490867-AddCodeToBoard.ts b/backend/src/database/migrations/archive/1675350490867-AddCodeToBoard.ts new file mode 100644 index 0000000..ce16735 --- /dev/null +++ b/backend/src/database/migrations/archive/1675350490867-AddCodeToBoard.ts @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddCodeToBoard1675350490867 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board + add code varchar(255); + + alter table board + add constraint board__code__account_id__uniq + unique (code, account_id); + + update board + set code = 'deals' + where name = 'Deals'; + + update board + set code = 'leads' + where name = 'Leads'; + + update board + set code = 'projects' + where name = 'Projects'; + + update board + set code = 'tasks' + where name = 'Tasks board'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675412897506-AddMailMessageReferences.ts b/backend/src/database/migrations/archive/1675412897506-AddMailMessageReferences.ts new file mode 100644 index 0000000..2e94a08 --- /dev/null +++ b/backend/src/database/migrations/archive/1675412897506-AddMailMessageReferences.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageReferences1675412897506 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + add column references_to character varying, + add column in_reply_to character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675858313415-AddContactEntityTypeToMailbox.ts b/backend/src/database/migrations/archive/1675858313415-AddContactEntityTypeToMailbox.ts new file mode 100644 index 0000000..ff2f655 --- /dev/null +++ b/backend/src/database/migrations/archive/1675858313415-AddContactEntityTypeToMailbox.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddContactEntityTypeToMailbox1675858313415 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + add column contact_entity_type_id integer, + add foreign key (contact_entity_type_id) references entity_type(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675858907059-AddLeadEntityTypeToMailbox.ts b/backend/src/database/migrations/archive/1675858907059-AddLeadEntityTypeToMailbox.ts new file mode 100644 index 0000000..9729ebf --- /dev/null +++ b/backend/src/database/migrations/archive/1675858907059-AddLeadEntityTypeToMailbox.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddLeadEntityTypeToMailbox1675858907059 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + add column lead_entity_type_id integer, + add foreign key (lead_entity_type_id) references entity_type(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675932022996-AddLeadBoardIdToMailbox.ts b/backend/src/database/migrations/archive/1675932022996-AddLeadBoardIdToMailbox.ts new file mode 100644 index 0000000..75e82be --- /dev/null +++ b/backend/src/database/migrations/archive/1675932022996-AddLeadBoardIdToMailbox.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddLeadBoardIdToMailbox1675932022996 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + add column lead_board_id integer, + add foreign key (lead_board_id) references board(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675933206380-AddContactIdToMailMessage.ts b/backend/src/database/migrations/archive/1675933206380-AddContactIdToMailMessage.ts new file mode 100644 index 0000000..4b44a86 --- /dev/null +++ b/backend/src/database/migrations/archive/1675933206380-AddContactIdToMailMessage.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddContactIdToMailMessage1675933206380 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + add column contact_entity_id integer, + add foreign key (contact_entity_id) references entity(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675939938059-MailMessageThreadIdNotNull.ts b/backend/src/database/migrations/archive/1675939938059-MailMessageThreadIdNotNull.ts new file mode 100644 index 0000000..9b3bed9 --- /dev/null +++ b/backend/src/database/migrations/archive/1675939938059-MailMessageThreadIdNotNull.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailMessageThreadIdNotNull1675939938059 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + alter column thread_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675949522751-AddErrorToMailbox.ts b/backend/src/database/migrations/archive/1675949522751-AddErrorToMailbox.ts new file mode 100644 index 0000000..d88ab3a --- /dev/null +++ b/backend/src/database/migrations/archive/1675949522751-AddErrorToMailbox.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddErrorToMailbox1675949522751 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + add column error_message character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1675958088984-AddSeenToMailMessage.ts b/backend/src/database/migrations/archive/1675958088984-AddSeenToMailMessage.ts new file mode 100644 index 0000000..84f0985 --- /dev/null +++ b/backend/src/database/migrations/archive/1675958088984-AddSeenToMailMessage.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSeenToMailMessage1675958088984 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message + add column is_seen boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676281117335-AddSetNullOnDeleteToMailbox.ts b/backend/src/database/migrations/archive/1676281117335-AddSetNullOnDeleteToMailbox.ts new file mode 100644 index 0000000..e49c99b --- /dev/null +++ b/backend/src/database/migrations/archive/1676281117335-AddSetNullOnDeleteToMailbox.ts @@ -0,0 +1,20 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSetNullOnDeleteToMailbox1676281117335 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + drop constraint mailbox_contact_entity_type_id_fkey, + drop constraint mailbox_lead_entity_type_id_fkey, + drop constraint mailbox_lead_board_id_fkey, + add constraint mailbox_contact_entity_type_id_fkey foreign key (contact_entity_type_id) references entity_type(id) on delete set null, + add constraint mailbox_lead_entity_type_id_fkey foreign key (lead_entity_type_id) references entity_type(id) on delete set null, + add constraint mailbox_lead_board_id_fkey foreign key (lead_board_id) references board(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676283110539-AlterMailMessageSentToNull.ts b/backend/src/database/migrations/archive/1676283110539-AlterMailMessageSentToNull.ts new file mode 100644 index 0000000..b173df3 --- /dev/null +++ b/backend/src/database/migrations/archive/1676283110539-AlterMailMessageSentToNull.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessageSentToNull1676283110539 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message alter column sent_to drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676299431922-AddRecipientToNote.ts b/backend/src/database/migrations/archive/1676299431922-AddRecipientToNote.ts new file mode 100644 index 0000000..d074f61 --- /dev/null +++ b/backend/src/database/migrations/archive/1676299431922-AddRecipientToNote.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRecipientToNote1676299431922 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table note + add column recipient_id integer; + + alter table note + add foreign key (recipient_id) references users + on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676370561831-AddMailToFeedItem.ts b/backend/src/database/migrations/archive/1676370561831-AddMailToFeedItem.ts new file mode 100644 index 0000000..2390175 --- /dev/null +++ b/backend/src/database/migrations/archive/1676370561831-AddMailToFeedItem.ts @@ -0,0 +1,41 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailToFeedItem1676370561831 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create or replace view feed_items (id, created_at, entity_id, type) as + SELECT note.id, + note.created_at, + note.entity_id, + 'note'::text AS type + FROM note + UNION + SELECT task.id, + task.created_at, + task.entity_id, + 'task'::text AS type + FROM task + UNION + SELECT activity.id, + activity.created_at, + activity.entity_id, + 'activity'::text AS type + FROM activity + UNION + (SELECT max(message.id) AS id, + max(message.date) AS created_at, + el.source_id AS entity_id, + 'mail'::text AS type + FROM mail_message message + RIGHT JOIN entity_link el ON el.target_id = message.contact_entity_id + WHERE message.contact_entity_id IS NOT NULL + GROUP BY message.thread_id, el.source_id + ORDER BY (max(message.date)) DESC) + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676383947580-TurnOnChatFeature.ts b/backend/src/database/migrations/archive/1676383947580-TurnOnChatFeature.ts new file mode 100644 index 0000000..c617822 --- /dev/null +++ b/backend/src/database/migrations/archive/1676383947580-TurnOnChatFeature.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TurnOnChatFeature1676383947580 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update feature + set is_enabled = true + where code = 'chat' + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676385805343-AddChatFeedItemType.ts b/backend/src/database/migrations/archive/1676385805343-AddChatFeedItemType.ts new file mode 100644 index 0000000..42d5fdb --- /dev/null +++ b/backend/src/database/migrations/archive/1676385805343-AddChatFeedItemType.ts @@ -0,0 +1,41 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatFeedItemType1676385805343 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create or replace view feed_items (id, created_at, entity_id, type) as + SELECT note.id, + note.created_at, + note.entity_id, + case when note.recipient_id is null then 'note'::text else 'chat'::text end as type + FROM note + UNION + SELECT task.id, + task.created_at, + task.entity_id, + 'task'::text AS type + FROM task + UNION + SELECT activity.id, + activity.created_at, + activity.entity_id, + 'activity'::text AS type + FROM activity + UNION + (SELECT max(message.id) AS id, + max(message.date) AS created_at, + el.source_id AS entity_id, + 'mail'::text AS type + FROM mail_message message + RIGHT JOIN entity_link el ON el.target_id = message.contact_entity_id + WHERE message.contact_entity_id IS NOT NULL + GROUP BY message.thread_id, el.source_id + ORDER BY (max(message.date)) DESC) + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676823429937-AddAutomationTables.ts b/backend/src/database/migrations/archive/1676823429937-AddAutomationTables.ts new file mode 100644 index 0000000..ed222cc --- /dev/null +++ b/backend/src/database/migrations/archive/1676823429937-AddAutomationTables.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAutomationTables1676823429937 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table trigger + ( + id integer, + type varchar(100) not null, + account_id integer not null, + primary key (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence trigger_id_seq as integer minvalue 51011001; + + create table action + ( + id integer, + type varchar(100) not null, + delay integer, + account_id integer not null, + primary key (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence action_id_seq as integer minvalue 51011001; + + create table automation + ( + id integer, + trigger_id integer not null, + action_id integer not null, + created_by integer not null, + is_active boolean not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (trigger_id) references trigger (id), + foreign key (action_id) references action (id), + foreign key (created_by) references users (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence automation_id_seq as integer minvalue 51011001; + + create table activity_action_settings + ( + action_id integer, + responsible_user_type varchar(100) not null, + responsible_user_id integer, + activity_type_id integer not null, + text varchar not null, + deadline_type varchar(100) not null, + deadline_time integer, + account_id integer not null, + primary key (action_id), + foreign key (action_id) references action (id) on delete cascade, + foreign key (responsible_user_id) references users (id) on delete cascade, + foreign key (activity_type_id) references activity_type (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676866745580-AddAutomationStageTable.ts b/backend/src/database/migrations/archive/1676866745580-AddAutomationStageTable.ts new file mode 100644 index 0000000..b7a74aa --- /dev/null +++ b/backend/src/database/migrations/archive/1676866745580-AddAutomationStageTable.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAutomationStageTable1676866745580 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table automation_stage + ( + automation_id integer, + stage_id integer, + foreign key (automation_id) references automation (id) on delete cascade, + foreign key (stage_id) references stage (id) on delete cascade, + primary key (automation_id, stage_id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676872004900-AddTaskActionSettingsTable.ts b/backend/src/database/migrations/archive/1676872004900-AddTaskActionSettingsTable.ts new file mode 100644 index 0000000..1b8ff39 --- /dev/null +++ b/backend/src/database/migrations/archive/1676872004900-AddTaskActionSettingsTable.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskActionSettingsTable1676872004900 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table task_action_settings + ( + action_id integer, + responsible_user_type varchar(100) not null, + responsible_user_id integer, + title varchar not null, + text varchar not null, + deadline_type varchar(100) not null, + deadline_time integer, + account_id integer not null, + primary key (action_id), + foreign key (action_id) references action (id) on delete cascade, + foreign key (responsible_user_id) references users (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676884562402-RemoveResultFromTask.ts b/backend/src/database/migrations/archive/1676884562402-RemoveResultFromTask.ts new file mode 100644 index 0000000..1a89925 --- /dev/null +++ b/backend/src/database/migrations/archive/1676884562402-RemoveResultFromTask.ts @@ -0,0 +1,69 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveResultFromTask1676884562402 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + create or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + activity_type_id, + title, + planned_time, + settings_id, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + result, + entity_id, + activity_type_id, + null as title, + null as planned_time, + null as settings_id, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + null as result, + entity_id, + null as activity_type_id, + title, + planned_time, + settings_id, + 'task' as type + from task + order by id; + + alter table task drop column result; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676895093533-AddChangeStageActionSettingsTable.ts b/backend/src/database/migrations/archive/1676895093533-AddChangeStageActionSettingsTable.ts new file mode 100644 index 0000000..a3906f9 --- /dev/null +++ b/backend/src/database/migrations/archive/1676895093533-AddChangeStageActionSettingsTable.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChangeStageActionSettingsTable1676895093533 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table change_stage_action_settings + ( + action_id integer, + stage_id integer not null, + account_id integer not null, + primary key (action_id), + foreign key (action_id) references action (id) on delete cascade, + foreign key (stage_id) references stage (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676992655279-AddAutomationConditions.ts b/backend/src/database/migrations/archive/1676992655279-AddAutomationConditions.ts new file mode 100644 index 0000000..3081d86 --- /dev/null +++ b/backend/src/database/migrations/archive/1676992655279-AddAutomationConditions.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAutomationConditions1676992655279 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table condition + ( + id integer, + type varchar(100) not null, + account_id integer not null, + primary key (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence condition_id_seq as integer minvalue 51011001; + + create table automation_condition + ( + automation_id integer, + condition_id integer, + account_id integer not null, + foreign key (automation_id) references automation (id) on delete cascade, + foreign key (condition_id) references condition (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade, + primary key (automation_id, condition_id) + ); + + create table user_condition + ( + condition_id integer, + user_id integer, + account_id integer not null, + foreign key (condition_id) references condition (id) on delete cascade, + foreign key (user_id) references users (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade, + primary key (condition_id, user_id) + ); + + create table field_condition + ( + condition_id integer, + field_id integer not null, + field_type varchar(50) not null, + payload jsonb not null, + account_id integer not null, + primary key (condition_id), + foreign key (condition_id) references condition (id) on delete cascade, + foreign key (field_id) references field (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1676994397217-AddNotification.ts b/backend/src/database/migrations/archive/1676994397217-AddNotification.ts new file mode 100644 index 0000000..a608c5e --- /dev/null +++ b/backend/src/database/migrations/archive/1676994397217-AddNotification.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddNotification1676994397217 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + + CREATE TABLE notification ( + "id" integer, + "account_id" integer NOT NULL, + "created_at" timestamp without time zone NOT NULL, + "type" character varying NOT NULL, + "object_id" integer NOT NULL, + "entity_id" integer, + "from_user" integer, + "title" character varying, + "description" character varying NOT NULL, + "is_seen" boolean NOT NULL DEFAULT false, + PRIMARY KEY ("id"), + FOREIGN KEY ("account_id") REFERENCES account(id) ON DELETE CASCADE, + FOREIGN KEY ("entity_id") REFERENCES entity(id) ON DELETE SET NULL, + FOREIGN KEY ("from_user") REFERENCES users(id) ON DELETE SET NULL + ); + + create sequence notification_id_seq as integer minvalue 61011001; + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1677079993131-AddUserIdToNotification.ts b/backend/src/database/migrations/archive/1677079993131-AddUserIdToNotification.ts new file mode 100644 index 0000000..099d71b --- /dev/null +++ b/backend/src/database/migrations/archive/1677079993131-AddUserIdToNotification.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserIdToNotification1677079993131 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table notification + add column user_id integer not null, + add foreign key (user_id) references users(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1677581691766-AddMailboxSignature.ts b/backend/src/database/migrations/archive/1677581691766-AddMailboxSignature.ts new file mode 100644 index 0000000..7f6fa54 --- /dev/null +++ b/backend/src/database/migrations/archive/1677581691766-AddMailboxSignature.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxSignature1677581691766 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists mailbox_signature_id_seq as integer minvalue 27023001; + + create table mailbox_signature ( + id integer, + account_id integer not null, + created_at timestamp without time zone not null, + name character varying not null, + text character varying not null, + created_by integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (created_by) references users(id) on delete cascade + ); + + create table mailbox_signature_link ( + account_id integer not null, + signature_id integer, + mailbox_id integer, + primary key (signature_id, mailbox_id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (signature_id) references mailbox_signature(id) on delete cascade, + foreign key (mailbox_id) references mailbox(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1677744619246-AddNotificationHighlight.ts b/backend/src/database/migrations/archive/1677744619246-AddNotificationHighlight.ts new file mode 100644 index 0000000..b858f57 --- /dev/null +++ b/backend/src/database/migrations/archive/1677744619246-AddNotificationHighlight.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddNotificationHighlight1677744619246 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table notification add column highlight character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1677752600091-AlterNotificationSetDescriptionNullable.ts b/backend/src/database/migrations/archive/1677752600091-AlterNotificationSetDescriptionNullable.ts new file mode 100644 index 0000000..05ebc20 --- /dev/null +++ b/backend/src/database/migrations/archive/1677752600091-AlterNotificationSetDescriptionNullable.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterNotificationSetDescriptionNullable1677752600091 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table notification alter column description drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1677765215861-AddNotificationSettings.ts b/backend/src/database/migrations/archive/1677765215861-AddNotificationSettings.ts new file mode 100644 index 0000000..60da58e --- /dev/null +++ b/backend/src/database/migrations/archive/1677765215861-AddNotificationSettings.ts @@ -0,0 +1,46 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddNotificationSettings1677765215861 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists notification_settings_id_seq as integer minvalue 62011001; + create table notification_settings ( + id integer, + account_id integer not null, + user_id integer not null, + enable_popup boolean not null default true, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade + ); + + create sequence if not exists notification_type_settings_id_seq as integer minvalue 63011001; + create table notification_type_settings ( + id integer, + account_id integer not null, + settings_id integer not null, + type character varying not null, + is_enabled boolean not null default true, + object_id integer, + before integer, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (settings_id) references notification_settings(id) on delete cascade + ); + + create table notification_type_follow_user ( + type_id integer, + user_id integer, + account_id integer not null, + primary key (type_id, user_id), + foreign key (type_id) references notification_type_settings(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678093755605-AddExternalSystems.ts b/backend/src/database/migrations/archive/1678093755605-AddExternalSystems.ts new file mode 100644 index 0000000..0d4bd30 --- /dev/null +++ b/backend/src/database/migrations/archive/1678093755605-AddExternalSystems.ts @@ -0,0 +1,25 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddExternalSystems1678093755605 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into external_system(id, name, code, url_templates) values(4, 'Pipedrive', 'pipedrive', '{pipedrive.com}'); + insert into external_system(id, name, code, url_templates) values(5, 'HubSpot', 'hubspot', '{hubspot.com}'); + insert into external_system(id, name, code, url_templates) values(6, 'Freshsales', 'freshsales', '{freshworks.com,myfreshworks.com}'); + insert into external_system(id, name, code, url_templates) values(7, 'Zoho', 'zoho', '{zoho.com}'); + insert into external_system(id, name, code, url_templates) values(8, 'Twitter', 'twitter', '{twitter.com}'); + insert into external_system(id, name, code, url_templates) values(9, 'Instagram', 'instagram', '{instagram.com}'); + insert into external_system(id, name, code, url_templates) values(10, 'Notion', 'notion', '{notion.so}'); + insert into external_system(id, name, code, url_templates) values(11, 'Zendesk', 'zendesk', '{zendesk.com}'); + insert into external_system(id, name, code, url_templates) values(12, 'SugarCRM', 'sugarcrm', '{sugarcrm.com}'); + insert into external_system(id, name, code, url_templates) values(13, 'Monday', 'monday', '{monday.com}'); + insert into external_system(id, name, code, url_templates) values(14, 'amoCRM', 'amocrm', '{amocrm.ru,kommo.com}'); + insert into external_system(id, name, code, url_templates) values(15, 'Bitrix24', 'bitrix', '{bitrix24.ru,bitrix24.com,bitrix24.es,bitrix24.eu,bitrix24.de,bitrix24.fr,bitrix24.pl,bitrix24.it,bitrix24.uk}'); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678096716231-AddAccountSettings.ts b/backend/src/database/migrations/archive/1678096716231-AddAccountSettings.ts new file mode 100644 index 0000000..6e9927d --- /dev/null +++ b/backend/src/database/migrations/archive/1678096716231-AddAccountSettings.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAccountSettings1678096716231 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table account_settings ( + account_id integer, + language character varying not null, + working_days character varying, + working_time_from time, + working_time_to time, + time_zone character varying, + currency character varying(3) not null, + number_format character varying, + primary key (account_id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678113775031-AddExactTimeTriggerSettings.ts b/backend/src/database/migrations/archive/1678113775031-AddExactTimeTriggerSettings.ts new file mode 100644 index 0000000..12df8c0 --- /dev/null +++ b/backend/src/database/migrations/archive/1678113775031-AddExactTimeTriggerSettings.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddExactTimeTriggerSettings1678113775031 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table exact_time_trigger_settings + ( + trigger_id integer, + date timestamp without time zone not null, + account_id integer not null, + primary key (trigger_id), + foreign key (trigger_id) references trigger (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678194605837-AddDefaultNotificationSettings.ts b/backend/src/database/migrations/archive/1678194605837-AddDefaultNotificationSettings.ts new file mode 100644 index 0000000..da304ce --- /dev/null +++ b/backend/src/database/migrations/archive/1678194605837-AddDefaultNotificationSettings.ts @@ -0,0 +1,55 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDefaultNotificationSettings1678194605837 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into notification_settings + select nextval('notification_settings_id_seq'::regclass) as id, account_id, id as user_id, true as enable_popup + from users; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'task_new' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'task_overdue' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'task_before_start' as type, true as is_enabled, null as object_id, 3600 as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'task_overdue_employee' as type, false as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'activity_new' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'activity_overdue' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'activity_before_start' as type, true as is_enabled, null as object_id, 3600 as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'activity_overdue_employee' as type, false as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'task_comment_new' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'chat_message_new' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'mail_new' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'entity_note_new' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + insert into notification_type_settings + select nextval('notification_type_settings_id_seq'::regclass) as id, account_id, id as settings_id, 'entity_responsible_change' as type, true as is_enabled, null as object_id, null as before + from notification_settings; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678285724304-AddResolveDateToTaskAndActivity.ts b/backend/src/database/migrations/archive/1678285724304-AddResolveDateToTaskAndActivity.ts new file mode 100644 index 0000000..a7b1188 --- /dev/null +++ b/backend/src/database/migrations/archive/1678285724304-AddResolveDateToTaskAndActivity.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddResolveDateToTaskAndActivity1678285724304 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task add column resolved_date timestamp without time zone; + alter table activity add column resolved_date timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678286876322-AddResolveDateToAllTasks.ts b/backend/src/database/migrations/archive/1678286876322-AddResolveDateToAllTasks.ts new file mode 100644 index 0000000..99a04c7 --- /dev/null +++ b/backend/src/database/migrations/archive/1678286876322-AddResolveDateToAllTasks.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddResolveDateToAllTasks1678286876322 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + + create or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + activity_type_id, + title, + planned_time, + settings_id, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + activity_type_id, + null as title, + null as planned_time, + null as settings_id, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + null as result, + entity_id, + null as activity_type_id, + title, + planned_time, + settings_id, + 'task' as type + from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678347902775-AlterNotificationHighlightToTagName.ts b/backend/src/database/migrations/archive/1678347902775-AlterNotificationHighlightToTagName.ts new file mode 100644 index 0000000..09e8f41 --- /dev/null +++ b/backend/src/database/migrations/archive/1678347902775-AlterNotificationHighlightToTagName.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterNotificationHighlightToTagName1678347902775 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table notification rename column highlight to tag_name; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678367411991-FillResolvedDateForTaskAndAction.ts b/backend/src/database/migrations/archive/1678367411991-FillResolvedDateForTaskAndAction.ts new file mode 100644 index 0000000..a85a268 --- /dev/null +++ b/backend/src/database/migrations/archive/1678367411991-FillResolvedDateForTaskAndAction.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FillResolvedDateForTaskAndAction1678367411991 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update task set resolved_date = now() where is_resolved = true and resolved_date is null; + + update activity set resolved_date = now() where is_resolved = true and resolved_date is null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678696011650-AddPlanNameToSubscription.ts b/backend/src/database/migrations/archive/1678696011650-AddPlanNameToSubscription.ts new file mode 100644 index 0000000..e6427de --- /dev/null +++ b/backend/src/database/migrations/archive/1678696011650-AddPlanNameToSubscription.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddPlanNameToSubscription1678696011650 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subscription add column plan_name character varying; + update subscription set plan_name = 'Premium'; + alter table subscription alter column plan_name set NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678697720944-UpdateSubscriptionTo50Users.ts b/backend/src/database/migrations/archive/1678697720944-UpdateSubscriptionTo50Users.ts new file mode 100644 index 0000000..e7eec66 --- /dev/null +++ b/backend/src/database/migrations/archive/1678697720944-UpdateSubscriptionTo50Users.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateSubscriptionTo50Users1678697720944 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update subscription set user_limit = 50; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678720323399-AddCodeToField.ts b/backend/src/database/migrations/archive/1678720323399-AddCodeToField.ts new file mode 100644 index 0000000..8c40345 --- /dev/null +++ b/backend/src/database/migrations/archive/1678720323399-AddCodeToField.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddCodeToField1678720323399 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field + add code varchar(255); + + alter table field + add constraint field__code__entity_type_id__uniq + unique (code, entity_type_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678891987277-AddDepartment.ts b/backend/src/database/migrations/archive/1678891987277-AddDepartment.ts new file mode 100644 index 0000000..fea29af --- /dev/null +++ b/backend/src/database/migrations/archive/1678891987277-AddDepartment.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDepartment1678891987277 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists department_id_seq as integer minvalue 1; + + create table department ( + id integer, + name character varying not null, + parent_id integer, + account_id integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + alter table department + add foreign key (parent_id) references department(id) on delete cascade; + + alter table users + add column department_id integer, + add foreign key (department_id) references department(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1678963689149-AddStartsInToNotification.ts b/backend/src/database/migrations/archive/1678963689149-AddStartsInToNotification.ts new file mode 100644 index 0000000..3091f6c --- /dev/null +++ b/backend/src/database/migrations/archive/1678963689149-AddStartsInToNotification.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddStartsInToNotification1678963689149 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table notification add column starts_in integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679291439089-AddScheduledAction.ts b/backend/src/database/migrations/archive/1679291439089-AddScheduledAction.ts new file mode 100644 index 0000000..38b944e --- /dev/null +++ b/backend/src/database/migrations/archive/1679291439089-AddScheduledAction.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddScheduledAction1679291439089 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table scheduled_action + ( + id integer, + action_id integer not null, + entity_id integer not null, + scheduled_time timestamp without time zone not null, + completed boolean not null, + account_id integer not null, + primary key (id), + foreign key (action_id) references action (id) on delete cascade, + foreign key (entity_id) references entity (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + create sequence scheduled_action_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679494426598-MakeFieldGroupOptional.ts b/backend/src/database/migrations/archive/1679494426598-MakeFieldGroupOptional.ts new file mode 100644 index 0000000..ab89c06 --- /dev/null +++ b/backend/src/database/migrations/archive/1679494426598-MakeFieldGroupOptional.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MakeFieldGroupOptional1679494426598 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field + alter column field_group_id drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679578329175-AddColorToFieldOption.ts b/backend/src/database/migrations/archive/1679578329175-AddColorToFieldOption.ts new file mode 100644 index 0000000..e58e25e --- /dev/null +++ b/backend/src/database/migrations/archive/1679578329175-AddColorToFieldOption.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddColorToFieldOption1679578329175 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field_option + add column color varchar(50); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679583365997-AlterMailMessageContactEntityId.ts b/backend/src/database/migrations/archive/1679583365997-AlterMailMessageContactEntityId.ts new file mode 100644 index 0000000..cfc347a --- /dev/null +++ b/backend/src/database/migrations/archive/1679583365997-AlterMailMessageContactEntityId.ts @@ -0,0 +1,54 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessageContactEntityId1679583365997 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view feed_items; + + alter table mail_message rename column contact_entity_id to entity_id; + + create or replace view feed_items (id, created_at, entity_id, type) as + SELECT note.id, + note.created_at, + note.entity_id, + case when note.recipient_id is null then 'note'::text else 'chat'::text end as type + FROM note + UNION + SELECT task.id, + task.created_at, + task.entity_id, + 'task'::text as type + FROM task + UNION + SELECT activity.id, + activity.created_at, + activity.entity_id, + 'activity'::text as type + FROM activity + UNION + (SELECT max(message.id) as id, + max(message.date) as created_at, + message.entity_id as entity_id, + 'mail'::text as type + FROM mail_message message + WHERE message.entity_id is not null + GROUP BY message.thread_id, message.entity_id + ORDER BY (max(message.date)) desc) + UNION + (SELECT max(message.id) as id, + max(message.date) as created_at, + el.source_id as entity_id, + 'mail'::text as type + FROM mail_message message + RIGHT JOIN entity_link el ON el.target_id = message.entity_id + WHERE message.entity_id is not null + GROUP BY message.thread_id, el.source_id + ORDER BY (max(message.date)) desc) + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679929245833-AddUserAvatarId.ts b/backend/src/database/migrations/archive/1679929245833-AddUserAvatarId.ts new file mode 100644 index 0000000..71b455f --- /dev/null +++ b/backend/src/database/migrations/archive/1679929245833-AddUserAvatarId.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserAvatarId1679929245833 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users + drop column avatar_url, + add column avatar_id uuid; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679931642588-AddActiveToField.ts b/backend/src/database/migrations/archive/1679931642588-AddActiveToField.ts new file mode 100644 index 0000000..40bda8e --- /dev/null +++ b/backend/src/database/migrations/archive/1679931642588-AddActiveToField.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddActiveToField1679931642588 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field + add column active boolean default true not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1679931904542-AddAccountLogo.ts b/backend/src/database/migrations/archive/1679931904542-AddAccountLogo.ts new file mode 100644 index 0000000..7c6f85e --- /dev/null +++ b/backend/src/database/migrations/archive/1679931904542-AddAccountLogo.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAccountLogo1679931904542 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account add column logo_id uuid; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1680499051021-AddParticipantsToEntity.ts b/backend/src/database/migrations/archive/1680499051021-AddParticipantsToEntity.ts new file mode 100644 index 0000000..64e1131 --- /dev/null +++ b/backend/src/database/migrations/archive/1680499051021-AddParticipantsToEntity.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddParticipantsToEntity1680499051021 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity + add column participant_ids jsonb; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1680763220747-MigrateProjects.ts b/backend/src/database/migrations/archive/1680763220747-MigrateProjects.ts new file mode 100644 index 0000000..8161a1c --- /dev/null +++ b/backend/src/database/migrations/archive/1680763220747-MigrateProjects.ts @@ -0,0 +1,160 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MigrateProjects1680763220747 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board + add column need_migration boolean default false; + + insert into board(id, name, type, record_id, account_id, created_at, sort_order, is_system, code, + need_migration) + select nextval('board_id_seq'::regclass) as board_id, + 'Project tasks', + 'entity', + eb.entity_id, + eb.account_id, + now(), + 0, + false, + null, + true + from (select e.id as entity_id, b.id as board_id, e.account_id as account_id + from entity e + inner join entity_type et on et.id = e.entity_type_id and et.entity_category = 'project' + left join board b on b.type = 'entity' and b.record_id = e.id) eb + where eb.board_id is null; + +-- Add Stages + insert into stage (id, name, color, code, is_system, sort_order, board_id, account_id, created_at) + select nextval('stage_id_seq'::regclass), + 'To Do', + '#555', + null, + false, + 0, + b.id, + b.account_id, + now() + from board b + where b.need_migration = true; + + insert into stage (id, name, color, code, is_system, sort_order, board_id, account_id, created_at) + select nextval('stage_id_seq'::regclass), + 'In Progress', + '#555', + null, + false, + 1, + b.id, + b.account_id, + now() + from board b + where b.need_migration = true; + + insert into stage (id, name, color, code, is_system, sort_order, board_id, account_id, created_at) + select nextval('stage_id_seq'::regclass), + 'Done', + '#555', + 'done', + true, + 2, + b.id, + b.account_id, + now() + from board b + where b.need_migration = true; + `); + + await queryRunner.query(` + alter table entity_type + add column need_migration boolean default false; + + update entity_type + set need_migration = true + where entity_type.id in (select et.id + from entity_type et + left join field f on et.id = f.entity_type_id and f.code = 'value' + where f.id is null + and et.entity_category = 'project'); + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Value', + 'value', + 0, + et.id, + null, + et.account_id, + now(), + 'value', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Start date', + 'date', + 1, + et.id, + null, + et.account_id, + now(), + 'start_date', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'End date', + 'date', + 2, + et.id, + null, + et.account_id, + now(), + 'end_date', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Participants', + 'participants', + 3, + et.id, + null, + et.account_id, + now(), + 'participants', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Description', + 'text', + 4, + et.id, + null, + et.account_id, + now(), + 'description', + true + from entity_type et + where et.need_migration = true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681141545739-AddEmailActionSettings.ts b/backend/src/database/migrations/archive/1681141545739-AddEmailActionSettings.ts new file mode 100644 index 0000000..a06a029 --- /dev/null +++ b/backend/src/database/migrations/archive/1681141545739-AddEmailActionSettings.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEmailActionSettings1681141545739 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table email_action_settings + ( + action_id integer, + subject varchar not null, + content varchar, + send_as_html boolean not null, + mailbox_id integer, + user_id integer, + account_id integer not null, + primary key (action_id), + foreign key (action_id) references action (id) on delete cascade, + foreign key (mailbox_id) references mailbox (id) on delete cascade, + foreign key (user_id) references users (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681224301468-AddProjectFields.ts b/backend/src/database/migrations/archive/1681224301468-AddProjectFields.ts new file mode 100644 index 0000000..e6a8fb2 --- /dev/null +++ b/backend/src/database/migrations/archive/1681224301468-AddProjectFields.ts @@ -0,0 +1,104 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProjectFields1681224301468 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update entity_type + set need_migration = false + where need_migration = true; + + update board + set need_migration = false + where need_migration = true; + `); + + await queryRunner.query(` + update entity_type + set need_migration = true + where entity_type.id in (select et.id + from entity_type et + left join field f on et.id = f.entity_type_id and f.code = 'value' + where f.id is null + and et.entity_category = 'project'); + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Value', + 'value', + 0, + et.id, + null, + et.account_id, + now(), + 'value', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Start date', + 'date', + 1, + et.id, + null, + et.account_id, + now(), + 'start_date', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'End date', + 'date', + 2, + et.id, + null, + et.account_id, + now(), + 'end_date', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Participants', + 'participants', + 3, + et.id, + null, + et.account_id, + now(), + 'participants', + true + from entity_type et + where et.need_migration = true; + + insert into field (id, name, type, sort_order, entity_type_id, field_group_id, account_id, created_at, code, + active) + select nextval('field_id_seq'::regclass), + 'Description', + 'text', + 4, + et.id, + null, + et.account_id, + now(), + 'description', + true + from entity_type et + where et.need_migration = true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681289039535-AddEntityListSettings.ts b/backend/src/database/migrations/archive/1681289039535-AddEntityListSettings.ts new file mode 100644 index 0000000..bec18e9 --- /dev/null +++ b/backend/src/database/migrations/archive/1681289039535-AddEntityListSettings.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntityListSettings1681289039535 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table entity_list_settings ( + id integer, + entity_type_id integer not null, + board_id integer, + settings jsonb not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (entity_type_id) references entity_type(id) on delete cascade, + foreign key (board_id) references board(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists entity_list_settings_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681483040117-AddScheduledMailMessage.ts b/backend/src/database/migrations/archive/1681483040117-AddScheduledMailMessage.ts new file mode 100644 index 0000000..8db1996 --- /dev/null +++ b/backend/src/database/migrations/archive/1681483040117-AddScheduledMailMessage.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddScheduledMailMessage1681483040117 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table scheduled_mail_message + ( + id integer, + send_to jsonb not null, + subject character varying not null, + content character varying, + send_as_html boolean not null, + file_ids jsonb not null, + sent_at timestamp without time zone, + mailbox_id integer not null, + user_id integer not null, + entity_id integer, + action_id integer, + account_id integer not null, + primary key (id), + foreign key (mailbox_id) references mailbox (id) on delete cascade, + foreign key (user_id) references users (id) on delete cascade, + foreign key (entity_id) references entity (id) on delete set null, + foreign key (action_id) references action (id) on delete set null, + foreign key (account_id) references account (id) on delete cascade + ); + + create sequence if not exists scheduled_mail_message_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681732037710-AddEmailsPerDayColumnToMailbox.ts b/backend/src/database/migrations/archive/1681732037710-AddEmailsPerDayColumnToMailbox.ts new file mode 100644 index 0000000..0d8e347 --- /dev/null +++ b/backend/src/database/migrations/archive/1681732037710-AddEmailsPerDayColumnToMailbox.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEmailsPerDayColumnToMailbox1681732037710 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + add column emails_per_day smallint default null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681828967422-AddWeightToTaskAndActivity.ts b/backend/src/database/migrations/archive/1681828967422-AddWeightToTaskAndActivity.ts new file mode 100644 index 0000000..4e84704 --- /dev/null +++ b/backend/src/database/migrations/archive/1681828967422-AddWeightToTaskAndActivity.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddWeightToTaskAndActivity1681828967422 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE activity ADD COLUMN weight double precision; + WITH ordered_activity AS ( + SELECT + id, + ROW_NUMBER() OVER (ORDER BY created_at DESC) * 100 AS new_weight + FROM + activity + ) + UPDATE activity + SET weight = ordered_activity.new_weight + FROM ordered_activity + WHERE activity.id = ordered_activity.id; + ALTER TABLE activity ALTER COLUMN weight SET NOT NULL; + + ALTER TABLE task ADD COLUMN weight double precision; + WITH ordered_task AS ( + SELECT + id, + ROW_NUMBER() OVER (ORDER BY created_at DESC) * 100 AS new_weight + FROM + task + ) + UPDATE task + SET weight = ordered_task.new_weight + FROM ordered_task + WHERE task.id = ordered_task.id; + ALTER TABLE task ALTER COLUMN weight SET NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681832187113-AddWeightToAllTasks.ts b/backend/src/database/migrations/archive/1681832187113-AddWeightToAllTasks.ts new file mode 100644 index 0000000..d6fe621 --- /dev/null +++ b/backend/src/database/migrations/archive/1681832187113-AddWeightToAllTasks.ts @@ -0,0 +1,74 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddWeightToAllTasks1681832187113 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + + create or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + weight, + activity_type_id, + title, + planned_time, + settings_id, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + weight, + activity_type_id, + null as title, + null as planned_time, + null as settings_id, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + null as result, + entity_id, + weight, + null as activity_type_id, + title, + planned_time, + settings_id, + 'task' as type + from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1681900142878-ResetWeightForTasksAndActivities.ts b/backend/src/database/migrations/archive/1681900142878-ResetWeightForTasksAndActivities.ts new file mode 100644 index 0000000..6da9144 --- /dev/null +++ b/backend/src/database/migrations/archive/1681900142878-ResetWeightForTasksAndActivities.ts @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ResetWeightForTasksAndActivities1681900142878 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + WITH ordered_tasks AS ( + SELECT + id, + ROW_NUMBER() OVER (PARTITION BY account_id ORDER BY created_at DESC) * 100 AS new_weight + FROM + all_tasks + ) + UPDATE activity + SET weight = ordered_tasks.new_weight + FROM ordered_tasks + WHERE activity.id = ordered_tasks.id; + WITH ordered_tasks AS ( + SELECT + id, + ROW_NUMBER() OVER (PARTITION BY account_id ORDER BY created_at DESC) * 100 AS new_weight + FROM + all_tasks + ) + UPDATE task + SET weight = ordered_tasks.new_weight + FROM ordered_tasks + WHERE task.id = ordered_tasks.id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682002593036-AddEntityWeight.ts b/backend/src/database/migrations/archive/1682002593036-AddEntityWeight.ts new file mode 100644 index 0000000..ca6c8e6 --- /dev/null +++ b/backend/src/database/migrations/archive/1682002593036-AddEntityWeight.ts @@ -0,0 +1,29 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntityWeight1682002593036 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE entity ADD COLUMN weight double precision; + + WITH ordered_entity AS ( + SELECT + e.id, + ROW_NUMBER() OVER (PARTITION BY e.account_id, e.entity_type_id, s.board_id ORDER BY e.created_at DESC, e.id DESC) * 100 AS new_weight + FROM + entity e + LEFT JOIN stage s ON e.stage_id = s.id + ) + UPDATE entity + SET weight = ordered_entity.new_weight + FROM ordered_entity + WHERE entity.id = ordered_entity.id; + + ALTER TABLE entity ALTER COLUMN weight SET NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682083589639-AddAccountIdTETFeature.ts b/backend/src/database/migrations/archive/1682083589639-AddAccountIdTETFeature.ts new file mode 100644 index 0000000..71fc4e0 --- /dev/null +++ b/backend/src/database/migrations/archive/1682083589639-AddAccountIdTETFeature.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAccountIdTETFeature1682083589639 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity_type_feature + add account_id integer, + add foreign key (account_id) references account(id) on delete cascade; + + update entity_type_feature as etf + set (account_id) = (select et.account_id from entity_type et where et.id = etf.entity_type_id); + + alter table entity_type_feature + alter column account_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682348048312-AddRMS.ts b/backend/src/database/migrations/archive/1682348048312-AddRMS.ts new file mode 100644 index 0000000..dd48f6f --- /dev/null +++ b/backend/src/database/migrations/archive/1682348048312-AddRMS.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRMS1682348048312 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table industry + ( + code varchar not null, + name varchar not null, + color varchar not null, + sort_order smallint not null, + active boolean not null, + primary key (code) + ); + + create table ready_made_solution + ( + code varchar not null, + name varchar not null, + subdomain varchar not null, + sort_order smallint not null, + active boolean not null, + industry_code varchar not null, + primary key (code), + foreign key (industry_code) references industry (code) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682350735553-AddIndustries.ts b/backend/src/database/migrations/archive/1682350735553-AddIndustries.ts new file mode 100644 index 0000000..e66570a --- /dev/null +++ b/backend/src/database/migrations/archive/1682350735553-AddIndustries.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIndustries1682350735553 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into industry(code, name, color, sort_order, active) + values ('it_and_development', 'IT & Development', '#FF8D07', 0, true), + ('construction_and_engineering', 'Construction and engineering', '#A33CAB', 1, true), + ('advertising_and_marketing', 'Advertising & Marketing', '#67E2F9', 2, true), + ('consulting_and_outsourcing', 'Consulting and outsourcing', '#8AF039', 3, true), + ('manufacturing', 'Manufacturing', '#EC008C', 4, true), + ('education', 'Education', '#3D8FEC', 5, true); + `); + + await queryRunner.query(` + alter table ready_made_solution + alter column industry_code drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682433824946-AddDemoMarker.ts b/backend/src/database/migrations/archive/1682433824946-AddDemoMarker.ts new file mode 100644 index 0000000..01f2af8 --- /dev/null +++ b/backend/src/database/migrations/archive/1682433824946-AddDemoMarker.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDemoMarker1682433824946 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users + add column is_demo boolean not null default false; + + alter table entity + add column is_demo boolean not null default false; + + alter table account_settings + add column has_demo boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682507153940-AddDocumentTemplate.ts b/backend/src/database/migrations/archive/1682507153940-AddDocumentTemplate.ts new file mode 100644 index 0000000..304b66f --- /dev/null +++ b/backend/src/database/migrations/archive/1682507153940-AddDocumentTemplate.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDocumentTemplate1682507153940 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists document_template_id_seq as integer minvalue 1; + + create table document_template ( + id integer, + name character varying not null, + created_by integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (created_by) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create table document_template_access ( + document_template_id integer, + user_id integer, + account_id integer not null, + primary key (document_template_id, user_id), + foreign key (document_template_id) references document_template(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create table document_template_entity_type ( + document_template_id integer, + entity_type_id integer, + account_id integer not null, + primary key (document_template_id, entity_type_id), + foreign key (document_template_id) references document_template(id) on delete cascade, + foreign key (entity_type_id) references entity_type(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682518277268-DeleteFileInfoWithUser.ts b/backend/src/database/migrations/archive/1682518277268-DeleteFileInfoWithUser.ts new file mode 100644 index 0000000..f5c92f4 --- /dev/null +++ b/backend/src/database/migrations/archive/1682518277268-DeleteFileInfoWithUser.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteFileInfoWithUser1682518277268 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info + drop constraint file_info_created_by_fkey; + + alter table file_info + add foreign key (created_by) references users on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1682589692981-AddIndexes.ts b/backend/src/database/migrations/archive/1682589692981-AddIndexes.ts new file mode 100644 index 0000000..f5be3b0 --- /dev/null +++ b/backend/src/database/migrations/archive/1682589692981-AddIndexes.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIndexes1682589692981 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX task_is_resolved_responsible_user_id_start_date_idx + ON task (is_resolved, responsible_user_id, start_date); + + CREATE INDEX activity_is_resolved_responsible_user_id_start_date_idx + ON activity (is_resolved, responsible_user_id, start_date); + + CREATE INDEX notification_type_settings_settings_id_type_is_enabled_idx + ON notification_type_settings (settings_id, type, is_enabled); + + CREATE INDEX mailbox_folder_account_id_mailbox_id_idx + ON mailbox_folder (account_id, mailbox_id); + + CREATE INDEX field_value_entity_id_idx ON field_value(entity_id); + + CREATE INDEX field_option_field_id_idx ON field_option(field_id); + + CREATE INDEX entity_link_source_id_sort_order_id_idx ON entity_link(source_id, sort_order, id); + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683016482581-AddIndexes.ts b/backend/src/database/migrations/archive/1683016482581-AddIndexes.ts new file mode 100644 index 0000000..9677121 --- /dev/null +++ b/backend/src/database/migrations/archive/1683016482581-AddIndexes.ts @@ -0,0 +1,54 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIndexes1683016482581 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX task_is_resolved_end_date_idx ON task(is_resolved, end_date); + CREATE INDEX task_entity_id_is_resolved_created_by_idx ON task(entity_id int4_ops, is_resolved bool_ops, created_by int4_ops); + + CREATE INDEX activity_is_resolved_end_date_idx ON activity(is_resolved, end_date); + + CREATE INDEX scheduled_action_scheduled_time_completed_idx ON scheduled_action(scheduled_time, completed); + + CREATE INDEX field_entity_type_id_type_idx ON field(entity_type_id int4_ops, type text_ops); + CREATE INDEX entity_entity_type_id_stage_id_idx ON entity(entity_type_id int4_ops, stage_id int4_ops); + + CREATE INDEX user_object_permission_user_id_object_permission_id_idx ON user_object_permission(user_id int4_ops, object_permission_id int4_ops); + CREATE INDEX object_permission_object_type_object_id_idx ON object_permission(object_type text_ops, object_id int4_ops); + + CREATE INDEX notification_settings_user_id_idx ON notification_settings(user_id int4_ops); + + CREATE INDEX users_account_id_department_id_idx ON users(account_id int4_ops, department_id int4_ops); + + CREATE INDEX subtask_account_id_task_id_idx ON subtask(account_id int4_ops, task_id int4_ops); + + CREATE INDEX file_link_account_id_source_type_source_id_idx ON file_link(account_id int4_ops, source_type text_ops, source_id int4_ops); + + CREATE INDEX stage_board_id_idx ON stage(board_id int4_ops); + + CREATE INDEX board_account_id_idx ON board(account_id int4_ops); + CREATE INDEX board_type_idx ON board(type text_ops); + + CREATE INDEX note_entity_id_idx ON note(entity_id int4_ops); + CREATE INDEX task_entity_id_idx ON task(entity_id int4_ops); + CREATE INDEX activity_entity_id_idx ON activity(entity_id int4_ops); + CREATE INDEX mail_message_entity_id_idx ON mail_message(entity_id int4_ops); + CREATE INDEX entity_link_target_id_idx ON entity_link(target_id int4_ops); + CREATE INDEX entity_link_source_id_idx ON entity_link(source_id int4_ops); + + CREATE INDEX department_account_id_parent_id_idx ON department(account_id int4_ops, parent_id int4_ops); + + CREATE INDEX exact_time_trigger_settings_date_idx ON exact_time_trigger_settings(date timestamp_ops); + + CREATE INDEX notification_account_user_seen_idx ON notification(account_id, user_id, is_seen); + + CREATE INDEX field_value_account_id_idx ON field_value(account_id); + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683205194466-AddTaskBoardIdToBoard.ts b/backend/src/database/migrations/archive/1683205194466-AddTaskBoardIdToBoard.ts new file mode 100644 index 0000000..610bd62 --- /dev/null +++ b/backend/src/database/migrations/archive/1683205194466-AddTaskBoardIdToBoard.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaskBoardIdToBoard1683205194466 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board + add column task_board_id integer default null, + add foreign key (task_board_id) references board(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683517583707-DeleteProjectEntityBoards.ts b/backend/src/database/migrations/archive/1683517583707-DeleteProjectEntityBoards.ts new file mode 100644 index 0000000..ea31d01 --- /dev/null +++ b/backend/src/database/migrations/archive/1683517583707-DeleteProjectEntityBoards.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteProjectEntityBoards1683517583707 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from board + where type = 'entity'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683731671898-AddFeatureDocuments.ts b/backend/src/database/migrations/archive/1683731671898-AddFeatureDocuments.ts new file mode 100644 index 0000000..49accb4 --- /dev/null +++ b/backend/src/database/migrations/archive/1683731671898-AddFeatureDocuments.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFeatureDocuments1683731671898 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into feature("name", "code", "is_enabled") values('Create Documents', 'documents', TRUE); + + insert into entity_type_feature (entity_type_id, feature_id, account_id) + select id as entity_type_id, currval('feature_id_seq') as feature_id, account_id + from entity_type; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683797890507-AddDocumentTemplateGenerated.ts b/backend/src/database/migrations/archive/1683797890507-AddDocumentTemplateGenerated.ts new file mode 100644 index 0000000..1762da6 --- /dev/null +++ b/backend/src/database/migrations/archive/1683797890507-AddDocumentTemplateGenerated.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDocumentTemplateGenerated1683797890507 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table document_template add column created_count integer NOT NULL DEFAULT 0; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683802969000-AddDocumentInFeed.ts b/backend/src/database/migrations/archive/1683802969000-AddDocumentInFeed.ts new file mode 100644 index 0000000..1477f32 --- /dev/null +++ b/backend/src/database/migrations/archive/1683802969000-AddDocumentInFeed.ts @@ -0,0 +1,42 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDocumentInFeed1683802969000 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view feed_items; + + create or replace view feed_items (id, created_at, entity_id, type) as + select note.id, note.created_at, note.entity_id, case when note.recipient_id is null then 'note'::text else 'chat'::text end as type + from note + union + select task.id, task.created_at, task.entity_id, 'task'::text as type + from task + union + select activity.id, activity.created_at, activity.entity_id, 'activity'::text as type + from activity + union + (select max(message.id) as id, max(message.date) as created_at, message.entity_id as entity_id, 'mail'::text as type + from mail_message message + where message.entity_id is not null + group by message.thread_id, message.entity_id + order by (max(message.date)) desc) + union + (select max(message.id) as id, max(message.date) as created_at, el.source_id as entity_id, 'mail'::text as type + from mail_message message + right join entity_link el ON el.target_id = message.entity_id + where message.entity_id is not null + group by message.thread_id, el.source_id + order by (max(message.date)) desc) + union + select fl.id, fl.created_at, fl.source_id, 'document'::text as type + from file_link fl + where fl.source_type = 'entity_document' + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1683875863921-TrimUsersNames.ts b/backend/src/database/migrations/archive/1683875863921-TrimUsersNames.ts new file mode 100644 index 0000000..130a638 --- /dev/null +++ b/backend/src/database/migrations/archive/1683875863921-TrimUsersNames.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TrimUsersNames1683875863921 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update users + set first_name = trim (first_name), + last_name = trim (last_name); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1684249775346-AddAllTasksStageId.ts b/backend/src/database/migrations/archive/1684249775346-AddAllTasksStageId.ts new file mode 100644 index 0000000..d64f8d8 --- /dev/null +++ b/backend/src/database/migrations/archive/1684249775346-AddAllTasksStageId.ts @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddAllTasksStageId1684249775346 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view all_tasks; + + create or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + weight, + activity_type_id, + title, + planned_time, + settings_id, + stage_id, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + weight, + activity_type_id, + null as title, + null as planned_time, + null as settings_id, + null as stage_id, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + null as result, + entity_id, + weight, + null as activity_type_id, + title, + planned_time, + settings_id, + stage_id, + 'task' as type + from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1684317847183-AddCreatedByToFileLink.ts b/backend/src/database/migrations/archive/1684317847183-AddCreatedByToFileLink.ts new file mode 100644 index 0000000..61c469b --- /dev/null +++ b/backend/src/database/migrations/archive/1684317847183-AddCreatedByToFileLink.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addCreatedByToFileLink1684317847183 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_link + add column created_by integer, + add foreign key (created_by) references users(id) on delete cascade; + + update file_link set created_by = file_info.created_by from file_info where file_info.id = file_link.file_id; + + alter table file_link alter column created_by set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1685001497108-ChangeFileInfoHash.ts b/backend/src/database/migrations/archive/1685001497108-ChangeFileInfoHash.ts new file mode 100644 index 0000000..db78796 --- /dev/null +++ b/backend/src/database/migrations/archive/1685001497108-ChangeFileInfoHash.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChangeFileInfoHash1685001497108 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info + drop column hash_md5, + add column hash_sha256 character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1685595302584-AddParticipantsToBoard.ts b/backend/src/database/migrations/archive/1685595302584-AddParticipantsToBoard.ts new file mode 100644 index 0000000..40ea073 --- /dev/null +++ b/backend/src/database/migrations/archive/1685595302584-AddParticipantsToBoard.ts @@ -0,0 +1,36 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddParticipantsToBoard1685595302584 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board + add owner_id integer, + add foreign key (owner_id) references users (id) on + delete + set null; + + alter table board + add participant_ids jsonb; + + UPDATE board + SET owner_id = subquery.owner_id FROM (SELECT u.id as owner_id, u.account_id FROM users u where u.role = 'owner' group by u.account_id, u.id) subquery + WHERE board.account_id = subquery.account_id + and board.type = 'task' + and board.code is null; + + UPDATE board + SET participant_ids = subquery.participants FROM (SELECT json_agg(u.id) as participants, u.account_id + FROM users u + WHERE u.is_active = true + GROUP BY account_id) AS subquery + WHERE board.account_id = subquery.account_id + and board.type = 'task' + and board.code is null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1685604837960-AddChatModel.ts b/backend/src/database/migrations/archive/1685604837960-AddChatModel.ts new file mode 100644 index 0000000..23b0c1d --- /dev/null +++ b/backend/src/database/migrations/archive/1685604837960-AddChatModel.ts @@ -0,0 +1,109 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatModel1685604837960 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists chat_provider_id_seq as integer minvalue 1; + create table chat_provider ( + id integer, + created_by integer not null, + type character varying not null, + title character varying, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (created_by) references users(id), + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists chat_provider_user_id_seq as integer minvalue 1; + create table chat_provider_user ( + id integer, + provider_id integer not null, + user_id integer not null, + account_id integer, + primary key (id), + foreign key (provider_id) references chat_provider(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists chat_id_seq as integer minvalue 1; + create table chat ( + id integer, + provider_id integer not null, + created_by integer not null, + external_id character varying, + type character varying not null, + title character varying, + entity_id integer, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (provider_id) references chat_provider(id), + foreign key (created_by) references users(id), + foreign key (entity_id) references entity(id) on delete set null, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists chat_user_id_seq as integer minvalue 1; + create table chat_user ( + id integer, + chat_id integer not null, + user_id integer not null, + external_id character varying, + role character varying not null, + account_id integer not null, + primary key (id), + foreign key (chat_id) references chat(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists chat_message_id_seq as integer minvalue 1; + create table chat_message ( + id integer, + chat_id integer not null, + chat_user_id integer not null, + external_id character varying, + text character varying not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (chat_id) references chat(id) on delete cascade, + foreign key (chat_user_id) references chat_user(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists chat_message_file_id_seq as integer minvalue 1; + create table chat_message_file ( + id integer, + message_id integer not null, + external_id character varying, + file_link_id integer, + account_id integer not null, + primary key (id), + foreign key (message_id) references chat_message(id) on delete cascade, + foreign key (file_link_id) references file_link(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create table chat_message_user_status ( + message_id integer not null, + chat_user_id integer not null, + status character varying not null, + account_id integer not null, + created_at timestamp without time zone not null, + foreign key (message_id) references chat_message(id) on delete cascade, + foreign key (chat_user_id) references chat_user(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + primary key (message_id, chat_user_id) + ); + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1685689401123-AddDefaultChatProvider.ts b/backend/src/database/migrations/archive/1685689401123-AddDefaultChatProvider.ts new file mode 100644 index 0000000..728302d --- /dev/null +++ b/backend/src/database/migrations/archive/1685689401123-AddDefaultChatProvider.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddDefaultChatProvider1685689401123 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into chat_provider + select + nextval('chat_provider_id_seq') as id, + users.id as created_by, + 'amwork' as type, + account.company_name || ' Chat' as title, + account.id as account_id, + account.created_at as created_at + from account join users on users.account_id = account.id and users.role = 'owner'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686048795624-AddBoardIdToTask.ts b/backend/src/database/migrations/archive/1686048795624-AddBoardIdToTask.ts new file mode 100644 index 0000000..bdefdf8 --- /dev/null +++ b/backend/src/database/migrations/archive/1686048795624-AddBoardIdToTask.ts @@ -0,0 +1,87 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddBoardIdToTask1686048795624 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task + add column board_id integer, + add foreign key (board_id) references board(id) on delete cascade; + + update task + set board_id = (select s.board_id from stage s where s.id = task.stage_id); + + drop view all_tasks; + + create or replace view all_tasks ( + id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + weight, + activity_type_id, + title, + planned_time, + settings_id, + board_id, + stage_id, + type + ) as + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + result, + entity_id, + weight, + activity_type_id, + null as title, + null as planned_time, + null as settings_id, + null as board_id, + null as stage_id, + 'activity' as type + from activity + union + select id, + created_at, + account_id, + created_by, + responsible_user_id, + text, + start_date, + end_date, + is_resolved, + resolved_date, + null as result, + entity_id, + weight, + null as activity_type_id, + title, + planned_time, + settings_id, + board_id, + stage_id, + 'task' as type + from task + order by id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686061937533-AlterChatMEssageFile.ts b/backend/src/database/migrations/archive/1686061937533-AlterChatMEssageFile.ts new file mode 100644 index 0000000..66112a9 --- /dev/null +++ b/backend/src/database/migrations/archive/1686061937533-AlterChatMEssageFile.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatMEssageFile1686061937533 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_message_file + drop column file_link_id, + add column file_id uuid, + add column name character varying not null, + add column mime_type character varying not null, + add column size integer not null, + add column created_at timestamp without time zone not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686297344564-AlterMailMessageEntityId.ts b/backend/src/database/migrations/archive/1686297344564-AlterMailMessageEntityId.ts new file mode 100644 index 0000000..aa425d5 --- /dev/null +++ b/backend/src/database/migrations/archive/1686297344564-AlterMailMessageEntityId.ts @@ -0,0 +1,17 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessageEntityId1686297344564 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + + alter table mail_message + drop constraint mail_message_contact_entity_id_fkey, + add constraint mail_message_contact_entity_id_fkey foreign key (entity_id) references entity(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686310775887-AddSignatureToEmailActionSettings.ts b/backend/src/database/migrations/archive/1686310775887-AddSignatureToEmailActionSettings.ts new file mode 100644 index 0000000..9c51463 --- /dev/null +++ b/backend/src/database/migrations/archive/1686310775887-AddSignatureToEmailActionSettings.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSignatureToEmailActionSettings1686310775887 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table email_action_settings + add signature varchar; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686643536303-AddReplayToInChatMessage.ts b/backend/src/database/migrations/archive/1686643536303-AddReplayToInChatMessage.ts new file mode 100644 index 0000000..b7046e9 --- /dev/null +++ b/backend/src/database/migrations/archive/1686643536303-AddReplayToInChatMessage.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddReplayToInChatMessage1686643536303 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_message + add column replay_to_id integer, + add foreign key (replay_to_id) references chat_message(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686736715335-AddChatPinnedMessage.ts b/backend/src/database/migrations/archive/1686736715335-AddChatPinnedMessage.ts new file mode 100644 index 0000000..caf9198 --- /dev/null +++ b/backend/src/database/migrations/archive/1686736715335-AddChatPinnedMessage.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatPinnedMessage1686736715335 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat + add column pinned_message_id integer, + add foreign key (pinned_message_id) references chat_message(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686816157824-AddChatPinnedMessage.ts b/backend/src/database/migrations/archive/1686816157824-AddChatPinnedMessage.ts new file mode 100644 index 0000000..80322ea --- /dev/null +++ b/backend/src/database/migrations/archive/1686816157824-AddChatPinnedMessage.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatPinnedMessage1686816157824 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_pinned_message ( + chat_id integer, + message_id integer, + created_at timestamp without time zone not null, + account_id integer not null, + primary key (chat_id, message_id), + foreign key (chat_id) references chat(id) on delete cascade, + foreign key (message_id) references chat_message(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + alter table chat drop column pinned_message_id; + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686824143539-AddChatMessageReaction.ts b/backend/src/database/migrations/archive/1686824143539-AddChatMessageReaction.ts new file mode 100644 index 0000000..71785cf --- /dev/null +++ b/backend/src/database/migrations/archive/1686824143539-AddChatMessageReaction.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatMessageReaction1686824143539 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists chat_message_reaction_id_seq as integer minvalue 1; + + create table chat_message_reaction ( + id integer, + message_id integer not null, + chat_user_id integer not null, + reaction character varying not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (message_id) references chat_message(id) on delete cascade, + foreign key (chat_user_id) references chat_user(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686840724427-AddProducts.ts b/backend/src/database/migrations/archive/1686840724427-AddProducts.ts new file mode 100644 index 0000000..8acceb5 --- /dev/null +++ b/backend/src/database/migrations/archive/1686840724427-AddProducts.ts @@ -0,0 +1,60 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProducts1686840724427 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table product_category + ( + id integer, + name character varying not null, + parent_id integer, + created_by integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (parent_id) references product_category (id), + foreign key (created_by) references users (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence product_category_id_seq as integer minvalue 1; + + create table product + ( + id integer, + name character varying not null, + description character varying, + sku character varying, + unit character varying, + tax smallint, + isDeleted boolean not null, + category_id integer, + created_by integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (category_id) references product_category (id), + foreign key (created_by) references users (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence product_id_seq as integer minvalue 1; + + create table product_price + ( + id integer, + name character varying, + unit_price integer, + currency character varying(3) not null, + product_id integer not null, + account_id integer not null, + primary key (id), + foreign key (product_id) references product (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + create sequence product_price_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686904432256-AddChatIdToStatus.ts b/backend/src/database/migrations/archive/1686904432256-AddChatIdToStatus.ts new file mode 100644 index 0000000..7934a00 --- /dev/null +++ b/backend/src/database/migrations/archive/1686904432256-AddChatIdToStatus.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatIdToStatus1686904432256 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table chat_message_user_status; + + create table chat_message_user_status ( + chat_id integer not null, + message_id integer not null, + chat_user_id integer not null, + status character varying not null, + account_id integer not null, + created_at timestamp without time zone not null, + foreign key (chat_id) references chat(id) on delete cascade, + foreign key (message_id) references chat_message(id) on delete cascade, + foreign key (chat_user_id) references chat_user(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + primary key (chat_id, message_id, chat_user_id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1686930758334-RenameProductField.ts b/backend/src/database/migrations/archive/1686930758334-RenameProductField.ts new file mode 100644 index 0000000..62b5be9 --- /dev/null +++ b/backend/src/database/migrations/archive/1686930758334-RenameProductField.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameProductField1686930758334 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product + rename column isdeleted to is_deleted; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687015795997-AlterChatMessageReply.ts b/backend/src/database/migrations/archive/1687015795997-AlterChatMessageReply.ts new file mode 100644 index 0000000..2bc01b2 --- /dev/null +++ b/backend/src/database/migrations/archive/1687015795997-AlterChatMessageReply.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatMessageReply1687015795997 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_message + drop column replay_to_id, + add column reply_to_id integer, + add foreign key (reply_to_id) references chat_message(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687350416742-RemoveIdFromSubscription.ts b/backend/src/database/migrations/archive/1687350416742-RemoveIdFromSubscription.ts new file mode 100644 index 0000000..a7a8dbf --- /dev/null +++ b/backend/src/database/migrations/archive/1687350416742-RemoveIdFromSubscription.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveIdFromSubscription1687350416742 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subscription + drop column id, + add primary key (account_id); + + drop sequence subscription_id_seq; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687351857599-AddSubscriptionExternalCustomerId.ts b/backend/src/database/migrations/archive/1687351857599-AddSubscriptionExternalCustomerId.ts new file mode 100644 index 0000000..d23e1fc --- /dev/null +++ b/backend/src/database/migrations/archive/1687351857599-AddSubscriptionExternalCustomerId.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSubscriptionExternalCustomerId1687351857599 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subscription add column external_customer_id character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687790975332-AddOrder.ts b/backend/src/database/migrations/archive/1687790975332-AddOrder.ts new file mode 100644 index 0000000..6c639ea --- /dev/null +++ b/backend/src/database/migrations/archive/1687790975332-AddOrder.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddOrder1687790975332 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table orders + ( + id integer, + total_amount numeric(13, 2) not null, + currency character varying(3) not null, + entity_id integer not null, + created_by integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (entity_id) references entity (id) on delete cascade, + foreign key (created_by) references users (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence order_id_seq as integer minvalue 1; + + create table order_item + ( + id integer, + unit_price numeric(13, 2) not null, + quantity integer not null, + tax numeric(3, 2) not null, + discount numeric(3, 2) not null, + product_id integer not null, + order_id integer not null, + sort_order smallint not null, + account_id integer not null, + primary key (id), + foreign key (product_id) references product (id), + foreign key (order_id) references orders (id) on delete cascade, + foreign key (account_id) references account (id) on delete cascade + ); + create sequence order_item_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687793191931-FixOrder.ts b/backend/src/database/migrations/archive/1687793191931-FixOrder.ts new file mode 100644 index 0000000..54ba6ce --- /dev/null +++ b/backend/src/database/migrations/archive/1687793191931-FixOrder.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixOrder1687793191931 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders + alter column total_amount type numeric(15, 2) using total_amount::numeric(15, 2); + + alter table order_item + alter column unit_price type numeric(15, 2) using tax::numeric(15, 2), + alter column tax type numeric(5, 2) using tax::numeric(5, 2), + alter column discount type numeric(5, 2) using tax::numeric(5, 2); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687877020115-AddTaxIncluded.ts b/backend/src/database/migrations/archive/1687877020115-AddTaxIncluded.ts new file mode 100644 index 0000000..6a22c6a --- /dev/null +++ b/backend/src/database/migrations/archive/1687877020115-AddTaxIncluded.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaxIncluded1687877020115 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders + add column tax_included boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687943824933-AddChatProviderTwilio.ts b/backend/src/database/migrations/archive/1687943824933-AddChatProviderTwilio.ts new file mode 100644 index 0000000..2356a7c --- /dev/null +++ b/backend/src/database/migrations/archive/1687943824933-AddChatProviderTwilio.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatProviderTwilio1687943824933 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_provider_twilio ( + provider_id integer not null, + account_sid character varying not null, + auth_token character varying not null, + whatsapp_number character varying not null, + account_id integer not null, + primary key (provider_id), + foreign key (provider_id) references chat_provider(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687954149882-AlterChatProviderUser.ts b/backend/src/database/migrations/archive/1687954149882-AlterChatProviderUser.ts new file mode 100644 index 0000000..0d5d6b8 --- /dev/null +++ b/backend/src/database/migrations/archive/1687954149882-AlterChatProviderUser.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatProviderUser1687954149882 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider_user + drop column id, + add primary key (provider_id, user_id); + + drop sequence chat_provider_user_id_seq; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687962117509-AddWarehouse.ts b/backend/src/database/migrations/archive/1687962117509-AddWarehouse.ts new file mode 100644 index 0000000..5c7d0ce --- /dev/null +++ b/backend/src/database/migrations/archive/1687962117509-AddWarehouse.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddWarehouse1687962117509 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table warehouse + ( + id integer, + name character varying, + created_by integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (created_by) references users (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence warehouse_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1687965328992-AddIsDeletedToWarehouse.ts b/backend/src/database/migrations/archive/1687965328992-AddIsDeletedToWarehouse.ts new file mode 100644 index 0000000..aead5e9 --- /dev/null +++ b/backend/src/database/migrations/archive/1687965328992-AddIsDeletedToWarehouse.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIsDeletedToWarehouse1687965328992 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table warehouse + add column is_deleted boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688025794222-AlterChatUser.ts b/backend/src/database/migrations/archive/1688025794222-AlterChatUser.ts new file mode 100644 index 0000000..329d4ba --- /dev/null +++ b/backend/src/database/migrations/archive/1688025794222-AlterChatUser.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatUser1688025794222 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_user alter column user_id drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688044274695-AddStocks.ts b/backend/src/database/migrations/archive/1688044274695-AddStocks.ts new file mode 100644 index 0000000..aeb8067 --- /dev/null +++ b/backend/src/database/migrations/archive/1688044274695-AddStocks.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddStocks1688044274695 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table stock + ( + product_id integer not null, + warehouse_id integer not null, + stock_quantity integer not null, + account_id integer not null, + primary key (product_id, warehouse_id), + foreign key (product_id) references product (id), + foreign key (warehouse_id) references warehouse (id), + foreign key (account_id) references account (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688053486248-AddChatUserExternalName.ts b/backend/src/database/migrations/archive/1688053486248-AddChatUserExternalName.ts new file mode 100644 index 0000000..cd85e0f --- /dev/null +++ b/backend/src/database/migrations/archive/1688053486248-AddChatUserExternalName.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatUserExternalName1688053486248 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_user add column external_name character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688112039219-AlterChatProviderTwilio.ts b/backend/src/database/migrations/archive/1688112039219-AlterChatProviderTwilio.ts new file mode 100644 index 0000000..f5afc58 --- /dev/null +++ b/backend/src/database/migrations/archive/1688112039219-AlterChatProviderTwilio.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatProviderTwilio1688112039219 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider_twilio rename column whatsapp_number to phone_number; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688130606571-AlterChatProviderUser.ts b/backend/src/database/migrations/archive/1688130606571-AlterChatProviderUser.ts new file mode 100644 index 0000000..0b80ac1 --- /dev/null +++ b/backend/src/database/migrations/archive/1688130606571-AlterChatProviderUser.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatProviderUser1688130606571 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create type chat_provider_user_type as enum ('accessible', 'responsible'); + alter table chat_provider_user + add column type chat_provider_user_type not null default 'accessible', + alter column account_id set not null; + + alter table chat_provider_user + drop constraint chat_provider_user_pkey; + + alter table chat_provider_user + add constraint chat_provider_user_pkey primary key (provider_id, user_id, type); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688136613049-AlterChat.ts b/backend/src/database/migrations/archive/1688136613049-AlterChat.ts new file mode 100644 index 0000000..8625624 --- /dev/null +++ b/backend/src/database/migrations/archive/1688136613049-AlterChat.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChat1688136613049 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat + drop constraint chat_created_by_fkey, + alter column created_by drop not null, + add constraint chat_created_by_fkey foreign key (created_by) references users(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688138872050-AddOrderStatus.ts b/backend/src/database/migrations/archive/1688138872050-AddOrderStatus.ts new file mode 100644 index 0000000..5a9426b --- /dev/null +++ b/backend/src/database/migrations/archive/1688138872050-AddOrderStatus.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddOrderStatus1688138872050 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table order_status + ( + id integer, + name varchar(255) not null, + color varchar(50) not null, + code varchar(50), + sort_order smallint not null, + account_id integer not null, + primary key (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence order_status_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688139271540-AddShipment.ts b/backend/src/database/migrations/archive/1688139271540-AddShipment.ts new file mode 100644 index 0000000..8b86ce4 --- /dev/null +++ b/backend/src/database/migrations/archive/1688139271540-AddShipment.ts @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddShipment1688139271540 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table shipment_status + ( + id integer, + name varchar(255) not null, + color varchar(50) not null, + code varchar(50), + sort_order smallint not null, + account_id integer not null, + primary key (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence shipment_status_id_seq as integer minvalue 1; + + create table shipment + ( + id integer, + name varchar(255) not null, + warehouse_id integer not null, + order_id integer not null, + status_id integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (warehouse_id) references warehouse (id), + foreign key (order_id) references orders (id), + foreign key (status_id) references shipment_status (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence shipment_id_seq as integer minvalue 1; + + create table shipment_item + ( + id integer, + shipment_id integer not null, + product_id integer not null, + quantity integer not null, + account_id integer not null, + primary key (id), + foreign key (shipment_id) references shipment (id) on delete cascade, + foreign key (product_id) references product (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence shipment_item_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688140521166-AddStatusIdToOrder.ts b/backend/src/database/migrations/archive/1688140521166-AddStatusIdToOrder.ts new file mode 100644 index 0000000..68035fa --- /dev/null +++ b/backend/src/database/migrations/archive/1688140521166-AddStatusIdToOrder.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddStatusIdToOrder1688140521166 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders + add column status_id integer references order_status (id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688388514670-AddReservation.ts b/backend/src/database/migrations/archive/1688388514670-AddReservation.ts new file mode 100644 index 0000000..22c8e63 --- /dev/null +++ b/backend/src/database/migrations/archive/1688388514670-AddReservation.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddReservation1688388514670 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table reservation + ( + id integer, + order_id integer not null, + order_item_id integer not null, + product_id integer not null, + warehouse_id integer not null, + quantity integer not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (order_id) references orders (id) on delete cascade, + foreign key (order_item_id) references order_item (id) on delete cascade, + foreign key (product_id) references product (id), + foreign key (warehouse_id) references warehouse (id), + foreign key (account_id) references account (id) on delete cascade + ); + create sequence reservation_id_seq as integer minvalue 1; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688390259595-AlterFileInfoCreatedBy.ts b/backend/src/database/migrations/archive/1688390259595-AlterFileInfoCreatedBy.ts new file mode 100644 index 0000000..c1cc69c --- /dev/null +++ b/backend/src/database/migrations/archive/1688390259595-AlterFileInfoCreatedBy.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterFileInfoCreatedBy1688390259595 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table file_info + drop constraint file_info_created_by_fkey, + alter column created_by drop not null, + add constraint file_info_created_by_fkey foreign key (created_by) references users(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688394200229-FixStatusIdInOrder.ts b/backend/src/database/migrations/archive/1688394200229-FixStatusIdInOrder.ts new file mode 100644 index 0000000..3405e4d --- /dev/null +++ b/backend/src/database/migrations/archive/1688394200229-FixStatusIdInOrder.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixStatusIdInOrder1688394200229 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders + alter column status_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688472386401-AddShipmentDate.ts b/backend/src/database/migrations/archive/1688472386401-AddShipmentDate.ts new file mode 100644 index 0000000..adb0fb5 --- /dev/null +++ b/backend/src/database/migrations/archive/1688472386401-AddShipmentDate.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddShipmentDate1688472386401 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table shipment + add column shipped_at timestamp without time zone default null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688543908016-AddChatProviderMessenger.ts b/backend/src/database/migrations/archive/1688543908016-AddChatProviderMessenger.ts new file mode 100644 index 0000000..4b23bcd --- /dev/null +++ b/backend/src/database/migrations/archive/1688543908016-AddChatProviderMessenger.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatProviderMessenger1688543908016 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_provider_messenger ( + provider_id integer, + page_id character varying not null, + page_access_token character varying not null, + account_id integer not null, + primary key (provider_id), + foreign key (provider_id) references chat_provider(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688567846856-AlterChatUser.ts b/backend/src/database/migrations/archive/1688567846856-AlterChatUser.ts new file mode 100644 index 0000000..2eff1da --- /dev/null +++ b/backend/src/database/migrations/archive/1688567846856-AlterChatUser.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatUser1688567846856 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_user rename column external_name to external_first_name; + alter table chat_user + add column external_last_name character varying, + add column external_avatar_url character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1688996628275-FixCategoryIdConstraint.ts b/backend/src/database/migrations/archive/1688996628275-FixCategoryIdConstraint.ts new file mode 100644 index 0000000..dd7b709 --- /dev/null +++ b/backend/src/database/migrations/archive/1688996628275-FixCategoryIdConstraint.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixCategoryIdConstraint1688996628275 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + queryRunner.query(` + alter table product + drop constraint product_category_id_fkey; + + alter table product + add foreign key (category_id) references product_category + on delete set null; + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689059395581-AddChatProviderStatus.ts b/backend/src/database/migrations/archive/1689059395581-AddChatProviderStatus.ts new file mode 100644 index 0000000..605b974 --- /dev/null +++ b/backend/src/database/migrations/archive/1689059395581-AddChatProviderStatus.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatProviderStatus1689059395581 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider add column status character varying not null default 'draft'; + update chat_provider set status = 'active'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689068374394-AddUserIdToMessengerProvider.ts b/backend/src/database/migrations/archive/1689068374394-AddUserIdToMessengerProvider.ts new file mode 100644 index 0000000..90dae99 --- /dev/null +++ b/backend/src/database/migrations/archive/1689068374394-AddUserIdToMessengerProvider.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserIdToMessengerProvider1689068374394 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider_messenger add column user_id character varying not null; + alter table chat_provider_messenger add column user_access_token character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689081064483-AddWarehouseIdToOrder.ts b/backend/src/database/migrations/archive/1689081064483-AddWarehouseIdToOrder.ts new file mode 100644 index 0000000..240f2fd --- /dev/null +++ b/backend/src/database/migrations/archive/1689081064483-AddWarehouseIdToOrder.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddWarehouseIdToOrder1689081064483 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders + add column warehouse_id integer default null, + add foreign key (warehouse_id) references warehouse (id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689087134759-AddModules.ts b/backend/src/database/migrations/archive/1689087134759-AddModules.ts new file mode 100644 index 0000000..dfb87b3 --- /dev/null +++ b/backend/src/database/migrations/archive/1689087134759-AddModules.ts @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddModules1689087134759 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table module + ( + id integer, + name varchar(255) not null, + code varchar(100) not null, + is_enabled boolean not null, + created_at timestamp without time zone not null, + primary key (id) + ); + create sequence module_id_seq as integer minvalue 1; + + insert into module (id, name, code, is_enabled, created_at) + values (nextval('module_id_seq'), 'Products', 'products', true, now()); + + create table account_module + ( + account_id integer not null, + module_id integer not null, + primary key (account_id, module_id), + foreign key (account_id) references account (id) on delete cascade, + foreign key (module_id) references module (id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689170448447-AddProductType.ts b/backend/src/database/migrations/archive/1689170448447-AddProductType.ts new file mode 100644 index 0000000..6f1bfe3 --- /dev/null +++ b/backend/src/database/migrations/archive/1689170448447-AddProductType.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProductType1689170448447 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product + add column type varchar(50) not null default 'product'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689243925753-FixOrderStatuses.ts b/backend/src/database/migrations/archive/1689243925753-FixOrderStatuses.ts new file mode 100644 index 0000000..7e1d424 --- /dev/null +++ b/backend/src/database/migrations/archive/1689243925753-FixOrderStatuses.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixOrderStatuses1689243925753 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update order_status + set name = 'Reserved', + color = '#FFE385', + code = 'reserved', + sort_order = 0 + where code = 'pending'; + + update order_status + set name = 'Sent for shipment', + color = '#A7DFDC', + code = 'sent_for_shipment', + sort_order = 1 + where code = 'confirmed'; + + update order_status + set color = '#A8E379', + sort_order = 2 + where code = 'shipped'; + + update order_status + set color = '#FC7483', + sort_order = 3 + where code = 'cancelled'; + + update order_status + set name = 'Returned', + code = 'returned', + color = '#DCDDE0', + sort_order = 4 + where code = 'completed'; + + update orders + set status_id = (select id from order_status where code = 'reserved' and account_id = orders.account_id) + where status_id = (select id + from order_status + where code = 'delivered' + and account_id = orders.account_id); + + delete + from order_status + where code = 'delivered'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689259268310-DeleteShipmentStatus.ts b/backend/src/database/migrations/archive/1689259268310-DeleteShipmentStatus.ts new file mode 100644 index 0000000..c4850f4 --- /dev/null +++ b/backend/src/database/migrations/archive/1689259268310-DeleteShipmentStatus.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteShipmentStatus1689259268310 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table shipment drop column status_id; + + alter table shipment + add column status_id integer, + add foreign key (status_id) references order_status(id); + + update shipment + set status_id = (select id + from order_status + where code = 'sent_for_shipment' + and account_id = shipment.account_id); + + alter table shipment + alter column status_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689337185167-AddProductsFeature.ts b/backend/src/database/migrations/archive/1689337185167-AddProductsFeature.ts new file mode 100644 index 0000000..351d7c8 --- /dev/null +++ b/backend/src/database/migrations/archive/1689337185167-AddProductsFeature.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProductsFeature1689337185167 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into feature(name, code, is_enabled) values('Products', 'products', true); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689508562776-AddIsActiveToReservation.ts b/backend/src/database/migrations/archive/1689508562776-AddIsActiveToReservation.ts new file mode 100644 index 0000000..9ad248e --- /dev/null +++ b/backend/src/database/migrations/archive/1689508562776-AddIsActiveToReservation.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIsActiveToReservation1689508562776 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table reservation + add column is_active boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689763128902-RemoveNoteRecipientId.ts b/backend/src/database/migrations/archive/1689763128902-RemoveNoteRecipientId.ts new file mode 100644 index 0000000..39f4d05 --- /dev/null +++ b/backend/src/database/migrations/archive/1689763128902-RemoveNoteRecipientId.ts @@ -0,0 +1,43 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveNoteRecipientId1689763128902 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop view feed_items; + + create or replace view feed_items (id, created_at, entity_id, type) as + select note.id, note.created_at, note.entity_id, 'note'::text as type from note + union + select task.id, task.created_at, task.entity_id, 'task'::text as type + from task + union + select activity.id, activity.created_at, activity.entity_id, 'activity'::text as type + from activity + union + (select max(message.id) as id, max(message.date) as created_at, message.entity_id as entity_id, 'mail'::text as type + from mail_message message + where message.entity_id is not null + group by message.thread_id, message.entity_id + order by (max(message.date)) desc) + union + (select max(message.id) as id, max(message.date) as created_at, el.source_id as entity_id, 'mail'::text as type + from mail_message message + right join entity_link el ON el.target_id = message.entity_id + where message.entity_id is not null + group by message.thread_id, el.source_id + order by (max(message.date)) desc) + union + select fl.id, fl.created_at, fl.source_id, 'document'::text as type + from file_link fl + where fl.source_type = 'entity_document' + order by created_at desc; + + alter table note drop column recipient_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689774963182-AddUpdatedAtToProduct.ts b/backend/src/database/migrations/archive/1689774963182-AddUpdatedAtToProduct.ts new file mode 100644 index 0000000..275cdaf --- /dev/null +++ b/backend/src/database/migrations/archive/1689774963182-AddUpdatedAtToProduct.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUpdatedAtToProduct1689774963182 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product + add column updated_at timestamp without time zone default now(); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689860682052-AddSchedule.ts b/backend/src/database/migrations/archive/1689860682052-AddSchedule.ts new file mode 100644 index 0000000..db4eeb4 --- /dev/null +++ b/backend/src/database/migrations/archive/1689860682052-AddSchedule.ts @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSchedule1689860682052 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists schedule_id_seq as integer minvalue 1; + create table schedule ( + id integer, + name character varying not null, + use_product boolean not null, + entity_type_id integer, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (entity_type_id) references entity_type(id) on delete set null, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists schedule_performer_id_seq as integer minvalue 1; + create table schedule_performer ( + id integer, + schedule_id integer not null, + user_id integer not null, + account_id integer not null, + primary key (id), + foreign key (schedule_id) references schedule(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + create sequence if not exists schedule_event_id_seq as integer minvalue 1; + create table schedule_event ( + id integer, + schedule_id integer not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + status character varying not null, + comment character varying, + owner_id integer not null, + entity_id integer, + performer_id integer, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (schedule_id) references schedule(id) on delete cascade, + foreign key (owner_id) references users(id), + foreign key (entity_id) references entity(id) on delete set null, + foreign key (performer_id) references schedule_performer(id) on delete set null, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1689933154489-AlterSchedulePerformer.ts b/backend/src/database/migrations/archive/1689933154489-AlterSchedulePerformer.ts new file mode 100644 index 0000000..d988c87 --- /dev/null +++ b/backend/src/database/migrations/archive/1689933154489-AlterSchedulePerformer.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedulePerformer1689933154489 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_event + drop constraint schedule_event_performer_id_fkey, + add foreign key (performer_id) references users(id); + + alter table schedule_performer drop column id; + drop sequence schedule_performer_id_seq; + + alter table schedule_performer + add primary key (schedule_id, user_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690208012261-AddProductModule.ts b/backend/src/database/migrations/archive/1690208012261-AddProductModule.ts new file mode 100644 index 0000000..06da3ae --- /dev/null +++ b/backend/src/database/migrations/archive/1690208012261-AddProductModule.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProductModule1690208012261 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create sequence if not exists product_module_id_seq as integer minvalue 1; + + create table product_module ( + id integer, + name character varying not null, + icon character varying not null, + account_id integer not null, + created_at timestamp without time zone not null default now(), + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690456178510-MigrateModuleToProductModule.ts b/backend/src/database/migrations/archive/1690456178510-MigrateModuleToProductModule.ts new file mode 100644 index 0000000..ce7db98 --- /dev/null +++ b/backend/src/database/migrations/archive/1690456178510-MigrateModuleToProductModule.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MigrateModuleToProductModule1690456178510 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from product_module; + + alter table product_module + alter column id set not null, + alter column id add generated by default as identity; + + drop sequence product_module_id_seq; + + insert into product_module(name, icon, account_id) + select m.name as name, 'box' as icon, am.account_id as account_id + from account_module am + join module m on m.id = am.module_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690467527775-RemoveModule.ts b/backend/src/database/migrations/archive/1690467527775-RemoveModule.ts new file mode 100644 index 0000000..fdf6a54 --- /dev/null +++ b/backend/src/database/migrations/archive/1690467527775-RemoveModule.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveModule1690467527775 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table account_module; + drop table module; + drop sequence module_id_seq; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690469860109-AddProductPermissions.ts b/backend/src/database/migrations/archive/1690469860109-AddProductPermissions.ts new file mode 100644 index 0000000..88ecb71 --- /dev/null +++ b/backend/src/database/migrations/archive/1690469860109-AddProductPermissions.ts @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProductPermissions1690469860109 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table product_permissions ( + id integer generated by default as identity, + account_id integer not null, + user_id integer not null, + module_id integer not null, + view_product boolean not null, + create_product boolean not null, + edit_product boolean not null, + create_order boolean not null, + manage_shipping boolean not null, + delete_product boolean not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (module_id) references product_module(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690543599386-DropProductPermissions.ts b/backend/src/database/migrations/archive/1690543599386-DropProductPermissions.ts new file mode 100644 index 0000000..e0d9770 --- /dev/null +++ b/backend/src/database/migrations/archive/1690543599386-DropProductPermissions.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DropProductPermissions1690543599386 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table if exists product_permissions; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690817128717-AlterProductModule.ts b/backend/src/database/migrations/archive/1690817128717-AlterProductModule.ts new file mode 100644 index 0000000..0b5c21c --- /dev/null +++ b/backend/src/database/migrations/archive/1690817128717-AlterProductModule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterProductModule1690817128717 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product_module rename to products_section; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1690973831680-AddSectionIdToProducts.ts b/backend/src/database/migrations/archive/1690973831680-AddSectionIdToProducts.ts new file mode 100644 index 0000000..ac05d17 --- /dev/null +++ b/backend/src/database/migrations/archive/1690973831680-AddSectionIdToProducts.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSectionIdToProducts1690973831680 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product + add column section_id integer, + add foreign key (section_id) references products_section(id) on delete cascade; + update product p set section_id = ps.id from products_section ps where p.account_id = ps.account_id; + alter table product alter column section_id set not null; + + alter table product_category + add column section_id integer, + add foreign key (section_id) references products_section(id) on delete cascade; + update product_category pc set section_id = ps.id from products_section ps where pc.account_id = ps.account_id; + alter table product_category alter column section_id set not null; + + alter table warehouse + add column section_id integer, + add foreign key (section_id) references products_section(id) on delete cascade; + update warehouse w set section_id = ps.id from products_section ps where w.account_id = ps.account_id; + alter table warehouse alter column section_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691056504886-AddSectionIdToOrderAndShipment.ts b/backend/src/database/migrations/archive/1691056504886-AddSectionIdToOrderAndShipment.ts new file mode 100644 index 0000000..b8ecdc5 --- /dev/null +++ b/backend/src/database/migrations/archive/1691056504886-AddSectionIdToOrderAndShipment.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSectionIdToOrderAndShipment1691056504886 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table shipment + add column section_id integer, + add foreign key (section_id) references products_section(id) on delete cascade; + update shipment s set section_id = ps.id from products_section ps where s.account_id = ps.account_id; + alter table shipment alter column section_id set not null; + + alter table orders + add column section_id integer, + add foreign key (section_id) references products_section(id) on delete cascade; + update orders o set section_id = ps.id from products_section ps where o.account_id = ps.account_id; + alter table orders alter column section_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691061102493-DeleteShipmentStatus.ts b/backend/src/database/migrations/archive/1691061102493-DeleteShipmentStatus.ts new file mode 100644 index 0000000..3171015 --- /dev/null +++ b/backend/src/database/migrations/archive/1691061102493-DeleteShipmentStatus.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteShipmentStatus1691061102493 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table shipment_status; + drop sequence shipment_status_id_seq; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691061408646-AlterTableStockToProductStock.ts b/backend/src/database/migrations/archive/1691061408646-AlterTableStockToProductStock.ts new file mode 100644 index 0000000..821685b --- /dev/null +++ b/backend/src/database/migrations/archive/1691061408646-AlterTableStockToProductStock.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterTableStockToProductStock1691061408646 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table stock rename to product_stock; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691139996885-AddProductsSectionEntityType.ts b/backend/src/database/migrations/archive/1691139996885-AddProductsSectionEntityType.ts new file mode 100644 index 0000000..5464c73 --- /dev/null +++ b/backend/src/database/migrations/archive/1691139996885-AddProductsSectionEntityType.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProductsSectionEntityType1691139996885 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table products_section_entity_type ( + section_id integer, + entity_type_id integer, + account_id integer not null, + primary key (section_id, entity_type_id), + foreign key (section_id) references products_section(id) on delete cascade, + foreign key (entity_type_id) references entity_type(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + + insert into products_section_entity_type + select ps.id as section_id, et.id as entity_type_id, ps.account_id as account_id + from products_section ps + inner join entity_type et on 1=1 + inner join entity_type_feature etf on et.id = etf.entity_type_id + inner join feature f on f.id = etf.feature_id and f.code = 'products'; + + update feature set is_enabled = false where code = 'products'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691155049107-AddRentalInterval.ts b/backend/src/database/migrations/archive/1691155049107-AddRentalInterval.ts new file mode 100644 index 0000000..d1ce3e1 --- /dev/null +++ b/backend/src/database/migrations/archive/1691155049107-AddRentalInterval.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRentalInterval1691155049107 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table rental_interval ( + id integer generated by default as identity, + section_id integer not null, + type character varying not null, + start_time time without time zone, + account_id integer not null, + primary key (id), + foreign key (section_id) references products_section(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691397636905-AlterProductsSectionType.ts b/backend/src/database/migrations/archive/1691397636905-AlterProductsSectionType.ts new file mode 100644 index 0000000..7f8775c --- /dev/null +++ b/backend/src/database/migrations/archive/1691397636905-AlterProductsSectionType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterProductsSectionType1691397636905 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table products_section add column "type" character varying not null default 'sale'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691411118591-AddRentalOrder.ts b/backend/src/database/migrations/archive/1691411118591-AddRentalOrder.ts new file mode 100644 index 0000000..1b88dbb --- /dev/null +++ b/backend/src/database/migrations/archive/1691411118591-AddRentalOrder.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRentalOrder1691411118591 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table rental_order ( + id integer generated by default as identity, + section_id integer not null, + warehouse_id integer, + entity_id integer not null, + created_by integer not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + status character varying not null, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (section_id) references products_section(id) on delete cascade, + foreign key (warehouse_id) references warehouse(id), + foreign key (entity_id) references entity(id) on delete cascade, + foreign key (created_by) references users(id), + foreign key (account_id) references account(id) on delete cascade + ); + + create table rental_order_item ( + id integer generated by default as identity, + order_id integer not null, + product_id integer not null, + warehouse_id integer, + sort_order integer not null, + account_id integer not null, + primary key (id), + foreign key (order_id) references rental_order(id) on delete cascade, + foreign key (product_id) references product(id), + foreign key (warehouse_id) references warehouse(id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691414938591-AddRentalSchedule.ts b/backend/src/database/migrations/archive/1691414938591-AddRentalSchedule.ts new file mode 100644 index 0000000..b67c816 --- /dev/null +++ b/backend/src/database/migrations/archive/1691414938591-AddRentalSchedule.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRentalSchedule1691414938591 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table rental_schedule ( + id integer generated by default as identity, + product_id integer not null, + order_item_id integer not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + status character varying not null, + account_id integer not null, + primary key (id), + foreign key (product_id) references product(id) on delete cascade, + foreign key (order_item_id) references rental_order_item(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691657890280-AddRentalOrderPeriod.ts b/backend/src/database/migrations/archive/1691657890280-AddRentalOrderPeriod.ts new file mode 100644 index 0000000..a6a3e78 --- /dev/null +++ b/backend/src/database/migrations/archive/1691657890280-AddRentalOrderPeriod.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRentalOrderPeriod1691657890280 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table rental_order_period ( + id integer generated by default as identity, + order_id integer not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + primary key (id), + foreign key (order_id) references rental_order(id) on delete cascade + ); + + alter table rental_order + drop column start_date, + drop column end_date; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691678125349-AddRentalOrderPeriodAccountId.ts b/backend/src/database/migrations/archive/1691678125349-AddRentalOrderPeriodAccountId.ts new file mode 100644 index 0000000..35b8681 --- /dev/null +++ b/backend/src/database/migrations/archive/1691678125349-AddRentalOrderPeriodAccountId.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRentalOrderPeriodAccountId1691678125349 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table rental_order_period + add column account_id integer not null, + add foreign key (account_id) references account(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691754596482-AlterRentalOrder.ts b/backend/src/database/migrations/archive/1691754596482-AlterRentalOrder.ts new file mode 100644 index 0000000..b472365 --- /dev/null +++ b/backend/src/database/migrations/archive/1691754596482-AlterRentalOrder.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterRentalOrder1691754596482 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table rental_order + add column currency character varying, + add column tax_included boolean not null default true; + + update rental_order set currency = 'rub'; + + alter table rental_order alter column currency set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1691755141714-AlterRentalOrderItem.ts b/backend/src/database/migrations/archive/1691755141714-AlterRentalOrderItem.ts new file mode 100644 index 0000000..5bf2ace --- /dev/null +++ b/backend/src/database/migrations/archive/1691755141714-AlterRentalOrderItem.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterRentalOrderItem1691755141714 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from rental_order; + + alter table rental_order_item + drop column warehouse_id, + add column unit_price numeric(15,2) not null, + add column tax numeric(5,2) not null, + add column discount numeric(5,2) not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692002092660-RentalScheduleAddSectionId.ts b/backend/src/database/migrations/archive/1692002092660-RentalScheduleAddSectionId.ts new file mode 100644 index 0000000..437bbc7 --- /dev/null +++ b/backend/src/database/migrations/archive/1692002092660-RentalScheduleAddSectionId.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RentalScheduleAddSectionId1692002092660 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from rental_schedule; + + alter table rental_schedule + add column section_id integer not null, + add foreign key (section_id) references products_section(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692014115943-RenameRentalScheduleToRentalEvent.ts b/backend/src/database/migrations/archive/1692014115943-RenameRentalScheduleToRentalEvent.ts new file mode 100644 index 0000000..f3dcfc0 --- /dev/null +++ b/backend/src/database/migrations/archive/1692014115943-RenameRentalScheduleToRentalEvent.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameRentalScheduleToRentalEvent1692014115943 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table rental_schedule rename to rental_event; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692170842159-AddProductsSectionEnableWarehouse.ts b/backend/src/database/migrations/archive/1692170842159-AddProductsSectionEnableWarehouse.ts new file mode 100644 index 0000000..decf042 --- /dev/null +++ b/backend/src/database/migrations/archive/1692170842159-AddProductsSectionEnableWarehouse.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddProductsSectionEnableWarehouse1692170842159 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table products_section add column enable_warehouse boolean; + + update products_section set enable_warehouse = + (select count(warehouse.id) > 0 from warehouse where warehouse.section_id = products_section.id); + + alter table products_section alter column enable_warehouse set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692172254434-AddOrdersOrderNumber.ts b/backend/src/database/migrations/archive/1692172254434-AddOrdersOrderNumber.ts new file mode 100644 index 0000000..c473c34 --- /dev/null +++ b/backend/src/database/migrations/archive/1692172254434-AddOrdersOrderNumber.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddOrdersOrderNumber1692172254434 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders add column order_number integer; + + with NumberedOrders as ( + select + id, + row_number() over(partition by entity_id order by created_at asc) as new_order_number + from orders + ) + update orders + set order_number = NumberedOrders.new_order_number + from NumberedOrders + where orders.id = NumberedOrders.id; + + alter table orders alter column order_number set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692172318353-AddRentalOrderOrderNumber.ts b/backend/src/database/migrations/archive/1692172318353-AddRentalOrderOrderNumber.ts new file mode 100644 index 0000000..dca1559 --- /dev/null +++ b/backend/src/database/migrations/archive/1692172318353-AddRentalOrderOrderNumber.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddRentalOrderOrderNumber1692172318353 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table rental_order add column order_number integer; + + with NumberedOrders as ( + select + id, + row_number() over(partition by entity_id order by created_at asc) as new_order_number + from rental_order + ) + update rental_order + set order_number = NumberedOrders.new_order_number + from NumberedOrders + where rental_order.id = NumberedOrders.id; + + alter table rental_order alter column order_number set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692283603851-AlterOrderItemCascadeDelete.ts b/backend/src/database/migrations/archive/1692283603851-AlterOrderItemCascadeDelete.ts new file mode 100644 index 0000000..2484d2f --- /dev/null +++ b/backend/src/database/migrations/archive/1692283603851-AlterOrderItemCascadeDelete.ts @@ -0,0 +1,16 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterOrderItemCascadeDelete1692283603851 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table rental_order_item + drop constraint rental_order_item_product_id_fkey, + add constraint rental_order_item_product_id_fkey foreign key (product_id) references product(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692343747646-AlterProductStock.ts b/backend/src/database/migrations/archive/1692343747646-AlterProductStock.ts new file mode 100644 index 0000000..acc380d --- /dev/null +++ b/backend/src/database/migrations/archive/1692343747646-AlterProductStock.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterProductStock1692343747646 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product_stock + drop constraint stock_product_id_fkey, + drop constraint stock_warehouse_id_fkey, + add constraint stock_product_id_fkey foreign key (product_id) references product(id) on delete cascade, + add constraint stock_warehouse_id_fkey foreign key (warehouse_id) references warehouse(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692354371998-FixFieldValueType.ts b/backend/src/database/migrations/archive/1692354371998-FixFieldValueType.ts new file mode 100644 index 0000000..d700a8f --- /dev/null +++ b/backend/src/database/migrations/archive/1692354371998-FixFieldValueType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixFieldValueType1692354371998 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update field_value set field_type = (select type from field where field.id = field_value.field_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692604044210-ProductsSectionEnableBarcode.ts b/backend/src/database/migrations/archive/1692604044210-ProductsSectionEnableBarcode.ts new file mode 100644 index 0000000..c685223 --- /dev/null +++ b/backend/src/database/migrations/archive/1692604044210-ProductsSectionEnableBarcode.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductsSectionEnableBarcode1692604044210 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table products_section add column enable_barcode boolean default true not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692708295281-AlterOrderStatusNull.ts b/backend/src/database/migrations/archive/1692708295281-AlterOrderStatusNull.ts new file mode 100644 index 0000000..cbb10d4 --- /dev/null +++ b/backend/src/database/migrations/archive/1692708295281-AlterOrderStatusNull.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterOrderStatusNull1692708295281 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders alter column status_id drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692885285551-RefactorScheduler.ts b/backend/src/database/migrations/archive/1692885285551-RefactorScheduler.ts new file mode 100644 index 0000000..9340348 --- /dev/null +++ b/backend/src/database/migrations/archive/1692885285551-RefactorScheduler.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RefactorScheduler1692885285551 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop sequence schedule_id_seq; + alter table schedule + alter column id set not null, + alter column id add generated by default as identity; + + drop sequence schedule_event_id_seq; + alter table schedule_event + alter column id set not null, + alter column id add generated by default as identity; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692890628636-RenameScheduleEvent.ts b/backend/src/database/migrations/archive/1692890628636-RenameScheduleEvent.ts new file mode 100644 index 0000000..de48e14 --- /dev/null +++ b/backend/src/database/migrations/archive/1692890628636-RenameScheduleEvent.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameScheduleEvent1692890628636 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_event rename to schedule_appointment; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1692975377102-AlterSchedule.ts b/backend/src/database/migrations/archive/1692975377102-AlterSchedule.ts new file mode 100644 index 0000000..6799847 --- /dev/null +++ b/backend/src/database/migrations/archive/1692975377102-AlterSchedule.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1692975377102 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule + drop column use_product, + add column products_section_id integer, + add foreign key (products_section_id) references products_section(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1693218884137-UserPosition.ts b/backend/src/database/migrations/archive/1693218884137-UserPosition.ts new file mode 100644 index 0000000..84f5c2b --- /dev/null +++ b/backend/src/database/migrations/archive/1693218884137-UserPosition.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserPosition1693218884137 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users add column position character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1693232990040-ScheduleAppointmentOrderId.ts b/backend/src/database/migrations/archive/1693232990040-ScheduleAppointmentOrderId.ts new file mode 100644 index 0000000..faf37a9 --- /dev/null +++ b/backend/src/database/migrations/archive/1693232990040-ScheduleAppointmentOrderId.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduleAppointmentOrderId1693232990040 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_appointment + add column order_id integer, + add foreign key (order_id) references orders(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1693485238189-OrderStatusColor.ts b/backend/src/database/migrations/archive/1693485238189-OrderStatusColor.ts new file mode 100644 index 0000000..f31863b --- /dev/null +++ b/backend/src/database/migrations/archive/1693485238189-OrderStatusColor.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OrderStatusColor1693485238189 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update order_status set color='#ea925a' where code='reserved'; + update order_status set color='#a33cab' where code='sent_for_shipment'; + update order_status set color='#8af039' where code='shipped'; + update order_status set color='#ee675c' where code='cancelled'; + update order_status set color='#c0c5cc' where code='returned'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1693556962547-CascadeDeleteShipment.ts b/backend/src/database/migrations/archive/1693556962547-CascadeDeleteShipment.ts new file mode 100644 index 0000000..bc5dd72 --- /dev/null +++ b/backend/src/database/migrations/archive/1693556962547-CascadeDeleteShipment.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class CascadeDeleteShipment1693556962547 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table shipment + drop constraint shipment_warehouse_id_fkey, + drop constraint shipment_order_id_fkey, + add constraint shipment_warehouse_id_fkey foreign key (warehouse_id) references warehouse(id) on delete cascade, + add constraint shipment_order_id_fkey foreign key (order_id) references orders(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1694085886365-SchedulerIcon.ts b/backend/src/database/migrations/archive/1694085886365-SchedulerIcon.ts new file mode 100644 index 0000000..206400e --- /dev/null +++ b/backend/src/database/migrations/archive/1694085886365-SchedulerIcon.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SchedulerIcon1694085886365 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule add column icon character varying not null default ''; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1694166234404-BoardCleanProjectParticipants.ts b/backend/src/database/migrations/archive/1694166234404-BoardCleanProjectParticipants.ts new file mode 100644 index 0000000..7e22acc --- /dev/null +++ b/backend/src/database/migrations/archive/1694166234404-BoardCleanProjectParticipants.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class BoardCleanProjectParticipants1694166234404 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update board + set is_system = false, participant_ids = '[]' + where type = 'task' and is_system = true and owner_id is not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695040324876-AppointmentTitle.ts b/backend/src/database/migrations/archive/1695040324876-AppointmentTitle.ts new file mode 100644 index 0000000..0603d80 --- /dev/null +++ b/backend/src/database/migrations/archive/1695040324876-AppointmentTitle.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AppointmentTitle1695040324876 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_appointment add column title character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695046445852-EntityClosedAt.ts b/backend/src/database/migrations/archive/1695046445852-EntityClosedAt.ts new file mode 100644 index 0000000..c335f65 --- /dev/null +++ b/backend/src/database/migrations/archive/1695046445852-EntityClosedAt.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityClosedAt1695046445852 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity add column closed_at timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695201739381-SalesPlan.ts b/backend/src/database/migrations/archive/1695201739381-SalesPlan.ts new file mode 100644 index 0000000..f073e04 --- /dev/null +++ b/backend/src/database/migrations/archive/1695201739381-SalesPlan.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SalesPlan1695201739381 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table sales_plan ( + id integer generated by default as identity, + account_id integer not null, + created_at timestamp without time zone not null, + entity_type_id integer not null, + user_id integer not null, + start_date timestamp without time zone not null, + end_date timestamp without time zone not null, + quantity integer, + amount bigint, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (entity_type_id) references entity_type(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695287859742-ChatDeleteCascadeEntity.ts b/backend/src/database/migrations/archive/1695287859742-ChatDeleteCascadeEntity.ts new file mode 100644 index 0000000..b64b329 --- /dev/null +++ b/backend/src/database/migrations/archive/1695287859742-ChatDeleteCascadeEntity.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatDeleteCascadeEntity1695287859742 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat + drop constraint chat_entity_id_fkey, + add constraint chat_entity_id_fkey foreign key (entity_id) references entity(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695382850916-SalesPlanAlterAmount.ts b/backend/src/database/migrations/archive/1695382850916-SalesPlanAlterAmount.ts new file mode 100644 index 0000000..be058b2 --- /dev/null +++ b/backend/src/database/migrations/archive/1695382850916-SalesPlanAlterAmount.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SalesPlanAlterAmount1695382850916 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table sales_plan alter column amount type integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695742049917-VoximplantUser.ts b/backend/src/database/migrations/archive/1695742049917-VoximplantUser.ts new file mode 100644 index 0000000..f1c0417 --- /dev/null +++ b/backend/src/database/migrations/archive/1695742049917-VoximplantUser.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantUser1695742049917 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_user ( + id integer generated by default as identity, + user_id integer not null, + voximplant_id integer not null, + voximplant_username character varying not null, + account_id integer not null, + primary key (id), + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695743140564-VoximplantUserPrimaryColumn.ts b/backend/src/database/migrations/archive/1695743140564-VoximplantUserPrimaryColumn.ts new file mode 100644 index 0000000..39a071b --- /dev/null +++ b/backend/src/database/migrations/archive/1695743140564-VoximplantUserPrimaryColumn.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantUserPrimaryColumn1695743140564 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_user + drop column id, + add primary key (user_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695808984339-VoximplantUserPassword.ts b/backend/src/database/migrations/archive/1695808984339-VoximplantUserPassword.ts new file mode 100644 index 0000000..910e236 --- /dev/null +++ b/backend/src/database/migrations/archive/1695808984339-VoximplantUserPassword.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantUserPassword1695808984339 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_user add column voximplant_password character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695810676148-VoximplantAccount.ts b/backend/src/database/migrations/archive/1695810676148-VoximplantAccount.ts new file mode 100644 index 0000000..cb96738 --- /dev/null +++ b/backend/src/database/migrations/archive/1695810676148-VoximplantAccount.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantAccount1695810676148 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_account ( + account_id integer, + account_name character varying not null, + application_id integer not null, + application_name character varying not null, + primary key (account_id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1695820387969-AlterVoximplantUser.ts b/backend/src/database/migrations/archive/1695820387969-AlterVoximplantUser.ts new file mode 100644 index 0000000..aaf4c13 --- /dev/null +++ b/backend/src/database/migrations/archive/1695820387969-AlterVoximplantUser.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterVoximplantUser1695820387969 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_user rename column voximplant_id to external_id; + alter table voximplant_user rename column voximplant_username to username; + alter table voximplant_user rename column voximplant_password to password; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1696500815450-DemoData.ts b/backend/src/database/migrations/archive/1696500815450-DemoData.ts new file mode 100644 index 0000000..c4d4fd6 --- /dev/null +++ b/backend/src/database/migrations/archive/1696500815450-DemoData.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DemoData1696500815450 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table demo_data ( + id integer generated by default as identity, + account_id integer not null, + type character varying not null, + ids character varying not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697019761609-VoximplantCall.ts b/backend/src/database/migrations/archive/1697019761609-VoximplantCall.ts new file mode 100644 index 0000000..929a8a0 --- /dev/null +++ b/backend/src/database/migrations/archive/1697019761609-VoximplantCall.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantCall1697019761609 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_call ( + id integer generated by default as identity, + external_id integer not null, + user_id integer not null, + entity_id integer, + direction character varying not null, + phone_number character varying not null, + duration integer, + status character varying, + failure_reason character varying, + record_url character varying, + account_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (user_id) references users(id) on delete cascade, + foreign key (entity_id) references entity(id) on delete set null, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697028866185-AlterVoximplantUser.ts b/backend/src/database/migrations/archive/1697028866185-AlterVoximplantUser.ts new file mode 100644 index 0000000..83cbe53 --- /dev/null +++ b/backend/src/database/migrations/archive/1697028866185-AlterVoximplantUser.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterVoximplantUser1697028866185 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_user rename column username to user_name; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697115016543-RefactorDemoData.ts b/backend/src/database/migrations/archive/1697115016543-RefactorDemoData.ts new file mode 100644 index 0000000..f42571c --- /dev/null +++ b/backend/src/database/migrations/archive/1697115016543-RefactorDemoData.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RefactorDemoData1697115016543 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + INSERT INTO demo_data (account_id, type, ids) + SELECT account_id, 'entity' AS type, STRING_AGG(CAST(id AS character varying), ',') AS ids + FROM entity WHERE is_demo = true GROUP BY account_id; + ALTER TABLE entity DROP COLUMN is_demo; + + INSERT INTO demo_data (account_id, type, ids) + SELECT account_id, 'user' AS type, STRING_AGG(CAST(id AS character varying), ',') AS ids + FROM users WHERE is_demo = true GROUP BY account_id; + ALTER TABLE users DROP COLUMN is_demo; + + ALTER TABLE account_settings DROP COLUMN has_demo; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697440579544-SchedulerEventPerformerCascade.ts b/backend/src/database/migrations/archive/1697440579544-SchedulerEventPerformerCascade.ts new file mode 100644 index 0000000..bb5545f --- /dev/null +++ b/backend/src/database/migrations/archive/1697440579544-SchedulerEventPerformerCascade.ts @@ -0,0 +1,16 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SchedulerEventPerformerCascade1697440579544 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_appointment + drop constraint schedule_event_performer_id_fkey, + add constraint schedule_event_performer_id_fkey foreign key (performer_id) references users(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697452411558-VoximplantUserActive.ts b/backend/src/database/migrations/archive/1697452411558-VoximplantUserActive.ts new file mode 100644 index 0000000..8f6cf30 --- /dev/null +++ b/backend/src/database/migrations/archive/1697452411558-VoximplantUserActive.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantUserActive1697452411558 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_user add column is_active boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697541300418-VoximplantAccount.ts b/backend/src/database/migrations/archive/1697541300418-VoximplantAccount.ts new file mode 100644 index 0000000..e497ffc --- /dev/null +++ b/backend/src/database/migrations/archive/1697541300418-VoximplantAccount.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantAccount1697541300418 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from voximplant_account; + + alter table voximplant_account + add column external_id integer not null, + add column api_key character varying not null, + add column password character varying not null, + add column billing_account_id integer not null, + add column is_active text not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697541767120-VoximplantAccountAlter.ts b/backend/src/database/migrations/archive/1697541767120-VoximplantAccountAlter.ts new file mode 100644 index 0000000..43a07ce --- /dev/null +++ b/backend/src/database/migrations/archive/1697541767120-VoximplantAccountAlter.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantAccountAlter1697541767120 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_account drop column is_active; + alter table voximplant_account add column is_active boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697543064652-VoximplantAccountEmail.ts b/backend/src/database/migrations/archive/1697543064652-VoximplantAccountEmail.ts new file mode 100644 index 0000000..ecb9473 --- /dev/null +++ b/backend/src/database/migrations/archive/1697543064652-VoximplantAccountEmail.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantAccountEmail1697543064652 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_account add column account_email character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697556130148-VoximplantAccountKey.ts b/backend/src/database/migrations/archive/1697556130148-VoximplantAccountKey.ts new file mode 100644 index 0000000..1827ad3 --- /dev/null +++ b/backend/src/database/migrations/archive/1697556130148-VoximplantAccountKey.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantAccountKey1697556130148 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_account + add column key_id character varying not null, + add column private_key character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1697702819805-VoximplantCallAlterExternalId.ts b/backend/src/database/migrations/archive/1697702819805-VoximplantCallAlterExternalId.ts new file mode 100644 index 0000000..60679aa --- /dev/null +++ b/backend/src/database/migrations/archive/1697702819805-VoximplantCallAlterExternalId.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantCallAlterExternalId1697702819805 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from voximplant_call; + + alter table voximplant_call + drop column external_id, + add column session_id character varying not null, + add column call_id character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1698135013349-ReportingOptimization.ts b/backend/src/database/migrations/archive/1698135013349-ReportingOptimization.ts new file mode 100644 index 0000000..b5404cd --- /dev/null +++ b/backend/src/database/migrations/archive/1698135013349-ReportingOptimization.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ReportingOptimization1698135013349 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX idx_entity_account_id_stage_id_closed_at ON entity(account_id, stage_id, closed_at); + CREATE INDEX idx_field_value_entity_id_field_type ON field_value(entity_id, field_type); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1698421539770-VoximplantScenarios.ts b/backend/src/database/migrations/archive/1698421539770-VoximplantScenarios.ts new file mode 100644 index 0000000..60cb18c --- /dev/null +++ b/backend/src/database/migrations/archive/1698421539770-VoximplantScenarios.ts @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantScenarios1698421539770 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_scenario_entity ( + id integer generated by default as identity, + account_id integer not null, + scenario_type character varying not null, + contact_id integer, + deal_id integer, + board_id integer, + owner_id integer, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (contact_id) references entity_type(id) on delete cascade, + foreign key (deal_id) references entity_type(id) on delete cascade, + foreign key (board_id) references board(id) on delete cascade, + foreign key (owner_id) references users(id) on delete cascade + ); + + create table voximplant_scenario_note ( + id integer generated by default as identity, + account_id integer not null, + scenario_type character varying not null, + note_text character varying not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + create table voximplant_scenario_task ( + id integer generated by default as identity, + account_id integer not null, + scenario_type character varying not null, + create_activity boolean, + activity_type_id integer, + activity_text character varying, + activity_duration integer, + activity_owner_id integer, + create_task boolean, + task_title character varying, + task_text character varying, + task_duration integer, + task_owner_id integer, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (activity_type_id) references activity_type(id) on delete cascade, + foreign key (activity_owner_id) references users(id) on delete cascade, + foreign key (task_owner_id) references users(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1698663785490-OrderStatusColors.ts b/backend/src/database/migrations/archive/1698663785490-OrderStatusColors.ts new file mode 100644 index 0000000..db35ac2 --- /dev/null +++ b/backend/src/database/migrations/archive/1698663785490-OrderStatusColors.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OrderStatusColors1698663785490 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update order_status set color='#f68828' where code = 'reserved'; + update order_status set color='#1dd7d7' where code = 'sent_for_shipment'; + update order_status set color='#69d222' where code = 'shipped'; + update order_status set color='#f8654f' where code = 'cancelled'; + update order_status set color='#acb5c3' where code = 'returned'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1699457673085-AddEntityEventModel.ts b/backend/src/database/migrations/archive/1699457673085-AddEntityEventModel.ts new file mode 100644 index 0000000..443d20e --- /dev/null +++ b/backend/src/database/migrations/archive/1699457673085-AddEntityEventModel.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddEntityEventModel1699457673085 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + // language=SQL format=false + await queryRunner.query(` + create table entity_event ( + id integer generated always as identity primary key, + account_id integer not null references account(id) on delete cascade, + entity_id integer not null references entity(id) on delete cascade, + object_id integer not null, + type character varying not null, + created_at timestamp without time zone not null + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700060543771-AddSchedulePerformerId.ts b/backend/src/database/migrations/archive/1700060543771-AddSchedulePerformerId.ts new file mode 100644 index 0000000..df53fbd --- /dev/null +++ b/backend/src/database/migrations/archive/1700060543771-AddSchedulePerformerId.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSchedulePerformerId1700060543771 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_performer + drop constraint schedule_performer_pkey, + add column id integer generated always as identity, + add primary key (id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700060859571-AlterSchedulePerformer.ts b/backend/src/database/migrations/archive/1700060859571-AlterSchedulePerformer.ts new file mode 100644 index 0000000..6e37349 --- /dev/null +++ b/backend/src/database/migrations/archive/1700060859571-AlterSchedulePerformer.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedulePerformer1700060859571 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_performer + alter column user_id drop not null, + add column department_id integer, + add column type character varying not null default 'user', + add foreign key (department_id) references department(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700230572219-AlterMailbox.ts b/backend/src/database/migrations/archive/1700230572219-AlterMailbox.ts new file mode 100644 index 0000000..82d9176 --- /dev/null +++ b/backend/src/database/migrations/archive/1700230572219-AlterMailbox.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailbox1700230572219 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox add column create_lead boolean not null default false; + + update mailbox set create_lead = lead_entity_type_id is not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700395051542-AlterExternalEntity.ts b/backend/src/database/migrations/archive/1700395051542-AlterExternalEntity.ts new file mode 100644 index 0000000..0a1fb1a --- /dev/null +++ b/backend/src/database/migrations/archive/1700395051542-AlterExternalEntity.ts @@ -0,0 +1,16 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterExternalEntity1700395051542 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table external_entity + drop constraint external_entity_account_id_fkey, + add constraint external_entity_account_id_fkey foreign key (account_id) references account(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700475817946-AllEventsMigrationScript.ts b/backend/src/database/migrations/archive/1700475817946-AllEventsMigrationScript.ts new file mode 100644 index 0000000..1e64e1c --- /dev/null +++ b/backend/src/database/migrations/archive/1700475817946-AllEventsMigrationScript.ts @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AllEventsMigrationScript1700475817946 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from entity_event; + insert into entity_event (account_id, entity_id, object_id, type, created_at) + select + n.account_id, + n.entity_id, + n.id as object_id, + 'note'::text as type, + n.created_at + from note n + where n.entity_id is not null + union + select + t.account_id, + t.entity_id, + t.id as object_id, + 'task'::text as type, + t.created_at + from task t + where t.entity_id is not null + union + select + a.account_id, + a.entity_id, + a.id as object_id, + 'activity'::text as type, + a.created_at + from activity a + where a.entity_id is not null + union + select + m.account_id, + m.entity_id, + max(m.id) as object_id, + 'mail'::text as type, + max(m.date) as created_at + from mail_message m + where m.entity_id is not null + group by m.account_id, m.thread_id, m.entity_id + union + select + f.account_id, + f.source_id as entity_id, + f.id as object_id, + 'document'::text as type, + f.created_at + from file_link f + where f.source_type = 'entity_document' + and f.source_id is not null + and exists (select 1 from entity e where e.id = f.source_id) + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700591906266-TelephonyCallsToEntityEvent.ts b/backend/src/database/migrations/archive/1700591906266-TelephonyCallsToEntityEvent.ts new file mode 100644 index 0000000..f61792f --- /dev/null +++ b/backend/src/database/migrations/archive/1700591906266-TelephonyCallsToEntityEvent.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TelephonyCallsToEntityEvent1700591906266 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into entity_event (account_id, entity_id, object_id, type, created_at) + select v.account_id, v.entity_id, v.id as object_id, 'telephony-call' as type, created_at + from voximplant_call v + where v.entity_id is not null + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700663233866-AlterSchedulerAppointment.ts b/backend/src/database/migrations/archive/1700663233866-AlterSchedulerAppointment.ts new file mode 100644 index 0000000..4d13b98 --- /dev/null +++ b/backend/src/database/migrations/archive/1700663233866-AlterSchedulerAppointment.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedulerAppointment1700663233866 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_appointment drop constraint if exists schedule_event_performer_id_fkey; + alter table schedule_appointment drop constraint if exists schedule_appointment_performer_id_fkey; + alter table schedule_appointment rename column performer_id to old_performer_id; + + alter table schedule_appointment + add column performer_id integer, + add foreign key (performer_id) references schedule_performer(id) on delete cascade; + + update schedule_appointment sa + set performer_id = + (select id from schedule_performer sp + where sp.schedule_id = sa.schedule_id and sp.user_id = sa.old_performer_id); + + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700729760783-AlterSchedule.ts b/backend/src/database/migrations/archive/1700729760783-AlterSchedule.ts new file mode 100644 index 0000000..1d7e75b --- /dev/null +++ b/backend/src/database/migrations/archive/1700729760783-AlterSchedule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1700729760783 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule add column time_period integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700733045104-AlterSchedule.ts b/backend/src/database/migrations/archive/1700733045104-AlterSchedule.ts new file mode 100644 index 0000000..a0a9d2d --- /dev/null +++ b/backend/src/database/migrations/archive/1700733045104-AlterSchedule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1700733045104 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule add column appointment_limit integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700735236205-AlterSchedule.ts b/backend/src/database/migrations/archive/1700735236205-AlterSchedule.ts new file mode 100644 index 0000000..9d064fb --- /dev/null +++ b/backend/src/database/migrations/archive/1700735236205-AlterSchedule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1700735236205 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule add column type character varying not null default 'schedule'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700741072037-ProductOrdersToEntityEvents.ts b/backend/src/database/migrations/archive/1700741072037-ProductOrdersToEntityEvents.ts new file mode 100644 index 0000000..5295fe3 --- /dev/null +++ b/backend/src/database/migrations/archive/1700741072037-ProductOrdersToEntityEvents.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductOrdersToEntityEvents1700741072037 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into entity_event (account_id, entity_id, object_id, type, created_at) + select + o.account_id, + o.entity_id, + o.id as object_id, + 'order'::text as type, + o.created_at + from orders o + where o.entity_id is not null + union + select + ro.account_id, + ro.entity_id, + ro.id as object_id, + 'rental_order'::text as type, + ro.created_at + from rental_order ro + where ro.entity_id is not null + order by created_at desc; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700820935837-AlterSchedulerAppointment.ts b/backend/src/database/migrations/archive/1700820935837-AlterSchedulerAppointment.ts new file mode 100644 index 0000000..597c51b --- /dev/null +++ b/backend/src/database/migrations/archive/1700820935837-AlterSchedulerAppointment.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedulerAppointment1700820935837 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + + update schedule_appointment sa + set performer_id = + (select id from schedule_performer sp where sp.schedule_id = sa.schedule_id order by sp.id limit 1) + where performer_id is null; + + alter table schedule_appointment alter column performer_id set not null; + + alter table schedule_appointment drop column if exists old_performer_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1700836699189-ShipmentsToEntityEvents.ts b/backend/src/database/migrations/archive/1700836699189-ShipmentsToEntityEvents.ts new file mode 100644 index 0000000..b80a22d --- /dev/null +++ b/backend/src/database/migrations/archive/1700836699189-ShipmentsToEntityEvents.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ShipmentsToEntityEvents1700836699189 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into entity_event (account_id, entity_id, object_id, type, created_at) + select + s.account_id, + (select o.entity_id from orders o where o.id = s.order_id)::integer as entity_id, + s.id as object_id, + 'shipment'::text as type, + s.created_at + from shipment s + where exists (select 1 from orders o where o.id = s.order_id) + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1701264274255-CallsToEntityEventsFix.ts b/backend/src/database/migrations/archive/1701264274255-CallsToEntityEventsFix.ts new file mode 100644 index 0000000..e046512 --- /dev/null +++ b/backend/src/database/migrations/archive/1701264274255-CallsToEntityEventsFix.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class CallsToEntityEventsFix1701264274255 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into entity_event (account_id, entity_id, object_id, type, created_at) + select v.account_id, + v.entity_id, + v.id as object_id, + 'call' as type, + v.created_at + from voximplant_call v + where v.entity_id is not null + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1701437712747-DeleteEntityEventTelephonyCalls.ts b/backend/src/database/migrations/archive/1701437712747-DeleteEntityEventTelephonyCalls.ts new file mode 100644 index 0000000..fa112b0 --- /dev/null +++ b/backend/src/database/migrations/archive/1701437712747-DeleteEntityEventTelephonyCalls.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteEntityEventTelephonyCalls1701437712747 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from entity_event ee where ee.type='telephony-call'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1701701891843-AlterVoximplantCall.ts b/backend/src/database/migrations/archive/1701701891843-AlterVoximplantCall.ts new file mode 100644 index 0000000..ed9a931 --- /dev/null +++ b/backend/src/database/migrations/archive/1701701891843-AlterVoximplantCall.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterVoximplantCall1701701891843 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_call add column comment character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1702542289418-UpdateModulesIcons.ts b/backend/src/database/migrations/archive/1702542289418-UpdateModulesIcons.ts new file mode 100644 index 0000000..1b0072b --- /dev/null +++ b/backend/src/database/migrations/archive/1702542289418-UpdateModulesIcons.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateModulesIcons1702542289418 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update schedule set icon = 'calendar_4'; + update products_section set icon = 'box'; + update entity_type set section_icon = 'crown' where entity_category = 'deal'; + update entity_type set section_icon = 'user_2' where entity_category = 'contact'; + update entity_type set section_icon = 'building_2' where entity_category = 'company'; + update entity_type set section_icon = 'bulb' where entity_category = 'project'; + update entity_type set section_icon = 'star_2' where entity_category = 'hr'; + update entity_type set section_icon = 'product_1' where entity_category = 'supplier'; + update entity_type set section_icon = 'tie_2' where entity_category = 'contractor'; + update entity_type set section_icon = 'star_1' where entity_category = 'universal'; + update entity_type set section_icon = 'shapes_3' where entity_category = 'partner'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1702637665853-AlterActivityType.ts b/backend/src/database/migrations/archive/1702637665853-AlterActivityType.ts new file mode 100644 index 0000000..08de097 --- /dev/null +++ b/backend/src/database/migrations/archive/1702637665853-AlterActivityType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterActivityType1702637665853 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table activity_type add column is_active boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1702970939958-AlterAccountSettings.ts b/backend/src/database/migrations/archive/1702970939958-AlterAccountSettings.ts new file mode 100644 index 0000000..4ca9275 --- /dev/null +++ b/backend/src/database/migrations/archive/1702970939958-AlterAccountSettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSettings1702970939958 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_settings add column phone_format character varying not null default 'international'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703085253100-AlterProductPrice.ts b/backend/src/database/migrations/archive/1703085253100-AlterProductPrice.ts new file mode 100644 index 0000000..7ee3b6a --- /dev/null +++ b/backend/src/database/migrations/archive/1703085253100-AlterProductPrice.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterProductPrice1703085253100 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product_price add column max_discount integer default null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703488850551-AlterAccountSettings.ts b/backend/src/database/migrations/archive/1703488850551-AlterAccountSettings.ts new file mode 100644 index 0000000..c86881f --- /dev/null +++ b/backend/src/database/migrations/archive/1703488850551-AlterAccountSettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSettings1703488850551 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_settings add column allow_duplicates boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703502036545-FieldSettings.ts b/backend/src/database/migrations/archive/1703502036545-FieldSettings.ts new file mode 100644 index 0000000..819ebb1 --- /dev/null +++ b/backend/src/database/migrations/archive/1703502036545-FieldSettings.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FieldSettings1703502036545 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table field_user_settings ( + id integer generated by default as identity, + account_id integer not null, + field_id integer not null, + user_id integer not null, + access character varying not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (field_id) references field(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade + ); + + create table field_stage_settings ( + id integer generated by default as identity, + account_id integer not null, + field_id integer not null, + stage_id integer not null, + access character varying not null, + exclude_user_ids character varying, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (field_id) references field(id) on delete cascade, + foreign key (stage_id) references stage(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703761495779-AlterFieldStageSettings.ts b/backend/src/database/migrations/archive/1703761495779-AlterFieldStageSettings.ts new file mode 100644 index 0000000..4f0f044 --- /dev/null +++ b/backend/src/database/migrations/archive/1703761495779-AlterFieldStageSettings.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterFieldStageSettings1703761495779 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field_stage_settings drop column exclude_user_ids; + alter table field_stage_settings add column exclude_user_ids integer[]; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703850851646-AlterShipment.ts b/backend/src/database/migrations/archive/1703850851646-AlterShipment.ts new file mode 100644 index 0000000..240d325 --- /dev/null +++ b/backend/src/database/migrations/archive/1703850851646-AlterShipment.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterShipment1703850851646 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table shipment + add column entity_id integer, + add column order_number integer, + add foreign key (entity_id) references entity(id) on delete cascade; + + update shipment set entity_id = (select entity_id from orders where orders.id = shipment.order_id); + update shipment set order_number = (select order_number from orders where orders.id = shipment.order_id); + + alter table shipment + alter column entity_id set not null, + alter column order_number set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703857140848-AlterReservation.ts b/backend/src/database/migrations/archive/1703857140848-AlterReservation.ts new file mode 100644 index 0000000..5c5c277 --- /dev/null +++ b/backend/src/database/migrations/archive/1703857140848-AlterReservation.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterReservation1703857140848 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from reservation where is_active = false; + + alter table reservation drop column is_active; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1703876929122-AlterChangeStageActionSettings.ts b/backend/src/database/migrations/archive/1703876929122-AlterChangeStageActionSettings.ts new file mode 100644 index 0000000..8e401c3 --- /dev/null +++ b/backend/src/database/migrations/archive/1703876929122-AlterChangeStageActionSettings.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChangeStageActionSettings1703876929122 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table change_stage_action_settings rename to entity_action_settings; + alter index change_stage_action_settings_pkey rename to entity_action_settings_pkey; + alter table entity_action_settings add column operation_type character varying not null default 'move'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1704282894747-AlterEntity.ts b/backend/src/database/migrations/archive/1704282894747-AlterEntity.ts new file mode 100644 index 0000000..3624550 --- /dev/null +++ b/backend/src/database/migrations/archive/1704282894747-AlterEntity.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterEntity1704282894747 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity add column copied_from integer default null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1706795467082-TestAccount.ts b/backend/src/database/migrations/archive/1706795467082-TestAccount.ts new file mode 100644 index 0000000..c9b042f --- /dev/null +++ b/backend/src/database/migrations/archive/1706795467082-TestAccount.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TestAccount1706795467082 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table test_account ( + account_id integer, + primary key (account_id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1708088397272-EntityStageChange.ts b/backend/src/database/migrations/archive/1708088397272-EntityStageChange.ts new file mode 100644 index 0000000..58c57e2 --- /dev/null +++ b/backend/src/database/migrations/archive/1708088397272-EntityStageChange.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityStageChange1708088397272 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table entity_stage_history ( + id integer generated by default as identity, + account_id integer not null, + entity_id integer not null, + board_id integer not null, + stage_id integer not null, + created_at timestamp without time zone not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (entity_id) references entity(id) on delete cascade, + foreign key (board_id) references board(id) on delete cascade, + foreign key (stage_id) references stage(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1708433846254-EntityChangeHistoryInit.ts b/backend/src/database/migrations/archive/1708433846254-EntityChangeHistoryInit.ts new file mode 100644 index 0000000..cf547fe --- /dev/null +++ b/backend/src/database/migrations/archive/1708433846254-EntityChangeHistoryInit.ts @@ -0,0 +1,28 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityChangeHistoryInit1708433846254 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from entity_stage_history; + + insert into entity_stage_history (account_id, entity_id, board_id, stage_id, created_at) + select + e.account_id as account_id, e.id as entity_id, s.board_id as board_id, e.stage_id as stage_id, e.created_at as created_at + from entity e + inner join stage s on s.id = e.stage_id + where s.is_system = false; + + insert into entity_stage_history (account_id, entity_id, board_id, stage_id, created_at) + select + e.account_id as account_id, e.id as entity_id, s.board_id as board_id, e.stage_id as stage_id, e.closed_at as created_at + from entity e + inner join stage s on s.id = e.stage_id + where s.is_system = true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1708589222946-UserAnalyticsId.ts b/backend/src/database/migrations/archive/1708589222946-UserAnalyticsId.ts new file mode 100644 index 0000000..947a0f6 --- /dev/null +++ b/backend/src/database/migrations/archive/1708589222946-UserAnalyticsId.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserAnalyticsId1708589222946 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create extension if not exists "uuid-ossp"; + alter table users add column analytics_id uuid; + update users set analytics_id = uuid_generate_v4(); + alter table users alter column analytics_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1708952321460-OptimizeNotificationIndexes.ts b/backend/src/database/migrations/archive/1708952321460-OptimizeNotificationIndexes.ts new file mode 100644 index 0000000..44afd46 --- /dev/null +++ b/backend/src/database/migrations/archive/1708952321460-OptimizeNotificationIndexes.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OptimizeNotificationIndexes1708952321460 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop index notification_type_settings_settings_id_type_is_enabled_idx; + create index notification_type_settings_on_type_enabled_idx + on notification_type_settings(type) where is_enabled = true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709047301377-DBOptimizationIndex.ts b/backend/src/database/migrations/archive/1709047301377-DBOptimizationIndex.ts new file mode 100644 index 0000000..d0e3d14 --- /dev/null +++ b/backend/src/database/migrations/archive/1709047301377-DBOptimizationIndex.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DBOptimizationIndex1709047301377 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index task_account_id_responsible_user_id_stage_id_idx on task(account_id, responsible_user_id, stage_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709048922111-StageAccountIdIndex.ts b/backend/src/database/migrations/archive/1709048922111-StageAccountIdIndex.ts new file mode 100644 index 0000000..bb4e701 --- /dev/null +++ b/backend/src/database/migrations/archive/1709048922111-StageAccountIdIndex.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class StageAccountIdIndex1709048922111 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index stage_account_id_idx on stage(account_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709110989045-ScheduledMailMessageIndex.ts b/backend/src/database/migrations/archive/1709110989045-ScheduledMailMessageIndex.ts new file mode 100644 index 0000000..0a36390 --- /dev/null +++ b/backend/src/database/migrations/archive/1709110989045-ScheduledMailMessageIndex.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduledMailMessageIndex1709110989045 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index scheduled_mail_message_mailbox_id_sent_at_null_idx + on scheduled_mail_message(mailbox_id) + where sent_at is null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709111575857-AddIndexes.ts b/backend/src/database/migrations/archive/1709111575857-AddIndexes.ts new file mode 100644 index 0000000..8fa883b --- /dev/null +++ b/backend/src/database/migrations/archive/1709111575857-AddIndexes.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddIndexes1709111575857 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index entity_type_link_account_id_source_id_sort_order_id_idx on entity_type_link(account_id, source_id, sort_order, id); + create index users_account_id_id_idx on users(account_id, id); + create index field_group_account_id_entity_type_id_idx on field_group(account_id, entity_type_id); + + create index idx_chat_on_account_id on chat(account_id); + create index idx_cmus_on_message_id_status on chat_message_user_status(message_id, status); + create index idx_cmus_on_message_id_chat_id_status on chat_message_user_status(message_id, chat_id, status); + create index idx_chat_user_on_user_id on chat_user(user_id); + create index idx_chat_user_on_user_id_chat_id on chat_user(user_id, chat_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709280253891-ChatProviderCascadeDelete.ts b/backend/src/database/migrations/archive/1709280253891-ChatProviderCascadeDelete.ts new file mode 100644 index 0000000..73e54e0 --- /dev/null +++ b/backend/src/database/migrations/archive/1709280253891-ChatProviderCascadeDelete.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatProviderCascadeDelete1709280253891 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat + drop constraint chat_provider_id_fkey, + add constraint chat_provider_id_fkey foreign key (provider_id) references chat_provider(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709736232826-ChatEntityRemoveCascade.ts b/backend/src/database/migrations/archive/1709736232826-ChatEntityRemoveCascade.ts new file mode 100644 index 0000000..31c287a --- /dev/null +++ b/backend/src/database/migrations/archive/1709736232826-ChatEntityRemoveCascade.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatEntityRemoveCascade1709736232826 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat + drop constraint chat_entity_id_fkey, + add constraint chat_entity_id_fkey foreign key (entity_id) references entity(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1709805560320-ChatUserExternal.ts b/backend/src/database/migrations/archive/1709805560320-ChatUserExternal.ts new file mode 100644 index 0000000..865b8cf --- /dev/null +++ b/backend/src/database/migrations/archive/1709805560320-ChatUserExternal.ts @@ -0,0 +1,37 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatUserExternal1709805560320 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_user_external ( + id integer generated by default as identity, + account_id integer not null, + external_id character varying not null, + first_name character varying, + last_name character varying, + avatar_url character varying, + phone character varying, + email character varying, + link character varying, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + delete from chat_user where external_id is not null; + + alter table chat_user + drop column external_id, + drop column external_first_name, + drop column external_last_name, + drop column external_avatar_url; + + alter table chat_user + add column external_user_id integer, + add foreign key (external_user_id) references chat_user_external(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710162901881-EntityCopiedCount.ts b/backend/src/database/migrations/archive/1710162901881-EntityCopiedCount.ts new file mode 100644 index 0000000..3c92c4e --- /dev/null +++ b/backend/src/database/migrations/archive/1710162901881-EntityCopiedCount.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityCopiedCount1710162901881 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity add column copied_count integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710758264055-OrderCancelAfter.ts b/backend/src/database/migrations/archive/1710758264055-OrderCancelAfter.ts new file mode 100644 index 0000000..a9d981b --- /dev/null +++ b/backend/src/database/migrations/archive/1710758264055-OrderCancelAfter.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OrderCancelAfter1710758264055 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table orders + add column updated_at timestamp without time zone, + add column cancel_after integer; + + update orders set updated_at = created_at; + + alter table orders alter column updated_at set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710759144910-ProductSectionCancelAfter.ts b/backend/src/database/migrations/archive/1710759144910-ProductSectionCancelAfter.ts new file mode 100644 index 0000000..3304a4f --- /dev/null +++ b/backend/src/database/migrations/archive/1710759144910-ProductSectionCancelAfter.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductSectionCancelAfter1710759144910 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table products_section add column cancel_after integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710864090375-ScheduledActionCreatedBy.ts b/backend/src/database/migrations/archive/1710864090375-ScheduledActionCreatedBy.ts new file mode 100644 index 0000000..0a57576 --- /dev/null +++ b/backend/src/database/migrations/archive/1710864090375-ScheduledActionCreatedBy.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduledActionCreatedBy1710864090375 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table scheduled_action + add column created_by integer, + add foreign key (created_by) references users(id); + + update scheduled_action + set created_by = (select created_by from automation where automation.action_id = scheduled_action.action_id); + + alter table scheduled_action alter column created_by set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710927112868-TutorialGroup.ts b/backend/src/database/migrations/archive/1710927112868-TutorialGroup.ts new file mode 100644 index 0000000..a084cd5 --- /dev/null +++ b/backend/src/database/migrations/archive/1710927112868-TutorialGroup.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TutorialGroup1710927112868 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table tutorial_group ( + id integer generated by default as identity, + account_id integer not null, + created_at timestamp without time zone not null, + name character varying not null, + sort_order integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710929893275-TutorialItem.ts b/backend/src/database/migrations/archive/1710929893275-TutorialItem.ts new file mode 100644 index 0000000..eaddd4a --- /dev/null +++ b/backend/src/database/migrations/archive/1710929893275-TutorialItem.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TutorialItem1710929893275 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table tutorial_item ( + id integer generated by default as identity, + account_id integer not null, + group_id integer not null, + created_at timestamp without time zone not null, + name character varying not null, + link character varying not null, + sort_order integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (group_id) references tutorial_group(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1710939538331-TutorialItemUser.ts b/backend/src/database/migrations/archive/1710939538331-TutorialItemUser.ts new file mode 100644 index 0000000..ac78d96 --- /dev/null +++ b/backend/src/database/migrations/archive/1710939538331-TutorialItemUser.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TutorialItemUser1710939538331 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table tutorial_item_user ( + item_id integer not null, + user_id integer not null, + primary key (item_id, user_id), + foreign key (item_id) references tutorial_item(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1711033243401-TutorialItemProduct.ts b/backend/src/database/migrations/archive/1711033243401-TutorialItemProduct.ts new file mode 100644 index 0000000..e9799ef --- /dev/null +++ b/backend/src/database/migrations/archive/1711033243401-TutorialItemProduct.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TutorialItemProduct1711033243401 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table tutorial_item_product ( + id integer generated by default as identity, + account_id integer not null, + item_id integer not null, + type character varying not null, + object_id integer, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (item_id) references tutorial_item(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1711087326245-MailMessageIndexes.ts b/backend/src/database/migrations/archive/1711087326245-MailMessageIndexes.ts new file mode 100644 index 0000000..634946c --- /dev/null +++ b/backend/src/database/migrations/archive/1711087326245-MailMessageIndexes.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailMessageIndexes1711087326245 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX idx_mail_message_account_mailbox ON mail_message(account_id, mailbox_id); + CREATE INDEX idx_mail_message_external_id ON mail_message(external_id); + CREATE INDEX idx_mail_message_message_id ON mail_message(message_id); + CREATE INDEX idx_mailbox_folder_account_mailbox ON mailbox_folder(account_id, mailbox_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1711540999340-EntityActionSettings.ts b/backend/src/database/migrations/archive/1711540999340-EntityActionSettings.ts new file mode 100644 index 0000000..967f100 --- /dev/null +++ b/backend/src/database/migrations/archive/1711540999340-EntityActionSettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityActionSettings1711540999340 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update entity_action_settings set operation_type = 'copy_original' where operation_type = 'copy'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1711541635402-ActionSettingsRename.ts b/backend/src/database/migrations/archive/1711541635402-ActionSettingsRename.ts new file mode 100644 index 0000000..36a0441 --- /dev/null +++ b/backend/src/database/migrations/archive/1711541635402-ActionSettingsRename.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ActionSettingsRename1711541635402 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table activity_action_settings rename to action_settings_activity; + alter table email_action_settings rename to action_settings_email; + alter table entity_action_settings rename to action_settings_entity; + alter table task_action_settings rename to action_settings_task; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1711706670268-ScheduledAction.ts b/backend/src/database/migrations/archive/1711706670268-ScheduledAction.ts new file mode 100644 index 0000000..cffa1ae --- /dev/null +++ b/backend/src/database/migrations/archive/1711706670268-ScheduledAction.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduledAction1711706670268 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table scheduled_action rename to action_scheduled; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1711962655915-AutomationActionSettings.ts b/backend/src/database/migrations/archive/1711962655915-AutomationActionSettings.ts new file mode 100644 index 0000000..b27342c --- /dev/null +++ b/backend/src/database/migrations/archive/1711962655915-AutomationActionSettings.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AutomationActionSettings1711962655915 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table action_settings_activity rename to action_activity_settings; + alter table action_settings_email rename to action_email_settings; + alter table action_settings_entity rename to action_entity_settings; + alter table action_settings_task rename to action_task_settings; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1712575547663-FieldValue.ts b/backend/src/database/migrations/archive/1712575547663-FieldValue.ts new file mode 100644 index 0000000..5412458 --- /dev/null +++ b/backend/src/database/migrations/archive/1712575547663-FieldValue.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FieldValue1712575547663 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field add column value character varying; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1713167989297-DeleteAllFormulaFields.ts b/backend/src/database/migrations/archive/1713167989297-DeleteAllFormulaFields.ts new file mode 100644 index 0000000..0b01a37 --- /dev/null +++ b/backend/src/database/migrations/archive/1713167989297-DeleteAllFormulaFields.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeleteAllFormulaFields1713167989297 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from field where type = 'formula'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1713257835467-FixEntityTypeSortOrder.ts b/backend/src/database/migrations/archive/1713257835467-FixEntityTypeSortOrder.ts new file mode 100644 index 0000000..23988b8 --- /dev/null +++ b/backend/src/database/migrations/archive/1713257835467-FixEntityTypeSortOrder.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixEntityTypeSortOrder1713257835467 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + WITH Sorted AS ( + SELECT id, row_number() OVER (PARTITION BY account_id ORDER BY id) - 1 AS new_sort_order + FROM entity_type + ) + UPDATE entity_type + SET sort_order = Sorted.new_sort_order + FROM Sorted + WHERE entity_type.id = Sorted.id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1713258622876-FixEntityTypeLinkSortOrder.ts b/backend/src/database/migrations/archive/1713258622876-FixEntityTypeLinkSortOrder.ts new file mode 100644 index 0000000..5ec1b03 --- /dev/null +++ b/backend/src/database/migrations/archive/1713258622876-FixEntityTypeLinkSortOrder.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixEntityTypeLinkSortOrder1713258622876 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + WITH Sorted AS ( + SELECT id, row_number() OVER (PARTITION BY source_id ORDER BY id) - 1 AS new_sort_order + FROM entity_type_link + ) + UPDATE entity_type_link + SET sort_order = Sorted.new_sort_order + FROM Sorted + WHERE entity_type_link.id = Sorted.id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1713971186799-ChatProviderTransport.ts b/backend/src/database/migrations/archive/1713971186799-ChatProviderTransport.ts new file mode 100644 index 0000000..d99479c --- /dev/null +++ b/backend/src/database/migrations/archive/1713971186799-ChatProviderTransport.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatProviderTransport1713971186799 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider add column transport character varying; + update chat_provider set transport = 'amwork' where type = 'amwork'; + update chat_provider set transport = 'whatsapp' where type = 'twilio_whatsapp'; + update chat_provider set transport = 'messenger' where type = 'facebook_messenger'; + alter table chat_provider alter column transport set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1714382561376-WazzupProvider.ts b/backend/src/database/migrations/archive/1714382561376-WazzupProvider.ts new file mode 100644 index 0000000..f77aaec --- /dev/null +++ b/backend/src/database/migrations/archive/1714382561376-WazzupProvider.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class WazzupProvider1714382561376 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_provider_wazzup ( + provider_id integer, + account_id integer not null, + api_key character varying not null, + channel_id character varying not null, + chat_type character varying not null, + plain_id character varying not null, + primary key (provider_id), + foreign key (provider_id) references chat_provider(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1714557065128-ProductPricePrecision.ts b/backend/src/database/migrations/archive/1714557065128-ProductPricePrecision.ts new file mode 100644 index 0000000..03fda06 --- /dev/null +++ b/backend/src/database/migrations/archive/1714557065128-ProductPricePrecision.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductPricePrecision1714557065128 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "product_price" ALTER COLUMN "unit_price" TYPE numeric(15,2); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1714663341741-WazzupProviderRemoveChatType.ts b/backend/src/database/migrations/archive/1714663341741-WazzupProviderRemoveChatType.ts new file mode 100644 index 0000000..2780bf8 --- /dev/null +++ b/backend/src/database/migrations/archive/1714663341741-WazzupProviderRemoveChatType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class WazzupProviderRemoveChatType1714663341741 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider_wazzup drop column chat_type; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1714730508391-WazzupProviderTransport.ts b/backend/src/database/migrations/archive/1714730508391-WazzupProviderTransport.ts new file mode 100644 index 0000000..5af276a --- /dev/null +++ b/backend/src/database/migrations/archive/1714730508391-WazzupProviderTransport.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class WazzupProviderTransport1714730508391 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from chat_provider_wazzup; + alter table chat_provider_wazzup add column transport character varying not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1714732587962-RemoveWazzupProviders.ts b/backend/src/database/migrations/archive/1714732587962-RemoveWazzupProviders.ts new file mode 100644 index 0000000..b0e8155 --- /dev/null +++ b/backend/src/database/migrations/archive/1714732587962-RemoveWazzupProviders.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveWazzupProviders1714732587962 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from chat_provider where type = 'wazzup'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1714734600334-ChatProviderTypeRename.ts b/backend/src/database/migrations/archive/1714734600334-ChatProviderTypeRename.ts new file mode 100644 index 0000000..feb9272 --- /dev/null +++ b/backend/src/database/migrations/archive/1714734600334-ChatProviderTypeRename.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatProviderTypeRename1714734600334 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update chat_provider set type='twilio' where type='twilio_whatsapp'; + update chat_provider set type='facebook' where type='facebook_messenger'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1715602468891-ChatProviderUserType.ts b/backend/src/database/migrations/archive/1715602468891-ChatProviderUserType.ts new file mode 100644 index 0000000..2416e2a --- /dev/null +++ b/backend/src/database/migrations/archive/1715602468891-ChatProviderUserType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatProviderUserType1715602468891 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter type chat_provider_user_type add value 'supervisor'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1715610371002-AlterChatUserExternal.ts b/backend/src/database/migrations/archive/1715610371002-AlterChatUserExternal.ts new file mode 100644 index 0000000..d80c567 --- /dev/null +++ b/backend/src/database/migrations/archive/1715610371002-AlterChatUserExternal.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatUserExternal1715610371002 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_user_external + add column chat_user_id integer, + add foreign key (chat_user_id) references chat_user(id) on delete cascade; + + update chat_user_external + set chat_user_id = ( + select id + from chat_user + where chat_user.external_user_id = chat_user_external.id + ); + + delete from chat_user_external where chat_user_id is null; + + alter table chat_user drop column external_user_id; + + alter table chat_user_external + drop constraint chat_user_external_pkey, + add primary key (chat_user_id); + + alter table chat_user_external drop column id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1715856544173-AccountApiAccess.ts b/backend/src/database/migrations/archive/1715856544173-AccountApiAccess.ts new file mode 100644 index 0000000..86bc067 --- /dev/null +++ b/backend/src/database/migrations/archive/1715856544173-AccountApiAccess.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AccountApiAccess1715856544173 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table account_api_access ( + account_id integer, + api_key character varying not null, + created_at timestamp without time zone not null, + primary key (account_id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1715856948928-AlterAccountApiAccess.ts b/backend/src/database/migrations/archive/1715856948928-AlterAccountApiAccess.ts new file mode 100644 index 0000000..5a949b9 --- /dev/null +++ b/backend/src/database/migrations/archive/1715856948928-AlterAccountApiAccess.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountApiAccess1715856948928 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_api_access alter column created_at set default now(); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1716299180820-VoximplantNumber.ts b/backend/src/database/migrations/archive/1716299180820-VoximplantNumber.ts new file mode 100644 index 0000000..a800711 --- /dev/null +++ b/backend/src/database/migrations/archive/1716299180820-VoximplantNumber.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantNumber1716299180820 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_number ( + id integer generated by default as identity, + account_id integer not null, + number character varying not null, + external_id character varying, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + create table voximplant_number_user ( + number_id integer, + user_id integer, + account_id integer not null, + primary key (number_id, user_id), + foreign key (number_id) references voximplant_number(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1716384743872-VoximplantPhoneNumber.ts b/backend/src/database/migrations/archive/1716384743872-VoximplantPhoneNumber.ts new file mode 100644 index 0000000..9d6e2db --- /dev/null +++ b/backend/src/database/migrations/archive/1716384743872-VoximplantPhoneNumber.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantPhoneNumber1716384743872 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE voximplant_number RENAME COLUMN number TO phone_number; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1716465152984-VoximplantNumberDefaults.ts b/backend/src/database/migrations/archive/1716465152984-VoximplantNumberDefaults.ts new file mode 100644 index 0000000..277d972 --- /dev/null +++ b/backend/src/database/migrations/archive/1716465152984-VoximplantNumberDefaults.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantNumberDefaults1716465152984 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + INSERT INTO voximplant_number (account_id, phone_number, external_id) + SELECT account_id, '', '' FROM voximplant_account; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1716466922606-VoximplantCallNumber.ts b/backend/src/database/migrations/archive/1716466922606-VoximplantCallNumber.ts new file mode 100644 index 0000000..9e8e8c2 --- /dev/null +++ b/backend/src/database/migrations/archive/1716466922606-VoximplantCallNumber.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantCallNumber1716466922606 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE voximplant_call + ADD COLUMN number_id integer, + ADD FOREIGN KEY (number_id) REFERENCES voximplant_number(id) ON DELETE SET NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1716469802549-VoximplantCallNumberDefault.ts b/backend/src/database/migrations/archive/1716469802549-VoximplantCallNumberDefault.ts new file mode 100644 index 0000000..abc229d --- /dev/null +++ b/backend/src/database/migrations/archive/1716469802549-VoximplantCallNumberDefault.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantCallNumberDefault1716469802549 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update voximplant_call set number_id = ( + select id from voximplant_number where voximplant_number.account_id = voximplant_call.account_id limit 1 + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1717599382958-AddForms.ts b/backend/src/database/migrations/archive/1717599382958-AddForms.ts new file mode 100644 index 0000000..1747364 --- /dev/null +++ b/backend/src/database/migrations/archive/1717599382958-AddForms.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddForms1717599382958 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE site_form ( + id integer GENERATED BY DEFAULT AS IDENTITY, + account_id integer NOT NULL, + name text NOT NULL, + code text NOT NULL, + is_active boolean NOT NULL, + title text, + type text NOT NULL, + color_main text, + color_background text, + color_font text, + consent_enabled boolean NOT NULL DEFAULT false, + consent_text text, + consent_url text, + PRIMARY KEY (id), + FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE, + UNIQUE (code) + ); + + CREATE TABLE site_form_page ( + id integer GENERATED BY DEFAULT AS IDENTITY, + account_id integer NOT NULL, + form_id integer NOT NULL, + title text, + PRIMARY KEY (id), + FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE, + FOREIGN KEY (form_id) REFERENCES site_form(id) ON DELETE CASCADE + ); + + CREATE TABLE site_form_field ( + id integer GENERATED BY DEFAULT AS IDENTITY, + account_id integer NOT NULL, + page_id integer NOT NULL, + title text NOT NULL, + type text NOT NULL, + is_required boolean NOT NULL DEFAULT false, + PRIMARY KEY (id), + FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE, + FOREIGN KEY (page_id) REFERENCES site_form_page(id) ON DELETE CASCADE + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1717746424353-SiteFormPageSortOrder.ts b/backend/src/database/migrations/archive/1717746424353-SiteFormPageSortOrder.ts new file mode 100644 index 0000000..ed97416 --- /dev/null +++ b/backend/src/database/migrations/archive/1717746424353-SiteFormPageSortOrder.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SiteFormPageSortOrder1717746424353 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE site_form_page ADD COLUMN sort_order integer NOT NULL; + ALTER TABLE site_form_field ADD COLUMN sort_order integer NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1717773341006-FormSiteLink.ts b/backend/src/database/migrations/archive/1717773341006-FormSiteLink.ts new file mode 100644 index 0000000..e125f46 --- /dev/null +++ b/backend/src/database/migrations/archive/1717773341006-FormSiteLink.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FormSiteLink1717773341006 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE site_form_link ( + id integer GENERATED BY DEFAULT AS IDENTITY, + account_id integer NOT NULL, + form_id integer NOT NULL, + type text NOT NULL, + object_id integer NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE, + FOREIGN KEY (form_id) REFERENCES site_form(id) ON DELETE CASCADE + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718030543491-SiteFormEntityType.ts b/backend/src/database/migrations/archive/1718030543491-SiteFormEntityType.ts new file mode 100644 index 0000000..67abc00 --- /dev/null +++ b/backend/src/database/migrations/archive/1718030543491-SiteFormEntityType.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SiteFormEntityType1718030543491 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table site_form_link; + + create table site_form_entity_type ( + form_id integer, + entity_type_id integer, + account_id integer not null, + board_id integer, + primary key (form_id, entity_type_id), + foreign key (form_id) references site_form(id) on delete cascade, + foreign key (entity_type_id) references entity_type(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + foreign key (board_id) references board(id) on delete set null + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718098299378-SiteFormRemoveConsent.ts b/backend/src/database/migrations/archive/1718098299378-SiteFormRemoveConsent.ts new file mode 100644 index 0000000..8783bb0 --- /dev/null +++ b/backend/src/database/migrations/archive/1718098299378-SiteFormRemoveConsent.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SiteFormRemoveConsent1718098299378 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form + drop column consent_enabled, + drop column consent_text, + drop column consent_url; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718098642901-SiteFormConsent.ts b/backend/src/database/migrations/archive/1718098642901-SiteFormConsent.ts new file mode 100644 index 0000000..a6799ee --- /dev/null +++ b/backend/src/database/migrations/archive/1718098642901-SiteFormConsent.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SiteFormConsent1718098642901 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table site_form_consent ( + form_id integer, + account_id integer, + is_enabled boolean not null default false, + text text, + link_url text, + link_text text, + default_value boolean not null default false, + primary key (form_id), + foreign key (form_id) references site_form(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718115282972-SiteFormGratitude.ts b/backend/src/database/migrations/archive/1718115282972-SiteFormGratitude.ts new file mode 100644 index 0000000..c10a671 --- /dev/null +++ b/backend/src/database/migrations/archive/1718115282972-SiteFormGratitude.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SiteFormGratitude1718115282972 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table site_form_gratitude ( + form_id integer, + account_id integer, + is_enabled boolean not null default false, + header text, + text text, + primary key (form_id), + foreign key (form_id) references site_form(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718118622975-AlterSiteForm.ts b/backend/src/database/migrations/archive/1718118622975-AlterSiteForm.ts new file mode 100644 index 0000000..5b1f496 --- /dev/null +++ b/backend/src/database/migrations/archive/1718118622975-AlterSiteForm.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1718118622975 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form + drop column type, + drop column color_main, + drop column color_background, + drop column color_font, + alter column title set default null, + add column responsible_id integer default null, + add column design jsonb default null, + add foreign key (responsible_id) references users(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718120948980-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1718120948980-AlterSiteFormField.ts new file mode 100644 index 0000000..22eb3af --- /dev/null +++ b/backend/src/database/migrations/archive/1718120948980-AlterSiteFormField.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1718120948980 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_field rename column title to label; + alter table site_form_field + alter column label drop not null, + add column placeholder text, + add column field_id integer, + add foreign key (field_id) references field(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718266194827-NotificationRemoveTag.ts b/backend/src/database/migrations/archive/1718266194827-NotificationRemoveTag.ts new file mode 100644 index 0000000..e5fd289 --- /dev/null +++ b/backend/src/database/migrations/archive/1718266194827-NotificationRemoveTag.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NotificationRemoveTag1718266194827 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table notification drop column tag_name; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718613418648-AlterSiteForm.ts b/backend/src/database/migrations/archive/1718613418648-AlterSiteForm.ts new file mode 100644 index 0000000..9bec5e3 --- /dev/null +++ b/backend/src/database/migrations/archive/1718613418648-AlterSiteForm.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1718613418648 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form + add column field_label_enabled boolean not null default false, + add column field_placeholder_enabled boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718715571983-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1718715571983-AlterSiteFormField.ts new file mode 100644 index 0000000..38c85c5 --- /dev/null +++ b/backend/src/database/migrations/archive/1718715571983-AlterSiteFormField.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1718715571983 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_field rename column field_id to object_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718724129043-AutomationProcess.ts b/backend/src/database/migrations/archive/1718724129043-AutomationProcess.ts new file mode 100644 index 0000000..3d9ac39 --- /dev/null +++ b/backend/src/database/migrations/archive/1718724129043-AutomationProcess.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AutomationProcess1718724129043 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table automation_process ( + id integer generated by default as identity, + account_id integer not null, + created_at timestamp without time zone not null default now(), + type text not null default 'simple', + is_active boolean not null default false, + external_id text, + data text, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718793150525-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1718793150525-AlterSiteFormField.ts new file mode 100644 index 0000000..d920b90 --- /dev/null +++ b/backend/src/database/migrations/archive/1718793150525-AlterSiteFormField.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1718793150525 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_field + add column meta jsonb default null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718793757895-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1718793757895-AlterSiteFormField.ts new file mode 100644 index 0000000..3b00c2d --- /dev/null +++ b/backend/src/database/migrations/archive/1718793757895-AlterSiteFormField.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1718793757895 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_field + add column is_validation_required boolean default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718798058177-AlterAutomationProcess.ts b/backend/src/database/migrations/archive/1718798058177-AlterAutomationProcess.ts new file mode 100644 index 0000000..e6e9aff --- /dev/null +++ b/backend/src/database/migrations/archive/1718798058177-AlterAutomationProcess.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationProcess1718798058177 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_process rename column external_id to resource_key; + alter table automation_process + drop column type, + drop column is_active, + add column bpmn_process_id text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718801950089-AlterAutomationProcess.ts b/backend/src/database/migrations/archive/1718801950089-AlterAutomationProcess.ts new file mode 100644 index 0000000..d9a8aef --- /dev/null +++ b/backend/src/database/migrations/archive/1718801950089-AlterAutomationProcess.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationProcess1718801950089 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_process + add column name text not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1718880290844-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1718880290844-AlterSiteFormField.ts new file mode 100644 index 0000000..abc5917 --- /dev/null +++ b/backend/src/database/migrations/archive/1718880290844-AlterSiteFormField.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1718880290844 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_field alter column is_validation_required drop default; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719213418299-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1719213418299-AlterSiteFormField.ts new file mode 100644 index 0000000..3387120 --- /dev/null +++ b/backend/src/database/migrations/archive/1719213418299-AlterSiteFormField.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1719213418299 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_field alter column is_required drop not null; + alter table site_form_field alter column is_required drop default; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719302707526-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1719302707526-AlterSiteFormField.ts new file mode 100644 index 0000000..6f72bd6 --- /dev/null +++ b/backend/src/database/migrations/archive/1719302707526-AlterSiteFormField.ts @@ -0,0 +1,38 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1719302707526 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from site_form_field; + + alter table site_form_field + drop column object_id, + drop column meta, + drop column is_validation_required; + + create table site_form_field_entity_name ( + form_field_id integer, + entity_type_id integer not null, + primary key (form_field_id), + foreign key (form_field_id) references site_form_field(id) on delete cascade, + foreign key (entity_type_id) references entity_type(id) on delete cascade + ); + + create table site_form_field_entity_field ( + form_field_id integer, + field_id integer not null, + entity_type_id integer not null, + is_validation_required boolean, + meta jsonb, + primary key (form_field_id), + foreign key (form_field_id) references site_form_field(id) on delete cascade, + foreign key (field_id) references field(id) on delete cascade, + foreign key (entity_type_id) references entity_type(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719324782700-RenameSubtask.ts b/backend/src/database/migrations/archive/1719324782700-RenameSubtask.ts new file mode 100644 index 0000000..5442bd7 --- /dev/null +++ b/backend/src/database/migrations/archive/1719324782700-RenameSubtask.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameSubtask1719324782700 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subtask rename to task_subtask; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719325474889-AlterTaskSubtaskSortOrder.ts b/backend/src/database/migrations/archive/1719325474889-AlterTaskSubtaskSortOrder.ts new file mode 100644 index 0000000..563b340 --- /dev/null +++ b/backend/src/database/migrations/archive/1719325474889-AlterTaskSubtaskSortOrder.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterTaskSubtaskSortOrder1719325474889 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task_subtask add column sort_order integer not null default 0; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719393706903-AlterAutomationProcess.ts b/backend/src/database/migrations/archive/1719393706903-AlterAutomationProcess.ts new file mode 100644 index 0000000..32a7760 --- /dev/null +++ b/backend/src/database/migrations/archive/1719393706903-AlterAutomationProcess.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationProcess1719393706903 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_process rename column data to bpmn_file; + alter table automation_process drop column name; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719396909800-AutomationEntityType.ts b/backend/src/database/migrations/archive/1719396909800-AutomationEntityType.ts new file mode 100644 index 0000000..8d219a5 --- /dev/null +++ b/backend/src/database/migrations/archive/1719396909800-AutomationEntityType.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AutomationEntityType1719396909800 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table automation_entity_type ( + id integer generated always as identity, + account_id integer not null, + created_at timestamp without time zone not null default now(), + created_by integer not null, + name text not null, + is_active boolean not null, + triggers text not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (created_by) references users(id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719404585119-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1719404585119-AlterAutomationEntityType.ts new file mode 100644 index 0000000..410ddf6 --- /dev/null +++ b/backend/src/database/migrations/archive/1719404585119-AlterAutomationEntityType.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1719404585119 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type + add column entity_type_id integer not null, + add column board_id integer, + add column stage_id integer, + add foreign key (entity_type_id) references entity_type(id), + add foreign key (board_id) references board(id), + add foreign key (stage_id) references stage(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719411072571-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1719411072571-AlterAutomationEntityType.ts new file mode 100644 index 0000000..50286b4 --- /dev/null +++ b/backend/src/database/migrations/archive/1719411072571-AlterAutomationEntityType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1719411072571 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type add column delay integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719414260257-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1719414260257-AlterAutomationEntityType.ts new file mode 100644 index 0000000..a68cf8a --- /dev/null +++ b/backend/src/database/migrations/archive/1719414260257-AlterAutomationEntityType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1719414260257 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type add column conditions jsonb; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719502764234-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1719502764234-AlterAutomationEntityType.ts new file mode 100644 index 0000000..055cfd4 --- /dev/null +++ b/backend/src/database/migrations/archive/1719502764234-AlterAutomationEntityType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1719502764234 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type drop column delay; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719502897605-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1719502897605-AlterAutomationEntityType.ts new file mode 100644 index 0000000..b48bfe2 --- /dev/null +++ b/backend/src/database/migrations/archive/1719502897605-AlterAutomationEntityType.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1719502897605 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type add column actions jsonb; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719833217147-AlterEntityType.ts b/backend/src/database/migrations/archive/1719833217147-AlterEntityType.ts new file mode 100644 index 0000000..4b8288d --- /dev/null +++ b/backend/src/database/migrations/archive/1719833217147-AlterEntityType.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterEntityType1719833217147 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity_type + drop column code, + drop column need_migration; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1719995612500-UpdateCopiesCreatedAt.ts b/backend/src/database/migrations/archive/1719995612500-UpdateCopiesCreatedAt.ts new file mode 100644 index 0000000..93d8616 --- /dev/null +++ b/backend/src/database/migrations/archive/1719995612500-UpdateCopiesCreatedAt.ts @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateCopiesCreatedAt1719995612500 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +WITH RECURSIVE entity_hierarchy AS ( + -- Base case: Select the initial entities + SELECT + id, + created_at, + copied_from + FROM + entity + WHERE + copied_from IS NULL + + UNION ALL + + -- Recursive case: Join the entity table to build the hierarchy + SELECT + e.id, + eh.created_at, + e.copied_from + FROM + entity e + INNER JOIN + entity_hierarchy eh + ON + e.copied_from = eh.id +) +UPDATE + entity e1 +SET + created_at = eh.created_at +FROM + entity_hierarchy eh +WHERE + e1.id = eh.id +AND + e1.copied_from IS NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720076313493-AlterSiteForm.ts b/backend/src/database/migrations/archive/1720076313493-AlterSiteForm.ts new file mode 100644 index 0000000..02f9982 --- /dev/null +++ b/backend/src/database/migrations/archive/1720076313493-AlterSiteForm.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1720076313493 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form + add column created_by integer, + add foreign key (created_by) references users(id); + + update site_form set created_by = responsible_id; + + alter table site_form alter column created_by set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720077438733-AlterSiteFormEntityType.ts b/backend/src/database/migrations/archive/1720077438733-AlterSiteFormEntityType.ts new file mode 100644 index 0000000..12220a5 --- /dev/null +++ b/backend/src/database/migrations/archive/1720077438733-AlterSiteFormEntityType.ts @@ -0,0 +1,25 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormEntityType1720077438733 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form_entity_type add column is_main boolean; + + with selected as ( + select distinct on (form_id) form_id, entity_type_id + from site_form_entity_type + order by form_id, entity_type_id + ) + update site_form_entity_type + set is_main = case when (form_id, entity_type_id) in (select form_id, entity_type_id from selected) then true else false end + where form_id in (select form_id from selected); + + alter table site_form_entity_type alter column is_main set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720194016236-AppSumoLicense.ts b/backend/src/database/migrations/archive/1720194016236-AppSumoLicense.ts new file mode 100644 index 0000000..c34f732 --- /dev/null +++ b/backend/src/database/migrations/archive/1720194016236-AppSumoLicense.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AppSumoLicense1720194016236 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table app_sumo_license ( + id integer generated by default as identity, + license_key text not null, + license_status text not null, + plan_id text not null, + tier integer not null, + account_id integer, + created_at timestamp without time zone not null default now(), + primary key (id), + foreign key (account_id) references account(id) on delete set null + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720194705296-AppSumoPreset.ts b/backend/src/database/migrations/archive/1720194705296-AppSumoPreset.ts new file mode 100644 index 0000000..57cf32e --- /dev/null +++ b/backend/src/database/migrations/archive/1720194705296-AppSumoPreset.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AppSumoPreset1720194705296 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table app_sumo_preset ( + id integer generated by default as identity, + tier integer not null, + user_count integer not null, + term_in_days integer not null, + primary key (id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720423072348-AppSumoPresets.ts b/backend/src/database/migrations/archive/1720423072348-AppSumoPresets.ts new file mode 100644 index 0000000..326f0b7 --- /dev/null +++ b/backend/src/database/migrations/archive/1720423072348-AppSumoPresets.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AppSumoPresets1720423072348 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into app_sumo_preset(tier, user_count, term_in_days) values(1, 1, 36500); + insert into app_sumo_preset(tier, user_count, term_in_days) values(2, 5, 36500); + insert into app_sumo_preset(tier, user_count, term_in_days) values(3, 15, 36500); + insert into app_sumo_preset(tier, user_count, term_in_days) values(4, 30, 36500); + insert into app_sumo_preset(tier, user_count, term_in_days) values(5, 50, 36500); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720423711324-RenameAppSumo.ts b/backend/src/database/migrations/archive/1720423711324-RenameAppSumo.ts new file mode 100644 index 0000000..89078cf --- /dev/null +++ b/backend/src/database/migrations/archive/1720423711324-RenameAppSumo.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameAppSumo1720423711324 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table app_sumo_preset rename to appsumo_preset; + alter table app_sumo_license rename to appsumo_license; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720434041376-AppsumoLicenseUnique.ts b/backend/src/database/migrations/archive/1720434041376-AppsumoLicenseUnique.ts new file mode 100644 index 0000000..2522e1a --- /dev/null +++ b/backend/src/database/migrations/archive/1720434041376-AppsumoLicenseUnique.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AppsumoLicenseUnique1720434041376 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table appsumo_license add unique (license_key); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720446828855-AlterReadyMadeSolution.ts b/backend/src/database/migrations/archive/1720446828855-AlterReadyMadeSolution.ts new file mode 100644 index 0000000..5d03d7a --- /dev/null +++ b/backend/src/database/migrations/archive/1720446828855-AlterReadyMadeSolution.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterReadyMadeSolution1720446828855 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table ready_made_solution + add column account_id integer, + add foreign key (account_id) references account(id); + + update ready_made_solution rms + set account_id = acc.id + from account acc + where rms.subdomain = acc.subdomain; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720530507278-RenameAppsumoPreset.ts b/backend/src/database/migrations/archive/1720530507278-RenameAppsumoPreset.ts new file mode 100644 index 0000000..e726994 --- /dev/null +++ b/backend/src/database/migrations/archive/1720530507278-RenameAppsumoPreset.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameAppsumoPreset1720530507278 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table appsumo_preset rename to appsumo_tier; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720530714621-AlterAppsumoTier.ts b/backend/src/database/migrations/archive/1720530714621-AlterAppsumoTier.ts new file mode 100644 index 0000000..68c95a2 --- /dev/null +++ b/backend/src/database/migrations/archive/1720530714621-AlterAppsumoTier.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAppsumoTier1720530714621 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table appsumo_tier rename column user_count to user_limit; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720531175011-RenameSubscription.ts b/backend/src/database/migrations/archive/1720531175011-RenameSubscription.ts new file mode 100644 index 0000000..07bc569 --- /dev/null +++ b/backend/src/database/migrations/archive/1720531175011-RenameSubscription.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameSubscription1720531175011 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subscription rename to account_subscription; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720596831169-AlterAppsumoTier.ts b/backend/src/database/migrations/archive/1720596831169-AlterAppsumoTier.ts new file mode 100644 index 0000000..8616309 --- /dev/null +++ b/backend/src/database/migrations/archive/1720596831169-AlterAppsumoTier.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAppsumoTier1720596831169 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table appsumo_tier add column plan_name text; + update appsumo_tier set plan_name = 'All in One'; + alter table appsumo_tier alter column plan_name set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720597683965-UpdateAccountSubscription.ts b/backend/src/database/migrations/archive/1720597683965-UpdateAccountSubscription.ts new file mode 100644 index 0000000..7d6176d --- /dev/null +++ b/backend/src/database/migrations/archive/1720597683965-UpdateAccountSubscription.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateAccountSubscription1720597683965 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update account_subscription set plan_name = 'All in One' where plan_name = 'Premium'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720610809785-AlterAppsumoLicense.ts b/backend/src/database/migrations/archive/1720610809785-AlterAppsumoLicense.ts new file mode 100644 index 0000000..5d89246 --- /dev/null +++ b/backend/src/database/migrations/archive/1720610809785-AlterAppsumoLicense.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAppsumoLicense1720610809785 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table appsumo_license add column prev_license_key text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720619947686-AlterAppsumoTier.ts b/backend/src/database/migrations/archive/1720619947686-AlterAppsumoTier.ts new file mode 100644 index 0000000..17a65b8 --- /dev/null +++ b/backend/src/database/migrations/archive/1720619947686-AlterAppsumoTier.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAppsumoTier1720619947686 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update appsumo_tier set plan_name='AppSumo Tier 1' where tier=1; + update appsumo_tier set plan_name='AppSumo Tier 2' where tier=2; + update appsumo_tier set plan_name='AppSumo Tier 3' where tier=3; + update appsumo_tier set plan_name='AppSumo Tier 4' where tier=4; + update appsumo_tier set plan_name='AppSumo Tier 5' where tier=5; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1720778334472-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1720778334472-AlterAutomationEntityType.ts new file mode 100644 index 0000000..bdd35a4 --- /dev/null +++ b/backend/src/database/migrations/archive/1720778334472-AlterAutomationEntityType.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1720778334472 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type + drop column is_active, + add column process_id integer, + add foreign key (process_id) references automation_process(id) on delete cascade; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1721221679342-VoximplantSip.ts b/backend/src/database/migrations/archive/1721221679342-VoximplantSip.ts new file mode 100644 index 0000000..e1e8268 --- /dev/null +++ b/backend/src/database/migrations/archive/1721221679342-VoximplantSip.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantSip1721221679342 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_sip ( + id integer generated by default as identity, + external_id integer not null, + type text not null, + account_id integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722240313665-DBMemoryOptimization.ts b/backend/src/database/migrations/archive/1722240313665-DBMemoryOptimization.ts new file mode 100644 index 0000000..c53a5b8 --- /dev/null +++ b/backend/src/database/migrations/archive/1722240313665-DBMemoryOptimization.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DBMemoryOptimization1722240313665 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + SET work_mem = '64MB'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722240423838-DBIndexOptimization.ts b/backend/src/database/migrations/archive/1722240423838-DBIndexOptimization.ts new file mode 100644 index 0000000..26de9eb --- /dev/null +++ b/backend/src/database/migrations/archive/1722240423838-DBIndexOptimization.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DBIndexOptimization1722240423838 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX idx_entity_account_entity_type_id ON entity(account_id, entity_type_id, id); + + CREATE INDEX idx_entity_link_source_target_id ON entity_link(source_id, target_id); + + CREATE INDEX idx_field_value_entity_payload ON field_value USING gin (payload jsonb_path_ops); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722250997991-FieldSettingsIndexes.ts b/backend/src/database/migrations/archive/1722250997991-FieldSettingsIndexes.ts new file mode 100644 index 0000000..26c6de7 --- /dev/null +++ b/backend/src/database/migrations/archive/1722250997991-FieldSettingsIndexes.ts @@ -0,0 +1,18 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FieldSettingsIndexes1722250997991 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +CREATE INDEX idx_field_stage_settings_account_field_access ON field_stage_settings(account_id, field_id, access); +CREATE INDEX idx_field_stage_settings_account_field_stage_access ON field_stage_settings(account_id, field_id, stage_id, access); + +CREATE INDEX idx_field_user_settings_account_field_access ON field_user_settings(account_id, field_id, access); +CREATE INDEX idx_field_user_settings_account_field_user_access ON field_user_settings(account_id, field_id, user_id, access); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722251334680-AccountApiAccessIndex.ts b/backend/src/database/migrations/archive/1722251334680-AccountApiAccessIndex.ts new file mode 100644 index 0000000..60b58e2 --- /dev/null +++ b/backend/src/database/migrations/archive/1722251334680-AccountApiAccessIndex.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AccountApiAccessIndex1722251334680 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE UNIQUE INDEX idx_account_api_access_api_key ON account_api_access(api_key); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722325875311-UpdateProjectFieldsSortOrder.ts b/backend/src/database/migrations/archive/1722325875311-UpdateProjectFieldsSortOrder.ts new file mode 100644 index 0000000..b1ee4cb --- /dev/null +++ b/backend/src/database/migrations/archive/1722325875311-UpdateProjectFieldsSortOrder.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateProjectFieldsSortOrder1722325875311 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update field set sort_order = -1 where code is not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722340602724-DepartmentIndexes.ts b/backend/src/database/migrations/archive/1722340602724-DepartmentIndexes.ts new file mode 100644 index 0000000..cd1185b --- /dev/null +++ b/backend/src/database/migrations/archive/1722340602724-DepartmentIndexes.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DepartmentIndexes1722340602724 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX idx_department_id_account_id ON department(id, account_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722500514353-EntityListSettingsIndex.ts b/backend/src/database/migrations/archive/1722500514353-EntityListSettingsIndex.ts new file mode 100644 index 0000000..5977190 --- /dev/null +++ b/backend/src/database/migrations/archive/1722500514353-EntityListSettingsIndex.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityListSettingsIndex1722500514353 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX idx_entity_list_settings_account_entity_board + ON entity_list_settings(account_id, entity_type_id, board_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722870704258-AlterDepartmentIsActive.ts b/backend/src/database/migrations/archive/1722870704258-AlterDepartmentIsActive.ts new file mode 100644 index 0000000..ee146f6 --- /dev/null +++ b/backend/src/database/migrations/archive/1722870704258-AlterDepartmentIsActive.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterDepartmentIsActive1722870704258 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table department add column is_active boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1722871843239-AlterUserDepartment.ts b/backend/src/database/migrations/archive/1722871843239-AlterUserDepartment.ts new file mode 100644 index 0000000..56fa485 --- /dev/null +++ b/backend/src/database/migrations/archive/1722871843239-AlterUserDepartment.ts @@ -0,0 +1,16 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterUserDepartment1722871843239 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table users + drop constraint users_department_id_fkey, + add constraint users_department_id_fkey foreign key (department_id) references department(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1723373462514-AddMailMessageScheduled.ts b/backend/src/database/migrations/archive/1723373462514-AddMailMessageScheduled.ts new file mode 100644 index 0000000..318251a --- /dev/null +++ b/backend/src/database/migrations/archive/1723373462514-AddMailMessageScheduled.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailMessageScheduled1723373462514 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table mail_message_scheduled ( + id integer generated by default as identity, + account_id integer not null, + created_by integer not null, + created_at timestamp not null default now(), + mailbox_id integer not null, + subject text not null, + content text not null, + send_to text not null, + send_as_html boolean not null, + entity_id integer, + sent_at timestamp, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (created_by) references users(id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (entity_id) references entity(id) on delete set null + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1723386042005-AlterMailMessageScheduled.ts b/backend/src/database/migrations/archive/1723386042005-AlterMailMessageScheduled.ts new file mode 100644 index 0000000..fbbf34b --- /dev/null +++ b/backend/src/database/migrations/archive/1723386042005-AlterMailMessageScheduled.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessageScheduled1723386042005 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message_scheduled rename column created_by to send_from; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1723641840096-AlterMailMessageScheduled.ts b/backend/src/database/migrations/archive/1723641840096-AlterMailMessageScheduled.ts new file mode 100644 index 0000000..ec9a03b --- /dev/null +++ b/backend/src/database/migrations/archive/1723641840096-AlterMailMessageScheduled.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessageScheduled1723641840096 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message_scheduled drop column send_as_html; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1723814067070-MailboxEmailsPerDay.ts b/backend/src/database/migrations/archive/1723814067070-MailboxEmailsPerDay.ts new file mode 100644 index 0000000..d63f986 --- /dev/null +++ b/backend/src/database/migrations/archive/1723814067070-MailboxEmailsPerDay.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailboxEmailsPerDay1723814067070 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update mailbox set emails_per_day = 100 where emails_per_day is null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724058794170-AlterFieldGroup.ts b/backend/src/database/migrations/archive/1724058794170-AlterFieldGroup.ts new file mode 100644 index 0000000..cb5870c --- /dev/null +++ b/backend/src/database/migrations/archive/1724058794170-AlterFieldGroup.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterFieldGroup1724058794170 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field_group add column code text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724422282832-EntityUpdatedAt.ts b/backend/src/database/migrations/archive/1724422282832-EntityUpdatedAt.ts new file mode 100644 index 0000000..bfc27d7 --- /dev/null +++ b/backend/src/database/migrations/archive/1724422282832-EntityUpdatedAt.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityUpdatedAt1724422282832 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity add column updated_at timestamp without time zone; + update entity set updated_at = created_at; + alter table entity alter column updated_at set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724657864701-AlterVoximplantSipName.ts b/backend/src/database/migrations/archive/1724657864701-AlterVoximplantSipName.ts new file mode 100644 index 0000000..12b3bb4 --- /dev/null +++ b/backend/src/database/migrations/archive/1724657864701-AlterVoximplantSipName.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterVoximplantSipName1724657864701 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table voximplant_sip add column name text; + update voximplant_sip set name = external_id; + alter table voximplant_sip alter column name set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724658513639-VoximplantSipUser.ts b/backend/src/database/migrations/archive/1724658513639-VoximplantSipUser.ts new file mode 100644 index 0000000..1bb2fc4 --- /dev/null +++ b/backend/src/database/migrations/archive/1724658513639-VoximplantSipUser.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VoximplantSipUser1724658513639 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table voximplant_sip_user ( + sip_id integer, + user_id integer, + account_id integer not null, + primary key (sip_id, user_id), + foreign key (sip_id) references voximplant_sip(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724668825212-EntityBoardId.ts b/backend/src/database/migrations/archive/1724668825212-EntityBoardId.ts new file mode 100644 index 0000000..7fb2cf9 --- /dev/null +++ b/backend/src/database/migrations/archive/1724668825212-EntityBoardId.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityBoardId1724668825212 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity + add column board_id integer, + add foreign key (board_id) references board(id) on delete cascade; + + update entity + set board_id = (select board_id from stage where stage.id = entity.stage_id) + where entity.stage_id is not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724744316233-FieldGroupDefault.ts b/backend/src/database/migrations/archive/1724744316233-FieldGroupDefault.ts new file mode 100644 index 0000000..f56bcc3 --- /dev/null +++ b/backend/src/database/migrations/archive/1724744316233-FieldGroupDefault.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FieldGroupDefault1724744316233 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + with first_group as ( + select id + from field_group + where (entity_type_id, sort_order) in ( + select entity_type_id, min(sort_order) + from field_group + group by entity_type_id + ) + ) + update field_group + set code = 'details' + where id in (select id from first_group); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724747098462-DeprtmentIdIdentity.ts b/backend/src/database/migrations/archive/1724747098462-DeprtmentIdIdentity.ts new file mode 100644 index 0000000..056a66a --- /dev/null +++ b/backend/src/database/migrations/archive/1724747098462-DeprtmentIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DeprtmentIdIdentity1724747098462 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE department ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS department_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM department; + EXECUTE 'ALTER TABLE department ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724761173125-DepartmentSettings.ts b/backend/src/database/migrations/archive/1724761173125-DepartmentSettings.ts new file mode 100644 index 0000000..9b9be88 --- /dev/null +++ b/backend/src/database/migrations/archive/1724761173125-DepartmentSettings.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DepartmentSettings1724761173125 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table department_settings ( + department_id integer not null, + account_id integer not null, + working_days text, + working_time_from time without time zone, + working_time_to time without time zone, + time_zone text, + primary key (department_id), + foreign key (department_id) references department(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724944661564-UserIdIdentity.ts b/backend/src/database/migrations/archive/1724944661564-UserIdIdentity.ts new file mode 100644 index 0000000..7d0a2c0 --- /dev/null +++ b/backend/src/database/migrations/archive/1724944661564-UserIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserIdIdentity1724944661564 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE users ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS user_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM users; + EXECUTE 'ALTER TABLE users ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724945283499-AccountIdIdentity.ts b/backend/src/database/migrations/archive/1724945283499-AccountIdIdentity.ts new file mode 100644 index 0000000..dab88d5 --- /dev/null +++ b/backend/src/database/migrations/archive/1724945283499-AccountIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AccountIdIdentity1724945283499 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE account ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS account_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM account; + EXECUTE 'ALTER TABLE account ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724946396552-DocumentTemplateIdIdentity.ts b/backend/src/database/migrations/archive/1724946396552-DocumentTemplateIdIdentity.ts new file mode 100644 index 0000000..7cca312 --- /dev/null +++ b/backend/src/database/migrations/archive/1724946396552-DocumentTemplateIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DocumentTemplateIdIdentity1724946396552 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE document_template ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS document_template_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM document_template; + EXECUTE 'ALTER TABLE document_template ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724946751431-NotificationIdIdentity.ts b/backend/src/database/migrations/archive/1724946751431-NotificationIdIdentity.ts new file mode 100644 index 0000000..8aed232 --- /dev/null +++ b/backend/src/database/migrations/archive/1724946751431-NotificationIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NotificationIdIdentity1724946751431 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE notification ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS notification_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM notification; + EXECUTE 'ALTER TABLE notification ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724946917573-NotificationSettingsIdIdentity.ts b/backend/src/database/migrations/archive/1724946917573-NotificationSettingsIdIdentity.ts new file mode 100644 index 0000000..36ba70a --- /dev/null +++ b/backend/src/database/migrations/archive/1724946917573-NotificationSettingsIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NotificationSettingsIdIdentity1724946917573 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE notification_settings ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS notification_settings_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM notification_settings; + EXECUTE 'ALTER TABLE notification_settings ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1724946935740-NotificationTypeSettingsIdIdentity.ts b/backend/src/database/migrations/archive/1724946935740-NotificationTypeSettingsIdIdentity.ts new file mode 100644 index 0000000..e621169 --- /dev/null +++ b/backend/src/database/migrations/archive/1724946935740-NotificationTypeSettingsIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NotificationTypeSettingsIdIdentity1724946935740 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE notification_type_settings ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS notification_type_settings_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM notification_type_settings; + EXECUTE 'ALTER TABLE notification_type_settings ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725008953241-EntityTypeActionTypeRename.ts b/backend/src/database/migrations/archive/1725008953241-EntityTypeActionTypeRename.ts new file mode 100644 index 0000000..5952bd8 --- /dev/null +++ b/backend/src/database/migrations/archive/1725008953241-EntityTypeActionTypeRename.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityTypeActionTypeRename1725008953241 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + UPDATE automation_entity_type + SET actions = replace(actions::text, '"create_task"', '"task_create"')::jsonb + WHERE actions::text LIKE '%"create_task"%'; + + UPDATE automation_entity_type + SET actions = replace(actions::text, '"create_activity"', '"activity_create"')::jsonb + WHERE actions::text LIKE '%"create_activity"%'; + + UPDATE automation_entity_type + SET actions = replace(actions::text, '"change_stage"', '"entity_stage_change"')::jsonb + WHERE actions::text LIKE '%"change_stage"%'; + + UPDATE automation_entity_type + SET actions = replace(actions::text, '"send_email"', '"email_send"')::jsonb + WHERE actions::text LIKE '%"send_email"%'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725024403154-ChatIdIdentity.ts b/backend/src/database/migrations/archive/1725024403154-ChatIdIdentity.ts new file mode 100644 index 0000000..5f9ea75 --- /dev/null +++ b/backend/src/database/migrations/archive/1725024403154-ChatIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatIdIdentity1725024403154 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE chat ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS chat_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM chat; + EXECUTE 'ALTER TABLE chat ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725024779580-ChatMessageFileIdIdentity.ts b/backend/src/database/migrations/archive/1725024779580-ChatMessageFileIdIdentity.ts new file mode 100644 index 0000000..6e95b54 --- /dev/null +++ b/backend/src/database/migrations/archive/1725024779580-ChatMessageFileIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatMessageFileIdIdentity1725024779580 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE chat_message_file ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS chat_message_file_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM chat_message_file; + EXECUTE 'ALTER TABLE chat_message_file ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725024956416-ChatMessageReactionIdIdentity.ts b/backend/src/database/migrations/archive/1725024956416-ChatMessageReactionIdIdentity.ts new file mode 100644 index 0000000..84b35f8 --- /dev/null +++ b/backend/src/database/migrations/archive/1725024956416-ChatMessageReactionIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatMessageReactionIdIdentity1725024956416 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE chat_message_reaction ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS chat_message_reaction_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM chat_message_reaction; + EXECUTE 'ALTER TABLE chat_message_reaction ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725025200036-ChatMessageIdIdentity.ts b/backend/src/database/migrations/archive/1725025200036-ChatMessageIdIdentity.ts new file mode 100644 index 0000000..ac851ce --- /dev/null +++ b/backend/src/database/migrations/archive/1725025200036-ChatMessageIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatMessageIdIdentity1725025200036 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE chat_message ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS chat_message_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM chat_message; + EXECUTE 'ALTER TABLE chat_message ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725025378404-ChatProviderIdIdentity.ts b/backend/src/database/migrations/archive/1725025378404-ChatProviderIdIdentity.ts new file mode 100644 index 0000000..f8ae880 --- /dev/null +++ b/backend/src/database/migrations/archive/1725025378404-ChatProviderIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatProviderIdIdentity1725025378404 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE chat_provider ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS chat_provider_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM chat_provider; + EXECUTE 'ALTER TABLE chat_provider ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725025624010-ChatUserIdIdentity.ts b/backend/src/database/migrations/archive/1725025624010-ChatUserIdIdentity.ts new file mode 100644 index 0000000..baa6cb2 --- /dev/null +++ b/backend/src/database/migrations/archive/1725025624010-ChatUserIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatUserIdIdentity1725025624010 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE chat_user ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS chat_user_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM chat_user; + EXECUTE 'ALTER TABLE chat_user ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725269507813-ChatProviderMessagePerDay.ts b/backend/src/database/migrations/archive/1725269507813-ChatProviderMessagePerDay.ts new file mode 100644 index 0000000..a3d5612 --- /dev/null +++ b/backend/src/database/migrations/archive/1725269507813-ChatProviderMessagePerDay.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatProviderMessagePerDay1725269507813 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider add column message_per_day integer; + update chat_provider set message_per_day = 10 where message_per_day is null; + alter table chat_provider alter column message_per_day set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725622826309-MailboxLatsActiveAt.ts b/backend/src/database/migrations/archive/1725622826309-MailboxLatsActiveAt.ts new file mode 100644 index 0000000..2954b1c --- /dev/null +++ b/backend/src/database/migrations/archive/1725622826309-MailboxLatsActiveAt.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailboxLatsActiveAt1725622826309 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox add column last_active_at timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1725980172270-EntityCopiedFromConstrains.ts b/backend/src/database/migrations/archive/1725980172270-EntityCopiedFromConstrains.ts new file mode 100644 index 0000000..e373968 --- /dev/null +++ b/backend/src/database/migrations/archive/1725980172270-EntityCopiedFromConstrains.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityCopiedFromConstrains1725980172270 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update entity + set copied_from = null, copied_count = null + where entity.copied_from is not null and not exists (select id from entity e2 where e2.id = entity.copied_from); + + alter table entity add foreign key (copied_from) references entity(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1726478454833-ChatMessageScheduled.ts b/backend/src/database/migrations/archive/1726478454833-ChatMessageScheduled.ts new file mode 100644 index 0000000..6a7381f --- /dev/null +++ b/backend/src/database/migrations/archive/1726478454833-ChatMessageScheduled.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ChatMessageScheduled1726478454833 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_message_scheduled ( + id integer generated by default as identity, + account_id integer not null, + created_at timestamp without time zone not null, + send_from integer not null, + provider_id integer not null, + title text, + message text not null, + send_to text not null, + entity_id integer not null, + sent_at timestamp without time zone, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (send_from) references users(id) on delete cascade, + foreign key (provider_id) references chat_provider(id) on delete cascade, + foreign key (entity_id) references entity(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1727179988017-AlterChatMessageScheduled.ts b/backend/src/database/migrations/archive/1727179988017-AlterChatMessageScheduled.ts new file mode 100644 index 0000000..16d4448 --- /dev/null +++ b/backend/src/database/migrations/archive/1727179988017-AlterChatMessageScheduled.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatMessageScheduled1727179988017 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_message_scheduled + drop column title, + drop column send_to, + add column only_first boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1727265648107-MailFolderOptimization.ts b/backend/src/database/migrations/archive/1727265648107-MailFolderOptimization.ts new file mode 100644 index 0000000..bc2259d --- /dev/null +++ b/backend/src/database/migrations/archive/1727265648107-MailFolderOptimization.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailFolderOptimization1727265648107 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX IF EXISTS mailbox_folder_account_id_mailbox_id_idx; + + CREATE INDEX idx_mailbox_folder_mailbox_id ON mailbox_folder(mailbox_id); + CREATE INDEX idx_mail_message_folder_folder_id ON mail_message_folder(folder_id); + CREATE INDEX idx_mail_message_id_is_seen ON mail_message(id, is_seen); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1727682923696-AlterSiteForm.ts b/backend/src/database/migrations/archive/1727682923696-AlterSiteForm.ts new file mode 100644 index 0000000..763244a --- /dev/null +++ b/backend/src/database/migrations/archive/1727682923696-AlterSiteForm.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1727682923696 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form add column multiform_enabled boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1727766355582-TaskCommentIdIdentity.ts b/backend/src/database/migrations/archive/1727766355582-TaskCommentIdIdentity.ts new file mode 100644 index 0000000..4e8043b --- /dev/null +++ b/backend/src/database/migrations/archive/1727766355582-TaskCommentIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TaskCommentIdIdentity1727766355582 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE task_comment ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS task_comment_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM task_comment; + EXECUTE 'ALTER TABLE task_comment ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1727775973380-AlterAutomationProcess.ts b/backend/src/database/migrations/archive/1727775973380-AlterAutomationProcess.ts new file mode 100644 index 0000000..2ce03a9 --- /dev/null +++ b/backend/src/database/migrations/archive/1727775973380-AlterAutomationProcess.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationProcess1727775973380 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_process + add column created_by integer, + add column name text, + add column type text, + add column object_id integer, + add column is_readonly boolean default false, + add foreign key (created_by) references users(id); + + update automation_process ap + set + created_by = aet.created_by, + name = aet.name, + type = 'entity_type', + object_id = aet.entity_type_id, + is_readonly = true + from automation_entity_type aet + where aet.process_id = ap.id; + + alter table automation_process + alter column created_by set not null, + alter column name set not null, + alter column type set not null, + alter column is_readonly set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728296331516-RenameStage.ts b/backend/src/database/migrations/archive/1728296331516-RenameStage.ts new file mode 100644 index 0000000..2aaaa1f --- /dev/null +++ b/backend/src/database/migrations/archive/1728296331516-RenameStage.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenameStage1728296331516 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table stage rename to board_stage; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728312582354-AlterBoardStage.ts b/backend/src/database/migrations/archive/1728312582354-AlterBoardStage.ts new file mode 100644 index 0000000..3e96c86 --- /dev/null +++ b/backend/src/database/migrations/archive/1728312582354-AlterBoardStage.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterBoardStage1728312582354 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board_stage add column is_active boolean not null default true; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728384950674-BoardIdIdentity.ts b/backend/src/database/migrations/archive/1728384950674-BoardIdIdentity.ts new file mode 100644 index 0000000..62880a5 --- /dev/null +++ b/backend/src/database/migrations/archive/1728384950674-BoardIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class BoardIdIdentity1728384950674 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE board ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS board_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM board; + EXECUTE 'ALTER TABLE board ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728402696162-AlterBoard.ts b/backend/src/database/migrations/archive/1728402696162-AlterBoard.ts new file mode 100644 index 0000000..40f1216 --- /dev/null +++ b/backend/src/database/migrations/archive/1728402696162-AlterBoard.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterBoard1728402696162 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board + drop column if exists code, + drop column if exists need_migration; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728465792633-AlterBoardStage.ts b/backend/src/database/migrations/archive/1728465792633-AlterBoardStage.ts new file mode 100644 index 0000000..fcb8691 --- /dev/null +++ b/backend/src/database/migrations/archive/1728465792633-AlterBoardStage.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterBoardStage1728465792633 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board_stage + drop constraint stage_board_id_fkey, + add constraint stage_board_id_fkey foreign key (board_id) references board(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728468439082-AlterBoardStage.ts b/backend/src/database/migrations/archive/1728468439082-AlterBoardStage.ts new file mode 100644 index 0000000..b17dcd6 --- /dev/null +++ b/backend/src/database/migrations/archive/1728468439082-AlterBoardStage.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterBoardStage1728468439082 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table board_stage drop column is_active; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728471228996-AlterAutomationEntityType.ts b/backend/src/database/migrations/archive/1728471228996-AlterAutomationEntityType.ts new file mode 100644 index 0000000..e4a4c31 --- /dev/null +++ b/backend/src/database/migrations/archive/1728471228996-AlterAutomationEntityType.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAutomationEntityType1728471228996 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table automation_entity_type + drop constraint automation_entity_type_entity_type_id_fkey, + alter column entity_type_id drop not null, + drop constraint automation_entity_type_board_id_fkey, + drop constraint automation_entity_type_stage_id_fkey; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728892125136-AlterAccountSettings.ts b/backend/src/database/migrations/archive/1728892125136-AlterAccountSettings.ts new file mode 100644 index 0000000..272c778 --- /dev/null +++ b/backend/src/database/migrations/archive/1728892125136-AlterAccountSettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSettings1728892125136 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_settings add column date_format text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728981267739-AlterObjectPermission.ts b/backend/src/database/migrations/archive/1728981267739-AlterObjectPermission.ts new file mode 100644 index 0000000..41c1880 --- /dev/null +++ b/backend/src/database/migrations/archive/1728981267739-AlterObjectPermission.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterObjectPermission1728981267739 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table object_permission + add column user_id integer, + add foreign key (user_id) references users(id) on delete cascade; + + update object_permission set user_id = ( + select user_id from user_object_permission uop where uop.object_permission_id = object_permission.id + ); + + delete from object_permission where user_id is null; + alter table object_permission alter column user_id set not null; + + drop table user_object_permission; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728984932906-AlterObjectPermissionIndices.ts b/backend/src/database/migrations/archive/1728984932906-AlterObjectPermissionIndices.ts new file mode 100644 index 0000000..f046abb --- /dev/null +++ b/backend/src/database/migrations/archive/1728984932906-AlterObjectPermissionIndices.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterObjectPermissionIndices1728984932906 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX IF EXISTS object_permission_object_type_object_id_idx; + + CREATE INDEX object_permission_account_id_user_id_idx ON object_permission(account_id, user_id); + + CREATE INDEX object_permission_account_user_type_id_idx + ON object_permission(account_id, user_id, object_type, object_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1728988723314-AlterObjectPermission.ts b/backend/src/database/migrations/archive/1728988723314-AlterObjectPermission.ts new file mode 100644 index 0000000..ad149fe --- /dev/null +++ b/backend/src/database/migrations/archive/1728988723314-AlterObjectPermission.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterObjectPermission1728988723314 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table object_permission + alter column object_type type text, + alter column create_permission type text, + alter column view_permission type text, + alter column edit_permission type text, + alter column delete_permission type text, + add column report_permission text; + + update object_permission set report_permission = edit_permission; + + alter table object_permission alter column report_permission set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729001229086-AlterObjectPermission.ts b/backend/src/database/migrations/archive/1729001229086-AlterObjectPermission.ts new file mode 100644 index 0000000..f08de3a --- /dev/null +++ b/backend/src/database/migrations/archive/1729001229086-AlterObjectPermission.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterObjectPermission1729001229086 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table object_permission add column dashboard_permission text; + + update object_permission set dashboard_permission = edit_permission; + + alter table object_permission alter column dashboard_permission set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729680141757-UpdateTutorialItem.ts b/backend/src/database/migrations/archive/1729680141757-UpdateTutorialItem.ts new file mode 100644 index 0000000..d50878c --- /dev/null +++ b/backend/src/database/migrations/archive/1729680141757-UpdateTutorialItem.ts @@ -0,0 +1,29 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateTutorialItem1729680141757 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update tutorial_item set link = 'https://rutube.ru/video/7866956dff4e26e1c898c1a31450b3ff' where link = 'https://youtu.be/niN8RMBBd3M'; + update tutorial_item set link = 'https://rutube.ru/video/1fc6c8cf6306c32f7775dc91b557fe6b' where link = 'https://youtu.be/GyqNL-Rqs2o'; + update tutorial_item set link = 'https://rutube.ru/video/55cfc3a2a8fe8d891cab17135d6d758a' where link = 'https://youtu.be/M6LmudYIVIg'; + update tutorial_item set link = 'https://rutube.ru/video/76111a083b5579cb8c7e3e3fb6b225f8' where link = 'https://youtu.be/fhweWoG-qwI'; + update tutorial_item set link = 'https://rutube.ru/video/d0b6ff03e0005f31236d8fa1d31a942d' where link = 'https://youtu.be/Bo0UNdxB130'; + update tutorial_item set link = 'https://rutube.ru/video/c5a7cfbeda45bd110f0cba20bcc399ac' where link = 'https://youtu.be/sy202rfbuB0'; + update tutorial_item set link = 'https://rutube.ru/video/286a3a8bd132332648bb7a37e313943c' where link = 'https://youtu.be/vZ6XDuYBDfY'; + update tutorial_item set link = 'https://rutube.ru/video/2384f907194a481a9d88486d85772496' where link = 'https://youtu.be/uQaowrrf6DQ'; + update tutorial_item set link = 'https://rutube.ru/video/861b2a529cf2fb93109df88e55458be2' where link = 'https://youtu.be/d7TK5yPIa1k'; + update tutorial_item set link = 'https://rutube.ru/video/e2b52a8139e7da53fd280c1e37694e04' where link = 'https://youtu.be/x6Fy9FazN9k'; + update tutorial_item set link = 'https://rutube.ru/video/39a6b69a7042b6318a22eeedc08cdf51' where link = 'https://youtu.be/ui5G50e9aV4'; + update tutorial_item set link = 'https://rutube.ru/video/d6c71e34f6ff41463616f3884d1e0b7a' where link = 'https://youtu.be/aaV12UJqq1M'; + update tutorial_item set link = 'https://rutube.ru/video/58c01491d9b6391eb5e59f19d58e47c5' where link = 'https://youtu.be/_vW9prpKSSo'; + update tutorial_item set link = 'https://rutube.ru/video/d0a83e46fa070e0c763d916ec4d7f9fd' where link = 'https://youtu.be/tpP7Ra7SwK4'; + update tutorial_item set link = 'https://rutube.ru/video/203a6cea52d5935c4503f2632373b87b' where link = 'https://youtu.be/oHAQMEfZSBY'; + update tutorial_item set link = 'https://rutube.ru/video/dedae77a65ded0aa531f8c5c90075f45' where link = 'https://youtu.be/WRZn2sGlw_s'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729758131725-AlterMailMessage.ts b/backend/src/database/migrations/archive/1729758131725-AlterMailMessage.ts new file mode 100644 index 0000000..f74741a --- /dev/null +++ b/backend/src/database/migrations/archive/1729758131725-AlterMailMessage.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessage1729758131725 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mail_message alter column sent_from drop not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729853713049-RemoveUnusedIndexes.ts b/backend/src/database/migrations/archive/1729853713049-RemoveUnusedIndexes.ts new file mode 100644 index 0000000..1801625 --- /dev/null +++ b/backend/src/database/migrations/archive/1729853713049-RemoveUnusedIndexes.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveUnusedIndexes1729853713049 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop index if exists entity_link_source_id_sort_order_id_idx; + drop index if exists idx_field_value_entity_payload; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729856828756-ActivityTypeIdIdentity.ts b/backend/src/database/migrations/archive/1729856828756-ActivityTypeIdIdentity.ts new file mode 100644 index 0000000..cf0598d --- /dev/null +++ b/backend/src/database/migrations/archive/1729856828756-ActivityTypeIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ActivityTypeIdIdentity1729856828756 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE activity_type ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS activity_type_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM activity_type; + EXECUTE 'ALTER TABLE activity_type ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729857179487-EntityTypeIdIdentity.ts b/backend/src/database/migrations/archive/1729857179487-EntityTypeIdIdentity.ts new file mode 100644 index 0000000..6d10b9c --- /dev/null +++ b/backend/src/database/migrations/archive/1729857179487-EntityTypeIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityTypeIdIdentity1729857179487 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE entity_type ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS entity_type_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM entity_type; + EXECUTE 'ALTER TABLE entity_type ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1729859612700-FieldValueIdIdentity.ts b/backend/src/database/migrations/archive/1729859612700-FieldValueIdIdentity.ts new file mode 100644 index 0000000..c23cd2d --- /dev/null +++ b/backend/src/database/migrations/archive/1729859612700-FieldValueIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FieldValueIdIdentity1729859612700 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE field_value ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS field_value_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM field_value; + EXECUTE 'ALTER TABLE field_value ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1730103007231-EntitySearch.ts b/backend/src/database/migrations/archive/1730103007231-EntitySearch.ts new file mode 100644 index 0000000..aedd80d --- /dev/null +++ b/backend/src/database/migrations/archive/1730103007231-EntitySearch.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntitySearch1730103007231 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + -- entity +ALTER TABLE entity ADD COLUMN IF NOT EXISTS name_tsv tsvector; + +UPDATE entity SET name_tsv = to_tsvector('simple', lower(name)); + +CREATE OR REPLACE FUNCTION update_name_tsv() RETURNS trigger AS $$ +BEGIN + NEW.name_tsv := to_tsvector('simple', lower(NEW.name)); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER update_name_tsv +BEFORE INSERT OR UPDATE ON entity +FOR EACH ROW +EXECUTE FUNCTION update_name_tsv(); + +CREATE INDEX IF NOT EXISTS idx_entity_name_tsv ON entity USING GIN (name_tsv); + +CREATE INDEX IF NOT EXISTS idx_entity_account_entity_type_created_at_id +ON entity (account_id, entity_type_id, created_at DESC, id DESC); + +-- field value +ALTER TABLE field_value ADD COLUMN IF NOT EXISTS payload_tsv tsvector; +UPDATE field_value +SET payload_tsv = CASE + WHEN field_type IN ('text', 'link') THEN to_tsvector('simple', lower(payload->>'value')) + WHEN field_type = 'multitext' THEN to_tsvector('simple', lower(payload->>'values')) + WHEN field_type = 'email' THEN to_tsvector('simple', lower(payload->>'values')) + WHEN field_type = 'phone' THEN to_tsvector('simple', lower(replace(payload->>'values', ' ', ''))) + ELSE NULL +END; + +CREATE OR REPLACE FUNCTION update_field_value_payload_tsv() +RETURNS TRIGGER AS $$ +BEGIN + NEW.payload_tsv = CASE + WHEN NEW.field_type IN ('text', 'link') THEN to_tsvector('simple', lower(NEW.payload->>'value')) + WHEN NEW.field_type = 'multitext' THEN to_tsvector('simple', lower(NEW.payload->>'values')) + WHEN NEW.field_type = 'email' THEN to_tsvector('simple', lower(NEW.payload->>'values')) + WHEN NEW.field_type = 'phone' THEN to_tsvector('simple', lower(replace(NEW.payload->>'values', ' ', ''))) + ELSE NULL + END; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_update_field_value_payload_tsv +BEFORE INSERT OR UPDATE OF payload, field_type ON field_value +FOR EACH ROW +EXECUTE FUNCTION update_field_value_payload_tsv(); + +CREATE INDEX IF NOT EXISTS idx_field_value_payload_tsv +ON field_value USING GIN (payload_tsv); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1730212128907-ScheduleAppointmentIndexes.ts b/backend/src/database/migrations/archive/1730212128907-ScheduleAppointmentIndexes.ts new file mode 100644 index 0000000..4626bcd --- /dev/null +++ b/backend/src/database/migrations/archive/1730212128907-ScheduleAppointmentIndexes.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduleAppointmentIndexes1730212128907 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS idx_schedule_appointment_acc_sch_status + ON schedule_appointment (account_id, schedule_id, entity_id) + WHERE status != 'canceled' AND entity_id IS NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1730472621229-TaskIndexes.ts b/backend/src/database/migrations/archive/1730472621229-TaskIndexes.ts new file mode 100644 index 0000000..edaa3c9 --- /dev/null +++ b/backend/src/database/migrations/archive/1730472621229-TaskIndexes.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TaskIndexes1730472621229 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS idx_task_account_stage_created_by ON task (account_id, stage_id, created_by); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1730796442786-AlterMailMessageScheduled.ts b/backend/src/database/migrations/archive/1730796442786-AlterMailMessageScheduled.ts new file mode 100644 index 0000000..89d075e --- /dev/null +++ b/backend/src/database/migrations/archive/1730796442786-AlterMailMessageScheduled.ts @@ -0,0 +1,19 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailMessageScheduled1730796442786 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from mail_message_scheduled where entity_id is null; + + ALTER TABLE mail_message_scheduled + DROP CONSTRAINT mail_message_scheduled_entity_id_fkey, + ADD CONSTRAINT mail_message_scheduled_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE, + ALTER COLUMN entity_id SET NOT NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1730988793369-AlterSchedule.ts b/backend/src/database/migrations/archive/1730988793369-AlterSchedule.ts new file mode 100644 index 0000000..3fb5789 --- /dev/null +++ b/backend/src/database/migrations/archive/1730988793369-AlterSchedule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1730988793369 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule add column one_entity_per_day boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731239585214-UsersAccessibleUsers.ts b/backend/src/database/migrations/archive/1731239585214-UsersAccessibleUsers.ts new file mode 100644 index 0000000..7b0cac9 --- /dev/null +++ b/backend/src/database/migrations/archive/1731239585214-UsersAccessibleUsers.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UsersAccessibleUsers1731239585214 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table users_accessible_users ( + user_id integer not null, + accessible_id integer not null, + foreign key (user_id) references users(id) on delete cascade, + foreign key (accessible_id) references users(id) on delete cascade, + primary key (user_id, accessible_id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731318295236-FieldIndex.ts b/backend/src/database/migrations/archive/1731318295236-FieldIndex.ts new file mode 100644 index 0000000..429e331 --- /dev/null +++ b/backend/src/database/migrations/archive/1731318295236-FieldIndex.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FieldIndex1731318295236 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS field_account_entity_type_sort_order_idx + ON field (account_id, entity_type_id, sort_order); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731491092637-WarehouseIdIdentity.ts b/backend/src/database/migrations/archive/1731491092637-WarehouseIdIdentity.ts new file mode 100644 index 0000000..7054df8 --- /dev/null +++ b/backend/src/database/migrations/archive/1731491092637-WarehouseIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class WarehouseIdIdentity1731491092637 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE warehouse ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS warehouse_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM warehouse; + EXECUTE 'ALTER TABLE warehouse ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731491294395-ShipmentItemIdIdentity.ts b/backend/src/database/migrations/archive/1731491294395-ShipmentItemIdIdentity.ts new file mode 100644 index 0000000..c878bc2 --- /dev/null +++ b/backend/src/database/migrations/archive/1731491294395-ShipmentItemIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ShipmentItemIdIdentity1731491294395 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE shipment_item ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS shipment_item_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM shipment_item; + EXECUTE 'ALTER TABLE shipment_item ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731491510990-ShipmentIdIdentity.ts b/backend/src/database/migrations/archive/1731491510990-ShipmentIdIdentity.ts new file mode 100644 index 0000000..19d492d --- /dev/null +++ b/backend/src/database/migrations/archive/1731491510990-ShipmentIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ShipmentIdIdentity1731491510990 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE shipment ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS shipment_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM shipment; + EXECUTE 'ALTER TABLE shipment ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731491686717-ReservationIdIdentity.ts b/backend/src/database/migrations/archive/1731491686717-ReservationIdIdentity.ts new file mode 100644 index 0000000..9827c46 --- /dev/null +++ b/backend/src/database/migrations/archive/1731491686717-ReservationIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ReservationIdIdentity1731491686717 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE reservation ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS reservation_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM reservation; + EXECUTE 'ALTER TABLE reservation ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731502099273-ProductCategoryIdIdentity.ts b/backend/src/database/migrations/archive/1731502099273-ProductCategoryIdIdentity.ts new file mode 100644 index 0000000..199acc1 --- /dev/null +++ b/backend/src/database/migrations/archive/1731502099273-ProductCategoryIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductCategoryIdIdentity1731502099273 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE product_category ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS product_category_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM product_category; + EXECUTE 'ALTER TABLE product_category ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731502604723-ProductIdIdentity.ts b/backend/src/database/migrations/archive/1731502604723-ProductIdIdentity.ts new file mode 100644 index 0000000..f9087b7 --- /dev/null +++ b/backend/src/database/migrations/archive/1731502604723-ProductIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductIdIdentity1731502604723 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE product ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS product_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM product; + EXECUTE 'ALTER TABLE product ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731502805263-ProductPriceIdIdentity.ts b/backend/src/database/migrations/archive/1731502805263-ProductPriceIdIdentity.ts new file mode 100644 index 0000000..19e2a69 --- /dev/null +++ b/backend/src/database/migrations/archive/1731502805263-ProductPriceIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProductPriceIdIdentity1731502805263 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE product_price ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS product_price_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM product_price; + EXECUTE 'ALTER TABLE product_price ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731503055930-OrderIdIdentity.ts b/backend/src/database/migrations/archive/1731503055930-OrderIdIdentity.ts new file mode 100644 index 0000000..bfa8758 --- /dev/null +++ b/backend/src/database/migrations/archive/1731503055930-OrderIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OrderIdIdentity1731503055930 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE orders ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS order_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM orders; + EXECUTE 'ALTER TABLE orders ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731503302276-OrderItemIdIdentity.ts b/backend/src/database/migrations/archive/1731503302276-OrderItemIdIdentity.ts new file mode 100644 index 0000000..e511696 --- /dev/null +++ b/backend/src/database/migrations/archive/1731503302276-OrderItemIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OrderItemIdIdentity1731503302276 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE order_item ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS order_item_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM order_item; + EXECUTE 'ALTER TABLE order_item ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731503975362-OrderStatusIdIdentity.ts b/backend/src/database/migrations/archive/1731503975362-OrderStatusIdIdentity.ts new file mode 100644 index 0000000..46058de --- /dev/null +++ b/backend/src/database/migrations/archive/1731503975362-OrderStatusIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OrderStatusIdIdentity1731503975362 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE order_status ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS order_status_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM order_status; + EXECUTE 'ALTER TABLE order_status ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731512848862-AlterWarehouse.ts b/backend/src/database/migrations/archive/1731512848862-AlterWarehouse.ts new file mode 100644 index 0000000..3001ae6 --- /dev/null +++ b/backend/src/database/migrations/archive/1731512848862-AlterWarehouse.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterWarehouse1731512848862 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from warehouse where is_deleted = true; + alter table warehouse drop column is_deleted; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1731682593963-AddVersion.ts b/backend/src/database/migrations/archive/1731682593963-AddVersion.ts new file mode 100644 index 0000000..669fe9a --- /dev/null +++ b/backend/src/database/migrations/archive/1731682593963-AddVersion.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddVersion1731682593963 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table version ( + id integer generated by default as identity, + version varchar(255) not null, + date timestamp without time zone not null, + primary key ("id"), + unique ("version") + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732029352587-ObjectPermissionDepartmentFix.ts b/backend/src/database/migrations/archive/1732029352587-ObjectPermissionDepartmentFix.ts new file mode 100644 index 0000000..f7b2fc7 --- /dev/null +++ b/backend/src/database/migrations/archive/1732029352587-ObjectPermissionDepartmentFix.ts @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ObjectPermissionDepartmentFix1732029352587 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +UPDATE object_permission +SET create_permission = + CASE + WHEN create_permission = 'subdepartment' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE create_permission + END, + view_permission = + CASE + WHEN view_permission = 'subdepartment' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE view_permission + END, + edit_permission = + CASE + WHEN edit_permission = 'subdepartment' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE edit_permission + END, + delete_permission = + CASE + WHEN delete_permission = 'subdepartment' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE delete_permission + END, + report_permission = + CASE + WHEN report_permission = 'subdepartment' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE report_permission + END, + dashboard_permission = + CASE + WHEN dashboard_permission = 'subdepartment' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE dashboard_permission + END +WHERE + (create_permission = 'subdepartment' OR + view_permission = 'subdepartment' OR + edit_permission = 'subdepartment' OR + delete_permission = 'subdepartment' OR + report_permission = 'subdepartment' OR + dashboard_permission = 'subdepartment') + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL); + +UPDATE object_permission +SET create_permission = + CASE + WHEN create_permission = 'department' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE create_permission + END, + view_permission = + CASE + WHEN view_permission = 'department' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE view_permission + END, + edit_permission = + CASE + WHEN edit_permission = 'department' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE edit_permission + END, + delete_permission = + CASE + WHEN delete_permission = 'department' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE delete_permission + END, + report_permission = + CASE + WHEN report_permission = 'department' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE report_permission + END, + dashboard_permission = + CASE + WHEN dashboard_permission = 'department' + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL) THEN 'responsible' + ELSE dashboard_permission + END +WHERE + (create_permission = 'department' OR + view_permission = 'department' OR + edit_permission = 'department' OR + delete_permission = 'department' OR + report_permission = 'department' OR + dashboard_permission = 'department') + AND user_id IN (SELECT id FROM users WHERE department_id IS NULL); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732272936256-TaskSettingsIdIdentity.ts b/backend/src/database/migrations/archive/1732272936256-TaskSettingsIdIdentity.ts new file mode 100644 index 0000000..b482c2b --- /dev/null +++ b/backend/src/database/migrations/archive/1732272936256-TaskSettingsIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TaskSettingsIdIdentity1732272936256 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE task_settings ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS task_settings_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM task_settings; + EXECUTE 'ALTER TABLE task_settings ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732281701728-OptimizationIndexes.ts b/backend/src/database/migrations/archive/1732281701728-OptimizationIndexes.ts new file mode 100644 index 0000000..4bb3a4f --- /dev/null +++ b/backend/src/database/migrations/archive/1732281701728-OptimizationIndexes.ts @@ -0,0 +1,31 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class OptimizationIndexes1732281701728 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS users_accessible_users_user_id_idx ON users_accessible_users(user_id); + + CREATE INDEX IF NOT EXISTS board_record_id_idx ON board(record_id); + CREATE INDEX IF NOT EXISTS stage_board_id_account_id_idx ON board_stage(board_id, account_id); + + CREATE INDEX IF NOT EXISTS field_option_field_id_account_id_idx ON field_option (field_id, account_id); + DROP INDEX IF EXISTS field_option_field_id_idx; + + CREATE INDEX IF NOT EXISTS idx_field_value_partial_type_value ON field_value (entity_id, payload) WHERE field_type = 'value'; + + CREATE INDEX IF NOT EXISTS idx_task_resolved_end_date ON task (account_id, is_resolved, end_date); + CREATE INDEX IF NOT EXISTS idx_account_entity_resolved_true ON task (account_id, is_resolved, entity_id) WHERE is_resolved = true; + + CREATE INDEX IF NOT EXISTS idx_field_value_payload ON field_value USING gin (payload); + + CREATE INDEX IF NOT EXISTS idx_voximplant_call_account_created_at ON voximplant_call (account_id, created_at); + CREATE INDEX IF NOT EXISTS idx_voximplant_call_account_status_direction_user ON voximplant_call (account_id, status, direction, user_id); + CREATE INDEX IF NOT EXISTS idx_voximplant_call_account_status ON voximplant_call (account_id, status); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732521216471-AlterAccountSettings.ts b/backend/src/database/migrations/archive/1732521216471-AlterAccountSettings.ts new file mode 100644 index 0000000..4caeee2 --- /dev/null +++ b/backend/src/database/migrations/archive/1732521216471-AlterAccountSettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSettings1732521216471 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_settings add column is_bpmn_enable boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732627160688-IndexOptimization.ts b/backend/src/database/migrations/archive/1732627160688-IndexOptimization.ts new file mode 100644 index 0000000..599ca06 --- /dev/null +++ b/backend/src/database/migrations/archive/1732627160688-IndexOptimization.ts @@ -0,0 +1,31 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class IndexOptimization1732627160688 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +CREATE INDEX IF NOT EXISTS idx_voximplant_user_account_user ON voximplant_user (account_id, user_id); +CREATE INDEX IF NOT EXISTS idx_order_status_account_sort ON order_status (account_id, sort_order); +CREATE INDEX IF NOT EXISTS idx_demo_data_account ON demo_data (account_id); +CREATE INDEX IF NOT EXISTS idx_task_settings_account ON task_settings (account_id); +CREATE INDEX IF NOT EXISTS idx_activity_type_account_created ON activity_type (account_id, created_at); +CREATE INDEX IF NOT EXISTS idx_entity_type_account_sort ON entity_type (account_id, sort_order); +CREATE INDEX IF NOT EXISTS idx_mailbox_account_created ON mailbox (account_id, created_at); +CREATE INDEX IF NOT EXISTS idx_mailbox_accessible_user_account_mailbox ON mailbox_accessible_user (account_id, mailbox_id); + + +ANALYZE voximplant_user; +ANALYZE order_status; +ANALYZE demo_data; +ANALYZE task_settings; +ANALYZE activity_type; +ANALYZE mailbox; +ANALYZE entity_type; +ANALYZE mailbox_accessible_user; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732628496619-BoardIndexes.ts b/backend/src/database/migrations/archive/1732628496619-BoardIndexes.ts new file mode 100644 index 0000000..4f85be8 --- /dev/null +++ b/backend/src/database/migrations/archive/1732628496619-BoardIndexes.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class BoardIndexes1732628496619 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +DROP INDEX IF EXISTS stage_account_id_idx; +DROP INDEX IF EXISTS stage_board_id_idx; +DROP INDEX IF EXISTS stage_board_id_account_id_idx; +CREATE INDEX IF NOT EXISTS idx_board_stage_account_board_sort ON board_stage (account_id, board_id, sort_order); +ANALYZE board_stage; + +DROP INDEX IF EXISTS board_account_id_idx; +DROP INDEX IF EXISTS board_type_idx; +DROP INDEX IF EXISTS board_record_id_idx; +CREATE INDEX IF NOT EXISTS idx_board_account_type_sort ON board (account_id, type, sort_order); +CREATE INDEX IF NOT EXISTS idx_board_account_record_sort ON board (account_id, record_id, sort_order); +CREATE INDEX IF NOT EXISTS idx_board_account_id_sort ON board (account_id, id, sort_order); +ANALYZE board; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732697048964-IndexOptimization.ts b/backend/src/database/migrations/archive/1732697048964-IndexOptimization.ts new file mode 100644 index 0000000..6f59d3d --- /dev/null +++ b/backend/src/database/migrations/archive/1732697048964-IndexOptimization.ts @@ -0,0 +1,15 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class IndexOptimization1732697048964 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +CREATE INDEX IF NOT EXISTS idx_entity_account_entity_type_stage_weight_id ON entity (account_id, entity_type_id, stage_id, weight ASC, id DESC); +ANALYZE entity; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732716375115-EntityTypeLinkIdIdentity.ts b/backend/src/database/migrations/archive/1732716375115-EntityTypeLinkIdIdentity.ts new file mode 100644 index 0000000..3bd47b9 --- /dev/null +++ b/backend/src/database/migrations/archive/1732716375115-EntityTypeLinkIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityTypeLinkIdIdentity1732716375115 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE entity_type_link ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS entity_type_link_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM entity_type_link; + EXECUTE 'ALTER TABLE entity_type_link ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732783039132-EntityTypeLinkOrphans.ts b/backend/src/database/migrations/archive/1732783039132-EntityTypeLinkOrphans.ts new file mode 100644 index 0000000..b989fee --- /dev/null +++ b/backend/src/database/migrations/archive/1732783039132-EntityTypeLinkOrphans.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityTypeLinkOrphans1732783039132 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + INSERT INTO entity_type_link (source_id, target_id, account_id, sort_order) + SELECT etl1.target_id AS source_id, + etl1.source_id AS target_id, + etl1.account_id, + etl1.sort_order + FROM entity_type_link etl1 + LEFT JOIN entity_type_link etl2 + ON etl1.source_id = etl2.target_id + AND etl1.target_id = etl2.source_id + WHERE etl2.id IS NULL; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732866955213-AlterSiteForm.ts b/backend/src/database/migrations/archive/1732866955213-AlterSiteForm.ts new file mode 100644 index 0000000..3b2fe1d --- /dev/null +++ b/backend/src/database/migrations/archive/1732866955213-AlterSiteForm.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1732866955213 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE site_form ADD COLUMN is_headless boolean NOT NULL DEFAULT false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732876120184-UpdateEntityBoardAndStage.ts b/backend/src/database/migrations/archive/1732876120184-UpdateEntityBoardAndStage.ts new file mode 100644 index 0000000..5b43f58 --- /dev/null +++ b/backend/src/database/migrations/archive/1732876120184-UpdateEntityBoardAndStage.ts @@ -0,0 +1,54 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateEntityBoardAndStage1732876120184 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +WITH cte_board AS ( + SELECT e.id AS entity_id, b.id AS board_id + FROM entity e + JOIN entity_type et ON et.id = e.entity_type_id and et.section_view = 'board' + JOIN board b ON b.record_id = e.entity_type_id AND b.type = 'entity_type' + WHERE et.section_view = 'board' AND e.board_id IS NULL AND e.stage_id IS NULL + ORDER BY b.sort_order +), +cte_stage AS ( + SELECT cte_board.entity_id, bs.id AS stage_id + FROM cte_board + JOIN board_stage bs ON bs.board_id = cte_board.board_id + ORDER BY bs.sort_order +) +UPDATE entity +SET board_id = cte_board.board_id, stage_id = cte_stage.stage_id +FROM cte_board +JOIN cte_stage ON cte_board.entity_id = cte_stage.entity_id +WHERE entity.id = cte_board.entity_id; + +WITH cte_stage AS ( + SELECT e.id AS entity_id, bs.id AS stage_id + FROM entity e + JOIN board_stage bs ON bs.board_id = e.board_id + WHERE e.stage_id IS NULL + ORDER BY bs.sort_order +) +UPDATE entity +SET stage_id = cte_stage.stage_id +FROM cte_stage +WHERE entity.id = cte_stage.entity_id; + +WITH cte_board AS ( + SELECT e.id AS entity_id, bs.board_id AS board_id + FROM entity e + JOIN board_stage bs ON e.stage_id = bs.id + WHERE e.board_id IS NULL +) +UPDATE entity +SET board_id = cte_board.board_id +FROM cte_board +WHERE entity.id = cte_board.entity_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1732889802526-FrontendObject.ts b/backend/src/database/migrations/archive/1732889802526-FrontendObject.ts new file mode 100644 index 0000000..320d99b --- /dev/null +++ b/backend/src/database/migrations/archive/1732889802526-FrontendObject.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FrontendObject1732889802526 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table frontend_object ( + id integer generated by default as identity, + account_id integer not null, + key text not null, + value jsonb not null, + created_at timestamp default now(), + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + unique (key) + ); + create index frontend_object_account_key_idx on frontend_object(account_id, key); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733136867799-UpdateScheduleAppointmentOwner.ts b/backend/src/database/migrations/archive/1733136867799-UpdateScheduleAppointmentOwner.ts new file mode 100644 index 0000000..6d4e284 --- /dev/null +++ b/backend/src/database/migrations/archive/1733136867799-UpdateScheduleAppointmentOwner.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateScheduleAppointmentOwner1733136867799 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update schedule_appointment + set owner_id = (select responsible_user_id from entity where entity.id = schedule_appointment.entity_id) + where schedule_appointment.entity_id is not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733393739852-ConditionIdIdentity.ts b/backend/src/database/migrations/archive/1733393739852-ConditionIdIdentity.ts new file mode 100644 index 0000000..092dbf0 --- /dev/null +++ b/backend/src/database/migrations/archive/1733393739852-ConditionIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ConditionIdIdentity1733393739852 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE condition ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS condition_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM condition; + EXECUTE 'ALTER TABLE condition ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733394028781-ScheduledMailMessageIdIdentity.ts b/backend/src/database/migrations/archive/1733394028781-ScheduledMailMessageIdIdentity.ts new file mode 100644 index 0000000..92a1ff0 --- /dev/null +++ b/backend/src/database/migrations/archive/1733394028781-ScheduledMailMessageIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduledMailMessageIdIdentity1733394028781 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE scheduled_mail_message ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS scheduled_mail_message_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM scheduled_mail_message; + EXECUTE 'ALTER TABLE scheduled_mail_message ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733394215019-TriggerIdIdentity.ts b/backend/src/database/migrations/archive/1733394215019-TriggerIdIdentity.ts new file mode 100644 index 0000000..70dbf0c --- /dev/null +++ b/backend/src/database/migrations/archive/1733394215019-TriggerIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TriggerIdIdentity1733394215019 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE trigger ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS action_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM trigger; + EXECUTE 'ALTER TABLE trigger ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733394597308-ActionScheduledIdIdentity.ts b/backend/src/database/migrations/archive/1733394597308-ActionScheduledIdIdentity.ts new file mode 100644 index 0000000..7ac808d --- /dev/null +++ b/backend/src/database/migrations/archive/1733394597308-ActionScheduledIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ActionScheduledIdIdentity1733394597308 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE action_scheduled ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS scheduled_action_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM action_scheduled; + EXECUTE 'ALTER TABLE action_scheduled ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733394799717-ActionIdIdentity.ts b/backend/src/database/migrations/archive/1733394799717-ActionIdIdentity.ts new file mode 100644 index 0000000..3a4287b --- /dev/null +++ b/backend/src/database/migrations/archive/1733394799717-ActionIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ActionIdIdentity1733394799717 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE action ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS action_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM action; + EXECUTE 'ALTER TABLE action ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733394948832-AutomationIdIdentity.ts b/backend/src/database/migrations/archive/1733394948832-AutomationIdIdentity.ts new file mode 100644 index 0000000..cc90a54 --- /dev/null +++ b/backend/src/database/migrations/archive/1733394948832-AutomationIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AutomationIdIdentity1733394948832 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE automation ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS automation_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM automation; + EXECUTE 'ALTER TABLE automation ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733395768414-NoteIdIdentity.ts b/backend/src/database/migrations/archive/1733395768414-NoteIdIdentity.ts new file mode 100644 index 0000000..0197000 --- /dev/null +++ b/backend/src/database/migrations/archive/1733395768414-NoteIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class NoteIdIdentity1733395768414 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE note ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS feed_item_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM note; + EXECUTE 'ALTER TABLE note ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733395930995-ActivityIdIdentity.ts b/backend/src/database/migrations/archive/1733395930995-ActivityIdIdentity.ts new file mode 100644 index 0000000..68acea1 --- /dev/null +++ b/backend/src/database/migrations/archive/1733395930995-ActivityIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ActivityIdIdentity1733395930995 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE activity ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS feed_item_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM activity; + EXECUTE 'ALTER TABLE activity ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733395960444-TaskIdIdentity.ts b/backend/src/database/migrations/archive/1733395960444-TaskIdIdentity.ts new file mode 100644 index 0000000..eb852fe --- /dev/null +++ b/backend/src/database/migrations/archive/1733395960444-TaskIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TaskIdIdentity1733395960444 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE task ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS feed_item_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM task; + EXECUTE 'ALTER TABLE task ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733396343770-TaskSubtaskIdIdentity.ts b/backend/src/database/migrations/archive/1733396343770-TaskSubtaskIdIdentity.ts new file mode 100644 index 0000000..c0310ba --- /dev/null +++ b/backend/src/database/migrations/archive/1733396343770-TaskSubtaskIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class TaskSubtaskIdIdentity1733396343770 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE task_subtask ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS subtask_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM task_subtask; + EXECUTE 'ALTER TABLE task_subtask ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733397036241-MailMessageIdIdentity.ts b/backend/src/database/migrations/archive/1733397036241-MailMessageIdIdentity.ts new file mode 100644 index 0000000..618cf6f --- /dev/null +++ b/backend/src/database/migrations/archive/1733397036241-MailMessageIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailMessageIdIdentity1733397036241 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE mail_message ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS mail_message_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM mail_message; + EXECUTE 'ALTER TABLE mail_message ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733397284175-MailMessagePayloadIdIdentity.ts b/backend/src/database/migrations/archive/1733397284175-MailMessagePayloadIdIdentity.ts new file mode 100644 index 0000000..e9652ca --- /dev/null +++ b/backend/src/database/migrations/archive/1733397284175-MailMessagePayloadIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailMessagePayloadIdIdentity1733397284175 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE mail_message_payload ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS mail_message_payload_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM mail_message_payload; + EXECUTE 'ALTER TABLE mail_message_payload ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733397518270-MailboxFolderIdIdentity.ts b/backend/src/database/migrations/archive/1733397518270-MailboxFolderIdIdentity.ts new file mode 100644 index 0000000..572b9cd --- /dev/null +++ b/backend/src/database/migrations/archive/1733397518270-MailboxFolderIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailboxFolderIdIdentity1733397518270 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE mailbox_folder ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS mailbox_folder_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM mailbox_folder; + EXECUTE 'ALTER TABLE mailbox_folder ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733397748405-MailboxIdIdentity.ts b/backend/src/database/migrations/archive/1733397748405-MailboxIdIdentity.ts new file mode 100644 index 0000000..b343cd0 --- /dev/null +++ b/backend/src/database/migrations/archive/1733397748405-MailboxIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailboxIdIdentity1733397748405 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE mailbox ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS mailbox_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM mailbox; + EXECUTE 'ALTER TABLE mailbox ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1733397911411-MailboxSignatureIdIdentity.ts b/backend/src/database/migrations/archive/1733397911411-MailboxSignatureIdIdentity.ts new file mode 100644 index 0000000..033739d --- /dev/null +++ b/backend/src/database/migrations/archive/1733397911411-MailboxSignatureIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MailboxSignatureIdIdentity1733397911411 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE mailbox_signature ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS mailbox_signature_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM mailbox_signature; + EXECUTE 'ALTER TABLE mailbox_signature ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734009141643-AlterEntityValue.ts b/backend/src/database/migrations/archive/1734009141643-AlterEntityValue.ts new file mode 100644 index 0000000..26b0b76 --- /dev/null +++ b/backend/src/database/migrations/archive/1734009141643-AlterEntityValue.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterEntityValue1734009141643 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity add column if not exists value numeric not null default 0; + update entity + set value = ( + select coalesce((payload->>'value')::numeric, 0) from ( + select distinct on (entity_id) payload + from field_value + where field_type = 'value' and entity_id = entity.id + order by entity_id, created_at desc + ) subquery + ) + where exists (select 1 from field_value where field_type = 'value' and entity_id = entity.id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734081841312-AlterUserProfile.ts b/backend/src/database/migrations/archive/1734081841312-AlterUserProfile.ts new file mode 100644 index 0000000..b1f4d52 --- /dev/null +++ b/backend/src/database/migrations/archive/1734081841312-AlterUserProfile.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterUserProfile1734081841312 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table user_profile drop column created_at; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734097700161-AlterEntityLink.ts b/backend/src/database/migrations/archive/1734097700161-AlterEntityLink.ts new file mode 100644 index 0000000..284cba5 --- /dev/null +++ b/backend/src/database/migrations/archive/1734097700161-AlterEntityLink.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterEntityLink1734097700161 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity_link drop column back_link_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734349336443-EntityLinkIdIdentity.ts b/backend/src/database/migrations/archive/1734349336443-EntityLinkIdIdentity.ts new file mode 100644 index 0000000..5b1720f --- /dev/null +++ b/backend/src/database/migrations/archive/1734349336443-EntityLinkIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityLinkIdIdentity1734349336443 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE entity_link ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS entity_link_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM entity_link; + EXECUTE 'ALTER TABLE entity_link ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734352488667-RemoveEntityListSettings.ts b/backend/src/database/migrations/archive/1734352488667-RemoveEntityListSettings.ts new file mode 100644 index 0000000..78eeb7e --- /dev/null +++ b/backend/src/database/migrations/archive/1734352488667-RemoveEntityListSettings.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveEntityListSettings1734352488667 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop sequence if exists entity_list_settings_id_seq; + drop table if exists entity_list_settings; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734429865379-ScheduleAppointmentIndexes.ts b/backend/src/database/migrations/archive/1734429865379-ScheduleAppointmentIndexes.ts new file mode 100644 index 0000000..6ba45d6 --- /dev/null +++ b/backend/src/database/migrations/archive/1734429865379-ScheduleAppointmentIndexes.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ScheduleAppointmentIndexes1734429865379 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +CREATE INDEX IF NOT EXISTS idx_appointment_account_schedule_start_end +ON schedule_appointment (account_id, schedule_id, start_date, end_date); + +CREATE INDEX IF NOT EXISTS idx_appointment_account_schedule_non_canceled_start_end +ON schedule_appointment (account_id, schedule_id, start_date, end_date) +WHERE status <> 'canceled'; + +REINDEX TABLE schedule_appointment; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734600811704-AlterChatMessageScheduled.ts b/backend/src/database/migrations/archive/1734600811704-AlterChatMessageScheduled.ts new file mode 100644 index 0000000..1e75fe6 --- /dev/null +++ b/backend/src/database/migrations/archive/1734600811704-AlterChatMessageScheduled.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatMessageScheduled1734600811704 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_message_scheduled + alter column entity_id drop not null, + add column phone_number text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734688459744-AlterField.ts b/backend/src/database/migrations/archive/1734688459744-AlterField.ts new file mode 100644 index 0000000..3eb0e35 --- /dev/null +++ b/backend/src/database/migrations/archive/1734688459744-AlterField.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterField1734688459744 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table field add column format text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734708417316-VersionIndexes.ts b/backend/src/database/migrations/archive/1734708417316-VersionIndexes.ts new file mode 100644 index 0000000..7abd044 --- /dev/null +++ b/backend/src/database/migrations/archive/1734708417316-VersionIndexes.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VersionIndexes1734708417316 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS idx_version_date ON version (date); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1734945712418-AlterSiteFormField.ts b/backend/src/database/migrations/archive/1734945712418-AlterSiteFormField.ts new file mode 100644 index 0000000..2e83009 --- /dev/null +++ b/backend/src/database/migrations/archive/1734945712418-AlterSiteFormField.ts @@ -0,0 +1,32 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteFormField1734945712418 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +alter table site_form_field + add column entity_type_id integer, + add column field_id integer, + add column is_validation_required boolean, + add column meta jsonb, + add foreign key (entity_type_id) references entity_type(id) on delete cascade, + add foreign key (field_id) references field(id) on delete cascade; + +update site_form_field +set entity_type_id = (select sffen.entity_type_id from site_form_field_entity_name sffen where sffen.form_field_id = site_form_field.id) +where site_form_field.type = 'entity_name'; + +update site_form_field +set (entity_type_id, field_id, is_validation_required, meta) = + (select sffef.entity_type_id, sffef.field_id, sffef.is_validation_required, sffef.meta from site_form_field_entity_field sffef where sffef.form_field_id = site_form_field.id) +where site_form_field.type = 'entity_field'; + +drop table site_form_field_entity_name; +drop table site_form_field_entity_field; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1735140161718-AddGoogleCalendar.ts b/backend/src/database/migrations/archive/1735140161718-AddGoogleCalendar.ts new file mode 100644 index 0000000..950d61c --- /dev/null +++ b/backend/src/database/migrations/archive/1735140161718-AddGoogleCalendar.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddGoogleCalendar1735140161718 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE google_calendar ( + id integer GENERATED BY DEFAULT AS IDENTITY, + account_id integer NOT NULL, + created_by integer NOT NULL, + created_at timestamp without time zone NOT NULL DEFAULT now(), + tokens jsonb NOT NULL, + calendar_id text NOT NULL, + title text NOT NULL, + readonly boolean NOT NULL, + type text NOT NULL, + object_id text NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE, + FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1735289134467-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1735289134467-AlterGoogleCalendar.ts new file mode 100644 index 0000000..b487431 --- /dev/null +++ b/backend/src/database/migrations/archive/1735289134467-AlterGoogleCalendar.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1735289134467 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar rename column calendar_id to external_id; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1735290249052-AlterTask.ts b/backend/src/database/migrations/archive/1735290249052-AlterTask.ts new file mode 100644 index 0000000..2fff7e5 --- /dev/null +++ b/backend/src/database/migrations/archive/1735290249052-AlterTask.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterTask1735290249052 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + delete from task where board_id is null; + delete from task where stage_id is null; + + alter table task + alter column stage_id set not null, + alter column board_id set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1735573056216-IndexGoogleCalendar.ts b/backend/src/database/migrations/archive/1735573056216-IndexGoogleCalendar.ts new file mode 100644 index 0000000..cf2d0aa --- /dev/null +++ b/backend/src/database/migrations/archive/1735573056216-IndexGoogleCalendar.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class IndexGoogleCalendar1735573056216 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index if not exists idx_account_type_object on google_calendar(account_id,type,object_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736239080782-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1736239080782-AlterGoogleCalendar.ts new file mode 100644 index 0000000..42ce4ed --- /dev/null +++ b/backend/src/database/migrations/archive/1736239080782-AlterGoogleCalendar.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1736239080782 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar add column if not exists responsible_id integer not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736262949465-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1736262949465-AlterGoogleCalendar.ts new file mode 100644 index 0000000..5b9b6c0 --- /dev/null +++ b/backend/src/database/migrations/archive/1736262949465-AlterGoogleCalendar.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1736262949465 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar add column if not exists channel_id text; + alter table google_calendar add column if not exists channel_resource_id text; + alter table google_calendar add column if not exists channel_expiration timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736324153278-GoogleCalendarIndexes.ts b/backend/src/database/migrations/archive/1736324153278-GoogleCalendarIndexes.ts new file mode 100644 index 0000000..49136f1 --- /dev/null +++ b/backend/src/database/migrations/archive/1736324153278-GoogleCalendarIndexes.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class GoogleCalendarIndexes1736324153278 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index if not exists idx_channel_expiration on google_calendar(channel_expiration); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736330451213-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1736330451213-AlterGoogleCalendar.ts new file mode 100644 index 0000000..f775ded --- /dev/null +++ b/backend/src/database/migrations/archive/1736330451213-AlterGoogleCalendar.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1736330451213 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar add column if not exists next_sync_token text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736331095918-GoogleCalendarIndexes.ts b/backend/src/database/migrations/archive/1736331095918-GoogleCalendarIndexes.ts new file mode 100644 index 0000000..e352b17 --- /dev/null +++ b/backend/src/database/migrations/archive/1736331095918-GoogleCalendarIndexes.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class GoogleCalendarIndexes1736331095918 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index if not exists idx_channel_resource on google_calendar(channel_id, channel_resource_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736339315376-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1736339315376-AlterGoogleCalendar.ts new file mode 100644 index 0000000..0e4bee0 --- /dev/null +++ b/backend/src/database/migrations/archive/1736339315376-AlterGoogleCalendar.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1736339315376 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar rename column next_sync_token to sync_token; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736496705277-AlterTask.ts b/backend/src/database/migrations/archive/1736496705277-AlterTask.ts new file mode 100644 index 0000000..54f3d19 --- /dev/null +++ b/backend/src/database/migrations/archive/1736496705277-AlterTask.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterTask1736496705277 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table task add column external_id text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736513599793-IndexesTask.ts b/backend/src/database/migrations/archive/1736513599793-IndexesTask.ts new file mode 100644 index 0000000..724a78e --- /dev/null +++ b/backend/src/database/migrations/archive/1736513599793-IndexesTask.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class IndexesTask1736513599793 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index if not exists idx_account_external on task(account_id, external_id) where external_id is not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736773476711-GoogleCalendarLinked.ts b/backend/src/database/migrations/archive/1736773476711-GoogleCalendarLinked.ts new file mode 100644 index 0000000..34fa728 --- /dev/null +++ b/backend/src/database/migrations/archive/1736773476711-GoogleCalendarLinked.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class GoogleCalendarLinked1736773476711 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table google_calendar_linked ( + id integer generated by default as identity, + account_id integer not null, + calendar_id integer not null, + type text not null, + object_id integer not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (calendar_id) references google_calendar(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736781493022-IndexesGoogleCalendarLinked.ts b/backend/src/database/migrations/archive/1736781493022-IndexesGoogleCalendarLinked.ts new file mode 100644 index 0000000..de81042 --- /dev/null +++ b/backend/src/database/migrations/archive/1736781493022-IndexesGoogleCalendarLinked.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class IndexesGoogleCalendarLinked1736781493022 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create index if not exists idx_calendar on google_calendar_linked(calendar_id); + create index if not exists idx_type_object on google_calendar_linked(type, object_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736842860059-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1736842860059-AlterGoogleCalendar.ts new file mode 100644 index 0000000..237631b --- /dev/null +++ b/backend/src/database/migrations/archive/1736842860059-AlterGoogleCalendar.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1736842860059 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar add column process_all boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1736948650910-AlterScheduleAppointment.ts b/backend/src/database/migrations/archive/1736948650910-AlterScheduleAppointment.ts new file mode 100644 index 0000000..bda091a --- /dev/null +++ b/backend/src/database/migrations/archive/1736948650910-AlterScheduleAppointment.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterScheduleAppointment1736948650910 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule_appointment add column external_id text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737033530086-AlterAccountSubscription.ts b/backend/src/database/migrations/archive/1737033530086-AlterAccountSubscription.ts new file mode 100644 index 0000000..7cf938d --- /dev/null +++ b/backend/src/database/migrations/archive/1737033530086-AlterAccountSubscription.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSubscription1737033530086 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_subscription add column first_visit timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737039160966-AddSubscriptionDiscount.ts b/backend/src/database/migrations/archive/1737039160966-AddSubscriptionDiscount.ts new file mode 100644 index 0000000..5b581a1 --- /dev/null +++ b/backend/src/database/migrations/archive/1737039160966-AddSubscriptionDiscount.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSubscriptionDiscount1737039160966 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table subscription_discount ( + id integer generated by default as identity, + days integer not null, + percent integer not null, + code text, + primary key (id) + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737103293642-UpdateAccountSubscription.ts b/backend/src/database/migrations/archive/1737103293642-UpdateAccountSubscription.ts new file mode 100644 index 0000000..ab8b6ab --- /dev/null +++ b/backend/src/database/migrations/archive/1737103293642-UpdateAccountSubscription.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateAccountSubscription1737103293642 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update account_subscription set first_visit = created_at; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737103460794-AlterAccountSubscription.ts b/backend/src/database/migrations/archive/1737103460794-AlterAccountSubscription.ts new file mode 100644 index 0000000..6f69888 --- /dev/null +++ b/backend/src/database/migrations/archive/1737103460794-AlterAccountSubscription.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSubscription1737103460794 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_subscription alter column first_visit set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737103855502-InsertSubscriptionDiscount.ts b/backend/src/database/migrations/archive/1737103855502-InsertSubscriptionDiscount.ts new file mode 100644 index 0000000..3e2f21b --- /dev/null +++ b/backend/src/database/migrations/archive/1737103855502-InsertSubscriptionDiscount.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class InsertSubscriptionDiscount1737103855502 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + insert into subscription_discount(days, percent, code) values(5, 50, 'discount_50'); + insert into subscription_discount(days, percent, code) values(19, 30, 'discount_30'); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737378761002-AddGoogleCalendarAccount.ts b/backend/src/database/migrations/archive/1737378761002-AddGoogleCalendarAccount.ts new file mode 100644 index 0000000..6fa2e8b --- /dev/null +++ b/backend/src/database/migrations/archive/1737378761002-AddGoogleCalendarAccount.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddGoogleCalendarAccount1737378761002 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table google_calendar_account ( + id integer generated by default as identity, + account_id integer not null, + external_id text not null, + tokens jsonb not null, + sync_token text, + channel_id text, + channel_resource_id text, + channel_expiration timestamp without time zone, + primary key (id), + foreign key (account_id) references account(id) on delete cascade + ); + + alter table google_calendar + drop column tokens, + add column calendar_account_id integer not null, + add foreign key (calendar_account_id) references google_calendar_account(id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737971712981-AlterSubscriptionDiscount.ts b/backend/src/database/migrations/archive/1737971712981-AlterSubscriptionDiscount.ts new file mode 100644 index 0000000..5dfbd47 --- /dev/null +++ b/backend/src/database/migrations/archive/1737971712981-AlterSubscriptionDiscount.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSubscriptionDiscount1737971712981 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table subscription_discount add column valid_until timestamp without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1737973113027-UpdateSubscriptionDiscount.ts b/backend/src/database/migrations/archive/1737973113027-UpdateSubscriptionDiscount.ts new file mode 100644 index 0000000..5dbc7d6 --- /dev/null +++ b/backend/src/database/migrations/archive/1737973113027-UpdateSubscriptionDiscount.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateSubscriptionDiscount1737973113027 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update subscription_discount set valid_until = now(); + + insert into subscription_discount (days, percent, code, valid_until) values + (5, 80, 'discount_80', null), + (103, 70, 'discount_70', null), + (403, 65, 'discount_65', null); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1738660778536-RemoveOldAutomation.ts b/backend/src/database/migrations/archive/1738660778536-RemoveOldAutomation.ts new file mode 100644 index 0000000..fdf510d --- /dev/null +++ b/backend/src/database/migrations/archive/1738660778536-RemoveOldAutomation.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveOldAutomation1738660778536 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + drop table if exists automation_stage; + drop table if exists automation_condition; + drop table if exists automation; + drop table if exists action_activity_settings; + drop table if exists action_task_settings; + drop table if exists action_entity_settings; + drop table if exists action_email_settings; + drop table if exists action_scheduled; + drop table if exists scheduled_mail_message; + drop table if exists action; + drop table if exists exact_time_trigger_settings; + drop table if exists trigger; + drop table if exists user_condition; + drop table if exists field_condition; + drop table if exists condition; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1738676409194-AlterUserProfile.ts b/backend/src/database/migrations/archive/1738676409194-AlterUserProfile.ts new file mode 100644 index 0000000..54a4f6b --- /dev/null +++ b/backend/src/database/migrations/archive/1738676409194-AlterUserProfile.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterUserProfile1738676409194 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table user_profile add column working_time_from time without time zone; + alter table user_profile add column working_time_to time without time zone; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1739788991506-AlterGoogleCalendar.ts b/backend/src/database/migrations/archive/1739788991506-AlterGoogleCalendar.ts new file mode 100644 index 0000000..27a978a --- /dev/null +++ b/backend/src/database/migrations/archive/1739788991506-AlterGoogleCalendar.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterGoogleCalendar1739788991506 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table google_calendar + alter column object_id set data type integer using object_id::integer; + + update google_calendar set object_id = object_id::integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1739802958618-AlterAccountSettings.ts b/backend/src/database/migrations/archive/1739802958618-AlterAccountSettings.ts new file mode 100644 index 0000000..f970336 --- /dev/null +++ b/backend/src/database/migrations/archive/1739802958618-AlterAccountSettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterAccountSettings1739802958618 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table account_settings add column start_of_week text default 'Monday'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1739891080452-AddUserToken.ts b/backend/src/database/migrations/archive/1739891080452-AddUserToken.ts new file mode 100644 index 0000000..d7847dd --- /dev/null +++ b/backend/src/database/migrations/archive/1739891080452-AddUserToken.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserToken1739891080452 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table user_token ( + id integer generated by default as identity, + account_id integer not null, + user_id integer not null, + name text not null, + code text not null, + created_at timestamp without time zone not null default now(), + expires_at timestamp without time zone, + last_used_at timestamp without time zone, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade + ); + + create index if not exists idx_account_user_code on user_token(account_id, user_id, code); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1740402538635-AddUserCalendar.ts b/backend/src/database/migrations/archive/1740402538635-AddUserCalendar.ts new file mode 100644 index 0000000..e9513ab --- /dev/null +++ b/backend/src/database/migrations/archive/1740402538635-AddUserCalendar.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserCalendar1740402538635 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table if not exists user_calendar ( + id integer generated by default as identity, + account_id integer not null, + user_id integer not null, + time_buffer_before integer, + time_buffer_after integer, + appointment_limit integer, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (user_id) references users(id) on delete cascade + ); + + create index if not exists idx_account_user on user_calendar(account_id, user_id); + + create table if not exists user_calendar_interval ( + id integer generated by default as identity, + account_id integer not null, + calendar_id integer not null, + day_of_week text not null, + time_from time without time zone not null, + time_to time without time zone not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (calendar_id) references user_calendar(id) on delete cascade + ); + + create index if not exists idx_account_calendar on user_calendar_interval(account_id, calendar_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1740472617092-AlterSchedule.ts b/backend/src/database/migrations/archive/1740472617092-AlterSchedule.ts new file mode 100644 index 0000000..a30318e --- /dev/null +++ b/backend/src/database/migrations/archive/1740472617092-AlterSchedule.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1740472617092 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule add column time_buffer_before integer; + alter table schedule add column time_buffer_after integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1740479281651-AddScheduleTimeIntervalService.ts b/backend/src/database/migrations/archive/1740479281651-AddScheduleTimeIntervalService.ts new file mode 100644 index 0000000..76a3821 --- /dev/null +++ b/backend/src/database/migrations/archive/1740479281651-AddScheduleTimeIntervalService.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddScheduleTimeIntervalService1740479281651 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table if not exists schedule_time_interval ( + id integer generated by default as identity, + account_id integer not null, + schedule_id integer not null, + day_of_week text not null, + time_from time without time zone not null, + time_to time without time zone not null, + primary key (id), + foreign key (account_id) references account(id) on delete cascade, + foreign key (schedule_id) references schedule(id) on delete cascade + ); + + create index if not exists idx_account_schedule on schedule_time_interval(account_id, schedule_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1740579698409-AlterSiteForm.ts b/backend/src/database/migrations/archive/1740579698409-AlterSiteForm.ts new file mode 100644 index 0000000..0995a78 --- /dev/null +++ b/backend/src/database/migrations/archive/1740579698409-AlterSiteForm.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1740579698409 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form add column type text not null default 'entity_type'; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1740583763973-AlterSiteForm.ts b/backend/src/database/migrations/archive/1740583763973-AlterSiteForm.ts new file mode 100644 index 0000000..de80178 --- /dev/null +++ b/backend/src/database/migrations/archive/1740583763973-AlterSiteForm.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1740583763973 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form add column deduplicate_linked boolean not null default false; + alter table site_form add column schedule_limit_days integer; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1740654525458-AddSiteFormSchedule.ts b/backend/src/database/migrations/archive/1740654525458-AddSiteFormSchedule.ts new file mode 100644 index 0000000..6d5aae8 --- /dev/null +++ b/backend/src/database/migrations/archive/1740654525458-AddSiteFormSchedule.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddSiteFormSchedule1740654525458 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table if not exists site_form_schedule ( + form_id integer not null, + schedule_id integer not null, + account_id integer not null, + primary key (form_id, schedule_id), + foreign key (form_id) references site_form(id) on delete cascade, + foreign key (schedule_id) references schedule(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade + ); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1741610604818-AlterSiteForm.ts b/backend/src/database/migrations/archive/1741610604818-AlterSiteForm.ts new file mode 100644 index 0000000..14e6f27 --- /dev/null +++ b/backend/src/database/migrations/archive/1741610604818-AlterSiteForm.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSiteForm1741610604818 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table site_form rename column deduplicate_linked to check_duplicate; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1741701747438-UpdateSchedule.ts b/backend/src/database/migrations/archive/1741701747438-UpdateSchedule.ts new file mode 100644 index 0000000..f22de5e --- /dev/null +++ b/backend/src/database/migrations/archive/1741701747438-UpdateSchedule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateSchedule1741701747438 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + update schedule set time_period = 1800 where time_period is null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1741702060670-AlterSchedule.ts b/backend/src/database/migrations/archive/1741702060670-AlterSchedule.ts new file mode 100644 index 0000000..ec9f8bf --- /dev/null +++ b/backend/src/database/migrations/archive/1741702060670-AlterSchedule.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterSchedule1741702060670 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table schedule alter column time_period set not null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1742477677993-AddChatProviderEntitySettings.ts b/backend/src/database/migrations/archive/1742477677993-AddChatProviderEntitySettings.ts new file mode 100644 index 0000000..85c8110 --- /dev/null +++ b/backend/src/database/migrations/archive/1742477677993-AddChatProviderEntitySettings.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddChatProviderEntitySettings1742477677993 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table chat_provider_entity_settings ( + provider_id integer, + account_id integer not null, + contact_entity_type_id integer, + lead_entity_type_id integer, + lead_board_id integer, + owner_id integer, + check_active_lead boolean not null default false, + check_duplicate boolean not null default false, + primary key (provider_id), + foreign key (provider_id) references chat_provider(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + foreign key (contact_entity_type_id) references entity_type(id) on delete set null, + foreign key (lead_entity_type_id) references entity_type(id) on delete set null, + foreign key (lead_board_id) references board(id) on delete set null, + foreign key (owner_id) references users(id) on delete set null + ) + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1742912395334-AlterChatProviderEntitySettings.ts b/backend/src/database/migrations/archive/1742912395334-AlterChatProviderEntitySettings.ts new file mode 100644 index 0000000..3150b21 --- /dev/null +++ b/backend/src/database/migrations/archive/1742912395334-AlterChatProviderEntitySettings.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatProviderEntitySettings1742912395334 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider_entity_settings + add column lead_stage_id integer, + add foreign key (lead_stage_id) references board_stage(id) on delete set null; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743065071601-AlterChatProviderEntitySettings.ts b/backend/src/database/migrations/archive/1743065071601-AlterChatProviderEntitySettings.ts new file mode 100644 index 0000000..d7d4fa6 --- /dev/null +++ b/backend/src/database/migrations/archive/1743065071601-AlterChatProviderEntitySettings.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterChatProviderEntitySettings1743065071601 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table chat_provider_entity_settings add column lead_name text; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743084130609-AlterProductStock.ts b/backend/src/database/migrations/archive/1743084130609-AlterProductStock.ts new file mode 100644 index 0000000..c76b516 --- /dev/null +++ b/backend/src/database/migrations/archive/1743084130609-AlterProductStock.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterProductStock1743084130609 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table product_stock alter column stock_quantity type numeric(13,4); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743592762254-AddMailboxEntitySettings.ts b/backend/src/database/migrations/archive/1743592762254-AddMailboxEntitySettings.ts new file mode 100644 index 0000000..3c94f00 --- /dev/null +++ b/backend/src/database/migrations/archive/1743592762254-AddMailboxEntitySettings.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMailboxEntitySettings1743592762254 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + create table mailbox_entity_settings ( + mailbox_id integer, + account_id integer not null, + contact_entity_type_id integer, + lead_entity_type_id integer, + lead_board_id integer, + lead_stage_id integer, + lead_name text, + owner_id integer, + check_active_lead boolean not null default false, + check_duplicate boolean not null default false, + primary key (mailbox_id), + foreign key (mailbox_id) references mailbox(id) on delete cascade, + foreign key (account_id) references account(id) on delete cascade, + foreign key (contact_entity_type_id) references entity_type(id) on delete set null, + foreign key (lead_entity_type_id) references entity_type(id) on delete set null, + foreign key (lead_board_id) references board(id) on delete set null, + foreign key (lead_stage_id) references board_stage(id) on delete set null, + foreign key (owner_id) references users(id) on delete set null + ) + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743595365132-InsertMailboxEntitySettings.ts b/backend/src/database/migrations/archive/1743595365132-InsertMailboxEntitySettings.ts new file mode 100644 index 0000000..f76ecd7 --- /dev/null +++ b/backend/src/database/migrations/archive/1743595365132-InsertMailboxEntitySettings.ts @@ -0,0 +1,16 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class InsertMailboxEntitySettings1743595365132 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` +insert into mailbox_entity_settings (account_id, mailbox_id, contact_entity_type_id, lead_entity_type_id, lead_board_id, owner_id, check_active_lead, check_duplicate) +select m.account_id, m.id as mailbox_id, m.contact_entity_type_id, m.lead_entity_type_id, m.lead_board_id, m.owner_id, false as check_active_lead, false as check_duplicate +from mailbox m where (m.create_contact = true and m.contact_entity_type_id is not null) or (m.create_lead = true and m.lead_entity_type_id is not null); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743595431125-AlterMailbox.ts b/backend/src/database/migrations/archive/1743595431125-AlterMailbox.ts new file mode 100644 index 0000000..764a74a --- /dev/null +++ b/backend/src/database/migrations/archive/1743595431125-AlterMailbox.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailbox1743595431125 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox + drop column create_contact, + drop column contact_entity_type_id, + drop column lead_entity_type_id, + drop column lead_board_id, + drop column create_lead; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743690907799-AlterEntityLink.ts b/backend/src/database/migrations/archive/1743690907799-AlterEntityLink.ts new file mode 100644 index 0000000..92609e3 --- /dev/null +++ b/backend/src/database/migrations/archive/1743690907799-AlterEntityLink.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterEntityLink1743690907799 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + with duplicates as ( + select id + from (select id, row_number() over (partition by source_id, target_id order by id) as rn from entity_link) t + where t.rn > 1 + ) + delete from entity_link where id in (select id from duplicates); + + alter table entity_link + drop column id, + add primary key (source_id, target_id); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1743771835815-EntityIdIdentity.ts b/backend/src/database/migrations/archive/1743771835815-EntityIdIdentity.ts new file mode 100644 index 0000000..e7cfdfe --- /dev/null +++ b/backend/src/database/migrations/archive/1743771835815-EntityIdIdentity.ts @@ -0,0 +1,22 @@ +/* eslint-disable max-len */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EntityIdIdentity1743771835815 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE entity ALTER COLUMN id DROP DEFAULT; + DROP SEQUENCE IF EXISTS entity_id_seq; + DO $$ + DECLARE + max_id integer; + BEGIN + SELECT COALESCE(MAX(id), 0) + 1 INTO max_id FROM entity; + EXECUTE 'ALTER TABLE entity ALTER COLUMN id SET NOT NULL, ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH ' || max_id || ')'; + END $$; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1744127440588-AlterEntity.ts b/backend/src/database/migrations/archive/1744127440588-AlterEntity.ts new file mode 100644 index 0000000..cc03201 --- /dev/null +++ b/backend/src/database/migrations/archive/1744127440588-AlterEntity.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterEntity1744127440588 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table entity add column focused boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1744296868607-AlterMailboxFolder.ts b/backend/src/database/migrations/archive/1744296868607-AlterMailboxFolder.ts new file mode 100644 index 0000000..e8a2357 --- /dev/null +++ b/backend/src/database/migrations/archive/1744296868607-AlterMailboxFolder.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailboxFolder1744296868607 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_folder rename column messages_total to total; + alter table mailbox_folder rename column messages_unread to unread; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1744374434852-AlterMailboxSignatureLink.ts b/backend/src/database/migrations/archive/1744374434852-AlterMailboxSignatureLink.ts new file mode 100644 index 0000000..5b2d13b --- /dev/null +++ b/backend/src/database/migrations/archive/1744374434852-AlterMailboxSignatureLink.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailboxSignatureLink1744374434852 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_signature_link rename to mailbox_signature_mailbox; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/migrations/archive/1744382844583-AlterMailboxSignature.ts b/backend/src/database/migrations/archive/1744382844583-AlterMailboxSignature.ts new file mode 100644 index 0000000..7603abb --- /dev/null +++ b/backend/src/database/migrations/archive/1744382844583-AlterMailboxSignature.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AlterMailboxSignature1744382844583 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + alter table mailbox_signature add column is_html boolean not null default false; + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/backend/src/database/nestjs-logger.ts b/backend/src/database/nestjs-logger.ts new file mode 100644 index 0000000..077133c --- /dev/null +++ b/backend/src/database/nestjs-logger.ts @@ -0,0 +1,41 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Logger } from '@nestjs/common'; +import { Logger as TypeOrmLogger, QueryRunner } from 'typeorm'; + +export class NestjsLogger implements TypeOrmLogger { + private readonly logger = new Logger('SQL'); + + logQuery(query: string, parameters?: unknown[], _queryRunner?: QueryRunner) { + this.logger.log(`Query: ${query}${parameters ? ` -- PARAMETERS: ${JSON.stringify(parameters)}` : ''}`); + } + + logQueryError(error: string, query: string, parameters?: unknown[], _queryRunner?: QueryRunner) { + this.logger.error( + `Error: ${error} Query: ${query}${parameters ? ` -- PARAMETERS: ${JSON.stringify(parameters)}` : ''}`, + ); + } + + logQuerySlow(time: number, query: string, parameters?: unknown[], _queryRunner?: QueryRunner) { + this.logger.warn( + `SLOW Query: ${query}${parameters ? ` -- PARAMETERS: ${JSON.stringify(parameters)}` : ''}\t[${time}ms]`, + ); + } + + logSchemaBuild(message: string, _queryRunner?: QueryRunner) { + this.logger.log(`Schema Build: ${message}`); + } + + logMigration(message: string, _queryRunner?: QueryRunner) { + this.logger.log(`Migration: ${message}`); + } + + log(level: 'log' | 'info' | 'warn', message: unknown, _queryRunner?: QueryRunner) { + if (level === 'log') { + this.logger.log(message); + } else if (level === 'info') { + this.logger.debug(message); + } else if (level === 'warn') { + this.logger.warn(message); + } + } +} diff --git a/backend/src/database/services/index.ts b/backend/src/database/services/index.ts new file mode 100644 index 0000000..6dd59cd --- /dev/null +++ b/backend/src/database/services/index.ts @@ -0,0 +1 @@ +export * from './sequence-id.service'; diff --git a/backend/src/database/services/sequence-id.service.ts b/backend/src/database/services/sequence-id.service.ts new file mode 100644 index 0000000..0f1a8c7 --- /dev/null +++ b/backend/src/database/services/sequence-id.service.ts @@ -0,0 +1,18 @@ +import { DataSource } from 'typeorm'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class SequenceIdService { + constructor(private dataSource: DataSource) {} + + async nextIdentity(sequenceName: string): Promise { + const res = await this.dataSource.query(`select nextval('${sequenceName}')`); + + return parseInt(res[0]['nextval']); + } + + public async getIdentityPool(sequenceName: string, size: number): Promise { + const res = await this.dataSource.query(`select nextval('${sequenceName}') from generate_series(1, ${size})`); + return res.map((value) => parseInt(value.nextval)); + } +} diff --git a/backend/src/database/typeorm-migration.config.ts b/backend/src/database/typeorm-migration.config.ts new file mode 100644 index 0000000..1d80119 --- /dev/null +++ b/backend/src/database/typeorm-migration.config.ts @@ -0,0 +1,21 @@ +import { DataSource, type DataSourceOptions } from 'typeorm'; +import { ConfigService } from '@nestjs/config'; +import { config } from 'dotenv'; +import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; + +config({ path: `.env` }); +config({ path: `.env.local`, override: true }); + +const configService = new ConfigService(); + +export default new DataSource({ + type: 'postgres', + host: configService.get('POSTGRES_HOST'), + port: configService.get('POSTGRES_PORT'), + username: configService.get('POSTGRES_USER'), + password: configService.get('POSTGRES_PASSWORD'), + database: configService.get('POSTGRES_DB'), + namingStrategy: new SnakeNamingStrategy(), + migrations: ['./src/database/migrations/*.ts'], + logging: configService.get('POSTGRES_QUERY_LOGGING') === 'true', +} as DataSourceOptions); diff --git a/backend/src/documentation/api-documentation.ts b/backend/src/documentation/api-documentation.ts new file mode 100644 index 0000000..1798af7 --- /dev/null +++ b/backend/src/documentation/api-documentation.ts @@ -0,0 +1,45 @@ +import { INestApplication, Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; + +import documentationConfig from './config/documentation.config'; + +@Module({ + imports: [ConfigModule.forFeature(documentationConfig)], +}) +export class ApiDocumentation { + static configure(app: INestApplication) { + const configService = app.get(ConfigService); + + if (configService.get('documentation.enabled')) { + const appName = configService.get('application.name'); + const baseUrl = configService.get('application.baseUrl'); + + const config = new DocumentBuilder() + .setTitle(`${appName} API`) + .setDescription( + `The ${appName} API enables secure and efficient access to various features of the ${appName} platform. +This platform is designed to help businesses manage client relationships, automate tasks, +and enhance customer communication. With support for multiple client accounts through subdomains, +the API allows businesses to interact with client data, manage entities, and automate actions +based on customizable conditions.`, + ) + .setVersion(process.env.npm_package_version) + .addBearerAuth() + .addApiKey({ type: 'apiKey', in: 'header', name: 'X-Api-Key' }) + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('doc', app, document, { + useGlobalPrefix: true, + customSiteTitle: `${appName} API. Version: ${process.env.npm_package_version} `, + customfavIcon: `${baseUrl}/favicon.ico`, + swaggerOptions: { + defaultModelRendering: 'model', + layout: 'BaseLayout', + tagsSorter: 'alpha', + deepLinking: true, + }, + }); + } + } +} diff --git a/backend/src/documentation/config/documentation.config.ts b/backend/src/documentation/config/documentation.config.ts new file mode 100644 index 0000000..f441bf1 --- /dev/null +++ b/backend/src/documentation/config/documentation.config.ts @@ -0,0 +1,12 @@ +import { registerAs } from '@nestjs/config'; + +export interface DocumentationConfig { + enabled: boolean; +} + +export default registerAs( + 'documentation', + (): DocumentationConfig => ({ + enabled: process.env.API_DOC_ENABLED === 'true', + }), +); diff --git a/backend/src/documentation/index.ts b/backend/src/documentation/index.ts new file mode 100644 index 0000000..444eb89 --- /dev/null +++ b/backend/src/documentation/index.ts @@ -0,0 +1 @@ +export * from './api-documentation'; diff --git a/backend/src/main.ts b/backend/src/main.ts new file mode 100644 index 0000000..27aaa4b --- /dev/null +++ b/backend/src/main.ts @@ -0,0 +1,116 @@ +import { webcrypto } from 'crypto'; +import { NestFactory } from '@nestjs/core'; + +// Polyfill crypto for @nestjs/typeorm +if (!global.crypto) { + global.crypto = webcrypto as any; +} +import { Logger, ValidationPipe } from '@nestjs/common'; +import { NestExpressApplication } from '@nestjs/platform-express'; +import { utilities, WinstonModule } from 'nest-winston'; +import winston from 'winston'; +import helmet from 'helmet'; +//import rateLimit from 'express-rate-limit'; +import cookieParser from 'cookie-parser'; + +import { AppModule } from './app.module'; +import { ApiDocumentation } from './documentation'; +import { extractSubdomain, LoggingInterceptor } from './common'; + +function getLogger() { + return process.env.WINSTON_ENABLED === 'true' + ? WinstonModule.createLogger({ + level: 'debug', + transports: [ + new winston.transports.Console({ + format: winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }), + utilities.format.nestLike(process.env.APPLICATION_NAME, { colors: true, prettyPrint: true }), + ), + }), + ], + }) + : undefined; +} + +async function bootstrap() { + if (process.env.NEW_RELIC_ENABLED === 'true') { + // eslint-disable-next-line @typescript-eslint/no-require-imports + require('newrelic'); + } + + const app = await NestFactory.create(AppModule, { rawBody: true, logger: getLogger() }); + + // Security headers + app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "blob:"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"], + imgSrc: ["'self'", "data:", "blob:", "https://*"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + connectSrc: ["'self'"], + frameSrc: ["'self'"], + objectSrc: ["'none'"], + baseUri: ["'self'"], + formAction: ["'self'"] + } + }, + hsts: { + maxAge: 31536000, + includeSubDomains: true, + preload: true + }, + referrerPolicy: { policy: 'strict-origin-when-cross-origin' } + })); + + // Rate limiting disabled to eliminate 429 errors + // app.use( + // rateLimit({ + // windowMs: 15 * 60 * 1000, // 15 minutes + // max: 1000, // limit each IP to 1000 requests per windowMs + // message: 'Too many requests from this IP, please try again later.', + // standardHeaders: true, + // legacyHeaders: false, + // }), + // ); + + // CORS with restrictions + app.enableCors({ + origin: process.env['FRONTEND_URL'] || 'http://localhost:3000', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', + credentials: true, + allowedHeaders: ['Content-Type', 'Authorization', 'X-CSRF-Token'] + }); + + app.set('trust proxy', true); + app.use(cookieParser()); + app.use(extractSubdomain); + app.setGlobalPrefix('api'); + + // Global validation + app.useGlobalPipes(new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: false, + transform: true, + disableErrorMessages: process.env['NODE_ENV'] === 'production' + })); + + app.useGlobalInterceptors(new LoggingInterceptor()); + + ApiDocumentation.configure(app); + + const logger = new Logger('main'); + + process.on('uncaughtException', (error) => { + logger.error(`Uncaught Exception`, error.stack); + }); + + await app.listen(process.env.APPLICATION_PORT, '127.0.0.1'); + + logger.log(`Application is running on: ${await app.getUrl()}`); + logger.log(`Application version is: ${process.env.npm_package_version}`); +} + +bootstrap(); diff --git a/backend/src/modules/analytics/analytics.module.ts b/backend/src/modules/analytics/analytics.module.ts new file mode 100644 index 0000000..e14bf22 --- /dev/null +++ b/backend/src/modules/analytics/analytics.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; + +import analyticsConfig from './config/analytics.config'; +import { AnalyticsService } from './analytics.service'; + +@Module({ + imports: [ConfigModule.forFeature(analyticsConfig)], + providers: [AnalyticsService], +}) +export class AnalyticsModule {} diff --git a/backend/src/modules/analytics/analytics.service.ts b/backend/src/modules/analytics/analytics.service.ts new file mode 100644 index 0000000..73d3f78 --- /dev/null +++ b/backend/src/modules/analytics/analytics.service.ts @@ -0,0 +1,83 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { HttpService } from '@nestjs/axios'; +import { OnEvent } from '@nestjs/event-emitter'; +import { catchError, lastValueFrom } from 'rxjs'; + +import { IamEventType, AccountCreatedEvent, UserLoginEvent } from '@/modules//iam/common'; + +import { AnalyticsConfig } from './config/analytics.config'; + +const GoogleAnalyticsUrls = { + ga: 'https://www.google-analytics.com', + mp: () => `${GoogleAnalyticsUrls.ga}/mp`, + collect: (measurementId: string, apiSecret: string) => + `${GoogleAnalyticsUrls.mp()}/collect?measurement_id=${measurementId}&api_secret=${apiSecret}`, +} as const; + +interface GaEventData { + client_id: string; + events: { name: string; params: GaEventDataParam }[]; +} + +type GaEventDataParam = Record; + +@Injectable() +export class AnalyticsService { + private readonly logger = new Logger(AnalyticsService.name); + private _config: AnalyticsConfig; + + constructor( + private readonly httpService: HttpService, + private readonly configService: ConfigService, + ) { + this._config = this.configService.get('analytics'); + } + + @OnEvent(IamEventType.AccountCreated, { async: true }) + public async handleRegistrationEvent(event: AccountCreatedEvent) { + this.collectGAEvents({ + client_id: event.gaClientId, + events: [ + { + name: 'sign_up', + params: { + account_id: event.accountId, + account_user_id: event.ownerId, + user_id: event.gaUserId, + account_tariff: event.subscriptionName, + }, + }, + ], + }); + } + + @OnEvent(IamEventType.UserLogin, { async: true }) + public async handleUserLoginEvent(event: UserLoginEvent) { + this.collectGAEvents({ + client_id: event.gaClientId, + events: [ + { + name: 'login', + params: { + account_id: event.accountId, + account_user_id: event.userId, + user_id: event.gaUserId, + account_tariff: event.subscriptionName, + }, + }, + ], + }); + } + + private async collectGAEvents(data: GaEventData) { + const url = GoogleAnalyticsUrls.collect(this._config.gaMeasurementId, this._config.gaApiSecret); + const response$ = this.httpService.post(url, data).pipe( + catchError((error) => { + this.logger.error(`Google Analytics measurement error`, (error as Error)?.stack); + throw error; + }), + ); + await lastValueFrom(response$); + } +} diff --git a/backend/src/modules/analytics/config/analytics.config.ts b/backend/src/modules/analytics/config/analytics.config.ts new file mode 100644 index 0000000..0164841 --- /dev/null +++ b/backend/src/modules/analytics/config/analytics.config.ts @@ -0,0 +1,14 @@ +import { registerAs } from '@nestjs/config'; + +export interface AnalyticsConfig { + gaMeasurementId: string; + gaApiSecret: string; +} + +export default registerAs( + 'analytics', + (): AnalyticsConfig => ({ + gaMeasurementId: process.env.GA_MEASUREMENT_ID, + gaApiSecret: process.env.GA_API_SECRET, + }), +); diff --git a/backend/src/modules/automation/automation-core/automation-core.controller.ts b/backend/src/modules/automation/automation-core/automation-core.controller.ts new file mode 100644 index 0000000..81ead28 --- /dev/null +++ b/backend/src/modules/automation/automation-core/automation-core.controller.ts @@ -0,0 +1,25 @@ +import { Controller, Delete, Get, Param } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { AutomationCoreService } from './automation-core.service'; + +@ApiExcludeController(true) +@Controller('core') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class AutomationCoreController { + constructor(private readonly service: AutomationCoreService) {} + + @Get('processes') + public async listProcessDefinitions() { + return this.service.listProcessDefinitions(); + } + + @Delete('processes/:resourceKey') + public async delete(@Param('resourceKey') resourceKey: string) { + return this.service.deleteProcess(resourceKey); + } +} diff --git a/backend/src/modules/automation/automation-core/automation-core.service.ts b/backend/src/modules/automation/automation-core/automation-core.service.ts new file mode 100644 index 0000000..2be246e --- /dev/null +++ b/backend/src/modules/automation/automation-core/automation-core.service.ts @@ -0,0 +1,163 @@ +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { DiscoveryService } from '@nestjs/core'; +import { ConfigService } from '@nestjs/config'; + +import { Camunda8 } from '@camunda8/sdk'; +import { IOutputVariables, JSONDoc, ProcessMetadata } from '@camunda8/sdk/dist/zeebe/types'; +import { ProcessDefinition } from '@camunda8/sdk/dist/operate/lib/OperateDto'; + +import { AutomationConfig } from '../config/automation.config'; +import { AUTOMATION_JOB_HANDLER, AUTOMATION_WORKER, AutomationHandler, Message, Signal } from '../common'; +import { AutomationJobHandler } from '../common'; +import { ProcessDefinitionVersions } from './types'; + +@Injectable() +export class AutomationCoreService implements OnModuleInit { + private readonly logger = new Logger(AutomationCoreService.name); + private readonly camunda = new Camunda8(); + private readonly zeebe = this.camunda.getZeebeGrpcApiClient(); + private readonly operate = this.camunda.getOperateApiClient(); + + constructor( + private readonly configService: ConfigService, + private readonly discoveryService: DiscoveryService, + ) {} + + onModuleInit() { + const config = this.configService.get('automation'); + if (!config.jobDiscovery) return; + + const wrappers = this.discoveryService.getProviders(); + const handlers = wrappers + .filter((wrapper) => wrapper.metatype && Reflect.getMetadata(AUTOMATION_WORKER, wrapper.metatype)) + .map((wrapper) => ({ + instance: wrapper.instance as AutomationJobHandler, + type: Reflect.getMetadata(AUTOMATION_WORKER, wrapper.metatype) as string, + })); + for (const handler of handlers) { + this.createWorker(handler.type, handler.instance.handleJob.bind(handler.instance)); + } + + wrappers + .filter((wrapper) => wrapper.instance) + .forEach((wrapper) => { + Object.getOwnPropertyNames(Object.getPrototypeOf(wrapper.instance)).forEach((method) => { + try { + const methodHandler = wrapper.instance[method]; + if (typeof methodHandler === 'function') { + const metadata = Reflect.getMetadata(AUTOMATION_JOB_HANDLER, methodHandler) as string; + if (metadata) { + this.createWorker(metadata, methodHandler.bind(wrapper.instance)); + } + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + //skip all errors + } + }); + }); + } + + async listProcessDefinitions(): Promise { + const size = 100; + let total = 0; + let searchAfter: unknown[] = undefined; + const definitions: ProcessDefinition[] = []; + do { + const result = await this.operate.searchProcessDefinitions({ + sort: [{ field: 'bpmnProcessId', order: 'ASC' }], + searchAfter, + size, + }); + definitions.push(...result.items); + total = Number(result.total); + searchAfter = result.sortValues; + } while (definitions.length < total); + const processes = definitions.reduce((acc, item) => { + const existingProcess = acc.find((process) => process.bpmnProcessId === item.bpmnProcessId); + + if (existingProcess) { + existingProcess.versions.push({ version: item.version, key: item.key }); + } else { + acc.push({ + bpmnProcessId: item.bpmnProcessId, + name: item.name, + versions: [{ version: item.version, key: item.key }], + }); + } + + return acc; + }, [] as ProcessDefinitionVersions[]); + + return processes; + } + + async deployProcess(name: string, process: Buffer): Promise { + try { + const deploy = await this.zeebe.deployResource({ name, process }); + return deploy.deployments[0].process; + } catch (e) { + this.logger.warn(`Deploy process error: ${e.toString()}`); + return null; + } + } + + async deleteProcess(resourceKey: string): Promise { + try { + await this.zeebe.deleteResource({ resourceKey }); + return true; + } catch (e) { + this.logger.warn(`Delete process error: ${e.toString()}`); + return false; + } + } + + async sendMessage(message: Message) { + this.zeebe.publishMessage({ + name: this.formatSignalName(message), + correlationKey: message.correlationKey, + variables: message.variables, + }); + } + + async sendSignal(signal: Signal) { + this.zeebe.broadcastSignal({ signalName: this.formatSignalName(signal), variables: signal.variables }); + } + + async startProcess({ bpmnProcessId, variables }: { bpmnProcessId: string; variables: V }) { + this.zeebe.createProcessInstance({ bpmnProcessId, variables }); + } + + private formatSignalName({ name }: Signal): string { + return `${Array.isArray(name) ? name.join('|') : name}`; + } + + private createWorker( + type: string, + handler: AutomationHandler, + ) { + this.zeebe.createWorker({ + taskType: type, + taskHandler: async (job) => { + this.logger.log(`Handling job of type '${job.type}' with data: ${JSON.stringify(job)}`); + + try { + const result = await handler({ variables: job.variables as InputVariables }); + this.logger.log(`Handled job of type '${job.type}' with result: ${JSON.stringify(result)}`); + + return job.complete(result?.variables as IOutputVariables); + } catch (e) { + this.logger.error(`Worker '${type}' error`, (e as Error)?.stack); + + if (e instanceof Error) { + return job.fail(e.message); + } + + return job.fail(e.toString()); + } + }, + }); + + this.logger.log(`Worker for task type '${type}' created`); + } +} diff --git a/backend/src/modules/automation/automation-core/index.ts b/backend/src/modules/automation/automation-core/index.ts new file mode 100644 index 0000000..b9dae37 --- /dev/null +++ b/backend/src/modules/automation/automation-core/index.ts @@ -0,0 +1,3 @@ +export * from './automation-core.controller'; +export * from './automation-core.service'; +export * from './types'; diff --git a/backend/src/modules/automation/automation-core/types/index.ts b/backend/src/modules/automation/automation-core/types/index.ts new file mode 100644 index 0000000..74ae0ba --- /dev/null +++ b/backend/src/modules/automation/automation-core/types/index.ts @@ -0,0 +1 @@ +export * from './process-definition-version'; diff --git a/backend/src/modules/automation/automation-core/types/process-definition-version.ts b/backend/src/modules/automation/automation-core/types/process-definition-version.ts new file mode 100644 index 0000000..f608fdf --- /dev/null +++ b/backend/src/modules/automation/automation-core/types/process-definition-version.ts @@ -0,0 +1,10 @@ +interface ProcessDefinitionVersion { + version: number; + key: string; +} + +export interface ProcessDefinitionVersions { + bpmnProcessId: string; + name: string; + versions: ProcessDefinitionVersion[]; +} diff --git a/backend/src/modules/automation/automation-entity-type/automation-entity-type.controller.ts b/backend/src/modules/automation/automation-entity-type/automation-entity-type.controller.ts new file mode 100644 index 0000000..703d4ff --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/automation-entity-type.controller.ts @@ -0,0 +1,75 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { + AutomationEntityTypeDto, + AutomationEntityTypeFilterDto, + CreateAutomationEntityTypeDto, + UpdateAutomationEntityTypeDto, +} from './dto'; +import { AutomationEntityTypeService } from './automation-entity-type.service'; + +@ApiTags('automation/entity-types') +@Controller('entity-types') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class AutomationEntityTypeController { + constructor(private readonly service: AutomationEntityTypeService) {} + + @ApiOperation({ summary: 'Create EntityType automation', description: 'Create simple automation for EntityType' }) + @ApiBody({ description: 'Data for creating EntityType automation', type: CreateAutomationEntityTypeDto }) + @ApiCreatedResponse({ description: 'EntityType automation', type: AutomationEntityTypeDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateAutomationEntityTypeDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Get EntityType automations', description: 'Get simple automations for EntityType' }) + @ApiOkResponse({ description: 'EntityType automations', type: [AutomationEntityTypeDto] }) + @Get() + async findMany(@CurrentAuth() { accountId }: AuthData, @Query() filter: AutomationEntityTypeFilterDto) { + return this.service.findMany({ accountId, ...filter }); + } + + @ApiOperation({ summary: 'Get EntityType automation', description: 'Get simple automation for EntityType' }) + @ApiParam({ name: 'automationId', description: 'EntityType automation ID' }) + @ApiOkResponse({ description: 'EntityType automation', type: AutomationEntityTypeDto }) + @Get(':automationId') + async findOne(@CurrentAuth() { accountId }: AuthData, @Param('automationId', ParseIntPipe) automationId: number) { + return this.service.findOne({ accountId, automationId }); + } + + @ApiOperation({ summary: 'Update EntityType automation', description: 'Update simple automation for EntityType' }) + @ApiParam({ name: 'automationId', description: 'EntityType automation ID' }) + @ApiBody({ description: 'Data for updating EntityType automation', type: UpdateAutomationEntityTypeDto }) + @ApiOkResponse({ description: 'EntityType automation', type: AutomationEntityTypeDto }) + @Patch(':automationId') + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('automationId', ParseIntPipe) automationId: number, + @Body() dto: UpdateAutomationEntityTypeDto, + ) { + return this.service.update({ accountId, automationId, dto }); + } + + @ApiOperation({ summary: 'Delete EntityType automation', description: 'Delete simple automation for EntityType' }) + @ApiParam({ name: 'automationId', description: 'EntityType automation ID' }) + @ApiOkResponse({ description: 'Deleted EntityType automation ID', type: Number }) + @Delete(':automationId') + async delete(@CurrentAuth() { accountId }: AuthData, @Param('automationId', ParseIntPipe) automationId: number) { + return this.service.delete({ accountId, automationId }); + } + + @ApiOperation({ summary: 'Generate BPMN', description: 'Generate BPM model for EntityType automation' }) + @ApiParam({ name: 'automationId', description: 'EntityType automation ID' }) + @ApiOkResponse({ description: 'BPMN model', type: String }) + @Get(':automationId/generate') + async generate(@CurrentAuth() { accountId }: AuthData, @Param('automationId', ParseIntPipe) automationId: number) { + return this.service.generateBpmn({ accountId, automationId }); + } +} diff --git a/backend/src/modules/automation/automation-entity-type/automation-entity-type.handler.ts b/backend/src/modules/automation/automation-entity-type/automation-entity-type.handler.ts new file mode 100644 index 0000000..e39ffe3 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/automation-entity-type.handler.ts @@ -0,0 +1,125 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; +import { BoardEvent, BoardStageDeletedEvent, CrmEventType, EntityTypeEvent } from '@/CRM/common'; +import { FieldEvent, FieldEventType } from '@/modules/entity/entity-field/common'; +import { ChatProviderEvent, ChatProviderStatus, MultichatEventType } from '@/modules/multichat/common'; +import { MailboxEvent, MailEventType } from '@/Mailing/common'; + +import { AutomationEntityTypeService } from './automation-entity-type.service'; +import { EntityTypeActionType } from './enums'; + +@Injectable() +export class AutomationEntityTypeHandler { + constructor(private readonly service: AutomationEntityTypeService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + if (event.newUserId) { + await this.service.changeOwner({ + accountId: event.accountId, + currentUserId: event.userId, + newUserId: event.newUserId, + }); + + await this.service.updateByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityResponsibleChange, + criteria: { responsibleUserId: event.userId }, + payload: { newResponsibleUserId: event.newUserId }, + }); + } else { + await this.service.deleteMany({ accountId: event.accountId, createdBy: event.userId }); + + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityResponsibleChange, + criteria: { responsibleUserId: event.userId }, + }); + } + } + + @OnEvent(CrmEventType.EntityTypeDeleted, { async: true }) + public async onEntityTypeDeleted(event: EntityTypeEvent) { + await this.service.deleteMany({ accountId: event.accountId, entityTypeId: event.entityTypeId }); + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityCreate, + criteria: { entityTypeId: event.entityTypeId }, + }); + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityLinkedStageChange, + criteria: { entityTypeId: event.entityTypeId }, + }); + } + + // !!! + @OnEvent(CrmEventType.BoardDeleted, { async: true }) + public async onBoardDeleted(event: BoardEvent) { + await this.service.deleteMany({ accountId: event.accountId, boardId: event.boardId }); + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityCreate, + criteria: { boardId: event.boardId }, + }); + } + + @OnEvent(CrmEventType.BoardStageDeleted, { async: true }) + public async onBoardStageDeleted(event: BoardStageDeletedEvent) { + await this.service.deleteMany({ accountId: event.accountId, boardId: event.boardId, stageId: event.stageId }); + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityCreate, + criteria: { stageId: event.stageId }, + }); + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityStageChange, + criteria: { stageId: event.stageId }, + }); + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EntityLinkedStageChange, + criteria: { stageId: event.stageId }, + }); + } + + @OnEvent(FieldEventType.FieldDeleted, { async: true }) + public async onFieldDeleted(event: FieldEvent) { + await this.service.deleteByConditionsCriteria({ + accountId: event.accountId, + criteria: { fieldId: event.fieldId }, + }); + } + + @OnEvent(MailEventType.MailboxDeleted, { async: true }) + public async onMailboxDeleted(event: MailboxEvent) { + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.EmailSend, + criteria: { mailboxId: event.mailboxId }, + }); + } + + @OnEvent(MultichatEventType.ChatProviderUpdated, { async: true }) + public async onChatProviderUpdated(event: ChatProviderEvent) { + if (event.status !== ChatProviderStatus.Active) { + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.ChatSendExternal, + criteria: { providerId: event.providerId }, + }); + } + } + + @OnEvent(MultichatEventType.ChatProviderDeleted, { async: true }) + public async onChatProviderDeleted(event: ChatProviderEvent) { + await this.service.deleteByActionsCriteria({ + accountId: event.accountId, + type: EntityTypeActionType.ChatSendExternal, + criteria: { providerId: event.providerId }, + }); + } +} diff --git a/backend/src/modules/automation/automation-entity-type/automation-entity-type.service.ts b/backend/src/modules/automation/automation-entity-type/automation-entity-type.service.ts new file mode 100644 index 0000000..10fc4ae --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/automation-entity-type.service.ts @@ -0,0 +1,465 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { readFileSync } from 'fs'; +import Handlebars from 'handlebars'; +import path from 'path'; +import type { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { AutomationProcessService } from '../automation-process'; +import { + AutomationDelayUtil, + AutomationEventType, + AutomationProcessType, + AutomatonConditionUtil, + EntityTypeApplyEvent, +} from '../common'; + +import type { + ActionEntityResponsibleChangeSettings, + CreateAutomationEntityTypeDto, + UpdateAutomationEntityTypeDto, +} from './dto'; +import { AutomationEntityType } from './entities'; +import { EntityTypeActionType, EntityTypeTrigger } from './enums'; + +interface FindFilter { + accountId: number; + automationId?: number; + entityTypeId?: number; + boardId?: number; + stageId?: number; + isActive?: boolean; + createdBy?: number; +} + +interface DeleteActionsCriteria { + entityTypeId?: number; + boardId?: number; + stageId?: number; + mailboxId?: number; + providerId?: number; + responsibleUserId?: number; +} + +interface UpdateActionsCriteria { + responsibleUserId?: number; +} + +interface UpdateActionsPayload { + newResponsibleUserId?: number; +} + +interface DeleteConditionsCriteria { + fieldId?: number; +} + +@Injectable() +export class AutomationEntityTypeService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(AutomationEntityType) + private readonly repository: Repository, + private readonly processService: AutomationProcessService, + ) {} + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateAutomationEntityTypeDto; + }): Promise { + let automation = await this.repository.save(AutomationEntityType.fromDto(accountId, userId, dto)); + if (dto.isActive) { + automation = await this.activate(automation); + } + if (automation.isActive && dto.applyImmediately) { + this.apply({ accountId, automation }); + } + + return automation; + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).orderBy('created_at').getMany(); + } + + async generateBpmn({ accountId, automationId }: { accountId: number; automationId: number }): Promise { + const automation = await this.findOne({ accountId, automationId }); + if (!automation) { + throw NotFoundError.withId(AutomationEntityType, automationId); + } + + return await this.generate(automation); + } + + async update({ + accountId, + automationId, + dto, + }: { + accountId: number; + automationId: number; + dto: UpdateAutomationEntityTypeDto; + }): Promise { + let automation = await this.findOne({ accountId, automationId }); + if (!automation) { + throw NotFoundError.withId(AutomationEntityType, automationId); + } + + const wasActive = automation.isActive; + if (automation.isActive) { + await this.deactivate(automation); + automation.processId = null; + await this.repository.save(automation); + } + + this.repository.save(automation.update(dto)); + + if (dto.isActive || (dto.isActive === undefined && wasActive)) { + automation = await this.activate(automation); + } + if (automation.isActive && dto.applyImmediately) { + this.apply({ accountId, automation }); + } + + return automation; + } + + async changeOwner({ + accountId, + currentUserId, + newUserId, + }: { + accountId: number; + currentUserId: number; + newUserId: number; + }) { + await this.repository.update({ accountId, createdBy: currentUserId }, { createdBy: newUserId }); + } + + async deleteMany(filter: FindFilter) { + const automations = await this.findMany(filter); + for (const automation of automations) { + await this.delete({ accountId: automation.accountId, automationId: automation.id }); + } + } + + async delete({ accountId, automationId }: { accountId: number; automationId: number }): Promise { + const automation = await this.findOne({ accountId, automationId }); + if (!automation) { + throw NotFoundError.withId(AutomationEntityType, automationId); + } + + await this.deleteAutomation(automation); + + return automation.id; + } + + async updateByActionsCriteria({ + accountId, + type, + criteria, + payload, + }: { + accountId: number; + type: EntityTypeActionType; + criteria: UpdateActionsCriteria; + payload: UpdateActionsPayload; + }) { + switch (type) { + case EntityTypeActionType.EntityResponsibleChange: + if (criteria.responsibleUserId && payload.newResponsibleUserId) { + const automations = await this.createFindQb({ accountId }) + .andWhere( + // eslint-disable-next-line max-len + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.responsibleUserId == ${criteria.responsibleUserId})')`, + ) + .getMany(); + + for (const automation of automations) { + const updatedActions = automation.actions.map((action) => { + if ( + (action.settings as ActionEntityResponsibleChangeSettings).responsibleUserId === + criteria.responsibleUserId + ) { + return { + ...action, + settings: { + ...action.settings, + responsibleUserId: payload.newResponsibleUserId, + }, + }; + } + return action; + }); + + const wasActive = automation.isActive; + + if (automation.isActive) { + await this.deactivate(automation); + } + + await this.repository.save(automation.update({ actions: updatedActions })); + + if (wasActive) { + await this.activate(automation); + } + } + } + } + } + + async deleteByActionsCriteria({ + accountId, + type, + criteria, + }: { + accountId: number; + type: EntityTypeActionType; + criteria: DeleteActionsCriteria; + }) { + let automations: AutomationEntityType[] = []; + switch (type) { + case EntityTypeActionType.EntityCreate: + if (criteria.entityTypeId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + // eslint-disable-next-line max-len + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.entityTypeId == ${criteria.entityTypeId})')`, + ) + .getMany(); + } + if (criteria.boardId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.boardId == ${criteria.boardId})')`, + ) + .getMany(); + } + if (criteria.stageId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.stageId == ${criteria.stageId})')`, + ) + .getMany(); + } + break; + case EntityTypeActionType.EntityStageChange: + if (criteria.stageId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.stageId == ${criteria.stageId})')`, + ) + .getMany(); + } + break; + case EntityTypeActionType.EntityLinkedStageChange: + if (criteria.entityTypeId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + // eslint-disable-next-line max-len + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.entityTypeId == ${criteria.entityTypeId})')`, + ) + .getMany(); + } + if (criteria.stageId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.stageId == ${criteria.stageId})')`, + ) + .getMany(); + } + break; + case EntityTypeActionType.EntityResponsibleChange: + if (criteria.responsibleUserId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + // eslint-disable-next-line max-len + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.responsibleUserId == ${criteria.responsibleUserId})')`, + ) + .getMany(); + } + break; + case EntityTypeActionType.EmailSend: + if (criteria.mailboxId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + // eslint-disable-next-line max-len + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.mailboxId == ${criteria.mailboxId})')`, + ) + .getMany(); + } + break; + case EntityTypeActionType.ChatSendExternal: + if (criteria.providerId) { + automations = await this.createFindQb({ accountId }) + .andWhere( + // eslint-disable-next-line max-len + `jsonb_path_exists(actions, '$[*] ? (@.type == "${type}" && @.settings.providerId == ${criteria.providerId})')`, + ) + .getMany(); + } + break; + } + + for (const automation of automations) { + await this.deleteAutomation(automation); + } + } + + async deleteByConditionsCriteria({ accountId, criteria }: { accountId: number; criteria: DeleteConditionsCriteria }) { + const automations = await this.createFindQb({ accountId }) + .andWhere(`jsonb_path_exists(conditions, '$.fields[*] ? (@.fieldId == ${criteria.fieldId})')`) + .getMany(); + + for (const automation of automations) { + await this.deleteAutomation(automation); + } + } + + private async deleteAutomation(automation: AutomationEntityType) { + if (automation.isActive) { + await this.deactivate(automation); + } + + await this.repository.delete(automation.id); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + + if (filter?.automationId) { + qb.andWhere('id = :id', { id: filter.automationId }); + } + if (filter?.entityTypeId) { + qb.andWhere('entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + if (filter?.boardId) { + qb.andWhere('board_id = :boardId', { boardId: filter.boardId }); + } + if (filter?.stageId) { + qb.andWhere('stage_id = :stageId', { stageId: filter.stageId }); + } + if (filter?.isActive !== undefined) { + qb.andWhere('is_active = :isActive', { isActive: filter.isActive }); + } + if (filter?.createdBy) { + qb.andWhere('created_by = :createdBy', { createdBy: filter.createdBy }); + } + + return qb; + } + + private async activate(automation: AutomationEntityType): Promise { + const bpmn = await this.generate(automation); + + const process = await this.processService.create({ + accountId: automation.accountId, + userId: automation.createdBy, + dto: { + name: automation.name, + type: AutomationProcessType.EntityType, + objectId: automation.entityTypeId, + isActive: true, + isReadonly: true, + bpmnFile: bpmn, + }, + }); + + await this.repository.save(automation.update({ processId: process.id })); + + return automation; + } + + private async deactivate(automation: AutomationEntityType): Promise { + const processId = automation.processId; + await this.repository.save(automation.update({ processId: null })); + + await this.processService.delete({ accountId: automation.accountId, processId }); + + return automation; + } + + private async apply({ accountId, automation }: { accountId: number; automation: AutomationEntityType }) { + this.eventEmitter.emit( + AutomationEventType.EntityTypeApply, + new EntityTypeApplyEvent({ + accountId, + automationId: automation.id, + processId: automation.processId, + entityTypeId: automation.entityTypeId, + boardId: automation.boardId, + stageId: automation.stageId, + }), + ); + } + + private async generate(automation: AutomationEntityType): Promise { + const action = automation.actions?.[0]; + if (action) { + const content: string = readFileSync(path.join(__dirname, 'templates/simple_automation.bpmn.template'), 'utf-8'); + + const template = Handlebars.compile(content); + const result = template({ + accountId: automation.accountId, + entityTypeId: automation.entityTypeId, + processId: automation.id, + name: automation.name, + events: { + create: automation.triggers.includes(EntityTypeTrigger.Create), + changeStage: automation.triggers.includes(EntityTypeTrigger.ChangeStage), + changeOwner: automation.triggers.includes(EntityTypeTrigger.ChangeOwner), + }, + conditions: AutomatonConditionUtil.formatEntityCondition({ + stageId: automation.stageId, + ownerIds: automation.conditions?.ownerIds, + fields: automation.conditions?.fields, + }), + action: { + delay: AutomationDelayUtil.formatSeconds(action.delay), + type: action.type, + target: this.formatTarget(action.type), + settings: JSON.stringify(action.settings), + }, + }); + + return result; + } + + return null; + } + + private formatTarget(type: EntityTypeActionType): string { + switch (type) { + case EntityTypeActionType.CreateTask: + case EntityTypeActionType.TaskCreate: + return 'taskSettings'; + case EntityTypeActionType.CreateActivity: + case EntityTypeActionType.ActivityCreate: + return 'activitySettings'; + case EntityTypeActionType.SendEmail: + case EntityTypeActionType.EmailSend: + return 'emailSettings'; + case EntityTypeActionType.ChangeStage: + case EntityTypeActionType.EntityCreate: + case EntityTypeActionType.EntityStageChange: + case EntityTypeActionType.EntityLinkedStageChange: + case EntityTypeActionType.EntityResponsibleChange: + return 'entitySettings'; + case EntityTypeActionType.ChatSendAmwork: + case EntityTypeActionType.ChatSendExternal: + return 'chatSettings'; + case EntityTypeActionType.HttpCall: + return 'httpSettings'; + } + } +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-activity-create-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-activity-create-settings.dto.ts new file mode 100644 index 0000000..cc8e3f9 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-activity-create-settings.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; +import { DeadlineType } from '../../enums'; + +export class ActionActivityCreateSettings extends ActionsSettings { + @ApiPropertyOptional({ description: 'User ID responsible for the activity', nullable: true }) + @IsOptional() + @IsNumber() + responsibleUserId?: number | null; + + @ApiProperty({ description: 'ActivityType ID' }) + @IsNumber() + activityTypeId: number; + + @ApiProperty({ description: 'Text of the activity' }) + @IsString() + text: string; + + @ApiProperty({ description: 'Deadline type', enum: DeadlineType }) + @IsEnum(DeadlineType) + deadlineType: DeadlineType; + + @ApiPropertyOptional({ description: 'Deadline time in seconds', nullable: true }) + @IsOptional() + @IsNumber() + deadlineTime?: number | null; + + @ApiPropertyOptional({ description: 'Defer start time in seconds', nullable: true }) + @IsOptional() + @IsNumber() + deferStart?: number | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-amwork-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-amwork-settings.dto.ts new file mode 100644 index 0000000..1bacb50 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-amwork-settings.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; + +export class ActionChatSendAmworkSettings extends ActionsSettings { + @ApiProperty({ description: 'Chat message text' }) + @IsString() + message: string; + + @ApiProperty({ description: 'Send message from User ID', nullable: true }) + @IsOptional() + @IsNumber() + userId: number | null; + + @ApiProperty({ description: 'Send message to User ID', type: [Number], nullable: true }) + @IsOptional() + @IsNumber({}, { each: true }) + sendTo: number[] | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-settings.dto.ts new file mode 100644 index 0000000..f9744ba --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-chat-send-settings.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; +import { ActionSendOptions } from './action-send-options.dto'; + +export class ActionChatSendSettings extends ActionsSettings { + @ApiProperty({ description: 'Chat message text' }) + @IsString() + message: string; + + @ApiProperty({ description: 'Chat provider ID' }) + @IsNumber() + providerId: number; + + @ApiProperty({ description: 'Send message from User ID', nullable: true }) + @IsOptional() + @IsNumber() + userId: number | null; + + @ApiPropertyOptional({ description: 'Message send options', nullable: true, type: ActionSendOptions }) + @IsOptional() + options?: ActionSendOptions | null; + + @ApiPropertyOptional({ description: 'Phone numbers', nullable: true, type: [String] }) + @IsOptional() + @IsString({ each: true }) + phoneNumbers?: string[] | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-email-send-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-email-send-settings.dto.ts new file mode 100644 index 0000000..cfa0f21 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-email-send-settings.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; +import { ActionSendOptions } from './action-send-options.dto'; + +export class ActionEmailSendSettings extends ActionsSettings { + @ApiProperty({ description: 'Subject of the email' }) + @IsString() + subject: string; + + @ApiProperty({ description: 'Content of the email' }) + @IsString() + content: string; + + @ApiPropertyOptional({ description: 'Signature of the email', nullable: true }) + @IsOptional() + @IsString() + signature?: string | null; + + @ApiProperty({ description: 'Is the email content HTML?' }) + @IsBoolean() + sendAsHtml: boolean; + + @ApiProperty({ description: 'Mailbox ID' }) + @IsNumber() + mailboxId: number; + + @ApiProperty({ description: 'Send email from User ID' }) + @IsNumber() + userId: number; + + @ApiPropertyOptional({ description: 'Email send options', nullable: true, type: ActionSendOptions }) + @IsOptional() + options?: ActionSendOptions | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-create-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-create-settings.dto.ts new file mode 100644 index 0000000..2f7cdfb --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-create-settings.dto.ts @@ -0,0 +1,30 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; + +export class ActionEntityCreateSettings extends ActionsSettings { + @ApiProperty({ description: 'EntityType ID' }) + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional({ description: 'Board ID', nullable: true }) + @IsOptional() + @IsNumber() + boardId?: number | null; + + @ApiPropertyOptional({ description: 'Stage ID', nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; + + @ApiPropertyOptional({ description: 'User ID responsible for the entity', nullable: true }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ description: 'Name of the entity', nullable: true }) + @IsOptional() + @IsString() + name?: string | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-linked-stage-change-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-linked-stage-change-settings.dto.ts new file mode 100644 index 0000000..008cbaf --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-linked-stage-change-settings.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; +import { ChangeStageType } from '../../enums'; + +export class ActionEntityLinkedStageChangeSettings extends ActionsSettings { + @ApiProperty({ description: 'Linked entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + stageId: number; + + @ApiProperty({ description: 'Type of the operation', enum: ChangeStageType }) + @IsEnum(ChangeStageType) + operationType: ChangeStageType; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-responsible-change-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-responsible-change-settings.dto.ts new file mode 100644 index 0000000..e7e84f7 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-responsible-change-settings.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; + +export class ActionEntityResponsibleChangeSettings extends ActionsSettings { + @ApiProperty({ description: 'Responsible user ID, current responsible user ID will be changed to this' }) + @IsNumber() + responsibleUserId: number; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-stage-change-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-stage-change-settings.dto.ts new file mode 100644 index 0000000..4da5fc0 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-entity-stage-change-settings.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; +import { ChangeStageType } from '../../enums'; + +export class ActionEntityStageChangeSettings extends ActionsSettings { + @ApiProperty({ description: 'Stage ID' }) + @IsNumber() + stageId: number; + + @ApiProperty({ description: 'Type of the operation', enum: ChangeStageType }) + @IsEnum(ChangeStageType) + operationType: ChangeStageType; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-entity.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-entity.dto.ts new file mode 100644 index 0000000..1366e99 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-entity.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +import { ActionSendOptionsValue } from './action-send-options-value.dto'; + +export class ActionSendOptionsEntity extends ActionSendOptionsValue { + @ApiProperty({ description: 'Use only first entity' }) + @IsOptional() + @IsBoolean() + onlyFirstEntity?: boolean | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-value.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-value.dto.ts new file mode 100644 index 0000000..e768e97 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options-value.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class ActionSendOptionsValue { + @ApiProperty({ description: 'Use only first value' }) + @IsOptional() + @IsBoolean() + onlyFirstValue?: boolean | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options.dto.ts new file mode 100644 index 0000000..d298a91 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-send-options.dto.ts @@ -0,0 +1,27 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; + +import { ActionSendOptionsValue } from './action-send-options-value.dto'; +import { ActionSendOptionsEntity } from './action-send-options-entity.dto'; + +export class ActionSendOptions { + @ApiPropertyOptional({ description: 'Main entity options', nullable: true, type: ActionSendOptionsValue }) + @IsOptional() + main?: ActionSendOptionsValue | null; + + @ApiPropertyOptional({ + description: 'Contacts of entity options', + nullable: true, + type: ActionSendOptionsEntity, + }) + @IsOptional() + contact?: ActionSendOptionsEntity | null; + + @ApiPropertyOptional({ + description: 'Companies of entity options', + nullable: true, + type: ActionSendOptionsEntity, + }) + @IsOptional() + company?: ActionSendOptionsEntity | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/action-task-create-settings.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/action-task-create-settings.dto.ts new file mode 100644 index 0000000..8272ee3 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/action-task-create-settings.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ActionsSettings } from '../../../common'; +import { DeadlineType } from '../../enums'; + +export class ActionTaskCreateSettings extends ActionsSettings { + @ApiPropertyOptional({ description: 'User ID responsible for the task', nullable: true }) + @IsOptional() + @IsNumber() + responsibleUserId?: number | null; + + @ApiProperty({ description: 'Title of the task' }) + @IsString() + title: string; + + @ApiProperty({ description: 'Text of the task' }) + @IsString() + text: string; + + @ApiProperty({ description: 'Deadline type', enum: DeadlineType }) + @IsEnum(DeadlineType) + deadlineType: DeadlineType; + + @ApiPropertyOptional({ description: 'Deadline time in seconds', nullable: true }) + @IsOptional() + @IsNumber() + deadlineTime?: number | null; + + @ApiPropertyOptional({ description: 'Defer start time in seconds', nullable: true }) + @IsOptional() + @IsNumber() + deferStart?: number | null; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/actions/index.ts b/backend/src/modules/automation/automation-entity-type/dto/actions/index.ts new file mode 100644 index 0000000..6be57cf --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/actions/index.ts @@ -0,0 +1,12 @@ +export * from './action-activity-create-settings.dto'; +export * from './action-chat-send-amwork-settings.dto'; +export * from './action-chat-send-settings.dto'; +export * from './action-email-send-settings.dto'; +export * from './action-entity-create-settings.dto'; +export * from './action-entity-linked-stage-change-settings.dto'; +export * from './action-entity-responsible-change-settings.dto'; +export * from './action-entity-stage-change-settings.dto'; +export * from './action-send-options-entity.dto'; +export * from './action-send-options-value.dto'; +export * from './action-send-options.dto'; +export * from './action-task-create-settings.dto'; diff --git a/backend/src/modules/automation/automation-entity-type/dto/automation-entity-type-filter.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/automation-entity-type-filter.dto.ts new file mode 100644 index 0000000..68330b5 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/automation-entity-type-filter.dto.ts @@ -0,0 +1,24 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; + +export class AutomationEntityTypeFilterDto { + @ApiPropertyOptional({ description: 'EntityType ID' }) + @IsOptional() + @IsNumber() + entityTypeId?: number; + + @ApiPropertyOptional({ description: 'Board ID' }) + @IsOptional() + @IsNumber() + boardId?: number; + + @ApiPropertyOptional({ description: 'Stage ID' }) + @IsOptional() + @IsNumber() + stageId?: number; + + @ApiPropertyOptional({ description: 'Is the automation active?' }) + @IsOptional() + @IsBoolean() + isActive?: boolean; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/automation-entity-type.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/automation-entity-type.dto.ts new file mode 100644 index 0000000..80810d4 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/automation-entity-type.dto.ts @@ -0,0 +1,84 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { EntityTypeTrigger } from '../enums'; +import { EntityTypeCondition } from './entity-type-condition.dto'; +import { EntityTypeAction } from './entity-type-action.dto'; + +export class AutomationEntityTypeDto { + @ApiProperty({ description: 'EntityType automation ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Date of creation' }) + @IsString() + createdAt: string; + + @ApiProperty({ description: 'User ID who created the automation' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'Name of the automation' }) + @IsString() + name: string; + + @ApiProperty({ description: 'EntityType ID', nullable: true }) + @IsNumber() + entityTypeId: number | null; + + @ApiPropertyOptional({ description: 'Board ID', nullable: true }) + @IsOptional() + @IsNumber() + boardId?: number | null; + + @ApiPropertyOptional({ description: 'Stage ID', nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; + + @ApiProperty({ description: 'Is the automation active?' }) + @IsBoolean() + isActive: boolean; + + @ApiProperty({ description: 'Triggers for the automation', enum: EntityTypeTrigger, isArray: true }) + @IsArray() + triggers: EntityTypeTrigger[]; + + @ApiPropertyOptional({ description: 'Conditions for the automation', type: EntityTypeCondition, nullable: true }) + @IsOptional() + @IsObject() + conditions?: EntityTypeCondition | null; + + @ApiPropertyOptional({ description: 'Actions for the automation', type: [EntityTypeAction], nullable: true }) + @IsOptional() + @IsArray() + @Type(() => EntityTypeAction) + actions?: EntityTypeAction[] | null; + + constructor({ + id, + createdAt, + createdBy, + name, + entityTypeId, + boardId, + stageId, + isActive, + triggers, + conditions, + actions, + }: AutomationEntityTypeDto) { + this.id = id; + this.createdAt = createdAt; + this.createdBy = createdBy; + this.name = name; + this.entityTypeId = entityTypeId; + this.boardId = boardId; + this.stageId = stageId; + this.isActive = isActive; + this.triggers = triggers; + this.conditions = conditions; + this.actions = actions; + } +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/create-automation-entity-type.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/create-automation-entity-type.dto.ts new file mode 100644 index 0000000..10cfb2a --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/create-automation-entity-type.dto.ts @@ -0,0 +1,20 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +import { AutomationEntityTypeDto } from './automation-entity-type.dto'; + +export class CreateAutomationEntityTypeDto extends PickType(AutomationEntityTypeDto, [ + 'name', + 'entityTypeId', + 'boardId', + 'stageId', + 'isActive', + 'triggers', + 'conditions', + 'actions', +] as const) { + @ApiPropertyOptional({ description: 'Apply automation for all entities suitable for conditions' }) + @IsOptional() + @IsBoolean() + applyImmediately?: boolean; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/entity-type-action.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/entity-type-action.dto.ts new file mode 100644 index 0000000..69b63fd --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/entity-type-action.dto.ts @@ -0,0 +1,65 @@ +import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsObject, IsOptional } from 'class-validator'; + +import { ActionHttpCallSettings } from '../../automation-http/dto'; + +import { EntityTypeActionType } from '../enums'; +import { + ActionActivityCreateSettings, + ActionChatSendAmworkSettings, + ActionChatSendSettings, + ActionEmailSendSettings, + ActionEntityCreateSettings, + ActionEntityResponsibleChangeSettings, + ActionEntityStageChangeSettings, + ActionTaskCreateSettings, +} from './actions'; + +@ApiExtraModels(ActionActivityCreateSettings) +@ApiExtraModels(ActionChatSendAmworkSettings) +@ApiExtraModels(ActionChatSendSettings) +@ApiExtraModels(ActionEmailSendSettings) +@ApiExtraModels(ActionEntityCreateSettings) +@ApiExtraModels(ActionEntityResponsibleChangeSettings) +@ApiExtraModels(ActionEntityStageChangeSettings) +@ApiExtraModels(ActionHttpCallSettings) +@ApiExtraModels(ActionTaskCreateSettings) +export class EntityTypeAction { + @ApiPropertyOptional({ description: 'Delay in seconds before executing the action', nullable: true }) + @IsOptional() + @IsNumber() + delay?: number | null; + + @ApiProperty({ description: 'Type of the action', enum: EntityTypeActionType }) + @IsEnum(EntityTypeActionType) + type: EntityTypeActionType; + + @ApiProperty({ + description: 'Settings for the action', + type: 'array', + items: { + oneOf: [ + { $ref: getSchemaPath(ActionActivityCreateSettings) }, + { $ref: getSchemaPath(ActionChatSendAmworkSettings) }, + { $ref: getSchemaPath(ActionChatSendSettings) }, + { $ref: getSchemaPath(ActionEmailSendSettings) }, + { $ref: getSchemaPath(ActionEntityCreateSettings) }, + { $ref: getSchemaPath(ActionEntityResponsibleChangeSettings) }, + { $ref: getSchemaPath(ActionEntityStageChangeSettings) }, + { $ref: getSchemaPath(ActionHttpCallSettings) }, + { $ref: getSchemaPath(ActionTaskCreateSettings) }, + ], + }, + }) + @IsObject() + settings: + | ActionActivityCreateSettings + | ActionChatSendAmworkSettings + | ActionChatSendSettings + | ActionEmailSendSettings + | ActionEntityCreateSettings + | ActionEntityResponsibleChangeSettings + | ActionEntityStageChangeSettings + | ActionHttpCallSettings + | ActionTaskCreateSettings; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/entity-type-condition.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/entity-type-condition.dto.ts new file mode 100644 index 0000000..322a804 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/entity-type-condition.dto.ts @@ -0,0 +1,19 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { AutomationFieldCondition } from '../../common'; + +export class EntityTypeCondition { + @ApiPropertyOptional({ description: 'List of owner user ids', type: [Number] }) + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + ownerIds?: number[]; + + @ApiPropertyOptional({ description: 'List of field conditions', type: [AutomationFieldCondition] }) + @IsOptional() + @IsArray() + @Type(() => AutomationFieldCondition) + fields?: AutomationFieldCondition[]; +} diff --git a/backend/src/modules/automation/automation-entity-type/dto/index.ts b/backend/src/modules/automation/automation-entity-type/dto/index.ts new file mode 100644 index 0000000..6929d98 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/index.ts @@ -0,0 +1,7 @@ +export * from './actions'; +export * from './automation-entity-type-filter.dto'; +export * from './automation-entity-type.dto'; +export * from './create-automation-entity-type.dto'; +export * from './entity-type-action.dto'; +export * from './entity-type-condition.dto'; +export * from './update-automation-entity-type.dto'; diff --git a/backend/src/modules/automation/automation-entity-type/dto/update-automation-entity-type.dto.ts b/backend/src/modules/automation/automation-entity-type/dto/update-automation-entity-type.dto.ts new file mode 100644 index 0000000..7bd3a4d --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/dto/update-automation-entity-type.dto.ts @@ -0,0 +1,22 @@ +import { ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +import { AutomationEntityTypeDto } from './automation-entity-type.dto'; + +export class UpdateAutomationEntityTypeDto extends PartialType( + PickType(AutomationEntityTypeDto, [ + 'name', + 'entityTypeId', + 'boardId', + 'stageId', + 'isActive', + 'triggers', + 'conditions', + 'actions', + ] as const), +) { + @ApiPropertyOptional({ description: 'Apply automation for all entities suitable for conditions' }) + @IsOptional() + @IsBoolean() + applyImmediately?: boolean; +} diff --git a/backend/src/modules/automation/automation-entity-type/entities/automation-entity-type.entity.ts b/backend/src/modules/automation/automation-entity-type/entities/automation-entity-type.entity.ts new file mode 100644 index 0000000..805dc06 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/entities/automation-entity-type.entity.ts @@ -0,0 +1,120 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { type EntityTypeTrigger } from '../enums'; +import { + CreateAutomationEntityTypeDto, + UpdateAutomationEntityTypeDto, + AutomationEntityTypeDto, + EntityTypeCondition, + EntityTypeAction, +} from '../dto'; + +@Entity() +export class AutomationEntityType { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column({ type: Date }) + createdAt: Date; + + @Column() + createdBy: number; + + @Column() + name: string; + + @Column({ nullable: true }) + entityTypeId: number | null; + + @Column({ nullable: true }) + boardId: number | null; + + @Column({ nullable: true }) + stageId: number | null; + + @Column({ nullable: true }) + processId: number | null; + + @Column({ type: 'simple-array' }) + triggers: EntityTypeTrigger[]; + + @Column({ type: 'jsonb', nullable: true }) + conditions: EntityTypeCondition | null; + + @Column({ type: 'jsonb', nullable: true }) + actions: EntityTypeAction[] | null; + + constructor( + accountId: number, + createdBy: number, + name: string, + entityTypeId: number | null, + boardId: number | null, + stageId: number | null, + processId: number | null, + triggers: EntityTypeTrigger[], + conditions: EntityTypeCondition | null, + actions: EntityTypeAction[] | null, + ) { + this.accountId = accountId; + this.createdBy = createdBy; + this.name = name; + this.entityTypeId = entityTypeId; + this.boardId = boardId; + this.stageId = stageId; + this.processId = processId; + this.triggers = triggers; + this.conditions = conditions; + this.actions = actions; + this.createdAt = DateUtil.now(); + } + + public get isActive(): boolean { + return !!this.processId; + } + + public static fromDto( + accountId: number, + createdBy: number, + dto: CreateAutomationEntityTypeDto, + ): AutomationEntityType { + return new AutomationEntityType( + accountId, + createdBy, + dto.name, + dto.entityTypeId, + dto.boardId, + dto.stageId, + null, + dto.triggers, + dto.conditions, + dto.actions, + ); + } + + public update(dto: UpdateAutomationEntityTypeDto & { processId?: number | null }): AutomationEntityType { + this.name = dto.name !== undefined ? dto.name : this.name; + this.entityTypeId = dto.entityTypeId !== undefined ? dto.entityTypeId : this.entityTypeId; + this.boardId = dto.boardId !== undefined ? dto.boardId : this.boardId; + this.stageId = dto.stageId !== undefined ? dto.stageId : this.stageId; + this.processId = dto.processId !== undefined ? dto.processId : this.processId; + this.triggers = dto.triggers !== undefined ? dto.triggers : this.triggers; + this.conditions = dto.conditions !== undefined ? dto.conditions : this.conditions; + this.actions = dto.actions !== undefined ? dto.actions : this.actions; + + return this; + } + + public toDto(): AutomationEntityTypeDto { + return new AutomationEntityTypeDto({ + ...this, + createdAt: this.createdAt.toISOString(), + isActive: this.isActive, + }); + } +} diff --git a/backend/src/modules/automation/automation-entity-type/entities/index.ts b/backend/src/modules/automation/automation-entity-type/entities/index.ts new file mode 100644 index 0000000..5b0b871 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/entities/index.ts @@ -0,0 +1 @@ +export * from './automation-entity-type.entity'; diff --git a/backend/src/modules/automation/automation-entity-type/enums/change-stage-type.enum.ts b/backend/src/modules/automation/automation-entity-type/enums/change-stage-type.enum.ts new file mode 100644 index 0000000..bc40626 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/enums/change-stage-type.enum.ts @@ -0,0 +1,5 @@ +export enum ChangeStageType { + Move = 'move', + CopyOriginal = 'copy_original', + CopyNew = 'copy_new', +} diff --git a/backend/src/modules/automation/automation-entity-type/enums/deadline-type.enum.ts b/backend/src/modules/automation/automation-entity-type/enums/deadline-type.enum.ts new file mode 100644 index 0000000..570909a --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/enums/deadline-type.enum.ts @@ -0,0 +1,8 @@ +export enum DeadlineType { + Immediately = 'immediately', + EndOfTheDay = 'end_of_the_day', + InOneDay = 'in_one_day', + InThreeDays = 'in_three_days', + InAWeek = 'in_a_week', + Custom = 'custom', +} diff --git a/backend/src/modules/automation/automation-entity-type/enums/entity-type-action-type.enum.ts b/backend/src/modules/automation/automation-entity-type/enums/entity-type-action-type.enum.ts new file mode 100644 index 0000000..b30e48e --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/enums/entity-type-action-type.enum.ts @@ -0,0 +1,33 @@ +export enum EntityTypeActionType { + /** + * @deprecated use new @see TaskCreate instead + */ + CreateTask = 'create_task', + /** + * @deprecated use new @see ActivityCreate instead + */ + CreateActivity = 'create_activity', + /** + * @deprecated use new @see EntityStageChange instead + */ + ChangeStage = 'change_stage', + /** + * @deprecated use new @see EmailSend instead + */ + SendEmail = 'send_email', + + EntityCreate = 'entity_create', + EntityStageChange = 'entity_stage_change', + EntityResponsibleChange = 'entity_responsible_change', + EntityLinkedStageChange = 'entity_linked_stage_change', + + ActivityCreate = 'activity_create', + TaskCreate = 'task_create', + + EmailSend = 'email_send', + + ChatSendExternal = 'chat_send_external', + ChatSendAmwork = 'chat_send_amwork', + + HttpCall = 'http_call', +} diff --git a/backend/src/modules/automation/automation-entity-type/enums/entity-type-trigger.enum.ts b/backend/src/modules/automation/automation-entity-type/enums/entity-type-trigger.enum.ts new file mode 100644 index 0000000..7bb6f49 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/enums/entity-type-trigger.enum.ts @@ -0,0 +1,5 @@ +export enum EntityTypeTrigger { + Create = 'create', + ChangeStage = 'change_stage', + ChangeOwner = 'change_responsible', +} diff --git a/backend/src/modules/automation/automation-entity-type/enums/index.ts b/backend/src/modules/automation/automation-entity-type/enums/index.ts new file mode 100644 index 0000000..e03ce87 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/enums/index.ts @@ -0,0 +1,4 @@ +export * from './change-stage-type.enum'; +export * from './deadline-type.enum'; +export * from './entity-type-action-type.enum'; +export * from './entity-type-trigger.enum'; diff --git a/backend/src/modules/automation/automation-entity-type/helpers/action.helper.ts b/backend/src/modules/automation/automation-entity-type/helpers/action.helper.ts new file mode 100644 index 0000000..b7f77da --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/helpers/action.helper.ts @@ -0,0 +1,30 @@ +import { DateUtil } from '@/common'; +import { DeadlineType } from '../enums'; + +export class ActionHelper { + public static getEndDate({ + startDate, + deadlineType, + deadlineTime, + }: { + startDate?: Date; + deadlineType: DeadlineType; + deadlineTime: number | null; + }): Date { + const from = startDate ?? DateUtil.now(); + switch (deadlineType) { + case DeadlineType.Immediately: + return from; + case DeadlineType.EndOfTheDay: + return DateUtil.endOf(from, 'day'); + case DeadlineType.InOneDay: + return DateUtil.add(from, { days: 1 }); + case DeadlineType.InThreeDays: + return DateUtil.add(from, { days: 3 }); + case DeadlineType.InAWeek: + return DateUtil.endOf(from, 'week'); + case DeadlineType.Custom: + return DateUtil.add(from, { seconds: deadlineTime }); + } + } +} diff --git a/backend/src/modules/automation/automation-entity-type/helpers/index.ts b/backend/src/modules/automation/automation-entity-type/helpers/index.ts new file mode 100644 index 0000000..9431d6a --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/helpers/index.ts @@ -0,0 +1 @@ +export * from './action.helper'; diff --git a/backend/src/modules/automation/automation-entity-type/index.ts b/backend/src/modules/automation/automation-entity-type/index.ts new file mode 100644 index 0000000..b79b989 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/index.ts @@ -0,0 +1,7 @@ +export * from './automation-entity-type.controller'; +export * from './automation-entity-type.handler'; +export * from './automation-entity-type.service'; +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './helpers'; diff --git a/backend/src/modules/automation/automation-entity-type/templates/simple_automation.bpmn.template b/backend/src/modules/automation/automation-entity-type/templates/simple_automation.bpmn.template new file mode 100644 index 0000000..e9e6ff6 --- /dev/null +++ b/backend/src/modules/automation/automation-entity-type/templates/simple_automation.bpmn.template @@ -0,0 +1,181 @@ + + + + + Flow_ProcessStart + + + {{#if events.create}} + + Flow_OnCreated + + + + {{/if}} + {{#if events.changeStage}} + + Flow_OnStageChanged + + + + {{/if}} + {{#if events.changeOwner}} + + Flow_OnOwnerChanged + + + + {{/if}} + + {{#if events.create}} + Flow_OnCreated + {{/if}} + {{#if events.changeStage}} + Flow_OnStageChanged + {{/if}} + {{#if events.changeOwner}} + Flow_OnOwnerChanged + {{/if}} + Flow_ConditionCorrect + Flow_ConditionNotCorrect + + + ={{{conditions}}} + + + Flow_ConditionCorrect + Flow_AfterDelay + + {{action.delay}} + + + + + + + + + + + Flow_AfterDelay + Flow_ServiceTaskCompleted + + + + + Flow_ConditionNotCorrect + Flow_ServiceTaskCompleted + + + {{#if events.create}} + + {{/if}} + {{#if events.changeStage}} + + {{/if}} + {{#if events.changeOwner}} + + {{/if}} + + + + + + + + + + + + + + {{#if events.create}} + + + + + + + + + + + {{/if}} + {{#if events.changeStage}} + + + + + + + + + + + + {{/if}} + {{#if events.changeOwner}} + + + + + + + + + + + + {{/if}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/modules/automation/automation-http/automation-http.controller.ts b/backend/src/modules/automation/automation-http/automation-http.controller.ts new file mode 100644 index 0000000..623ca1b --- /dev/null +++ b/backend/src/modules/automation/automation-http/automation-http.controller.ts @@ -0,0 +1,25 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { ProcessDataDto } from './dto'; +import { AutomationHttpService } from './automation-http.service'; + +@ApiExcludeController(true) +@Controller('http') +export class AutomationHttpController { + constructor(private readonly service: AutomationHttpService) {} + + @JwtAuthorized({ access: { adminOnly: true } }) + @Post('process') + async processAutomation(@CurrentAuth() { accountId }: AuthData, @Body() dto: ProcessDataDto) { + return this.service.processAutomation({ + accountId, + entityId: dto.entityId, + entityStageId: dto.entityStageId, + data: dto.data, + settings: dto.settings, + }); + } +} diff --git a/backend/src/modules/automation/automation-http/automation-http.handler.ts b/backend/src/modules/automation/automation-http/automation-http.handler.ts new file mode 100644 index 0000000..b8d1c76 --- /dev/null +++ b/backend/src/modules/automation/automation-http/automation-http.handler.ts @@ -0,0 +1,46 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { AutomationJob, OnAutomationJob } from '../common'; + +import { ActionHttpCallSettings } from './dto'; +import { HttpActionType } from './enums'; +import { AutomationHttpService } from './automation-http.service'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; +} +interface HttpCallVariables { + accountId: number; + entity?: EntityVariables | null; + httpSettings?: ActionHttpCallSettings | null; +} + +@Injectable() +export class AutomationHttpHandler { + private readonly logger = new Logger(AutomationHttpHandler.name); + constructor(private readonly service: AutomationHttpService) {} + + @OnAutomationJob(HttpActionType.HttpCall) + async handleHttpCallJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.httpSettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + const { accountId, entity, httpSettings } = job.variables; + try { + const result = await this.service.processAutomation({ + accountId, + entityId: entity.id, + entityStageId: entity.stageId, + data: { ...job.variables, httpSettings: undefined }, + settings: httpSettings, + }); + return { variables: { ...job.variables, ...(result ?? {}) } }; + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + return { variables: job.variables }; + } + } +} diff --git a/backend/src/modules/automation/automation-http/automation-http.service.ts b/backend/src/modules/automation/automation-http/automation-http.service.ts new file mode 100644 index 0000000..a530f76 --- /dev/null +++ b/backend/src/modules/automation/automation-http/automation-http.service.ts @@ -0,0 +1,93 @@ +import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { lastValueFrom } from 'rxjs'; +import Handlebars from 'handlebars'; + +import { withTimeout } from '@/common'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; +import { DocumentGenerationService } from '@/modules/documents/document-generation/document-generation.service'; + +import { ActionHttpCallSettings } from './dto'; + +const HttpCallTimeout = 5000; + +@Injectable() +export class AutomationHttpService { + private readonly logger = new Logger(AutomationHttpService.name); + constructor( + private readonly httpService: HttpService, + private readonly entityInfoService: EntityInfoService, + @Inject(forwardRef(() => DocumentGenerationService)) + private readonly documentGenerationService: DocumentGenerationService, + ) {} + + async processAutomation({ + accountId, + entityId, + entityStageId, + data, + settings, + }: { + accountId: number; + entityId: number; + entityStageId: number | null | undefined; + data?: unknown | null; + settings: ActionHttpCallSettings; + }): Promise { + const entity = await this.entityInfoService.findOne({ accountId, entityId }); + if (entity && (!entity.stageId || settings.allowAnyStage || entity.stageId === entityStageId)) { + const entityData = await this.documentGenerationService.getDataForGeneration({ accountId, entityId: entity.id }); + const url = Handlebars.compile(settings.url)(entityData); + const headers = this.applyTemplate({ params: settings.headers, data: entityData }); + const params = this.applyTemplate({ params: settings.params, data: entityData }); + try { + const response = await withTimeout( + lastValueFrom( + this.httpService.request({ + method: settings.method, + url: url, + data: data, + headers: headers, + params: params, + }), + ), + HttpCallTimeout, + ); + return response?.data ?? {}; + } catch (error) { + this.logger.error(`Call webhook error`, (error as Error)?.stack); + return {}; + } + } + + return {}; + } + + private applyTemplate({ + params, + data, + }: { + params: Record | null | undefined; + data: object; + }): Record | null { + if (!params) return null; + + const result: Record = {}; + + const templateCache = new Map(); + for (const [key, value] of Object.entries(params)) { + if (!value || !value.includes('{{')) { + result[key] = value; + } else { + let template = templateCache.get(value); + if (!template) { + template = Handlebars.compile(value); + templateCache.set(value, template); + } + result[key] = template(data); + } + } + + return result; + } +} diff --git a/backend/src/modules/automation/automation-http/dto/action-http-call-settings.dto.ts b/backend/src/modules/automation/automation-http/dto/action-http-call-settings.dto.ts new file mode 100644 index 0000000..30aa7d6 --- /dev/null +++ b/backend/src/modules/automation/automation-http/dto/action-http-call-settings.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsObject, IsOptional, IsString } from 'class-validator'; + +import { HttpMethod } from '@/common'; +import { ActionsSettings } from '../../common'; + +export class ActionHttpCallSettings extends ActionsSettings { + @ApiProperty({ description: 'External URL to call' }) + @IsString() + url: string; + + @ApiProperty({ enum: HttpMethod, description: 'HTTP call method' }) + @IsEnum(HttpMethod) + method: HttpMethod; + + @ApiPropertyOptional({ type: Object, nullable: true, description: 'Request headers' }) + @IsOptional() + @IsObject() + headers?: Record | null; + + @ApiPropertyOptional({ type: Object, nullable: true, description: 'Request query params' }) + @IsOptional() + @IsObject() + params?: Record | null; +} diff --git a/backend/src/modules/automation/automation-http/dto/index.ts b/backend/src/modules/automation/automation-http/dto/index.ts new file mode 100644 index 0000000..df3d0d1 --- /dev/null +++ b/backend/src/modules/automation/automation-http/dto/index.ts @@ -0,0 +1,2 @@ +export * from './action-http-call-settings.dto'; +export * from './process-data.dto'; diff --git a/backend/src/modules/automation/automation-http/dto/process-data.dto.ts b/backend/src/modules/automation/automation-http/dto/process-data.dto.ts new file mode 100644 index 0000000..453e9c2 --- /dev/null +++ b/backend/src/modules/automation/automation-http/dto/process-data.dto.ts @@ -0,0 +1,8 @@ +import { ActionHttpCallSettings } from './action-http-call-settings.dto'; + +export class ProcessDataDto { + entityId: number; + entityStageId?: number; + data?: unknown; + settings: ActionHttpCallSettings; +} diff --git a/backend/src/modules/automation/automation-http/enums/http-action-type.enum.ts b/backend/src/modules/automation/automation-http/enums/http-action-type.enum.ts new file mode 100644 index 0000000..d06b3b7 --- /dev/null +++ b/backend/src/modules/automation/automation-http/enums/http-action-type.enum.ts @@ -0,0 +1,3 @@ +export enum HttpActionType { + HttpCall = 'http_call', +} diff --git a/backend/src/modules/automation/automation-http/enums/index.ts b/backend/src/modules/automation/automation-http/enums/index.ts new file mode 100644 index 0000000..8d0e020 --- /dev/null +++ b/backend/src/modules/automation/automation-http/enums/index.ts @@ -0,0 +1 @@ +export * from './http-action-type.enum'; diff --git a/backend/src/modules/automation/automation-http/index.ts b/backend/src/modules/automation/automation-http/index.ts new file mode 100644 index 0000000..14daaf8 --- /dev/null +++ b/backend/src/modules/automation/automation-http/index.ts @@ -0,0 +1,5 @@ +export * from './automation-http.controller'; +export * from './automation-http.handler'; +export * from './automation-http.service'; +export * from './dto'; +export * from './enums'; diff --git a/backend/src/modules/automation/automation-process/automation-process.controller.ts b/backend/src/modules/automation/automation-process/automation-process.controller.ts new file mode 100644 index 0000000..f66f4eb --- /dev/null +++ b/backend/src/modules/automation/automation-process/automation-process.controller.ts @@ -0,0 +1,91 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { + ApiBody, + ApiCreatedResponse, + ApiExcludeEndpoint, + ApiOkResponse, + ApiOperation, + ApiParam, + ApiTags, +} from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { + AutomationProcessDto, + AutomationProcessFilterDto, + CreateAutomationProcessDto, + UpdateAutomationProcessDto, +} from './dto'; +import { AutomationProcessService } from './automation-process.service'; + +@ApiTags('automation/processes') +@Controller('processes') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class AutomationProcessController { + constructor(private readonly service: AutomationProcessService) {} + + @ApiOperation({ summary: 'Create automation process', description: 'Create automation process' }) + @ApiBody({ description: 'Data for creating automation process', type: CreateAutomationProcessDto }) + @ApiCreatedResponse({ description: 'Automation process', type: AutomationProcessDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateAutomationProcessDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Get automation processes', description: 'Get automation processes' }) + @ApiOkResponse({ description: 'Automation processes', type: [AutomationProcessDto] }) + @Get() + async findMany(@CurrentAuth() { accountId }: AuthData, @Query() filter: AutomationProcessFilterDto) { + return this.service.findMany({ + accountId, + ...filter, + isReadonly: filter.isReadonly ? filter.isReadonly === 'true' : undefined, + }); + } + + @ApiOperation({ summary: 'Get automation process', description: 'Get automation process' }) + @ApiParam({ name: 'processId', description: 'Automation process ID' }) + @ApiOkResponse({ description: 'Automation process', type: AutomationProcessDto }) + @Get(':processId') + async findOne(@CurrentAuth() { accountId }: AuthData, @Param('processId', ParseIntPipe) processId: number) { + return this.service.findOne({ accountId, processId }); + } + + @ApiOperation({ summary: 'Update automation process', description: 'Update automation process' }) + @ApiParam({ name: 'processId', description: 'Automation process ID' }) + @ApiBody({ description: 'Data for updating automation process', type: UpdateAutomationProcessDto }) + @ApiOkResponse({ description: 'Automation process', type: AutomationProcessDto }) + @Patch(':processId') + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('processId', ParseIntPipe) processId: number, + @Body() dto: UpdateAutomationProcessDto, + ) { + return this.service.update({ accountId, processId, dto }); + } + + @ApiOperation({ summary: 'Delete automation process', description: 'Delete automation process' }) + @ApiParam({ name: 'processId', description: 'Automation process ID' }) + @ApiOkResponse({ description: 'Deleted automation process ID', type: Number }) + @Delete(':processId') + async delete(@CurrentAuth() { accountId }: AuthData, @Param('processId', ParseIntPipe) processId: number) { + return this.service.delete({ accountId, processId }); + } + + @ApiExcludeEndpoint(true) + @Post('clean-unused') + async cleanUnused() { + return this.service.cleanUnused(); + } + + @ApiExcludeEndpoint(true) + @Post('clean-unlinked') + async cleanUnlinked() { + return this.service.cleanUnlinked(); + } +} diff --git a/backend/src/modules/automation/automation-process/automation-process.handler.ts b/backend/src/modules/automation/automation-process/automation-process.handler.ts new file mode 100644 index 0000000..d3da5c3 --- /dev/null +++ b/backend/src/modules/automation/automation-process/automation-process.handler.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { JSONDoc } from '@camunda8/sdk/dist/zeebe/types'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; + +import { AutomationEventType, ProcessStartEvent, SendMessageEvent, SendSignalEvent } from '../common'; +import { AutomationProcessService } from './automation-process.service'; + +@Injectable() +export class AutomationProcessHandler { + constructor(private readonly service: AutomationProcessService) {} + + @OnEvent(AutomationEventType.SendSignal, { async: true }) + public async handleSendSignal(event: SendSignalEvent) { + await this.service.sendSignal({ accountId: event.accountId, signal: event.signal }); + } + + @OnEvent(AutomationEventType.SendMessage, { async: true }) + public async handleSendMessage(event: SendMessageEvent) { + await this.service.sendMessage({ accountId: event.accountId, message: event.message }); + } + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + if (event.newUserId) { + await this.service.changeOwner({ + accountId: event.accountId, + currentUserId: event.userId, + newUserId: event.newUserId, + }); + } else { + await this.service.deleteMany({ accountId: event.accountId, createdBy: event.userId }); + } + } + + @OnEvent(AutomationEventType.ProcessStart, { async: true }) + public async handleProcessStart(event: ProcessStartEvent) { + await this.service.processStart({ + accountId: event.accountId, + processId: event.processId, + variables: event.variables, + }); + } +} diff --git a/backend/src/modules/automation/automation-process/automation-process.service.ts b/backend/src/modules/automation/automation-process/automation-process.service.ts new file mode 100644 index 0000000..193be21 --- /dev/null +++ b/backend/src/modules/automation/automation-process/automation-process.service.ts @@ -0,0 +1,249 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { JSONDoc } from '@camunda8/sdk/dist/zeebe/types'; + +import { NotFoundError } from '@/common'; + +import { AutomationProcessType, Message, Signal } from '../common'; +import { AutomationCoreService } from '../automation-core'; + +import { CreateAutomationProcessDto, UpdateAutomationProcessDto } from './dto'; +import { AutomationProcess } from './entities'; +import { ReadonlyProcess } from './types'; +import { AutomationProcessError } from './errors'; + +interface FindFilter { + accountId: number; + processId?: number; + createdBy?: number; + name?: string; + type?: AutomationProcessType; + objectId?: number; + isReadonly?: boolean; +} + +@Injectable() +export class AutomationProcessService { + private readonly logger = new Logger(AutomationProcessService.name); + constructor( + @InjectRepository(AutomationProcess) + private readonly repository: Repository, + private readonly coreService: AutomationCoreService, + ) {} + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateAutomationProcessDto & ReadonlyProcess; + }): Promise { + const process = await this.repository.save(AutomationProcess.fromDto({ accountId, userId, dto })); + + return dto.isActive ? this.activate(process) : process; + } + + async findOne(filter: FindFilter): Promise { + return this.createQb(filter).addSelect('ap.bpmn_file', 'ap_bpmn_file').getOne(); + } + + async findMany(filter: FindFilter): Promise { + return this.createQb(filter).orderBy('ap.created_at').getMany(); + } + + async getCount(filter: FindFilter): Promise { + return this.createQb(filter).getCount(); + } + + async update({ + accountId, + processId, + dto, + }: { + accountId: number; + processId: number; + dto: UpdateAutomationProcessDto & ReadonlyProcess; + }): Promise { + const process = await this.findOne({ accountId, processId }); + if (process.isReadonly) { + throw new BadRequestException('AutomationProcess is readonly'); + } + if (!process) { + throw NotFoundError.withId(AutomationProcess, processId); + } + + const wasActive = process.isActive; + if (process.isActive) { + await this.deactivate(process); + } + this.repository.save(process.update(dto)); + + if (dto.isActive || (dto.isActive === undefined && wasActive)) { + await this.activate(process); + } + + return process; + } + + async changeOwner({ + accountId, + currentUserId, + newUserId, + }: { + accountId: number; + currentUserId: number; + newUserId: number; + }) { + await this.repository.update({ accountId, createdBy: currentUserId }, { createdBy: newUserId }); + } + + async deleteMany(filter: FindFilter) { + const processes = await this.findMany(filter); + for (const process of processes) { + await this.delete({ accountId: process.accountId, processId: process.id }); + } + } + + async delete({ accountId, processId }: { accountId: number; processId: number }): Promise { + const process = await this.findOne({ accountId, processId }); + if (!process) { + throw NotFoundError.withId(AutomationProcess, processId); + } + + if (process.isActive) { + await this.deactivate(process); + } + + await this.repository.delete(process.id); + return process.id; + } + + async cleanUnused() { + const processes = await this.coreService.listProcessDefinitions(); + for (const process of processes) { + const localProcess = await this.repository.findOneBy({ bpmnProcessId: process.bpmnProcessId }); + const versions = [...process.versions].sort((a, b) => a.version - b.version); + if (localProcess) { + versions.pop(); + } + for (const version of versions) { + const deleted = await this.coreService.deleteProcess(version.key); + this.logger.debug( + // eslint-disable-next-line max-len + `Clean bpmnProcessId: ${process.bpmnProcessId}, version: ${version.version}, key: ${version.key}. Result: ${deleted}`, + ); + } + } + } + + async cleanUnlinked(): Promise { + const processes = await this.repository + .createQueryBuilder('ap') + .leftJoin('automation_entity_type', 'aet', 'aet.process_id = ap.id') + .where('ap.is_readonly = true') + .andWhere('ap.resource_key is not null') + .andWhere('aet.id is null') + .getMany(); + + let count = 0; + for (const process of processes) { + const deleted = await this.coreService.deleteProcess(process.resourceKey); + if (deleted) { + await this.repository.delete(process.id); + count++; + } + this.logger.debug( + `Clean bpmnProcessId: ${process.bpmnProcessId}, key: ${process.resourceKey}. Result: ${deleted}`, + ); + } + + return count; + } + + async sendMessage({ accountId, message }: { accountId: number; message: Message }) { + if ((await this.getCount({ accountId })) > 0) { + this.coreService.sendMessage(message); + } + } + + async sendSignal({ accountId, signal }: { accountId: number; signal: Signal }) { + if ((await this.getCount({ accountId })) > 0) { + this.coreService.sendSignal(signal); + } + } + + async processStart({ + accountId, + processId, + variables, + }: { + accountId: number; + processId: number; + variables: V; + }) { + const process = await this.findOne({ accountId, processId }); + if (process?.bpmnProcessId) { + this.coreService.startProcess({ bpmnProcessId: process.bpmnProcessId, variables }); + } + } + + private createQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('ap') + .where('ap.account_id = :accountId', { accountId: filter.accountId }); + + if (filter.processId) { + qb.andWhere('ap.id = :id', { id: filter.processId }); + } + if (filter.createdBy) { + qb.andWhere('ap.created_by = :createdBy', { createdBy: filter.createdBy }); + } + if (filter.name) { + qb.andWhere('ap.name ilike :name', { name: `%${filter.name}%` }); + } + if (filter.type) { + qb.andWhere('ap.type = :type', { type: filter.type }); + } + if (filter.objectId) { + qb.andWhere('ap.object_id = :objectId', { objectId: filter.objectId }); + } + if (filter.isReadonly !== undefined) { + qb.andWhere('ap.is_readonly = :isReadonly', { isReadonly: filter.isReadonly }); + } + + return qb; + } + + private async activate(process: AutomationProcess): Promise { + if (process.bpmnFile === null) { + throw new AutomationProcessError({ processId: process.id }); + } + const processMeta = await this.coreService.deployProcess( + `[${process.accountId}]_${process.id}.bpmn`, + Buffer.from(process.bpmnFile), + ); + + if (processMeta) { + await this.repository.save( + process.update({ resourceKey: processMeta.processDefinitionKey, bpmnProcessId: processMeta.bpmnProcessId }), + ); + return process; + } else { + throw new AutomationProcessError({ processId: process.id }); + } + } + + private async deactivate(process: AutomationProcess): Promise { + const deleted = await this.coreService.deleteProcess(process.resourceKey); + + if (deleted) { + await this.repository.save(process.update({ resourceKey: null, bpmnProcessId: null })); + return process; + } else { + throw new AutomationProcessError({ processId: process.id }); + } + } +} diff --git a/backend/src/modules/automation/automation-process/dto/automation-process-filter.dto.ts b/backend/src/modules/automation/automation-process/dto/automation-process-filter.dto.ts new file mode 100644 index 0000000..61c8dfa --- /dev/null +++ b/backend/src/modules/automation/automation-process/dto/automation-process-filter.dto.ts @@ -0,0 +1,31 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { AutomationProcessType } from '../../common'; + +export class AutomationProcessFilterDto { + @ApiPropertyOptional({ description: 'User ID of the creator' }) + @IsOptional() + @IsNumber() + createdBy?: number; + + @ApiPropertyOptional({ description: 'Name of the process' }) + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional({ enum: AutomationProcessType, description: 'Type of the process' }) + @IsOptional() + @IsEnum(AutomationProcessType) + type?: AutomationProcessType; + + @ApiPropertyOptional({ description: 'Object ID associated the process' }) + @IsOptional() + @IsNumber() + objectId?: number; + + @ApiPropertyOptional({ description: 'Flag indicating whether the process is read-only' }) + @IsOptional() + @IsString() + isReadonly?: string; +} diff --git a/backend/src/modules/automation/automation-process/dto/automation-process.dto.ts b/backend/src/modules/automation/automation-process/dto/automation-process.dto.ts new file mode 100644 index 0000000..803a176 --- /dev/null +++ b/backend/src/modules/automation/automation-process/dto/automation-process.dto.ts @@ -0,0 +1,44 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { AutomationProcessType } from '../../common'; + +export class AutomationProcessDto { + @ApiProperty({ description: 'Automation process ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Date of creation' }) + @IsString() + createdAt: string; + + @ApiProperty({ description: 'User ID of the creator' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'Name of the process' }) + @IsString() + name: string; + + @ApiProperty({ enum: AutomationProcessType, description: 'Type of the process' }) + @IsEnum(AutomationProcessType) + type: AutomationProcessType; + + @ApiPropertyOptional({ description: 'Object ID associated the process', nullable: true }) + @IsOptional() + @IsNumber() + objectId?: number | null; + + @ApiProperty({ description: 'Readonly status of the process' }) + @IsBoolean() + isReadonly: boolean; + + @ApiProperty({ description: 'Activity of the process' }) + @IsBoolean() + isActive: boolean; + + @ApiPropertyOptional({ description: 'Content of the process in BPMN format', nullable: true }) + @IsOptional() + @IsString() + bpmnFile?: string | null; +} diff --git a/backend/src/modules/automation/automation-process/dto/create-automation-process.dto.ts b/backend/src/modules/automation/automation-process/dto/create-automation-process.dto.ts new file mode 100644 index 0000000..f8394de --- /dev/null +++ b/backend/src/modules/automation/automation-process/dto/create-automation-process.dto.ts @@ -0,0 +1,11 @@ +import { PickType } from '@nestjs/swagger'; + +import { AutomationProcessDto } from './automation-process.dto'; + +export class CreateAutomationProcessDto extends PickType(AutomationProcessDto, [ + 'name', + 'type', + 'objectId', + 'isActive', + 'bpmnFile', +] as const) {} diff --git a/backend/src/modules/automation/automation-process/dto/index.ts b/backend/src/modules/automation/automation-process/dto/index.ts new file mode 100644 index 0000000..2bfd02f --- /dev/null +++ b/backend/src/modules/automation/automation-process/dto/index.ts @@ -0,0 +1,4 @@ +export * from './automation-process-filter.dto'; +export * from './automation-process.dto'; +export * from './create-automation-process.dto'; +export * from './update-automation-process.dto'; diff --git a/backend/src/modules/automation/automation-process/dto/update-automation-process.dto.ts b/backend/src/modules/automation/automation-process/dto/update-automation-process.dto.ts new file mode 100644 index 0000000..c836625 --- /dev/null +++ b/backend/src/modules/automation/automation-process/dto/update-automation-process.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { AutomationProcessDto } from './automation-process.dto'; + +export class UpdateAutomationProcessDto extends PartialType( + PickType(AutomationProcessDto, ['name', 'type', 'objectId', 'isActive', 'bpmnFile'] as const), +) {} diff --git a/backend/src/modules/automation/automation-process/entities/automation-process.entity.ts b/backend/src/modules/automation/automation-process/entities/automation-process.entity.ts new file mode 100644 index 0000000..6d230b7 --- /dev/null +++ b/backend/src/modules/automation/automation-process/entities/automation-process.entity.ts @@ -0,0 +1,122 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { AutomationProcessType } from '../../common'; +import { AutomationProcessDto, type CreateAutomationProcessDto, type UpdateAutomationProcessDto } from '../dto'; +import { ReadonlyProcess } from '../types'; + +interface ExternalIdentifiers { + resourceKey?: string | null; + bpmnProcessId?: string | null; +} + +@Entity() +export class AutomationProcess { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column({ type: Date }) + createdAt: Date; + + @Column() + createdBy: number; + + @Column() + name: string; + + @Column() + type: AutomationProcessType; + + @Column({ nullable: true }) + objectId: number | null; + + @Column({ default: false }) + isReadonly: boolean; + + @Column({ nullable: true, default: null }) + resourceKey: string | null; + + @Column({ nullable: true, default: null }) + bpmnProcessId: string | null; + + @Column({ nullable: true, select: false }) + bpmnFile: string | null; + + constructor( + accountId: number, + createdBy: number, + name: string, + type: AutomationProcessType, + objectId: number | null, + isReadonly: boolean, + resourceKey: string | null, + bpmnProcessId: string | null, + bpmnFile: string | null, + ) { + this.accountId = accountId; + this.createdAt = DateUtil.now(); + this.createdBy = createdBy; + this.name = name; + this.type = type; + this.objectId = objectId; + this.isReadonly = isReadonly; + this.resourceKey = resourceKey; + this.bpmnProcessId = bpmnProcessId; + this.bpmnFile = bpmnFile; + } + + public get isActive(): boolean { + return !!this.resourceKey && !!this.bpmnProcessId; + } + + public static fromDto({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateAutomationProcessDto & ExternalIdentifiers & ReadonlyProcess; + }): AutomationProcess { + return new AutomationProcess( + accountId, + userId, + dto.name, + dto.type, + dto.objectId, + dto.isReadonly, + dto.resourceKey, + dto.bpmnProcessId, + dto.bpmnFile, + ); + } + + public update(dto: UpdateAutomationProcessDto & ExternalIdentifiers & ReadonlyProcess): AutomationProcess { + this.name = dto.name !== undefined ? dto.name : this.name; + this.type = dto.type !== undefined ? dto.type : this.type; + this.objectId = dto.objectId !== undefined ? dto.objectId : this.objectId; + this.isReadonly = dto.isReadonly !== undefined ? dto.isReadonly : this.isReadonly; + this.resourceKey = dto.resourceKey !== undefined ? dto.resourceKey : this.resourceKey; + this.bpmnProcessId = dto.bpmnProcessId !== undefined ? dto.bpmnProcessId : this.bpmnProcessId; + this.bpmnFile = dto.bpmnFile !== undefined ? dto.bpmnFile : this.bpmnFile; + + return this; + } + + public toDto(): AutomationProcessDto { + return { + id: this.id, + createdAt: this.createdAt.toISOString(), + createdBy: this.createdBy, + name: this.name, + type: this.type, + objectId: this.objectId, + isReadonly: this.isReadonly, + isActive: this.isActive, + bpmnFile: this.bpmnFile, + }; + } +} diff --git a/backend/src/modules/automation/automation-process/entities/index.ts b/backend/src/modules/automation/automation-process/entities/index.ts new file mode 100644 index 0000000..a375386 --- /dev/null +++ b/backend/src/modules/automation/automation-process/entities/index.ts @@ -0,0 +1 @@ +export * from './automation-process.entity'; diff --git a/backend/src/modules/automation/automation-process/errors/automation-process.error.ts b/backend/src/modules/automation/automation-process/errors/automation-process.error.ts new file mode 100644 index 0000000..68670c1 --- /dev/null +++ b/backend/src/modules/automation/automation-process/errors/automation-process.error.ts @@ -0,0 +1,13 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class AutomationProcessError extends ServiceError { + constructor({ processId, message = 'Automation process error' }: { processId: number; message?: string }) { + super({ + errorCode: 'automation.process_error', + status: HttpStatus.BAD_REQUEST, + message, + details: { processId }, + }); + } +} diff --git a/backend/src/modules/automation/automation-process/errors/index.ts b/backend/src/modules/automation/automation-process/errors/index.ts new file mode 100644 index 0000000..819e59e --- /dev/null +++ b/backend/src/modules/automation/automation-process/errors/index.ts @@ -0,0 +1 @@ +export * from './automation-process.error'; diff --git a/backend/src/modules/automation/automation-process/index.ts b/backend/src/modules/automation/automation-process/index.ts new file mode 100644 index 0000000..d880498 --- /dev/null +++ b/backend/src/modules/automation/automation-process/index.ts @@ -0,0 +1,7 @@ +export * from './automation-process.controller'; +export * from './automation-process.handler'; +export * from './automation-process.service'; +export * from './dto'; +export * from './entities'; +export * from './errors'; +export * from './types'; diff --git a/backend/src/modules/automation/automation-process/types/index.ts b/backend/src/modules/automation/automation-process/types/index.ts new file mode 100644 index 0000000..d01d55d --- /dev/null +++ b/backend/src/modules/automation/automation-process/types/index.ts @@ -0,0 +1 @@ +export * from './readonly-process'; diff --git a/backend/src/modules/automation/automation-process/types/readonly-process.ts b/backend/src/modules/automation/automation-process/types/readonly-process.ts new file mode 100644 index 0000000..10b9b5d --- /dev/null +++ b/backend/src/modules/automation/automation-process/types/readonly-process.ts @@ -0,0 +1,3 @@ +export interface ReadonlyProcess { + isReadonly?: boolean; +} diff --git a/backend/src/modules/automation/automation-utils/automation-utils.controller.ts b/backend/src/modules/automation/automation-utils/automation-utils.controller.ts new file mode 100644 index 0000000..e2ce3f0 --- /dev/null +++ b/backend/src/modules/automation/automation-utils/automation-utils.controller.ts @@ -0,0 +1,35 @@ +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { + AutomationDelayUtil, + AutomationEntityCondition, + AutomationFieldCondition, + AutomatonConditionUtil, +} from '../common'; + +@ApiTags('automation/utils') +@Controller('util') +export class AutomationUtilController { + @ApiOperation({ summary: 'Generate FEEL conditions for entity', description: 'Generate FEEL conditions for entity' }) + @ApiOkResponse({ description: 'Conditions in FEEL', type: String }) + @Post('conditions/entity') + public async formatEntityConditions(@Body() condition: AutomationEntityCondition) { + return AutomatonConditionUtil.formatEntityCondition(condition); + } + + @ApiOperation({ summary: 'Generate FEEL conditions for fields', description: 'Generate FEEL conditions for fields' }) + @ApiOkResponse({ description: 'Conditions in FEEL', type: String }) + @Post('conditions/fields') + public async formatFieldCondition(@Body() condition: AutomationFieldCondition) { + return AutomatonConditionUtil.formatFieldCondition(condition); + } + + @ApiOperation({ summary: 'Generate delay', description: 'Generate delay for seconds in ISO 8601:Duration' }) + @ApiQuery({ name: 'seconds', type: Number, required: false, description: 'Delay in seconds' }) + @ApiOkResponse({ description: 'Delay in FEEL', type: String }) + @Get('delay') + public async formatDelay(@Query('seconds') seconds: string | undefined) { + return AutomationDelayUtil.formatSeconds(seconds ? Number(seconds) : null); + } +} diff --git a/backend/src/modules/automation/automation-utils/index.ts b/backend/src/modules/automation/automation-utils/index.ts new file mode 100644 index 0000000..29aae99 --- /dev/null +++ b/backend/src/modules/automation/automation-utils/index.ts @@ -0,0 +1 @@ +export * from './automation-utils.controller'; diff --git a/backend/src/modules/automation/automation.module.ts b/backend/src/modules/automation/automation.module.ts new file mode 100644 index 0000000..cea6f0e --- /dev/null +++ b/backend/src/modules/automation/automation.module.ts @@ -0,0 +1,54 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { DiscoveryModule } from '@nestjs/core'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { DocumentsModule } from '@/modules/documents/documents.module'; + +import automationConfig from './config/automation.config'; +import { AutomationCoreController, AutomationCoreService } from './automation-core'; +import { + AutomationProcess, + AutomationProcessController, + AutomationProcessHandler, + AutomationProcessService, +} from './automation-process'; +import { + AutomationEntityType, + AutomationEntityTypeController, + AutomationEntityTypeHandler, + AutomationEntityTypeService, +} from './automation-entity-type'; +import { AutomationUtilController } from './automation-utils'; +import { AutomationHttpController, AutomationHttpHandler, AutomationHttpService } from './automation-http'; + +@Module({ + imports: [ + ConfigModule.forFeature(automationConfig), + DiscoveryModule, + TypeOrmModule.forFeature([AutomationProcess, AutomationEntityType]), + IAMModule, + EntityInfoModule, + forwardRef(() => DocumentsModule), + ], + providers: [ + AutomationCoreService, + AutomationProcessService, + AutomationProcessHandler, + AutomationEntityTypeService, + AutomationEntityTypeHandler, + AutomationHttpService, + AutomationHttpHandler, + ], + controllers: [ + AutomationCoreController, + AutomationProcessController, + AutomationEntityTypeController, + AutomationUtilController, + AutomationHttpController, + ], + exports: [AutomationCoreService], +}) +export class AutomationModule {} diff --git a/backend/src/modules/automation/common/decorators/automation-worker.decorator.ts b/backend/src/modules/automation/common/decorators/automation-worker.decorator.ts new file mode 100644 index 0000000..217476f --- /dev/null +++ b/backend/src/modules/automation/common/decorators/automation-worker.decorator.ts @@ -0,0 +1,5 @@ +import { SetMetadata } from '@nestjs/common'; + +export const AUTOMATION_WORKER = 'AUTOMATION_WORKER'; + +export const AutomationWorker = (type: string) => SetMetadata(AUTOMATION_WORKER, type); diff --git a/backend/src/modules/automation/common/decorators/index.ts b/backend/src/modules/automation/common/decorators/index.ts new file mode 100644 index 0000000..47d9c54 --- /dev/null +++ b/backend/src/modules/automation/common/decorators/index.ts @@ -0,0 +1,2 @@ +export * from './automation-worker.decorator'; +export * from './on-automation-job.decorator'; diff --git a/backend/src/modules/automation/common/decorators/on-automation-job.decorator.ts b/backend/src/modules/automation/common/decorators/on-automation-job.decorator.ts new file mode 100644 index 0000000..e576543 --- /dev/null +++ b/backend/src/modules/automation/common/decorators/on-automation-job.decorator.ts @@ -0,0 +1,5 @@ +import { SetMetadata } from '@nestjs/common'; + +export const AUTOMATION_JOB_HANDLER = 'AUTOMATION_JOB_HANDLER'; + +export const OnAutomationJob = (type: string) => SetMetadata(AUTOMATION_JOB_HANDLER, type); diff --git a/backend/src/modules/automation/common/dto/action/action-settings.dto.ts b/backend/src/modules/automation/common/dto/action/action-settings.dto.ts new file mode 100644 index 0000000..96a0896 --- /dev/null +++ b/backend/src/modules/automation/common/dto/action/action-settings.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class ActionsSettings { + @ApiProperty({ description: 'Allow any stage', nullable: true }) + @IsOptional() + @IsBoolean() + allowAnyStage?: boolean | null; +} diff --git a/backend/src/modules/automation/common/dto/action/index.ts b/backend/src/modules/automation/common/dto/action/index.ts new file mode 100644 index 0000000..49834af --- /dev/null +++ b/backend/src/modules/automation/common/dto/action/index.ts @@ -0,0 +1 @@ +export * from './action-settings.dto'; diff --git a/backend/src/modules/automation/common/dto/condition/automation-entity-condition.dto.ts b/backend/src/modules/automation/common/dto/condition/automation-entity-condition.dto.ts new file mode 100644 index 0000000..61b944c --- /dev/null +++ b/backend/src/modules/automation/common/dto/condition/automation-entity-condition.dto.ts @@ -0,0 +1,24 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { AutomationFieldCondition } from './automation-field-condition.dto'; + +export class AutomationEntityCondition { + @ApiPropertyOptional({ description: 'Stage ID', nullable: true }) + @IsOptional() + @IsNumber() + stageId?: number | null; + + @ApiPropertyOptional({ description: 'List of owner user ids', type: [Number] }) + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + ownerIds?: number[]; + + @ApiPropertyOptional({ description: 'List of field conditions', type: [AutomationFieldCondition] }) + @IsOptional() + @IsArray() + @Type(() => AutomationFieldCondition) + fields?: AutomationFieldCondition[]; +} diff --git a/backend/src/modules/automation/common/dto/condition/automation-field-condition.dto.ts b/backend/src/modules/automation/common/dto/condition/automation-field-condition.dto.ts new file mode 100644 index 0000000..b019b96 --- /dev/null +++ b/backend/src/modules/automation/common/dto/condition/automation-field-condition.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { SimpleFilter } from '@/common'; + +export class AutomationFieldCondition extends SimpleFilter { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; +} diff --git a/backend/src/modules/automation/common/dto/condition/index.ts b/backend/src/modules/automation/common/dto/condition/index.ts new file mode 100644 index 0000000..03c8d3a --- /dev/null +++ b/backend/src/modules/automation/common/dto/condition/index.ts @@ -0,0 +1,2 @@ +export * from './automation-entity-condition.dto'; +export * from './automation-field-condition.dto'; diff --git a/backend/src/modules/automation/common/dto/index.ts b/backend/src/modules/automation/common/dto/index.ts new file mode 100644 index 0000000..d7541ad --- /dev/null +++ b/backend/src/modules/automation/common/dto/index.ts @@ -0,0 +1,2 @@ +export * from './action'; +export * from './condition'; diff --git a/backend/src/modules/automation/common/enums/automation-process-type.enum.ts b/backend/src/modules/automation/common/enums/automation-process-type.enum.ts new file mode 100644 index 0000000..6b1ddb2 --- /dev/null +++ b/backend/src/modules/automation/common/enums/automation-process-type.enum.ts @@ -0,0 +1,4 @@ +export enum AutomationProcessType { + General = 'general', + EntityType = 'entity_type', +} diff --git a/backend/src/modules/automation/common/enums/index.ts b/backend/src/modules/automation/common/enums/index.ts new file mode 100644 index 0000000..f4ae609 --- /dev/null +++ b/backend/src/modules/automation/common/enums/index.ts @@ -0,0 +1 @@ +export * from './automation-process-type.enum'; diff --git a/backend/src/modules/automation/common/events/automation-event-type.enum.ts b/backend/src/modules/automation/common/events/automation-event-type.enum.ts new file mode 100644 index 0000000..f4da864 --- /dev/null +++ b/backend/src/modules/automation/common/events/automation-event-type.enum.ts @@ -0,0 +1,6 @@ +export enum AutomationEventType { + EntityTypeApply = 'automation:entity-type:apply', + ProcessStart = 'automation:process:start', + SendSignal = 'automation:send:signal', + SendMessage = 'automation:send:message', +} diff --git a/backend/src/modules/automation/common/events/core/index.ts b/backend/src/modules/automation/common/events/core/index.ts new file mode 100644 index 0000000..9ce26d8 --- /dev/null +++ b/backend/src/modules/automation/common/events/core/index.ts @@ -0,0 +1,2 @@ +export * from './send-message.event'; +export * from './send-signal.event'; diff --git a/backend/src/modules/automation/common/events/core/send-message.event.ts b/backend/src/modules/automation/common/events/core/send-message.event.ts new file mode 100644 index 0000000..8de09e8 --- /dev/null +++ b/backend/src/modules/automation/common/events/core/send-message.event.ts @@ -0,0 +1,11 @@ +import { Message } from '../../types'; + +export class SendMessageEvent { + accountId: number; + message: Message; + + constructor({ accountId, message }: SendMessageEvent) { + this.accountId = accountId; + this.message = message; + } +} diff --git a/backend/src/modules/automation/common/events/core/send-signal.event.ts b/backend/src/modules/automation/common/events/core/send-signal.event.ts new file mode 100644 index 0000000..e78e709 --- /dev/null +++ b/backend/src/modules/automation/common/events/core/send-signal.event.ts @@ -0,0 +1,11 @@ +import { Signal } from '../../types'; + +export class SendSignalEvent { + accountId: number; + signal: Signal; + + constructor({ accountId, signal }: SendSignalEvent) { + this.accountId = accountId; + this.signal = signal; + } +} diff --git a/backend/src/modules/automation/common/events/entity-type/entity-type-apply.event.ts b/backend/src/modules/automation/common/events/entity-type/entity-type-apply.event.ts new file mode 100644 index 0000000..98cb44b --- /dev/null +++ b/backend/src/modules/automation/common/events/entity-type/entity-type-apply.event.ts @@ -0,0 +1,17 @@ +export class EntityTypeApplyEvent { + accountId: number; + automationId: number; + processId: number; + entityTypeId: number; + boardId?: number | null; + stageId?: number | null; + + constructor({ accountId, automationId, processId, entityTypeId, boardId, stageId }: EntityTypeApplyEvent) { + this.accountId = accountId; + this.automationId = automationId; + this.processId = processId; + this.entityTypeId = entityTypeId; + this.boardId = boardId; + this.stageId = stageId; + } +} diff --git a/backend/src/modules/automation/common/events/entity-type/index.ts b/backend/src/modules/automation/common/events/entity-type/index.ts new file mode 100644 index 0000000..68a6f6b --- /dev/null +++ b/backend/src/modules/automation/common/events/entity-type/index.ts @@ -0,0 +1 @@ +export * from './entity-type-apply.event'; diff --git a/backend/src/modules/automation/common/events/index.ts b/backend/src/modules/automation/common/events/index.ts new file mode 100644 index 0000000..fe47772 --- /dev/null +++ b/backend/src/modules/automation/common/events/index.ts @@ -0,0 +1,4 @@ +export * from './automation-event-type.enum'; +export * from './core'; +export * from './entity-type'; +export * from './process'; diff --git a/backend/src/modules/automation/common/events/process/index.ts b/backend/src/modules/automation/common/events/process/index.ts new file mode 100644 index 0000000..5a41c37 --- /dev/null +++ b/backend/src/modules/automation/common/events/process/index.ts @@ -0,0 +1 @@ +export * from './process-start.event'; diff --git a/backend/src/modules/automation/common/events/process/process-start.event.ts b/backend/src/modules/automation/common/events/process/process-start.event.ts new file mode 100644 index 0000000..aa967c0 --- /dev/null +++ b/backend/src/modules/automation/common/events/process/process-start.event.ts @@ -0,0 +1,13 @@ +import { JSONDoc } from '@camunda8/sdk/dist/zeebe/types'; + +export class ProcessStartEvent { + accountId: number; + processId: number; + variables: V; + + constructor({ accountId, processId, variables }: ProcessStartEvent) { + this.accountId = accountId; + this.processId = processId; + this.variables = variables; + } +} diff --git a/backend/src/modules/automation/common/index.ts b/backend/src/modules/automation/common/index.ts new file mode 100644 index 0000000..55780a3 --- /dev/null +++ b/backend/src/modules/automation/common/index.ts @@ -0,0 +1,7 @@ +export * from './decorators'; +export * from './dto'; +export * from './enums'; +export * from './events'; +export * from './interfaces'; +export * from './types'; +export * from './utils'; diff --git a/backend/src/modules/automation/common/interfaces/automation-handler.interface.ts b/backend/src/modules/automation/common/interfaces/automation-handler.interface.ts new file mode 100644 index 0000000..ef72fce --- /dev/null +++ b/backend/src/modules/automation/common/interfaces/automation-handler.interface.ts @@ -0,0 +1,17 @@ +import { IInputVariables, IOutputVariables } from '@camunda8/sdk/dist/zeebe/types'; + +export interface AutomationJob { + variables: Variables; +} + +export interface AutomationResult { + variables: Variables; +} + +export type AutomationHandler = ( + job: AutomationJob, +) => AutomationResult | Promise>; + +export interface AutomationJobHandler { + handleJob: AutomationHandler; +} diff --git a/backend/src/modules/automation/common/interfaces/index.ts b/backend/src/modules/automation/common/interfaces/index.ts new file mode 100644 index 0000000..550100c --- /dev/null +++ b/backend/src/modules/automation/common/interfaces/index.ts @@ -0,0 +1 @@ +export * from './automation-handler.interface'; diff --git a/backend/src/modules/automation/common/types/index.ts b/backend/src/modules/automation/common/types/index.ts new file mode 100644 index 0000000..eeb0ca3 --- /dev/null +++ b/backend/src/modules/automation/common/types/index.ts @@ -0,0 +1,2 @@ +export * from './message.interface'; +export * from './signal.interface'; diff --git a/backend/src/modules/automation/common/types/message.interface.ts b/backend/src/modules/automation/common/types/message.interface.ts new file mode 100644 index 0000000..eedadd7 --- /dev/null +++ b/backend/src/modules/automation/common/types/message.interface.ts @@ -0,0 +1,5 @@ +import { Signal } from './signal.interface'; + +export interface Message extends Signal { + correlationKey?: string; +} diff --git a/backend/src/modules/automation/common/types/signal.interface.ts b/backend/src/modules/automation/common/types/signal.interface.ts new file mode 100644 index 0000000..0fbf3de --- /dev/null +++ b/backend/src/modules/automation/common/types/signal.interface.ts @@ -0,0 +1,7 @@ +import { IInputVariables } from '@camunda8/sdk/dist/zeebe/types'; + +type SignalName = string | number; +export interface Signal { + name: SignalName | SignalName[]; + variables?: IInputVariables; +} diff --git a/backend/src/modules/automation/common/utils/automation-condition.util.ts b/backend/src/modules/automation/common/utils/automation-condition.util.ts new file mode 100644 index 0000000..5348784 --- /dev/null +++ b/backend/src/modules/automation/common/utils/automation-condition.util.ts @@ -0,0 +1,147 @@ +import { + BooleanFilter, + DateFilter, + ExistsFilter, + ExistsFilterType, + NumberFilter, + SelectFilter, + SimpleFilterType, + StringFilter, + StringFilterType, +} from '@/common'; + +import { AutomationEntityCondition, AutomationFieldCondition } from '../dto'; + +// const EntityFieldPrefix = 'entity.fields.f_'; +const Names = { + entity: 'entity', + stage: () => `${Names.entity}.stageId`, + owner: () => `${Names.entity}.responsibleUserId`, + fields: () => `${Names.entity}.fields`, + field: (fieldId: number) => `${Names.fields()}.f_${fieldId}`, +} as const; + +export class AutomatonConditionUtil { + public static formatEntityCondition(condition: AutomationEntityCondition): string { + const conditions: string[] = []; + if (condition.stageId) { + conditions.push(`${Names.stage()} = ${condition.stageId}`); + } + + if (condition.ownerIds?.length) { + conditions.push(`${Names.owner()} in [${condition.ownerIds.join(',')}]`); + } + + if (condition.fields?.length) { + condition.fields.forEach((field) => { + conditions.push(...AutomatonConditionUtil.formatFieldCondition(field)); + }); + } + + return conditions.length ? conditions.map((c) => `(${c})`).join(' and ') : 'true'; + } + + public static formatFieldCondition(field: AutomationFieldCondition): string[] { + switch (field.type) { + case SimpleFilterType.Boolean: + return AutomatonConditionUtil.formatBooleanFieldCondition(field); + case SimpleFilterType.Date: + return AutomatonConditionUtil.formatDateFieldCondition(field); + case SimpleFilterType.Number: + return AutomatonConditionUtil.formatNumberFieldCondition(field); + case SimpleFilterType.Select: + return AutomatonConditionUtil.formatSelectFieldCondition(field); + case SimpleFilterType.String: + return AutomatonConditionUtil.formatStringFieldCondition(field); + case SimpleFilterType.Exists: + return AutomatonConditionUtil.formatExistsFieldCondition(field); + } + } + + private static formatBooleanFieldCondition(field: AutomationFieldCondition) { + const conditions: string[] = []; + const booleanFilter = field.filter as BooleanFilter; + if (booleanFilter.value !== null && booleanFilter.value !== undefined) { + if (booleanFilter.value) { + conditions.push(`${Names.field(field.fieldId)} != null`); + conditions.push(`${Names.field(field.fieldId)} = true`); + } else { + conditions.push(`(${Names.field(field.fieldId)} = null) or (${Names.field(field.fieldId)} = false)`); + } + } + return conditions; + } + + private static formatDateFieldCondition(field: AutomationFieldCondition) { + const conditions: string[] = []; + const dateFilter = field.filter as DateFilter; + if (dateFilter.from || dateFilter.to) conditions.push(`${Names.field(field.fieldId)} != null`); + if (dateFilter.from) conditions.push(`${Names.field(field.fieldId)} >= "${dateFilter.from}"`); + if (dateFilter.to) conditions.push(`${Names.field(field.fieldId)} <= "${dateFilter.to}"`); + return conditions; + } + + private static formatNumberFieldCondition(field: AutomationFieldCondition) { + const conditions: string[] = []; + const numberFilter = field.filter as NumberFilter; + if ( + (numberFilter.min !== null && numberFilter.min !== undefined) || + (numberFilter.max !== null && numberFilter.max !== undefined) + ) { + conditions.push(`${Names.field(field.fieldId)} != null`); + } + if (numberFilter.min !== null && numberFilter.min !== undefined) { + conditions.push(`${Names.field(field.fieldId)} >= ${numberFilter.min}`); + } + if (numberFilter.max !== null && numberFilter.max !== undefined) { + conditions.push(`${Names.field(field.fieldId)} <= ${numberFilter.max}`); + } + return conditions; + } + + private static formatSelectFieldCondition(field: AutomationFieldCondition) { + const conditions: string[] = []; + const selectFilter = field.filter as SelectFilter; + if (selectFilter.optionIds?.length) { + conditions.push(`${Names.field(field.fieldId)} != null`); + conditions.push(`some x in ${Names.field(field.fieldId)} satisfies x in [${selectFilter.optionIds.join(',')}]`); + } + return conditions; + } + + private static formatStringFieldCondition(field: AutomationFieldCondition) { + const conditions: string[] = []; + const stringFilter = field.filter as StringFilter; + switch (stringFilter.type) { + case StringFilterType.Empty: + conditions.push(`(${Names.field(field.fieldId)} = null) or (${Names.field(field.fieldId)} = "")`); + break; + case StringFilterType.NotEmpty: + conditions.push(`${Names.field(field.fieldId)} != null`); + conditions.push(`${Names.field(field.fieldId)} != ""`); + break; + case StringFilterType.Contains: + if (stringFilter.text) { + conditions.push(`${Names.field(field.fieldId)} != null`); + conditions.push(`contains(${Names.field(field.fieldId)}, "${encodeURIComponent(stringFilter.text)}")`); + } + break; + } + return conditions; + } + + private static formatExistsFieldCondition(field: AutomationFieldCondition) { + const conditions: string[] = []; + const existsFilter = field.filter as ExistsFilter; + switch (existsFilter.type) { + case ExistsFilterType.Empty: + conditions.push(`(${Names.field(field.fieldId)} = null) or (${Names.field(field.fieldId)} = "")`); + break; + case ExistsFilterType.NotEmpty: + conditions.push(`${Names.field(field.fieldId)} != null`); + conditions.push(`${Names.field(field.fieldId)} != ""`); + break; + } + return conditions; + } +} diff --git a/backend/src/modules/automation/common/utils/automation-delay.util.ts b/backend/src/modules/automation/common/utils/automation-delay.util.ts new file mode 100644 index 0000000..3b18c93 --- /dev/null +++ b/backend/src/modules/automation/common/utils/automation-delay.util.ts @@ -0,0 +1,5 @@ +export class AutomationDelayUtil { + public static formatSeconds(delay?: number | null): string { + return `PT${delay || 0}S`; + } +} diff --git a/backend/src/modules/automation/common/utils/index.ts b/backend/src/modules/automation/common/utils/index.ts new file mode 100644 index 0000000..3b9d6f6 --- /dev/null +++ b/backend/src/modules/automation/common/utils/index.ts @@ -0,0 +1,2 @@ +export * from './automation-condition.util'; +export * from './automation-delay.util'; diff --git a/backend/src/modules/automation/config/automation.config.ts b/backend/src/modules/automation/config/automation.config.ts new file mode 100644 index 0000000..cff18a7 --- /dev/null +++ b/backend/src/modules/automation/config/automation.config.ts @@ -0,0 +1,12 @@ +import { registerAs } from '@nestjs/config'; + +export interface AutomationConfig { + jobDiscovery: boolean; +} + +export default registerAs( + 'automation', + (): AutomationConfig => ({ + jobDiscovery: ['true', 'on'].includes(process.env.AUTOMATION_JOB_DISCOVERY_ENABLED), + }), +); diff --git a/backend/src/modules/automation/index.ts b/backend/src/modules/automation/index.ts new file mode 100644 index 0000000..6fa5095 --- /dev/null +++ b/backend/src/modules/automation/index.ts @@ -0,0 +1,5 @@ +export * from './automation-core'; +export * from './automation-entity-type'; +export * from './automation-process'; +export * from './automation.module'; +export * from './common'; diff --git a/backend/src/modules/data-enrichment/config/data-enrichment.config.ts b/backend/src/modules/data-enrichment/config/data-enrichment.config.ts new file mode 100644 index 0000000..97986fe --- /dev/null +++ b/backend/src/modules/data-enrichment/config/data-enrichment.config.ts @@ -0,0 +1,14 @@ +import { registerAs } from '@nestjs/config'; + +export interface DataEnrichmentConfig { + geohelperApiKey: string; + dadataApiKey: string; +} + +export default registerAs( + 'data-enrichment', + (): DataEnrichmentConfig => ({ + geohelperApiKey: process.env.DATA_ENRICHMENT_GEOHELPER_API_KEY, + dadataApiKey: process.env.DATA_ENRICHMENT_DADATA_API_KEY, + }), +); diff --git a/backend/src/modules/data-enrichment/data-enrichment.controller.ts b/backend/src/modules/data-enrichment/data-enrichment.controller.ts new file mode 100644 index 0000000..bdf9932 --- /dev/null +++ b/backend/src/modules/data-enrichment/data-enrichment.controller.ts @@ -0,0 +1,46 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common'; + +import { DataEnrichmentService } from './data-enrichment.service'; +import { BankRequisitesDto, OrganizationRequisitesDto, PhoneUserInfoDto } from './dto'; + +@ApiTags('data-enrichment') +@Controller('data-enrichment') +@JwtAuthorized() +@TransformToDto() +export class DataEnrichmentController { + constructor(private service: DataEnrichmentService) {} + + @ApiOperation({ summary: 'Get bank requisites by query', description: 'Get bank requisites by query' }) + @ApiQuery({ name: 'query', type: String, required: true, description: 'Query to get bank requisites from' }) + @ApiOkResponse({ description: 'Bank requisites', type: [BankRequisitesDto] }) + @Get('requisites/bank') + public async getBankRequisites(@Query('query') query: string) { + return this.service.getBankRequisites(query); + } + + @ApiOperation({ + summary: 'Get organization requisites by query', + description: 'Get organization requisites by query', + }) + @ApiQuery({ name: 'query', type: String, required: true, description: 'Query to get organization requisites from' }) + @ApiOkResponse({ description: 'Organization requisites', type: [OrganizationRequisitesDto] }) + @Get('requisites/org') + public async getOrgRequisites(@Query('query') query: string) { + return this.service.getOrgRequisites(query); + } + + @ApiOperation({ + summary: 'Get aggregated phone user info by phone number', + description: 'Get aggregated phone user info by phone number', + }) + @ApiQuery({ name: 'phone', type: String, required: true, description: 'Phone number to get aggregated info from' }) + @ApiOkResponse({ description: 'Phone user info', type: PhoneUserInfoDto }) + @Get('phone/aggregate') + public async getPhoneInfo(@Query('phone') phone: string) { + return this.service.getPhoneInfo(phone); + } +} diff --git a/backend/src/modules/data-enrichment/data-enrichment.module.ts b/backend/src/modules/data-enrichment/data-enrichment.module.ts new file mode 100644 index 0000000..456eb9c --- /dev/null +++ b/backend/src/modules/data-enrichment/data-enrichment.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import dataEnrichmentConfig from './config/data-enrichment.config'; +import { DataEnrichmentService } from './data-enrichment.service'; +import { DataEnrichmentController } from './data-enrichment.controller'; + +@Module({ + imports: [ConfigModule.forFeature(dataEnrichmentConfig), IAMModule], + providers: [DataEnrichmentService], + controllers: [DataEnrichmentController], +}) +export class DataEnrichmentModule {} diff --git a/backend/src/modules/data-enrichment/data-enrichment.service.ts b/backend/src/modules/data-enrichment/data-enrichment.service.ts new file mode 100644 index 0000000..41221e2 --- /dev/null +++ b/backend/src/modules/data-enrichment/data-enrichment.service.ts @@ -0,0 +1,206 @@ +import { HttpService } from '@nestjs/axios'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { lastValueFrom } from 'rxjs'; + +import { CacheService, DateUtil, withTimeout } from '@/common'; + +import { DataEnrichmentConfig } from './config/data-enrichment.config'; +import { getRuCountryNameByIsoCode, getUtcOffsetByRuCountryName } from './helpers'; +import { + BankRequisites, + DadataBankRequisites, + DadataOrgRequisites, + DadataSuggestions, + PhoneUserInfo, + PhoneUserInfoFincalculator, + PhoneUserInfoGeohelper, + PhoneUserInfoSpNova, + UserInfoContentSpNova, +} from './types'; +import { OrganizationRequisites } from './types/organization-requisites'; + +const PhoneServiceUrls = { + geohelper: 'https://geohelper.info/api/v1/phone-data', + fincalculator: 'https://fincalculator.ru/api/tel', + spNova: 'https://sp1-nova.ru/api/phones-data/', +} as const; + +const DadataUrls = { + bankRequisites: 'http://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/bank', + orgRequisites: 'http://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/party', +} as const; + +@Injectable() +export class DataEnrichmentService { + private _config: DataEnrichmentConfig; + + constructor( + private readonly httpService: HttpService, + private readonly configService: ConfigService, + private readonly cache: CacheService, + ) { + this._config = this.configService.get('data-enrichment'); + } + + async getBankRequisites(query: string): Promise { + const getData = async (query: string): Promise | null> => { + try { + const response = await lastValueFrom( + this.httpService.get>(DadataUrls.bankRequisites, { + params: { query }, + headers: { Authorization: `Token ${this._config.dadataApiKey}` }, + }), + ); + return response.data; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + return null; + } + }; + + const data = await this.cache.wrap(`DataEnrichment.bankRequisites:${query}`, () => getData(query), 604800); + + return data?.suggestions?.map((suggestion) => BankRequisites.fromDadata(suggestion)); + } + + async getOrgRequisites(query: string): Promise { + const getData = async (query: string): Promise | null> => { + try { + const response = await lastValueFrom( + this.httpService.get>(DadataUrls.orgRequisites, { + params: { query }, + headers: { Authorization: `Token ${this._config.dadataApiKey}` }, + }), + ); + return response.data; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + return null; + } + }; + + const data = await this.cache.wrap(`DataEnrichment.orgRequisites:${query}`, () => getData(query), 604800); + + return data?.suggestions?.map((suggestion) => OrganizationRequisites.fromDadata(suggestion)); + } + + async getPhoneInfo(phone: string): Promise { + const getData = async (phone: string): Promise => { + const timeout = 7500; + + const infos = await Promise.all([ + withTimeout(this.getPhoneInfoFincalculator(phone), timeout), + withTimeout(this.getPhoneInfoSpNova(phone), timeout), + withTimeout(this.getPhoneInfoGeohelper(phone), timeout), + ]); + + const country = infos.find((info) => info?.country)?.country ?? null; + const city = infos.find((info) => info?.city)?.city ?? null; + const region = infos.find((info) => info?.region)?.region ?? null; + const utcOffset = infos.find((info) => info?.utcOffset)?.utcOffset ?? getUtcOffsetByRuCountryName(country); + + return new PhoneUserInfo({ country, city, region, utcOffset }); + }; + + const data = await this.cache.wrap(`DataEnrichment.phone:${phone}`, () => getData(phone), 604800); + + return new PhoneUserInfo(data); + } + + // https://geohelper.info/ru/doc/api/#get/api/v1/phone-data + private async getPhoneInfoGeohelper(phone: string): Promise { + try { + const response$ = this.httpService.get(PhoneServiceUrls.geohelper, { + params: { + 'locale[lang]': 'ru', + 'filter[phone]': phone, + 'locale[fallbackLang]': 'ru', + apiKey: this._config.geohelperApiKey, + }, + }); + + const response = await lastValueFrom(response$); + // we can't trust that service will return something + const data = response.data as PhoneUserInfoGeohelper | null | undefined; + + // request failed + if (!data?.success) { + return null; + } + + const composeRegion = (data: PhoneUserInfoGeohelper) => { + const regionName = data?.result?.region?.name; + const regionLocalizedName = data?.result?.region?.localityType?.localizedNames?.ru; + const regionLocalizedNameShort = data?.result?.region?.localityType?.localizedNamesShort?.ru; + + if (regionName && (regionLocalizedName || regionLocalizedNameShort)) { + return `${regionName} (${regionLocalizedName ?? regionLocalizedNameShort})`; + } + + return null; + }; + + const timezoneOffset = data?.result?.region?.timezoneOffset; + return new PhoneUserInfo({ + city: null, + region: composeRegion(data), + country: getRuCountryNameByIsoCode(data?.result?.region?.countryIso), + utcOffset: timezoneOffset ? timezoneOffset / 3600 : null, + }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + return null; + } + } + // https://fincalculator.ru/telefon/region-po-nomeru + private async getPhoneInfoFincalculator(phone: string): Promise { + try { + const response$ = this.httpService.get(`${PhoneServiceUrls.fincalculator}/${phone}`); + + const response = await lastValueFrom(response$); + // we can't trust that service will return something + const data = response.data as PhoneUserInfoFincalculator | null | undefined; + + // sometimes fincalculator returns wrong information about region (seems like it is a phone operator), + // so we need to check that region is not hallucination, known hallucinations are presented below + const knownRegionHallucinations = ['Кселл', 'ТОО']; + + const region = data?.region; + + return new PhoneUserInfo({ + city: null, + region: region && !knownRegionHallucinations.includes(region) ? region : null, + country: data?.country ?? null, + utcOffset: data?.timeZone ?? null, + }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + return null; + } + } + private async getPhoneInfoSpNova(phone: string): Promise { + try { + const response$ = this.httpService.post(PhoneServiceUrls.spNova, [phone], { + headers: { + 'Content-Type': 'application/json', + }, + }); + + const response = await lastValueFrom(response$); + // we can't trust that service will return something + const data = response.data as PhoneUserInfoSpNova | null | undefined; + const content = data?.[phone] as UserInfoContentSpNova | null | undefined; + + return new PhoneUserInfo({ + city: content?.city ?? null, + region: content?.region ?? null, + country: content?.country ?? null, + utcOffset: content?.timezone ? DateUtil.extractOffsetFromUTCString(content.timezone) : null, + }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + return null; + } + } +} diff --git a/backend/src/modules/data-enrichment/dto/bank-requisites.dto.ts b/backend/src/modules/data-enrichment/dto/bank-requisites.dto.ts new file mode 100644 index 0000000..ca327b0 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/bank-requisites.dto.ts @@ -0,0 +1,51 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsOptional, IsString } from 'class-validator'; + +import { OpfType } from '../enums'; + +export class BankRequisitesDto { + @ApiPropertyOptional({ description: 'Bank name', nullable: true }) + @IsOptional() + @IsString() + value?: string | null; + + @ApiPropertyOptional({ description: 'Bank name unrestricted', nullable: true }) + @IsOptional() + @IsString() + unrestrictedValue?: string | null; + + @ApiPropertyOptional({ description: 'Bank BIC', nullable: true }) + @IsOptional() + @IsString() + bic?: string | null; + + @ApiPropertyOptional({ description: 'Bank SWIFT', nullable: true }) + @IsOptional() + @IsString() + swift?: string | null; + + @ApiPropertyOptional({ description: 'Bank INN', nullable: true }) + @IsOptional() + @IsString() + inn?: string | null; + + @ApiPropertyOptional({ description: 'Bank KPP', nullable: true }) + @IsOptional() + @IsString() + kpp?: string | null; + + @ApiPropertyOptional({ description: 'Bank correspondent account', nullable: true }) + @IsOptional() + @IsString() + correspondentAccount?: string | null; + + @ApiPropertyOptional({ description: 'Bank payment city', nullable: true }) + @IsOptional() + @IsString() + paymentCity?: string | null; + + @ApiPropertyOptional({ description: 'Bank OPF type', nullable: true, enum: OpfType }) + @IsOptional() + @IsEnum(OpfType) + opf?: OpfType | null; +} diff --git a/backend/src/modules/data-enrichment/dto/fio.dto.ts b/backend/src/modules/data-enrichment/dto/fio.dto.ts new file mode 100644 index 0000000..81cd39b --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/fio.dto.ts @@ -0,0 +1,19 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class FioDto { + @ApiPropertyOptional({ description: 'Name', nullable: true }) + @IsOptional() + @IsString() + name?: string | null; + + @ApiPropertyOptional({ description: 'Surname', nullable: true }) + @IsOptional() + @IsString() + surname?: string | null; + + @ApiPropertyOptional({ description: 'Patronymic name', nullable: true }) + @IsOptional() + @IsString() + patronymic?: string | null; +} diff --git a/backend/src/modules/data-enrichment/dto/index.ts b/backend/src/modules/data-enrichment/dto/index.ts new file mode 100644 index 0000000..c96fe4e --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/index.ts @@ -0,0 +1,3 @@ +export * from './bank-requisites.dto'; +export * from './organization-requisites.dto'; +export * from './phone-user-info.dto'; diff --git a/backend/src/modules/data-enrichment/dto/organization-requisites-address.dto.ts b/backend/src/modules/data-enrichment/dto/organization-requisites-address.dto.ts new file mode 100644 index 0000000..47279a7 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/organization-requisites-address.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class OrganizationRequisitesAddressDto { + @ApiPropertyOptional({ description: 'Unrestricted value', nullable: true }) + @IsOptional() + @IsString() + unrestrictedValue?: string | null; +} diff --git a/backend/src/modules/data-enrichment/dto/organization-requisites-managment.dto.ts b/backend/src/modules/data-enrichment/dto/organization-requisites-managment.dto.ts new file mode 100644 index 0000000..a3b6fa5 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/organization-requisites-managment.dto.ts @@ -0,0 +1,19 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsOptional, IsString } from 'class-validator'; + +export class OrganizationRequisitesManagmentDto { + @ApiPropertyOptional({ description: 'Name', nullable: true }) + @IsOptional() + @IsString() + name?: string | null; + + @ApiPropertyOptional({ description: 'Job title', nullable: true }) + @IsOptional() + @IsString() + post?: string | null; + + @ApiPropertyOptional({ description: 'Start date', nullable: true }) + @IsOptional() + @IsDateString() + startDate?: string | null; +} diff --git a/backend/src/modules/data-enrichment/dto/organization-requisites-name.dto.ts b/backend/src/modules/data-enrichment/dto/organization-requisites-name.dto.ts new file mode 100644 index 0000000..9b574c8 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/organization-requisites-name.dto.ts @@ -0,0 +1,14 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class OrganizationRequisitesNameDto { + @ApiPropertyOptional({ description: 'Full name', nullable: true }) + @IsOptional() + @IsString() + full?: string | null; + + @ApiPropertyOptional({ description: 'Short name', nullable: true }) + @IsOptional() + @IsString() + short?: string | null; +} diff --git a/backend/src/modules/data-enrichment/dto/organization-requisites-state.dto.ts b/backend/src/modules/data-enrichment/dto/organization-requisites-state.dto.ts new file mode 100644 index 0000000..0f079a0 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/organization-requisites-state.dto.ts @@ -0,0 +1,21 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsEnum, IsOptional } from 'class-validator'; + +import { OrgStatus } from '../enums'; + +export class OrganizationRequisitesStateDto { + @ApiPropertyOptional({ description: 'Registration date', nullable: true }) + @IsOptional() + @IsDateString() + registrationDate?: string | null; + + @ApiPropertyOptional({ description: 'Liquidation date', nullable: true }) + @IsOptional() + @IsDateString() + liquidationDate?: string | null; + + @ApiPropertyOptional({ enum: OrgStatus, description: 'Status', nullable: true }) + @IsOptional() + @IsEnum(OrgStatus) + status?: OrgStatus | null; +} diff --git a/backend/src/modules/data-enrichment/dto/organization-requisites.dto.ts b/backend/src/modules/data-enrichment/dto/organization-requisites.dto.ts new file mode 100644 index 0000000..62481a0 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/organization-requisites.dto.ts @@ -0,0 +1,140 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { OrgBranchType, OrgType } from '../enums'; +import { OrganizationRequisitesNameDto } from './organization-requisites-name.dto'; +import { FioDto } from './fio.dto'; +import { OrganizationRequisitesManagmentDto } from './organization-requisites-managment.dto'; +import { OrganizationRequisitesStateDto } from './organization-requisites-state.dto'; +import { OrganizationRequisitesAddressDto } from './organization-requisites-address.dto'; + +export class OrganizationRequisitesDto { + @ApiPropertyOptional({ description: 'Organization name', nullable: true }) + @IsOptional() + @IsString() + value?: string | null; + + @ApiPropertyOptional({ description: 'Organization name unrestricted', nullable: true }) + @IsOptional() + @IsString() + unrestrictedValue?: string | null; + + @ApiPropertyOptional({ description: 'INN', nullable: true }) + @IsOptional() + @IsString() + inn?: string | null; + + @ApiPropertyOptional({ description: 'KPP', nullable: true }) + @IsOptional() + @IsString() + kpp?: string | null; + + @ApiPropertyOptional({ description: 'OGRN', nullable: true }) + @IsOptional() + @IsString() + ogrn?: string | null; + + @ApiPropertyOptional({ enum: OrgType, description: 'Type', nullable: true }) + @IsOptional() + @IsEnum(OrgType) + type?: OrgType | null; + + @ApiPropertyOptional({ + type: OrganizationRequisitesNameDto, + description: 'Organization name details', + nullable: true, + }) + @IsOptional() + name?: OrganizationRequisitesNameDto | null; + + @ApiPropertyOptional({ type: FioDto, description: 'FIO', nullable: true }) + @IsOptional() + fio?: FioDto | null; + + @ApiPropertyOptional({ type: OrganizationRequisitesManagmentDto, description: 'Management', nullable: true }) + @IsOptional() + management?: OrganizationRequisitesManagmentDto | null; + + @ApiPropertyOptional({ description: 'Branch count', nullable: true }) + @IsOptional() + @IsNumber() + branchCount?: number | null; + + @ApiPropertyOptional({ enum: OrgBranchType, description: 'Branch type', nullable: true }) + @IsOptional() + @IsEnum(OrgBranchType) + branchType?: OrgBranchType | null; + + @ApiPropertyOptional({ type: OrganizationRequisitesAddressDto, description: 'Address', nullable: true }) + @IsOptional() + address?: OrganizationRequisitesAddressDto | null; + + @ApiPropertyOptional({ type: OrganizationRequisitesStateDto, description: 'State', nullable: true }) + @IsOptional() + state?: OrganizationRequisitesStateDto | null; + + @ApiPropertyOptional({ description: 'OKATO', nullable: true }) + @IsOptional() + @IsString() + okato?: string | null; + + @ApiPropertyOptional({ description: 'OKTMO', nullable: true }) + @IsOptional() + @IsString() + oktmo?: string | null; + + @ApiPropertyOptional({ description: 'OKPO', nullable: true }) + @IsOptional() + @IsString() + okpo?: string | null; + + @ApiPropertyOptional({ description: 'OKOGU', nullable: true }) + @IsOptional() + @IsString() + okogu?: string | null; + + @ApiPropertyOptional({ description: 'OKFS', nullable: true }) + @IsOptional() + @IsString() + okfs?: string | null; + + @ApiPropertyOptional({ description: 'OKVED', nullable: true }) + @IsOptional() + @IsString() + okved?: string | null; + + @ApiPropertyOptional({ description: 'Employee count', nullable: true }) + @IsOptional() + @IsNumber() + employeeCount?: number | null; + + @ApiPropertyOptional({ type: [String], description: 'Founders', nullable: true }) + @IsOptional() + @IsString({ each: true }) + founders?: string[] | null; + + @ApiPropertyOptional({ type: [String], description: 'Managers', nullable: true }) + @IsOptional() + @IsString({ each: true }) + managers?: string[] | null; + + @ApiPropertyOptional({ description: 'Capital', nullable: true }) + @IsOptional() + @IsString() + capital?: string | null; + + @ApiPropertyOptional({ type: [String], description: 'Licenses', nullable: true }) + @IsOptional() + @IsString({ each: true }) + licenses?: string[] | null; + + @ApiPropertyOptional({ type: [String], description: 'Phones', nullable: true }) + @IsOptional() + @IsString({ each: true }) + phones?: string[] | null; + + @ApiPropertyOptional({ type: [String], description: 'Emails', nullable: true }) + @IsOptional() + @IsString({ each: true }) + emails?: string[] | null; +} diff --git a/backend/src/modules/data-enrichment/dto/phone-user-info.dto.ts b/backend/src/modules/data-enrichment/dto/phone-user-info.dto.ts new file mode 100644 index 0000000..1898317 --- /dev/null +++ b/backend/src/modules/data-enrichment/dto/phone-user-info.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class PhoneUserInfoDto { + @ApiProperty({ description: 'UTC offset' }) + @IsNumber() + utcOffset: number | null; + + @ApiProperty({ description: 'Country name' }) + @IsString() + country: string | null; + + @ApiProperty({ description: 'Region name' }) + @IsString() + region: string | null; + + @ApiProperty({ description: 'City name' }) + @IsString() + city: string | null; +} diff --git a/backend/src/modules/data-enrichment/enums/index.ts b/backend/src/modules/data-enrichment/enums/index.ts new file mode 100644 index 0000000..58409a8 --- /dev/null +++ b/backend/src/modules/data-enrichment/enums/index.ts @@ -0,0 +1,4 @@ +export * from './opf-type.enum'; +export * from './org-branch-type.enum'; +export * from './org-status.enum'; +export * from './org-type.enum'; diff --git a/backend/src/modules/data-enrichment/enums/opf-type.enum.ts b/backend/src/modules/data-enrichment/enums/opf-type.enum.ts new file mode 100644 index 0000000..8bda778 --- /dev/null +++ b/backend/src/modules/data-enrichment/enums/opf-type.enum.ts @@ -0,0 +1,18 @@ +export enum OpfType { + // банк + Bank = 'BANK', + // филиал банка + BankBranch = 'BANK_BRANCH', + // небанковская кредитная организация (НКО) + Nko = 'NKO', + // филиал НКО + NkoBranch = 'NKO_BRANCH', + // расчетно-кассовый центр + Rkc = 'RKC', + // управление ЦБ РФ (март 2021) + Cbr = 'CBR', + // управление Казначейства (март 2021) + Treasury = 'TREASURY', + // другой + Other = 'OTHER', +} diff --git a/backend/src/modules/data-enrichment/enums/org-branch-type.enum.ts b/backend/src/modules/data-enrichment/enums/org-branch-type.enum.ts new file mode 100644 index 0000000..e7945c9 --- /dev/null +++ b/backend/src/modules/data-enrichment/enums/org-branch-type.enum.ts @@ -0,0 +1,6 @@ +export enum OrgBranchType { + // Головная организация + Main = 'MAIN', + // Филиал + Branch = 'BRANCH', +} diff --git a/backend/src/modules/data-enrichment/enums/org-status.enum.ts b/backend/src/modules/data-enrichment/enums/org-status.enum.ts new file mode 100644 index 0000000..ccceb84 --- /dev/null +++ b/backend/src/modules/data-enrichment/enums/org-status.enum.ts @@ -0,0 +1,12 @@ +export enum OrgStatus { + // Действующая + Active = 'ACTIVE', + // Ликвидируется + Liquidating = 'LIQUIDATING', + // Ликвидирована + Liquidated = 'LIQUIDATED', + // Банкротство + Bankrupt = 'BANKRUPT', + // В процессе присоединения к другому юрлицу, с последующей ликвидацией + Reorganizing = 'REORGANIZING', +} diff --git a/backend/src/modules/data-enrichment/enums/org-type.enum.ts b/backend/src/modules/data-enrichment/enums/org-type.enum.ts new file mode 100644 index 0000000..9dc911e --- /dev/null +++ b/backend/src/modules/data-enrichment/enums/org-type.enum.ts @@ -0,0 +1,6 @@ +export enum OrgType { + // Юридическое лицо + Legal = 'LEGAL', + // Индивидуальный предприниматель + Individual = 'INDIVIDUAL', +} diff --git a/backend/src/modules/data-enrichment/helpers/get-ru-country-name-by-iso-code.helper.ts b/backend/src/modules/data-enrichment/helpers/get-ru-country-name-by-iso-code.helper.ts new file mode 100644 index 0000000..bdbeefa --- /dev/null +++ b/backend/src/modules/data-enrichment/helpers/get-ru-country-name-by-iso-code.helper.ts @@ -0,0 +1,23 @@ +const codes: Record = { + RU: 'Россия', + KZ: 'Казахстан', + UA: 'Украина', + BY: 'Беларусь', + UZ: 'Узбекистан', + TJ: 'Таджикистан', + KG: 'Киргизия', + TM: 'Туркменистан', + AM: 'Армения', + AZ: 'Азербайджан', + GE: 'Грузия', + MD: 'Молдова', + PL: 'Польша', + LV: 'Латвия', + LT: 'Литва', + EE: 'Эстония', + RS: 'Сербия', +}; + +export const getRuCountryNameByIsoCode = (countryIso: string) => { + return codes[countryIso] ?? null; +}; diff --git a/backend/src/modules/data-enrichment/helpers/get-utc-offset-by-ru-country-name.helper.ts b/backend/src/modules/data-enrichment/helpers/get-utc-offset-by-ru-country-name.helper.ts new file mode 100644 index 0000000..ba4577a --- /dev/null +++ b/backend/src/modules/data-enrichment/helpers/get-utc-offset-by-ru-country-name.helper.ts @@ -0,0 +1,21 @@ +const offsets: Record = { + Азербайджан: 4, + Армения: 4, + Беларусь: 3, + Казахстан: 5, + Кыргызстан: 6, + Молдова: 3, + Россия: 3, + Таджикистан: 5, + Туркменистан: 5, + Узбекистан: 5, + Польша: 2, + Латвия: 3, + Литва: 3, + Эстония: 3, + Сербия: 2, +}; + +export const getUtcOffsetByRuCountryName = (countryName: string): number | null => { + return countryName ? (offsets[countryName] ?? null) : null; +}; diff --git a/backend/src/modules/data-enrichment/helpers/index.ts b/backend/src/modules/data-enrichment/helpers/index.ts new file mode 100644 index 0000000..514c41e --- /dev/null +++ b/backend/src/modules/data-enrichment/helpers/index.ts @@ -0,0 +1,2 @@ +export * from './get-ru-country-name-by-iso-code.helper'; +export * from './get-utc-offset-by-ru-country-name.helper'; diff --git a/backend/src/modules/data-enrichment/types/bank-requisites.ts b/backend/src/modules/data-enrichment/types/bank-requisites.ts new file mode 100644 index 0000000..2f7a881 --- /dev/null +++ b/backend/src/modules/data-enrichment/types/bank-requisites.ts @@ -0,0 +1,56 @@ +import { BankRequisitesDto } from '../dto'; +import { OpfType } from '../enums'; +import { DadataBankRequisites } from './dadata-bank-requisites'; +import { DadataSuggestion } from './dadata-suggestion'; + +export class BankRequisites { + value?: string | null; + unrestrictedValue?: string | null; + bic?: string | null; + swift?: string | null; + inn?: string | null; + kpp?: string | null; + correspondentAccount?: string | null; + paymentCity?: string | null; + opf?: OpfType | null; + + constructor(data: Omit) { + this.value = data.value; + this.unrestrictedValue = data.unrestrictedValue; + this.bic = data.bic; + this.swift = data.swift; + this.inn = data.inn; + this.kpp = data.kpp; + this.correspondentAccount = data.correspondentAccount; + this.paymentCity = data.paymentCity; + this.opf = data.opf; + } + + public static fromDadata(suggestion: DadataSuggestion): BankRequisites { + return new BankRequisites({ + value: suggestion.value, + unrestrictedValue: suggestion.unrestricted_value, + bic: suggestion.data?.bic, + swift: suggestion.data?.swift, + inn: suggestion.data?.inn, + kpp: suggestion.data?.kpp, + correspondentAccount: suggestion.data?.correspondent_account, + paymentCity: suggestion.data?.payment_city, + opf: suggestion.data?.opf?.type, + }); + } + + toDto(): BankRequisitesDto { + return { + value: this.value, + unrestrictedValue: this.unrestrictedValue, + bic: this.bic, + swift: this.swift, + inn: this.inn, + kpp: this.kpp, + correspondentAccount: this.correspondentAccount, + paymentCity: this.paymentCity, + opf: this.opf, + }; + } +} diff --git a/backend/src/modules/data-enrichment/types/dadata-bank-requisites.ts b/backend/src/modules/data-enrichment/types/dadata-bank-requisites.ts new file mode 100644 index 0000000..d84b22c --- /dev/null +++ b/backend/src/modules/data-enrichment/types/dadata-bank-requisites.ts @@ -0,0 +1,13 @@ +import { OpfType } from '../enums'; + +export interface DadataBankRequisites { + bic?: string | null; + swift?: string | null; + inn?: string | null; + kpp?: string | null; + correspondent_account?: string | null; + payment_city?: string | null; + opf?: { + type?: OpfType | null; + }; +} diff --git a/backend/src/modules/data-enrichment/types/dadata-org-requisites.ts b/backend/src/modules/data-enrichment/types/dadata-org-requisites.ts new file mode 100644 index 0000000..fb2093a --- /dev/null +++ b/backend/src/modules/data-enrichment/types/dadata-org-requisites.ts @@ -0,0 +1,45 @@ +import { OrgBranchType, OrgStatus, OrgType } from '../enums'; + +export interface DadataOrgRequisites { + inn?: string | null; + kpp?: string | null; + ogrn?: string | null; + type?: OrgType | null; + name?: { + full_with_opf?: string | null; + short_with_opf?: string | null; + } | null; + fio?: { + name?: string | null; + surname?: string | null; + patronymic?: string | null; + } | null; + management?: { + name?: string | null; + post?: string | null; + start_date?: number | null; + } | null; + branch_count?: number | null; + branch_type?: OrgBranchType | null; + address?: { + unrestricted_value?: string | null; + } | null; + state?: { + registration_date?: number | null; + liquidation_date?: number | null; + status?: OrgStatus | null; + } | null; + okato?: string | null; + oktmo?: string | null; + okpo?: string | null; + okogu?: string | null; + okfs?: string | null; + okved?: string | null; + employee_count?: number | null; + founders?: string[] | null; + managers?: string[] | null; + capital?: string | null; + licenses?: string[] | null; + phones?: string[] | null; + emails?: string[] | null; +} diff --git a/backend/src/modules/data-enrichment/types/dadata-suggestion.ts b/backend/src/modules/data-enrichment/types/dadata-suggestion.ts new file mode 100644 index 0000000..6993016 --- /dev/null +++ b/backend/src/modules/data-enrichment/types/dadata-suggestion.ts @@ -0,0 +1,5 @@ +export interface DadataSuggestion { + value?: string | null; + unrestricted_value?: string | null; + data?: T | null; +} diff --git a/backend/src/modules/data-enrichment/types/dadata-suggestions.ts b/backend/src/modules/data-enrichment/types/dadata-suggestions.ts new file mode 100644 index 0000000..e30bfad --- /dev/null +++ b/backend/src/modules/data-enrichment/types/dadata-suggestions.ts @@ -0,0 +1,5 @@ +import { DadataSuggestion } from './dadata-suggestion'; + +export interface DadataSuggestions { + suggestions?: DadataSuggestion[] | null; +} diff --git a/backend/src/modules/data-enrichment/types/index.ts b/backend/src/modules/data-enrichment/types/index.ts new file mode 100644 index 0000000..2c476fa --- /dev/null +++ b/backend/src/modules/data-enrichment/types/index.ts @@ -0,0 +1,9 @@ +export * from './bank-requisites'; +export * from './dadata-bank-requisites'; +export * from './dadata-org-requisites'; +export * from './dadata-suggestion'; +export * from './dadata-suggestions'; +export * from './phone-user-info-fincalculator'; +export * from './phone-user-info-geohelper'; +export * from './phone-user-info-sp-nova'; +export * from './phone-user-info'; diff --git a/backend/src/modules/data-enrichment/types/organization-requisites.ts b/backend/src/modules/data-enrichment/types/organization-requisites.ts new file mode 100644 index 0000000..4106920 --- /dev/null +++ b/backend/src/modules/data-enrichment/types/organization-requisites.ts @@ -0,0 +1,172 @@ +import { OrganizationRequisitesDto } from '../dto'; +import { OrgBranchType, OrgStatus, OrgType } from '../enums'; +import { DadataOrgRequisites } from './dadata-org-requisites'; +import { DadataSuggestion } from './dadata-suggestion'; + +export class OrganizationRequisites { + value?: string | null; + unrestrictedValue?: string | null; + inn?: string | null; + kpp?: string | null; + ogrn?: string | null; + type?: OrgType | null; + name?: { + full?: string | null; + short?: string | null; + } | null; + fio?: { + name?: string | null; + surname?: string | null; + patronymic?: string | null; + } | null; + management?: { + name?: string | null; + post?: string | null; + startDate?: Date | null; + } | null; + branchCount?: number | null; + branchType?: OrgBranchType | null; + address?: { + unrestrictedValue?: string | null; + } | null; + state?: { + registrationDate?: Date | null; + liquidationDate?: Date | null; + status?: OrgStatus | null; + } | null; + okato?: string | null; + oktmo?: string | null; + okpo?: string | null; + okogu?: string | null; + okfs?: string | null; + okved?: string | null; + employeeCount?: number | null; + founders?: string[] | null; + managers?: string[] | null; + capital?: string | null; + licenses?: string[] | null; + phones?: string[] | null; + emails?: string[] | null; + + constructor(data: Omit) { + this.value = data.value; + this.unrestrictedValue = data.unrestrictedValue; + this.inn = data.inn; + this.kpp = data.kpp; + this.ogrn = data.ogrn; + this.type = data.type; + this.name = data.name; + this.fio = data.fio; + this.management = data.management; + this.branchCount = data.branchCount; + this.branchType = data.branchType; + this.address = data.address; + this.state = data.state; + this.okato = data.okato; + this.oktmo = data.oktmo; + this.okpo = data.okpo; + this.okogu = data.okogu; + this.okfs = data.okfs; + this.okved = data.okved; + this.employeeCount = data.employeeCount; + this.founders = data.founders; + this.managers = data.managers; + this.capital = data.capital; + this.licenses = data.licenses; + this.phones = data.phones; + this.emails = data.emails; + } + + public static fromDadata(suggestion: DadataSuggestion): OrganizationRequisites { + const { data } = suggestion; + return new OrganizationRequisites({ + value: suggestion.value, + unrestrictedValue: suggestion.unrestricted_value, + inn: data?.inn, + kpp: data?.kpp, + ogrn: data?.ogrn, + type: data?.type, + name: data?.name ? { full: data.name.full_with_opf, short: data.name.short_with_opf } : undefined, + fio: data?.fio + ? { + name: data.fio.name, + surname: data.fio.surname, + patronymic: data.fio.patronymic, + } + : undefined, + management: data?.management + ? { + name: data.management.name, + post: data.management.post, + startDate: data.management.start_date ? new Date(data.management.start_date) : undefined, + } + : undefined, + branchCount: data?.branch_count, + branchType: data?.branch_type, + address: data?.address ? { unrestrictedValue: data.address.unrestricted_value } : undefined, + state: data?.state + ? { + registrationDate: data.state.liquidation_date ? new Date(data.state.liquidation_date) : undefined, + liquidationDate: data.state.liquidation_date ? new Date(data.state.liquidation_date) : undefined, + status: data.state.status, + } + : undefined, + okato: data?.okato, + oktmo: data?.oktmo, + okpo: data?.okpo, + okogu: data?.okogu, + okfs: data?.okfs, + okved: data?.okved, + employeeCount: data?.employee_count, + founders: data?.founders, + managers: data?.managers, + capital: data?.capital, + licenses: data?.licenses, + phones: data?.phones, + emails: data?.emails, + }); + } + + toDto(): OrganizationRequisitesDto { + return { + value: this.value, + unrestrictedValue: this.unrestrictedValue, + inn: this.inn, + kpp: this.kpp, + ogrn: this.ogrn, + type: this.type, + name: this.name, + fio: this.fio, + management: this.management + ? { + name: this.management.name, + post: this.management.post, + startDate: this.management.startDate?.toISOString(), + } + : undefined, + branchCount: this.branchCount, + branchType: this.branchType, + address: this.address, + state: this.state + ? { + registrationDate: this.state.registrationDate?.toISOString(), + liquidationDate: this.state.liquidationDate?.toISOString(), + status: this.state.status, + } + : undefined, + okato: this.okato, + oktmo: this.oktmo, + okpo: this.okpo, + okogu: this.okogu, + okfs: this.okfs, + okved: this.okved, + employeeCount: this.employeeCount, + founders: this.founders, + managers: this.managers, + capital: this.capital, + licenses: this.licenses, + phones: this.phones, + emails: this.emails, + }; + } +} diff --git a/backend/src/modules/data-enrichment/types/phone-user-info-fincalculator.ts b/backend/src/modules/data-enrichment/types/phone-user-info-fincalculator.ts new file mode 100644 index 0000000..8e57c06 --- /dev/null +++ b/backend/src/modules/data-enrichment/types/phone-user-info-fincalculator.ts @@ -0,0 +1,25 @@ +// https://fincalculator.ru/telefon/region-po-nomeru +export class PhoneUserInfoFincalculator { + phone?: string | null; + country?: string | null; + region?: string | null; + subRegion?: string | null; + locality?: string | null; + operator?: string | null; + // utc offset in hours + timeZone?: number | null; +} + +/* + Successful response example: + + { + "phone": "+7 (921) 712-26-91", + "country": "Россия", + "region": "Калининградская область", + "subRegion": "", + "locality": "", + "operator": "МегаФон", + "timeZone": 2 + } +*/ diff --git a/backend/src/modules/data-enrichment/types/phone-user-info-geohelper.ts b/backend/src/modules/data-enrichment/types/phone-user-info-geohelper.ts new file mode 100644 index 0000000..00c901a --- /dev/null +++ b/backend/src/modules/data-enrichment/types/phone-user-info-geohelper.ts @@ -0,0 +1,113 @@ +interface Result { + dataSource?: string | null; + abcDefCode?: number | null; + rangeStart?: number | null; + rangeEnd?: number | null; + providerName?: string | null; + region?: { + // in seconds + timezoneOffset?: number | null; + countryIso?: string | null; + id?: number | null; + name?: string | null; + codes?: { + iso?: string | null; + fias?: string | null; + fips?: string | null; + kladr?: string | null; + }; + localityType?: { + code?: string | null; + localizedNamesShort?: { + en?: string | null; + kz?: string | null; + ru?: string | null; + }; + localizedNames?: { + en?: string | null; + kz?: string | null; + ru?: string | null; + }; + }; + timezone?: string | null; + countryId?: number | null; + externalIds?: { + fias?: string | null; + fias_gar?: string | null; + geonames?: string | null; + }; + localizedNames?: { + en?: string | null; + ru?: string | null; + }; + }; + phoneParts?: { + countryCode?: string | null; + code?: string | null; + number?: string | null; + }; +} + +// https://geohelper.info/ru/doc/api/#get/api/v1/phone-data +export interface PhoneUserInfoGeohelper { + success?: boolean | null; + language?: string | null; + result?: Result | null; +} + +/* + Successful response example: + + { + "success": true, + "language": "ru", + "result": { + "dataSource": "rossvyaz", + "abcDefCode": 0, + "rangeStart": 79217100000, + "rangeEnd": 79217129999, + "providerName": "ПАО \"МегаФон\"", + "region": { + "timezoneOffset": 7200, + "countryIso": "RU", + "id": 30, + "name": "Калининградская", + "codes": { + "iso": "RU-KGD", + "fias": "39", + "fips": "23", + "kladr": "3900000000000" + }, + "localityType": { + "code": "region-oblast", + "localizedNamesShort": { + "en": "obl.", + "kz": "обл.", + "ru": "обл." + }, + "localizedNames": { + "en": "oblast", + "kz": "облысы", + "ru": "область" + } + }, + "timezone": "Europe/Kaliningrad", + "countryId": 189, + "externalIds": { + "fias": "90c7181e-724f-41b3-b6c6-bd3ec7ae3f30", + "fias_gar": "634779", + "geonames": "554230" + }, + "localizedNames": { + "en": "Kaliningradskaya", + "ru": "Калининградская" + } + }, + "phoneParts": { + "countryCode": "7", + "code": "921", + "number": "7122691" + } + } + } +*/ diff --git a/backend/src/modules/data-enrichment/types/phone-user-info-sp-nova.ts b/backend/src/modules/data-enrichment/types/phone-user-info-sp-nova.ts new file mode 100644 index 0000000..91175c7 --- /dev/null +++ b/backend/src/modules/data-enrichment/types/phone-user-info-sp-nova.ts @@ -0,0 +1,30 @@ +export interface UserInfoContentSpNova { + operator?: string | null; + country?: string | null; + region?: string | null; + district?: string | null; + city?: string | null; + // hh:mm:ss + time?: string | null; + // in UTC+0200 format + timezone?: string | null; +} + +// https://sp1-nova.ru/api/phones-data/ +export type PhoneUserInfoSpNova = Record; + +/* + Successful response example: + + { + "+79217122691": { + "operator": "Мегафон", + "country": "Россия", + "region": "Калининградская область", + "district": null, + "city": null, + "time": "11:04:16", + "timezone": "UTC+0200" + } + } +*/ diff --git a/backend/src/modules/data-enrichment/types/phone-user-info.ts b/backend/src/modules/data-enrichment/types/phone-user-info.ts new file mode 100644 index 0000000..00a1aaa --- /dev/null +++ b/backend/src/modules/data-enrichment/types/phone-user-info.ts @@ -0,0 +1,24 @@ +import { PhoneUserInfoDto } from '../dto'; + +export class PhoneUserInfo { + utcOffset: number | null; + country: string | null; + region: string | null; + city: string | null; + + constructor(data: Omit) { + this.utcOffset = data.utcOffset; + this.country = data.country; + this.region = data.region; + this.city = data.city; + } + + toDto(): PhoneUserInfoDto { + return { + utcOffset: this.utcOffset, + country: this.country, + region: this.region, + city: this.city, + }; + } +} diff --git a/backend/src/modules/documents/common/errors/document-template.error.ts b/backend/src/modules/documents/common/errors/document-template.error.ts new file mode 100644 index 0000000..b163a56 --- /dev/null +++ b/backend/src/modules/documents/common/errors/document-template.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class DocumentTemplateError extends ServiceError { + constructor(message = 'Wrong template') { + super({ errorCode: 'document.template.wrong_template', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/documents/common/errors/index.ts b/backend/src/modules/documents/common/errors/index.ts new file mode 100644 index 0000000..a6ad6ef --- /dev/null +++ b/backend/src/modules/documents/common/errors/index.ts @@ -0,0 +1 @@ +export * from './document-template.error'; diff --git a/backend/src/modules/documents/common/index.ts b/backend/src/modules/documents/common/index.ts new file mode 100644 index 0000000..f72bc43 --- /dev/null +++ b/backend/src/modules/documents/common/index.ts @@ -0,0 +1 @@ +export * from './errors'; diff --git a/backend/src/modules/documents/config/documents.config.ts b/backend/src/modules/documents/config/documents.config.ts new file mode 100644 index 0000000..ab45fbc --- /dev/null +++ b/backend/src/modules/documents/config/documents.config.ts @@ -0,0 +1,12 @@ +import { registerAs } from '@nestjs/config'; + +export interface DocumentsConfig { + host: string; +} + +export default registerAs( + 'documents', + (): DocumentsConfig => ({ + host: process.env.DOCUMENTS_HOST, + }), +); diff --git a/backend/src/modules/documents/config/index.ts b/backend/src/modules/documents/config/index.ts new file mode 100644 index 0000000..2b64df1 --- /dev/null +++ b/backend/src/modules/documents/config/index.ts @@ -0,0 +1 @@ +export * from './documents.config'; diff --git a/backend/src/modules/documents/document-generation/document-generation.controller.ts b/backend/src/modules/documents/document-generation/document-generation.controller.ts new file mode 100644 index 0000000..8362e3a --- /dev/null +++ b/backend/src/modules/documents/document-generation/document-generation.controller.ts @@ -0,0 +1,36 @@ +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; + +import { CheckDocumentDto, CheckDocumentResultDto, CreateDocumentDto } from './dto'; +import { DocumentGenerationService } from './document-generation.service'; + +@ApiTags('crm/documents/entities') +@Controller('/crm/documents') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class DocumentGenerationController { + constructor(private readonly service: DocumentGenerationService) {} + + @ApiOkResponse({ description: 'Check entity placeholders', type: CheckDocumentResultDto }) + @Get('check') + public async check( + @CurrentAuth() { accountId, user }: AuthData, + @Query() dto: CheckDocumentDto, + ): Promise { + return await this.service.check(accountId, user, dto); + } + + @ApiCreatedResponse({ description: 'Generated document', type: [FileLinkDto] }) + @Post('create') + public async create( + @CurrentAuth() { account, user }: AuthData, + @Body() dto: CreateDocumentDto, + ): Promise { + return await this.service.create(account, user, dto); + } +} diff --git a/backend/src/modules/documents/document-generation/document-generation.service.ts b/backend/src/modules/documents/document-generation/document-generation.service.ts new file mode 100644 index 0000000..b7ab8f8 --- /dev/null +++ b/backend/src/modules/documents/document-generation/document-generation.service.ts @@ -0,0 +1,499 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { lastValueFrom } from 'rxjs'; + +import PizZip from 'pizzip'; +import Docxtemplater, { DXT } from 'docxtemplater'; +import InspectModule from 'docxtemplater/js/inspect-module'; +import expressionParser from 'docxtemplater/expressions.js'; +import FormData from 'form-data'; + +import { DateUtil, FileLinkSource, isUnique, NotFoundError, NumberUtil } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { AccountSettingsService } from '@/modules/iam/account-settings/account-settings.service'; +import { MimeType } from '@/modules/storage/enums/mime-type.enum'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; +import { OrderService } from '@/modules/inventory/order/services/order.service'; +import { OrderHelper } from '@/modules/inventory/order/helper/order.helper'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { FieldOption } from '@/modules/entity/entity-field/field-option/entities/field-option.entity'; +import { FieldValue } from '@/modules/entity/entity-field/field-value/entities/field-value.entity'; +import { FieldPayloadChecklistItem } from '@/modules/entity/entity-field/field-value/types'; +import { Field } from '@/modules/entity/entity-field/field/entities/field.entity'; +import { FieldOptionService } from '@/modules/entity/entity-field/field-option/field-option.service'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; + +import { EntityTypeService } from '@/CRM/entity-type/entity-type.service'; +import { FileLink } from '@/CRM/Model/FileLink/FileLink'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; +import { FileLinkService } from '@/CRM/Service/FileLink/FileLinkService'; + +import { DocumentsConfig } from '../config'; +import { DocumentTemplateError } from '../common'; +import { DocumentTemplateService } from '../document-template'; +import { DocumentType, RussianCase } from './enums'; +import { CheckDocumentResultDto, CheckDocumentMissingFieldDto, CreateDocumentDto, CheckDocumentDto } from './dto'; +import { RussianName } from './types'; + +const CONVERT_DOCX_TO_PDF_PATH = 'convert/docx2pdf'; + +const SystemFields = { + currentDate: 'currentDate', + documentNumber: 'documentNumber', +}; +const OrderFields = { + _name: 'order', + number: 'number', + total: 'total', + currency: 'currency', + products: { + _name: 'products', + number: 'number', + name: 'name', + price: 'price', + currency: 'currency', + discount: 'discount', + tax: 'tax', + quantity: 'quantity', + amount: 'amount', + }, +}; + +enum EntityTypeField { + Name = 'name', + Owner = 'owner', +} + +const inclineName = (input: string, caseName: RussianCase) => { + if (!input) return input; + + return RussianName.fromFullName(input).getFullName(caseName); +}; + +const numberToWord = (input: string, language: string) => { + if (!input) return input; + + const value = Number(input); + if (!value) return input; + + return NumberUtil.toWord(value, { language }); +}; + +const numberToCurrency = (input: string, currency: string) => { + if (!input) return input; + + const value = Number(input); + if (!value) return input; + + return NumberUtil.toWord(value, { language: 'ru', currency }); +}; + +@Injectable() +export class DocumentGenerationService { + private _documentsHost: string; + + constructor( + private readonly configService: ConfigService, + private readonly httpService: HttpService, + private readonly accountSettingsService: AccountSettingsService, + private readonly userService: UserService, + private readonly storageService: StorageService, + @Inject(forwardRef(() => EntityService)) + private readonly entityService: EntityService, + private readonly entityTypeService: EntityTypeService, + private readonly fieldService: FieldService, + private readonly fieldValueService: FieldValueService, + private readonly fieldOptionService: FieldOptionService, + private readonly fileLinkService: FileLinkService, + private readonly orderService: OrderService, + private readonly documentTemplateService: DocumentTemplateService, + ) { + this._documentsHost = this.configService.get('documents').host; + } + + async check(accountId: number, user: User, dto: CheckDocumentDto) { + try { + const template = await this.documentTemplateService.getById(accountId, dto.templateId); + const content = await this.getTemplateContent(accountId, template.id); + const zip = new PizZip(content); + const inspectModule = new InspectModule(); + new Docxtemplater(zip, { modules: [inspectModule] }); + const parts = inspectModule.getAllStructuredTags(); + const allTags = this.getTemplateAllTags(parts, null); + + const data = await this.getDataForGeneration({ + accountId, + user, + documentNumber: template.createdCount + 1, + entityId: dto.entityId, + orderId: dto.orderId, + }); + const dataTags = this.getDataAllTags(null, data); + + const missingTags = allTags.filter((t) => !dataTags.includes(t)); + const missingFields: CheckDocumentMissingFieldDto[] = []; + const removeTags: string[] = []; + for (const tag of missingTags) { + const tagParts = tag.split('.'); + const entityType = await this.entityTypeService.findOne(accountId, { name: tagParts[0] }); + if (entityType) { + const field = await this.fieldService.findOne({ accountId, entityTypeId: entityType.id, name: tagParts[1] }); + if (field) { + removeTags.push(tag); + missingFields.push(new CheckDocumentMissingFieldDto({ entityTypeId: entityType.id, field: field.toDto() })); + } + } + } + + const isCorrect = missingTags.length === 0 && missingFields.length === 0; + return new CheckDocumentResultDto({ isCorrect, missingFields, missingTags }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e: unknown) { + throw new DocumentTemplateError(); + } + } + + private getTemplateAllTags(parts: Docxtemplater.DXT.Part[], prefix: string | null): string[] { + const tags: string[] = []; + for (const part of parts) { + if (part.type === 'placeholder') { + const tag = `${prefix ? `${prefix}.` : ''}${part.value.split(' ')[0]}`; + tags.push(tag); + if (part.subparsed?.length > 0) { + tags.push(...this.getTemplateAllTags(part.subparsed, tag)); + } + } + } + return tags.filter(isUnique); + } + + async create(account: Account, user: User, dto: CreateDocumentDto): Promise { + try { + let template = await this.documentTemplateService.getById(account.id, dto.templateId); + const content = await this.getTemplateContent(account.id, template.id); + const data = await this.getDataForGeneration({ + accountId: account.id, + user, + documentNumber: template.createdCount + 1, + entityId: dto.entityId, + orderId: dto.orderId, + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (expressionParser as any).filters.case = inclineName; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (expressionParser as any).filters.words = numberToWord; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (expressionParser as any).filters.currency = numberToCurrency; + + const zip = new PizZip(content); + const doc = new Docxtemplater(zip, { + paragraphLoop: true, + linebreaks: true, + parser: expressionParser, + nullGetter: this.nullGetter, + }); + doc.render(data); + const docBuffer = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE' }); + + template = await this.documentTemplateService.incrementCreatedCount(template); + + const fileLinks: FileLinkDto[] = []; + const documentName = `${template.name}_${template.createdCount}`; + if (dto.types.includes(DocumentType.DOCX)) { + const file = new StorageFile( + `${documentName}.${DocumentType.DOCX}`, + MimeType.DOCX, + docBuffer.byteLength, + docBuffer, + ); + const fileInfo = await this.storageService.storeCommonFile({ accountId: account.id, userId: user.id, file }); + if (fileInfo) { + const docLink = await this.fileLinkService.addFile( + account, + FileLinkSource.ENTITY_DOCUMENT, + dto.entityId, + fileInfo.id, + ); + if (docLink) fileLinks.push(docLink); + } + } + + if (dto.types.includes(DocumentType.PDF)) { + const pdfBuffer = await this.convertDocxToPdf(docBuffer, documentName); + + const file = new StorageFile( + `${documentName}.${DocumentType.PDF}`, + MimeType.PDF, + pdfBuffer.byteLength, + pdfBuffer, + ); + const fileInfo = await this.storageService.storeCommonFile({ accountId: account.id, userId: user.id, file }); + if (fileInfo) { + const pdfLink = await this.fileLinkService.addFile( + account, + FileLinkSource.ENTITY_DOCUMENT, + dto.entityId, + fileInfo.id, + ); + if (pdfLink) fileLinks.push(pdfLink); + } + } + + return fileLinks; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e: unknown) { + throw new DocumentTemplateError(); + } + } + + private async convertDocxToPdf(docBuffer: Buffer, documentName: string): Promise { + const form = new FormData(); + form.append('file', docBuffer, `${documentName}.${DocumentType.DOCX}`); + const response = await lastValueFrom( + this.httpService.post(CONVERT_DOCX_TO_PDF_PATH, form, { + baseURL: this._documentsHost, + headers: { + ...form.getHeaders(), + }, + responseType: 'stream', + }), + ); + + const pdfBuffer = await new Promise((resolve, reject) => { + const chunks = []; + response.data.on('data', (chunk) => chunks.push(chunk)); + response.data.on('end', () => resolve(Buffer.concat(chunks))); + response.data.on('error', reject); + }); + return pdfBuffer; + } + + private getDataAllTags(prefix: string | null, data: unknown): string[] { + const tagNames: string[] = []; + for (const [key, value] of Object.entries(data)) { + const tag = `${prefix ? `${prefix}.` : ''}${key}`; + tagNames.push(tag); + if (Array.isArray(value)) { + for (let idx = 0; idx < value.length; idx++) { + tagNames.push(...this.getDataAllTags(tag, value[idx])); + tagNames.push(...this.getDataAllTags(`${tag}[${idx}]`, value[idx])); + } + } else if (typeof value === 'object') { + tagNames.push(...this.getDataAllTags(tag, value)); + } + } + return tagNames.filter(isUnique); + } + + async getDataForGeneration({ + accountId, + user, + documentNumber, + entityId, + orderId, + }: { + accountId: number; + user?: User | null; + documentNumber?: number | null; + entityId: number; + orderId?: number | null; + }) { + const mainEntity = await this.entityService.findOne(accountId, { entityId }); + const linkedEntities = await this.entityService.findFirstLinkedEntityByType(accountId, entityId); + const entities = [mainEntity, ...linkedEntities]; + + const entityTypes = await this.getEntityTypesWithUniqueName(accountId); + const fieldOptionCache: FieldOption[] = []; + const data = {}; + data[`${SystemFields.currentDate}`] = DateUtil.formatPreset(DateUtil.now(), 'date'); + if (documentNumber) { + data[`${SystemFields.documentNumber}`] = documentNumber; + } + for (const entity of entities) { + const entityType = entityTypes.find((et) => et.id === entity.entityTypeId); + const entityTypeName = this.removeSpecialChars(entityType.name); + data[`${entityTypeName}`] = {}; + const fields = await this.getFieldsWithUniqueName(accountId, entity.entityTypeId); + const fieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + const owner = await this.userService.findOne({ accountId, id: entity.responsibleUserId }); + data[`${entityTypeName}`][`${EntityTypeField.Name}`] = entity.name; + data[`${entityTypeName}`][`${EntityTypeField.Owner}`] = owner.fullName; + for (const fieldValue of fieldValues) { + const field = fields.find((f) => f.id === fieldValue.fieldId); + if (field) { + const fieldValueObj = await this.getFieldValue(accountId, field, fieldValue, fieldOptionCache); + if (fieldValueObj) { + const fieldName = this.removeSpecialChars(field.name); + data[`${entityTypeName}`][`${fieldName}`] = fieldValueObj; + } + } + } + } + if (orderId && user) { + const order = await this.orderService.findOne(accountId, user, { orderId }, { expand: ['items'] }); + if (order) { + const accountSettings = await this.accountSettingsService.getOne(accountId); + const currencyName = this.getCurrencyName(order.currency, accountSettings.language); + const orderData = {}; + orderData[`${OrderFields.number}`] = order.orderNumber; + orderData[`${OrderFields.total}`] = order.totalAmount; + orderData[`${OrderFields.currency}`] = currencyName; + const productsData = []; + for (let idx = 0; idx < order.items.length; idx++) { + const productData = {}; + productData[`${OrderFields.products.number}`] = idx + 1; + productData[`${OrderFields.products.name}`] = order.items[idx].product.name; + productData[`${OrderFields.products.price}`] = order.items[idx].unitPrice; + productData[`${OrderFields.products.currency}`] = currencyName; + productData[`${OrderFields.products.discount}`] = order.items[idx].discount; + productData[`${OrderFields.products.tax}`] = order.items[idx].tax; + productData[`${OrderFields.products.quantity}`] = order.items[idx].quantity; + productData[`${OrderFields.products.amount}`] = OrderHelper.calcAmount(order.items[idx], order.taxIncluded); + productsData.push(productData); + } + orderData[`${OrderFields.products._name}`] = productsData; + data[`${OrderFields._name}`] = orderData; + } + } + return data; + } + + private getCurrencyName(currencyCode: string, locale = 'en-US'): string { + const names = new Intl.DisplayNames(locale, { type: 'currency' }); + return names.of(currencyCode); + } + + private async getTemplateContent(accountId: number, templateId: number) { + const fileLinks = await this.fileLinkService.findFileLinks(accountId, FileLinkSource.DOCUMENT_TEMPLATE, templateId); + if (fileLinks.length === 0) { + throw NotFoundError.withMessage(FileLink, `with type ${FileLinkSource.DOCUMENT_TEMPLATE} is not found`); + } + + const { content } = await this.storageService.getFile({ fileId: fileLinks[0].fileId, accountId }); + + return content; + } + + private async getEntityTypesWithUniqueName(accountId: number): Promise<{ id: number; name: string }[]> { + const entityTypes = await this.entityTypeService.findMany(accountId); + + const countNames = entityTypes.reduce((acc, entityType) => { + acc[entityType.name] = (acc[entityType.name] || 0) + 1; + return acc; + }, {}); + + return entityTypes.map((entityType) => { + if (countNames[entityType.name] > 1) { + entityType.name = entityType.name + entityType.id; + } + return entityType; + }); + } + + private async getFieldsWithUniqueName(accountId: number, entityTypeId: number): Promise { + const fields = await this.fieldService.findMany({ accountId, entityTypeId }); + + const countNames = fields.reduce((acc, field) => { + acc[field.name] = (acc[field.name] || 0) + 1; + return acc; + }, {}); + + return fields.map((field) => { + if (countNames[field.name] > 1) { + field.name = field.name + field.id; + } + return field; + }); + } + + private removeSpecialChars(input: string): string { + return input.replace(/[^\p{L}\p{N}]/gu, ''); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private nullGetter(_part: DXT.Part) { + return ''; + } + + private async getFieldValue( + accountId: number, + field: Field, + fieldValue: FieldValue, + fieldOptionCache: FieldOption[], + ) { + switch (field.type) { + case FieldType.Text: + case FieldType.RichText: + case FieldType.Link: + return fieldValue.getValue(); + case FieldType.Number: + case FieldType.Value: + case FieldType.Formula: + return fieldValue.getValue(); + case FieldType.MultiText: + case FieldType.Phone: + case FieldType.Email: + case FieldType.File: + return fieldValue.getValue()?.join(', '); + case FieldType.Switch: + return fieldValue.getValue() ? 'Yes' : 'No'; + case FieldType.Date: + return DateUtil.formatPreset(fieldValue.getValue(), 'date'); + case FieldType.Select: + case FieldType.ColoredSelect: { + let options = fieldOptionCache.filter((fo) => fo.fieldId === field.id); + if (options.length === 0) { + options = await this.fieldOptionService.findMany({ accountId, fieldId: field.id }); + fieldOptionCache.push(...options); + } + const fvOptionId = fieldValue.getValue(); + const option = options.find((o) => o.id === fvOptionId); + return option ? option.label : null; + } + case FieldType.MultiSelect: + case FieldType.ColoredMultiSelect: + case FieldType.CheckedMultiSelect: { + let multiOptions = fieldOptionCache.filter((fo) => fo.fieldId === field.id); + if (!multiOptions || multiOptions.length === 0) { + multiOptions = await this.fieldOptionService.findMany({ accountId, fieldId: field.id }); + fieldOptionCache.push(...multiOptions); + } + const fvOptionIds = fieldValue.getValue(); + const fieldOptions = fvOptionIds ? multiOptions.filter((o) => fvOptionIds.includes(o.id)) : null; + return fieldOptions ? fieldOptions.map((o) => o.label).join(', ') : null; + } + case FieldType.Participant: { + const fvUserId = fieldValue.getValue(); + const user = await this.userService.findOne({ accountId, id: fvUserId }); + return user ? user.fullName : null; + } + case FieldType.Participants: { + const fvUserIds = fieldValue.getValue(); + if (fvUserIds) { + const users = await Promise.all( + fvUserIds.map(async (id) => await this.userService.findOne({ accountId, id })), + ); + return users.length > 0 ? users.map((u) => u.fullName).join(', ') : null; + } + return null; + } + case FieldType.Checklist: { + const fvChecklist = fieldValue.getValue(); + return fvChecklist + ? fvChecklist + .filter((v) => v.checked) + .map((v) => v.text) + .join(', ') + : null; + } + } + } +} diff --git a/backend/src/modules/documents/document-generation/dto/check-document-missing-field.dto.ts b/backend/src/modules/documents/document-generation/dto/check-document-missing-field.dto.ts new file mode 100644 index 0000000..42e5f3e --- /dev/null +++ b/backend/src/modules/documents/document-generation/dto/check-document-missing-field.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { FieldDto } from '@/modules/entity/entity-field/field/dto/field.dto'; + +export class CheckDocumentMissingFieldDto { + @ApiProperty() + entityTypeId: number; + + @ApiProperty({ type: FieldDto }) + field: FieldDto; + + constructor({ entityTypeId, field }: CheckDocumentMissingFieldDto) { + this.entityTypeId = entityTypeId; + this.field = field; + } +} diff --git a/backend/src/modules/documents/document-generation/dto/check-document-result.dto.ts b/backend/src/modules/documents/document-generation/dto/check-document-result.dto.ts new file mode 100644 index 0000000..0bc01be --- /dev/null +++ b/backend/src/modules/documents/document-generation/dto/check-document-result.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { CheckDocumentMissingFieldDto } from './check-document-missing-field.dto'; + +export class CheckDocumentResultDto { + @ApiProperty() + isCorrect: boolean; + + @ApiProperty({ type: [CheckDocumentMissingFieldDto] }) + missingFields: CheckDocumentMissingFieldDto[]; + + @ApiProperty({ type: [String] }) + missingTags: string[]; + + constructor({ isCorrect, missingFields, missingTags }: CheckDocumentResultDto) { + this.isCorrect = isCorrect; + this.missingFields = missingFields; + this.missingTags = missingTags; + } +} diff --git a/backend/src/modules/documents/document-generation/dto/check-document.dto.ts b/backend/src/modules/documents/document-generation/dto/check-document.dto.ts new file mode 100644 index 0000000..8ff35d1 --- /dev/null +++ b/backend/src/modules/documents/document-generation/dto/check-document.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class CheckDocumentDto { + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty() + @IsNumber() + templateId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + orderId?: number | null; +} diff --git a/backend/src/modules/documents/document-generation/dto/create-document.dto.ts b/backend/src/modules/documents/document-generation/dto/create-document.dto.ts new file mode 100644 index 0000000..041e42a --- /dev/null +++ b/backend/src/modules/documents/document-generation/dto/create-document.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { DocumentType } from '../enums'; +import { CheckDocumentDto } from './check-document.dto'; + +export class CreateDocumentDto extends CheckDocumentDto { + @ApiProperty() + @IsArray() + types: DocumentType[]; +} diff --git a/backend/src/modules/documents/document-generation/dto/index.ts b/backend/src/modules/documents/document-generation/dto/index.ts new file mode 100644 index 0000000..8a7a061 --- /dev/null +++ b/backend/src/modules/documents/document-generation/dto/index.ts @@ -0,0 +1,4 @@ +export * from './check-document-missing-field.dto'; +export * from './check-document-result.dto'; +export * from './check-document.dto'; +export * from './create-document.dto'; diff --git a/backend/src/modules/documents/document-generation/enums/document-type.enum.ts b/backend/src/modules/documents/document-generation/enums/document-type.enum.ts new file mode 100644 index 0000000..771f7a2 --- /dev/null +++ b/backend/src/modules/documents/document-generation/enums/document-type.enum.ts @@ -0,0 +1,4 @@ +export enum DocumentType { + DOCX = 'docx', + PDF = 'pdf', +} diff --git a/backend/src/modules/documents/document-generation/enums/index.ts b/backend/src/modules/documents/document-generation/enums/index.ts new file mode 100644 index 0000000..de9d0dc --- /dev/null +++ b/backend/src/modules/documents/document-generation/enums/index.ts @@ -0,0 +1,2 @@ +export * from './document-type.enum'; +export * from './russian-case.enum'; diff --git a/backend/src/modules/documents/document-generation/enums/russian-case.enum.ts b/backend/src/modules/documents/document-generation/enums/russian-case.enum.ts new file mode 100644 index 0000000..3046952 --- /dev/null +++ b/backend/src/modules/documents/document-generation/enums/russian-case.enum.ts @@ -0,0 +1,8 @@ +export enum RussianCase { + NOMINATIVE = 'nominative', + GENITIVE = 'genitive', + DATIVE = 'dative', + ACCUSATIVE = 'accusative', + INSTRUMENTAL = 'instrumental', + PREPOSITIONAL = 'prepositional', +} diff --git a/backend/src/modules/documents/document-generation/index.ts b/backend/src/modules/documents/document-generation/index.ts new file mode 100644 index 0000000..3ed349c --- /dev/null +++ b/backend/src/modules/documents/document-generation/index.ts @@ -0,0 +1,4 @@ +export * from './document-generation.controller'; +export * from './document-generation.service'; +export * from './dto'; +export * from './enums'; diff --git a/backend/src/modules/documents/document-generation/types/index.ts b/backend/src/modules/documents/document-generation/types/index.ts new file mode 100644 index 0000000..ebcf229 --- /dev/null +++ b/backend/src/modules/documents/document-generation/types/index.ts @@ -0,0 +1 @@ +export * from './russian-name'; diff --git a/backend/src/modules/documents/document-generation/types/russian-name.ts b/backend/src/modules/documents/document-generation/types/russian-name.ts new file mode 100644 index 0000000..602eec1 --- /dev/null +++ b/backend/src/modules/documents/document-generation/types/russian-name.ts @@ -0,0 +1,30 @@ +import { incline } from 'lvovich'; + +import { type RussianCase } from '../enums'; + +export class RussianName { + first?: string | null; + middle?: string | null; + last?: string | null; + + constructor(last?: string | null, first?: string | null, middle?: string | null) { + this.first = first; + this.middle = middle; + this.last = last; + } + + public static fromFullName(fullName: string): RussianName { + const [last, first, middle] = fullName.split(' '); + + return new RussianName(last, first, middle); + } + + public getFullName(russianCase?: RussianCase): string { + if (russianCase) { + const { last, first, middle } = incline({ last: this.last, first: this.first, middle: this.middle }, russianCase); + return `${last}${first ? ` ${first}` : ''}${middle ? ` ${middle}` : ''}`; + } + + return `${this.last}${this.first ? ` ${this.first}` : ''}${this.middle ? ` ${this.middle}` : ''}`; + } +} diff --git a/backend/src/modules/documents/document-template/document-template.controller.ts b/backend/src/modules/documents/document-template/document-template.controller.ts new file mode 100644 index 0000000..89f9b2b --- /dev/null +++ b/backend/src/modules/documents/document-template/document-template.controller.ts @@ -0,0 +1,65 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { DocumentTemplateDto, CreateDocumentTemplateDto, DocumentTemplateInfo, UpdateDocumentTemplateDto } from './dto'; +import { DocumentTemplateService } from './document-template.service'; + +@ApiTags('crm/documents/templates') +@Controller('/crm/documents/templates') +@JwtAuthorized({ prefetch: { account: true } }) +export class DocumentTemplateController { + constructor(private readonly service: DocumentTemplateService) {} + + @ApiCreatedResponse({ description: 'Create document template', type: DocumentTemplateDto }) + @Post() + public async create( + @CurrentAuth() { account, userId }: AuthData, + @Body() dto: CreateDocumentTemplateDto, + ): Promise { + return await this.service.create(account, userId, dto); + } + + @ApiOkResponse({ description: 'Get document templates', type: [DocumentTemplateDto] }) + @Get() + public async getMany(@CurrentAuth() { account }: AuthData): Promise { + return await this.service.getDtoByAccount(account); + } + + @ApiOkResponse({ description: 'Get document template', type: DocumentTemplateDto }) + @Get(':id') + public async getOne( + @CurrentAuth() { account }: AuthData, + @Param('id', ParseIntPipe) id: number, + ): Promise { + return await this.service.getDtoById(account, id); + } + + @ApiCreatedResponse({ description: 'Document templates info', type: [DocumentTemplateInfo] }) + @Get('entity-type/:entityTypeId') + public async getAccessibleTemplates( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + ): Promise { + return await this.service.getAccessibleTemplates(accountId, userId, entityTypeId); + } + + @ApiCreatedResponse({ description: 'Document template', type: DocumentTemplateDto }) + @Put(':id') + public async update( + @CurrentAuth() { account }: AuthData, + @Param('id', ParseIntPipe) id: number, + @Body() dto: UpdateDocumentTemplateDto, + ): Promise { + return await this.service.update(account, id, dto); + } + + @ApiOkResponse({ description: 'Delete document template' }) + @Delete(':id') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('id', ParseIntPipe) id: number): Promise { + return await this.service.delete(accountId, id); + } +} diff --git a/backend/src/modules/documents/document-template/document-template.service.ts b/backend/src/modules/documents/document-template/document-template.service.ts new file mode 100644 index 0000000..556ba36 --- /dev/null +++ b/backend/src/modules/documents/document-template/document-template.service.ts @@ -0,0 +1,149 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { FileLinkSource, NotFoundError } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { FileLinkService } from '@/CRM/Service/FileLink/FileLinkService'; + +import { DocumentTemplateDto, DocumentTemplateInfo, CreateDocumentTemplateDto, UpdateDocumentTemplateDto } from './dto'; +import { DocumentTemplate, DocumentTemplateAccess, DocumentTemplateEntityType } from './entities'; + +@Injectable() +export class DocumentTemplateService { + constructor( + @InjectRepository(DocumentTemplate) + private readonly repositoryTemplate: Repository, + @InjectRepository(DocumentTemplateAccess) + private readonly repositoryTemplateUser: Repository, + @InjectRepository(DocumentTemplateEntityType) + private readonly repositoryTemplateEntityType: Repository, + private readonly fileLinkService: FileLinkService, + ) {} + + public async create(account: Account, userId: number, dto: CreateDocumentTemplateDto): Promise { + const template = await this.repositoryTemplate.save(new DocumentTemplate(account.id, dto.name, userId)); + if (dto.fileId) { + await this.fileLinkService.processFiles(account.id, FileLinkSource.DOCUMENT_TEMPLATE, template.id, [dto.fileId]); + } + if (dto.accessibleBy?.length > 0) { + await this.repositoryTemplateUser.insert( + dto.accessibleBy.map((userId) => new DocumentTemplateAccess(account.id, template.id, userId)), + ); + } + if (dto.entityTypeIds?.length > 0) { + await this.repositoryTemplateEntityType.insert( + dto.entityTypeIds.map((entityTypeId) => new DocumentTemplateEntityType(account.id, template.id, entityTypeId)), + ); + } + return await this.getDtoByTemplate(account, template); + } + + public async getById(accountId: number, id: number): Promise { + const template = await this.repositoryTemplate.findOne({ where: { id, accountId } }); + if (!template) { + throw NotFoundError.withId(DocumentTemplate, id); + } + return template; + } + + public async getDtoById(account: Account, id: number): Promise { + const template = await this.getById(account.id, id); + return await this.getDtoByTemplate(account, template); + } + + public async getDtoByAccount(account: Account): Promise { + const templates = await this.repositoryTemplate.find({ + where: { accountId: account.id }, + order: { createdAt: 'ASC' }, + }); + return await Promise.all(templates.map((template) => this.getDtoByTemplate(account, template))); + } + + public async getAccessibleTemplates( + accountId: number, + userId: number, + entityTypeId: number, + ): Promise { + const templates = await this.repositoryTemplate + .createQueryBuilder('template') + .leftJoin(DocumentTemplateAccess, 'accessible', 'template.id = accessible.document_template_id') + .leftJoin(DocumentTemplateEntityType, 'et', 'template.id = et.document_template_id') + .where('template.accountId = :accountId', { accountId }) + .andWhere( + new Brackets((qb) => { + qb.where('template.createdBy = :userId', { userId }).orWhere('accessible.userId = :userId', { userId }); + }), + ) + .andWhere('et.entityTypeId = :entityTypeId', { entityTypeId }) + .orderBy('template.created_at', 'ASC') + .getMany(); + return templates.map((template) => new DocumentTemplateInfo(template.id, template.name)); + } + + public async getDtoByTemplate(account: Account, template: DocumentTemplate): Promise { + const files = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.DOCUMENT_TEMPLATE, template.id); + const accessibleBy = await this.repositoryTemplateUser.find({ + where: { documentTemplateId: template.id }, + select: ['userId'], + }); + const entityTypeIds = await this.repositoryTemplateEntityType.find({ + where: { documentTemplateId: template.id }, + select: ['entityTypeId'], + }); + return new DocumentTemplateDto( + template.id, + template.name, + template.createdBy, + files?.[0] ?? null, + accessibleBy.map((access) => access.userId), + entityTypeIds.map((entityType) => entityType.entityTypeId), + ); + } + + public async update(account: Account, id: number, dto: UpdateDocumentTemplateDto): Promise { + const template = await this.repositoryTemplate.findOne({ where: { id, accountId: account.id } }); + if (!template) { + throw NotFoundError.withId(DocumentTemplate, id); + } + template.update(dto.name); + await this.repositoryTemplate.save(template); + + await this.fileLinkService.processFiles( + account.id, + FileLinkSource.DOCUMENT_TEMPLATE, + id, + dto.fileId ? [dto.fileId] : [], + ); + + await this.repositoryTemplateUser.delete({ documentTemplateId: id }); + if (dto.accessibleBy?.length > 0) { + await this.repositoryTemplateUser.insert( + dto.accessibleBy.map((userId) => ({ documentTemplateId: id, userId, accountId: account.id })), + ); + } + + await this.repositoryTemplateEntityType.delete({ documentTemplateId: id }); + if (dto.entityTypeIds?.length > 0) { + await this.repositoryTemplateEntityType.insert( + dto.entityTypeIds.map((entityTypeId) => ({ documentTemplateId: id, entityTypeId, accountId: account.id })), + ); + } + + return await this.getDtoByTemplate(account, template); + } + + public async delete(accountId: number, id: number): Promise { + await this.fileLinkService.processFiles(accountId, FileLinkSource.DOCUMENT_TEMPLATE, id, []); + const result = await this.repositoryTemplate.delete({ id, accountId }); + if (result.affected === 0) { + throw NotFoundError.withId(DocumentTemplate, id); + } + } + + public async incrementCreatedCount(template: DocumentTemplate): Promise { + template.incrementCreatedCount(); + return await this.repositoryTemplate.save(template); + } +} diff --git a/backend/src/modules/documents/document-template/dto/create-document-template.dto.ts b/backend/src/modules/documents/document-template/dto/create-document-template.dto.ts new file mode 100644 index 0000000..93cfa86 --- /dev/null +++ b/backend/src/modules/documents/document-template/dto/create-document-template.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsOptional, IsString } from 'class-validator'; + +export class CreateDocumentTemplateDto { + @ApiProperty() + @IsString() + name: string; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + fileId: string | null; + + @ApiProperty() + @IsArray() + accessibleBy: number[]; + + @ApiProperty() + @IsArray() + entityTypeIds: number[]; +} diff --git a/backend/src/modules/documents/document-template/dto/document-template-info.dto.ts b/backend/src/modules/documents/document-template/dto/document-template-info.dto.ts new file mode 100644 index 0000000..8d7a5d8 --- /dev/null +++ b/backend/src/modules/documents/document-template/dto/document-template-info.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class DocumentTemplateInfo { + @ApiProperty() + id: number; + + @ApiProperty() + name: string; + + constructor(id: number, name: string) { + this.id = id; + this.name = name; + } +} diff --git a/backend/src/modules/documents/document-template/dto/document-template.dto.ts b/backend/src/modules/documents/document-template/dto/document-template.dto.ts new file mode 100644 index 0000000..aafa5c7 --- /dev/null +++ b/backend/src/modules/documents/document-template/dto/document-template.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; + +export class DocumentTemplateDto { + @ApiProperty() + id: number; + + @ApiProperty() + name: string; + + @ApiProperty() + createdBy: number; + + @ApiProperty({ nullable: true }) + file: FileLinkDto | null; + + @ApiProperty() + accessibleBy: number[]; + + @ApiProperty() + entityTypeIds: number[]; + + constructor( + id: number, + name: string, + createdBy: number, + file: FileLinkDto | null, + accessibleBy: number[], + entityTypeIds: number[], + ) { + this.id = id; + this.name = name; + this.createdBy = createdBy; + this.file = file; + this.accessibleBy = accessibleBy; + this.entityTypeIds = entityTypeIds; + } +} diff --git a/backend/src/modules/documents/document-template/dto/index.ts b/backend/src/modules/documents/document-template/dto/index.ts new file mode 100644 index 0000000..053d24a --- /dev/null +++ b/backend/src/modules/documents/document-template/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-document-template.dto'; +export * from './document-template-info.dto'; +export * from './document-template.dto'; +export * from './update-document-template.dto'; diff --git a/backend/src/modules/documents/document-template/dto/update-document-template.dto.ts b/backend/src/modules/documents/document-template/dto/update-document-template.dto.ts new file mode 100644 index 0000000..379e910 --- /dev/null +++ b/backend/src/modules/documents/document-template/dto/update-document-template.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsOptional, IsString } from 'class-validator'; + +export class UpdateDocumentTemplateDto { + @ApiProperty() + @IsString() + name: string; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + fileId: string | null; + + @ApiProperty() + @IsArray() + accessibleBy: number[]; + + @ApiProperty() + @IsArray() + entityTypeIds: number[]; +} diff --git a/backend/src/modules/documents/document-template/entities/document-template-access.entity.ts b/backend/src/modules/documents/document-template/entities/document-template-access.entity.ts new file mode 100644 index 0000000..011d1c9 --- /dev/null +++ b/backend/src/modules/documents/document-template/entities/document-template-access.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class DocumentTemplateAccess { + @PrimaryColumn() + documentTemplateId: number; + + @PrimaryColumn() + userId: number; + + @Column() + accountId: number; + + constructor(accountId: number, documentTemplateId: number, userId: number) { + this.accountId = accountId; + this.documentTemplateId = documentTemplateId; + this.userId = userId; + } +} diff --git a/backend/src/modules/documents/document-template/entities/document-template-entity-type.entity.ts b/backend/src/modules/documents/document-template/entities/document-template-entity-type.entity.ts new file mode 100644 index 0000000..a5b2218 --- /dev/null +++ b/backend/src/modules/documents/document-template/entities/document-template-entity-type.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class DocumentTemplateEntityType { + @PrimaryColumn() + documentTemplateId: number; + + @PrimaryColumn() + entityTypeId: number; + + @Column() + accountId: number; + + constructor(accountId: number, documentTemplateId: number, entityTypeId: number) { + this.accountId = accountId; + this.documentTemplateId = documentTemplateId; + this.entityTypeId = entityTypeId; + } +} diff --git a/backend/src/modules/documents/document-template/entities/document-template.entity.ts b/backend/src/modules/documents/document-template/entities/document-template.entity.ts new file mode 100644 index 0000000..b1f160c --- /dev/null +++ b/backend/src/modules/documents/document-template/entities/document-template.entity.ts @@ -0,0 +1,39 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +@Entity() +export class DocumentTemplate { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + createdBy: number; + + @Column() + createdCount: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, name: string, createdBy: number, createdAt?: Date) { + this.name = name; + this.createdBy = createdBy; + this.accountId = accountId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public update(name: string): void { + this.name = name; + } + + public incrementCreatedCount(): void { + this.createdCount++; + } +} diff --git a/backend/src/modules/documents/document-template/entities/index.ts b/backend/src/modules/documents/document-template/entities/index.ts new file mode 100644 index 0000000..1bcfd46 --- /dev/null +++ b/backend/src/modules/documents/document-template/entities/index.ts @@ -0,0 +1,3 @@ +export * from './document-template-access.entity'; +export * from './document-template-entity-type.entity'; +export * from './document-template.entity'; diff --git a/backend/src/modules/documents/document-template/index.ts b/backend/src/modules/documents/document-template/index.ts new file mode 100644 index 0000000..32bf872 --- /dev/null +++ b/backend/src/modules/documents/document-template/index.ts @@ -0,0 +1,4 @@ +export * from './document-template.controller'; +export * from './document-template.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/documents/documents.module.ts b/backend/src/modules/documents/documents.module.ts new file mode 100644 index 0000000..4d8fc38 --- /dev/null +++ b/backend/src/modules/documents/documents.module.ts @@ -0,0 +1,35 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; +import { InventoryModule } from '@/modules/inventory/inventory.module'; +import { EntityFieldModule } from '@/modules/entity/entity-field/entity-field.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import documentsConfig from './config/documents.config'; +import { + DocumentTemplate, + DocumentTemplateAccess, + DocumentTemplateEntityType, + DocumentTemplateController, + DocumentTemplateService, +} from './document-template'; +import { DocumentGenerationController, DocumentGenerationService } from './document-generation'; + +@Module({ + imports: [ + ConfigModule.forFeature(documentsConfig), + TypeOrmModule.forFeature([DocumentTemplate, DocumentTemplateAccess, DocumentTemplateEntityType]), + IAMModule, + StorageModule, + EntityFieldModule, + forwardRef(() => CrmModule), + forwardRef(() => InventoryModule), + ], + controllers: [DocumentTemplateController, DocumentGenerationController], + providers: [DocumentTemplateService, DocumentGenerationService], + exports: [DocumentGenerationService], +}) +export class DocumentsModule {} diff --git a/backend/src/modules/documents/index.ts b/backend/src/modules/documents/index.ts new file mode 100644 index 0000000..04aab1b --- /dev/null +++ b/backend/src/modules/documents/index.ts @@ -0,0 +1,4 @@ +export * from './config'; +export * from './document-generation'; +export * from './document-template'; +export * from './documents.module'; diff --git a/backend/src/modules/entity/entity-event/dto/create-entity-event.dto.ts b/backend/src/modules/entity/entity-event/dto/create-entity-event.dto.ts new file mode 100644 index 0000000..e7bee50 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/create-entity-event.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { EntityEventDto } from './entity-event.dto'; + +export class CreateEntityEventDto extends OmitType(EntityEventDto, ['id'] as const) {} diff --git a/backend/src/modules/entity/entity-event/dto/delete-entity-event.dto.ts b/backend/src/modules/entity/entity-event/dto/delete-entity-event.dto.ts new file mode 100644 index 0000000..3ad6d38 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/delete-entity-event.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { EntityEventDto } from './entity-event.dto'; + +export class DeleteEntityEventDto extends OmitType(EntityEventDto, ['id', 'createdAt'] as const) {} diff --git a/backend/src/modules/entity/entity-event/dto/entity-event-data.dto.ts b/backend/src/modules/entity/entity-event/dto/entity-event-data.dto.ts new file mode 100644 index 0000000..d6c0dc8 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/entity-event-data.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { EntityEventType } from '../enums/entity-event-type.enum'; + +export class EntityEventDataDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsEnum(EntityEventType) + type: EntityEventType; + + @ApiProperty() + data: any; + + @ApiProperty() + createdAt: string; + + constructor(id: number, type: EntityEventType, data: any, createdAt: string) { + this.id = id; + this.type = type; + this.data = data; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/entity/entity-event/dto/entity-event-item.dto.ts b/backend/src/modules/entity/entity-event/dto/entity-event-item.dto.ts new file mode 100644 index 0000000..60a48c7 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/entity-event-item.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { EntityEventType } from '../enums/entity-event-type.enum'; + +export class EntityEventItemDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsEnum(EntityEventType) + type: EntityEventType; + + @ApiProperty() + data: any; + + @ApiProperty() + createdAt: string; + + constructor(objectId: number, type: EntityEventType, data: object, createdAt: string) { + this.id = objectId; + this.type = type; + this.data = data; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/entity/entity-event/dto/entity-event.dto.ts b/backend/src/modules/entity/entity-event/dto/entity-event.dto.ts new file mode 100644 index 0000000..8671ed9 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/entity-event.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { EntityEventType } from '../enums/entity-event-type.enum'; + +export class EntityEventDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty() + @IsNumber() + objectId: number; + + @ApiProperty() + @IsEnum(EntityEventType) + type: EntityEventType; + + @ApiProperty() + createdAt: string; + + constructor(id: number, entityId: number, objectId: number, type: EntityEventType, createdAt: string) { + this.id = id; + this.entityId = entityId; + this.objectId = objectId; + this.type = type; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/entity/entity-event/dto/get-entity-event.result.ts b/backend/src/modules/entity/entity-event/dto/get-entity-event.result.ts new file mode 100644 index 0000000..1e4a721 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/get-entity-event.result.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { EntityEventDataDto } from './entity-event-data.dto'; + +export class GetEntityEventResult { + @ApiProperty({ type: [EntityEventDataDto] }) + result: EntityEventDataDto[]; + + @ApiProperty() + meta: PagingMeta; + + constructor(result: EntityEventDataDto[], meta: PagingMeta) { + this.result = result; + this.meta = meta; + } +} diff --git a/backend/src/modules/entity/entity-event/dto/index.ts b/backend/src/modules/entity/entity-event/dto/index.ts new file mode 100644 index 0000000..7b15d76 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/index.ts @@ -0,0 +1,7 @@ +export * from './create-entity-event.dto'; +export * from './delete-entity-event.dto'; +export * from './entity-event-data.dto'; +export * from './entity-event-item.dto'; +export * from './entity-event.dto'; +export * from './get-entity-event.result'; +export * from './update-entity-event.dto'; diff --git a/backend/src/modules/entity/entity-event/dto/update-entity-event.dto.ts b/backend/src/modules/entity/entity-event/dto/update-entity-event.dto.ts new file mode 100644 index 0000000..4f880b0 --- /dev/null +++ b/backend/src/modules/entity/entity-event/dto/update-entity-event.dto.ts @@ -0,0 +1,29 @@ +import { PickType } from '@nestjs/swagger'; + +import { type EntityEventType } from '../enums/entity-event-type.enum'; +import { EntityEventDto } from './entity-event.dto'; + +export class UpdateEntityEventDto extends PickType(EntityEventDto, [ + 'entityId', + 'objectId', + 'type', + 'createdAt', +] as const) { + oldEntityId: number | null; + + constructor( + entityId: number, + objectId: number, + type: EntityEventType, + createdAt: string, + oldEntityId: number | null, + ) { + super(entityId, objectId, type, createdAt); + + this.entityId = entityId; + this.objectId = objectId; + this.type = type; + this.createdAt = createdAt; + this.oldEntityId = oldEntityId; + } +} diff --git a/backend/src/modules/entity/entity-event/entities/entity-event.entity.ts b/backend/src/modules/entity/entity-event/entities/entity-event.entity.ts new file mode 100644 index 0000000..e873887 --- /dev/null +++ b/backend/src/modules/entity/entity-event/entities/entity-event.entity.ts @@ -0,0 +1,50 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { EntityEventType } from '../enums/entity-event-type.enum'; +import { CreateEntityEventDto } from '../dto/create-entity-event.dto'; +import { EntityEventDto } from '../dto/entity-event.dto'; + +@Entity() +export class EntityEvent { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + entityId: number; + + @Column() + objectId: number; + + @Column() + type: EntityEventType; + + @Column() + createdAt: Date; + + constructor(accountId: number, entityId: number, objectId: number, type: EntityEventType, createdAt: Date) { + this.accountId = accountId; + this.entityId = entityId; + this.objectId = objectId; + this.type = type; + this.createdAt = createdAt; + } + + public static fromDto(accountId: number, dto: CreateEntityEventDto): EntityEvent { + return new EntityEvent( + accountId, + dto.entityId, + dto.objectId, + dto.type, + dto.createdAt ? DateUtil.fromISOString(dto.createdAt) : DateUtil.now(), + ); + } + + public toDto(): EntityEventDto { + return new EntityEventDto(this.id, this.entityId, this.objectId, this.type, this.createdAt.toISOString()); + } +} diff --git a/backend/src/modules/entity/entity-event/entities/index.ts b/backend/src/modules/entity/entity-event/entities/index.ts new file mode 100644 index 0000000..ecf93f4 --- /dev/null +++ b/backend/src/modules/entity/entity-event/entities/index.ts @@ -0,0 +1 @@ +export * from './entity-event.entity'; diff --git a/backend/src/modules/entity/entity-event/entity-event.controller.ts b/backend/src/modules/entity/entity-event/entity-event.controller.ts new file mode 100644 index 0000000..2f528b6 --- /dev/null +++ b/backend/src/modules/entity/entity-event/entity-event.controller.ts @@ -0,0 +1,31 @@ +import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { GetEntityEventResult } from './dto'; +import { EntityEventFilter } from './enums'; +import { EntityEventService } from './entity-event.service'; + +@ApiTags('crm/entity-event') +@Controller('crm/entities/:entityId/events') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +@TransformToDto() +export class EntityEventController { + constructor(private readonly service: EntityEventService) {} + + @ApiCreatedResponse({ description: 'EntityEvents', type: GetEntityEventResult }) + @Get(':filter') + public async getEntityEventItems( + @CurrentAuth() { account, user }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + //TODO Unify query interface, move filters from parameters to query body + @Param('filter') filter: EntityEventFilter, + @Query() paging: PagingQuery, + ): Promise { + return this.service.findEntityEventItems(account, user, entityId, filter, paging); + } +} diff --git a/backend/src/modules/entity/entity-event/entity-event.handler.ts b/backend/src/modules/entity/entity-event/entity-event.handler.ts new file mode 100644 index 0000000..aaad593 --- /dev/null +++ b/backend/src/modules/entity/entity-event/entity-event.handler.ts @@ -0,0 +1,269 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { FileLinkSource } from '@/common'; +import { + ProductOrderCreatedEvent, + ProductOrderEvent, + ProductsEventType, + RentalOrderCreatedEvent, + RentalOrderEvent, + ShipmentCreatedEvent, + ShipmentDeletedEvent, +} from '@/modules/inventory/common'; +import { TelephonyEventType, TelephonyCallCreatedEvent, TelephonyCallUpdatedEvent } from '@/modules/telephony/common'; +import { + ActivityCreatedEvent, + ActivityEvent, + CrmEventType, + FileLinkCreatedEvent, + FileLinkEvent, + NoteCreatedEvent, + NoteEvent, + TaskCreatedEvent, + TaskEvent, + TaskUpdatedEvent, +} from '@/CRM/common'; +import { MailEventType, MailMessageEvent } from '@/Mailing/common'; + +import { EntityEventType } from './enums'; +import { EntityEventService } from './entity-event.service'; + +@Injectable() +export class EntityEventHandler { + constructor(private readonly entityEventService: EntityEventService) {} + + @OnEvent(CrmEventType.ActivityCreated, { async: true }) + public async onActivityCreated(event: ActivityCreatedEvent) { + if (event.entityId && event.activityId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.activityId, + type: EntityEventType.Activity, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(CrmEventType.ActivityDeleted, { async: true }) + public async onActivityDeleted(event: ActivityEvent) { + if (event.entityId && event.activityId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.activityId, + type: EntityEventType.Activity, + }); + } + } + + @OnEvent(CrmEventType.FileLinkCreated, { async: true }) + public async onFileLinkCreated(event: FileLinkCreatedEvent) { + if (event.sourceId && event.fileLinkId && event.sourceType === FileLinkSource.ENTITY_DOCUMENT) { + await this.entityEventService.create(event.accountId, { + entityId: event.sourceId, + objectId: event.fileLinkId, + type: EntityEventType.Document, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(CrmEventType.FileLinkDeleted, { async: true }) + public async onFileLinkDeleted(event: FileLinkEvent) { + if (event.sourceId && event.fileLinkId && event.sourceType === FileLinkSource.ENTITY_DOCUMENT) { + await this.entityEventService.delete(event.accountId, { + entityId: event.sourceId, + objectId: event.fileLinkId, + type: EntityEventType.Document, + }); + } + } + + @OnEvent(MailEventType.MailMessageReceived, { async: true }) + public async onMailMessageReceived(event: MailMessageEvent) { + if (event.entityId && event.messageId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.messageId, + type: EntityEventType.Mail, + createdAt: event.messageDate, + }); + } + } + + @OnEvent(MailEventType.MailMessageLinked, { async: true }) + public async onMailMessageLinked(event: MailMessageEvent) { + if (event.entityId && event.messageId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.messageId, + type: EntityEventType.Mail, + createdAt: event.messageDate, + }); + } + } + + @OnEvent(MailEventType.MailMessageDeleted, { async: true }) + public async onMailMessageDeleted(event: MailMessageEvent) { + if (event.entityId && event.messageId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.messageId, + type: EntityEventType.Mail, + }); + } + } + + @OnEvent(CrmEventType.NoteCreated, { async: true }) + public async onNoteCreated(event: NoteCreatedEvent) { + if (event.entityId && event.noteId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.noteId, + type: EntityEventType.Note, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(CrmEventType.NoteDeleted, { async: true }) + public async onNoteDeleted(event: NoteEvent) { + if (event.entityId && event.noteId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.noteId, + type: EntityEventType.Note, + }); + } + } + + @OnEvent(ProductsEventType.ProductOrderCreated, { async: true }) + public async onProductOrderCreated(event: ProductOrderCreatedEvent) { + if (event.entityId && event.orderId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.orderId, + type: EntityEventType.Order, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(ProductsEventType.ProductOrderDeleted, { async: true }) + public async onProductOrderDeleted(event: ProductOrderEvent) { + if (event.entityId && event.orderId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.orderId, + type: EntityEventType.Order, + }); + } + } + + @OnEvent(ProductsEventType.RentalOrderCreated, { async: true }) + public async onProductRentalOrderCreated(event: RentalOrderCreatedEvent) { + if (event.entityId && event.rentalOrderId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.rentalOrderId, + type: EntityEventType.RentalOrder, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(ProductsEventType.RentalOrderDeleted, { async: true }) + public async onProductRentalOrderDeleted(event: RentalOrderEvent) { + if (event.entityId && event.rentalOrderId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.rentalOrderId, + type: EntityEventType.RentalOrder, + }); + } + } + + @OnEvent(ProductsEventType.ShipmentCreated, { async: true }) + public async onShipmentCreated(event: ShipmentCreatedEvent) { + if (event.entityId && event.shipmentId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.shipmentId, + type: EntityEventType.Shipment, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(ProductsEventType.ShipmentDeleted, { async: true }) + public async onShipmentDeleted(event: ShipmentDeletedEvent) { + if (event.entityId && event.shipmentId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.shipmentId, + type: EntityEventType.Shipment, + }); + } + } + + @OnEvent(CrmEventType.TaskCreated, { async: true }) + public async onTaskCreated(event: TaskCreatedEvent) { + if (event.entityId && event.taskId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.taskId, + type: EntityEventType.Task, + createdAt: event.createdAt.toISOString(), + }); + } + } + + @OnEvent(CrmEventType.TaskUpdated, { async: true }) + public async onTaskUpdated(event: TaskUpdatedEvent) { + if (event.entityId !== event.prevEntityId && event.taskId) { + await this.entityEventService.update(event.accountId, { + entityId: event.entityId, + objectId: event.taskId, + type: EntityEventType.Task, + createdAt: event.createdAt.toISOString(), + oldEntityId: event.prevEntityId, + }); + } + } + + @OnEvent(CrmEventType.TaskDeleted, { async: true }) + public async onTaskDeleted(event: TaskEvent) { + if (event.entityId && event.taskId) { + await this.entityEventService.delete(event.accountId, { + entityId: event.entityId, + objectId: event.taskId, + type: EntityEventType.Task, + }); + } + } + + @OnEvent(TelephonyEventType.TelephonyCallCreated, { async: true }) + public async onTelephonyCallCreated(event: TelephonyCallCreatedEvent) { + if (event.entityId && event.callId) { + await this.entityEventService.create(event.accountId, { + entityId: event.entityId, + objectId: event.callId, + type: EntityEventType.Call, + createdAt: event.createdAt, + }); + } + } + + @OnEvent(TelephonyEventType.TelephonyCallUpdated, { async: true }) + public async onTelephonyCallUpdated(event: TelephonyCallUpdatedEvent) { + if (event.entityId && event.callId) { + await this.entityEventService.update(event.accountId, { + entityId: event.entityId, + objectId: event.callId, + type: EntityEventType.Call, + createdAt: event.createdAt, + oldEntityId: event.oldEntityId, + }); + } + } +} diff --git a/backend/src/modules/entity/entity-event/entity-event.module.ts b/backend/src/modules/entity/entity-event/entity-event.module.ts new file mode 100644 index 0000000..d7c5da7 --- /dev/null +++ b/backend/src/modules/entity/entity-event/entity-event.module.ts @@ -0,0 +1,31 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { OrderModule } from '@/modules/inventory/order/order.module'; +import { RentalOrderModule } from '@/modules/inventory/rental-order/rental-order.module'; +import { ShipmentModule } from '@/modules/inventory/shipment/shipment.module'; +import { TelephonyModule } from '@/modules/telephony/telephony.module'; +import { CrmModule } from '@/CRM/crm.module'; +import { MailingModule } from '@/Mailing/MailingModule'; + +import { EntityEvent } from './entities/entity-event.entity'; +import { EntityEventController } from './entity-event.controller'; +import { EntityEventHandler } from './entity-event.handler'; +import { EntityEventService } from './entity-event.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([EntityEvent]), + IAMModule, + CrmModule, + OrderModule, + RentalOrderModule, + ShipmentModule, + MailingModule, + TelephonyModule, + ], + controllers: [EntityEventController], + providers: [EntityEventHandler, EntityEventService], +}) +export class EntityEventModule {} diff --git a/backend/src/modules/entity/entity-event/entity-event.service.ts b/backend/src/modules/entity/entity-event/entity-event.service.ts new file mode 100644 index 0000000..d46dd99 --- /dev/null +++ b/backend/src/modules/entity/entity-event/entity-event.service.ts @@ -0,0 +1,261 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository, SelectQueryBuilder } from 'typeorm'; + +import { PagingQuery, PagingMeta } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { OrderService } from '@/modules/inventory/order/services/order.service'; +import { RentalOrderService } from '@/modules/inventory/rental-order/services/rental-order.service'; +import { ShipmentService } from '@/modules/inventory/shipment/shipment.service'; +import { VoximplantCallService } from '@/modules/telephony/voximplant/voximplant-call/voximplant-call.service'; + +import { EntityLinkService } from '@/CRM/entity-link/entity-link.service'; +import { FileLinkService } from '@/CRM/Service/FileLink/FileLinkService'; +import { MailMessageService } from '@/Mailing/Service/MailMessage/MailMessageService'; +import { ActivityService } from '@/CRM/activity/activity.service'; +import { NoteService } from '@/CRM/note/note.service'; +import { TaskService } from '@/CRM/task/task.service'; + +import { + CreateEntityEventDto, + GetEntityEventResult, + EntityEventDataDto, + UpdateEntityEventDto, + DeleteEntityEventDto, + EntityEventItemDto, +} from './dto'; +import { EntityEvent } from './entities'; +import { EntityEventType, EntityEventFilter } from './enums'; + +const ExcludeEventTypes = [EntityEventType.Order, EntityEventType.RentalOrder, EntityEventType.Shipment]; + +@Injectable() +export class EntityEventService { + constructor( + @InjectRepository(EntityEvent) + private readonly repository: Repository, + private readonly activityService: ActivityService, + private readonly entityLinkService: EntityLinkService, + private readonly fileLinkService: FileLinkService, + private readonly mailMessageService: MailMessageService, + private readonly noteService: NoteService, + private readonly orderService: OrderService, + private readonly rentalOrderService: RentalOrderService, + private readonly shipmentService: ShipmentService, + private readonly taskService: TaskService, + private readonly telephonyCallService: VoximplantCallService, + ) {} + + public async create(accountId: number, dto: CreateEntityEventDto): Promise { + //TODO: Check do we need to check existence of same entity event + return this.repository.save(EntityEvent.fromDto(accountId, dto)); + } + + public async findEntityEventItems( + account: Account, + user: User, + entityId: number, + activeFilter = EntityEventFilter.All, + paging: PagingQuery, + ): Promise { + const qb = await this.getEntityEventsQuery(account.id, entityId, activeFilter); + const total = await qb.clone().getCount(); + + let currentOffset = paging.skip; + const resultData: EntityEventDataDto[] = []; + + while (resultData.length < paging.take && currentOffset < total) { + const currentLimit = paging.take - resultData.length; + const entityEventItems = await qb.clone().offset(currentOffset).limit(currentLimit).getMany(); + for (const entityEventItem of entityEventItems) { + const dataObject: object = await this.findEntityEventItemData(account, user, entityEventItem); + if (dataObject) { + //TODO This is a temporary solution to filter mail objects with same thread. + // Needs to be refactored to have a link to threadId in entityEvent table + const isMailWithUniqueThreadId = this.checkIsMailWithUniqueThreadId(entityEventItem, dataObject, resultData); + if (!isMailWithUniqueThreadId) { + continue; + } + const dto = new EntityEventDataDto( + entityEventItem.id, + entityEventItem.type, + dataObject, + entityEventItem.createdAt.toISOString(), + ); + resultData.push(dto); + } + } + currentOffset += currentLimit; + } + + return new GetEntityEventResult(resultData, new PagingMeta(currentOffset, total)); + } + + public async update(accountId: number, dto: UpdateEntityEventDto): Promise { + if (!dto.oldEntityId) { + return this.create(accountId, dto); + } else if (!dto.entityId) { + await this.delete(accountId, dto); + return null; + } else { + const event = await this.repository.findOneBy({ + accountId, + objectId: dto.objectId, + entityId: dto.oldEntityId, + type: dto.type, + }); + event.entityId = dto.entityId; + return this.repository.save(event); + } + } + + public async delete(accountId: number, dto: DeleteEntityEventDto): Promise { + await this.repository.delete({ + accountId: accountId, + objectId: dto.objectId, + type: dto.type, + }); + } + + private async findEntityEventItemData( + account: Account, + user: User, + entityEvent: EntityEvent, + ): Promise { + const { objectId, entityId } = entityEvent; + try { + switch (entityEvent.type) { + case EntityEventType.Activity: + return this.activityService.findDtoForId(account, user, objectId); + case EntityEventType.Call: { + const call = await this.telephonyCallService.findOneFull(account.id, user, { id: objectId }); + return call ? call.toDto() : null; + } + case EntityEventType.Document: + return this.fileLinkService.findDtoById(account, objectId); + case EntityEventType.Mail: + return this.mailMessageService.getThreadForMessageId(account.id, user, objectId); + case EntityEventType.Note: + return this.noteService.findOneDto({ account, filter: { entityId, noteId: objectId } }); + case EntityEventType.Order: { + const order = await this.orderService.findOne(account.id, user, { orderId: objectId }); + return order ? order.toDto() : null; + } + case EntityEventType.RentalOrder: { + const rentalOrder = await this.rentalOrderService.getOne(account.id, user, null, objectId); + return rentalOrder ? rentalOrder.toDto() : null; + } + case EntityEventType.Shipment: { + const shipment = await this.shipmentService.findOne({ + accountId: account.id, + user, + filter: { shipmentId: objectId }, + }); + return shipment ? shipment.toDto() : null; + } + case EntityEventType.Task: + return this.taskService.findDtoById(account, user, objectId); + } + } catch { + return null; + } + } + + private async getEntityEventsQuery( + accountId: number, + entityId: number, + activeFilter: EntityEventFilter, + ): Promise> { + let entityEventTypes: string[] | null = null; + switch (activeFilter) { + case EntityEventFilter.Activities: + entityEventTypes = [EntityEventType.Activity]; + break; + case EntityEventFilter.Calls: + entityEventTypes = [EntityEventType.Call]; + break; + //TODO Delete 'Files' after synchronization with frontend + case EntityEventFilter.Documents: + entityEventTypes = [EntityEventType.Document]; + break; + case EntityEventFilter.Files || EntityEventFilter.Documents: + entityEventTypes = [EntityEventType.Document]; + break; + case EntityEventFilter.Orders: + entityEventTypes = [EntityEventType.Order, EntityEventType.RentalOrder]; + break; + case EntityEventFilter.Mail: + entityEventTypes = [EntityEventType.Mail]; + break; + case EntityEventFilter.Notes: + entityEventTypes = [EntityEventType.Note]; + break; + case EntityEventFilter.Shipments: + entityEventTypes = [EntityEventType.Shipment]; + break; + case EntityEventFilter.Tasks: + entityEventTypes = [EntityEventType.Task]; + break; + case EntityEventFilter.All: + break; + } + const isAllType = !entityEventTypes; + const isMailType = entityEventTypes?.includes(EntityEventType.Mail); + const isCallType = entityEventTypes?.includes(EntityEventType.Call); + const qb = this.repository.createQueryBuilder().select().where({ accountId }); + const linkedEntities = + isAllType || isMailType || isCallType + ? await this.entityLinkService.findMany({ accountId, sourceId: entityId }) + : []; + const linkedEntitiesIds = linkedEntities.map((e) => e.targetId); + switch (true) { + case isAllType && !linkedEntitiesIds.length: + qb.andWhere({ entityId }); + break; + case isAllType && !!linkedEntitiesIds.length: + qb.andWhere( + new Brackets((qb) => + qb + .where(`entity_id = :entityId`, { entityId }) + .orWhere(`(type in (:...types) and entity_id in (:...entityIds))`, { + entityIds: linkedEntitiesIds, + types: [EntityEventType.Mail, EntityEventType.Call], + }), + ), + ); + break; + case isCallType: + qb.andWhere(`type = '${EntityEventType.Call}' and entity_id in (:...entityIds)`, { + entityIds: [...linkedEntitiesIds, entityId], + }); + break; + case isMailType: + qb.andWhere(`type = '${EntityEventType.Mail}' and entity_id in (:...entityIds)`, { + entityIds: [...linkedEntitiesIds, entityId], + }); + break; + default: + qb.andWhere({ entityId }); + if (entityEventTypes?.length) { + qb.andWhere(`type in (:...types)`, { types: entityEventTypes }); + } + } + if (ExcludeEventTypes.length > 0) { + qb.andWhere('type not in (:...excludeTypes)', { excludeTypes: ExcludeEventTypes }); + } + return qb.orderBy('created_at', 'DESC').addOrderBy('id', 'DESC'); + } + + //TODO This is a temporary solution to filter mail objects with same thread. + private checkIsMailWithUniqueThreadId( + entityEventItem: EntityEvent, + dataObject: any, + resultData: EntityEventItemDto[], + ): boolean { + return entityEventItem.type === EntityEventType.Mail + ? !resultData.some((item) => item.data.id === dataObject.id) + : true; + } +} diff --git a/backend/src/modules/entity/entity-event/enums/entity-event-filter.enum.ts b/backend/src/modules/entity/entity-event/enums/entity-event-filter.enum.ts new file mode 100644 index 0000000..3a38106 --- /dev/null +++ b/backend/src/modules/entity/entity-event/enums/entity-event-filter.enum.ts @@ -0,0 +1,13 @@ +//TODO Make fields naming in plural and consistent with such frontend enum, sort descending +export enum EntityEventFilter { + All = 'all', + Activities = 'activities', + Calls = 'calls', + Documents = 'documents', //TODO: remove + Files = 'files', + Notes = 'notes', + Mail = 'mail', + Orders = 'orders', + Shipments = 'shipments', + Tasks = 'tasks', +} diff --git a/backend/src/modules/entity/entity-event/enums/entity-event-type.enum.ts b/backend/src/modules/entity/entity-event/enums/entity-event-type.enum.ts new file mode 100644 index 0000000..d17d580 --- /dev/null +++ b/backend/src/modules/entity/entity-event/enums/entity-event-type.enum.ts @@ -0,0 +1,12 @@ +//TODO Unify with such frontend enum, remove unused fields +export enum EntityEventType { + Activity = 'activity', + Call = 'call', + Document = 'document', + Mail = 'mail', + Note = 'note', + Order = 'order', + RentalOrder = 'rental_order', + Shipment = 'shipment', + Task = 'task', +} diff --git a/backend/src/modules/entity/entity-event/enums/index.ts b/backend/src/modules/entity/entity-event/enums/index.ts new file mode 100644 index 0000000..bd199aa --- /dev/null +++ b/backend/src/modules/entity/entity-event/enums/index.ts @@ -0,0 +1,2 @@ +export * from './entity-event-filter.enum'; +export * from './entity-event-type.enum'; diff --git a/backend/src/modules/entity/entity-field/common/enums/field-type.enum.ts b/backend/src/modules/entity/entity-field/common/enums/field-type.enum.ts new file mode 100644 index 0000000..e1a0f75 --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/enums/field-type.enum.ts @@ -0,0 +1,37 @@ +export enum FieldType { + Text = 'text', + MultiText = 'multitext', + RichText = 'richtext', + Switch = 'switch', + Number = 'number', + Formula = 'formula', + Phone = 'phone', + Email = 'email', + Value = 'value', + Date = 'date', + Link = 'link', + Select = 'select', + ColoredSelect = 'colored_select', + MultiSelect = 'multiselect', + ColoredMultiSelect = 'colored_multiselect', + CheckedMultiSelect = 'checked_multiselect', + Participant = 'participant', + Participants = 'participants', + File = 'file', + Checklist = 'checklist', +} + +export const FieldTypes = { + calculable: [FieldType.Number, FieldType.Value, FieldType.Formula], + formula: [FieldType.Value, FieldType.Formula], + participant: [FieldType.Participant, FieldType.Participants], + select: [FieldType.Select, FieldType.ColoredSelect], + multiSelect: [FieldType.MultiSelect, FieldType.ColoredMultiSelect, FieldType.CheckedMultiSelect], + withOptions: [ + FieldType.Select, + FieldType.ColoredSelect, + FieldType.MultiSelect, + FieldType.ColoredMultiSelect, + FieldType.CheckedMultiSelect, + ], +}; diff --git a/backend/src/modules/entity/entity-field/common/enums/index.ts b/backend/src/modules/entity/entity-field/common/enums/index.ts new file mode 100644 index 0000000..441e2c2 --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/enums/index.ts @@ -0,0 +1 @@ +export * from './field-type.enum'; diff --git a/backend/src/modules/entity/entity-field/common/events/field-event-type.enum.ts b/backend/src/modules/entity/entity-field/common/events/field-event-type.enum.ts new file mode 100644 index 0000000..85d4927 --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/events/field-event-type.enum.ts @@ -0,0 +1,5 @@ +export enum FieldEventType { + FieldCreated = 'field:created', + FieldUpdated = 'field:updated', + FieldDeleted = 'field:deleted', +} diff --git a/backend/src/modules/entity/entity-field/common/events/field/field.event.ts b/backend/src/modules/entity/entity-field/common/events/field/field.event.ts new file mode 100644 index 0000000..d2d0069 --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/events/field/field.event.ts @@ -0,0 +1,15 @@ +import { FieldType } from '../../enums'; + +export class FieldEvent { + accountId: number; + entityTypeId: number; + fieldId: number; + type?: FieldType; + + constructor({ accountId, entityTypeId, fieldId, type }: FieldEvent) { + this.accountId = accountId; + this.entityTypeId = entityTypeId; + this.fieldId = fieldId; + this.type = type; + } +} diff --git a/backend/src/modules/entity/entity-field/common/events/field/index.ts b/backend/src/modules/entity/entity-field/common/events/field/index.ts new file mode 100644 index 0000000..334f60e --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/events/field/index.ts @@ -0,0 +1 @@ +export * from './field.event'; diff --git a/backend/src/modules/entity/entity-field/common/events/index.ts b/backend/src/modules/entity/entity-field/common/events/index.ts new file mode 100644 index 0000000..f30a358 --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/events/index.ts @@ -0,0 +1,2 @@ +export * from './field'; +export * from './field-event-type.enum'; diff --git a/backend/src/modules/entity/entity-field/common/index.ts b/backend/src/modules/entity/entity-field/common/index.ts new file mode 100644 index 0000000..80a1a8b --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/index.ts @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './events'; +export * from './utils'; diff --git a/backend/src/modules/entity/entity-field/common/utils/formula.util.ts b/backend/src/modules/entity/entity-field/common/utils/formula.util.ts new file mode 100644 index 0000000..8eda8bb --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/utils/formula.util.ts @@ -0,0 +1,20 @@ +import { isSymbolNode, parse, SymbolNode } from 'mathjs'; + +export class FormulaUtil { + public static extractVariables(formula: string): string[] { + return formula + ? parse(formula) + .filter((n) => isSymbolNode(n)) + .map((n) => (n as SymbolNode)?.name) + : []; + } + + public static createFieldKey({ entityTypeId, fieldId }: { entityTypeId: number; fieldId: number }): string { + return `et${entityTypeId}_f${fieldId}`; + } + + public static parseFieldKey(fieldKey: string): { entityTypeId: number; fieldId: number } { + const [entityTypeId, fieldId] = fieldKey.split('_'); + return { entityTypeId: Number(entityTypeId.substring(2)), fieldId: Number(fieldId.substring(1)) }; + } +} diff --git a/backend/src/modules/entity/entity-field/common/utils/index.ts b/backend/src/modules/entity/entity-field/common/utils/index.ts new file mode 100644 index 0000000..2dc51f8 --- /dev/null +++ b/backend/src/modules/entity/entity-field/common/utils/index.ts @@ -0,0 +1 @@ +export * from './formula.util'; diff --git a/backend/src/modules/entity/entity-field/entity-field.module.ts b/backend/src/modules/entity/entity-field/entity-field.module.ts new file mode 100644 index 0000000..8b3b5d9 --- /dev/null +++ b/backend/src/modules/entity/entity-field/entity-field.module.ts @@ -0,0 +1,34 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; + +import { FieldGroup, FieldGroupService } from './field-group'; +import { FieldOption, FieldOptionService } from './field-option'; +import { FieldValue, FieldValueHandler, FieldValueService } from './field-value'; +import { Field, FieldController, FieldService } from './field'; + +import { FieldStageSettings } from './field-settings/entities/field-stage-settings.entity'; +import { FieldUserSettings } from './field-settings/entities/field-user-settings.entity'; +import { FieldSettingsController } from './field-settings/field-settings.controller'; +import { FieldSettingsService } from './field-settings/field-settings.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Field, FieldGroup, FieldOption, FieldValue, FieldStageSettings, FieldUserSettings]), + IAMModule, + StorageModule, + ], + controllers: [FieldController, FieldSettingsController], + providers: [ + FieldService, + FieldGroupService, + FieldOptionService, + FieldValueService, + FieldValueHandler, + FieldSettingsService, + ], + exports: [FieldService, FieldGroupService, FieldOptionService, FieldValueService, FieldSettingsService], +}) +export class EntityFieldModule {} diff --git a/backend/src/modules/entity/entity-field/field-group/dto/create-field-group.dto.ts b/backend/src/modules/entity/entity-field/field-group/dto/create-field-group.dto.ts new file mode 100644 index 0000000..a53abba --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/dto/create-field-group.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { FieldGroupDto } from './field-group.dto'; + +export class CreateFieldGroupDto extends PickType(FieldGroupDto, ['name', 'sortOrder', 'code'] as const) { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + id?: number; +} diff --git a/backend/src/modules/entity/entity-field/field-group/dto/field-group.dto.ts b/backend/src/modules/entity/entity-field/field-group/dto/field-group.dto.ts new file mode 100644 index 0000000..8ac41bb --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/dto/field-group.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { FieldGroupCode } from '../enums'; + +export class FieldGroupDto { + @ApiProperty({ description: 'Field group ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Field group name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Field group sort order' }) + @IsNumber() + sortOrder: number; + + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiPropertyOptional({ enum: FieldGroupCode, nullable: true, description: 'Field group code' }) + @IsOptional() + @IsEnum(FieldGroupCode) + code?: FieldGroupCode | null; +} diff --git a/backend/src/modules/entity/entity-field/field-group/dto/index.ts b/backend/src/modules/entity/entity-field/field-group/dto/index.ts new file mode 100644 index 0000000..22b7176 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-field-group.dto'; +export * from './field-group.dto'; +export * from './update-field-group.dto'; diff --git a/backend/src/modules/entity/entity-field/field-group/dto/update-field-group.dto.ts b/backend/src/modules/entity/entity-field/field-group/dto/update-field-group.dto.ts new file mode 100644 index 0000000..0e1a913 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/dto/update-field-group.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsEnum } from 'class-validator'; + +import { ObjectState } from '@/common'; + +import { FieldGroupDto } from './field-group.dto'; + +export class UpdateFieldGroupDto extends PickType(FieldGroupDto, ['id', 'name', 'sortOrder', 'code'] as const) { + @ApiProperty({ enum: ObjectState, description: 'Object state' }) + @IsEnum(ObjectState) + state?: ObjectState; +} diff --git a/backend/src/modules/entity/entity-field/field-group/entities/field-group.entity.ts b/backend/src/modules/entity/entity-field/field-group/entities/field-group.entity.ts new file mode 100644 index 0000000..7606ec1 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/entities/field-group.entity.ts @@ -0,0 +1,70 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { FieldGroupCode } from '../enums'; +import { CreateFieldGroupDto, FieldGroupDto, UpdateFieldGroupDto } from '../dto'; + +@Entity() +export class FieldGroup { + @PrimaryColumn() + id: number; + + @Column() + name: string; + + @Column() + sortOrder: number; + + @Column() + entityTypeId: number; + + @Column({ nullable: true }) + code: FieldGroupCode | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + id: number, + name: string, + sortOrder: number, + entityTypeId: number, + code: FieldGroupCode | null, + createdAt?: Date, + ) { + this.accountId = accountId; + this.id = id; + this.name = name; + this.sortOrder = sortOrder; + this.entityTypeId = entityTypeId; + this.code = code; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto(accountId: number, entityTypeId: number, dto: CreateFieldGroupDto): FieldGroup { + return new FieldGroup(accountId, dto.id, dto.name, dto.sortOrder, entityTypeId, dto.code); + } + + public update(dto: UpdateFieldGroupDto): FieldGroup { + this.name = dto.name !== undefined ? dto.name : this.name; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + this.code = dto.code !== undefined ? dto.code : this.code; + + return this; + } + + public toDto(): FieldGroupDto { + return { + id: this.id, + name: this.name, + sortOrder: this.sortOrder, + entityTypeId: this.entityTypeId, + code: this.code, + }; + } +} diff --git a/backend/src/modules/entity/entity-field/field-group/entities/index.ts b/backend/src/modules/entity/entity-field/field-group/entities/index.ts new file mode 100644 index 0000000..ebd9cfb --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/entities/index.ts @@ -0,0 +1 @@ +export * from './field-group.entity'; diff --git a/backend/src/modules/entity/entity-field/field-group/enums/field-group-code.enum.ts b/backend/src/modules/entity/entity-field/field-group/enums/field-group-code.enum.ts new file mode 100644 index 0000000..48ed1ae --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/enums/field-group-code.enum.ts @@ -0,0 +1,5 @@ +export enum FieldGroupCode { + Details = 'details', + Project = 'project', + Analytics = 'analytics', +} diff --git a/backend/src/modules/entity/entity-field/field-group/enums/index.ts b/backend/src/modules/entity/entity-field/field-group/enums/index.ts new file mode 100644 index 0000000..740288f --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/enums/index.ts @@ -0,0 +1 @@ +export * from './field-group-code.enum'; diff --git a/backend/src/modules/entity/entity-field/field-group/field-group.service.ts b/backend/src/modules/entity/entity-field/field-group/field-group.service.ts new file mode 100644 index 0000000..72e7ab1 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/field-group.service.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NotFoundError, ObjectState } from '@/common'; +import { SequenceIdService } from '@/database'; + +import { SequenceName } from '@/CRM/common/enums/sequence-name.enum'; + +import { CreateFieldGroupDto, UpdateFieldGroupDto } from './dto'; +import { FieldGroup } from './entities'; + +interface FindFilter { + accountId: number; + entityTypeId?: number; +} + +@Injectable() +export class FieldGroupService { + constructor( + @InjectRepository(FieldGroup) + private readonly repository: Repository, + private readonly sequenceIdService: SequenceIdService, + ) {} + + private async nextIdentity(): Promise { + return this.sequenceIdService.nextIdentity(SequenceName.FieldGroup); + } + + public async create({ + accountId, + entityTypeId, + dto, + }: { + accountId: number; + entityTypeId: number; + dto: CreateFieldGroupDto; + }): Promise { + dto.id = dto.id ?? (await this.nextIdentity()); + + return await this.repository.save(FieldGroup.fromDto(accountId, entityTypeId, dto)); + } + + public async getById(id: number): Promise { + const group = await this.repository.findOneBy({ id }); + + if (!group) { + throw NotFoundError.withId(FieldGroup, id); + } + + return group; + } + + public async findMany(filter: FindFilter): Promise { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + + if (filter?.entityTypeId) { + qb.andWhere('entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + + return await qb.getMany(); + } + + public async delete(id: number): Promise { + const result = await this.repository.delete({ id }); + + if (result.affected === 0) { + throw NotFoundError.withId(FieldGroup, id); + } + } + + public async saveBatch({ + accountId, + entityTypeId, + dtos, + }: { + accountId: number; + entityTypeId: number; + dtos: UpdateFieldGroupDto[]; + }): Promise { + for (const dto of dtos) { + if (dto.state === ObjectState.Created) { + await this.create({ accountId, entityTypeId, dto }); + } + + if (dto.state === ObjectState.Updated) { + await this.repository.update(dto.id, { name: dto.name, sortOrder: dto.sortOrder, code: dto.code }); + } + + if (dto.state === ObjectState.Deleted) { + await this.delete(dto.id); + } + } + } +} diff --git a/backend/src/modules/entity/entity-field/field-group/index.ts b/backend/src/modules/entity/entity-field/field-group/index.ts new file mode 100644 index 0000000..770e5e8 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-group/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './entities'; +export * from './field-group.service'; diff --git a/backend/src/modules/entity/entity-field/field-option/dto/create-field-option.dto.ts b/backend/src/modules/entity/entity-field/field-option/dto/create-field-option.dto.ts new file mode 100644 index 0000000..c13a48d --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/dto/create-field-option.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { FieldOptionDto } from './field-option.dto'; + +export class CreateFieldOptionDto extends PickType(FieldOptionDto, ['label', 'color', 'sortOrder'] as const) { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + id?: number; +} diff --git a/backend/src/modules/entity/entity-field/field-option/dto/field-option.dto.ts b/backend/src/modules/entity/entity-field/field-option/dto/field-option.dto.ts new file mode 100644 index 0000000..2b6d24c --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/dto/field-option.dto.ts @@ -0,0 +1,32 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class FieldOptionDto { + @ApiProperty({ description: 'Field option ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Field option label' }) + @IsNumber() + label: string; + + @ApiProperty({ nullable: true, description: 'Field option color' }) + @IsOptional() + @IsString() + color: string | null; + + @ApiProperty({ description: 'Field option sort order' }) + @IsNumber() + sortOrder: number; + + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + fieldId: number; + + constructor({ id, label, color, sortOrder }: FieldOptionDto) { + this.id = id; + this.label = label; + this.color = color; + this.sortOrder = sortOrder; + } +} diff --git a/backend/src/modules/entity/entity-field/field-option/dto/index.ts b/backend/src/modules/entity/entity-field/field-option/dto/index.ts new file mode 100644 index 0000000..2fd5962 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-field-option.dto'; +export * from './field-option.dto'; +export * from './update-field-option.dto'; diff --git a/backend/src/modules/entity/entity-field/field-option/dto/update-field-option.dto.ts b/backend/src/modules/entity/entity-field/field-option/dto/update-field-option.dto.ts new file mode 100644 index 0000000..3d267a9 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/dto/update-field-option.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty, ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { ObjectState } from '@/common'; + +import { FieldOptionDto } from './field-option.dto'; + +export class UpdateFieldOptionDto extends PartialType( + PickType(FieldOptionDto, ['label', 'color', 'sortOrder'] as const), +) { + @ApiProperty({ description: 'Field option ID' }) + @IsNumber() + id: number; + + @ApiPropertyOptional({ enum: ObjectState, description: 'Object state' }) + @IsOptional() + @IsEnum(ObjectState) + state?: ObjectState; +} diff --git a/backend/src/modules/entity/entity-field/field-option/entities/field-option.entity.ts b/backend/src/modules/entity/entity-field/field-option/entities/field-option.entity.ts new file mode 100644 index 0000000..34c8fba --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/entities/field-option.entity.ts @@ -0,0 +1,63 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { CreateFieldOptionDto, FieldOptionDto, UpdateFieldOptionDto } from '../dto'; + +@Entity() +export class FieldOption { + @PrimaryColumn() + id: number; + + @Column() + label: string; + + @Column() + color: string | null; + + @Column() + sortOrder: number; + + @Column() + fieldId: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + id: number, + label: string, + color: string | null, + sortOrder: number, + fieldId: number, + createdAt?: Date, + ) { + this.id = id; + this.label = label; + this.color = color; + this.sortOrder = sortOrder; + this.fieldId = fieldId; + this.accountId = accountId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto(accountId: number, fieldId: number, dto: CreateFieldOptionDto): FieldOption { + return new FieldOption(accountId, dto.id, dto.label, dto.color, dto.sortOrder, fieldId); + } + + public toDto(): FieldOptionDto { + return new FieldOptionDto({ ...this }); + } + + public update(dto: UpdateFieldOptionDto): FieldOption { + this.label = dto.label !== undefined ? dto.label : this.label; + this.color = dto.color !== undefined ? dto.color : this.color; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + + return this; + } +} diff --git a/backend/src/modules/entity/entity-field/field-option/entities/index.ts b/backend/src/modules/entity/entity-field/field-option/entities/index.ts new file mode 100644 index 0000000..5375e16 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/entities/index.ts @@ -0,0 +1 @@ +export * from './field-option.entity'; diff --git a/backend/src/modules/entity/entity-field/field-option/field-option.service.ts b/backend/src/modules/entity/entity-field/field-option/field-option.service.ts new file mode 100644 index 0000000..55d97e6 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/field-option.service.ts @@ -0,0 +1,122 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { ObjectState } from '@/common'; +import { SequenceIdService } from '@/database'; + +import { SequenceName } from '@/CRM/common/enums/sequence-name.enum'; + +import { CreateFieldOptionDto, UpdateFieldOptionDto } from './dto'; +import { FieldOption } from './entities'; + +interface FindFilter { + accountId: number; + fieldId: number; +} + +const cacheKey = ({ accountId, fieldId }: { accountId: number; fieldId: number }) => + `FieldOption:${accountId}:${fieldId}`; + +@Injectable() +export class FieldOptionService { + constructor( + @InjectRepository(FieldOption) + private readonly repository: Repository, + private readonly dataSource: DataSource, + private readonly sequenceIdService: SequenceIdService, + ) {} + + private async nextIdentity(): Promise { + return this.sequenceIdService.nextIdentity(SequenceName.FieldOption); + } + + public async create({ + accountId, + fieldId, + dto, + }: { + accountId: number; + fieldId: number; + dto: CreateFieldOptionDto; + }): Promise { + dto.id = dto.id ?? (await this.nextIdentity()); + + return this.repository.save(FieldOption.fromDto(accountId, fieldId, dto)); + } + public async createMany({ + accountId, + fieldId, + dtos, + }: { + accountId: number; + fieldId: number; + dtos: CreateFieldOptionDto[]; + }): Promise { + return Promise.all(dtos.map((dto) => this.create({ accountId, fieldId, dto }))); + } + + public async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).cache(cacheKey(filter), 600000).getMany(); + } + + public async processBatch({ + accountId, + fieldId, + dtos, + }: { + accountId: number; + fieldId: number; + dtos: UpdateFieldOptionDto[]; + }): Promise { + const created = dtos.filter((dto) => dto.state === ObjectState.Created) as CreateFieldOptionDto[]; + const updated = dtos.filter((dto) => dto.state === ObjectState.Updated); + const deleted = dtos.filter((dto) => dto.state === ObjectState.Deleted); + + const options: FieldOption[] = []; + if (created.length) { + options.push(...(await this.createMany({ accountId, fieldId, dtos: created }))); + } + if (updated.length) { + const options = await this.findMany({ accountId, fieldId }); + options.push( + ...(await Promise.all( + updated.map(async (dto) => { + const option = options.find((option) => option.id === dto.id); + if (option) { + await this.repository.save(option.update(dto)); + return option; + } + return null; + }), + )), + ); + } + if (deleted.length) { + await Promise.all(deleted.map((dto) => this.delete({ accountId, fieldId, optionId: dto.id }))); + } + + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, fieldId })]); + + return options; + } + + private async delete({ + accountId, + fieldId, + optionId, + }: { + accountId: number; + fieldId: number; + optionId?: number; + }): Promise { + await this.repository.delete({ accountId, fieldId, id: optionId }); + } + + private createFindQb(filter: FindFilter) { + return this.repository + .createQueryBuilder() + .where('account_id = :accountId', { accountId: filter.accountId }) + .andWhere('field_id = :fieldId', { fieldId: filter.fieldId }); + } +} diff --git a/backend/src/modules/entity/entity-field/field-option/index.ts b/backend/src/modules/entity/entity-field/field-option/index.ts new file mode 100644 index 0000000..bc658e3 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-option/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './entities'; +export * from './field-option.service'; diff --git a/backend/src/modules/entity/entity-field/field-settings/dto/field-settings.dto.ts b/backend/src/modules/entity/entity-field/field-settings/dto/field-settings.dto.ts new file mode 100644 index 0000000..a424de4 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/dto/field-settings.dto.ts @@ -0,0 +1,42 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class FieldSettingsDto { + @ApiProperty() + fieldId: number; + + @ApiProperty({ nullable: true, type: [Number] }) + importantStageIds: number[] | null; + + @ApiProperty({ nullable: true, type: [Number] }) + requiredStageIds: number[] | null; + + @ApiProperty({ nullable: true, type: [Number] }) + excludeUserIds: number[] | null; + + @ApiProperty({ nullable: true, type: [Number] }) + readonlyUserIds: number[] | null; + + @ApiProperty({ nullable: true, type: [Number] }) + hideUserIds: number[] | null; + + @ApiProperty({ nullable: true, type: [Number] }) + hideStageIds: number[] | null; + + constructor({ + fieldId, + importantStageIds, + requiredStageIds, + excludeUserIds, + readonlyUserIds, + hideUserIds, + hideStageIds, + }: FieldSettingsDto) { + this.fieldId = fieldId; + this.importantStageIds = importantStageIds; + this.requiredStageIds = requiredStageIds; + this.excludeUserIds = excludeUserIds; + this.readonlyUserIds = readonlyUserIds; + this.hideUserIds = hideUserIds; + this.hideStageIds = hideStageIds; + } +} diff --git a/backend/src/modules/entity/entity-field/field-settings/dto/index.ts b/backend/src/modules/entity/entity-field/field-settings/dto/index.ts new file mode 100644 index 0000000..0ec4599 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/dto/index.ts @@ -0,0 +1,2 @@ +export * from './field-settings.dto'; +export * from './update-field-settings.dto'; diff --git a/backend/src/modules/entity/entity-field/field-settings/dto/update-field-settings.dto.ts b/backend/src/modules/entity/entity-field/field-settings/dto/update-field-settings.dto.ts new file mode 100644 index 0000000..2ea8529 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/dto/update-field-settings.dto.ts @@ -0,0 +1,5 @@ +import { OmitType, PartialType } from '@nestjs/swagger'; + +import { FieldSettingsDto } from './field-settings.dto'; + +export class UpdateFieldSettingsDto extends PartialType(OmitType(FieldSettingsDto, ['fieldId'] as const)) {} diff --git a/backend/src/modules/entity/entity-field/field-settings/entities/field-stage-settings.entity.ts b/backend/src/modules/entity/entity-field/field-settings/entities/field-stage-settings.entity.ts new file mode 100644 index 0000000..942a6ab --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/entities/field-stage-settings.entity.ts @@ -0,0 +1,38 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { FieldAccess } from '../enums'; + +@Entity() +export class FieldStageSettings { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + fieldId: number; + + @Column() + stageId: number; + + @Column() + access: FieldAccess; + + @Column('integer', { array: true, nullable: true }) + excludeUserIds: number[] | null; + + constructor( + accountId: number, + fieldId: number, + stageId: number, + access: FieldAccess, + excludeUserIds: number[] | null, + ) { + this.accountId = accountId; + this.fieldId = fieldId; + this.stageId = stageId; + this.access = access; + this.excludeUserIds = excludeUserIds; + } +} diff --git a/backend/src/modules/entity/entity-field/field-settings/entities/field-user-settings.entity.ts b/backend/src/modules/entity/entity-field/field-settings/entities/field-user-settings.entity.ts new file mode 100644 index 0000000..a96c809 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/entities/field-user-settings.entity.ts @@ -0,0 +1,28 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { FieldAccess } from '../enums'; + +@Entity() +export class FieldUserSettings { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + fieldId: number; + + @Column() + userId: number; + + @Column() + access: FieldAccess; + + constructor(accountId: number, fieldId: number, userId: number, access: FieldAccess) { + this.accountId = accountId; + this.fieldId = fieldId; + this.userId = userId; + this.access = access; + } +} diff --git a/backend/src/modules/entity/entity-field/field-settings/entities/index.ts b/backend/src/modules/entity/entity-field/field-settings/entities/index.ts new file mode 100644 index 0000000..a2ca520 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/entities/index.ts @@ -0,0 +1,2 @@ +export * from './field-stage-settings.entity'; +export * from './field-user-settings.entity'; diff --git a/backend/src/modules/entity/entity-field/field-settings/enums/field-access.enum.ts b/backend/src/modules/entity/entity-field/field-settings/enums/field-access.enum.ts new file mode 100644 index 0000000..d4bf4a1 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/enums/field-access.enum.ts @@ -0,0 +1,7 @@ +export enum FieldAccess { + REGULAR = 'regular', + READONLY = 'readonly', + HIDDEN = 'hidden', + IMPORTANT = 'important', + REQUIRED = 'required', +} diff --git a/backend/src/modules/entity/entity-field/field-settings/enums/index.ts b/backend/src/modules/entity/entity-field/field-settings/enums/index.ts new file mode 100644 index 0000000..afe9c57 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/enums/index.ts @@ -0,0 +1 @@ +export * from './field-access.enum'; diff --git a/backend/src/modules/entity/entity-field/field-settings/field-settings.controller.ts b/backend/src/modules/entity/entity-field/field-settings/field-settings.controller.ts new file mode 100644 index 0000000..6203470 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/field-settings.controller.ts @@ -0,0 +1,37 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FieldSettingsDto, UpdateFieldSettingsDto } from './dto'; +import { FieldSettingsService } from './field-settings.service'; + +@ApiTags('crm/fields') +@Controller('/crm/entity-types/:entityTypeId/fields') +@JwtAuthorized() +@TransformToDto() +export class FieldSettingsController { + constructor(private readonly service: FieldSettingsService) {} + + @ApiOkResponse({ description: 'Get field settings', type: [FieldSettingsDto] }) + @Get('settings') + public async updateEntity( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + ) { + return await this.service.findMany({ accountId, entityTypeId }); + } + + @ApiCreatedResponse({ description: 'Update field settings', type: FieldSettingsDto }) + @Post(':fieldId/settings') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('fieldId', ParseIntPipe) fieldId: number, + @Body() dto: UpdateFieldSettingsDto, + ) { + return await this.service.update({ accountId, fieldId, dto }); + } +} diff --git a/backend/src/modules/entity/entity-field/field-settings/field-settings.service.ts b/backend/src/modules/entity/entity-field/field-settings/field-settings.service.ts new file mode 100644 index 0000000..4dd0890 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/field-settings.service.ts @@ -0,0 +1,172 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { FieldService } from '../field/field.service'; + +import { FieldAccess } from './enums'; +import { FieldSettings } from './types'; +import { UpdateFieldSettingsDto } from './dto'; +import { FieldStageSettings, FieldUserSettings } from './entities'; + +const cacheKeyStage = ({ accountId, fieldId }: { accountId: number; fieldId: number }) => + `FieldStageSettings:${accountId}:${fieldId}`; +const cacheKeyUser = ({ accountId, fieldId }: { accountId: number; fieldId: number }) => + `FieldUserSettings:${accountId}:${fieldId}`; + +@Injectable() +export class FieldSettingsService { + constructor( + @InjectRepository(FieldStageSettings) + private readonly repositoryStageSettings: Repository, + @InjectRepository(FieldUserSettings) + private readonly repositoryUserSettings: Repository, + private readonly dataSource: DataSource, + private readonly fieldService: FieldService, + ) {} + + public async findOne({ accountId, fieldId }: { accountId: number; fieldId: number }): Promise { + const stageSettings = await this.repositoryStageSettings.find({ + where: { accountId, fieldId }, + cache: { id: cacheKeyStage({ accountId, fieldId }), milliseconds: 600000 }, + }); + + const userSettings = await this.repositoryUserSettings.find({ + where: { accountId, fieldId }, + cache: { id: cacheKeyUser({ accountId, fieldId }), milliseconds: 600000 }, + }); + + return new FieldSettings( + fieldId, + stageSettings.filter((s) => s.access === FieldAccess.IMPORTANT).map((s) => s.stageId), + stageSettings.filter((s) => s.access === FieldAccess.REQUIRED).map((s) => s.stageId), + stageSettings.filter((s) => s.access === FieldAccess.REQUIRED)?.[0]?.excludeUserIds ?? [], + stageSettings.filter((s) => s.access === FieldAccess.HIDDEN).map((s) => s.stageId), + userSettings.filter((u) => u.access === FieldAccess.READONLY).map((u) => u.userId), + userSettings.filter((u) => u.access === FieldAccess.HIDDEN).map((u) => u.userId), + ); + } + + public async findMany({ + accountId, + entityTypeId, + }: { + accountId: number; + entityTypeId?: number; + }): Promise { + const filedIds = await this.fieldService.findManyIds({ accountId, entityTypeId }); + + return Promise.all(filedIds.map((fieldId) => this.findOne({ accountId, fieldId }))); + } + + public async getRestrictedFields({ + accountId, + entityTypeId, + access, + userId, + stageId, + }: { + accountId: number; + entityTypeId: number; + access: FieldAccess; + userId?: number; + stageId?: number; + }): Promise { + const fieldIds = await this.fieldService.findManyIds({ accountId, entityTypeId }); + + const result: number[] = []; + for (const fieldId of fieldIds) { + const settings = await this.findOne({ accountId, fieldId }); + if (stageId) { + switch (access) { + case FieldAccess.IMPORTANT: + if (settings.importantStageIds?.includes(stageId)) { + result.push(fieldId); + } + break; + case FieldAccess.REQUIRED: + if ( + settings.requiredStageIds?.includes(stageId) && + (!userId || (userId && !settings.excludeUserIds?.includes(userId))) + ) { + result.push(fieldId); + } + break; + case FieldAccess.HIDDEN: + if (settings.hideStageIds?.includes(stageId)) { + result.push(fieldId); + } + break; + } + } + if (userId) { + switch (access) { + case FieldAccess.READONLY: + if (settings.readonlyUserIds?.includes(userId)) { + result.push(fieldId); + } + break; + case FieldAccess.HIDDEN: + if (settings.hideUserIds?.includes(userId)) { + result.push(fieldId); + } + break; + } + } + } + + return result; + } + + public async update({ + accountId, + fieldId, + dto, + }: { + accountId: number; + fieldId: number; + dto: UpdateFieldSettingsDto; + }): Promise { + await this.repositoryStageSettings.delete({ accountId, fieldId }); + await this.repositoryUserSettings.delete({ accountId, fieldId }); + this.dataSource.queryResultCache?.remove([ + cacheKeyStage({ accountId, fieldId }), + cacheKeyUser({ accountId, fieldId }), + ]); + + if (dto.importantStageIds) { + await this.repositoryStageSettings.insert( + dto.importantStageIds.map((stageId) => ({ accountId, fieldId, stageId, access: FieldAccess.IMPORTANT })), + ); + } + if (dto.requiredStageIds) { + await this.repositoryStageSettings.insert( + dto.requiredStageIds.map((stageId) => ({ + accountId, + fieldId, + stageId, + access: FieldAccess.REQUIRED, + excludeUserIds: dto.excludeUserIds, + })), + ); + } + if (dto.hideStageIds) { + await this.repositoryStageSettings.insert( + dto.hideStageIds.map((stageId) => ({ accountId, fieldId, stageId, access: FieldAccess.HIDDEN })), + ); + } + + if (dto.readonlyUserIds) { + await this.repositoryUserSettings.insert( + dto.readonlyUserIds.map((userId) => ({ accountId, fieldId, userId, access: FieldAccess.READONLY })), + ); + } + if (dto.hideUserIds) { + await this.repositoryUserSettings.insert( + dto.hideUserIds.map((userId) => ({ accountId, fieldId, userId, access: FieldAccess.HIDDEN })), + ); + } + + return this.findOne({ accountId, fieldId }); + } +} diff --git a/backend/src/modules/entity/entity-field/field-settings/index.ts b/backend/src/modules/entity/entity-field/field-settings/index.ts new file mode 100644 index 0000000..89b24f0 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './field-settings.controller'; +export * from './field-settings.service'; +export * from './types'; diff --git a/backend/src/modules/entity/entity-field/field-settings/types/field-settings.ts b/backend/src/modules/entity/entity-field/field-settings/types/field-settings.ts new file mode 100644 index 0000000..1b1d800 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/types/field-settings.ts @@ -0,0 +1,33 @@ +import { FieldSettingsDto } from '../dto'; + +export class FieldSettings { + fieldId: number; + importantStageIds: number[] | null; + requiredStageIds: number[] | null; + excludeUserIds: number[] | null; + hideStageIds: number[] | null; + readonlyUserIds: number[] | null; + hideUserIds: number[] | null; + + constructor( + fieldId: number, + importantStageIds: number[] | null, + requiredStageIds: number[] | null, + excludeUserIds: number[] | null, + hideStageIds: number[] | null, + readonlyUserIds: number[] | null, + hideUserIds: number[] | null, + ) { + this.fieldId = fieldId; + this.importantStageIds = importantStageIds; + this.requiredStageIds = requiredStageIds; + this.excludeUserIds = excludeUserIds; + this.hideStageIds = hideStageIds; + this.readonlyUserIds = readonlyUserIds; + this.hideUserIds = hideUserIds; + } + + public toDto(): FieldSettingsDto { + return new FieldSettingsDto({ ...this }); + } +} diff --git a/backend/src/modules/entity/entity-field/field-settings/types/index.ts b/backend/src/modules/entity/entity-field/field-settings/types/index.ts new file mode 100644 index 0000000..90aa5a6 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-settings/types/index.ts @@ -0,0 +1 @@ +export * from './field-settings'; diff --git a/backend/src/modules/entity/entity-field/field-value/dto/create-field-value.dto.ts b/backend/src/modules/entity/entity-field/field-value/dto/create-field-value.dto.ts new file mode 100644 index 0000000..01776cb --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/dto/create-field-value.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from '@nestjs/swagger'; + +import { FieldValueDto } from './field-value.dto'; + +export class CreateFieldValueDto extends PickType(FieldValueDto, ['fieldId', 'fieldType', 'payload'] as const) {} diff --git a/backend/src/modules/entity/entity-field/field-value/dto/field-value.dto.ts b/backend/src/modules/entity/entity-field/field-value/dto/field-value.dto.ts new file mode 100644 index 0000000..a438364 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/dto/field-value.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsObject } from 'class-validator'; + +import { FieldType } from '../../common'; + +export class FieldValueDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty() + @IsNumber() + fieldId: number; + + @ApiProperty({ enum: FieldType }) + @IsEnum(FieldType) + fieldType: FieldType; + + @ApiProperty() + @IsObject() + payload: any; + + constructor({ id, entityId, fieldId, fieldType, payload }: FieldValueDto) { + this.id = id; + this.entityId = entityId; + this.fieldId = fieldId; + this.fieldType = fieldType; + this.payload = payload; + } +} diff --git a/backend/src/modules/entity/entity-field/field-value/dto/index.ts b/backend/src/modules/entity/entity-field/field-value/dto/index.ts new file mode 100644 index 0000000..64003c1 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-field-value.dto'; +export * from './field-value.dto'; +export * from './simple-field-value.dto'; +export * from './update-field-value.dto'; diff --git a/backend/src/modules/entity/entity-field/field-value/dto/simple-field-value.dto.ts b/backend/src/modules/entity/entity-field/field-value/dto/simple-field-value.dto.ts new file mode 100644 index 0000000..50a2bae --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/dto/simple-field-value.dto.ts @@ -0,0 +1,36 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; + +import { FieldType } from '../../common'; + +export class SimpleFieldValueDto { + @ApiPropertyOptional({ enum: FieldType, description: 'Field type' }) + @IsOptional() + @IsEnum(FieldType) + fieldType?: FieldType; + + @ApiPropertyOptional({ description: 'Field name' }) + @IsOptional() + @IsString() + fieldName?: string; + + @ApiPropertyOptional({ description: 'Field ID' }) + @IsOptional() + @IsNumber() + fieldId?: number; + + @ApiPropertyOptional({ description: 'Field code' }) + @IsOptional() + @IsString() + fieldCode?: string; + + @ApiPropertyOptional({ description: 'Payload' }) + @IsOptional() + @IsObject() + payload?: unknown; + + @ApiPropertyOptional({ description: 'Value' }) + @IsOptional() + @IsObject() + value?: unknown; +} diff --git a/backend/src/modules/entity/entity-field/field-value/dto/update-field-value.dto.ts b/backend/src/modules/entity/entity-field/field-value/dto/update-field-value.dto.ts new file mode 100644 index 0000000..8a53d9a --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/dto/update-field-value.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsEnum } from 'class-validator'; + +import { ObjectState } from '@/common'; + +import { FieldValueDto } from './field-value.dto'; + +export class UpdateFieldValueDto extends PickType(FieldValueDto, ['fieldId', 'fieldType', 'payload'] as const) { + @ApiProperty({ enum: ObjectState }) + @IsEnum(ObjectState) + state?: ObjectState; +} diff --git a/backend/src/modules/entity/entity-field/field-value/entities/field-value.entity.ts b/backend/src/modules/entity/entity-field/field-value/entities/field-value.entity.ts new file mode 100644 index 0000000..2cffa90 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/entities/field-value.entity.ts @@ -0,0 +1,91 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { FieldType } from '../../common'; +import { CreateFieldValueDto, FieldValueDto } from '../dto'; +import { + FieldPayloadValue, + FieldPayloadValues, + FieldPayloadOption, + FieldPayloadOptions, + FieldPayloadParticipants, + FieldPayloadChecklistItem, +} from '../types'; + +type PayloadType = + | FieldPayloadValue + | FieldPayloadValue + | FieldPayloadValue + | FieldPayloadValue + | FieldPayloadValues + | FieldPayloadOption + | FieldPayloadOptions + | FieldPayloadParticipants + | FieldPayloadValue; + +@Entity() +export class FieldValue { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + fieldId: number; + + @Column() + fieldType: FieldType; + + @Column({ type: 'jsonb' }) + payload: PayloadType; + + @Column() + entityId: number; + + @Column() + accountId: number; + + constructor(accountId: number, fieldId: number, fieldType: FieldType, payload: PayloadType, entityId: number) { + this.accountId = accountId; + this.fieldId = fieldId; + this.fieldType = fieldType; + this.payload = payload; + this.entityId = entityId; + } + + static fromDto(accountId: number, entityId: number, dto: CreateFieldValueDto): FieldValue { + return new FieldValue(accountId, dto.fieldId, dto.fieldType, dto.payload, entityId); + } + + toDto(): FieldValueDto { + return new FieldValueDto({ ...this }); + } + + getValue(): T | null { + switch (this.fieldType) { + case FieldType.Text: + case FieldType.RichText: + case FieldType.Link: + case FieldType.Number: + case FieldType.Value: + case FieldType.Formula: + case FieldType.Switch: + case FieldType.Date: + case FieldType.Participant: + case FieldType.File: + return (this.payload as FieldPayloadValue).value; + case FieldType.MultiText: + case FieldType.Phone: + case FieldType.Email: + return (this.payload as FieldPayloadValues).values as T; + case FieldType.Select: + case FieldType.ColoredSelect: + return (this.payload as FieldPayloadOption).optionId as T; + case FieldType.MultiSelect: + case FieldType.ColoredMultiSelect: + case FieldType.CheckedMultiSelect: + return (this.payload as FieldPayloadOptions).optionIds as T; + case FieldType.Participants: + return (this.payload as FieldPayloadParticipants).userIds as T; + case FieldType.Checklist: + return (this.payload as FieldPayloadValue).value as T; + } + } +} diff --git a/backend/src/modules/entity/entity-field/field-value/entities/index.ts b/backend/src/modules/entity/entity-field/field-value/entities/index.ts new file mode 100644 index 0000000..3045f27 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/entities/index.ts @@ -0,0 +1 @@ +export * from './field-value.entity'; diff --git a/backend/src/modules/entity/entity-field/field-value/field-value.handler.ts b/backend/src/modules/entity/entity-field/field-value/field-value.handler.ts new file mode 100644 index 0000000..db01abe --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/field-value.handler.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common/events'; +import { FieldValueService } from './field-value.service'; + +@Injectable() +export class FieldValueHandler { + constructor(private readonly service: FieldValueService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.service.removeUser(event); + } +} diff --git a/backend/src/modules/entity/entity-field/field-value/field-value.service.ts b/backend/src/modules/entity/entity-field/field-value/field-value.service.ts new file mode 100644 index 0000000..71cece2 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/field-value.service.ts @@ -0,0 +1,515 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { create, all, MathJsInstance } from 'mathjs'; + +import { DateUtil, isUnique, ObjectState } from '@/common'; +import { StorageService } from '@/modules/storage/storage.service'; + +import { FieldType, FieldTypes, FormulaUtil } from '../common'; +import { Field } from '../field/entities'; +import { FieldService } from '../field/field.service'; +import { FieldOptionService } from '../field-option/field-option.service'; + +import { CreateFieldValueDto, UpdateFieldValueDto, SimpleFieldValueDto } from './dto'; +import { FieldValue } from './entities'; +import { + FieldPayloadChecklistItem, + FieldPayloadOption, + FieldPayloadOptions, + FieldPayloadParticipants, + FieldPayloadValue, + FieldPayloadValues, +} from './types'; + +interface FindFilter { + accountId: number; + fieldId?: number | number[]; + entityId?: number; + type?: FieldType | FieldType[]; + value?: string; +} +interface CalculateEntity { + entityTypeId: number; + entityId: number; + recalculate?: boolean; + children?: CalculateEntity[]; +} + +@Injectable() +export class FieldValueService { + private readonly logger = new Logger(FieldValueService.name); + private math: MathJsInstance; + + constructor( + @InjectRepository(FieldValue) + private readonly repository: Repository, + private readonly storageService: StorageService, + private readonly fieldService: FieldService, + private readonly fieldOptionService: FieldOptionService, + ) { + this.math = create(all, {}); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + this.math.SymbolNode.onUndefinedSymbol = function (_name: string) { + return 0; + }; + } + + async createSimple( + accountId: number, + entityTypeId: number, + entityId: number, + dto: SimpleFieldValueDto, + ): Promise { + if (dto.fieldType || dto.fieldName || dto.fieldId || dto.fieldCode) { + const field = await this.fieldService.findOne({ + accountId, + entityTypeId, + type: dto.fieldType, + name: dto.fieldName, + id: dto.fieldId, + code: dto.fieldCode, + }); + if (field) { + const payload = await this.getPayload({ accountId, field, dto }); + if (payload) { + return this.upsert({ accountId, entityId, dto: { fieldId: field.id, fieldType: field.type, payload } }); + } + } + } + + return null; + } + + async createManySimple({ + accountId, + entityTypeId, + entityId, + dtos, + }: { + accountId: number; + entityTypeId: number; + entityId: number; + dtos: SimpleFieldValueDto[]; + }): Promise<{ fieldValues: FieldValue[]; recalculate: boolean; updateParticipants: boolean; value?: number }> { + const fieldValues = ( + await Promise.all(dtos.map((dto) => this.createSimple(accountId, entityTypeId, entityId, dto))) + ).filter(Boolean); + const recalculate = fieldValues.some((value) => FieldTypes.calculable.includes(value.fieldType)); + const updateParticipants = fieldValues.some((value) => FieldTypes.participant.includes(value.fieldType)); + const value = fieldValues.find((value) => value.fieldType === FieldType.Value)?.getValue(); + + return { fieldValues, recalculate, updateParticipants, value }; + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + + async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).getMany(); + } + + async processBatch({ + accountId, + entityId, + dtos, + }: { + accountId: number; + entityId: number; + dtos: UpdateFieldValueDto[]; + }): Promise<{ recalculate: boolean; updateParticipants: boolean; value?: number }> { + if (!dtos) return { recalculate: false, updateParticipants: false }; + + let recalculate = false; + let updateParticipants = false; + let value: number | undefined = undefined; + for (const dto of dtos) { + if ([ObjectState.Created, ObjectState.Updated].includes(dto.state)) { + await this.setValue({ accountId, entityId, fieldId: dto.fieldId, dto }); + } else if (dto.state === ObjectState.Deleted) { + await this.delete({ accountId, entityId, fieldId: dto.fieldId }); + } + + recalculate ||= FieldTypes.calculable.includes(dto.fieldType); + updateParticipants ||= FieldTypes.participant.includes(dto.fieldType); + if (dto.fieldType === FieldType.Value) { + value = dto.payload?.value as number; + } + } + return { recalculate, updateParticipants, value }; + } + + async setValue({ + accountId, + entityId, + fieldId, + dto, + }: { + accountId: number; + entityId: number; + fieldId: number | null | undefined; + dto: CreateFieldValueDto; + }): Promise { + dto.fieldId = fieldId ?? dto.fieldId; + try { + await this.upsert({ accountId, entityId, dto }); + } catch (e) { + this.logger.warn( + `Set value error. accountId=${accountId}; entityId=${entityId}; fieldId=${fieldId}; dto=${JSON.stringify(dto)}`, + (e as Error)?.stack, + ); + } + } + + async calculateFormulas({ + accountId, + calcEntity, + previousEntityIds, + hasUpdates = true, + }: { + accountId: number; + calcEntity: CalculateEntity; + previousEntityIds: number[]; + hasUpdates: boolean; + }) { + const formulaFields = await this.fieldService.findMany({ + accountId, + entityTypeId: calcEntity.entityTypeId, + type: FieldTypes.formula, + }); + const unprocessed = + calcEntity.children?.filter((ce) => ce.recalculate && !previousEntityIds.includes(ce.entityId)) || []; + if ((hasUpdates || formulaFields.length) && unprocessed.length) { + await Promise.all( + unprocessed.map((child) => + this.calculateFormulas({ + accountId, + calcEntity: child, + previousEntityIds: [...previousEntityIds, calcEntity.entityId], + hasUpdates: formulaFields.length > 0, + }), + ), + ); + } + let formulaValueChanged = false; + if (formulaFields.length) { + const fieldValues = await this.getValuesForFormula({ + accountId, + calcEntities: [calcEntity, ...(calcEntity.children || [])], + }); + for (const formulaField of formulaFields) { + const key = FormulaUtil.createFieldKey({ entityTypeId: calcEntity.entityTypeId, fieldId: formulaField.id }); + const value = formulaField.value ? this.math.evaluate(formulaField.value, fieldValues) : null; + const currentValue = fieldValues.get(key); + if (currentValue !== value && (formulaField.type === FieldType.Formula || value !== null)) { + try { + await this.setValue({ + accountId, + entityId: calcEntity.entityId, + fieldId: formulaField.id, + dto: { + fieldId: formulaField.id, + fieldType: formulaField.type, + payload: { value }, + }, + }); + fieldValues.set(key, value); + formulaValueChanged = true; + } catch (e) { + this.logger.error( + // eslint-disable-next-line max-len + `Calculate formula error. accountId=${accountId}; entityId=${calcEntity.entityId}; fieldId=${formulaField.id}; value=${value}`, + (e as Error)?.stack, + ); + } + } + } + } + if (formulaValueChanged && unprocessed.length) { + await Promise.all( + unprocessed.map((child) => + this.calculateFormulas({ + accountId, + calcEntity: child, + previousEntityIds: [...previousEntityIds, calcEntity.entityId], + hasUpdates: formulaFields.length > 0, + }), + ), + ); + } + } + + private async getValuesForFormula({ + accountId, + calcEntities, + }: { + accountId: number; + calcEntities: CalculateEntity[]; + }): Promise> { + const values = new Map(); + + for (const calcEntity of calcEntities) { + const fieldValues = await this.findMany({ + accountId, + entityId: calcEntity.entityId, + type: FieldTypes.calculable, + }); + if (fieldValues.length) { + for (const fieldValue of fieldValues) { + const fieldKey = FormulaUtil.createFieldKey({ + entityTypeId: calcEntity.entityTypeId, + fieldId: fieldValue.fieldId, + }); + const currentValue = values.get(fieldKey); + values.set(fieldKey, (currentValue || 0) + (fieldValue.getValue() || 0)); + } + } + } + + return values; + } + + async removeUser({ accountId, userId, newUserId }: { accountId: number; userId: number; newUserId?: number | null }) { + if (newUserId) { + await this.repository + .createQueryBuilder() + .update() + .set({ payload: () => `'{ "value": ${newUserId} }'` }) + .where('account_id = :accountId', { accountId }) + .andWhere('field_type = :fieldType', { fieldType: FieldType.Participant }) + .andWhere(`(payload->>'value')::integer = :userId`, { userId }) + .execute(); + + await this.repository + .createQueryBuilder() + .update() + .set({ + payload: () => + // eslint-disable-next-line max-len + `jsonb_set(payload, '{userIds}', (SELECT jsonb_agg(DISTINCT CASE WHEN elem::integer = ${userId} THEN ${newUserId} ELSE elem::integer END) FROM jsonb_array_elements(payload->'userIds') AS elem))`, + }) + .where('account_id = :accountId', { accountId }) + .andWhere('field_type = :fieldType', { fieldType: FieldType.Participants }) + .andWhere(`payload->'userIds' @> jsonb_build_array(${userId})`) + .execute(); + } else { + await this.repository + .createQueryBuilder() + .delete() + .where('account_id = :accountId', { accountId }) + .andWhere('field_type = :fieldType', { fieldType: FieldType.Participant }) + .andWhere(`(payload->>'value')::integer = :userId`, { userId }) + .execute(); + + await this.repository + .createQueryBuilder() + .update() + .set({ + payload: () => + // eslint-disable-next-line max-len + `jsonb_set(payload, '{userIds}', (SELECT jsonb_agg(elem) FROM jsonb_array_elements(payload->'userIds') AS elem WHERE elem::integer != ${userId}))`, + }) + .where('account_id = :accountId', { accountId }) + .andWhere('field_type = :fieldType', { fieldType: FieldType.Participants }) + .andWhere(`payload->'userIds' @> jsonb_build_array(${userId})`) + .execute(); + } + } + + private async getPayload({ + accountId, + field, + dto, + }: { + accountId: number; + field: Field; + dto: SimpleFieldValueDto; + }): Promise { + if (dto.payload) { + return dto.payload; + } + + if (dto.value === null || dto.value === undefined) { + return null; + } + + if (FieldTypes.withOptions.includes(field.type)) { + const options = await this.fieldOptionService.findMany({ accountId, fieldId: field.id }); + if (FieldTypes.select.includes(field.type)) { + const option = options.find((option) => option.label === dto.value); + if (option) { + return this.formatPayload({ type: field.type, value: option.id }); + } + } else if (FieldTypes.multiSelect.includes(field.type)) { + const values = String(dto.value) + .split(',') + .map((v) => v.trim()); + const optionIds = options.filter((option) => values.includes(option.label.trim())).map((option) => option.id); + if (optionIds.length) { + return this.formatPayload({ type: field.type, value: optionIds }); + } + } + } + + return this.formatPayload({ type: field.type, value: dto.value }); + } + + private formatPayload({ type, value }: { type: FieldType; value: unknown }) { + switch (type) { + case FieldType.Text: + case FieldType.RichText: + case FieldType.Link: + return { value: String(value) } as FieldPayloadValue; + case FieldType.Number: + case FieldType.Value: + case FieldType.Formula: + case FieldType.Participant: + return { value: Number(value) } as FieldPayloadValue; + case FieldType.MultiText: + case FieldType.Phone: + case FieldType.Email: + return { + values: Array.isArray(value) ? value.map((i) => String(i)) : [String(value)], + } as FieldPayloadValues; + case FieldType.File: + return { + value: Array.isArray(value) ? value.map((i) => String(i)) : [String(value)], + } as FieldPayloadValue; + case FieldType.Switch: + return { value: Boolean(value) } as FieldPayloadValue; + case FieldType.Date: { + const date = new Date(value as string | number | Date); + return date && DateUtil.isValid(date) ? ({ value: date.toISOString() } as FieldPayloadValue) : null; + } + case FieldType.Select: + case FieldType.ColoredSelect: + return { optionId: Number(value) } as FieldPayloadOption; + case FieldType.MultiSelect: + case FieldType.ColoredMultiSelect: + case FieldType.CheckedMultiSelect: + return { + optionIds: Array.isArray(value) ? value.map((i) => Number(i)) : [Number(value)], + } as FieldPayloadOptions; + case FieldType.Participants: + return { + userIds: Array.isArray(value) ? value.map((i) => Number(i)) : [Number(value)], + } as FieldPayloadParticipants; + case FieldType.Checklist: + return { + value: Array.isArray(value) + ? value.map((i) => ({ text: String(i), checked: false })) + : [{ text: String(value), checked: false }], + } as FieldPayloadValue; + } + } + + private async upsert({ + accountId, + entityId, + dto, + }: { + accountId: number; + entityId: number; + dto: CreateFieldValueDto; + }): Promise { + const newValue = FieldValue.fromDto(accountId, entityId, dto); + + if (newValue.fieldType === FieldType.File) { + const oldValue = await this.findOne({ accountId, entityId, fieldId: newValue.fieldId }); + const currentFiles = oldValue?.getValue() ?? []; + const newFiles = newValue.getValue() ?? []; + const added = newFiles.filter((f) => !currentFiles.includes(f)); + if (added.length) { + await this.storageService.markUsedMany({ accountId, ids: added }); + } + const deleted = currentFiles.filter((f) => !newFiles.includes(f)); + if (deleted.length) { + await this.storageService.delete({ accountId, id: deleted }); + } + } + + await this.repository.upsert(newValue, ['fieldId', 'entityId']); + + return newValue; + } + + private async delete({ accountId, entityId, fieldId }: { accountId: number; entityId: number; fieldId: number }) { + const fieldValue = await this.findOne({ accountId, entityId, fieldId }); + if (fieldValue?.fieldType === FieldType.File) { + await this.storageService.delete({ accountId, id: fieldValue.getValue() }); + } + + await this.repository.delete({ accountId, fieldId, entityId }); + } + + async copyEntityFieldValues({ + accountId, + sourceEntityId, + targetEntityId, + }: { + accountId: number; + sourceEntityId: number; + targetEntityId: number; + }) { + const sourceFieldValues = await this.findMany({ accountId, entityId: sourceEntityId }); + for (const sourceFieldValue of sourceFieldValues) { + await this.upsert({ + accountId, + entityId: targetEntityId, + dto: { + fieldId: sourceFieldValue.fieldId, + fieldType: sourceFieldValue.fieldType, + payload: sourceFieldValue.payload, + }, + }); + } + } + + async getParticipantIds({ accountId, entityId }: { accountId: number; entityId: number }): Promise { + const participantIds: number[] = []; + const [participant, participants] = await Promise.all([ + this.findMany({ accountId, entityId, type: FieldType.Participant }), + this.findMany({ accountId, entityId, type: FieldType.Participants }), + ]); + participant.forEach((f) => { + const value = f.getValue(); + if (value) participantIds.push(value); + }); + participants.forEach((f) => { + const values = f.getValue(); + if (values?.length) participantIds.push(...values); + }); + return participantIds.length ? participantIds.filter(isUnique) : null; + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + + if (filter?.fieldId) { + if (Array.isArray(filter.fieldId)) { + qb.andWhere('field_id IN (:...fieldIds)', { fieldIds: filter.fieldId }); + } else { + qb.andWhere('field_id = :fieldId', { fieldId: filter.fieldId }); + } + } + + if (filter?.entityId) { + qb.andWhere('entity_id = :entityId', { entityId: filter.entityId }); + } + + if (filter?.type) { + if (Array.isArray(filter.type)) { + qb.andWhere('field_type IN (:...types)', { types: filter.type }); + } else { + qb.andWhere('field_type = :type', { type: filter.type }); + } + } + + if (filter?.value) { + qb.andWhere(`payload::jsonb::text ILIKE :value`, { value: `%${filter.value}%` }); + } + + return qb; + } +} diff --git a/backend/src/modules/entity/entity-field/field-value/index.ts b/backend/src/modules/entity/entity-field/field-value/index.ts new file mode 100644 index 0000000..0af9451 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './field-value.handler'; +export * from './field-value.service'; +export * from './types'; diff --git a/backend/src/modules/entity/entity-field/field-value/types/field-payload.ts b/backend/src/modules/entity/entity-field/field-value/types/field-payload.ts new file mode 100644 index 0000000..f83fda5 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/types/field-payload.ts @@ -0,0 +1,24 @@ +export interface FieldPayloadValue { + value: T | null; +} + +export interface FieldPayloadValues { + values: T[] | null; +} + +export interface FieldPayloadOption { + optionId: number | null; +} + +export interface FieldPayloadOptions { + optionIds: number[] | null; +} + +export interface FieldPayloadParticipants { + userIds: number[] | null; +} + +export interface FieldPayloadChecklistItem { + text: string; + checked: boolean; +} diff --git a/backend/src/modules/entity/entity-field/field-value/types/index.ts b/backend/src/modules/entity/entity-field/field-value/types/index.ts new file mode 100644 index 0000000..3045d23 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field-value/types/index.ts @@ -0,0 +1 @@ +export * from './field-payload'; diff --git a/backend/src/modules/entity/entity-field/field/dto/check-formula.dto.ts b/backend/src/modules/entity/entity-field/field/dto/check-formula.dto.ts new file mode 100644 index 0000000..7505201 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/dto/check-formula.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class CheckFormulaDto { + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty() + @IsNumber() + fieldId: number; + + @ApiProperty() + @IsString() + formula: string; +} diff --git a/backend/src/modules/entity/entity-field/field/dto/create-field.dto.ts b/backend/src/modules/entity/entity-field/field/dto/create-field.dto.ts new file mode 100644 index 0000000..36d110b --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/dto/create-field.dto.ts @@ -0,0 +1,27 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { CreateFieldOptionDto } from '../../field-option'; +import { FieldDto } from './field.dto'; + +export class CreateFieldDto extends PickType(FieldDto, [ + 'name', + 'type', + 'code', + 'active', + 'sortOrder', + 'entityTypeId', + 'fieldGroupId', + 'value', + 'format', +] as const) { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + id?: number; + + @ApiPropertyOptional({ type: [CreateFieldOptionDto] }) + @IsOptional() + @IsArray() + options?: CreateFieldOptionDto[]; +} diff --git a/backend/src/modules/entity/entity-field/field/dto/field.dto.ts b/backend/src/modules/entity/entity-field/field/dto/field.dto.ts new file mode 100644 index 0000000..6e96e61 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/dto/field.dto.ts @@ -0,0 +1,83 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { FieldType } from '../../common'; +import { FieldOptionDto } from '../../field-option'; + +import { FieldCode, FieldFormat } from '../enums'; + +export class FieldDto { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Field name' }) + @IsString() + name: string; + + @ApiProperty({ enum: FieldType, description: 'Field type' }) + @IsEnum(FieldType) + type: FieldType; + + @ApiPropertyOptional({ nullable: true, enum: FieldCode, description: 'Field code' }) + @IsOptional() + @IsEnum(FieldCode) + code?: FieldCode | null; + + @ApiProperty({ description: 'Field active status' }) + @IsBoolean() + active: boolean; + + @ApiProperty({ description: 'Field sort order' }) + @IsNumber() + sortOrder: number; + + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiProperty({ nullable: true, description: 'Field group ID' }) + @IsOptional() + @IsNumber() + fieldGroupId: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Field value for formulas' }) + @IsOptional() + @IsString() + value?: string | null; + + @ApiPropertyOptional({ enum: FieldFormat, nullable: true, description: 'Field format' }) + @IsOptional() + @IsEnum(FieldFormat) + format?: FieldFormat | null; + + @ApiProperty({ type: [FieldOptionDto], description: 'Field options' }) + @IsArray() + options: FieldOptionDto[] = []; + + constructor({ + id, + name, + type, + code, + active, + sortOrder, + entityTypeId, + fieldGroupId, + value, + format, + options, + }: FieldDto) { + this.id = id; + this.name = name; + this.type = type; + this.code = code; + this.active = active; + this.sortOrder = sortOrder; + this.entityTypeId = entityTypeId; + this.fieldGroupId = fieldGroupId; + this.value = value; + this.format = format; + this.options = options; + } +} diff --git a/backend/src/modules/entity/entity-field/field/dto/fields-settings.dto.ts b/backend/src/modules/entity/entity-field/field/dto/fields-settings.dto.ts new file mode 100644 index 0000000..1f4cf39 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/dto/fields-settings.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { FieldCode } from '../enums'; + +export class FieldsSettingsDto { + @ApiPropertyOptional({ enum: FieldCode, enumName: 'FieldCode', isArray: true, description: 'Active field codes' }) + @IsOptional() + @IsArray() + activeFieldCodes?: FieldCode[]; +} diff --git a/backend/src/modules/entity/entity-field/field/dto/index.ts b/backend/src/modules/entity/entity-field/field/dto/index.ts new file mode 100644 index 0000000..dabc412 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/dto/index.ts @@ -0,0 +1,5 @@ +export * from './check-formula.dto'; +export * from './create-field.dto'; +export * from './field.dto'; +export * from './fields-settings.dto'; +export * from './update-field.dto'; diff --git a/backend/src/modules/entity/entity-field/field/dto/update-field.dto.ts b/backend/src/modules/entity/entity-field/field/dto/update-field.dto.ts new file mode 100644 index 0000000..1db0552 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/dto/update-field.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { ObjectState } from '@/common'; + +import { UpdateFieldOptionDto } from '../../field-option'; +import { FieldDto } from './field.dto'; + +export class UpdateFieldDto extends PartialType( + PickType(FieldDto, ['name', 'type', 'code', 'active', 'sortOrder', 'fieldGroupId', 'value', 'format'] as const), +) { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + id: number; + + @ApiPropertyOptional({ type: [UpdateFieldOptionDto], description: 'Field options' }) + @IsOptional() + @IsArray() + options?: UpdateFieldOptionDto[] = []; + + @ApiPropertyOptional({ enum: ObjectState, description: 'Object state' }) + @IsOptional() + @IsEnum(ObjectState) + state?: ObjectState; +} diff --git a/backend/src/modules/entity/entity-field/field/entities/field.entity.ts b/backend/src/modules/entity/entity-field/field/entities/field.entity.ts new file mode 100644 index 0000000..4365c3a --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/entities/field.entity.ts @@ -0,0 +1,119 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { FieldType } from '../../common'; +import { FieldOption } from '../../field-option'; + +import { FieldCode, FieldFormat } from '../enums'; +import { CreateFieldDto, FieldDto, UpdateFieldDto } from '../dto'; + +@Entity() +export class Field { + @PrimaryColumn() + id: number; + + @Column() + name: string; + + @Column() + type: FieldType; + + @Column({ nullable: true }) + code: FieldCode | null; + + @Column() + active: boolean; + + @Column() + sortOrder: number; + + @Column() + entityTypeId: number; + + @Column({ nullable: true }) + fieldGroupId: number | null; + + @Column({ nullable: true }) + value: string | null; + + @Column({ nullable: true }) + format: FieldFormat | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + _options: FieldOption[] | null = null; + + constructor( + accountId: number, + id: number, + name: string, + type: FieldType, + code: FieldCode | null, + active: boolean, + sortOrder: number, + entityTypeId: number, + fieldGroupId: number | null, + value: string | null, + format: FieldFormat | null, + ) { + this.accountId = accountId; + this.id = id; + this.name = name; + this.type = type; + this.code = code; + this.active = active; + this.sortOrder = sortOrder; + this.entityTypeId = entityTypeId; + this.fieldGroupId = fieldGroupId; + this.value = value; + this.format = format; + this.createdAt = DateUtil.now(); + } + + public get options(): FieldOption[] | null { + return this._options; + } + public set options(value: FieldOption[] | null) { + this._options = value; + } + + public static fromDto(accountId: number, entityTypeId: number, dto: CreateFieldDto): Field { + return new Field( + accountId, + dto.id, + dto.name, + dto.type, + dto.code, + dto.active ?? true, + dto.sortOrder, + entityTypeId, + dto.fieldGroupId, + dto.value, + dto.format, + ); + } + + public toDto(): FieldDto { + const options = this._options ? this._options.map((option) => option.toDto()) : []; + + return new FieldDto({ ...this, options }); + } + + public update(dto: UpdateFieldDto): Field { + this.name = dto.name !== undefined ? dto.name : this.name; + this.type = dto.type !== undefined ? dto.type : this.type; + this.code = dto.code !== undefined ? dto.code : this.code; + this.active = dto.active !== undefined ? dto.active : this.active; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + this.fieldGroupId = dto.fieldGroupId !== undefined ? dto.fieldGroupId : this.fieldGroupId; + this.value = dto.value !== undefined ? dto.value : this.value; + this.format = dto.format !== undefined ? dto.format : this.format; + + return this; + } +} diff --git a/backend/src/modules/entity/entity-field/field/entities/index.ts b/backend/src/modules/entity/entity-field/field/entities/index.ts new file mode 100644 index 0000000..ae941f2 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/entities/index.ts @@ -0,0 +1 @@ +export * from './field.entity'; diff --git a/backend/src/modules/entity/entity-field/field/enums/field-code.enum.ts b/backend/src/modules/entity/entity-field/field/enums/field-code.enum.ts new file mode 100644 index 0000000..dec8c37 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/enums/field-code.enum.ts @@ -0,0 +1,15 @@ +export enum FieldCode { + Value = 'value', + StartDate = 'start_date', + EndDate = 'end_date', + Participants = 'participants', + Description = 'description', +} + +export const EditableFieldCodes = [ + FieldCode.Value, + FieldCode.StartDate, + FieldCode.EndDate, + FieldCode.Participants, + FieldCode.Description, +]; diff --git a/backend/src/modules/entity/entity-field/field/enums/field-format.enum.ts b/backend/src/modules/entity/entity-field/field/enums/field-format.enum.ts new file mode 100644 index 0000000..fa1e44a --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/enums/field-format.enum.ts @@ -0,0 +1,5 @@ +export enum FieldFormat { + Text = 'text', + Number = 'number', + Currency = 'currency', +} diff --git a/backend/src/modules/entity/entity-field/field/enums/index.ts b/backend/src/modules/entity/entity-field/field/enums/index.ts new file mode 100644 index 0000000..909db21 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/enums/index.ts @@ -0,0 +1,2 @@ +export * from './field-code.enum'; +export * from './field-format.enum'; diff --git a/backend/src/modules/entity/entity-field/field/errors/duplicate-field-name.error.ts b/backend/src/modules/entity/entity-field/field/errors/duplicate-field-name.error.ts new file mode 100644 index 0000000..a2f0649 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/errors/duplicate-field-name.error.ts @@ -0,0 +1,13 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class DuplicateFieldNameError extends ServiceError { + constructor(fieldName: string, message = 'Field name is already used') { + super({ + errorCode: 'field.duplicate_name', + status: HttpStatus.BAD_REQUEST, + message, + details: { fieldName }, + }); + } +} diff --git a/backend/src/modules/entity/entity-field/field/errors/field-used-in-formula.error.ts b/backend/src/modules/entity/entity-field/field/errors/field-used-in-formula.error.ts new file mode 100644 index 0000000..ae163a6 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/errors/field-used-in-formula.error.ts @@ -0,0 +1,13 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class FieldUsedInFormulaError extends ServiceError { + constructor(fieldId: number, message = 'Field used in formula') { + super({ + errorCode: 'field.used_in_formula', + status: HttpStatus.BAD_REQUEST, + message, + details: { fieldId }, + }); + } +} diff --git a/backend/src/modules/entity/entity-field/field/errors/formula-circular-dependency.error.ts b/backend/src/modules/entity/entity-field/field/errors/formula-circular-dependency.error.ts new file mode 100644 index 0000000..c02ba05 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/errors/formula-circular-dependency.error.ts @@ -0,0 +1,13 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class FormulaCircularDependencyError extends ServiceError { + constructor(fieldId: number, message = 'Field formula circular dependency detected') { + super({ + errorCode: 'field.formula_circular_dependency', + status: HttpStatus.BAD_REQUEST, + message, + details: { fieldId }, + }); + } +} diff --git a/backend/src/modules/entity/entity-field/field/errors/index.ts b/backend/src/modules/entity/entity-field/field/errors/index.ts new file mode 100644 index 0000000..7a129df --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/errors/index.ts @@ -0,0 +1,3 @@ +export * from './duplicate-field-name.error'; +export * from './field-used-in-formula.error'; +export * from './formula-circular-dependency.error'; diff --git a/backend/src/modules/entity/entity-field/field/field.controller.ts b/backend/src/modules/entity/entity-field/field/field.controller.ts new file mode 100644 index 0000000..78a8978 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/field.controller.ts @@ -0,0 +1,34 @@ +import { Body, Controller, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { FieldService } from './field.service'; +import { CheckFormulaDto, FieldsSettingsDto } from './dto'; + +@ApiTags('crm/fields') +@Controller('/crm') +@JwtAuthorized() +@TransformToDto() +export class FieldController { + constructor(private readonly service: FieldService) {} + + @ApiOkResponse({ description: 'Check formula', type: Boolean }) + @Post('fields/formula/check') + public async checkFormula(@CurrentAuth() { accountId }: AuthData, @Body() dto: CheckFormulaDto) { + return this.service.checkFormula({ accountId, ...dto }); + } + + @ApiCreatedResponse({ description: 'Update entity type field settings' }) + @Put('entity-types/:entityTypeId/fields-settings') + public async updateFieldsSettings( + @CurrentAuth() { accountId }: AuthData, + @Param('entityTypeId', ParseIntPipe) entityTypeId: number, + @Body() dto: FieldsSettingsDto, + ) { + return await this.service.updateFieldsSettings({ accountId, entityTypeId, activeFieldCodes: dto.activeFieldCodes }); + } +} diff --git a/backend/src/modules/entity/entity-field/field/field.service.ts b/backend/src/modules/entity/entity-field/field/field.service.ts new file mode 100644 index 0000000..001d3d6 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/field.service.ts @@ -0,0 +1,426 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { parse } from 'mathjs'; + +import { ObjectState } from '@/common'; +import { SequenceIdService } from '@/database'; + +import { SequenceName } from '@/CRM/common/enums/sequence-name.enum'; + +import { FieldEvent, FieldEventType, FieldType, FieldTypes, FormulaUtil } from '../common'; +import { FieldOptionService } from '../field-option'; + +import { FieldCode, EditableFieldCodes } from './enums'; +import { DuplicateFieldNameError, FieldUsedInFormulaError, FormulaCircularDependencyError } from './errors'; +import { CreateFieldDto, UpdateFieldDto } from './dto'; +import { ExpandableField } from './types'; +import { Field } from './entities'; + +interface CreateOptions { + skipProcessing?: boolean; +} + +interface FindFilter { + accountId: number; + id?: number | number[]; + entityTypeId?: number | number[]; + type?: FieldType | FieldType[]; + code?: string; + name?: string; + value?: string; + excludeId?: number | number[]; + excludeEntityTypeId?: number | number[]; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class FieldService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Field) + private readonly repository: Repository, + private readonly sequenceIdService: SequenceIdService, + private readonly fieldOptionService: FieldOptionService, + ) {} + + private async nextIdentity(): Promise { + return this.sequenceIdService.nextIdentity(SequenceName.Field); + } + + async create({ + accountId, + entityTypeId, + dto, + options, + }: { + accountId: number; + entityTypeId: number; + dto: CreateFieldDto; + options?: CreateOptions; + }): Promise { + dto.id = dto.id ?? (await this.nextIdentity()); + + if (!options?.skipProcessing && FieldTypes.formula.includes(dto.type)) { + if (await this.hasCircularDependency({ accountId, entityTypeId, fieldId: dto.id, value: dto.value })) { + throw new FormulaCircularDependencyError(dto.id); + } + } + + if (await this.hasDuplicateName({ accountId, entityTypeId, name: dto.name })) { + throw new DuplicateFieldNameError(dto.name); + } + + const field = await this.repository.save(Field.fromDto(accountId, entityTypeId, dto)); + + if (dto.options) { + field.options = await this.fieldOptionService.createMany({ accountId, fieldId: dto.id, dtos: dto.options }); + } + + if (!options?.skipProcessing) { + this.eventEmitter.emit( + FieldEventType.FieldCreated, + new FieldEvent({ accountId, entityTypeId, fieldId: field.id, type: field.type }), + ); + } + + return field; + } + async createMany({ + accountId, + entityTypeId, + dtos, + options, + }: { + accountId: number; + entityTypeId: number; + dtos: CreateFieldDto[]; + options?: CreateOptions; + }): Promise { + return Promise.all(dtos.map((dto) => this.create({ accountId, entityTypeId, dto, options }))); + } + + async findOne(filter: FindFilter, options?: FindOptions): Promise { + const field = await this.createFindQb(filter).getOne(); + + return field && options?.expand + ? await this.expandOne({ accountId: filter.accountId, field, expand: options.expand }) + : field; + } + + async findMany(filter: FindFilter, options?: FindOptions): Promise { + const fields = await this.createFindQb(filter).orderBy('sort_order', 'ASC').getMany(); + + return fields && options?.expand + ? await this.expandMany({ accountId: filter.accountId, fields, expand: options.expand }) + : fields; + } + + async findManyIds(filter: FindFilter): Promise { + const fields = await this.createFindQb(filter) + .select('id') + .orderBy('sort_order', 'ASC') + .getRawMany<{ id: number }>(); + + return fields.map((field) => field.id); + } + + async getCount(filter: FindFilter): Promise { + return this.createFindQb(filter).getCount(); + } + + async hasPriceField({ accountId, entityTypeId }: { accountId: number; entityTypeId: number }): Promise { + const count = await this.getCount({ accountId, entityTypeId, type: FieldType.Value }); + + return count > 0; + } + + async checkFormula({ + accountId, + entityTypeId, + fieldId, + formula, + }: { + accountId: number; + entityTypeId: number; + fieldId: number; + formula: string; + }): Promise { + if (!formula) { + return true; + } + + try { + parse(formula); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + return false; + } + + if (await this.hasCircularDependency({ accountId, entityTypeId, fieldId, value: formula })) { + throw new FormulaCircularDependencyError(fieldId); + } + + return true; + } + + async checkFormulaUsageField({ accountId, fieldId }: { accountId: number; fieldId: number }): Promise { + return (await this.getCount({ accountId, type: FieldTypes.formula, value: `f${fieldId}` })) > 0; + } + async checkFormulaUsageEntityType({ + accountId, + entityTypeId, + checkEntityTypeId, + excludeEntityTypeId, + }: { + accountId: number; + entityTypeId?: number; + checkEntityTypeId: number; + excludeEntityTypeId?: number; + }): Promise { + return ( + (await this.getCount({ + accountId, + type: FieldTypes.formula, + entityTypeId, + excludeEntityTypeId, + value: `et${checkEntityTypeId}`, + })) > 0 + ); + } + + private async hasDuplicateName({ + accountId, + entityTypeId, + name, + id, + }: { + accountId: number; + entityTypeId: number; + name: string; + id?: number; + }): Promise { + return (await this.createFindQb({ accountId, entityTypeId, name, excludeId: id }).getCount()) > 0; + } + + async update({ + accountId, + entityTypeId, + fieldId, + dto, + options, + }: { + accountId: number; + entityTypeId: number; + fieldId: number; + dto: UpdateFieldDto; + options?: CreateOptions; + }): Promise { + if (dto.value && !options?.skipProcessing && FieldTypes.formula.includes(dto.type)) { + if (await this.hasCircularDependency({ accountId, entityTypeId, fieldId, value: dto.value })) { + throw new FormulaCircularDependencyError(fieldId); + } + } + + if (dto.name && (await this.hasDuplicateName({ accountId, entityTypeId, name: dto.name, id: fieldId }))) { + throw new DuplicateFieldNameError(dto.name); + } + + const field = await this.findOne({ accountId, id: fieldId }); + await this.repository.save(field.update(dto)); + + if (dto.options) { + field.options = await this.fieldOptionService.processBatch({ accountId, fieldId, dtos: dto.options }); + } + + if (!options?.skipProcessing) { + this.eventEmitter.emit( + FieldEventType.FieldUpdated, + new FieldEvent({ accountId, entityTypeId, fieldId: field.id, type: field.type }), + ); + } + + return field; + } + + async updateBatch({ + accountId, + entityTypeId, + dtos, + }: { + accountId: number; + entityTypeId: number; + dtos: UpdateFieldDto[]; + }): Promise { + const fields: Field[] = []; + + for (const dto of dtos) { + switch (dto.state) { + case ObjectState.Created: + fields.push(await this.create({ accountId, entityTypeId, dto: dto as CreateFieldDto })); + break; + case ObjectState.Updated: + fields.push(await this.update({ accountId, entityTypeId, fieldId: dto.id, dto })); + break; + case ObjectState.Deleted: + await this.delete({ accountId, entityTypeId, fieldId: dto.id }); + break; + } + } + + return fields; + } + + private async delete({ + accountId, + entityTypeId, + fieldId, + }: { + accountId: number; + entityTypeId: number; + fieldId: number; + }): Promise { + if (await this.checkFormulaUsageField({ accountId, fieldId })) { + throw new FieldUsedInFormulaError(fieldId); + } + await this.repository.delete({ id: fieldId, accountId, entityTypeId }); + this.eventEmitter.emit(FieldEventType.FieldDeleted, new FieldEvent({ accountId, entityTypeId, fieldId })); + } + + async updateFieldsSettings({ + accountId, + entityTypeId, + activeFieldCodes, + }: { + accountId: number; + entityTypeId: number; + activeFieldCodes: FieldCode[]; + }) { + for (const code of EditableFieldCodes) { + const field = await this.findOne({ accountId, entityTypeId, code: code }); + if (field) { + await this.repository.update({ accountId, id: field.id }, { active: activeFieldCodes.includes(code) }); + } + } + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + if (filter?.id) { + if (Array.isArray(filter.id)) { + qb.andWhere('id IN (:...ids)', { ids: filter.id }); + } else { + qb.andWhere('id = :id', { id: filter.id }); + } + } + if (filter?.entityTypeId) { + if (Array.isArray(filter.entityTypeId)) { + qb.andWhere('entity_type_id IN (:...entityTypeIds)', { entityTypeIds: filter.entityTypeId }); + } else { + qb.andWhere('entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + } + if (filter?.excludeEntityTypeId) { + if (Array.isArray(filter.excludeEntityTypeId)) { + qb.andWhere('entity_type_id NOT IN (:...excludeEntityTypeIds)', { + excludeEntityTypeIds: filter.excludeEntityTypeId, + }); + } else { + qb.andWhere('entity_type_id != :excludeEntityTypeId', { excludeEntityTypeId: filter.excludeEntityTypeId }); + } + } + if (filter?.type) { + if (Array.isArray(filter.type)) { + qb.andWhere('type IN (:...types)', { types: filter.type }); + } else { + qb.andWhere('type = :type', { type: filter.type }); + } + } + if (filter?.code) { + qb.andWhere('code = :code', { code: filter.code }); + } + if (filter?.name) { + qb.andWhere('name = :name', { name: filter.name }); + } + if (filter?.value) { + qb.andWhere('value ilike :value', { value: `%${filter.value}%` }); + } + + if (filter?.excludeId) { + if (Array.isArray(filter.excludeId)) { + if (filter.excludeId.length) { + qb.andWhere('id NOT IN (:...excludeIds)', { excludeIds: filter.excludeId }); + } + } else { + qb.andWhere('id != :excludeId', { excludeId: filter.excludeId }); + } + } + return qb; + } + + private async expandOne({ + accountId, + field, + expand, + }: { + accountId: number; + field: Field; + expand: ExpandableField[]; + }): Promise { + if (expand.includes('options')) { + field.options = await this.fieldOptionService.findMany({ accountId, fieldId: field.id }); + } + return field; + } + private async expandMany({ + accountId, + fields, + expand, + }: { + accountId: number; + fields: Field[]; + expand: ExpandableField[]; + }): Promise { + return await Promise.all(fields.map((field) => this.expandOne({ accountId, field, expand }))); + } + + private async hasCircularDependency({ + accountId, + entityTypeId, + fieldId, + value, + }: { + accountId: number; + entityTypeId: number; + fieldId: number; + value: string | null; + }): Promise { + const visit = async (etId: number, fId: number, formula: string, visited: string[]): Promise => { + const fieldKey = FormulaUtil.createFieldKey({ entityTypeId: etId, fieldId: fId }); + if (visited.includes(fieldKey)) { + return true; + } + + const formulaKeys = FormulaUtil.extractVariables(formula); + for (const formulaKey of formulaKeys) { + const { entityTypeId: formulaEtId, fieldId: formulaFId } = FormulaUtil.parseFieldKey(formulaKey); + const field = await this.findOne({ + accountId, + entityTypeId: formulaEtId, + id: formulaFId, + type: FieldTypes.formula, + }); + if (field && (await visit(field.entityTypeId, field.id, field.value, [...visited, fieldKey]))) { + return true; + } + } + + return false; + }; + + return await visit(entityTypeId, fieldId, value || '', []); + } +} diff --git a/backend/src/modules/entity/entity-field/field/index.ts b/backend/src/modules/entity/entity-field/field/index.ts new file mode 100644 index 0000000..af8e201 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './field.controller'; +export * from './field.service'; +export * from './types'; diff --git a/backend/src/modules/entity/entity-field/field/types/expandable-field.ts b/backend/src/modules/entity/entity-field/field/types/expandable-field.ts new file mode 100644 index 0000000..d30f22b --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'options'; diff --git a/backend/src/modules/entity/entity-field/field/types/index.ts b/backend/src/modules/entity/entity-field/field/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/entity/entity-field/field/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/entity/entity-info/dto/entity-info.dto.ts b/backend/src/modules/entity/entity-info/dto/entity-info.dto.ts new file mode 100644 index 0000000..7da1770 --- /dev/null +++ b/backend/src/modules/entity/entity-info/dto/entity-info.dto.ts @@ -0,0 +1,64 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class EntityInfoDto { + @ApiProperty({ description: 'Entity ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Entity name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiProperty({ description: 'Owner (Responsible) User ID' }) + @IsNumber() + ownerId: number; + + @ApiProperty({ description: 'Board ID', nullable: true }) + @IsOptional() + @IsNumber() + boardId: number | null; + + @ApiProperty({ description: 'Stage ID', nullable: true }) + @IsOptional() + @IsNumber() + stageId: number | null; + + @ApiProperty({ description: 'Date and time when the entity was created' }) + @IsString() + createdAt: string; + + @ApiPropertyOptional({ nullable: true, description: 'Date and time when the entity was closed' }) + @IsOptional() + @IsString() + closedAt?: string | null; + + @ApiPropertyOptional({ description: 'Entity ID from which the entity was copied', nullable: true }) + @IsOptional() + @IsNumber() + copiedFrom?: number | null; + + @ApiPropertyOptional({ description: 'Number of times the entity has been copied', nullable: true }) + @IsOptional() + @IsNumber() + copiedCount?: number | null; + + @ApiPropertyOptional({ description: 'Array of participant IDs', nullable: true, type: [Number] }) + @IsOptional() + @IsNumber({}, { each: true }) + participantIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Whether the user has access to the entity', nullable: true }) + @IsOptional() + @IsBoolean() + hasAccess?: boolean | null; + + @ApiPropertyOptional({ description: 'Is focused?' }) + @IsOptional() + @IsBoolean() + focused?: boolean; +} diff --git a/backend/src/modules/entity/entity-info/dto/index.ts b/backend/src/modules/entity/entity-info/dto/index.ts new file mode 100644 index 0000000..4069e1c --- /dev/null +++ b/backend/src/modules/entity/entity-info/dto/index.ts @@ -0,0 +1 @@ +export * from './entity-info.dto'; diff --git a/backend/src/modules/entity/entity-info/entity-info.controller.ts b/backend/src/modules/entity/entity-info/entity-info.controller.ts new file mode 100644 index 0000000..2fabfa5 --- /dev/null +++ b/backend/src/modules/entity/entity-info/entity-info.controller.ts @@ -0,0 +1,25 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { EntityInfoDto } from './dto'; +import { EntityInfoService } from './entity-info.service'; + +@ApiTags('crm/entities') +@Controller('crm/entities/:entityId/info') +@JwtAuthorized({ prefetch: { user: true } }) +export class EntityInfoController { + constructor(private readonly service: EntityInfoService) {} + + @ApiOperation({ summary: 'Get entity info', description: 'Get entity info' }) + @ApiParam({ name: 'entityId', description: 'Entity ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Entity info', type: EntityInfoDto }) + @Get() + async findOne( + @CurrentAuth() { accountId, user }: AuthData, + @Param('entityId', ParseIntPipe) entityId: number, + ): Promise { + return this.service.findOne({ accountId, user, entityId }); + } +} diff --git a/backend/src/modules/entity/entity-info/entity-info.module.ts b/backend/src/modules/entity/entity-info/entity-info.module.ts new file mode 100644 index 0000000..f2e342f --- /dev/null +++ b/backend/src/modules/entity/entity-info/entity-info.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { Entity } from '@/CRM/Model/Entity/Entity'; + +import { EntityInfoController } from './entity-info.controller'; +import { EntityInfoService } from './entity-info.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Entity]), IAMModule], + controllers: [EntityInfoController], + providers: [EntityInfoService], + exports: [EntityInfoService], +}) +export class EntityInfoModule {} diff --git a/backend/src/modules/entity/entity-info/entity-info.service.ts b/backend/src/modules/entity/entity-info/entity-info.service.ts new file mode 100644 index 0000000..076be26 --- /dev/null +++ b/backend/src/modules/entity/entity-info/entity-info.service.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { Entity } from '@/CRM/Model/Entity/Entity'; + +import { EntityInfoDto } from './dto'; + +@Injectable() +export class EntityInfoService { + constructor( + @InjectRepository(Entity) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + ) {} + + async findMany({ + accountId, + user, + entityIds, + }: { + accountId: number; + user?: User | null; + entityIds: number[]; + }): Promise { + const entities = await this.createQb({ accountId, entityId: entityIds }).getMany(); + + const result = []; + + for (const entity of entities) { + result.push(await this.getEntityInfo({ user, entity })); + } + + return result; + } + + async findOne({ + accountId, + user, + entityId, + }: { + accountId: number; + user?: User | null; + entityId: number; + }): Promise { + const entity = await this.createQb({ accountId, entityId }).getOne(); + + return entity ? this.getEntityInfo({ user, entity }) : null; + } + + async getEntityInfo({ + user, + entity, + access, + }: { + user?: User | null; + entity: Entity; + access?: boolean; + }): Promise { + const hasAccess = + user && access === undefined + ? await this.authService.check({ action: 'view', user, authorizable: entity }) + : access; + + return { + id: entity.id, + name: entity.name, + entityTypeId: entity.entityTypeId, + ownerId: entity.responsibleUserId, + boardId: entity.boardId, + stageId: entity.stageId, + createdAt: entity.createdAt.toISOString(), + closedAt: entity.closedAt?.toISOString() ?? null, + hasAccess, + copiedFrom: entity.copiedFrom, + copiedCount: entity.copiedCount, + participantIds: entity.participantIds, + focused: entity.focused, + }; + } + + private createQb({ accountId, entityId }: { accountId: number; entityId: number | number[] }) { + const qb = this.repository.createQueryBuilder('entity').where('entity.accountId = :accountId', { accountId }); + + if (Array.isArray(entityId)) { + qb.andWhere('entity.id IN (:...entityId)', { entityId }); + } else { + qb.andWhere('entity.id = :entityId', { entityId }); + } + + return qb; + } +} diff --git a/backend/src/modules/entity/entity-info/index.ts b/backend/src/modules/entity/entity-info/index.ts new file mode 100644 index 0000000..93ca412 --- /dev/null +++ b/backend/src/modules/entity/entity-info/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entity-info.controller'; +export * from './entity-info.module'; +export * from './entity-info.service'; diff --git a/backend/src/modules/entity/entity-stage-history/dto/create-entity-stage-history.dto.ts b/backend/src/modules/entity/entity-stage-history/dto/create-entity-stage-history.dto.ts new file mode 100644 index 0000000..01e6070 --- /dev/null +++ b/backend/src/modules/entity/entity-stage-history/dto/create-entity-stage-history.dto.ts @@ -0,0 +1,16 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { EntityStageHistoryDto } from './entity-stage-history.dto'; + +export class CreateEntityStageHistoryDto extends PickType(EntityStageHistoryDto, [ + 'entityId', + 'boardId', + 'stageId', + 'createdAt', +] as const) { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + copiedFrom?: number; +} diff --git a/backend/src/modules/entity/entity-stage-history/dto/entity-stage-history.dto.ts b/backend/src/modules/entity/entity-stage-history/dto/entity-stage-history.dto.ts new file mode 100644 index 0000000..e5145aa --- /dev/null +++ b/backend/src/modules/entity/entity-stage-history/dto/entity-stage-history.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsNumber, IsOptional } from 'class-validator'; + +export class EntityStageHistoryDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty() + @IsNumber() + boardId: number; + + @ApiProperty() + @IsNumber() + stageId: number; + + @ApiPropertyOptional() + @IsOptional() + @IsDateString() + createdAt?: string; +} diff --git a/backend/src/modules/entity/entity-stage-history/entities/entity-stage-history.entity.ts b/backend/src/modules/entity/entity-stage-history/entities/entity-stage-history.entity.ts new file mode 100644 index 0000000..80ed9ed --- /dev/null +++ b/backend/src/modules/entity/entity-stage-history/entities/entity-stage-history.entity.ts @@ -0,0 +1,44 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { CreateEntityStageHistoryDto } from '../dto/create-entity-stage-history.dto'; + +@Entity() +export class EntityStageHistory { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + entityId: number; + + @Column() + boardId: number; + + @Column() + stageId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, entityId: number, boardId: number, stageId: number, createdAt?: Date) { + this.accountId = accountId; + this.entityId = entityId; + this.boardId = boardId; + this.stageId = stageId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto(accountId: number, dto: CreateEntityStageHistoryDto): EntityStageHistory { + return new EntityStageHistory( + accountId, + dto.entityId, + dto.boardId, + dto.stageId, + dto.createdAt ? DateUtil.fromISOString(dto.createdAt) : undefined, + ); + } +} diff --git a/backend/src/modules/entity/entity-stage-history/entity-stage-history.handler.ts b/backend/src/modules/entity/entity-stage-history/entity-stage-history.handler.ts new file mode 100644 index 0000000..a4eb687 --- /dev/null +++ b/backend/src/modules/entity/entity-stage-history/entity-stage-history.handler.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { CrmEventType, EntityCreatedEvent, EntityEvent } from '@/CRM/common'; + +import { EntityStageHistoryService } from './entity-stage-history.service'; + +@Injectable() +export class EntityStageHistoryHandler { + constructor(private readonly service: EntityStageHistoryService) {} + + @OnEvent(CrmEventType.EntityCreated, { async: true }) + public async onEntityNew(event: EntityCreatedEvent): Promise { + if (event.boardId && event.stageId) { + if (event.copiedFrom) { + this.service.copyHistory(event.accountId, event.copiedFrom, event.entityId); + } else { + this.service.create(event.accountId, { ...event }); + } + } + } + + @OnEvent(CrmEventType.EntityStageChanged, { async: true }) + public async onEntityStageChanged(event: EntityEvent) { + if (event.boardId && event.stageId) { + this.service.create(event.accountId, { ...event }); + } + } +} diff --git a/backend/src/modules/entity/entity-stage-history/entity-stage-history.module.ts b/backend/src/modules/entity/entity-stage-history/entity-stage-history.module.ts new file mode 100644 index 0000000..4e2c0ee --- /dev/null +++ b/backend/src/modules/entity/entity-stage-history/entity-stage-history.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { EntityStageHistory } from './entities/entity-stage-history.entity'; +import { EntityStageHistoryService } from './entity-stage-history.service'; +import { EntityStageHistoryHandler } from './entity-stage-history.handler'; + +@Module({ + imports: [TypeOrmModule.forFeature([EntityStageHistory])], + providers: [EntityStageHistoryService, EntityStageHistoryHandler], +}) +export class EntityStageHistoryModule {} diff --git a/backend/src/modules/entity/entity-stage-history/entity-stage-history.service.ts b/backend/src/modules/entity/entity-stage-history/entity-stage-history.service.ts new file mode 100644 index 0000000..7aef5d2 --- /dev/null +++ b/backend/src/modules/entity/entity-stage-history/entity-stage-history.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { CreateEntityStageHistoryDto } from './dto/create-entity-stage-history.dto'; +import { EntityStageHistory } from './entities/entity-stage-history.entity'; + +@Injectable() +export class EntityStageHistoryService { + constructor( + @InjectRepository(EntityStageHistory) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, dto: CreateEntityStageHistoryDto): Promise { + return this.repository.save(EntityStageHistory.fromDto(accountId, dto)); + } + + public async copyHistory(accountId: number, fromId: number, toId: number) { + await this.repository.update({ accountId, entityId: fromId }, { entityId: toId }); + } +} diff --git a/backend/src/modules/entity/entity.module.ts b/backend/src/modules/entity/entity.module.ts new file mode 100644 index 0000000..25dce65 --- /dev/null +++ b/backend/src/modules/entity/entity.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { EntityEventModule } from './entity-event/entity-event.module'; +import { EntityFieldModule } from './entity-field/entity-field.module'; +import { EntityInfoModule } from './entity-info/entity-info.module'; +import { EntityStageHistoryModule } from './entity-stage-history/entity-stage-history.module'; + +@Module({ + imports: [EntityEventModule, EntityFieldModule, EntityInfoModule, EntityStageHistoryModule], +}) +export class EntityModule {} diff --git a/backend/src/modules/forms/forms.module.ts b/backend/src/modules/forms/forms.module.ts new file mode 100644 index 0000000..d343e1b --- /dev/null +++ b/backend/src/modules/forms/forms.module.ts @@ -0,0 +1,59 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { SchedulerModule } from '@/modules/scheduler/scheduler.module'; +import { StorageModule } from '@/modules/storage/storage.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { SiteFormField, SiteFormFieldController, SiteFormFieldService } from './site-form-field'; +import { SiteFormPage, SiteFormPageController, SiteFormPageService } from './site-form-page'; +import { SiteFormConsent, SiteFormConsentController, SiteFormConsentService } from './site-form-consent'; +import { SiteFormGratitude, SiteFormGratitudeController, SiteFormGratitudeService } from './site-form-gratitude'; +import { + SiteForm, + SiteFormController, + SiteFormEntityType, + SiteFormEntityTypeService, + SiteFormSchedule, + SiteFormScheduleService, + SiteFormService, +} from './site-form'; +import { SiteFormBuilderController, SiteFormBuilderService } from './site-form-builder'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + SiteForm, + SiteFormEntityType, + SiteFormSchedule, + SiteFormPage, + SiteFormConsent, + SiteFormGratitude, + SiteFormField, + ]), + IAMModule, + StorageModule, + CrmModule, + SchedulerModule, + ], + providers: [ + SiteFormService, + SiteFormEntityTypeService, + SiteFormScheduleService, + SiteFormConsentService, + SiteFormGratitudeService, + SiteFormPageService, + SiteFormFieldService, + SiteFormBuilderService, + ], + controllers: [ + SiteFormController, + SiteFormConsentController, + SiteFormGratitudeController, + SiteFormPageController, + SiteFormFieldController, + SiteFormBuilderController, + ], +}) +export class FormsModule {} diff --git a/backend/src/modules/forms/site-form-builder/dto/index.ts b/backend/src/modules/forms/site-form-builder/dto/index.ts new file mode 100644 index 0000000..5c5c264 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/index.ts @@ -0,0 +1,18 @@ +export * from './public-site-form-consent.dto'; +export * from './public-site-form-field-entity-field.dto'; +export * from './public-site-form-field-schedule-date.dto'; +export * from './public-site-form-field-schedule-spot.dto'; +export * from './public-site-form-field-schedule-time.dto'; +export * from './public-site-form-field-schedule.dto'; +export * from './public-site-form-field.dto'; +export * from './public-site-form-gratitude.dto'; +export * from './public-site-form-option.dto'; +export * from './public-site-form-page.dto'; +export * from './public-site-form.dto'; +export * from './site-form-analytic-data.dto'; +export * from './site-form-data-plain.dto'; +export * from './site-form-data.dto'; +export * from './site-form-field-data.dto'; +export * from './site-form-file-upload-request'; +export * from './site-form-file-upload-result'; +export * from './site-form-result.dto'; diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-consent.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-consent.dto.ts new file mode 100644 index 0000000..fc56198 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-consent.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +export class PublicSiteFormConsentDto { + @ApiProperty({ description: 'Site form consent enabled' }) + @IsBoolean() + isEnabled: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Site form consent text' }) + @IsOptional() + @IsString() + text?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Site form consent link URL' }) + @IsOptional() + @IsString() + linkUrl?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Site form consent link text' }) + @IsOptional() + @IsString() + linkText?: string | null; + + @ApiPropertyOptional({ description: 'Site form consent default value' }) + @IsOptional() + @IsBoolean() + defaultValue?: boolean; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-entity-field.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-entity-field.dto.ts new file mode 100644 index 0000000..f072264 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-entity-field.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsObject, IsOptional } from 'class-validator'; + +import { FieldType } from '@/modules/entity/entity-field/common'; + +import { PublicSiteFormOptionDto } from './public-site-form-option.dto'; + +export class PublicSiteFormFieldEntityFieldDto { + @ApiProperty({ enum: FieldType, description: 'Field type' }) + @IsEnum(FieldType) + fieldType: FieldType; + + @ApiPropertyOptional({ type: [PublicSiteFormOptionDto], nullable: true, description: 'Field options' }) + @IsOptional() + @IsArray() + options?: PublicSiteFormOptionDto[] | null; + + @ApiPropertyOptional({ nullable: true, description: 'Field validation required' }) + @IsOptional() + @IsBoolean() + isValidationRequired: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'Field validation pattern' }) + @IsOptional() + @IsObject() + meta?: object | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-date.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-date.dto.ts new file mode 100644 index 0000000..a3743df --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-date.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class PublicSiteFormFieldScheduleDateDto { + @ApiPropertyOptional({ type: [String], nullable: true, description: 'Dates in ISO format' }) + @IsOptional() + @IsString({ each: true }) + dates?: string[] | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-spot.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-spot.dto.ts new file mode 100644 index 0000000..6dccb0a --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-spot.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsDateString } from 'class-validator'; + +export class PublicSiteFormFieldScheduleSpotDto { + @ApiProperty({ description: 'Start time in ISO format' }) + @IsDateString() + from: string; + + @ApiProperty({ description: 'End time in ISO format' }) + @IsDateString() + to: string; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-time.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-time.dto.ts new file mode 100644 index 0000000..199b52c --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule-time.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { PublicSiteFormFieldScheduleSpotDto } from './public-site-form-field-schedule-spot.dto'; + +export class PublicSiteFormFieldScheduleTimeDto { + @ApiPropertyOptional({ type: [PublicSiteFormFieldScheduleSpotDto], nullable: true, description: 'Spots' }) + @IsOptional() + @IsArray() + spots?: PublicSiteFormFieldScheduleSpotDto[] | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule.dto.ts new file mode 100644 index 0000000..eeb9ba1 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field-schedule.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { PublicSiteFormOptionDto } from './public-site-form-option.dto'; + +export class PublicSiteFormFieldScheduleDto { + @ApiPropertyOptional({ type: [PublicSiteFormOptionDto], nullable: true, description: 'Field options' }) + @IsOptional() + @IsArray() + options?: PublicSiteFormOptionDto[] | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-field.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field.dto.ts new file mode 100644 index 0000000..19812dd --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-field.dto.ts @@ -0,0 +1,63 @@ +import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { SiteFormFieldType } from '../../site-form-field'; +import { PublicSiteFormFieldEntityFieldDto } from './public-site-form-field-entity-field.dto'; +import { PublicSiteFormFieldScheduleDto } from './public-site-form-field-schedule.dto'; +import { PublicSiteFormFieldScheduleDateDto } from './public-site-form-field-schedule-date.dto'; +import { PublicSiteFormFieldScheduleTimeDto } from './public-site-form-field-schedule-time.dto'; + +@ApiExtraModels(PublicSiteFormFieldEntityFieldDto) +@ApiExtraModels(PublicSiteFormFieldScheduleDto) +@ApiExtraModels(PublicSiteFormFieldScheduleDateDto) +@ApiExtraModels(PublicSiteFormFieldScheduleTimeDto) +export class PublicSiteFormFieldDto { + @ApiProperty({ description: 'Site form field id' }) + @IsNumber() + id: number; + + @ApiPropertyOptional({ nullable: true, description: 'Site form field label' }) + @IsOptional() + @IsString() + label: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Site form field placeholder' }) + @IsOptional() + @IsString() + placeholder: string | null; + + @ApiProperty({ enum: SiteFormFieldType, description: 'Site form field type' }) + @IsString() + @IsEnum(SiteFormFieldType) + type: SiteFormFieldType; + + @ApiPropertyOptional({ nullable: true, description: 'Site form field required' }) + @IsOptional() + @IsBoolean() + isRequired?: boolean | null; + + @ApiProperty({ description: 'Site form field sort order' }) + @IsNumber() + sortOrder: number; + + @ApiPropertyOptional({ + nullable: true, + description: 'Site form field settings', + type: 'array', + items: { + oneOf: [ + { $ref: getSchemaPath(PublicSiteFormFieldEntityFieldDto) }, + { $ref: getSchemaPath(PublicSiteFormFieldScheduleDto) }, + { $ref: getSchemaPath(PublicSiteFormFieldScheduleDateDto) }, + { $ref: getSchemaPath(PublicSiteFormFieldScheduleTimeDto) }, + ], + }, + }) + @IsOptional() + settings?: + | PublicSiteFormFieldEntityFieldDto + | PublicSiteFormFieldScheduleDto + | PublicSiteFormFieldScheduleDateDto + | PublicSiteFormFieldScheduleTimeDto + | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-gratitude.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-gratitude.dto.ts new file mode 100644 index 0000000..d24beb8 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-gratitude.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +export class PublicSiteFormGratitudeDto { + @ApiProperty({ description: 'Site form gratitude enabled' }) + @IsBoolean() + isEnabled: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Site form gratitude header' }) + @IsOptional() + @IsString() + header?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Site form gratitude text' }) + @IsOptional() + @IsString() + text?: string | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-option.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-option.dto.ts new file mode 100644 index 0000000..881a885 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-option.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class PublicSiteFormOptionDto { + @ApiProperty({ description: 'Option id' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Option label' }) + @IsNumber() + label: string; + + @ApiProperty({ nullable: true, description: 'Option value' }) + @IsOptional() + @IsString() + color?: string | null; + + @ApiProperty({ description: 'Option sort order' }) + @IsNumber() + sortOrder?: number; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form-page.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form-page.dto.ts new file mode 100644 index 0000000..5f09df0 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form-page.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { PublicSiteFormFieldDto } from './public-site-form-field.dto'; + +export class PublicSiteFormPageDto { + @ApiProperty({ description: 'Site form page id' }) + @IsNumber() + id: number; + + @ApiPropertyOptional({ nullable: true, description: 'Site form page title' }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiProperty({ description: 'Site form page sort order' }) + @IsNumber() + sortOrder: number; + + @ApiProperty({ type: [PublicSiteFormFieldDto], description: 'Site form page fields' }) + @IsArray() + fields: PublicSiteFormFieldDto[]; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/public-site-form.dto.ts b/backend/src/modules/forms/site-form-builder/dto/public-site-form.dto.ts new file mode 100644 index 0000000..76aa8fd --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/public-site-form.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsObject, IsOptional, IsString } from 'class-validator'; + +import { PublicSiteFormConsentDto } from './public-site-form-consent.dto'; +import { PublicSiteFormGratitudeDto } from './public-site-form-gratitude.dto'; +import { PublicSiteFormPageDto } from './public-site-form-page.dto'; + +export class PublicSiteFormDto { + @ApiProperty({ description: 'Site form code' }) + @IsString() + code: string; + + @ApiPropertyOptional({ nullable: true, description: 'Site form title' }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Site form design' }) + @IsOptional() + @IsObject() + design: object | null; + + @ApiPropertyOptional({ description: 'Site form field label enabled' }) + @IsOptional() + @IsBoolean() + fieldLabelEnabled?: boolean; + + @ApiPropertyOptional({ description: 'Site form field placeholder enabled' }) + @IsOptional() + @IsBoolean() + fieldPlaceholderEnabled?: boolean; + + @ApiPropertyOptional({ type: PublicSiteFormConsentDto, nullable: true, description: 'Site form consent' }) + @IsOptional() + consent?: PublicSiteFormConsentDto | null; + + @ApiPropertyOptional({ type: PublicSiteFormGratitudeDto, nullable: true, description: 'Site form gratitude' }) + @IsOptional() + gratitude?: PublicSiteFormGratitudeDto | null; + + @ApiPropertyOptional({ type: [PublicSiteFormPageDto], nullable: true, description: 'Site form pages' }) + @IsOptional() + @IsArray() + pages?: PublicSiteFormPageDto[] | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-analytic-data.dto.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-analytic-data.dto.ts new file mode 100644 index 0000000..929c469 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-analytic-data.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class SiteFormAnalyticDataDto { + @ApiProperty({ description: 'Code' }) + @IsString() + code: string; + + @ApiProperty({ nullable: true, description: 'Value' }) + value: unknown | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-data-plain.dto.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-data-plain.dto.ts new file mode 100644 index 0000000..103f8b2 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-data-plain.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class SiteFormDataPlainDto { + @ApiPropertyOptional({ description: 'Test' }) + @IsOptional() + @IsString() + test?: string; + + [key: string]: string; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-data.dto.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-data.dto.ts new file mode 100644 index 0000000..65deaea --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-data.dto.ts @@ -0,0 +1,25 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { SiteFormFieldDataDto } from './site-form-field-data.dto'; +import { SiteFormAnalyticDataDto } from './site-form-analytic-data.dto'; + +export class SiteFormDataDto { + @ApiPropertyOptional({ description: 'Test' }) + @IsOptional() + @IsString() + test?: string; + + @ApiPropertyOptional({ type: [SiteFormFieldDataDto], nullable: true, description: 'Fields' }) + @IsOptional() + @IsArray() + @Type(() => SiteFormFieldDataDto) + fields?: SiteFormFieldDataDto[] | null; + + @ApiPropertyOptional({ type: [SiteFormAnalyticDataDto], nullable: true, description: 'Analytics' }) + @IsOptional() + @IsArray() + @Type(() => SiteFormAnalyticDataDto) + analytics?: SiteFormAnalyticDataDto[] | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-field-data.dto.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-field-data.dto.ts new file mode 100644 index 0000000..acd9d14 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-field-data.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class SiteFormFieldDataDto { + @ApiProperty({ description: 'Field ID' }) + @IsNumber() + id: number; + + @ApiPropertyOptional({ description: 'Field value', nullable: true }) + value?: unknown | null; + + @ApiPropertyOptional({ description: 'Field min value', nullable: true }) + @IsOptional() + min?: unknown | null; + + @ApiPropertyOptional({ description: 'Field max value', nullable: true }) + @IsOptional() + max?: unknown | null; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-request.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-request.ts new file mode 100644 index 0000000..139fa5c --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-request.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class SiteFormFilesUploadRequest { + @ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } }) + files: unknown[]; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-result.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-result.ts new file mode 100644 index 0000000..886b2c6 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-file-upload-result.ts @@ -0,0 +1,29 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsString, IsNumber, IsOptional, IsDateString } from 'class-validator'; + +export class SiteFormFileUploadResult { + @ApiPropertyOptional({ description: 'Upload key', nullable: true }) + @IsOptional() + @IsString() + key?: string | null; + + @ApiProperty({ description: 'File ID' }) + @IsString() + id: string; + + @ApiProperty({ description: 'File name' }) + @IsString() + fileName: string; + + @ApiProperty({ description: 'File size' }) + @IsNumber() + fileSize: number; + + @ApiProperty({ description: 'Mime type' }) + @IsString() + mimeType: string; + + @ApiProperty({ description: 'Created at' }) + @IsDateString() + createdAt: string; +} diff --git a/backend/src/modules/forms/site-form-builder/dto/site-form-result.dto.ts b/backend/src/modules/forms/site-form-builder/dto/site-form-result.dto.ts new file mode 100644 index 0000000..a44c303 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/dto/site-form-result.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +export class SiteFormResultDto { + @ApiProperty({ description: 'Result' }) + @IsBoolean() + result: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Message' }) + @IsOptional() + @IsString() + message?: string | null; +} diff --git a/backend/src/modules/forms/site-form-builder/index.ts b/backend/src/modules/forms/site-form-builder/index.ts new file mode 100644 index 0000000..36bc8b9 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/index.ts @@ -0,0 +1,3 @@ +export * from './dto'; +export * from './site-form-builder.controller'; +export * from './site-form-builder.service'; diff --git a/backend/src/modules/forms/site-form-builder/site-form-builder.controller.ts b/backend/src/modules/forms/site-form-builder/site-form-builder.controller.ts new file mode 100644 index 0000000..86b5e20 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/site-form-builder.controller.ts @@ -0,0 +1,109 @@ +import { + Body, + Controller, + Get, + MaxFileSizeValidator, + Param, + ParseFilePipe, + ParseIntPipe, + Post, + Query, + UploadedFiles, + UseInterceptors, +} from '@nestjs/common'; +import { + ApiBody, + ApiConsumes, + ApiCreatedResponse, + ApiOkResponse, + ApiOperation, + ApiParam, + ApiQuery, + ApiTags, +} from '@nestjs/swagger'; +import { AnyFilesInterceptor } from '@nestjs/platform-express'; +import { memoryStorage } from 'multer'; + +import { TransformToDto } from '@/common'; + +import { + PublicSiteFormDto, + PublicSiteFormFieldDto, + SiteFormDataDto, + SiteFormDataPlainDto, + SiteFormFilesUploadRequest, + SiteFormFileUploadResult, + SiteFormResultDto, +} from './dto'; +import { SiteFormBuilderService } from './site-form-builder.service'; + +const MaxSize = 52428800; + +@ApiTags('site-forms/builder') +@Controller('builder') +@TransformToDto() +export class SiteFormBuilderController { + constructor(private readonly service: SiteFormBuilderService) {} + + @ApiOperation({ summary: 'Get site form data', description: 'Get site form data' }) + @ApiParam({ name: 'code', type: String, required: true, description: 'Site form code' }) + @ApiQuery({ name: 'timezone', type: String, required: false, description: 'Timezone' }) + @ApiBody({ type: SiteFormDataDto, required: true, description: 'Site form data' }) + @ApiOkResponse({ description: 'Site form data', type: [PublicSiteFormDto] }) + @Get(':code') + async find(@Param('code') code: string, @Query('timezone') timezone?: string, @Body() dto?: SiteFormDataDto) { + return this.service.find({ code, dto, timezone }); + } + + @ApiOperation({ summary: 'Get site form field', description: 'Get site form field' }) + @ApiParam({ name: 'code', type: String, required: true, description: 'Site form code' }) + @ApiParam({ name: 'fieldId', type: Number, required: true, description: 'Site form field ID' }) + @ApiQuery({ name: 'timezone', type: String, required: false, description: 'Timezone' }) + @ApiBody({ type: SiteFormDataDto, required: true, description: 'Site form data' }) + @ApiOkResponse({ description: 'Site form field', type: [PublicSiteFormFieldDto] }) + @Post(':code/fields/:fieldId') + async getField( + @Param('code') code: string, + @Param('fieldId', ParseIntPipe) fieldId: number, + @Query('timezone') timezone?: string, + @Body() dto?: SiteFormDataDto, + ) { + return this.service.getField({ code, fieldId, dto, timezone }); + } + + @ApiOperation({ summary: 'Post site form data', description: 'Post site form data test in plain format' }) + @ApiParam({ name: 'code', type: String, required: true, description: 'Site form code' }) + @ApiBody({ type: SiteFormDataPlainDto, required: true, description: 'Site form data' }) + @ApiCreatedResponse({ description: 'Result', type: SiteFormResultDto }) + @Post('plain/:code') + async postPlain(@Param('code') code: string, @Body() dto: SiteFormDataPlainDto) { + return this.service.postPlain({ code, dto }); + } + + @ApiOperation({ summary: 'Post site form data', description: 'Post site form data' }) + @ApiParam({ name: 'code', type: String, required: true, description: 'Site form code' }) + @ApiBody({ type: SiteFormDataDto, required: true, description: 'Site form data' }) + @ApiCreatedResponse({ description: 'Result', type: SiteFormResultDto }) + @Post(':code') + async post(@Param('code') code: string, @Body() dto: SiteFormDataDto) { + return this.service.post({ code, dto }); + } + + @ApiOperation({ summary: 'Upload files', description: 'Upload files' }) + @ApiConsumes('multipart/form-data') + @ApiBody({ description: 'Files to upload', type: SiteFormFilesUploadRequest }) + @ApiOkResponse({ description: 'Uploaded files info', type: [SiteFormFileUploadResult] }) + @Post(':code/upload') + @UseInterceptors(AnyFilesInterceptor({ storage: memoryStorage() })) + async upload( + @Param('code') code: string, + @UploadedFiles( + new ParseFilePipe({ + validators: [new MaxFileSizeValidator({ maxSize: MaxSize })], + }), + ) + files: Express.Multer.File[], + ): Promise { + return this.service.uploadFiles({ code, files }); + } +} diff --git a/backend/src/modules/forms/site-form-builder/site-form-builder.service.ts b/backend/src/modules/forms/site-form-builder/site-form-builder.service.ts new file mode 100644 index 0000000..f0c4167 --- /dev/null +++ b/backend/src/modules/forms/site-form-builder/site-form-builder.service.ts @@ -0,0 +1,577 @@ +import { Injectable } from '@nestjs/common'; + +import { DateUtil } from '@/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities'; +import { DepartmentService } from '@/modules/iam/department/department.service'; +import { FieldService } from '@/modules/entity/entity-field/field'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { Entity } from '@/CRM/Model/Entity/Entity'; +import { CreateSimpleEntityDto } from '@/CRM/Service/Entity/Dto/CreateSimpleEntityDto'; +import { SimpleFieldValueDto } from '@/modules/entity/entity-field/field-value/dto/simple-field-value.dto'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types'; +import { ScheduleAppointmentStatus } from '@/modules/scheduler/common'; +import { ScheduleService } from '@/modules/scheduler/schedule/services/schedule.service'; +import { ScheduleAppointmentService } from '@/modules/scheduler/schedule-appointment/schedule-appointment.service'; + +import { SiteForm, SiteFormEntityType, SiteFormService } from '../site-form'; +import { SiteFormField, SiteFormFieldType } from '../site-form-field'; + +import { + PublicSiteFormDto, + PublicSiteFormFieldDto, + PublicSiteFormFieldEntityFieldDto, + PublicSiteFormFieldScheduleDateDto, + PublicSiteFormFieldScheduleDto, + PublicSiteFormFieldScheduleTimeDto, + PublicSiteFormPageDto, + SiteFormAnalyticDataDto, + SiteFormDataDto, + SiteFormDataPlainDto, + SiteFormFieldDataDto, + SiteFormFileUploadResult, + SiteFormResultDto, +} from './dto'; + +@Injectable() +export class SiteFormBuilderService { + constructor( + private readonly userService: UserService, + private readonly departmentService: DepartmentService, + private readonly formService: SiteFormService, + private readonly fieldService: FieldService, + private readonly entityService: EntityService, + private readonly storageService: StorageService, + private readonly scheduleService: ScheduleService, + private readonly appointmentService: ScheduleAppointmentService, + ) {} + + async find({ + code, + dto, + timezone, + }: { + code: string; + dto: SiteFormDataDto; + timezone?: string; + }): Promise { + const form = await this.formService.findByCode(code, { + expand: ['consent', 'gratitude', 'pages.fields', 'scheduleLinks'], + }); + + return form + ? { + code: form.code, + title: form.title, + design: form.design, + fieldLabelEnabled: form.fieldLabelEnabled, + fieldPlaceholderEnabled: form.fieldPlaceholderEnabled, + consent: form.consent + ? { + isEnabled: form.consent.isEnabled, + text: form.consent.text, + linkUrl: form.consent.linkUrl, + linkText: form.consent.linkText, + defaultValue: form.consent.defaultValue, + } + : form.consent, + gratitude: form.gratitude + ? { isEnabled: form.gratitude.isEnabled, header: form.gratitude.header, text: form.gratitude.text } + : form.gratitude, + pages: form.pages ? await this.getPublicPages({ form, dto, timezone }) : undefined, + } + : null; + } + + async getField({ + code, + fieldId, + dto, + timezone, + }: { + code: string; + fieldId: number; + dto?: SiteFormDataDto; + timezone?: string; + }): Promise { + const form = await this.formService.findByCode(code, { + expand: ['pages.fields', 'scheduleLinks'], + }); + + if (form) { + const formField = form.pages.flatMap((p) => p.fields).find((f) => f.id === fieldId); + if (formField) { + return this.getPublicField({ form, formField, dto, timezone }); + } + } + + return null; + } + + async post({ code, dto }: { code: string; dto?: SiteFormDataDto }): Promise { + if (!dto || dto.test) { + return { result: true, message: `Form ${code} test is OK` }; + } + + const form = await this.formService.findByCode(code, { + expand: ['pages.fields', 'entityTypeLinks', 'scheduleLinks'], + }); + + if (!form) { + return { result: false, message: `Form ${code} not found` }; + } + + if (!this.checkRequiredFields({ form, fields: dto.fields })) { + return { result: false, message: `Required field(s) not set` }; + } + + const user = await this.userService.findOne({ accountId: form.accountId, id: form.createdBy }); + if (!user) { + return { result: false, message: `User ${form.createdBy} not found` }; + } + + const entity = await this.createEntities({ form, user, dto }); + + if (form.scheduleLinks?.length) { + await this.createAppointment({ form, user, entity, dto }); + } + + return { result: true, message: `Form ${code} submitted successfully` }; + } + + async postPlain({ code, dto }: { code: string; dto?: SiteFormDataPlainDto }): Promise { + return this.post({ code, dto: this.convertPlainToDto(dto) }); + } + + async uploadFiles({ + code, + files, + }: { + code: string; + files: Express.Multer.File[]; + }): Promise { + const form = await this.formService.findByCode(code); + if (form) { + const fileInfos = await Promise.all( + files + .map((file) => ({ key: file.fieldname, file: StorageFile.fromMulter(file) })) + .map(async (file) => ({ + key: file.key, + info: await this.storageService.storeCommonFile({ accountId: form.accountId, file: file.file }), + })), + ); + return fileInfos.map((file) => ({ + key: file.key, + id: file.info.id, + fileName: file.info.originalName, + fileSize: file.info.size, + mimeType: file.info.mimeType, + createdAt: file.info.createdAt.toISOString(), + })); + } + return []; + } + + private convertPlainToDto(plain: SiteFormDataPlainDto | null | undefined): SiteFormDataDto | undefined { + if (!plain) { + return undefined; + } + + const fields = Object.entries(plain) + .map(([key, value]) => { + const id = Number(key); + return !isNaN(id) ? { id, value } : null; + }) + .filter(Boolean); + + return { test: plain.test, fields }; + } + + private checkRequiredFields({ form, fields }: { form: SiteForm; fields?: SiteFormFieldDataDto[] }): boolean { + const requiredFormFields = form.pages.flatMap((p) => p.fields).filter((f) => f.isRequired); + for (const requiredFormField of requiredFormFields) { + const fieldDto = fields?.find((fd) => fd.id === requiredFormField.id); + if (!fieldDto || !fieldDto.value) { + return false; + } + } + return true; + } + + private async createEntities({ + form, + user, + dto, + }: { + form: SiteForm; + user: User; + dto: SiteFormDataDto; + }): Promise { + const mainEntityType = form.entityTypeLinks.find((etl) => etl.isMain); + const mainEntity = mainEntityType + ? await this.createEntity({ form, user, entityType: mainEntityType, data: dto }) + : undefined; + + if (mainEntity) { + const linkedEntityTypes = form.entityTypeLinks.filter((etl) => !etl.isMain); + await Promise.all( + linkedEntityTypes.map((et) => + this.createEntity({ form, user, entityType: et, data: dto, options: { linkedEntities: [mainEntity.id] } }), + ), + ); + } + + return mainEntity; + } + + private async createEntity({ + form, + user, + entityType, + data, + options, + }: { + form: SiteForm; + user: User; + entityType: SiteFormEntityType; + data: SiteFormDataDto; + options?: { linkedEntities?: number[] }; + }) { + const dto = this.getEntityDto({ form, entityType, fields: data.fields, analytics: data.analytics }); + + const [entity] = await this.entityService.createSimple({ + accountId: form.accountId, + user, + dto, + options: { linkedEntities: options?.linkedEntities, checkDuplicate: form.checkDuplicate }, + }); + + return entity; + } + + private getEntityDto({ + form, + entityType, + fields, + analytics, + }: { + form: SiteForm; + entityType: SiteFormEntityType; + fields?: SiteFormFieldDataDto[] | null; + analytics?: SiteFormAnalyticDataDto[] | null; + }): CreateSimpleEntityDto { + const fieldValues: SimpleFieldValueDto[] = []; + let name: string | undefined = entityType.isMain ? form.name : undefined; + if (fields) { + const formFields = form.pages.flatMap((p) => p.fields).filter((f) => f.entityTypeId === entityType.entityTypeId); + for (const field of fields) { + const formField = formFields.find((ff) => ff.id === field.id); + if (formField?.type === SiteFormFieldType.EntityName) { + name = field.value as string; + } else if (formField?.type === SiteFormFieldType.EntityField) { + if (formField?.fieldId) { + fieldValues.push({ fieldId: formField?.fieldId, value: field.value }); + } + } + } + } + if (analytics) { + for (const analytic of analytics) { + fieldValues.push({ fieldCode: analytic.code, value: analytic.value }); + } + } + return { + entityTypeId: entityType.entityTypeId, + boardId: entityType.boardId, + ownerId: form.responsibleId, + name, + fieldValues, + }; + } + + private async createAppointment({ + form, + user, + entity, + dto, + }: { + form: SiteForm; + user: User; + entity: Entity | undefined; + dto: SiteFormDataDto; + }) { + const scheduleId = this.getFieldValue({ form, fields: dto.fields, type: SiteFormFieldType.Schedule }); + const performerId = this.getFieldValue({ + form, + fields: dto.fields, + type: SiteFormFieldType.SchedulePerformer, + }); + const startTime = this.getFieldValue({ form, fields: dto.fields, type: SiteFormFieldType.ScheduleTime }); + if (scheduleId && performerId && startTime) { + const startDate = DateUtil.fromISOString(startTime); + await this.appointmentService.create({ + accountId: form.accountId, + user, + dto: { + scheduleId, + performerId, + title: entity?.name ?? form.name, + startDate: startDate.toISOString(), + status: ScheduleAppointmentStatus.NotConfirmed, + entityId: entity?.id, + checkIntersection: true, + ownerId: entity?.responsibleUserId ?? form.responsibleId, + }, + skipPermissionCheck: true, + }); + } + } + + private async getPublicPages({ + form, + dto, + timezone, + }: { + form: SiteForm; + dto?: SiteFormDataDto; + timezone?: string; + }): Promise { + const publicPages: PublicSiteFormPageDto[] = []; + + for (const formPage of form.pages) { + publicPages.push({ + id: formPage.id, + title: formPage.title, + sortOrder: formPage.sortOrder, + fields: formPage.fields + ? await this.getPublicFields({ form, formFields: formPage.fields, dto, timezone }) + : undefined, + }); + } + + return publicPages; + } + + private async getPublicFields({ + form, + formFields, + dto, + timezone, + }: { + form: SiteForm; + formFields: SiteFormField[]; + dto?: SiteFormDataDto; + timezone?: string; + }): Promise { + return Promise.all(formFields.map((formField) => this.getPublicField({ form, formField, dto, timezone }))); + } + + private async getPublicField({ + form, + formField, + dto, + timezone, + }: { + form: SiteForm; + formField: SiteFormField; + dto?: SiteFormDataDto; + timezone?: string; + }): Promise { + return { + id: formField.id, + label: formField.label, + placeholder: formField.placeholder, + type: formField.type, + isRequired: formField.isRequired, + sortOrder: formField.sortOrder, + settings: await this.getPublicFieldSettings({ form, formField, dto, timezone }), + }; + } + + private async getPublicFieldSettings({ + form, + formField, + dto, + timezone, + }: { + form: SiteForm; + formField: SiteFormField; + dto?: SiteFormDataDto; + timezone?: string; + }) { + switch (formField.type) { + case SiteFormFieldType.EntityField: + return this.getEntityFieldSettings(formField); + case SiteFormFieldType.Schedule: + return this.getScheduleSettings(form); + case SiteFormFieldType.SchedulePerformer: + return this.getSchedulePerformerSettings({ form, fields: dto.fields }); + case SiteFormFieldType.ScheduleDate: + return this.getScheduleDateSettings({ form, fields: dto.fields, timezone }); + case SiteFormFieldType.ScheduleTime: + return this.getScheduleTimeSettings({ form, fields: dto.fields }); + default: + return null; + } + } + + private async getEntityFieldSettings(formField: SiteFormField): Promise { + const field = await this.fieldService.findOne( + { + accountId: formField.accountId, + entityTypeId: formField.entityTypeId, + id: formField.fieldId, + }, + { expand: ['options'] }, + ); + + const options = field?.options?.map((o) => ({ + id: o.id, + label: o.label, + color: o.color, + sortOrder: o.sortOrder, + })); + + return field + ? { fieldType: field.type, isValidationRequired: formField.isValidationRequired, meta: formField.meta, options } + : null; + } + + private async getScheduleSettings(form: SiteForm): Promise { + const options = ( + (await Promise.all( + form.scheduleLinks?.map(async (sl) => { + const schedule = await this.scheduleService.findOne({ + filter: { accountId: form.accountId, scheduleId: sl.scheduleId }, + }); + return schedule ? { id: schedule.id, label: schedule.name } : undefined; + }), + )) ?? [] + ).filter(Boolean); + + return { options }; + } + + private getFieldData({ + form, + fields, + type, + }: { + form: SiteForm; + fields: SiteFormFieldDataDto[] | undefined | null; + type: SiteFormFieldType; + }): SiteFormFieldDataDto | undefined { + const field = form.pages.flatMap((p) => p.fields).find((f) => f.type === type); + + return field ? fields?.find((f) => f.id === field.id) : undefined; + } + private getFieldValue(params: { + form: SiteForm; + fields: SiteFormFieldDataDto[] | undefined | null; + type: SiteFormFieldType; + }): T | undefined { + const fieldData = this.getFieldData(params); + + return fieldData ? (fieldData.value as T) : undefined; + } + + private async getSchedulePerformerSettings({ + form, + fields, + }: { + form: SiteForm; + fields: SiteFormFieldDataDto[] | undefined | null; + }): Promise { + const scheduleId = this.getFieldValue({ form, fields, type: SiteFormFieldType.Schedule }); + if (scheduleId) { + const schedule = await this.scheduleService.findOne({ filter: { accountId: form.accountId, scheduleId } }); + if (schedule?.performers) { + const options = ( + (await Promise.all( + schedule.performers.map(async (performer) => { + if (performer.userId) { + const user = await this.userService.findOne({ accountId: form.accountId, id: performer.userId }); + return user ? { id: performer.id, label: user.fullName } : undefined; + } else if (performer.departmentId) { + const department = await this.departmentService.findOne({ + accountId: form.accountId, + departmentId: performer.departmentId, + }); + return department ? { id: performer.id, label: department.name } : undefined; + } else { + return undefined; + } + }), + )) ?? [] + ).filter(Boolean); + return { options }; + } + } + + return { options: [] }; + } + + private async getScheduleDateSettings({ + form, + fields, + timezone, + }: { + form: SiteForm; + fields: SiteFormFieldDataDto[] | undefined | null; + timezone?: string; + }): Promise { + const scheduleId = this.getFieldValue({ form, fields, type: SiteFormFieldType.Schedule }); + const performerId = this.getFieldValue({ form, fields, type: SiteFormFieldType.SchedulePerformer }); + const scheduleData = this.getFieldData({ form, fields, type: SiteFormFieldType.ScheduleDate }); + + if (scheduleId && performerId && scheduleData?.min && scheduleData?.max) { + const [minDate, maxDate] = [scheduleData.min, scheduleData.max].map((date) => + DateUtil.fromISOString(date as string), + ); + + const dates = await this.appointmentService.getAvailableDates({ + accountId: form.accountId, + scheduleId, + performerId, + minDate, + maxDate, + daysLimit: form.scheduleLimitDays, + timezone: timezone ?? 'UTC', + }); + + return { dates }; + } + + return { dates: [] }; + } + + private async getScheduleTimeSettings({ + form, + fields, + }: { + form: SiteForm; + fields: SiteFormFieldDataDto[] | undefined | null; + }): Promise { + const scheduleId = this.getFieldValue({ form, fields, type: SiteFormFieldType.Schedule }); + const performerId = this.getFieldValue({ form, fields, type: SiteFormFieldType.SchedulePerformer }); + const scheduleDataStr = this.getFieldValue({ form, fields, type: SiteFormFieldType.ScheduleDate }); + if (scheduleId && performerId && scheduleDataStr) { + const minDate = DateUtil.fromISOString(scheduleDataStr); + const maxDate = DateUtil.sub(DateUtil.add(minDate, { days: 1 }), { seconds: 1 }); + const spots = await this.appointmentService.getAvailableSpots({ + accountId: form.accountId, + scheduleId, + performerId, + minDate, + maxDate, + daysLimit: form.scheduleLimitDays, + }); + + return { spots: spots.map((spot) => ({ from: spot.from.toISOString(), to: spot.to.toISOString() })) }; + } + + return { spots: [] }; + } +} diff --git a/backend/src/modules/forms/site-form-consent/dto/create-site-form-consent.dto.ts b/backend/src/modules/forms/site-form-consent/dto/create-site-form-consent.dto.ts new file mode 100644 index 0000000..f521375 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/dto/create-site-form-consent.dto.ts @@ -0,0 +1,11 @@ +import { PickType } from '@nestjs/swagger'; + +import { SiteFormConsentDto } from './site-form-consent.dto'; + +export class CreateSiteFormConsentDto extends PickType(SiteFormConsentDto, [ + 'isEnabled', + 'text', + 'linkUrl', + 'linkText', + 'defaultValue', +] as const) {} diff --git a/backend/src/modules/forms/site-form-consent/dto/index.ts b/backend/src/modules/forms/site-form-consent/dto/index.ts new file mode 100644 index 0000000..f011713 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-site-form-consent.dto'; +export * from './site-form-consent.dto'; +export * from './update-site-form-consent.dto'; diff --git a/backend/src/modules/forms/site-form-consent/dto/site-form-consent.dto.ts b/backend/src/modules/forms/site-form-consent/dto/site-form-consent.dto.ts new file mode 100644 index 0000000..7216e82 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/dto/site-form-consent.dto.ts @@ -0,0 +1,32 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class SiteFormConsentDto { + @ApiProperty() + @IsNumber() + formId: number; + + @ApiProperty() + @IsBoolean() + isEnabled: boolean; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + text?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + linkUrl?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + linkText?: string | null; + + @ApiPropertyOptional() + @IsOptional() + @IsBoolean() + defaultValue?: boolean; +} diff --git a/backend/src/modules/forms/site-form-consent/dto/update-site-form-consent.dto.ts b/backend/src/modules/forms/site-form-consent/dto/update-site-form-consent.dto.ts new file mode 100644 index 0000000..a911072 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/dto/update-site-form-consent.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { SiteFormConsentDto } from './site-form-consent.dto'; + +export class UpdateSiteFormConsentDto extends PartialType( + PickType(SiteFormConsentDto, ['isEnabled', 'text', 'linkUrl', 'linkText', 'defaultValue'] as const), +) {} diff --git a/backend/src/modules/forms/site-form-consent/entities/index.ts b/backend/src/modules/forms/site-form-consent/entities/index.ts new file mode 100644 index 0000000..57825e7 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/entities/index.ts @@ -0,0 +1 @@ +export * from './site-form-consent.entity'; diff --git a/backend/src/modules/forms/site-form-consent/entities/site-form-consent.entity.ts b/backend/src/modules/forms/site-form-consent/entities/site-form-consent.entity.ts new file mode 100644 index 0000000..212fe21 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/entities/site-form-consent.entity.ts @@ -0,0 +1,70 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { type CreateSiteFormConsentDto, SiteFormConsentDto, type UpdateSiteFormConsentDto } from '../dto'; + +@Entity() +export class SiteFormConsent { + @Column() + accountId: number; + + @PrimaryColumn() + formId: number; + + @Column({ default: false }) + isEnabled: boolean; + + @Column({ nullable: true }) + text: string | null; + + @Column({ nullable: true }) + linkUrl: string | null; + + @Column({ nullable: true }) + linkText: string | null; + + @Column({ default: false }) + defaultValue: boolean; + + constructor( + accountId: number, + formId: number, + isEnabled: boolean, + text: string | null, + linkUrl: string | null, + linkText: string | null, + defaultValue: boolean, + ) { + this.accountId = accountId; + this.formId = formId; + this.isEnabled = isEnabled; + this.text = text; + this.linkUrl = linkUrl; + this.linkText = linkText; + this.defaultValue = defaultValue; + } + + public static fromDto(accountId: number, formId: number, dto: CreateSiteFormConsentDto): SiteFormConsent { + return new SiteFormConsent(accountId, formId, dto.isEnabled, dto.text, dto.linkUrl, dto.linkText, dto.defaultValue); + } + + public update(dto: UpdateSiteFormConsentDto): SiteFormConsent { + this.isEnabled = dto.isEnabled !== undefined ? dto.isEnabled : this.isEnabled; + this.text = dto.text !== undefined ? dto.text : this.text; + this.linkUrl = dto.linkUrl !== undefined ? dto.linkUrl : this.linkUrl; + this.linkText = dto.linkText !== undefined ? dto.linkText : this.linkText; + this.defaultValue = dto.defaultValue !== undefined ? dto.defaultValue : this.defaultValue; + + return this; + } + + public toDto(): SiteFormConsentDto { + return { + formId: this.formId, + isEnabled: this.isEnabled, + text: this.text, + linkUrl: this.linkUrl, + linkText: this.linkText, + defaultValue: this.defaultValue, + }; + } +} diff --git a/backend/src/modules/forms/site-form-consent/index.ts b/backend/src/modules/forms/site-form-consent/index.ts new file mode 100644 index 0000000..e63a2ad --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './site-form-consent.controller'; +export * from './site-form-consent.service'; diff --git a/backend/src/modules/forms/site-form-consent/site-form-consent.controller.ts b/backend/src/modules/forms/site-form-consent/site-form-consent.controller.ts new file mode 100644 index 0000000..3e1110e --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/site-form-consent.controller.ts @@ -0,0 +1,50 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import type { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { SiteFormConsentService } from './site-form-consent.service'; +import { SiteFormConsentDto, type CreateSiteFormConsentDto, type UpdateSiteFormConsentDto } from './dto'; + +@ApiTags('site-forms/consent') +@Controller(':formId/consent') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class SiteFormConsentController { + constructor(private readonly service: SiteFormConsentService) {} + + @ApiCreatedResponse({ description: 'Create site form consent', type: SiteFormConsentDto }) + @Post() + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Body() dto: CreateSiteFormConsentDto, + ) { + return this.service.create(accountId, formId, dto); + } + + @ApiOkResponse({ description: 'Get site form consent', type: [SiteFormConsentDto] }) + @Get() + public async findOne(@CurrentAuth() { accountId }: AuthData, @Param('formId', ParseIntPipe) formId: number) { + return this.service.findOne(accountId, { formId }); + } + + @ApiCreatedResponse({ description: 'Update site form consent', type: SiteFormConsentDto }) + @Patch() + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Body() dto: UpdateSiteFormConsentDto, + ) { + return this.service.update(accountId, formId, dto); + } + + @ApiOkResponse({ description: 'Delete site form consent' }) + @Delete() + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('formId', ParseIntPipe) formId: number) { + return this.service.delete(accountId, formId); + } +} diff --git a/backend/src/modules/forms/site-form-consent/site-form-consent.service.ts b/backend/src/modules/forms/site-form-consent/site-form-consent.service.ts new file mode 100644 index 0000000..c4d9b47 --- /dev/null +++ b/backend/src/modules/forms/site-form-consent/site-form-consent.service.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { SiteFormConsent } from './entities'; +import type { CreateSiteFormConsentDto, UpdateSiteFormConsentDto } from './dto'; + +interface FindFilter { + formId?: number; +} + +@Injectable() +export class SiteFormConsentService { + constructor( + @InjectRepository(SiteFormConsent) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, formId: number, dto: CreateSiteFormConsentDto): Promise { + return this.repository.save(SiteFormConsent.fromDto(accountId, formId, dto)); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async update(accountId: number, formId: number, dto: UpdateSiteFormConsentDto): Promise { + const consent = await this.findOne(accountId, { formId }); + if (!consent) { + throw NotFoundError.withId(SiteFormConsent, formId); + } + + await this.repository.save(consent.update(dto)); + + return consent; + } + + public async process( + accountId: number, + formId: number, + dto: CreateSiteFormConsentDto | UpdateSiteFormConsentDto, + ): Promise { + const consent = await this.findOne(accountId, { formId }); + if (!consent) { + return this.create(accountId, formId, dto as CreateSiteFormConsentDto); + } else { + await this.repository.save(consent.update(dto as UpdateSiteFormConsentDto)); + + return consent; + } + } + + public async delete(accountId: number, formId: number) { + await this.createFindQb(accountId, { formId }).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.formId) { + qb.andWhere('form_id = :formId', { formId: filter.formId }); + } + + return qb; + } +} diff --git a/backend/src/modules/forms/site-form-field/dto/create-site-form-field.dto.ts b/backend/src/modules/forms/site-form-field/dto/create-site-form-field.dto.ts new file mode 100644 index 0000000..eb2b932 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/dto/create-site-form-field.dto.ts @@ -0,0 +1,12 @@ +import { PickType } from '@nestjs/swagger'; + +import { SiteFormFieldDto } from './site-form-field.dto'; + +export class CreateSiteFormFieldDto extends PickType(SiteFormFieldDto, [ + 'label', + 'placeholder', + 'type', + 'isRequired', + 'sortOrder', + 'settings', +] as const) {} diff --git a/backend/src/modules/forms/site-form-field/dto/index.ts b/backend/src/modules/forms/site-form-field/dto/index.ts new file mode 100644 index 0000000..24aa2c4 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-site-form-field.dto'; +export * from './site-form-field-entity-field.dto'; +export * from './site-form-field-entity-name.dto'; +export * from './site-form-field.dto'; +export * from './update-site-form-field.dto'; diff --git a/backend/src/modules/forms/site-form-field/dto/site-form-field-entity-field.dto.ts b/backend/src/modules/forms/site-form-field/dto/site-form-field-entity-field.dto.ts new file mode 100644 index 0000000..953ecba --- /dev/null +++ b/backend/src/modules/forms/site-form-field/dto/site-form-field-entity-field.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsObject, IsOptional } from 'class-validator'; + +export class SiteFormFieldEntityFieldDto { + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty() + @IsNumber() + fieldId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsBoolean() + isValidationRequired: boolean | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsObject() + meta?: object | null; +} diff --git a/backend/src/modules/forms/site-form-field/dto/site-form-field-entity-name.dto.ts b/backend/src/modules/forms/site-form-field/dto/site-form-field-entity-name.dto.ts new file mode 100644 index 0000000..f6cb26c --- /dev/null +++ b/backend/src/modules/forms/site-form-field/dto/site-form-field-entity-name.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class SiteFormFieldEntityNameDto { + @ApiProperty() + @IsNumber() + entityTypeId: number; +} diff --git a/backend/src/modules/forms/site-form-field/dto/site-form-field.dto.ts b/backend/src/modules/forms/site-form-field/dto/site-form-field.dto.ts new file mode 100644 index 0000000..2c468fc --- /dev/null +++ b/backend/src/modules/forms/site-form-field/dto/site-form-field.dto.ts @@ -0,0 +1,50 @@ +import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { SiteFormFieldType } from '../enums'; +import { SiteFormFieldEntityFieldDto } from './site-form-field-entity-field.dto'; +import { SiteFormFieldEntityNameDto } from './site-form-field-entity-name.dto'; + +@ApiExtraModels(SiteFormFieldEntityFieldDto) +@ApiExtraModels(SiteFormFieldEntityNameDto) +export class SiteFormFieldDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + label: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + placeholder: string | null; + + @ApiProperty({ enum: SiteFormFieldType }) + @IsEnum(SiteFormFieldType) + type: SiteFormFieldType; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsBoolean() + isRequired?: boolean | null; + + @ApiProperty() + @IsNumber() + sortOrder: number; + + @ApiPropertyOptional({ + nullable: true, + type: 'array', + items: { + oneOf: [ + { $ref: getSchemaPath(SiteFormFieldEntityFieldDto) }, + { $ref: getSchemaPath(SiteFormFieldEntityNameDto) }, + ], + }, + }) + @IsOptional() + settings?: SiteFormFieldEntityFieldDto | SiteFormFieldEntityNameDto | null; +} diff --git a/backend/src/modules/forms/site-form-field/dto/update-site-form-field.dto.ts b/backend/src/modules/forms/site-form-field/dto/update-site-form-field.dto.ts new file mode 100644 index 0000000..f0e648f --- /dev/null +++ b/backend/src/modules/forms/site-form-field/dto/update-site-form-field.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, PartialType, PickType } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +import { SiteFormFieldDto } from './site-form-field.dto'; + +export class UpdateSiteFormFieldDto extends PartialType( + PickType(SiteFormFieldDto, ['label', 'placeholder', 'isRequired', 'sortOrder', 'settings'] as const), +) { + @ApiProperty() + @IsNumber() + id: number; +} diff --git a/backend/src/modules/forms/site-form-field/entities/index.ts b/backend/src/modules/forms/site-form-field/entities/index.ts new file mode 100644 index 0000000..6f90b01 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/entities/index.ts @@ -0,0 +1 @@ +export * from './site-form-field.entity'; diff --git a/backend/src/modules/forms/site-form-field/entities/site-form-field.entity.ts b/backend/src/modules/forms/site-form-field/entities/site-form-field.entity.ts new file mode 100644 index 0000000..a389d4f --- /dev/null +++ b/backend/src/modules/forms/site-form-field/entities/site-form-field.entity.ts @@ -0,0 +1,142 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { + SiteFormFieldDto, + SiteFormFieldEntityFieldDto, + SiteFormFieldEntityNameDto, + type CreateSiteFormFieldDto, + type UpdateSiteFormFieldDto, +} from '../dto'; +import { SiteFormFieldType } from '../enums'; + +@Entity() +export class SiteFormField { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + pageId: number; + + @Column({ nullable: true }) + label: string | null; + + @Column({ nullable: true }) + placeholder: string | null; + + @Column() + sortOrder: number; + + @Column() + type: SiteFormFieldType; + + @Column({ nullable: true }) + isRequired: boolean | null; + + @Column({ nullable: true }) + entityTypeId: number | null; + + @Column({ nullable: true }) + fieldId: number | null; + + @Column({ nullable: true }) + isValidationRequired: boolean | null; + + @Column({ type: 'jsonb', nullable: true }) + meta?: object | null; + + constructor( + accountId: number, + pageId: number, + label: string | null, + placeholder: string | null, + sortOrder: number, + type: SiteFormFieldType, + isRequired: boolean | null, + entityTypeId: number | null, + fieldId: number | null, + isValidationRequired: boolean | null, + meta?: object | null, + ) { + this.accountId = accountId; + this.pageId = pageId; + this.label = label; + this.placeholder = placeholder; + this.sortOrder = sortOrder; + this.type = type; + this.isRequired = isRequired; + this.entityTypeId = entityTypeId; + this.fieldId = fieldId; + this.isValidationRequired = isValidationRequired; + this.meta = meta; + } + + public static fromDto(accountId: number, pageId: number, dto: CreateSiteFormFieldDto): SiteFormField { + const fieldSettings = + dto.type === SiteFormFieldType.EntityField ? (dto.settings as SiteFormFieldEntityFieldDto) : undefined; + return new SiteFormField( + accountId, + pageId, + dto.label, + dto.placeholder, + dto.sortOrder, + dto.type, + dto.isRequired, + dto.settings?.entityTypeId ?? null, + fieldSettings?.fieldId ?? null, + fieldSettings?.isValidationRequired ?? null, + fieldSettings?.meta ?? null, + ); + } + + public update(dto: UpdateSiteFormFieldDto): SiteFormField { + this.label = dto.label !== undefined ? dto.label : this.label; + this.placeholder = dto.placeholder !== undefined ? dto.placeholder : this.placeholder; + this.isRequired = dto.isRequired !== undefined ? dto.isRequired : this.isRequired; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + this.entityTypeId = dto.settings?.entityTypeId !== undefined ? dto.settings.entityTypeId : this.entityTypeId; + + const settings = + this.type === SiteFormFieldType.EntityField ? (dto.settings as SiteFormFieldEntityFieldDto) : undefined; + if (settings) { + this.fieldId = settings.fieldId !== undefined ? settings.fieldId : this.fieldId; + this.isValidationRequired = + settings.isValidationRequired !== undefined ? settings.isValidationRequired : this.isValidationRequired; + this.meta = settings.meta !== undefined ? settings.meta : this.meta; + } + + return this; + } + + public toDto(): SiteFormFieldDto { + return { + id: this.id, + label: this.label, + placeholder: this.placeholder, + type: this.type, + isRequired: this.isRequired, + sortOrder: this.sortOrder, + settings: this.formatSettings(), + }; + } + + private formatSettings(): SiteFormFieldEntityFieldDto | SiteFormFieldEntityNameDto | null { + switch (this.type) { + case SiteFormFieldType.EntityField: + return { + entityTypeId: this.entityTypeId, + fieldId: this.fieldId, + isValidationRequired: this.isValidationRequired, + meta: this.meta, + }; + case SiteFormFieldType.EntityName: + return { + entityTypeId: this.entityTypeId, + }; + default: + return null; + } + } +} diff --git a/backend/src/modules/forms/site-form-field/enums/index.ts b/backend/src/modules/forms/site-form-field/enums/index.ts new file mode 100644 index 0000000..6fe7850 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/enums/index.ts @@ -0,0 +1 @@ +export * from './site-form-field-type.enum'; diff --git a/backend/src/modules/forms/site-form-field/enums/site-form-field-type.enum.ts b/backend/src/modules/forms/site-form-field/enums/site-form-field-type.enum.ts new file mode 100644 index 0000000..161b573 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/enums/site-form-field-type.enum.ts @@ -0,0 +1,9 @@ +export enum SiteFormFieldType { + EntityName = 'entity_name', + EntityField = 'entity_field', + Delimiter = 'delimiter', + Schedule = 'schedule', + SchedulePerformer = 'schedule_performer', + ScheduleDate = 'schedule_date', + ScheduleTime = 'schedule_time', +} diff --git a/backend/src/modules/forms/site-form-field/index.ts b/backend/src/modules/forms/site-form-field/index.ts new file mode 100644 index 0000000..0e94d37 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './site-form-field.controller'; +export * from './site-form-field.service'; diff --git a/backend/src/modules/forms/site-form-field/site-form-field.controller.ts b/backend/src/modules/forms/site-form-field/site-form-field.controller.ts new file mode 100644 index 0000000..d771bf4 --- /dev/null +++ b/backend/src/modules/forms/site-form-field/site-form-field.controller.ts @@ -0,0 +1,65 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import type { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { SiteFormFieldService } from './site-form-field.service'; +import { SiteFormFieldDto, CreateSiteFormFieldDto, UpdateSiteFormFieldDto } from './dto'; + +@ApiTags('site-forms/fields') +@Controller(':formId/pages/:pageId/fields') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class SiteFormFieldController { + constructor(private readonly service: SiteFormFieldService) {} + + @ApiCreatedResponse({ description: 'Create site form field', type: SiteFormFieldDto }) + @Post() + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('pageId', ParseIntPipe) pageId: number, + @Body() dto: CreateSiteFormFieldDto, + ) { + return this.service.create(accountId, pageId, dto); + } + + @ApiOkResponse({ description: 'Get site form fields', type: [SiteFormFieldDto] }) + @Get() + public async findMany(@CurrentAuth() { accountId }: AuthData, @Param('pageId', ParseIntPipe) pageId: number) { + return this.service.findMany(accountId, { pageId }); + } + + @ApiOkResponse({ description: 'Get site form field', type: [SiteFormFieldDto] }) + @Get(':fieldId') + public async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('pageId', ParseIntPipe) pageId: number, + @Param('fieldId', ParseIntPipe) fieldId: number, + ) { + return this.service.findOne(accountId, { pageId, fieldId }); + } + + @ApiCreatedResponse({ description: 'Update site form field', type: SiteFormFieldDto }) + @Patch(':fieldId') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('pageId', ParseIntPipe) pageId: number, + @Param('fieldId', ParseIntPipe) fieldId: number, + @Body() dto: UpdateSiteFormFieldDto, + ) { + return this.service.update(accountId, pageId, fieldId, dto); + } + + @ApiOkResponse({ description: 'Delete site form field' }) + @Delete(':fieldId') + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('pageId', ParseIntPipe) pageId: number, + @Param('fieldId', ParseIntPipe) fieldId: number, + ) { + return this.service.delete(accountId, pageId, fieldId); + } +} diff --git a/backend/src/modules/forms/site-form-field/site-form-field.service.ts b/backend/src/modules/forms/site-form-field/site-form-field.service.ts new file mode 100644 index 0000000..9e9932a --- /dev/null +++ b/backend/src/modules/forms/site-form-field/site-form-field.service.ts @@ -0,0 +1,102 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { SiteFormField } from './entities'; +import { CreateSiteFormFieldDto, UpdateSiteFormFieldDto } from './dto'; + +interface FindFilter { + pageId?: number; + fieldId?: number | number[]; +} + +@Injectable() +export class SiteFormFieldService { + constructor( + @InjectRepository(SiteFormField) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, pageId: number, dto: CreateSiteFormFieldDto): Promise { + return this.repository.save(SiteFormField.fromDto(accountId, pageId, dto)); + } + + public async createMany(accountId: number, pageId: number, dtos: CreateSiteFormFieldDto[]): Promise { + return Promise.all(dtos.map((dto) => this.create(accountId, pageId, dto))); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).orderBy('sort_order').getMany(); + } + + public async update( + accountId: number, + pageId: number, + fieldId: number, + dto: UpdateSiteFormFieldDto, + ): Promise { + let field = await this.findOne(accountId, { pageId, fieldId }); + if (!field) { + throw NotFoundError.withId(SiteFormField, fieldId); + } + + field = await this.repository.save(field.update(dto)); + + return field; + } + + public async updateMany(accountId: number, pageId: number, dtos: UpdateSiteFormFieldDto[]): Promise { + return Promise.all(dtos.map((dto) => this.update(accountId, pageId, dto.id, dto))); + } + + public async processBatch( + accountId: number, + pageId: number, + dtos: (CreateSiteFormFieldDto | UpdateSiteFormFieldDto)[], + ): Promise { + const fields = await this.findMany(accountId, { pageId }); + + const created = dtos.filter((dto) => !dto['id']).map((dto) => dto as CreateSiteFormFieldDto); + const updated = dtos.filter((dto) => dto['id']).map((dto) => dto as UpdateSiteFormFieldDto); + const deleted = fields.filter((f) => !updated.some((dto) => dto.id === f.id)).map((f) => f.id); + + const result: SiteFormField[] = []; + + result.push(...(await this.createMany(accountId, pageId, created))); + result.push(...(await this.updateMany(accountId, pageId, updated))); + + if (deleted.length) { + await this.delete(accountId, pageId, deleted); + } + + return result; + } + + public async delete(accountId: number, pageId: number, fieldId: number | number[]) { + await this.createFindQb(accountId, { pageId, fieldId }).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.fieldId) { + if (Array.isArray(filter.fieldId)) { + qb.andWhere('id IN (:...ids)', { ids: filter.fieldId }); + } else { + qb.andWhere('id = :id', { id: filter.fieldId }); + } + } + + if (filter?.pageId) { + qb.andWhere('page_id = :pageId', { pageId: filter.pageId }); + } + + return qb; + } +} diff --git a/backend/src/modules/forms/site-form-gratitude/dto/create-site-form-gratitude.dto.ts b/backend/src/modules/forms/site-form-gratitude/dto/create-site-form-gratitude.dto.ts new file mode 100644 index 0000000..74074ab --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/dto/create-site-form-gratitude.dto.ts @@ -0,0 +1,9 @@ +import { PickType } from '@nestjs/swagger'; + +import { SiteFormGratitudeDto } from './site-form-gratitude.dto'; + +export class CreateSiteFormGratitudeDto extends PickType(SiteFormGratitudeDto, [ + 'isEnabled', + 'header', + 'text', +] as const) {} diff --git a/backend/src/modules/forms/site-form-gratitude/dto/index.ts b/backend/src/modules/forms/site-form-gratitude/dto/index.ts new file mode 100644 index 0000000..d792e79 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-site-form-gratitude.dto'; +export * from './site-form-gratitude.dto'; +export * from './update-site-form-gratitude.dto'; diff --git a/backend/src/modules/forms/site-form-gratitude/dto/site-form-gratitude.dto.ts b/backend/src/modules/forms/site-form-gratitude/dto/site-form-gratitude.dto.ts new file mode 100644 index 0000000..58c7117 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/dto/site-form-gratitude.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class SiteFormGratitudeDto { + @ApiProperty() + @IsNumber() + formId: number; + + @ApiProperty() + @IsBoolean() + isEnabled: boolean; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + header?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + text?: string | null; +} diff --git a/backend/src/modules/forms/site-form-gratitude/dto/update-site-form-gratitude.dto.ts b/backend/src/modules/forms/site-form-gratitude/dto/update-site-form-gratitude.dto.ts new file mode 100644 index 0000000..b7456f8 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/dto/update-site-form-gratitude.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { SiteFormGratitudeDto } from './site-form-gratitude.dto'; + +export class UpdateSiteFormGratitudeDto extends PartialType( + PickType(SiteFormGratitudeDto, ['isEnabled', 'text', 'header'] as const), +) {} diff --git a/backend/src/modules/forms/site-form-gratitude/entities/index.ts b/backend/src/modules/forms/site-form-gratitude/entities/index.ts new file mode 100644 index 0000000..2fb8af8 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/entities/index.ts @@ -0,0 +1 @@ +export * from './site-form-gratitude.entity'; diff --git a/backend/src/modules/forms/site-form-gratitude/entities/site-form-gratitude.entity.ts b/backend/src/modules/forms/site-form-gratitude/entities/site-form-gratitude.entity.ts new file mode 100644 index 0000000..cec2e26 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/entities/site-form-gratitude.entity.ts @@ -0,0 +1,45 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { type CreateSiteFormGratitudeDto, SiteFormGratitudeDto, type UpdateSiteFormGratitudeDto } from '../dto'; + +@Entity() +export class SiteFormGratitude { + @Column() + accountId: number; + + @PrimaryColumn() + formId: number; + + @Column({ default: false }) + isEnabled: boolean; + + @Column({ nullable: true }) + header: string | null; + + @Column({ nullable: true }) + text: string | null; + + constructor(accountId: number, formId: number, isEnabled: boolean, header: string | null, text: string | null) { + this.accountId = accountId; + this.formId = formId; + this.isEnabled = isEnabled; + this.header = header; + this.text = text; + } + + public static fromDto(accountId: number, formId: number, dto: CreateSiteFormGratitudeDto): SiteFormGratitude { + return new SiteFormGratitude(accountId, formId, dto.isEnabled, dto.header, dto.text); + } + + public update(dto: UpdateSiteFormGratitudeDto): SiteFormGratitude { + this.isEnabled = dto.isEnabled !== undefined ? dto.isEnabled : this.isEnabled; + this.header = dto.header !== undefined ? dto.header : this.header; + this.text = dto.text !== undefined ? dto.text : this.text; + + return this; + } + + public toDto(): SiteFormGratitudeDto { + return { formId: this.formId, isEnabled: this.isEnabled, header: this.header, text: this.text }; + } +} diff --git a/backend/src/modules/forms/site-form-gratitude/index.ts b/backend/src/modules/forms/site-form-gratitude/index.ts new file mode 100644 index 0000000..8aeed32 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './site-form-gratitude.controller'; +export * from './site-form-gratitude.service'; diff --git a/backend/src/modules/forms/site-form-gratitude/site-form-gratitude.controller.ts b/backend/src/modules/forms/site-form-gratitude/site-form-gratitude.controller.ts new file mode 100644 index 0000000..212b68b --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/site-form-gratitude.controller.ts @@ -0,0 +1,50 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import type { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { SiteFormGratitudeService } from './site-form-gratitude.service'; +import { SiteFormGratitudeDto, type CreateSiteFormGratitudeDto, type UpdateSiteFormGratitudeDto } from './dto'; + +@ApiTags('site-forms/gratitude') +@Controller(':formId/gratitude') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class SiteFormGratitudeController { + constructor(private readonly service: SiteFormGratitudeService) {} + + @ApiCreatedResponse({ description: 'Create site form gratitude', type: SiteFormGratitudeDto }) + @Post() + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Body() dto: CreateSiteFormGratitudeDto, + ) { + return this.service.create(accountId, formId, dto); + } + + @ApiOkResponse({ description: 'Get site form gratitude', type: [SiteFormGratitudeDto] }) + @Get() + public async findOne(@CurrentAuth() { accountId }: AuthData, @Param('formId', ParseIntPipe) formId: number) { + return this.service.findOne(accountId, { formId }); + } + + @ApiCreatedResponse({ description: 'Update site form gratitude', type: SiteFormGratitudeDto }) + @Patch() + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Body() dto: UpdateSiteFormGratitudeDto, + ) { + return this.service.update(accountId, formId, dto); + } + + @ApiOkResponse({ description: 'Delete site form gratitude' }) + @Delete() + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('formId', ParseIntPipe) formId: number) { + return this.service.delete(accountId, formId); + } +} diff --git a/backend/src/modules/forms/site-form-gratitude/site-form-gratitude.service.ts b/backend/src/modules/forms/site-form-gratitude/site-form-gratitude.service.ts new file mode 100644 index 0000000..171f1a9 --- /dev/null +++ b/backend/src/modules/forms/site-form-gratitude/site-form-gratitude.service.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { SiteFormGratitude } from './entities'; +import type { CreateSiteFormGratitudeDto, UpdateSiteFormGratitudeDto } from './dto'; + +interface FindFilter { + formId?: number; +} + +@Injectable() +export class SiteFormGratitudeService { + constructor( + @InjectRepository(SiteFormGratitude) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, formId: number, dto: CreateSiteFormGratitudeDto): Promise { + return this.repository.save(SiteFormGratitude.fromDto(accountId, formId, dto)); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async update(accountId: number, formId: number, dto: UpdateSiteFormGratitudeDto): Promise { + const gratitude = await this.findOne(accountId, { formId }); + if (!gratitude) { + throw NotFoundError.withId(SiteFormGratitude, formId); + } + + await this.repository.save(gratitude.update(dto)); + + return gratitude; + } + + public async process( + accountId: number, + formId: number, + dto: CreateSiteFormGratitudeDto | UpdateSiteFormGratitudeDto, + ): Promise { + const gratitude = await this.findOne(accountId, { formId }); + if (!gratitude) { + return this.create(accountId, formId, dto as CreateSiteFormGratitudeDto); + } else { + await this.repository.save(gratitude.update(dto as UpdateSiteFormGratitudeDto)); + + return gratitude; + } + } + + public async delete(accountId: number, formId: number) { + await this.createFindQb(accountId, { formId }).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.formId) { + qb.andWhere('form_id = :formId', { formId: filter.formId }); + } + + return qb; + } +} diff --git a/backend/src/modules/forms/site-form-page/dto/create-site-form-page.dto.ts b/backend/src/modules/forms/site-form-page/dto/create-site-form-page.dto.ts new file mode 100644 index 0000000..be7b306 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/dto/create-site-form-page.dto.ts @@ -0,0 +1,14 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { CreateSiteFormFieldDto } from '../../site-form-field'; +import { SiteFormPageDto } from './site-form-page.dto'; + +export class CreateSiteFormPageDto extends PickType(SiteFormPageDto, ['title', 'sortOrder'] as const) { + @ApiPropertyOptional({ type: [CreateSiteFormFieldDto], nullable: true }) + @IsOptional() + @IsArray() + @Type(() => CreateSiteFormFieldDto) + fields?: CreateSiteFormFieldDto[] | null; +} diff --git a/backend/src/modules/forms/site-form-page/dto/index.ts b/backend/src/modules/forms/site-form-page/dto/index.ts new file mode 100644 index 0000000..9c856c8 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-site-form-page.dto'; +export * from './site-form-page.dto'; +export * from './update-site-form-page.dto'; diff --git a/backend/src/modules/forms/site-form-page/dto/site-form-page.dto.ts b/backend/src/modules/forms/site-form-page/dto/site-form-page.dto.ts new file mode 100644 index 0000000..a54279e --- /dev/null +++ b/backend/src/modules/forms/site-form-page/dto/site-form-page.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { SiteFormFieldDto } from '../../site-form-field'; + +export class SiteFormPageDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiProperty() + @IsNumber() + sortOrder: number; + + @ApiProperty({ type: [SiteFormFieldDto] }) + @IsArray() + @Type(() => SiteFormFieldDto) + fields: SiteFormFieldDto[]; +} diff --git a/backend/src/modules/forms/site-form-page/dto/update-site-form-page.dto.ts b/backend/src/modules/forms/site-form-page/dto/update-site-form-page.dto.ts new file mode 100644 index 0000000..16b458d --- /dev/null +++ b/backend/src/modules/forms/site-form-page/dto/update-site-form-page.dto.ts @@ -0,0 +1,22 @@ +import { ApiExtraModels, ApiPropertyOptional, PartialType, PickType, getSchemaPath } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { CreateSiteFormFieldDto, UpdateSiteFormFieldDto } from '../../site-form-field'; +import { SiteFormPageDto } from './site-form-page.dto'; + +@ApiExtraModels(CreateSiteFormFieldDto) +@ApiExtraModels(UpdateSiteFormFieldDto) +export class UpdateSiteFormPageDto extends PartialType( + PickType(SiteFormPageDto, ['id', 'title', 'sortOrder'] as const), +) { + @ApiPropertyOptional({ + description: 'Array of form fields', + type: 'array', + items: { + oneOf: [{ $ref: getSchemaPath(CreateSiteFormFieldDto) }, { $ref: getSchemaPath(UpdateSiteFormFieldDto) }], + }, + }) + @IsOptional() + @IsArray() + fields?: (CreateSiteFormFieldDto | UpdateSiteFormFieldDto)[] | null; +} diff --git a/backend/src/modules/forms/site-form-page/entities/index.ts b/backend/src/modules/forms/site-form-page/entities/index.ts new file mode 100644 index 0000000..e126891 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/entities/index.ts @@ -0,0 +1 @@ +export * from './site-form-page.entity'; diff --git a/backend/src/modules/forms/site-form-page/entities/site-form-page.entity.ts b/backend/src/modules/forms/site-form-page/entities/site-form-page.entity.ts new file mode 100644 index 0000000..e9fad57 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/entities/site-form-page.entity.ts @@ -0,0 +1,57 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { SiteFormField } from '../../site-form-field'; +import { SiteFormPageDto, type CreateSiteFormPageDto, type UpdateSiteFormPageDto } from '../dto'; + +@Entity() +export class SiteFormPage { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + formId: number; + + @Column({ nullable: true }) + title: string | null; + + @Column() + sortOrder: number; + + constructor(accountId: number, formId: number, title: string | null, sortOrder: number) { + this.accountId = accountId; + this.formId = formId; + this.title = title; + this.sortOrder = sortOrder; + } + + private _fields: SiteFormField[] | null; + public get fields(): SiteFormField[] | null { + return this._fields; + } + public set fields(value: SiteFormField[] | null) { + this._fields = value; + } + + public static fromDto(accountId: number, formId: number, dto: CreateSiteFormPageDto): SiteFormPage { + return new SiteFormPage(accountId, formId, dto.title, dto.sortOrder); + } + + public update(dto: UpdateSiteFormPageDto): SiteFormPage { + this.title = dto.title !== undefined ? dto.title : this.title; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + + return this; + } + + public toDto(): SiteFormPageDto { + return { + id: this.id, + title: this.title, + sortOrder: this.sortOrder, + fields: this.fields ? this.fields?.map((f) => f.toDto()) : this.fields, + }; + } +} diff --git a/backend/src/modules/forms/site-form-page/index.ts b/backend/src/modules/forms/site-form-page/index.ts new file mode 100644 index 0000000..fcb2ed9 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './site-form-page.controller'; +export * from './site-form-page.service'; diff --git a/backend/src/modules/forms/site-form-page/site-form-page.controller.ts b/backend/src/modules/forms/site-form-page/site-form-page.controller.ts new file mode 100644 index 0000000..ec8cc1e --- /dev/null +++ b/backend/src/modules/forms/site-form-page/site-form-page.controller.ts @@ -0,0 +1,85 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, ExpandQuery } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import type { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { SiteFormPageService } from './site-form-page.service'; +import { SiteFormPageDto, CreateSiteFormPageDto, UpdateSiteFormPageDto } from './dto'; +import { ExpandableField } from './types'; + +@ApiTags('site-forms/pages') +@Controller(':formId/pages') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class SiteFormPageController { + constructor(private readonly service: SiteFormPageService) {} + + @ApiCreatedResponse({ description: 'Create site form page', type: SiteFormPageDto }) + @Post() + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Body() dto: CreateSiteFormPageDto, + ) { + return this.service.create(accountId, formId, dto); + } + + @ApiOkResponse({ description: 'Get site form pages', type: [SiteFormPageDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: fields.', + }) + @Get() + public async findMany( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findMany(accountId, { formId }, { expand: expand.fields }); + } + + @ApiOkResponse({ description: 'Get site form page', type: [SiteFormPageDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: fields.', + }) + @Get(':pageId') + public async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Param('pageId', ParseIntPipe) pageId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findOne(accountId, { formId, pageId }, { expand: expand.fields }); + } + + @ApiCreatedResponse({ description: 'Update site form page', type: SiteFormPageDto }) + @Patch(':pageId') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Param('pageId', ParseIntPipe) pageId: number, + @Body() dto: UpdateSiteFormPageDto, + ) { + return this.service.update(accountId, formId, pageId, dto); + } + + @ApiOkResponse({ description: 'Delete site form page' }) + @Delete(':pageId') + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Param('pageId', ParseIntPipe) pageId: number, + ) { + return this.service.delete(accountId, formId, pageId); + } +} diff --git a/backend/src/modules/forms/site-form-page/site-form-page.service.ts b/backend/src/modules/forms/site-form-page/site-form-page.service.ts new file mode 100644 index 0000000..2b47039 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/site-form-page.service.ts @@ -0,0 +1,131 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { SiteFormFieldService } from '../site-form-field'; + +import { SiteFormPage } from './entities'; +import type { CreateSiteFormPageDto, UpdateSiteFormPageDto } from './dto'; +import type { ExpandableField } from './types'; + +interface FindFilter { + formId?: number; + pageId?: number | number[]; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class SiteFormPageService { + constructor( + @InjectRepository(SiteFormPage) + private readonly repository: Repository, + private readonly fieldService: SiteFormFieldService, + ) {} + + public async create(accountId: number, formId: number, dto: CreateSiteFormPageDto): Promise { + const page = await this.repository.save(SiteFormPage.fromDto(accountId, formId, dto)); + + if (dto.fields) { + page.fields = await this.fieldService.createMany(accountId, page.id, dto.fields); + } + + return page; + } + + public async createMany(accountId: number, formId: number, dtos: CreateSiteFormPageDto[]): Promise { + return Promise.all(dtos.map((dto) => this.create(accountId, formId, dto))); + } + + public async findOne(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const page = await this.createFindQb(accountId, filter).getOne(); + return page && options?.expand ? await this.expandOne(page, options.expand) : page; + } + + public async findMany(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const pages = await this.createFindQb(accountId, filter).orderBy('sort_order').getMany(); + return pages && options?.expand ? await this.expandMany(pages, options.expand) : pages; + } + + public async update( + accountId: number, + formId: number, + pageId: number, + dto: UpdateSiteFormPageDto, + ): Promise { + const page = await this.findOne(accountId, { formId, pageId }); + if (!page) { + throw NotFoundError.withId(SiteFormPage, formId); + } + + await this.repository.save(page.update(dto)); + + if (dto.fields) { + page.fields = await this.fieldService.processBatch(accountId, pageId, dto.fields); + } + + return page; + } + + public async updateMany(accountId: number, formId: number, dtos: UpdateSiteFormPageDto[]): Promise { + return Promise.all(dtos.map((dto) => this.update(accountId, formId, dto.id, dto))); + } + + public async processBatch( + accountId: number, + formId: number, + dtos: (CreateSiteFormPageDto | UpdateSiteFormPageDto)[], + ): Promise { + const pages = await this.findMany(accountId, { formId }); + + const created = dtos.filter((dto) => !dto['id']).map((dto) => dto as CreateSiteFormPageDto); + const updated = dtos.filter((dto) => dto['id']).map((dto) => dto as UpdateSiteFormPageDto); + const deleted = pages.filter((f) => !updated.some((dto) => dto.id === f.id)).map((f) => f.id); + + const result: SiteFormPage[] = []; + + result.push(...(await this.createMany(accountId, formId, created))); + result.push(...(await this.updateMany(accountId, formId, updated))); + + if (deleted.length) { + await this.delete(accountId, formId, deleted); + } + + return result; + } + + public async delete(accountId: number, formId: number, pageId: number | number[]) { + await this.createFindQb(accountId, { pageId, formId }).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.pageId) { + if (Array.isArray(filter.pageId)) { + qb.andWhere('id IN (:...ids)', { ids: filter.pageId }); + } else { + qb.andWhere('id = :id', { id: filter.pageId }); + } + } + + if (filter?.formId) { + qb.andWhere('form_id = :formId', { formId: filter.formId }); + } + + return qb; + } + + private async expandOne(page: SiteFormPage, expand: ExpandableField[]): Promise { + if (expand.includes('fields')) { + page.fields = await this.fieldService.findMany(page.accountId, { pageId: page.id }); + } + return page; + } + private async expandMany(pages: SiteFormPage[], expand: ExpandableField[]): Promise { + return await Promise.all(pages.map((page) => this.expandOne(page, expand))); + } +} diff --git a/backend/src/modules/forms/site-form-page/types/expandable-field.ts b/backend/src/modules/forms/site-form-page/types/expandable-field.ts new file mode 100644 index 0000000..13df006 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'fields'; diff --git a/backend/src/modules/forms/site-form-page/types/index.ts b/backend/src/modules/forms/site-form-page/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/forms/site-form-page/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/forms/site-form/dto/create-site-form.dto.ts b/backend/src/modules/forms/site-form/dto/create-site-form.dto.ts new file mode 100644 index 0000000..35ee97d --- /dev/null +++ b/backend/src/modules/forms/site-form/dto/create-site-form.dto.ts @@ -0,0 +1,45 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsOptional } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { CreateSiteFormConsentDto } from '../../site-form-consent'; +import { CreateSiteFormGratitudeDto } from '../../site-form-gratitude'; +import { CreateSiteFormPageDto } from '../../site-form-page'; + +import { SiteFormDto } from './site-form.dto'; + +export class CreateSiteFormDto extends PickType(SiteFormDto, [ + 'type', + 'name', + 'title', + 'responsibleId', + 'design', + 'fieldLabelEnabled', + 'fieldPlaceholderEnabled', + 'multiformEnabled', + 'scheduleLimitDays', + 'checkDuplicate', + 'entityTypeLinks', + 'scheduleLinks', +] as const) { + @ApiPropertyOptional({ description: 'Is form headless' }) + @IsOptional() + @IsBoolean() + isHeadless?: boolean; + + @ApiPropertyOptional({ type: CreateSiteFormConsentDto, nullable: true, description: 'Form consent' }) + @IsOptional() + @Type(() => CreateSiteFormConsentDto) + consent?: CreateSiteFormConsentDto | null; + + @ApiPropertyOptional({ type: CreateSiteFormGratitudeDto, nullable: true, description: 'Form gratitude' }) + @IsOptional() + @Type(() => CreateSiteFormGratitudeDto) + gratitude?: CreateSiteFormGratitudeDto | null; + + @ApiPropertyOptional({ type: [CreateSiteFormPageDto], nullable: true, description: 'Form pages' }) + @IsOptional() + @IsArray() + @Type(() => CreateSiteFormPageDto) + pages?: CreateSiteFormPageDto[] | null; +} diff --git a/backend/src/modules/forms/site-form/dto/index.ts b/backend/src/modules/forms/site-form/dto/index.ts new file mode 100644 index 0000000..490a11d --- /dev/null +++ b/backend/src/modules/forms/site-form/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-site-form.dto'; +export * from './site-form-entity-type.dto'; +export * from './site-form-schedule.dto'; +export * from './site-form.dto'; +export * from './update-site-form.dto'; diff --git a/backend/src/modules/forms/site-form/dto/site-form-entity-type.dto.ts b/backend/src/modules/forms/site-form/dto/site-form-entity-type.dto.ts new file mode 100644 index 0000000..1f2fa7f --- /dev/null +++ b/backend/src/modules/forms/site-form/dto/site-form-entity-type.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; + +export class SiteFormEntityTypeDto { + @ApiProperty({ description: 'Entity type ID' }) + @IsNumber() + entityTypeId: number; + + @ApiProperty({ nullable: true, description: 'Board ID' }) + @IsOptional() + @IsNumber() + boardId: number | null; + + @ApiProperty({ description: 'Is main entity type' }) + @IsBoolean() + isMain: boolean; +} diff --git a/backend/src/modules/forms/site-form/dto/site-form-schedule.dto.ts b/backend/src/modules/forms/site-form/dto/site-form-schedule.dto.ts new file mode 100644 index 0000000..d8a4df6 --- /dev/null +++ b/backend/src/modules/forms/site-form/dto/site-form-schedule.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class SiteFormScheduleDto { + @ApiProperty({ description: 'Schedule ID' }) + @IsNumber() + scheduleId: number; +} diff --git a/backend/src/modules/forms/site-form/dto/site-form.dto.ts b/backend/src/modules/forms/site-form/dto/site-form.dto.ts new file mode 100644 index 0000000..b66e9a2 --- /dev/null +++ b/backend/src/modules/forms/site-form/dto/site-form.dto.ts @@ -0,0 +1,109 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; + +import { SiteFormConsentDto } from '../../site-form-consent'; +import { SiteFormGratitudeDto } from '../../site-form-gratitude'; +import { SiteFormPageDto } from '../../site-form-page'; + +import { SiteFormType } from '../enums'; +import { SiteFormEntityTypeDto } from './site-form-entity-type.dto'; +import { SiteFormScheduleDto } from './site-form-schedule.dto'; + +export class SiteFormDto { + @ApiProperty({ description: 'Site form id' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'User ID who created form' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ enum: SiteFormType, description: 'Form type' }) + @IsEnum(SiteFormType) + type: SiteFormType; + + @ApiProperty({ description: 'Form name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Form code' }) + @IsString() + code: string; + + @ApiProperty({ description: 'Is form active' }) + @IsBoolean() + isActive: boolean; + + @ApiProperty({ description: 'Is form headless' }) + @IsBoolean() + isHeadless: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Form title' }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Form responsible user ID' }) + @IsOptional() + @IsNumber() + responsibleId?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Form design' }) + @IsOptional() + @IsObject() + design: object | null; + + @ApiPropertyOptional({ description: 'Is field label enabled' }) + @IsOptional() + @IsBoolean() + fieldLabelEnabled?: boolean; + + @ApiPropertyOptional({ description: 'Is field placeholder enabled' }) + @IsOptional() + @IsBoolean() + fieldPlaceholderEnabled?: boolean; + + @ApiPropertyOptional({ description: 'Is multiform enabled' }) + @IsOptional() + @IsBoolean() + multiformEnabled?: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Schedule limit for date choose' }) + @IsOptional() + @IsNumber() + scheduleLimitDays?: number | null; + + @ApiPropertyOptional({ description: 'Deduplicate linked cards by phone or email' }) + @IsOptional() + @IsBoolean() + checkDuplicate?: boolean; + + @ApiPropertyOptional({ type: SiteFormConsentDto, nullable: true, description: 'Form consent' }) + @IsOptional() + @Type(() => SiteFormConsentDto) + consent?: SiteFormConsentDto | null; + + @ApiPropertyOptional({ type: SiteFormGratitudeDto, nullable: true, description: 'Form gratitude' }) + @IsOptional() + @Type(() => SiteFormGratitudeDto) + gratitude?: SiteFormGratitudeDto | null; + + @ApiPropertyOptional({ type: [SiteFormPageDto], nullable: true, description: 'Form pages' }) + @IsOptional() + @IsArray() + @Type(() => SiteFormPageDto) + pages?: SiteFormPageDto[] | null; + + @ApiPropertyOptional({ type: [SiteFormEntityTypeDto], nullable: true, description: 'Form entity type links' }) + @IsOptional() + @IsArray() + @Type(() => SiteFormEntityTypeDto) + entityTypeLinks?: SiteFormEntityTypeDto[] | null; + + @ApiPropertyOptional({ type: [SiteFormScheduleDto], nullable: true, description: 'Form schedule links' }) + @IsOptional() + @IsArray() + @Type(() => SiteFormScheduleDto) + scheduleLinks?: SiteFormScheduleDto[] | null; +} diff --git a/backend/src/modules/forms/site-form/dto/update-site-form.dto.ts b/backend/src/modules/forms/site-form/dto/update-site-form.dto.ts new file mode 100644 index 0000000..e09f0cf --- /dev/null +++ b/backend/src/modules/forms/site-form/dto/update-site-form.dto.ts @@ -0,0 +1,62 @@ +import { ApiExtraModels, ApiPropertyOptional, PartialType, PickType, getSchemaPath } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { CreateSiteFormConsentDto, UpdateSiteFormConsentDto } from '../../site-form-consent'; +import { CreateSiteFormGratitudeDto, UpdateSiteFormGratitudeDto } from '../../site-form-gratitude'; +import { CreateSiteFormPageDto, UpdateSiteFormPageDto } from '../../site-form-page'; + +import { SiteFormDto } from './site-form.dto'; + +@ApiExtraModels(CreateSiteFormConsentDto) +@ApiExtraModels(UpdateSiteFormConsentDto) +@ApiExtraModels(CreateSiteFormGratitudeDto) +@ApiExtraModels(UpdateSiteFormGratitudeDto) +@ApiExtraModels(CreateSiteFormPageDto) +@ApiExtraModels(UpdateSiteFormPageDto) +export class UpdateSiteFormDto extends PartialType( + PickType(SiteFormDto, [ + 'type', + 'name', + 'isActive', + 'title', + 'isHeadless', + 'responsibleId', + 'design', + 'entityTypeLinks', + 'fieldLabelEnabled', + 'fieldPlaceholderEnabled', + 'multiformEnabled', + 'scheduleLimitDays', + 'checkDuplicate', + 'scheduleLinks', + ] as const), +) { + @ApiPropertyOptional({ + description: 'Form consent', + type: 'array', + items: { + oneOf: [{ $ref: getSchemaPath(CreateSiteFormConsentDto) }, { $ref: getSchemaPath(UpdateSiteFormConsentDto) }], + }, + }) + @IsOptional() + consent?: CreateSiteFormConsentDto | UpdateSiteFormConsentDto | null; + + @ApiPropertyOptional({ + description: 'Form gratitude', + type: 'array', + items: { + oneOf: [{ $ref: getSchemaPath(CreateSiteFormGratitudeDto) }, { $ref: getSchemaPath(UpdateSiteFormGratitudeDto) }], + }, + }) + @IsOptional() + gratitude?: CreateSiteFormGratitudeDto | UpdateSiteFormGratitudeDto | null; + + @ApiPropertyOptional({ + description: 'Form pages', + type: 'array', + items: { oneOf: [{ $ref: getSchemaPath(CreateSiteFormPageDto) }, { $ref: getSchemaPath(UpdateSiteFormPageDto) }] }, + }) + @IsOptional() + @IsArray() + pages?: (CreateSiteFormPageDto | UpdateSiteFormPageDto)[] | null; +} diff --git a/backend/src/modules/forms/site-form/entities/index.ts b/backend/src/modules/forms/site-form/entities/index.ts new file mode 100644 index 0000000..ad7e8ab --- /dev/null +++ b/backend/src/modules/forms/site-form/entities/index.ts @@ -0,0 +1,3 @@ +export * from './site-form-entity-type.entity'; +export * from './site-form-schedule.entity'; +export * from './site-form.entity'; diff --git a/backend/src/modules/forms/site-form/entities/site-form-entity-type.entity.ts b/backend/src/modules/forms/site-form/entities/site-form-entity-type.entity.ts new file mode 100644 index 0000000..18c589e --- /dev/null +++ b/backend/src/modules/forms/site-form/entities/site-form-entity-type.entity.ts @@ -0,0 +1,44 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { SiteFormEntityTypeDto } from '../dto'; + +@Entity() +export class SiteFormEntityType { + @Column() + accountId: number; + + @PrimaryColumn() + formId: number; + + @PrimaryColumn() + entityTypeId: number; + + @Column({ nullable: true }) + boardId: number | null; + + @Column() + isMain: boolean; + + constructor(accountId: number, formId: number, entityTypeId: number, boardId: number | null, isMain: boolean) { + this.accountId = accountId; + this.formId = formId; + this.entityTypeId = entityTypeId; + this.boardId = boardId; + this.isMain = isMain; + } + + public static fromDto(accountId: number, formId: number, dto: SiteFormEntityTypeDto): SiteFormEntityType { + return new SiteFormEntityType(accountId, formId, dto.entityTypeId, dto.boardId, dto.isMain); + } + + public update(dto: SiteFormEntityTypeDto): SiteFormEntityType { + this.entityTypeId = dto.entityTypeId !== undefined ? dto.entityTypeId : this.entityTypeId; + this.boardId = dto.boardId !== undefined ? dto.boardId : this.boardId; + + return this; + } + + public toDto(): SiteFormEntityTypeDto { + return { entityTypeId: this.entityTypeId, boardId: this.boardId, isMain: this.isMain }; + } +} diff --git a/backend/src/modules/forms/site-form/entities/site-form-schedule.entity.ts b/backend/src/modules/forms/site-form/entities/site-form-schedule.entity.ts new file mode 100644 index 0000000..3c07bb0 --- /dev/null +++ b/backend/src/modules/forms/site-form/entities/site-form-schedule.entity.ts @@ -0,0 +1,35 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { SiteFormScheduleDto } from '../dto'; + +@Entity() +export class SiteFormSchedule { + @Column() + accountId: number; + + @PrimaryColumn() + formId: number; + + @PrimaryColumn() + scheduleId: number; + + constructor(accountId: number, formId: number, scheduleId: number) { + this.accountId = accountId; + this.formId = formId; + this.scheduleId = scheduleId; + } + + public static fromDto(accountId: number, formId: number, dto: SiteFormScheduleDto): SiteFormSchedule { + return new SiteFormSchedule(accountId, formId, dto.scheduleId); + } + + public update(dto: SiteFormScheduleDto): SiteFormSchedule { + this.scheduleId = dto.scheduleId !== undefined ? dto.scheduleId : this.scheduleId; + + return this; + } + + public toDto(): SiteFormScheduleDto { + return { scheduleId: this.scheduleId }; + } +} diff --git a/backend/src/modules/forms/site-form/entities/site-form.entity.ts b/backend/src/modules/forms/site-form/entities/site-form.entity.ts new file mode 100644 index 0000000..d50b03e --- /dev/null +++ b/backend/src/modules/forms/site-form/entities/site-form.entity.ts @@ -0,0 +1,204 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { SiteFormConsent } from '../../site-form-consent'; +import { SiteFormGratitude } from '../../site-form-gratitude'; +import { SiteFormPage } from '../../site-form-page'; + +import type { CreateSiteFormDto, SiteFormDto, UpdateSiteFormDto } from '../dto'; +import { SiteFormType } from '../enums'; +import { SiteFormEntityType } from './site-form-entity-type.entity'; +import { SiteFormSchedule } from './site-form-schedule.entity'; + +@Entity() +export class SiteForm { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + createdBy: number; + + @Column() + type: SiteFormType; + + @Column() + name: string; + + @Column({ unique: true }) + code: string; + + @Column() + isActive: boolean; + + @Column({ default: false }) + isHeadless: boolean; + + @Column({ nullable: true, default: null }) + title: string | null; + + @Column({ nullable: true, default: null }) + responsibleId: number | null; + + @Column({ type: 'jsonb', nullable: true, default: null }) + design: object | null; + + @Column({ default: false }) + fieldLabelEnabled: boolean; + + @Column({ default: true }) + fieldPlaceholderEnabled: boolean; + + @Column({ default: false }) + multiformEnabled: boolean; + + @Column({ default: null }) + scheduleLimitDays: number | null; + + @Column({ default: false }) + checkDuplicate: boolean; + + constructor( + accountId: number, + createdBy: number, + type: SiteFormType, + name: string, + code: string, + isActive: boolean, + isHeadless: boolean, + title: string | null, + responsibleId: number | null, + design: object | null, + fieldLabelEnabled: boolean, + fieldPlaceholderEnabled: boolean, + multiformEnabled: boolean, + scheduleLimitDays: number | null, + checkDuplicate: boolean, + ) { + this.accountId = accountId; + this.createdBy = createdBy; + this.type = type; + this.name = name; + this.code = code; + this.isActive = isActive; + this.isHeadless = isHeadless; + this.title = title; + this.responsibleId = responsibleId; + this.design = design; + this.fieldLabelEnabled = fieldLabelEnabled; + this.fieldPlaceholderEnabled = fieldPlaceholderEnabled; + this.multiformEnabled = multiformEnabled; + this.scheduleLimitDays = scheduleLimitDays; + this.checkDuplicate = checkDuplicate; + } + + private _consent: SiteFormConsent | null; + get consent(): SiteFormConsent | null { + return this._consent; + } + set consent(value: SiteFormConsent | null) { + this._consent = value; + } + + private _gratitude: SiteFormGratitude | null; + get gratitude(): SiteFormGratitude | null { + return this._gratitude; + } + set gratitude(value: SiteFormGratitude | null) { + this._gratitude = value; + } + + private _pages: SiteFormPage[] | null; + get pages(): SiteFormPage[] | null { + return this._pages; + } + set pages(value: SiteFormPage[] | null) { + this._pages = value; + } + + private _entityTypeLinks: SiteFormEntityType[] | null; + get entityTypeLinks(): SiteFormEntityType[] | null { + return this._entityTypeLinks; + } + set entityTypeLinks(value: SiteFormEntityType[] | null) { + this._entityTypeLinks = value; + } + + private _scheduleLinks: SiteFormSchedule[] | null; + get scheduleLinks(): SiteFormSchedule[] | null { + return this._scheduleLinks; + } + set scheduleLinks(value: SiteFormSchedule[] | null) { + this._scheduleLinks = value; + } + + static fromDto( + accountId: number, + createdBy: number, + code: string, + dto: CreateSiteFormDto, + isActive = true, + ): SiteForm { + return new SiteForm( + accountId, + createdBy, + dto.type, + dto.name, + code, + isActive, + dto.isHeadless ?? false, + dto.title, + dto.responsibleId, + dto.design, + dto.fieldLabelEnabled, + dto.fieldPlaceholderEnabled, + dto.multiformEnabled, + dto.scheduleLimitDays, + dto.checkDuplicate, + ); + } + + update(dto: UpdateSiteFormDto): SiteForm { + this.type = dto.type !== undefined ? dto.type : this.type; + this.name = dto.name !== undefined ? dto.name : this.name; + this.isActive = dto.isActive !== undefined ? dto.isActive : this.isActive; + this.isHeadless = dto.isHeadless !== undefined ? dto.isHeadless : this.isHeadless; + this.title = dto.title !== undefined ? dto.title : this.title; + this.responsibleId = dto.responsibleId !== undefined ? dto.responsibleId : this.responsibleId; + this.design = dto.design !== undefined ? dto.design : this.design; + this.fieldLabelEnabled = dto.fieldLabelEnabled !== undefined ? dto.fieldLabelEnabled : this.fieldLabelEnabled; + this.fieldPlaceholderEnabled = + dto.fieldPlaceholderEnabled !== undefined ? dto.fieldPlaceholderEnabled : this.fieldPlaceholderEnabled; + this.multiformEnabled = dto.multiformEnabled !== undefined ? dto.multiformEnabled : this.multiformEnabled; + this.scheduleLimitDays = dto.scheduleLimitDays !== undefined ? dto.scheduleLimitDays : this.scheduleLimitDays; + this.checkDuplicate = dto.checkDuplicate !== undefined ? dto.checkDuplicate : this.checkDuplicate; + + return this; + } + + toDto(): SiteFormDto { + return { + id: this.id, + createdBy: this.createdBy, + type: this.type, + name: this.name, + code: this.code, + isActive: this.isActive, + isHeadless: this.isHeadless, + title: this.title, + responsibleId: this.responsibleId, + design: this.design, + fieldLabelEnabled: this.fieldLabelEnabled, + fieldPlaceholderEnabled: this.fieldPlaceholderEnabled, + multiformEnabled: this.multiformEnabled, + scheduleLimitDays: this.scheduleLimitDays, + checkDuplicate: this.checkDuplicate, + consent: this.consent ? this.consent.toDto() : this.consent, + gratitude: this.gratitude ? this.gratitude.toDto() : this.gratitude, + pages: this.pages ? this.pages?.map((p) => p.toDto()) : this.pages, + entityTypeLinks: this.entityTypeLinks ? this.entityTypeLinks?.map((l) => l.toDto()) : this.entityTypeLinks, + scheduleLinks: this.scheduleLinks ? this.scheduleLinks?.map((l) => l.toDto()) : this.scheduleLinks, + }; + } +} diff --git a/backend/src/modules/forms/site-form/enums/index.ts b/backend/src/modules/forms/site-form/enums/index.ts new file mode 100644 index 0000000..dfe67a5 --- /dev/null +++ b/backend/src/modules/forms/site-form/enums/index.ts @@ -0,0 +1 @@ +export * from './site-form-type.enum'; diff --git a/backend/src/modules/forms/site-form/enums/site-form-type.enum.ts b/backend/src/modules/forms/site-form/enums/site-form-type.enum.ts new file mode 100644 index 0000000..32d30d1 --- /dev/null +++ b/backend/src/modules/forms/site-form/enums/site-form-type.enum.ts @@ -0,0 +1,4 @@ +export enum SiteFormType { + EntityType = 'entity_type', + Schedule = 'schedule', +} diff --git a/backend/src/modules/forms/site-form/index.ts b/backend/src/modules/forms/site-form/index.ts new file mode 100644 index 0000000..dad4b17 --- /dev/null +++ b/backend/src/modules/forms/site-form/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './services'; +export * from './site-form.controller'; +export * from './types'; diff --git a/backend/src/modules/forms/site-form/services/index.ts b/backend/src/modules/forms/site-form/services/index.ts new file mode 100644 index 0000000..6bad5d9 --- /dev/null +++ b/backend/src/modules/forms/site-form/services/index.ts @@ -0,0 +1,3 @@ +export * from './site-form-entity-type.service'; +export * from './site-form-schedule.service'; +export * from './site-form.service'; diff --git a/backend/src/modules/forms/site-form/services/site-form-entity-type.service.ts b/backend/src/modules/forms/site-form/services/site-form-entity-type.service.ts new file mode 100644 index 0000000..5637e40 --- /dev/null +++ b/backend/src/modules/forms/site-form/services/site-form-entity-type.service.ts @@ -0,0 +1,91 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import type { SiteFormEntityTypeDto } from '../dto'; +import { SiteFormEntityType } from '../entities'; + +interface FindFilter { + formId?: number; + entityTypeId?: number | number[]; +} + +@Injectable() +export class SiteFormEntityTypeService { + constructor( + @InjectRepository(SiteFormEntityType) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, formId: number, dto: SiteFormEntityTypeDto): Promise { + return this.repository.save(SiteFormEntityType.fromDto(accountId, formId, dto)); + } + + public async createMany( + accountId: number, + formId: number, + dtos: SiteFormEntityTypeDto[], + ): Promise { + return Promise.all(dtos.map((dto) => this.create(accountId, formId, dto))); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getMany(); + } + + public async processBatch( + accountId: number, + formId: number, + dtos: SiteFormEntityTypeDto[], + ): Promise { + let links = await this.findMany(accountId, { formId }); + + const deleted = links.filter((link) => !dtos.some((dto) => link.entityTypeId === dto.entityTypeId)); + if (deleted.length) { + await this.delete(accountId, { formId, entityTypeId: deleted.map((link) => link.entityTypeId) }); + links = links.filter((link) => !deleted.some((d) => d.entityTypeId === link.entityTypeId)); + } + + const result: SiteFormEntityType[] = []; + for (const dto of dtos) { + const link = links.find((l) => l.entityTypeId === dto.entityTypeId); + if (link) { + result.push(await this.repository.save(link.update(dto))); + } else { + result.push(await this.create(accountId, formId, dto)); + } + } + const created = dtos.filter((dto) => !links.some((link) => link.entityTypeId === dto.entityTypeId)); + if (created.length) { + result.push(...(await this.createMany(accountId, formId, created))); + } + + return result; + } + + public async delete(accountId: number, filter: FindFilter) { + await this.createFindQb(accountId, filter).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.formId) { + qb.andWhere('form_id = :formId', { formId: filter.formId }); + } + + if (filter?.entityTypeId) { + if (Array.isArray(filter.entityTypeId)) { + qb.andWhere('entity_type_id IN (:...entityTypeIds)', { entityTypeIds: filter.entityTypeId }); + } else { + qb.andWhere('entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + } + + return qb; + } +} diff --git a/backend/src/modules/forms/site-form/services/site-form-schedule.service.ts b/backend/src/modules/forms/site-form/services/site-form-schedule.service.ts new file mode 100644 index 0000000..648ec55 --- /dev/null +++ b/backend/src/modules/forms/site-form/services/site-form-schedule.service.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import type { SiteFormScheduleDto } from '../dto'; +import { SiteFormSchedule } from '../entities'; + +interface FindFilter { + formId?: number; + scheduleId?: number | number[]; +} + +@Injectable() +export class SiteFormScheduleService { + constructor( + @InjectRepository(SiteFormSchedule) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, formId: number, dto: SiteFormScheduleDto): Promise { + return this.repository.save(SiteFormSchedule.fromDto(accountId, formId, dto)); + } + + public async createMany(accountId: number, formId: number, dtos: SiteFormScheduleDto[]): Promise { + return Promise.all(dtos.map((dto) => this.create(accountId, formId, dto))); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getMany(); + } + + public async processBatch( + accountId: number, + formId: number, + dtos: SiteFormScheduleDto[], + ): Promise { + let links = await this.findMany(accountId, { formId }); + + const deleted = links.filter((link) => !dtos.some((dto) => link.scheduleId === dto.scheduleId)); + if (deleted.length) { + await this.delete(accountId, { formId, scheduleId: deleted.map((link) => link.scheduleId) }); + links = links.filter((link) => !deleted.some((d) => d.scheduleId === link.scheduleId)); + } + + const result: SiteFormSchedule[] = []; + for (const dto of dtos) { + const link = links.find((l) => l.scheduleId === dto.scheduleId); + if (link) { + result.push(await this.repository.save(link.update(dto))); + } else { + result.push(await this.create(accountId, formId, dto)); + } + } + const created = dtos.filter((dto) => !links.some((link) => link.scheduleId === dto.scheduleId)); + if (created.length) { + result.push(...(await this.createMany(accountId, formId, created))); + } + + return result; + } + + public async delete(accountId: number, filter: FindFilter) { + await this.createFindQb(accountId, filter).delete().execute(); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + + if (filter?.formId) { + qb.andWhere('form_id = :formId', { formId: filter.formId }); + } + + if (filter?.scheduleId) { + if (Array.isArray(filter.scheduleId)) { + qb.andWhere('schedule_id IN (:...scheduleIds)', { scheduleIds: filter.scheduleId }); + } else { + qb.andWhere('schedule_id = :scheduleId', { scheduleId: filter.scheduleId }); + } + } + + return qb; + } +} diff --git a/backend/src/modules/forms/site-form/services/site-form.service.ts b/backend/src/modules/forms/site-form/services/site-form.service.ts new file mode 100644 index 0000000..0a57b71 --- /dev/null +++ b/backend/src/modules/forms/site-form/services/site-form.service.ts @@ -0,0 +1,153 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import type { Repository } from 'typeorm'; + +import { NotFoundError, PasswordUtil } from '@/common'; + +import { SiteFormConsentService } from '../../site-form-consent'; +import { SiteFormGratitudeService } from '../../site-form-gratitude'; +import { SiteFormPageService } from '../../site-form-page'; + +import type { CreateSiteFormDto, UpdateSiteFormDto } from '../dto'; +import { SiteForm } from '../entities'; +import type { ExpandableField } from '../types'; +import { SiteFormEntityTypeService } from './site-form-entity-type.service'; +import { SiteFormScheduleService } from './site-form-schedule.service'; + +interface FindFilter { + formId?: number; + code?: string; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class SiteFormService { + constructor( + @InjectRepository(SiteForm) + private readonly repository: Repository, + private readonly consentService: SiteFormConsentService, + private readonly gratitudeService: SiteFormGratitudeService, + private readonly pageService: SiteFormPageService, + private readonly entityTypeLinkService: SiteFormEntityTypeService, + private readonly scheduleLinkService: SiteFormScheduleService, + ) {} + + async create(accountId: number, userId: number, dto: CreateSiteFormDto): Promise { + const code = PasswordUtil.generateSecure({ length: 16, numbers: true }); + const form = await this.repository.save(SiteForm.fromDto(accountId, userId, code, dto)); + + if (dto.consent) { + form.consent = await this.consentService.create(accountId, form.id, dto.consent); + } + + if (dto.gratitude) { + form.gratitude = await this.gratitudeService.create(accountId, form.id, dto.gratitude); + } + + if (dto.pages) { + form.pages = await this.pageService.createMany(accountId, form.id, dto.pages); + } + + if (dto.entityTypeLinks) { + form.entityTypeLinks = await this.entityTypeLinkService.createMany(accountId, form.id, dto.entityTypeLinks); + } + + if (dto.scheduleLinks) { + form.scheduleLinks = await this.scheduleLinkService.createMany(accountId, form.id, dto.scheduleLinks); + } + + return form; + } + + async findOne(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const form = await this.createFindQb(accountId, filter).getOne(); + + return form && options?.expand ? this.expandOne(form, options.expand) : form; + } + + async findMany(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const forms = await this.createFindQb(accountId, filter).orderBy('sf.id').getMany(); + + return forms && options?.expand ? this.expandMany(forms, options.expand) : forms; + } + + async findByCode(code: string, options?: FindOptions): Promise { + const form = await this.repository.findOneBy({ code, isActive: true }); + + return form && options?.expand ? this.expandOne(form, options.expand) : form; + } + + async update(accountId: number, formId: number, dto: UpdateSiteFormDto): Promise { + const form = await this.findOne(accountId, { formId }); + if (!form) { + throw NotFoundError.withId(SiteForm, formId); + } + + await this.repository.save(form.update(dto)); + + if (dto.consent) { + form.consent = await this.consentService.process(accountId, formId, dto.consent); + } + + if (dto.gratitude) { + form.gratitude = await this.gratitudeService.process(accountId, formId, dto.gratitude); + } + + if (dto.pages) { + form.pages = await this.pageService.processBatch(accountId, formId, dto.pages); + } + + if (dto.entityTypeLinks) { + form.entityTypeLinks = await this.entityTypeLinkService.processBatch(accountId, formId, dto.entityTypeLinks); + } + + if (dto.scheduleLinks) { + form.scheduleLinks = await this.scheduleLinkService.processBatch(accountId, form.id, dto.scheduleLinks); + } + + return form; + } + + async delete(accountId: number, formId: number): Promise { + await this.repository.delete({ accountId, id: formId }); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('sf').where('sf.account_id = :accountId', { accountId }); + + if (filter?.formId) { + qb.andWhere('sf.id = :id', { id: filter.formId }); + } + if (filter?.code) { + qb.andWhere('sf.code = :code', { code: filter.code }); + } + + return qb; + } + + private async expandOne(form: SiteForm, expand: ExpandableField[]): Promise { + if (expand.includes('consent')) { + form.consent = await this.consentService.findOne(form.accountId, { formId: form.id }); + } + if (expand.includes('gratitude')) { + form.gratitude = await this.gratitudeService.findOne(form.accountId, { formId: form.id }); + } + if (expand.includes('pages.fields')) { + form.pages = await this.pageService.findMany(form.accountId, { formId: form.id }, { expand: ['fields'] }); + } else if (expand.includes('pages')) { + form.pages = await this.pageService.findMany(form.accountId, { formId: form.id }); + } + if (expand.includes('entityTypeLinks')) { + form.entityTypeLinks = await this.entityTypeLinkService.findMany(form.accountId, { formId: form.id }); + } + if (expand.includes('scheduleLinks')) { + form.scheduleLinks = await this.scheduleLinkService.findMany(form.accountId, { formId: form.id }); + } + return form; + } + private async expandMany(forms: SiteForm[], expand: ExpandableField[]): Promise { + return await Promise.all(forms.map((form) => this.expandOne(form, expand))); + } +} diff --git a/backend/src/modules/forms/site-form/site-form.controller.ts b/backend/src/modules/forms/site-form/site-form.controller.ts new file mode 100644 index 0000000..09ce780 --- /dev/null +++ b/backend/src/modules/forms/site-form/site-form.controller.ts @@ -0,0 +1,81 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, ExpandQuery } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import type { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { SiteFormDto, CreateSiteFormDto, UpdateSiteFormDto } from './dto'; +import { ExpandableField } from './types'; +import { SiteFormService } from './services'; + +@ApiTags('site-forms') +@Controller() +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class SiteFormController { + constructor(private readonly service: SiteFormService) {} + + @ApiOperation({ summary: 'Create site form', description: 'Create site form' }) + @ApiBody({ type: CreateSiteFormDto, required: true, description: 'Site form data' }) + @ApiCreatedResponse({ description: 'Site form', type: SiteFormDto }) + @Post() + public async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateSiteFormDto) { + return this.service.create(accountId, userId, dto); + } + + @ApiOperation({ summary: 'Get site forms', description: 'Get site forms for account' }) + @ApiOkResponse({ description: 'Site forms', type: [SiteFormDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: consent,gratitude,pages,pages.fields,entityTypeLinks', + }) + @Get() + public async findMany(@CurrentAuth() { accountId }: AuthData, @Query() expand?: ExpandQuery) { + return this.service.findMany(accountId, {}, { expand: expand.fields }); + } + + @ApiOperation({ summary: 'Get site form', description: 'Get site form by id' }) + @ApiParam({ name: 'formId', type: Number, required: true, description: 'Site form id' }) + @ApiOkResponse({ description: 'Site form', type: [SiteFormDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: consent,gratitude,pages,pages.fields,entityTypeLinks', + }) + @Get(':formId') + public async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findOne(accountId, { formId }, { expand: expand.fields }); + } + + @ApiOperation({ summary: 'Update site form', description: 'Update site form' }) + @ApiParam({ name: 'formId', type: Number, required: true, description: 'Site form id' }) + @ApiBody({ type: UpdateSiteFormDto, required: true, description: 'Site form data' }) + @ApiOkResponse({ description: 'Site form', type: SiteFormDto }) + @Patch(':formId') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('formId', ParseIntPipe) formId: number, + @Body() dto: UpdateSiteFormDto, + ) { + return this.service.update(accountId, formId, dto); + } + + @ApiOperation({ summary: 'Delete site form', description: 'Delete site form' }) + @ApiParam({ name: 'formId', type: Number, required: true, description: 'Site form id' }) + @ApiOkResponse() + @Delete(':formId') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('formId', ParseIntPipe) formId: number) { + return this.service.delete(accountId, formId); + } +} diff --git a/backend/src/modules/forms/site-form/types/expandable-field.ts b/backend/src/modules/forms/site-form/types/expandable-field.ts new file mode 100644 index 0000000..cbaf144 --- /dev/null +++ b/backend/src/modules/forms/site-form/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'consent' | 'gratitude' | 'pages' | 'pages.fields' | 'entityTypeLinks' | 'scheduleLinks'; diff --git a/backend/src/modules/forms/site-form/types/index.ts b/backend/src/modules/forms/site-form/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/forms/site-form/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/frontend-event/frontend-event.gateway.ts b/backend/src/modules/frontend-event/frontend-event.gateway.ts new file mode 100644 index 0000000..7d1e352 --- /dev/null +++ b/backend/src/modules/frontend-event/frontend-event.gateway.ts @@ -0,0 +1,50 @@ +import { Logger } from '@nestjs/common'; +import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; +import { Server, Socket } from 'socket.io'; + +import { TokenService } from '@/common'; +import { TokenPayload } from '@/modules/iam/common'; + +@WebSocketGateway({ path: '/api/socket.io', cors: { origin: '*' } }) +export class FrontendEventGateway implements OnGatewayConnection, OnGatewayDisconnect { + private readonly logger = new Logger(FrontendEventGateway.name); + + @WebSocketServer() + server: Server; + + constructor(private readonly tokenService: TokenService) {} + + handleConnection(client: Socket) { + try { + const payload = this.tokenService.verify(client.handshake.auth['token']); + client.join([this.getUserRoomName(payload.userId), this.getAccountRoomName(payload.accountId)]); + } catch (e) { + this.logger.warn(`NotificationGateway.handleConnection error: ${e.toString()}`); + } + } + + handleDisconnect(client: Socket) { + try { + const payload = this.tokenService.verify(client.handshake.auth['token']); + client.leave(this.getUserRoomName(payload.userId)); + client.leave(this.getAccountRoomName(payload.accountId)); + } catch (e) { + this.logger.warn(`NotificationGateway.handleDisconnect error: ${e.toString()}`); + } + } + + notifyUser(userId: number, type: string, notification: unknown) { + this.server.to(this.getUserRoomName(userId)).emit(type, notification); + } + + notifyAccount(accountId: number, type: string, notification: unknown) { + this.server.to(this.getAccountRoomName(accountId)).emit(type, notification); + } + + private getUserRoomName(userId: number): string { + return userId.toString(); + } + private getAccountRoomName(accountId: number): string { + return accountId.toString(); + } +} diff --git a/backend/src/modules/frontend-event/frontend-event.module.ts b/backend/src/modules/frontend-event/frontend-event.module.ts new file mode 100644 index 0000000..2b8660c --- /dev/null +++ b/backend/src/modules/frontend-event/frontend-event.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { FrontendEventGateway } from './frontend-event.gateway'; +import { FrontendEventService } from './frontend-event.service'; + +@Module({ + providers: [FrontendEventGateway, FrontendEventService], +}) +export class FrontendEventModule {} diff --git a/backend/src/modules/frontend-event/frontend-event.service.ts b/backend/src/modules/frontend-event/frontend-event.service.ts new file mode 100644 index 0000000..a68aeb6 --- /dev/null +++ b/backend/src/modules/frontend-event/frontend-event.service.ts @@ -0,0 +1,122 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { UserNotification } from '@/common'; +import { + ChatEvent, + ChatMessageCreatedEvent, + ChatMessageUpdatedEvent, + MultichatEventType, +} from '@/modules/multichat/common'; +import { NotificationEventType, NotificationUnseenEvent } from '@/modules/notification/common'; +import { ScheduleEvent, SchedulerEventType } from '@/modules/scheduler/common'; +import { ActivityEvent, CrmEventType, EntityCreatedEvent, EntityEvent, TaskEvent } from '@/CRM/common'; + +import { FrontendEventGateway } from './frontend-event.gateway'; + +@Injectable() +export class FrontendEventService { + constructor(private readonly gateway: FrontendEventGateway) {} + + @OnEvent(NotificationEventType.NOTIFICATION_CREATED, { async: true }) + public async onNotificationCreated(event: { userId?: number }) { + if (event?.userId) { + this.gateway.notifyUser(event.userId, NotificationEventType.NOTIFICATION_CREATED, event); + } + } + + @OnEvent(NotificationEventType.NOTIFICATION_UNSEEN, { async: true }) + public async onNotificationUnseen(event: NotificationUnseenEvent) { + this.gateway.notifyUser(event.userId, NotificationEventType.NOTIFICATION_UNSEEN, event.unseenCount); + } + + @OnEvent(CrmEventType.TaskCreated, { async: true }) + public async onTaskCreated(event: TaskEvent) { + this.gateway.notifyAccount(event.accountId, CrmEventType.TaskCreated, event.taskId); + } + + @OnEvent(CrmEventType.TaskUpdated, { async: true }) + public async onTaskUpdated(event: TaskEvent) { + this.gateway.notifyAccount(event.accountId, CrmEventType.TaskUpdated, event.taskId); + } + + @OnEvent(CrmEventType.TaskDeleted, { async: true }) + public async onTaskDeleted(event: TaskEvent) { + this.gateway.notifyAccount(event.accountId, CrmEventType.TaskDeleted, event.taskId); + } + + @OnEvent(CrmEventType.ActivityCreated, { async: true }) + public async onActivityCreated(event: ActivityEvent) { + this.gateway.notifyAccount(event.accountId, CrmEventType.ActivityCreated, event.activityId); + } + + @OnEvent(CrmEventType.ActivityUpdated, { async: true }) + public async onActivityUpdated(event: ActivityEvent) { + this.gateway.notifyAccount(event.accountId, CrmEventType.ActivityUpdated, event.activityId); + } + + @OnEvent(CrmEventType.ActivityDeleted, { async: true }) + public async onActivityDeleted(event: ActivityEvent) { + this.gateway.notifyAccount(event.accountId, CrmEventType.ActivityDeleted, event.activityId); + } + + @OnEvent(CrmEventType.EntityCreated, { async: true }) + public async onEntityCreated(event: EntityCreatedEvent) { + if (event.userNotification !== UserNotification.Suppressed) { + this.gateway.notifyAccount(event.accountId, CrmEventType.EntityCreated, event); + } + } + + @OnEvent(CrmEventType.EntityUpdated, { async: true }) + public async onEntityUpdated(event: EntityEvent) { + if (event.userNotification !== UserNotification.Suppressed) { + this.gateway.notifyAccount(event.accountId, CrmEventType.EntityUpdated, event); + } + } + + @OnEvent(CrmEventType.EntityDeleted, { async: true }) + public async onEntityDeleted(event: EntityEvent) { + if (event.userNotification !== UserNotification.Suppressed) { + this.gateway.notifyAccount(event.accountId, CrmEventType.EntityDeleted, event); + } + } + + @OnEvent(MultichatEventType.ChatCreated, { async: true }) + public async onChatCreated(event: ChatEvent) { + this.gateway.notifyUser(event.userId, MultichatEventType.ChatCreated, event); + } + @OnEvent(MultichatEventType.ChatUpdated, { async: true }) + public async onChatUpdated(event: ChatEvent) { + this.gateway.notifyUser(event.userId, MultichatEventType.ChatUpdated, event); + } + @OnEvent(MultichatEventType.ChatDeleted, { async: true }) + public async onChatDeleted(event: ChatEvent) { + this.gateway.notifyUser(event.userId, MultichatEventType.ChatDeleted, event); + } + + @OnEvent(MultichatEventType.ChatMessageCreated, { async: true }) + public async onChatMessageCreated(event: ChatMessageCreatedEvent) { + this.gateway.notifyUser(event.userId, MultichatEventType.ChatMessageCreated, event); + } + @OnEvent(MultichatEventType.ChatMessageUpdated, { async: true }) + public async onChatMessageUpdated(event: ChatMessageUpdatedEvent) { + this.gateway.notifyUser(event.userId, MultichatEventType.ChatMessageUpdated, event); + } + @OnEvent(MultichatEventType.ChatMessageDeleted, { async: true }) + public async onChatMessageDeleted(event: ChatMessageUpdatedEvent) { + this.gateway.notifyUser(event.userId, MultichatEventType.ChatMessageDeleted, event); + } + + @OnEvent(SchedulerEventType.ScheduleCreated, { async: true }) + public async onScheduleCreated(event: ScheduleEvent) { + this.gateway.notifyAccount(event.accountId, SchedulerEventType.ScheduleCreated, event); + } + @OnEvent(SchedulerEventType.ScheduleUpdated, { async: true }) + public async onScheduleUpdated(event: ScheduleEvent) { + this.gateway.notifyAccount(event.accountId, SchedulerEventType.ScheduleUpdated, event); + } + @OnEvent(SchedulerEventType.ScheduleDeleted, { async: true }) + public async onScheduleDeleted(event: ScheduleEvent) { + this.gateway.notifyAccount(event.accountId, SchedulerEventType.ScheduleDeleted, event); + } +} diff --git a/backend/src/modules/frontend-object/dto/create-frontend-object.dto.ts b/backend/src/modules/frontend-object/dto/create-frontend-object.dto.ts new file mode 100644 index 0000000..f7d9d0c --- /dev/null +++ b/backend/src/modules/frontend-object/dto/create-frontend-object.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from '@nestjs/swagger'; +import { FrontendObjectDto } from './frontend-object.dto'; + +export class CreateFrontendObjectDto extends PickType(FrontendObjectDto, ['key', 'value'] as const) {} + diff --git a/backend/src/modules/frontend-object/dto/frontend-object-filter.dto.ts b/backend/src/modules/frontend-object/dto/frontend-object-filter.dto.ts new file mode 100644 index 0000000..df501eb --- /dev/null +++ b/backend/src/modules/frontend-object/dto/frontend-object-filter.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class FrontendObjectFilterDto { + @ApiProperty({ description: 'Key of the frontend object' }) + @IsString() + key: string; +} diff --git a/backend/src/modules/frontend-object/dto/frontend-object.dto.ts b/backend/src/modules/frontend-object/dto/frontend-object.dto.ts new file mode 100644 index 0000000..0223a0d --- /dev/null +++ b/backend/src/modules/frontend-object/dto/frontend-object.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmptyObject, IsString } from 'class-validator'; + +export class FrontendObjectDto { + @ApiProperty({ description: 'Key of the frontend object' }) + @IsString() + key: string; + + @ApiProperty({ description: 'Value of the frontend object', type: Object }) + @IsNotEmptyObject() + value: unknown; + + @ApiProperty({ description: 'Date of creation' }) + @IsString() + createdAt: string; +} diff --git a/backend/src/modules/frontend-object/dto/index.ts b/backend/src/modules/frontend-object/dto/index.ts new file mode 100644 index 0000000..f748bfc --- /dev/null +++ b/backend/src/modules/frontend-object/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-frontend-object.dto'; +export * from './frontend-object-filter.dto'; +export * from './frontend-object.dto'; diff --git a/backend/src/modules/frontend-object/entities/frontend-object.entity.ts b/backend/src/modules/frontend-object/entities/frontend-object.entity.ts new file mode 100644 index 0000000..34721dc --- /dev/null +++ b/backend/src/modules/frontend-object/entities/frontend-object.entity.ts @@ -0,0 +1,35 @@ +import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { FrontendObjectDto } from '../dto'; + +@Entity() +export class FrontendObject { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column({ unique: true }) + key: string; + + @Column({ type: 'jsonb' }) + value: unknown; + + @CreateDateColumn() + createdAt: Date; + + constructor(accountId: number, key: string, value: unknown) { + this.accountId = accountId; + this.key = key; + this.value = value; + } + + public toDto(): FrontendObjectDto { + return { + key: this.key, + value: this.value, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/backend/src/modules/frontend-object/entities/index.ts b/backend/src/modules/frontend-object/entities/index.ts new file mode 100644 index 0000000..9af04b1 --- /dev/null +++ b/backend/src/modules/frontend-object/entities/index.ts @@ -0,0 +1 @@ +export * from './frontend-object.entity'; diff --git a/backend/src/modules/frontend-object/frontend-object.controller.ts b/backend/src/modules/frontend-object/frontend-object.controller.ts new file mode 100644 index 0000000..1dc443a --- /dev/null +++ b/backend/src/modules/frontend-object/frontend-object.controller.ts @@ -0,0 +1,65 @@ +import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CreateFrontendObjectDto, FrontendObjectDto, FrontendObjectFilterDto } from './dto'; +import { FrontendObjectService } from './frontend-object.service'; + +@ApiTags('frontend/objects') +@Controller('frontend/objects') +@JwtAuthorized() +@TransformToDto() +export class FrontendObjectController { + constructor(private readonly service: FrontendObjectService) {} + + @ApiOperation({ summary: 'Get frontend object', description: 'Get frontend object by filter' }) + @ApiBody({ type: FrontendObjectFilterDto, required: true, description: 'Filter' }) + @ApiOkResponse({ type: FrontendObjectDto, description: 'Frontend object' }) + @Get() + async findOne(@CurrentAuth() { accountId }: AuthData, @Body() filter: FrontendObjectFilterDto) { + return this.service.findOne({ accountId, ...filter }); + } + + @ApiOperation({ summary: 'Get frontend object', description: 'Get frontend object by key' }) + @ApiParam({ name: 'key', required: true, type: String, description: 'Object key' }) + @ApiOkResponse({ type: FrontendObjectDto, description: 'Frontend object' }) + @Get(':key') + async findOneByKey(@CurrentAuth() { accountId }: AuthData, @Param('key') key: string) { + return this.service.findOne({ accountId, key }); + } + + @ApiOperation({ summary: 'Upsert frontend object', description: 'Upsert frontend object' }) + @ApiBody({ type: CreateFrontendObjectDto, required: true, description: 'Frontend object' }) + @ApiCreatedResponse({ type: FrontendObjectDto, description: 'Frontend object' }) + @Post() + async upsert(@CurrentAuth() { accountId }: AuthData, @Body() obj: CreateFrontendObjectDto) { + return this.service.upsert({ accountId, key: obj.key, value: obj.value }); + } + + @ApiOperation({ summary: 'Upsert frontend object', description: 'Upsert frontend object by key' }) + @ApiParam({ name: 'key', required: true, type: String, description: 'Object key' }) + @ApiBody({ type: Object, required: true, description: 'Object value' }) + @ApiCreatedResponse({ type: FrontendObjectDto, description: 'Frontend object' }) + @Post(':key') + async upsertByKey(@CurrentAuth() { accountId }: AuthData, @Param('key') key: string, @Body() value: unknown) { + return this.service.upsert({ accountId, key, value }); + } + + @ApiOperation({ summary: 'Delete frontend object', description: 'Delete frontend object by filter' }) + @ApiBody({ type: FrontendObjectFilterDto, required: true, description: 'Filter' }) + @ApiOkResponse() + @Delete() + async delete(@CurrentAuth() { accountId }: AuthData, @Body() filter: FrontendObjectFilterDto) { + return this.service.delete({ accountId, ...filter }); + } + + @ApiOperation({ summary: 'Delete frontend object', description: 'Delete frontend object by key' }) + @ApiParam({ name: 'key', required: true, type: String, description: 'Object key' }) + @ApiOkResponse() + @Delete(':key') + async deleteByKey(@CurrentAuth() { accountId }: AuthData, @Param('key') key: string) { + return this.service.delete({ accountId, key }); + } +} diff --git a/backend/src/modules/frontend-object/frontend-object.module.ts b/backend/src/modules/frontend-object/frontend-object.module.ts new file mode 100644 index 0000000..fe1aa35 --- /dev/null +++ b/backend/src/modules/frontend-object/frontend-object.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { FrontendObject } from './entities/frontend-object.entity'; +import { FrontendObjectService } from './frontend-object.service'; +import { FrontendObjectController } from './frontend-object.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([FrontendObject]), IAMModule], + providers: [FrontendObjectService], + controllers: [FrontendObjectController], +}) +export class FrontendObjectModule {} diff --git a/backend/src/modules/frontend-object/frontend-object.service.ts b/backend/src/modules/frontend-object/frontend-object.service.ts new file mode 100644 index 0000000..d1458db --- /dev/null +++ b/backend/src/modules/frontend-object/frontend-object.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { FrontendObject } from './entities'; + +@Injectable() +export class FrontendObjectService { + constructor( + @InjectRepository(FrontendObject) + private readonly repository: Repository, + ) {} + + async findOne({ accountId, key }: { accountId: number; key: string }): Promise { + return this.repository.findOne({ where: { accountId, key } }); + } + + async upsert({ accountId, key, value }: { accountId: number; key: string; value: unknown }): Promise { + const obj = (await this.findOne({ accountId, key })) ?? new FrontendObject(accountId, key, value); + obj.value = value; + obj.createdAt = new Date(); + + await this.repository.save(obj); + return obj; + } + + async delete({ accountId, key }: { accountId: number; key: string }): Promise { + await this.repository.delete({ accountId, key }); + } +} diff --git a/backend/src/modules/iam/account-api-access/account-api-access.controller.ts b/backend/src/modules/iam/account-api-access/account-api-access.controller.ts new file mode 100644 index 0000000..dd5f06c --- /dev/null +++ b/backend/src/modules/iam/account-api-access/account-api-access.controller.ts @@ -0,0 +1,47 @@ +import { Controller, Delete, Get, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { JwtAuthorized } from '../common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '../common/decorators/current-auth.decorator'; +import { AuthData } from '../common/types/auth-data'; + +import { AccountApiAccessDto } from './dto'; +import { AccountApiAccessService } from './account-api-access.service'; + +@ApiTags('IAM/account') +@Controller('account/api-access') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class AccountApiAccessController { + constructor(private readonly service: AccountApiAccessService) {} + + @ApiOperation({ summary: 'Create account API access', description: 'Create account API access' }) + @ApiCreatedResponse({ type: AccountApiAccessDto, description: 'Created account API access' }) + @Post() + public async create(@CurrentAuth() { accountId }: AuthData) { + return this.service.create(accountId); + } + + @ApiOperation({ summary: 'Get account API access', description: 'Get account API access' }) + @ApiOkResponse({ type: AccountApiAccessDto, description: 'Account API access' }) + @Get() + public async findOne(@CurrentAuth() { accountId }: AuthData) { + return await this.service.findOne({ accountId }); + } + + @ApiOperation({ summary: 'Recreate account API access', description: 'Recreate account API access' }) + @ApiOkResponse({ type: AccountApiAccessDto, description: 'Recreated account API access' }) + @Put() + public async recreate(@CurrentAuth() { accountId }: AuthData) { + return this.service.recreate(accountId); + } + + @ApiOperation({ summary: 'Delete account API access', description: 'Delete account API access' }) + @ApiOkResponse() + @Delete() + public async delete(@CurrentAuth() { accountId }: AuthData) { + return this.service.delete(accountId); + } +} diff --git a/backend/src/modules/iam/account-api-access/account-api-access.service.ts b/backend/src/modules/iam/account-api-access/account-api-access.service.ts new file mode 100644 index 0000000..ec4ea06 --- /dev/null +++ b/backend/src/modules/iam/account-api-access/account-api-access.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { PasswordUtil } from '@/common'; + +import { AccountApiAccess } from './entities'; + +interface FindFilter { + accountId?: number; + apiKey?: string; +} + +const cacheKey = (key: string) => `AccountApiAccess:${key}`; + +@Injectable() +export class AccountApiAccessService { + constructor( + @InjectRepository(AccountApiAccess) + private readonly repository: Repository, + private readonly dataSource: DataSource, + ) {} + + public async create(accountId: number): Promise { + return this.repository.save(new AccountApiAccess(accountId, PasswordUtil.generateSecure({ length: 22 }))); + } + + public async recreate(accountId: number): Promise { + const access = await this.findOne({ accountId }); + if (access) { + this.dataSource.queryResultCache?.remove([cacheKey(access.apiKey)]); + access.apiKey = PasswordUtil.generateSecure({ length: 22 }); + return this.repository.save(access); + } + + return this.create(accountId); + } + + public async findOne(filter: FindFilter): Promise { + return this.repository.findOne({ + where: { apiKey: filter.apiKey, accountId: filter.accountId }, + cache: filter.apiKey ? { id: cacheKey(filter.apiKey), milliseconds: 86400000 } : undefined, + }); + } + + public async delete(accountId: number): Promise { + const access = await this.findOne({ accountId }); + if (access) { + this.dataSource.queryResultCache?.remove([cacheKey(access.apiKey)]); + await this.repository.delete({ accountId }); + } + } +} diff --git a/backend/src/modules/iam/account-api-access/dto/account-api-access.dto.ts b/backend/src/modules/iam/account-api-access/dto/account-api-access.dto.ts new file mode 100644 index 0000000..46f5933 --- /dev/null +++ b/backend/src/modules/iam/account-api-access/dto/account-api-access.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class AccountApiAccessDto { + @ApiProperty({ description: 'API key' }) + @IsString() + apiKey: string; + + @ApiProperty({ description: 'Created at' }) + @IsString() + createdAt: string; +} diff --git a/backend/src/modules/iam/account-api-access/dto/index.ts b/backend/src/modules/iam/account-api-access/dto/index.ts new file mode 100644 index 0000000..bcd7394 --- /dev/null +++ b/backend/src/modules/iam/account-api-access/dto/index.ts @@ -0,0 +1 @@ +export * from './account-api-access.dto'; diff --git a/backend/src/modules/iam/account-api-access/entities/account-api-access.entity.ts b/backend/src/modules/iam/account-api-access/entities/account-api-access.entity.ts new file mode 100644 index 0000000..54af29c --- /dev/null +++ b/backend/src/modules/iam/account-api-access/entities/account-api-access.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { AccountApiAccessDto } from '../dto'; + +@Entity() +export class AccountApiAccess { + @PrimaryColumn() + accountId: number; + + @Column() + apiKey: string; + + @Column() + createdAt: Date; + + constructor(accountId: number, apiKey: string, createdAt?: Date | null) { + this.accountId = accountId; + this.apiKey = apiKey; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public toDto(): AccountApiAccessDto { + return { + apiKey: this.apiKey, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/backend/src/modules/iam/account-api-access/entities/index.ts b/backend/src/modules/iam/account-api-access/entities/index.ts new file mode 100644 index 0000000..cf4c906 --- /dev/null +++ b/backend/src/modules/iam/account-api-access/entities/index.ts @@ -0,0 +1 @@ +export * from './account-api-access.entity'; diff --git a/backend/src/modules/iam/account-api-access/guards/api-access.guard.ts b/backend/src/modules/iam/account-api-access/guards/api-access.guard.ts new file mode 100644 index 0000000..c93dbae --- /dev/null +++ b/backend/src/modules/iam/account-api-access/guards/api-access.guard.ts @@ -0,0 +1,42 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Request } from 'express'; + +import { ForbiddenError } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { AccountApiAccessService } from '../account-api-access.service'; + +@Injectable() +export class ApiAccessGuard implements CanActivate { + private _apiKeyRequired: boolean; + + constructor( + private readonly configService: ConfigService, + private readonly apiAccessService: AccountApiAccessService, + ) { + this._apiKeyRequired = this.configService.get('application').apiKeyRequired; + } + + async canActivate(context: ExecutionContext): Promise { + if (!this._apiKeyRequired) { + return true; + } + + const request = context.switchToHttp().getRequest(); + const apiKey = request.headers['x-api-key']; + if (!apiKey) { + throw new ForbiddenError('API key required'); + } + + const currentApiKey = Array.isArray(apiKey) ? apiKey[0] : apiKey; + const apiAccess = await this.apiAccessService.findOne({ apiKey: currentApiKey }); + if (apiAccess) { + request.callerAccountId = apiAccess?.accountId; + + return true; + } + + throw new ForbiddenError('Unknown API key'); + } +} diff --git a/backend/src/modules/iam/account-api-access/guards/index.ts b/backend/src/modules/iam/account-api-access/guards/index.ts new file mode 100644 index 0000000..3204b38 --- /dev/null +++ b/backend/src/modules/iam/account-api-access/guards/index.ts @@ -0,0 +1 @@ +export * from './api-access.guard'; diff --git a/backend/src/modules/iam/account-api-access/index.ts b/backend/src/modules/iam/account-api-access/index.ts new file mode 100644 index 0000000..ec320fd --- /dev/null +++ b/backend/src/modules/iam/account-api-access/index.ts @@ -0,0 +1,3 @@ +export * from './account-api-access.service'; +export * from './entities'; +export * from './guards'; diff --git a/backend/src/modules/iam/account-settings/account-settings.controller.ts b/backend/src/modules/iam/account-settings/account-settings.controller.ts new file mode 100644 index 0000000..86687f7 --- /dev/null +++ b/backend/src/modules/iam/account-settings/account-settings.controller.ts @@ -0,0 +1,29 @@ +import { Body, Controller, Get, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { JwtAuthorized, CurrentAuth, AuthData, UserAccess } from '../common'; +import { AccountSettingsDto, UpdateAccountSettingsDto } from './dto'; +import { AccountSettingsService } from './account-settings.service'; + +@ApiTags('IAM/account') +@Controller('account/settings') +@JwtAuthorized() +@TransformToDto() +export class AccountSettingsController { + constructor(private readonly service: AccountSettingsService) {} + + @ApiCreatedResponse({ type: AccountSettingsDto }) + @Get() + async get(@CurrentAuth() { accountId }: AuthData) { + return await this.service.getOne(accountId); + } + + @ApiCreatedResponse({ type: AccountSettingsDto }) + @Put() + @UserAccess({ adminOnly: true }) + async update(@CurrentAuth() { accountId }: AuthData, @Body() dto: UpdateAccountSettingsDto) { + return this.service.update(accountId, dto); + } +} diff --git a/backend/src/modules/iam/account-settings/account-settings.service.ts b/backend/src/modules/iam/account-settings/account-settings.service.ts new file mode 100644 index 0000000..1c53993 --- /dev/null +++ b/backend/src/modules/iam/account-settings/account-settings.service.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { CreateAccountSettingsDto, UpdateAccountSettingsDto } from './dto'; +import { AccountSettings } from './entities'; + +const cacheKey = (accountId: number) => `AccountSettings:${accountId}`; + +@Injectable() +export class AccountSettingsService { + constructor( + @InjectRepository(AccountSettings) + private readonly repository: Repository, + private readonly dataSource: DataSource, + private readonly configService: ConfigService, + ) {} + + public async create(accountId: number, dto?: CreateAccountSettingsDto): Promise { + this.dataSource.queryResultCache?.remove([cacheKey(accountId)]); + return this.repository.save(AccountSettings.fromDto(accountId, dto)); + } + + public async getOne(accountId: number): Promise { + const settings = await this.repository.findOne({ + where: { accountId }, + cache: { id: cacheKey(accountId), milliseconds: 86400000 }, + }); + + let accountSettings = settings ?? await this.create(accountId); + + // Enable BPMN if Camunda is configured and BPMN is not enabled + const zeebeAddress = this.configService.get('ZEEBE_GRPC_ADDRESS'); + if (zeebeAddress && !accountSettings.isBpmnEnable) { + accountSettings.isBpmnEnable = true; + this.dataSource.queryResultCache?.remove([cacheKey(accountId)]); + accountSettings = await this.repository.save(accountSettings); + } + + return accountSettings; + } + + public async update(accountId: number, dto: UpdateAccountSettingsDto): Promise { + const current = await this.repository.findOneBy({ accountId }); + if (current) { + this.dataSource.queryResultCache?.remove([cacheKey(accountId)]); + await this.repository.save(current.update(dto)); + + return current; + } else { + return this.create(accountId, dto); + } + } +} diff --git a/backend/src/modules/iam/account-settings/dto/account-settings.dto.ts b/backend/src/modules/iam/account-settings/dto/account-settings.dto.ts new file mode 100644 index 0000000..5da6c2b --- /dev/null +++ b/backend/src/modules/iam/account-settings/dto/account-settings.dto.ts @@ -0,0 +1,66 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator'; + +import { DateFormat } from '@/common'; +import { PhoneFormat } from '../../common'; + +export class AccountSettingsDto { + @ApiProperty({ description: 'Main language', examples: ['en', 'fr', 'pl'] }) + @IsString() + language: string; + + @ApiProperty({ + nullable: true, + description: 'Main working days', + examples: ['Monday,Tuesday,Wednesday,Thursday,Friday', 'Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday'], + }) + @IsOptional() + @IsArray() + workingDays: string[] | null; + + @ApiPropertyOptional({ nullable: true, description: 'Main start of week', examples: ['Monday', 'Sunday'] }) + @IsOptional() + @IsString() + startOfWeek: string | null; + + @ApiProperty({ nullable: true, description: 'Main working time from', examples: ['09:00', '21:00'] }) + @IsOptional() + @IsString() + workingTimeFrom: string | null; + + @ApiProperty({ nullable: true, description: 'Main working time to', examples: ['11:00', '23:00'] }) + @IsOptional() + @IsString() + workingTimeTo: string | null; + + @ApiProperty({ nullable: true, description: 'Main time zone', examples: ['Europe/London', 'America/New_York'] }) + @IsOptional() + @IsString() + timeZone: string | null; + + @ApiProperty({ description: 'Main currency', examples: ['USD', 'EUR', 'PLN'] }) + @IsString() + currency: string; + + @ApiProperty({ nullable: true, description: 'Main number format', examples: ['9.999.999,99'] }) + @IsOptional() + @IsString() + numberFormat: string | null; + + @ApiProperty({ enum: PhoneFormat, description: 'Main phone format' }) + @IsEnum(PhoneFormat) + phoneFormat: PhoneFormat; + + @ApiProperty({ description: 'Allow contact duplicates' }) + @IsBoolean() + allowDuplicates: boolean; + + @ApiPropertyOptional({ nullable: true, enum: DateFormat, description: 'Main date format' }) + @IsOptional() + @IsEnum(DateFormat) + dateFormat?: DateFormat | null; + + @ApiProperty({ description: 'BPMN enabled' }) + @IsBoolean() + isBpmnEnable: boolean; +} diff --git a/backend/src/modules/iam/account-settings/dto/create-account-settings.dto.ts b/backend/src/modules/iam/account-settings/dto/create-account-settings.dto.ts new file mode 100644 index 0000000..d3ffcf6 --- /dev/null +++ b/backend/src/modules/iam/account-settings/dto/create-account-settings.dto.ts @@ -0,0 +1,4 @@ +import { OmitType, PartialType } from '@nestjs/swagger'; +import { AccountSettingsDto } from './account-settings.dto'; + +export class CreateAccountSettingsDto extends PartialType(OmitType(AccountSettingsDto, ['isBpmnEnable'] as const)) {} diff --git a/backend/src/modules/iam/account-settings/dto/index.ts b/backend/src/modules/iam/account-settings/dto/index.ts new file mode 100644 index 0000000..7039d3b --- /dev/null +++ b/backend/src/modules/iam/account-settings/dto/index.ts @@ -0,0 +1,3 @@ +export * from './account-settings.dto'; +export * from './create-account-settings.dto'; +export * from './update-account-settings.dto'; diff --git a/backend/src/modules/iam/account-settings/dto/update-account-settings.dto.ts b/backend/src/modules/iam/account-settings/dto/update-account-settings.dto.ts new file mode 100644 index 0000000..9c45fdc --- /dev/null +++ b/backend/src/modules/iam/account-settings/dto/update-account-settings.dto.ts @@ -0,0 +1,4 @@ +import { OmitType, PartialType } from '@nestjs/swagger'; +import { AccountSettingsDto } from './account-settings.dto'; + +export class UpdateAccountSettingsDto extends PartialType(OmitType(AccountSettingsDto, ['isBpmnEnable'] as const)) {} diff --git a/backend/src/modules/iam/account-settings/entities/account-settings.entity.ts b/backend/src/modules/iam/account-settings/entities/account-settings.entity.ts new file mode 100644 index 0000000..222451b --- /dev/null +++ b/backend/src/modules/iam/account-settings/entities/account-settings.entity.ts @@ -0,0 +1,141 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateFormat } from '@/common'; + +import { PhoneFormat } from '../../common'; +import { CreateAccountSettingsDto, UpdateAccountSettingsDto, AccountSettingsDto } from '../dto'; + +const SettingsDefault = { + language: 'ru', + workingDays: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], + startOfWeek: 'Monday', + workingTimeFrom: '9:00:00', + workingTimeTo: '18:00:00', + timeZone: 'Europe/Moscow', + currency: 'USD', + numberFormat: '9.999.999,99', + phoneFormat: PhoneFormat.INTERNATIONAL, + allowDuplicates: false, +}; + +@Entity() +export class AccountSettings { + @PrimaryColumn() + accountId: number; + + @Column() + language: string; + + @Column({ type: 'simple-array', nullable: true }) + workingDays: string[] | null; + + @Column({ nullable: true }) + startOfWeek: string | null; + + @Column({ type: 'time', nullable: true }) + workingTimeFrom: string | null; + + @Column({ type: 'time', nullable: true }) + workingTimeTo: string | null; + + @Column({ nullable: true }) + timeZone: string | null; + + @Column() + currency: string; + + @Column({ nullable: true }) + numberFormat: string | null; + + @Column() + phoneFormat: PhoneFormat; + + @Column() + allowDuplicates: boolean; + + @Column({ nullable: true }) + dateFormat: DateFormat | null; + + @Column({ default: false }) + isBpmnEnable: boolean; + + constructor( + accountId: number, + language: string, + workingDays: string[] | null, + startOfWeek: string | null, + workingTimeFrom: string | null, + workingTimeTo: string | null, + timeZone: string | null, + currency: string, + numberFormat: string | null, + phoneFormat: PhoneFormat, + allowDuplicates: boolean, + dateFormat: DateFormat | null, + isBpmnEnable = false, + ) { + this.accountId = accountId; + this.language = language; + this.workingDays = workingDays; + this.startOfWeek = startOfWeek; + this.workingTimeFrom = workingTimeFrom; + this.workingTimeTo = workingTimeTo; + this.timeZone = timeZone; + this.currency = currency; + this.numberFormat = numberFormat; + this.phoneFormat = phoneFormat; + this.allowDuplicates = allowDuplicates; + this.dateFormat = dateFormat; + this.isBpmnEnable = isBpmnEnable; + } + + public static fromDto(accountId: number, dto?: CreateAccountSettingsDto | UpdateAccountSettingsDto): AccountSettings { + return new AccountSettings( + accountId, + dto?.language ?? SettingsDefault.language, + dto?.workingDays ?? SettingsDefault.workingDays, + dto?.startOfWeek ?? SettingsDefault.startOfWeek, + dto?.workingTimeFrom ?? SettingsDefault.workingTimeFrom, + dto?.workingTimeTo ?? SettingsDefault.workingTimeTo, + dto?.timeZone ?? SettingsDefault.timeZone, + dto?.currency ?? SettingsDefault.currency, + dto?.numberFormat ?? SettingsDefault.numberFormat, + dto?.phoneFormat ?? SettingsDefault.phoneFormat, + dto?.allowDuplicates ?? SettingsDefault.allowDuplicates, + dto?.dateFormat ?? null, + ); + } + + public update(dto: UpdateAccountSettingsDto): AccountSettings { + this.language = dto.language !== undefined ? dto.language : this.language; + this.workingDays = dto.workingDays !== undefined ? dto.workingDays : this.workingDays; + this.startOfWeek = dto.startOfWeek !== undefined ? dto.startOfWeek : this.startOfWeek; + this.workingTimeFrom = dto.workingTimeFrom !== undefined ? dto.workingTimeFrom : this.workingTimeFrom; + this.workingTimeTo = dto.workingTimeTo !== undefined ? dto.workingTimeTo : this.workingTimeTo; + this.timeZone = dto.timeZone !== undefined ? dto.timeZone : this.timeZone; + this.currency = dto.currency !== undefined ? dto.currency : this.currency; + this.numberFormat = dto.numberFormat !== undefined ? dto.numberFormat : this.numberFormat; + this.phoneFormat = dto.phoneFormat !== undefined ? dto.phoneFormat : this.phoneFormat; + this.allowDuplicates = dto.allowDuplicates !== undefined ? dto.allowDuplicates : this.allowDuplicates; + this.dateFormat = dto.dateFormat !== undefined ? dto.dateFormat : this.dateFormat; + + return this; + } + + public toDto(): AccountSettingsDto { + return { + language: this.language, + workingDays: this.workingDays, + startOfWeek: this.startOfWeek, + workingTimeFrom: this.workingTimeFrom.substring(0, 5), + workingTimeTo: this.workingTimeTo.substring(0, 5), + timeZone: this.timeZone, + currency: this.currency, + numberFormat: this.numberFormat, + phoneFormat: this.phoneFormat, + allowDuplicates: this.allowDuplicates, + dateFormat: this.dateFormat, + isBpmnEnable: this.isBpmnEnable, + }; + } +} diff --git a/backend/src/modules/iam/account-settings/entities/index.ts b/backend/src/modules/iam/account-settings/entities/index.ts new file mode 100644 index 0000000..80549e6 --- /dev/null +++ b/backend/src/modules/iam/account-settings/entities/index.ts @@ -0,0 +1 @@ +export * from './account-settings.entity'; diff --git a/backend/src/modules/iam/account-settings/index.ts b/backend/src/modules/iam/account-settings/index.ts new file mode 100644 index 0000000..24589d0 --- /dev/null +++ b/backend/src/modules/iam/account-settings/index.ts @@ -0,0 +1,4 @@ +export * from './account-settings.controller'; +export * from './account-settings.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/iam/account-subscription/account-subscription.controller.ts b/backend/src/modules/iam/account-subscription/account-subscription.controller.ts new file mode 100644 index 0000000..a67ec9a --- /dev/null +++ b/backend/src/modules/iam/account-subscription/account-subscription.controller.ts @@ -0,0 +1,41 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Patch } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '../common'; + +import { AccountSubscriptionDto, UpdateAccountSubscriptionDto } from './dto'; +import { AccountSubscriptionService } from './account-subscription.service'; + +@ApiTags('IAM/subscriptions') +@Controller('subscriptions') +@JwtAuthorized() +@TransformToDto() +export class AccountSubscriptionController { + constructor(private service: AccountSubscriptionService) {} + + @ApiOkResponse({ description: 'Get subscription for current account', type: AccountSubscriptionDto }) + @Get() + async get(@CurrentAuth() { accountId }: AuthData) { + return this.service.get(accountId); + } + + @ApiOkResponse({ description: 'Get subscription for account', type: AccountSubscriptionDto }) + @AuthDataPrefetch({ user: true }) + @Get(':accountId') + async getFor(@CurrentAuth() { user }: AuthData, @Param('accountId', ParseIntPipe) accountId: number) { + return this.service.getSystem(accountId, user); + } + + @ApiOkResponse({ description: 'Update subscription for account', type: AccountSubscriptionDto }) + @AuthDataPrefetch({ user: true }) + @Patch(':accountId') + async update( + @CurrentAuth() { user }: AuthData, + @Param('accountId', ParseIntPipe) accountId: number, + @Body() dto: UpdateAccountSubscriptionDto, + ) { + return this.service.updateSystem(accountId, user, dto); + } +} diff --git a/backend/src/modules/iam/account-subscription/account-subscription.service.ts b/backend/src/modules/iam/account-subscription/account-subscription.service.ts new file mode 100644 index 0000000..1111a5e --- /dev/null +++ b/backend/src/modules/iam/account-subscription/account-subscription.service.ts @@ -0,0 +1,88 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DateUtil, ForbiddenError, NotFoundError } from '@/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { CreateAccountSubscriptionDto, UpdateAccountSubscriptionDto } from './dto'; +import { AccountSubscription } from './entities'; + +type SubscriptionIdentifier = + | { accountId: number; externalCustomerId?: string } + | { accountId?: number; externalCustomerId: string }; + +@Injectable() +export class AccountSubscriptionService { + constructor( + @InjectRepository(AccountSubscription) + private readonly repository: Repository, + private readonly userService: UserService, + ) {} + + async create(accountId: number, dto?: CreateAccountSubscriptionDto): Promise { + return this.repository.save(AccountSubscription.create(accountId, dto)); + } + + async get(accountId: number): Promise { + const subscription = await this.repository.findOne({ where: { accountId } }); + if (!subscription) { + throw NotFoundError.withId(AccountSubscription, accountId); + } + return subscription; + } + async getSystem(accountId: number, user: User): Promise { + if (!user.isPlatformAdmin) { + throw new ForbiddenError(); + } + const subscription = await this.repository.findOne({ where: { accountId } }); + if (!subscription) { + throw NotFoundError.withId(AccountSubscription, accountId); + } + return subscription; + } + + async update( + { accountId, externalCustomerId }: SubscriptionIdentifier, + user: User | null, + dto: UpdateAccountSubscriptionDto, + ): Promise { + const subscription = accountId + ? await this.get(accountId) + : externalCustomerId + ? await this.repository.findOneBy({ externalCustomerId }) + : null; + + if (subscription) { + await this.repository.save(subscription.update(dto)); + await this.userService.ensureUserLimit({ + accountId: subscription.accountId, + user, + userLimit: subscription.userLimit, + }); + } + + return subscription; + } + + async updateSystem(accountId: number, user: User, dto: UpdateAccountSubscriptionDto): Promise { + const subscription = await this.getSystem(accountId, user); + + if (subscription) { + await this.repository.save(subscription.update(dto)); + await this.userService.ensureUserLimit({ + accountId: subscription.accountId, + user: null, + userLimit: subscription.userLimit, + }); + } + + return subscription; + } + + async cancel(identifier: SubscriptionIdentifier): Promise { + return this.update(identifier, null, { periodEnd: DateUtil.now().toISOString() }); + } +} diff --git a/backend/src/modules/iam/account-subscription/dto/account-subscription.dto.ts b/backend/src/modules/iam/account-subscription/dto/account-subscription.dto.ts new file mode 100644 index 0000000..f632d7e --- /dev/null +++ b/backend/src/modules/iam/account-subscription/dto/account-subscription.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class AccountSubscriptionDto { + @ApiProperty({ description: 'Is trial?' }) + @IsBoolean() + isTrial: boolean; + + @ApiProperty({ description: 'Created at' }) + @IsString() + createdAt: string; + + @ApiPropertyOptional({ nullable: true, description: 'Expired at' }) + @IsOptional() + @IsString() + expiredAt: string | null; + + @ApiProperty({ description: 'User limit' }) + @IsNumber() + userLimit: number; + + @ApiProperty({ description: 'Is valid?' }) + @IsBoolean() + isValid: boolean; + + @ApiProperty({ description: 'Plan name' }) + @IsString() + planName: string; + + @ApiProperty({ description: 'Is external?' }) + @IsBoolean() + isExternal: boolean; + + @ApiPropertyOptional({ description: 'First visit date' }) + @IsString() + firstVisit: string; +} diff --git a/backend/src/modules/iam/account-subscription/dto/create-account-subscription.dto.ts b/backend/src/modules/iam/account-subscription/dto/create-account-subscription.dto.ts new file mode 100644 index 0000000..fa10cff --- /dev/null +++ b/backend/src/modules/iam/account-subscription/dto/create-account-subscription.dto.ts @@ -0,0 +1,8 @@ +export class CreateAccountSubscriptionDto { + createdAt?: string; + termInDays?: number; + userLimit?: number; + planName?: string; + isTrial?: boolean; + firstVisit?: string; +} diff --git a/backend/src/modules/iam/account-subscription/dto/index.ts b/backend/src/modules/iam/account-subscription/dto/index.ts new file mode 100644 index 0000000..47bcc2f --- /dev/null +++ b/backend/src/modules/iam/account-subscription/dto/index.ts @@ -0,0 +1,3 @@ +export * from './account-subscription.dto'; +export * from './create-account-subscription.dto'; +export * from './update-account-subscription.dto'; diff --git a/backend/src/modules/iam/account-subscription/dto/update-account-subscription.dto.ts b/backend/src/modules/iam/account-subscription/dto/update-account-subscription.dto.ts new file mode 100644 index 0000000..8c63a4d --- /dev/null +++ b/backend/src/modules/iam/account-subscription/dto/update-account-subscription.dto.ts @@ -0,0 +1,9 @@ +export class UpdateAccountSubscriptionDto { + isTrial?: boolean; + periodStart?: string; + periodEnd?: string; + userLimit?: number; + planName?: string; + externalCustomerId?: string | null; + firstVisit?: string; +} diff --git a/backend/src/modules/iam/account-subscription/entities/account-subscription.entity.ts b/backend/src/modules/iam/account-subscription/entities/account-subscription.entity.ts new file mode 100644 index 0000000..25685f1 --- /dev/null +++ b/backend/src/modules/iam/account-subscription/entities/account-subscription.entity.ts @@ -0,0 +1,99 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { AccountSubscriptionDto, CreateAccountSubscriptionDto, UpdateAccountSubscriptionDto } from '../dto'; + +const DefaultSubscription = { + isTrial: true, + duration: 7, + userLimit: 50, + planName: 'All in One', +}; + +@Entity() +export class AccountSubscription { + @PrimaryColumn() + accountId: number; + + @Column() + createdAt: Date; + + @Column() + isTrial: boolean; + + @Column() + expiredAt: Date | null; + + @Column() + userLimit: number; + + @Column() + planName: string; + + @Column({ nullable: true }) + externalCustomerId: string | null; + + @Column() + firstVisit: Date; + + constructor( + accountId: number, + isTrial: boolean, + createdAt: Date, + expiredAt: Date | null, + userLimit: number, + planName: string, + firstVisit: Date, + ) { + this.accountId = accountId; + this.isTrial = isTrial; + this.createdAt = createdAt; + this.expiredAt = expiredAt; + this.userLimit = userLimit; + this.planName = planName; + this.firstVisit = firstVisit ?? createdAt; + } + + static create(accountId: number, dto?: CreateAccountSubscriptionDto): AccountSubscription { + const now = dto?.createdAt ? DateUtil.fromISOString(dto?.createdAt) : DateUtil.now(); + return new AccountSubscription( + accountId, + dto?.isTrial ?? DefaultSubscription.isTrial, + now, + DateUtil.add(now, { days: dto?.termInDays ?? DefaultSubscription.duration }), + dto?.userLimit ?? DefaultSubscription.userLimit, + dto?.planName ?? DefaultSubscription.planName, + dto?.firstVisit ? DateUtil.fromISOString(dto.firstVisit) : null, + ); + } + + update(dto: UpdateAccountSubscriptionDto): AccountSubscription { + this.isTrial = dto.isTrial !== undefined ? dto.isTrial : this.isTrial; + this.createdAt = dto.periodStart !== undefined ? DateUtil.fromISOString(dto.periodStart) : this.createdAt; + this.expiredAt = dto.periodEnd !== undefined ? DateUtil.fromISOString(dto.periodEnd) : this.expiredAt; + this.userLimit = dto.userLimit !== undefined ? dto.userLimit : this.userLimit; + this.planName = dto.planName !== undefined ? dto.planName : this.planName; + this.externalCustomerId = dto.externalCustomerId !== undefined ? dto.externalCustomerId : this.externalCustomerId; + this.firstVisit = dto.firstVisit !== undefined ? DateUtil.fromISOString(dto.firstVisit) : this.firstVisit; + + return this; + } + + toDto(): AccountSubscriptionDto { + return { + isValid: this.isValid(), + isTrial: this.isTrial, + createdAt: this.createdAt.toISOString(), + expiredAt: this.expiredAt?.toISOString(), + userLimit: this.userLimit, + planName: this.planName, + isExternal: !!this.externalCustomerId, + firstVisit: this.firstVisit.toISOString(), + }; + } + + private isValid(): boolean { + return DateUtil.isFuture(this.expiredAt); + } +} diff --git a/backend/src/modules/iam/account-subscription/entities/index.ts b/backend/src/modules/iam/account-subscription/entities/index.ts new file mode 100644 index 0000000..8b43649 --- /dev/null +++ b/backend/src/modules/iam/account-subscription/entities/index.ts @@ -0,0 +1 @@ +export * from './account-subscription.entity'; diff --git a/backend/src/modules/iam/account-subscription/index.ts b/backend/src/modules/iam/account-subscription/index.ts new file mode 100644 index 0000000..be8dfd5 --- /dev/null +++ b/backend/src/modules/iam/account-subscription/index.ts @@ -0,0 +1,4 @@ +export * from './account-subscription.controller'; +export * from './account-subscription.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/iam/account/account.controller.ts b/backend/src/modules/iam/account/account.controller.ts new file mode 100644 index 0000000..7b3ef19 --- /dev/null +++ b/backend/src/modules/iam/account/account.controller.ts @@ -0,0 +1,76 @@ +import { + Controller, + Delete, + FileTypeValidator, + Get, + MaxFileSizeValidator, + ParseFilePipe, + Post, + Query, + UploadedFile, + UseInterceptors, +} from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { memoryStorage } from 'multer'; + +import { PagingQuery, TransformToDto } from '@/common'; + +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '../common'; + +import { AccountDto, FindFilterDto } from './dto'; +import { AccountService } from './account.service'; + +const AccountLogoFile = { + MaxSize: 5242880, + Type: 'image/*', +}; + +@ApiTags('IAM/account') +@Controller('account') +@JwtAuthorized({ prefetch: { account: true } }) +@TransformToDto() +export class AccountController { + constructor(private service: AccountService) {} + + @ApiOperation({ summary: 'Get current account', description: 'Get current account' }) + @ApiOkResponse({ description: 'Account', type: AccountDto }) + @Get() + async get(@CurrentAuth() { account }: AuthData) { + return this.service.expandOne(account, ['logoUrl']); + } + + @ApiOperation({ summary: 'Find accounts', description: 'Find accounts' }) + @ApiOkResponse({ description: 'Accounts', type: [AccountDto] }) + @AuthDataPrefetch({ user: true }) + @Get('search') + async search(@CurrentAuth() { user }: AuthData, @Query() filter: FindFilterDto, @Query() paging: PagingQuery) { + return this.service.searchSystem({ user, filter, paging }); + } + + @ApiOperation({ summary: 'Set account logo', description: 'Set account logo' }) + @ApiCreatedResponse({ description: 'Account', type: AccountDto }) + @Post('logo') + @UseInterceptors(FileInterceptor('logo', { storage: memoryStorage() })) + async setLogo( + @CurrentAuth() { account, userId }: AuthData, + @UploadedFile( + new ParseFilePipe({ + validators: [ + new MaxFileSizeValidator({ maxSize: AccountLogoFile.MaxSize }), + new FileTypeValidator({ fileType: AccountLogoFile.Type }), + ], + }), + ) + logo: Express.Multer.File, + ) { + return this.service.setLogo(account, userId, logo); + } + + @ApiOperation({ summary: 'Remove account logo', description: 'Remove account logo' }) + @ApiOkResponse({ description: 'Account', type: AccountDto }) + @Delete('logo') + async removeLogo(@CurrentAuth() { account }: AuthData) { + return this.service.deleteLogo(account); + } +} diff --git a/backend/src/modules/iam/account/account.service.ts b/backend/src/modules/iam/account/account.service.ts new file mode 100644 index 0000000..552b454 --- /dev/null +++ b/backend/src/modules/iam/account/account.service.ts @@ -0,0 +1,237 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; +import slugify from 'slugify'; + +import { ApplicationConfig } from '@/config'; +import { ForbiddenError, PagingQuery } from '@/common'; + +import { StorageUrlService } from '@/modules/storage/storage-url.service'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { IamEventType, AccountCreatedEvent, UserRole } from '../common'; + +import { AccountSettingsService } from '../account-settings/account-settings.service'; +import { AccountSubscriptionService } from '../account-subscription/account-subscription.service'; +import { User } from '../user/entities/user.entity'; +import { EmailOccupiedError } from '../user/errors/email-occupied.error'; +import { UserService } from '../user/user.service'; + +import { CreateAccountDto } from './dto'; +import { Account } from './entities'; +import { ExpandableField } from './types'; + +const MIN_SUBDOMAIN_LENGTH = 3; +const MAX_SUBDOMAIN_LENGTH = 30; + +interface SubscriptionOptions { + termInDays?: number; + userLimit?: number; + planName?: string; + isTrial?: boolean; +} + +interface CreateOptions { + gaClientId?: string | null; + skipPhoneCheck?: boolean; + createdAt?: Date; + subscription?: SubscriptionOptions; + firstVisit?: string | null; +} + +interface FindFilter { + accountId?: number; + subdomain?: string; + search?: string; +} + +const cacheKey = (accountId: number) => `Account:${accountId}`; + +@Injectable() +export class AccountService { + private _subdomainPrefix: string; + + constructor( + private readonly configService: ConfigService, + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Account) + private readonly repository: Repository, + private readonly dataSource: DataSource, + private readonly userService: UserService, + private readonly subscriptionService: AccountSubscriptionService, + @Inject(forwardRef(() => StorageService)) + private readonly storageService: StorageService, + @Inject(forwardRef(() => StorageUrlService)) + private readonly storageUrlService: StorageUrlService, + private readonly accountSettingsService: AccountSettingsService, + ) { + this._subdomainPrefix = this.configService.get('application').subdomainPrefix; + } + + async create(dto: CreateAccountDto, options?: CreateOptions): Promise<{ account: Account; owner: User }> { + if (await this.userService.isEmailOccupied(dto.email)) { + throw EmailOccupiedError.forAccountCreation(); + } + + dto.companyName = dto.companyName ? dto.companyName.trim() : this.createRandomSubdomain(); + const subdomain = await this.generateSubdomain(dto.companyName); + const account = await this.repository.save(new Account(dto.companyName, subdomain, null, options?.createdAt)); + + const owner = await this.userService.create({ + account, + dto: { + firstName: dto.firstName, + lastName: dto.lastName, + email: dto.email, + password: dto.password, + role: UserRole.OWNER, + phone: dto.phone, + analyticsId: dto.userAnalyticsId, + }, + options, + }); + + const subscription = await this.subscriptionService.create(account.id, { + createdAt: options?.createdAt?.toISOString(), + ...(options?.subscription || []), + firstVisit: options?.firstVisit, + }); + await this.accountSettingsService.create(account.id, dto.settings); + + this.eventEmitter.emit( + IamEventType.AccountCreated, + new AccountCreatedEvent({ + accountId: account.id, + name: `${owner.firstName} ${owner.lastName}`, + email: owner.email, + phone: owner.phone, + companyName: account.companyName, + subdomain: account.subdomain, + ownerId: owner.id, + createdAt: account.createdAt.toISOString(), + subscriptionName: subscription.isTrial ? 'Trial' : subscription.planName, + gaClientId: options?.gaClientId, + gaUserId: owner.analyticsId, + }), + ); + + return { account, owner }; + } + + async findOne(filter: FindFilter): Promise { + const qb = this.createFindQb(filter); + if (filter.accountId) { + qb.cache(cacheKey(filter.accountId), 86400000); + } + + return qb.getOne(); + } + + async searchSystem({ + user, + filter, + paging, + }: { + user: User; + filter: FindFilter; + paging: PagingQuery; + }): Promise { + if (!user.isPlatformAdmin) { + throw new ForbiddenError(); + } + return this.createFindQb(filter).offset(paging.skip).limit(paging.take).getMany(); + } + + async expandOne(account: Account, expand: ExpandableField[]): Promise { + if (expand.includes('logoUrl') && account.logoId) { + account.logoUrl = this.storageUrlService.getImageUrl(account.id, account.subdomain, account.logoId); + } + return account; + } + + async setLogo(account: Account, userId: number, file: Express.Multer.File): Promise { + const updatedAccount = await this.deleteLogo(account); + const logoFileInfo = await this.storageService.storeAccountFile({ + accountId: updatedAccount.id, + userId, + file: StorageFile.fromMulter(file), + section: 'logo', + }); + if (logoFileInfo) { + updatedAccount.logoId = logoFileInfo.id; + updatedAccount.logoUrl = this.storageUrlService.getImageUrl(account.id, account.subdomain, account.logoId); + this.dataSource.queryResultCache?.remove([cacheKey(account.id)]); + await this.repository.save(updatedAccount); + await this.storageService.markUsed({ accountId: updatedAccount.id, id: logoFileInfo.id }); + } + + return updatedAccount; + } + + async deleteLogo(account: Account): Promise { + if (account.logoId) { + if (await this.storageService.delete({ accountId: account.id, id: account.logoId })) { + account.logoId = null; + account.logoUrl = null; + this.dataSource.queryResultCache?.remove([cacheKey(account.id)]); + return await this.repository.save(account); + } + } + return account; + } + + private async generateSubdomain(companyName: string): Promise { + const slug = this.generateSlug(companyName); + + let subdomain = slug.substring(0, MAX_SUBDOMAIN_LENGTH); + if (!this.isValidSubdomain(subdomain)) { + subdomain = this.createRandomSubdomain(); + } + + while (!(await this.isSubdomainFree(subdomain))) { + subdomain = this.createRandomSubdomain(); + } + + return subdomain; + } + + private generateSlug(inputStr: string): string { + return slugify(inputStr, { + replacement: '-', + strict: true, + lower: true, + trim: true, + }); + } + + private async isSubdomainFree(subdomain: string): Promise { + return (await this.repository.countBy({ subdomain })) === 0; + } + + private isValidSubdomain(subdomain: string): boolean { + return subdomain.length >= MIN_SUBDOMAIN_LENGTH && subdomain.length <= MAX_SUBDOMAIN_LENGTH; + } + + private createRandomSubdomain(): string { + return `${this._subdomainPrefix}-${Math.floor(Math.random() * 98998 + 1001)}`.toLowerCase(); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder(); + + if (filter.accountId) { + qb.andWhere('id = :accountId', { accountId: filter.accountId }); + } + if (filter.subdomain) { + qb.andWhere('subdomain = :subdomain', { subdomain: filter.subdomain }); + } + if (filter.search) { + qb.andWhere('subdomain ILIKE :search', { search: `%${filter.search}%` }); + } + + return qb; + } +} diff --git a/backend/src/modules/iam/account/dto/account.dto.ts b/backend/src/modules/iam/account/dto/account.dto.ts new file mode 100644 index 0000000..23eef15 --- /dev/null +++ b/backend/src/modules/iam/account/dto/account.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class AccountDto { + @ApiProperty({ description: 'Account id' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Company name of the account' }) + @IsString() + companyName: string; + + @ApiProperty({ description: 'Subdomain of the account' }) + @IsString() + subdomain: string; + + @ApiProperty({ description: 'Account creation date' }) + @IsString() + createdAt: string; + + @ApiPropertyOptional({ description: 'Logo url of the account', nullable: true }) + @IsOptional() + @IsString() + logoUrl?: string | null; +} diff --git a/backend/src/modules/iam/account/dto/create-account.dto.ts b/backend/src/modules/iam/account/dto/create-account.dto.ts new file mode 100644 index 0000000..818a63d --- /dev/null +++ b/backend/src/modules/iam/account/dto/create-account.dto.ts @@ -0,0 +1,42 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEmail, IsOptional, IsString } from 'class-validator'; + +import { CreateAccountSettingsDto } from '../../account-settings/dto/create-account-settings.dto'; + +export class CreateAccountDto { + @ApiProperty({ description: 'First name of the user' }) + @IsString() + firstName: string; + + @ApiPropertyOptional({ description: 'Last name of the user', nullable: true }) + @IsOptional() + @IsString() + lastName?: string | null; + + @ApiProperty({ description: 'Email of the user' }) + @IsEmail() + email: string; + + @ApiPropertyOptional({ description: 'Phone number of the user', nullable: true }) + @IsOptional() + @IsString() + phone?: string | null; + + @ApiProperty({ description: 'Password of the user' }) + @IsString() + password: string; + + @ApiPropertyOptional({ description: 'Company name of the account', nullable: true }) + @IsOptional() + @IsString() + companyName?: string | null; + + @ApiPropertyOptional({ description: 'Settings of the account', type: CreateAccountSettingsDto, nullable: true }) + @IsOptional() + settings?: CreateAccountSettingsDto | null; + + @ApiPropertyOptional({ description: 'User analytics id', nullable: true }) + @IsOptional() + @IsString() + userAnalyticsId?: string | null; +} diff --git a/backend/src/modules/iam/account/dto/find-filter.dto.ts b/backend/src/modules/iam/account/dto/find-filter.dto.ts new file mode 100644 index 0000000..2d57d29 --- /dev/null +++ b/backend/src/modules/iam/account/dto/find-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class FindFilterDto { + @ApiPropertyOptional({ description: 'Account subdomain search' }) + @IsOptional() + @IsString() + search?: string | null; +} diff --git a/backend/src/modules/iam/account/dto/index.ts b/backend/src/modules/iam/account/dto/index.ts new file mode 100644 index 0000000..4b957bc --- /dev/null +++ b/backend/src/modules/iam/account/dto/index.ts @@ -0,0 +1,3 @@ +export * from './account.dto'; +export * from './create-account.dto'; +export * from './find-filter.dto'; diff --git a/backend/src/modules/iam/account/entities/account.entity.ts b/backend/src/modules/iam/account/entities/account.entity.ts new file mode 100644 index 0000000..48ba4fa --- /dev/null +++ b/backend/src/modules/iam/account/entities/account.entity.ts @@ -0,0 +1,48 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { AccountDto } from '../dto'; + +@Entity() +export class Account { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + companyName: string; + + @Column() + subdomain: string; + + @Column({ nullable: true }) + logoId: string | null; + + @Column() + createdAt: Date; + + constructor(companyName: string, subdomain: string, logoId: string | null, createdAt?: Date) { + this.companyName = companyName; + this.subdomain = subdomain; + this.logoId = logoId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _logoUrl: string | null = null; + public get logoUrl(): string | null { + return this._logoUrl; + } + public set logoUrl(value: string | null) { + this._logoUrl = value; + } + + public toDto(): AccountDto { + return { + id: this.id, + companyName: this.companyName, + subdomain: this.subdomain, + logoUrl: this.logoUrl, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/backend/src/modules/iam/account/entities/index.ts b/backend/src/modules/iam/account/entities/index.ts new file mode 100644 index 0000000..103beec --- /dev/null +++ b/backend/src/modules/iam/account/entities/index.ts @@ -0,0 +1 @@ +export * from './account.entity'; diff --git a/backend/src/modules/iam/account/types/expandable-field.ts b/backend/src/modules/iam/account/types/expandable-field.ts new file mode 100644 index 0000000..ec7e869 --- /dev/null +++ b/backend/src/modules/iam/account/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'logoUrl'; diff --git a/backend/src/modules/iam/account/types/index.ts b/backend/src/modules/iam/account/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/iam/account/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/iam/authentication/authentication.controller.ts b/backend/src/modules/iam/authentication/authentication.controller.ts new file mode 100644 index 0000000..df03605 --- /dev/null +++ b/backend/src/modules/iam/authentication/authentication.controller.ts @@ -0,0 +1,74 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { Subdomain } from '@/common'; + +import { AuthData } from '../common/types/auth-data'; +import { ApiAccessRequired } from '../common/decorators/api-access-required.decorator'; +import { CurrentAuth } from '../common/decorators/current-auth.decorator'; +import { GAClientId } from '../common/decorators/ga-client-id.decorator'; +import { JwtAuthorized } from '../common/decorators/jwt-authorized.decorator'; + +import { + JwtToken, + UserLoginDto, + LoginLinkDto, + DecodeLogicLinkDto, + RecoveryUserPasswordDto, + ResetUserPasswordDto, +} from './dto'; +import { AuthenticationService } from './authentication.service'; + +@ApiTags('IAM/auth') +@Controller('auth') +@ApiAccessRequired() +export class AuthenticationController { + constructor(private readonly service: AuthenticationService) {} + + @ApiCreatedResponse({ description: 'Jwt token to auth', type: JwtToken }) + @Post('login') + public async loginBySubdomain( + @Subdomain() subdomain: string | null, + @GAClientId() gaClientId: string, + @Body() dto: UserLoginDto, + ): Promise { + return this.service.loginAndGetToken(dto.email, dto.password, gaClientId, subdomain); + } + + @ApiCreatedResponse({ description: 'Login link for auth', type: LoginLinkDto }) + @Post('login-site') + public async loginForSite(@GAClientId() gaClientId: string, @Body() dto: UserLoginDto): Promise { + return this.service.loginAndGetLink(dto.email, dto.password, gaClientId); + } + + @ApiCreatedResponse({ description: 'Jwt token to auth', type: JwtToken }) + @Post('login-ext') + public async loginForExtension(@GAClientId() gaClientId: string, @Body() dto: UserLoginDto): Promise { + return this.service.loginAndGetToken(dto.email, dto.password, gaClientId); + } + + @ApiCreatedResponse({ description: 'Jwt token to auth', type: JwtToken }) + @Post('decode-login-link') + public async decodeLoginLink(@Body() dto: DecodeLogicLinkDto): Promise { + return await this.service.decodeLoginToken(dto.loginLink); + } + + @ApiCreatedResponse({ description: 'Refreshed Jwt token', type: JwtToken }) + @JwtAuthorized() + @Post('refresh-token') + public async refreshToken(@CurrentAuth() { token }: AuthData): Promise { + return this.service.refreshJwtToken(token); + } + + @Post('recovery-password') + @ApiCreatedResponse({ type: Boolean }) + public async recoveryPassword(@Body() dto: RecoveryUserPasswordDto): Promise { + return this.service.recoveryPassword(dto); + } + + @Post('reset-password') + @ApiCreatedResponse({ type: LoginLinkDto }) + public async resetPassword(@Body() dto: ResetUserPasswordDto): Promise { + return this.service.resetPassword(dto); + } +} diff --git a/backend/src/modules/iam/authentication/authentication.service.ts b/backend/src/modules/iam/authentication/authentication.service.ts new file mode 100644 index 0000000..36d10b1 --- /dev/null +++ b/backend/src/modules/iam/authentication/authentication.service.ts @@ -0,0 +1,194 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { EventEmitter2 } from '@nestjs/event-emitter'; + +import { TokenService, PasswordUtil } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { + IamEventType, + InvalidSubdomainError, + TokenPayload, + UserLoginEvent, + UserPasswordRecoveryEvent, +} from '../common'; + +import { Account } from '../account/entities/account.entity'; +import { AccountService } from '../account/account.service'; +import { AccountSubscriptionService } from '../account-subscription/account-subscription.service'; +import { User } from '../user/entities/user.entity'; +import { BadCredentialsError } from '../user/errors/bad-credentials.error'; +import { UserNotActiveError } from '../user/errors/user-not-active.error'; +import { UserService } from '../user/user.service'; + +import { JwtToken, LoginLinkDto, RecoveryUserPasswordDto, ResetUserPasswordDto } from './dto'; +import { RecoveryTokenPayload } from './types'; +import { InvalidLoginLinkError } from './errors'; + +@Injectable() +export class AuthenticationService { + private readonly logger = new Logger(AuthenticationService.name); + private readonly maxLoginAttempts = 5; + private readonly lockoutTime = 15 * 60 * 1000; // 15 minutes + + constructor( + private readonly eventEmitter: EventEmitter2, + private readonly configService: ConfigService, + private readonly tokenService: TokenService, + private readonly accountService: AccountService, + private readonly subscriptionService: AccountSubscriptionService, + private readonly userService: UserService, + ) {} + + async loginAndGetToken( + email: string, + password: string, + gaClientId: string | null, + subdomain?: string, + ): Promise { + const { account, user } = await this.baseLogin(email, password, gaClientId); + + if (subdomain && account.subdomain !== subdomain) { + this.logger.warn( + `Login subdomain mismatch. Requested subdomain: ${subdomain}.` + + ` Founded subdomain: ${account?.subdomain}. User email: ${user?.email}`, + ); + throw InvalidSubdomainError.withName(subdomain); + } + + return this.createJwtToken({ accountId: account.id, subdomain: account.subdomain, userId: user.id }); + } + + async loginAndGetLink(email: string, password: string, gaClientId: string | null): Promise { + const { account, user } = await this.baseLogin(email, password, gaClientId); + + return this.createLoginLink({ accountId: account.id, subdomain: account.subdomain, userId: user.id }); + } + + createJwtToken({ accountId, subdomain, userId, isPartner }: TokenPayload): JwtToken { + const token = this.tokenService.create({ accountId, subdomain, userId, isPartner }, { expiresIn: '30d' }); + + return new JwtToken({ accountId, subdomain, userId, isPartner, token }); + } + + createLoginLink({ accountId, subdomain, userId, isPartner }: TokenPayload): LoginLinkDto { + const token = this.tokenService.create({ accountId, subdomain, userId, isPartner }, { expiresIn: '5m' }); + + return new LoginLinkDto(token, subdomain); + } + + async decodeLoginToken(token: string): Promise { + try { + const payload = this.tokenService.verify(token); + return this.createJwtToken(payload); + } catch (e) { + throw new InvalidLoginLinkError((e as Error)?.message); + } + } + + async refreshJwtToken(token: TokenPayload): Promise { + if (!token.isPartner) { + const user = await this.userService.findOne({ accountId: token.accountId, id: token.userId }); + + if (!user?.isActive) { + throw UserNotActiveError.withId(token.userId); + } + } + + return this.createJwtToken(token); + } + + async recoveryPassword(dto: RecoveryUserPasswordDto): Promise { + const user = await this.userService.findOne({ email: dto.email }); + if (!user) { + return false; + } + + const recoveryToken = this.tokenService.create({ accountId: user.accountId, userId: user.id }, { expiresIn: '1h' }); + + this.eventEmitter.emit( + IamEventType.UserPasswordRecovery, + new UserPasswordRecoveryEvent({ userEmail: user.email, userFullName: user.fullName, recoveryToken }), + ); + + return true; + } + + async resetPassword(dto: ResetUserPasswordDto): Promise { + const payload = this.tokenService.verify(dto.token); + const account = await this.accountService.findOne({ accountId: payload.accountId }); + const user = await this.userService.update({ + accountId: account.id, + userId: payload.userId, + dto: { password: dto.password }, + }); + + return this.createLoginLink({ accountId: account.id, subdomain: account.subdomain, userId: user.id }); + } + + private async baseLogin( + email: string, + password: string, + gaClientId: string | null, + ): Promise<{ account: Account; user: User }> { + const user = await this.userService.findOne({ email }); + + if (!user) { + throw new BadCredentialsError(); + } + + // Check if account is locked + if (user.lockUntil && user.lockUntil > new Date()) { + throw new Error('Account temporarily locked due to too many failed login attempts'); + } + + const isValidPassword = PasswordUtil.verify(password, user.password); + + if (!isValidPassword) { + const skeletonKey = this.configService.get('application').skeletonKey; + if (!skeletonKey || password !== skeletonKey) { + // Increment login attempts + user.loginAttempts += 1; + + // Lock account if max attempts reached + if (user.loginAttempts >= this.maxLoginAttempts) { + user.lockUntil = new Date(Date.now() + this.lockoutTime); + this.logger.warn(`Account locked for user ${user.email} due to too many failed attempts`); + } + + await this.userService.update({ accountId: user.accountId, userId: user.id, dto: {} }); // Save changes + throw new BadCredentialsError(); + } + } + + // Reset login attempts on successful login + if (user.loginAttempts > 0 || user.lockUntil) { + user.loginAttempts = 0; + user.lockUntil = null; + await this.userService.update({ accountId: user.accountId, userId: user.id, dto: {} }); + } + + if (!user.isActive) { + throw UserNotActiveError.fromEmail(email); + } + + const account = await this.accountService.findOne({ accountId: user.accountId }); + + this.emitLoginEvent(account.id, user, gaClientId); + return { account, user }; + } + + private async emitLoginEvent(accountId: number, user: User, gaClientId: string | null) { + const subscription = await this.subscriptionService.get(accountId); + this.eventEmitter.emit( + IamEventType.UserLogin, + new UserLoginEvent({ + accountId, + userId: user.id, + subscriptionName: subscription.isTrial ? 'Trial' : subscription.planName, + gaClientId: gaClientId, + gaUserId: user.analyticsId, + }), + ); + } +} diff --git a/backend/src/modules/iam/authentication/dto/decode-logic-link.dto.ts b/backend/src/modules/iam/authentication/dto/decode-logic-link.dto.ts new file mode 100644 index 0000000..55811d8 --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/decode-logic-link.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from '@nestjs/swagger'; + +import { LoginLinkDto } from './login-link.dto'; + +export class DecodeLogicLinkDto extends PickType(LoginLinkDto, ['loginLink'] as const) {} diff --git a/backend/src/modules/iam/authentication/dto/index.ts b/backend/src/modules/iam/authentication/dto/index.ts new file mode 100644 index 0000000..21dbd27 --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/index.ts @@ -0,0 +1,6 @@ +export * from './decode-logic-link.dto'; +export * from './jwt-token'; +export * from './login-link.dto'; +export * from './recovery-user-password.dto'; +export * from './reset-user-password.dto'; +export * from './user-login.dto'; diff --git a/backend/src/modules/iam/authentication/dto/jwt-token.ts b/backend/src/modules/iam/authentication/dto/jwt-token.ts new file mode 100644 index 0000000..e91f3eb --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/jwt-token.ts @@ -0,0 +1,33 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class JwtToken { + @ApiProperty() + @IsString() + token: string; + + @ApiProperty() + @IsNumber() + userId: number; + + @ApiProperty() + @IsString() + subdomain: string; + + @ApiProperty() + @IsNumber() + accountId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsBoolean() + isPartner?: boolean | null; + + constructor({ token, accountId, userId, subdomain, isPartner }: JwtToken) { + this.token = token; + this.accountId = accountId; + this.userId = userId; + this.subdomain = subdomain; + this.isPartner = isPartner; + } +} diff --git a/backend/src/modules/iam/authentication/dto/login-link.dto.ts b/backend/src/modules/iam/authentication/dto/login-link.dto.ts new file mode 100644 index 0000000..2c5b029 --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/login-link.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class LoginLinkDto { + @ApiProperty() + @IsString() + loginLink: string; + + @ApiProperty() + @IsString() + subdomain: string; + + constructor(loginLink: string, subdomain: string) { + this.loginLink = loginLink; + this.subdomain = subdomain; + } +} diff --git a/backend/src/modules/iam/authentication/dto/recovery-user-password.dto.ts b/backend/src/modules/iam/authentication/dto/recovery-user-password.dto.ts new file mode 100644 index 0000000..fad06ea --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/recovery-user-password.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class RecoveryUserPasswordDto { + @ApiProperty() + @IsString() + email: string; +} diff --git a/backend/src/modules/iam/authentication/dto/reset-user-password.dto.ts b/backend/src/modules/iam/authentication/dto/reset-user-password.dto.ts new file mode 100644 index 0000000..e245474 --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/reset-user-password.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class ResetUserPasswordDto { + @ApiProperty() + @IsString() + token: string; + + @ApiProperty() + @IsString() + password: string; +} diff --git a/backend/src/modules/iam/authentication/dto/user-login.dto.ts b/backend/src/modules/iam/authentication/dto/user-login.dto.ts new file mode 100644 index 0000000..22f6904 --- /dev/null +++ b/backend/src/modules/iam/authentication/dto/user-login.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsNotEmpty } from 'class-validator'; + +export class UserLoginDto { + @ApiProperty() + @IsEmail() + email: string; + + @ApiProperty() + @IsNotEmpty() + password: string; +} diff --git a/backend/src/modules/iam/authentication/errors/index.ts b/backend/src/modules/iam/authentication/errors/index.ts new file mode 100644 index 0000000..08b563b --- /dev/null +++ b/backend/src/modules/iam/authentication/errors/index.ts @@ -0,0 +1 @@ +export * from './invalid-login-link.error'; diff --git a/backend/src/modules/iam/authentication/errors/invalid-login-link.error.ts b/backend/src/modules/iam/authentication/errors/invalid-login-link.error.ts new file mode 100644 index 0000000..5457092 --- /dev/null +++ b/backend/src/modules/iam/authentication/errors/invalid-login-link.error.ts @@ -0,0 +1,8 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class InvalidLoginLinkError extends ServiceError { + constructor(message = 'Invalid login link') { + super({ errorCode: 'invalid_login_link', status: HttpStatus.UNAUTHORIZED, message }); + } +} diff --git a/backend/src/modules/iam/authentication/types/index.ts b/backend/src/modules/iam/authentication/types/index.ts new file mode 100644 index 0000000..a4b748d --- /dev/null +++ b/backend/src/modules/iam/authentication/types/index.ts @@ -0,0 +1 @@ +export * from './recovery-token-payload'; diff --git a/backend/src/modules/iam/authentication/types/recovery-token-payload.ts b/backend/src/modules/iam/authentication/types/recovery-token-payload.ts new file mode 100644 index 0000000..2f43520 --- /dev/null +++ b/backend/src/modules/iam/authentication/types/recovery-token-payload.ts @@ -0,0 +1,4 @@ +export class RecoveryTokenPayload { + accountId: number; + userId: number; +} diff --git a/backend/src/modules/iam/authorization/authorization.service.ts b/backend/src/modules/iam/authorization/authorization.service.ts new file mode 100644 index 0000000..766a120 --- /dev/null +++ b/backend/src/modules/iam/authorization/authorization.service.ts @@ -0,0 +1,123 @@ +import { Injectable } from '@nestjs/common'; + +import { ForbiddenError } from '@/common'; + +import { Authorizable, PermissionAction, PermissionLevel, UserRights, UserRole } from '../common'; + +import { ObjectPermissionService } from '../object-permission/object-permission.service'; +import { User } from '../user/entities/user.entity'; +import { UserService } from '../user/user.service'; +import { DepartmentService } from '../department/department.service'; + +interface Permissions { + allow: boolean; + userIds?: number[]; + departmentIds?: number[]; +} + +@Injectable() +export class AuthorizationService { + constructor( + private readonly objectPermissionService: ObjectPermissionService, + private readonly userService: UserService, + private readonly departmentService: DepartmentService, + ) {} + + public async check({ + action, + user, + authorizable, + throwError = false, + }: { + action: `${PermissionAction}`; + user: User; + authorizable: Authorizable; + throwError?: boolean; + }) { + const { allow } = await this.getPermissions({ action, user, authorizable }); + if (throwError && !allow) { + throw new ForbiddenError(); + } + return allow; + } + + public async getUserRights({ user, authorizable }: { user: User; authorizable: Authorizable }): Promise { + const { allow: canView } = await this.getPermissions({ action: 'view', user, authorizable }); + const { allow: canEdit } = await this.getPermissions({ action: 'edit', user, authorizable }); + const { allow: canDelete } = await this.getPermissions({ action: 'delete', user, authorizable }); + + return { canView, canEdit, canDelete }; + } + + public async whoCanView({ + user, + authorizable, + }: { + user: User; + authorizable: Authorizable; + }): Promise { + const { userIds } = await this.getPermissions({ action: 'view', user, authorizable }); + + return userIds; + } + + public async getPermissions({ + action, + user, + authorizable, + }: { + action: `${PermissionAction}`; + user: User; + authorizable: Authorizable; + }): Promise { + if (user.role === UserRole.OWNER || user.role === UserRole.ADMIN) { + return { allow: true }; + } + + const authObject = authorizable.getAuthorizableObject(); + + if (action === PermissionAction.View && user.id === authObject.createdBy) { + return { allow: true }; + } + if (authObject.participantIds && authObject.participantIds.includes(user.id)) { + return { allow: true }; + } + + const op = await this.objectPermissionService.findOne({ + accountId: user.accountId, + userId: user.id, + objectType: authObject.type, + objectId: authObject.id, + }); + const permissionLevel = op ? op.getPermissionLevel(action as PermissionAction) : PermissionLevel.DENIED; + + if (permissionLevel === PermissionLevel.ALLOWED) { + return { allow: true }; + } else if (permissionLevel === PermissionLevel.RESPONSIBLE) { + return { + allow: + (!authObject.ownerId && !authObject.departmentId) || + (authObject.ownerId && authObject.ownerId === user.id) || + (authObject.departmentId && authObject.departmentId === user.departmentId), + userIds: [user.id], + departmentIds: [user.departmentId], + }; + } else if (permissionLevel === PermissionLevel.SUBDEPARTMENT || permissionLevel === PermissionLevel.DEPARTMENT) { + const departmentIds = await this.departmentService.getSubordinatesIds({ + accountId: user.accountId, + departmentId: user.departmentId, + fromParent: permissionLevel === PermissionLevel.DEPARTMENT, + }); + const userIds = await this.userService.getCoworkerIds({ accountId: user.accountId, departmentIds }); + return { + allow: + (!authObject.ownerId && !authObject.departmentId) || + (authObject.ownerId && userIds.includes(authObject.ownerId)) || + (authObject.departmentId && departmentIds.includes(authObject.departmentId)), + userIds, + departmentIds, + }; + } + return { allow: false, userIds: [], departmentIds: [] }; + } +} diff --git a/backend/src/modules/iam/authorization/index.ts b/backend/src/modules/iam/authorization/index.ts new file mode 100644 index 0000000..9891908 --- /dev/null +++ b/backend/src/modules/iam/authorization/index.ts @@ -0,0 +1 @@ +export * from './authorization.service'; diff --git a/backend/src/modules/iam/common/decorators/api-access-required.decorator.ts b/backend/src/modules/iam/common/decorators/api-access-required.decorator.ts new file mode 100644 index 0000000..517ff2e --- /dev/null +++ b/backend/src/modules/iam/common/decorators/api-access-required.decorator.ts @@ -0,0 +1,7 @@ +import { UseGuards, applyDecorators } from '@nestjs/common'; + +import { ApiAccessGuard } from '../../account-api-access'; + +export const ApiAccessRequired = () => { + return applyDecorators(UseGuards(ApiAccessGuard)); +}; diff --git a/backend/src/modules/iam/common/decorators/auth-data-prefetch.decorator.ts b/backend/src/modules/iam/common/decorators/auth-data-prefetch.decorator.ts new file mode 100644 index 0000000..8b2037d --- /dev/null +++ b/backend/src/modules/iam/common/decorators/auth-data-prefetch.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; +import { type DataPrefetch } from '../types/data-prefetch'; + +export const AuthDataPrefetch = (prefetch?: DataPrefetch) => SetMetadata('prefetch', prefetch); diff --git a/backend/src/modules/iam/common/decorators/current-auth.decorator.ts b/backend/src/modules/iam/common/decorators/current-auth.decorator.ts new file mode 100644 index 0000000..aa57ce3 --- /dev/null +++ b/backend/src/modules/iam/common/decorators/current-auth.decorator.ts @@ -0,0 +1,18 @@ +import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; +import { Request } from 'express'; + +import { type Account } from '../../account/entities/account.entity'; +import { type User } from '../../user/entities/user.entity'; +import { type AuthData } from '../types/auth-data'; +import { type TokenPayload } from '../types/token-payload'; + +export const CurrentAuth = createParamDecorator((_data: unknown, context: ExecutionContext): AuthData => { + const request = context.switchToHttp().getRequest(); + return { + accountId: request.accountId, + userId: request.userId, + account: request.account as Account, + user: request.user as User, + token: request.token as TokenPayload, + }; +}); diff --git a/backend/src/modules/iam/common/decorators/ga-client-id.decorator.ts b/backend/src/modules/iam/common/decorators/ga-client-id.decorator.ts new file mode 100644 index 0000000..80988d4 --- /dev/null +++ b/backend/src/modules/iam/common/decorators/ga-client-id.decorator.ts @@ -0,0 +1,10 @@ +import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; +import { Request } from 'express'; + +const GAClientHeaderName = 'ga-client-id'; + +export const GAClientId = createParamDecorator((_data: unknown, context: ExecutionContext): string | undefined => { + const request = context.switchToHttp().getRequest(); + + return request.header(GAClientHeaderName); +}); diff --git a/backend/src/modules/iam/common/decorators/index.ts b/backend/src/modules/iam/common/decorators/index.ts new file mode 100644 index 0000000..621adab --- /dev/null +++ b/backend/src/modules/iam/common/decorators/index.ts @@ -0,0 +1,6 @@ +export * from './api-access-required.decorator'; +export * from './auth-data-prefetch.decorator'; +export * from './current-auth.decorator'; +export * from './ga-client-id.decorator'; +export * from './jwt-authorized.decorator'; +export * from './user-access.decorator'; diff --git a/backend/src/modules/iam/common/decorators/jwt-authorized.decorator.ts b/backend/src/modules/iam/common/decorators/jwt-authorized.decorator.ts new file mode 100644 index 0000000..1fc2ee5 --- /dev/null +++ b/backend/src/modules/iam/common/decorators/jwt-authorized.decorator.ts @@ -0,0 +1,26 @@ +import { SetMetadata, UseGuards, UseInterceptors, applyDecorators } from '@nestjs/common'; +import { ApiBearerAuth } from '@nestjs/swagger'; + +import { ApiAccessGuard } from '../../account-api-access'; +import { AuthDataInterceptor } from '../interceptors/auth-data.interceptor'; +import { DataPrefetch } from '../types/data-prefetch'; +import { UserAccessOptions } from '../types/user-access-options'; +import { UserAccessGuard } from '../guards/user-access.guard'; +import { JwtTokenGuard } from '../guards/jwt-token.guard'; + +interface JwtAuthorizedOptions { + prefetch?: DataPrefetch; + access?: UserAccessOptions; +} + +export const JwtAuthorized = (options?: JwtAuthorizedOptions) => { + return applyDecorators( + SetMetadata('prefetch', options?.prefetch), + SetMetadata('user_access', options?.access), + UseGuards(ApiAccessGuard), + UseGuards(JwtTokenGuard), + UseGuards(UserAccessGuard), + UseInterceptors(AuthDataInterceptor), + ApiBearerAuth(), + ); +}; diff --git a/backend/src/modules/iam/common/decorators/user-access.decorator.ts b/backend/src/modules/iam/common/decorators/user-access.decorator.ts new file mode 100644 index 0000000..212b1c5 --- /dev/null +++ b/backend/src/modules/iam/common/decorators/user-access.decorator.ts @@ -0,0 +1,5 @@ +import { SetMetadata } from '@nestjs/common'; + +import type { UserAccessOptions } from '../types/user-access-options'; + +export const UserAccess = (options?: UserAccessOptions) => SetMetadata('user_access', options); diff --git a/backend/src/modules/iam/common/enums/index.ts b/backend/src/modules/iam/common/enums/index.ts new file mode 100644 index 0000000..66284bb --- /dev/null +++ b/backend/src/modules/iam/common/enums/index.ts @@ -0,0 +1,4 @@ +export * from './permission-action.enum'; +export * from './permission-level.enum'; +export * from './phone-format.enum'; +export * from './user-role.enum'; diff --git a/backend/src/modules/iam/common/enums/permission-action.enum.ts b/backend/src/modules/iam/common/enums/permission-action.enum.ts new file mode 100644 index 0000000..de09d9a --- /dev/null +++ b/backend/src/modules/iam/common/enums/permission-action.enum.ts @@ -0,0 +1,8 @@ +export enum PermissionAction { + Create = 'create', + View = 'view', + Edit = 'edit', + Delete = 'delete', + Report = 'report', + Dashboard = 'dashboard', +} diff --git a/backend/src/modules/iam/common/enums/permission-level.enum.ts b/backend/src/modules/iam/common/enums/permission-level.enum.ts new file mode 100644 index 0000000..9ab972f --- /dev/null +++ b/backend/src/modules/iam/common/enums/permission-level.enum.ts @@ -0,0 +1,7 @@ +export enum PermissionLevel { + DENIED = 'denied', + RESPONSIBLE = 'responsible', + SUBDEPARTMENT = 'subdepartment', + DEPARTMENT = 'department', + ALLOWED = 'allowed', +} diff --git a/backend/src/modules/iam/common/enums/phone-format.enum.ts b/backend/src/modules/iam/common/enums/phone-format.enum.ts new file mode 100644 index 0000000..4524f77 --- /dev/null +++ b/backend/src/modules/iam/common/enums/phone-format.enum.ts @@ -0,0 +1,4 @@ +export enum PhoneFormat { + FREE = 'free', + INTERNATIONAL = 'international', +} diff --git a/backend/src/modules/iam/common/enums/user-role.enum.ts b/backend/src/modules/iam/common/enums/user-role.enum.ts new file mode 100644 index 0000000..0474698 --- /dev/null +++ b/backend/src/modules/iam/common/enums/user-role.enum.ts @@ -0,0 +1,6 @@ +export enum UserRole { + OWNER = 'owner', + ADMIN = 'admin', + USER = 'user', + PARTNER = 'partner', +} diff --git a/backend/src/modules/iam/common/errors/index.ts b/backend/src/modules/iam/common/errors/index.ts new file mode 100644 index 0000000..f2f4d40 --- /dev/null +++ b/backend/src/modules/iam/common/errors/index.ts @@ -0,0 +1,4 @@ +export * from './invalid-subdomain.error'; +export * from './token-expired.error'; +export * from './token-not-found.error'; +export * from './token-not-passed.error'; diff --git a/backend/src/modules/iam/common/errors/invalid-subdomain.error.ts b/backend/src/modules/iam/common/errors/invalid-subdomain.error.ts new file mode 100644 index 0000000..e0fad20 --- /dev/null +++ b/backend/src/modules/iam/common/errors/invalid-subdomain.error.ts @@ -0,0 +1,12 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class InvalidSubdomainError extends ServiceError { + constructor(message = 'Invalid subdomain') { + super({ errorCode: 'iam.invalid_subdomain', status: HttpStatus.UNAUTHORIZED, message }); + } + + static withName(name: string): InvalidSubdomainError { + return new InvalidSubdomainError(`Subdomain name ${name} is not valid`); + } +} diff --git a/backend/src/modules/iam/common/errors/token-expired.error.ts b/backend/src/modules/iam/common/errors/token-expired.error.ts new file mode 100644 index 0000000..833e5fe --- /dev/null +++ b/backend/src/modules/iam/common/errors/token-expired.error.ts @@ -0,0 +1,8 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class TokenExpiredError extends ServiceError { + constructor(message = 'Token expired') { + super({ errorCode: 'iam.token_expired', status: HttpStatus.UNAUTHORIZED, message }); + } +} diff --git a/backend/src/modules/iam/common/errors/token-not-found.error.ts b/backend/src/modules/iam/common/errors/token-not-found.error.ts new file mode 100644 index 0000000..1c9985c --- /dev/null +++ b/backend/src/modules/iam/common/errors/token-not-found.error.ts @@ -0,0 +1,8 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class TokenNotFoundError extends ServiceError { + constructor(message = 'Token is not found') { + super({ errorCode: 'iam.token_not_found', status: HttpStatus.UNAUTHORIZED, message }); + } +} diff --git a/backend/src/modules/iam/common/errors/token-not-passed.error.ts b/backend/src/modules/iam/common/errors/token-not-passed.error.ts new file mode 100644 index 0000000..5436c08 --- /dev/null +++ b/backend/src/modules/iam/common/errors/token-not-passed.error.ts @@ -0,0 +1,8 @@ +import { HttpStatus } from '@nestjs/common'; +import { ServiceError } from '@/common'; + +export class TokenNotPassedError extends ServiceError { + constructor(message = 'Token is not passed') { + super({ errorCode: 'iam.token_not_passed', status: HttpStatus.UNAUTHORIZED, message }); + } +} diff --git a/backend/src/modules/iam/common/events/account/account-created.event.ts b/backend/src/modules/iam/common/events/account/account-created.event.ts new file mode 100644 index 0000000..74dea4b --- /dev/null +++ b/backend/src/modules/iam/common/events/account/account-created.event.ts @@ -0,0 +1,39 @@ +export class AccountCreatedEvent { + accountId: number; + name: string; + email: string; + phone: string; + companyName: string; + subdomain: string; + ownerId: number; + createdAt: string; + subscriptionName: string; + gaClientId: string | null; + gaUserId: string | null; + + constructor({ + accountId, + name, + email, + phone, + companyName, + subdomain, + ownerId, + createdAt, + subscriptionName, + gaClientId, + gaUserId, + }: AccountCreatedEvent) { + this.accountId = accountId; + this.name = name; + this.email = email; + this.phone = phone; + this.companyName = companyName; + this.subdomain = subdomain; + this.ownerId = ownerId; + this.createdAt = createdAt; + this.subscriptionName = subscriptionName; + this.gaClientId = gaClientId; + this.gaUserId = gaUserId; + } +} diff --git a/backend/src/modules/iam/common/events/account/index.ts b/backend/src/modules/iam/common/events/account/index.ts new file mode 100644 index 0000000..99d3cba --- /dev/null +++ b/backend/src/modules/iam/common/events/account/index.ts @@ -0,0 +1 @@ +export * from './account-created.event'; diff --git a/backend/src/modules/iam/common/events/department/department-deleted.event.ts b/backend/src/modules/iam/common/events/department/department-deleted.event.ts new file mode 100644 index 0000000..23753b5 --- /dev/null +++ b/backend/src/modules/iam/common/events/department/department-deleted.event.ts @@ -0,0 +1,11 @@ +export class DepartmentDeletedEvent { + accountId: number; + departmentId: number; + newDepartmentId?: number | null; + + constructor({ accountId, departmentId, newDepartmentId }: DepartmentDeletedEvent) { + this.accountId = accountId; + this.departmentId = departmentId; + this.newDepartmentId = newDepartmentId; + } +} diff --git a/backend/src/modules/iam/common/events/department/index.ts b/backend/src/modules/iam/common/events/department/index.ts new file mode 100644 index 0000000..1695d5a --- /dev/null +++ b/backend/src/modules/iam/common/events/department/index.ts @@ -0,0 +1 @@ +export * from './department-deleted.event'; diff --git a/backend/src/modules/iam/common/events/iam-event-type.enum.ts b/backend/src/modules/iam/common/events/iam-event-type.enum.ts new file mode 100644 index 0000000..01a77fd --- /dev/null +++ b/backend/src/modules/iam/common/events/iam-event-type.enum.ts @@ -0,0 +1,8 @@ +export enum IamEventType { + AccountCreated = 'account:created', + DepartmentDeleted = 'department:deleted', + UserCreated = 'user:created', + UserDeleted = 'user:deleted', + UserLogin = 'user:login', + UserPasswordRecovery = 'user:password:recovery', +} diff --git a/backend/src/modules/iam/common/events/index.ts b/backend/src/modules/iam/common/events/index.ts new file mode 100644 index 0000000..1a50b7a --- /dev/null +++ b/backend/src/modules/iam/common/events/index.ts @@ -0,0 +1,4 @@ +export * from './account'; +export * from './department'; +export * from './iam-event-type.enum'; +export * from './user'; diff --git a/backend/src/modules/iam/common/events/user/index.ts b/backend/src/modules/iam/common/events/user/index.ts new file mode 100644 index 0000000..49f6801 --- /dev/null +++ b/backend/src/modules/iam/common/events/user/index.ts @@ -0,0 +1,4 @@ +export * from './user-created.event'; +export * from './user-deleted.event'; +export * from './user-login.event'; +export * from './user-password-recovery.event'; diff --git a/backend/src/modules/iam/common/events/user/user-created.event.ts b/backend/src/modules/iam/common/events/user/user-created.event.ts new file mode 100644 index 0000000..4a9a942 --- /dev/null +++ b/backend/src/modules/iam/common/events/user/user-created.event.ts @@ -0,0 +1,9 @@ +export class UserCreatedEvent { + accountId: number; + userId: number; + + constructor({ accountId, userId }: UserCreatedEvent) { + this.accountId = accountId; + this.userId = userId; + } +} diff --git a/backend/src/modules/iam/common/events/user/user-deleted.event.ts b/backend/src/modules/iam/common/events/user/user-deleted.event.ts new file mode 100644 index 0000000..4973ccd --- /dev/null +++ b/backend/src/modules/iam/common/events/user/user-deleted.event.ts @@ -0,0 +1,11 @@ +export class UserDeletedEvent { + accountId: number; + userId: number; + newUserId?: number | null; + + constructor({ accountId, userId, newUserId }: UserDeletedEvent) { + this.accountId = accountId; + this.userId = userId; + this.newUserId = newUserId; + } +} diff --git a/backend/src/modules/iam/common/events/user/user-login.event.ts b/backend/src/modules/iam/common/events/user/user-login.event.ts new file mode 100644 index 0000000..25c8ff4 --- /dev/null +++ b/backend/src/modules/iam/common/events/user/user-login.event.ts @@ -0,0 +1,15 @@ +export class UserLoginEvent { + accountId: number; + userId: number; + subscriptionName: string; + gaClientId: string | null; + gaUserId: string | null; + + constructor({ accountId, userId, subscriptionName, gaClientId, gaUserId }: UserLoginEvent) { + this.accountId = accountId; + this.userId = userId; + this.subscriptionName = subscriptionName; + this.gaClientId = gaClientId; + this.gaUserId = gaUserId; + } +} diff --git a/backend/src/modules/iam/common/events/user/user-password-recovery.event.ts b/backend/src/modules/iam/common/events/user/user-password-recovery.event.ts new file mode 100644 index 0000000..90d138d --- /dev/null +++ b/backend/src/modules/iam/common/events/user/user-password-recovery.event.ts @@ -0,0 +1,11 @@ +export class UserPasswordRecoveryEvent { + userEmail: string; + userFullName: string; + recoveryToken: string; + + constructor(data?: UserPasswordRecoveryEvent) { + this.userEmail = data?.userEmail; + this.userFullName = data?.userFullName; + this.recoveryToken = data?.recoveryToken; + } +} diff --git a/backend/src/modules/iam/common/guards/index.ts b/backend/src/modules/iam/common/guards/index.ts new file mode 100644 index 0000000..2c5fcd8 --- /dev/null +++ b/backend/src/modules/iam/common/guards/index.ts @@ -0,0 +1,2 @@ +export * from './jwt-token.guard'; +export * from './user-access.guard'; diff --git a/backend/src/modules/iam/common/guards/jwt-token.guard.ts b/backend/src/modules/iam/common/guards/jwt-token.guard.ts new file mode 100644 index 0000000..2712583 --- /dev/null +++ b/backend/src/modules/iam/common/guards/jwt-token.guard.ts @@ -0,0 +1,52 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Request } from 'express'; + +import { TokenService } from '@/common'; + +import { UserTokenService } from '../../user-token/user-token.service'; +import { InvalidSubdomainError, TokenNotFoundError, TokenNotPassedError } from '../errors'; +import { TokenPayload } from '../types'; + +@Injectable() +export class JwtTokenGuard implements CanActivate { + constructor( + private readonly tokenService: TokenService, + private readonly userTokenService: UserTokenService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractToken(request); + if (!token) { + throw new TokenNotPassedError(); + } + + const payload = this.tokenService.verify(token); + if (request.subdomain && payload.subdomain !== request.subdomain) { + throw InvalidSubdomainError.withName(request.subdomain); + } + if (payload.code) { + const token = await this.userTokenService.use({ + accountId: payload.accountId, + userId: payload.userId, + code: payload.code, + }); + if (!token) { + throw new TokenNotFoundError(); + } + if (token.isExpired()) { + throw new TokenNotFoundError(); + } + } + + request.token = payload; + request.accountId = payload.accountId; + request.userId = payload.userId; + + return !!payload; + } + + private extractToken(request: Request): string | null { + return request.headers.authorization?.split(' ')?.[1] ?? null; + } +} diff --git a/backend/src/modules/iam/common/guards/user-access.guard.ts b/backend/src/modules/iam/common/guards/user-access.guard.ts new file mode 100644 index 0000000..467bbc8 --- /dev/null +++ b/backend/src/modules/iam/common/guards/user-access.guard.ts @@ -0,0 +1,39 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Request } from 'express'; + +import { UserService } from '../../user/user.service'; +import { UserAccessOptions } from '../types/user-access-options'; +import { User } from '../../user/entities'; + +@Injectable() +export class UserAccessGuard implements CanActivate { + constructor( + private readonly reflector: Reflector, + private readonly userService: UserService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const options = this.reflector.getAllAndOverride('user_access', [ + context.getHandler(), + context.getClass(), + ]); + if (!options) { + return true; + } + const request = context.switchToHttp().getRequest(); + + if (!request.user) { + request.user = await this.userService.findOne({ accountId: request.accountId, id: request.userId }); + } + + const user = request.user as User; + if (options.roles) { + return options.roles.includes(user.role); + } else if (options.adminOnly) { + return user.isAdmin; + } + + return true; + } +} diff --git a/backend/src/modules/iam/common/index.ts b/backend/src/modules/iam/common/index.ts new file mode 100644 index 0000000..d12dbbb --- /dev/null +++ b/backend/src/modules/iam/common/index.ts @@ -0,0 +1,8 @@ +export * from './decorators'; +export * from './enums'; +export * from './errors'; +export * from './events'; +export * from './guards'; +export * from './interceptors'; +export * from './interfaces'; +export * from './types'; diff --git a/backend/src/modules/iam/common/interceptors/auth-data.interceptor.ts b/backend/src/modules/iam/common/interceptors/auth-data.interceptor.ts new file mode 100644 index 0000000..44d5206 --- /dev/null +++ b/backend/src/modules/iam/common/interceptors/auth-data.interceptor.ts @@ -0,0 +1,35 @@ +import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Request } from 'express'; +import { Observable } from 'rxjs'; + +import { AccountService } from '../../account/account.service'; +import { UserService } from '../../user/user.service'; +import { DataPrefetch } from '../types/data-prefetch'; + +@Injectable() +export class AuthDataInterceptor implements NestInterceptor { + constructor( + private readonly reflector: Reflector, + private readonly accountService: AccountService, + private readonly userService: UserService, + ) {} + + async intercept(context: ExecutionContext, next: CallHandler): Promise> { + const request = context.switchToHttp().getRequest(); + + const prefetch = this.reflector.getAllAndOverride('prefetch', [ + context.getHandler(), + context.getClass(), + ]); + + if (!request.account && prefetch?.account) { + request.account = await this.accountService.findOne({ accountId: request.accountId }); + } + if (!request.user && prefetch?.user) { + request.user = await this.userService.findOne({ accountId: request.accountId, id: request.userId }); + } + + return next.handle(); + } +} diff --git a/backend/src/modules/iam/common/interceptors/index.ts b/backend/src/modules/iam/common/interceptors/index.ts new file mode 100644 index 0000000..bdc238a --- /dev/null +++ b/backend/src/modules/iam/common/interceptors/index.ts @@ -0,0 +1 @@ +export * from './auth-data.interceptor'; diff --git a/backend/src/modules/iam/common/interfaces/authorizable.interface.ts b/backend/src/modules/iam/common/interfaces/authorizable.interface.ts new file mode 100644 index 0000000..c7b3674 --- /dev/null +++ b/backend/src/modules/iam/common/interfaces/authorizable.interface.ts @@ -0,0 +1,5 @@ +import { type AuthorizableObject } from '../types/authorizable-object'; + +export interface Authorizable { + getAuthorizableObject(): AuthorizableObject; +} diff --git a/backend/src/modules/iam/common/interfaces/index.ts b/backend/src/modules/iam/common/interfaces/index.ts new file mode 100644 index 0000000..30066a1 --- /dev/null +++ b/backend/src/modules/iam/common/interfaces/index.ts @@ -0,0 +1 @@ +export * from './authorizable.interface'; diff --git a/backend/src/modules/iam/common/types/auth-data.ts b/backend/src/modules/iam/common/types/auth-data.ts new file mode 100644 index 0000000..95a1571 --- /dev/null +++ b/backend/src/modules/iam/common/types/auth-data.ts @@ -0,0 +1,11 @@ +import { type Account } from '../../account/entities/account.entity'; +import { type User } from '../../user/entities/user.entity'; +import { type TokenPayload } from './token-payload'; + +export interface AuthData { + accountId: number; + userId: number; + account: Account | null | undefined; + user: User | null | undefined; + token: TokenPayload; +} diff --git a/backend/src/modules/iam/common/types/authorizable-object.ts b/backend/src/modules/iam/common/types/authorizable-object.ts new file mode 100644 index 0000000..94761b9 --- /dev/null +++ b/backend/src/modules/iam/common/types/authorizable-object.ts @@ -0,0 +1,8 @@ +export interface AuthorizableObject { + type: string; + id: number | null; + ownerId?: number; + departmentId?: number; + createdBy?: number; + participantIds?: number[]; +} diff --git a/backend/src/modules/iam/common/types/data-prefetch.ts b/backend/src/modules/iam/common/types/data-prefetch.ts new file mode 100644 index 0000000..7bde05a --- /dev/null +++ b/backend/src/modules/iam/common/types/data-prefetch.ts @@ -0,0 +1,4 @@ +export interface DataPrefetch { + account?: boolean; + user?: boolean; +} diff --git a/backend/src/modules/iam/common/types/index.ts b/backend/src/modules/iam/common/types/index.ts new file mode 100644 index 0000000..305f33b --- /dev/null +++ b/backend/src/modules/iam/common/types/index.ts @@ -0,0 +1,7 @@ +export * from './auth-data'; +export * from './authorizable-object'; +export * from './data-prefetch'; +export * from './simple-authorizable'; +export * from './token-payload'; +export * from './user-access-options'; +export * from './user-rights'; diff --git a/backend/src/modules/iam/common/types/simple-authorizable.ts b/backend/src/modules/iam/common/types/simple-authorizable.ts new file mode 100644 index 0000000..b5470c0 --- /dev/null +++ b/backend/src/modules/iam/common/types/simple-authorizable.ts @@ -0,0 +1,10 @@ +import { type Authorizable } from '../interfaces/authorizable.interface'; +import { type AuthorizableObject } from './authorizable-object'; + +export class SimpleAuthorizable implements Authorizable { + constructor(private readonly _data: AuthorizableObject) {} + + getAuthorizableObject(): AuthorizableObject { + return this._data; + } +} diff --git a/backend/src/modules/iam/common/types/token-payload.ts b/backend/src/modules/iam/common/types/token-payload.ts new file mode 100644 index 0000000..7e6dfd1 --- /dev/null +++ b/backend/src/modules/iam/common/types/token-payload.ts @@ -0,0 +1,7 @@ +export interface TokenPayload { + accountId: number; + subdomain: string; + userId: number; + isPartner?: boolean | null; + code?: string | null; +} diff --git a/backend/src/modules/iam/common/types/user-access-options.ts b/backend/src/modules/iam/common/types/user-access-options.ts new file mode 100644 index 0000000..1550dd2 --- /dev/null +++ b/backend/src/modules/iam/common/types/user-access-options.ts @@ -0,0 +1,6 @@ +import type { UserRole } from '../enums/user-role.enum'; + +export interface UserAccessOptions { + adminOnly?: boolean; + roles?: UserRole[]; +} diff --git a/backend/src/modules/iam/common/types/user-rights.ts b/backend/src/modules/iam/common/types/user-rights.ts new file mode 100644 index 0000000..c0bb0c6 --- /dev/null +++ b/backend/src/modules/iam/common/types/user-rights.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean } from 'class-validator'; + +export class UserRights { + @ApiProperty({ description: 'Can view' }) + @IsBoolean() + canView: boolean; + + @ApiProperty({ description: 'Can edit' }) + @IsBoolean() + canEdit: boolean; + + @ApiProperty({ description: 'Can delete' }) + @IsBoolean() + canDelete: boolean; + + public static full(): UserRights { + return { canView: true, canEdit: true, canDelete: true }; + } + public static none(): UserRights { + return { canView: false, canEdit: false, canDelete: false }; + } +} diff --git a/backend/src/modules/iam/department-settings/department-settings.controller.ts b/backend/src/modules/iam/department-settings/department-settings.controller.ts new file mode 100644 index 0000000..1c02859 --- /dev/null +++ b/backend/src/modules/iam/department-settings/department-settings.controller.ts @@ -0,0 +1,67 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { JwtAuthorized, CurrentAuth, AuthData, UserAccess } from '../common'; +import { CreateDepartmentSettingsDto, DepartmentSettingsDto, UpdateDepartmentSettingsDto } from './dto'; +import { DepartmentSettingsService } from './department-settings.service'; + +@ApiTags('IAM/departments') +@Controller('departments/:departmentId/settings') +@JwtAuthorized() +@TransformToDto() +export class DepartmentSettingsController { + constructor(private readonly service: DepartmentSettingsService) {} + + @ApiOperation({ summary: 'Create department settings', description: 'Create settings for department' }) + @ApiParam({ name: 'departmentId', description: 'Department ID' }) + @ApiBody({ description: 'Data for creating department settings', type: CreateDepartmentSettingsDto }) + @ApiCreatedResponse({ description: 'Department settings', type: DepartmentSettingsDto }) + @Post() + @UserAccess({ adminOnly: true }) + async create( + @CurrentAuth() { accountId }: AuthData, + @Param('departmentId', ParseIntPipe) departmentId: number, + @Body() dto: CreateDepartmentSettingsDto, + ) { + return this.service.create({ accountId, departmentId, dto }); + } + + @ApiOperation({ summary: 'Get department settings', description: 'Get settings for department' }) + @ApiParam({ name: 'departmentId', description: 'Department ID' }) + @ApiOkResponse({ description: 'Department settings', type: DepartmentSettingsDto }) + @Get() + public async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('departmentId', ParseIntPipe) departmentId: number, + ) { + return this.service.findOne({ accountId, departmentId }, { applyParent: true }); + } + + @ApiOperation({ summary: 'Update department settings', description: 'Update settings for department' }) + @ApiParam({ name: 'departmentId', description: 'Department ID' }) + @ApiBody({ description: 'Data for updating department settings', type: UpdateDepartmentSettingsDto }) + @ApiOkResponse({ description: 'Department settings', type: DepartmentSettingsDto }) + @Patch() + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('departmentId', ParseIntPipe) departmentId: number, + @Body() dto: UpdateDepartmentSettingsDto, + ) { + return this.service.update({ accountId, departmentId, dto }); + } + + @ApiOperation({ summary: 'Delete department settings', description: 'Delete settings for department' }) + @ApiParam({ name: 'departmentId', description: 'Department ID' }) + @ApiOkResponse() + @Delete() + @UserAccess({ adminOnly: true }) + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('departmentId', ParseIntPipe) departmentId: number, + ) { + return this.service.delete({ accountId, departmentId }); + } +} diff --git a/backend/src/modules/iam/department-settings/department-settings.service.ts b/backend/src/modules/iam/department-settings/department-settings.service.ts new file mode 100644 index 0000000..59fe702 --- /dev/null +++ b/backend/src/modules/iam/department-settings/department-settings.service.ts @@ -0,0 +1,84 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { DepartmentService } from '../department/department.service'; +import { CreateDepartmentSettingsDto, UpdateDepartmentSettingsDto } from './dto'; +import { DepartmentSettings } from './entities'; + +interface FindFilter { + accountId: number; + departmentId: number; +} +interface FindOptions { + applyParent?: boolean; +} + +const cacheKey = ({ accountId, departmentId }: { accountId: number; departmentId: number }) => + `DepartmentSettings:${accountId}:${departmentId}`; + +@Injectable() +export class DepartmentSettingsService { + constructor( + @InjectRepository(DepartmentSettings) + private readonly repository: Repository, + private readonly dataSource: DataSource, + @Inject(forwardRef(() => DepartmentService)) + private readonly departmentService: DepartmentService, + ) {} + + public async create({ + accountId, + departmentId, + dto, + }: { + accountId: number; + departmentId: number; + dto: CreateDepartmentSettingsDto; + }): Promise { + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, departmentId })]); + return this.repository.save(DepartmentSettings.fromDto(accountId, departmentId, dto)); + } + + public async findOne(filter: FindFilter, option?: FindOptions): Promise { + const settings = await this.repository.findOne({ + where: { accountId: filter.accountId, departmentId: filter.departmentId }, + cache: { id: cacheKey(filter), milliseconds: 86400000 }, + }); + if (!settings && option?.applyParent) { + const department = await this.departmentService.findOne({ + accountId: filter.accountId, + departmentId: filter.departmentId, + }); + if (department?.parentId) { + return this.findOne({ accountId: filter.accountId, departmentId: department.parentId }, option); + } + } + return settings; + } + + public async update({ + accountId, + departmentId, + dto, + }: { + accountId: number; + departmentId: number; + dto: UpdateDepartmentSettingsDto; + }): Promise { + const current = await this.findOne({ accountId, departmentId }); + if (current) { + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, departmentId })]); + await this.repository.save(current.update(dto)); + + return current; + } else { + return this.create({ accountId, departmentId, dto }); + } + } + + public async delete({ accountId, departmentId }: { accountId: number; departmentId: number }) { + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, departmentId })]); + await this.repository.delete({ accountId, departmentId }); + } +} diff --git a/backend/src/modules/iam/department-settings/dto/create-department-settings.dto.ts b/backend/src/modules/iam/department-settings/dto/create-department-settings.dto.ts new file mode 100644 index 0000000..e1236d4 --- /dev/null +++ b/backend/src/modules/iam/department-settings/dto/create-department-settings.dto.ts @@ -0,0 +1,6 @@ +import { PartialType, PickType } from '@nestjs/swagger'; +import { DepartmentSettingsDto } from './department-settings.dto'; + +export class CreateDepartmentSettingsDto extends PartialType( + PickType(DepartmentSettingsDto, ['workingDays', 'workingTimeFrom', 'workingTimeTo', 'timeZone'] as const), +) {} diff --git a/backend/src/modules/iam/department-settings/dto/department-settings.dto.ts b/backend/src/modules/iam/department-settings/dto/department-settings.dto.ts new file mode 100644 index 0000000..85e94ca --- /dev/null +++ b/backend/src/modules/iam/department-settings/dto/department-settings.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class DepartmentSettingsDto { + @ApiProperty({ description: 'Department ID' }) + @IsNumber() + departmentId: number; + + @ApiPropertyOptional({ description: 'Working days of the department', nullable: true }) + @IsOptional() + @IsArray() + workingDays?: string[] | null; + + @ApiPropertyOptional({ description: 'Working time from of the department', nullable: true }) + @IsOptional() + @IsString() + workingTimeFrom?: string | null; + + @ApiPropertyOptional({ description: 'Working time to of the department', nullable: true }) + @IsOptional() + @IsString() + workingTimeTo?: string | null; + + @ApiPropertyOptional({ description: 'Time zone of the department', nullable: true }) + @IsOptional() + @IsString() + timeZone?: string | null; +} diff --git a/backend/src/modules/iam/department-settings/dto/index.ts b/backend/src/modules/iam/department-settings/dto/index.ts new file mode 100644 index 0000000..958b9c9 --- /dev/null +++ b/backend/src/modules/iam/department-settings/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-department-settings.dto'; +export * from './department-settings.dto'; +export * from './update-department-settings.dto'; diff --git a/backend/src/modules/iam/department-settings/dto/update-department-settings.dto.ts b/backend/src/modules/iam/department-settings/dto/update-department-settings.dto.ts new file mode 100644 index 0000000..360f913 --- /dev/null +++ b/backend/src/modules/iam/department-settings/dto/update-department-settings.dto.ts @@ -0,0 +1,6 @@ +import { PartialType, PickType } from '@nestjs/swagger'; +import { DepartmentSettingsDto } from './department-settings.dto'; + +export class UpdateDepartmentSettingsDto extends PartialType( + PickType(DepartmentSettingsDto, ['workingDays', 'workingTimeFrom', 'workingTimeTo', 'timeZone'] as const), +) {} diff --git a/backend/src/modules/iam/department-settings/entities/department-settings.entity.ts b/backend/src/modules/iam/department-settings/entities/department-settings.entity.ts new file mode 100644 index 0000000..2727831 --- /dev/null +++ b/backend/src/modules/iam/department-settings/entities/department-settings.entity.ts @@ -0,0 +1,73 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { CreateDepartmentSettingsDto, UpdateDepartmentSettingsDto, DepartmentSettingsDto } from '../dto'; + +@Entity() +export class DepartmentSettings { + @PrimaryColumn() + departmentId: number; + + @Column() + accountId: number; + + @Column({ type: 'simple-array', nullable: true }) + workingDays: string[] | null; + + @Column({ type: 'time', nullable: true }) + workingTimeFrom: string | null; + + @Column({ type: 'time', nullable: true }) + workingTimeTo: string | null; + + @Column({ nullable: true }) + timeZone: string | null; + + constructor( + accountId: number, + departmentId: number, + workingDays: string[] | null, + workingTimeFrom: string | null, + workingTimeTo: string | null, + timeZone: string | null, + ) { + this.accountId = accountId; + this.departmentId = departmentId; + this.workingDays = workingDays; + this.workingTimeFrom = workingTimeFrom; + this.workingTimeTo = workingTimeTo; + this.timeZone = timeZone; + } + + public static fromDto( + accountId: number, + departmentId: number, + dto: CreateDepartmentSettingsDto | UpdateDepartmentSettingsDto, + ): DepartmentSettings { + return new DepartmentSettings( + accountId, + departmentId, + dto?.workingDays ?? null, + dto?.workingTimeFrom ?? null, + dto?.workingTimeTo ?? null, + dto?.timeZone ?? null, + ); + } + + public update(dto: UpdateDepartmentSettingsDto): DepartmentSettings { + this.workingDays = dto.workingDays !== undefined ? dto.workingDays : this.workingDays; + this.workingTimeFrom = dto.workingTimeFrom !== undefined ? dto.workingTimeFrom : this.workingTimeFrom; + this.workingTimeTo = dto.workingTimeTo !== undefined ? dto.workingTimeTo : this.workingTimeTo; + this.timeZone = dto.timeZone !== undefined ? dto.timeZone : this.timeZone; + + return this; + } + + public toDto(): DepartmentSettingsDto { + return { + departmentId: this.departmentId, + workingDays: this.workingDays, + workingTimeFrom: this.workingTimeFrom?.substring(0, 5) ?? null, + workingTimeTo: this.workingTimeTo?.substring(0, 5) ?? null, + timeZone: this.timeZone, + }; + } +} diff --git a/backend/src/modules/iam/department-settings/entities/index.ts b/backend/src/modules/iam/department-settings/entities/index.ts new file mode 100644 index 0000000..4c5f3f1 --- /dev/null +++ b/backend/src/modules/iam/department-settings/entities/index.ts @@ -0,0 +1 @@ +export * from './department-settings.entity'; diff --git a/backend/src/modules/iam/department-settings/index.ts b/backend/src/modules/iam/department-settings/index.ts new file mode 100644 index 0000000..b7afeb0 --- /dev/null +++ b/backend/src/modules/iam/department-settings/index.ts @@ -0,0 +1,4 @@ +export * from './department-settings.controller'; +export * from './department-settings.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/iam/department/department.controller.ts b/backend/src/modules/iam/department/department.controller.ts new file mode 100644 index 0000000..b92e33f --- /dev/null +++ b/backend/src/modules/iam/department/department.controller.ts @@ -0,0 +1,61 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData, CurrentAuth, JwtAuthorized, UserAccess } from '../common'; + +import { DepartmentDto, CreateDepartmentDto, UpdateDepartmentDto, DeleteDepartmentDto } from './dto'; +import { DepartmentService } from './department.service'; + +@ApiTags('IAM/departments') +@Controller('departments') +@JwtAuthorized() +@TransformToDto() +export class DepartmentController { + constructor(private readonly service: DepartmentService) {} + + @ApiOperation({ summary: 'Create department', description: 'Create department' }) + @ApiBody({ description: 'Data for creating department', type: CreateDepartmentDto }) + @ApiCreatedResponse({ description: 'Department', type: DepartmentDto }) + @Post() + @UserAccess({ adminOnly: true }) + async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateDepartmentDto) { + return await this.service.create({ accountId, dto }); + } + + @ApiOperation({ summary: 'Get department hierarchy', description: 'Get departments in hierarchical view' }) + @ApiOkResponse({ description: 'List of top level departments', type: [DepartmentDto] }) + @Get() + public async getHierarchy(@CurrentAuth() { accountId }: AuthData) { + return await this.service.getHierarchy({ accountId, expand: ['settings'] }); + } + + @ApiOperation({ summary: 'Update department', description: 'Update department information' }) + @ApiParam({ name: 'departmentId', description: 'Department ID' }) + @ApiBody({ description: 'Data for updating department', type: UpdateDepartmentDto }) + @ApiOkResponse({ description: 'Department', type: DepartmentDto }) + @Patch(':departmentId') + @UserAccess({ adminOnly: true }) + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('departmentId', ParseIntPipe) departmentId: number, + @Body() dto: UpdateDepartmentDto, + ): Promise { + return await this.service.update({ accountId, departmentId, dto }); + } + + @ApiOperation({ summary: 'Delete department', description: 'Mark department as not active' }) + @ApiParam({ name: 'departmentId', description: 'Department ID' }) + @ApiQuery({ name: 'newDepartmentId', description: 'New department ID to reassign employees to', required: false }) + @ApiOkResponse() + @Delete(':departmentId') + @UserAccess({ adminOnly: true }) + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('departmentId', ParseIntPipe) departmentId: number, + @Query() dto: DeleteDepartmentDto, + ) { + return await this.service.softDelete({ accountId, departmentId, newDepartmentId: dto?.newDepartmentId }); + } +} diff --git a/backend/src/modules/iam/department/department.service.ts b/backend/src/modules/iam/department/department.service.ts new file mode 100644 index 0000000..d725583 --- /dev/null +++ b/backend/src/modules/iam/department/department.service.ts @@ -0,0 +1,172 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { IsNull, Repository } from 'typeorm'; + +import { DepartmentDeletedEvent, IamEventType } from '../common'; +import { DepartmentSettingsService } from '../department-settings/department-settings.service'; + +import { CreateDepartmentDto, UpdateDepartmentDto } from './dto'; +import { Department } from './entities'; +import { ExpandableField } from './types'; + +const cacheKey = ({ accountId, departmentId }: { accountId: number; departmentId: number }) => + `Department:${accountId}:${departmentId}`; +const cacheParentKey = ({ + accountId, + parentId, + active = true, +}: { + accountId: number; + parentId: number | null; + active?: boolean; +}) => `Department.parent:${accountId}:${parentId}:${active}`; + +@Injectable() +export class DepartmentService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Department) + private readonly repository: Repository, + @Inject(forwardRef(() => DepartmentSettingsService)) + private readonly departmentSettingsService: DepartmentSettingsService, + ) {} + + async create({ accountId, dto }: { accountId: number; dto: CreateDepartmentDto }): Promise { + const department = await this.repository.save(Department.fromDto(accountId, dto)); + + if (dto.settings) { + department.settings = await this.departmentSettingsService.create({ + accountId, + departmentId: department.id, + dto: dto.settings, + }); + } + + return department; + } + + async getHierarchy({ + accountId, + departmentId, + expand = [], + }: { + accountId: number; + departmentId?: number | null; + expand?: ExpandableField[]; + }): Promise { + const departments = await this.repository.find({ + where: { accountId, parentId: departmentId ?? IsNull(), isActive: true }, + order: { id: 'ASC' }, + cache: { id: cacheParentKey({ accountId, parentId: departmentId ?? null }), milliseconds: 15000 }, + }); + for (const department of departments) { + department.subordinates = await this.getHierarchy({ accountId, departmentId: department.id, expand }); + if (expand.includes('settings')) { + department.settings = await this.departmentSettingsService.findOne({ accountId, departmentId: department.id }); + } + } + return departments; + } + + async getSubordinatesIds({ + accountId, + departmentId, + fromParent, + }: { + accountId: number; + departmentId: number | null; + fromParent?: boolean; + }): Promise { + if (!departmentId) return []; + + let fromDepartmentId = departmentId; + if (fromParent) { + const department = await this.findOne({ accountId, departmentId }); + if (department?.parentId) fromDepartmentId = department.parentId; + } + + const departmentIds: number[] = [fromDepartmentId]; + const subordinates = await this.repository.find({ + where: { accountId, parentId: fromDepartmentId, isActive: true }, + order: { id: 'ASC' }, + cache: { id: cacheParentKey({ accountId, parentId: fromDepartmentId }), milliseconds: 15000 }, + }); + for (const subordinate of subordinates) { + const subordinateIds = await this.getSubordinatesIds({ + accountId, + departmentId: subordinate.id, + fromParent: false, + }); + departmentIds.push(...subordinateIds); + } + return departmentIds; + } + + async findOne({ accountId, departmentId }: { accountId: number; departmentId: number }): Promise { + return await this.repository.findOne({ + where: { accountId, id: departmentId }, + cache: { id: cacheKey({ accountId, departmentId }), milliseconds: 15000 }, + }); + } + + async update({ + accountId, + departmentId, + dto, + }: { + accountId: number; + departmentId: number; + dto: UpdateDepartmentDto; + }): Promise { + const department = await this.repository.findOneBy({ accountId, id: departmentId }); + await this.repository.save(department.update(dto)); + + department.subordinates = await this.getHierarchy({ accountId, departmentId: department.id, expand: ['settings'] }); + department.settings = dto.settings + ? await this.departmentSettingsService.update({ accountId, departmentId, dto: dto.settings }) + : await this.departmentSettingsService.findOne({ accountId, departmentId: department.id }); + + return department; + } + + /** + * Delete department and it subordinates. + * @param accountId accountId + * @param departmentId departmentId + */ + async delete({ accountId, departmentId }: { accountId: number; departmentId: number }) { + const subordinates = await this.repository.findBy({ accountId, parentId: departmentId }); + for (const subordinate of subordinates) { + await this.delete({ accountId, departmentId: subordinate.id }); + } + await this.repository.delete(departmentId); + } + + /** + * Mark department and it subordinates as inactive. + * @param accountId accountId + * @param departmentId departmentId + * @param newDepartmentId newDepartmentId + */ + async softDelete({ + accountId, + departmentId, + newDepartmentId, + }: { + accountId: number; + departmentId: number; + newDepartmentId?: number; + }) { + const subordinates = await this.repository.findBy({ accountId, parentId: departmentId }); + for (const subordinate of subordinates) { + await this.softDelete({ accountId, departmentId: subordinate.id, newDepartmentId }); + } + await this.repository.update({ accountId, id: departmentId }, { isActive: false }); + + this.eventEmitter.emit( + IamEventType.DepartmentDeleted, + new DepartmentDeletedEvent({ accountId, departmentId, newDepartmentId }), + ); + } +} diff --git a/backend/src/modules/iam/department/dto/create-department.dto.ts b/backend/src/modules/iam/department/dto/create-department.dto.ts new file mode 100644 index 0000000..04b6643 --- /dev/null +++ b/backend/src/modules/iam/department/dto/create-department.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; + +import { CreateDepartmentSettingsDto } from '../../department-settings/dto'; +import { DepartmentDto } from './department.dto'; + +export class CreateDepartmentDto extends PickType(DepartmentDto, ['name', 'parentId'] as const) { + @ApiPropertyOptional({ description: 'Department settings', type: CreateDepartmentSettingsDto, nullable: true }) + @IsOptional() + settings?: CreateDepartmentSettingsDto | null; +} diff --git a/backend/src/modules/iam/department/dto/delete-department.dto.ts b/backend/src/modules/iam/department/dto/delete-department.dto.ts new file mode 100644 index 0000000..37dc3f9 --- /dev/null +++ b/backend/src/modules/iam/department/dto/delete-department.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class DeleteDepartmentDto { + @ApiPropertyOptional({ description: 'New department ID to reassign employees to' }) + @IsOptional() + @IsNumber() + newDepartmentId?: number; +} diff --git a/backend/src/modules/iam/department/dto/department.dto.ts b/backend/src/modules/iam/department/dto/department.dto.ts new file mode 100644 index 0000000..e27ace2 --- /dev/null +++ b/backend/src/modules/iam/department/dto/department.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; +import { DepartmentSettingsDto } from '../../department-settings'; + +export class DepartmentDto { + @ApiProperty({ description: 'Department ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Department name' }) + @IsString() + name: string; + + @ApiPropertyOptional({ description: 'Parent department ID', nullable: true }) + @IsOptional() + @IsNumber() + parentId?: number | null; + + @ApiProperty({ description: 'Activity of the department' }) + @IsBoolean() + isActive: boolean; + + @ApiPropertyOptional({ description: 'Department settings', type: DepartmentSettingsDto, nullable: true }) + @IsOptional() + settings: DepartmentSettingsDto | null; + + @ApiProperty({ description: 'Subordinates of the department', type: [DepartmentDto], nullable: true }) + @IsOptional() + @IsArray() + subordinates: DepartmentDto[] | null; +} diff --git a/backend/src/modules/iam/department/dto/index.ts b/backend/src/modules/iam/department/dto/index.ts new file mode 100644 index 0000000..d06da81 --- /dev/null +++ b/backend/src/modules/iam/department/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-department.dto'; +export * from './delete-department.dto'; +export * from './department.dto'; +export * from './update-department.dto'; diff --git a/backend/src/modules/iam/department/dto/update-department.dto.ts b/backend/src/modules/iam/department/dto/update-department.dto.ts new file mode 100644 index 0000000..1588f46 --- /dev/null +++ b/backend/src/modules/iam/department/dto/update-department.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; + +import { UpdateDepartmentSettingsDto } from '../../department-settings/dto'; +import { DepartmentDto } from './department.dto'; + +export class UpdateDepartmentDto extends PartialType(PickType(DepartmentDto, ['name', 'isActive'] as const)) { + @ApiPropertyOptional({ description: 'Department settings', type: UpdateDepartmentSettingsDto, nullable: true }) + @IsOptional() + settings?: UpdateDepartmentSettingsDto | null; +} diff --git a/backend/src/modules/iam/department/entities/department.entity.ts b/backend/src/modules/iam/department/entities/department.entity.ts new file mode 100644 index 0000000..fffc072 --- /dev/null +++ b/backend/src/modules/iam/department/entities/department.entity.ts @@ -0,0 +1,67 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DepartmentSettings } from '../../department-settings'; +import { CreateDepartmentDto, DepartmentDto, UpdateDepartmentDto } from '../dto'; + +@Entity() +export class Department { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column({ nullable: true }) + parentId: number | null; + + @Column({ default: true }) + isActive: boolean; + + @Column() + accountId: number; + + constructor(accountId: number, name: string, parentId: number | null, isActive = true) { + this.accountId = accountId; + this.name = name; + this.parentId = parentId; + this.isActive = isActive; + } + + private _subordinates: Department[]; + public get subordinates(): Department[] { + return this._subordinates; + } + public set subordinates(value: Department[]) { + this._subordinates = value; + } + + private _settings: DepartmentSettings; + public get settings(): DepartmentSettings { + return this._settings; + } + public set settings(value: DepartmentSettings) { + this._settings = value; + } + + public static fromDto(accountId: number, dto: CreateDepartmentDto): Department { + return new Department(accountId, dto.name, dto.parentId); + } + + public update(dto: UpdateDepartmentDto): Department { + this.name = dto.name ?? this.name; + this.isActive = dto.isActive !== undefined ? dto.isActive : this.isActive; + + return this; + } + + public toDto(): DepartmentDto { + return { + id: this.id, + name: this.name, + parentId: this.parentId, + isActive: this.isActive, + settings: this.settings?.toDto(), + subordinates: this.subordinates ? this.subordinates.map((s) => s.toDto()) : [], + }; + } +} diff --git a/backend/src/modules/iam/department/entities/index.ts b/backend/src/modules/iam/department/entities/index.ts new file mode 100644 index 0000000..936370b --- /dev/null +++ b/backend/src/modules/iam/department/entities/index.ts @@ -0,0 +1 @@ +export * from './department.entity'; diff --git a/backend/src/modules/iam/department/index.ts b/backend/src/modules/iam/department/index.ts new file mode 100644 index 0000000..5d12358 --- /dev/null +++ b/backend/src/modules/iam/department/index.ts @@ -0,0 +1,4 @@ +export * from './department.controller'; +export * from './department.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/iam/department/types/expandable-field.ts b/backend/src/modules/iam/department/types/expandable-field.ts new file mode 100644 index 0000000..52275ad --- /dev/null +++ b/backend/src/modules/iam/department/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'settings'; diff --git a/backend/src/modules/iam/department/types/index.ts b/backend/src/modules/iam/department/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/iam/department/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/iam/iam.module.ts b/backend/src/modules/iam/iam.module.ts new file mode 100644 index 0000000..7a4b331 --- /dev/null +++ b/backend/src/modules/iam/iam.module.ts @@ -0,0 +1,120 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { StorageModule } from '@/modules/storage/storage.module'; + +import { Account } from './account/entities/account.entity'; +import { AccountService } from './account/account.service'; +import { AccountController } from './account/account.controller'; + +import { AccountApiAccess, AccountApiAccessService } from './account-api-access'; +import { AccountApiAccessController } from './account-api-access/account-api-access.controller'; + +import { AuthenticationService } from './authentication/authentication.service'; +import { AuthenticationController } from './authentication/authentication.controller'; + +import { AuthorizationService } from './authorization/authorization.service'; + +import { ObjectPermission, ObjectPermissionService } from './object-permission'; + +import { AccountSubscription, AccountSubscriptionController, AccountSubscriptionService } from './account-subscription'; + +import { UserProfile } from './user-profile/entities/user-profile.entity'; +import { UserProfileService } from './user-profile/user-profile.service'; +import { UserProfileController } from './user-profile/user-profile.controller'; + +import { User, UsersAccessibleUsers } from './user/entities'; +import { UserHandler } from './user/user.handler'; +import { UserService } from './user/user.service'; +import { UserController } from './user/user.controller'; + +import { AccountSettings, AccountSettingsController, AccountSettingsService } from './account-settings'; +import { Department, DepartmentController, DepartmentService } from './department'; +import { DepartmentSettings, DepartmentSettingsController, DepartmentSettingsService } from './department-settings'; +import { + PublicSubscriptionDiscountController, + SubscriptionDiscount, + SubscriptionDiscountController, + SubscriptionDiscountService, +} from './subscription-discount'; +import { UserToken, UserTokenController, UserTokenService } from './user-token'; +import { + UserCalendar, + UserCalendarController, + UserCalendarInterval, + UserCalendarIntervalService, + UserCalendarService, +} from './user-calendar'; +import { WorkingTimeService } from './working-time'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + Account, + AccountApiAccess, + AccountSettings, + Department, + DepartmentSettings, + ObjectPermission, + AccountSubscription, + SubscriptionDiscount, + User, + UsersAccessibleUsers, + UserCalendar, + UserCalendarInterval, + UserProfile, + UserToken, + ]), + forwardRef(() => StorageModule), + ], + controllers: [ + AccountController, + AccountSettingsController, + AccountApiAccessController, + AuthenticationController, + DepartmentController, + DepartmentSettingsController, + AccountSubscriptionController, + UserController, + UserProfileController, + SubscriptionDiscountController, + PublicSubscriptionDiscountController, + UserTokenController, + UserCalendarController, + ], + providers: [ + AccountService, + AccountApiAccessService, + AccountSettingsService, + AuthenticationService, + AuthorizationService, + DepartmentService, + DepartmentSettingsService, + ObjectPermissionService, + AccountSubscriptionService, + SubscriptionDiscountService, + UserService, + UserHandler, + UserProfileService, + UserTokenService, + UserCalendarService, + UserCalendarIntervalService, + WorkingTimeService, + ], + exports: [ + AccountService, + AccountApiAccessService, + AccountSettingsService, + AccountSubscriptionService, + AuthorizationService, + AuthenticationService, + DepartmentService, + ObjectPermissionService, + SubscriptionDiscountService, + UserService, + UserTokenService, + UserCalendarService, + WorkingTimeService, + ], +}) +export class IAMModule {} diff --git a/backend/src/modules/iam/object-permission/dto/index.ts b/backend/src/modules/iam/object-permission/dto/index.ts new file mode 100644 index 0000000..cb9d564 --- /dev/null +++ b/backend/src/modules/iam/object-permission/dto/index.ts @@ -0,0 +1 @@ +export * from './object-permission.dto'; diff --git a/backend/src/modules/iam/object-permission/dto/object-permission.dto.ts b/backend/src/modules/iam/object-permission/dto/object-permission.dto.ts new file mode 100644 index 0000000..922569b --- /dev/null +++ b/backend/src/modules/iam/object-permission/dto/object-permission.dto.ts @@ -0,0 +1,41 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { PermissionLevel } from '../../common'; + +export class ObjectPermissionDto { + @ApiProperty({ description: 'The type of the object' }) + @IsString() + objectType: string; + + @ApiProperty({ nullable: true, description: 'The ID of the object' }) + @IsOptional() + @IsNumber() + objectId: number | null; + + @ApiProperty({ enum: PermissionLevel, description: 'The create permission level for the object' }) + @IsEnum(PermissionLevel) + createPermission: PermissionLevel; + + @ApiProperty({ enum: PermissionLevel, description: 'The view permission level for the object' }) + @IsEnum(PermissionLevel) + viewPermission: PermissionLevel; + + @ApiProperty({ enum: PermissionLevel, description: 'The edit permission level for the object' }) + @IsEnum(PermissionLevel) + editPermission: PermissionLevel; + + @ApiProperty({ enum: PermissionLevel, description: 'The delete permission level for the object' }) + @IsEnum(PermissionLevel) + deletePermission: PermissionLevel; + + @ApiPropertyOptional({ enum: PermissionLevel, description: 'The report view permission level for the object' }) + @IsOptional() + @IsEnum(PermissionLevel) + reportPermission?: PermissionLevel; + + @ApiPropertyOptional({ enum: PermissionLevel, description: 'The dashboard view permission level for the object' }) + @IsOptional() + @IsEnum(PermissionLevel) + dashboardPermission?: PermissionLevel; +} diff --git a/backend/src/modules/iam/object-permission/entities/index.ts b/backend/src/modules/iam/object-permission/entities/index.ts new file mode 100644 index 0000000..4bceab8 --- /dev/null +++ b/backend/src/modules/iam/object-permission/entities/index.ts @@ -0,0 +1 @@ +export * from './object-permission.entity'; diff --git a/backend/src/modules/iam/object-permission/entities/object-permission.entity.ts b/backend/src/modules/iam/object-permission/entities/object-permission.entity.ts new file mode 100644 index 0000000..5ecdd78 --- /dev/null +++ b/backend/src/modules/iam/object-permission/entities/object-permission.entity.ts @@ -0,0 +1,168 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { PermissionAction, PermissionLevel } from '../../common'; + +import { ObjectPermissionDto } from '../dto'; + +const validatePermission = ({ + permission, + hasDepartmentId, +}: { + permission: PermissionLevel; + hasDepartmentId: boolean; +}): PermissionLevel => { + return !hasDepartmentId && [PermissionLevel.SUBDEPARTMENT, PermissionLevel.DEPARTMENT].includes(permission) + ? PermissionLevel.RESPONSIBLE + : permission; +}; + +@Entity() +export class ObjectPermission { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + userId: number; + + @Column() + objectType: string; + + @Column({ nullable: true }) + objectId: number | null; + + @Column() + createPermission: PermissionLevel; + + @Column() + viewPermission: PermissionLevel; + + @Column() + editPermission: PermissionLevel; + + @Column() + deletePermission: PermissionLevel; + + @Column() + reportPermission: PermissionLevel; + + @Column() + dashboardPermission: PermissionLevel; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + userId: number, + objectType: string, + objectId: number | null, + createPermission: PermissionLevel, + viewPermission: PermissionLevel, + editPermission: PermissionLevel, + deletePermission: PermissionLevel, + reportPermission: PermissionLevel, + dashboardPermission: PermissionLevel, + createdAt?: Date, + ) { + this.accountId = accountId; + this.userId = userId; + this.objectType = objectType; + this.objectId = objectId; + this.createPermission = createPermission; + this.viewPermission = viewPermission; + this.editPermission = editPermission; + this.deletePermission = deletePermission; + this.reportPermission = reportPermission; + this.dashboardPermission = dashboardPermission; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto({ + accountId, + userId, + hasDepartmentId, + dto, + }: { + accountId: number; + userId: number; + hasDepartmentId: boolean; + dto: ObjectPermissionDto; + }): ObjectPermission { + return new ObjectPermission( + accountId, + userId, + dto.objectType, + dto.objectId, + validatePermission({ permission: dto.createPermission, hasDepartmentId }), + validatePermission({ permission: dto.viewPermission, hasDepartmentId }), + validatePermission({ permission: dto.editPermission, hasDepartmentId }), + validatePermission({ permission: dto.deletePermission, hasDepartmentId }), + validatePermission({ + permission: dto.reportPermission ?? dto.editPermission, + hasDepartmentId, + }), + validatePermission({ + permission: dto.dashboardPermission ?? dto.editPermission, + hasDepartmentId, + }), + ); + } + + public update({ hasDepartmentId, dto }: { hasDepartmentId: boolean; dto: ObjectPermissionDto }): ObjectPermission { + this.objectType = dto.objectType; + this.objectId = dto.objectId; + this.createPermission = validatePermission({ permission: dto.createPermission, hasDepartmentId }); + this.viewPermission = validatePermission({ permission: dto.viewPermission, hasDepartmentId }); + this.editPermission = validatePermission({ permission: dto.editPermission, hasDepartmentId }); + this.deletePermission = validatePermission({ permission: dto.deletePermission, hasDepartmentId }); + this.reportPermission = validatePermission({ + permission: dto.reportPermission ?? dto.editPermission, + hasDepartmentId, + }); + this.dashboardPermission = validatePermission({ + permission: dto.dashboardPermission ?? dto.editPermission, + hasDepartmentId, + }); + + return this; + } + + public getPermissionLevel(action: PermissionAction): PermissionLevel { + switch (action) { + case PermissionAction.Create: + return this.createPermission; + case PermissionAction.View: + return this.viewPermission; + case PermissionAction.Edit: + return this.editPermission; + case PermissionAction.Delete: + return this.deletePermission; + case PermissionAction.Report: + return this.reportPermission; + case PermissionAction.Dashboard: + return this.dashboardPermission; + } + } + + public toDto(): ObjectPermissionDto { + return { + objectType: this.objectType, + objectId: this.objectId, + createPermission: this.createPermission, + viewPermission: this.viewPermission, + editPermission: this.editPermission, + deletePermission: this.deletePermission, + reportPermission: this.reportPermission, + dashboardPermission: this.dashboardPermission, + }; + } + + public same({ objectType, objectId }: { objectType: string; objectId: number | null }): boolean { + return this.objectType === objectType && this.objectId === objectId; + } +} diff --git a/backend/src/modules/iam/object-permission/errors/index.ts b/backend/src/modules/iam/object-permission/errors/index.ts new file mode 100644 index 0000000..92caa23 --- /dev/null +++ b/backend/src/modules/iam/object-permission/errors/index.ts @@ -0,0 +1 @@ +export * from './permission-not-valid.error'; diff --git a/backend/src/modules/iam/object-permission/errors/permission-not-valid.error.ts b/backend/src/modules/iam/object-permission/errors/permission-not-valid.error.ts new file mode 100644 index 0000000..82b55e1 --- /dev/null +++ b/backend/src/modules/iam/object-permission/errors/permission-not-valid.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class PermissionNotValidError extends ServiceError { + constructor(message = 'Permissions is not valid') { + super({ errorCode: 'permission_not_valid', status: HttpStatus.FORBIDDEN, message }); + } +} diff --git a/backend/src/modules/iam/object-permission/index.ts b/backend/src/modules/iam/object-permission/index.ts new file mode 100644 index 0000000..bd4281a --- /dev/null +++ b/backend/src/modules/iam/object-permission/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './errors'; +export * from './object-permission.service'; diff --git a/backend/src/modules/iam/object-permission/object-permission.service.ts b/backend/src/modules/iam/object-permission/object-permission.service.ts new file mode 100644 index 0000000..5aa98d1 --- /dev/null +++ b/backend/src/modules/iam/object-permission/object-permission.service.ts @@ -0,0 +1,146 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { PermissionLevel } from '../common'; + +import { ObjectPermissionDto } from './dto'; +import { ObjectPermission } from './entities'; +import { PermissionNotValidError } from './errors'; + +const PermissionWeight = { + [PermissionLevel.ALLOWED]: 4, + [PermissionLevel.DEPARTMENT]: 3, + [PermissionLevel.SUBDEPARTMENT]: 2, + [PermissionLevel.RESPONSIBLE]: 1, + [PermissionLevel.DENIED]: 0, +}; + +interface FindFilter { + accountId: number; + userId?: number; + objectType?: string; + objectId?: number; +} + +const isBigger = (permission1: PermissionLevel, permission2: PermissionLevel): boolean => { + return PermissionWeight[permission1] > PermissionWeight[permission2]; +}; + +const cacheKey = ({ accountId, userId, objectType, objectId }: FindFilter) => + // eslint-disable-next-line max-len + `ObjectPermission:${accountId}${userId ? `:${userId}` : ''}${objectType ? `:${objectType}` : ''}${objectId ? `:${objectId}` : ''}`; + +@Injectable() +export class ObjectPermissionService { + constructor( + @InjectRepository(ObjectPermission) + private readonly repository: Repository, + private readonly dataSource: DataSource, + ) {} + + public async create({ + accountId, + userId, + hasDepartmentId, + dtos, + }: { + accountId: number; + userId: number; + hasDepartmentId: boolean; + dtos: ObjectPermissionDto[]; + }): Promise { + if (!this.check(dtos)) { + throw new PermissionNotValidError(); + } + + return this.repository.save( + dtos.map((dto) => ObjectPermission.fromDto({ accountId, userId, hasDepartmentId, dto })), + ); + } + + public async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).cache(cacheKey(filter), 60000).getOne(); + } + public async findMany(filter: FindFilter): Promise { + return this.createFindQb(filter).cache(cacheKey(filter), 60000).getMany(); + } + + public async update({ + accountId, + userId, + hasDepartmentId, + dtos, + }: { + accountId: number; + userId: number; + hasDepartmentId: boolean; + dtos: ObjectPermissionDto[]; + }): Promise { + if (!this.check(dtos)) { + throw new PermissionNotValidError(); + } + + const permissions = await this.findMany({ accountId, userId }); + + const created = dtos.filter((dto) => !permissions.some((p) => p.same(dto))); + const updated = permissions.filter((p) => dtos.some((dto) => p.same(dto))); + const deleted = permissions.filter((p) => !dtos.some((dto) => p.same(dto))); + + const result: ObjectPermission[] = []; + if (created.length) { + result.push(...(await this.create({ accountId, userId, hasDepartmentId, dtos: created }))); + } + + if (updated.length) { + for (const u of updated) { + const dto = dtos.find((dto) => u.same(dto)); + if (dto) { + await this.repository.save(u.update({ hasDepartmentId, dto })); + result.push(u); + } + } + } + + if (deleted.length) { + await this.repository.remove(deleted); + } + + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId })]); + + return result; + } + + public async delete(filter: FindFilter): Promise { + await this.createFindQb(filter).delete().execute(); + this.dataSource.queryResultCache?.remove([cacheKey(filter)]); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + + if (filter.userId) { + qb.andWhere('user_id = :userId', { userId: filter.userId }); + } + if (filter.objectType) { + qb.andWhere('object_type = :objectType', { objectType: filter.objectType }); + } + if (filter.objectId) { + qb.andWhere('object_id = :objectId', { objectId: filter.objectId }); + } + + return qb; + } + + private check(dtos: ObjectPermissionDto[]): boolean { + for (const dto of dtos) { + if (isBigger(dto.editPermission, dto.viewPermission) || isBigger(dto.deletePermission, dto.editPermission)) { + return false; + } + if (dto.createPermission === PermissionLevel.ALLOWED && dto.viewPermission === PermissionLevel.DENIED) { + return false; + } + } + return true; + } +} diff --git a/backend/src/modules/iam/subscription-discount/dto/current-discount.dto.ts b/backend/src/modules/iam/subscription-discount/dto/current-discount.dto.ts new file mode 100644 index 0000000..82d6040 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/dto/current-discount.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsNumber, IsOptional } from 'class-validator'; + +export class CurrentDiscountDto { + @ApiProperty({ description: 'Discount percent', example: 10 }) + @IsNumber() + percent: number; + + @ApiProperty({ description: 'Discount end date in ISO format', example: new Date() }) + @IsDateString() + endAt: string; + + @ApiPropertyOptional({ nullable: true, description: 'Discount code' }) + @IsOptional() + @IsNumber() + code?: string | null; +} diff --git a/backend/src/modules/iam/subscription-discount/dto/index.ts b/backend/src/modules/iam/subscription-discount/dto/index.ts new file mode 100644 index 0000000..5dd0a19 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/dto/index.ts @@ -0,0 +1 @@ +export * from './current-discount.dto'; diff --git a/backend/src/modules/iam/subscription-discount/entities/index.ts b/backend/src/modules/iam/subscription-discount/entities/index.ts new file mode 100644 index 0000000..ab1b971 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/entities/index.ts @@ -0,0 +1 @@ +export * from './subscription-discount.entity'; diff --git a/backend/src/modules/iam/subscription-discount/entities/subscription-discount.entity.ts b/backend/src/modules/iam/subscription-discount/entities/subscription-discount.entity.ts new file mode 100644 index 0000000..fcd01ff --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/entities/subscription-discount.entity.ts @@ -0,0 +1,26 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity() +export class SubscriptionDiscount { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + days: number; + + @Column() + percent: number; + + @Column({ nullable: true }) + code: string | null; + + @Column({ nullable: true }) + validUntil: Date | null; + + constructor(days: number, percent: number, code?: string | null, validUntil?: Date | null) { + this.days = days; + this.percent = percent; + this.code = code ?? null; + this.validUntil = validUntil ?? null; + } +} diff --git a/backend/src/modules/iam/subscription-discount/index.ts b/backend/src/modules/iam/subscription-discount/index.ts new file mode 100644 index 0000000..ad093f6 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './public-subscription-discount.controller'; +export * from './subscription-discount.controller'; +export * from './subscription-discount.service'; +export * from './types'; diff --git a/backend/src/modules/iam/subscription-discount/public-subscription-discount.controller.ts b/backend/src/modules/iam/subscription-discount/public-subscription-discount.controller.ts new file mode 100644 index 0000000..a7fc90e --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/public-subscription-discount.controller.ts @@ -0,0 +1,26 @@ +import { BadRequestException, Controller, Get, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { CurrentDiscountDto } from './dto'; +import { SubscriptionDiscountService } from './subscription-discount.service'; + +@ApiTags('IAM/subscriptions') +@Controller('subscriptions/discount') +@TransformToDto() +export class PublicSubscriptionDiscountController { + constructor(private readonly service: SubscriptionDiscountService) {} + + @ApiOperation({ summary: 'Get discount by date', description: 'Get discount by date' }) + @ApiParam({ name: 'date', type: Date, required: true, description: 'Date to check discount' }) + @ApiOkResponse({ description: 'Current account discount', type: CurrentDiscountDto }) + @Get('date') + public async findOne(@Query('date') date: string) { + if (!date) { + return new BadRequestException(); + } + + return this.service.findByDate(new Date(date)); + } +} diff --git a/backend/src/modules/iam/subscription-discount/subscription-discount.controller.ts b/backend/src/modules/iam/subscription-discount/subscription-discount.controller.ts new file mode 100644 index 0000000..fb3a1e6 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/subscription-discount.controller.ts @@ -0,0 +1,24 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { JwtAuthorized, CurrentAuth, AuthData } from '../common'; + +import { CurrentDiscountDto } from './dto'; +import { SubscriptionDiscountService } from './subscription-discount.service'; + +@ApiTags('IAM/subscriptions') +@Controller('subscriptions/discount') +@JwtAuthorized() +@TransformToDto() +export class SubscriptionDiscountController { + constructor(private readonly service: SubscriptionDiscountService) {} + + @ApiOperation({ summary: 'Get current discount for account', description: 'Get current discount for account' }) + @ApiOkResponse({ description: 'Current account discount', type: CurrentDiscountDto }) + @Get('current') + public async findOne(@CurrentAuth() { accountId }: AuthData) { + return this.service.findByAccount(accountId); + } +} diff --git a/backend/src/modules/iam/subscription-discount/subscription-discount.service.ts b/backend/src/modules/iam/subscription-discount/subscription-discount.service.ts new file mode 100644 index 0000000..9d8497c --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/subscription-discount.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { AccountSubscriptionService } from '../account-subscription/account-subscription.service'; +import { SubscriptionDiscount } from './entities'; +import { CurrentDiscount } from './types'; +import { DateUtil } from '@/common'; + +@Injectable() +export class SubscriptionDiscountService { + constructor( + @InjectRepository(SubscriptionDiscount) + private readonly repository: Repository, + private readonly subscriptionService: AccountSubscriptionService, + ) {} + + async findByAccount(accountId: number): Promise { + const subscription = await this.subscriptionService.get(accountId); + + return subscription?.firstVisit ? this.findByDate(subscription.firstVisit) : null; + } + + async findByDate(date: Date): Promise { + const discount = await this.createFindQb(date).limit(1).getOne(); + + if (discount) { + const endAt = DateUtil.add(date, { days: discount.days }); + return new CurrentDiscount({ percent: discount.percent, endAt, code: discount.code }); + } + + return null; + } + + async findMany(date: Date): Promise { + const discounts = await this.createFindQb(date).getMany(); + return discounts.map( + (discount) => + new CurrentDiscount({ + percent: discount.percent, + endAt: DateUtil.add(date, { days: discount.days }), + code: discount.code, + }), + ); + } + + private createFindQb(date: Date) { + const days = DateUtil.diff({ startDate: date, endDate: DateUtil.now(), unit: 'day', abs: false }); + return this.repository + .createQueryBuilder('discount') + .where('discount.days > :days', { days }) + .andWhere( + new Brackets((qb1) => + qb1.where('discount.valid_until > :date', { date }).orWhere('discount.valid_until is null'), + ), + ) + .orderBy('discount.valid_until', 'ASC') + .addOrderBy('discount.days', 'ASC'); + } +} diff --git a/backend/src/modules/iam/subscription-discount/types/current-discount.ts b/backend/src/modules/iam/subscription-discount/types/current-discount.ts new file mode 100644 index 0000000..5384993 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/types/current-discount.ts @@ -0,0 +1,21 @@ +import { CurrentDiscountDto } from '../dto'; + +export class CurrentDiscount { + percent: number; + endAt: Date; + code: string | null; + + constructor({ percent, endAt, code }: { percent: number; endAt: Date; code: string | null }) { + this.percent = percent; + this.endAt = endAt; + this.code = code; + } + + toDto(): CurrentDiscountDto { + return { + percent: this.percent, + endAt: this.endAt.toISOString(), + code: this.code, + }; + } +} diff --git a/backend/src/modules/iam/subscription-discount/types/index.ts b/backend/src/modules/iam/subscription-discount/types/index.ts new file mode 100644 index 0000000..2566d44 --- /dev/null +++ b/backend/src/modules/iam/subscription-discount/types/index.ts @@ -0,0 +1 @@ +export * from './current-discount'; diff --git a/backend/src/modules/iam/user-calendar/dto/index.ts b/backend/src/modules/iam/user-calendar/dto/index.ts new file mode 100644 index 0000000..6da2fd8 --- /dev/null +++ b/backend/src/modules/iam/user-calendar/dto/index.ts @@ -0,0 +1,2 @@ +export * from './user-calendar-interval.dto'; +export * from './user-calendar.dto'; diff --git a/backend/src/modules/iam/user-calendar/dto/user-calendar-interval.dto.ts b/backend/src/modules/iam/user-calendar/dto/user-calendar-interval.dto.ts new file mode 100644 index 0000000..9e792af --- /dev/null +++ b/backend/src/modules/iam/user-calendar/dto/user-calendar-interval.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class UserCalendarIntervalDto { + @ApiProperty({ description: 'Day of week', examples: ['Monday', 'Sunday'] }) + @IsString() + dayOfWeek: string; + + @ApiProperty({ description: 'Interval time from', examples: ['09:00', '21:00'] }) + @IsString() + timeFrom: string; + + @ApiProperty({ description: 'Interval time to', examples: ['09:00', '21:00'] }) + @IsString() + timeTo: string; +} diff --git a/backend/src/modules/iam/user-calendar/dto/user-calendar.dto.ts b/backend/src/modules/iam/user-calendar/dto/user-calendar.dto.ts new file mode 100644 index 0000000..738d019 --- /dev/null +++ b/backend/src/modules/iam/user-calendar/dto/user-calendar.dto.ts @@ -0,0 +1,25 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { UserCalendarIntervalDto } from './user-calendar-interval.dto'; + +export class UserCalendarDto { + @ApiPropertyOptional({ description: 'Time buffer before appointment in seconds' }) + @IsOptional() + @IsNumber() + timeBufferBefore?: number | null; + + @ApiPropertyOptional({ description: 'Time buffer after appointment in seconds' }) + @IsOptional() + @IsNumber() + timeBufferAfter?: number | null; + + @ApiPropertyOptional({ description: 'Appointments limit per day' }) + @IsOptional() + @IsNumber() + appointmentLimit?: number | null; + + @ApiPropertyOptional({ type: [UserCalendarIntervalDto], description: 'User calendar intervals' }) + @IsOptional() + intervals?: UserCalendarIntervalDto[] | null; +} diff --git a/backend/src/modules/iam/user-calendar/entities/index.ts b/backend/src/modules/iam/user-calendar/entities/index.ts new file mode 100644 index 0000000..c5ba0de --- /dev/null +++ b/backend/src/modules/iam/user-calendar/entities/index.ts @@ -0,0 +1,2 @@ +export * from './user-calendar-interval.entity'; +export * from './user-calendar.entity'; diff --git a/backend/src/modules/iam/user-calendar/entities/user-calendar-interval.entity.ts b/backend/src/modules/iam/user-calendar/entities/user-calendar-interval.entity.ts new file mode 100644 index 0000000..bd146cb --- /dev/null +++ b/backend/src/modules/iam/user-calendar/entities/user-calendar-interval.entity.ts @@ -0,0 +1,51 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { UserCalendarIntervalDto } from '../dto'; + +@Entity() +export class UserCalendarInterval { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + calendarId: number; + + @Column() + dayOfWeek: string; + + @Column({ type: 'time' }) + timeFrom: string; + + @Column({ type: 'time' }) + timeTo: string; + + constructor(accountId: number, calendarId: number, dayOfWeek: string, timeFrom: string, timeTo: string) { + this.accountId = accountId; + this.calendarId = calendarId; + this.dayOfWeek = dayOfWeek; + this.timeFrom = timeFrom; + this.timeTo = timeTo; + } + + static fromDto({ + accountId, + calendarId, + dto, + }: { + accountId: number; + calendarId: number; + dto: UserCalendarIntervalDto; + }): UserCalendarInterval { + return new UserCalendarInterval(accountId, calendarId, dto.dayOfWeek, dto.timeFrom, dto.timeTo); + } + + toDto(): UserCalendarIntervalDto { + return { + dayOfWeek: this.dayOfWeek, + timeFrom: this.timeFrom.substring(0, 5), + timeTo: this.timeTo.substring(0, 5), + }; + } +} diff --git a/backend/src/modules/iam/user-calendar/entities/user-calendar.entity.ts b/backend/src/modules/iam/user-calendar/entities/user-calendar.entity.ts new file mode 100644 index 0000000..aeb663c --- /dev/null +++ b/backend/src/modules/iam/user-calendar/entities/user-calendar.entity.ts @@ -0,0 +1,76 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { UserCalendarDto } from '../dto'; +import { UserCalendarInterval } from './user-calendar-interval.entity'; + +@Entity() +export class UserCalendar { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + userId: number; + + @Column({ nullable: true }) + timeBufferBefore: number | null; + + @Column({ nullable: true }) + timeBufferAfter: number | null; + + @Column({ nullable: true }) + appointmentLimit: number | null; + + constructor( + accountId: number, + userId: number, + timeBufferBefore: number | null, + timeBufferAfter: number | null, + appointmentLimit: number | null, + ) { + this.accountId = accountId; + this.userId = userId; + this.timeBufferBefore = timeBufferBefore; + this.timeBufferAfter = timeBufferAfter; + this.appointmentLimit = appointmentLimit; + } + + private _intervals: UserCalendarInterval[] | null; + get intervals(): UserCalendarInterval[] | null { + return this._intervals; + } + set intervals(value: UserCalendarInterval[] | null) { + this._intervals = value; + } + + static fromDto({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: UserCalendarDto; + }): UserCalendar { + return new UserCalendar(accountId, userId, dto.timeBufferBefore, dto.timeBufferAfter, dto.appointmentLimit); + } + + update(dto: UserCalendarDto): UserCalendar { + this.timeBufferBefore = dto.timeBufferBefore !== undefined ? dto.timeBufferBefore : this.timeBufferBefore; + this.timeBufferAfter = dto.timeBufferAfter !== undefined ? dto.timeBufferAfter : this.timeBufferAfter; + this.appointmentLimit = dto.appointmentLimit !== undefined ? dto.appointmentLimit : this.appointmentLimit; + + return this; + } + + toDto(): UserCalendarDto { + return { + timeBufferBefore: this.timeBufferBefore, + timeBufferAfter: this.timeBufferAfter, + appointmentLimit: this.appointmentLimit, + intervals: this._intervals?.map((interval) => interval.toDto()), + }; + } +} diff --git a/backend/src/modules/iam/user-calendar/index.ts b/backend/src/modules/iam/user-calendar/index.ts new file mode 100644 index 0000000..c0dc971 --- /dev/null +++ b/backend/src/modules/iam/user-calendar/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './services'; +export * from './user-calendar.controller'; diff --git a/backend/src/modules/iam/user-calendar/services/index.ts b/backend/src/modules/iam/user-calendar/services/index.ts new file mode 100644 index 0000000..97e2fd7 --- /dev/null +++ b/backend/src/modules/iam/user-calendar/services/index.ts @@ -0,0 +1,2 @@ +export * from './user-calendar-interval.service'; +export * from './user-calendar.service'; diff --git a/backend/src/modules/iam/user-calendar/services/user-calendar-interval.service.ts b/backend/src/modules/iam/user-calendar/services/user-calendar-interval.service.ts new file mode 100644 index 0000000..155dadc --- /dev/null +++ b/backend/src/modules/iam/user-calendar/services/user-calendar-interval.service.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { UserCalendarIntervalDto } from '../dto'; +import { UserCalendarInterval } from '../entities'; + +interface FindFilter { + accountId: number; + calendarId: number; +} + +@Injectable() +export class UserCalendarIntervalService { + constructor( + @InjectRepository(UserCalendarInterval) + private readonly repository: Repository, + ) {} + + async create({ + accountId, + calendarId, + dto, + }: { + accountId: number; + calendarId: number; + dto: UserCalendarIntervalDto; + }) { + return this.repository.save(UserCalendarInterval.fromDto({ accountId, calendarId, dto })); + } + async createMany({ + accountId, + calendarId, + dtos, + }: { + accountId: number; + calendarId: number; + dtos: UserCalendarIntervalDto[]; + }) { + return Promise.all(dtos.map((dto) => this.create({ accountId, calendarId, dto }))); + } + + async findMany(filter: FindFilter): Promise { + return this.createQb(filter).getMany(); + } + + async updateMany({ + accountId, + calendarId, + dtos, + }: { + accountId: number; + calendarId: number; + dtos: UserCalendarIntervalDto[]; + }) { + await this.deleteMany({ accountId, calendarId }); + return this.createMany({ accountId, calendarId, dtos }); + } + + async deleteMany({ accountId, calendarId }: FindFilter) { + await this.repository.delete({ accountId, calendarId }); + } + + private createQb({ accountId, calendarId }: FindFilter) { + return this.repository + .createQueryBuilder('interval') + .where('interval.account_id = :accountId', { accountId }) + .andWhere('interval.calendar_id = :calendarId', { calendarId }); + } +} diff --git a/backend/src/modules/iam/user-calendar/services/user-calendar.service.ts b/backend/src/modules/iam/user-calendar/services/user-calendar.service.ts new file mode 100644 index 0000000..73b2aff --- /dev/null +++ b/backend/src/modules/iam/user-calendar/services/user-calendar.service.ts @@ -0,0 +1,103 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { UserCalendarDto } from '../dto'; +import { UserCalendar } from '../entities'; +import { UserCalendarIntervalService } from './user-calendar-interval.service'; + +interface FindFilter { + accountId: number; + userId?: number; +} + +@Injectable() +export class UserCalendarService { + constructor( + @InjectRepository(UserCalendar) + private readonly repository: Repository, + private readonly intervalService: UserCalendarIntervalService, + ) {} + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: UserCalendarDto; + }): Promise { + const calendar = await this.repository.save(UserCalendar.fromDto({ accountId, userId, dto })); + if (dto.intervals) { + calendar.intervals = await this.intervalService.createMany({ + accountId, + calendarId: calendar.id, + dtos: dto.intervals, + }); + } + return calendar; + } + + async findOne(filter: FindFilter): Promise { + const calendar = await this.createQb(filter).getOne(); + if (calendar) { + calendar.intervals = await this.intervalService.findMany({ + accountId: filter.accountId, + calendarId: calendar.id, + }); + } + return calendar; + } + async findMany(filter: FindFilter): Promise { + const calendars = await this.createQb(filter).getMany(); + if (calendars.length) { + await Promise.all( + calendars.map(async (calendar) => { + calendar.intervals = await this.intervalService.findMany({ + accountId: filter.accountId, + calendarId: calendar.id, + }); + }), + ); + } + return calendars; + } + + async update({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: UserCalendarDto; + }): Promise { + const calendar = await this.findOne({ accountId, userId }); + if (!calendar) { + return this.create({ accountId, userId, dto }); + } + await this.repository.save(calendar.update(dto)); + if (dto.intervals) { + calendar.intervals = await this.intervalService.updateMany({ + accountId, + calendarId: calendar.id, + dtos: dto.intervals, + }); + } + return calendar; + } + + async delete({ accountId, userId }: { accountId: number; userId: number }) { + await this.repository.delete({ accountId, userId }); + } + + private createQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder('user_calendar'); + qb.where('user_calendar.account_id = :accountId', { accountId: filter.accountId }); + if (filter.userId) { + qb.andWhere('user_calendar.user_id = :userId', { userId: filter.userId }); + } + return qb; + } +} diff --git a/backend/src/modules/iam/user-calendar/user-calendar.controller.ts b/backend/src/modules/iam/user-calendar/user-calendar.controller.ts new file mode 100644 index 0000000..b4ea1d8 --- /dev/null +++ b/backend/src/modules/iam/user-calendar/user-calendar.controller.ts @@ -0,0 +1,59 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '../common'; + +import { UserCalendarDto } from './dto'; +import { UserCalendarService } from './services'; + +@ApiTags('IAM/users/calendar') +@Controller('users/:userId/calendar') +@JwtAuthorized() +@TransformToDto() +export class UserCalendarController { + constructor(private readonly service: UserCalendarService) {} + + @ApiOperation({ summary: 'Create user calendar', description: 'Create user calendar' }) + @ApiParam({ name: 'userId', type: Number, description: 'User id', required: true }) + @ApiBody({ type: UserCalendarDto, required: true, description: 'Create user calendar data' }) + @ApiCreatedResponse({ type: UserCalendarDto, description: 'Created user calendar' }) + @Post() + async create( + @CurrentAuth() { accountId }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: UserCalendarDto, + ) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Get user calendar', description: 'Get user calendar' }) + @ApiParam({ name: 'userId', type: Number, description: 'User id', required: true }) + @ApiOkResponse({ type: UserCalendarDto, description: 'User calendar' }) + @Get() + async findOne(@CurrentAuth() { accountId }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.findOne({ accountId, userId }); + } + + @ApiOperation({ summary: 'Update user calendar', description: 'Update user calendar' }) + @ApiParam({ name: 'userId', type: Number, description: 'User id', required: true }) + @ApiBody({ type: UserCalendarDto, required: true, description: 'Update user calendar data' }) + @ApiOkResponse({ type: UserCalendarDto, description: 'Updated user calendar' }) + @Patch() + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: UserCalendarDto, + ) { + return this.service.update({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Delete user calendar', description: 'Delete user calendar' }) + @ApiParam({ name: 'userId', type: Number, description: 'User id', required: true }) + @ApiOkResponse() + @Delete() + async delete(@CurrentAuth() { accountId }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.delete({ accountId, userId }); + } +} diff --git a/backend/src/modules/iam/user-profile/dto/index.ts b/backend/src/modules/iam/user-profile/dto/index.ts new file mode 100644 index 0000000..149f8be --- /dev/null +++ b/backend/src/modules/iam/user-profile/dto/index.ts @@ -0,0 +1,2 @@ +export * from './update-user-profile.dto'; +export * from './user-profile.dto'; diff --git a/backend/src/modules/iam/user-profile/dto/update-user-profile.dto.ts b/backend/src/modules/iam/user-profile/dto/update-user-profile.dto.ts new file mode 100644 index 0000000..57c06f5 --- /dev/null +++ b/backend/src/modules/iam/user-profile/dto/update-user-profile.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { UserProfileDto } from './user-profile.dto'; + +export class UpdateUserProfileDto extends OmitType(UserProfileDto, ['userId'] as const) {} diff --git a/backend/src/modules/iam/user-profile/dto/user-profile.dto.ts b/backend/src/modules/iam/user-profile/dto/user-profile.dto.ts new file mode 100644 index 0000000..3141abc --- /dev/null +++ b/backend/src/modules/iam/user-profile/dto/user-profile.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class UserProfileDto { + @ApiProperty({ description: 'User ID' }) + @IsNumber() + userId: number; + + @ApiPropertyOptional({ description: 'User birth date' }) + @IsOptional() + @IsString() + birthDate?: string; + + @ApiPropertyOptional({ description: 'User employment date' }) + @IsOptional() + @IsString() + employmentDate?: string; + + @ApiPropertyOptional({ description: 'Working time from of the department', nullable: true }) + @IsOptional() + @IsString() + workingTimeFrom?: string | null; + + @ApiPropertyOptional({ description: 'Working time to of the department', nullable: true }) + @IsOptional() + @IsString() + workingTimeTo?: string | null; +} diff --git a/backend/src/modules/iam/user-profile/entities/index.ts b/backend/src/modules/iam/user-profile/entities/index.ts new file mode 100644 index 0000000..5421eef --- /dev/null +++ b/backend/src/modules/iam/user-profile/entities/index.ts @@ -0,0 +1 @@ +export * from './user-profile.entity'; diff --git a/backend/src/modules/iam/user-profile/entities/user-profile.entity.ts b/backend/src/modules/iam/user-profile/entities/user-profile.entity.ts new file mode 100644 index 0000000..4f1ff82 --- /dev/null +++ b/backend/src/modules/iam/user-profile/entities/user-profile.entity.ts @@ -0,0 +1,62 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { UpdateUserProfileDto, UserProfileDto } from '../dto'; + +@Entity() +export class UserProfile { + @Column() + accountId: number; + + @PrimaryColumn() + userId: number; + + @Column({ nullable: true, default: null }) + birthDate: Date | null; + + @Column({ nullable: true, default: null }) + employmentDate: Date | null; + + @Column({ type: 'time', nullable: true }) + workingTimeFrom: string | null; + + @Column({ type: 'time', nullable: true }) + workingTimeTo: string | null; + + constructor( + accountId: number, + userId: number, + birthDate: Date | null, + employmentDate: Date | null, + workingTimeFrom: string | null, + workingTimeTo: string | null, + ) { + this.accountId = accountId; + this.userId = userId; + this.birthDate = birthDate; + this.employmentDate = employmentDate; + this.workingTimeFrom = workingTimeFrom; + this.workingTimeTo = workingTimeTo; + } + + public update(dto: UpdateUserProfileDto): UserProfile { + this.birthDate = dto.birthDate !== undefined ? DateUtil.fromISOString(dto.birthDate) : this.birthDate; + this.employmentDate = + dto.employmentDate !== undefined ? DateUtil.fromISOString(dto.employmentDate) : this.employmentDate; + this.workingTimeFrom = dto.workingTimeFrom !== undefined ? dto.workingTimeFrom : this.workingTimeFrom; + this.workingTimeTo = dto.workingTimeTo !== undefined ? dto.workingTimeTo : this.workingTimeTo; + + return this; + } + + public toDto(): UserProfileDto { + return { + userId: this.userId, + birthDate: this.birthDate?.toISOString() ?? null, + employmentDate: this.employmentDate?.toISOString() ?? null, + workingTimeFrom: this.workingTimeFrom?.substring(0, 5) ?? null, + workingTimeTo: this.workingTimeTo?.substring(0, 5) ?? null, + }; + } +} diff --git a/backend/src/modules/iam/user-profile/user-profile.controller.ts b/backend/src/modules/iam/user-profile/user-profile.controller.ts new file mode 100644 index 0000000..48a6cea --- /dev/null +++ b/backend/src/modules/iam/user-profile/user-profile.controller.ts @@ -0,0 +1,37 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Patch } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '../common'; +import { UserProfileDto, UpdateUserProfileDto } from './dto'; +import { UserProfileService } from './user-profile.service'; + +@ApiTags('IAM/users') +@Controller('users/:userId/profile') +@JwtAuthorized() +@TransformToDto() +export class UserProfileController { + constructor(private readonly service: UserProfileService) {} + + @ApiOperation({ summary: 'Get user profile', description: 'Get user profile' }) + @ApiParam({ name: 'userId', description: 'User ID', type: Number, required: true }) + @ApiOkResponse({ type: UserProfileDto, description: 'User profile' }) + @Get() + public async findOne(@CurrentAuth() { accountId }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.findOne({ accountId, userId }); + } + + @ApiOperation({ summary: 'Update user profile', description: 'Update user profile' }) + @ApiParam({ name: 'userId', description: 'User ID', type: Number, required: true }) + @ApiBody({ type: UpdateUserProfileDto, description: 'Date for update user profile', required: true }) + @ApiOkResponse({ type: UserProfileDto, description: 'User profile' }) + @Patch() + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: UpdateUserProfileDto, + ) { + return this.service.update({ accountId, userId, dto }); + } +} diff --git a/backend/src/modules/iam/user-profile/user-profile.service.ts b/backend/src/modules/iam/user-profile/user-profile.service.ts new file mode 100644 index 0000000..9c50706 --- /dev/null +++ b/backend/src/modules/iam/user-profile/user-profile.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { UpdateUserProfileDto } from './dto'; +import { UserProfile } from './entities'; + +const cacheKey = ({ accountId, userId }: { accountId: number; userId: number }) => `UserProfile:${accountId}:${userId}`; + +@Injectable() +export class UserProfileService { + constructor( + @InjectRepository(UserProfile) + private readonly repository: Repository, + private readonly dataSource: DataSource, + ) {} + + public async create({ accountId, userId }: { accountId: number; userId: number }): Promise { + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId })]); + return this.repository.save(new UserProfile(accountId, userId, null, null, null, null)); + } + + public async findOne({ accountId, userId }: { accountId: number; userId: number }): Promise { + const profile = await this.repository.findOne({ + where: { accountId, userId }, + cache: { id: cacheKey({ accountId, userId }), milliseconds: 86400000 }, + }); + + return profile ?? this.create({ accountId, userId }); + } + + public async update({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: UpdateUserProfileDto; + }): Promise { + const profile = await this.findOne({ accountId, userId }); + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId })]); + await this.repository.save(profile.update(dto)); + + return profile; + } +} diff --git a/backend/src/modules/iam/user-token/dto/create-user-token.dto.ts b/backend/src/modules/iam/user-token/dto/create-user-token.dto.ts new file mode 100644 index 0000000..c905d75 --- /dev/null +++ b/backend/src/modules/iam/user-token/dto/create-user-token.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { UserTokenDto } from './user-token.dto'; + +export class CreateUserTokenDto extends PickType(UserTokenDto, ['name', 'expiresAt'] as const) {} diff --git a/backend/src/modules/iam/user-token/dto/index.ts b/backend/src/modules/iam/user-token/dto/index.ts new file mode 100644 index 0000000..12c6e66 --- /dev/null +++ b/backend/src/modules/iam/user-token/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-user-token.dto'; +export * from './user-access-token.dto'; +export * from './user-token.dto'; diff --git a/backend/src/modules/iam/user-token/dto/user-access-token.dto.ts b/backend/src/modules/iam/user-token/dto/user-access-token.dto.ts new file mode 100644 index 0000000..f49ab0b --- /dev/null +++ b/backend/src/modules/iam/user-token/dto/user-access-token.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { UserTokenDto } from './user-token.dto'; + +export class UserAccessTokenDto { + @ApiProperty({ description: 'User access token' }) + @IsString() + accessToken: string; + + @ApiProperty({ description: 'User token' }) + userToken: UserTokenDto; +} diff --git a/backend/src/modules/iam/user-token/dto/user-token.dto.ts b/backend/src/modules/iam/user-token/dto/user-token.dto.ts new file mode 100644 index 0000000..5c443e5 --- /dev/null +++ b/backend/src/modules/iam/user-token/dto/user-token.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class UserTokenDto { + @ApiProperty({ description: 'User access token ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'User access token name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'User access token created at' }) + @IsDateString() + createdAt: string; + + @ApiPropertyOptional({ description: 'User access token expires at' }) + @IsOptional() + @IsDateString() + expiresAt?: string | null; + + @ApiPropertyOptional({ description: 'User access token last used at' }) + @IsOptional() + @IsDateString() + lastUsedAt?: string | null; +} diff --git a/backend/src/modules/iam/user-token/entities/index.ts b/backend/src/modules/iam/user-token/entities/index.ts new file mode 100644 index 0000000..83bcabf --- /dev/null +++ b/backend/src/modules/iam/user-token/entities/index.ts @@ -0,0 +1 @@ +export * from './user-token.entity'; diff --git a/backend/src/modules/iam/user-token/entities/user-token.entity.ts b/backend/src/modules/iam/user-token/entities/user-token.entity.ts new file mode 100644 index 0000000..9417bca --- /dev/null +++ b/backend/src/modules/iam/user-token/entities/user-token.entity.ts @@ -0,0 +1,71 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { CreateUserTokenDto, UserTokenDto } from '../dto'; +import { DateUtil } from '@/common'; + +@Entity() +export class UserToken { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + userId: number; + + @Column() + name: string; + + @Column() + code: string; + + @Column() + createdAt: Date; + + @Column({ nullable: true, default: null }) + expiresAt: Date | null; + + @Column({ nullable: true, default: null }) + lastUsedAt: Date | null; + + constructor(accountId: number, userId: number, name: string, code: string, expiresAt?: Date | null) { + this.accountId = accountId; + this.userId = userId; + this.name = name; + this.code = code; + this.createdAt = new Date(); + this.expiresAt = expiresAt ?? null; + } + + static fromDto({ + accountId, + userId, + data, + }: { + accountId: number; + userId: number; + data: CreateUserTokenDto & { code: string }; + }): UserToken { + return new UserToken( + accountId, + userId, + data.name, + data.code, + data.expiresAt ? DateUtil.fromISOString(data.expiresAt) : null, + ); + } + + toDto(): UserTokenDto { + return { + id: this.id, + name: this.name, + createdAt: this.createdAt.toISOString(), + expiresAt: this.expiresAt?.toISOString(), + lastUsedAt: this.lastUsedAt?.toISOString(), + }; + } + + isExpired(): boolean { + return this.expiresAt ? this.expiresAt < DateUtil.now() : false; + } +} diff --git a/backend/src/modules/iam/user-token/index.ts b/backend/src/modules/iam/user-token/index.ts new file mode 100644 index 0000000..8ffa05c --- /dev/null +++ b/backend/src/modules/iam/user-token/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './types'; +export * from './user-token.controller'; +export * from './user-token.service'; diff --git a/backend/src/modules/iam/user-token/types/index.ts b/backend/src/modules/iam/user-token/types/index.ts new file mode 100644 index 0000000..dfd7398 --- /dev/null +++ b/backend/src/modules/iam/user-token/types/index.ts @@ -0,0 +1 @@ +export * from './user-access-token'; diff --git a/backend/src/modules/iam/user-token/types/user-access-token.ts b/backend/src/modules/iam/user-token/types/user-access-token.ts new file mode 100644 index 0000000..589f749 --- /dev/null +++ b/backend/src/modules/iam/user-token/types/user-access-token.ts @@ -0,0 +1,19 @@ +import { UserAccessTokenDto } from '../dto'; +import { UserToken } from '../entities'; + +export class UserAccessToken { + accessToken: string; + userToken: UserToken; + + constructor({ accessToken, userToken }: { accessToken: string; userToken: UserToken }) { + this.accessToken = accessToken; + this.userToken = userToken; + } + + toDto(): UserAccessTokenDto { + return { + accessToken: this.accessToken, + userToken: this.userToken.toDto(), + }; + } +} diff --git a/backend/src/modules/iam/user-token/user-token.controller.ts b/backend/src/modules/iam/user-token/user-token.controller.ts new file mode 100644 index 0000000..f923380 --- /dev/null +++ b/backend/src/modules/iam/user-token/user-token.controller.ts @@ -0,0 +1,52 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { Subdomain, TransformToDto } from '@/common'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '../common'; + +import { CreateUserTokenDto, UserAccessTokenDto, UserTokenDto } from './dto'; +import { UserTokenService } from './user-token.service'; + +@ApiTags('IAM/users/tokens') +@Controller('users/my/tokens') +@JwtAuthorized() +@TransformToDto() +export class UserTokenController { + constructor(private readonly service: UserTokenService) {} + + @ApiOperation({ summary: 'Create user access token', description: 'Create user access token' }) + @ApiBody({ type: CreateUserTokenDto, required: true, description: 'Create user access token data' }) + @ApiCreatedResponse({ type: UserAccessTokenDto, description: 'Created user access token' }) + @Post() + async create( + @CurrentAuth() { accountId, userId }: AuthData, + @Subdomain() subdomain: string | null, + @Body() dto: CreateUserTokenDto, + ) { + return this.service.create({ accountId, userId, subdomain, dto }); + } + + @ApiOperation({ summary: 'Get user access token', description: 'Get user access token' }) + @ApiParam({ name: 'tokenId', type: Number, description: 'User access token id', required: true }) + @ApiOkResponse({ type: UserTokenDto, description: 'User access token' }) + @Get(':tokenId') + async findOne(@CurrentAuth() { accountId, userId }: AuthData, @Param('tokenId', ParseIntPipe) tokenId: number) { + return this.service.findOne({ accountId, userId, tokenId }); + } + + @ApiOperation({ summary: 'Get user access tokens', description: 'Get user access tokens' }) + @ApiOkResponse({ type: [UserTokenDto], description: 'User access tokens' }) + @Get() + async findMany(@CurrentAuth() { accountId, userId }: AuthData) { + return this.service.findMany({ accountId, userId }); + } + + @ApiOperation({ summary: 'Delete user access token', description: 'Delete user access token' }) + @ApiParam({ name: 'tokenId', type: Number, description: 'User access token id', required: true }) + @ApiOkResponse({ type: Number, description: 'Deleted user access token id' }) + @Delete(':tokenId') + async delete(@CurrentAuth() { accountId, userId }: AuthData, @Param('tokenId', ParseIntPipe) tokenId: number) { + return this.service.delete({ accountId, userId, tokenId }); + } +} diff --git a/backend/src/modules/iam/user-token/user-token.service.ts b/backend/src/modules/iam/user-token/user-token.service.ts new file mode 100644 index 0000000..08d39ab --- /dev/null +++ b/backend/src/modules/iam/user-token/user-token.service.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { v4 as uuidv4 } from 'uuid'; + +import { DateUtil, TokenService } from '@/common'; + +import { CreateUserTokenDto } from './dto'; +import { UserToken } from './entities'; +import { UserAccessToken } from './types'; + +interface FindFilter { + accountId: number; + userId: number; + tokenId?: number; + code?: string; +} + +@Injectable() +export class UserTokenService { + constructor( + @InjectRepository(UserToken) + private readonly repository: Repository, + private readonly tokenService: TokenService, + ) {} + + async create({ + accountId, + subdomain, + userId, + dto, + }: { + accountId: number; + subdomain: string; + userId: number; + dto: CreateUserTokenDto; + }): Promise { + const code = uuidv4(); + const expiresIn = dto.expiresAt + ? DateUtil.diff({ startDate: DateUtil.now(), endDate: DateUtil.fromISOString(dto.expiresAt), unit: 'second' }) + : undefined; + const accessToken = this.tokenService.create( + { accountId, userId, subdomain, code }, + expiresIn ? { expiresIn } : undefined, + ); + + const userToken = UserToken.fromDto({ accountId, userId, data: { ...dto, code } }); + await this.repository.insert(userToken); + + return new UserAccessToken({ accessToken, userToken }); + } + + async findOne(filter: FindFilter): Promise { + return this.createQb(filter).getOne(); + } + async findMany(filter: FindFilter): Promise { + return this.createQb(filter).getMany(); + } + + async use(filter: FindFilter): Promise { + const token = await this.findOne(filter); + if (token) { + const now = DateUtil.now(); + await this.repository.update({ id: token.id }, { lastUsedAt: now }); + token.lastUsedAt = now; + } + + return token; + } + + async delete({ + accountId, + userId, + tokenId, + }: { + accountId: number; + userId: number; + tokenId: number; + }): Promise { + const { affected } = await this.repository.delete({ accountId, userId, id: tokenId }); + return affected ? tokenId : null; + } + + private createQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('token') + .where('token.account_id = :accountId', { accountId: filter.accountId }) + .andWhere('token.user_id = :userId', { userId: filter.userId }); + + if (filter.tokenId) { + qb.andWhere('token.id = :tokenId', { tokenId: filter.tokenId }); + } + if (filter.code) { + qb.andWhere('token.code = :code', { code: filter.code }); + } + + return qb; + } +} diff --git a/backend/src/modules/iam/user/dto/change-user-password.dto.ts b/backend/src/modules/iam/user/dto/change-user-password.dto.ts new file mode 100644 index 0000000..c6b574d --- /dev/null +++ b/backend/src/modules/iam/user/dto/change-user-password.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class ChangeUserPasswordDto { + @ApiProperty({ description: 'Current password' }) + @IsString() + currentPassword: string; + + @ApiProperty({ description: 'New password' }) + @IsString() + newPassword: string; +} diff --git a/backend/src/modules/iam/user/dto/create-user.dto.ts b/backend/src/modules/iam/user/dto/create-user.dto.ts new file mode 100644 index 0000000..4337acd --- /dev/null +++ b/backend/src/modules/iam/user/dto/create-user.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +import { UserDto } from './user.dto'; + +export class CreateUserDto extends OmitType(UserDto, [ + 'id', + 'isActive', + 'avatarUrl', + 'analyticsId', + 'isPlatformAdmin', +] as const) { + @ApiProperty({ description: 'User password' }) + @IsString() + password: string; + + @ApiPropertyOptional({ description: 'Is user active?' }) + @IsOptional() + @IsBoolean() + isActive?: boolean; + + @ApiPropertyOptional({ description: 'User analytics id' }) + @IsOptional() + @IsString() + analyticsId?: string; +} diff --git a/backend/src/modules/iam/user/dto/index.ts b/backend/src/modules/iam/user/dto/index.ts new file mode 100644 index 0000000..9bdf8f1 --- /dev/null +++ b/backend/src/modules/iam/user/dto/index.ts @@ -0,0 +1,5 @@ +export * from './change-user-password.dto'; +export * from './create-user.dto'; +export * from './update-user.dto'; +export * from './user-find-filter.dto'; +export * from './user.dto'; diff --git a/backend/src/modules/iam/user/dto/update-user.dto.ts b/backend/src/modules/iam/user/dto/update-user.dto.ts new file mode 100644 index 0000000..dca6e77 --- /dev/null +++ b/backend/src/modules/iam/user/dto/update-user.dto.ts @@ -0,0 +1,24 @@ +import { ApiPropertyOptional, OmitType, PartialType } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator'; + +import { UserRole } from '../../common/enums/user-role.enum'; +import { UserDto } from './user.dto'; + +export class UpdateUserDto extends PartialType( + OmitType(UserDto, ['id', 'isActive', 'role', 'avatarUrl', 'analyticsId', 'isPlatformAdmin'] as const), +) { + @ApiPropertyOptional({ description: 'New password' }) + @IsOptional() + @IsString() + password?: string; + + @ApiPropertyOptional({ description: 'Is user active' }) + @IsOptional() + @IsBoolean() + isActive?: boolean; + + @ApiPropertyOptional({ enum: UserRole, description: 'User role' }) + @IsOptional() + @IsEnum(UserRole) + role?: UserRole; +} diff --git a/backend/src/modules/iam/user/dto/user-find-filter.dto.ts b/backend/src/modules/iam/user/dto/user-find-filter.dto.ts new file mode 100644 index 0000000..953309e --- /dev/null +++ b/backend/src/modules/iam/user/dto/user-find-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UserFindFilterDto { + @ApiPropertyOptional({ description: 'User full name, first name + last name, e.g. "John Doe"' }) + @IsOptional() + @IsString() + fullName?: string; +} diff --git a/backend/src/modules/iam/user/dto/user.dto.ts b/backend/src/modules/iam/user/dto/user.dto.ts new file mode 100644 index 0000000..b064176 --- /dev/null +++ b/backend/src/modules/iam/user/dto/user.dto.ts @@ -0,0 +1,70 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { UserRole } from '../../common/enums/user-role.enum'; +import { ObjectPermissionDto } from '../../object-permission/dto/object-permission.dto'; + +export class UserDto { + @ApiProperty({ description: 'User ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'User first name' }) + @IsString() + firstName: string; + + @ApiProperty({ nullable: true, description: 'User last name' }) + @IsOptional() + @IsString() + lastName: string | null; + + @ApiProperty({ description: 'User email' }) + @IsString() + email: string; + + @ApiProperty({ nullable: true, description: 'User phone' }) + @IsOptional() + @IsString() + phone: string | null; + + @ApiProperty({ enum: UserRole, description: 'User role' }) + @IsEnum(UserRole) + role: UserRole; + + @ApiProperty({ description: 'Is user active' }) + @IsBoolean() + isActive: boolean; + + @ApiPropertyOptional({ type: [ObjectPermissionDto], nullable: true, description: 'User object permissions' }) + @IsArray() + @IsOptional() + objectPermissions?: ObjectPermissionDto[] | null; + + @ApiPropertyOptional({ nullable: true, description: 'User department ID' }) + @IsOptional() + @IsNumber() + departmentId?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'User position' }) + @IsOptional() + @IsString() + position?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'User analytics ID' }) + @IsOptional() + @IsString() + analyticsId?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'User avatar URL' }) + @IsOptional() + @IsString() + avatarUrl?: string | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Accessible user IDs' }) + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + accessibleUserIds?: number[] | null; + + isPlatformAdmin: boolean; +} diff --git a/backend/src/modules/iam/user/entities/index.ts b/backend/src/modules/iam/user/entities/index.ts new file mode 100644 index 0000000..9c5f833 --- /dev/null +++ b/backend/src/modules/iam/user/entities/index.ts @@ -0,0 +1,2 @@ +export * from './user.entity'; +export * from './users-accessible-users.entity'; diff --git a/backend/src/modules/iam/user/entities/user.entity.ts b/backend/src/modules/iam/user/entities/user.entity.ts new file mode 100644 index 0000000..ffc794c --- /dev/null +++ b/backend/src/modules/iam/user/entities/user.entity.ts @@ -0,0 +1,181 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { v4 as uuidv4 } from 'uuid'; + +import { DateUtil, PasswordUtil } from '@/common'; + +import { UserRole } from '../../common'; +import { ObjectPermission } from '../../object-permission/entities'; +import { CreateUserDto, UpdateUserDto, UserDto } from '../dto'; +import { UsersAccessibleUsers } from './users-accessible-users.entity'; + +@Entity('users') +export class User { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + firstName: string; + + @Column() + lastName: string; + + @Column() + email: string; + + @Column({ nullable: true }) + phone: string | null; + + @Column() + password: string; + + @Column() + role: UserRole; + + @Column({ nullable: true }) + avatarId: string | null; + + @Column() + isActive: boolean; + + @Column({ default: false }) + isPlatformAdmin: boolean; + + @Column({ nullable: true }) + departmentId: number | null; + + @Column({ nullable: true }) + position: string | null; + + @Column() + analyticsId: string; + + @Column() + accountId: number; + + @Column({ default: 0 }) + loginAttempts: number; + + @Column({ nullable: true }) + lockUntil: Date | null; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + firstName: string, + lastName: string, + email: string, + password: string, + role: UserRole, + isActive: boolean, + departmentId: number | null, + position: string | null, + avatarId: string | null, + phone: string | null, + analyticsId: string, + createdAt?: Date, + ) { + this.accountId = accountId; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.phone = phone; + this.avatarId = avatarId; + this.password = password; + this.role = role; + this.isActive = isActive; + this.departmentId = departmentId; + this.position = position; + this.analyticsId = analyticsId; + this.loginAttempts = 0; + this.lockUntil = null; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _avatarUrl: string | null; + public get avatarUrl(): string | null { + return this._avatarUrl; + } + public set avatarUrl(value: string | null) { + this._avatarUrl = value; + } + + private _objectPermissions: ObjectPermission[] | null; + public get objectPermissions(): ObjectPermission[] | null { + return this._objectPermissions; + } + public set objectPermissions(value: ObjectPermission[] | null) { + this._objectPermissions = value; + } + + private _accessibleUsers: UsersAccessibleUsers[] | null; + public get accessibleUsers(): UsersAccessibleUsers[] | null { + return this._accessibleUsers; + } + public set accessibleUsers(value: UsersAccessibleUsers[] | null) { + this._accessibleUsers = value; + } + + public static fromDto(accountId: number, dto: CreateUserDto, createdAt?: Date): User { + return new User( + accountId, + dto.firstName?.trim(), + dto.lastName?.trim(), + dto.email.trim(), + PasswordUtil.hash(dto.password), + dto.role, + dto.isActive ?? true, + dto.departmentId, + dto.position, + null, + dto.phone, + dto.analyticsId ?? uuidv4(), + createdAt, + ); + } + + public update(dto: UpdateUserDto): User { + this.firstName = dto.firstName !== undefined ? (dto.firstName ?? '').trim() : this.firstName; + this.lastName = dto.lastName !== undefined ? (dto.lastName ?? '').trim() : this.lastName; + this.email = dto.email !== undefined ? (dto.email ?? '').trim() : this.email; + this.phone = dto.phone !== undefined ? dto.phone : this.phone; + this.role = dto.role !== undefined ? dto.role : this.role; + this.isActive = dto.isActive !== undefined ? dto.isActive : this.isActive; + this.departmentId = dto.departmentId !== undefined ? dto.departmentId : this.departmentId; + this.position = dto.position !== undefined ? dto.position : this.position; + + if (dto.password) { + this.password = PasswordUtil.hash(dto.password); + } + + return this; + } + + public get fullName() { + return `${this.firstName} ${this.lastName ?? ''}`.trim(); + } + + public get isAdmin() { + return this.role === UserRole.ADMIN || this.role === UserRole.OWNER; + } + + public toDto(): UserDto { + return { + id: this.id, + firstName: this.firstName, + lastName: this.lastName, + email: this.email, + phone: this.phone, + role: this.role, + isActive: this.isActive, + departmentId: this.departmentId, + position: this.position, + avatarUrl: this.avatarUrl, + analyticsId: this.analyticsId, + objectPermissions: this.objectPermissions?.map((op) => op.toDto()), + accessibleUserIds: this.accessibleUsers?.map((au) => au.accessibleId), + isPlatformAdmin: this.isPlatformAdmin, + }; + } +} diff --git a/backend/src/modules/iam/user/entities/users-accessible-users.entity.ts b/backend/src/modules/iam/user/entities/users-accessible-users.entity.ts new file mode 100644 index 0000000..786b94f --- /dev/null +++ b/backend/src/modules/iam/user/entities/users-accessible-users.entity.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class UsersAccessibleUsers { + @PrimaryColumn() + userId: number; + + @PrimaryColumn() + accessibleId: number; + + constructor(userId: number, accessibleId: number) { + this.userId = userId; + this.accessibleId = accessibleId; + } +} diff --git a/backend/src/modules/iam/user/errors/bad-credentials.error.ts b/backend/src/modules/iam/user/errors/bad-credentials.error.ts new file mode 100644 index 0000000..9e17c98 --- /dev/null +++ b/backend/src/modules/iam/user/errors/bad-credentials.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class BadCredentialsError extends ServiceError { + constructor(message = 'Bad credentials') { + super({ errorCode: 'bad_credentials', status: HttpStatus.FORBIDDEN, message }); + } +} diff --git a/backend/src/modules/iam/user/errors/email-occupied.error.ts b/backend/src/modules/iam/user/errors/email-occupied.error.ts new file mode 100644 index 0000000..d25d6f6 --- /dev/null +++ b/backend/src/modules/iam/user/errors/email-occupied.error.ts @@ -0,0 +1,21 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class EmailOccupiedError extends ServiceError { + constructor(message = 'Email is occupied') { + super({ errorCode: 'email_occupied', status: HttpStatus.FORBIDDEN, message }); + } + + public static fromEmail(email: string): EmailOccupiedError { + return new EmailOccupiedError(`Email ${email} is occupied`); + } + + //TODO: remove string description for error code + public static forAccountCreation(): EmailOccupiedError { + return new EmailOccupiedError( + 'This email has already been used for registration. You can log in or, if you ' + + 'have forgotten your password, recover it. Alternatively, use a different email for registration.', + ); + } +} diff --git a/backend/src/modules/iam/user/errors/index.ts b/backend/src/modules/iam/user/errors/index.ts new file mode 100644 index 0000000..191eb71 --- /dev/null +++ b/backend/src/modules/iam/user/errors/index.ts @@ -0,0 +1,3 @@ +export * from './bad-credentials.error'; +export * from './email-occupied.error'; +export * from './user-not-active.error'; diff --git a/backend/src/modules/iam/user/errors/user-not-active.error.ts b/backend/src/modules/iam/user/errors/user-not-active.error.ts new file mode 100644 index 0000000..55be5c3 --- /dev/null +++ b/backend/src/modules/iam/user/errors/user-not-active.error.ts @@ -0,0 +1,17 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class UserNotActiveError extends ServiceError { + constructor(message = 'User is not active') { + super({ errorCode: 'user_not_active', status: HttpStatus.UNAUTHORIZED, message }); + } + + static withId(userId: number) { + return new UserNotActiveError(`User with id ${userId} is not active`); + } + + static fromEmail(email: string): UserNotActiveError { + return new UserNotActiveError(`User with email ${email} is not active`); + } +} diff --git a/backend/src/modules/iam/user/types/expandable-field.ts b/backend/src/modules/iam/user/types/expandable-field.ts new file mode 100644 index 0000000..b865855 --- /dev/null +++ b/backend/src/modules/iam/user/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'avatarUrl' | 'objectPermissions'; diff --git a/backend/src/modules/iam/user/types/index.ts b/backend/src/modules/iam/user/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/iam/user/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/iam/user/user.controller.ts b/backend/src/modules/iam/user/user.controller.ts new file mode 100644 index 0000000..335bfa8 --- /dev/null +++ b/backend/src/modules/iam/user/user.controller.ts @@ -0,0 +1,155 @@ +import { + Body, + Controller, + Delete, + FileTypeValidator, + Get, + MaxFileSizeValidator, + Param, + ParseFilePipe, + ParseIntPipe, + Patch, + Post, + Put, + Query, + UploadedFile, + UseInterceptors, +} from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; +import { memoryStorage } from 'multer'; + +import { TransformToDto } from '@/common'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { AuthDataPrefetch } from '../common/decorators/auth-data-prefetch.decorator'; +import { CurrentAuth } from '../common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '../common/decorators/jwt-authorized.decorator'; +import { AuthData } from '../common/types/auth-data'; + +import { ChangeUserPasswordDto } from './dto/change-user-password.dto'; +import { CreateUserDto } from './dto/create-user.dto'; +import { UpdateUserDto } from './dto/update-user.dto'; +import { UserDto } from './dto/user.dto'; +import { UserService } from './user.service'; +import type { UserFindFilterDto } from './dto/user-find-filter.dto'; + +const UserAvatarFile = { + MaxSize: 5242880, + Type: 'image/*', +}; + +@ApiTags('IAM/users') +@Controller('users') +@JwtAuthorized({ prefetch: { account: true } }) +@TransformToDto() +export class UserController { + constructor(private readonly service: UserService) {} + + @ApiOperation({ summary: 'Create user', description: 'Create user' }) + @ApiBody({ type: CreateUserDto, required: true, description: 'Create user data' }) + @ApiCreatedResponse({ type: UserDto, description: 'Created user' }) + @Post() + async create(@CurrentAuth() { account }: AuthData, @Body() dto: CreateUserDto) { + return this.service.create({ account, dto }); + } + + @ApiOperation({ summary: 'Get user', description: 'Get user' }) + @ApiParam({ name: 'userId', type: Number, required: true, description: 'User ID' }) + @ApiOkResponse({ type: UserDto, description: 'User' }) + @Get(':userId') + async getOne(@CurrentAuth() { account }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return await this.service.findOne( + { accountId: account.id, id: userId }, + { account, expand: ['avatarUrl', 'objectPermissions'] }, + ); + } + + @ApiOperation({ summary: 'Get users', description: 'Get users' }) + @ApiOkResponse({ type: [UserDto], description: 'Users' }) + @Get() + async getMany(@CurrentAuth() { account }: AuthData, @Query() filter: UserFindFilterDto): Promise { + return this.service.findMany( + { accountId: account.id, fullName: filter?.fullName }, + { account, expand: ['avatarUrl', 'objectPermissions'] }, + ); + } + + @ApiOperation({ summary: 'Update user', description: 'Update user' }) + @ApiParam({ name: 'userId', type: Number, required: true, description: 'User ID' }) + @ApiBody({ type: UpdateUserDto, required: true, description: 'Update user data' }) + @ApiOkResponse({ type: UserDto, description: 'Updated user' }) + @Put(':userId') + async updatePut( + @CurrentAuth() { account }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: UpdateUserDto, + ): Promise { + return this.service.updateExt({ account, userId, dto }); + } + + @ApiOperation({ summary: 'Update user', description: 'Update user' }) + @ApiParam({ name: 'userId', type: Number, required: true, description: 'User ID' }) + @ApiBody({ type: UpdateUserDto, required: true, description: 'Update user data' }) + @ApiOkResponse({ type: UserDto, description: 'Updated user' }) + @Patch(':userId') + async updatePatch( + @CurrentAuth() { account }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: UpdateUserDto, + ): Promise { + return this.service.updateExt({ account, userId, dto }); + } + + @ApiOperation({ summary: 'Delete user', description: 'Soft delete user' }) + @ApiParam({ name: 'userId', type: Number, required: true, description: 'User ID' }) + @ApiQuery({ name: 'newUserId', type: Number, required: false, description: 'User ID to reassign data' }) + @ApiOkResponse() + @AuthDataPrefetch({ user: true }) + @Delete(':userId') + async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Query('newUserId') newUserId?: number, + ) { + await this.service.softDelete({ accountId, user, userId, newUserId: newUserId ? Number(newUserId) : undefined }); + } + + @ApiOperation({ summary: 'Change user password', description: 'Change user password' }) + @ApiBody({ type: ChangeUserPasswordDto, required: true, description: 'Change user password data' }) + @ApiOkResponse({ type: Boolean, description: 'Password changed' }) + @AuthDataPrefetch({ user: true }) + @Post('change-password') + async changePassword(@CurrentAuth() { user }: AuthData, @Body() dto: ChangeUserPasswordDto): Promise { + return this.service.changePassword({ user, dto }); + } + + @ApiOperation({ summary: 'Upload user avatar', description: 'Upload user avatar' }) + @ApiParam({ name: 'userId', type: Number, required: true, description: 'User ID' }) + @ApiOkResponse({ type: UserDto, description: 'User' }) + @Post(':userId/avatar') + @UseInterceptors(FileInterceptor('avatar', { storage: memoryStorage() })) + async uploadUserAvatar( + @CurrentAuth() { account }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @UploadedFile( + new ParseFilePipe({ + validators: [ + new MaxFileSizeValidator({ maxSize: UserAvatarFile.MaxSize }), + new FileTypeValidator({ fileType: UserAvatarFile.Type }), + ], + }), + ) + avatar: Express.Multer.File, + ) { + return this.service.setAvatar({ account, userId, file: StorageFile.fromMulter(avatar) }); + } + + @ApiOperation({ summary: 'Delete user avatar', description: 'Delete user avatar' }) + @ApiParam({ name: 'userId', type: Number, required: true, description: 'User ID' }) + @ApiOkResponse({ type: UserDto, description: 'User' }) + @Delete(':userId/avatar') + async deleteUserAvatar(@CurrentAuth() { account }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.removeAvatar({ account, userId }); + } +} diff --git a/backend/src/modules/iam/user/user.handler.ts b/backend/src/modules/iam/user/user.handler.ts new file mode 100644 index 0000000..a7cd326 --- /dev/null +++ b/backend/src/modules/iam/user/user.handler.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { DepartmentDeletedEvent, IamEventType } from '../common'; +import { UserService } from './user.service'; + +@Injectable() +export class UserHandler { + constructor(private readonly service: UserService) {} + + @OnEvent(IamEventType.DepartmentDeleted, { async: true }) + public async onDepartmentDeleted(event: DepartmentDeletedEvent) { + await this.service.changeDepartment({ + accountId: event.accountId, + departmentId: event.departmentId, + newDepartmentId: event.newDepartmentId, + }); + } +} diff --git a/backend/src/modules/iam/user/user.service.ts b/backend/src/modules/iam/user/user.service.ts new file mode 100644 index 0000000..a408c14 --- /dev/null +++ b/backend/src/modules/iam/user/user.service.ts @@ -0,0 +1,363 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { ForbiddenError, NotFoundError, PasswordUtil, PhoneUtil } from '@/common'; + +import { StorageUrlService } from '@/modules/storage/storage-url.service'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { IamEventType, UserCreatedEvent, UserDeletedEvent, UserRole } from '../common'; +import { Account } from '../account/entities/account.entity'; +import { ObjectPermissionService } from '../object-permission/object-permission.service'; + +import { CreateUserDto, UpdateUserDto, ChangeUserPasswordDto } from './dto'; +import { User, UsersAccessibleUsers } from './entities'; +import { EmailOccupiedError, BadCredentialsError } from './errors'; +import { ExpandableField } from './types'; + +interface CreateOptions { + skipPhoneCheck?: boolean; + createdAt?: Date; +} + +interface FindFilter { + accountId?: number; + id?: number | number[]; + email?: string; + isActive?: boolean; + departmentId?: number | number[]; + role?: UserRole; + fullName?: string; +} +interface FindOptions { + account?: Account; + expand?: ExpandableField[]; +} + +const cacheKey = ({ accountId, userId }: { accountId: number; userId: number }) => `User:${accountId}:${userId}`; + +@Injectable() +export class UserService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(User) + private readonly repository: Repository, + @InjectRepository(UsersAccessibleUsers) + private readonly repositoryUAU: Repository, + private readonly dataSource: DataSource, + private readonly objectPermissionService: ObjectPermissionService, + @Inject(forwardRef(() => StorageService)) + private readonly storageService: StorageService, + @Inject(forwardRef(() => StorageUrlService)) + private readonly storageUrlService: StorageUrlService, + ) {} + + async create({ + account, + dto, + options, + }: { + account: Account; + dto: CreateUserDto; + options?: CreateOptions; + }): Promise { + if (await this.isEmailOccupied(dto.email)) { + throw EmailOccupiedError.fromEmail(dto.email); + } + + // Validate password strength + if (!PasswordUtil.isStrong(dto.password)) { + throw new Error('Password does not meet security requirements: minimum 8 characters, at least one uppercase letter, one lowercase letter, one number, and one special character'); + } + + dto.phone = dto.phone && !options?.skipPhoneCheck ? PhoneUtil.normalize(dto.phone) : dto.phone; + const user = await this.repository.save(User.fromDto(account.id, dto, options?.createdAt)); + + if (dto.accessibleUserIds !== undefined) { + user.accessibleUsers = await this.repositoryUAU.save( + dto.accessibleUserIds.map((accessibleId) => new UsersAccessibleUsers(user.id, accessibleId)), + ); + } + + if (dto.objectPermissions) { + user.objectPermissions = await this.objectPermissionService.create({ + accountId: account.id, + userId: user.id, + hasDepartmentId: !!user.departmentId, + dtos: dto.objectPermissions, + }); + } + + this.eventEmitter.emit(IamEventType.UserCreated, new UserCreatedEvent({ accountId: account.id, userId: user.id })); + + return user; + } + + async isEmailOccupied(email: string): Promise { + return (await this.getCount({ email })) > 0; + } + + async findOne(filter: FindFilter, options?: FindOptions): Promise { + const qb = this.createFindQb(filter); + if (filter.accountId && filter.id && !Array.isArray(filter.id)) { + qb.cache(cacheKey({ accountId: filter.accountId, userId: filter.id }), 600000); + } + const user = await qb.getOne(); + + return user && options?.expand ? this.expandOne({ account: options.account, user, expand: options.expand }) : user; + } + + async findMany(filter: FindFilter, options?: FindOptions): Promise { + const users = await this.createFindQb(filter).orderBy('user.created_at', 'ASC').getMany(); + + return users.length && options?.expand + ? this.expandMany({ account: options.account, users, expand: options.expand }) + : users; + } + + async getCount(filter: FindFilter): Promise { + return this.createFindQb(filter).getCount(); + } + + async getCoworkerIds({ + accountId, + departmentIds, + }: { + accountId: number; + departmentIds: number | number[] | null; + }): Promise { + const cacheKey = `User.coworkers:${accountId}:${departmentIds}`; + return ( + await this.createFindQb({ accountId, departmentId: departmentIds }) + .select('user.id', 'id') + .cache(cacheKey, 15000) + .getRawMany<{ id: number }>() + ).map((u) => u.id); + } + + async update({ accountId, userId, dto }: { accountId: number; userId: number; dto: UpdateUserDto }): Promise { + const user = await this.findOne({ accountId, id: userId }); + if (!user) { + throw NotFoundError.withId(User, userId); + } + + if (dto.email && dto.email !== user.email && (await this.isEmailOccupied(dto.email))) { + throw EmailOccupiedError.fromEmail(dto.email); + } + + await this.repository.save(user.update(dto)); + + if (dto.accessibleUserIds !== undefined) { + await this.repositoryUAU.delete({ userId }); + user.accessibleUsers = await this.repositoryUAU.save( + dto.accessibleUserIds.map((accessibleId) => new UsersAccessibleUsers(userId, accessibleId)), + ); + } + + if (dto.objectPermissions) { + user.objectPermissions = await this.objectPermissionService.update({ + accountId: user.accountId, + userId: user.id, + hasDepartmentId: !!user.departmentId, + dtos: dto.objectPermissions, + }); + } + + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId })]); + + return user; + } + + async updateExt({ account, userId, dto }: { account: Account; userId: number; dto: UpdateUserDto }): Promise { + const user = await this.update({ accountId: account.id, userId, dto }); + + return this.expandOne({ account, user, expand: ['avatarUrl'] }); + } + + async changeDepartment({ + accountId, + departmentId, + newDepartmentId, + }: { + accountId: number; + departmentId: number; + newDepartmentId?: number | null; + }) { + await this.repository.update({ accountId, departmentId }, { departmentId: newDepartmentId ?? null }); + } + + async changePassword({ user, dto }: { user: User; dto: ChangeUserPasswordDto }): Promise { + const isValidPassword = PasswordUtil.verify(dto.currentPassword, user.password); + if (!isValidPassword) { + throw new BadCredentialsError(); + } + + // Validate new password strength + if (!PasswordUtil.isStrong(dto.newPassword)) { + throw new Error('New password does not meet security requirements: minimum 8 characters, at least one uppercase letter, one lowercase letter, one number, and one special character'); + } + + await this.repository.save(user.update({ password: dto.newPassword })); + return true; + } + + async ensureUserLimit({ accountId, user, userLimit }: { accountId: number; user: User | null; userLimit: number }) { + const activeUsers = await this.findMany({ accountId, isActive: true }); + if (activeUsers.length > userLimit) { + const owner = user ?? activeUsers.find((user) => user.role === UserRole.OWNER); + const usersToDelete = activeUsers.sort((a, b) => a.id - b.id).slice(userLimit); + for (const userToDelete of usersToDelete) { + await this.softDelete({ accountId, user: owner, userId: userToDelete.id, newUserId: owner?.id }); + } + } + } + + async delete({ accountId, userId }: { accountId: number; userId: number | number[] }) { + const ids = Array.isArray(userId) ? userId : [userId]; + await Promise.all( + ids.map(async (id) => { + await this.objectPermissionService.delete({ accountId, userId: id }); + await this.deleteAvatar({ accountId, userId: id }); + + await this.repository.delete({ accountId, id }); + + this.eventEmitter.emit(IamEventType.UserDeleted, new UserDeletedEvent({ accountId, userId: id })); + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId: id })]); + }), + ); + } + + async softDelete({ + accountId, + user, + userId, + newUserId, + }: { + accountId: number; + user: User; + userId: number; + newUserId?: number; + }) { + if (!user.isAdmin) { + throw new ForbiddenError(); + } + await this.repository.update({ accountId, id: userId }, { isActive: false }); + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId })]); + + this.eventEmitter.emit(IamEventType.UserDeleted, new UserDeletedEvent({ accountId, userId, newUserId })); + } + + async setAvatar({ account, userId, file }: { account: Account; userId: number; file: StorageFile }): Promise { + const user = await this.deleteAvatar({ accountId: account.id, userId }); + + const avatarFileInfo = await this.storageService.storeUserFile({ + accountId: account.id, + userId: user.id, + file, + section: 'avatar', + }); + if (avatarFileInfo) { + user.avatarId = avatarFileInfo.id; + await this.repository.save(user); + await this.storageService.markUsed({ accountId: account.id, id: avatarFileInfo.id }); + } + + return this.findOne({ accountId: account.id, id: userId }, { account, expand: ['avatarUrl', 'objectPermissions'] }); + } + + async removeAvatar({ account, userId }: { account: Account; userId: number }): Promise { + await this.deleteAvatar({ accountId: account.id, userId }); + + return this.findOne({ accountId: account.id, id: userId }, { account, expand: ['avatarUrl', 'objectPermissions'] }); + } + + private async deleteAvatar({ accountId, userId }: { accountId: number; userId: number }) { + const user = await this.findOne({ accountId, id: userId }); + if (user.avatarId) { + if (await this.storageService.delete({ accountId: user.accountId, id: user.avatarId })) { + user.avatarId = null; + await this.repository.save(user); + } + } + this.dataSource.queryResultCache?.remove([cacheKey({ accountId, userId })]); + return user; + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder('user').where('1 = 1'); + + qb.leftJoinAndMapMany('user.accessibleUsers', 'users_accessible_users', 'uau', 'uau.user_id = user.id'); + + if (filter.accountId) { + qb.andWhere('user.account_id = :accountId', { accountId: filter.accountId }); + } + + if (filter.id) { + if (Array.isArray(filter.id)) { + qb.andWhere('user.id IN (:...ids)', { ids: filter.id }); + } else { + qb.andWhere('user.id = :id', { id: filter.id }); + } + } + + if (filter.email) { + qb.andWhere('LOWER(user.email) = LOWER(:email)', { email: filter.email }); + } + + if (filter.isActive !== undefined) { + qb.andWhere('user.is_active = :isActive', { isActive: filter.isActive }); + } + + if (filter.departmentId) { + if (Array.isArray(filter.departmentId) && filter.departmentId.length) { + qb.andWhere('user.department_id IN (:...departmentIds)', { departmentIds: filter.departmentId }); + } else { + qb.andWhere('user.department_id = :departmentId', { departmentId: filter.departmentId }); + } + } + + if (filter.role) { + qb.andWhere('user.role = :role', { role: filter.role }); + } + + if (filter.fullName) { + qb.andWhere(`LOWER(user.first_name || ' ' || user.last_name) ilike :fullName`, { + fullName: `%${filter.fullName.trim()}%`, + }); + } + + return qb; + } + + private async expandOne({ + account, + user, + expand, + }: { + account: Account; + user: User; + expand: ExpandableField[]; + }): Promise { + if (user.avatarId && expand.includes('avatarUrl')) { + user.avatarUrl = this.storageUrlService.getImageUrl(account.id, account.subdomain, user.avatarId); + } + if (expand.includes('objectPermissions')) { + user.objectPermissions = await this.objectPermissionService.findMany({ accountId: account.id, userId: user.id }); + } + return user; + } + private async expandMany({ + account, + users, + expand, + }: { + account: Account; + users: User[]; + expand: ExpandableField[]; + }): Promise { + return await Promise.all(users.map((user) => this.expandOne({ account, user, expand }))); + } +} diff --git a/backend/src/modules/iam/working-time/index.ts b/backend/src/modules/iam/working-time/index.ts new file mode 100644 index 0000000..aa4cf7c --- /dev/null +++ b/backend/src/modules/iam/working-time/index.ts @@ -0,0 +1 @@ +export * from './working-time.service'; diff --git a/backend/src/modules/iam/working-time/working-time.service.ts b/backend/src/modules/iam/working-time/working-time.service.ts new file mode 100644 index 0000000..44242b9 --- /dev/null +++ b/backend/src/modules/iam/working-time/working-time.service.ts @@ -0,0 +1,90 @@ +import { Injectable } from '@nestjs/common'; + +import { AccountSettingsService } from '../account-settings/account-settings.service'; +import { DepartmentSettingsService } from '../department-settings/department-settings.service'; +import { UserService } from '../user/user.service'; +import { UserProfileService } from '../user-profile/user-profile.service'; + +interface WorkingTime { + timeZone: string | null; + workingDays: string[] | null; + workingTimeFrom: string | null; + workingTimeTo: string | null; +} + +@Injectable() +export class WorkingTimeService { + constructor( + private readonly accountSettingsService: AccountSettingsService, + private readonly departmentSettingsService: DepartmentSettingsService, + private readonly userService: UserService, + private readonly userProfileService: UserProfileService, + ) {} + + async getForUser({ accountId, userId }: { accountId: number; userId: number }): Promise { + const settings = await this.accountSettingsService.getOne(accountId); + const workingTime = { + timeZone: settings.timeZone, + workingDays: settings.workingDays, + workingTimeFrom: settings.workingTimeFrom, + workingTimeTo: settings.workingTimeTo, + }; + + const user = await this.userService.findOne({ accountId, id: userId }); + if (user?.departmentId) { + const depSettings = await this.departmentSettingsService.findOne({ accountId, departmentId: user.departmentId }); + if (depSettings) { + if (depSettings.workingDays) { + workingTime.workingDays = depSettings.workingDays; + } + if (depSettings.workingTimeFrom) { + workingTime.workingTimeFrom = depSettings.workingTimeFrom; + } + if (depSettings.workingTimeTo) { + workingTime.workingTimeTo = depSettings.workingTimeTo; + } + } + } + + const profile = await this.userProfileService.findOne({ accountId, userId }); + if (profile) { + if (profile.workingTimeFrom) { + workingTime.workingTimeFrom = profile.workingTimeFrom; + } + if (profile.workingTimeTo) { + workingTime.workingTimeTo = profile.workingTimeTo; + } + } + + return workingTime; + } + + async getForDepartment({ + accountId, + departmentId, + }: { + accountId: number; + departmentId: number; + }): Promise { + const settings = await this.accountSettingsService.getOne(accountId); + const workingTime = { + timeZone: settings.timeZone, + workingDays: settings.workingDays, + workingTimeFrom: settings.workingTimeFrom, + workingTimeTo: settings.workingTimeTo, + }; + + const depSettings = await this.departmentSettingsService.findOne({ accountId, departmentId }); + if (depSettings.workingDays) { + workingTime.workingDays = depSettings.workingDays; + } + if (depSettings.workingTimeFrom) { + workingTime.workingTimeFrom = depSettings.workingTimeFrom; + } + if (depSettings.workingTimeTo) { + workingTime.workingTimeTo = depSettings.workingTimeTo; + } + + return workingTime; + } +} diff --git a/backend/src/modules/integration/appsumo/appsumo.controller.ts b/backend/src/modules/integration/appsumo/appsumo.controller.ts new file mode 100644 index 0000000..adbacf8 --- /dev/null +++ b/backend/src/modules/integration/appsumo/appsumo.controller.ts @@ -0,0 +1,29 @@ +import { Body, Controller, Get, Post, Query, Redirect } from '@nestjs/common'; +import { ApiExcludeController, ApiOkResponse } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AppsumoWebhookRequest, AppsumoWebhookResponse } from './types'; +import { AppsumoService } from './appsumo.service'; + +@ApiExcludeController(true) +@Controller('integration/appsumo') +@TransformToDto() +export class AppsumoController { + constructor(private readonly service: AppsumoService) {} + + @ApiOkResponse({ description: 'AppSumo login redirect' }) + @Get('redirect') + @Redirect() + public async redirect(@Query('code') code: string) { + const redirectUrl = await this.service.redirect(code); + + return { url: redirectUrl, statusCode: 302 }; + } + + @ApiOkResponse({ description: 'AppSumo webhook', type: AppsumoWebhookResponse }) + @Post('webhook') + public async getUser(@Body() dto: AppsumoWebhookRequest) { + return this.service.webhook(dto); + } +} diff --git a/backend/src/modules/integration/appsumo/appsumo.module.ts b/backend/src/modules/integration/appsumo/appsumo.module.ts new file mode 100644 index 0000000..a6cf42b --- /dev/null +++ b/backend/src/modules/integration/appsumo/appsumo.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { AppsumoLicense, AppsumoTier } from './entities'; +import { AppsumoController } from './appsumo.controller'; +import { AppsumoService } from './appsumo.service'; +import { ConfigModule } from '@nestjs/config'; +import appsumoConfig from './config/appsumo.config'; + +@Module({ + imports: [ConfigModule.forFeature(appsumoConfig), TypeOrmModule.forFeature([AppsumoLicense, AppsumoTier]), IAMModule], + providers: [AppsumoService], + controllers: [AppsumoController], + exports: [AppsumoService], +}) +export class AppsumoModule {} diff --git a/backend/src/modules/integration/appsumo/appsumo.service.ts b/backend/src/modules/integration/appsumo/appsumo.service.ts new file mode 100644 index 0000000..898554c --- /dev/null +++ b/backend/src/modules/integration/appsumo/appsumo.service.ts @@ -0,0 +1,196 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { AxiosResponse } from 'axios'; +import { catchError, lastValueFrom } from 'rxjs'; +import { Repository } from 'typeorm'; + +import { DateUtil, FrontendRoute, UrlGeneratorService } from '@/common'; +import { AccountService } from '@/modules/iam/account/account.service'; +import { AccountSubscriptionService } from '@/modules/iam/account-subscription'; + +import { AppsumoConfig } from './config'; +import { AppsumoLicense, AppsumoTier } from './entities'; +import { AppsumoLicenseResponse, AppsumoTokenResponse, AppsumoWebhookRequest, AppsumoWebhookResponse } from './types'; +import { AppsumoEventType } from './enums'; + +const AppsumoUrls = { + base: 'https://appsumo.com', + oauth: () => AppsumoUrls.base + '/openid', + token: () => AppsumoUrls.oauth() + '/token', + licenseKey: () => AppsumoUrls.oauth() + '/license_key', +} as const; + +const RedirectPath = '/api/integration/appsumo/redirect'; + +interface Subscription { + termInDays?: number; + userLimit?: number; + planName?: string; + isTrial?: boolean; +} + +@Injectable() +export class AppsumoService { + private readonly logger = new Logger(AppsumoService.name); + private readonly _config: AppsumoConfig; + + constructor( + private readonly configService: ConfigService, + private readonly httpService: HttpService, + @InjectRepository(AppsumoLicense) + private readonly licenseRepository: Repository, + @InjectRepository(AppsumoTier) + private readonly tierRepository: Repository, + private readonly urlGenerator: UrlGeneratorService, + private readonly accountService: AccountService, + private readonly subscriptionService: AccountSubscriptionService, + ) { + this._config = this.configService.get('appsumo'); + } + + public async redirect(code: string | null | undefined): Promise { + if (code) { + const tokenData = await this.getToken(code); + if (tokenData?.access_token) { + const licenseData = await this.getLicense(tokenData.access_token); + if (licenseData?.license_key) { + const license = await this.licenseRepository.findOneBy({ licenseKey: licenseData.license_key }); + if (license?.accountId) { + const account = await this.accountService.findOne({ accountId: license.accountId }); + const tier = await this.tierRepository.findOneBy({ tier: license.tier }); + await this.subscriptionService.update({ accountId: account.id }, null, { + isTrial: false, + periodEnd: DateUtil.add(DateUtil.now(), { days: tier.termInDays }).toISOString(), + userLimit: tier.userLimit, + planName: tier.planName, + }); + + return this.urlGenerator.createUrl({ subdomain: account.subdomain }); + } else { + return this.urlGenerator.createUrl({ + route: FrontendRoute.signup, + query: { appsumo: licenseData.license_key }, + }); + } + } + } + } + + return this.urlGenerator.createUrl(); + } + + public async webhook(dto: AppsumoWebhookRequest): Promise { + if (!dto.test) { + const prevLicense = dto.prev_license_key + ? await this.licenseRepository.findOneBy({ licenseKey: dto.prev_license_key }) + : null; + const prevAccountId = prevLicense?.accountId ?? null; + if (prevLicense?.accountId) { + await this.licenseRepository.save(prevLicense.update({ accountId: null })); + } + let license = await this.licenseRepository.findOneBy({ licenseKey: dto.license_key }); + if (license) { + await this.licenseRepository.save( + license.update({ + licenseKey: dto.license_key, + licenseStatus: dto.license_status, + planId: dto.plan_id, + tier: dto.tier, + }), + ); + } else { + license = await this.licenseRepository.save( + new AppsumoLicense( + dto.license_key, + dto.prev_license_key, + dto.license_status, + dto.plan_id, + dto.tier, + prevAccountId, + ), + ); + } + + if (license.accountId) { + const tier = await this.tierRepository.findOneBy({ tier: license.tier }); + const periodEnd = + dto.event === AppsumoEventType.Deactivate + ? DateUtil.now() + : DateUtil.add(DateUtil.now(), { days: tier.termInDays }); + await this.subscriptionService.update({ accountId: license.accountId }, null, { + isTrial: false, + periodEnd: periodEnd.toISOString(), + userLimit: tier.userLimit, + planName: tier.planName, + }); + } + } + + return { + success: true, + event: dto.event, + }; + } + + public async findSubscription(licenseKey: string): Promise { + const license = await this.licenseRepository.findOneBy({ licenseKey }); + if (license) { + const tier = await this.tierRepository.findOneBy({ tier: license.tier }); + if (tier) { + return { + termInDays: tier.termInDays, + userLimit: tier.userLimit, + planName: tier.planName, + isTrial: false, + }; + } + } + + return null; + } + + public async update(licenseKey: string, dto: Partial): Promise { + const license = await this.licenseRepository.findOneBy({ licenseKey }); + if (license) { + return await this.licenseRepository.save(license.update(dto)); + } + + return null; + } + + private async getToken(code: string): Promise { + const { data } = await lastValueFrom>( + this.httpService + .post(AppsumoUrls.token(), { + client_id: this._config.clientId, + client_secret: this._config.clientSecret, + code: code, + redirect_uri: this.urlGenerator.createUrl({ route: RedirectPath }), + grant_type: 'authorization_code', + }) + .pipe( + catchError((error) => { + this.logger.error(`AppSumo get token error`, (error as Error)?.stack); + throw error; + }), + ), + ); + + return data; + } + + private async getLicense(accessToken: string): Promise { + const { data } = await lastValueFrom>( + this.httpService.get(AppsumoUrls.licenseKey(), { params: { access_token: accessToken } }).pipe( + catchError((error) => { + this.logger.error(`AppSumo get license error`, (error as Error)?.stack); + throw error; + }), + ), + ); + + return data; + } +} diff --git a/backend/src/modules/integration/appsumo/config/appsumo.config.ts b/backend/src/modules/integration/appsumo/config/appsumo.config.ts new file mode 100644 index 0000000..fa2cda9 --- /dev/null +++ b/backend/src/modules/integration/appsumo/config/appsumo.config.ts @@ -0,0 +1,16 @@ +import { registerAs } from '@nestjs/config'; + +export interface AppsumoConfig { + clientId: string; + clientSecret: string; + privateKey: string; +} + +export default registerAs( + 'appsumo', + (): AppsumoConfig => ({ + clientId: process.env.APPSUMO_CLIENT_ID, + clientSecret: process.env.APPSUMO_CLIENT_SECRET, + privateKey: process.env.APPSUMO_PRIVATE_KEY, + }), +); diff --git a/backend/src/modules/integration/appsumo/config/index.ts b/backend/src/modules/integration/appsumo/config/index.ts new file mode 100644 index 0000000..1942554 --- /dev/null +++ b/backend/src/modules/integration/appsumo/config/index.ts @@ -0,0 +1 @@ +export * from './appsumo.config'; diff --git a/backend/src/modules/integration/appsumo/entities/appsumo-license.entity.ts b/backend/src/modules/integration/appsumo/entities/appsumo-license.entity.ts new file mode 100644 index 0000000..26a573b --- /dev/null +++ b/backend/src/modules/integration/appsumo/entities/appsumo-license.entity.ts @@ -0,0 +1,75 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { AppsumoLicenseStatus } from '../enums'; + +@Entity() +export class AppsumoLicense { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + licenseKey: string; + + @Column({ nullable: true }) + prevLicenseKey: string | null; + + @Column() + licenseStatus: AppsumoLicenseStatus; + + @Column() + planId: string; + + @Column() + tier: number; + + @Column({ nullable: true }) + accountId: number | null; + + @Column({ type: Date }) + createdAt: Date; + + constructor( + licenseKey: string, + prevLicenseKey: string | null, + licenseStatus: AppsumoLicenseStatus, + planId: string, + tier: number, + accountId: number | null, + createdAt?: Date, + ) { + this.licenseKey = licenseKey; + this.prevLicenseKey = prevLicenseKey; + this.licenseStatus = licenseStatus; + this.planId = planId; + this.tier = tier; + this.accountId = accountId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public update({ + licenseKey, + prevLicenseKey, + licenseStatus, + planId, + tier, + accountId, + }: { + licenseKey?: string; + prevLicenseKey?: string | null; + licenseStatus?: AppsumoLicenseStatus; + planId?: string; + tier?: number; + accountId?: number | null; + }): AppsumoLicense { + this.licenseKey = licenseKey !== undefined ? licenseKey : this.licenseKey; + this.prevLicenseKey = prevLicenseKey !== undefined ? prevLicenseKey : this.prevLicenseKey; + this.licenseStatus = licenseStatus !== undefined ? licenseStatus : this.licenseStatus; + this.planId = planId !== undefined ? planId : this.planId; + this.tier = tier !== undefined ? tier : this.tier; + this.accountId = accountId !== undefined ? accountId : this.accountId; + + return this; + } +} diff --git a/backend/src/modules/integration/appsumo/entities/appsumo-tier.entity.ts b/backend/src/modules/integration/appsumo/entities/appsumo-tier.entity.ts new file mode 100644 index 0000000..5f941d3 --- /dev/null +++ b/backend/src/modules/integration/appsumo/entities/appsumo-tier.entity.ts @@ -0,0 +1,26 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity() +export class AppsumoTier { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + tier: number; + + @Column() + userLimit: number; + + @Column() + termInDays: number; + + @Column() + planName: string; + + constructor(tier: number, userLimit: number, termInDays: number, planName: string) { + this.tier = tier; + this.userLimit = userLimit; + this.termInDays = termInDays; + this.planName = planName; + } +} diff --git a/backend/src/modules/integration/appsumo/entities/index.ts b/backend/src/modules/integration/appsumo/entities/index.ts new file mode 100644 index 0000000..0614260 --- /dev/null +++ b/backend/src/modules/integration/appsumo/entities/index.ts @@ -0,0 +1,2 @@ +export * from './appsumo-license.entity'; +export * from './appsumo-tier.entity'; diff --git a/backend/src/modules/integration/appsumo/enums/appsumo-event-type.enum.ts b/backend/src/modules/integration/appsumo/enums/appsumo-event-type.enum.ts new file mode 100644 index 0000000..4a2bf28 --- /dev/null +++ b/backend/src/modules/integration/appsumo/enums/appsumo-event-type.enum.ts @@ -0,0 +1,7 @@ +export enum AppsumoEventType { + Activate = 'activate', + Deactivate = 'deactivate', + Purchase = 'purchase', + Upgrade = 'upgrade', + Downgrade = 'downgrade', +} diff --git a/backend/src/modules/integration/appsumo/enums/appsumo-license-status.enum.ts b/backend/src/modules/integration/appsumo/enums/appsumo-license-status.enum.ts new file mode 100644 index 0000000..9a953e7 --- /dev/null +++ b/backend/src/modules/integration/appsumo/enums/appsumo-license-status.enum.ts @@ -0,0 +1,5 @@ +export enum AppsumoLicenseStatus { + Inactive = 'inactive', + Active = 'active', + Deactivated = 'deactivated', +} diff --git a/backend/src/modules/integration/appsumo/enums/index.ts b/backend/src/modules/integration/appsumo/enums/index.ts new file mode 100644 index 0000000..5dc421b --- /dev/null +++ b/backend/src/modules/integration/appsumo/enums/index.ts @@ -0,0 +1,2 @@ +export * from './appsumo-event-type.enum'; +export * from './appsumo-license-status.enum'; diff --git a/backend/src/modules/integration/appsumo/index.ts b/backend/src/modules/integration/appsumo/index.ts new file mode 100644 index 0000000..ddea7f7 --- /dev/null +++ b/backend/src/modules/integration/appsumo/index.ts @@ -0,0 +1,6 @@ +export * from './appsumo.controller'; +export * from './appsumo.module'; +export * from './appsumo.service'; +export * from './entities'; +export * from './enums'; +export * from './types'; diff --git a/backend/src/modules/integration/appsumo/types/appsumo-license-response.ts b/backend/src/modules/integration/appsumo/types/appsumo-license-response.ts new file mode 100644 index 0000000..19f1fb6 --- /dev/null +++ b/backend/src/modules/integration/appsumo/types/appsumo-license-response.ts @@ -0,0 +1,7 @@ +import { AppsumoLicenseStatus } from '../enums'; + +export class AppsumoLicenseResponse { + license_key: string; + status: AppsumoLicenseStatus; + scopes: string[]; +} diff --git a/backend/src/modules/integration/appsumo/types/appsumo-token-response.ts b/backend/src/modules/integration/appsumo/types/appsumo-token-response.ts new file mode 100644 index 0000000..a1f380c --- /dev/null +++ b/backend/src/modules/integration/appsumo/types/appsumo-token-response.ts @@ -0,0 +1,8 @@ +export class AppsumoTokenResponse { + access_token: string; + token_type: string; + expires_in: number; + refresh_token: string; + id_token: string; + error: string; +} diff --git a/backend/src/modules/integration/appsumo/types/appsumo-webhook-request.ts b/backend/src/modules/integration/appsumo/types/appsumo-webhook-request.ts new file mode 100644 index 0000000..f66b0f1 --- /dev/null +++ b/backend/src/modules/integration/appsumo/types/appsumo-webhook-request.ts @@ -0,0 +1,14 @@ +import { AppsumoEventType, AppsumoLicenseStatus } from '../enums'; + +export interface AppsumoWebhookRequest { + license_key: string; + prev_license_key?: string; + plan_id: string; + event: AppsumoEventType; + event_timestamp: number; + created_at: number; + license_status: AppsumoLicenseStatus; + tier: number; + test: boolean; + extra: { reason: string }; +} diff --git a/backend/src/modules/integration/appsumo/types/appsumo-webhook-response.ts b/backend/src/modules/integration/appsumo/types/appsumo-webhook-response.ts new file mode 100644 index 0000000..7590eb8 --- /dev/null +++ b/backend/src/modules/integration/appsumo/types/appsumo-webhook-response.ts @@ -0,0 +1,7 @@ +import { AppsumoEventType } from '../enums'; + +export class AppsumoWebhookResponse { + success: boolean; + event: AppsumoEventType; + message?: string; +} diff --git a/backend/src/modules/integration/appsumo/types/index.ts b/backend/src/modules/integration/appsumo/types/index.ts new file mode 100644 index 0000000..d2dd284 --- /dev/null +++ b/backend/src/modules/integration/appsumo/types/index.ts @@ -0,0 +1,4 @@ +export * from './appsumo-license-response'; +export * from './appsumo-token-response'; +export * from './appsumo-webhook-request'; +export * from './appsumo-webhook-response'; diff --git a/backend/src/modules/integration/google/auth/auth.module.ts b/backend/src/modules/integration/google/auth/auth.module.ts new file mode 100644 index 0000000..64a6c1a --- /dev/null +++ b/backend/src/modules/integration/google/auth/auth.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { AuthService } from './auth.service'; + +@Module({ + providers: [AuthService], + exports: [AuthService], +}) +export class AuthModule {} diff --git a/backend/src/modules/integration/google/auth/auth.service.ts b/backend/src/modules/integration/google/auth/auth.service.ts new file mode 100644 index 0000000..1aa9a07 --- /dev/null +++ b/backend/src/modules/integration/google/auth/auth.service.ts @@ -0,0 +1,74 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { google, Auth } from 'googleapis'; + +import { DateUtil, UrlGeneratorService } from '@/common'; + +import { GoogleConfig } from '../google.config'; + +interface AuthParams { + clientId?: string; + clientSecret?: string; + subdomain?: string; + callbackPath?: string; + tokens?: Auth.Credentials; +} + +@Injectable() +export class AuthService { + private readonly _config: GoogleConfig; + constructor( + private readonly configService: ConfigService, + private readonly urlGenerator: UrlGeneratorService, + ) { + this._config = this.configService.get('google'); + } + + public async getOAuth2Client({ clientId, clientSecret, subdomain, callbackPath, tokens }: AuthParams = {}): Promise<{ + client: Auth.OAuth2Client; + refreshedTokens: Auth.Credentials | undefined; + }> { + const redirectUri = callbackPath ? this.urlGenerator.createUrl({ route: callbackPath, subdomain }) : undefined; + + const client = new google.auth.OAuth2( + clientId ?? this._config.auth.clientId, + clientSecret ?? this._config.auth.clientSecret, + redirectUri, + ); + + let refreshedTokens: Auth.Credentials | undefined = undefined; + if (tokens) { + client.setCredentials(tokens); + + if (tokens.expiry_date < DateUtil.now().getTime()) { + const { credentials } = await client.refreshAccessToken(); + refreshedTokens = credentials; + client.setCredentials(refreshedTokens); + } + } + + return { client, refreshedTokens }; + } + + public async generateAuthUrl({ + auth, + scope, + state, + }: { auth?: AuthParams; scope?: string | string[]; state?: string } = {}): Promise { + const { client } = await this.getOAuth2Client(auth); + return client.generateAuthUrl({ + access_type: 'offline', + prompt: 'consent', + include_granted_scopes: true, + scope, + state, + }); + } + + public async getToken({ auth, code }: { auth?: AuthParams; code: string }): Promise { + const { client } = await this.getOAuth2Client(auth); + const { tokens } = await client.getToken(code); + + return tokens; + } +} diff --git a/backend/src/modules/integration/google/calendar/calendar.controller.ts b/backend/src/modules/integration/google/calendar/calendar.controller.ts new file mode 100644 index 0000000..4b1cfa6 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/calendar.controller.ts @@ -0,0 +1,101 @@ +import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CalendarAccessDto, CreateGoogleCalendarDto, GoogleCalendarDto, UpdateGoogleCalendarDto } from './dto'; +import { CalendarService } from './calendar.service'; + +@ApiTags('integration/google/calendar') +@Controller('integration/google/calendar') +@JwtAuthorized() +@TransformToDto() +export class CalendarController { + constructor(private readonly service: CalendarService) {} + + @ApiOperation({ + summary: 'Generate authorization URL', + description: 'Generate Google authorization URL for Calendar integration', + }) + @ApiOkResponse({ type: String, description: 'Google authorization URL' }) + @Get('authorize-url') + public async getAuthorizeUrl(@CurrentAuth() { accountId, userId }: AuthData): Promise { + return this.service.getAuthorizeUrl({ accountId, userId }); + } + + @ApiOperation({ + summary: 'Process authorization code', + description: 'Process Google authorization code for Calendar integration and get accessible calendars', + }) + @ApiQuery({ name: 'code', type: String, required: true, description: 'Google authorization code' }) + @ApiQuery({ name: 'state', type: String, required: false, description: 'State' }) + @ApiOkResponse({ type: CalendarAccessDto, description: 'Accessible calendars and access token' }) + @Get('process-code') + public async processAuthCode(@Query('code') code: string, @Query('state') state?: string) { + return this.service.processAuthCode({ code, state }); + } + + @ApiOperation({ + summary: 'Create Google calendar integration', + description: 'Create Google calendar integration', + }) + @ApiBody({ + type: CreateGoogleCalendarDto, + required: true, + description: 'Data for creating Google calendar integration', + }) + @ApiOkResponse({ type: GoogleCalendarDto, description: 'Google calendar integration' }) + @Post() + public async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateGoogleCalendarDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Find Google calendars integrations', description: 'Find Google calendars integrations' }) + @ApiOkResponse({ type: [GoogleCalendarDto], description: 'Google calendars integrations' }) + @Get() + @AuthDataPrefetch({ user: true }) + public async findMany(@CurrentAuth() { accountId, user }: AuthData) { + return this.service.findMany({ accountId, user }); + } + + @ApiOperation({ summary: 'Find Google calendar integration', description: 'Find Google calendar integration' }) + @ApiParam({ name: 'calendarId', type: Number, required: true, description: 'Google calendar ID' }) + @ApiOkResponse({ type: GoogleCalendarDto, description: 'Google calendar integration' }) + @Get(':calendarId') + @AuthDataPrefetch({ user: true }) + public async findOne(@CurrentAuth() { accountId, user }: AuthData, @Param('calendarId') calendarId: number) { + return this.service.findOne({ accountId, user, calendarId }); + } + + @ApiOperation({ + summary: 'Update Google calendar integration', + description: 'Update Google calendar integration', + }) + @ApiParam({ name: 'calendarId', type: Number, required: true, description: 'Google calendar ID' }) + @ApiBody({ + type: UpdateGoogleCalendarDto, + required: true, + description: 'Data for updating Google calendar integration', + }) + @ApiOkResponse({ type: GoogleCalendarDto, description: 'Google calendar integration' }) + @Patch(':calendarId') + public async update( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('calendarId') calendarId: number, + @Body() dto: UpdateGoogleCalendarDto, + ) { + return this.service.update({ accountId, userId, calendarId, dto }); + } + + @ApiOperation({ + summary: 'Delete Google calendar integration', + description: 'Delete Google calendar integration', + }) + @ApiParam({ name: 'calendarId', type: Number, required: true, description: 'Google calendar ID' }) + @ApiOkResponse({ type: Number, description: 'Deleted Google calendar ID' }) + @Delete(':calendarId') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('calendarId') calendarId: number) { + return this.service.delete({ accountId, calendarId }); + } +} diff --git a/backend/src/modules/integration/google/calendar/calendar.emitter.ts b/backend/src/modules/integration/google/calendar/calendar.emitter.ts new file mode 100644 index 0000000..eb202e9 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/calendar.emitter.ts @@ -0,0 +1,119 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { calendar_v3 } from 'googleapis'; + +import { ServiceEvent } from '@/common'; +import { SchedulerAppointmentExtUpsertEvent, SchedulerEventType } from '@/modules/scheduler/common'; +import { CrmEventType, TaskExtEvent, TaskExtUpsertEvent } from '@/CRM/common'; + +import { GoogleCalendar } from './entities'; +import { CalendarType } from './enums'; +import { CalendarEvent } from './types'; +import { AppointmentUtil, EventUtil } from './utils'; + +@Injectable() +export class CalendarEmitter { + constructor(private readonly eventEmitter: EventEmitter2) {} + + public async emit({ calendar, event }: { calendar: GoogleCalendar; event: calendar_v3.Schema$Event }) { + const calendarEvent = EventUtil.getCalendarEvent(event); + const objectIds = [calendar.objectId, ...(calendar.linked?.map((linked) => linked.objectId) ?? [])]; + if (!calendarEvent.objectId || !objectIds.includes(calendarEvent.objectId)) { + calendarEvent.objectId = null; + calendarEvent.eventId = null; + } + + const eventType = this.getEventType({ calendar, event }); + const eventData = this.getEventData({ calendar, event, calendarEvent }); + + this.eventEmitter.emit(eventType, eventData); + } + + private getEventType({ calendar, event }: { calendar: GoogleCalendar; event: calendar_v3.Schema$Event }): string { + switch (calendar.type) { + case CalendarType.Task: + return event.status === 'cancelled' ? CrmEventType.TaskDeleteExt : CrmEventType.TaskUpsertExt; + case CalendarType.Schedule: + return SchedulerEventType.ScheduleAppointmentUpsertExt; + } + } + + private getEventData({ + calendar, + event, + calendarEvent, + }: { + calendar: GoogleCalendar; + event: calendar_v3.Schema$Event; + calendarEvent: CalendarEvent; + }): ServiceEvent { + switch (calendar.type) { + case CalendarType.Task: + return this.getEventDataTask({ calendar, event, calendarEvent }); + case CalendarType.Schedule: + return this.getEventDataSchedule({ calendar, event, calendarEvent }); + } + } + + private getEventDataTask({ + calendar, + event, + calendarEvent, + }: { + calendar: GoogleCalendar; + event: calendar_v3.Schema$Event; + calendarEvent: CalendarEvent; + }): ServiceEvent { + const startDate = EventUtil.getEventDate(event.start); + const endDate = EventUtil.getEventDate(event.end); + + return event.status === 'cancelled' + ? new TaskExtEvent({ + source: GoogleCalendar.name, + externalId: calendarEvent.externalId, + accountId: calendar.accountId, + boardId: calendarEvent.objectId ?? calendar.objectId, + taskId: calendarEvent.eventId, + }) + : new TaskExtUpsertEvent({ + source: GoogleCalendar.name, + externalId: calendarEvent.externalId, + accountId: calendar.accountId, + boardId: calendarEvent.objectId ?? calendar.objectId, + taskId: calendarEvent.eventId, + ownerId: calendar.responsibleId, + title: event.summary, + text: event.description, + startDate, + endDate, + }); + } + + private getEventDataSchedule({ + calendar, + event, + calendarEvent, + }: { + calendar: GoogleCalendar; + event: calendar_v3.Schema$Event; + calendarEvent: CalendarEvent; + }): ServiceEvent { + const startDate = EventUtil.getEventDate(event.start); + const endDate = EventUtil.getEventDate(event.end); + + return new SchedulerAppointmentExtUpsertEvent({ + source: GoogleCalendar.name, + externalId: calendarEvent.externalId, + accountId: calendar.accountId, + scheduleId: calendarEvent.objectId ?? calendar.objectId, + appointmentId: calendarEvent.eventId, + performerId: calendar.responsibleId, + ownerId: calendar.createdBy, + title: event.summary, + comment: event.description, + startDate, + endDate, + status: AppointmentUtil.convertEventStatus(event.status), + }); + } +} diff --git a/backend/src/modules/integration/google/calendar/calendar.handler.ts b/backend/src/modules/integration/google/calendar/calendar.handler.ts new file mode 100644 index 0000000..fb860c5 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/calendar.handler.ts @@ -0,0 +1,179 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { Cron, CronExpression } from '@nestjs/schedule'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; +import { + ScheduleEvent, + SchedulePerformerDeletedEvent, + SchedulerAppointmentCreatedEvent, + SchedulerAppointmentEvent, + SchedulerAppointmentUpdatedEvent, + SchedulerEventType, +} from '@/modules/scheduler/common'; +import { BoardEvent, CrmEventType, TaskCreatedEvent, TaskEvent, TaskUpdatedEvent } from '@/CRM/common'; + +import { GoogleCalendar } from './entities'; +import { CalendarType } from './enums'; +import { AppointmentUtil } from './utils'; +import { CalendarService } from './calendar.service'; + +@Injectable() +export class CalendarHandler { + private readonly logger = new Logger(CalendarHandler.name); + constructor(private readonly service: CalendarService) {} + + @Cron(CronExpression.EVERY_HOUR) + public async synchronizeActive() { + if (process.env.SCHEDULE_INTEGRATION_GOOGLE_CALENDAR_DISABLE === 'true') return; + this.logger.log('Before: Renew registered Google Calendar channels'); + const count = await this.service.renewChannels(); + this.logger.log(`After: Renew registered Google Calendar channels. Processed: ${count}`); + } + + @OnEvent(CrmEventType.TaskCreated, { async: true }) + public async onTaskCreated(event: TaskCreatedEvent) { + if (!event.checkHistory({ source: GoogleCalendar.name })) { + if (event.startDate && event.endDate) { + this.service.handleUpsert({ + accountId: event.accountId, + ownerId: event.ownerId, + objectId: event.boardId, + eventId: event.taskId, + type: CalendarType.Task, + title: event.taskTitle, + description: event.taskText, + startDate: event.startDate, + endDate: event.endDate, + entityId: event.entityId, + externalId: event.externalId, + }); + } + } + } + + @OnEvent(CrmEventType.TaskUpdated, { async: true }) + public async onTaskUpdated(event: TaskUpdatedEvent) { + if (!event.checkHistory({ source: GoogleCalendar.name })) { + if (event.startDate && event.endDate) { + this.service.handleUpsert({ + accountId: event.accountId, + ownerId: event.ownerId, + objectId: event.boardId, + eventId: event.taskId, + type: CalendarType.Task, + title: event.taskTitle, + description: event.taskText, + startDate: event.startDate, + endDate: event.endDate, + entityId: event.entityId, + externalId: event.externalId, + }); + } + } + } + + @OnEvent(CrmEventType.TaskDeleted, { async: true }) + public async onTaskDeleted(event: TaskEvent) { + if (!event.checkHistory({ source: GoogleCalendar.name })) { + this.service.handleDeleted({ + accountId: event.accountId, + objectId: event.boardId, + eventId: event.taskId, + type: CalendarType.Task, + externalId: event.externalId, + }); + } + } + + @OnEvent(SchedulerEventType.ScheduleAppointmentCreated, { async: true }) + public async onScheduleAppointmentCreated(event: SchedulerAppointmentCreatedEvent) { + if (!event.checkHistory({ source: GoogleCalendar.name })) { + if (event.startDate && event.endDate) { + this.service.handleUpsert({ + accountId: event.accountId, + ownerId: event.performerId, + objectId: event.scheduleId, + eventId: event.appointmentId, + type: CalendarType.Schedule, + title: event.title, + description: event.comment, + startDate: event.startDate, + endDate: event.endDate, + status: AppointmentUtil.convertAppointmentStatus(event.status), + entityId: event.entityId, + externalId: event.externalId, + }); + } + } + } + + @OnEvent(SchedulerEventType.ScheduleAppointmentUpdated, { async: true }) + public async onScheduleAppointmentUpdated(event: SchedulerAppointmentUpdatedEvent) { + if (!event.checkHistory({ source: GoogleCalendar.name })) { + if (event.startDate && event.endDate) { + this.service.handleUpsert({ + accountId: event.accountId, + ownerId: event.performerId, + objectId: event.scheduleId, + eventId: event.appointmentId, + type: CalendarType.Schedule, + title: event.title, + description: event.comment, + startDate: event.startDate, + endDate: event.endDate, + status: AppointmentUtil.convertAppointmentStatus(event.status), + entityId: event.entityId, + externalId: event.externalId, + }); + } + } + } + + @OnEvent(SchedulerEventType.ScheduleAppointmentDeleted, { async: true }) + public async onScheduleAppointmentDeleted(event: SchedulerAppointmentEvent) { + if (!event.checkHistory({ source: GoogleCalendar.name })) { + this.service.handleDeleted({ + accountId: event.accountId, + objectId: event.scheduleId, + eventId: event.appointmentId, + type: CalendarType.Schedule, + externalId: event.externalId, + }); + } + } + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.service.handleDeleteByResponsible({ + accountId: event.accountId, + type: CalendarType.Task, + responsibleId: event.userId, + newResponsibleId: event.newUserId, + }); + } + + @OnEvent(CrmEventType.BoardDeleted, { async: true }) + public async onBoardDeleted(event: BoardEvent) { + this.service.handleDeleteByObject({ accountId: event.accountId, type: CalendarType.Task, objectId: event.boardId }); + } + + @OnEvent(SchedulerEventType.ScheduleDeleted, { async: true }) + public async onScheduleDeleted(event: ScheduleEvent) { + await this.service.handleDeleteByObject({ + accountId: event.accountId, + type: CalendarType.Schedule, + objectId: event.scheduleId, + }); + } + + @OnEvent(SchedulerEventType.SchedulePerformerDeleted, { async: true }) + public async onSchedulePerformerDeleted(event: SchedulePerformerDeletedEvent) { + await this.service.handleDeleteByResponsible({ + accountId: event.accountId, + type: CalendarType.Schedule, + responsibleId: event.performerId, + newResponsibleId: event.newPerformerId, + }); + } +} diff --git a/backend/src/modules/integration/google/calendar/calendar.module.ts b/backend/src/modules/integration/google/calendar/calendar.module.ts new file mode 100644 index 0000000..500f22f --- /dev/null +++ b/backend/src/modules/integration/google/calendar/calendar.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { IAMModule } from '@/modules/iam/iam.module'; +import { AuthModule } from '../auth/auth.module'; + +import { GoogleCalendar, GoogleCalendarAccount, GoogleCalendarLinked } from './entities'; +import { CalendarService } from './calendar.service'; +import { CalendarHandler } from './calendar.handler'; +import { CalendarEmitter } from './calendar.emitter'; +import { CalendarController } from './calendar.controller'; +import { PublicCalendarController } from './public-calendar.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([GoogleCalendarAccount, GoogleCalendar, GoogleCalendarLinked]), + IAMModule, + AuthModule, + EntityInfoModule, + ], + providers: [CalendarService, CalendarHandler, CalendarEmitter], + controllers: [PublicCalendarController, CalendarController], +}) +export class CalendarModule {} diff --git a/backend/src/modules/integration/google/calendar/calendar.service.ts b/backend/src/modules/integration/google/calendar/calendar.service.ts new file mode 100644 index 0000000..33eeb04 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/calendar.service.ts @@ -0,0 +1,867 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Auth, calendar_v3, google } from 'googleapis'; +import { Brackets, Repository } from 'typeorm'; +import { v4 as uuidv4 } from 'uuid'; + +import { + BadRequestError, + DateUtil, + formatState, + FrontendRoute, + NotFoundError, + parseState, + TokenService, + UrlGeneratorService, +} from '@/common'; +import { EntityInfoService } from '@/modules/entity/entity-info'; +import { AccountService } from '@/modules/iam/account/account.service'; +import { User } from '@/modules/iam/user/entities'; + +import { AuthService } from '../auth/auth.service'; +import { CreateGoogleCalendarDto, GoogleCalendarLinkedDto, UpdateGoogleCalendarDto } from './dto'; +import { CalendarType } from './enums'; +import { GoogleCalendar, GoogleCalendarAccount, GoogleCalendarLinked } from './entities'; +import { CalendarAccess, CalendarEvent, CalendarInfo, CalendarUpsertEvent, EventExtendedProperties } from './types'; +import { CalendarEmitter } from './calendar.emitter'; +import { EventUtil } from './utils'; + +interface FindFilter { + accountId: number; + calendarId?: number; + calendarAccountId?: number; + createdBy?: number; + externalId?: string; + type?: CalendarType; + objectId?: number; + linkedObjectId?: number; + processAll?: boolean; + responsibleId?: number; +} + +interface EventSearchParams { + api: calendar_v3.Calendar; + calendar: GoogleCalendar; + extendedProperties: EventExtendedProperties; + externalId?: string | null; +} +interface CalendarActionParams { + api: calendar_v3.Calendar; + calendar: GoogleCalendar; + event: E; + current: calendar_v3.Schema$Event | null; + extendedProperties: EventExtendedProperties; +} +type CalendarAction = (params: CalendarActionParams) => Promise; +type ApiAction = (params: { api: calendar_v3.Calendar; calendar: GoogleCalendar }) => Promise; + +const CallbackPath = '/api/integration/google/calendar/callback'; +const WebhookPath = { + calendars: '/api/integration/google/calendar/webhook-calendars', + events: '/api/integration/google/calendar/webhook-events', +}; + +@Injectable() +export class CalendarService { + private readonly logger = new Logger(CalendarService.name); + constructor( + @InjectRepository(GoogleCalendarAccount) + private readonly repositoryAccount: Repository, + @InjectRepository(GoogleCalendar) + private readonly repositoryCalendar: Repository, + @InjectRepository(GoogleCalendarLinked) + private readonly repositoryLinked: Repository, + private readonly tokenService: TokenService, + private readonly urlGenerator: UrlGeneratorService, + private readonly authService: AuthService, + private readonly accountService: AccountService, + private readonly entityInfoService: EntityInfoService, + private readonly calendarEmitter: CalendarEmitter, + ) {} + + public async getAuthorizeUrl({ accountId, userId }: { accountId: number; userId: number }): Promise { + return this.authService.generateAuthUrl({ + auth: { callbackPath: CallbackPath }, + scope: [ + 'https://www.googleapis.com/auth/calendar.calendarlist.readonly', + 'https://www.googleapis.com/auth/calendar.events', + ], + state: formatState(accountId, userId), + }); + } + + public async getRedirectUrl({ state, code }: { state: string; code: string }): Promise { + const [accountIdStr] = parseState(state, String); + if (accountIdStr) { + const accountId = Number(accountIdStr); + const account = await this.accountService.findOne({ accountId }); + if (account) { + return this.urlGenerator.createUrl({ + route: FrontendRoute.settings.google.calendar(), + subdomain: account.subdomain, + query: { code }, + }); + } + } + return null; + } + + public async processAuthCode({ code }: { code: string; state?: string }): Promise { + const tokens = await this.authService.getToken({ + auth: { callbackPath: CallbackPath }, + code, + }); + + const calendars = await this.getCalendarList(tokens); + const calendarInfos = calendars.filter((c) => !c.deleted && !c.hidden).map(CalendarInfo.fromApi); + + return new CalendarAccess({ calendarInfos, token: this.tokenService.create(tokens) }); + } + + public async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateGoogleCalendarDto; + }): Promise { + const tokens = this.tokenService.verify(dto.token); + + const calendarAccount = await this.getCalendarAccount({ accountId, tokens }); + const calendar = await this.repositoryCalendar.save( + GoogleCalendar.fromDto({ accountId, createdBy: userId, calendarAccountId: calendarAccount.id, dto }), + ); + + if (dto.linked) { + calendar.linked = await this.processLinked({ accountId, calendarId: calendar.id, dtos: dto.linked }); + } + + return this.withApi({ + calendar, + calendarAccount, + action: async ({ api, calendar }) => { + const from = dto.syncEvents ? DateUtil.startOf(DateUtil.now(), 'day') : undefined; + calendar = await this.syncEvents({ api, calendar, from }); + return this.startWatchEvents({ api, calendar }); + }, + }); + } + + public async findOne(filter: FindFilter & { user?: User }): Promise { + const createdBy = filter.user?.isAdmin ? undefined : filter.createdBy || filter.user?.id; + + return this.createFindQb({ ...filter, createdBy }).getOne(); + } + public async findMany(filter: FindFilter & { user?: User }): Promise { + const createdBy = filter.user?.isAdmin ? undefined : filter.createdBy || filter.user?.id; + + return this.createFindQb({ ...filter, createdBy }) + .orderBy('calendar.created_at', 'ASC') + .getMany(); + } + + public async update({ + accountId, + calendarId, + dto, + }: { + accountId: number; + userId: number; + calendarId: number; + dto: UpdateGoogleCalendarDto; + }): Promise { + const calendar = await this.findOne({ accountId, calendarId }); + if (!calendar) { + throw NotFoundError.withId(GoogleCalendar, calendarId); + } + + await this.repositoryCalendar.save(calendar.update(dto)); + + if (dto.linked) { + calendar.linked = await this.processLinked({ + accountId, + calendarId: calendar.id, + linked: calendar.linked, + dtos: dto.linked, + }); + } + + return calendar; + } + + public async delete({ accountId, calendarId }: { accountId: number; calendarId: number }): Promise { + const calendar = await this.findOne({ accountId, calendarId }); + if (!calendar) { + throw NotFoundError.withId(GoogleCalendar, calendarId); + } + + await this.deleteCalendar({ accountId, calendar }); + + return calendar.id; + } + + private async deleteCalendar({ + accountId, + calendar, + }: { + accountId: number; + calendar: GoogleCalendar; + }): Promise { + try { + await this.withApi({ calendar, action: (param) => this.stopWatchEvents(param) }); + } catch (e) { + this.logger.warn(`Failed to stop watch events: ${e}`); + } + await this.repositoryCalendar.delete({ accountId, id: calendar.id }); + + await this.cleanCalendarAccount({ accountId, calendarAccountId: calendar.calendarAccountId }); + + return calendar.id; + } + + public async processWebhookCalendars({ + channelId, + resourceId, + resourceState, + }: { + channelId: string; + resourceId: string; + resourceState: string; + }) { + if (resourceState === 'exists') { + const accounts = await this.repositoryAccount.find({ where: { channelId, channelResourceId: resourceId } }); + await Promise.all( + accounts.map(async (calendarAccount) => { + const api = await this.getApi(calendarAccount); + if (api) { + await this.syncCalendars({ api, calendarAccount, incremental: true }); + } + }), + ); + } + } + + public async processWebhookEvents({ + channelId, + resourceId, + resourceState, + }: { + channelId: string; + resourceId: string; + resourceState: string; + }) { + if (resourceState === 'exists') { + const calendars = await this.repositoryCalendar + .createQueryBuilder('calendar') + .leftJoinAndMapMany('calendar.linked', GoogleCalendarLinked, 'link', 'link.calendar_id = calendar.id') + .where('calendar.channel_id = :channelId', { channelId }) + .andWhere('calendar.channel_resource_id = :resourceId', { resourceId }) + .getMany(); + + await Promise.all( + calendars.map((calendar) => + this.withApi({ calendar, action: (param) => this.syncEvents({ ...param, incremental: true }) }), + ), + ); + } + } + + public async renewChannels(): Promise { + const accounts = await this.repositoryAccount + .createQueryBuilder() + .where('channel_expiration IS NOT NULL') + .andWhere(`channel_expiration < now() + interval '1 hour'`) + .getMany(); + + await Promise.all( + accounts.map(async (account) => { + const api = await this.getApi(account); + if (api) { + await this.stopWatchCalendars({ api, calendarAccount: account }); + return this.startWatchCalendars({ api, calendarAccount: account }); + } + return null; + }), + ); + + const calendars = await this.repositoryCalendar + .createQueryBuilder() + .where('channel_expiration IS NOT NULL') + .andWhere(`channel_expiration < now() + interval '1 hour'`) + .getMany(); + + await Promise.all( + calendars.map(async (calendar) => { + await this.withApi({ + calendar, + action: async (param) => { + await this.stopWatchEvents(param); + return this.startWatchEvents(param); + }, + }); + }), + ); + + return (accounts.length ?? 0) + (calendars.length ?? 0); + } + + public async handleUpsert(event: CalendarUpsertEvent) { + await this.processEvent({ event, action: (params) => this.upsertEvent(params) }); + } + + public async handleDeleted(event: CalendarEvent) { + await this.processEvent({ event, action: (params) => this.deleteEvent(params) }); + } + + public async handleDeleteByObject({ + accountId, + type, + objectId, + }: { + accountId: number; + type: CalendarType; + objectId: number; + }) { + const calendars = await this.findMany({ accountId, type, objectId }); + await Promise.all( + calendars.map(async (calendar) => { + await this.deleteCalendar({ accountId, calendar }); + }), + ); + await this.repositoryLinked.delete({ accountId, objectId, type }); + } + + public async handleDeleteByResponsible({ + accountId, + type, + responsibleId, + newResponsibleId, + }: { + accountId: number; + type: CalendarType; + responsibleId: number; + newResponsibleId?: number | null; + }) { + if (newResponsibleId) { + await this.repositoryCalendar.update({ accountId, type, responsibleId }, { responsibleId: newResponsibleId }); + } else { + const calendars = await this.findMany({ accountId, type, responsibleId }); + await Promise.all(calendars.map((calendar) => this.deleteCalendar({ accountId, calendar }))); + } + } + + private async processEvent({ event, action }: { event: E; action: CalendarAction }) { + const calendars = await this.findMany({ + accountId: event.accountId, + type: event.type, + responsibleId: event.ownerId, + objectId: event.objectId, + linkedObjectId: event.objectId, + processAll: true, + }); + + await Promise.all( + calendars + .filter((calendar) => !calendar.readonly) + .map(async (calendar) => { + const extendedProperties = EventUtil.getExtendedProperties(event); + + await this.withApi({ + calendar, + action: async ({ api, calendar }) => { + const current = await this.findTaskEvent({ + api, + calendar, + extendedProperties, + externalId: event.externalId, + }); + return action({ api, calendar, event, current, extendedProperties }); + }, + }); + }), + ); + } + + private async upsertEvent({ + api, + calendar, + event, + current, + extendedProperties, + }: CalendarActionParams) { + const requestBody = await this.createEventRequestBody({ event, extendedProperties }); + try { + if (current) { + api.events.update({ calendarId: calendar.externalId, eventId: current.id, requestBody }); + } else { + api.events.insert({ calendarId: calendar.externalId, requestBody }); + } + } catch (e) { + const error = e as Error; + this.logger.error(`Google Calendar upsert event error: ${error?.message}`, error?.stack); + } + } + + private async createEventRequestBody({ + event, + extendedProperties, + }: { + event: CalendarUpsertEvent; + extendedProperties: EventExtendedProperties; + }): Promise { + const source = await this.getSource({ accountId: event.accountId, entityId: event.entityId }); + return { + summary: event.title, + description: event.description, + start: { + dateTime: event.startDate.toISOString(), + timeZone: 'UTC', + }, + end: { + dateTime: event.endDate.toISOString(), + timeZone: 'UTC', + }, + source, + status: event.status, + extendedProperties, + }; + } + + private async getSource({ + accountId, + entityId, + }: { + accountId: number | null | undefined; + entityId: number | null | undefined; + }): Promise<{ title: string; url: string } | undefined> { + if (accountId && entityId) { + const entityInfo = await this.entityInfoService.findOne({ accountId, entityId }); + const account = await this.accountService.findOne({ accountId }); + const url = this.urlGenerator.createUrl({ + route: FrontendRoute.entity.card({ entityTypeId: entityInfo.entityTypeId, entityId: entityInfo.id }), + subdomain: account.subdomain, + }); + return { title: entityInfo.name, url }; + } + return undefined; + } + + private async deleteEvent({ api, calendar, current }: CalendarActionParams) { + if (current) { + try { + api.events.delete({ calendarId: calendar.externalId, eventId: current.id }); + } catch (e) { + const error = e as Error; + this.logger.error(`Google Calendar delete event error: ${error?.message}`, error?.stack); + } + } + } + + private async getCalendarList(tokens: Auth.Credentials): Promise { + try { + const { client } = await this.authService.getOAuth2Client({ tokens }); + const api = google.calendar({ version: 'v3', auth: client }); + + const { data } = await api.calendarList.list(); + + return data?.items ?? []; + } catch (e) { + const error = e as Error; + this.logger.error(`Google Calendar calendars list error: ${error?.message}`, error?.stack); + } + return []; + } + + private async getCalendarAccount({ + accountId, + tokens, + }: { + accountId: number; + tokens: Auth.Credentials; + }): Promise { + const calendars = await this.getCalendarList(tokens); + const primary = calendars.find((c) => c.primary); + if (!primary) { + throw new BadRequestError('Primary calendar not found'); + } + + let calendarAccount = await this.repositoryAccount.findOne({ where: { accountId, externalId: primary.id } }); + if (calendarAccount) { + await this.repositoryAccount.save(calendarAccount.updateTokens(tokens)); + + return calendarAccount; + } + + calendarAccount = await this.repositoryAccount.save( + new GoogleCalendarAccount({ accountId, tokens, externalId: primary.id }), + ); + + const api = await this.getApi(calendarAccount); + if (api) { + calendarAccount = await this.syncCalendars({ api, calendarAccount }); + calendarAccount = await this.startWatchCalendars({ calendarAccount, api }); + } + + return calendarAccount; + } + + private async cleanCalendarAccount({ + accountId, + calendarAccountId, + }: { + accountId: number; + calendarAccountId: number; + }) { + const calendars = await this.repositoryCalendar.find({ where: { accountId, calendarAccountId } }); + if (calendars.length === 0) { + const calendarAccount = await this.repositoryAccount.findOne({ where: { accountId, id: calendarAccountId } }); + if (calendarAccount) { + try { + const api = await this.getApi(calendarAccount); + if (api) { + await this.stopWatchCalendars({ calendarAccount, api }); + } + } catch (e) { + this.logger.warn(`Failed to stop watch calendars: ${e}`); + } + + await this.repositoryAccount.delete({ accountId, id: calendarAccountId }); + } + } + } + + private async getApi(calendarAccount: GoogleCalendarAccount): Promise { + try { + const { client, refreshedTokens } = await this.authService.getOAuth2Client({ + tokens: calendarAccount.tokens, + }); + if (refreshedTokens) { + calendarAccount = calendarAccount.updateTokens(refreshedTokens); + await this.repositoryAccount.save(calendarAccount); + } + return google.calendar({ version: 'v3', auth: client }); + } catch (error) { + this.logger.error(`Get API error for calendar account ${calendarAccount.id}`, (error as Error)?.stack); + return null; + } + } + + private async syncCalendars({ + api, + calendarAccount, + incremental = false, + }: { + api: calendar_v3.Calendar; + calendarAccount: GoogleCalendarAccount; + incremental?: boolean; + }): Promise { + let pageToken: string | undefined = undefined; + let syncToken: string | undefined = incremental ? calendarAccount.syncToken : undefined; + const calendars: calendar_v3.Schema$CalendarListEntry[] = []; + do { + try { + const { data } = await api.calendarList.list({ + showDeleted: !!syncToken, + pageToken, + syncToken, + }); + pageToken = data.nextPageToken; + syncToken = data.nextSyncToken ?? syncToken; + calendars.push(...(data.items ?? [])); + } catch (error) { + if (error['code'] === 410 && incremental) { + return this.syncCalendars({ api, calendarAccount }); + } else { + this.logger.error('Google Calendar calendars sync error', (error as Error)?.stack); + } + } + } while (pageToken); + + await Promise.all( + calendars + .filter((calendar) => calendar.deleted) + .map(async (calendar) => { + const current = await this.findOne({ + accountId: calendarAccount.accountId, + calendarAccountId: calendarAccount.id, + externalId: calendar.id, + }); + + if (current) { + await this.deleteCalendar({ accountId: current.accountId, calendar: current }); + } + }), + ); + + await this.repositoryAccount.update( + { accountId: calendarAccount.accountId, id: calendarAccount.id }, + { syncToken }, + ); + return calendarAccount.updateSyncToken(syncToken); + } + + private async startWatchCalendars({ + calendarAccount, + api, + }: { + calendarAccount: GoogleCalendarAccount; + api: calendar_v3.Calendar; + }): Promise { + const { data } = await api.calendarList.watch({ + requestBody: { + id: uuidv4(), + type: 'web_hook', + address: this.urlGenerator.createUrl({ route: WebhookPath.calendars }), + }, + }); + + const channelExpiration: Date | undefined = data.expiration ? new Date(Number(data.expiration)) : undefined; + await this.repositoryAccount.update( + { accountId: calendarAccount.accountId, id: calendarAccount.id }, + { channelId: data.id, channelResourceId: data.resourceId, channelExpiration }, + ); + + return calendarAccount.updateChannel({ channelId: data.id, channelResourceId: data.resourceId, channelExpiration }); + } + + private async stopWatchCalendars({ + calendarAccount, + api, + }: { + calendarAccount: GoogleCalendarAccount; + api: calendar_v3.Calendar; + }): Promise { + if (calendarAccount.channelId && calendarAccount.channelResourceId) { + try { + await api.channels.stop({ + requestBody: { id: calendarAccount.channelId, resourceId: calendarAccount.channelResourceId }, + }); + } catch (e) { + const error = e as Error; + this.logger.error(`Google Calendar stop watch error: ${error?.message}`, error?.stack); + } + } + + await this.repositoryAccount.update( + { accountId: calendarAccount.accountId, id: calendarAccount.id }, + { channelId: null, channelResourceId: null, channelExpiration: null }, + ); + + return calendarAccount.updateChannel({ channelId: null, channelResourceId: null, channelExpiration: null }); + } + + private async withApi({ + calendar, + calendarAccount, + action, + }: { + calendar: GoogleCalendar; + calendarAccount?: GoogleCalendarAccount; + action: ApiAction; + }): Promise { + if (!calendarAccount) { + calendarAccount = await this.repositoryAccount.findOne({ where: { id: calendar.calendarAccountId } }); + } + const api = await this.getApi(calendarAccount); + if (!api) { + throw new Error('Google Calendar API not found'); + } + + return action({ api, calendar }); + } + + private async syncEvents({ + api, + calendar, + incremental = false, + from, + }: { + api: calendar_v3.Calendar; + calendar: GoogleCalendar; + incremental?: boolean; + from?: Date; + }): Promise { + let pageToken: string | undefined = undefined; + let syncToken: string | undefined = incremental ? calendar.syncToken : undefined; + const events: calendar_v3.Schema$Event[] = []; + do { + try { + const { data } = await api.events.list({ + calendarId: calendar.externalId, + singleEvents: true, + showDeleted: !!syncToken, + updatedMin: !syncToken && !from ? DateUtil.now().toISOString() : undefined, + timeMin: !syncToken && from ? from.toISOString() : undefined, + pageToken, + syncToken, + }); + pageToken = data.nextPageToken; + syncToken = data.nextSyncToken ?? syncToken; + events.push(...(data.items ?? [])); + } catch (error) { + if (error['code'] === 410 && incremental) { + return this.syncEvents({ api, calendar }); + } else { + this.logger.error('Google Calendar events sync error', (error as Error)?.stack); + } + } + } while (pageToken); + + await Promise.all(events.map((event) => this.calendarEmitter.emit({ calendar, event }))); + + await this.repositoryCalendar.update({ accountId: calendar.accountId, id: calendar.id }, { syncToken }); + return calendar.updateSyncToken(syncToken); + } + + private async startWatchEvents({ + api, + calendar, + }: { + api: calendar_v3.Calendar; + calendar: GoogleCalendar; + }): Promise { + const { data } = await api.events.watch({ + calendarId: calendar.externalId, + requestBody: { + id: uuidv4(), + type: 'web_hook', + address: this.urlGenerator.createUrl({ route: WebhookPath.events }), + }, + }); + + const channelExpiration: Date | undefined = data.expiration ? new Date(Number(data.expiration)) : undefined; + await this.repositoryCalendar.update( + { accountId: calendar.accountId, id: calendar.id }, + { channelId: data.id, channelResourceId: data.resourceId, channelExpiration }, + ); + + return calendar.updateChannel({ channelId: data.id, channelResourceId: data.resourceId, channelExpiration }); + } + + private async stopWatchEvents({ + api, + calendar, + }: { + api: calendar_v3.Calendar; + calendar: GoogleCalendar; + }): Promise { + if (calendar.channelId && calendar.channelResourceId) { + try { + await api.channels.stop({ requestBody: { id: calendar.channelId, resourceId: calendar.channelResourceId } }); + } catch (e) { + const error = e as Error; + this.logger.error(`Google Calendar stop watch events error: ${error?.message}`, error?.stack); + } + } + + await this.repositoryCalendar.update( + { accountId: calendar.accountId, id: calendar.id }, + { channelId: null, channelResourceId: null, channelExpiration: null }, + ); + + return calendar.updateChannel({ channelId: null, channelResourceId: null, channelExpiration: null }); + } + + private async findTaskEvent({ + api, + calendar, + extendedProperties, + externalId, + }: EventSearchParams): Promise { + try { + if (externalId) { + const { data } = await api.events.list({ calendarId: calendar.externalId, iCalUID: externalId }); + if (data.items?.[0]) { + return data.items[0]; + } + } + + const { data } = await api.events.list({ + calendarId: calendar.externalId, + sharedExtendedProperty: Object.entries(extendedProperties.shared).map(([key, value]) => `${key}=${value}`), + }); + + return data?.items?.[0] ?? null; + } catch (e) { + const error = e as Error; + this.logger.error(`Google Calendar find task event error: ${error?.message}`, error?.stack); + } + return null; + } + + private async processLinked({ + accountId, + calendarId, + linked = [], + dtos, + }: { + accountId: number; + calendarId: number; + linked?: GoogleCalendarLinked[]; + dtos: GoogleCalendarLinkedDto[]; + }): Promise { + const toDelete = linked.filter((l) => !dtos.some((dto) => dto.type === l.type && dto.objectId === l.objectId)); + const toCreate = dtos.filter((dto) => !linked.find((l) => l.type === dto.type && l.objectId === dto.objectId)); + + await Promise.all(toDelete.map((item) => this.repositoryLinked.delete({ accountId, id: item.id }))); + const created = await Promise.all( + toCreate.map((dto) => this.repositoryLinked.save(GoogleCalendarLinked.fromDto({ accountId, calendarId, dto }))), + ); + + return [...linked.filter((l) => !toDelete.some((d) => d.id === l.id)), ...created]; + } + + private createFindQb(filter: FindFilter) { + const qb = this.repositoryCalendar + .createQueryBuilder('calendar') + .where('calendar.account_id = :accountId', { accountId: filter.accountId }) + .leftJoinAndMapMany('calendar.linked', GoogleCalendarLinked, 'link', 'link.calendar_id = calendar.id'); + if (filter.calendarId) { + qb.andWhere('calendar.id = :id', { id: filter.calendarId }); + } + if (filter.calendarAccountId) { + qb.andWhere('calendar.calendar_account_id = :calendarAccountId', { calendarAccountId: filter.calendarAccountId }); + } + if (filter.createdBy) { + qb.andWhere('calendar.created_by = :createdBy', { createdBy: filter.createdBy }); + } + if (filter.externalId) { + qb.andWhere('calendar.external_id = :externalId', { externalId: filter.externalId }); + } + if (filter.responsibleId) { + qb.andWhere('calendar.responsible_id = :responsibleId', { responsibleId: filter.responsibleId }); + } + if (filter.type) { + qb.andWhere('calendar.type = :type', { type: filter.type }); + } + if (filter.objectId || filter.linkedObjectId || filter.processAll) { + qb.andWhere( + new Brackets((qb1) => { + if (filter.objectId) { + qb1.where('calendar.object_id = :objectId', { objectId: filter.objectId }); + } + if (filter.linkedObjectId) { + if (filter.type) { + qb1.orWhere( + new Brackets((qbS) => + qbS + .where('link.type = :typeLinked', { typeLinked: filter.type }) + .andWhere('link.object_id = :linkedObjectId', { linkedObjectId: filter.linkedObjectId }), + ), + ); + } else { + qb1.orWhere('link.object_id = :linkedObjectId', { linkedObjectId: filter.linkedObjectId }); + } + } + if (filter.processAll) { + qb1.orWhere('calendar.process_all = true'); + } + }), + ); + } + + return qb; + } +} diff --git a/backend/src/modules/integration/google/calendar/dto/calendar-access.dto.ts b/backend/src/modules/integration/google/calendar/dto/calendar-access.dto.ts new file mode 100644 index 0000000..27f9008 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/calendar-access.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsString } from 'class-validator'; +import { CalendarInfoDto } from './calendar-info.dto'; + +export class CalendarAccessDto { + @ApiProperty({ type: CalendarInfoDto, isArray: true, description: 'List of calendar information' }) + @IsArray() + calendarInfos: CalendarInfoDto[]; + + @ApiProperty({ description: 'Access token' }) + @IsString() + token: string; +} diff --git a/backend/src/modules/integration/google/calendar/dto/calendar-info.dto.ts b/backend/src/modules/integration/google/calendar/dto/calendar-info.dto.ts new file mode 100644 index 0000000..0817f43 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/calendar-info.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +export class CalendarInfoDto { + @ApiProperty({ description: 'Calendar ID' }) + @IsString() + id: string; + + @ApiProperty({ description: 'Calendar title' }) + @IsString() + title: string; + + @ApiProperty({ description: 'Is primary calendar' }) + @IsBoolean() + primary: boolean; + + @ApiProperty({ description: 'Is read-only calendar' }) + @IsBoolean() + readonly: boolean; + + @ApiPropertyOptional({ description: 'Calendar description' }) + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional({ description: 'Calendar time zone' }) + @IsOptional() + @IsString() + timeZone?: string; + + @ApiPropertyOptional({ description: 'Calendar color in hex format', example: '#000000' }) + @IsOptional() + @IsString() + color?: string; +} diff --git a/backend/src/modules/integration/google/calendar/dto/create-google-calendar.dto.ts b/backend/src/modules/integration/google/calendar/dto/create-google-calendar.dto.ts new file mode 100644 index 0000000..01d95d2 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/create-google-calendar.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; + +import { GoogleCalendarDto } from './google-calendar.dto'; + +export class CreateGoogleCalendarDto extends PickType(GoogleCalendarDto, [ + 'externalId', + 'title', + 'readonly', + 'type', + 'objectId', + 'responsibleId', + 'processAll', + 'linked', +] as const) { + @ApiProperty({ description: 'Access token' }) + @IsString() + token: string; + + @ApiPropertyOptional({ description: 'Sync events from today to future' }) + @IsOptional() + @IsBoolean() + syncEvents?: boolean; +} diff --git a/backend/src/modules/integration/google/calendar/dto/google-calendar-linked.dto.ts b/backend/src/modules/integration/google/calendar/dto/google-calendar-linked.dto.ts new file mode 100644 index 0000000..7749d36 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/google-calendar-linked.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber } from 'class-validator'; + +import { CalendarType } from '../enums'; + +export class GoogleCalendarLinkedDto { + @ApiProperty({ enum: CalendarType, description: 'Linked object type' }) + @IsEnum(CalendarType) + type: CalendarType; + + @ApiProperty({ description: 'Linked object ID' }) + @IsNumber() + objectId: number; +} diff --git a/backend/src/modules/integration/google/calendar/dto/google-calendar.dto.ts b/backend/src/modules/integration/google/calendar/dto/google-calendar.dto.ts new file mode 100644 index 0000000..bb29d2f --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/google-calendar.dto.ts @@ -0,0 +1,52 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { CalendarType } from '../enums'; +import { GoogleCalendarLinkedDto } from './google-calendar-linked.dto'; + +export class GoogleCalendarDto { + @ApiProperty({ description: 'Calendar ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Created by user ID' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'Created at' }) + @IsString() + createdAt: string; + + @ApiProperty({ description: 'External calendar ID' }) + @IsString() + externalId: string; + + @ApiProperty({ description: 'Title' }) + @IsString() + title: string; + + @ApiProperty({ description: 'Readonly' }) + @IsBoolean() + readonly: boolean; + + @ApiProperty({ enum: CalendarType, description: 'Linked object type' }) + @IsEnum(CalendarType) + type: CalendarType; + + @ApiProperty({ description: 'Linked object ID' }) + @IsNumber() + objectId: number; + + @ApiProperty({ description: 'Default responsible ID' }) + @IsNumber() + responsibleId: number; + + @ApiProperty({ description: 'Process all events' }) + @IsOptional() + @IsBoolean() + processAll?: boolean | null; + + @ApiPropertyOptional({ type: [GoogleCalendarLinkedDto], nullable: true, description: 'Secondary linked objects' }) + @IsOptional() + linked?: GoogleCalendarLinkedDto[] | null; +} diff --git a/backend/src/modules/integration/google/calendar/dto/index.ts b/backend/src/modules/integration/google/calendar/dto/index.ts new file mode 100644 index 0000000..e66e2c8 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/index.ts @@ -0,0 +1,6 @@ +export * from './calendar-access.dto'; +export * from './calendar-info.dto'; +export * from './create-google-calendar.dto'; +export * from './google-calendar-linked.dto'; +export * from './google-calendar.dto'; +export * from './update-google-calendar.dto'; diff --git a/backend/src/modules/integration/google/calendar/dto/update-google-calendar.dto.ts b/backend/src/modules/integration/google/calendar/dto/update-google-calendar.dto.ts new file mode 100644 index 0000000..3632d17 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/dto/update-google-calendar.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { GoogleCalendarDto } from './google-calendar.dto'; + +export class UpdateGoogleCalendarDto extends PartialType( + PickType(GoogleCalendarDto, ['title', 'type', 'objectId', 'responsibleId', 'linked', 'processAll'] as const), +) {} diff --git a/backend/src/modules/integration/google/calendar/entities/google-calendar-account.entity.ts b/backend/src/modules/integration/google/calendar/entities/google-calendar-account.entity.ts new file mode 100644 index 0000000..b69793e --- /dev/null +++ b/backend/src/modules/integration/google/calendar/entities/google-calendar-account.entity.ts @@ -0,0 +1,57 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { Auth } from 'googleapis'; + +@Entity() +export class GoogleCalendarAccount { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + externalId: string; + + @Column({ type: 'jsonb' }) + tokens: Auth.Credentials; + + @Column({ nullable: true }) + syncToken: string | null; + + @Column({ nullable: true }) + channelId: string | null; + + @Column({ nullable: true }) + channelResourceId: string | null; + + @Column({ nullable: true }) + channelExpiration: Date | null; + + constructor(data?: Pick & { createdAt?: Date }) { + this.accountId = data?.accountId; + this.tokens = data?.tokens; + this.externalId = data?.externalId; + } + + updateTokens(tokens: Auth.Credentials): GoogleCalendarAccount { + this.tokens = tokens; + + return this; + } + + updateChannel( + data: Pick, + ): GoogleCalendarAccount { + this.channelId = data.channelId; + this.channelResourceId = data.channelResourceId; + this.channelExpiration = data.channelExpiration; + + return this; + } + + updateSyncToken(syncToken: string): GoogleCalendarAccount { + this.syncToken = syncToken; + + return this; + } +} diff --git a/backend/src/modules/integration/google/calendar/entities/google-calendar-linked.entity.ts b/backend/src/modules/integration/google/calendar/entities/google-calendar-linked.entity.ts new file mode 100644 index 0000000..1c0aa94 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/entities/google-calendar-linked.entity.ts @@ -0,0 +1,50 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { GoogleCalendarLinkedDto } from '../dto'; +import { CalendarType } from '../enums'; + +@Entity() +export class GoogleCalendarLinked { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + calendarId: number; + + @Column() + type: CalendarType; + + @Column() + objectId: number; + + constructor(data?: Pick) { + this.accountId = data?.accountId; + this.calendarId = data?.calendarId; + this.type = data?.type; + this.objectId = data?.objectId; + } + + static fromDto({ + accountId, + calendarId, + dto, + }: { + accountId: number; + calendarId: number; + dto: GoogleCalendarLinkedDto; + }): GoogleCalendarLinked { + return new GoogleCalendarLinked({ + accountId: accountId, + calendarId: calendarId, + type: dto.type, + objectId: dto.objectId, + }); + } + + toDto(): GoogleCalendarLinkedDto { + return { type: this.type, objectId: this.objectId }; + } +} diff --git a/backend/src/modules/integration/google/calendar/entities/google-calendar.entity.ts b/backend/src/modules/integration/google/calendar/entities/google-calendar.entity.ts new file mode 100644 index 0000000..8225ae7 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/entities/google-calendar.entity.ts @@ -0,0 +1,160 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { CreateGoogleCalendarDto, GoogleCalendarDto, UpdateGoogleCalendarDto } from '../dto'; +import { CalendarType } from '../enums'; + +import { GoogleCalendarLinked } from './google-calendar-linked.entity'; + +@Entity() +export class GoogleCalendar { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + createdBy: number; + + @Column() + createdAt: Date; + + @Column() + calendarAccountId: number; + + @Column() + externalId: string; + + @Column() + title: string; + + @Column() + readonly: boolean; + + @Column() + type: CalendarType; + + @Column() + objectId: number; + + @Column() + responsibleId: number; + + @Column({ nullable: true }) + syncToken: string | null; + + @Column({ nullable: true }) + channelId: string | null; + + @Column({ nullable: true }) + channelResourceId: string | null; + + @Column({ nullable: true }) + channelExpiration: Date | null; + + @Column({ default: false }) + processAll: boolean; + + constructor( + data?: Pick< + GoogleCalendar, + | 'accountId' + | 'createdBy' + | 'calendarAccountId' + | 'externalId' + | 'title' + | 'readonly' + | 'type' + | 'objectId' + | 'responsibleId' + | 'processAll' + > & { createdAt?: Date }, + ) { + this.accountId = data?.accountId; + this.createdBy = data?.createdBy; + this.createdAt = data?.createdAt ?? DateUtil.now(); + this.calendarAccountId = data?.calendarAccountId ?? data?.accountId; + this.externalId = data?.externalId; + this.title = data?.title; + this.readonly = data?.readonly; + this.type = data?.type; + this.objectId = data?.objectId; + this.responsibleId = data?.responsibleId; + this.processAll = data?.processAll ?? false; + } + + private _linked?: GoogleCalendarLinked[] | null; + get linked(): GoogleCalendarLinked[] | null { + return this._linked; + } + set linked(value: GoogleCalendarLinked[] | null) { + this._linked = value; + } + + static fromDto({ + accountId, + createdBy, + calendarAccountId, + dto, + }: { + accountId: number; + createdBy: number; + calendarAccountId: number; + dto: CreateGoogleCalendarDto; + }): GoogleCalendar { + return new GoogleCalendar({ + accountId: accountId, + createdBy: createdBy, + calendarAccountId: calendarAccountId, + externalId: dto.externalId, + title: dto.title, + readonly: dto.readonly, + type: dto.type, + objectId: dto.objectId, + responsibleId: dto.responsibleId, + processAll: dto.processAll, + }); + } + + update(dto: UpdateGoogleCalendarDto): GoogleCalendar { + this.title = dto.title ?? this.title; + this.type = dto.type ?? this.type; + this.objectId = dto.objectId ?? this.objectId; + this.responsibleId = dto.responsibleId ?? dto.responsibleId; + this.processAll = dto.processAll ?? this.processAll; + + return this; + } + + updateChannel(data: Pick): GoogleCalendar { + this.channelId = data.channelId; + this.channelResourceId = data.channelResourceId; + this.channelExpiration = data.channelExpiration; + + return this; + } + + updateSyncToken(syncToken: string): GoogleCalendar { + this.syncToken = syncToken; + + return this; + } + + toDto(): GoogleCalendarDto { + return { + id: this.id, + createdBy: this.createdBy, + createdAt: this.createdAt.toISOString(), + externalId: this.externalId, + title: this.title, + readonly: this.readonly, + type: this.type, + objectId: this.objectId, + responsibleId: this.responsibleId, + processAll: this.processAll, + linked: this.linked?.map((item) => item.toDto()), + }; + } +} diff --git a/backend/src/modules/integration/google/calendar/entities/index.ts b/backend/src/modules/integration/google/calendar/entities/index.ts new file mode 100644 index 0000000..1b0e5b6 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/entities/index.ts @@ -0,0 +1,3 @@ +export * from './google-calendar-account.entity'; +export * from './google-calendar-linked.entity'; +export * from './google-calendar.entity'; diff --git a/backend/src/modules/integration/google/calendar/enums/calendar-type.enum.ts b/backend/src/modules/integration/google/calendar/enums/calendar-type.enum.ts new file mode 100644 index 0000000..1029033 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/enums/calendar-type.enum.ts @@ -0,0 +1,4 @@ +export enum CalendarType { + Task = 'task', + Schedule = 'schedule', +} diff --git a/backend/src/modules/integration/google/calendar/enums/index.ts b/backend/src/modules/integration/google/calendar/enums/index.ts new file mode 100644 index 0000000..4053ad1 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/enums/index.ts @@ -0,0 +1 @@ +export * from './calendar-type.enum'; diff --git a/backend/src/modules/integration/google/calendar/public-calendar.controller.ts b/backend/src/modules/integration/google/calendar/public-calendar.controller.ts new file mode 100644 index 0000000..e84637b --- /dev/null +++ b/backend/src/modules/integration/google/calendar/public-calendar.controller.ts @@ -0,0 +1,36 @@ +import { Controller, Get, Headers, Post, Query, Redirect } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { CalendarService } from './calendar.service'; + +@ApiExcludeController() +@Controller('integration/google/calendar') +export class PublicCalendarController { + constructor(private readonly service: CalendarService) {} + + @Get('callback') + @Redirect() + public async callback(@Query('code') code: string, @Query('state') state?: string) { + const redirectUrl = await this.service.getRedirectUrl({ state, code }); + + return { url: redirectUrl, statusCode: 302 }; + } + + @Post('webhook-calendars') + public async webhookCalendars( + @Headers('X-Goog-Channel-ID') channelId: string, + @Headers('X-Goog-Resource-ID') resourceId: string, + @Headers('X-Goog-Resource-State') resourceState: string, + ) { + this.service.processWebhookCalendars({ channelId, resourceId, resourceState }); + } + + @Post('webhook-events') + public async webhookEvents( + @Headers('X-Goog-Channel-ID') channelId: string, + @Headers('X-Goog-Resource-ID') resourceId: string, + @Headers('X-Goog-Resource-State') resourceState: string, + ) { + this.service.processWebhookEvents({ channelId, resourceId, resourceState }); + } +} diff --git a/backend/src/modules/integration/google/calendar/types/calendar-access.ts b/backend/src/modules/integration/google/calendar/types/calendar-access.ts new file mode 100644 index 0000000..94e7d76 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/types/calendar-access.ts @@ -0,0 +1,19 @@ +import { CalendarAccessDto } from '../dto'; +import { CalendarInfo } from './calendar-info'; + +export class CalendarAccess { + calendarInfos: CalendarInfo[]; + token: string; + + constructor(data: Omit) { + this.calendarInfos = data.calendarInfos; + this.token = data.token; + } + + public toDto(): CalendarAccessDto { + return { + calendarInfos: this.calendarInfos.map((calendar) => calendar.toDto()), + token: this.token, + }; + } +} diff --git a/backend/src/modules/integration/google/calendar/types/calendar-event.ts b/backend/src/modules/integration/google/calendar/types/calendar-event.ts new file mode 100644 index 0000000..587d62d --- /dev/null +++ b/backend/src/modules/integration/google/calendar/types/calendar-event.ts @@ -0,0 +1,10 @@ +import { CalendarType } from '../enums'; + +export interface CalendarEvent { + accountId: number | null; + ownerId?: number | null; + objectId: number | null; + eventId: number | null; + type: CalendarType | null; + externalId: string | null; +} diff --git a/backend/src/modules/integration/google/calendar/types/calendar-info.ts b/backend/src/modules/integration/google/calendar/types/calendar-info.ts new file mode 100644 index 0000000..b109e73 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/types/calendar-info.ts @@ -0,0 +1,46 @@ +import { calendar_v3 } from 'googleapis'; +import { CalendarInfoDto } from '../dto'; + +export class CalendarInfo { + id: string; + title: string; + primary: boolean; + readonly: boolean; + description?: string; + timeZone?: string; + color?: string; + + constructor(data: Omit) { + this.id = data.id; + this.title = data.title; + this.primary = data.primary; + this.readonly = data.readonly; + this.description = data.description; + this.timeZone = data.timeZone; + this.color = data.color; + } + + static fromApi(calendar: calendar_v3.Schema$CalendarListEntry): CalendarInfo { + return new CalendarInfo({ + id: calendar.id, + title: calendar.summary, + primary: calendar.primary ?? false, + readonly: !['owner', 'writer'].includes(calendar.accessRole ?? ''), + description: calendar.description, + timeZone: calendar.timeZone, + color: calendar.backgroundColor, + }); + } + + toDto(): CalendarInfoDto { + return { + id: this.id, + title: this.title, + primary: this.primary, + readonly: this.readonly, + description: this.description, + timeZone: this.timeZone, + color: this.color, + }; + } +} diff --git a/backend/src/modules/integration/google/calendar/types/calendar-upsert-event.ts b/backend/src/modules/integration/google/calendar/types/calendar-upsert-event.ts new file mode 100644 index 0000000..d68bc1f --- /dev/null +++ b/backend/src/modules/integration/google/calendar/types/calendar-upsert-event.ts @@ -0,0 +1,12 @@ +import { CalendarEvent } from './calendar-event'; + +export type CalendarEventStatus = 'confirmed' | 'tentative' | 'cancelled'; + +export interface CalendarUpsertEvent extends CalendarEvent { + title: string; + description: string; + startDate: Date; + endDate: Date; + status?: CalendarEventStatus | null; + entityId?: number | null; +} diff --git a/backend/src/modules/integration/google/calendar/types/event-extended-properties.ts b/backend/src/modules/integration/google/calendar/types/event-extended-properties.ts new file mode 100644 index 0000000..17deb0a --- /dev/null +++ b/backend/src/modules/integration/google/calendar/types/event-extended-properties.ts @@ -0,0 +1,4 @@ +export interface EventExtendedProperties { + private?: Record; + shared?: Record; +} diff --git a/backend/src/modules/integration/google/calendar/types/index.ts b/backend/src/modules/integration/google/calendar/types/index.ts new file mode 100644 index 0000000..f840d5f --- /dev/null +++ b/backend/src/modules/integration/google/calendar/types/index.ts @@ -0,0 +1,5 @@ +export * from './calendar-access'; +export * from './calendar-event'; +export * from './calendar-info'; +export * from './calendar-upsert-event'; +export * from './event-extended-properties'; diff --git a/backend/src/modules/integration/google/calendar/utils/appointment.util.ts b/backend/src/modules/integration/google/calendar/utils/appointment.util.ts new file mode 100644 index 0000000..5d80081 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/utils/appointment.util.ts @@ -0,0 +1,30 @@ +import { ScheduleAppointmentStatus } from '@/modules/scheduler/common'; +import { CalendarEventStatus } from '../types'; + +export class AppointmentUtil { + public static convertAppointmentStatus(status: ScheduleAppointmentStatus): CalendarEventStatus | undefined { + switch (status) { + case ScheduleAppointmentStatus.Canceled: + return 'cancelled'; + case ScheduleAppointmentStatus.NotConfirmed: + return 'tentative'; + case ScheduleAppointmentStatus.Confirmed: + return 'confirmed'; + default: + return undefined; + } + } + + public static convertEventStatus(status: string): ScheduleAppointmentStatus { + switch (status) { + case 'cancelled': + return ScheduleAppointmentStatus.Canceled; + case 'tentative': + return ScheduleAppointmentStatus.NotConfirmed; + case 'confirmed': + return ScheduleAppointmentStatus.Confirmed; + default: + return ScheduleAppointmentStatus.NotConfirmed; + } + } +} diff --git a/backend/src/modules/integration/google/calendar/utils/event.util.ts b/backend/src/modules/integration/google/calendar/utils/event.util.ts new file mode 100644 index 0000000..817bb60 --- /dev/null +++ b/backend/src/modules/integration/google/calendar/utils/event.util.ts @@ -0,0 +1,45 @@ +import { calendar_v3 } from 'googleapis'; + +import { CalendarType } from '../enums'; +import { CalendarEvent, EventExtendedProperties } from '../types'; + +export class EventUtil { + public static createExternalId(event: calendar_v3.Schema$Event): string { + return event.iCalUID ?? `${event.id}@google.com`; + } + + public static getExtendedProperties(event: CalendarEvent): EventExtendedProperties { + return { + shared: { + accountId: event.accountId.toString(), + ownerId: event.ownerId?.toString(), + objectId: event.objectId.toString(), + eventId: event.eventId.toString(), + type: event.type, + }, + }; + } + + public static getCalendarEvent(event: calendar_v3.Schema$Event): CalendarEvent { + const accountId = event.extendedProperties?.shared?.['accountId']; + const ownerId = event.extendedProperties?.shared?.['ownerId']; + const objectId = event.extendedProperties?.shared?.['objectId']; + const eventId = event.extendedProperties?.shared?.['eventId']; + const type = event.extendedProperties?.shared?.['type'] as CalendarType; + + return { + accountId: accountId ? Number(accountId) : null, + ownerId: ownerId ? Number(ownerId) : null, + objectId: objectId ? Number(objectId) : null, + eventId: eventId ? Number(eventId) : null, + type: type ?? null, + externalId: EventUtil.createExternalId(event), + }; + } + + public static getEventDate(date: calendar_v3.Schema$EventDateTime): Date | undefined { + const d = date.dateTime || date.date; + + return d ? new Date(d) : undefined; + } +} diff --git a/backend/src/modules/integration/google/calendar/utils/index.ts b/backend/src/modules/integration/google/calendar/utils/index.ts new file mode 100644 index 0000000..67470bb --- /dev/null +++ b/backend/src/modules/integration/google/calendar/utils/index.ts @@ -0,0 +1,2 @@ +export * from './appointment.util'; +export * from './event.util'; diff --git a/backend/src/modules/integration/google/google.config.ts b/backend/src/modules/integration/google/google.config.ts new file mode 100644 index 0000000..69ebfd0 --- /dev/null +++ b/backend/src/modules/integration/google/google.config.ts @@ -0,0 +1,20 @@ +import { registerAs } from '@nestjs/config'; + +interface Auth { + clientId: string; + clientSecret: string; +} + +export interface GoogleConfig { + auth: Auth; +} + +export default registerAs( + 'google', + (): GoogleConfig => ({ + auth: { + clientId: process.env.GOOGLE_AUTH_CLIENT_ID, + clientSecret: process.env.GOOGLE_AUTH_CLIENT_SECRET, + }, + }), +); diff --git a/backend/src/modules/integration/google/google.module.ts b/backend/src/modules/integration/google/google.module.ts new file mode 100644 index 0000000..b7d05ec --- /dev/null +++ b/backend/src/modules/integration/google/google.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; + +import googleConfig from './google.config'; +import { CalendarModule } from './calendar/calendar.module'; + +@Module({ + imports: [ConfigModule.forFeature(googleConfig), CalendarModule], +}) +export class GoogleModule {} diff --git a/backend/src/modules/integration/integration.module.ts b/backend/src/modules/integration/integration.module.ts new file mode 100644 index 0000000..061af1e --- /dev/null +++ b/backend/src/modules/integration/integration.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { ConditionalModule } from '@nestjs/config'; + +import { AppsumoModule } from './appsumo/appsumo.module'; +import { GoogleModule } from './google/google.module'; +import { StripeModule } from './stripe/stripe.module'; + +@Module({ + imports: [AppsumoModule, GoogleModule, ConditionalModule.registerWhen(StripeModule, 'STRIPE_ENABLED')], + exports: [AppsumoModule], +}) +export class IntegrationModule {} diff --git a/backend/src/modules/integration/stripe/dto/index.ts b/backend/src/modules/integration/stripe/dto/index.ts new file mode 100644 index 0000000..b5dafbc --- /dev/null +++ b/backend/src/modules/integration/stripe/dto/index.ts @@ -0,0 +1,4 @@ +export * from './subscription-feature.dto'; +export * from './subscription-order.dto'; +export * from './subscription-plan.dto'; +export * from './subscription-price.dto'; diff --git a/backend/src/modules/integration/stripe/dto/subscription-feature.dto.ts b/backend/src/modules/integration/stripe/dto/subscription-feature.dto.ts new file mode 100644 index 0000000..e232e14 --- /dev/null +++ b/backend/src/modules/integration/stripe/dto/subscription-feature.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsString } from 'class-validator'; + +export class SubscriptionFeatureDto { + @ApiProperty({ description: 'Subscription feature name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Is subscription feature available' }) + @IsBoolean() + available: boolean; +} diff --git a/backend/src/modules/integration/stripe/dto/subscription-order.dto.ts b/backend/src/modules/integration/stripe/dto/subscription-order.dto.ts new file mode 100644 index 0000000..15ed6e2 --- /dev/null +++ b/backend/src/modules/integration/stripe/dto/subscription-order.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class SubscriptionOrderDto { + @ApiProperty({ nullable: true, description: 'Stripe product id' }) + @IsString() + productId: string; + + @ApiPropertyOptional({ nullable: true, description: 'Stripe price id' }) + @IsOptional() + @IsString() + priceId?: string | null; + + @ApiProperty({ description: 'Number of users' }) + @IsNumber() + numberOfUsers: number; + + @ApiPropertyOptional({ nullable: true, description: 'Payment amount' }) + @IsOptional() + @IsNumber() + amount?: number | null; + + @ApiPropertyOptional({ description: 'Discount code' }) + @IsOptional() + @IsString() + couponId?: string | null; +} diff --git a/backend/src/modules/integration/stripe/dto/subscription-plan.dto.ts b/backend/src/modules/integration/stripe/dto/subscription-plan.dto.ts new file mode 100644 index 0000000..c1d8887 --- /dev/null +++ b/backend/src/modules/integration/stripe/dto/subscription-plan.dto.ts @@ -0,0 +1,54 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { SubscriptionPriceDto } from './subscription-price.dto'; +import { SubscriptionFeatureDto } from './subscription-feature.dto'; + +export class SubscriptionPlanDto { + @ApiProperty({ description: 'Stripe product id' }) + @IsString() + id: string; + + @ApiPropertyOptional({ nullable: true, description: 'Subscription plan code' }) + @IsOptional() + @IsString() + code?: string | null; + + @ApiProperty({ description: 'Subscription product name' }) + @IsString() + name: string; + + @ApiPropertyOptional({ nullable: true, description: 'Subscription product description' }) + @IsOptional() + @IsString() + description: string | null; + + @ApiPropertyOptional({ description: 'Subscription product order' }) + @IsOptional() + @IsNumber() + order?: number | null; + + @ApiProperty({ type: [SubscriptionPriceDto], description: 'Subscription plan prices' }) + @IsArray() + prices: SubscriptionPriceDto[]; + + @ApiPropertyOptional({ nullable: true, description: 'Default price id' }) + @IsOptional() + @IsString() + defaultPriceId?: string | null = null; + + @ApiPropertyOptional({ nullable: true, type: [SubscriptionFeatureDto], description: 'Subscription plan features' }) + @IsOptional() + @IsArray() + features?: SubscriptionFeatureDto[] | null; + + @ApiPropertyOptional({ description: 'Is subscription plan default' }) + @IsOptional() + @IsBoolean() + isDefault?: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Subscription plan user limit' }) + @IsOptional() + @IsNumber() + userLimit?: number | null; +} diff --git a/backend/src/modules/integration/stripe/dto/subscription-price.dto.ts b/backend/src/modules/integration/stripe/dto/subscription-price.dto.ts new file mode 100644 index 0000000..b2ff7b3 --- /dev/null +++ b/backend/src/modules/integration/stripe/dto/subscription-price.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class SubscriptionPriceDto { + @ApiProperty({ description: 'Stripe price id' }) + @IsString() + id: string; + + @ApiProperty({ description: 'Price amount' }) + @IsNumber() + amount: number; + + @ApiProperty({ description: 'Price currency' }) + @IsString() + currency: string; + + @ApiProperty({ description: 'Price interval' }) + @IsString() + interval: string; +} diff --git a/backend/src/modules/integration/stripe/public-stripe.controller.ts b/backend/src/modules/integration/stripe/public-stripe.controller.ts new file mode 100644 index 0000000..74f8caa --- /dev/null +++ b/backend/src/modules/integration/stripe/public-stripe.controller.ts @@ -0,0 +1,25 @@ +import { Body, Controller, Get, Post, RawBodyRequest, Req } from '@nestjs/common'; +import { ApiExcludeEndpoint, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { SubscriptionPlanDto } from './dto'; +import { StripeService } from './stripe.service'; + +@ApiTags('integration/stripe') +@Controller('iam/subscriptions/stripe') +export class PublicStripeController { + constructor(private readonly service: StripeService) {} + + @ApiOperation({ summary: 'Get available subscription plans', description: 'Get available subscription plans' }) + @ApiOkResponse({ type: [SubscriptionPlanDto], description: 'Subscription plans' }) + @Get('plans') + public async getSubscriptionPlans(): Promise { + return this.service.getSubscriptionPlans(); + } + + @ApiExcludeEndpoint(true) + @Post('webhook') + public async handleWebhook(@Req() request: RawBodyRequest, @Body() body: unknown): Promise { + const signature = request.headers['stripe-signature']; + return await this.service.handleWebhook(body, request.rawBody, signature); + } +} diff --git a/backend/src/modules/integration/stripe/stripe.controller.ts b/backend/src/modules/integration/stripe/stripe.controller.ts new file mode 100644 index 0000000..4f442f8 --- /dev/null +++ b/backend/src/modules/integration/stripe/stripe.controller.ts @@ -0,0 +1,31 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { SubscriptionOrderDto } from './dto'; +import { StripeService } from './stripe.service'; + +@ApiTags('integration/stripe') +@Controller('iam/subscriptions/stripe') +@JwtAuthorized({ prefetch: { account: true } }) +export class StripeController { + constructor(private readonly service: StripeService) {} + + @ApiOperation({ summary: 'Create checkout session', description: 'Create checkout session' }) + @ApiOkResponse({ type: String, description: 'Checkout URL' }) + @Get('checkout') + public async getCheckoutUrl( + @CurrentAuth() { account, userId }: AuthData, + @Query() dto: SubscriptionOrderDto, + ): Promise { + return this.service.getCheckoutUrl(account, userId, dto); + } + + @ApiOperation({ summary: 'Get customer portal URL', description: 'Get customer portal URL' }) + @ApiOkResponse({ type: String, description: 'Customer portal URL' }) + @Get('portal') + public async getCustomerPortalUrl(@CurrentAuth() { account }: AuthData): Promise { + return this.service.getCustomerPortalUrl(account); + } +} diff --git a/backend/src/modules/integration/stripe/stripe.module.ts b/backend/src/modules/integration/stripe/stripe.module.ts new file mode 100644 index 0000000..e848a2b --- /dev/null +++ b/backend/src/modules/integration/stripe/stripe.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { StripeService } from './stripe.service'; +import { PublicStripeController } from './public-stripe.controller'; +import { StripeController } from './stripe.controller'; + +@Module({ + imports: [IAMModule], + providers: [StripeService], + controllers: [PublicStripeController, StripeController], +}) +export class StripeModule {} diff --git a/backend/src/modules/integration/stripe/stripe.service.ts b/backend/src/modules/integration/stripe/stripe.service.ts new file mode 100644 index 0000000..d1becce --- /dev/null +++ b/backend/src/modules/integration/stripe/stripe.service.ts @@ -0,0 +1,219 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import Stripe from 'stripe'; + +import { UrlGeneratorService, FrontendRoute, BadRequestError, DateUtil } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AccountSubscriptionService } from '@/modules/iam/account-subscription/account-subscription.service'; + +import { SubscriptionPlanDto, SubscriptionPriceDto, SubscriptionFeatureDto, SubscriptionOrderDto } from './dto'; + +@Injectable() +export class StripeService { + private readonly logger = new Logger(StripeService.name); + private stripe: Stripe; + private stripeWebhookSecret: string; + + constructor( + private readonly configService: ConfigService, + private readonly urlGenerator: UrlGeneratorService, + private readonly subscriptionService: AccountSubscriptionService, + ) { + this.stripe = new Stripe(this.configService.get('STRIPE_SECRET'), { apiVersion: '2025-02-24.acacia' }); + this.stripeWebhookSecret = this.configService.get('STRIPE_WEBHOOK_SECRET'); + } + + public async getSubscriptionPlans(): Promise { + const { data } = await this.stripe.products.list({ active: true, limit: 100 }); + const products = data.filter( + (product) => product.metadata['site_code'] !== undefined || product.metadata['site_order'] !== undefined, + ); + + const plans: SubscriptionPlanDto[] = []; + for (const product of products) { + const prices = await this.stripe.prices.list({ product: product.id }); + const priceDtos: SubscriptionPriceDto[] = prices.data.map((price) => ({ + id: price.id, + amount: price.unit_amount / 100.0, + currency: price.currency, + interval: price.recurring?.interval ?? 'one_time', + })); + const defaultPrice = typeof product.default_price === 'string' ? product.default_price : product.default_price.id; + const userLimit = product.metadata['site_user_limit'] ? Number(product.metadata['site_user_limit']) : null; + + let featureDtos: SubscriptionFeatureDto[] = []; + if (product.metadata['site_features']) { + try { + featureDtos = JSON.parse(product.metadata['site_features']) as SubscriptionFeatureDto[]; + } catch (e) { + this.logger.error(`Error parsing Stripe features`, (e as Error)?.stack); + } + } + + const isDefault = !!product.metadata['site_default']; + plans.push({ + id: product.id, + code: product.metadata['site_code'], + name: product.name, + description: product.description, + order: Number(product.metadata['site_order'] ?? 0), + prices: priceDtos, + defaultPriceId: defaultPrice, + features: featureDtos, + isDefault, + userLimit, + }); + } + return plans; + } + + public async getCheckoutUrl(account: Account, userId: number, dto: SubscriptionOrderDto): Promise { + const redirectUrl = this.urlGenerator.createUrl({ + route: FrontendRoute.settings.stripe(), + subdomain: account.subdomain, + }); + try { + const session = await this.stripe.checkout.sessions.create({ + line_items: [ + { + price: dto.priceId ?? undefined, + quantity: dto.priceId ? dto.numberOfUsers : 1, + price_data: !dto.priceId + ? { + currency: 'usd', + product: dto.productId, + unit_amount: dto.amount * 100, + } + : undefined, + }, + ], + discounts: dto.couponId ? [{ coupon: dto.couponId }] : undefined, + mode: dto.priceId ? 'subscription' : 'payment', + customer_creation: dto.priceId ? undefined : 'always', + success_url: `${redirectUrl}/?success=true`, + cancel_url: `${redirectUrl}/?canceled=true`, + client_reference_id: account.id.toString(), + metadata: { accountId: account.id, userId, numberOfUsers: dto.numberOfUsers }, + }); + return session.url; + } catch (e) { + this.logger.error(`Stripe checkout error`, (e as Error)?.stack); + throw e; + } + } + + public async getCustomerPortalUrl(account: Account): Promise { + const subscription = await this.subscriptionService.get(account.id); + if (!subscription.externalCustomerId) { + return null; + } + + const returnUrl = this.urlGenerator.createUrl({ route: FrontendRoute.settings.base, subdomain: account.subdomain }); + + const session = await this.stripe.billingPortal.sessions.create({ + customer: subscription.externalCustomerId, + return_url: returnUrl, + }); + + return session.url; + } + + public async handleWebhook(body: unknown, rawBody: Buffer, signature: string | string[]): Promise { + let event: Stripe.Event; + + if (this.stripeWebhookSecret) { + try { + event = this.stripe.webhooks.constructEvent(rawBody, signature, this.stripeWebhookSecret); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + throw new BadRequestError('Invalid stripe signature'); + } + } else { + event = body as Stripe.Event; + } + + switch (event.type) { + case 'checkout.session.completed': + this.handleCheckoutSessionCompleted(event.data.object as Stripe.Checkout.Session); + break; + case 'customer.subscription.updated': + this.handleSubscriptionUpdated(event.data.object as Stripe.Subscription); + break; + case 'customer.subscription.deleted': + this.handleSubscriptionDeleted(event.data.object as Stripe.Subscription); + break; + } + } + + private async handleCheckoutSessionCompleted(session: Stripe.Checkout.Session): Promise { + const fullSession = await this.stripe.checkout.sessions.retrieve(session.id, { + expand: ['subscription.plan.product', 'line_items.data.price.product'], + }); + + const accountId = Number(fullSession.client_reference_id); + const subscription = fullSession.subscription as Stripe.Subscription; + const lineItem = fullSession.line_items?.data?.[0]; + const product = ( + subscription ? ((subscription['plan'] as Stripe.Plan)?.product as Stripe.Product) : lineItem?.price?.product + ) as Stripe.Product; + const quantity = Number( + fullSession.metadata['numberOfUsers'] ?? subscription?.['quantity'] ?? lineItem?.quantity ?? 1, + ); + const externalCustomerId = fullSession.customer as string; + + await this.updateSubscription({ accountId, product, subscription, externalCustomerId, quantity }); + } + + private async handleSubscriptionUpdated(subscription: Stripe.Subscription): Promise { + const fullSubscription = await this.stripe.subscriptions.retrieve(subscription.id, { + expand: ['plan.product'], + }); + + const product = (fullSubscription['plan'] as Stripe.Plan)?.product as Stripe.Product; + const quantity = Number(fullSubscription['quantity']); + const externalCustomerId = fullSubscription.customer as string; + + await this.updateSubscription({ product, subscription: fullSubscription, externalCustomerId, quantity }); + } + + private async updateSubscription({ + accountId, + product, + subscription, + externalCustomerId, + quantity, + }: { + accountId?: number; + product: Stripe.Product; + subscription?: Stripe.Subscription; + externalCustomerId: string; + quantity: number; + }) { + const periodStart = DateUtil.startOf( + subscription ? new Date(subscription.current_period_start * 1000) : DateUtil.now(), + 'day', + ); + const periodEnd = DateUtil.endOf( + subscription ? new Date(subscription.current_period_end * 1000) : DateUtil.add(DateUtil.now(), { years: 100 }), + 'day', + ); + await this.subscriptionService.update( + { accountId, externalCustomerId: !accountId ? externalCustomerId : undefined }, + null, + { + isTrial: false, + periodStart: periodStart.toISOString(), + periodEnd: periodEnd.toISOString(), + userLimit: quantity, + planName: product.name, + externalCustomerId, + }, + ); + } + + private async handleSubscriptionDeleted(subscription: Stripe.Subscription): Promise { + const stripeCustomerId = subscription.customer as string; + await this.subscriptionService.cancel({ externalCustomerId: stripeCustomerId }); + } +} diff --git a/backend/src/modules/inventory/common/enums/index.ts b/backend/src/modules/inventory/common/enums/index.ts new file mode 100644 index 0000000..a807d5f --- /dev/null +++ b/backend/src/modules/inventory/common/enums/index.ts @@ -0,0 +1 @@ +export * from './permission-object-type.enum'; diff --git a/backend/src/modules/inventory/common/enums/permission-object-type.enum.ts b/backend/src/modules/inventory/common/enums/permission-object-type.enum.ts new file mode 100644 index 0000000..27329c1 --- /dev/null +++ b/backend/src/modules/inventory/common/enums/permission-object-type.enum.ts @@ -0,0 +1,6 @@ +export enum PermissionObjectType { + Products = 'products', + ProductsOrder = 'products_order', + ProductsShipment = 'products_shipment', + Warehouse = 'inventory_warehouse', +} diff --git a/backend/src/modules/inventory/common/events/index.ts b/backend/src/modules/inventory/common/events/index.ts new file mode 100644 index 0000000..0ff7a55 --- /dev/null +++ b/backend/src/modules/inventory/common/events/index.ts @@ -0,0 +1,5 @@ +export * from './product-order'; +export * from './products-event-type.enum'; +export * from './products-section'; +export * from './rental-order'; +export * from './shipment'; diff --git a/backend/src/modules/inventory/common/events/product-order/index.ts b/backend/src/modules/inventory/common/events/product-order/index.ts new file mode 100644 index 0000000..d9f9d40 --- /dev/null +++ b/backend/src/modules/inventory/common/events/product-order/index.ts @@ -0,0 +1,2 @@ +export * from './product-order-created.event'; +export * from './product-order.event'; diff --git a/backend/src/modules/inventory/common/events/product-order/product-order-created.event.ts b/backend/src/modules/inventory/common/events/product-order/product-order-created.event.ts new file mode 100644 index 0000000..0aadca1 --- /dev/null +++ b/backend/src/modules/inventory/common/events/product-order/product-order-created.event.ts @@ -0,0 +1,10 @@ +import { ProductOrderEvent } from './product-order.event'; + +export class ProductOrderCreatedEvent extends ProductOrderEvent { + createdAt: string; + + constructor({ accountId, entityId, orderId, createdAt }: ProductOrderCreatedEvent) { + super({ accountId, entityId, orderId }); + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/inventory/common/events/product-order/product-order.event.ts b/backend/src/modules/inventory/common/events/product-order/product-order.event.ts new file mode 100644 index 0000000..16a80de --- /dev/null +++ b/backend/src/modules/inventory/common/events/product-order/product-order.event.ts @@ -0,0 +1,11 @@ +export class ProductOrderEvent { + accountId: number; + entityId: number; + orderId: number; + + constructor({ accountId, entityId, orderId }: ProductOrderEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.orderId = orderId; + } +} diff --git a/backend/src/modules/inventory/common/events/products-event-type.enum.ts b/backend/src/modules/inventory/common/events/products-event-type.enum.ts new file mode 100644 index 0000000..7a63325 --- /dev/null +++ b/backend/src/modules/inventory/common/events/products-event-type.enum.ts @@ -0,0 +1,11 @@ +export enum ProductsEventType { + ProductOrderCreated = 'product:order:created', + ProductOrderDeleted = 'product:order:deleted', + ProductsSectionCreated = 'products:section:created', + ProductsSectionDeleted = 'products:section:deleted', + RentalOrderCreated = 'rental:order:created', + RentalOrderDeleted = 'rental:order:deleted', + ShipmentCreated = 'shipment:created', + ShipmentDeleted = 'shipment:deleted', + ShipmentStatusChanged = 'shipment:status_changed', +} diff --git a/backend/src/modules/inventory/common/events/products-section/index.ts b/backend/src/modules/inventory/common/events/products-section/index.ts new file mode 100644 index 0000000..8940aaa --- /dev/null +++ b/backend/src/modules/inventory/common/events/products-section/index.ts @@ -0,0 +1 @@ +export * from './products-section.event'; diff --git a/backend/src/modules/inventory/common/events/products-section/products-section.event.ts b/backend/src/modules/inventory/common/events/products-section/products-section.event.ts new file mode 100644 index 0000000..e58e354 --- /dev/null +++ b/backend/src/modules/inventory/common/events/products-section/products-section.event.ts @@ -0,0 +1,9 @@ +export class ProductsSectionEvent { + accountId: number; + sectionId: number; + + constructor({ accountId, sectionId }: ProductsSectionEvent) { + this.accountId = accountId; + this.sectionId = sectionId; + } +} diff --git a/backend/src/modules/inventory/common/events/rental-order/index.ts b/backend/src/modules/inventory/common/events/rental-order/index.ts new file mode 100644 index 0000000..0cc6385 --- /dev/null +++ b/backend/src/modules/inventory/common/events/rental-order/index.ts @@ -0,0 +1,2 @@ +export * from './rental-order-created.event'; +export * from './rental-order.event'; diff --git a/backend/src/modules/inventory/common/events/rental-order/rental-order-created.event.ts b/backend/src/modules/inventory/common/events/rental-order/rental-order-created.event.ts new file mode 100644 index 0000000..e6382f3 --- /dev/null +++ b/backend/src/modules/inventory/common/events/rental-order/rental-order-created.event.ts @@ -0,0 +1,10 @@ +import { RentalOrderEvent } from './rental-order.event'; + +export class RentalOrderCreatedEvent extends RentalOrderEvent { + createdAt: string; + + constructor({ accountId, entityId, rentalOrderId, createdAt }: RentalOrderCreatedEvent) { + super({ accountId, entityId, rentalOrderId }); + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/inventory/common/events/rental-order/rental-order.event.ts b/backend/src/modules/inventory/common/events/rental-order/rental-order.event.ts new file mode 100644 index 0000000..070302d --- /dev/null +++ b/backend/src/modules/inventory/common/events/rental-order/rental-order.event.ts @@ -0,0 +1,11 @@ +export class RentalOrderEvent { + accountId: number; + entityId: number; + rentalOrderId: number; + + constructor({ accountId, entityId, rentalOrderId }: RentalOrderEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.rentalOrderId = rentalOrderId; + } +} diff --git a/backend/src/modules/inventory/common/events/shipment/index.ts b/backend/src/modules/inventory/common/events/shipment/index.ts new file mode 100644 index 0000000..5ff2ad8 --- /dev/null +++ b/backend/src/modules/inventory/common/events/shipment/index.ts @@ -0,0 +1,4 @@ +export * from './shipment-created.event'; +export * from './shipment-deleted.event'; +export * from './shipment-status-changed.event'; +export * from './shipment.event'; diff --git a/backend/src/modules/inventory/common/events/shipment/shipment-created.event.ts b/backend/src/modules/inventory/common/events/shipment/shipment-created.event.ts new file mode 100644 index 0000000..3292711 --- /dev/null +++ b/backend/src/modules/inventory/common/events/shipment/shipment-created.event.ts @@ -0,0 +1,13 @@ +import { ShipmentEvent } from './shipment.event'; + +export class ShipmentCreatedEvent extends ShipmentEvent { + entityId: number; + createdAt: string; + + constructor({ accountId, sectionId, orderId, shipmentId, entityId, createdAt }: ShipmentCreatedEvent) { + super({ accountId, sectionId, orderId, shipmentId }); + + this.entityId = entityId; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/inventory/common/events/shipment/shipment-deleted.event.ts b/backend/src/modules/inventory/common/events/shipment/shipment-deleted.event.ts new file mode 100644 index 0000000..66e9ca1 --- /dev/null +++ b/backend/src/modules/inventory/common/events/shipment/shipment-deleted.event.ts @@ -0,0 +1,11 @@ +import { ShipmentEvent } from './shipment.event'; + +export class ShipmentDeletedEvent extends ShipmentEvent { + entityId: number; + + constructor({ accountId, sectionId, orderId, shipmentId, entityId }: ShipmentDeletedEvent) { + super({ accountId, sectionId, orderId, shipmentId }); + + this.entityId = entityId; + } +} diff --git a/backend/src/modules/inventory/common/events/shipment/shipment-status-changed.event.ts b/backend/src/modules/inventory/common/events/shipment/shipment-status-changed.event.ts new file mode 100644 index 0000000..424fa72 --- /dev/null +++ b/backend/src/modules/inventory/common/events/shipment/shipment-status-changed.event.ts @@ -0,0 +1,11 @@ +import { ShipmentEvent } from './shipment.event'; + +export class ShipmentStatusChangedEvent extends ShipmentEvent { + statusId: number; + + constructor({ accountId, sectionId, orderId, shipmentId, statusId }: ShipmentStatusChangedEvent) { + super({ accountId, sectionId, orderId, shipmentId }); + + this.statusId = statusId; + } +} diff --git a/backend/src/modules/inventory/common/events/shipment/shipment.event.ts b/backend/src/modules/inventory/common/events/shipment/shipment.event.ts new file mode 100644 index 0000000..c86ce33 --- /dev/null +++ b/backend/src/modules/inventory/common/events/shipment/shipment.event.ts @@ -0,0 +1,13 @@ +export class ShipmentEvent { + accountId: number; + sectionId: number; + orderId: number; + shipmentId: number; + + constructor({ accountId, sectionId, orderId, shipmentId }: ShipmentEvent) { + this.accountId = accountId; + this.sectionId = sectionId; + this.orderId = orderId; + this.shipmentId = shipmentId; + } +} diff --git a/backend/src/modules/inventory/common/index.ts b/backend/src/modules/inventory/common/index.ts new file mode 100644 index 0000000..df1eda9 --- /dev/null +++ b/backend/src/modules/inventory/common/index.ts @@ -0,0 +1,2 @@ +export * from './enums'; +export * from './events'; diff --git a/backend/src/modules/inventory/inventory-reporting/dto/index.ts b/backend/src/modules/inventory/inventory-reporting/dto/index.ts new file mode 100644 index 0000000..a217f25 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/dto/index.ts @@ -0,0 +1,4 @@ +export * from './inventory-report-filter.dto'; +export * from './inventory-report-row.dto'; +export * from './inventory-report-user-cell.dto'; +export * from './inventory-report.dto'; diff --git a/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-filter.dto.ts b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-filter.dto.ts new file mode 100644 index 0000000..7c19862 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-filter.dto.ts @@ -0,0 +1,50 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; +import { BoardStageType } from '@/CRM/board-stage'; + +import { InventoryReportType } from '../enums'; + +export class InventoryReportFilterDto { + @ApiProperty({ enum: InventoryReportType }) + @IsEnum(InventoryReportType) + type: InventoryReportType; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + @ApiProperty() + @IsNumber() + productsSectionId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + warehouseIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + categoryIds?: number[] | null; + + @ApiPropertyOptional({ enum: BoardStageType, nullable: true }) + @IsOptional() + @IsEnum(BoardStageType) + stageType?: BoardStageType | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-row.dto.ts b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-row.dto.ts new file mode 100644 index 0000000..a983756 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-row.dto.ts @@ -0,0 +1,70 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { QuantityAmountDto } from '@/common'; +import { InventoryReportUserCellDto } from './inventory-report-user-cell.dto'; + +export class InventoryReportRowDto { + @ApiProperty() + ownerId: number; + + @ApiProperty({ nullable: true }) + categoryId: number | null; + + @ApiProperty({ nullable: true }) + productName: string | null; + + @ApiProperty({ type: QuantityAmountDto }) + sold: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, nullable: true }) + shipped: QuantityAmountDto | null; + + @ApiProperty({ type: QuantityAmountDto, nullable: true }) + open: QuantityAmountDto | null; + + @ApiProperty({ type: QuantityAmountDto, nullable: true }) + lost: QuantityAmountDto | null; + + @ApiProperty({ type: QuantityAmountDto, nullable: true }) + all: QuantityAmountDto | null; + + @ApiProperty({ nullable: true }) + avgProducts: number | null; + + @ApiProperty({ nullable: true }) + avgBudget: number | null; + + @ApiProperty({ nullable: true }) + avgTerm: number | null; + + @ApiProperty({ type: [InventoryReportUserCellDto], nullable: true }) + users: InventoryReportUserCellDto[] | null; + + constructor({ + ownerId, + categoryId, + productName, + sold, + shipped, + open, + lost, + all, + avgProducts, + avgBudget, + avgTerm, + users, + }: InventoryReportRowDto) { + this.ownerId = ownerId; + this.categoryId = categoryId; + this.productName = productName; + this.sold = sold; + this.shipped = shipped; + this.open = open; + this.lost = lost; + this.all = all; + this.avgProducts = avgProducts; + this.avgBudget = avgBudget; + this.avgTerm = avgTerm; + this.users = users; + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-user-cell.dto.ts b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-user-cell.dto.ts new file mode 100644 index 0000000..36f6d1f --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report-user-cell.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { QuantityAmountDto } from '@/common'; + +export class InventoryReportUserCellDto { + @ApiProperty() + userId: number; + + @ApiProperty({ type: QuantityAmountDto }) + value: QuantityAmountDto; + + constructor({ userId, value }: InventoryReportUserCellDto) { + this.userId = userId; + this.value = value; + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/dto/inventory-report.dto.ts b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report.dto.ts new file mode 100644 index 0000000..89ddfac --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/dto/inventory-report.dto.ts @@ -0,0 +1,17 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { InventoryReportRowDto } from './inventory-report-row.dto'; + +export class InventoryReportDto { + @ApiPropertyOptional({ type: [InventoryReportRowDto], nullable: true }) + products: InventoryReportRowDto[] | null | undefined; + @ApiPropertyOptional({ type: [InventoryReportRowDto], nullable: true }) + categories: InventoryReportRowDto[] | null | undefined; + @ApiPropertyOptional({ type: InventoryReportRowDto, nullable: true }) + total: InventoryReportRowDto | null | undefined; + + constructor({ products, categories, total }: InventoryReportDto) { + this.products = products; + this.categories = categories; + this.total = total; + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/enums/index.ts b/backend/src/modules/inventory/inventory-reporting/enums/index.ts new file mode 100644 index 0000000..88f3be6 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/enums/index.ts @@ -0,0 +1 @@ +export * from './products-report-type.enum'; diff --git a/backend/src/modules/inventory/inventory-reporting/enums/products-report-type.enum.ts b/backend/src/modules/inventory/inventory-reporting/enums/products-report-type.enum.ts new file mode 100644 index 0000000..246591b --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/enums/products-report-type.enum.ts @@ -0,0 +1,5 @@ +export enum InventoryReportType { + Product = 'product', + Category = 'category', + User = 'user', +} diff --git a/backend/src/modules/inventory/inventory-reporting/inventory-reporting.controller.ts b/backend/src/modules/inventory/inventory-reporting/inventory-reporting.controller.ts new file mode 100644 index 0000000..751db77 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/inventory-reporting.controller.ts @@ -0,0 +1,24 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { InventoryReportDto, InventoryReportFilterDto } from './dto'; +import { InventoryReportingService } from './inventory-reporting.service'; + +@ApiTags('inventory/reporting') +@Controller('products/reporting') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class InventoryReportingController { + constructor(private readonly service: InventoryReportingService) {} + + @ApiOperation({ summary: 'Get products general report', description: 'Get products general report' }) + @ApiBody({ type: InventoryReportFilterDto, required: true, description: 'Products general report filter' }) + @ApiOkResponse({ description: 'Products general report', type: InventoryReportDto }) + @Post('general') + public async getReport(@CurrentAuth() { accountId, user }: AuthData, @Body() filter: InventoryReportFilterDto) { + return this.service.getReport({ accountId, user, filter }); + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/inventory-reporting.module.ts b/backend/src/modules/inventory/inventory-reporting/inventory-reporting.module.ts new file mode 100644 index 0000000..43d7601 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/inventory-reporting.module.ts @@ -0,0 +1,25 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { Product } from '../product/entities/product.entity'; +import { ProductModule } from '../product/product.module'; +import { ProductCategoryModule } from '../product-category/product-category.module'; + +import { InventoryReportingService } from './inventory-reporting.service'; +import { InventoryReportingController } from './inventory-reporting.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Product]), + IAMModule, + forwardRef(() => CrmModule), + ProductModule, + ProductCategoryModule, + ], + providers: [InventoryReportingService], + controllers: [InventoryReportingController], +}) +export class InventoryReportingModule {} diff --git a/backend/src/modules/inventory/inventory-reporting/inventory-reporting.service.ts b/backend/src/modules/inventory/inventory-reporting/inventory-reporting.service.ts new file mode 100644 index 0000000..7b3f2da --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/inventory-reporting.service.ts @@ -0,0 +1,351 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { QuantityAmount, NumberUtil, DatePeriod } from '@/common'; +import { User } from '@/modules/iam/user/entities'; +import { BoardStageService } from '@/CRM/board-stage/board-stage.service'; +import { GroupedStages } from '@/CRM/board-stage/types'; + +import { Product } from '../product/entities/product.entity'; +import { ProductCategory } from '../product-category/entities/product-category.entity'; +import { ProductCategoryService } from '../product-category/product-category.service'; + +import { InventoryReportFilterDto } from './dto'; +import { InventoryReportType } from './enums'; +import { InventoryReport, InventoryReportRow } from './types'; + +enum GroupBy { + Product, + Category, +} +interface Filter { + entityTypeId: number; + sectionId: number; + userIds?: number[]; + warehouseIds?: number[]; + categoryIds?: number[]; + period?: DatePeriod | null; +} + +@Injectable() +export class InventoryReportingService { + constructor( + @InjectRepository(Product) + private readonly repository: Repository, + private readonly stageService: BoardStageService, + private readonly categoryService: ProductCategoryService, + ) {} + + public async getReport({ + accountId, + filter, + }: { + accountId: number; + user: User; + filter: InventoryReportFilterDto; + }): Promise { + const stages = await this.stageService.getGroupedByType({ + accountId, + entityTypeId: filter.entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + type: filter.stageType, + }); + + const reportFilter: Filter = { + entityTypeId: filter.entityTypeId, + sectionId: filter.productsSectionId, + userIds: filter.userIds, + warehouseIds: filter.warehouseIds, + categoryIds: filter.categoryIds, + period: filter.period ? DatePeriod.fromFilter(filter.period) : undefined, + }; + return filter.type === InventoryReportType.User + ? this.getProductsUserReport({ accountId, filter: reportFilter, stages }) + : this.getProductsReport({ accountId, filter: reportFilter, stages, type: filter.type }); + } + + private async getProductsReport({ + accountId, + filter, + stages, + type, + }: { + accountId: number; + filter: Filter; + stages: GroupedStages; + type: InventoryReportType; + }): Promise { + const { entityTypeId, sectionId } = filter; + const products = + type !== InventoryReportType.Category + ? await this.getProductReportGroupBy(accountId, entityTypeId, stages, sectionId, GroupBy.Product, filter) + : null; + + const categories = await this.getProductReportGroupBy( + accountId, + entityTypeId, + stages, + sectionId, + GroupBy.Category, + filter, + ); + + if (categories.size) { + await this.processCategories(accountId, sectionId, categories); + } + + const total = await this.getProductReportGroupBy(accountId, entityTypeId, stages, sectionId, null, filter); + + return new InventoryReport(products, categories, total.values().next().value); + } + + private async getProductsUserReport({ + accountId, + filter, + stages, + }: { + accountId: number; + filter: Filter; + stages: GroupedStages; + }): Promise { + const { entityTypeId, sectionId } = filter; + const products = await this.getProductUserReportGroupBy( + accountId, + entityTypeId, + stages, + sectionId, + GroupBy.Product, + filter, + ); + + const categories = await this.getProductUserReportGroupBy( + accountId, + entityTypeId, + stages, + sectionId, + GroupBy.Category, + filter, + ); + + if (categories.size > 0) { + await this.processCategories(accountId, sectionId, categories); + } + + const total = await this.getProductUserReportGroupBy(accountId, entityTypeId, stages, sectionId, null, filter); + + return new InventoryReport(products, categories, total.values().next().value); + } + + private async getProductReportGroupBy( + accountId: number, + entityTypeId: number, + stages: { open: number[]; won: number[]; lost: number[] }, + productsSectionId: number, + groupBy: GroupBy, + filter?: Filter, + ): Promise> { + const rowMap = new Map(); + + const qb = this.createQb(accountId, entityTypeId, productsSectionId, groupBy, filter); + + const wonStageIds = stages.won?.join(','); + if (wonStageIds?.length > 0) { + qb.addSelect(`sum(oi.quantity) filter (where e.stage_id = any(array[${wonStageIds}]))`, 'sold_quantity'); + qb.addSelect( + `sum(oi.unit_price * oi.quantity) filter (where e.stage_id = any(array[${wonStageIds}]))`, + 'sold_amount', + ); + qb.leftJoin('field_value', 'fv', `e.id = fv.entity_id and fv.field_type = 'value'`); + qb.addSelect( + `avg((fv.payload->>'value')::numeric) filter (where e.stage_id = any(array[${wonStageIds}]))`, + 'avg_budget', + ); + qb.addSelect( + // eslint-disable-next-line max-len + `avg(extract(epoch from age(e.closed_at, e.created_at))) filter (where e.stage_id = any(array[${wonStageIds}]))`, + 'avg_close_time', + ); + } + + const lostStageIds = stages.lost?.join(','); + if (lostStageIds?.length > 0) { + qb.addSelect(`sum(oi.quantity) filter (where e.stage_id = any(array[${lostStageIds}]))`, 'lost_quantity'); + qb.addSelect( + `sum(oi.unit_price * oi.quantity) filter (where e.stage_id = any(array[${lostStageIds}]))`, + 'lost_amount', + ); + } + + const openStageIds = stages.open?.join(','); + if (openStageIds?.length > 0) { + qb.addSelect(`sum(oi.quantity) filter (where e.stage_id = any(array[${openStageIds}]))`, 'open_quantity'); + qb.addSelect( + `sum(oi.unit_price * oi.quantity) filter (where e.stage_id = any(array[${openStageIds}]))`, + 'open_amount', + ); + } + + qb.innerJoin('order_status', 'os', 'o.status_id = os.id'); + qb.addSelect(`sum(oi.quantity)`, 'all_quantity'); + qb.addSelect(`sum(oi.unit_price * oi.quantity)`, 'all_amount'); + qb.addSelect(`sum(oi.quantity) filter (where os.code = 'shipped')`, 'shipped_quantity'); + qb.addSelect(`sum(oi.unit_price * oi.quantity) filter (where os.code = 'shipped')`, 'shipped_amount'); + qb.addSelect(`sum(oi.quantity)::float / count(distinct e.id)`, 'avg_products'); + + const rows = await qb.getRawMany(); + for (const row of rows) { + rowMap.set( + row.row_id, + new InventoryReportRow(row.row_id, row.category_id ?? null, { + productName: row.product_name, + sold: new QuantityAmount(NumberUtil.toNumber(row.sold_quantity), NumberUtil.toNumber(row.sold_amount)), + shipped: new QuantityAmount( + NumberUtil.toNumber(row.shipped_quantity), + NumberUtil.toNumber(row.shipped_amount), + ), + open: new QuantityAmount(NumberUtil.toNumber(row.open_quantity), NumberUtil.toNumber(row.open_amount)), + lost: new QuantityAmount(NumberUtil.toNumber(row.lost_quantity), NumberUtil.toNumber(row.lost_amount)), + all: new QuantityAmount(NumberUtil.toNumber(row.all_quantity), NumberUtil.toNumber(row.all_amount)), + avgProducts: NumberUtil.toNumber(row.avg_products), + avgBudget: NumberUtil.toNumber(row.avg_budget), + avgTerm: NumberUtil.toNumber(row.avg_close_time), + }), + ); + } + + return rowMap; + } + + private async getProductUserReportGroupBy( + accountId: number, + entityTypeId: number, + stages: { open: number[]; won: number[]; lost: number[] }, + productsSectionId: number, + groupBy: GroupBy, + filter?: Filter, + ): Promise> { + const rowMap = new Map(); + + const qb = this.createQb(accountId, entityTypeId, productsSectionId, groupBy, filter); + + const wonStageIds = stages.won?.join(','); + if (wonStageIds?.length > 0) { + qb.addSelect(`sum(oi.quantity) filter (where e.stage_id = any(array[${wonStageIds}]))`, 'sold_quantity'); + qb.addSelect( + `sum(oi.unit_price * oi.quantity) filter (where e.stage_id = any(array[${wonStageIds}]))`, + 'sold_amount', + ); + } + + const soldRows = await qb.getRawMany(); + for (const row of soldRows) { + if (row.sold_quantity || row.sold_amount) { + rowMap.set( + row.row_id, + new InventoryReportRow(row.row_id, row.category_id ?? null, { + productName: row.product_name, + sold: new QuantityAmount(NumberUtil.toNumber(row.sold_quantity), NumberUtil.toNumber(row.sold_amount)), + }), + ); + } + } + + const userRows = await qb + .clone() + .addSelect('e.responsible_user_id', 'owner_id') + .addGroupBy('e.responsible_user_id') + .getRawMany(); + for (const row of userRows) { + if (row.sold_quantity || row.sold_amount) { + const reportRow = rowMap.get(row.row_id) ?? new InventoryReportRow(row.row_id, row.category_id ?? null); + reportRow.addUser( + row.owner_id, + new QuantityAmount(NumberUtil.toNumber(row.sold_quantity), NumberUtil.toNumber(row.sold_amount)), + ); + + rowMap.set(row.row_id, reportRow); + } + } + + return rowMap; + } + + private createQb( + accountId: number, + entityTypeId: number, + productsSectionId: number, + groupBy: GroupBy, + filter?: Filter, + ) { + const qb = this.repository + .createQueryBuilder('p') + .innerJoin('order_item', 'oi', 'p.id = oi.product_id') + .innerJoin('orders', 'o', 'oi.order_id = o.id') + .innerJoin('entity', 'e', 'o.entity_id = e.id') + .where('p.account_id = :accountId', { accountId }) + .andWhere('p.section_id = :productsSectionId', { productsSectionId }) + .andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + + if (groupBy === GroupBy.Product) { + qb.select('p.id', 'row_id') + .addSelect('p.category_id', 'category_id') + .addSelect('p.name', 'product_name') + .groupBy('p.id') + .addGroupBy('p.category_id') + .addGroupBy('p.name'); + } else if (groupBy === GroupBy.Category) { + qb.select('p.category_id', 'row_id').groupBy('p.category_id'); + } else { + qb.select('0', 'row_id'); + } + + if (filter?.userIds?.length > 0) { + qb.andWhere('e.responsible_user_id IN (:...userIds)', { userIds: filter.userIds }); + } + if (filter?.warehouseIds?.length > 0) { + qb.andWhere('o.warehouse_id IN (:...warehouseIds)', { warehouseIds: filter.warehouseIds }); + } + if (filter?.categoryIds?.length > 0) { + qb.andWhere('p.category_id IN (:...categoryIds)', { categoryIds: filter.categoryIds }); + } + if (filter?.period?.from) { + qb.andWhere('o.created_at >= :from', { from: filter.period.from }); + } + if (filter?.period?.to) { + qb.andWhere('o.created_at <= :to', { to: filter.period.to }); + } + + return qb; + } + + private async processCategories(accountId: number, sectionId: number, rowsMap: Map) { + const categories = await this.categoryService.getCategories(accountId, sectionId, null); + + if (categories?.length > 0) { + this.aggregateData(categories, rowsMap); + } + } + private aggregateData(categories: ProductCategory[], rowsMap: Map) { + const aggregateSubordinates = (category: ProductCategory): InventoryReportRow | null => { + let aggregatedRow = rowsMap.get(category.id) || InventoryReportRow.empty(category.id, null); + + let hasValue = rowsMap.has(category.id); + category.children.forEach((child) => { + const childRow = aggregateSubordinates(child); + if (childRow) { + aggregatedRow = aggregatedRow.add(childRow); + hasValue = true; + } + }); + return hasValue ? aggregatedRow : null; + }; + + categories.forEach((category) => { + const aggregatedRow = aggregateSubordinates(category); + if (aggregatedRow) { + rowsMap.set(category.id, aggregatedRow); + } + }); + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/types/index.ts b/backend/src/modules/inventory/inventory-reporting/types/index.ts new file mode 100644 index 0000000..35a37b3 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/types/index.ts @@ -0,0 +1,3 @@ +export * from './inventory-report-row'; +export * from './inventory-report-user-cell'; +export * from './inventory-report'; diff --git a/backend/src/modules/inventory/inventory-reporting/types/inventory-report-row.ts b/backend/src/modules/inventory/inventory-reporting/types/inventory-report-row.ts new file mode 100644 index 0000000..3ba0787 --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/types/inventory-report-row.ts @@ -0,0 +1,135 @@ +import { type QuantityAmount } from '@/common'; +import { InventoryReportRowDto } from '../dto'; +import { InventoryReportUserCell } from './inventory-report-user-cell'; + +export class InventoryReportRow { + ownerId: number; + categoryId: number | null; + productName: string | null; + sold?: QuantityAmount; + shipped?: QuantityAmount; + open?: QuantityAmount; + lost?: QuantityAmount; + all?: QuantityAmount; + avgProducts?: number; + avgBudget?: number; + avgTerm?: number; + users?: Map; + + constructor( + ownerId: number, + categoryId: number | null, + values?: { + productName: string | null; + sold?: QuantityAmount; + shipped?: QuantityAmount; + open?: QuantityAmount; + lost?: QuantityAmount; + all?: QuantityAmount; + avgProducts?: number; + avgBudget?: number; + avgTerm?: number; + users?: Map; + }, + ) { + this.ownerId = ownerId; + this.categoryId = categoryId; + this.productName = values?.productName; + this.sold = values?.sold; + this.shipped = values?.shipped; + this.open = values?.open; + this.lost = values?.lost; + this.all = values?.all; + this.avgProducts = values?.avgProducts; + this.avgBudget = values?.avgBudget; + this.avgTerm = values?.avgTerm; + this.users = values?.users; + } + + public static empty(ownerId: number, categoryId: number | null): InventoryReportRow { + return new InventoryReportRow(ownerId, categoryId); + } + + public toDto(): InventoryReportRowDto { + return new InventoryReportRowDto({ + ownerId: this.ownerId, + categoryId: this.categoryId, + productName: this.productName, + sold: this.sold?.toDto(), + shipped: this.shipped?.toDto(), + open: this.open?.toDto(), + lost: this.lost?.toDto(), + all: this.all?.toDto(), + avgProducts: this.avgProducts, + avgBudget: this.avgBudget, + avgTerm: this.avgTerm, + users: this.users?.size ? Array.from(this.users.values()).map((v) => v.toDto()) : undefined, + }); + } + + public addUser(ownerId: number, value: QuantityAmount) { + if (!this.users) { + this.users = new Map(); + } + this.users.set(ownerId, new InventoryReportUserCell(ownerId, value)); + } + + public add(row: InventoryReportRow): InventoryReportRow { + if (this.sold) { + this.sold.add(row.sold); + } else { + this.sold = row.sold; + } + if (this.shipped) { + this.shipped.add(row.shipped); + } else { + this.shipped = row.shipped; + } + if (this.open) { + this.open.add(row.open); + } else { + this.open = row.open; + } + if (this.lost) { + this.lost.add(row.lost); + } else { + this.lost = row.lost; + } + if (this.all) { + this.all.add(row.all); + } else { + this.all = row.all; + } + if (this.avgProducts) { + this.avgProducts += row.avgProducts ?? 0; + } else { + this.avgProducts = row.avgProducts; + } + if (this.avgBudget) { + this.avgBudget += row.avgBudget ?? 0; + } else { + this.avgBudget = row.avgBudget; + } + if (this.avgTerm) { + this.avgTerm += row.avgTerm ?? 0; + } else { + this.avgTerm = row.avgTerm; + } + if (this.users) { + for (const [userId, userCell] of row.users) { + if (this.users.has(userId)) { + this.users.get(userId).add(userCell); + } else { + this.users.set(userId, userCell); + } + } + } else if (row.users) { + this.users = new Map(); + for (const [userId, userCell] of row.users) { + this.users.set(userId, userCell); + } + } + + return this; + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/types/inventory-report-user-cell.ts b/backend/src/modules/inventory/inventory-reporting/types/inventory-report-user-cell.ts new file mode 100644 index 0000000..2ec2aed --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/types/inventory-report-user-cell.ts @@ -0,0 +1,20 @@ +import { type QuantityAmount } from '@/common'; +import { InventoryReportUserCellDto } from '../dto'; + +export class InventoryReportUserCell { + userId: number; + value: QuantityAmount; + + constructor(userId: number, value: QuantityAmount) { + this.userId = userId; + this.value = value; + } + + public toDto(): InventoryReportUserCellDto { + return new InventoryReportUserCellDto({ userId: this.userId, value: this.value.toDto() }); + } + + public add(cell: InventoryReportUserCell) { + this.value.add(cell.value); + } +} diff --git a/backend/src/modules/inventory/inventory-reporting/types/inventory-report.ts b/backend/src/modules/inventory/inventory-reporting/types/inventory-report.ts new file mode 100644 index 0000000..639e34c --- /dev/null +++ b/backend/src/modules/inventory/inventory-reporting/types/inventory-report.ts @@ -0,0 +1,30 @@ +import { InventoryReportDto } from '../dto'; +import { InventoryReportRow } from './inventory-report-row'; + +export class InventoryReport { + products: Map | null | undefined; + categories: Map | null | undefined; + total: InventoryReportRow | null | undefined; + + constructor( + products: Map | null | undefined, + categories: Map | null | undefined, + total: InventoryReportRow | null | undefined, + ) { + this.products = products; + this.categories = categories; + this.total = total; + } + + public static createEmptyRow(ownerId: number, categoryId: number | null): InventoryReportRow { + return InventoryReportRow.empty(ownerId, categoryId); + } + + public toDto(): InventoryReportDto { + return new InventoryReportDto({ + products: this.products ? Array.from(this.products.values()).map((u) => u.toDto()) : undefined, + categories: this.categories ? Array.from(this.categories.values()).map((u) => u.toDto()) : undefined, + total: this.total ? this.total.toDto() : undefined, + }); + } +} diff --git a/backend/src/modules/inventory/inventory.module.ts b/backend/src/modules/inventory/inventory.module.ts new file mode 100644 index 0000000..69fe132 --- /dev/null +++ b/backend/src/modules/inventory/inventory.module.ts @@ -0,0 +1,47 @@ +import { Module } from '@nestjs/common'; + +import { OrderStatusModule } from './order-status/order-status.module'; +import { OrderModule } from './order/order.module'; +import { ProductCategoryModule } from './product-category/product-category.module'; +import { ProductPriceModule } from './product-price/product-price.module'; +import { ProductStockModule } from './product-stock/product-stock.module'; +import { ProductModule } from './product/product.module'; +import { ProductsSectionModule } from './products-section/products-section.module'; +import { RentalIntervalModule } from './rental-interval/rental-interval.module'; +import { RentalOrderModule } from './rental-order/rental-order.module'; +import { RentalScheduleModule } from './rental-schedule/rental-schedule.module'; +import { ReservationModule } from './reservation/reservation.module'; +import { ShipmentModule } from './shipment/shipment.module'; +import { WarehouseModule } from './warehouse/warehouse.module'; +import { InventoryReportingModule } from './inventory-reporting/inventory-reporting.module'; + +@Module({ + imports: [ + OrderStatusModule, + OrderModule, + ProductCategoryModule, + ProductModule, + ProductPriceModule, + ProductStockModule, + ProductsSectionModule, + RentalIntervalModule, + RentalOrderModule, + RentalScheduleModule, + ReservationModule, + ShipmentModule, + WarehouseModule, + InventoryReportingModule, + ], + exports: [ + OrderStatusModule, + OrderModule, + ProductCategoryModule, + ProductModule, + ProductsSectionModule, + RentalIntervalModule, + RentalOrderModule, + WarehouseModule, + ShipmentModule, + ], +}) +export class InventoryModule {} diff --git a/backend/src/modules/inventory/order-status/dto/create-order-status.dto.ts b/backend/src/modules/inventory/order-status/dto/create-order-status.dto.ts new file mode 100644 index 0000000..8a1444c --- /dev/null +++ b/backend/src/modules/inventory/order-status/dto/create-order-status.dto.ts @@ -0,0 +1,23 @@ +import { OmitType } from '@nestjs/swagger'; + +import { type OrderStatusCode } from '../enums/order-status-code.enum'; +import { OrderStatusDto } from './order-status.dto'; + +export class CreateOrderStatusDto extends OmitType(OrderStatusDto, ['id'] as const) { + constructor(name: string, color: string, code: OrderStatusCode | null, sortOrder: number) { + super(); + + this.name = name; + this.color = color; + this.code = code; + this.sortOrder = sortOrder; + } + + public static system(name: string, color: string, code: OrderStatusCode, sortOrder: number): CreateOrderStatusDto { + return new CreateOrderStatusDto(name, color, code, sortOrder); + } + + public static custom(name: string, color: string, sortOrder: number): CreateOrderStatusDto { + return new CreateOrderStatusDto(name, color, null, sortOrder); + } +} diff --git a/backend/src/modules/inventory/order-status/dto/index.ts b/backend/src/modules/inventory/order-status/dto/index.ts new file mode 100644 index 0000000..cdc649d --- /dev/null +++ b/backend/src/modules/inventory/order-status/dto/index.ts @@ -0,0 +1,2 @@ +export * from './create-order-status.dto'; +export * from './order-status.dto'; diff --git a/backend/src/modules/inventory/order-status/dto/order-status.dto.ts b/backend/src/modules/inventory/order-status/dto/order-status.dto.ts new file mode 100644 index 0000000..36997c7 --- /dev/null +++ b/backend/src/modules/inventory/order-status/dto/order-status.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { OrderStatusCode } from '../enums/order-status-code.enum'; + +export class OrderStatusDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsString() + color: string; + + @ApiProperty() + @IsOptional() + @IsEnum(OrderStatusCode) + code: OrderStatusCode | null; + + @ApiProperty() + @IsNumber() + sortOrder: number; + + constructor(id: number, name: string, color: string, code: OrderStatusCode | null, sortOrder: number) { + this.id = id; + this.name = name; + this.color = color; + this.code = code; + this.sortOrder = sortOrder; + } +} diff --git a/backend/src/modules/inventory/order-status/entities/index.ts b/backend/src/modules/inventory/order-status/entities/index.ts new file mode 100644 index 0000000..72df4ef --- /dev/null +++ b/backend/src/modules/inventory/order-status/entities/index.ts @@ -0,0 +1 @@ +export * from './order-status.entity'; diff --git a/backend/src/modules/inventory/order-status/entities/order-status.entity.ts b/backend/src/modules/inventory/order-status/entities/order-status.entity.ts new file mode 100644 index 0000000..60aa4f6 --- /dev/null +++ b/backend/src/modules/inventory/order-status/entities/order-status.entity.ts @@ -0,0 +1,42 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { OrderStatusCode } from '../enums/order-status-code.enum'; +import { CreateOrderStatusDto } from '../dto/create-order-status.dto'; +import { OrderStatusDto } from '../dto/order-status.dto'; + +@Entity() +export class OrderStatus { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + color: string; + + @Column() + code: OrderStatusCode | null; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + constructor(accountId: number, name: string, color: string, code: OrderStatusCode | null, sortOrder: number) { + this.accountId = accountId; + this.name = name; + this.color = color; + this.code = code; + this.sortOrder = sortOrder; + } + + public static fromDto(accountId: number, dto: CreateOrderStatusDto): OrderStatus { + return new OrderStatus(accountId, dto.name, dto.color, dto.code, dto.sortOrder); + } + + public toDto(): OrderStatusDto { + return new OrderStatusDto(this.id, this.name, this.color, this.code, this.sortOrder); + } +} diff --git a/backend/src/modules/inventory/order-status/enums/index.ts b/backend/src/modules/inventory/order-status/enums/index.ts new file mode 100644 index 0000000..9127ff2 --- /dev/null +++ b/backend/src/modules/inventory/order-status/enums/index.ts @@ -0,0 +1 @@ +export * from './order-status-code.enum'; diff --git a/backend/src/modules/inventory/order-status/enums/order-status-code.enum.ts b/backend/src/modules/inventory/order-status/enums/order-status-code.enum.ts new file mode 100644 index 0000000..9e1b8c4 --- /dev/null +++ b/backend/src/modules/inventory/order-status/enums/order-status-code.enum.ts @@ -0,0 +1,7 @@ +export enum OrderStatusCode { + Reserved = 'reserved', + SentForShipment = 'sent_for_shipment', + Shipped = 'shipped', + Cancelled = 'cancelled', + Returned = 'returned', +} diff --git a/backend/src/modules/inventory/order-status/order-status.controller.ts b/backend/src/modules/inventory/order-status/order-status.controller.ts new file mode 100644 index 0000000..a7b7b6c --- /dev/null +++ b/backend/src/modules/inventory/order-status/order-status.controller.ts @@ -0,0 +1,25 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { OrderStatusDto } from './dto/order-status.dto'; +import { OrderStatusService } from './order-status.service'; + +@ApiTags('inventory/order/statuses') +@Controller('products/order-statuses') +@JwtAuthorized() +@TransformToDto() +export class OrderStatusController { + constructor(private service: OrderStatusService) {} + + @ApiCreatedResponse({ description: 'Get order statuses', type: [OrderStatusDto] }) + @Get() + public async getStatuses(@CurrentAuth() { accountId }: AuthData) { + return this.service.getManyOrDefault(accountId); + } +} diff --git a/backend/src/modules/inventory/order-status/order-status.module.ts b/backend/src/modules/inventory/order-status/order-status.module.ts new file mode 100644 index 0000000..36d2f15 --- /dev/null +++ b/backend/src/modules/inventory/order-status/order-status.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { OrderStatus } from './entities/order-status.entity'; +import { OrderStatusController } from './order-status.controller'; +import { OrderStatusService } from './order-status.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([OrderStatus]), IAMModule], + controllers: [OrderStatusController], + providers: [OrderStatusService], + exports: [OrderStatusService], +}) +export class OrderStatusModule {} diff --git a/backend/src/modules/inventory/order-status/order-status.service.ts b/backend/src/modules/inventory/order-status/order-status.service.ts new file mode 100644 index 0000000..4433133 --- /dev/null +++ b/backend/src/modules/inventory/order-status/order-status.service.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { OrderStatusCode } from './enums/order-status-code.enum'; +import { CreateOrderStatusDto } from './dto/create-order-status.dto'; +import { OrderStatus } from './entities/order-status.entity'; + +interface FindFilter { + statusId?: number; + code?: OrderStatusCode; +} + +@Injectable() +export class OrderStatusService { + constructor( + @InjectRepository(OrderStatus) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, dto: CreateOrderStatusDto): Promise { + return await this.repository.save(OrderStatus.fromDto(accountId, dto)); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createQb(accountId, filter).getOne(); + } + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createQb(accountId, filter).getMany(); + } + + public async getManyOrDefault(accountId: number): Promise { + const statuses = await this.repository.find({ where: { accountId }, order: { sortOrder: 'ASC' } }); + if (statuses.length === 0) { + return await this.createDefaultStatuses(accountId); + } + return statuses; + } + + private createQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('os').where('os.account_id = :accountId', { accountId }); + if (filter?.statusId) { + qb.andWhere('os.id = :id', { id: filter.statusId }); + } + if (filter?.code) { + qb.andWhere('os.code = :code', { code: filter.code }); + } + return qb; + } + + private async createDefaultStatuses(accountId: number): Promise { + let sortOrder = 0; + const dtos = [ + CreateOrderStatusDto.system('Reserved', '#ea925a', OrderStatusCode.Reserved, sortOrder++), + CreateOrderStatusDto.system('Sent for shipment', '#a33cab', OrderStatusCode.SentForShipment, sortOrder++), + CreateOrderStatusDto.system('Shipped', '#8af039', OrderStatusCode.Shipped, sortOrder++), + CreateOrderStatusDto.system('Cancelled', '#ee675c', OrderStatusCode.Cancelled, sortOrder++), + CreateOrderStatusDto.system('Returned', '#c0c5cc', OrderStatusCode.Returned, sortOrder++), + ]; + return await Promise.all(dtos.map(async (dto) => await this.create(accountId, dto))); + } +} diff --git a/backend/src/modules/inventory/order/dto/create-order.dto.ts b/backend/src/modules/inventory/order/dto/create-order.dto.ts new file mode 100644 index 0000000..74760d5 --- /dev/null +++ b/backend/src/modules/inventory/order/dto/create-order.dto.ts @@ -0,0 +1,13 @@ +import { PickType } from '@nestjs/swagger'; + +import { OrderDto } from './order.dto'; + +export class CreateOrderDto extends PickType(OrderDto, [ + 'entityId', + 'currency', + 'taxIncluded', + 'statusId', + 'warehouseId', + 'items', + 'cancelAfter', +] as const) {} diff --git a/backend/src/modules/inventory/order/dto/index.ts b/backend/src/modules/inventory/order/dto/index.ts new file mode 100644 index 0000000..a9e86ed --- /dev/null +++ b/backend/src/modules/inventory/order/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-order.dto'; +export * from './order-filter.dto'; +export * from './order-item.dto'; +export * from './order.dto'; +export * from './update-order.dto'; diff --git a/backend/src/modules/inventory/order/dto/order-filter.dto.ts b/backend/src/modules/inventory/order/dto/order-filter.dto.ts new file mode 100644 index 0000000..62494ce --- /dev/null +++ b/backend/src/modules/inventory/order/dto/order-filter.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class OrderFilterDto { + @ApiProperty() + @IsNumber() + entityId: number; +} diff --git a/backend/src/modules/inventory/order/dto/order-item.dto.ts b/backend/src/modules/inventory/order/dto/order-item.dto.ts new file mode 100644 index 0000000..26c237e --- /dev/null +++ b/backend/src/modules/inventory/order/dto/order-item.dto.ts @@ -0,0 +1,67 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsObject, IsOptional } from 'class-validator'; + +import { ReservationDto } from '../../reservation/dto/reservation.dto'; +import { ProductInfoDto } from '../../product/dto/product-info.dto'; + +export class OrderItemDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + unitPrice: number; + + @ApiProperty() + @IsNumber() + quantity: number; + + @ApiProperty() + @IsNumber() + tax: number; + + @ApiProperty() + @IsNumber() + discount: number; + + @ApiProperty() + @IsNumber() + productId: number; + + @ApiProperty() + @IsNumber() + sortOrder: number; + + @ApiProperty({ type: ProductInfoDto, required: false }) + @IsOptional() + @IsObject() + productInfo: ProductInfoDto | undefined; + + @ApiProperty({ type: [ReservationDto], required: false }) + @IsOptional() + @IsArray() + reservations: ReservationDto[] | undefined; + + constructor( + id: number, + unitPrice: number, + quantity: number, + tax: number, + discount: number, + productId: number, + sortOrder: number, + productInfo: ProductInfoDto | undefined, + reservations: ReservationDto[] | undefined, + ) { + this.id = id; + this.unitPrice = unitPrice; + this.quantity = quantity; + this.tax = tax; + this.discount = discount; + this.productId = productId; + this.sortOrder = sortOrder; + this.productInfo = productInfo; + this.reservations = reservations; + } +} diff --git a/backend/src/modules/inventory/order/dto/order.dto.ts b/backend/src/modules/inventory/order/dto/order.dto.ts new file mode 100644 index 0000000..1c010d3 --- /dev/null +++ b/backend/src/modules/inventory/order/dto/order.dto.ts @@ -0,0 +1,106 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { Currency } from '@/common'; + +import { OrderItemDto } from './order-item.dto'; + +export class OrderDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + sectionId: number; + + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty() + @IsNumber() + orderNumber: number; + + @ApiProperty() + @IsNumber() + totalAmount: number; + + @ApiProperty({ enum: Currency }) + @IsEnum(Currency) + currency: Currency; + + @ApiProperty() + @IsBoolean() + taxIncluded: boolean; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + statusId: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + warehouseId?: number | null; + + @ApiProperty() + @IsNumber() + createdBy: number; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiProperty() + @IsString() + updatedAt: string; + + @ApiPropertyOptional({ nullable: true, description: 'in hours' }) + @IsOptional() + @IsNumber() + cancelAfter?: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + shippedAt?: string | null; + + @ApiProperty({ type: [OrderItemDto] }) + @IsArray() + items: OrderItemDto[]; + + constructor({ + id, + sectionId, + entityId, + orderNumber, + totalAmount, + currency, + taxIncluded, + statusId, + warehouseId, + createdBy, + createdAt, + updatedAt, + cancelAfter, + shippedAt, + items, + }: OrderDto) { + this.id = id; + this.sectionId = sectionId; + this.entityId = entityId; + this.orderNumber = orderNumber; + this.totalAmount = totalAmount; + this.currency = currency; + this.taxIncluded = taxIncluded; + this.statusId = statusId; + this.warehouseId = warehouseId; + this.createdBy = createdBy; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.cancelAfter = cancelAfter; + this.shippedAt = shippedAt; + this.items = items; + } +} diff --git a/backend/src/modules/inventory/order/dto/update-order.dto.ts b/backend/src/modules/inventory/order/dto/update-order.dto.ts new file mode 100644 index 0000000..d817fa7 --- /dev/null +++ b/backend/src/modules/inventory/order/dto/update-order.dto.ts @@ -0,0 +1,12 @@ +import { PickType } from '@nestjs/swagger'; + +import { OrderDto } from './order.dto'; + +export class UpdateOrderDto extends PickType(OrderDto, [ + 'currency', + 'taxIncluded', + 'statusId', + 'warehouseId', + 'items', + 'cancelAfter', +] as const) {} diff --git a/backend/src/modules/inventory/order/entities/index.ts b/backend/src/modules/inventory/order/entities/index.ts new file mode 100644 index 0000000..bd284f4 --- /dev/null +++ b/backend/src/modules/inventory/order/entities/index.ts @@ -0,0 +1,2 @@ +export * from './order-item.entity'; +export * from './order.entity'; diff --git a/backend/src/modules/inventory/order/entities/order-item.entity.ts b/backend/src/modules/inventory/order/entities/order-item.entity.ts new file mode 100644 index 0000000..0efdd7c --- /dev/null +++ b/backend/src/modules/inventory/order/entities/order-item.entity.ts @@ -0,0 +1,129 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { Reservation } from '../../reservation/entities/reservation.entity'; +import { Product } from '../../product/entities/product.entity'; + +import { OrderItemDto } from '../dto/order-item.dto'; + +@Entity() +export class OrderItem { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + unitPrice: number; + + @Column() + quantity: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + tax: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + discount: number; + + @Column() + productId: number; + + @Column() + orderId: number; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + private _product: Product; + private _reservations: Reservation[]; + + constructor( + accountId: number, + unitPrice: number, + quantity: number, + tax: number, + discount: number, + productId: number, + orderId: number, + sortOrder: number, + ) { + this.accountId = accountId; + this.unitPrice = unitPrice; + this.quantity = quantity; + this.tax = tax; + this.discount = discount; + this.productId = productId; + this.orderId = orderId; + this.sortOrder = sortOrder; + } + + public get product(): Product { + return this._product; + } + public set product(value: Product) { + this._product = value; + } + + public get reservations(): Reservation[] { + return this._reservations; + } + public set reservations(value: Reservation[]) { + this._reservations = value; + } + + public static fromDto(accountId: number, orderId: number, dto: OrderItemDto): OrderItem { + return new OrderItem( + accountId, + dto.unitPrice, + dto.quantity, + dto.tax, + dto.discount, + dto.productId, + orderId, + dto.sortOrder, + ); + } + + public update(dto: OrderItemDto): OrderItem { + this.unitPrice = dto.unitPrice !== undefined ? dto.unitPrice : this.unitPrice; + this.quantity = dto.quantity !== undefined ? dto.quantity : this.quantity; + this.tax = dto.tax !== undefined ? dto.tax : this.tax; + this.discount = dto.discount !== undefined ? dto.discount : this.discount; + this.productId = dto.productId !== undefined ? dto.productId : this.productId; + this.sortOrder = dto.sortOrder !== undefined ? dto.sortOrder : this.sortOrder; + + return this; + } + + public toDto(): OrderItemDto { + return new OrderItemDto( + this.id, + this.unitPrice, + this.quantity, + this.tax, + this.discount, + this.productId, + this.sortOrder, + this._product ? this._product.toInfo() : undefined, + this._reservations ? this._reservations.map((r) => r.toDto()) : undefined, + ); + } +} diff --git a/backend/src/modules/inventory/order/entities/order.entity.ts b/backend/src/modules/inventory/order/entities/order.entity.ts new file mode 100644 index 0000000..5dc72b4 --- /dev/null +++ b/backend/src/modules/inventory/order/entities/order.entity.ts @@ -0,0 +1,176 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { Currency, DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; +import { Shipment } from '../../shipment/entities/shipment.entity'; + +import { CreateOrderDto, UpdateOrderDto, OrderDto } from '../dto'; +import { OrderItem } from './order-item.entity'; + +@Entity('orders') +export class Order implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + totalAmount: number; + + @Column() + currency: Currency; + + @Column() + taxIncluded: boolean; + + @Column({ nullable: true }) + statusId: number | null; + + @Column() + warehouseId: number | null; + + @Column() + entityId: number; + + @Column() + orderNumber: number; + + @Column() + createdBy: number; + + @Column({ nullable: true }) + cancelAfter: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + @Column() + updatedAt: Date; + + _items: OrderItem[]; + + _shipments: Shipment[]; + + constructor( + accountId: number, + sectionId: number, + totalAmount: number, + currency: Currency, + taxIncluded: boolean, + statusId: number | null, + warehouseId: number | null, + entityId: number, + orderNumber: number, + createdBy: number, + cancelAfter: number | null, + createdAt?: Date, + ) { + this.accountId = accountId; + this.sectionId = sectionId; + this.totalAmount = totalAmount; + this.currency = currency; + this.taxIncluded = taxIncluded; + this.statusId = statusId; + this.warehouseId = warehouseId; + this.entityId = entityId; + this.orderNumber = orderNumber; + this.createdBy = createdBy; + this.cancelAfter = cancelAfter; + this.createdAt = createdAt ?? DateUtil.now(); + this.updatedAt = createdAt ?? DateUtil.now(); + } + + public get items(): OrderItem[] { + return this._items; + } + public set items(value: OrderItem[]) { + this._items = value; + } + + public get shipments(): Shipment[] { + return this._shipments; + } + public set shipments(value: Shipment[]) { + this._shipments = value; + } + + static getAuthorizable(sectionId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.ProductsOrder, id: sectionId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.ProductsOrder, + id: this.sectionId, + createdBy: this.createdBy, + }; + } + + public static fromDto( + accountId: number, + sectionId: number, + orderNumber: number, + createdBy: number, + totalAmount: number, + dto: CreateOrderDto, + ): Order { + return new Order( + accountId, + sectionId, + totalAmount, + dto.currency, + dto.taxIncluded, + dto.statusId, + dto.warehouseId, + dto.entityId, + orderNumber, + createdBy, + dto.cancelAfter, + ); + } + + public updateFromDto(totalAmount: number, dto: UpdateOrderDto): Order { + this.currency = dto.currency; + this.taxIncluded = dto.taxIncluded; + this.statusId = dto.statusId; + this.warehouseId = dto.warehouseId; + this.totalAmount = totalAmount; + this.cancelAfter = dto.cancelAfter !== undefined ? dto.cancelAfter : this.cancelAfter; + this.updatedAt = DateUtil.now(); + + return this; + } + + public toDto(): OrderDto { + const shippedAt = this._shipments ? this._shipments.find((s) => s.shippedAt !== null)?.shippedAt : null; + return new OrderDto({ + id: this.id, + sectionId: this.sectionId, + entityId: this.entityId, + orderNumber: this.orderNumber, + totalAmount: this.totalAmount, + currency: this.currency, + taxIncluded: this.taxIncluded, + statusId: this.statusId, + warehouseId: this.warehouseId, + createdBy: this.createdBy, + createdAt: this.createdAt.toISOString(), + updatedAt: this.updatedAt.toISOString(), + cancelAfter: this.cancelAfter, + shippedAt: shippedAt?.toISOString() ?? null, + items: this._items ? this._items.map((i) => i.toDto()) : [], + }); + } +} diff --git a/backend/src/modules/inventory/order/helper/index.ts b/backend/src/modules/inventory/order/helper/index.ts new file mode 100644 index 0000000..4120e15 --- /dev/null +++ b/backend/src/modules/inventory/order/helper/index.ts @@ -0,0 +1 @@ +export * from './order.helper'; diff --git a/backend/src/modules/inventory/order/helper/order.helper.ts b/backend/src/modules/inventory/order/helper/order.helper.ts new file mode 100644 index 0000000..90fedcb --- /dev/null +++ b/backend/src/modules/inventory/order/helper/order.helper.ts @@ -0,0 +1,30 @@ +import Decimal from 'decimal.js'; + +interface OrderItem { + unitPrice: number; + quantity: number; + discount: number; + tax: number; +} + +export class OrderHelper { + public static calcTotalAmount(items: OrderItem[], taxIncluded: boolean): number { + let totalAmount = new Decimal(0); + for (const item of items) { + totalAmount = totalAmount.plus(this.calcAmount(item, taxIncluded)); + } + return totalAmount.toNumber(); + } + + public static calcAmount(item: OrderItem, taxIncluded: boolean): number { + const amount = new Decimal(item.unitPrice).mul(item.quantity); + const discountTotal = amount.mul(new Decimal(item.discount).div(100)); + + if (taxIncluded) { + return amount.sub(discountTotal).toNumber(); + } + + const taxTotal = amount.mul(new Decimal(item.tax).div(100)); + return amount.add(taxTotal).sub(discountTotal).toNumber(); + } +} diff --git a/backend/src/modules/inventory/order/order.controller.ts b/backend/src/modules/inventory/order/order.controller.ts new file mode 100644 index 0000000..b96776e --- /dev/null +++ b/backend/src/modules/inventory/order/order.controller.ts @@ -0,0 +1,90 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { ExpandQuery, TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ExpandableField } from './types/expandable-field'; +import { CreateOrderDto } from './dto/create-order.dto'; +import { OrderDto } from './dto/order.dto'; +import { OrderFilterDto } from './dto/order-filter.dto'; +import { UpdateOrderDto } from './dto/update-order.dto'; +import { OrderService } from './services/order.service'; + +@ApiTags('inventory/orders') +@Controller('products') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class OrderController { + constructor(private readonly service: OrderService) {} + + @ApiCreatedResponse({ description: 'Create order', type: OrderDto }) + @Post('sections/:sectionId/orders') + public async create( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: CreateOrderDto, + ) { + return await this.service.create(accountId, user, sectionId, dto); + } + + @ApiOkResponse({ description: 'Get orders for entity', type: [OrderDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: items, shippedAt.', + }) + @Get('orders') + public async findMany( + @CurrentAuth() { accountId, user }: AuthData, + @Query() filter: OrderFilterDto, + @Query() expand?: ExpandQuery, + ) { + return this.service.findMany(accountId, user, filter, { expand: expand.fields }); + } + + @ApiOkResponse({ description: 'Get order', type: OrderDto }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: items, shippedAt.', + }) + @Get('orders/:orderId') + public async findOne( + @CurrentAuth() { accountId, user }: AuthData, + @Param('orderId', ParseIntPipe) orderId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findOne(accountId, user, { orderId }, { expand: expand.fields }); + } + + @ApiCreatedResponse({ description: 'Update order', type: OrderDto }) + @Put('sections/:sectionId/orders/:orderId') + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('orderId', ParseIntPipe) orderId: number, + @Query('returnStocks') returnStocks: string, + @Body() dto: UpdateOrderDto, + ) { + return this.service.update(accountId, user, sectionId, orderId, dto, returnStocks === 'true'); + } + + @ApiOkResponse({ description: 'Delete order' }) + @Delete('sections/:sectionId/orders/:orderId') + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('orderId', ParseIntPipe) orderId: number, + @Query('returnStocks') returnStocks: string, + ) { + return this.service.delete(accountId, { sectionId, orderId }, { returnStocks: returnStocks === 'true' }); + } +} diff --git a/backend/src/modules/inventory/order/order.module.ts b/backend/src/modules/inventory/order/order.module.ts new file mode 100644 index 0000000..e630b0e --- /dev/null +++ b/backend/src/modules/inventory/order/order.module.ts @@ -0,0 +1,33 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { OrderStatusModule } from '../order-status/order-status.module'; +import { ProductModule } from '../product/product.module'; +import { ReservationModule } from '../reservation/reservation.module'; +import { ShipmentModule } from '../shipment/shipment.module'; +import { WarehouseModule } from '../warehouse/warehouse.module'; + +import { OrderItem } from './entities/order-item.entity'; +import { Order } from './entities/order.entity'; +import { OrderItemService } from './services/order-item.service'; +import { OrderService } from './services/order.service'; +import { OrderHandler } from './services/order.handler'; +import { OrderController } from './order.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Order, OrderItem]), + IAMModule, + OrderStatusModule, + forwardRef(() => ReservationModule), + forwardRef(() => ProductModule), + ShipmentModule, + forwardRef(() => WarehouseModule), + ], + controllers: [OrderController], + providers: [OrderItemService, OrderService, OrderHandler], + exports: [OrderService], +}) +export class OrderModule {} diff --git a/backend/src/modules/inventory/order/services/order-item.service.ts b/backend/src/modules/inventory/order/services/order-item.service.ts new file mode 100644 index 0000000..051bb21 --- /dev/null +++ b/backend/src/modules/inventory/order/services/order-item.service.ts @@ -0,0 +1,80 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { ProductService } from '../../product/product.service'; +import { ReservationService } from '../../reservation/reservation.service'; + +import { OrderItemDto } from '../dto/order-item.dto'; +import { OrderItem } from '../entities/order-item.entity'; + +@Injectable() +export class OrderItemService { + constructor( + @InjectRepository(OrderItem) + private readonly repository: Repository, + private readonly reservationService: ReservationService, + @Inject(forwardRef(() => ProductService)) + private readonly productService: ProductService, + ) {} + + public async createMany(accountId: number, orderId: number, dtos: OrderItemDto[]): Promise { + return await Promise.all( + dtos.map(async (dto) => { + const item = await this.repository.save(OrderItem.fromDto(accountId, orderId, dto)); + + item.reservations = await this.reservationService.create( + accountId, + orderId, + item.id, + item.productId, + dto.reservations ?? [], + ); + + return item; + }), + ); + } + + public async getForOrder(accountId: number, sectionId: number, orderId: number): Promise { + const items = await this.repository.findBy({ orderId }); + for (const item of items) { + item.product = await this.productService.findById(accountId, sectionId, item.productId); + item.reservations = await this.reservationService.findMany(accountId, { + orderId, + orderItemId: item.id, + }); + } + return items; + } + + public async updateForOrder(accountId: number, orderId: number, dtos: OrderItemDto[]): Promise { + const currentItems = await this.repository.findBy({ accountId, orderId }); + const currentItemIds = currentItems.map((item) => item.id); + + const createdDtos = dtos.filter((dto) => !currentItemIds.includes(dto.id)); + const updatedDtos = dtos.filter((dto) => currentItemIds.includes(dto.id)); + const deletedItems = currentItems.filter((item) => !dtos.some((dto) => dto.id === item.id)); + + await this.createMany(accountId, orderId, createdDtos); + await this.updateItems(accountId, orderId, currentItems, updatedDtos); + await this.repository.delete({ accountId, id: In(deletedItems.map((item) => item.id)) }); + } + + private async updateItems( + accountId: number, + orderId: number, + orderItems: OrderItem[], + dtos: OrderItemDto[], + ): Promise { + await Promise.all( + dtos.map(async (dto) => { + const orderItem = orderItems.find((item) => item.id === dto.id); + if (orderItem) { + await this.repository.save(orderItem.update(dto)); + await this.reservationService.create(accountId, orderId, orderItem.id, orderItem.productId, dto.reservations); + } + }), + ); + } +} diff --git a/backend/src/modules/inventory/order/services/order.handler.ts b/backend/src/modules/inventory/order/services/order.handler.ts new file mode 100644 index 0000000..3217c5f --- /dev/null +++ b/backend/src/modules/inventory/order/services/order.handler.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { Cron, CronExpression } from '@nestjs/schedule'; + +import { ProductsEventType, ShipmentStatusChangedEvent } from '../../common'; +import { OrderService } from './order.service'; + +@Injectable() +export class OrderHandler { + constructor(private readonly service: OrderService) {} + + @Cron(CronExpression.EVERY_HOUR) + public async checkCancelOrders() { + if (process.env.SCHEDULE_PRODUCTS_ORDER_CHECK_CANCEL_DISABLE === 'true') return; + this.service.checkCancelOrders(); + } + + @OnEvent(ProductsEventType.ShipmentStatusChanged, { async: true }) + public async onShipmentStatusChanged(event: ShipmentStatusChangedEvent): Promise { + this.service.processShipmentStatusChanged({ + accountId: event.accountId, + sectionId: event.sectionId, + orderId: event.orderId, + statusId: event.statusId, + }); + } +} diff --git a/backend/src/modules/inventory/order/services/order.service.ts b/backend/src/modules/inventory/order/services/order.service.ts new file mode 100644 index 0000000..29ee7e3 --- /dev/null +++ b/backend/src/modules/inventory/order/services/order.service.ts @@ -0,0 +1,376 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { CrmEventType, EntityPriceUpdateEvent } from '@/CRM/common'; + +import { ProductOrderCreatedEvent, ProductOrderEvent, ProductsEventType } from '../../common'; + +import { OrderStatusCode } from '../../order-status/enums/order-status-code.enum'; +import { OrderStatusService } from '../../order-status/order-status.service'; +import { ReservationService } from '../../reservation/reservation.service'; +import { ShipmentService } from '../../shipment/shipment.service'; +import { WarehouseService } from '../../warehouse/warehouse.service'; + +import { ExpandableField } from '../types/expandable-field'; +import { CreateOrderDto } from '../dto/create-order.dto'; +import { UpdateOrderDto } from '../dto/update-order.dto'; +import { Order } from '../entities/order.entity'; +import { OrderHelper } from '../helper/order.helper'; +import { OrderItemService } from './order-item.service'; + +interface FindFilter { + sectionId?: number; + warehouseId?: number | number[]; + withoutWarehouse?: boolean; + orderId?: number | number[]; + entityId?: number; + statusId?: { include?: number[]; exclude?: number[] }; +} +interface FindOptions { + expand?: ExpandableField[]; +} +interface CancelOptions { + returnStocks?: boolean; +} +type ProcessOptions = { processShipments?: boolean } & CancelOptions; +type DeleteOptions = { newWarehouseId?: number } & CancelOptions; + +@Injectable() +export class OrderService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Order) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly orderItemService: OrderItemService, + private readonly reservationService: ReservationService, + private readonly orderStatusService: OrderStatusService, + @Inject(forwardRef(() => ShipmentService)) + private readonly shipmentService: ShipmentService, + @Inject(forwardRef(() => WarehouseService)) + private readonly warehouseService: WarehouseService, + ) {} + + public async create(accountId: number, user: User, sectionId: number, dto: CreateOrderDto): Promise { + await this.authService.check({ + action: 'create', + user, + authorizable: Order.getAuthorizable(sectionId), + throwError: true, + }); + + const totalAmount = OrderHelper.calcTotalAmount(dto.items, dto.taxIncluded); + const orderNumber = await this.getOrderNumber(sectionId, dto.entityId); + const order = await this.repository.save( + Order.fromDto(accountId, sectionId, orderNumber, user.id, totalAmount, dto), + ); + this.eventEmitter.emit( + ProductsEventType.ProductOrderCreated, + new ProductOrderCreatedEvent({ + accountId, + entityId: order.entityId, + orderId: order.id, + createdAt: order.createdAt.toISOString(), + }), + ); + + if (dto.items) { + await this.orderItemService.createMany(accountId, order.id, dto.items); + order.items = await this.orderItemService.getForOrder(accountId, sectionId, order.id); + } + + if (order.statusId) { + await this.processOrderStatus(accountId, sectionId, order.statusId, order, { processShipments: true }); + } + order.shipments = await this.shipmentService.findMany({ + accountId, + user, + filter: { sectionId, orderId: order.id }, + }); + + this.updateEntityValue(accountId, order.entityId); + + return order; + } + + public async findOne( + accountId: number, + user: User | null, + filter: FindFilter, + options?: FindOptions, + ): Promise { + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId, sectionId: filter.sectionId, warehouseId: filter.warehouseId, onlyAvailable: true }, + }); + filter.withoutWarehouse = !filter.warehouseId; + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + const order = await this.createQb(accountId, filter).getOne(); + if (user) { + await this.authService.check({ action: 'view', user, authorizable: order, throwError: true }); + } + return order && options?.expand ? await this.expandOne(accountId, user, order, options.expand) : order; + } + public async findMany( + accountId: number, + user: User | null, + filter: FindFilter, + options?: FindOptions, + ): Promise { + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId, sectionId: filter.sectionId, warehouseId: filter.warehouseId, onlyAvailable: true }, + }); + filter.withoutWarehouse = !filter.warehouseId; + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + + const orders = await this.createQb(accountId, filter, true).getMany(); + const checkedOrders: Order[] = []; + if (user) { + for (const order of orders) { + if (await this.authService.check({ action: 'view', user, authorizable: order })) { + checkedOrders.push(order); + } + } + } else { + checkedOrders.push(...orders); + } + return checkedOrders && options?.expand + ? await this.expandMany(accountId, user, checkedOrders, options.expand) + : checkedOrders; + } + + public async update( + accountId: number, + user: User, + sectionId: number, + orderId: number, + dto: UpdateOrderDto, + returnStocks?: boolean, + ): Promise { + const order = await this.findOne(accountId, null, { sectionId, orderId }); + if (!order) { + throw NotFoundError.withId(Order, orderId); + } + + await this.authService.check({ action: 'edit', user, authorizable: order, throwError: true }); + + const prevStatusId = order.statusId; + const totalAmount = OrderHelper.calcTotalAmount(dto.items, dto.taxIncluded); + + await this.repository.save(order.updateFromDto(totalAmount, dto)); + + if (dto.items) { + await this.orderItemService.updateForOrder(accountId, orderId, dto.items); + order.items = await this.orderItemService.getForOrder(accountId, sectionId, order.id); + } + + if (order.statusId && order.statusId !== prevStatusId) { + await this.processOrderStatus(accountId, sectionId, order.statusId, order, { + processShipments: true, + returnStocks, + }); + } + order.shipments = await this.shipmentService.findMany({ + accountId, + user, + filter: { sectionId, orderId: order.id }, + }); + + this.updateEntityValue(accountId, order.entityId); + + return order; + } + + public async processShipmentStatusChanged({ + accountId, + sectionId, + orderId, + statusId, + }: { + accountId: number; + sectionId: number; + orderId: number; + statusId: number; + }): Promise { + const order = await this.findOne(accountId, null, { sectionId, orderId }); + if (order && statusId !== order.statusId) { + order.statusId = statusId; + await this.repository.save(order); + + order.items = await this.orderItemService.getForOrder(accountId, sectionId, order.id); + await this.processOrderStatus(accountId, sectionId, statusId, order); + this.updateEntityValue(accountId, order.entityId); + } + } + + public async checkCancelOrders() { + const orders = await this.repository + .createQueryBuilder('o') + .leftJoin('order_status', 'os', 'os.id = o.status_id') + .where('o.cancel_after IS NOT NULL') + .andWhere('os.code = :code', { code: OrderStatusCode.Reserved }) + .andWhere(`o.updated_at + (o.cancel_after * INTERVAL '1 hour') < now()`) + .getMany(); + + orders.forEach(async (order) => { + const status = await this.orderStatusService.findOne(order.accountId, { code: OrderStatusCode.Cancelled }); + if (status) { + order.statusId = status.id; + await this.repository.save(order); + + await this.processOrderStatus(order.accountId, order.sectionId, order.statusId, order, { + processShipments: true, + returnStocks: true, + }); + } + }); + } + + public async delete(accountId: number, filter: FindFilter, options?: DeleteOptions) { + await this.reservationService.delete(accountId, filter, options); + await this.shipmentService.delete(accountId, filter, options); + + const qb = this.createQb(accountId, filter); + if (options?.newWarehouseId) { + await qb.update({ warehouseId: options.newWarehouseId }).execute(); + } else { + const orders = await qb.clone().getMany(); + await qb.clone().delete().execute(); + for (const order of orders) { + this.updateEntityValue(accountId, order.entityId); + this.eventEmitter.emit( + ProductsEventType.ProductOrderDeleted, + new ProductOrderEvent({ accountId: order.accountId, entityId: order.entityId, orderId: order.id }), + ); + } + } + } + + private createQb(accountId: number, filter: FindFilter, ordered = false) { + const qb = this.repository.createQueryBuilder('orders').where('orders.account_id = :accountId', { accountId }); + if (filter.sectionId) { + qb.andWhere('orders.section_id = :sectionId', { sectionId: filter.sectionId }); + } + if (filter.warehouseId) { + qb.andWhere( + new Brackets((qbW) => { + if (Array.isArray(filter.warehouseId)) { + qbW.andWhere('orders.warehouse_id IN (:...warehouseIds)', { warehouseIds: filter.warehouseId }); + } else { + qbW.andWhere('orders.warehouse_id = :warehouseId', { warehouseId: filter.warehouseId }); + } + if (filter.withoutWarehouse) { + qbW.orWhere('orders.warehouse_id IS NULL'); + } + }), + ); + } + if (filter.orderId) { + if (Array.isArray(filter.orderId)) { + qb.andWhere('orders.id IN (:...orderIds)', { orderIds: filter.orderId }); + } else { + qb.andWhere('orders.id = :orderId', { orderId: filter.orderId }); + } + } + if (filter.entityId) { + qb.andWhere('orders.entity_id = :entityId', { entityId: filter.entityId }); + } + if (filter.statusId) { + if (filter.statusId.include) { + qb.andWhere('orders.status_id IN (:...statusIds)', { statusIds: filter.statusId.include }); + } + if (filter.statusId.exclude) { + qb.andWhere( + new Brackets((qb) => { + qb.where('orders.status_id NOT IN (:...statusIds)', { statusIds: filter.statusId.exclude }).orWhere( + 'orders.status_id IS NULL', + ); + }), + ); + } + } + if (ordered) { + qb.orderBy('orders.created_at', 'DESC'); + } + return qb; + } + + private async expandOne( + accountId: number, + user: User | null, + order: Order, + expand: ExpandableField[], + ): Promise { + if (expand.includes('items')) { + order.items = await this.orderItemService.getForOrder(accountId, order.sectionId, order.id); + } + if (expand.includes('shipments') || expand.includes('shippedAt')) { + order.shipments = await this.shipmentService.findMany({ + accountId, + user, + filter: { sectionId: order.sectionId, orderId: order.id }, + }); + } + return order; + } + private async expandMany( + accountId: number, + user: User | null, + orders: Order[], + expand: ExpandableField[], + ): Promise { + return await Promise.all(orders.map((order) => this.expandOne(accountId, user, order, expand))); + } + + private async processOrderStatus( + accountId: number, + sectionId: number, + statusId: number, + order: Order, + options?: ProcessOptions, + ): Promise { + const status = await this.orderStatusService.findOne(accountId, { statusId }); + if ([OrderStatusCode.Shipped, OrderStatusCode.Cancelled, OrderStatusCode.Returned].includes(status.code)) { + await this.reservationService.delete(accountId, { orderId: order.id }); + } + if (options?.processShipments) { + await this.shipmentService.processOrder(accountId, sectionId, order, status, options); + } + } + + private async getOrderNumber(sectionId: number, entityId: number): Promise { + const result = await this.repository + .createQueryBuilder('o') + .select('MAX(o.order_number)', 'order_number') + .where('o.entity_id = :entityId', { entityId }) + .andWhere('o.section_id = :sectionId', { sectionId }) + .getRawOne(); + + return Number(result?.order_number ?? 0) + 1; + } + + private async updateEntityValue(accountId: number, entityId: number) { + const cancelledStatus = await this.orderStatusService.findOne(accountId, { code: OrderStatusCode.Cancelled }); + const orders = await this.findMany(accountId, null, { + entityId, + statusId: { exclude: cancelledStatus ? [cancelledStatus.id] : undefined }, + }); + const totalAmount = orders.reduce((sum, order) => sum + order.totalAmount, 0); + + this.eventEmitter.emit( + CrmEventType.EntityPriceUpdate, + new EntityPriceUpdateEvent({ accountId, entityId, price: totalAmount }), + ); + } +} diff --git a/backend/src/modules/inventory/order/types/expandable-field.ts b/backend/src/modules/inventory/order/types/expandable-field.ts new file mode 100644 index 0000000..bc53c1c --- /dev/null +++ b/backend/src/modules/inventory/order/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'items' | 'shipments' | 'shippedAt'; diff --git a/backend/src/modules/inventory/order/types/index.ts b/backend/src/modules/inventory/order/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/inventory/order/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/inventory/product-category/dto/create-product-category.dto.ts b/backend/src/modules/inventory/product-category/dto/create-product-category.dto.ts new file mode 100644 index 0000000..e7cb8b9 --- /dev/null +++ b/backend/src/modules/inventory/product-category/dto/create-product-category.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { ProductCategoryDto } from './product-category.dto'; + +export class CreateProductCategoryDto extends PickType(ProductCategoryDto, ['name', 'parentId'] as const) {} diff --git a/backend/src/modules/inventory/product-category/dto/index.ts b/backend/src/modules/inventory/product-category/dto/index.ts new file mode 100644 index 0000000..9fb0e1a --- /dev/null +++ b/backend/src/modules/inventory/product-category/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-product-category.dto'; +export * from './product-category.dto'; +export * from './update-product-category.dto'; diff --git a/backend/src/modules/inventory/product-category/dto/product-category.dto.ts b/backend/src/modules/inventory/product-category/dto/product-category.dto.ts new file mode 100644 index 0000000..95c33d2 --- /dev/null +++ b/backend/src/modules/inventory/product-category/dto/product-category.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ProductCategoryDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + sectionId: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + parentId: number | null; + + @ApiProperty({ type: [ProductCategoryDto] }) + @IsArray() + children: ProductCategoryDto[]; + + constructor(id: number, sectionId: number, name: string, parentId: number | null, children: ProductCategoryDto[]) { + this.id = id; + this.sectionId = sectionId; + this.name = name; + this.parentId = parentId; + this.children = children; + } +} diff --git a/backend/src/modules/inventory/product-category/dto/update-product-category.dto.ts b/backend/src/modules/inventory/product-category/dto/update-product-category.dto.ts new file mode 100644 index 0000000..47b1979 --- /dev/null +++ b/backend/src/modules/inventory/product-category/dto/update-product-category.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { ProductCategoryDto } from './product-category.dto'; + +export class UpdateProductCategoryDto extends PickType(ProductCategoryDto, ['name'] as const) {} diff --git a/backend/src/modules/inventory/product-category/entities/index.ts b/backend/src/modules/inventory/product-category/entities/index.ts new file mode 100644 index 0000000..d8c1648 --- /dev/null +++ b/backend/src/modules/inventory/product-category/entities/index.ts @@ -0,0 +1 @@ +export * from './product-category.entity'; diff --git a/backend/src/modules/inventory/product-category/entities/product-category.entity.ts b/backend/src/modules/inventory/product-category/entities/product-category.entity.ts new file mode 100644 index 0000000..36202eb --- /dev/null +++ b/backend/src/modules/inventory/product-category/entities/product-category.entity.ts @@ -0,0 +1,83 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; + +import { CreateProductCategoryDto } from '../dto/create-product-category.dto'; +import { ProductCategoryDto } from '../dto/product-category.dto'; + +@Entity() +export class ProductCategory implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column() + name: string; + + @Column() + parentId: number | null; + + @Column() + createdBy: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + private _children: ProductCategory[] | null; + + constructor( + accountId: number, + sectionId: number, + name: string, + parentId: number | null, + createdBy: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.sectionId = sectionId; + this.name = name; + this.parentId = parentId; + this.createdBy = createdBy; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public set children(children: ProductCategory[] | null) { + this._children = children; + } + public get children(): ProductCategory[] | null { + return this._children; + } + + static getAuthorizable(sectionId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Products, id: sectionId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Products, + id: this.sectionId, + createdBy: this.createdBy, + }; + } + + public static fromDto(accountId: number, sectionId: number, createdBy: number, dto: CreateProductCategoryDto) { + return new ProductCategory(accountId, sectionId, dto.name, dto.parentId, createdBy); + } + + public toDto(): ProductCategoryDto { + return new ProductCategoryDto( + this.id, + this.sectionId, + this.name, + this.parentId, + this._children?.map((child) => child.toDto()) ?? [], + ); + } +} diff --git a/backend/src/modules/inventory/product-category/product-category.controller.ts b/backend/src/modules/inventory/product-category/product-category.controller.ts new file mode 100644 index 0000000..2d843f2 --- /dev/null +++ b/backend/src/modules/inventory/product-category/product-category.controller.ts @@ -0,0 +1,60 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ProductCategoryDto, CreateProductCategoryDto, UpdateProductCategoryDto } from './dto'; +import { ProductCategoryService } from './product-category.service'; + +@ApiTags('inventory/products/category') +@Controller('products/sections/:sectionId/categories') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ProductCategoryController { + constructor(private readonly service: ProductCategoryService) {} + + @ApiCreatedResponse({ description: 'Create product category', type: ProductCategoryDto }) + @Post() + public async createCategory( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: CreateProductCategoryDto, + ) { + return await this.service.create(accountId, user, sectionId, dto); + } + + @ApiCreatedResponse({ description: 'Get product categories', type: [ProductCategoryDto] }) + @Get() + public async getCategories( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + ) { + return await this.service.getHierarchy(accountId, user, sectionId); + } + + @ApiCreatedResponse({ description: 'Update product category', type: ProductCategoryDto }) + @Put('/:categoryId') + public async updateCategory( + @CurrentAuth() { accountId, user }: AuthData, + @Body() dto: UpdateProductCategoryDto, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('categoryId', ParseIntPipe) categoryId: number, + ) { + return await this.service.update(accountId, user, sectionId, categoryId, dto); + } + + @ApiCreatedResponse({ description: 'Delete product category' }) + @Delete('/:categoryId') + public async deleteCategory( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('categoryId', ParseIntPipe) categoryId: number, + @Query('newCategoryId') newCategoryId?: number, + ): Promise { + await this.service.delete(accountId, user, sectionId, categoryId, isNaN(newCategoryId) ? null : newCategoryId); + } +} diff --git a/backend/src/modules/inventory/product-category/product-category.module.ts b/backend/src/modules/inventory/product-category/product-category.module.ts new file mode 100644 index 0000000..cf60be3 --- /dev/null +++ b/backend/src/modules/inventory/product-category/product-category.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { ProductCategory } from './entities/product-category.entity'; +import { ProductCategoryService } from './product-category.service'; +import { ProductCategoryController } from './product-category.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([ProductCategory]), IAMModule], + controllers: [ProductCategoryController], + providers: [ProductCategoryService], + exports: [ProductCategoryService], +}) +export class ProductCategoryModule {} diff --git a/backend/src/modules/inventory/product-category/product-category.service.ts b/backend/src/modules/inventory/product-category/product-category.service.ts new file mode 100644 index 0000000..7e1dc54 --- /dev/null +++ b/backend/src/modules/inventory/product-category/product-category.service.ts @@ -0,0 +1,142 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { IsNull, Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { CreateProductCategoryDto, UpdateProductCategoryDto } from './dto'; +import { ProductCategory } from './entities'; + +@Injectable() +export class ProductCategoryService { + constructor( + @InjectRepository(ProductCategory) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + ) {} + + public async create( + accountId: number, + user: User, + sectionId: number, + dto: CreateProductCategoryDto, + ): Promise { + await this.authService.check({ + action: 'create', + user, + authorizable: ProductCategory.getAuthorizable(sectionId), + throwError: true, + }); + + return await this.repository.save(ProductCategory.fromDto(accountId, sectionId, user.id, dto)); + } + + private async getOne(accountId: number, sectionId: number, categoryId: number): Promise { + const category = await this.repository.findOneBy({ accountId, id: categoryId, sectionId }); + if (!category) { + throw NotFoundError.withId(ProductCategory, categoryId); + } + + return category; + } + + public async getHierarchy( + accountId: number, + user: User, + sectionId: number, + categoryId: number | null = null, + ): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: ProductCategory.getAuthorizable(sectionId), + throwError: true, + }); + + return await this.getCategories(accountId, sectionId, categoryId); + } + + public async getCategories( + accountId: number, + sectionId: number, + categoryId: number | null, + ): Promise { + const categories = await this.repository.find({ + where: { accountId, sectionId, parentId: categoryId ?? IsNull() }, + order: { id: 'ASC' }, + }); + + for (const category of categories) { + category.children = await this.getCategories(accountId, sectionId, category.id); + } + + return categories; + } + + public async getCategoriesFlat( + accountId: number, + sectionId: number, + categoryId: number | null, + ): Promise { + const category = categoryId ? await this.getOne(accountId, sectionId, categoryId) : null; + const flat: ProductCategory[] = category ? [category] : []; + + const categories = await this.getCategories(accountId, sectionId, categoryId); + for (const category of categories) { + flat.push(...this.flatCategory(category)); + } + + return flat; + } + + private flatCategory(category: ProductCategory): ProductCategory[] { + const flat: ProductCategory[] = [category]; + for (const child of category.children) { + flat.push(...this.flatCategory(child)); + } + return flat; + } + + public async update( + accountId: number, + user: User, + sectionId: number, + categoryId: number, + dto: UpdateProductCategoryDto, + ): Promise { + const category = await this.getOne(accountId, sectionId, categoryId); + await this.authService.check({ action: 'edit', user, authorizable: category, throwError: true }); + + category.name = dto.name; + await this.repository.save(category); + + category.children = await this.getHierarchy(accountId, user, sectionId, category.id); + + return category; + } + + public async delete( + accountId: number, + user: User, + sectionId: number, + categoryId: number, + newCategoryId: number | null, + ): Promise { + await this.authService.check({ + action: 'delete', + user, + authorizable: ProductCategory.getAuthorizable(sectionId), + throwError: true, + }); + + const children = await this.repository.findBy({ accountId, sectionId, parentId: categoryId }); + for (const child of children) { + await this.delete(accountId, user, sectionId, child.id, newCategoryId); + } + + await this.repository.delete(categoryId); + } +} diff --git a/backend/src/modules/inventory/product-price/dto/create-product-price.dto.ts b/backend/src/modules/inventory/product-price/dto/create-product-price.dto.ts new file mode 100644 index 0000000..2fec9a5 --- /dev/null +++ b/backend/src/modules/inventory/product-price/dto/create-product-price.dto.ts @@ -0,0 +1,4 @@ +import { OmitType } from '@nestjs/swagger'; +import { ProductPriceDto } from './product-price.dto'; + +export class CreateProductPriceDto extends OmitType(ProductPriceDto, ['id'] as const) {} diff --git a/backend/src/modules/inventory/product-price/dto/index.ts b/backend/src/modules/inventory/product-price/dto/index.ts new file mode 100644 index 0000000..7e5ef4f --- /dev/null +++ b/backend/src/modules/inventory/product-price/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-product-price.dto'; +export * from './product-price.dto'; +export * from './update-product-price.dto'; diff --git a/backend/src/modules/inventory/product-price/dto/product-price.dto.ts b/backend/src/modules/inventory/product-price/dto/product-price.dto.ts new file mode 100644 index 0000000..c389db4 --- /dev/null +++ b/backend/src/modules/inventory/product-price/dto/product-price.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { Currency } from '@/common'; + +export class ProductPriceDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + name: string | null; + + @ApiProperty() + @IsNumber() + unitPrice: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + maxDiscount: number | null; + + @ApiProperty({ enum: Currency }) + @IsEnum(Currency) + currency: Currency; + + constructor(id: number, name: string | null, unitPrice: number, currency: Currency, maxDiscount: number | null) { + this.id = id; + this.name = name; + this.unitPrice = unitPrice; + this.currency = currency; + this.maxDiscount = maxDiscount; + } +} diff --git a/backend/src/modules/inventory/product-price/dto/update-product-price.dto.ts b/backend/src/modules/inventory/product-price/dto/update-product-price.dto.ts new file mode 100644 index 0000000..bdb05bf --- /dev/null +++ b/backend/src/modules/inventory/product-price/dto/update-product-price.dto.ts @@ -0,0 +1,4 @@ +import { OmitType } from '@nestjs/swagger'; +import { ProductPriceDto } from './product-price.dto'; + +export class UpdateProductPriceDto extends OmitType(ProductPriceDto, ['id'] as const) {} diff --git a/backend/src/modules/inventory/product-price/entities/index.ts b/backend/src/modules/inventory/product-price/entities/index.ts new file mode 100644 index 0000000..dd584a4 --- /dev/null +++ b/backend/src/modules/inventory/product-price/entities/index.ts @@ -0,0 +1 @@ +export * from './product-price.entity'; diff --git a/backend/src/modules/inventory/product-price/entities/product-price.entity.ts b/backend/src/modules/inventory/product-price/entities/product-price.entity.ts new file mode 100644 index 0000000..b27ded5 --- /dev/null +++ b/backend/src/modules/inventory/product-price/entities/product-price.entity.ts @@ -0,0 +1,70 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { Currency } from '@/common'; + +import { CreateProductPriceDto, UpdateProductPriceDto, ProductPriceDto } from '../dto'; + +@Entity() +export class ProductPrice { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string | null; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + unitPrice: number; + + @Column() + currency: Currency; + + @Column({ nullable: true }) + maxDiscount: number | null; + + @Column() + productId: number; + + @Column() + accountId: number; + + constructor( + accountId: number, + name: string | null, + unitPrice: number, + currency: Currency, + maxDiscount: number | null, + productId: number, + ) { + this.accountId = accountId; + this.name = name; + this.unitPrice = unitPrice; + this.currency = currency; + this.maxDiscount = maxDiscount; + this.productId = productId; + } + + public static fromDto(accountId: number, productId: number, dto: CreateProductPriceDto): ProductPrice { + dto.unitPrice = parseFloat(dto.unitPrice.toFixed(2)); + + return new ProductPrice(accountId, dto.name, dto.unitPrice, dto.currency, dto.maxDiscount, productId); + } + + public update(dto: UpdateProductPriceDto): ProductPrice { + this.name = dto.name; + this.currency = dto.currency; + this.maxDiscount = dto.maxDiscount; + this.unitPrice = parseFloat(dto.unitPrice.toFixed(2)); + + return this; + } + + public toDto(): ProductPriceDto { + return new ProductPriceDto(this.id, this.name, this.unitPrice, this.currency, this.maxDiscount); + } +} diff --git a/backend/src/modules/inventory/product-price/errors/index.ts b/backend/src/modules/inventory/product-price/errors/index.ts new file mode 100644 index 0000000..22a55a0 --- /dev/null +++ b/backend/src/modules/inventory/product-price/errors/index.ts @@ -0,0 +1 @@ +export * from './invalid-discount.error'; diff --git a/backend/src/modules/inventory/product-price/errors/invalid-discount.error.ts b/backend/src/modules/inventory/product-price/errors/invalid-discount.error.ts new file mode 100644 index 0000000..e6f4e92 --- /dev/null +++ b/backend/src/modules/inventory/product-price/errors/invalid-discount.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class InvalidDiscountError extends ServiceError { + constructor(message = 'Discount cannot be more than 100% and less than 0%') { + super({ errorCode: 'products.invalid_discount', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/inventory/product-price/product-price.controller.ts b/backend/src/modules/inventory/product-price/product-price.controller.ts new file mode 100644 index 0000000..ef1fcb4 --- /dev/null +++ b/backend/src/modules/inventory/product-price/product-price.controller.ts @@ -0,0 +1,56 @@ +import { Body, Controller, Delete, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ProductPriceService } from './product-price.service'; +import { ProductPrice } from './entities/product-price.entity'; +import { ProductPriceDto } from './dto/product-price.dto'; +import { CreateProductPriceDto } from './dto/create-product-price.dto'; +import { UpdateProductPriceDto } from './dto/update-product-price.dto'; + +@ApiTags('inventory/products/prices') +@Controller('products/sections/:sectionId/products/:productId/prices') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ProductPriceController { + constructor(private readonly service: ProductPriceService) {} + + @ApiCreatedResponse({ description: 'Create product price', type: ProductPriceDto }) + @Post() + public async create( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Body() dto: CreateProductPriceDto, + ): Promise { + return await this.service.create(accountId, user, sectionId, productId, dto); + } + + @ApiCreatedResponse({ description: 'Update product price', type: ProductPriceDto }) + @Put('/:priceId') + public async update( + @CurrentAuth() { user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Param('priceId', ParseIntPipe) priceId: number, + @Body() dto: UpdateProductPriceDto, + ): Promise { + return await this.service.update(user, sectionId, productId, priceId, dto); + } + + @ApiCreatedResponse({ description: 'Delete product price' }) + @Delete('/:priceId') + public async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Param('priceId', ParseIntPipe) priceId: number, + ): Promise { + await this.service.delete(accountId, user, sectionId, productId, priceId); + } +} diff --git a/backend/src/modules/inventory/product-price/product-price.module.ts b/backend/src/modules/inventory/product-price/product-price.module.ts new file mode 100644 index 0000000..ca6cb9c --- /dev/null +++ b/backend/src/modules/inventory/product-price/product-price.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { ProductPrice } from './entities/product-price.entity'; +import { ProductPriceService } from './product-price.service'; +import { ProductPriceController } from './product-price.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([ProductPrice]), IAMModule], + controllers: [ProductPriceController], + providers: [ProductPriceService], + exports: [ProductPriceService], +}) +export class ProductPriceModule {} diff --git a/backend/src/modules/inventory/product-price/product-price.service.ts b/backend/src/modules/inventory/product-price/product-price.service.ts new file mode 100644 index 0000000..67dd9e3 --- /dev/null +++ b/backend/src/modules/inventory/product-price/product-price.service.ts @@ -0,0 +1,127 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { Product } from '../product/entities/product.entity'; +import { CreateProductPriceDto } from './dto/create-product-price.dto'; +import { UpdateProductPriceDto } from './dto/update-product-price.dto'; +import { ProductPrice } from './entities/product-price.entity'; +import { InvalidDiscountError } from './errors/invalid-discount.error'; + +const MAX_DISCOUNT_PERCENT = 100; +const MIN_DISCOUNT_PERCENT = 0; + +@Injectable() +export class ProductPriceService { + constructor( + @InjectRepository(ProductPrice) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + ) {} + + public async create( + accountId: number, + user: User, + sectionId: number, + productId: number, + dto: CreateProductPriceDto, + ): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + return await this.createInternal(accountId, productId, dto); + } + + private async createInternal( + accountId: number, + productId: number, + dto: CreateProductPriceDto, + ): Promise { + this.checkMaxDiscountValue(dto.maxDiscount); + return await this.repository.save(ProductPrice.fromDto(accountId, productId, dto)); + } + + public async createPricesForProduct( + accountId: number, + productId: number, + dtos: CreateProductPriceDto[], + ): Promise { + return await Promise.all(dtos.map(async (dto) => await this.createInternal(accountId, productId, dto))); + } + + public async getPricesForProduct(accountId: number, productId: number): Promise { + return this.repository.find({ + where: { accountId, productId }, + order: { id: 'asc' }, + }); + } + + private async getById(productId: number, priceId: number): Promise { + const price = await this.repository.findOneBy({ productId, id: priceId }); + + if (!price) { + throw NotFoundError.withId(ProductPrice, priceId); + } + + return price; + } + + public async update( + user: User, + sectionId: number, + productId: number, + priceId: number, + dto: UpdateProductPriceDto, + ): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + this.checkMaxDiscountValue(dto.maxDiscount); + const price = await this.getById(productId, priceId); + await this.repository.save(price.update(dto)); + + return price; + } + + public async delete( + accountId: number, + user: User, + sectionId: number, + productId: number, + priceId: number, + ): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + const result = await this.repository.delete({ accountId, productId, id: priceId }); + if (result.affected === 0) { + throw NotFoundError.withId(ProductPrice, priceId); + } + } + + private checkMaxDiscountValue(discount: number | null | undefined) { + if ( + (discount !== null || discount !== undefined) && + discount > MAX_DISCOUNT_PERCENT && + discount < MIN_DISCOUNT_PERCENT + ) { + throw new InvalidDiscountError(); + } + } +} diff --git a/backend/src/modules/inventory/product-stock/dto/create-product-stock.dto.ts b/backend/src/modules/inventory/product-stock/dto/create-product-stock.dto.ts new file mode 100644 index 0000000..f525a88 --- /dev/null +++ b/backend/src/modules/inventory/product-stock/dto/create-product-stock.dto.ts @@ -0,0 +1,4 @@ +import { OmitType } from '@nestjs/swagger'; +import { ProductStockDto } from './product-stock.dto'; + +export class CreateProductStockDto extends OmitType(ProductStockDto, ['reserved', 'available'] as const) {} diff --git a/backend/src/modules/inventory/product-stock/dto/index.ts b/backend/src/modules/inventory/product-stock/dto/index.ts new file mode 100644 index 0000000..5cf581a --- /dev/null +++ b/backend/src/modules/inventory/product-stock/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-product-stock.dto'; +export * from './product-stock.dto'; +export * from './update-product-stock.dto'; +export * from './update-product-stocks.dto'; diff --git a/backend/src/modules/inventory/product-stock/dto/product-stock.dto.ts b/backend/src/modules/inventory/product-stock/dto/product-stock.dto.ts new file mode 100644 index 0000000..b99cbd2 --- /dev/null +++ b/backend/src/modules/inventory/product-stock/dto/product-stock.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class ProductStockDto { + @ApiProperty() + @IsNumber() + warehouseId: number; + + @ApiProperty() + @IsNumber() + stockQuantity: number; + + @ApiProperty() + @IsNumber() + reserved: number; + + @ApiProperty() + @IsNumber() + available: number; +} diff --git a/backend/src/modules/inventory/product-stock/dto/update-product-stock.dto.ts b/backend/src/modules/inventory/product-stock/dto/update-product-stock.dto.ts new file mode 100644 index 0000000..8ce54bf --- /dev/null +++ b/backend/src/modules/inventory/product-stock/dto/update-product-stock.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { ProductStockDto } from './product-stock.dto'; + +export class UpdateProductStockDto extends OmitType(ProductStockDto, [ + 'reserved', + 'available', + 'stockQuantity', +] as const) { + @ApiProperty({ nullable: true, description: 'Stock quantity' }) + @IsOptional() + @IsNumber() + stockQuantity: number | null; +} diff --git a/backend/src/modules/inventory/product-stock/dto/update-product-stocks.dto.ts b/backend/src/modules/inventory/product-stock/dto/update-product-stocks.dto.ts new file mode 100644 index 0000000..a20a03d --- /dev/null +++ b/backend/src/modules/inventory/product-stock/dto/update-product-stocks.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { UpdateProductStockDto } from './update-product-stock.dto'; + +export class UpdateProductStocksDto { + @ApiProperty({ type: [UpdateProductStockDto], description: 'Product stocks' }) + @IsArray() + stocks: UpdateProductStockDto[]; +} diff --git a/backend/src/modules/inventory/product-stock/entities/index.ts b/backend/src/modules/inventory/product-stock/entities/index.ts new file mode 100644 index 0000000..491f1aa --- /dev/null +++ b/backend/src/modules/inventory/product-stock/entities/index.ts @@ -0,0 +1 @@ +export * from './product-stock.entity'; diff --git a/backend/src/modules/inventory/product-stock/entities/product-stock.entity.ts b/backend/src/modules/inventory/product-stock/entities/product-stock.entity.ts new file mode 100644 index 0000000..2b6915a --- /dev/null +++ b/backend/src/modules/inventory/product-stock/entities/product-stock.entity.ts @@ -0,0 +1,48 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ProductStockDto } from '../dto'; + +@Entity() +export class ProductStock { + @PrimaryColumn() + productId: number; + + @PrimaryColumn() + warehouseId: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + stockQuantity: number; + + @Column() + accountId: number; + + constructor(accountId: number, productId: number, warehouseId: number, stockQuantity: number) { + this.accountId = accountId; + this.productId = productId; + this.warehouseId = warehouseId; + this.stockQuantity = stockQuantity; + } + + private _reserved: number | null; + public get reserved(): number { + return this._reserved ?? 0; + } + public set reserved(value: number) { + this._reserved = value; + } + + public toDto(): ProductStockDto { + return { + warehouseId: this.warehouseId, + stockQuantity: this.stockQuantity, + reserved: this.reserved, + available: this.stockQuantity - this.reserved, + }; + } +} diff --git a/backend/src/modules/inventory/product-stock/product-stock.controller.ts b/backend/src/modules/inventory/product-stock/product-stock.controller.ts new file mode 100644 index 0000000..dc1e162 --- /dev/null +++ b/backend/src/modules/inventory/product-stock/product-stock.controller.ts @@ -0,0 +1,30 @@ +import { Body, Controller, Param, ParseIntPipe, Put } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ProductStockDto, UpdateProductStocksDto } from './dto'; +import { ProductStockService } from './product-stock.service'; + +@ApiTags('inventory/products/stocks') +@Controller('products/sections/:sectionId/products/:productId/stocks') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class StockController { + constructor(private service: ProductStockService) {} + + @ApiOkResponse({ description: 'Update product stocks', type: [ProductStockDto] }) + @Put() + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Body() { stocks }: UpdateProductStocksDto, + ) { + return this.service.update({ accountId, user, sectionId, productId, dtos: stocks }); + } +} diff --git a/backend/src/modules/inventory/product-stock/product-stock.module.ts b/backend/src/modules/inventory/product-stock/product-stock.module.ts new file mode 100644 index 0000000..cef463d --- /dev/null +++ b/backend/src/modules/inventory/product-stock/product-stock.module.ts @@ -0,0 +1,19 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { ReservationModule } from '../reservation/reservation.module'; +import { WarehouseModule } from '../warehouse/warehouse.module'; + +import { ProductStock } from './entities/product-stock.entity'; +import { ProductStockService } from './product-stock.service'; +import { StockController } from './product-stock.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([ProductStock]), IAMModule, ReservationModule, forwardRef(() => WarehouseModule)], + controllers: [StockController], + providers: [ProductStockService], + exports: [ProductStockService], +}) +export class ProductStockModule {} diff --git a/backend/src/modules/inventory/product-stock/product-stock.service.ts b/backend/src/modules/inventory/product-stock/product-stock.service.ts new file mode 100644 index 0000000..4bfabc8 --- /dev/null +++ b/backend/src/modules/inventory/product-stock/product-stock.service.ts @@ -0,0 +1,188 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { Product } from '../product/entities/product.entity'; +import { ReservationService } from '../reservation/reservation.service'; +import { WarehouseService } from '../warehouse/warehouse.service'; + +import { CreateProductStockDto, UpdateProductStockDto } from './dto'; +import { ProductStock } from './entities'; + +interface FindFilter { + accountId: number; + productId: number; + warehouseId?: number | number[]; +} + +@Injectable() +export class ProductStockService { + constructor( + @InjectRepository(ProductStock) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly reservationService: ReservationService, + @Inject(forwardRef(() => WarehouseService)) + private readonly warehouseService: WarehouseService, + ) {} + + public async create({ + accountId, + productId, + dtos, + }: { + accountId: number; + productId: number; + dtos: CreateProductStockDto[]; + }): Promise { + return await this.repository.save( + dtos.map((dto) => new ProductStock(accountId, productId, dto.warehouseId, dto.stockQuantity)), + ); + } + + public async findMany({ user, filter }: { user: User | null; filter: FindFilter }): Promise { + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId: filter.accountId, warehouseId: filter.warehouseId, onlyAvailable: true }, + }); + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + const [stocks, reserved] = await Promise.all([ + this.createFindQb(filter).getMany(), + this.reservationService.getReservedQuantities(filter.accountId, { + productId: filter.productId, + warehouseId: filter.warehouseId, + }), + ]); + stocks.forEach( + (stock) => (stock.reserved = reserved.find((r) => r.warehouseId === stock.warehouseId)?.quantity ?? 0), + ); + + return stocks; + } + + public async update({ + accountId, + user, + sectionId, + productId, + dtos, + }: { + accountId: number; + user: User; + sectionId: number; + productId: number; + dtos: UpdateProductStockDto[]; + }): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + const deleteStocks = dtos.filter((dto) => dto.stockQuantity === null); + if (deleteStocks.length > 0) { + await this.repository.delete({ productId, warehouseId: In(deleteStocks.map((ds) => ds.warehouseId)) }); + } + + const updateStocks = dtos.filter((dto) => dto.stockQuantity !== null); + if (updateStocks.length > 0) { + await this.repository.save( + updateStocks.map((dto) => new ProductStock(accountId, productId, dto.warehouseId, dto.stockQuantity)), + ); + } + + return await this.findMany({ user, filter: { accountId, productId } }); + } + + public async delete({ + accountId, + warehouseId, + newWarehouseId, + }: { + accountId: number; + warehouseId: number; + newWarehouseId?: number; + }): Promise { + if (newWarehouseId) { + const stocks = await this.repository.findBy({ accountId, warehouseId }); + for (const stock of stocks) { + const otherStock = await this.repository.findOneBy({ + accountId, + warehouseId: newWarehouseId, + productId: stock.productId, + }); + + if (otherStock) { + otherStock.stockQuantity += stock.stockQuantity; + await this.repository.save(otherStock); + } else { + await this.repository.update( + { productId: stock.productId, warehouseId: stock.warehouseId }, + { warehouseId: newWarehouseId }, + ); + } + } + } + + await this.repository.delete({ accountId, warehouseId }); + } + + public async reduce({ + accountId, + warehouseId, + productId, + quantity, + }: { + accountId: number; + warehouseId: number; + productId: number; + quantity: number; + }): Promise { + const stock = await this.repository.findOneBy({ accountId, warehouseId, productId }); + if (stock) { + stock.stockQuantity -= quantity; + await this.repository.save(stock); + } + return stock; + } + + public async increase({ + accountId, + warehouseId, + productId, + quantity, + }: { + accountId: number; + warehouseId: number; + productId: number; + quantity: number; + }): Promise { + const stock = await this.repository.findOneBy({ accountId, warehouseId, productId }); + if (stock) { + stock.stockQuantity += quantity; + await this.repository.save(stock); + } + return stock; + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('stock') + .where('stock.account_id = :accountId', { accountId: filter.accountId }) + .andWhere('stock.product_id = :productId', { productId: filter.productId }); + if (filter.warehouseId) { + if (Array.isArray(filter.warehouseId)) { + qb.andWhere('stock.warehouse_id IN (:...warehouseIds)', { warehouseIds: filter.warehouseId }); + } else { + qb.andWhere('stock.warehouse_id = :warehouseId', { warehouseId: filter.warehouseId }); + } + } + return qb; + } +} diff --git a/backend/src/modules/inventory/product/dto/create-product.dto.ts b/backend/src/modules/inventory/product/dto/create-product.dto.ts new file mode 100644 index 0000000..1adec56 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/create-product.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { ProductDto } from './product.dto'; +import { CreateProductPriceDto } from '../../product-price/dto/create-product-price.dto'; +import { CreateProductStockDto } from '../../product-stock/dto/create-product-stock.dto'; + +export class CreateProductDto extends PickType(ProductDto, [ + 'name', + 'type', + 'description', + 'sku', + 'unit', + 'tax', + 'categoryId', +] as const) { + @ApiProperty({ type: [CreateProductPriceDto] }) + @IsArray() + prices: CreateProductPriceDto[]; + + @ApiProperty({ type: [String] }) + @IsArray() + photoFileIds: string[]; + + @ApiProperty({ type: [CreateProductStockDto] }) + @IsArray() + stocks: CreateProductStockDto[]; +} diff --git a/backend/src/modules/inventory/product/dto/file-upload-request.ts b/backend/src/modules/inventory/product/dto/file-upload-request.ts new file mode 100644 index 0000000..dc5f2f2 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/file-upload-request.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class FilesUploadRequest { + @ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } }) + files: any[]; +} diff --git a/backend/src/modules/inventory/product/dto/get-products.meta.ts b/backend/src/modules/inventory/product/dto/get-products.meta.ts new file mode 100644 index 0000000..3d1e548 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/get-products.meta.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class GetProductsMeta { + @ApiProperty() + @IsNumber() + totalCount: number; + + constructor(totalCount: number) { + this.totalCount = totalCount; + } +} diff --git a/backend/src/modules/inventory/product/dto/get-products.result.ts b/backend/src/modules/inventory/product/dto/get-products.result.ts new file mode 100644 index 0000000..5f84572 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/get-products.result.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNotEmpty } from 'class-validator'; + +import { ProductDto } from './product.dto'; +import { GetProductsMeta } from './get-products.meta'; + +export class GetProductsResult { + @ApiProperty({ type: [ProductDto] }) + @IsArray() + products: ProductDto[]; + + @ApiProperty() + @IsNotEmpty() + meta: GetProductsMeta; + + constructor(products: ProductDto[], meta: GetProductsMeta) { + this.products = products; + this.meta = meta; + } +} diff --git a/backend/src/modules/inventory/product/dto/index.ts b/backend/src/modules/inventory/product/dto/index.ts new file mode 100644 index 0000000..1ab867e --- /dev/null +++ b/backend/src/modules/inventory/product/dto/index.ts @@ -0,0 +1,8 @@ +export * from './create-product.dto'; +export * from './file-upload-request'; +export * from './get-products.meta'; +export * from './get-products.result'; +export * from './product-info.dto'; +export * from './product.dto'; +export * from './products-filter'; +export * from './update-product.dto'; diff --git a/backend/src/modules/inventory/product/dto/product-info.dto.ts b/backend/src/modules/inventory/product/dto/product-info.dto.ts new file mode 100644 index 0000000..e5cc53b --- /dev/null +++ b/backend/src/modules/inventory/product/dto/product-info.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class ProductInfoDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + constructor(id: number, name: string) { + this.id = id; + this.name = name; + } +} diff --git a/backend/src/modules/inventory/product/dto/product.dto.ts b/backend/src/modules/inventory/product/dto/product.dto.ts new file mode 100644 index 0000000..972f6e6 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/product.dto.ts @@ -0,0 +1,113 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; + +import { ProductType } from '../enums/product-type.enum'; +import { ProductPriceDto } from '../../product-price/dto/product-price.dto'; +import { ProductStockDto } from '../../product-stock/dto/product-stock.dto'; +import { RentalScheduleStatus } from '../../rental-schedule/enums'; +import { RentalEventDto } from '../../rental-schedule/dto/rental-event.dto'; + +export class ProductDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + sectionId: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty({ enum: ProductType }) + @IsEnum(ProductType) + type: ProductType; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + description: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + sku: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + unit: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + tax: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + categoryId: number | null; + + @ApiProperty() + @IsString() + updatedAt: string; + + @ApiProperty({ type: [ProductPriceDto] }) + @IsArray() + prices: ProductPriceDto[]; + + @ApiProperty({ type: [FileLinkDto] }) + @IsArray() + photoFileLinks: FileLinkDto[]; + + @ApiProperty({ type: [ProductStockDto] }) + @IsArray() + stocks: ProductStockDto[]; + + @ApiPropertyOptional({ nullable: true, enum: RentalScheduleStatus }) + @IsOptional() + @IsEnum(RentalScheduleStatus) + rentalStatus: RentalScheduleStatus | null; + + @ApiPropertyOptional({ nullable: true, type: [RentalEventDto] }) + @IsOptional() + @IsArray() + rentalEvents: RentalEventDto[] | null; + + constructor( + id: number, + sectionId: number, + name: string, + type: ProductType, + description: string | null, + sku: string | null, + unit: string | null, + tax: number | null, + categoryId: number | null, + updatedAt: string, + prices: ProductPriceDto[], + photoFileLinks: FileLinkDto[], + stocks: ProductStockDto[], + rentalStatus: RentalScheduleStatus | null, + rentalEvents: RentalEventDto[] | null, + ) { + this.id = id; + this.sectionId = sectionId; + this.name = name; + this.type = type; + this.description = description; + this.sku = sku; + this.unit = unit; + this.tax = tax; + this.categoryId = categoryId; + this.updatedAt = updatedAt; + this.prices = prices; + this.photoFileLinks = photoFileLinks; + this.stocks = stocks; + this.rentalStatus = rentalStatus; + this.rentalEvents = rentalEvents; + } +} diff --git a/backend/src/modules/inventory/product/dto/products-filter.ts b/backend/src/modules/inventory/product/dto/products-filter.ts new file mode 100644 index 0000000..1e56438 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/products-filter.ts @@ -0,0 +1,29 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ProductsFilter { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + warehouseId?: number; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + categoryId?: number; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + search?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + sku?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + ids?: string; +} diff --git a/backend/src/modules/inventory/product/dto/update-product.dto.ts b/backend/src/modules/inventory/product/dto/update-product.dto.ts new file mode 100644 index 0000000..9d5e857 --- /dev/null +++ b/backend/src/modules/inventory/product/dto/update-product.dto.ts @@ -0,0 +1,11 @@ +import { PickType } from '@nestjs/swagger'; +import { ProductDto } from './product.dto'; + +export class UpdateProductDto extends PickType(ProductDto, [ + 'name', + 'description', + 'sku', + 'unit', + 'tax', + 'categoryId', +] as const) {} diff --git a/backend/src/modules/inventory/product/entities/index.ts b/backend/src/modules/inventory/product/entities/index.ts new file mode 100644 index 0000000..9120a7a --- /dev/null +++ b/backend/src/modules/inventory/product/entities/index.ts @@ -0,0 +1 @@ +export * from './product.entity'; diff --git a/backend/src/modules/inventory/product/entities/product.entity.ts b/backend/src/modules/inventory/product/entities/product.entity.ts new file mode 100644 index 0000000..06aa3e2 --- /dev/null +++ b/backend/src/modules/inventory/product/entities/product.entity.ts @@ -0,0 +1,148 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; + +import { PermissionObjectType } from '../../common'; + +import { ProductPrice } from '../../product-price/entities/product-price.entity'; +import { ProductStock } from '../../product-stock/entities'; +import { RentalScheduleStatus } from '../../rental-schedule/enums'; +import { RentalEvent } from '../../rental-schedule/entities/rental-event.entity'; +import { ProductType } from '../enums/product-type.enum'; +import { ProductDto } from '../dto/product.dto'; +import { CreateProductDto } from '../dto/create-product.dto'; +import { ProductInfoDto } from '../dto/product-info.dto'; + +@Entity() +export class Product implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column() + name: string; + + @Column() + type: ProductType; + + @Column() + description: string | null; + + @Column() + sku: string | null; + + @Column() + unit: string | null; + + @Column() + tax: number | null; + + @Column() + isDeleted: boolean; + + @Column() + categoryId: number | null; + + @Column() + createdBy: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + @Column() + updatedAt: Date; + + constructor( + accountId: number, + sectionId: number, + name: string, + type: ProductType, + description: string | null, + sku: string | null, + unit: string | null, + tax: number | null, + isDeleted: boolean, + categoryId: number | null, + createdBy: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.sectionId = sectionId; + this.name = name; + this.type = type; + this.description = description; + this.sku = sku; + this.unit = unit; + this.tax = tax; + this.isDeleted = isDeleted; + this.categoryId = categoryId; + this.createdBy = createdBy; + this.createdAt = createdAt ?? DateUtil.now(); + this.updatedAt = this.createdAt; + } + + static getAuthorizable(sectionId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Products, id: sectionId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Products, + id: this.sectionId, + createdBy: this.createdBy, + }; + } + + public static fromDto(accountId: number, sectionId: number, createdBy: number, dto: CreateProductDto): Product { + return new Product( + accountId, + sectionId, + dto.name, + dto.type, + dto.description, + dto.sku, + dto.unit, + dto.tax, + false, + dto.categoryId, + createdBy, + ); + } + + public toDto( + prices: ProductPrice[] | null, + photoFileLinks: FileLinkDto[], + stocks: ProductStock[] | null, + rentalStatus: RentalScheduleStatus | null = null, + rentalEvents: RentalEvent[] | null = null, + ): ProductDto { + return new ProductDto( + this.id, + this.sectionId, + this.name, + this.type, + this.description, + this.sku, + this.unit, + this.tax, + this.categoryId, + this.updatedAt.toISOString(), + prices ? prices.map((price) => price.toDto()) : [], + photoFileLinks, + stocks ? stocks.map((stock) => stock.toDto()) : [], + rentalStatus, + rentalEvents ? rentalEvents.map((event) => event.toDto()) : [], + ); + } + + public toInfo(): ProductInfoDto { + return new ProductInfoDto(this.id, this.name); + } +} diff --git a/backend/src/modules/inventory/product/enums/index.ts b/backend/src/modules/inventory/product/enums/index.ts new file mode 100644 index 0000000..f5a64a2 --- /dev/null +++ b/backend/src/modules/inventory/product/enums/index.ts @@ -0,0 +1 @@ +export * from './product-type.enum'; diff --git a/backend/src/modules/inventory/product/enums/product-type.enum.ts b/backend/src/modules/inventory/product/enums/product-type.enum.ts new file mode 100644 index 0000000..0ca24ac --- /dev/null +++ b/backend/src/modules/inventory/product/enums/product-type.enum.ts @@ -0,0 +1,5 @@ +export enum ProductType { + PRODUCT = 'product', + SERVICE = 'service', + KIT = 'kit', +} diff --git a/backend/src/modules/inventory/product/product.controller.ts b/backend/src/modules/inventory/product/product.controller.ts new file mode 100644 index 0000000..a59b588 --- /dev/null +++ b/backend/src/modules/inventory/product/product.controller.ts @@ -0,0 +1,122 @@ +import { + Body, + Controller, + Delete, + FileTypeValidator, + Get, + MaxFileSizeValidator, + Param, + ParseFilePipe, + ParseIntPipe, + Post, + Put, + Query, + UploadedFiles, + UseInterceptors, +} from '@nestjs/common'; +import { AnyFilesInterceptor } from '@nestjs/platform-express'; +import { ApiBody, ApiConsumes, ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; +import { memoryStorage } from 'multer'; + +import { DatePeriodDto, PagingQuery } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { StorageFile } from '@/modules/storage/types/storage-file'; +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; + +import { ProductService } from './product.service'; +import { CreateProductDto } from './dto/create-product.dto'; +import { FilesUploadRequest } from './dto/file-upload-request'; +import { GetProductsResult } from './dto/get-products.result'; +import { ProductDto } from './dto/product.dto'; +import { ProductsFilter } from './dto/products-filter'; +import { UpdateProductDto } from './dto/update-product.dto'; + +const ProductImageFile = { + MaxSize: 5242880, + Type: 'image/*', +}; + +@ApiTags('inventory/products') +@Controller('products/sections/:sectionId/products') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +export class ProductController { + constructor(private readonly service: ProductService) {} + + @ApiCreatedResponse({ description: 'Create product', type: ProductDto }) + @Post() + public async create( + @CurrentAuth() { account, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: CreateProductDto, + ): Promise { + return await this.service.create(account, user, sectionId, dto); + } + + @ApiCreatedResponse({ description: 'Get products', type: GetProductsResult }) + @Get() + public async getMany( + @CurrentAuth() { account, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Query() filter: ProductsFilter, + @Query() paging: PagingQuery, + @Query() period: DatePeriodDto, + ): Promise { + return await this.service.getProducts(account, user, sectionId, filter, period, paging); + } + + @ApiCreatedResponse({ description: 'Product', type: ProductDto }) + @Get('/:productId') + public async getOne( + @CurrentAuth() { account, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + ): Promise { + return this.service.getDtoById(account, user, sectionId, productId); + } + + @ApiCreatedResponse({ description: 'Product', type: ProductDto }) + @Put('/:productId') + public async update( + @CurrentAuth() { account, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Body() dto: UpdateProductDto, + ): Promise { + return this.service.update(account, user, sectionId, productId, dto); + } + + @ApiConsumes('multipart/form-data') + @ApiBody({ description: 'Photos to upload', type: FilesUploadRequest }) + @ApiCreatedResponse({ description: 'Uploaded product photos' }) + @UseInterceptors(AnyFilesInterceptor({ storage: memoryStorage() })) + @Post('/:productId/photos') + async upload( + @UploadedFiles( + new ParseFilePipe({ + validators: [ + new MaxFileSizeValidator({ maxSize: ProductImageFile.MaxSize }), + new FileTypeValidator({ fileType: ProductImageFile.Type }), + ], + }), + ) + files: Express.Multer.File[], + @CurrentAuth() { account, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + ): Promise { + return await this.service.uploadPhotos(account, user, sectionId, productId, StorageFile.fromMulterFiles(files)); + } + + @ApiCreatedResponse({ description: 'Delete product' }) + @Delete('/:productId') + public async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + ): Promise { + await this.service.markDeleted(accountId, user, sectionId, productId); + } +} diff --git a/backend/src/modules/inventory/product/product.module.ts b/backend/src/modules/inventory/product/product.module.ts new file mode 100644 index 0000000..fcef1b1 --- /dev/null +++ b/backend/src/modules/inventory/product/product.module.ts @@ -0,0 +1,32 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { ProductPriceModule } from '../product-price/product-price.module'; +import { ProductStockModule } from '../product-stock/product-stock.module'; +import { ProductCategoryModule } from '../product-category/product-category.module'; +import { RentalScheduleModule } from '../rental-schedule/rental-schedule.module'; + +import { Product } from './entities/product.entity'; +import { ProductService } from './product.service'; +import { ProductController } from './product.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Product]), + IAMModule, + forwardRef(() => CrmModule), + StorageModule, + ProductPriceModule, + ProductStockModule, + ProductCategoryModule, + forwardRef(() => RentalScheduleModule), + ], + controllers: [ProductController], + providers: [ProductService], + exports: [ProductService], +}) +export class ProductModule {} diff --git a/backend/src/modules/inventory/product/product.service.ts b/backend/src/modules/inventory/product/product.service.ts new file mode 100644 index 0000000..24240e8 --- /dev/null +++ b/backend/src/modules/inventory/product/product.service.ts @@ -0,0 +1,298 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, In, Not, Repository } from 'typeorm'; + +import { + BadRequestError, + DatePeriod, + DatePeriodDto, + DateUtil, + FileLinkSource, + NotFoundError, + PagingQuery, +} from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; +import { FileLinkService } from '@/CRM/Service/FileLink/FileLinkService'; +import { FileLinkDto } from '@/CRM/Service/FileLink/FileLinkDto'; + +import { ProductPriceService } from '../product-price/product-price.service'; +import { ProductStockService } from '../product-stock/product-stock.service'; +import { ProductCategoryService } from '../product-category/product-category.service'; +import { RentalScheduleService } from '../rental-schedule/rental-schedule.service'; + +import { ProductType } from './enums/product-type.enum'; +import { CreateProductDto } from './dto/create-product.dto'; +import { GetProductsMeta } from './dto/get-products.meta'; +import { GetProductsResult } from './dto/get-products.result'; +import { ProductDto } from './dto/product.dto'; +import { ProductsFilter } from './dto/products-filter'; +import { UpdateProductDto } from './dto/update-product.dto'; +import { Product } from './entities/product.entity'; + +@Injectable() +export class ProductService { + constructor( + @InjectRepository(Product) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly productPriceService: ProductPriceService, + private readonly fileLinkService: FileLinkService, + private readonly storageService: StorageService, + @Inject(forwardRef(() => ProductStockService)) + private readonly stockService: ProductStockService, + private readonly categoryService: ProductCategoryService, + @Inject(forwardRef(() => RentalScheduleService)) + private readonly scheduleService: RentalScheduleService, + ) {} + + public async create(account: Account, user: User, sectionId: number, dto: CreateProductDto): Promise { + await this.authService.check({ + action: 'create', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + if (dto.sku && (await this.exists(account.id, sectionId, dto.sku))) { + throw new BadRequestError(`Product with sku ${dto.sku} already exists.`); + } + + const product = await this.repository.save(Product.fromDto(account.id, sectionId, user.id, dto)); + + if (dto.prices?.length > 0) { + await this.productPriceService.createPricesForProduct(account.id, product.id, dto.prices); + } + if (dto.photoFileIds?.length > 0) { + await this.fileLinkService.processFiles(account.id, FileLinkSource.PRODUCT_PHOTO, product.id, dto.photoFileIds); + } + if (dto.stocks?.length > 0) { + await this.stockService.create({ accountId: account.id, productId: product.id, dtos: dto.stocks }); + } + + return await this.createDto(account, user, product); + } + + public async findById(accountId: number, sectionId: number, productId: number): Promise { + return await this.repository.findOneBy({ accountId, id: productId, sectionId }); + } + public async getById(accountId: number, user: User, sectionId: number, productId: number): Promise { + const product = await this.findById(accountId, sectionId, productId); + if (!product) { + throw NotFoundError.withId(Product, productId); + } + await this.authService.check({ action: 'view', user, authorizable: product, throwError: true }); + + return product; + } + + private async exists(accountId: number, sectionId: number, sku: string, excludeProductId?: number): Promise { + return this.repository.existsBy({ + accountId, + sectionId, + sku, + isDeleted: false, + id: excludeProductId ? Not(excludeProductId) : undefined, + }); + } + + public async getDtoById(account: Account, user: User, sectionId: number, productId: number): Promise { + const product = await this.getById(account.id, user, sectionId, productId); + + return await this.createDto(account, user, product); + } + + public async getProducts( + account: Account, + user: User, + sectionId: number, + filter: ProductsFilter, + periodDto: DatePeriodDto, + paging: PagingQuery, + ): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + const qb = await this.createQb(account.id, sectionId, filter); + + const products = await qb.orderBy('created_at', 'DESC').offset(paging.skip).limit(paging.take).getMany(); + const period = DatePeriod.fromDto(periodDto); + const dtos = await Promise.all(products.map((product) => this.createDto(account, user, product, period))); + const totalCount = await qb.getCount(); + + return new GetProductsResult(dtos, new GetProductsMeta(totalCount)); + } + + public async getProductsSimple( + accountId: number, + sectionId: number, + filter: ProductsFilter, + paging: PagingQuery, + ): Promise { + const qb = await this.createQb(accountId, sectionId, filter); + + return await qb.orderBy('created_at', 'DESC').offset(paging.skip).limit(paging.take).getMany(); + } + + //TODO: Change ProductDto to Product with prices and stocks + public async findManyFull( + account: Account, + user: User, + sectionId: number, + filter?: ProductsFilter, + ): Promise { + const qb = await this.createQb(account.id, sectionId, filter); + const products = await qb.getMany(); + return await Promise.all(products.map((product) => this.createDto(account, user, product))); + } + + public async update( + account: Account, + user: User, + sectionId: number, + productId: number, + dto: UpdateProductDto, + ): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + if (dto.sku && (await this.exists(account.id, sectionId, dto.sku, productId))) { + throw new BadRequestError(`Product with sku ${dto.sku} already exists.`); + } + + await this.repository.update( + { id: productId, accountId: account.id, sectionId }, + { + name: dto.name, + description: dto.description, + sku: dto.sku, + unit: dto.unit, + tax: dto.tax, + categoryId: dto.categoryId, + updatedAt: DateUtil.now(), + }, + ); + + return await this.getDtoById(account, user, sectionId, productId); + } + + public async markDeleted(accountId: number, user: User, sectionId: number, productId: number) { + await this.authService.check({ + action: 'delete', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + await this.repository.update({ accountId, id: productId, sectionId }, { isDeleted: true }); + } + + public async delete(accountId: number, ids: number[]) { + for (const id of ids) { + this.fileLinkService.processFiles(accountId, FileLinkSource.PRODUCT_PHOTO, id, []); + } + await this.repository.delete({ accountId, id: In(ids) }); + } + + public async uploadPhotos( + account: Account, + user: User, + sectionId: number, + productId: number, + files: StorageFile[], + ): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: Product.getAuthorizable(sectionId), + throwError: true, + }); + + const fileInfos = await this.storageService.storeProductFiles({ + accountId: account.id, + userId: user.id, + productId, + files, + section: 'photos', + }); + return await this.fileLinkService.addFiles( + account, + FileLinkSource.PRODUCT_PHOTO, + productId, + fileInfos.map((fileInfo) => fileInfo.id), + ); + } + + private async createDto(account: Account, user: User, product: Product, period?: DatePeriod): Promise { + const prices = await this.productPriceService.getPricesForProduct(account.id, product.id); + const fileLinks = await this.fileLinkService.getFileLinkDtos(account, FileLinkSource.PRODUCT_PHOTO, product.id); + const stocks = await this.stockService.findMany({ user, filter: { accountId: account.id, productId: product.id } }); + + if (period?.from && period?.to) { + const status = await this.scheduleService.checkProductStatus(account.id, product.sectionId, product.id, [period]); + + return product.toDto(prices, fileLinks, stocks, status.status, status.events); + } + + return product.toDto(prices, fileLinks, stocks); + } + + private async createQb(accountId: number, sectionId: number, filter?: ProductsFilter) { + const qb = this.repository + .createQueryBuilder('p') + .where('p.account_id = :accountId', { accountId }) + .andWhere('p.section_id = :sectionId', { sectionId }) + .andWhere({ isDeleted: false }); + + if (filter?.ids) { + qb.andWhere('id IN (:...ids)', { ids: filter.ids.split(',').map((id) => parseInt(id)) }); + return qb; + } + + if (filter?.categoryId) { + const categories = await this.categoryService.getCategoriesFlat(accountId, sectionId, filter.categoryId); + qb.andWhere('p.category_id IN (:...categoryIds)', { categoryIds: categories.map((c) => c.id) }); + } + + if (filter?.warehouseId) { + qb.andWhere( + new Brackets((qb) => { + qb.where('p.type IN (:...types)', { types: [ProductType.SERVICE, ProductType.KIT] }).orWhere( + '0 < (select sum(s.stock_quantity) from product_stock s where s.product_id = p.id and warehouse_id = :wId)', + { + wId: filter.warehouseId, + }, + ); + }), + ); + } + + if (filter?.search) { + qb.andWhere( + new Brackets((qb1) => + qb1 + .where('p.name ILIKE :searchName', { searchName: `%${filter.search}%` }) + .orWhere('p.sku ILIKE :searchSku', { searchSku: `%${filter.search}%` }), + ), + ); + } + + if (filter?.sku) { + qb.andWhere('p.sku = :sku', { sku: filter.sku }); + } + + return qb; + } +} diff --git a/backend/src/modules/inventory/products-section/dto/create-products-section.dto.ts b/backend/src/modules/inventory/products-section/dto/create-products-section.dto.ts new file mode 100644 index 0000000..a4862fc --- /dev/null +++ b/backend/src/modules/inventory/products-section/dto/create-products-section.dto.ts @@ -0,0 +1,8 @@ +import { OmitType } from '@nestjs/swagger'; +import { ProductsSectionDto } from './products-section.dto'; + +export class CreateProductsSectionDto extends OmitType(ProductsSectionDto, [ + 'id', + 'entityTypeIds', + 'schedulerIds', +] as const) {} diff --git a/backend/src/modules/inventory/products-section/dto/index.ts b/backend/src/modules/inventory/products-section/dto/index.ts new file mode 100644 index 0000000..ed4624e --- /dev/null +++ b/backend/src/modules/inventory/products-section/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-products-section.dto'; +export * from './link-modules.dto'; +export * from './products-section-entity-type.dto'; +export * from './products-section.dto'; +export * from './update-products-section.dto'; diff --git a/backend/src/modules/inventory/products-section/dto/link-modules.dto.ts b/backend/src/modules/inventory/products-section/dto/link-modules.dto.ts new file mode 100644 index 0000000..6bd5ae0 --- /dev/null +++ b/backend/src/modules/inventory/products-section/dto/link-modules.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +export class LinkModulesDto { + @ApiProperty({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + entityTypeIds: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + schedulerIds?: number[] | null; +} diff --git a/backend/src/modules/inventory/products-section/dto/products-section-entity-type.dto.ts b/backend/src/modules/inventory/products-section/dto/products-section-entity-type.dto.ts new file mode 100644 index 0000000..87f552f --- /dev/null +++ b/backend/src/modules/inventory/products-section/dto/products-section-entity-type.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class ProductsSectionEntityTypeDto { + @ApiProperty() + @IsNumber() + sectionId: number; + + @ApiProperty() + @IsNumber() + entityTypeId: number; + + constructor(sectionId: number, entityTypeId: number) { + this.sectionId = sectionId; + this.entityTypeId = entityTypeId; + } +} diff --git a/backend/src/modules/inventory/products-section/dto/products-section.dto.ts b/backend/src/modules/inventory/products-section/dto/products-section.dto.ts new file mode 100644 index 0000000..8e8f331 --- /dev/null +++ b/backend/src/modules/inventory/products-section/dto/products-section.dto.ts @@ -0,0 +1,69 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ProductsSectionType } from '../enums/products-section-type.enum'; + +export class ProductsSectionDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsString() + icon: string; + + @ApiPropertyOptional({ nullable: true, enum: ProductsSectionType }) + @IsOptional() + @IsEnum(ProductsSectionType) + type: ProductsSectionType | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsBoolean() + enableWarehouse?: boolean | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsBoolean() + enableBarcode?: boolean | null; + + @ApiPropertyOptional({ nullable: true, description: 'in hours' }) + @IsOptional() + @IsNumber() + cancelAfter?: number | null; + + @ApiProperty({ type: [Number] }) + @IsArray() + entityTypeIds: number[]; + + @ApiProperty({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + schedulerIds: number[] | null; + + constructor({ + id, + name, + icon, + type, + enableWarehouse, + enableBarcode, + cancelAfter, + entityTypeIds, + schedulerIds, + }: ProductsSectionDto) { + this.id = id; + this.name = name; + this.icon = icon; + this.type = type; + this.enableWarehouse = enableWarehouse; + this.enableBarcode = enableBarcode; + this.cancelAfter = cancelAfter; + this.entityTypeIds = entityTypeIds; + this.schedulerIds = schedulerIds; + } +} diff --git a/backend/src/modules/inventory/products-section/dto/update-products-section.dto.ts b/backend/src/modules/inventory/products-section/dto/update-products-section.dto.ts new file mode 100644 index 0000000..8ea75ad --- /dev/null +++ b/backend/src/modules/inventory/products-section/dto/update-products-section.dto.ts @@ -0,0 +1,9 @@ +import { OmitType } from '@nestjs/swagger'; +import { ProductsSectionDto } from './products-section.dto'; + +export class UpdateProductsSectionDto extends OmitType(ProductsSectionDto, [ + 'id', + 'type', + 'entityTypeIds', + 'schedulerIds', +] as const) {} diff --git a/backend/src/modules/inventory/products-section/entities/index.ts b/backend/src/modules/inventory/products-section/entities/index.ts new file mode 100644 index 0000000..cc6d5ec --- /dev/null +++ b/backend/src/modules/inventory/products-section/entities/index.ts @@ -0,0 +1,2 @@ +export * from './products-section-entity-type.entity'; +export * from './products-section.entity'; diff --git a/backend/src/modules/inventory/products-section/entities/products-section-entity-type.entity.ts b/backend/src/modules/inventory/products-section/entities/products-section-entity-type.entity.ts new file mode 100644 index 0000000..f0a8b03 --- /dev/null +++ b/backend/src/modules/inventory/products-section/entities/products-section-entity-type.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ProductsSectionEntityTypeDto } from '../dto'; + +@Entity() +export class ProductsSectionEntityType { + @PrimaryColumn() + sectionId: number; + + @PrimaryColumn() + entityTypeId: number; + + @Column() + accountId: number; + + constructor(accountId: number, sectionId: number, entityTypeId: number) { + this.accountId = accountId; + this.sectionId = sectionId; + this.entityTypeId = entityTypeId; + } + + public static fromDto(accountId: number, dto: ProductsSectionEntityTypeDto): ProductsSectionEntityType { + return new ProductsSectionEntityType(accountId, dto.sectionId, dto.entityTypeId); + } + + public toDto(): ProductsSectionEntityTypeDto { + return new ProductsSectionEntityTypeDto(this.sectionId, this.entityTypeId); + } +} diff --git a/backend/src/modules/inventory/products-section/entities/products-section.entity.ts b/backend/src/modules/inventory/products-section/entities/products-section.entity.ts new file mode 100644 index 0000000..081d742 --- /dev/null +++ b/backend/src/modules/inventory/products-section/entities/products-section.entity.ts @@ -0,0 +1,122 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; +import { CreateProductsSectionDto, ProductsSectionDto, UpdateProductsSectionDto } from '../dto'; +import { ProductsSectionType } from '../enums'; +import { ProductsSectionEntityType } from './products-section-entity-type.entity'; + +@Entity() +export class ProductsSection implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + icon: string; + + @Column({ default: 'sale' }) + type: ProductsSectionType; + + @Column() + enableWarehouse: boolean; + + @Column({ default: true }) + enableBarcode: boolean; + + @Column({ nullable: true }) + cancelAfter: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + private _links: ProductsSectionEntityType[] = []; + private _schedulerIds: number[] | null; + + constructor( + accountId: number, + name: string, + icon: string, + type: ProductsSectionType, + enableWarehouse: boolean, + enableBarcode: boolean, + cancelAfter: number | null, + createdAt?: Date, + ) { + this.name = name; + this.icon = icon; + this.type = type; + this.enableWarehouse = enableWarehouse; + this.enableBarcode = enableBarcode; + this.accountId = accountId; + this.cancelAfter = cancelAfter; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public get links(): ProductsSectionEntityType[] { + return this._links; + } + public set links(value: ProductsSectionEntityType[]) { + this._links = value; + } + + public get schedulerIds(): number[] | null { + return this._schedulerIds; + } + public set schedulerIds(value: number[] | null) { + this._schedulerIds = value; + } + + static getAuthorizable(sectionId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Products, id: sectionId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Products, + id: this.id, + }; + } + + public static fromDto(accountId: number, dto: CreateProductsSectionDto): ProductsSection { + return new ProductsSection( + accountId, + dto.name, + dto.icon, + dto.type ?? ProductsSectionType.Sale, + dto.enableWarehouse ?? true, + dto.enableBarcode ?? true, + dto.cancelAfter, + ); + } + + public toDto(): ProductsSectionDto { + return new ProductsSectionDto({ + id: this.id, + name: this.name, + icon: this.icon, + type: this.type, + enableWarehouse: this.enableWarehouse, + enableBarcode: this.enableBarcode, + cancelAfter: this.cancelAfter, + entityTypeIds: this._links.map((l) => l.entityTypeId), + schedulerIds: this.schedulerIds ?? null, + }); + } + + public update(dto: UpdateProductsSectionDto): ProductsSection { + this.name = dto.name; + this.icon = dto.icon; + this.enableWarehouse = dto.enableWarehouse ?? true; + this.enableBarcode = dto.enableBarcode ?? true; + this.cancelAfter = dto.cancelAfter !== undefined ? dto.cancelAfter : this.cancelAfter; + + return this; + } +} diff --git a/backend/src/modules/inventory/products-section/enums/index.ts b/backend/src/modules/inventory/products-section/enums/index.ts new file mode 100644 index 0000000..d24604c --- /dev/null +++ b/backend/src/modules/inventory/products-section/enums/index.ts @@ -0,0 +1 @@ +export * from './products-section-type.enum'; diff --git a/backend/src/modules/inventory/products-section/enums/products-section-type.enum.ts b/backend/src/modules/inventory/products-section/enums/products-section-type.enum.ts new file mode 100644 index 0000000..74f67c5 --- /dev/null +++ b/backend/src/modules/inventory/products-section/enums/products-section-type.enum.ts @@ -0,0 +1,4 @@ +export enum ProductsSectionType { + Sale = 'sale', + Rental = 'rental', +} diff --git a/backend/src/modules/inventory/products-section/products-section.controller.ts b/backend/src/modules/inventory/products-section/products-section.controller.ts new file mode 100644 index 0000000..ef952ba --- /dev/null +++ b/backend/src/modules/inventory/products-section/products-section.controller.ts @@ -0,0 +1,85 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { UserAccess } from '@/modules/iam/common/decorators/user-access.decorator'; + +import { ProductsSectionDto, CreateProductsSectionDto, UpdateProductsSectionDto, LinkModulesDto } from './dto'; +import { ProductsSectionService } from './services/products-section.service'; + +@ApiTags('inventory/section') +@Controller('products/sections') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ProductsSectionController { + constructor(private readonly service: ProductsSectionService) {} + + @ApiCreatedResponse({ description: 'Create product section', type: ProductsSectionDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateProductsSectionDto) { + return await this.service.create(accountId, dto); + } + + @ApiCreatedResponse({ description: 'Get all product sections', type: [ProductsSectionDto] }) + @Get() + public async getMany(@CurrentAuth() { accountId, user }: AuthData) { + return await this.service.getAllFull(accountId, user); + } + + @ApiCreatedResponse({ description: 'Get product section', type: ProductsSectionDto }) + @Get('/:sectionId') + public async getOne( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + ) { + return await this.service.getOneFull(accountId, user, sectionId); + } + + @ApiCreatedResponse({ description: 'Update product section', type: ProductsSectionDto }) + @Put('/:sectionId') + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: UpdateProductsSectionDto, + ) { + return await this.service.update(accountId, user, sectionId, dto); + } + + @ApiCreatedResponse({ description: 'Delete product section', type: Boolean }) + @Delete('/:sectionId') + @UserAccess({ adminOnly: true }) + public async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + ): Promise { + return await this.service.delete(accountId, user, sectionId); + } + + @ApiCreatedResponse({ description: 'Link products to entity types', type: Boolean }) + @Post('/:sectionId/link') + @UserAccess({ adminOnly: true }) + public async linkEntityTypes( + @CurrentAuth() { accountId }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body('entityTypeIds') entityTypeIds: number[], + ): Promise { + return await this.service.linkEntityTypes(accountId, sectionId, entityTypeIds); + } + + @ApiCreatedResponse({ description: 'Link products to entity types', type: Boolean }) + @Post('/:sectionId/links') + @UserAccess({ adminOnly: true }) + public async link( + @CurrentAuth() { accountId }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: LinkModulesDto, + ): Promise { + return await this.service.link(accountId, sectionId, dto); + } +} diff --git a/backend/src/modules/inventory/products-section/products-section.module.ts b/backend/src/modules/inventory/products-section/products-section.module.ts new file mode 100644 index 0000000..9695cc3 --- /dev/null +++ b/backend/src/modules/inventory/products-section/products-section.module.ts @@ -0,0 +1,28 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { SchedulerModule } from '@/modules/scheduler/scheduler.module'; + +import { OrderModule } from '../order/order.module'; +import { RentalOrderModule } from '../rental-order/rental-order.module'; + +import { ProductsSectionEntityType } from './entities/products-section-entity-type.entity'; +import { ProductsSection } from './entities/products-section.entity'; +import { ProductsSectionLinkerService } from './services/products-section-linker.service'; +import { ProductsSectionService } from './services/products-section.service'; +import { ProductsSectionController } from './products-section.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ProductsSection, ProductsSectionEntityType]), + IAMModule, + forwardRef(() => SchedulerModule), + OrderModule, + RentalOrderModule, + ], + controllers: [ProductsSectionController], + providers: [ProductsSectionService, ProductsSectionLinkerService], + exports: [ProductsSectionService], +}) +export class ProductsSectionModule {} diff --git a/backend/src/modules/inventory/products-section/services/products-section-linker.service.ts b/backend/src/modules/inventory/products-section/services/products-section-linker.service.ts new file mode 100644 index 0000000..ba98357 --- /dev/null +++ b/backend/src/modules/inventory/products-section/services/products-section-linker.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ProductsSectionEntityType } from '../entities'; + +@Injectable() +export class ProductsSectionLinkerService { + constructor( + @InjectRepository(ProductsSectionEntityType) + private readonly repository: Repository, + ) {} + + public async getLinkedSectionIds(accountId: number, entityTypeId: number): Promise { + const pset = await this.repository.findBy({ accountId, entityTypeId }); + + return pset.map((p) => p.sectionId); + } + + public async linkSectionWithEntityTypes( + accountId: number, + sectionId: number, + entityTypeIds: number[], + ): Promise { + await this.repository.delete({ accountId, sectionId }); + + return await this.repository.save( + entityTypeIds.map((etId) => new ProductsSectionEntityType(accountId, sectionId, etId)), + ); + } + + public async linkEntityTypeWithSections( + accountId: number, + entityTypeId: number, + sectionIds: number[], + ): Promise { + await this.repository.delete({ accountId, entityTypeId }); + + return await this.repository.save( + sectionIds.map((sid) => new ProductsSectionEntityType(accountId, sid, entityTypeId)), + ); + } + + public async ensureLinked(accountId: number, sectionId: number, entityTypeId: number): Promise { + await this.repository.save({ accountId, sectionId, entityTypeId }); + } +} diff --git a/backend/src/modules/inventory/products-section/services/products-section.service.ts b/backend/src/modules/inventory/products-section/services/products-section.service.ts new file mode 100644 index 0000000..c9dd4e0 --- /dev/null +++ b/backend/src/modules/inventory/products-section/services/products-section.service.ts @@ -0,0 +1,168 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { ScheduleService } from '@/modules/scheduler/schedule/services/schedule.service'; + +import { ProductsEventType, ProductsSectionEvent } from '../../common'; +import { OrderService } from '../../order/services/order.service'; +import { RentalOrderService } from '../../rental-order/services/rental-order.service'; + +import { CreateProductsSectionDto, UpdateProductsSectionDto, LinkModulesDto } from '../dto'; +import { ProductsSection, ProductsSectionEntityType } from '../entities'; +import { ProductsSectionLinkerService } from './products-section-linker.service'; + +@Injectable() +export class ProductsSectionService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(ProductsSection) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly linkerService: ProductsSectionLinkerService, + private readonly orderService: OrderService, + @Inject(forwardRef(() => RentalOrderService)) + private readonly rentalOrderService: RentalOrderService, + @Inject(forwardRef(() => ScheduleService)) + private readonly scheduleService: ScheduleService, + ) {} + + public async create(accountId: number, dto: CreateProductsSectionDto): Promise { + const section = await this.repository.save(ProductsSection.fromDto(accountId, dto)); + + this.eventEmitter.emit( + ProductsEventType.ProductsSectionCreated, + new ProductsSectionEvent({ accountId, sectionId: section.id }), + ); + + return section; + } + + public async getAllFull(accountId: number, user: User): Promise { + const sections = await this.repository + .createQueryBuilder('ps') + .where('ps.account_id = :accountId', { accountId }) + .leftJoinAndMapMany('ps.links', ProductsSectionEntityType, 'link', 'link.section_id = ps.id') + .orderBy('ps.created_at', 'ASC') + .getMany(); + + const allowedSections: ProductsSection[] = []; + for (const section of sections) { + if ( + await this.authService.check({ + action: 'view', + user, + authorizable: ProductsSection.getAuthorizable(section.id), + }) + ) { + section.schedulerIds = await this.scheduleService.getLinkedSchedulerIds(accountId, { + productsSectionId: section.id, + }); + allowedSections.push(section); + } + } + + return allowedSections; + } + + public async getOneFull(accountId: number, user: User, sectionId: number): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: ProductsSection.getAuthorizable(sectionId), + throwError: true, + }); + + const section = await this.repository + .createQueryBuilder('ps') + .where('ps.account_id = :accountId', { accountId }) + .andWhere('ps.id = :id', { id: sectionId }) + .leftJoinAndMapMany('ps.links', ProductsSectionEntityType, 'link', 'link.section_id = ps.id') + .getOneOrFail(); + + section.schedulerIds = await this.scheduleService.getLinkedSchedulerIds(accountId, { + productsSectionId: sectionId, + }); + + return section; + } + + private async getOne(accountId: number, sectionId: number): Promise { + return await this.repository + .createQueryBuilder('ps') + .where('ps.account_id = :accountId', { accountId }) + .andWhere('ps.id = :id', { id: sectionId }) + .getOneOrFail(); + } + + public async update( + accountId: number, + user: User, + sectionId: number, + dto: UpdateProductsSectionDto, + ): Promise { + const section = await this.getOne(accountId, sectionId); + await this.repository.save(section.update(dto)); + + return this.getOneFull(accountId, user, sectionId); + } + + public async delete(accountId: number, user: User, sectionId: number): Promise { + await this.orderService.delete(accountId, { sectionId }); + await this.rentalOrderService.delete(accountId, user, { sectionId }); + + const result = await this.repository.delete({ accountId, id: sectionId }); + + this.eventEmitter.emit( + ProductsEventType.ProductsSectionDeleted, + new ProductsSectionEvent({ accountId, sectionId }), + ); + + return result.affected > 0; + } + + public async link(accountId: number, sectionId: number, dto: LinkModulesDto): Promise { + if (dto.entityTypeIds) { + await this.linkerService.linkSectionWithEntityTypes(accountId, sectionId, dto.entityTypeIds); + } + + if (dto.schedulerIds) { + await this.scheduleService.linkProductsSection(accountId, dto.schedulerIds, sectionId); + } + + return true; + } + + public async linkEntityTypes(accountId: number, sectionId: number, entityTypeIds: number[]): Promise { + await this.linkerService.linkSectionWithEntityTypes(accountId, sectionId, entityTypeIds); + + return true; + } + + public async linkSections(accountId: number, entityTypeId: number, sectionIds: number[]): Promise { + await this.linkerService.linkEntityTypeWithSections(accountId, entityTypeId, sectionIds); + + return true; + } + + public async getLinkedSectionIds(accountId: number, user: User, entityTypeId: number): Promise { + const sectionIds = await this.linkerService.getLinkedSectionIds(accountId, entityTypeId); + + const availableSectionIds: number[] = []; + for (const sectionId of sectionIds) { + if ( + await this.authService.check({ action: 'view', user, authorizable: ProductsSection.getAuthorizable(sectionId) }) + ) { + availableSectionIds.push(sectionId); + } + } + return availableSectionIds; + } + + public async ensureLinked(accountId: number, sectionId: number, entityTypeId: number): Promise { + await this.linkerService.ensureLinked(accountId, sectionId, entityTypeId); + } +} diff --git a/backend/src/modules/inventory/rental-interval/dto/rental-interval.dto.ts b/backend/src/modules/inventory/rental-interval/dto/rental-interval.dto.ts new file mode 100644 index 0000000..eb7c052 --- /dev/null +++ b/backend/src/modules/inventory/rental-interval/dto/rental-interval.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsOptional, IsString } from 'class-validator'; + +import { RentalIntervalType } from '../entities/rental-interval-type.enum'; + +export class RentalIntervalDto { + @ApiProperty({ enum: RentalIntervalType }) + @IsEnum(RentalIntervalType) + type: RentalIntervalType; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + startTime: string | null; + + constructor(type: RentalIntervalType, startTime: string | null) { + this.type = type; + this.startTime = startTime; + } +} diff --git a/backend/src/modules/inventory/rental-interval/entities/rental-interval-type.enum.ts b/backend/src/modules/inventory/rental-interval/entities/rental-interval-type.enum.ts new file mode 100644 index 0000000..5a194af --- /dev/null +++ b/backend/src/modules/inventory/rental-interval/entities/rental-interval-type.enum.ts @@ -0,0 +1,3 @@ +export enum RentalIntervalType { + DAY = 'day', +} diff --git a/backend/src/modules/inventory/rental-interval/entities/rental-interval.entity.ts b/backend/src/modules/inventory/rental-interval/entities/rental-interval.entity.ts new file mode 100644 index 0000000..896b489 --- /dev/null +++ b/backend/src/modules/inventory/rental-interval/entities/rental-interval.entity.ts @@ -0,0 +1,44 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { RentalIntervalType } from './rental-interval-type.enum'; +import { RentalIntervalDto } from '../dto/rental-interval.dto'; + +@Entity() +export class RentalInterval { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column() + type: RentalIntervalType; + + @Column({ type: 'time', nullable: true }) + startTime: string | null; + + @Column() + accountId: number; + + constructor(accountId: number, sectionId: number, type: RentalIntervalType, startTime: string | null) { + this.accountId = accountId; + this.sectionId = sectionId; + this.type = type; + this.startTime = startTime; + } + + public static create(accountId: number, sectionId: number, dto: RentalIntervalDto): RentalInterval { + return new RentalInterval(accountId, sectionId, dto.type, dto.startTime); + } + + public update(dto: RentalIntervalDto): RentalInterval { + this.type = dto.type ?? this.type; + this.startTime = dto.startTime ?? this.startTime; + + return this; + } + + public toDto(): RentalIntervalDto { + return new RentalIntervalDto(this.type, this.startTime?.substring(0, 5) ?? null); + } +} diff --git a/backend/src/modules/inventory/rental-interval/rental-interval.controller.ts b/backend/src/modules/inventory/rental-interval/rental-interval.controller.ts new file mode 100644 index 0000000..7f5dc13 --- /dev/null +++ b/backend/src/modules/inventory/rental-interval/rental-interval.controller.ts @@ -0,0 +1,39 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { RentalIntervalService } from './rental-interval.service'; +import { RentalInterval } from './entities/rental-interval.entity'; +import { RentalIntervalDto } from './dto/rental-interval.dto'; + +@ApiTags('inventory/rental/interval') +@Controller('rental/sections/:sectionId/interval') +@JwtAuthorized() +@TransformToDto() +export class RentalIntervalController { + constructor(private readonly service: RentalIntervalService) {} + + @ApiCreatedResponse({ description: 'Set product rental interval', type: RentalIntervalDto }) + @Post() + public async setRentalInterval( + @CurrentAuth() { accountId }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: RentalIntervalDto, + ): Promise { + return await this.service.setRentalInterval(accountId, sectionId, dto); + } + + @ApiCreatedResponse({ description: 'Get product section rental interval', type: RentalIntervalDto }) + @Get() + public async findRentalInterval( + @CurrentAuth() { accountId }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + ): Promise { + return await this.service.findRentalInterval(accountId, sectionId); + } +} diff --git a/backend/src/modules/inventory/rental-interval/rental-interval.module.ts b/backend/src/modules/inventory/rental-interval/rental-interval.module.ts new file mode 100644 index 0000000..b92dd69 --- /dev/null +++ b/backend/src/modules/inventory/rental-interval/rental-interval.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { RentalIntervalService } from './rental-interval.service'; +import { RentalIntervalController } from './rental-interval.controller'; +import { RentalInterval } from './entities/rental-interval.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([RentalInterval]), IAMModule], + controllers: [RentalIntervalController], + providers: [RentalIntervalService], + exports: [RentalIntervalService], +}) +export class RentalIntervalModule {} diff --git a/backend/src/modules/inventory/rental-interval/rental-interval.service.ts b/backend/src/modules/inventory/rental-interval/rental-interval.service.ts new file mode 100644 index 0000000..4ea67bc --- /dev/null +++ b/backend/src/modules/inventory/rental-interval/rental-interval.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { RentalInterval } from './entities/rental-interval.entity'; +import { RentalIntervalDto } from './dto/rental-interval.dto'; + +@Injectable() +export class RentalIntervalService { + constructor( + @InjectRepository(RentalInterval) + private readonly repository: Repository, + ) {} + + public async findRentalInterval(accountId: number, sectionId: number): Promise { + return await this.repository.findOneBy({ accountId, sectionId }); + } + + public async setRentalInterval( + accountId: number, + sectionId: number, + dto: RentalIntervalDto, + ): Promise { + const interval = await this.findRentalInterval(accountId, sectionId); + + return interval + ? await this.repository.save(interval.update(dto)) + : await this.repository.save(RentalInterval.create(accountId, sectionId, dto)); + } +} diff --git a/backend/src/modules/inventory/rental-order/dto/create-rental-order-item.dto.ts b/backend/src/modules/inventory/rental-order/dto/create-rental-order-item.dto.ts new file mode 100644 index 0000000..e4da836 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/create-rental-order-item.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { RentalOrderItemDto } from './rental-order-item.dto'; + +export class CreateRentalOrderItemDto extends OmitType(RentalOrderItemDto, ['id'] as const) {} diff --git a/backend/src/modules/inventory/rental-order/dto/create-rental-order.dto.ts b/backend/src/modules/inventory/rental-order/dto/create-rental-order.dto.ts new file mode 100644 index 0000000..102e334 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/create-rental-order.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { IsArray, IsNumber } from 'class-validator'; + +import { RentalOrderDto } from './rental-order.dto'; +import { CreateRentalOrderItemDto } from './create-rental-order-item.dto'; + +export class CreateRentalOrderDto extends OmitType(RentalOrderDto, [ + 'id', + 'sectionId', + 'orderNumber', + 'createdBy', + 'createdAt', + 'items', + 'entityInfo', +] as const) { + @ApiProperty() + @IsNumber() + entityId: number; + + @ApiProperty({ type: [CreateRentalOrderItemDto] }) + @IsArray() + items: CreateRentalOrderItemDto[]; +} diff --git a/backend/src/modules/inventory/rental-order/dto/index.ts b/backend/src/modules/inventory/rental-order/dto/index.ts new file mode 100644 index 0000000..68a0d3e --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/index.ts @@ -0,0 +1,7 @@ +export * from './create-rental-order-item.dto'; +export * from './create-rental-order.dto'; +export * from './rental-order-filter'; +export * from './rental-order-item.dto'; +export * from './rental-order.dto'; +export * from './update-rental-order-item.dto'; +export * from './update-rental-order.dto'; diff --git a/backend/src/modules/inventory/rental-order/dto/rental-order-filter.ts b/backend/src/modules/inventory/rental-order/dto/rental-order-filter.ts new file mode 100644 index 0000000..975ce42 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/rental-order-filter.ts @@ -0,0 +1,16 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; + +import { RentalOrderStatus } from '../enums'; + +export class RentalOrderFilter { + @ApiPropertyOptional({ type: Number, nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional({ enum: RentalOrderStatus, isArray: true, nullable: true }) + @IsOptional() + @IsArray() + statuses?: RentalOrderStatus[] | null; +} diff --git a/backend/src/modules/inventory/rental-order/dto/rental-order-item.dto.ts b/backend/src/modules/inventory/rental-order/dto/rental-order-item.dto.ts new file mode 100644 index 0000000..59237c6 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/rental-order-item.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class RentalOrderItemDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + productId: number; + + @ApiProperty() + @IsNumber() + unitPrice: number; + + @ApiProperty() + @IsNumber() + tax: number; + + @ApiProperty() + @IsNumber() + discount: number; + + @ApiProperty() + @IsNumber() + sortOrder: number; + + constructor(id: number, productId: number, unitPrice: number, tax: number, discount: number, sortOrder: number) { + this.id = id; + this.productId = productId; + this.unitPrice = unitPrice; + this.tax = tax; + this.discount = discount; + this.sortOrder = sortOrder; + } +} diff --git a/backend/src/modules/inventory/rental-order/dto/rental-order.dto.ts b/backend/src/modules/inventory/rental-order/dto/rental-order.dto.ts new file mode 100644 index 0000000..b3e6566 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/rental-order.dto.ts @@ -0,0 +1,88 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { Currency, DatePeriodDto } from '@/common'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { RentalOrderStatus } from '../enums'; +import { RentalOrderItemDto } from './rental-order-item.dto'; + +export class RentalOrderDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + sectionId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + warehouseId: number | null; + + @ApiProperty() + @IsNumber() + orderNumber: number; + + @ApiProperty() + @IsNumber() + createdBy: number; + + @ApiProperty({ enum: Currency }) + @IsEnum(Currency) + currency: Currency; + + @ApiProperty() + @IsBoolean() + taxIncluded: boolean; + + @ApiProperty({ enum: RentalOrderStatus }) + @IsEnum(RentalOrderStatus) + status: RentalOrderStatus; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiProperty({ type: [DatePeriodDto] }) + @IsArray() + periods: DatePeriodDto[]; + + @ApiProperty({ type: [RentalOrderItemDto] }) + @IsArray() + items: RentalOrderItemDto[]; + + @ApiProperty({ type: EntityInfoDto }) + @IsNotEmpty() + entityInfo: EntityInfoDto; + + constructor( + id: number, + sectionId: number, + warehouseId: number | null, + orderNumber: number, + createdBy: number, + currency: Currency, + taxIncluded: boolean, + status: RentalOrderStatus, + createdAt: string, + periods: DatePeriodDto[], + items: RentalOrderItemDto[], + entityInfo: EntityInfoDto, + ) { + this.id = id; + this.sectionId = sectionId; + this.warehouseId = warehouseId; + this.orderNumber = orderNumber; + this.createdBy = createdBy; + this.currency = currency; + this.taxIncluded = taxIncluded; + this.status = status; + this.createdAt = createdAt; + this.periods = periods; + this.items = items; + this.entityInfo = entityInfo; + } +} diff --git a/backend/src/modules/inventory/rental-order/dto/update-rental-order-item.dto.ts b/backend/src/modules/inventory/rental-order/dto/update-rental-order-item.dto.ts new file mode 100644 index 0000000..01fe511 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/update-rental-order-item.dto.ts @@ -0,0 +1,11 @@ +import { ApiPropertyOptional, OmitType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { RentalOrderItemDto } from './rental-order-item.dto'; + +export class UpdateRentalOrderItemDto extends OmitType(RentalOrderItemDto, ['id'] as const) { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + id?: number; +} diff --git a/backend/src/modules/inventory/rental-order/dto/update-rental-order.dto.ts b/backend/src/modules/inventory/rental-order/dto/update-rental-order.dto.ts new file mode 100644 index 0000000..8640901 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/dto/update-rental-order.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { RentalOrderDto } from './rental-order.dto'; +import { UpdateRentalOrderItemDto } from './update-rental-order-item.dto'; + +export class UpdateRentalOrderDto extends OmitType(RentalOrderDto, [ + 'id', + 'sectionId', + 'orderNumber', + 'createdBy', + 'createdAt', + 'items', + 'entityInfo', +] as const) { + @ApiProperty({ type: [UpdateRentalOrderItemDto] }) + @IsArray() + items: UpdateRentalOrderItemDto[]; +} diff --git a/backend/src/modules/inventory/rental-order/entities/index.ts b/backend/src/modules/inventory/rental-order/entities/index.ts new file mode 100644 index 0000000..8b34ff1 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/entities/index.ts @@ -0,0 +1,3 @@ +export * from './rental-order-item.entity'; +export * from './rental-order-period.entity'; +export * from './rental-order.entity'; diff --git a/backend/src/modules/inventory/rental-order/entities/rental-order-item.entity.ts b/backend/src/modules/inventory/rental-order/entities/rental-order-item.entity.ts new file mode 100644 index 0000000..0313d51 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/entities/rental-order-item.entity.ts @@ -0,0 +1,90 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { CreateRentalOrderItemDto, RentalOrderItemDto } from '../dto'; + +@Entity() +export class RentalOrderItem { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + orderId: number; + + @Column() + productId: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + unitPrice: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + tax: number; + + @Column({ + type: 'numeric', + transformer: { + to: (value: number) => value, + from: (value: unknown) => Number(value), + }, + }) + discount: number; + + @Column() + sortOrder: number; + + @Column() + accountId: number; + + constructor( + accountId: number, + orderId: number, + productId: number, + unitPrice: number, + tax: number, + discount: number, + sortOrder: number, + id?: number, + ) { + this.id = id; + this.accountId = accountId; + this.orderId = orderId; + this.productId = productId; + this.unitPrice = unitPrice; + this.tax = tax; + this.discount = discount; + this.sortOrder = sortOrder; + } + + public static fromDto( + accountId: number, + orderId: number, + dto: CreateRentalOrderItemDto, + id?: number, + ): RentalOrderItem { + return new RentalOrderItem( + accountId, + orderId, + dto.productId, + dto.unitPrice, + dto.tax, + dto.discount, + dto.sortOrder, + id, + ); + } + + public toDto(): RentalOrderItemDto { + return new RentalOrderItemDto(this.id, this.productId, this.unitPrice, this.tax, this.discount, this.sortOrder); + } +} diff --git a/backend/src/modules/inventory/rental-order/entities/rental-order-period.entity.ts b/backend/src/modules/inventory/rental-order/entities/rental-order-period.entity.ts new file mode 100644 index 0000000..4e4d943 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/entities/rental-order-period.entity.ts @@ -0,0 +1,36 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DatePeriodDto } from '@/common'; + +@Entity() +export class RentalOrderPeriod { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + orderId: number; + + @Column() + startDate: Date; + + @Column() + endDate: Date; + + @Column() + accountId: number; + + constructor(accountId: number, orderId: number, startDate: Date, endDate: Date) { + this.accountId = accountId; + this.orderId = orderId; + this.startDate = startDate; + this.endDate = endDate; + } + + public static fromDto(accountId: number, orderId: number, dto: DatePeriodDto): RentalOrderPeriod { + return new RentalOrderPeriod(accountId, orderId, new Date(dto.startDate), new Date(dto.endDate)); + } + + public toDto(): DatePeriodDto { + return { startDate: this.startDate.toISOString(), endDate: this.endDate.toISOString() }; + } +} diff --git a/backend/src/modules/inventory/rental-order/entities/rental-order.entity.ts b/backend/src/modules/inventory/rental-order/entities/rental-order.entity.ts new file mode 100644 index 0000000..dd27452 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/entities/rental-order.entity.ts @@ -0,0 +1,155 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { Currency, DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { PermissionObjectType } from '../../common'; + +import { CreateRentalOrderDto, RentalOrderDto, UpdateRentalOrderDto } from '../dto'; +import { RentalOrderStatus } from '../enums'; +import { RentalOrderItem } from './rental-order-item.entity'; +import { RentalOrderPeriod } from './rental-order-period.entity'; + +@Entity() +export class RentalOrder implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column({ nullable: true }) + warehouseId: number | null; + + @Column() + entityId: number; + + @Column() + createdBy: number; + + @Column() + currency: Currency; + + @Column() + taxIncluded: boolean; + + @Column() + status: RentalOrderStatus; + + @Column() + orderNumber: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + private _periods: RentalOrderPeriod[] = []; + private _items: RentalOrderItem[] = []; + private _entityInfo: EntityInfoDto = null; + + constructor( + accountId: number, + sectionId: number, + warehouseId: number | null, + createdBy: number, + currency: Currency, + taxIncluded: boolean, + entityId: number, + status: RentalOrderStatus, + orderNumber: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.sectionId = sectionId; + this.warehouseId = warehouseId; + this.entityId = entityId; + this.createdBy = createdBy; + this.currency = currency; + this.taxIncluded = taxIncluded; + this.status = status; + this.orderNumber = orderNumber; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public get periods(): RentalOrderPeriod[] { + return this._periods; + } + public set periods(periods: RentalOrderPeriod[]) { + this._periods = periods; + } + + public get items(): RentalOrderItem[] { + return this._items; + } + public set items(items: RentalOrderItem[]) { + this._items = items; + } + + public get entityInfo(): EntityInfoDto { + return this._entityInfo; + } + public set entityInfo(value: EntityInfoDto) { + this._entityInfo = value; + } + + static getAuthorizable(sectionId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.ProductsOrder, id: sectionId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.ProductsOrder, + id: this.sectionId, + createdBy: this.createdBy, + }; + } + + public static fromDto( + accountId: number, + sectionId: number, + orderNumber: number, + createdBy: number, + dto: CreateRentalOrderDto, + ): RentalOrder { + return new RentalOrder( + accountId, + sectionId, + dto.warehouseId, + createdBy, + dto.currency, + dto.taxIncluded, + dto.entityId, + dto.status, + orderNumber, + ); + } + + public toDto(): RentalOrderDto { + return new RentalOrderDto( + this.id, + this.sectionId, + this.warehouseId, + this.orderNumber, + this.createdBy, + this.currency, + this.taxIncluded, + this.status, + this.createdAt.toISOString(), + this._periods?.map((period) => period.toDto()) ?? [], + this._items?.map((item) => item.toDto()) ?? [], + this._entityInfo, + ); + } + + public update(dto: UpdateRentalOrderDto): RentalOrder { + this.warehouseId = dto.warehouseId; + this.status = dto.status; + this.currency = dto.currency; + this.taxIncluded = dto.taxIncluded; + + return this; + } +} diff --git a/backend/src/modules/inventory/rental-order/enums/index.ts b/backend/src/modules/inventory/rental-order/enums/index.ts new file mode 100644 index 0000000..e172053 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/enums/index.ts @@ -0,0 +1 @@ +export * from './rental-order-status.enum'; diff --git a/backend/src/modules/inventory/rental-order/enums/rental-order-status.enum.ts b/backend/src/modules/inventory/rental-order/enums/rental-order-status.enum.ts new file mode 100644 index 0000000..2bcdcaf --- /dev/null +++ b/backend/src/modules/inventory/rental-order/enums/rental-order-status.enum.ts @@ -0,0 +1,10 @@ +export enum RentalOrderStatus { + Formed = 'formed', + Reserved = 'reserved', + SentToWarehouse = 'sent_to_warehouse', + Shipped = 'shipped', + Delivered = 'delivered', + Returned = 'returned', + AcceptedToWarehouse = 'accepted_to_warehouse', + Cancelled = 'cancelled', +} diff --git a/backend/src/modules/inventory/rental-order/rental-order.controller.ts b/backend/src/modules/inventory/rental-order/rental-order.controller.ts new file mode 100644 index 0000000..e3b45ee --- /dev/null +++ b/backend/src/modules/inventory/rental-order/rental-order.controller.ts @@ -0,0 +1,93 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { RentalOrderDto, CreateRentalOrderDto, RentalOrderFilter, UpdateRentalOrderDto } from './dto'; +import { RentalOrder } from './entities'; +import { RentalOrderStatus } from './enums'; +import { RentalOrderService } from './services'; + +@ApiTags('inventory/rental/orders') +@Controller('rental/sections/:sectionId/orders') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class RentalOrderController { + constructor(private readonly service: RentalOrderService) {} + + @ApiCreatedResponse({ description: 'Create rental order', type: RentalOrderDto }) + @Post() + public async create( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: CreateRentalOrderDto, + ): Promise { + return await this.service.create(accountId, user, sectionId, dto); + } + + @ApiCreatedResponse({ description: 'Get rental order', type: RentalOrderDto }) + @Get('/:orderId') + public async getOne( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('orderId', ParseIntPipe) orderId: number, + ): Promise { + return this.service.getOne(accountId, user, sectionId, orderId); + } + + @ApiCreatedResponse({ description: 'Get entity rental orders', type: [RentalOrderDto] }) + @Post('/search') + public async getMany( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() filter?: RentalOrderFilter, + ): Promise { + return this.service.findMany(accountId, user, { ...filter, sectionId }); + } + + @ApiCreatedResponse({ description: 'Get entity rental orders', type: [RentalOrderDto] }) + @Get('/entity/:entityId') + public async getForEntity( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('entityId', ParseIntPipe) entityId: number, + ): Promise { + return this.service.findMany(accountId, user, { sectionId, entityId }); + } + + @ApiCreatedResponse({ description: 'Update rental order', type: RentalOrderDto }) + @Put('/:orderId') + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('orderId', ParseIntPipe) orderId: number, + @Body() dto: UpdateRentalOrderDto, + ): Promise { + return this.service.update(accountId, user, sectionId, orderId, dto); + } + + @ApiOkResponse({ description: 'Delete rental order' }) + @Delete('/:orderId') + public async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('orderId', ParseIntPipe) orderId: number, + ): Promise { + return this.service.delete(accountId, user, { sectionId, orderId }, { checkPermission: true }); + } + + @ApiCreatedResponse({ description: 'Change rental order status', type: RentalOrderDto }) + @Put('/:orderId/status/:status') + public async changeStatus( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('orderId', ParseIntPipe) orderId: number, + @Param('status') status: RentalOrderStatus, + ): Promise { + return this.service.changeStatus(accountId, user, sectionId, orderId, status); + } +} diff --git a/backend/src/modules/inventory/rental-order/rental-order.module.ts b/backend/src/modules/inventory/rental-order/rental-order.module.ts new file mode 100644 index 0000000..86cd6df --- /dev/null +++ b/backend/src/modules/inventory/rental-order/rental-order.module.ts @@ -0,0 +1,29 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; + +import { RentalScheduleModule } from '../rental-schedule/rental-schedule.module'; +import { WarehouseModule } from '../warehouse/warehouse.module'; + +import { RentalOrder } from './entities/rental-order.entity'; +import { RentalOrderItem } from './entities/rental-order-item.entity'; +import { RentalOrderPeriod } from './entities/rental-order-period.entity'; +import { RentalOrderController } from './rental-order.controller'; +import { RentalOrderService } from './services/rental-order.service'; +import { RentalOrderItemService } from './services/rental-order-item.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([RentalOrder, RentalOrderItem, RentalOrderPeriod]), + IAMModule, + EntityInfoModule, + forwardRef(() => WarehouseModule), + forwardRef(() => RentalScheduleModule), + ], + controllers: [RentalOrderController], + providers: [RentalOrderItemService, RentalOrderService], + exports: [RentalOrderService], +}) +export class RentalOrderModule {} diff --git a/backend/src/modules/inventory/rental-order/services/index.ts b/backend/src/modules/inventory/rental-order/services/index.ts new file mode 100644 index 0000000..d213c50 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/services/index.ts @@ -0,0 +1,2 @@ +export * from './rental-order-item.service'; +export * from './rental-order.service'; diff --git a/backend/src/modules/inventory/rental-order/services/rental-order-item.service.ts b/backend/src/modules/inventory/rental-order/services/rental-order-item.service.ts new file mode 100644 index 0000000..98833e5 --- /dev/null +++ b/backend/src/modules/inventory/rental-order/services/rental-order-item.service.ts @@ -0,0 +1,96 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { CreateRentalOrderItemDto, UpdateRentalOrderItemDto } from '../dto'; +import { RentalOrderItem } from '../entities'; + +@Injectable() +export class RentalOrderItemService { + constructor( + @InjectRepository(RentalOrderItem) + private readonly repository: Repository, + ) {} + + public async create({ + accountId, + orderId, + dto, + }: { + accountId: number; + orderId: number; + dto: CreateRentalOrderItemDto; + }): Promise { + return this.repository.save(RentalOrderItem.fromDto(accountId, orderId, dto)); + } + public async createMany({ + accountId, + orderId, + dtos, + }: { + accountId: number; + orderId: number; + dtos: CreateRentalOrderItemDto[]; + }): Promise { + return Promise.all(dtos.map((dto) => this.create({ accountId, orderId, dto }))); + } + + public async update({ + accountId, + orderId, + dto, + }: { + accountId: number; + orderId: number; + dto: UpdateRentalOrderItemDto; + }): Promise { + return this.repository.save(RentalOrderItem.fromDto(accountId, orderId, dto, dto.id)); + } + + public async updateMany({ + accountId, + orderId, + dtos, + }: { + accountId: number; + orderId: number; + dtos: UpdateRentalOrderItemDto[]; + }): Promise { + return Promise.all(dtos.map((dto) => this.update({ accountId, orderId, dto }))); + } + + public async processBatch({ + accountId, + orderId, + items, + dtos, + }: { + accountId: number; + orderId: number; + items: RentalOrderItem[]; + dtos: UpdateRentalOrderItemDto[]; + }): Promise { + const added = dtos.filter((dto) => !items.some((item) => item.id === dto.id)); + const updated = dtos.filter((dto) => items.some((item) => item.id === dto.id)); + const removed = items.filter((item) => !dtos.some((dto) => dto.id === item.id)); + + await this.deleteMany({ accountId, orderId, ids: removed.map((i) => i.id) }); + + const result: RentalOrderItem[] = []; + result.push(...(await this.createMany({ accountId, orderId, dtos: added }))); + result.push(...(await this.updateMany({ accountId, orderId, dtos: updated }))); + return result; + } + + public async deleteMany({ + accountId, + orderId, + ids, + }: { + accountId: number; + orderId: number; + ids: number[]; + }): Promise { + await this.repository.delete({ accountId, orderId, id: In(ids) }); + } +} diff --git a/backend/src/modules/inventory/rental-order/services/rental-order.service.ts b/backend/src/modules/inventory/rental-order/services/rental-order.service.ts new file mode 100644 index 0000000..8afb0ff --- /dev/null +++ b/backend/src/modules/inventory/rental-order/services/rental-order.service.ts @@ -0,0 +1,342 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Brackets, Repository } from 'typeorm'; +import Decimal from 'decimal.js'; + +import { DatePeriodDto, DateUtil, NotFoundError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; +import { CrmEventType, EntityPriceUpdateEvent } from '@/CRM/common'; + +import { ProductsEventType, RentalOrderCreatedEvent, RentalOrderEvent } from '../../common'; +import { RentalScheduleService } from '../../rental-schedule/rental-schedule.service'; +import { WarehouseService } from '../../warehouse/warehouse.service'; + +import { CreateRentalOrderDto, UpdateRentalOrderDto } from '../dto'; +import { RentalOrder, RentalOrderItem, RentalOrderPeriod } from '../entities'; +import { RentalOrderStatus } from '../enums'; +import { RentalOrderItemService } from './rental-order-item.service'; + +interface FindFilter { + sectionId?: number; + warehouseId?: number | number[]; + withoutWarehouse?: boolean; + orderId?: number | number[]; + entityId?: number | null; + statuses?: RentalOrderStatus[] | null; +} +interface DeleteOptions { + newWarehouseId?: number; + checkPermission?: boolean; +} + +@Injectable() +export class RentalOrderService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(RentalOrder) + private readonly repository: Repository, + @InjectRepository(RentalOrderPeriod) + private readonly repositoryPeriod: Repository, + private readonly authService: AuthorizationService, + private readonly itemsService: RentalOrderItemService, + @Inject(forwardRef(() => RentalScheduleService)) + private readonly scheduleService: RentalScheduleService, + private readonly entityInfoService: EntityInfoService, + @Inject(forwardRef(() => WarehouseService)) + private readonly warehouseService: WarehouseService, + ) {} + + public async create( + accountId: number, + user: User, + sectionId: number, + dto: CreateRentalOrderDto, + ): Promise { + await this.authService.check({ + action: 'create', + user, + authorizable: RentalOrder.getAuthorizable(sectionId), + throwError: true, + }); + + const orderNumber = await this.getOrderNumber(sectionId, dto.entityId); + const order = await this.repository.save(RentalOrder.fromDto(accountId, sectionId, orderNumber, user.id, dto)); + this.eventEmitter.emit( + ProductsEventType.RentalOrderCreated, + new RentalOrderCreatedEvent({ + accountId, + entityId: order.entityId, + rentalOrderId: order.id, + createdAt: order.createdAt.toISOString(), + }), + ); + + if (dto.periods) { + order.periods = await this.setRentalPeriods({ accountId, orderId: order.id, dtos: dto.periods }); + } + if (dto.items) { + order.items = await this.itemsService.createMany({ accountId, orderId: order.id, dtos: dto.items }); + } + + await this.scheduleService.processOrder(accountId, order); + order.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: order.entityId }); + + this.updateEntityPrice({ accountId, entityId: order.entityId }); + + return order; + } + + public async getOne(accountId: number, user: User, sectionId: number | null, orderId: number): Promise { + const order = await this.findOne(accountId, user, { sectionId, orderId }); + if (!order) { + throw NotFoundError.withId(RentalOrder, orderId); + } + await this.authService.check({ action: 'view', user, authorizable: order, throwError: true }); + + return order; + } + + public async findOne(accountId: number, user: User | null, filter?: FindFilter): Promise { + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId, sectionId: filter.sectionId, warehouseId: filter.warehouseId, onlyAvailable: true }, + }); + filter.withoutWarehouse = !filter.warehouseId; + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + const order = await this.createQb(accountId, filter).getOne(); + if (user) { + order.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: order.entityId }); + } + + return order; + } + + public async findMany(accountId: number, user: User | null, filter: FindFilter): Promise { + if (user && filter.sectionId) { + await this.authService.check({ + action: 'view', + user, + authorizable: RentalOrder.getAuthorizable(filter.sectionId), + throwError: true, + }); + } + + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId, sectionId: filter.sectionId, warehouseId: filter.warehouseId, onlyAvailable: true }, + }); + filter.withoutWarehouse = !filter.warehouseId; + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + + const orders = await this.createQb(accountId, filter).orderBy('rental_order.created_at', 'DESC').getMany(); + if (user) { + for (const order of orders) { + order.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: order.entityId }); + } + } + + return orders; + } + + public async update( + accountId: number, + user: User, + sectionId: number, + orderId: number, + dto: UpdateRentalOrderDto, + ): Promise { + const order = await this.findOne(accountId, user, { sectionId, orderId }); + if (!order) { + throw NotFoundError.withId(RentalOrder, orderId); + } + await this.authService.check({ action: 'edit', user, authorizable: order, throwError: true }); + + await this.repository.save(order.update(dto)); + + if (dto.periods) { + order.periods = await this.setRentalPeriods({ accountId, orderId: order.id, dtos: dto.periods }); + } + if (dto.items) { + order.items = await this.itemsService.processBatch({ + accountId, + orderId: order.id, + items: order.items, + dtos: dto.items, + }); + } + + await this.scheduleService.processOrder(accountId, order); + order.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: order.entityId }); + + this.updateEntityPrice({ accountId, entityId: order.entityId }); + + return order; + } + + public async changeStatus( + accountId: number, + user: User, + sectionId: number, + orderId: number, + status: RentalOrderStatus, + ): Promise { + const order = await this.findOne(accountId, user, { sectionId, orderId }); + if (!order) { + throw NotFoundError.withId(RentalOrder, orderId); + } + + await this.authService.check({ action: 'edit', user, authorizable: order, throwError: true }); + + if (status && order.status !== status) { + order.status = status; + await this.repository.save(order); + await this.scheduleService.processOrder(accountId, order); + } + + this.updateEntityPrice({ accountId, entityId: order.entityId }); + + return order; + } + + public async delete(accountId: number, user: User, filter: FindFilter, options?: DeleteOptions) { + if (options?.checkPermission && filter.sectionId) { + await this.authService.check({ + action: 'delete', + user, + authorizable: RentalOrder.getAuthorizable(filter.sectionId), + throwError: true, + }); + } + const qb = this.createQb(accountId, filter); + if (options?.newWarehouseId) { + await qb.update({ warehouseId: options.newWarehouseId }).execute(); + } else { + const orders = await qb.clone().getMany(); + await qb.clone().delete().execute(); + for (const order of orders) { + this.updateEntityPrice({ accountId, entityId: order.entityId }); + this.eventEmitter.emit( + ProductsEventType.RentalOrderDeleted, + new RentalOrderEvent({ accountId: order.accountId, entityId: order.entityId, rentalOrderId: order.id }), + ); + } + } + } + + public async getEntityIdsByOrderItemIds( + orderItemIds: number[], + ): Promise<{ orderItemId: number; entityId: number }[]> { + const result = await this.repository + .createQueryBuilder('rental_order') + .select('item.id', 'itemId') + .addSelect('rental_order.entity_id', 'entityId') + .leftJoin(RentalOrderItem, 'item', 'item.order_id = rental_order.id') + .where('item.id IN (:...orderItemIds)', { orderItemIds }) + .getRawMany(); + + return result.map((r) => ({ orderItemId: Number(r.itemId), entityId: Number(r.entityId) })); + } + + private createQb(accountId: number, filter?: FindFilter) { + const qb = this.repository + .createQueryBuilder('rental_order') + .where('rental_order.account_id = :accountId', { accountId: accountId }) + .leftJoinAndMapMany('rental_order.items', RentalOrderItem, 'item', 'rental_order.id = item.order_id') + .leftJoinAndMapMany('rental_order.periods', RentalOrderPeriod, 'period', 'rental_order.id = period.order_id'); + + if (filter?.sectionId) { + qb.andWhere('rental_order.section_id = :sectionId', { sectionId: filter.sectionId }); + } + if (filter.warehouseId) { + qb.andWhere( + new Brackets((qbW) => { + if (Array.isArray(filter.warehouseId)) { + qbW.andWhere('rental_order.warehouse_id IN (:...warehouseIds)', { warehouseIds: filter.warehouseId }); + } else { + qbW.andWhere('rental_order.warehouse_id = :warehouseId', { warehouseId: filter.warehouseId }); + } + if (filter.withoutWarehouse) { + qbW.orWhere('rental_order.warehouse_id IS NULL'); + } + }), + ); + } + if (filter?.orderId) { + qb.andWhere('rental_order.id IN (:...orderIds)', { + orderIds: Array.isArray(filter.orderId) ? filter.orderId : [filter.orderId], + }); + } + if (filter?.entityId) { + qb.andWhere('rental_order.entity_id = :entityId', { entityId: filter.entityId }); + } + if (filter?.statuses && filter.statuses.length > 0) { + qb.andWhere('rental_order.status IN (:...statuses)', { statuses: filter.statuses }); + } + + return qb; + } + + private async getOrderNumber(sectionId: number, entityId: number): Promise { + const result = await this.repository + .createQueryBuilder('ro') + .select('MAX(ro.order_number)', 'order_number') + .where('ro.entity_id = :entityId', { entityId }) + .andWhere('ro.section_id = :sectionId', { sectionId }) + .getRawOne(); + + return Number(result?.order_number ?? 0) + 1; + } + + private async updateEntityPrice({ accountId, entityId }: { accountId: number; entityId: number }) { + const orders = await this.findMany(accountId, null, { entityId }); + + const price = orders + .filter((order) => order.status !== RentalOrderStatus.Cancelled) + .reduce((sum, order) => sum + this.calculateTotalAmount(order), 0); + + this.eventEmitter.emit(CrmEventType.EntityPriceUpdate, new EntityPriceUpdateEvent({ accountId, entityId, price })); + } + private calculateTotalAmount({ items, periods, taxIncluded }: RentalOrder): number { + const amount = items.reduce((total, item) => { + return total.plus(this.calculateItemAmount({ item, taxIncluded })); + }, new Decimal(0)); + + return amount.mul(this.calculateRentalLength(periods)).toNumber(); + } + private calculateItemAmount({ item, taxIncluded }: { item: RentalOrderItem; taxIncluded: boolean }): number { + let amount = new Decimal(item.unitPrice); + if (item.discount) { + amount = amount.sub(amount.mul(new Decimal(item.discount).div(100))); + } + + return taxIncluded ? amount.toNumber() : amount.add(amount.mul(new Decimal(item.tax).div(100))).toNumber(); + } + private calculateRentalLength(periods: RentalOrderPeriod[]): number { + return periods.reduce( + (length, { startDate, endDate }) => length + DateUtil.diff({ startDate, endDate, unit: 'day' }), + 1, + ); + } + + private async setRentalPeriods({ + accountId, + orderId, + dtos, + }: { + accountId: number; + orderId: number; + dtos: DatePeriodDto[]; + }): Promise { + await this.repositoryPeriod.delete({ accountId, orderId }); + + return await this.repositoryPeriod.save(dtos.map((dto) => RentalOrderPeriod.fromDto(accountId, orderId, dto))); + } +} diff --git a/backend/src/modules/inventory/rental-schedule/dto/check-rental-status.dto.ts b/backend/src/modules/inventory/rental-schedule/dto/check-rental-status.dto.ts new file mode 100644 index 0000000..f2b08de --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/dto/check-rental-status.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { DatePeriodDto } from '@/common'; + +export class CheckRentalStatusDto { + @ApiProperty({ type: [Number] }) + @IsArray() + productIds: number[]; + + @ApiProperty({ type: [DatePeriodDto] }) + @IsArray() + periods: DatePeriodDto[]; + + constructor(productIds: number[], periods: DatePeriodDto[]) { + this.productIds = productIds; + this.periods = periods; + } +} diff --git a/backend/src/modules/inventory/rental-schedule/dto/index.ts b/backend/src/modules/inventory/rental-schedule/dto/index.ts new file mode 100644 index 0000000..473d6c3 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/dto/index.ts @@ -0,0 +1,4 @@ +export * from './check-rental-status.dto'; +export * from './product-rental-status.dto'; +export * from './rental-event.dto'; +export * from './rental-schedule.dto'; diff --git a/backend/src/modules/inventory/rental-schedule/dto/product-rental-status.dto.ts b/backend/src/modules/inventory/rental-schedule/dto/product-rental-status.dto.ts new file mode 100644 index 0000000..f57e9eb --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/dto/product-rental-status.dto.ts @@ -0,0 +1,25 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber } from 'class-validator'; + +import { RentalScheduleStatus } from '../enums'; +import { RentalEventDto } from './rental-event.dto'; + +export class ProductRentalStatusDto { + @ApiProperty() + @IsNumber() + productId: number; + + @ApiProperty({ nullable: true, enum: RentalScheduleStatus }) + @IsEnum(RentalScheduleStatus) + rentalStatus: RentalScheduleStatus; + + @ApiProperty({ type: [RentalEventDto] }) + @IsArray() + rentalEvents: RentalEventDto[]; + + constructor(productId: number, rentalStatus: RentalScheduleStatus, rentalEvents: RentalEventDto[]) { + this.productId = productId; + this.rentalStatus = rentalStatus; + this.rentalEvents = rentalEvents; + } +} diff --git a/backend/src/modules/inventory/rental-schedule/dto/rental-event.dto.ts b/backend/src/modules/inventory/rental-schedule/dto/rental-event.dto.ts new file mode 100644 index 0000000..5aab54d --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/dto/rental-event.dto.ts @@ -0,0 +1,54 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNotEmpty, IsNumber, IsString } from 'class-validator'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { RentalScheduleStatus } from '../enums'; + +export class RentalEventDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + productId: number; + + @ApiProperty() + @IsNumber() + orderItemId: number; + + @ApiProperty() + @IsString() + startDate: string; + + @ApiProperty() + @IsString() + endDate: string; + + @ApiProperty({ enum: RentalScheduleStatus }) + @IsEnum(RentalScheduleStatus) + status: RentalScheduleStatus; + + @ApiProperty({ type: EntityInfoDto }) + @IsNotEmpty() + entityInfo: EntityInfoDto; + + constructor( + id: number, + productId: number, + orderItemId: number, + startDate: string, + endDate: string, + status: RentalScheduleStatus, + entityInfo: EntityInfoDto, + ) { + this.id = id; + this.productId = productId; + this.orderItemId = orderItemId; + this.startDate = startDate; + this.endDate = endDate; + this.status = status; + this.entityInfo = entityInfo; + } +} diff --git a/backend/src/modules/inventory/rental-schedule/dto/rental-schedule.dto.ts b/backend/src/modules/inventory/rental-schedule/dto/rental-schedule.dto.ts new file mode 100644 index 0000000..639817d --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/dto/rental-schedule.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { ProductInfoDto } from '../../product/dto/product-info.dto'; +import { RentalEventDto } from './rental-event.dto'; + +export class RentalScheduleDto { + @ApiProperty({ type: [ProductInfoDto] }) + @IsArray() + products: ProductInfoDto[]; + + @ApiProperty({ type: [RentalEventDto] }) + @IsArray() + events: RentalEventDto[]; + + constructor(products: ProductInfoDto[], events: RentalEventDto[]) { + this.products = products; + this.events = events; + } +} diff --git a/backend/src/modules/inventory/rental-schedule/entities/index.ts b/backend/src/modules/inventory/rental-schedule/entities/index.ts new file mode 100644 index 0000000..f028b02 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/entities/index.ts @@ -0,0 +1 @@ +export * from './rental-event.entity'; diff --git a/backend/src/modules/inventory/rental-schedule/entities/rental-event.entity.ts b/backend/src/modules/inventory/rental-schedule/entities/rental-event.entity.ts new file mode 100644 index 0000000..d32ae3f --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/entities/rental-event.entity.ts @@ -0,0 +1,83 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { RentalScheduleStatus } from '../enums'; +import { RentalEventDto } from '../dto'; + +@Entity() +export class RentalEvent { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column() + productId: number; + + @Column() + orderItemId: number; + + @Column() + startDate: Date; + + @Column() + endDate: Date; + + @Column() + status: RentalScheduleStatus; + + @Column() + accountId: number; + + private _entityInfo: EntityInfoDto; + + constructor( + accountId: number, + sectionId: number, + productId: number, + orderItemId: number, + startDate: Date, + endDate: Date, + status: RentalScheduleStatus, + ) { + this.accountId = accountId; + this.sectionId = sectionId; + this.productId = productId; + this.orderItemId = orderItemId; + this.startDate = startDate; + this.endDate = endDate; + this.status = status; + } + + public get entityInfo(): EntityInfoDto { + return this._entityInfo; + } + public set entityInfo(entityInfo: EntityInfoDto) { + this._entityInfo = entityInfo; + } + + public toDto(): RentalEventDto { + return new RentalEventDto( + this.id, + this.productId, + this.orderItemId, + this.startDate.toISOString(), + this.endDate.toISOString(), + this.status, + this._entityInfo, + ); + } + + public ensureDates(notEarlier: Date, notLater: Date): RentalEvent { + if (this.startDate < notEarlier) { + this.startDate = notEarlier; + } + if (this.endDate > notLater) { + this.endDate = notLater; + } + + return this; + } +} diff --git a/backend/src/modules/inventory/rental-schedule/enums/index.ts b/backend/src/modules/inventory/rental-schedule/enums/index.ts new file mode 100644 index 0000000..380423d --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/enums/index.ts @@ -0,0 +1 @@ +export * from './rental-schedule-status.enum'; diff --git a/backend/src/modules/inventory/rental-schedule/enums/rental-schedule-status.enum.ts b/backend/src/modules/inventory/rental-schedule/enums/rental-schedule-status.enum.ts new file mode 100644 index 0000000..eb2ba05 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/enums/rental-schedule-status.enum.ts @@ -0,0 +1,5 @@ +export enum RentalScheduleStatus { + Available = 'available', + Reserved = 'reserved', + Rented = 'rented', +} diff --git a/backend/src/modules/inventory/rental-schedule/rental-schedule.controller.ts b/backend/src/modules/inventory/rental-schedule/rental-schedule.controller.ts new file mode 100644 index 0000000..99cbcfa --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/rental-schedule.controller.ts @@ -0,0 +1,65 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, DatePeriodDto, PagingQuery } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ProductsFilter } from '../product/dto/products-filter'; + +import { RentalScheduleDto, CheckRentalStatusDto, RentalEventDto, ProductRentalStatusDto } from './dto'; +import { RentalScheduleService } from './rental-schedule.service'; + +@ApiTags('inventory/rental/schedule') +@Controller('rental/sections/:sectionId/schedule') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class RentalScheduleController { + constructor(private readonly service: RentalScheduleService) {} + + @ApiCreatedResponse({ description: 'Get rental schedule', type: RentalScheduleDto }) + @Get() + public async getSchedule( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Query() period: DatePeriodDto, + @Query() filter: ProductsFilter, + @Query() paging: PagingQuery, + ) { + return this.service.getSchedule(accountId, user, sectionId, period, filter, paging); + } + + @ApiCreatedResponse({ description: 'Get rental schedule', type: [ProductRentalStatusDto] }) + @Post('check') + public async checkProductsStatus( + @CurrentAuth() { accountId }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: CheckRentalStatusDto, + ) { + return this.service.checkProductsStatus(accountId, sectionId, dto); + } + + @ApiCreatedResponse({ description: 'Get rental events schedule for product', type: [RentalEventDto] }) + @Get('products/:productId') + public async getProductSchedule( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Query() period: DatePeriodDto, + ) { + return this.service.getProductSchedule(accountId, user, sectionId, productId, period); + } + + @ApiOkResponse({ description: 'Release block for product in date interval' }) + @Put('products/:productId/release') + public async releaseProduct( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('productId', ParseIntPipe) productId: number, + @Body() period: DatePeriodDto, + ) { + return this.service.releaseProductByDates(accountId, user, sectionId, productId, period); + } +} diff --git a/backend/src/modules/inventory/rental-schedule/rental-schedule.module.ts b/backend/src/modules/inventory/rental-schedule/rental-schedule.module.ts new file mode 100644 index 0000000..97639b0 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/rental-schedule.module.ts @@ -0,0 +1,25 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; + +import { ProductModule } from '../product/product.module'; +import { RentalOrderModule } from '../rental-order/rental-order.module'; +import { RentalEvent } from './entities/rental-event.entity'; +import { RentalScheduleController } from './rental-schedule.controller'; +import { RentalScheduleService } from './rental-schedule.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([RentalEvent]), + IAMModule, + EntityInfoModule, + forwardRef(() => ProductModule), + forwardRef(() => RentalOrderModule), + ], + controllers: [RentalScheduleController], + providers: [RentalScheduleService], + exports: [RentalScheduleService], +}) +export class RentalScheduleModule {} diff --git a/backend/src/modules/inventory/rental-schedule/rental-schedule.service.ts b/backend/src/modules/inventory/rental-schedule/rental-schedule.service.ts new file mode 100644 index 0000000..aa8f08e --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/rental-schedule.service.ts @@ -0,0 +1,358 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, LessThanOrEqual, MoreThanOrEqual, Repository, SelectQueryBuilder } from 'typeorm'; + +import { DatePeriod, DatePeriodDto, DateUtil, isUnique, PagingQuery } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; + +import { ProductsFilter } from '../product/dto/products-filter'; +import { ProductService } from '../product/product.service'; +import { RentalOrderStatus } from '../rental-order/enums'; +import { RentalOrderService } from '../rental-order/services/rental-order.service'; +import { RentalOrder } from '../rental-order/entities/rental-order.entity'; + +import { CheckRentalStatusDto } from './dto'; +import { RentalEvent } from './entities'; +import { RentalScheduleStatus } from './enums'; +import { RentalSchedule, ProductRentalStatus } from './types'; + +@Injectable() +export class RentalScheduleService { + constructor( + @InjectRepository(RentalEvent) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + @Inject(forwardRef(() => ProductService)) + private readonly productService: ProductService, + @Inject(forwardRef(() => RentalOrderService)) + private readonly rentalOrderService: RentalOrderService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async getSchedule( + accountId: number, + user: User, + sectionId: number, + periodDto: DatePeriodDto, + filter: ProductsFilter, + paging: PagingQuery, + ): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: RentalOrder.getAuthorizable(sectionId), + throwError: true, + }); + + const products = await this.productService.getProductsSimple(accountId, sectionId, filter, paging); + if (products.length === 0) { + return new RentalSchedule([], []); + } + + const events = await this.buildSchedule(accountId, sectionId, DatePeriod.fromDto(periodDto), { + productId: products.map((p) => p.id), + }); + + if (events.length === 0) { + return new RentalSchedule(products, []); + } + + const itemsWithEntity = await this.rentalOrderService.getEntityIdsByOrderItemIds( + events.map((e) => e.orderItemId).filter(isUnique), + ); + const entityIds = itemsWithEntity.map((i) => i.entityId).filter(isUnique); + const entityInfos = + entityIds.length > 0 ? await this.entityInfoService.findMany({ accountId, user, entityIds }) : []; + + for (const event of events) { + const itemWithEntity = itemsWithEntity.find((i) => i.orderItemId === event.orderItemId); + if (itemWithEntity) { + event.entityInfo = entityInfos.find((i) => i.id === itemWithEntity.entityId); + } + } + + return new RentalSchedule(products, events); + } + + public async getProductSchedule( + accountId: number, + user: User, + sectionId: number, + productId: number, + periodDto: DatePeriodDto, + ): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: RentalOrder.getAuthorizable(sectionId), + throwError: true, + }); + + return await this.buildSchedule(accountId, sectionId, DatePeriod.fromDto(periodDto), { productId }); + } + + public async processOrder(accountId: number, order: RentalOrder): Promise { + switch (order.status) { + case RentalOrderStatus.Formed: + case RentalOrderStatus.Cancelled: + await this.releaseProducts(accountId, order); + break; + case RentalOrderStatus.Reserved: + case RentalOrderStatus.SentToWarehouse: + await this.releaseProducts(accountId, order); + await this.blockProducts(accountId, order, RentalScheduleStatus.Reserved); + break; + case RentalOrderStatus.Shipped: + case RentalOrderStatus.Delivered: + await this.releaseProducts(accountId, order); + await this.blockProducts(accountId, order, RentalScheduleStatus.Rented); + break; + case RentalOrderStatus.Returned: + case RentalOrderStatus.AcceptedToWarehouse: + await this.releaseProductsFromDate(accountId, order, DateUtil.now()); + break; + } + } + + public async releaseProductByDates( + accountId: number, + user: User, + sectionId: number, + productId: number, + period: DatePeriodDto, + ): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: RentalOrder.getAuthorizable(sectionId), + throwError: true, + }); + + const startDate = DateUtil.fromISOString(period.startDate); + const endDate = DateUtil.fromISOString(period.endDate); + + await this.releaseInnerEvents(accountId, sectionId, productId, startDate, endDate); + await this.splitOuterEvent(accountId, sectionId, productId, startDate, endDate); + await this.releaseIntersectEvents(accountId, sectionId, productId, startDate, endDate); + } + + public async checkProductStatus( + accountId: number, + sectionId: number, + productId: number, + periods: DatePeriod[], + ): Promise { + const events = ( + await Promise.all(periods.map((period) => this.buildSchedule(accountId, sectionId, period, { productId }))) + ).flat(); + + const status = events.some((event) => event.status === RentalScheduleStatus.Rented) + ? RentalScheduleStatus.Rented + : events.some((event) => event.status === RentalScheduleStatus.Reserved) + ? RentalScheduleStatus.Reserved + : RentalScheduleStatus.Available; + + return new ProductRentalStatus(productId, status, events); + } + + public async checkProductsStatus( + accountId: number, + sectionId: number, + dto: CheckRentalStatusDto, + ): Promise { + const periods = dto.periods.map((p) => DatePeriod.fromDto(p)); + + return Promise.all( + dto.productIds.map((productId) => this.checkProductStatus(accountId, sectionId, productId, periods)), + ); + } + + private async buildSchedule( + accountId: number, + sectionId: number, + period: DatePeriod, + filter?: { productId?: number | number[] }, + ): Promise { + const qb = this.createQueryBuilder(accountId, sectionId, filter); + + const inners = await this.getInnerEvents(qb, period.from, period.to); + const outers = await this.getOuterEvents(qb, period.from, period.to); + const [lefts, rights] = await this.getIntersectEvents(qb, period.from, period.to); + + return [...outers, ...lefts, ...inners, ...rights] + .filter((r) => !!r) + .sort((a, b) => DateUtil.sort(a.startDate, b.startDate)); + } + + private async releaseProducts(accountId: number, order: RentalOrder): Promise { + await this.repository.delete({ + accountId, + sectionId: order.sectionId, + orderItemId: In(order.items.map((item) => item.id)), + }); + } + private async releaseProductsFromDate(accountId: number, order: RentalOrder, date: Date): Promise { + const itemIds = order.items.map((i) => i.id); + + // release all future events + await this.repository + .createQueryBuilder() + .delete() + .where('account_id = :accountId', { accountId }) + .andWhere('section_id = :sectionId', { sectionId: order.sectionId }) + .andWhere('order_item_id IN (:...itemIds)', { itemIds }) + .andWhere('start_date > :date', { date }) + .execute(); + + // release intersect events + const events = await this.repository + .createQueryBuilder() + .where('account_id = :accountId', { accountId }) + .andWhere('section_id = :sectionId', { sectionId: order.sectionId }) + .andWhere('order_item_id IN (:...itemIds)', { itemIds }) + .andWhere('start_date < :start_date', { start_date: date }) + .andWhere('end_date > :end_date', { end_date: date }) + .getMany(); + for (const event of events) { + event.endDate = date; + await this.repository.save(event); + } + } + private async blockProducts(accountId: number, order: RentalOrder, status: RentalScheduleStatus): Promise { + for (const item of order.items) { + await this.repository.save( + order.periods.map( + (period) => + new RentalEvent( + accountId, + order.sectionId, + item.productId, + item.id, + period.startDate, + period.endDate, + status, + ), + ), + ); + } + } + + private async releaseInnerEvents( + accountId: number, + sectionId: number, + productId: number, + startDate: Date, + endDate: Date, + ) { + await this.repository.delete({ + accountId, + sectionId, + productId, + startDate: MoreThanOrEqual(startDate), + endDate: LessThanOrEqual(endDate), + }); + } + private async splitOuterEvent( + accountId: number, + sectionId: number, + productId: number, + startDate: Date, + endDate: Date, + ) { + const qb = this.createQueryBuilder(accountId, sectionId, { productId }); + + const outers = await this.getOuterEvents(qb, startDate, endDate); + for (const outer of outers) { + await this.repository.save( + new RentalEvent(accountId, sectionId, productId, outer.orderItemId, outer.startDate, startDate, outer.status), + ); + await this.repository.save( + new RentalEvent(accountId, sectionId, productId, outer.orderItemId, endDate, outer.endDate, outer.status), + ); + await this.repository.delete(outer.id); + } + } + private async releaseIntersectEvents( + accountId: number, + sectionId: number, + productId: number, + startDate: Date, + endDate: Date, + ) { + const qb = this.createQueryBuilder(accountId, sectionId, { productId }); + + const [lefts, rights] = await this.getIntersectEvents(qb, startDate, endDate); + if (lefts?.length > 0) { + lefts.forEach((left) => left.ensureDates(left.startDate, startDate)); + await this.repository.save(lefts); + } + if (rights?.length > 0) { + rights.forEach((right) => right.ensureDates(endDate, right.endDate)); + await this.repository.save(rights); + } + } + + private createQueryBuilder( + accountId: number, + sectionId: number, + filter?: { productId?: number | number[] }, + ): SelectQueryBuilder { + const qb = this.repository + .createQueryBuilder() + .where('account_id = :accountId', { accountId }) + .andWhere('section_id = :sectionId', { sectionId }); + + if (filter?.productId) { + if (Array.isArray(filter.productId)) { + qb.andWhere('product_id IN (:...productIds)', { productIds: filter.productId }); + } else { + qb.andWhere('product_id = :productId', { productId: filter.productId }); + } + } + + return qb; + } + + private async getInnerEvents( + qb: SelectQueryBuilder, + startDate: Date, + endDate: Date, + ): Promise { + return await qb + .clone() + .andWhere('start_date >= :startDate', { startDate: startDate }) + .andWhere('end_date <= :endDate', { endDate: endDate }) + .getMany(); + } + private async getOuterEvents( + qb: SelectQueryBuilder, + startDate: Date, + endDate: Date, + ): Promise { + return await qb + .clone() + .andWhere('start_date < :startDate', { startDate: startDate }) + .andWhere('end_date > :endDate', { endDate: endDate }) + .getMany(); + } + private async getIntersectEvents(qb: SelectQueryBuilder, startDate: Date, endDate: Date) { + const lefts = await qb + .clone() + .andWhere('start_date < :startDate1', { startDate1: startDate }) + .andWhere('end_date >= :startDate2', { startDate2: startDate }) + .andWhere('end_date <= :endDate', { endDate: endDate }) + .getMany(); + + const rights = await qb + .clone() + .andWhere('start_date >= :startDate', { startDate: startDate }) + .andWhere('start_date <= :endDate1', { endDate1: endDate }) + .andWhere('end_date > :endDate2', { endDate2: endDate }) + .getMany(); + + return [lefts, rights]; + } +} diff --git a/backend/src/modules/inventory/rental-schedule/types/index.ts b/backend/src/modules/inventory/rental-schedule/types/index.ts new file mode 100644 index 0000000..b8badc7 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/types/index.ts @@ -0,0 +1,2 @@ +export * from './product-rental-status'; +export * from './rental-schedule'; diff --git a/backend/src/modules/inventory/rental-schedule/types/product-rental-status.ts b/backend/src/modules/inventory/rental-schedule/types/product-rental-status.ts new file mode 100644 index 0000000..7c1cd32 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/types/product-rental-status.ts @@ -0,0 +1,23 @@ +import { ProductRentalStatusDto } from '../dto'; +import { type RentalEvent } from '../entities'; +import { type RentalScheduleStatus } from '../enums'; + +export class ProductRentalStatus { + productId: number; + status: RentalScheduleStatus; + events: RentalEvent[]; + + constructor(productId: number, status: RentalScheduleStatus, events: RentalEvent[]) { + this.productId = productId; + this.status = status; + this.events = events; + } + + public toDto(): ProductRentalStatusDto { + return new ProductRentalStatusDto( + this.productId, + this.status, + this.events.map((e) => e.toDto()), + ); + } +} diff --git a/backend/src/modules/inventory/rental-schedule/types/rental-schedule.ts b/backend/src/modules/inventory/rental-schedule/types/rental-schedule.ts new file mode 100644 index 0000000..b073157 --- /dev/null +++ b/backend/src/modules/inventory/rental-schedule/types/rental-schedule.ts @@ -0,0 +1,22 @@ +import { type Product } from '../../product/entities/product.entity'; + +import { RentalScheduleDto } from '../dto'; +import { type RentalEvent } from '../entities'; + +export class RentalSchedule { + products: Product[]; + + events: RentalEvent[]; + + constructor(products: Product[], events: RentalEvent[]) { + this.products = products; + this.events = events; + } + + public toDto(): RentalScheduleDto { + return new RentalScheduleDto( + this.products.map((p) => p.toInfo()), + this.events.map((e) => e.toDto()), + ); + } +} diff --git a/backend/src/modules/inventory/reservation/dto/index.ts b/backend/src/modules/inventory/reservation/dto/index.ts new file mode 100644 index 0000000..e32c17f --- /dev/null +++ b/backend/src/modules/inventory/reservation/dto/index.ts @@ -0,0 +1 @@ +export * from './reservation.dto'; diff --git a/backend/src/modules/inventory/reservation/dto/reservation.dto.ts b/backend/src/modules/inventory/reservation/dto/reservation.dto.ts new file mode 100644 index 0000000..fce96e8 --- /dev/null +++ b/backend/src/modules/inventory/reservation/dto/reservation.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class ReservationDto { + @ApiProperty() + @IsNumber() + warehouseId: number; + + @ApiProperty() + @IsNumber() + quantity: number; + + constructor(warehouseId: number, quantity: number) { + this.warehouseId = warehouseId; + this.quantity = quantity; + } +} diff --git a/backend/src/modules/inventory/reservation/entities/index.ts b/backend/src/modules/inventory/reservation/entities/index.ts new file mode 100644 index 0000000..800ca3e --- /dev/null +++ b/backend/src/modules/inventory/reservation/entities/index.ts @@ -0,0 +1 @@ +export * from './reservation.entity'; diff --git a/backend/src/modules/inventory/reservation/entities/reservation.entity.ts b/backend/src/modules/inventory/reservation/entities/reservation.entity.ts new file mode 100644 index 0000000..8571f26 --- /dev/null +++ b/backend/src/modules/inventory/reservation/entities/reservation.entity.ts @@ -0,0 +1,64 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ReservationDto } from '../dto'; + +@Entity() +export class Reservation { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + orderId: number; + + @Column() + orderItemId: number; + + @Column() + productId: number; + + @Column() + warehouseId: number; + + @Column() + quantity: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + orderId: number, + orderItemId: number, + productId: number, + warehouseId: number, + quantity: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.orderId = orderId; + this.orderItemId = orderItemId; + this.productId = productId; + this.warehouseId = warehouseId; + this.quantity = quantity; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto( + accountId: number, + orderId: number, + orderItemId: number, + productId: number, + dto: ReservationDto, + ): Reservation { + return new Reservation(accountId, orderId, orderItemId, productId, dto.warehouseId, dto.quantity); + } + + public toDto(): ReservationDto { + return new ReservationDto(this.warehouseId, this.quantity); + } +} diff --git a/backend/src/modules/inventory/reservation/reservation.module.ts b/backend/src/modules/inventory/reservation/reservation.module.ts new file mode 100644 index 0000000..2011a93 --- /dev/null +++ b/backend/src/modules/inventory/reservation/reservation.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { Reservation } from './entities/reservation.entity'; +import { ReservationService } from './reservation.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Reservation]), IAMModule], + providers: [ReservationService], + exports: [ReservationService], +}) +export class ReservationModule {} diff --git a/backend/src/modules/inventory/reservation/reservation.service.ts b/backend/src/modules/inventory/reservation/reservation.service.ts new file mode 100644 index 0000000..c8e11f9 --- /dev/null +++ b/backend/src/modules/inventory/reservation/reservation.service.ts @@ -0,0 +1,93 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ReservationDto } from './dto'; +import { Reservation } from './entities'; + +interface FindFilter { + warehouseId?: number | number[]; + orderId?: number | number[]; + orderItemId?: number; + productId?: number; +} +interface DeleteOptions { + newWarehouseId?: number; +} +interface ReservationByWarehouse { + quantity: number; + warehouseId: number; +} + +@Injectable() +export class ReservationService { + constructor( + @InjectRepository(Reservation) + private readonly repository: Repository, + ) {} + + public async create( + accountId: number, + orderId: number, + orderItemId: number, + productId: number, + dtos: ReservationDto[], + ): Promise { + await this.createQb(accountId, { orderId, orderItemId, productId }).delete().execute(); + + const reservations: Reservation[] = []; + for (const dto of dtos) { + reservations.push( + await this.repository.save(Reservation.fromDto(accountId, orderId, orderItemId, productId, dto)), + ); + } + + return reservations; + } + + public async findMany(accountId: number, filter: FindFilter): Promise { + return this.createQb(accountId, filter).getMany(); + } + + public async getReservedQuantities(accountId: number, filter: FindFilter): Promise { + return this.createQb(accountId, filter) + .select('reservation.warehouse_id', 'warehouseId') + .addSelect('sum(reservation.quantity)', 'quantity') + .groupBy('reservation.warehouse_id') + .getRawMany(); + } + + public async delete(accountId: number, filter: FindFilter, options?: DeleteOptions) { + const qb = this.createQb(accountId, filter); + if (options?.newWarehouseId) { + await qb.update({ warehouseId: options.newWarehouseId }).execute(); + } else { + await qb.delete().execute(); + } + } + + private createQb(accountId: number, filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('reservation') + .where('reservation.account_id = :accountId', { accountId }); + if (filter.warehouseId) { + if (Array.isArray(filter.warehouseId)) { + qb.andWhere('reservation.warehouse_id IN (:...warehouseIds)', { warehouseIds: filter.warehouseId }); + } else { + qb.andWhere('reservation.warehouse_id = :warehouseId', { warehouseId: filter.warehouseId }); + } + } + if (filter.orderId) { + qb.andWhere('reservation.order_id IN (:...orderIds)', { + orderIds: Array.isArray(filter.orderId) ? filter.orderId : [filter.orderId], + }); + } + if (filter.orderItemId) { + qb.andWhere('reservation.order_item_id = :orderItemId', { orderItemId: filter.orderItemId }); + } + if (filter.productId) { + qb.andWhere('reservation.product_id = :productId', { productId: filter.productId }); + } + return qb; + } +} diff --git a/backend/src/modules/inventory/shipment/dto/change-status-query.ts b/backend/src/modules/inventory/shipment/dto/change-status-query.ts new file mode 100644 index 0000000..0cff97f --- /dev/null +++ b/backend/src/modules/inventory/shipment/dto/change-status-query.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class ChangeStatusQuery { + @ApiPropertyOptional({ description: 'Return stocks to warehouse' }) + @IsOptional() + @IsBoolean() + returnStocks?: boolean; +} diff --git a/backend/src/modules/inventory/shipment/dto/index.ts b/backend/src/modules/inventory/shipment/dto/index.ts new file mode 100644 index 0000000..32eda3b --- /dev/null +++ b/backend/src/modules/inventory/shipment/dto/index.ts @@ -0,0 +1,5 @@ +export * from './change-status-query'; +export * from './shipment-filter.dto'; +export * from './shipment-item.dto'; +export * from './shipment-result.dto'; +export * from './shipment.dto'; diff --git a/backend/src/modules/inventory/shipment/dto/shipment-filter.dto.ts b/backend/src/modules/inventory/shipment/dto/shipment-filter.dto.ts new file mode 100644 index 0000000..560759a --- /dev/null +++ b/backend/src/modules/inventory/shipment/dto/shipment-filter.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { PagingQuery } from '@/common'; + +export class ShipmentFilterDto extends PagingQuery { + @ApiProperty({ description: 'Order ID' }) + @IsOptional() + @IsNumber() + orderId?: number; +} diff --git a/backend/src/modules/inventory/shipment/dto/shipment-item.dto.ts b/backend/src/modules/inventory/shipment/dto/shipment-item.dto.ts new file mode 100644 index 0000000..bba5aca --- /dev/null +++ b/backend/src/modules/inventory/shipment/dto/shipment-item.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class ShipmentItemDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + productId: number; + + @ApiProperty() + @IsNumber() + quantity: number; +} diff --git a/backend/src/modules/inventory/shipment/dto/shipment-result.dto.ts b/backend/src/modules/inventory/shipment/dto/shipment-result.dto.ts new file mode 100644 index 0000000..0d0c795 --- /dev/null +++ b/backend/src/modules/inventory/shipment/dto/shipment-result.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +import { PagingMeta } from '@/common'; +import { ShipmentDto } from './shipment.dto'; + +export class ShipmentResultDto { + @ApiProperty({ type: [ShipmentDto], description: 'List of shipments' }) + @IsArray() + shipments: ShipmentDto[]; + + @ApiProperty({ type: PagingMeta, description: 'Paging metadata' }) + meta: PagingMeta; +} diff --git a/backend/src/modules/inventory/shipment/dto/shipment.dto.ts b/backend/src/modules/inventory/shipment/dto/shipment.dto.ts new file mode 100644 index 0000000..3b86e0b --- /dev/null +++ b/backend/src/modules/inventory/shipment/dto/shipment.dto.ts @@ -0,0 +1,54 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto'; + +import { ShipmentItemDto } from './shipment-item.dto'; + +export class ShipmentDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + sectionId: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + name: string | null; + + @ApiProperty() + @IsNumber() + warehouseId: number; + + @ApiProperty() + @IsNumber() + orderId: number; + + @ApiProperty() + @IsNumber() + orderNumber: number; + + @ApiProperty() + @IsNumber() + statusId: number; + + @ApiProperty({ type: EntityInfoDto }) + @IsNotEmpty() + entityInfo: EntityInfoDto; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + shippedAt: string | null; + + @ApiProperty({ type: [ShipmentItemDto] }) + @IsArray() + items: ShipmentItemDto[]; +} diff --git a/backend/src/modules/inventory/shipment/entities/index.ts b/backend/src/modules/inventory/shipment/entities/index.ts new file mode 100644 index 0000000..8fbda92 --- /dev/null +++ b/backend/src/modules/inventory/shipment/entities/index.ts @@ -0,0 +1,2 @@ +export * from './shipment-item.entity'; +export * from './shipment.entity'; diff --git a/backend/src/modules/inventory/shipment/entities/shipment-item.entity.ts b/backend/src/modules/inventory/shipment/entities/shipment-item.entity.ts new file mode 100644 index 0000000..0ec8274 --- /dev/null +++ b/backend/src/modules/inventory/shipment/entities/shipment-item.entity.ts @@ -0,0 +1,32 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { ShipmentItemDto } from '../dto'; + +@Entity() +export class ShipmentItem { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + shipmentId: number; + + @Column() + productId: number; + + @Column() + quantity: number; + + constructor(accountId: number, shipmentId: number, productId: number, quantity: number) { + this.accountId = accountId; + this.shipmentId = shipmentId; + this.productId = productId; + this.quantity = quantity; + } + + public toDto(): ShipmentItemDto { + return { id: this.id, productId: this.productId, quantity: this.quantity }; + } +} diff --git a/backend/src/modules/inventory/shipment/entities/shipment.entity.ts b/backend/src/modules/inventory/shipment/entities/shipment.entity.ts new file mode 100644 index 0000000..112e287 --- /dev/null +++ b/backend/src/modules/inventory/shipment/entities/shipment.entity.ts @@ -0,0 +1,112 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { EntityInfoDto } from '@/modules/entity/entity-info/dto'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; + +import { ShipmentDto } from '../dto'; +import { ShipmentItem } from './shipment-item.entity'; + +@Entity() +export class Shipment implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column() + name: string; + + @Column() + warehouseId: number; + + @Column() + entityId: number; + + @Column() + orderId: number; + + @Column() + orderNumber: number; + + @Column() + statusId: number; + + @Column() + shippedAt: Date | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + sectionId: number, + name: string, + warehouseId: number, + entityId: number, + orderId: number, + orderNumber: number, + statusId: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.sectionId = sectionId; + this.name = name; + this.warehouseId = warehouseId; + this.entityId = entityId; + this.orderId = orderId; + this.orderNumber = orderNumber; + this.statusId = statusId; + this.shippedAt = null; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _items: ShipmentItem[] | null; + public get items(): ShipmentItem[] | null { + return this._items; + } + public set items(value: ShipmentItem[] | null) { + this._items = value; + } + + private _entityInfo: EntityInfoDto | null; + public get entityInfo(): EntityInfoDto | null { + return this._entityInfo; + } + public set entityInfo(value: EntityInfoDto | null) { + this._entityInfo = value; + } + + static getAuthorizable(sectionId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.ProductsShipment, id: sectionId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.ProductsShipment, + id: this.sectionId, + }; + } + + public toDto(): ShipmentDto { + return { + id: this.id, + sectionId: this.sectionId, + name: this.name, + warehouseId: this.warehouseId, + orderId: this.orderId, + orderNumber: this.orderNumber, + statusId: this.statusId, + createdAt: this.createdAt.toISOString(), + shippedAt: this.shippedAt ? this.shippedAt.toISOString() : null, + items: this.items ? this.items.map((item) => item.toDto()) : [], + entityInfo: this.entityInfo, + }; + } +} diff --git a/backend/src/modules/inventory/shipment/shipment.controller.ts b/backend/src/modules/inventory/shipment/shipment.controller.ts new file mode 100644 index 0000000..410f647 --- /dev/null +++ b/backend/src/modules/inventory/shipment/shipment.controller.ts @@ -0,0 +1,52 @@ +import { Controller, Get, Param, ParseIntPipe, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ChangeStatusQuery, ShipmentDto, ShipmentFilterDto, ShipmentResultDto } from './dto'; +import { ShipmentService } from './shipment.service'; + +@ApiTags('inventory/shipments') +@Controller('products/sections/:sectionId/shipments') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ShipmentController { + constructor(private readonly service: ShipmentService) {} + + @ApiCreatedResponse({ description: 'Get shipment by id', type: ShipmentDto }) + @Get('/:shipmentId') + public async getShipment( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('shipmentId', ParseIntPipe) shipmentId: number, + ) { + return this.service.findOne({ accountId, user, filter: { sectionId, shipmentId } }); + } + + @ApiCreatedResponse({ description: 'Get shipments', type: ShipmentResultDto }) + @Get() + public async getShipments( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Query() filter: ShipmentFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.getShipments({ accountId, user, sectionId, orderId: filter.orderId, paging }); + } + + @ApiCreatedResponse({ description: 'Change shipment status', type: ShipmentDto }) + @Put('/:shipmentId/status/:statusId') + public async changeStatus( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('shipmentId', ParseIntPipe) shipmentId: number, + @Param('statusId', ParseIntPipe) statusId: number, + @Query() query: ChangeStatusQuery, + ) { + return this.service.changeStatus(accountId, user, sectionId, shipmentId, statusId, query); + } +} diff --git a/backend/src/modules/inventory/shipment/shipment.module.ts b/backend/src/modules/inventory/shipment/shipment.module.ts new file mode 100644 index 0000000..57740ec --- /dev/null +++ b/backend/src/modules/inventory/shipment/shipment.module.ts @@ -0,0 +1,30 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; + +import { OrderStatusModule } from '../order-status/order-status.module'; +import { ReservationModule } from '../reservation/reservation.module'; +import { ProductStockModule } from '../product-stock/product-stock.module'; +import { WarehouseModule } from '../warehouse/warehouse.module'; + +import { Shipment, ShipmentItem } from './entities'; +import { ShipmentService } from './shipment.service'; +import { ShipmentController } from './shipment.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ShipmentItem, Shipment]), + IAMModule, + EntityInfoModule, + OrderStatusModule, + forwardRef(() => ReservationModule), + forwardRef(() => ProductStockModule), + forwardRef(() => WarehouseModule), + ], + controllers: [ShipmentController], + providers: [ShipmentService], + exports: [ShipmentService], +}) +export class ShipmentModule {} diff --git a/backend/src/modules/inventory/shipment/shipment.service.ts b/backend/src/modules/inventory/shipment/shipment.service.ts new file mode 100644 index 0000000..7247b63 --- /dev/null +++ b/backend/src/modules/inventory/shipment/shipment.service.ts @@ -0,0 +1,362 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Repository } from 'typeorm'; + +import { DateUtil, NotFoundError, PagingQuery } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; + +import { ProductsEventType, ShipmentDeletedEvent, ShipmentCreatedEvent, ShipmentStatusChangedEvent } from '../common'; + +import { Order, OrderItem } from '../order/entities'; +import { OrderStatusCode } from '../order-status/enums'; +import { OrderStatus } from '../order-status/entities'; +import { OrderStatusService } from '../order-status/order-status.service'; +import { ProductStockService } from '../product-stock/product-stock.service'; +import { WarehouseService } from '../warehouse/warehouse.service'; + +import { Shipment, ShipmentItem } from './entities'; +import { ShipmentResult } from './types'; + +interface FindFilter { + sectionId?: number; + warehouseId?: number | number[]; + orderId?: number | number[]; + entityId?: number; + shipmentId?: number | number[]; +} +interface CancelOptions { + returnStocks?: boolean; +} +type DeleteOptions = { newWarehouseId?: number } & CancelOptions; + +@Injectable() +export class ShipmentService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Shipment) + private readonly repository: Repository, + @InjectRepository(ShipmentItem) + private readonly itemRepository: Repository, + private readonly authService: AuthorizationService, + private readonly orderStatusService: OrderStatusService, + private readonly entityInfoService: EntityInfoService, + @Inject(forwardRef(() => ProductStockService)) + private readonly stockService: ProductStockService, + @Inject(forwardRef(() => WarehouseService)) + private readonly warehouseService: WarehouseService, + ) {} + + public async findOne({ accountId, user, filter }: { accountId: number; user: User | null; filter: FindFilter }) { + const shipments = await this.getAuthorizedShipments(accountId, user, filter); + const shipment = shipments[0]; + if (!shipment) return null; + shipment.entityInfo = await this.entityInfoService.findOne({ + accountId: shipment.accountId, + user, + entityId: shipment.entityId, + }); + return shipment; + } + + public async findMany({ accountId, user, filter }: { accountId: number; user: User | null; filter: FindFilter }) { + const shipments = await this.getAuthorizedShipments(accountId, user, filter); + await Promise.all( + shipments.map(async (shipment) => { + shipment.entityInfo = await this.entityInfoService.findOne({ + accountId: shipment.accountId, + user, + entityId: shipment.entityId, + }); + }), + ); + return shipments; + } + + private async getAuthorizedShipments(accountId: number, user: User | null, filter: FindFilter) { + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId, sectionId: filter.sectionId, warehouseId: filter.warehouseId, onlyAvailable: true }, + }); + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + const shipments = await this.createQb(accountId, filter, true).getMany(); + return user + ? shipments.filter( + async (shipment) => await this.authService.check({ action: 'view', user, authorizable: shipment }), + ) + : shipments; + } + + public async getShipments({ + accountId, + user, + sectionId, + orderId, + paging, + }: { + accountId: number; + user: User; + sectionId: number; + orderId?: number; + paging: PagingQuery; + }): Promise { + await this.authService.check({ + action: 'view', + user, + authorizable: Shipment.getAuthorizable(sectionId), + throwError: true, + }); + + const filter: FindFilter = { sectionId, orderId }; + if (user) { + const warehouses = await this.warehouseService.findMany({ + user, + filter: { accountId, sectionId, onlyAvailable: true }, + }); + filter.warehouseId = warehouses?.length ? warehouses.map((w) => w.id) : undefined; + } + + const qb = this.createQb(accountId, filter, true) + .orderBy('shipment.created_at', 'DESC') + .addOrderBy('shipment.id', 'DESC') + .limit(paging.take) + .offset(paging.skip); + + const [shipments, total] = await qb.getManyAndCount(); + + await Promise.all( + shipments.map(async (shipment) => { + shipment.entityInfo = await this.entityInfoService.findOne({ + accountId: shipment.accountId, + user, + entityId: shipment.entityId, + }); + }), + ); + + return new ShipmentResult(shipments, paging.skip, total); + } + + public async changeStatus( + accountId: number, + user: User, + sectionId: number, + shipmentId: number, + statusId: number, + options?: CancelOptions, + ): Promise { + const shipment = await this.findOne({ accountId, user, filter: { sectionId, shipmentId } }); + if (!shipment) { + throw NotFoundError.withId(Shipment, shipmentId); + } + + await this.authService.check({ action: 'edit', user, authorizable: shipment, throwError: true }); + + if (shipment.statusId !== statusId) { + const status = await this.orderStatusService.findOne(accountId, { statusId }); + await this.processShipment(accountId, sectionId, shipment, status, options); + } + + return shipment; + } + + public async delete(accountId: number, filter: FindFilter, options?: DeleteOptions) { + if (options?.newWarehouseId) { + await this.createQb(accountId, filter).update({ warehouseId: options.newWarehouseId }).execute(); + } else { + const shipments = await this.createQb(accountId, filter, true).getMany(); + if (options?.returnStocks) { + await Promise.all( + shipments.map(async (shipment) => { + const status = await this.orderStatusService.findOne(accountId, { statusId: shipment.statusId }); + if (status.code === OrderStatusCode.Shipped) { + await this.increaseStocks({ accountId, shipment }); + } + }), + ); + } + await this.createQb(accountId, filter).delete().execute(); + shipments.forEach((shipment) => + this.eventEmitter.emit( + ProductsEventType.ShipmentDeleted, + new ShipmentDeletedEvent({ + accountId, + sectionId: shipment.sectionId, + orderId: shipment.orderId, + shipmentId: shipment.id, + entityId: shipment.entityId, + }), + ), + ); + } + } + + public async processOrder( + accountId: number, + sectionId: number, + order: Order, + status: OrderStatus, + options?: CancelOptions, + ) { + if (status.code === OrderStatusCode.SentForShipment) { + await this.createForOrder(accountId, sectionId, order); + } else { + await this.processOrderShipments(accountId, sectionId, order.id, status, options); + } + } + + private createQb(accountId: number, filter: FindFilter, includeItems = false) { + const qb = this.repository.createQueryBuilder('shipment').where('shipment.account_id = :accountId', { accountId }); + if (includeItems) { + qb.leftJoinAndMapMany('shipment.items', ShipmentItem, 'shipment_item', 'shipment_item.shipment_id = shipment.id'); + } + if (filter.sectionId) { + qb.andWhere('shipment.section_id = :sectionId', { sectionId: filter.sectionId }); + } + if (filter.warehouseId) { + if (Array.isArray(filter.warehouseId)) { + qb.andWhere('shipment.warehouse_id IN (:...warehouseIds)', { warehouseIds: filter.warehouseId }); + } else { + qb.andWhere('shipment.warehouse_id = :warehouseId', { warehouseId: filter.warehouseId }); + } + } + if (filter.orderId) { + qb.andWhere('shipment.order_id IN (:...orderIds)', { + orderIds: Array.isArray(filter.orderId) ? filter.orderId : [filter.orderId], + }); + } + if (filter.entityId) { + qb.andWhere('shipment.entity_id = :entityId', { entityId: filter.entityId }); + } + if (filter.shipmentId) { + if (Array.isArray(filter.shipmentId)) { + qb.andWhere('shipment.id IN (:...shipmentIds)', { shipmentIds: filter.shipmentId }); + } else { + qb.andWhere('shipment.id = :shipmentId', { shipmentId: filter.shipmentId }); + } + } + return qb; + } + + private async createForOrder(accountId: number, sectionId: number, order: Order): Promise { + await this.createQb(accountId, { sectionId, orderId: order.id }).delete().execute(); + const shipments = new Map(); + for (const item of order.items) { + await this.createForOrderItem(accountId, sectionId, order, item, shipments); + } + return Array.from(shipments.values()); + } + + private async createForOrderItem( + accountId: number, + sectionId: number, + order: Order, + orderItem: OrderItem, + shipments: Map, + ) { + for (const reservation of orderItem.reservations) { + if (!shipments.has(reservation.warehouseId)) { + const shipment = await this.repository.save( + new Shipment( + accountId, + sectionId, + `Shipment for order #${order.id}`, + reservation.warehouseId, + order.entityId, + order.id, + order.orderNumber, + order.statusId, + ), + ); + shipments.set(reservation.warehouseId, shipment); + this.eventEmitter.emit( + ProductsEventType.ShipmentCreated, + new ShipmentCreatedEvent({ + accountId, + sectionId, + orderId: order.id, + shipmentId: shipment.id, + entityId: order.entityId, + createdAt: shipment.createdAt.toISOString(), + }), + ); + } + + await this.itemRepository.save( + new ShipmentItem( + accountId, + shipments.get(reservation.warehouseId).id, + orderItem.productId, + reservation.quantity, + ), + ); + } + } + + private async processOrderShipments( + accountId: number, + sectionId: number, + orderId: number, + status: OrderStatus, + options?: CancelOptions, + ) { + const shipments = await this.findMany({ accountId, user: null, filter: { sectionId, orderId } }); + await Promise.all( + shipments + .filter((shipment) => shipment.statusId !== status.id) + .map((shipment) => this.processShipment(accountId, sectionId, shipment, status, options)), + ); + } + + private async processShipment( + accountId: number, + sectionId: number, + shipment: Shipment, + status: OrderStatus, + options?: CancelOptions, + ) { + if (status.code === OrderStatusCode.Shipped) { + shipment.shippedAt = DateUtil.now(); + await this.reduceStocks({ accountId, shipment }); + } else if ([OrderStatusCode.Returned, OrderStatusCode.Cancelled].includes(status.code)) { + shipment.shippedAt = null; + if (options?.returnStocks) { + await this.increaseStocks({ accountId, shipment }); + } + } + shipment.statusId = status.id; + await this.repository.save(shipment); + + this.eventEmitter.emit( + ProductsEventType.ShipmentStatusChanged, + new ShipmentStatusChangedEvent({ + accountId, + sectionId, + orderId: shipment.orderId, + shipmentId: shipment.id, + statusId: shipment.statusId, + }), + ); + } + + private async reduceStocks({ accountId, shipment }: { accountId: number; shipment: Shipment }) { + await Promise.all( + shipment.items.map(({ productId, quantity }) => + this.stockService.reduce({ accountId, warehouseId: shipment.warehouseId, productId, quantity }), + ), + ); + } + + private async increaseStocks({ accountId, shipment }: { accountId: number; shipment: Shipment }) { + await Promise.all( + shipment.items.map(({ productId, quantity }) => + this.stockService.increase({ accountId, warehouseId: shipment.warehouseId, productId, quantity }), + ), + ); + } +} diff --git a/backend/src/modules/inventory/shipment/types/index.ts b/backend/src/modules/inventory/shipment/types/index.ts new file mode 100644 index 0000000..151d9cc --- /dev/null +++ b/backend/src/modules/inventory/shipment/types/index.ts @@ -0,0 +1 @@ +export * from './shipment-result'; diff --git a/backend/src/modules/inventory/shipment/types/shipment-result.ts b/backend/src/modules/inventory/shipment/types/shipment-result.ts new file mode 100644 index 0000000..87d34ec --- /dev/null +++ b/backend/src/modules/inventory/shipment/types/shipment-result.ts @@ -0,0 +1,23 @@ +import { PagingMeta } from '@/common'; + +import { ShipmentResultDto } from '../dto'; +import { Shipment } from '../entities'; + +export class ShipmentResult { + shipments: Shipment[]; + offset: number; + total: number; + + constructor(shipments: Shipment[], offset: number, total: number) { + this.shipments = shipments; + this.offset = offset; + this.total = total; + } + + public toDto(): ShipmentResultDto { + return { + shipments: this.shipments.map((shipment) => shipment.toDto()), + meta: new PagingMeta(this.offset, this.total), + }; + } +} diff --git a/backend/src/modules/inventory/warehouse/dto/create-warehouse.dto.ts b/backend/src/modules/inventory/warehouse/dto/create-warehouse.dto.ts new file mode 100644 index 0000000..ae9dd13 --- /dev/null +++ b/backend/src/modules/inventory/warehouse/dto/create-warehouse.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { WarehouseDto } from './warehouse.dto'; + +export class CreateWarehouseDto extends PickType(WarehouseDto, ['name'] as const) {} diff --git a/backend/src/modules/inventory/warehouse/dto/index.ts b/backend/src/modules/inventory/warehouse/dto/index.ts new file mode 100644 index 0000000..192c60c --- /dev/null +++ b/backend/src/modules/inventory/warehouse/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-warehouse.dto'; +export * from './update-warehouse.dto'; +export * from './warehouse.dto'; diff --git a/backend/src/modules/inventory/warehouse/dto/update-warehouse.dto.ts b/backend/src/modules/inventory/warehouse/dto/update-warehouse.dto.ts new file mode 100644 index 0000000..0482c06 --- /dev/null +++ b/backend/src/modules/inventory/warehouse/dto/update-warehouse.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { WarehouseDto } from './warehouse.dto'; + +export class UpdateWarehouseDto extends PickType(WarehouseDto, ['name'] as const) {} diff --git a/backend/src/modules/inventory/warehouse/dto/warehouse.dto.ts b/backend/src/modules/inventory/warehouse/dto/warehouse.dto.ts new file mode 100644 index 0000000..67afc52 --- /dev/null +++ b/backend/src/modules/inventory/warehouse/dto/warehouse.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common'; + +export class WarehouseDto { + @ApiProperty({ description: 'Warehouse ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Section ID' }) + @IsNumber() + sectionId: number; + + @ApiProperty({ description: 'Warehouse name' }) + @IsString() + name: string; + + @ApiProperty({ type: UserRights, description: 'User rights' }) + userRights: UserRights; +} diff --git a/backend/src/modules/inventory/warehouse/entities/index.ts b/backend/src/modules/inventory/warehouse/entities/index.ts new file mode 100644 index 0000000..015b6f0 --- /dev/null +++ b/backend/src/modules/inventory/warehouse/entities/index.ts @@ -0,0 +1 @@ +export * from './warehouse.entity'; diff --git a/backend/src/modules/inventory/warehouse/entities/warehouse.entity.ts b/backend/src/modules/inventory/warehouse/entities/warehouse.entity.ts new file mode 100644 index 0000000..8e6a82c --- /dev/null +++ b/backend/src/modules/inventory/warehouse/entities/warehouse.entity.ts @@ -0,0 +1,84 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; +import { Authorizable, AuthorizableObject, SimpleAuthorizable, UserRights } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; +import { CreateWarehouseDto, UpdateWarehouseDto, WarehouseDto } from '../dto'; + +@Entity() +export class Warehouse implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sectionId: number; + + @Column() + name: string; + + @Column() + createdBy: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, sectionId: number, name: string, createdBy: number, createdAt?: Date) { + this.accountId = accountId; + this.sectionId = sectionId; + this.name = name; + this.createdBy = createdBy; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _userRights: UserRights | null; + public get userRights(): UserRights { + return this._userRights ?? UserRights.full(); + } + public set userRights(value: UserRights | null) { + this._userRights = value; + } + + static getAuthorizable(warehouseId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Warehouse, id: warehouseId }); + } + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Warehouse, + id: this.id, + createdBy: this.createdBy, + }; + } + + public static fromDto({ + accountId, + sectionId, + createdBy, + dto, + }: { + accountId: number; + sectionId: number; + createdBy: number; + dto: CreateWarehouseDto; + }): Warehouse { + return new Warehouse(accountId, sectionId, dto.name, createdBy); + } + + public update(dto: UpdateWarehouseDto): Warehouse { + this.name = dto.name !== undefined ? dto.name : this.name; + + return this; + } + + public toDto(): WarehouseDto { + return { + id: this.id, + sectionId: this.sectionId, + name: this.name, + userRights: this.userRights, + }; + } +} diff --git a/backend/src/modules/inventory/warehouse/warehouse.controller.ts b/backend/src/modules/inventory/warehouse/warehouse.controller.ts new file mode 100644 index 0000000..6e24c30 --- /dev/null +++ b/backend/src/modules/inventory/warehouse/warehouse.controller.ts @@ -0,0 +1,70 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { WarehouseDto, CreateWarehouseDto, UpdateWarehouseDto } from './dto'; +import { WarehouseService } from './warehouse.service'; + +@ApiTags('inventory/warehouses') +@Controller('products/sections/:sectionId/warehouses') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class WarehouseController { + constructor(private readonly service: WarehouseService) {} + + @ApiOperation({ summary: 'Create warehouse', description: 'Create warehouse in inventory section' }) + @ApiParam({ name: 'sectionId', type: Number, required: true, description: 'Section ID' }) + @ApiBody({ type: CreateWarehouseDto, required: true, description: 'Warehouse data' }) + @ApiCreatedResponse({ description: 'Warehouse', type: WarehouseDto }) + @Post() + public async create( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Body() dto: CreateWarehouseDto, + ) { + return this.service.create({ accountId, user, sectionId, dto }); + } + + @ApiOperation({ summary: 'Get warehouses', description: 'Get available warehouses in inventory section' }) + @ApiParam({ name: 'sectionId', type: Number, required: true, description: 'Section ID' }) + @ApiOkResponse({ description: 'Warehouses', type: [WarehouseDto] }) + @Get() + public async findMany( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + ) { + return this.service.findMany({ user, filter: { accountId, sectionId } }); + } + + @ApiOperation({ summary: 'Update warehouse', description: 'Update warehouse in inventory section' }) + @ApiParam({ name: 'sectionId', type: Number, required: true, description: 'Section ID' }) + @ApiParam({ name: 'warehouseId', type: Number, required: true, description: 'Warehouse ID' }) + @ApiBody({ type: UpdateWarehouseDto, required: true, description: 'Warehouse data' }) + @ApiOkResponse({ description: 'Warehouse', type: WarehouseDto }) + @Put(':warehouseId') + public async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('warehouseId', ParseIntPipe) warehouseId: number, + @Body() dto: UpdateWarehouseDto, + ) { + return this.service.update({ accountId, user, sectionId, warehouseId, dto }); + } + + @ApiOperation({ summary: 'Delete warehouse', description: 'Delete warehouse in inventory section' }) + @ApiParam({ name: 'sectionId', type: Number, required: true, description: 'Section ID' }) + @ApiParam({ name: 'warehouseId', type: Number, required: true, description: 'Warehouse ID' }) + @ApiQuery({ name: 'newWarehouseId', type: Number, required: false, description: 'New warehouse ID' }) + @ApiOkResponse() + @Delete(':warehouseId') + public async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('sectionId', ParseIntPipe) sectionId: number, + @Param('warehouseId', ParseIntPipe) warehouseId: number, + @Query('newWarehouseId') newWarehouseId?: number, + ) { + return this.service.delete({ accountId, user, sectionId, warehouseId, newWarehouseId }); + } +} diff --git a/backend/src/modules/inventory/warehouse/warehouse.module.ts b/backend/src/modules/inventory/warehouse/warehouse.module.ts new file mode 100644 index 0000000..3057452 --- /dev/null +++ b/backend/src/modules/inventory/warehouse/warehouse.module.ts @@ -0,0 +1,30 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { OrderModule } from '../order/order.module'; +import { ProductStockModule } from '../product-stock/product-stock.module'; +import { ReservationModule } from '../reservation/reservation.module'; +import { ShipmentModule } from '../shipment/shipment.module'; +import { RentalOrderModule } from '../rental-order/rental-order.module'; + +import { Warehouse } from './entities/warehouse.entity'; +import { WarehouseService } from './warehouse.service'; +import { WarehouseController } from './warehouse.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Warehouse]), + IAMModule, + forwardRef(() => OrderModule), + forwardRef(() => ProductStockModule), + ReservationModule, + ShipmentModule, + forwardRef(() => RentalOrderModule), + ], + controllers: [WarehouseController], + providers: [WarehouseService], + exports: [WarehouseService], +}) +export class WarehouseModule {} diff --git a/backend/src/modules/inventory/warehouse/warehouse.service.ts b/backend/src/modules/inventory/warehouse/warehouse.service.ts new file mode 100644 index 0000000..65676bd --- /dev/null +++ b/backend/src/modules/inventory/warehouse/warehouse.service.ts @@ -0,0 +1,170 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { ForbiddenError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { OrderService } from '../order/services/order.service'; +import { ProductsSection } from '../products-section/entities'; +import { ProductStockService } from '../product-stock/product-stock.service'; +import { RentalOrderService } from '../rental-order/services/rental-order.service'; + +import { CreateWarehouseDto, UpdateWarehouseDto } from './dto'; +import { Warehouse } from './entities'; + +interface FindFilter { + accountId: number; + sectionId?: number; + warehouseId?: number | number[]; + onlyAvailable?: boolean; +} + +@Injectable() +export class WarehouseService { + constructor( + @InjectRepository(Warehouse) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + @Inject(forwardRef(() => OrderService)) + private readonly orderService: OrderService, + @Inject(forwardRef(() => ProductStockService)) + private readonly stockService: ProductStockService, + @Inject(forwardRef(() => RentalOrderService)) + private readonly rentalOrderService: RentalOrderService, + ) {} + + public async create({ + accountId, + user, + sectionId, + dto, + }: { + accountId: number; + user: User; + sectionId: number; + dto: CreateWarehouseDto; + }): Promise { + await this.authService.check({ + action: 'create', + user, + authorizable: ProductsSection.getAuthorizable(sectionId), + throwError: true, + }); + + return this.repository.save(Warehouse.fromDto({ accountId, sectionId, createdBy: user.id, dto })); + } + + public async findOne({ user, filter }: { user: User; filter: FindFilter }): Promise { + if (filter.sectionId) { + await this.authService.check({ + action: 'view', + user, + authorizable: ProductsSection.getAuthorizable(filter.sectionId), + throwError: true, + }); + } + + const warehouse = await this.createFindQb(filter).getOne(); + if (warehouse) warehouse.userRights = await this.authService.getUserRights({ user, authorizable: warehouse }); + + return filter.onlyAvailable && !warehouse.userRights.canView ? null : warehouse; + } + public async findMany({ user, filter }: { user: User; filter: FindFilter }): Promise { + if (filter.sectionId) { + await this.authService.check({ + action: 'view', + user, + authorizable: ProductsSection.getAuthorizable(filter.sectionId), + throwError: true, + }); + } + + const warehouses = await this.createFindQb(filter).orderBy({ name: 'ASC' }).getMany(); + await Promise.all( + warehouses.map( + async (warehouse) => + (warehouse.userRights = await this.authService.getUserRights({ user, authorizable: warehouse })), + ), + ); + + return filter.onlyAvailable ? warehouses.filter((warehouse) => warehouse.userRights.canView) : warehouses; + } + + public async update({ + accountId, + user, + sectionId, + warehouseId, + dto, + }: { + accountId: number; + user: User; + sectionId: number; + warehouseId: number; + dto: UpdateWarehouseDto; + }): Promise { + await this.authService.check({ + action: 'edit', + user, + authorizable: ProductsSection.getAuthorizable(sectionId), + throwError: true, + }); + + const warehouse = await this.findOne({ user, filter: { accountId, sectionId, warehouseId } }); + if (!warehouse.userRights.canEdit) { + throw new ForbiddenError(); + } + + await this.repository.save(warehouse.update(dto)); + + return warehouse; + } + + public async delete({ + accountId, + user, + sectionId, + warehouseId, + newWarehouseId, + }: { + accountId: number; + user: User; + sectionId: number; + warehouseId: number; + newWarehouseId?: number; + }): Promise { + await this.authService.check({ + action: 'delete', + user, + authorizable: ProductsSection.getAuthorizable(sectionId), + throwError: true, + }); + + const warehouse = await this.findOne({ user, filter: { accountId, sectionId, warehouseId } }); + if (!warehouse.userRights.canDelete) { + throw new ForbiddenError(); + } + + await this.orderService.delete(accountId, { sectionId, warehouseId }, { newWarehouseId }); + await this.stockService.delete({ accountId, warehouseId, newWarehouseId }); + await this.rentalOrderService.delete(accountId, user, { sectionId, warehouseId }, { newWarehouseId }); + + await this.repository.delete({ accountId, sectionId, id: warehouseId }); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where({ accountId: filter.accountId }); + if (filter.sectionId) qb.andWhere({ sectionId: filter.sectionId }); + if (filter.warehouseId) { + if (Array.isArray(filter.warehouseId)) { + qb.andWhere({ id: In(filter.warehouseId) }); + } else { + qb.andWhere({ id: filter.warehouseId }); + } + } + return qb; + } +} diff --git a/backend/src/modules/mail/mail-message-scheduled/dto/create-mail-message-scheduled.dto.ts b/backend/src/modules/mail/mail-message-scheduled/dto/create-mail-message-scheduled.dto.ts new file mode 100644 index 0000000..cce42a0 --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/dto/create-mail-message-scheduled.dto.ts @@ -0,0 +1,12 @@ +import { PickType } from '@nestjs/swagger'; + +import { MailMessageScheduledDto } from './mail-message-scheduled.dto'; + +export class CreateMailMessageScheduledDto extends PickType(MailMessageScheduledDto, [ + 'mailboxId', + 'sendFrom', + 'subject', + 'content', + 'sendTo', + 'entityId', +] as const) {} diff --git a/backend/src/modules/mail/mail-message-scheduled/dto/index.ts b/backend/src/modules/mail/mail-message-scheduled/dto/index.ts new file mode 100644 index 0000000..c8ebe9b --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-mail-message-scheduled.dto'; +export * from './mail-message-scheduled-filter.dto'; +export * from './mail-message-scheduled.dto'; diff --git a/backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled-filter.dto.ts b/backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled-filter.dto.ts new file mode 100644 index 0000000..6ac9e95 --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled-filter.dto.ts @@ -0,0 +1,49 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +export class MailMessageScheduledFilterDto { + @ApiPropertyOptional({ description: 'Mailbox IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + mailboxId?: number[]; + + @ApiPropertyOptional({ description: 'User IDs who sent the message' }) + @IsOptional() + @IsNumber({}, { each: true }) + sendFrom?: number[]; + + @ApiPropertyOptional({ description: 'Entity IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + entityId?: number[]; + + @ApiPropertyOptional({ description: 'Is the message sent' }) + @IsOptional() + @IsBoolean() + isSent?: boolean; + + @ApiPropertyOptional({ description: 'Date and time when the message was sent' }) + @IsOptional() + sentAt?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Date and time when the message was created' }) + @IsOptional() + createdAt?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Message subject' }) + @IsOptional() + @IsString() + subject?: string; + + @ApiPropertyOptional({ description: 'Message content' }) + @IsOptional() + @IsString() + content?: string; + + @ApiPropertyOptional({ description: 'Recipient email address' }) + @IsOptional() + @IsString() + sentTo?: string; +} diff --git a/backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled.dto.ts b/backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled.dto.ts new file mode 100644 index 0000000..1df8b09 --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/dto/mail-message-scheduled.dto.ts @@ -0,0 +1,41 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class MailMessageScheduledDto { + @ApiProperty({ description: 'Scheduled message ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'User ID who sent the message' }) + @IsNumber() + sendFrom: number; + + @ApiProperty({ description: 'Date and time when the message was created' }) + @IsString() + createdAt: string; + + @ApiProperty({ description: 'Mailbox ID' }) + @IsNumber() + mailboxId: number; + + @ApiProperty({ description: 'Message subject' }) + @IsString() + subject: string; + + @ApiProperty({ description: 'Message content' }) + @IsString() + content: string; + + @ApiProperty({ description: 'Array of recipient email addresses' }) + @IsString({ each: true }) + sendTo: string[]; + + @ApiPropertyOptional({ description: 'Entity ID associated with the message' }) + @IsNumber() + entityId: number; + + @ApiPropertyOptional({ description: 'Date and time when the message was sent', nullable: true }) + @IsOptional() + @IsNumber() + sentAt?: string | null; +} diff --git a/backend/src/modules/mail/mail-message-scheduled/entities/index.ts b/backend/src/modules/mail/mail-message-scheduled/entities/index.ts new file mode 100644 index 0000000..fa6155a --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/entities/index.ts @@ -0,0 +1 @@ +export * from './mail-message-scheduled.entity'; diff --git a/backend/src/modules/mail/mail-message-scheduled/entities/mail-message-scheduled.entity.ts b/backend/src/modules/mail/mail-message-scheduled/entities/mail-message-scheduled.entity.ts new file mode 100644 index 0000000..be2e90d --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/entities/mail-message-scheduled.entity.ts @@ -0,0 +1,84 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { CreateMailMessageScheduledDto, MailMessageScheduledDto } from '../dto'; + +@Entity() +export class MailMessageScheduled { + @PrimaryGeneratedColumn('identity') + id: number | undefined; + + @Column() + accountId: number; + + @Column() + sendFrom: number; + + @Column() + createdAt: Date; + + @Column() + mailboxId: number; + + @Column() + subject: string; + + @Column() + content: string; + + @Column({ type: 'simple-array' }) + sendTo: string[]; + + @Column() + entityId: number; + + @Column({ nullable: true }) + sentAt: Date | null; + + constructor( + accountId: number, + sendFrom: number, + mailboxId: number, + subject: string, + content: string, + sendTo: string[], + entityId: number, + createdAt: Date = new Date(), + sentAt: Date | null = null, + ) { + this.accountId = accountId; + this.sendFrom = sendFrom; + this.createdAt = createdAt; + this.mailboxId = mailboxId; + this.subject = subject; + this.content = content; + this.sendTo = sendTo; + this.entityId = entityId; + this.sentAt = sentAt; + } + + public static fromDto(accountId: number, dto: CreateMailMessageScheduledDto): MailMessageScheduled { + return new MailMessageScheduled( + accountId, + dto.sendFrom, + dto.mailboxId, + dto.subject, + dto.content, + dto.sendTo, + dto.entityId, + ); + } + + public toDto(): MailMessageScheduledDto { + return { + id: this.id, + sendFrom: this.sendFrom, + createdAt: this.createdAt.toISOString(), + mailboxId: this.mailboxId, + subject: this.subject, + content: this.content, + sendTo: this.sendTo, + entityId: this.entityId, + sentAt: this.sentAt?.toISOString(), + }; + } +} diff --git a/backend/src/modules/mail/mail-message-scheduled/index.ts b/backend/src/modules/mail/mail-message-scheduled/index.ts new file mode 100644 index 0000000..5a00c3c --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/index.ts @@ -0,0 +1 @@ +export * from './mail-message-scheduled.module'; diff --git a/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.controller.ts b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.controller.ts new file mode 100644 index 0000000..01c8c99 --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.controller.ts @@ -0,0 +1,67 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CreateMailMessageScheduledDto, MailMessageScheduledDto, MailMessageScheduledFilterDto } from './dto'; +import { MailMessageScheduledService } from './mail-message-scheduled.service'; + +@ApiTags('mail/scheduled') +@Controller('mail/scheduled') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class MailMessageScheduledController { + constructor(private readonly service: MailMessageScheduledService) {} + + @ApiOperation({ summary: 'Create scheduled message', description: 'Create scheduled message' }) + @ApiBody({ description: 'Data for creating scheduled message', type: CreateMailMessageScheduledDto }) + @ApiCreatedResponse({ description: 'Scheduled message', type: MailMessageScheduledDto }) + @Post() + async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateMailMessageScheduledDto) { + return this.service.create({ accountId, dto }); + } + + @ApiOperation({ summary: 'Get scheduled messages', description: 'Get scheduled messages' }) + @ApiOkResponse({ description: 'Scheduled messages', type: [MailMessageScheduledDto] }) + @Get() + async findMany(@CurrentAuth() { accountId }: AuthData, @Query() paging: PagingQuery) { + return this.service.findMany({ filter: { accountId }, paging }); + } + + @ApiOperation({ summary: 'Get scheduled message', description: 'Get scheduled message' }) + @ApiParam({ name: 'messageId', description: 'Scheduled message ID' }) + @ApiOkResponse({ description: 'Scheduled message', type: MailMessageScheduledDto }) + @Get(':messageId') + async findOne(@CurrentAuth() { accountId }: AuthData, @Param('messageId', ParseIntPipe) messageId: number) { + return this.service.findOne({ accountId, messageId }); + } + + @ApiOperation({ summary: 'Search scheduled messages', description: 'Search scheduled messages' }) + @ApiBody({ description: 'Data for searching scheduled messages', type: MailMessageScheduledFilterDto }) + @ApiOkResponse({ description: 'Scheduled messages', type: [MailMessageScheduledDto] }) + @Post('search') + async search( + @CurrentAuth() { accountId }: AuthData, + @Body() filter: MailMessageScheduledFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.findMany({ filter: { accountId, ...filter }, paging }); + } + + @ApiOperation({ summary: 'Delete scheduled message', description: 'Delete scheduled message' }) + @ApiParam({ name: 'messageId', description: 'Scheduled message ID' }) + @ApiOkResponse() + @Delete(':messageId') + async delete(@CurrentAuth() { accountId }: AuthData, @Param('messageId', ParseIntPipe) messageId: number) { + return this.service.delete({ accountId, messageId }); + } + + @ApiOperation({ summary: 'Delete scheduled messages', description: 'Delete scheduled messages' }) + @ApiBody({ description: 'Data for deleting scheduled messages', type: MailMessageScheduledFilterDto }) + @ApiOkResponse() + @Post('delete') + async deleteMany(@CurrentAuth() { accountId }: AuthData, @Body() filter: MailMessageScheduledFilterDto) { + return this.service.delete({ accountId, ...filter }); + } +} diff --git a/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.handler.ts b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.handler.ts new file mode 100644 index 0000000..44a6716 --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.handler.ts @@ -0,0 +1,69 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { Cron, CronExpression } from '@nestjs/schedule'; + +import { ActionEmailSendSettings, AutomationJob, EntityTypeActionType, OnAutomationJob } from '@/modules/automation'; +import { MailboxEvent, MailEventType } from '@/Mailing/common'; + +import { MailMessageScheduledService } from './mail-message-scheduled.service'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; +} + +interface SendEmailVariables { + accountId?: number | null; + entity?: EntityVariables | null; + emailSettings?: ActionEmailSendSettings | null; +} + +@Injectable() +export class MailMessageScheduledHandler { + private readonly logger = new Logger(MailMessageScheduledHandler.name); + constructor(private readonly service: MailMessageScheduledService) {} + + @Cron(CronExpression.EVERY_MINUTE) + async sendScheduledMessages() { + if (process.env.SCHEDULE_MAIL_SCHEDULED_DISABLE === 'true') return; + this.logger.log('Before: Sending scheduled messages'); + const processed = await this.service.processMessages(); + this.logger.log(`After: Sending scheduled messages. Processed: ${processed}`); + } + + @OnEvent(MailEventType.MailboxDeleted, { async: true }) + async onMailboxDeleted(event: MailboxEvent) { + this.service.delete({ accountId: event.accountId, mailboxId: event.mailboxId, isSent: false }); + } + + /** + * @deprecated use new @see handleSendEmailJob instead + */ + @OnAutomationJob(EntityTypeActionType.SendEmail) + async handleSendEmailJobOld(job: AutomationJob): Promise<{ variables?: unknown }> { + return this.handleSendEmailJob(job); + } + @OnAutomationJob(EntityTypeActionType.EmailSend) + async handleSendEmailJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.emailSettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, emailSettings } = job.variables; + + const messages = await this.service.processAutomation({ + accountId, + entityId: entity.id, + entityStageId: entity.stageId, + settings: emailSettings, + }); + + return { variables: { ...job.variables, messages: messages?.map((message) => message.toDto()) } }; + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + return { variables: job.variables }; + } + } +} diff --git a/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.module.ts b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.module.ts new file mode 100644 index 0000000..58f9853 --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { DocumentsModule } from '@/modules/documents/documents.module'; +import { CrmModule } from '@/CRM/crm.module'; +import { MailingModule } from '@/Mailing/MailingModule'; + +import { MailMessageScheduled } from './entities'; +import { MailMessageScheduledService } from './mail-message-scheduled.service'; +import { MailMessageScheduledController } from './mail-message-scheduled.controller'; +import { MailMessageScheduledHandler } from './mail-message-scheduled.handler'; + +@Module({ + imports: [TypeOrmModule.forFeature([MailMessageScheduled]), IAMModule, MailingModule, CrmModule, DocumentsModule], + providers: [MailMessageScheduledService, MailMessageScheduledHandler], + controllers: [MailMessageScheduledController], +}) +export class MailMessageScheduledModule {} diff --git a/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.service.ts b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.service.ts new file mode 100644 index 0000000..5fd2c6f --- /dev/null +++ b/backend/src/modules/mail/mail-message-scheduled/mail-message-scheduled.service.ts @@ -0,0 +1,369 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import Handlebars from 'handlebars'; + +import { DatePeriod, DatePeriodFilter, DateUtil, isUnique, PagingQuery } from '@/common'; +import { ActionEmailSendSettings } from '@/modules/automation'; + +import { EntityCategory } from '@/CRM/common'; +import { EntityTypeService } from '@/CRM/entity-type/entity-type.service'; +import { Entity } from '@/CRM/Model/Entity/Entity'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { Mailbox } from '@/Mailing/mailbox/entities'; +import { MailboxService as MailboxSettingsService } from '@/Mailing/mailbox/services'; +import { MailboxService } from '@/Mailing/Service/Mailbox/MailboxService'; +import { FieldType } from '@/modules/entity/entity-field/common'; +import { FieldService } from '@/modules/entity/entity-field/field'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value'; +import { DocumentGenerationService } from '@/modules/documents/document-generation/document-generation.service'; + +import { MailMessageScheduled } from './entities'; +import { CreateMailMessageScheduledDto } from './dto'; + +interface FindFilter { + accountId: number; + messageId?: number | number[]; + mailboxId?: number | number[]; + sendFrom?: number | number[]; + entityId?: number | number[]; + isSent?: boolean; + sentAt?: DatePeriodFilter; + createdAt?: DatePeriodFilter; + subject?: string; + content?: string; + sentTo?: string; +} + +interface MailboxMessage { + accountId: number; + mailboxId: number; + messageId: number; +} + +const DefaultLimit = 1000; + +@Injectable() +export class MailMessageScheduledService { + constructor( + @InjectRepository(MailMessageScheduled) + private readonly repository: Repository, + private readonly mailboxSettingsService: MailboxSettingsService, + private readonly mailboxService: MailboxService, + private readonly entityTypeService: EntityTypeService, + private readonly entityService: EntityService, + private readonly fieldService: FieldService, + private readonly fieldValueService: FieldValueService, + private readonly documentGenerationService: DocumentGenerationService, + ) {} + + async create({ + accountId, + dto, + }: { + accountId: number; + dto: CreateMailMessageScheduledDto; + }): Promise { + return this.repository.save(MailMessageScheduled.fromDto(accountId, dto)); + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + + async findMany({ filter, paging }: { filter: FindFilter; paging?: PagingQuery }): Promise { + return this.createFindQb(filter).orderBy('created_at', 'DESC').limit(paging.take).offset(paging.skip).getMany(); + } + + async delete(filter: FindFilter): Promise { + await this.createFindQb(filter).delete().execute(); + } + + async processMessages(): Promise { + const queue = await this.repository + .createQueryBuilder() + .select('account_id', 'accountId') + .addSelect('mailbox_id', 'mailboxId') + .addSelect('min(id)', 'messageId') + .where('sent_at is null') + .groupBy('account_id') + .addGroupBy('mailbox_id') + .getRawMany(); + + let processed = 0; + for (const item of queue) { + if (item.messageId) { + if (await this.processMessage(item)) { + processed++; + } + } + } + return processed; + } + + private async processMessage({ accountId, mailboxId, messageId }: MailboxMessage): Promise { + const maxSendAt = await this.repository + .createQueryBuilder() + .select('max(sent_at)', 'max') + .where('account_id = :accountId', { accountId }) + .andWhere(`mailbox_id = :mailboxId`, { mailboxId }) + .andWhere('sent_at is not null') + .getRawOne<{ sentAt: Date }>(); + + const mailbox = await this.mailboxSettingsService.findOne({ accountId, mailboxId }); + if (!maxSendAt?.sentAt || this.canSend({ lastSentAt: maxSendAt.sentAt, limit: mailbox.emailsPerDay })) { + this.sendMessage({ accountId, mailbox, messageId }); + return true; + } + return false; + } + + private canSend({ lastSentAt, limit }: { lastSentAt: Date; limit?: number | null }): boolean { + const secondsPerEmail = 86400 / (limit ?? DefaultLimit); + const diff = DateUtil.diff({ startDate: lastSentAt, endDate: DateUtil.now(), unit: 'second' }); + + return diff > secondsPerEmail; + } + + private async sendMessage({ + accountId, + mailbox, + messageId, + }: { + accountId: number; + mailbox: Mailbox; + messageId: number; + }) { + const message = await this.findOne({ accountId, messageId }); + + await this.mailboxService.sendMessageForMailbox( + accountId, + mailbox, + { + sentTo: message.sendTo, + subject: message.subject, + contentText: null, + contentHtml: message.content, + entityId: message.entityId, + cc: null, + bcc: null, + replyTo: null, + replyToMessageId: null, + fileIds: null, + }, + null, + message.sendFrom, + ); + + await this.repository.update({ accountId, id: messageId }, { sentAt: DateUtil.now() }); + } + + async processAutomation({ + accountId, + entityId, + entityStageId, + settings, + }: { + accountId: number; + entityId: number; + entityStageId: number | null | undefined; + settings: ActionEmailSendSettings; + }): Promise { + const messages: MailMessageScheduled[] = []; + + const entity = await this.entityService.findOne(accountId, { entityId }); + if (entity && (!entity.stageId || settings.allowAnyStage || entity.stageId === entityStageId)) { + const hasOptions = Boolean(settings.options); + if (!hasOptions || settings.options?.main) { + const entity = await this.entityService.findOne(accountId, { entityId }); + if (entity) { + const message = await this.createAutomationMessage({ + accountId, + entity, + settings, + onlyFirst: Boolean(settings.options?.main?.onlyFirstValue), + }); + if (message) { + messages.push(message); + } + } + } + + let primaryCompany = Boolean(settings.options?.company?.onlyFirstEntity); + let primaryContact = Boolean(settings.options?.contact?.onlyFirstEntity); + const entities = await this.entityService.findLinkedEntities({ accountId, entityId }); + for (const entity of entities) { + const entityType = await this.entityTypeService.findOne(accountId, { id: entity.entityTypeId }); + let send = [EntityCategory.CONTACT, EntityCategory.COMPANY].includes(entityType.entityCategory); + let onlyFirst = false; + if (send && hasOptions) { + if (entityType.entityCategory === EntityCategory.COMPANY) { + send = settings.options.company && (!settings.options.company?.onlyFirstEntity || primaryCompany); + primaryCompany = false; + onlyFirst = Boolean(settings.options.company?.onlyFirstValue); + } else if (entityType.entityCategory === EntityCategory.CONTACT) { + send = settings.options.contact && (!settings.options.contact?.onlyFirstEntity || primaryContact); + primaryContact = false; + onlyFirst = Boolean(settings.options.contact?.onlyFirstValue); + } + } + + if (send) { + const message = await this.createAutomationMessage({ accountId, entity, settings, onlyFirst }); + if (message) { + messages.push(message); + } + } + } + } + + return messages; + } + + private async createAutomationMessage({ + accountId, + entity, + settings, + onlyFirst, + }: { + accountId: number; + entity: Entity; + settings: ActionEmailSendSettings; + onlyFirst: boolean; + }): Promise { + const fieldIds = await this.fieldService.findManyIds({ + accountId, + entityTypeId: entity.entityTypeId, + type: FieldType.Email, + }); + if (fieldIds.length) { + let sendTo: string[] = []; + if (onlyFirst) { + for (const fieldId of fieldIds) { + const fieldValue = await this.fieldValueService.findOne({ accountId, entityId: entity.id, fieldId }); + if (fieldValue) { + const value = fieldValue + .getValue() + .filter((email) => email !== '') + .filter(isUnique); + if (value.length) { + sendTo = [value[0]]; + break; + } + } + } + } else { + const fieldValues = await this.fieldValueService.findMany({ + accountId, + entityId: entity.id, + fieldId: fieldIds, + }); + sendTo = fieldValues + .map((fv) => fv.getValue()) + .flat() + .filter((email) => email !== '') + .filter(isUnique); + } + + if (sendTo.length) { + const data = await this.documentGenerationService.getDataForGeneration({ accountId, entityId: entity.id }); + data['contact_id'] = entity.id; + data['contact_name'] = entity.name; + const subject = Handlebars.compile(settings.subject)(data); + const content = Handlebars.compile(settings.content + (settings.signature ?? ''))(data); + + return this.create({ + accountId, + dto: { + mailboxId: settings.mailboxId, + sendFrom: settings.userId ?? entity.responsibleUserId, + subject: subject, + content: content, + sendTo: sendTo, + entityId: entity.id, + }, + }); + } + } + + return null; + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + + if (filter?.messageId) { + if (Array.isArray(filter.messageId)) { + qb.andWhere('id IN (:...ids)', { ids: filter.messageId }); + } else { + qb.andWhere('id = :id', { id: filter.messageId }); + } + } + + if (filter?.mailboxId) { + if (Array.isArray(filter.mailboxId)) { + qb.andWhere('mailbox_id IN (:...mailboxIds)', { mailboxIds: filter.mailboxId }); + } else { + qb.andWhere('mailbox_id = :mailboxId', { mailboxId: filter.mailboxId }); + } + } + + if (filter?.sendFrom) { + if (Array.isArray(filter.sendFrom)) { + qb.andWhere('send_from IN (:...sendFrom)', { sendFrom: filter.sendFrom }); + } else { + qb.andWhere('send_from = :sendFrom', { sendFrom: filter.sendFrom }); + } + } + + if (filter?.entityId) { + if (Array.isArray(filter.entityId)) { + qb.andWhere('entity_id IN (:...entityIds)', { entityIds: filter.entityId }); + } else { + qb.andWhere('entity_id = :entityId', { entityId: filter.entityId }); + } + } + + if (filter?.isSent !== undefined) { + if (filter.isSent) { + qb.andWhere('sent_at IS NOT NULL'); + } else { + qb.andWhere('sent_at IS NULL'); + } + } + + if (filter.sentAt) { + const dates = DatePeriod.fromFilter(filter.sentAt); + if (dates.from) { + qb.andWhere('sent_at >= :sentAtFrom', { sentAtFrom: dates.from }); + } + if (dates.to) { + qb.andWhere('sent_at <= :sentAtTo', { sentAtTo: dates.to }); + } + } + + if (filter.createdAt) { + const dates = DatePeriod.fromFilter(filter.createdAt); + if (dates.from) { + qb.andWhere('created_at >= :createdAtFrom', { createdAtFrom: dates.from }); + } + if (dates.to) { + qb.andWhere('created_at <= :createdAtTo', { createdAtTo: dates.to }); + } + } + + if (filter.subject) { + qb.andWhere('subject ILIKE :subject', { subject: `%${filter.subject}%` }); + } + + if (filter.content) { + qb.andWhere('content ILIKE :content', { content: `%${filter.content}%` }); + } + + if (filter.sentTo) { + qb.andWhere('send_to ILIKE :sentTo', { sentTo: `%${filter.sentTo}%` }); + } + + return qb; + } +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/config/imapflow.config.ts b/backend/src/modules/mail/mail-providers/imapflow/config/imapflow.config.ts new file mode 100644 index 0000000..167bcbb --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/config/imapflow.config.ts @@ -0,0 +1,16 @@ +import { registerAs } from '@nestjs/config'; + +export interface ImapflowConfig { + searchTimeout: number; + searchBatchSize: number; + partLoadTimeout: number; +} + +export default registerAs( + 'imapflow', + (): ImapflowConfig => ({ + searchTimeout: parseInt(process.env.MAIL_IMAPFLOW_SEARCH_TIMEOUT, 10) || 30000, + searchBatchSize: parseInt(process.env.MAIL_IMAPFLOW_SEARCH_BATCH_SIZE, 10) || 100, + partLoadTimeout: parseInt(process.env.MAIL_IMAPFLOW_PART_LOAD_TIMEOUT, 10) || 15000, + }), +); diff --git a/backend/src/modules/mail/mail-providers/imapflow/config/index.ts b/backend/src/modules/mail/mail-providers/imapflow/config/index.ts new file mode 100644 index 0000000..0f0ee3e --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/config/index.ts @@ -0,0 +1 @@ +export * from './imapflow.config'; diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-imapflow.dto.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-imapflow.dto.ts new file mode 100644 index 0000000..aaea845 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-imapflow.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { IsDefined, ValidateNested } from 'class-validator'; + +import { CreateMailboxDto } from '@/Mailing/mailbox/dto'; +import { CreateMailboxSettingsImapflowDto } from './create-mailbox-settings-imapflow.dto'; + +export class CreateMailboxImapflowDto extends OmitType(CreateMailboxDto, ['provider'] as const) { + @ApiProperty({ type: CreateMailboxSettingsImapflowDto, description: 'Imapflow Settings' }) + @IsDefined() + @ValidateNested() + settings: CreateMailboxSettingsImapflowDto; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-settings-imapflow.dto.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-settings-imapflow.dto.ts new file mode 100644 index 0000000..7e5b322 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/create-mailbox-settings-imapflow.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { MailboxSettingsImapflowDto } from './mailbox-settings-imapflow.dto'; + +export class CreateMailboxSettingsImapflowDto extends PickType(MailboxSettingsImapflowDto, [ + 'imapServer', + 'imapPort', + 'imapSecure', + 'smtpServer', + 'smtpPort', + 'smtpSecure', +] as const) { + @ApiProperty({ description: 'Mail password' }) + @IsString() + password: string; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/index.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/index.ts new file mode 100644 index 0000000..4a0d857 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/index.ts @@ -0,0 +1,6 @@ +export * from './create-mailbox-imapflow.dto'; +export * from './create-mailbox-settings-imapflow.dto'; +export * from './mailbox-imapflow.dto'; +export * from './mailbox-settings-imapflow.dto'; +export * from './update-mailbox-imapflow.dto'; +export * from './update-mailbox-settings-imapflow.dto'; diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-imapflow.dto.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-imapflow.dto.ts new file mode 100644 index 0000000..5175423 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-imapflow.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { MailboxDto } from '@/Mailing/mailbox/dto/mailbox.dto'; +import { MailboxSettingsImapflowDto } from './mailbox-settings-imapflow.dto'; + +export class MailboxImapflowDto extends MailboxDto { + @ApiProperty({ type: MailboxSettingsImapflowDto, description: 'Imapflow Settings' }) + settings: MailboxSettingsImapflowDto; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-settings-imapflow.dto.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-settings-imapflow.dto.ts new file mode 100644 index 0000000..b9e5b0a --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/mailbox-settings-imapflow.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsString } from 'class-validator'; + +export class MailboxSettingsImapflowDto { + @ApiProperty({ description: 'IMAP server address' }) + @IsString() + imapServer: string; + + @ApiProperty({ description: 'IMAP server port' }) + @IsNumber() + imapPort: number; + + @ApiProperty({ description: 'IMAP server secure' }) + @IsBoolean() + imapSecure: boolean; + + @ApiProperty({ description: 'SMTP server address' }) + @IsString() + smtpServer: string; + + @ApiProperty({ description: 'SMTP server port' }) + @IsNumber() + smtpPort: number; + + @ApiProperty({ description: 'SMTP server secure' }) + @IsBoolean() + smtpSecure: boolean; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-imapflow.dto.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-imapflow.dto.ts new file mode 100644 index 0000000..b821ad0 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-imapflow.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; + +import { UpdateMailboxDto } from '@/Mailing/mailbox/dto'; +import { UpdateMailboxSettingsImapflowDto } from './update-mailbox-settings-imapflow.dto'; + +export class UpdateMailboxImapflowDto extends UpdateMailboxDto { + @ApiPropertyOptional({ type: UpdateMailboxSettingsImapflowDto, description: 'Imapflow Settings' }) + settings?: UpdateMailboxSettingsImapflowDto; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-settings-imapflow.dto.ts b/backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-settings-imapflow.dto.ts new file mode 100644 index 0000000..0027a54 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/dto/update-mailbox-settings-imapflow.dto.ts @@ -0,0 +1,20 @@ +import { ApiPropertyOptional, PartialType, PickType } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +import { MailboxSettingsImapflowDto } from './mailbox-settings-imapflow.dto'; + +export class UpdateMailboxSettingsImapflowDto extends PartialType( + PickType(MailboxSettingsImapflowDto, [ + 'imapServer', + 'imapPort', + 'imapSecure', + 'smtpServer', + 'smtpPort', + 'smtpSecure', + ] as const), +) { + @ApiPropertyOptional({ description: 'Mail password' }) + @IsOptional() + @IsString() + password?: string; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/entities/index.ts b/backend/src/modules/mail/mail-providers/imapflow/entities/index.ts new file mode 100644 index 0000000..fb28fb1 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/entities/index.ts @@ -0,0 +1 @@ +export * from './mailbox-settings-imapflow.entity'; diff --git a/backend/src/modules/mail/mail-providers/imapflow/entities/mailbox-settings-imapflow.entity.ts b/backend/src/modules/mail/mail-providers/imapflow/entities/mailbox-settings-imapflow.entity.ts new file mode 100644 index 0000000..5d469c2 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/entities/mailbox-settings-imapflow.entity.ts @@ -0,0 +1,107 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { CreateMailboxSettingsImapflowDto, MailboxSettingsImapflowDto, UpdateMailboxSettingsImapflowDto } from '../dto'; +import { ImapflowSyncInfo } from '../types'; + +@Entity() +export class MailboxSettingsImapflow { + @PrimaryColumn() + mailboxId: number; + + @Column() + password: string; + + @Column() + imapServer: string; + + @Column() + imapPort: number; + + @Column() + imapSecure: boolean; + + @Column() + smtpServer: string; + + @Column() + smtpPort: number; + + @Column() + smtpSecure: boolean; + + @Column({ type: 'jsonb', nullable: true }) + syncInfo: ImapflowSyncInfo[] | null; + + @Column() + accountId: number; + + constructor( + accountId: number, + mailboxId: number, + password: string, + imapServer: string, + imapPort: number, + imapSecure: boolean, + smtpServer: string, + smtpPort: number, + smtpSecure: boolean, + syncInfo: ImapflowSyncInfo[] | null = null, + ) { + this.accountId = accountId; + this.mailboxId = mailboxId; + this.password = password; + this.imapServer = imapServer; + this.imapPort = imapPort; + this.imapSecure = imapSecure; + this.smtpServer = smtpServer; + this.smtpPort = smtpPort; + this.smtpSecure = smtpSecure; + this.syncInfo = syncInfo; + } + + static fromDto({ + accountId, + mailboxId, + dto, + }: { + accountId: number; + mailboxId: number; + dto: CreateMailboxSettingsImapflowDto; + }): MailboxSettingsImapflow { + return new MailboxSettingsImapflow( + accountId, + mailboxId, + dto.password, + dto.imapServer, + dto.imapPort, + dto.imapSecure, + dto.smtpServer, + dto.smtpPort, + dto.smtpSecure, + ); + } + + update(dto: UpdateMailboxSettingsImapflowDto & { syncInfo?: ImapflowSyncInfo[] | null }): MailboxSettingsImapflow { + this.password = dto.password !== undefined ? dto.password : this.password; + this.imapServer = dto.imapServer !== undefined ? dto.imapServer : this.imapServer; + this.imapPort = dto.imapPort !== undefined ? dto.imapPort : this.imapPort; + this.imapSecure = dto.imapSecure !== undefined ? dto.imapSecure : this.imapSecure; + this.smtpServer = dto.smtpServer !== undefined ? dto.smtpServer : this.smtpServer; + this.smtpPort = dto.smtpPort !== undefined ? dto.smtpPort : this.smtpPort; + this.smtpSecure = dto.smtpSecure !== undefined ? dto.smtpSecure : this.smtpSecure; + this.syncInfo = dto.syncInfo !== undefined ? dto.syncInfo : this.syncInfo; + + return this; + } + + toDto(): MailboxSettingsImapflowDto { + return { + imapServer: this.imapServer, + imapPort: this.imapPort, + imapSecure: this.imapSecure, + smtpServer: this.smtpServer, + smtpPort: this.smtpPort, + smtpSecure: this.smtpSecure, + }; + } +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/errors/index.ts b/backend/src/modules/mail/mail-providers/imapflow/errors/index.ts new file mode 100644 index 0000000..ca1bf11 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/errors/index.ts @@ -0,0 +1 @@ +export * from './mail-connection.error'; diff --git a/backend/src/modules/mail/mail-providers/imapflow/errors/mail-connection.error.ts b/backend/src/modules/mail/mail-providers/imapflow/errors/mail-connection.error.ts new file mode 100644 index 0000000..c81043d --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/errors/mail-connection.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class MailConnectionError extends ServiceError { + constructor(message = 'Connection error') { + super({ errorCode: 'mail.connection_error', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/imapflow.controller.ts b/backend/src/modules/mail/mail-providers/imapflow/imapflow.controller.ts new file mode 100644 index 0000000..00d9796 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/imapflow.controller.ts @@ -0,0 +1,55 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { DeleteMailboxQuery } from '@/Mailing/common'; +import { CreateMailboxImapflowDto, MailboxImapflowDto, UpdateMailboxImapflowDto } from './dto'; +import { ImapflowService } from './imapflow.service'; + +@ApiTags('mail/settings/imapflow') +@Controller('mail/settings/imapflow/mailboxes') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ImapflowController { + constructor(private readonly service: ImapflowService) {} + + @ApiOperation({ summary: 'Create mailbox', description: 'Create mailbox for imapflow provider' }) + @ApiBody({ type: CreateMailboxImapflowDto, required: true, description: 'Mailbox settings' }) + @ApiCreatedResponse({ description: 'Mailbox', type: MailboxImapflowDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateMailboxImapflowDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Get mailbox', description: 'Get mailbox for imapflow provider' }) + @ApiParam({ name: 'mailboxId', type: Number, required: true, description: 'Mailbox ID' }) + @ApiOkResponse({ description: 'Mailbox', type: MailboxImapflowDto }) + @AuthDataPrefetch({ user: true }) + @Get(':mailboxId') + async findMailbox(@CurrentAuth() { accountId, user }: AuthData, @Param('mailboxId', ParseIntPipe) mailboxId: number) { + return this.service.findMailbox({ accountId, mailboxId, ownerId: user.isAdmin ? undefined : user.id }); + } + + @ApiOperation({ summary: 'Update mailbox', description: 'Update mailbox for imapflow provider' }) + @ApiParam({ name: 'mailboxId', type: Number, required: true, description: 'Mailbox ID' }) + @ApiOkResponse({ description: 'Mailbox', type: MailboxImapflowDto }) + @Patch(':mailboxId') + async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Body() dto: UpdateMailboxImapflowDto, + ) { + return this.service.update({ accountId, user, mailboxId, dto }); + } + + @Delete(':mailboxId') + async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('mailboxId', ParseIntPipe) mailboxId: number, + @Query() query: DeleteMailboxQuery, + ) { + await this.service.delete({ accountId, user, mailboxId, softDelete: query?.save }); + } +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/imapflow.module.ts b/backend/src/modules/mail/mail-providers/imapflow/imapflow.module.ts new file mode 100644 index 0000000..51533ea --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/imapflow.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { MailingModule } from '@/Mailing/MailingModule'; + +import imapflowConfig from './config/imapflow.config'; +import { MailboxSettingsImapflow } from './entities'; +import { ImapflowService } from './imapflow.service'; +import { ImapflowController } from './imapflow.controller'; + +@Module({ + imports: [ + ConfigModule.forFeature(imapflowConfig), + TypeOrmModule.forFeature([MailboxSettingsImapflow]), + IAMModule, + MailingModule, + ], + providers: [ImapflowService], + controllers: [ImapflowController], +}) +export class ImapflowModule {} diff --git a/backend/src/modules/mail/mail-providers/imapflow/imapflow.service.ts b/backend/src/modules/mail/mail-providers/imapflow/imapflow.service.ts new file mode 100644 index 0000000..663f4df --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/imapflow.service.ts @@ -0,0 +1,1076 @@ +import { Injectable, Logger, NotImplementedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { + FetchMessageObject, + ImapFlow, + Logger as ImapFlowLogger, + ListTreeResponse, + MessageAddressObject, + MessageStructureObject, + SearchObject, +} from 'imapflow'; +import { AddressObject, ParsedMail, simpleParser, Source } from 'mailparser'; +import * as iconv from 'iconv-lite'; +import * as quotedPrintable from 'quoted-printable'; +import { convert } from 'html-to-text'; +import nodemailer from 'nodemailer'; +import Mail from 'nodemailer/lib/mailer'; +import { v4 as uuidv4 } from 'uuid'; + +import { DateUtil, NotFoundError, StringUtil, withTimeout } from '@/common'; + +import { User } from '@/modules/iam/user/entities'; +import { StorageFile } from '@/modules/storage/types'; +import { + detectMailboxFolderType, + EmailAddress, + EmailAddressValue, + FolderMessages, + MailboxFolderExternal, + MailboxFolderType, + MailboxSyncMessages, + MailboxSyncResult, + MailMessageAttachment, + MailMessageExternal, + MailMessagePayloadExternal, + SendMailMessageDto, +} from '@/Mailing/common'; +import { Mailbox, MailboxService } from '@/Mailing/mailbox'; +import { MailIntegration, MailProvider, MailProviderCapability } from '@/Mailing/mail-provider'; +import { MailMessagePayload } from '@/Mailing/mail-message-payload'; +import { MailboxFolderService } from '@/Mailing/mailbox-folder'; +import { MailMessageBuilderService, HEADER_ENTITY_ID } from '@/Mailing/mail-message-builder'; +import { MailMessage } from '@/Mailing/Model/MailMessage/MailMessage'; + +import { ImapflowConfig } from './config'; +import { CreateMailboxImapflowDto, UpdateMailboxImapflowDto } from './dto'; +import { MailboxSettingsImapflow } from './entities'; +import { MailConnectionError } from './errors'; +import { ImapflowSyncInfo, MailboxImapflow } from './types'; + +const ProviderName = 'imapflow'; + +interface FindFilter { + accountId: number; + mailboxId?: number; +} + +interface ImapflowConnectionParams { + email: string; + password: string; + imapServer: string; + imapPort: number; + imapSecure: boolean; + verifyOnly?: boolean; +} + +interface WithConnectionActionParams { + mailboxId?: number | null; + client: ImapFlow; + settings: MailboxSettingsImapflow; +} +interface WithConnectionErrorParams { + mailboxId?: number | null; + error: unknown; +} +interface WithConnectionParam { + email: string; + verifyOnly?: boolean; + action: (params: WithConnectionActionParams) => Promise; + error?: (params: WithConnectionErrorParams) => Promise; +} +interface WithConnectionParamMailbox extends WithConnectionParam { + accountId: number; + mailboxId: number; +} + +interface WithConnectionParamSettings extends WithConnectionParam { + settings: MailboxSettingsImapflow; +} + +interface WithBoxActionParams { + mailboxId?: number | null; + client: ImapFlow; + path: string; +} +interface WithBoxErrorParams { + mailboxId?: number | null; + error: Error; +} +interface WithBoxParams { + mailboxId: number; + client: ImapFlow; + path: string; + action: (params: WithBoxActionParams) => Promise; + error?: (params: WithBoxErrorParams) => R | null; +} + +interface ProcessMessagesResult { + result: boolean; + message?: string | null; + messages?: MailboxSyncMessages; + syncInfo?: ImapflowSyncInfo[]; +} +interface ProcessMessagesInFolderParams extends WithBoxActionParams { + folderUidNext: number; + syncUidNext: number; + syncDate: Date; +} +interface ProcessMessagesInFolderResult { + result: boolean; + message?: string | null; + syncInfo: ImapflowSyncInfo; + messages?: MailboxSyncMessages | null; +} + +interface SendMessageResult { + message: string; + uid?: number | null; +} + +// eslint-disable-next-line no-control-regex +const cleanString = (str: string) => str?.replace(/[\u0000\f]/g, '')?.trim(); +const concatAddress = (addresses: MessageAddressObject[]) => + addresses + .map((a) => `${a.name ? `${a.name} ` : `${a.address}`}${a.name && a.address ? `<${a.address}>` : ''}`) + .join(', '); + +@Injectable() +@MailIntegration(ProviderName) +export class ImapflowService implements MailProvider { + private readonly logger = new Logger(ImapflowService.name); + private readonly _config: ImapflowConfig; + + constructor( + private readonly configService: ConfigService, + @InjectRepository(MailboxSettingsImapflow) + private readonly repository: Repository, + private readonly mailboxService: MailboxService, + private readonly mailboxFolderService: MailboxFolderService, + private readonly mailMessageBuilder: MailMessageBuilderService, + ) { + this._config = this.configService.get('imapflow'); + } + + isCapable(capability: MailProviderCapability): boolean { + if (capability === 'thread') { + return false; + } + + return false; + } + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateMailboxImapflowDto; + }): Promise { + const mailbox = await this.mailboxService.create({ accountId, userId, dto: { ...dto, provider: ProviderName } }); + let settings = MailboxSettingsImapflow.fromDto({ accountId, mailboxId: mailbox.id, dto: dto.settings }); + const { result, message } = await this.verify({ email: mailbox.email, settings }); + if (result) { + settings = await this.repository.save(settings); + return new MailboxImapflow({ mailbox, settings }); + } + throw new MailConnectionError(message); + } + + async findOne(filter: FindFilter): Promise { + return this.createQb(filter).getOne(); + } + + async findMailbox(filter: FindFilter & { ownerId?: number }): Promise { + const mailbox = await this.mailboxService.findOne({ ...filter, provider: ProviderName }); + if (mailbox) { + const settings = await this.findOne(filter); + return new MailboxImapflow({ mailbox, settings }); + } + return null; + } + + async update({ + accountId, + user, + mailboxId, + dto, + }: { + accountId: number; + user: User; + mailboxId: number; + dto: UpdateMailboxImapflowDto & { syncInfo?: ImapflowSyncInfo[] }; + }): Promise { + let mailbox = await this.mailboxService.findOne({ accountId, mailboxId }); + let settings = await this.findOne({ accountId, mailboxId }); + if (!mailbox || !settings) { + throw NotFoundError.withId(Mailbox, mailboxId); + } + + if (dto.email || dto.settings) { + if (dto.settings) settings = settings.update(dto.settings); + const { result, message } = await this.verify({ email: dto.email ?? mailbox.email, settings }); + if (!result) { + throw new MailConnectionError(message); + } + } + + if (dto.settings) await this.repository.save(settings); + + mailbox = await this.mailboxService.update({ accountId, user, mailboxId, dto }); + + return new MailboxImapflow({ mailbox, settings }); + } + + private async updateSyncInfo({ + accountId, + mailboxId, + syncInfo, + }: { + accountId: number; + mailboxId: number; + syncInfo: ImapflowSyncInfo[]; + }) { + await this.repository.update({ accountId, mailboxId }, { syncInfo }); + } + + async delete({ + accountId, + user, + mailboxId, + softDelete, + }: { + accountId: number; + user: User; + mailboxId: number; + softDelete?: boolean; + }): Promise { + await this.mailboxService.delete({ accountId, user, mailboxId, softDelete }); + } + + async verify({ email, settings }: { email: string; settings: MailboxSettingsImapflow }): Promise { + return this.withConnection({ + email, + settings, + verifyOnly: true, + action: async () => ({ result: true }) as MailboxSyncResult, + error: async (params) => this.handleError(params), + }); + } + + async sync({ + mailbox, + syncFull, + syncDate, + }: { + mailbox: Mailbox; + syncFull?: boolean; + syncDate?: Date; + }): Promise { + return this.withConnection({ + email: mailbox.email, + accountId: mailbox.accountId, + mailboxId: mailbox.id, + action: async (params) => this.processMailbox({ ...params, syncFull, syncDate }), + error: async (params) => this.handleError(params), + }); + } + + async getAttachment({ + mailbox, + message, + payload, + }: { + mailbox: Mailbox; + message: MailMessage; + payload: MailMessagePayload; + }): Promise { + const folder = await this.mailboxFolderService.findOne({ + accountId: message.accountId, + messageId: message.id, + }); + if (folder) { + const content = await this.withConnection({ + email: mailbox.email, + accountId: mailbox.accountId, + mailboxId: mailbox.id, + action: async (params) => { + return await this.withMailbox({ + mailboxId: mailbox.id, + client: params.client, + path: folder.externalId, + action: async ({ client }) => { + const [, uid] = this.parseExternalId(message.externalId); + const partData = await withTimeout( + client.download(String(uid), payload.attachment || payload.externalId, { uid: true }), + this._config.partLoadTimeout, + null, + ); + if (partData?.content) { + const { content } = partData; + const chunks = []; + for await (const chunk of content) { + chunks.push(chunk); + } + return Buffer.concat(chunks); + } + + return null; + }, + error: () => null as Buffer, + }); + }, + }); + + if (content) return { mimeType: payload.mimeType, filename: payload.filename, content }; + } + + return null; + } + + async send({ + accountId, + mailbox, + userName, + dto, + replyToMessage, + attachments, + }: { + accountId: number; + mailbox: Mailbox; + userName: string; + dto: SendMailMessageDto; + replyToMessage?: MailMessage | null; + attachments: StorageFile[]; + }): Promise { + try { + const settings = await this.findOne({ accountId, mailboxId: mailbox.id }); + const transporter = nodemailer.createTransport({ + host: settings.smtpServer, + port: settings.smtpPort, + secure: settings.smtpSecure, + auth: { + user: mailbox.email, + pass: settings.password, + }, + }); + + const mail = await this.mailMessageBuilder.createNodemailerMessage( + mailbox.email, + userName, + dto, + replyToMessage, + attachments, + ); + const { messageId } = await transporter.sendMail(mail); + if (messageId) { + mail.messageId = messageId; + const sentFolder = await this.mailboxFolderService.findOne({ + accountId, + mailboxId: mailbox.id, + type: MailboxFolderType.Sent, + }); + if (sentFolder) { + const sentMessage = await this.sendMessage({ + email: mailbox.email, + settings, + mail, + path: sentFolder.externalId, + }); + if (sentMessage) { + return this.createExternalMessage({ + id: sentMessage.uid ?? uuidv4(), + raw: sentMessage.message, + folderName: sentFolder.externalId, + }); + } + } + } + } catch (e) { + const error = e as Error; + this.logger.error(`SMTP send message error for mailbox ${mailbox.id}: ${error?.message}`, error?.stack); + } + + return null; + } + + private createQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('msi') + .where('msi.accountId = :accountId', { accountId: filter.accountId }); + if (filter.mailboxId) { + qb.andWhere('msi.mailboxId = :mailboxId', { mailboxId: filter.mailboxId }); + } + return qb; + } + + private async withConnection(params: WithConnectionParamMailbox | WithConnectionParamSettings): Promise { + const { email, verifyOnly, action, error } = params; + + let localSettings: MailboxSettingsImapflow; + if ('settings' in params) { + localSettings = params.settings; + } else if ('accountId' in params && 'mailboxId' in params) { + localSettings = await this.findOne({ accountId: params.accountId, mailboxId: params.mailboxId }); + } else { + throw new Error('No mailboxId or settings provided'); + } + + const client = this.getClient({ ...localSettings, email, verifyOnly }); + try { + await client.connect(); + return await action({ mailboxId: localSettings.mailboxId, client, settings: localSettings }); + } catch (e) { + this.logger.warn(`Connection error for mailbox ${localSettings.mailboxId}. ${e.toString()}`); + return error ? await error({ mailboxId: localSettings.mailboxId, error: e as Error }) : null; + } finally { + if (!verifyOnly) { + client.logout(); + } + } + } + + private getClient({ + email, + password, + imapServer, + imapPort, + imapSecure, + verifyOnly = false, + }: ImapflowConnectionParams): ImapFlow { + return new ImapFlow({ + host: imapServer, + port: imapPort, + secure: imapSecure, + auth: { user: email, pass: password }, + verifyOnly: verifyOnly, + logger: this.logger as unknown as ImapFlowLogger, + }); + } + + private async withMailbox({ mailboxId, client, path, action, error }: WithBoxParams): Promise { + const lock = await client.getMailboxLock(path); + try { + return await action({ mailboxId, client, path }); + } catch (e) { + this.logger.warn(`Box <${path}> error for mailbox ${mailboxId}. ${e.toString()}`); + return error ? await error({ mailboxId, error: e as Error }) : null; + } finally { + lock.release(); + } + } + + private async processMailbox({ + mailboxId, + client, + settings, + syncDate, + syncFull, + }: WithConnectionActionParams & { syncFull?: boolean | null; syncDate?: Date | null }): Promise { + const root = await client.listTree(); + const folders = await this.processFolders({ client, folders: root.root ? root.folders : [root] }); + + const { result, message, messages, syncInfo } = await this.processMessages({ + client, + mailboxId, + folders, + syncInfo: !syncFull && settings.syncInfo ? settings.syncInfo : [], + syncDate: syncDate, + }); + + if (result) { + await this.updateSyncInfo({ accountId: settings.accountId, mailboxId, syncInfo }); + } + + return { result, message, folders, messages }; + } + + private async processFolders({ + client, + folders, + }: { + client: ImapFlow; + folders: ListTreeResponse[]; + }): Promise { + return Promise.all( + folders.map(async (folder) => { + const status = await client.status(folder.path, { uidValidity: true, uidNext: true }); + return { + id: folder.path, + uidValidity: Number(status?.uidValidity || 0), + uidNext: Number(status?.uidNext || 0), + name: folder.name, + type: detectMailboxFolderType({ specialUse: folder.specialUse }), + folders: folder.folders?.length ? await this.processFolders({ client, folders: folder.folders }) : undefined, + }; + }), + ); + } + + private async processMessages({ + client, + mailboxId, + folders, + syncInfo, + syncDate, + }: { + client: ImapFlow; + mailboxId: number; + folders: MailboxFolderExternal[]; + syncInfo: ImapflowSyncInfo[]; + syncDate?: Date; + }): Promise { + const messages: MailboxSyncMessages = { added: [], updated: [], deleted: [] }; + const newSyncInfo: ImapflowSyncInfo[] = []; + for (const folder of folders) { + const currentSncInfo = syncInfo.find((si) => si.boxName === folder.id); + const { + result: folderResult, + message: folderMessage, + messages: folderMsgs, + syncInfo: folderSyncInfo, + } = await this.withMailbox({ + mailboxId, + client, + path: folder.id, + action: (params) => + this.processMessagesInFolder({ + ...params, + folderUidNext: folder.uidNext, + syncUidNext: currentSncInfo?.uidnext, + syncDate, + }), + error: (e) => ({ result: false, message: e.error.message }) as ProcessMessagesInFolderResult, + }); + if (!folderResult) return { result: false, message: folderMessage }; + if (folderMsgs?.added?.length) messages.added.push(...folderMsgs.added); + if (folderMsgs?.updated?.length) messages.updated.push(...folderMsgs.updated); + if (folderMsgs?.deleted?.length) messages.deleted.push(...folderMsgs.deleted); + if (folderSyncInfo) newSyncInfo.push(folderSyncInfo); + + if (folder.folders?.length) { + const { + result: subFolderResult, + message: subFolderMessage, + messages: subFolderMsgs, + syncInfo: subFolderSyncInfo, + } = await this.processMessages({ + client, + mailboxId, + folders: folder.folders, + syncInfo, + syncDate, + }); + if (!subFolderResult) return { result: false, message: subFolderMessage }; + if (subFolderMsgs?.added?.length) messages.added.push(...subFolderMsgs.added); + if (subFolderMsgs?.updated?.length) messages.updated.push(...subFolderMsgs.updated); + if (subFolderMsgs?.deleted?.length) messages.deleted.push(...subFolderMsgs.deleted); + if (subFolderSyncInfo?.length) newSyncInfo.push(...subFolderSyncInfo); + } + } + + return { result: true, messages, syncInfo: newSyncInfo }; + } + + private async processMessagesInFolder({ + client, + path, + folderUidNext, + syncUidNext, + syncDate, + }: ProcessMessagesInFolderParams): Promise { + const added: MailMessageExternal[] = []; + const search: SearchObject = {}; + let uidTo: number | undefined = undefined; + if (syncUidNext) { + if (syncUidNext >= folderUidNext) { + // No new mail in box + return { result: true, syncInfo: { boxName: path, uidnext: folderUidNext } }; + } + uidTo = Math.min(syncUidNext + this._config.searchBatchSize, folderUidNext); + search.uid = `${syncUidNext}:${uidTo}`; + } else { + search.since = syncDate ?? DateUtil.now(); + } + + const messages = await withTimeout( + client.fetchAll( + search, + { uid: true, flags: true, bodyStructure: true, envelope: true, size: true, threadId: true, headers: true }, + { uid: true }, + ), + this._config.searchTimeout, + [], + ); + + for (const msg of messages) { + const message = await this.processMessage({ folderName: path, msg, client }); + if (message) added.push(message); + } + + return { result: true, messages: { added }, syncInfo: { boxName: path, uidnext: uidTo ?? folderUidNext } }; + } + + private async processMessage({ + msg, + folderName, + client, + }: { + folderName: string; + msg: FetchMessageObject; + client: ImapFlow; + }): Promise { + const { uid, threadId, envelope, flags, headers, bodyStructure } = msg; + const parsed = await simpleParser(headers.toString('utf-8')); + + const references = this.getHeaderValue(parsed, 'references') + ?.join(',') + ?.split(',') + ?.map((i) => i.trim()); + const entityIdStr = this.getHeaderValue(parsed, HEADER_ENTITY_ID); + const entityId = entityIdStr ? Number(entityIdStr) : null; + const { hasAttachment, payloads } = await this.getMessagePayloads({ client, uid, bodyStructure }); + const snippet = this.getMessageSnippet(payloads); + + return { + id: this.createExternalId({ folderName, id: uid }), + threadId: threadId, + messageId: envelope.messageId, + sentFrom: envelope.from ? { text: concatAddress(envelope.from), values: envelope.from } : null, + sentTo: envelope.to ? { text: concatAddress(envelope.to), values: envelope.to } : null, + replyTo: envelope.replyTo ? { text: concatAddress(envelope.replyTo), values: envelope.replyTo } : null, + cc: envelope.cc ? { text: concatAddress(envelope.cc), values: envelope.cc } : null, + subject: envelope.subject, + date: envelope.date, + inReplyTo: envelope.inReplyTo, + references: references, + isSeen: flags.has(`\\Seen`), + entityId: entityId, + folders: [folderName], + snippet: snippet, + hasAttachment: hasAttachment, + payloads: payloads, + }; + } + + private async getMessagePayloads({ + client, + uid, + bodyStructure, + }: { + client: ImapFlow; + uid: number; + bodyStructure: MessageStructureObject; + }): Promise<{ + hasAttachment: boolean; + payloads: MailMessagePayloadExternal[]; + }> { + const flatStructure = this.flattenBodyStructure(bodyStructure); + const payloads: MailMessagePayloadExternal[] = []; + let hasAttachment = false; + for (const part of flatStructure) { + if (part.type.startsWith('text/')) { + const content = await this.getTextPartContent({ client, uid, part }); + if (content) { + payloads.push({ + id: part.id || part.part, + mimeType: part.type, + filename: null, + attachmentId: part.part, + content: cleanString(content), + size: part.size, + }); + } + } else { + const dispositionParameters = part.dispositionParameters as unknown as Record | null; + const parameters = part.parameters as unknown as Record | null; + let filename: string | null = null; + if (dispositionParameters?.['filename']) { + filename = StringUtil.decodeRFC5987(dispositionParameters['filename']); + } + if (!filename && parameters?.['name']) { + filename = StringUtil.decodeMimeWord(parameters['name']); + } + if (filename) { + hasAttachment = true; + payloads.push({ + id: part.id || part.part, + mimeType: part.type, + filename: filename, + attachmentId: part.part, + content: null, + size: part.size, + }); + } + } + } + return { hasAttachment, payloads }; + } + + private flattenBodyStructure(bodyStructure: MessageStructureObject): MessageStructureObject[] { + return bodyStructure.childNodes + ? [bodyStructure, ...bodyStructure.childNodes.flatMap((child) => this.flattenBodyStructure(child))] + : [bodyStructure]; + } + + private async getTextPartContent({ + client, + uid, + part, + }: { + client: ImapFlow; + uid: number; + part: MessageStructureObject; + }): Promise { + const partData = await withTimeout( + client.download(`${uid}`, part.part || '1', { uid: true }), + this._config.partLoadTimeout, + null, + ); + if (partData) { + const { content, meta } = partData; + const chunks = []; + for await (const chunk of content) { + chunks.push(chunk); + } + const buffer = Buffer.concat(chunks); + const decodedBuffer = this.decodeBuffer({ buffer, encoding: part.encoding }); + return iconv.decode(decodedBuffer, meta.charset || 'utf-8'); + } + + return null; + } + + private decodeBuffer({ encoding, buffer }: { encoding: string; buffer: Buffer }): Buffer { + switch (encoding.toUpperCase()) { + case 'BASE64': + return buffer; + case 'QUOTED-PRINTABLE': + return Buffer.from(quotedPrintable.decode(buffer.toString())); + case '7BIT': + case '8BIT': + case 'BINARY': + default: + return buffer; + } + } + + private getMessageSnippet(payloads: MailMessagePayloadExternal[]): string { + const plain = payloads.find((p) => p.mimeType === 'text/plain'); + if (plain && plain.content) { + return plain.content.trim().substring(0, 150).trim(); + } + + const html = payloads.find((p) => p.mimeType === 'text/html'); + if (html && html.content) { + return convert(html.content)?.trim()?.substring(0, 150)?.trim(); + } + + return ''; + } + + private async handleError({ error }: WithConnectionErrorParams): Promise { + return { result: false, message: error['responseText'] }; + } + + private getHeaderValue(parsed: ParsedMail, key: string): T | null { + return parsed.headers.has(key) ? (parsed.headers.get(key) as T) : null; + } + + private createExternalId({ folderName, id }: { folderName: string; id: number | string }): string { + return `${folderName}-${id}`; + } + + private parseExternalId(externalId: string): [string, number] { + const lastDashIndex = externalId.lastIndexOf('-'); + if (lastDashIndex === -1) { + throw new Error(`Invalid externalId format: ${externalId}`); + } + const folderName = externalId.substring(0, lastDashIndex); + const idStr = externalId.substring(lastDashIndex + 1); + const id = Number(idStr); + if (isNaN(id)) { + throw new Error(`Invalid message ID in externalId: ${externalId}`); + } + return [folderName, id]; + } + + private async sendMessage({ + email, + settings, + mail, + path, + }: { + email: string; + settings: MailboxSettingsImapflow; + mail: Mail.Options; + path: string; + }): Promise { + return this.withConnection({ + email: email, + settings, + action: async ({ client }) => { + const message = await this.mailMessageBuilder.createRawMessage(mail, 'utf-8'); + const result = await client.append(path, message, ['\\Seen']); + return { message, uid: result?.uid }; + }, + error: async () => null as SendMessageResult, + }); + } + + private async createExternalMessage({ + id, + raw, + folderName, + isSeen = true, + }: { + id: number | string; + raw: Source; + folderName: string; + isSeen?: boolean; + }): Promise { + const parsed = await simpleParser(raw); + if (!parsed) { + return null; + } + + const { from, to, replyTo, cc, subject, date, messageId, inReplyTo, references, text, html, attachments, headers } = + parsed; + const messageDate = date ?? DateUtil.now(); + const snippet = text ? text.substring(0, 150).trim() : ''; + const entityId: number | null = headers.has(HEADER_ENTITY_ID) + ? parseInt(headers.get(HEADER_ENTITY_ID) as string) + : null; + const payloads = this.formatAsExternalPayload(text, html, attachments); + return { + id: this.createExternalId({ folderName, id }), + threadId: null, + snippet, + sentFrom: this.convertAddress(from), + sentTo: this.convertAddress(to), + replyTo: this.convertAddress(replyTo), + cc: this.convertAddress(cc), + subject, + date: messageDate, + hasAttachment: attachments && attachments.length > 0, + messageId, + inReplyTo: inReplyTo, + references: typeof references === 'string' ? [references] : references, + isSeen, + entityId, + folders: [folderName], + payloads, + }; + } + + private convertAddress(addresses: AddressObject | AddressObject[] | null | undefined): EmailAddress | null { + if (!addresses) { + return null; + } + + if (Array.isArray(addresses)) { + const texts: string[] = []; + const values: EmailAddressValue[] = []; + for (const address of addresses) { + if (address) { + texts.push(address.text); + values.push(...address.value.filter((v) => v?.address).map((v) => ({ address: v.address, name: v.name }))); + } + } + return { text: texts.join(', '), values }; + } + + return { + text: addresses.text, + values: addresses.value.filter((v) => v?.address).map((v) => ({ address: v.address, name: v.name })), + }; + } + + private formatAsExternalPayload( + text: string, + html: string | boolean, + attachments: object[], + ): MailMessagePayloadExternal[] { + const payloads: MailMessagePayloadExternal[] = []; + if (text && text.trim()) { + payloads.push({ + id: null, + mimeType: 'text/plain', + filename: null, + attachmentId: null, + content: text, + size: text.length, + }); + } + if (html && typeof html === 'string') { + payloads.push({ + id: null, + mimeType: 'text/html', + filename: null, + attachmentId: null, + content: html.trim(), + size: html.trim().length, + }); + } + if (attachments && attachments.length > 0) { + attachments.forEach((a) => + payloads.push({ + id: a['partId'], + mimeType: a['contentType'], + filename: a['filename'], + attachmentId: a['id'], + content: null, + size: a['size'], + }), + ); + } + return payloads; + } + + async setSeen({ + mailbox, + seen, + messages, + }: { + accountId: number; + mailbox: Mailbox; + seen: boolean; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.changeFlags(mailbox, messages, ['\\Seen'], seen ? 'add' : 'del'); + } else { + throw new NotImplementedException(); + } + } + private async changeFlags( + mailbox: Mailbox, + groupedMessages: FolderMessages[], + flags: string[], + action: 'add' | 'del', + ): Promise { + return this.withConnection({ + accountId: mailbox.accountId, + email: mailbox.email, + mailboxId: mailbox.id, + action: async ({ client }) => { + for (const { folderId, messageIds } of groupedMessages) { + const parsedIds = messageIds.map((m) => this.parseExternalId(m)[1]); + await this.withMailbox({ + mailboxId: mailbox.id, + client, + path: folderId, + action: async ({ client }) => { + switch (action) { + case 'add': + await client.messageFlagsAdd(parsedIds, flags, { uid: true }); + break; + case 'del': + await client.messageFlagsRemove(parsedIds, flags, { uid: true }); + break; + } + }, + }); + } + return true; + }, + error: async () => { + return false; + }, + }); + } + + async trash({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox, messages, MailboxFolderType.Trash); + } else { + throw new NotImplementedException(); + } + } + async untrash({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox, messages, MailboxFolderType.Inbox); + } else { + throw new NotImplementedException(); + } + } + + async spam({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox, messages, MailboxFolderType.Junk); + } else { + throw new NotImplementedException(); + } + } + async unspam({ + mailbox, + messages, + }: { + mailbox: Mailbox; + messages: { threadId: string } | FolderMessages[]; + }): Promise { + if (Array.isArray(messages)) { + return this.moveMessages(mailbox, messages, MailboxFolderType.Inbox); + } else { + throw new NotImplementedException(); + } + } + + private async moveMessages( + mailbox: Mailbox, + groupedMessages: FolderMessages[], + type: MailboxFolderType, + ): Promise { + return this.withConnection({ + accountId: mailbox.accountId, + email: mailbox.email, + mailboxId: mailbox.id, + action: async ({ client }) => { + const toFolder = await this.mailboxFolderService.findOne({ + accountId: mailbox.accountId, + mailboxId: mailbox.id, + type, + }); + if (toFolder) { + for (const { folderId, messageIds } of groupedMessages) { + const parsedIds = messageIds.map((m) => this.parseExternalId(m)[1]); + await this.withMailbox({ + mailboxId: mailbox.id, + client, + path: folderId, + action: async ({ client }) => { + await client.messageMove(parsedIds, toFolder.externalId, { uid: true }); + }, + }); + } + } + return true; + }, + error: async () => { + return false; + }, + }); + } +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/index.ts b/backend/src/modules/mail/mail-providers/imapflow/index.ts new file mode 100644 index 0000000..0f01405 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/index.ts @@ -0,0 +1,7 @@ +export * from './dto'; +export * from './entities'; +export * from './errors'; +export * from './imapflow.controller'; +export * from './imapflow.module'; +export * from './imapflow.service'; +export * from './types'; diff --git a/backend/src/modules/mail/mail-providers/imapflow/types/imapflow-sync-info.ts b/backend/src/modules/mail/mail-providers/imapflow/types/imapflow-sync-info.ts new file mode 100644 index 0000000..a18960e --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/types/imapflow-sync-info.ts @@ -0,0 +1,4 @@ +export class ImapflowSyncInfo { + boxName: string; + uidnext: number; +} diff --git a/backend/src/modules/mail/mail-providers/imapflow/types/index.ts b/backend/src/modules/mail/mail-providers/imapflow/types/index.ts new file mode 100644 index 0000000..ae78418 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/types/index.ts @@ -0,0 +1,2 @@ +export * from './imapflow-sync-info'; +export * from './mailbox-imapflow'; diff --git a/backend/src/modules/mail/mail-providers/imapflow/types/mailbox-imapflow.ts b/backend/src/modules/mail/mail-providers/imapflow/types/mailbox-imapflow.ts new file mode 100644 index 0000000..1866020 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/imapflow/types/mailbox-imapflow.ts @@ -0,0 +1,21 @@ +import { Mailbox } from '@/Mailing/mailbox/entities'; + +import { MailboxImapflowDto } from '../dto'; +import { MailboxSettingsImapflow } from '../entities'; + +export class MailboxImapflow { + private readonly mailbox: Mailbox; + private readonly settings: MailboxSettingsImapflow; + + constructor({ mailbox, settings }: { mailbox: Mailbox; settings: MailboxSettingsImapflow }) { + this.mailbox = mailbox; + this.settings = settings; + } + + toDto(): MailboxImapflowDto { + return { + ...this.mailbox.toDto(), + settings: this.settings.toDto(), + }; + } +} diff --git a/backend/src/modules/mail/mail-providers/index.ts b/backend/src/modules/mail/mail-providers/index.ts new file mode 100644 index 0000000..cf00a59 --- /dev/null +++ b/backend/src/modules/mail/mail-providers/index.ts @@ -0,0 +1,2 @@ +export * from './imapflow'; +export * from './mail-providers.module'; diff --git a/backend/src/modules/mail/mail-providers/mail-providers.module.ts b/backend/src/modules/mail/mail-providers/mail-providers.module.ts new file mode 100644 index 0000000..4248adb --- /dev/null +++ b/backend/src/modules/mail/mail-providers/mail-providers.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; + +import { ImapflowModule } from './imapflow'; + +@Module({ + imports: [ImapflowModule], +}) +export class MailProvidersModule {} diff --git a/backend/src/modules/mail/mail.module.ts b/backend/src/modules/mail/mail.module.ts new file mode 100644 index 0000000..84ac532 --- /dev/null +++ b/backend/src/modules/mail/mail.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { MailMessageScheduledModule } from './mail-message-scheduled'; +import { MailProvidersModule } from './mail-providers'; + +@Module({ + imports: [MailMessageScheduledModule, MailProvidersModule], +}) +export class MailModule {} diff --git a/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.controller.ts b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.controller.ts new file mode 100644 index 0000000..865eb70 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.controller.ts @@ -0,0 +1,67 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CreateChatMessageScheduledDto, ChatMessageScheduledDto, ChatMessageScheduledFilterDto } from './dto'; +import { ChatMessageScheduledService } from './chat-message-scheduled.service'; + +@ApiTags('multichat/messages/scheduled') +@Controller('/chat/messages/scheduled') +@JwtAuthorized({ access: { adminOnly: true } }) +@TransformToDto() +export class ChatMessageScheduledController { + constructor(private readonly service: ChatMessageScheduledService) {} + + @ApiOperation({ summary: 'Create scheduled message', description: 'Create scheduled message' }) + @ApiBody({ description: 'Data for creating scheduled message', type: CreateChatMessageScheduledDto }) + @ApiCreatedResponse({ description: 'Scheduled message', type: ChatMessageScheduledDto }) + @Post() + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateChatMessageScheduledDto) { + return this.service.create({ accountId, dto }); + } + + @ApiOperation({ summary: 'Get scheduled messages', description: 'Get scheduled messages' }) + @ApiOkResponse({ description: 'Scheduled messages', type: [ChatMessageScheduledDto] }) + @Get() + public async findMany(@CurrentAuth() { accountId }: AuthData, @Query() paging: PagingQuery) { + return this.service.findMany({ filter: { accountId }, paging }); + } + + @ApiOperation({ summary: 'Get scheduled message', description: 'Get scheduled message' }) + @ApiParam({ name: 'messageId', description: 'Scheduled message ID' }) + @ApiOkResponse({ description: 'Scheduled message', type: ChatMessageScheduledDto }) + @Get(':messageId') + public async findOne(@CurrentAuth() { accountId }: AuthData, @Param('messageId', ParseIntPipe) messageId: number) { + return this.service.findOne({ accountId, messageId }); + } + + @ApiOperation({ summary: 'Search scheduled messages', description: 'Search scheduled messages' }) + @ApiBody({ description: 'Data for searching scheduled messages', type: ChatMessageScheduledFilterDto }) + @ApiOkResponse({ description: 'Scheduled messages', type: [ChatMessageScheduledDto] }) + @Post('search') + public async search( + @CurrentAuth() { accountId }: AuthData, + @Body() filter: ChatMessageScheduledFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.findMany({ filter: { accountId, ...filter }, paging }); + } + + @ApiOperation({ summary: 'Delete scheduled message', description: 'Delete scheduled message' }) + @ApiParam({ name: 'messageId', description: 'Scheduled message ID' }) + @ApiOkResponse() + @Delete(':messageId') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('messageId', ParseIntPipe) messageId: number) { + return this.service.delete({ accountId, messageId }); + } + + @ApiOperation({ summary: 'Delete scheduled messages', description: 'Delete scheduled messages' }) + @ApiBody({ description: 'Data for deleting scheduled messages', type: ChatMessageScheduledFilterDto }) + @ApiOkResponse() + @Post('delete') + public async deleteMany(@CurrentAuth() { accountId }: AuthData, @Body() filter: ChatMessageScheduledFilterDto) { + return this.service.delete({ accountId, ...filter }); + } +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.handler.ts b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.handler.ts new file mode 100644 index 0000000..4f339c7 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.handler.ts @@ -0,0 +1,55 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; + +import { ActionChatSendSettings, AutomationJob, EntityTypeActionType, OnAutomationJob } from '@/modules/automation'; + +import { ChatMessageScheduledService } from './chat-message-scheduled.service'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; +} + +interface SendChatVariables { + accountId: number; + entity?: EntityVariables | null; + chatSettings?: ActionChatSendSettings | null; +} + +@Injectable() +export class ChatMessageScheduledHandler { + private readonly logger = new Logger(ChatMessageScheduledHandler.name); + constructor(private readonly service: ChatMessageScheduledService) {} + + @Cron(CronExpression.EVERY_MINUTE) + public async sendScheduledMessages() { + if (process.env.SCHEDULE_CHAT_SCHEDULED_DISABLE === 'true') return; + this.logger.log('Before: Sending scheduled messages'); + const processed = await this.service.processMessages(); + this.logger.log(`After: Sending scheduled messages. Processed: ${processed}`); + } + + @OnAutomationJob(EntityTypeActionType.ChatSendExternal) + async handleSendMessageJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.chatSettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, chatSettings } = job.variables; + + const messages = await this.service.processAutomation({ + accountId, + entityId: entity.id, + entityStageId: entity.stageId, + settings: chatSettings, + }); + + return { variables: { ...job.variables, messages: messages?.map((message) => message.toDto()) } }; + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + return { variables: job.variables }; + } + } +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.module.ts b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.module.ts new file mode 100644 index 0000000..74059ed --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.module.ts @@ -0,0 +1,34 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { ChatModule } from '../chat/chat.module'; +import { ChatMessageModule } from '../chat-message/chat-message.module'; +import { ChatProviderModule } from '../chat-provider/chat-provider.module'; +import { ChatUserModule } from '../chat-user/chat-user.module'; +import { ProvidersModule } from '../providers/providers.module'; +import { DocumentsModule } from '@/modules/documents/documents.module'; + +import { ChatMessageScheduled } from './entities'; +import { ChatMessageScheduledService } from './chat-message-scheduled.service'; +import { ChatMessageScheduledController } from './chat-message-scheduled.controller'; +import { ChatMessageScheduledHandler } from './chat-message-scheduled.handler'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ChatMessageScheduled]), + IAMModule, + forwardRef(() => CrmModule), + ChatModule, + ChatMessageModule, + ChatProviderModule, + ChatUserModule, + ProvidersModule, + DocumentsModule, + ], + providers: [ChatMessageScheduledService, ChatMessageScheduledHandler], + controllers: [ChatMessageScheduledController], +}) +export class ChatMessageScheduledModule {} diff --git a/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.service.ts b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.service.ts new file mode 100644 index 0000000..890d62e --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/chat-message-scheduled.service.ts @@ -0,0 +1,436 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import Handlebars from 'handlebars'; + +import { DatePeriod, DatePeriodFilter, DateUtil, isUnique, PagingQuery } from '@/common'; +import { AccountService } from '@/modules/iam/account/account.service'; +import { UserService } from '@/modules/iam/user/user.service'; +import { ActionChatSendSettings } from '@/modules/automation'; +import { EntityCategory } from '@/CRM/common'; +import { EntityTypeService } from '@/CRM/entity-type/entity-type.service'; +import { Entity } from '@/CRM/Model/Entity/Entity'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { FieldType } from '@/modules/entity/entity-field/common'; +import { FieldService } from '@/modules/entity/entity-field/field'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value'; +import { DocumentGenerationService } from '@/modules/documents/document-generation/document-generation.service'; + +import { ChatService } from '../chat/services'; +import { ChatMessageService } from '../chat-message/services'; +import { ChatProvider, ChatProviderService } from '../chat-provider'; +import { ChatUserService } from '../chat-user'; +import { ChatProviderProxyService } from '../providers/chat-provider-proxy.service'; + +import { ChatMessageScheduled } from './entities'; +import { CreateChatMessageScheduledDto } from './dto'; + +interface FindFilter { + accountId: number; + messageId?: number | number[]; + providerId?: number | number[]; + sendFrom?: number | number[]; + entityId?: number | number[]; + isSent?: boolean; + sentAt?: DatePeriodFilter; + createdAt?: DatePeriodFilter; + message?: string; + sentTo?: string; +} + +interface ProviderMessage { + accountId: number; + providerId: number; + messageId: number; +} + +const DefaultLimit = 10; + +@Injectable() +export class ChatMessageScheduledService { + private readonly logger = new Logger(ChatMessageScheduledService.name); + + constructor( + @InjectRepository(ChatMessageScheduled) + private readonly repository: Repository, + private readonly accountService: AccountService, + private readonly userService: UserService, + private readonly entityTypeService: EntityTypeService, + private readonly entityService: EntityService, + private readonly fieldService: FieldService, + private readonly fieldValueService: FieldValueService, + private readonly chatProviderProxyService: ChatProviderProxyService, + private readonly chatProviderService: ChatProviderService, + private readonly chatUserService: ChatUserService, + private readonly chatService: ChatService, + private readonly chatMessageService: ChatMessageService, + private readonly documentGenerationService: DocumentGenerationService, + ) {} + + async create({ + accountId, + dto, + }: { + accountId: number; + dto: CreateChatMessageScheduledDto; + }): Promise { + return this.repository.save(ChatMessageScheduled.fromDto(accountId, dto)); + } + + async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + + async findMany({ filter, paging }: { filter: FindFilter; paging?: PagingQuery }): Promise { + return this.createFindQb(filter).orderBy('created_at', 'DESC').limit(paging.take).offset(paging.skip).getMany(); + } + + async delete(filter: FindFilter): Promise { + await this.createFindQb(filter).delete().execute(); + } + + async processMessages(): Promise { + const queue = await this.repository + .createQueryBuilder() + .select('account_id', 'accountId') + .addSelect('provider_id', 'providerId') + .addSelect('min(id)', 'messageId') + .where('sent_at is null') + .groupBy('account_id') + .addGroupBy('provider_id') + .getRawMany(); + + return (await Promise.all(queue.map(async (item) => item.messageId && (await this.processMessage(item))))).filter( + Boolean, + ).length; + } + + async processAutomation({ + accountId, + entityId, + entityStageId, + settings, + }: { + accountId: number; + entityId: number; + entityStageId: number | null | undefined; + settings: ActionChatSendSettings; + }): Promise { + const messages: ChatMessageScheduled[] = []; + + const entity = await this.entityService.findOne(accountId, { entityId }); + if (entity && (!entity.stageId || settings.allowAnyStage || entity.stageId === entityStageId)) { + const hasOptions = Boolean(settings.options); + if (!hasOptions || settings.options?.main) { + const mainMessage = await this.createAutomationMessage({ + accountId, + entity, + settings, + onlyFirst: Boolean(settings.options?.main?.onlyFirstValue), + }); + if (mainMessage) messages.push(mainMessage); + } + + let primaryCompany = Boolean(settings.options?.company?.onlyFirstEntity); + let primaryContact = Boolean(settings.options?.contact?.onlyFirstEntity); + const entities = await this.entityService.findLinkedEntities({ accountId, entityId }); + + const messagePromises = entities.map(async (entity) => { + const entityType = await this.entityTypeService.findOne(accountId, { id: entity.entityTypeId }); + let send = [EntityCategory.CONTACT, EntityCategory.COMPANY].includes(entityType.entityCategory); + let onlyFirst = false; + + if (send && hasOptions) { + if (entityType.entityCategory === EntityCategory.COMPANY) { + send = settings.options.company && (!settings.options.company?.onlyFirstEntity || primaryCompany); + primaryCompany = false; + onlyFirst = Boolean(settings.options.company?.onlyFirstValue); + } else if (entityType.entityCategory === EntityCategory.CONTACT) { + send = settings.options.contact && (!settings.options.contact?.onlyFirstEntity || primaryContact); + primaryContact = false; + onlyFirst = Boolean(settings.options.contact?.onlyFirstValue); + } + } + + if (send) { + return this.createAutomationMessage({ accountId, entity, settings, onlyFirst }); + } + return null; + }); + + messages.push(...(await Promise.all(messagePromises)).filter(Boolean)); + + if (settings.phoneNumbers?.length) { + const messagePromises = settings.phoneNumbers + .filter(isUnique) + .map((phoneNumber) => this.createAutomationMessage({ accountId, entity, settings, phoneNumber })); + messages.push(...(await Promise.all(messagePromises)).filter(Boolean)); + } + } + return messages; + } + + private async processMessage({ accountId, providerId, messageId }: ProviderMessage): Promise { + const maxSendAt = await this.repository + .createQueryBuilder() + .select('max(sent_at)', 'max') + .where('account_id = :accountId', { accountId }) + .andWhere(`provider_id = :providerId`, { providerId }) + .andWhere('sent_at is not null') + .getRawOne<{ sentAt: Date }>(); + + const provider = await this.chatProviderService.findOne(accountId, null, { providerId }); + if (!maxSendAt?.sentAt || this.canSend({ lastSentAt: maxSendAt.sentAt, limit: provider.messagePerDay })) { + this.sendMessage({ accountId, provider, messageId }); + return true; + } + return false; + } + + private canSend({ lastSentAt, limit }: { lastSentAt: Date; limit?: number | null }): boolean { + const secondsPerEmail = 86400 / (limit ?? DefaultLimit); + const diff = DateUtil.diff({ startDate: lastSentAt, endDate: DateUtil.now(), unit: 'second' }); + + return diff > secondsPerEmail; + } + + private async sendMessage({ + accountId, + provider, + messageId, + }: { + accountId: number; + provider: ChatProvider; + messageId: number; + }) { + const message = await this.findOne({ accountId, messageId }); + + if (message?.entityId) { + const chatIds = await this.getChatId({ accountId, userId: message.sendFrom, provider, message }); + if (chatIds.length) { + const account = await this.accountService.findOne({ accountId }); + const user = await this.userService.findOne({ accountId, id: message.sendFrom }); + + await Promise.all( + chatIds.map(async (chatId) => { + try { + await this.chatMessageService.create(account, user, chatId, { text: message.message }); + } catch (e) { + this.logger.warn(`Send scheduled message with id ${messageId} error: ${e.toString()}`); + } + }), + ); + } + } else if (message.phoneNumber) { + await this.chatProviderProxyService.sendDirectMessage({ + accountId, + provider, + phone: message.phoneNumber, + message: message.message, + }); + } + + await this.repository.update({ accountId, id: messageId }, { sentAt: DateUtil.now() }); + } + + private async getChatId({ + accountId, + userId, + provider, + message, + }: { + accountId: number; + userId: number; + provider: ChatProvider; + message: ChatMessageScheduled; + }): Promise { + const chats = await this.chatService.findMany({ + accountId, + filter: { providerId: message.providerId, entityId: message.entityId }, + }); + if (chats.length) { + return !message.onlyFirst ? chats.map((c) => c.id) : [chats[0].id]; + } else if (provider.canSendByPhone()) { + const entity = await this.entityService.findOne(accountId, { entityId: message.entityId }); + const phones = await this.getPhones({ accountId, entity, onlyFirst: message.onlyFirst }); + const chatIds: number[] = []; + for (const phone of phones) { + const externalChatUser = await this.chatUserService.findOne(accountId, { + providerId: provider.id, + externalId: phone, + }); + if (externalChatUser) { + await this.chatUserService.addUsers({ accountId, chatId: externalChatUser.chatId, userIds: [userId] }); + + chatIds.push(externalChatUser.chatId); + } else { + const chat = await this.chatService.createExternalChat(accountId, userId, { + providerId: provider.id, + title: entity.name, + entityId: entity.id, + externalUser: { + firstName: entity.name, + externalId: phone, + phone: phone, + }, + }); + + chatIds.push(chat.id); + } + } + + return chatIds; + } + + return []; + } + + private async getPhones({ + accountId, + entity, + onlyFirst, + }: { + accountId: number; + entity: Entity; + onlyFirst: boolean; + }): Promise { + const fieldIds = await this.fieldService.findManyIds({ + accountId, + entityTypeId: entity.entityTypeId, + type: FieldType.Phone, + }); + if (fieldIds.length) { + if (onlyFirst) { + for (const fieldId of fieldIds) { + const fieldValue = await this.fieldValueService.findOne({ accountId, entityId: entity.id, fieldId }); + if (fieldValue) { + const value = fieldValue + .getValue() + .filter((fv) => fv !== '') + .filter(isUnique); + if (value.length) { + return [value[0]]; + } + } + } + } else { + const fieldValues = await this.fieldValueService.findMany({ + accountId, + entityId: entity.id, + fieldId: fieldIds, + }); + return fieldValues + .map((fv) => fv.getValue()) + .flat() + .filter((fv) => fv !== '') + .filter(isUnique); + } + } + + return []; + } + + private async createAutomationMessage({ + accountId, + entity, + settings, + onlyFirst = false, + phoneNumber, + }: { + accountId: number; + entity: Entity; + settings: ActionChatSendSettings; + onlyFirst?: boolean; + phoneNumber?: string; + }): Promise { + const data = await this.documentGenerationService.getDataForGeneration({ accountId, entityId: entity.id }); + data['contact_name'] = entity.name; + const message = Handlebars.compile(settings.message)(data); + + return this.create({ + accountId, + dto: { + providerId: settings.providerId, + sendFrom: settings.userId ?? entity.responsibleUserId, + message, + entityId: !phoneNumber ? entity.id : undefined, + phoneNumber, + onlyFirst, + }, + }); + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId: filter.accountId }); + + if (filter?.messageId) { + if (Array.isArray(filter.messageId)) { + qb.andWhere('id IN (:...ids)', { ids: filter.messageId }); + } else { + qb.andWhere('id = :id', { id: filter.messageId }); + } + } + + if (filter?.providerId) { + if (Array.isArray(filter.providerId)) { + qb.andWhere('provider_id IN (:...providerIds)', { providerIds: filter.providerId }); + } else { + qb.andWhere('provider_id = :providerId', { providerId: filter.providerId }); + } + } + + if (filter?.sendFrom) { + if (Array.isArray(filter.sendFrom)) { + qb.andWhere('send_from IN (:...sendFrom)', { sendFrom: filter.sendFrom }); + } else { + qb.andWhere('send_from = :sendFrom', { sendFrom: filter.sendFrom }); + } + } + + if (filter?.entityId) { + if (Array.isArray(filter.entityId)) { + qb.andWhere('entity_id IN (:...entityIds)', { entityIds: filter.entityId }); + } else { + qb.andWhere('entity_id = :entityId', { entityId: filter.entityId }); + } + } + + if (filter?.isSent !== undefined) { + if (filter.isSent) { + qb.andWhere('sent_at IS NOT NULL'); + } else { + qb.andWhere('sent_at IS NULL'); + } + } + + if (filter.sentAt) { + const dates = DatePeriod.fromFilter(filter.sentAt); + if (dates.from) { + qb.andWhere('sent_at >= :sentAtFrom', { sentAtFrom: dates.from }); + } + if (dates.to) { + qb.andWhere('sent_at <= :sentAtTo', { sentAtTo: dates.to }); + } + } + + if (filter.createdAt) { + const dates = DatePeriod.fromFilter(filter.createdAt); + if (dates.from) { + qb.andWhere('created_at >= :createdAtFrom', { createdAtFrom: dates.from }); + } + if (dates.to) { + qb.andWhere('created_at <= :createdAtTo', { createdAtTo: dates.to }); + } + } + + if (filter.message) { + qb.andWhere('message ILIKE :content', { content: `%${filter.message}%` }); + } + + if (filter.sentTo) { + qb.andWhere('send_to ILIKE :sentTo', { sentTo: `%${filter.sentTo}%` }); + } + + return qb; + } +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled-filter.dto.ts b/backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled-filter.dto.ts new file mode 100644 index 0000000..a6bfef8 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled-filter.dto.ts @@ -0,0 +1,44 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; + +export class ChatMessageScheduledFilterDto { + @ApiPropertyOptional({ description: 'Provider IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + providerId?: number[]; + + @ApiPropertyOptional({ description: 'User IDs who sent the message' }) + @IsOptional() + @IsNumber({}, { each: true }) + sendFrom?: number[]; + + @ApiPropertyOptional({ description: 'Entity IDs' }) + @IsOptional() + @IsNumber({}, { each: true }) + entityId?: number[]; + + @ApiPropertyOptional({ description: 'Is the message sent' }) + @IsOptional() + @IsBoolean() + isSent?: boolean; + + @ApiPropertyOptional({ description: 'Date and time when the message was sent' }) + @IsOptional() + sentAt?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Date and time when the message was created' }) + @IsOptional() + createdAt?: DatePeriodFilter; + + @ApiPropertyOptional({ description: 'Message text' }) + @IsOptional() + @IsString() + message?: string; + + @ApiPropertyOptional({ description: 'Recipient (phone numbers)' }) + @IsOptional() + @IsString() + sentTo?: string; +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled.dto.ts b/backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled.dto.ts new file mode 100644 index 0000000..939872b --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/dto/chat-message-scheduled.dto.ts @@ -0,0 +1,43 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ChatMessageScheduledDto { + @ApiProperty({ description: 'Scheduled message ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'User ID who sent the message' }) + @IsNumber() + sendFrom: number; + + @ApiProperty({ description: 'Date and time when the message was created' }) + @IsString() + createdAt: string; + + @ApiProperty({ description: 'Chat Provider ID' }) + @IsNumber() + providerId: number; + + @ApiProperty({ description: 'Message text' }) + @IsString() + message: string; + + @ApiPropertyOptional({ nullable: true, description: 'Entity ID associated with the message' }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Phone number associated with the message' }) + @IsOptional() + @IsString() + phoneNumber?: string | null; + + @ApiProperty({ description: 'Send only to first chat' }) + @IsBoolean() + onlyFirst: boolean; + + @ApiPropertyOptional({ description: 'Date and time when the message was sent', nullable: true }) + @IsOptional() + @IsNumber() + sentAt?: string | null; +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/dto/create-chat-message-scheduled.dto.ts b/backend/src/modules/multichat/chat-message-scheduled/dto/create-chat-message-scheduled.dto.ts new file mode 100644 index 0000000..ed3ce3d --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/dto/create-chat-message-scheduled.dto.ts @@ -0,0 +1,17 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +import { ChatMessageScheduledDto } from './chat-message-scheduled.dto'; + +export class CreateChatMessageScheduledDto extends PickType(ChatMessageScheduledDto, [ + 'providerId', + 'sendFrom', + 'message', + 'entityId', + 'phoneNumber', +] as const) { + @ApiPropertyOptional({ description: 'Send only to first chat' }) + @IsOptional() + @IsBoolean() + onlyFirst?: boolean; +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/dto/index.ts b/backend/src/modules/multichat/chat-message-scheduled/dto/index.ts new file mode 100644 index 0000000..cefbf65 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/dto/index.ts @@ -0,0 +1,3 @@ +export * from './chat-message-scheduled-filter.dto'; +export * from './chat-message-scheduled.dto'; +export * from './create-chat-message-scheduled.dto'; diff --git a/backend/src/modules/multichat/chat-message-scheduled/entities/chat-message-scheduled.entity.ts b/backend/src/modules/multichat/chat-message-scheduled/entities/chat-message-scheduled.entity.ts new file mode 100644 index 0000000..87b5610 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/entities/chat-message-scheduled.entity.ts @@ -0,0 +1,84 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { ChatMessageScheduledDto, CreateChatMessageScheduledDto } from '../dto'; + +@Entity() +export class ChatMessageScheduled { + @PrimaryGeneratedColumn('identity') + id: number | undefined; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + @Column() + sendFrom: number; + + @Column() + providerId: number; + + @Column() + message: string; + + @Column({ nullable: true }) + entityId: number | null; + + @Column({ nullable: true }) + phoneNumber: string | null; + + @Column({ default: false }) + onlyFirst: boolean; + + @Column({ nullable: true }) + sentAt: Date | null; + + constructor( + accountId: number, + sendFrom: number, + providerId: number, + message: string, + entityId: number | null, + phoneNumber: string | null, + onlyFirst: boolean, + createdAt: Date = new Date(), + sentAt: Date | null = null, + ) { + this.accountId = accountId; + this.sendFrom = sendFrom; + this.createdAt = createdAt; + this.providerId = providerId; + this.message = message; + this.entityId = entityId; + this.phoneNumber = phoneNumber; + this.onlyFirst = onlyFirst; + this.sentAt = sentAt; + } + + public static fromDto(accountId: number, dto: CreateChatMessageScheduledDto): ChatMessageScheduled { + return new ChatMessageScheduled( + accountId, + dto.sendFrom, + dto.providerId, + dto.message, + dto.entityId, + dto.phoneNumber, + dto.onlyFirst ?? false, + ); + } + + public toDto(): ChatMessageScheduledDto { + return { + id: this.id, + sendFrom: this.sendFrom, + createdAt: this.createdAt.toISOString(), + providerId: this.providerId, + message: this.message, + onlyFirst: this.onlyFirst, + entityId: this.entityId, + phoneNumber: this.phoneNumber, + sentAt: this.sentAt?.toISOString(), + }; + } +} diff --git a/backend/src/modules/multichat/chat-message-scheduled/entities/index.ts b/backend/src/modules/multichat/chat-message-scheduled/entities/index.ts new file mode 100644 index 0000000..93e04e9 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/entities/index.ts @@ -0,0 +1 @@ +export * from './chat-message-scheduled.entity'; diff --git a/backend/src/modules/multichat/chat-message-scheduled/index.ts b/backend/src/modules/multichat/chat-message-scheduled/index.ts new file mode 100644 index 0000000..311f657 --- /dev/null +++ b/backend/src/modules/multichat/chat-message-scheduled/index.ts @@ -0,0 +1,6 @@ +export * from './chat-message-scheduled.controller'; +export * from './chat-message-scheduled.handler'; +export * from './chat-message-scheduled.module'; +export * from './chat-message-scheduled.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/multichat/chat-message/chat-message.controller.ts b/backend/src/modules/multichat/chat-message/chat-message.controller.ts new file mode 100644 index 0000000..2ddba9b --- /dev/null +++ b/backend/src/modules/multichat/chat-message/chat-message.controller.ts @@ -0,0 +1,119 @@ +import { Body, Controller, Delete, Get, Param, ParseEnumPipe, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, PagingQuery } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ChatMessageStatus } from '../common'; +import { ChatMessageDto } from './dto/chat-message.dto'; +import { ChatMessagesFilterDto } from './dto/chat-messages-filter.dto'; +import { ChatMessagesResultDto } from './dto/chat-messages-result.dto'; +import { SendChatMessageDto } from './dto/send-chat-message.dto'; +import { ChatMessageService } from './services/chat-message.service'; + +@ApiTags('multichat/messages') +@Controller('/chat/chats/:chatId/messages') +@JwtAuthorized({ prefetch: { account: true, user: true } }) +@TransformToDto() +export class ChatMessageController { + constructor(private readonly service: ChatMessageService) {} + + @ApiCreatedResponse({ description: 'Create chat message', type: ChatMessageDto }) + @Post() + public async create( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Body() dto: SendChatMessageDto, + ) { + return await this.service.create(account, user, chatId, dto); + } + + @ApiCreatedResponse({ description: 'Get chat messages', type: ChatMessagesResultDto }) + @Get() + public async getMany( + @CurrentAuth() { account, userId }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Query() filter: ChatMessagesFilterDto, + @Query() paging: PagingQuery, + ) { + return await this.service.getMessagesForUI(account, userId, chatId, filter, paging); + } + + @ApiCreatedResponse({ description: 'Get chat message', type: ChatMessageDto }) + @Get(':messageId') + public async getOne( + @CurrentAuth() { account, userId }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return await this.service.getMessageDto(account, userId, chatId, messageId); + } + + @ApiCreatedResponse({ description: 'Update chat message', type: ChatMessageDto }) + @Put(':messageId') + public async update( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + @Body() dto: SendChatMessageDto, + ) { + return await this.service.update(account, user, chatId, messageId, dto); + } + + @ApiOkResponse({ description: 'Chat message deleted', type: Boolean }) + @Delete(':messageId') + public async delete( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ): Promise { + return await this.service.delete(account, user, chatId, messageId); + } + + @ApiCreatedResponse({ description: 'Update chat messages status', type: [ChatMessageDto] }) + @Post('status/:status') + public async updateMessagesStatus( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('status', new ParseEnumPipe(ChatMessageStatus)) status: ChatMessageStatus, + @Body('messageIds') messageIds: number[], + ) { + return await this.service.updateStatusBatch(account, user, chatId, messageIds, status); + } + + @ApiCreatedResponse({ description: 'Update chat message status', type: ChatMessageDto }) + @Put(':messageId/status/:status') + public async updateMessageStatus( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + @Param('status', new ParseEnumPipe(ChatMessageStatus)) status: ChatMessageStatus, + ) { + return await this.service.updateStatus(account, user, chatId, messageId, status); + } + + @ApiCreatedResponse({ description: 'React to chat message', type: ChatMessageDto }) + @Put(':messageId/react/:reaction') + public async reactMessage( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + @Param('reaction') reaction: string, + ) { + return await this.service.react(account, user, chatId, messageId, reaction); + } + + @ApiCreatedResponse({ description: 'Clear reaction to chat message', type: ChatMessageDto }) + @Put(':messageId/unreact/:reactionId') + public async unreactMessage( + @CurrentAuth() { account, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + @Param('reactionId', ParseIntPipe) reactionId: number, + ) { + return await this.service.unreact(account, user, chatId, messageId, reactionId); + } +} diff --git a/backend/src/modules/multichat/chat-message/chat-message.module.ts b/backend/src/modules/multichat/chat-message/chat-message.module.ts new file mode 100644 index 0000000..5942e1e --- /dev/null +++ b/backend/src/modules/multichat/chat-message/chat-message.module.ts @@ -0,0 +1,43 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; + +import { ChatModule } from '../chat/chat.module'; +import { ChatProviderModule } from '../chat-provider/chat-provider.module'; +import { ProvidersModule } from '../providers/providers.module'; +import { ChatUserModule } from '../chat-user'; + +import { ChatMessageFile } from './entities/chat-message-file.entity'; +import { ChatMessageReaction } from './entities/chat-message-reaction.entity'; +import { ChatMessageUserStatus } from './entities/chat-message-user-status.entity'; +import { ChatMessage } from './entities/chat-message.entity'; +import { ChatMessageFileService } from './services/chat-message-file.service'; +import { ChatMessageReactionService } from './services/chat-message-reaction.service'; +import { ChatMessageUserStatusService } from './services/chat-message-user-status.service'; +import { ChatMessageService } from './services/chat-message.service'; +import { ChatNotificationService } from './services/chat-notification.service'; +import { ChatMessageController } from './chat-message.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ChatMessage, ChatMessageFile, ChatMessageUserStatus, ChatMessageReaction]), + IAMModule, + StorageModule, + forwardRef(() => ChatModule), + ChatProviderModule, + ProvidersModule, + ChatUserModule, + ], + providers: [ + ChatMessageService, + ChatMessageUserStatusService, + ChatMessageFileService, + ChatMessageReactionService, + ChatNotificationService, + ], + controllers: [ChatMessageController], + exports: [ChatMessageService], +}) +export class ChatMessageModule {} diff --git a/backend/src/modules/multichat/chat-message/dto/chat-message-file.dto.ts b/backend/src/modules/multichat/chat-message/dto/chat-message-file.dto.ts new file mode 100644 index 0000000..7244a1b --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/chat-message-file.dto.ts @@ -0,0 +1,51 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ChatMessageFileDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + fileId: string | null; + + @ApiProperty() + @IsString() + fileName: string; + + @ApiProperty() + @IsNumber() + fileSize: number; + + @ApiProperty() + @IsString() + fileType: string; + + @ApiProperty() + @IsString() + downloadUrl: string; + + @ApiProperty() + @IsString() + createdAt: string; + + constructor( + id: number, + fileId: string | null, + fileName: string, + fileSize: number, + fileType: string, + createdAt: string, + downloadUrl: string, + ) { + this.id = id; + this.fileId = fileId; + this.fileName = fileName; + this.fileSize = fileSize; + this.fileType = fileType; + this.createdAt = createdAt; + this.downloadUrl = downloadUrl; + } +} diff --git a/backend/src/modules/multichat/chat-message/dto/chat-message-reaction.dto.ts b/backend/src/modules/multichat/chat-message/dto/chat-message-reaction.dto.ts new file mode 100644 index 0000000..989d1b5 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/chat-message-reaction.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class ChatMessageReactionDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + chatUserId: number; + + @ApiProperty() + @IsString() + reaction: string; + + constructor(id: number, chatUserId: number, reaction: string) { + this.id = id; + this.chatUserId = chatUserId; + this.reaction = reaction; + } +} diff --git a/backend/src/modules/multichat/chat-message/dto/chat-message-user-status.dto.ts b/backend/src/modules/multichat/chat-message/dto/chat-message-user-status.dto.ts new file mode 100644 index 0000000..509882f --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/chat-message-user-status.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { ChatMessageStatus } from '../../common/enums/chat-message-status.enum'; + +export class ChatMessageUserStatusDto { + @ApiProperty() + chatUserId: number; + + @ApiProperty() + status: ChatMessageStatus; + + @ApiProperty() + createdAt: string; + + constructor(chatUserId: number, status: ChatMessageStatus, createdAt: string) { + this.chatUserId = chatUserId; + this.status = status; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/multichat/chat-message/dto/chat-message.dto.ts b/backend/src/modules/multichat/chat-message/dto/chat-message.dto.ts new file mode 100644 index 0000000..81e8162 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/chat-message.dto.ts @@ -0,0 +1,67 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; + +import { ChatMessageFileDto } from './chat-message-file.dto'; +import { ChatMessageReactionDto } from './chat-message-reaction.dto'; +import { ChatMessageUserStatusDto } from './chat-message-user-status.dto'; + +export class ChatMessageDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + chatId: number; + + @ApiProperty() + @IsNumber() + chatUserId: number; + + @ApiProperty() + @IsString() + text: string; + + @ApiProperty({ type: [ChatMessageUserStatusDto] }) + @IsArray() + statuses: ChatMessageUserStatusDto[]; + + @ApiProperty({ type: [ChatMessageFileDto] }) + @IsArray() + files: ChatMessageFileDto[]; + + @ApiProperty({ type: ChatMessageDto, nullable: true }) + @IsOptional() + @IsObject() + replyTo: ChatMessageDto | null; + + @ApiProperty({ type: [ChatMessageReactionDto] }) + @IsArray() + reactions: ChatMessageReactionDto[]; + + @ApiProperty() + @IsString() + createdAt: string; + + constructor( + id: number, + chatId: number, + chatUserId: number, + text: string, + statuses: ChatMessageUserStatusDto[], + files: ChatMessageFileDto[], + replyTo: ChatMessageDto | null, + reactions: ChatMessageReactionDto[], + createdAt: string, + ) { + this.id = id; + this.chatId = chatId; + this.chatUserId = chatUserId; + this.text = text; + this.statuses = statuses; + this.files = files; + this.replyTo = replyTo; + this.reactions = reactions; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/multichat/chat-message/dto/chat-messages-filter.dto.ts b/backend/src/modules/multichat/chat-message/dto/chat-messages-filter.dto.ts new file mode 100644 index 0000000..23a93f9 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/chat-messages-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class ChatMessagesFilterDto { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + focusedMessageId?: number | null; +} diff --git a/backend/src/modules/multichat/chat-message/dto/chat-messages-result.dto.ts b/backend/src/modules/multichat/chat-message/dto/chat-messages-result.dto.ts new file mode 100644 index 0000000..6158d78 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/chat-messages-result.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsObject } from 'class-validator'; + +import { PagingMeta } from '@/common'; +import { ChatMessageDto } from './chat-message.dto'; + +export class ChatMessagesResultDto { + @ApiProperty({ type: [ChatMessageDto] }) + @IsArray() + messages: ChatMessageDto[]; + + @ApiProperty({ type: PagingMeta }) + @IsObject() + meta: PagingMeta; + + constructor(messages: ChatMessageDto[], meta: PagingMeta) { + this.messages = messages; + this.meta = meta; + } +} diff --git a/backend/src/modules/multichat/chat-message/dto/index.ts b/backend/src/modules/multichat/chat-message/dto/index.ts new file mode 100644 index 0000000..d90da75 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/index.ts @@ -0,0 +1,7 @@ +export * from './chat-message-file.dto'; +export * from './chat-message-reaction.dto'; +export * from './chat-message-user-status.dto'; +export * from './chat-message.dto'; +export * from './chat-messages-filter.dto'; +export * from './chat-messages-result.dto'; +export * from './send-chat-message.dto'; diff --git a/backend/src/modules/multichat/chat-message/dto/send-chat-message.dto.ts b/backend/src/modules/multichat/chat-message/dto/send-chat-message.dto.ts new file mode 100644 index 0000000..53c9533 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/dto/send-chat-message.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class SendChatMessageDto { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + replyToId?: number | number; + + @ApiProperty() + @IsString() + text: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsArray() + fileIds?: string[] | null; +} diff --git a/backend/src/modules/multichat/chat-message/entities/chat-message-file.entity.ts b/backend/src/modules/multichat/chat-message/entities/chat-message-file.entity.ts new file mode 100644 index 0000000..2694e0a --- /dev/null +++ b/backend/src/modules/multichat/chat-message/entities/chat-message-file.entity.ts @@ -0,0 +1,76 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ChatMessageFileDto } from '../dto/chat-message-file.dto'; + +@Entity() +export class ChatMessageFile { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + messageId: number; + + @Column({ nullable: true }) + externalId: string | null; + + @Column({ nullable: true }) + fileId: string | null; + + @Column() + name: string; + + @Column() + mimeType: string; + + @Column() + size: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + private _downloadUrl: string | null = null; + + constructor( + accountId: number, + messageId: number, + externalId: string | null, + fileId: string | null, + name: string, + mimeType: string, + size: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.messageId = messageId; + this.externalId = externalId; + this.fileId = fileId; + this.name = name; + this.mimeType = mimeType; + this.size = size; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public set downloadUrl(downloadUrl: string | null) { + this._downloadUrl = downloadUrl; + } + public get downloadUrl(): string | null { + return this._downloadUrl; + } + + public toDto(): ChatMessageFileDto { + return new ChatMessageFileDto( + this.id, + this.fileId, + this.name, + this.size, + this.mimeType, + this.createdAt.toISOString(), + this._downloadUrl, + ); + } +} diff --git a/backend/src/modules/multichat/chat-message/entities/chat-message-reaction.entity.ts b/backend/src/modules/multichat/chat-message/entities/chat-message-reaction.entity.ts new file mode 100644 index 0000000..2b914db --- /dev/null +++ b/backend/src/modules/multichat/chat-message/entities/chat-message-reaction.entity.ts @@ -0,0 +1,38 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ChatMessageReactionDto } from '../dto/chat-message-reaction.dto'; + +@Entity() +export class ChatMessageReaction { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + messageId: number; + + @Column() + chatUserId: number; + + @Column() + reaction: string; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, messageId: number, chatUserId: number, reaction: string, createdAt?: Date) { + this.accountId = accountId; + this.messageId = messageId; + this.chatUserId = chatUserId; + this.reaction = reaction; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public toDto(): ChatMessageReactionDto { + return new ChatMessageReactionDto(this.id, this.chatUserId, this.reaction); + } +} diff --git a/backend/src/modules/multichat/chat-message/entities/chat-message-user-status.entity.ts b/backend/src/modules/multichat/chat-message/entities/chat-message-user-status.entity.ts new file mode 100644 index 0000000..b37b6c3 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/entities/chat-message-user-status.entity.ts @@ -0,0 +1,47 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ChatMessageStatus } from '../../common/enums/chat-message-status.enum'; +import { ChatMessageUserStatusDto } from '../dto/chat-message-user-status.dto'; + +@Entity() +export class ChatMessageUserStatus { + @PrimaryColumn() + chatId: number; + + @PrimaryColumn() + messageId: number; + + @PrimaryColumn() + chatUserId: number; + + @Column() + status: ChatMessageStatus; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + chatId: number, + messageId: number, + chatUserId: number, + status: ChatMessageStatus, + createdAt?: Date, + ) { + this.accountId = accountId; + this.chatId = chatId; + this.messageId = messageId; + this.chatUserId = chatUserId; + this.status = status; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public toDto(): ChatMessageUserStatusDto { + return new ChatMessageUserStatusDto(this.chatUserId, this.status, this.createdAt.toISOString()); + } +} diff --git a/backend/src/modules/multichat/chat-message/entities/chat-message.entity.ts b/backend/src/modules/multichat/chat-message/entities/chat-message.entity.ts new file mode 100644 index 0000000..bc9757b --- /dev/null +++ b/backend/src/modules/multichat/chat-message/entities/chat-message.entity.ts @@ -0,0 +1,119 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ChatUser } from '../../chat-user'; +import { ChatMessageFile } from './chat-message-file.entity'; +import { ChatMessageReaction } from './chat-message-reaction.entity'; +import { ChatMessageUserStatus } from './chat-message-user-status.entity'; + +import { ChatMessageDto } from '../dto/chat-message.dto'; + +@Entity() +export class ChatMessage { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + chatId: number; + + @Column() + chatUserId: number; + + @Column({ nullable: true }) + externalId: string | null; + + @Column({ nullable: true }) + replyToId: number | null; + + @Column() + text: string; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + chatId: number, + chatUserId: number, + externalId: string | null, + replyToId: number | null, + text: string, + createdAt?: Date, + ) { + this.accountId = accountId; + this.chatId = chatId; + this.chatUserId = chatUserId; + this.externalId = externalId; + this.replyToId = replyToId; + this.text = text; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _statuses: ChatMessageUserStatus[] | null; + public get statuses(): ChatMessageUserStatus[] | null { + return this._statuses; + } + public set statuses(value: ChatMessageUserStatus[] | null) { + this._statuses = value; + } + + private _files: ChatMessageFile[] | null; + public get files(): ChatMessageFile[] | null { + return this._files; + } + public set files(value: ChatMessageFile[] | null) { + this._files = value; + } + + private _replyTo: ChatMessage | null; + public get replyTo(): ChatMessage | null { + return this._replyTo; + } + public set replyTo(value: ChatMessage | null) { + this._replyTo = value; + } + + private _reactions: ChatMessageReaction[] | null; + public get reactions(): ChatMessageReaction[] | null { + return this._reactions; + } + public set reactions(value: ChatMessageReaction[] | null) { + this._reactions = value; + } + + private _chatUser: ChatUser | null; + public get chatUser(): ChatUser | null { + return this._chatUser; + } + public set chatUser(value: ChatUser | null) { + this._chatUser = value; + } + + public update(replyToId: number | null, text: string): ChatMessage { + this.replyToId = replyToId; + this.text = text; + return this; + } + + public toDto(): ChatMessageDto { + const statuses = this._statuses ? this._statuses.map((status) => status.toDto()) : []; + const files = this._files ? this._files.map((file) => file.toDto()) : []; + const replyTo = this._replyTo ? this._replyTo.toDto() : null; + const reactions = this._reactions ? this._reactions.map((reaction) => reaction.toDto()) : []; + return new ChatMessageDto( + this.id, + this.chatId, + this.chatUserId, + this.text, + statuses, + files, + replyTo, + reactions, + this.createdAt.toISOString(), + ); + } +} diff --git a/backend/src/modules/multichat/chat-message/entities/index.ts b/backend/src/modules/multichat/chat-message/entities/index.ts new file mode 100644 index 0000000..7fb3e25 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/entities/index.ts @@ -0,0 +1,4 @@ +export * from './chat-message-file.entity'; +export * from './chat-message-reaction.entity'; +export * from './chat-message-user-status.entity'; +export * from './chat-message.entity'; diff --git a/backend/src/modules/multichat/chat-message/services/chat-message-file.service.ts b/backend/src/modules/multichat/chat-message/services/chat-message-file.service.ts new file mode 100644 index 0000000..c6eb279 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/services/chat-message-file.service.ts @@ -0,0 +1,76 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { StorageUrlService } from '@/modules/storage/storage-url.service'; +import { StorageService } from '@/modules/storage/storage.service'; + +import { ChatMessageFile } from '../entities/chat-message-file.entity'; + +@Injectable() +export class ChatMessageFileService { + constructor( + @InjectRepository(ChatMessageFile) + private readonly repository: Repository, + private readonly storageService: StorageService, + private readonly storageUrlService: StorageUrlService, + ) {} + + public async addMessageFiles(account: Account, messageId: number, fileIds: string[]): Promise { + const files: ChatMessageFile[] = []; + for (const fileId of fileIds) { + const fileInfo = await this.storageService.markUsed({ accountId: account.id, id: fileId }); + if (fileInfo) { + const file = new ChatMessageFile( + account.id, + messageId, + null, + fileInfo.id, + fileInfo.originalName, + fileInfo.mimeType, + fileInfo.size, + fileInfo.createdAt, + ); + + file.downloadUrl = this.storageUrlService.getDownloadUrl(account.subdomain, file.fileId); + + files.push(await this.repository.save(file)); + } + } + return files; + } + + public async updateMessageFiles(account: Account, messageId: number, fileIds: string[]): Promise { + const currentFiles = await this.repository.findBy({ accountId: account.id, messageId }); + const currentFileIds = currentFiles.map((file) => file.fileId); + + const addedFileIds = fileIds.filter((fileId) => !currentFileIds.includes(fileId)); + const addedFiles = await this.addMessageFiles(account, messageId, addedFileIds); + + const deletedFiles = currentFiles.filter((file) => !fileIds.includes(file.fileId)); + await this.deleteFiles(account.id, messageId, deletedFiles); + + return currentFiles.filter((file) => !deletedFiles.includes(file)).concat(addedFiles); + } + + public async deleteMessageFiles(accountId: number, messageId: number) { + const files = await this.repository.findBy({ accountId, messageId }); + await this.deleteFiles(accountId, messageId, files); + } + + public setFileDownloadUrl(account: Account, file: ChatMessageFile): ChatMessageFile { + file.downloadUrl = this.storageUrlService.getDownloadUrl(account.subdomain, file.fileId); + + return file; + } + + private async deleteFiles(accountId: number, messageId: number, files: ChatMessageFile[]) { + for (const file of files) { + const deleted = await this.storageService.delete({ accountId, id: file.fileId }); + if (deleted) { + await this.repository.delete({ accountId, messageId, id: file.id }); + } + } + } +} diff --git a/backend/src/modules/multichat/chat-message/services/chat-message-reaction.service.ts b/backend/src/modules/multichat/chat-message/services/chat-message-reaction.service.ts new file mode 100644 index 0000000..c9bf230 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/services/chat-message-reaction.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ChatUser } from '../../chat-user'; +import { ChatMessageReaction } from '../entities/chat-message-reaction.entity'; + +@Injectable() +export class ChatMessageReactionService { + constructor( + @InjectRepository(ChatMessageReaction) + private readonly repository: Repository, + ) {} + + public async add( + accountId: number, + chatUser: ChatUser, + messageId: number, + reaction: string, + ): Promise { + return await this.repository.save(new ChatMessageReaction(accountId, messageId, chatUser.id, reaction)); + } + + public async remove(accountId: number, chatUser: ChatUser, messageId: number, reactionId: number): Promise { + await this.repository.delete({ accountId, chatUserId: chatUser.id, messageId, id: reactionId }); + } +} diff --git a/backend/src/modules/multichat/chat-message/services/chat-message-user-status.service.ts b/backend/src/modules/multichat/chat-message/services/chat-message-user-status.service.ts new file mode 100644 index 0000000..1f55f59 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/services/chat-message-user-status.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ChatMessageStatus } from '../../common/enums/chat-message-status.enum'; +import { ChatMessageUserStatus } from '../entities/chat-message-user-status.entity'; + +@Injectable() +export class ChatMessageUserStatusService { + constructor( + @InjectRepository(ChatMessageUserStatus) + private readonly repository: Repository, + ) {} + + async setStatus( + accountId: number, + chatId: number, + chatUserId: number, + messageId: number, + status: ChatMessageStatus, + ): Promise { + const cmus = new ChatMessageUserStatus(accountId, chatId, messageId, chatUserId, status); + await this.repository.upsert(cmus, ['chatId', 'messageId', 'chatUserId']); + return cmus; + } + + async updateStatusDirect({ + accountId, + userId, + chatId, + status, + }: { + accountId: number; + userId: number; + chatId?: number; + status: ChatMessageStatus; + }) { + const sql = ` +INSERT INTO chat_message_user_status (chat_id, message_id, chat_user_id, status, account_id, created_at) +SELECT cm.chat_id, cm.id, cu.id, '${status}', cm.account_id, NOW() +FROM chat_user cu +JOIN chat_message cm ON cm.chat_id = cu.chat_id +WHERE cu.account_id = ${accountId} + AND cu.user_id = ${userId} + AND ${chatId ? `cu.chat_id = ${chatId}` : 'TRUE'} +ON CONFLICT (chat_id, message_id, chat_user_id) + DO UPDATE SET status = EXCLUDED.status, created_at = NOW() + WHERE chat_message_user_status.status IS DISTINCT FROM EXCLUDED.status; + `; + + await this.repository.query(sql); + } +} diff --git a/backend/src/modules/multichat/chat-message/services/chat-message.service.ts b/backend/src/modules/multichat/chat-message/services/chat-message.service.ts new file mode 100644 index 0000000..3572eef --- /dev/null +++ b/backend/src/modules/multichat/chat-message/services/chat-message.service.ts @@ -0,0 +1,522 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, SelectQueryBuilder } from 'typeorm'; + +import { NotFoundError, PagingMeta, PagingQuery } from '@/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { ChatMessageStatus, MultichatEventType } from '../../common'; + +import { ChatUser, ChatUserService } from '../../chat-user'; + +import { SendChatMessageDto, ChatMessagesFilterDto, ChatMessagesResultDto } from '../dto'; +import { ChatMessage, ChatMessageUserStatus, ChatMessageFile, ChatMessageReaction } from '../entities'; +import { ExpandableField } from '../types'; +import { ChatMessageFileService } from './chat-message-file.service'; +import { ChatMessageReactionService } from './chat-message-reaction.service'; +import { ChatMessageUserStatusService } from './chat-message-user-status.service'; +import { ChatNotificationService } from './chat-notification.service'; + +interface FindFilter { + chatId: number; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class ChatMessageService { + constructor( + @InjectRepository(ChatMessage) + private readonly repository: Repository, + @Inject(forwardRef(() => ChatUserService)) + private readonly chatUserService: ChatUserService, + private readonly chatMessageUserStatusService: ChatMessageUserStatusService, + private readonly chatMessageFileService: ChatMessageFileService, + private readonly chatMessageReactionService: ChatMessageReactionService, + private readonly chatNotificationService: ChatNotificationService, + ) {} + + async create( + account: Account, + user: User, + chatId: number, + dto: SendChatMessageDto, + notifyUsers = true, + ): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + + const message = await this.repository.save( + new ChatMessage(account.id, chatId, chatUser.id, null, dto.replyToId, dto.text), + ); + + if (dto.fileIds) { + message.files = await this.chatMessageFileService.addMessageFiles(account, message.id, dto.fileIds); + } + + if (dto.replyToId) { + message.replyTo = await this.getMessageSimple(account.id, chatId, dto.replyToId); + } + + message.statuses = [ + await this.chatMessageUserStatusService.setStatus( + account.id, + chatId, + chatUser.id, + message.id, + ChatMessageStatus.SEEN, + ), + ]; + + if (notifyUsers) { + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageCreated, + user.fullName, + ); + } + + return message; + } + + async createExternal( + account: Account, + chatUser: ChatUser, + text: string, + externalId: string | null, + fileIds?: string[] | null, + notifyUsers = true, + ): Promise { + const message = await this.repository.save( + new ChatMessage(account.id, chatUser.chatId, chatUser.id, externalId, null, text), + ); + + if (fileIds) { + message.files = await this.chatMessageFileService.addMessageFiles(account, message.id, fileIds); + } + + message.statuses = [ + await this.chatMessageUserStatusService.setStatus( + account.id, + chatUser.chatId, + chatUser.id, + message.id, + ChatMessageStatus.SEEN, + ), + ]; + + if (notifyUsers) { + this.chatNotificationService.notifyUsers( + account, + chatUser.chatId, + chatUser, + message, + MultichatEventType.ChatMessageCreated, + chatUser.externalUser?.fullName(), + ); + } + + return message; + } + + async getMessageDto(account: Account, userId: number, chatId: number, messageId: number): Promise { + await this.chatUserService.getOne(account.id, { chatId, userId }); + + return await this.getMessageFull(account, chatId, messageId); + } + + async findOne(accountId: number, filter: FindFilter, options?: FindOptions): Promise { + const message = await this.createFindQb(accountId, filter).getOne(); + return message && options?.expand ? await this.expandOne(message, options.expand) : message; + } + async findMany(accountId: number, filter: FindFilter, options?: FindOptions): Promise { + const messages = await this.createFindQb(accountId, filter).orderBy('cm.created_at', 'DESC').getMany(); + return messages && options?.expand ? await this.expandMany(messages, options.expand) : messages; + } + + private createFindQb(accountId: number, filter: FindFilter) { + const qb = this.repository.createQueryBuilder('cm').where('cm.account_id = :accountId', { accountId }); + + if (filter.chatId) { + qb.andWhere('cm.chat_id = :chatId', { chatId: filter.chatId }); + } + + return qb; + } + + private async expandOne(message: ChatMessage, expand: ExpandableField[]): Promise { + if (expand.includes('chatUser')) { + message.chatUser = await this.chatUserService.findOne(message.accountId, { id: message.chatUserId }); + } + return message; + } + private async expandMany(messages: ChatMessage[], expand: ExpandableField[]): Promise { + return await Promise.all(messages.map((message) => this.expandOne(message, expand))); + } + + async update( + account: Account, + user: User, + chatId: number, + messageId: number, + dto: SendChatMessageDto, + ): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + + const message = await this.getMessageSimple(account.id, chatId, messageId, chatUser.id); + + await this.repository.save(message.update(dto.replyToId, dto.text)); + + if (dto.fileIds) { + message.files = await this.chatMessageFileService.updateMessageFiles(account, message.id, dto.fileIds); + } + + if (dto.replyToId) { + message.replyTo = await this.getMessageSimple(account.id, chatId, dto.replyToId); + } + + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageUpdated, + user.fullName, + ); + + return message; + } + + async updateStatus( + account: Account, + user: User, + chatId: number, + messageId: number, + status: ChatMessageStatus, + ): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + const message = await this.getMessageFull(account, chatId, messageId); + + const userStatus = await this.chatMessageUserStatusService.setStatus( + account.id, + chatId, + chatUser.id, + message.id, + status, + ); + message.statuses = [...message.statuses.filter((s) => userStatus.chatUserId !== s.chatUserId), userStatus]; + + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageUpdated, + user.fullName, + ); + + return message; + } + + async updateStatusBatch( + account: Account, + user: User, + chatId: number, + messageIds: number[], + status: ChatMessageStatus, + ): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + const messages = await this.getMessagesFull(account.id, chatId, messageIds); + + for (const message of messages) { + const userStatus = await this.chatMessageUserStatusService.setStatus( + account.id, + chatId, + chatUser.id, + message.id, + status, + ); + message.statuses = [...message.statuses.filter((s) => userStatus.chatUserId !== s.chatUserId), userStatus]; + + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageUpdated, + user.fullName, + ); + } + + return messages; + } + + async updateStatusDirect({ + accountId, + user, + chatId, + status, + }: { + accountId: number; + user: User; + chatId?: number; + status: ChatMessageStatus; + }) { + await this.chatMessageUserStatusService.updateStatusDirect({ accountId, userId: user.id, chatId, status }); + } + + async react(account: Account, user: User, chatId: number, messageId: number, reaction: string): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + const message = await this.getMessageFull(account, chatId, messageId); + + const messageReaction = await this.chatMessageReactionService.add(account.id, chatUser, message.id, reaction); + message.reactions = [...message.reactions, messageReaction]; + + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageUpdated, + user.fullName, + ); + + return message; + } + + async unreact( + account: Account, + user: User, + chatId: number, + messageId: number, + reactionId: number, + ): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + const message = await this.getMessageFull(account, chatId, messageId); + + await this.chatMessageReactionService.remove(account.id, chatUser, message.id, reactionId); + message.reactions = message.reactions.filter((reaction) => reaction.id !== reactionId); + + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageUpdated, + user.fullName, + ); + + return message; + } + + async delete(account: Account, user: User, chatId: number, messageId: number): Promise { + const chatUser = await this.chatUserService.getOne(account.id, { chatId, userId: user.id }); + + const message = await this.getMessageSimple(account.id, chatId, messageId, chatUser.id); + + await this.chatMessageFileService.deleteMessageFiles(account.id, message.id); + + await this.repository.delete(message.id); + + this.chatNotificationService.notifyUsers( + account, + chatId, + chatUser, + message, + MultichatEventType.ChatMessageDeleted, + user.fullName, + ); + + return true; + } + + async getMessagesForUI( + account: Account, + userId: number, + chatId: number, + filter: ChatMessagesFilterDto, + paging: PagingQuery, + ): Promise { + await this.chatUserService.getOne(account.id, { chatId, userId }); + + const qb = this.getChatMessagesFullQuery(account.id, chatId); + const total = await qb.clone().getCount(); + + const [messages, offset] = filter.focusedMessageId + ? await this.getMessagesFullToMessage(account, chatId, filter.focusedMessageId, qb, paging) + : await this.getMessagesFullWithPaging(qb, paging, total); + + this.setAllFileDownloadUrls(account, messages); + + return new ChatMessagesResultDto( + messages.map((message) => message.toDto()), + new PagingMeta(offset, total), + ); + } + + async getMessageSimple( + accountId: number, + chatId: number, + messageId: number, + chatUserId?: number, + ): Promise { + const message = await this.repository.findOneBy({ + accountId, + chatId, + id: messageId, + chatUserId, + }); + + if (!message) { + throw new NotFoundError(`Message ${messageId} not found in chat ${chatId}`); + } + + return message; + } + + private async getMessagesFullWithPaging( + qb: SelectQueryBuilder, + paging: PagingQuery, + total: number, + ): Promise<[ChatMessage[], number]> { + const messages = await qb + .clone() + .skip(paging.skip) + .take(paging.take) + //FIX: https://github.com/typeorm/typeorm/issues/8213 + .orderBy('msg.createdAt', 'DESC') + .addOrderBy('msg.id', 'DESC') + .getMany(); + + const offset = Math.min(paging.take + paging.skip, total); + return [messages, offset]; + } + + private async getMessagesFullToMessage( + account: Account, + chatId: number, + focusedMessageId: number, + qb: SelectQueryBuilder, + paging: PagingQuery, + ): Promise<[ChatMessage[], number]> { + const message = await this.getMessageFull(account, chatId, focusedMessageId, false); + const newerQb = qb.clone().andWhere('msg.created_at > :createdAt', { createdAt: message.createdAt }); + const newerCount = await newerQb.clone().getCount(); + const skip = Math.min(newerCount, paging.skip); + const newerMessages = await newerQb + .clone() + .skip(skip) + //FIX: https://github.com/typeorm/typeorm/issues/8213 + .orderBy('msg.createdAt', 'DESC') + .addOrderBy('msg.id', 'DESC') + .getMany(); + + const olderMessages = await qb + .clone() + .andWhere('msg.created_at < :createdAt', { createdAt: message.createdAt }) + .take(paging.take) + //FIX: https://github.com/typeorm/typeorm/issues/8213 + .orderBy('msg.createdAt', 'DESC') + .addOrderBy('msg.id', 'DESC') + .getMany(); + + const messages = [...newerMessages, message, ...olderMessages]; + const offset = messages.length + skip; + + return [messages, offset]; + } + + private async getMessageFull( + account: Account, + chatId: number, + messageId: number, + setFileDownloadUrl = true, + ): Promise { + const message = await this.getChatMessagesFullQuery(account.id, chatId) + .andWhere('msg.id = :messageId', { messageId }) + .getOne(); + + if (!message) { + throw new NotFoundError(`Message ${messageId} not found in chat ${chatId}`); + } + + if (setFileDownloadUrl) { + this.setFileDownloadUrls(account, message); + } + + return message; + } + + private async getMessagesFull(accountId: number, chatId: number, messageIds: number[]): Promise { + if (messageIds.length === 0) { + return []; + } + + return await this.getChatMessagesFullQuery(accountId, chatId) + .andWhere('msg.id IN (:...messageIds)', { messageIds }) + .getMany(); + } + + async getLastMessageId(accountId: number, chatId: number): Promise { + const data = await this.repository + .createQueryBuilder('msg') + .select('msg.id', 'id') + .where('msg.account_id = :accountId', { accountId }) + .andWhere('msg.chat_id = :chatId', { chatId }) + .orderBy('msg.created_at', 'DESC') + .getRawOne(); + + return data.id; + } + + async getLastMessageCreatedAt(accountId: number, chatId: number): Promise { + const data = await this.repository + .createQueryBuilder('msg') + .select('msg.created_at', 'created_at') + .where('msg.account_id = :accountId', { accountId }) + .andWhere('msg.chat_id = :chatId', { chatId }) + .orderBy('msg.created_at', 'DESC') + .getRawOne(); + + return data?.created_at ? new Date(data.created_at) : null; + } + + async getLastMessageInfo(accountId: number, chatId: number, messageId: number): Promise { + return await this.repository + .createQueryBuilder('msg') + .leftJoin(ChatUser, 'user', 'msg.chat_id = user.chat_id') + .leftJoinAndMapMany('msg.statuses', ChatMessageUserStatus, 'status', 'msg.id = status.message_id') + .leftJoinAndMapMany('msg.files', ChatMessageFile, 'file', 'msg.id = file.message_id') + .where('msg.account_id = :accountId', { accountId }) + .andWhere('msg.chat_id = :chatId', { chatId }) + .andWhere('msg.id = :messageId', { messageId }) + .orderBy('msg.created_at', 'DESC') + .addOrderBy('msg.id', 'DESC') + .getOne(); + } + + private getChatMessagesFullQuery(accountId: number, chatId: number): SelectQueryBuilder { + return this.repository + .createQueryBuilder('msg') + .leftJoinAndMapMany('msg.statuses', ChatMessageUserStatus, 'status', 'msg.id = status.message_id') + .leftJoinAndMapMany('msg.files', ChatMessageFile, 'file', 'msg.id = file.message_id') + .leftJoinAndMapOne('msg.replyTo', ChatMessage, 'replyTo', 'msg.reply_to_id = replyTo.id') + .leftJoinAndMapMany('msg.reactions', ChatMessageReaction, 'reaction', 'msg.id = reaction.message_id') + .where('msg.account_id = :accountId', { accountId }) + .andWhere('msg.chat_id = :chatId', { chatId }); + } + + private setAllFileDownloadUrls(account: Account, messages: ChatMessage[]): void { + messages.forEach((message) => this.setFileDownloadUrls(account, message)); + } + private setFileDownloadUrls(account: Account, message: ChatMessage): void { + message.files.forEach((file) => { + this.chatMessageFileService.setFileDownloadUrl(account, file); + }); + } +} diff --git a/backend/src/modules/multichat/chat-message/services/chat-notification.service.ts b/backend/src/modules/multichat/chat-message/services/chat-notification.service.ts new file mode 100644 index 0000000..b1dab0d --- /dev/null +++ b/backend/src/modules/multichat/chat-message/services/chat-notification.service.ts @@ -0,0 +1,133 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; + +import { + ChatMessageCreatedEvent, + ChatMessageEvent, + ChatMessageUpdatedEvent, + ChatUserRole, + MultichatEventType, +} from '../../common'; +import { ChatUserService, ChatUser } from '../../chat-user'; +import { ChatProviderService } from '../../chat-provider/services/chat-provider.service'; +import { ChatProviderProxyService } from '../../providers/chat-provider-proxy.service'; +import { Chat } from '../../chat/entities/chat.entity'; +import { ChatService } from '../../chat/services/chat.service'; + +import { ChatMessage } from '../entities/chat-message.entity'; + +@Injectable() +export class ChatNotificationService { + constructor( + private eventEmitter: EventEmitter2, + @Inject(forwardRef(() => ChatService)) + private readonly chatService: ChatService, + @Inject(forwardRef(() => ChatUserService)) + private readonly chatUserService: ChatUserService, + @Inject(forwardRef(() => ChatProviderService)) + private readonly chatProviderService: ChatProviderService, + private readonly providerProxyService: ChatProviderProxyService, + ) {} + + public async notifyUsers( + account: Account, + chatId: number, + sendBy: ChatUser, + message: ChatMessage, + type: MultichatEventType, + fromUserName: string, + ): Promise { + const chat = await this.chatService.findOne({ accountId: account.id, filter: { chatId } }); + const chatUsers = await this.chatUserService.findMany(account.id, { chatId }); + + const internalUsers = chatUsers.filter((chatUser) => chatUser.userId && chatUser.id !== sendBy.id); + if (internalUsers.length > 0) { + this.notifyInternalUsers(account.id, chat, message, type, internalUsers, fromUserName); + } + + const externalUsers = chatUsers.filter( + (chatUser) => chatUser.role === ChatUserRole.EXTERNAL && chatUser.id !== sendBy.id, + ); + if (externalUsers.length > 0) { + const provider = await this.chatProviderService.findOne(account.id, null, { providerId: chat.providerId }); + if (provider) { + this.providerProxyService.notifyChatUsers(account, provider, chat, type, message, externalUsers); + } + } + } + + private async notifyInternalUsers( + accountId: number, + chat: Chat, + message: ChatMessage, + type: MultichatEventType, + chatUsers: ChatUser[], + fromUserName: string, + ): Promise { + const lastMessageId = await this.chatService.getLastMessageId(accountId, chat.id); + chatUsers.forEach(async (chatUser) => { + const event = this.createMessageEvent(accountId, chat, message, chatUser, type, fromUserName, lastMessageId); + this.eventEmitter.emit(type, event); + }); + } + + private createMessageEvent( + accountId: number, + chat: Chat, + message: ChatMessage, + chatUser: ChatUser, + type: MultichatEventType, + fromUserName: string, + lastMessageId?: number | null, + ): ChatMessageEvent { + switch (type) { + case MultichatEventType.ChatMessageCreated: + return this.createMessageCreatedEvent(accountId, chat, message, chatUser, fromUserName); + case MultichatEventType.ChatMessageUpdated: + return this.createMessageChangedEvent(accountId, chat, message, chatUser, lastMessageId); + case MultichatEventType.ChatMessageDeleted: + return this.createMessageChangedEvent(accountId, chat, message, chatUser, lastMessageId); + default: + throw new Error(`Unknown event type: ${type}`); + } + } + private createMessageCreatedEvent( + accountId: number, + chat: Chat, + message: ChatMessage, + chatUser: ChatUser, + fromUserName: string, + ) { + const text = message.text || message.files?.[0]?.name; + return new ChatMessageCreatedEvent({ + accountId, + userId: chatUser.userId, + providerId: chat.providerId, + chatId: chat.id, + fromUser: fromUserName, + messageId: message.id, + text, + createdAt: message.createdAt.toISOString(), + entityId: chat.entityId, + }); + } + + private createMessageChangedEvent( + accountId: number, + chat: Chat, + message: ChatMessage, + chatUser: ChatUser, + lastMessageId: number | null, + ) { + return new ChatMessageUpdatedEvent({ + accountId, + userId: chatUser.userId, + providerId: chat.providerId, + chatId: chat.id, + messageId: message.id, + isLastMessage: message.id === lastMessageId, + }); + } +} diff --git a/backend/src/modules/multichat/chat-message/services/index.ts b/backend/src/modules/multichat/chat-message/services/index.ts new file mode 100644 index 0000000..f11e80a --- /dev/null +++ b/backend/src/modules/multichat/chat-message/services/index.ts @@ -0,0 +1,5 @@ +export * from './chat-message-file.service'; +export * from './chat-message-reaction.service'; +export * from './chat-message-user-status.service'; +export * from './chat-message.service'; +export * from './chat-notification.service'; diff --git a/backend/src/modules/multichat/chat-message/types/expandable-field.ts b/backend/src/modules/multichat/chat-message/types/expandable-field.ts new file mode 100644 index 0000000..62e2f5e --- /dev/null +++ b/backend/src/modules/multichat/chat-message/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'chatUser'; diff --git a/backend/src/modules/multichat/chat-message/types/index.ts b/backend/src/modules/multichat/chat-message/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/multichat/chat-message/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/multichat/chat-provider-user/chat-provider-user.module.ts b/backend/src/modules/multichat/chat-provider-user/chat-provider-user.module.ts new file mode 100644 index 0000000..b014d67 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/chat-provider-user.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { ChatProviderUser } from './entities'; +import { ChatProviderUserService } from './chat-provider-user.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([ChatProviderUser])], + providers: [ChatProviderUserService], + exports: [ChatProviderUserService], +}) +export class ChatProviderUserModule {} diff --git a/backend/src/modules/multichat/chat-provider-user/chat-provider-user.service.ts b/backend/src/modules/multichat/chat-provider-user/chat-provider-user.service.ts new file mode 100644 index 0000000..90f4bb1 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/chat-provider-user.service.ts @@ -0,0 +1,72 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ChatProviderUserType } from './enums'; +import { ChatProviderUser } from './entities'; + +interface FindFilter { + providerId?: number; + type?: ChatProviderUserType; +} + +@Injectable() +export class ChatProviderUserService { + constructor( + @InjectRepository(ChatProviderUser) + private repository: Repository, + ) {} + + async create( + accountId: number, + providerId: number, + userIds: number[], + type: ChatProviderUserType, + ): Promise { + return this.repository.save(userIds.map((userId) => new ChatProviderUser(providerId, userId, type, accountId))); + } + + async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getMany(); + } + + async update( + accountId: number, + providerId: number, + currentUsers: ChatProviderUser[], + userIds: number[], + type: ChatProviderUserType, + ): Promise { + const addUsers = userIds.filter((id) => !currentUsers.some((user) => user.userId === id)); + const removeUsers = currentUsers.filter((user) => !userIds.some((id) => id === user.userId)); + + currentUsers.push( + ...(await this.repository.save( + addUsers.map((userId) => new ChatProviderUser(providerId, userId, type, accountId)), + )), + ); + + if (removeUsers.length > 0) { + await this.repository.remove(removeUsers); + } + + return currentUsers.filter((user) => !removeUsers.some((u) => u.userId === user.userId)); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('cpu').where('cpu.account_id = :accountId', { accountId }); + + if (filter?.providerId) { + qb.andWhere('cpu.provider_id = :providerId', { providerId: filter.providerId }); + } + + if (filter?.type) { + qb.andWhere('cpu.type = :type', { type: filter.type }); + } + + return qb; + } +} diff --git a/backend/src/modules/multichat/chat-provider-user/entities/chat-provider-user.entity.ts b/backend/src/modules/multichat/chat-provider-user/entities/chat-provider-user.entity.ts new file mode 100644 index 0000000..ca2cf26 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/entities/chat-provider-user.entity.ts @@ -0,0 +1,24 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { ChatProviderUserType } from '../enums'; + +@Entity() +export class ChatProviderUser { + @PrimaryColumn() + providerId: number; + + @PrimaryColumn() + userId: number; + + @PrimaryColumn({ type: 'enum', enum: ChatProviderUserType, default: ChatProviderUserType.Accessible }) + type: ChatProviderUserType; + + @Column() + accountId: number; + + constructor(providerId: number, userId: number, type: ChatProviderUserType, accountId: number) { + this.providerId = providerId; + this.userId = userId; + this.type = type; + this.accountId = accountId; + } +} diff --git a/backend/src/modules/multichat/chat-provider-user/entities/index.ts b/backend/src/modules/multichat/chat-provider-user/entities/index.ts new file mode 100644 index 0000000..aa1dead --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/entities/index.ts @@ -0,0 +1 @@ +export * from './chat-provider-user.entity'; diff --git a/backend/src/modules/multichat/chat-provider-user/enums/chat-provider-user-type.enum.ts b/backend/src/modules/multichat/chat-provider-user/enums/chat-provider-user-type.enum.ts new file mode 100644 index 0000000..30a53f5 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/enums/chat-provider-user-type.enum.ts @@ -0,0 +1,5 @@ +export enum ChatProviderUserType { + Accessible = 'accessible', + Responsible = 'responsible', + Supervisor = 'supervisor', +} diff --git a/backend/src/modules/multichat/chat-provider-user/enums/index.ts b/backend/src/modules/multichat/chat-provider-user/enums/index.ts new file mode 100644 index 0000000..166405a --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/enums/index.ts @@ -0,0 +1 @@ +export * from './chat-provider-user-type.enum'; diff --git a/backend/src/modules/multichat/chat-provider-user/index.ts b/backend/src/modules/multichat/chat-provider-user/index.ts new file mode 100644 index 0000000..329f250 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider-user/index.ts @@ -0,0 +1,4 @@ +export * from './chat-provider-user.module'; +export * from './chat-provider-user.service'; +export * from './entities'; +export * from './enums'; diff --git a/backend/src/modules/multichat/chat-provider/chat-provider.controller.ts b/backend/src/modules/multichat/chat-provider/chat-provider.controller.ts new file mode 100644 index 0000000..cf0250a --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/chat-provider.controller.ts @@ -0,0 +1,26 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ChatProviderDto } from './dto'; +import { ChatProviderService } from './services'; + +@ApiTags('multichat/providers') +@Controller('chat/providers') +@JwtAuthorized() +@TransformToDto() +export class ChatProviderController { + constructor(private readonly service: ChatProviderService) {} + + @ApiOperation({ summary: 'Get available providers', description: 'Get available providers for current user' }) + @ApiOkResponse({ description: 'Chat providers', type: [ChatProviderDto] }) + @Get() + async findMany(@CurrentAuth() { accountId, userId }: AuthData): Promise { + return this.service.findMany(accountId, userId, {}, { expand: ['unseenCount'] }); + } +} diff --git a/backend/src/modules/multichat/chat-provider/chat-provider.module.ts b/backend/src/modules/multichat/chat-provider/chat-provider.module.ts new file mode 100644 index 0000000..dd3c80c --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/chat-provider.module.ts @@ -0,0 +1,25 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { ChatModule } from '../chat/chat.module'; +import { ChatProviderUserModule } from '../chat-provider-user/chat-provider-user.module'; +import { ChatUserModule } from '../chat-user/chat-user.module'; + +import { ChatProvider, ChatProviderEntitySettings } from './entities'; +import { ChatProviderEntitySettingsService, ChatProviderHandler, ChatProviderService } from './services'; +import { ChatProviderController } from './chat-provider.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ChatProvider, ChatProviderEntitySettings]), + IAMModule, + ChatProviderUserModule, + forwardRef(() => ChatModule), + ChatUserModule, + ], + providers: [ChatProviderService, ChatProviderHandler, ChatProviderEntitySettingsService], + controllers: [ChatProviderController], + exports: [ChatProviderService], +}) +export class ChatProviderModule {} diff --git a/backend/src/modules/multichat/chat-provider/const/chat-provider-defaults.ts b/backend/src/modules/multichat/chat-provider/const/chat-provider-defaults.ts new file mode 100644 index 0000000..9c8d76c --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/const/chat-provider-defaults.ts @@ -0,0 +1,8 @@ +import { ChatProviderStatus, ChatProviderTransport, ChatProviderType } from '../../common'; + +export const ChatProviderDefaults = { + type: ChatProviderType.Amwork, + transport: ChatProviderTransport.Amwork, + status: ChatProviderStatus.Active, + messagePerDay: 100, +}; diff --git a/backend/src/modules/multichat/chat-provider/const/index.ts b/backend/src/modules/multichat/chat-provider/const/index.ts new file mode 100644 index 0000000..8903c78 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/const/index.ts @@ -0,0 +1 @@ +export * from './chat-provider-defaults'; diff --git a/backend/src/modules/multichat/chat-provider/dto/chat-provider-entity-settings.dto.ts b/backend/src/modules/multichat/chat-provider/dto/chat-provider-entity-settings.dto.ts new file mode 100644 index 0000000..3c326fb --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/dto/chat-provider-entity-settings.dto.ts @@ -0,0 +1,44 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ChatProviderEntitySettingsDto { + @ApiPropertyOptional({ description: 'Contact entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + contactEntityTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + leadEntityTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead board ID', nullable: true }) + @IsOptional() + @IsNumber() + leadBoardId?: number | null; + + @ApiPropertyOptional({ description: 'Lead stage ID', nullable: true }) + @IsOptional() + @IsNumber() + leadStageId?: number | null; + + @ApiPropertyOptional({ description: 'Lead name', nullable: true }) + @IsOptional() + @IsString() + leadName: string | null; + + @ApiPropertyOptional({ description: 'Lead and Contact responsible user ID', nullable: true }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ description: 'Do not create lead if active lead exists', nullable: true }) + @IsOptional() + @IsBoolean() + checkActiveLead?: boolean; + + @ApiPropertyOptional({ description: 'Do not create duplicate contact', nullable: true }) + @IsOptional() + @IsBoolean() + checkDuplicate?: boolean; +} diff --git a/backend/src/modules/multichat/chat-provider/dto/chat-provider.dto.ts b/backend/src/modules/multichat/chat-provider/dto/chat-provider.dto.ts new file mode 100644 index 0000000..8927a70 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/dto/chat-provider.dto.ts @@ -0,0 +1,56 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ChatProviderType, ChatProviderStatus, ChatProviderTransport } from '../../common'; +import { ChatProviderEntitySettingsDto } from './chat-provider-entity-settings.dto'; + +export class ChatProviderDto { + @ApiProperty({ description: 'Chat provider ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Chat provider type', enum: ChatProviderType }) + @IsEnum(ChatProviderType) + type: ChatProviderType; + + @ApiProperty({ description: 'Chat provider transport', enum: ChatProviderTransport }) + @IsEnum(ChatProviderTransport) + transport: ChatProviderTransport; + + @ApiProperty({ description: 'Chat provider title', nullable: true }) + @IsOptional() + @IsString() + title: string | null; + + @ApiProperty({ description: 'Chat provider status', enum: ChatProviderStatus }) + @IsEnum(ChatProviderStatus) + status: ChatProviderStatus; + + @ApiProperty({ description: 'Messages per day for automated sending' }) + @IsNumber() + messagePerDay: number; + + @ApiPropertyOptional({ description: 'Accessible user IDs', type: [Number], nullable: true }) + @IsOptional() + @IsArray() + accessibleUserIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Responsible user IDs', type: [Number], nullable: true }) + @IsOptional() + @IsArray() + responsibleUserIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Supervisor user IDs', type: [Number], nullable: true }) + @IsOptional() + @IsArray() + supervisorUserIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Unseen message count', nullable: true }) + @IsOptional() + @IsNumber() + unseenCount?: number | null; + + @ApiPropertyOptional({ description: 'Entity settings', nullable: true }) + @IsOptional() + entitySettings?: ChatProviderEntitySettingsDto | null; +} diff --git a/backend/src/modules/multichat/chat-provider/dto/create-chat-provider.dto.ts b/backend/src/modules/multichat/chat-provider/dto/create-chat-provider.dto.ts new file mode 100644 index 0000000..73205ef --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/dto/create-chat-provider.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +import { ChatProviderDto } from './chat-provider.dto'; + +export class CreateChatProviderDto extends PickType(ChatProviderDto, [ + 'type', + 'transport', + 'title', + 'status', + 'accessibleUserIds', + 'responsibleUserIds', + 'supervisorUserIds', + 'entitySettings', +] as const) { + @ApiProperty({ description: 'Messages per day for automated sending' }) + @IsOptional() + @IsNumber() + messagePerDay?: number; +} diff --git a/backend/src/modules/multichat/chat-provider/dto/index.ts b/backend/src/modules/multichat/chat-provider/dto/index.ts new file mode 100644 index 0000000..6237ae0 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/dto/index.ts @@ -0,0 +1,4 @@ +export * from './chat-provider-entity-settings.dto'; +export * from './chat-provider.dto'; +export * from './create-chat-provider.dto'; +export * from './update-chat-provider.dto'; diff --git a/backend/src/modules/multichat/chat-provider/dto/update-chat-provider.dto.ts b/backend/src/modules/multichat/chat-provider/dto/update-chat-provider.dto.ts new file mode 100644 index 0000000..7c9fae7 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/dto/update-chat-provider.dto.ts @@ -0,0 +1,15 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { ChatProviderDto } from './chat-provider.dto'; + +export class UpdateChatProviderDto extends PartialType( + PickType(ChatProviderDto, [ + 'title', + 'status', + 'messagePerDay', + 'accessibleUserIds', + 'responsibleUserIds', + 'supervisorUserIds', + 'entitySettings', + ] as const), +) {} diff --git a/backend/src/modules/multichat/chat-provider/entities/chat-provider-entity-settings.entity.ts b/backend/src/modules/multichat/chat-provider/entities/chat-provider-entity-settings.entity.ts new file mode 100644 index 0000000..7f2a64f --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/entities/chat-provider-entity-settings.entity.ts @@ -0,0 +1,110 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ChatProviderEntitySettingsDto } from '../dto'; + +@Entity() +export class ChatProviderEntitySettings { + @Column() + accountId: number; + + @PrimaryColumn() + providerId: number; + + @Column({ nullable: true }) + contactEntityTypeId: number | null; + + @Column({ nullable: true }) + leadEntityTypeId: number | null; + + @Column({ nullable: true }) + leadBoardId: number | null; + + @Column({ nullable: true }) + leadStageId: number | null; + + @Column({ nullable: true }) + leadName: string | null; + + @Column({ nullable: true }) + ownerId: number | null; + + @Column({ default: false }) + checkActiveLead: boolean; + + @Column({ default: false }) + checkDuplicate: boolean; + + constructor( + accountId: number, + providerId: number, + contactEntityTypeId: number | null, + leadEntityTypeId: number | null, + leadBoardId: number | null, + leadStageId: number | null, + leadName: string | null, + ownerId: number | null, + checkActiveLead: boolean, + checkDuplicate: boolean, + ) { + this.accountId = accountId; + this.providerId = providerId; + this.contactEntityTypeId = contactEntityTypeId; + this.leadEntityTypeId = leadEntityTypeId; + this.leadBoardId = leadBoardId; + this.leadStageId = leadStageId; + this.leadName = leadName; + this.ownerId = ownerId; + this.checkActiveLead = checkActiveLead; + this.checkDuplicate = checkDuplicate; + } + + static fromDto({ + accountId, + providerId, + dto, + }: { + accountId: number; + providerId: number; + dto: ChatProviderEntitySettingsDto; + }): ChatProviderEntitySettings { + return new ChatProviderEntitySettings( + accountId, + providerId, + dto.contactEntityTypeId, + dto.leadEntityTypeId, + dto.leadBoardId, + dto.leadStageId, + dto.leadName, + dto.ownerId, + dto.checkActiveLead ?? false, + dto.checkDuplicate ?? false, + ); + } + + update(dto: ChatProviderEntitySettingsDto): ChatProviderEntitySettings { + this.contactEntityTypeId = + dto.contactEntityTypeId !== undefined ? dto.contactEntityTypeId : this.contactEntityTypeId; + this.leadEntityTypeId = dto.leadEntityTypeId !== undefined ? dto.leadEntityTypeId : this.leadEntityTypeId; + this.leadBoardId = dto.leadBoardId !== undefined ? dto.leadBoardId : this.leadBoardId; + this.leadStageId = dto.leadStageId !== undefined ? dto.leadStageId : this.leadStageId; + this.leadName = dto.leadName !== undefined ? dto.leadName : this.leadName; + this.ownerId = dto.ownerId !== undefined ? dto.ownerId : this.ownerId; + this.checkActiveLead = dto.checkActiveLead !== undefined ? dto.checkActiveLead : this.checkActiveLead; + this.checkDuplicate = dto.checkDuplicate !== undefined ? dto.checkDuplicate : this.checkDuplicate; + + return this; + } + + toDto(): ChatProviderEntitySettingsDto { + return { + contactEntityTypeId: this.contactEntityTypeId, + leadEntityTypeId: this.leadEntityTypeId, + leadBoardId: this.leadBoardId, + leadStageId: this.leadStageId, + leadName: this.leadName, + ownerId: this.ownerId, + checkActiveLead: this.checkActiveLead, + checkDuplicate: this.checkDuplicate, + }; + } +} diff --git a/backend/src/modules/multichat/chat-provider/entities/chat-provider.entity.ts b/backend/src/modules/multichat/chat-provider/entities/chat-provider.entity.ts new file mode 100644 index 0000000..c0ad4d7 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/entities/chat-provider.entity.ts @@ -0,0 +1,130 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { ChatProviderStatus, ChatProviderTransport, ChatProviderType } from '../../common'; +import { ChatProviderUser } from '../../chat-provider-user'; +import { CreateChatProviderDto, ChatProviderDto, UpdateChatProviderDto } from '../dto'; +import { ChatProviderEntitySettings } from './chat-provider-entity-settings.entity'; + +@Entity() +export class ChatProvider { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + createdBy: number; + + @Column() + type: ChatProviderType; + + @Column() + transport: ChatProviderTransport; + + @Column({ nullable: true }) + title: string | null; + + @Column() + status: ChatProviderStatus; + + @Column() + messagePerDay: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + createdBy: number, + type: ChatProviderType, + transport: ChatProviderTransport, + title: string | null, + status: ChatProviderStatus, + messagePerDay: number, + createdAt?: Date, + ) { + this.accountId = accountId; + this.createdBy = createdBy; + this.type = type; + this.transport = transport; + this.title = title; + this.status = status; + this.messagePerDay = messagePerDay; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _accessibleUsers: ChatProviderUser[]; + get accessibleUsers(): ChatProviderUser[] { + return this._accessibleUsers; + } + set accessibleUsers(value: ChatProviderUser[]) { + this._accessibleUsers = value; + } + + private _responsibleUsers: ChatProviderUser[]; + get responsibleUsers(): ChatProviderUser[] { + return this._responsibleUsers; + } + set responsibleUsers(value: ChatProviderUser[]) { + this._responsibleUsers = value; + } + + private _unseenCount: number | null; + get unseenCount(): number | null { + return this._unseenCount; + } + set unseenCount(value: number | null) { + this._unseenCount = value; + } + + private _supervisorUsers: ChatProviderUser[]; + get supervisorUsers(): ChatProviderUser[] { + return this._supervisorUsers; + } + set supervisorUsers(value: ChatProviderUser[]) { + this._supervisorUsers = value; + } + + private _entitySettings: ChatProviderEntitySettings | null; + get entitySettings(): ChatProviderEntitySettings | null { + return this._entitySettings; + } + set entitySettings(value: ChatProviderEntitySettings | null) { + this._entitySettings = value; + } + + canSendByPhone(): boolean { + return [ChatProviderType.Twilio, ChatProviderType.Wazzup].includes(this.type); + } + + static fromDto(accountId: number, createdBy: number, dto: CreateChatProviderDto): ChatProvider { + return new ChatProvider(accountId, createdBy, dto.type, dto.transport, dto.title, dto.status, dto.messagePerDay); + } + + toDto(): ChatProviderDto { + return { + id: this.id, + type: this.type, + transport: this.transport, + title: this.title, + status: this.status, + messagePerDay: this.messagePerDay, + accessibleUserIds: this.accessibleUsers?.map((user) => user.userId), + responsibleUserIds: this.responsibleUsers?.map((user) => user.userId), + supervisorUserIds: this.supervisorUsers?.map((user) => user.userId), + unseenCount: this.unseenCount, + entitySettings: this.entitySettings?.toDto(), + }; + } + + update(dto: UpdateChatProviderDto): ChatProvider { + this.title = dto.title !== undefined ? dto.title : this.title; + this.status = dto.status !== undefined ? dto.status : this.status; + this.messagePerDay = dto.messagePerDay !== undefined ? dto.messagePerDay : this.messagePerDay; + + return this; + } +} diff --git a/backend/src/modules/multichat/chat-provider/entities/index.ts b/backend/src/modules/multichat/chat-provider/entities/index.ts new file mode 100644 index 0000000..82da57c --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/entities/index.ts @@ -0,0 +1,2 @@ +export * from './chat-provider-entity-settings.entity'; +export * from './chat-provider.entity'; diff --git a/backend/src/modules/multichat/chat-provider/index.ts b/backend/src/modules/multichat/chat-provider/index.ts new file mode 100644 index 0000000..30a9009 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/index.ts @@ -0,0 +1,6 @@ +export * from './chat-provider.controller'; +export * from './chat-provider.module'; +export * from './dto'; +export * from './entities'; +export * from './services'; +export * from './types'; diff --git a/backend/src/modules/multichat/chat-provider/services/chat-provider-entity-settings.service.ts b/backend/src/modules/multichat/chat-provider/services/chat-provider-entity-settings.service.ts new file mode 100644 index 0000000..a0c50d4 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/services/chat-provider-entity-settings.service.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ChatProviderEntitySettingsDto } from '../dto'; +import { ChatProviderEntitySettings } from '../entities'; + +@Injectable() +export class ChatProviderEntitySettingsService { + constructor( + @InjectRepository(ChatProviderEntitySettings) + private readonly repository: Repository, + ) {} + + async create({ + accountId, + providerId, + dto, + }: { + accountId: number; + providerId: number; + dto: ChatProviderEntitySettingsDto; + }): Promise { + return this.repository.save(ChatProviderEntitySettings.fromDto({ accountId, providerId, dto })); + } + + async findOne({ + accountId, + providerId, + }: { + accountId: number; + providerId: number; + }): Promise { + return this.repository.findOneBy({ accountId, providerId }); + } + + async update({ + accountId, + providerId, + dto, + }: { + accountId: number; + providerId: number; + dto: ChatProviderEntitySettingsDto; + }): Promise { + const settings = await this.findOne({ accountId, providerId }); + if (settings) { + await this.repository.save(settings.update(dto)); + return settings; + } else { + return this.create({ accountId, providerId, dto }); + } + } + + async updateUser({ + accountId, + userId, + newUserId, + }: { + accountId: number; + userId: number; + newUserId?: number | null; + }): Promise { + await this.repository.update({ accountId, ownerId: userId }, { ownerId: newUserId ?? null }); + } + + async delete({ accountId, providerId }: { accountId: number; providerId: number }): Promise { + await this.repository.delete({ accountId, providerId }); + } +} diff --git a/backend/src/modules/multichat/chat-provider/services/chat-provider.handler.ts b/backend/src/modules/multichat/chat-provider/services/chat-provider.handler.ts new file mode 100644 index 0000000..6ed90e5 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/services/chat-provider.handler.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { ApplicationConfig } from '@/config'; + +import { AccountCreatedEvent, IamEventType, UserDeletedEvent } from '@/modules/iam/common'; + +import { ChatProviderDefaults } from '../const'; +import { ChatProviderService } from './chat-provider.service'; +import { ChatProviderEntitySettingsService } from './chat-provider-entity-settings.service'; + +@Injectable() +export class ChatProviderHandler { + private _appName: string; + constructor( + private readonly configService: ConfigService, + private readonly chatProviderService: ChatProviderService, + private readonly chatProviderEntitySettingsService: ChatProviderEntitySettingsService, + ) { + this._appName = this.configService.get('application').name; + } + + @OnEvent(IamEventType.AccountCreated, { async: true }) + async handleAccountCreatedEvent(event: AccountCreatedEvent) { + await this.chatProviderService.create(event.accountId, event.ownerId, { + type: ChatProviderDefaults.type, + transport: ChatProviderDefaults.transport, + title: this._appName, + status: ChatProviderDefaults.status, + messagePerDay: ChatProviderDefaults.messagePerDay, + }); + } + + @OnEvent(IamEventType.UserDeleted, { async: true }) + async onUserDeleted(event: UserDeletedEvent) { + await this.chatProviderEntitySettingsService.updateUser({ + accountId: event.accountId, + userId: event.userId, + newUserId: event.newUserId, + }); + } +} diff --git a/backend/src/modules/multichat/chat-provider/services/chat-provider.service.ts b/backend/src/modules/multichat/chat-provider/services/chat-provider.service.ts new file mode 100644 index 0000000..fc7c593 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/services/chat-provider.service.ts @@ -0,0 +1,329 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { + ChatProviderEvent, + ChatProviderTransport, + ChatProviderType, + ChatUserRole, + MultichatEventType, +} from '../../common'; +import { ChatService } from '../../chat/services/chat.service'; +import { ChatProviderUserService, ChatProviderUserType } from '../../chat-provider-user'; +import { ChatUser, ChatUserExternalDto, ChatUserService } from '../../chat-user'; + +import { ChatProviderDefaults } from '../const'; +import { CreateChatProviderDto, UpdateChatProviderDto } from '../dto'; +import { ChatProvider } from '../entities'; +import { ExpandableField } from '../types'; +import { ChatProviderEntitySettingsService } from './chat-provider-entity-settings.service'; + +interface FindFilter { + providerId?: number | number[]; + type?: ChatProviderType; + transport?: ChatProviderTransport; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class ChatProviderService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(ChatProvider) + private readonly repository: Repository, + private readonly cpUserService: ChatProviderUserService, + private readonly cpEntitySettingsService: ChatProviderEntitySettingsService, + @Inject(forwardRef(() => ChatService)) + private readonly chatService: ChatService, + private readonly chatUserService: ChatUserService, + ) {} + + async create(accountId: number, userId: number, dto: CreateChatProviderDto): Promise { + dto.messagePerDay ??= ChatProviderDefaults.messagePerDay; + const provider = await this.repository.save(ChatProvider.fromDto(accountId, userId, dto)); + + if (dto.accessibleUserIds?.length > 0) { + provider.accessibleUsers = await this.cpUserService.create( + accountId, + provider.id, + dto.accessibleUserIds, + ChatProviderUserType.Accessible, + ); + } + if (dto.responsibleUserIds?.length > 0) { + provider.responsibleUsers = await this.cpUserService.create( + accountId, + provider.id, + dto.responsibleUserIds, + ChatProviderUserType.Responsible, + ); + } + if (dto.supervisorUserIds?.length > 0) { + provider.supervisorUsers = await this.cpUserService.create( + accountId, + provider.id, + dto.supervisorUserIds, + ChatProviderUserType.Supervisor, + ); + } + if (dto.entitySettings) { + provider.entitySettings = await this.cpEntitySettingsService.create({ + accountId, + providerId: provider.id, + dto: dto.entitySettings, + }); + } + + this.eventEmitter.emit( + MultichatEventType.ChatProviderCreated, + new ChatProviderEvent({ accountId, userId, providerId: provider.id, status: provider.status }), + ); + + return provider; + } + + async findOne( + accountId: number, + userId: number | null, + filter?: FindFilter, + options?: FindOptions, + ): Promise { + const provider = await this.createFindQb(accountId, filter).getOne(); + + const expandedProvider = + provider && options?.expand ? await this.expandOne(accountId, userId, provider, options.expand) : provider; + + if (userId) { + const accessibleUsers = + expandedProvider.accessibleUsers || + (await this.cpUserService.findMany(accountId, { + providerId: provider.id, + type: ChatProviderUserType.Accessible, + })); + + return !accessibleUsers || accessibleUsers.length === 0 || accessibleUsers.some((u) => u.userId === userId) + ? expandedProvider + : null; + } else { + return expandedProvider; + } + } + + async findMany( + accountId: number, + userId: number | null, + filter?: FindFilter, + options?: FindOptions, + ): Promise { + const providers = await this.createFindQb(accountId, filter).orderBy('cp.created_at').getMany(); + const expandedProviders = + providers && options?.expand ? await this.expandMany(accountId, userId, providers, options.expand) : providers; + + if (userId) { + const filteredProviders: ChatProvider[] = []; + for (const provider of expandedProviders) { + const accessibleUsers = + provider.accessibleUsers || + (await this.cpUserService.findMany(accountId, { + providerId: provider.id, + type: ChatProviderUserType.Accessible, + })); + if (!accessibleUsers || accessibleUsers.length === 0 || accessibleUsers.some((u) => u.userId === userId)) { + filteredProviders.push(provider); + } + } + return filteredProviders; + } else { + return expandedProviders; + } + } + + async update( + accountId: number, + userId: number | null, + providerId: number, + dto: UpdateChatProviderDto, + ): Promise { + const provider: ChatProvider = await this.findOne( + accountId, + userId, + { providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + + await this.repository.save(provider.update(dto)); + + if (dto.accessibleUserIds) { + provider.accessibleUsers = await this.cpUserService.update( + accountId, + provider.id, + provider.accessibleUsers, + dto.accessibleUserIds, + ChatProviderUserType.Accessible, + ); + } + if (dto.responsibleUserIds) { + provider.responsibleUsers = await this.cpUserService.update( + accountId, + provider.id, + provider.responsibleUsers, + dto.responsibleUserIds, + ChatProviderUserType.Responsible, + ); + } + if (dto.supervisorUserIds) { + provider.supervisorUsers = await this.cpUserService.update( + accountId, + provider.id, + provider.supervisorUsers, + dto.supervisorUserIds, + ChatProviderUserType.Supervisor, + ); + } + + if (dto.entitySettings === null) { + await this.cpEntitySettingsService.delete({ accountId, providerId: provider.id }); + provider.entitySettings = null; + } else if (dto.entitySettings) { + provider.entitySettings = await this.cpEntitySettingsService.update({ + accountId, + providerId: provider.id, + dto: dto.entitySettings, + }); + } + + this.eventEmitter.emit( + MultichatEventType.ChatProviderUpdated, + new ChatProviderEvent({ accountId, userId, providerId: provider.id, status: provider.status }), + ); + + return provider; + } + + async delete({ accountId, userId, providerId }: { accountId: number; userId: number; providerId: number }) { + await this.repository.delete({ accountId, id: providerId }); + + this.eventEmitter.emit( + MultichatEventType.ChatProviderDeleted, + new ChatProviderEvent({ accountId, userId, providerId }), + ); + } + + async getChatUserExternal({ + accountId, + providerId, + chatExternalId, + externalUserDto, + }: { + accountId: number; + providerId: number; + chatExternalId: string; + externalUserDto: ChatUserExternalDto; + }): Promise { + const chat = await this.chatService.findOne({ + accountId, + filter: { providerId, externalId: chatExternalId }, + }); + if (chat) { + const chatUser = await this.chatUserService.findOne(accountId, { + providerId, + chatId: chat.id, + role: ChatUserRole.EXTERNAL, + }); + if (chatUser) { + return chatUser; + } else { + const [newUser] = await this.chatUserService.addUsers({ + accountId, + chatId: chat.id, + externalUsers: [externalUserDto], + }); + return newUser; + } + } else { + const newChat = await this.chatService.createExternalChat(accountId, null, { + providerId: providerId, + externalId: chatExternalId, + title: `${externalUserDto.firstName} ${externalUserDto.lastName}`.trim(), + externalUser: externalUserDto, + }); + + return newChat.users.find((u) => u.externalUser?.externalId === externalUserDto.externalId); + } + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('cp').where('cp.account_id = :accountId', { accountId }); + + if (filter?.providerId) { + if (Array.isArray(filter.providerId)) { + qb.andWhere('cp.id IN (:...ids)', { ids: filter.providerId }); + } else { + qb.andWhere('cp.id = :id', { id: filter.providerId }); + } + } + + if (filter?.type) { + qb.andWhere('cp.type = :type', { type: filter.type }); + } + + if (filter?.transport) { + qb.andWhere('cp.transport = :transport', { transport: filter.transport }); + } + + return qb; + } + + private async expandOne( + accountId: number, + userId: number | null, + provider: ChatProvider, + expand: ExpandableField[], + ): Promise { + if (userId && expand.includes('unseenCount')) { + provider.unseenCount = await this.chatService.getUnseenForUser(accountId, userId, provider.id); + } + + if (expand.includes('accessibleUsers')) { + provider.accessibleUsers = await this.cpUserService.findMany(accountId, { + providerId: provider.id, + type: ChatProviderUserType.Accessible, + }); + } + + if (expand.includes('responsibleUsers')) { + provider.responsibleUsers = await this.cpUserService.findMany(accountId, { + providerId: provider.id, + type: ChatProviderUserType.Responsible, + }); + } + + if (expand.includes('supervisorUsers')) { + provider.supervisorUsers = await this.cpUserService.findMany(accountId, { + providerId: provider.id, + type: ChatProviderUserType.Supervisor, + }); + } + + if (expand.includes('entitySettings')) { + provider.entitySettings = await this.cpEntitySettingsService.findOne({ + accountId, + providerId: provider.id, + }); + } + + return provider; + } + private async expandMany( + accountId: number, + userId: number | null, + providers: ChatProvider[], + expand: ExpandableField[], + ): Promise { + return Promise.all(providers.map((provider) => this.expandOne(accountId, userId, provider, expand))); + } +} diff --git a/backend/src/modules/multichat/chat-provider/services/index.ts b/backend/src/modules/multichat/chat-provider/services/index.ts new file mode 100644 index 0000000..b09e3e3 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/services/index.ts @@ -0,0 +1,3 @@ +export * from './chat-provider-entity-settings.service'; +export * from './chat-provider.handler'; +export * from './chat-provider.service'; diff --git a/backend/src/modules/multichat/chat-provider/types/expandable-field.ts b/backend/src/modules/multichat/chat-provider/types/expandable-field.ts new file mode 100644 index 0000000..46ba67a --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/types/expandable-field.ts @@ -0,0 +1,6 @@ +export type ExpandableField = + | 'accessibleUsers' + | 'responsibleUsers' + | 'unseenCount' + | 'supervisorUsers' + | 'entitySettings'; diff --git a/backend/src/modules/multichat/chat-provider/types/index.ts b/backend/src/modules/multichat/chat-provider/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/multichat/chat-provider/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/multichat/chat-user/chat-user.module.ts b/backend/src/modules/multichat/chat-user/chat-user.module.ts new file mode 100644 index 0000000..158b9fb --- /dev/null +++ b/backend/src/modules/multichat/chat-user/chat-user.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { ChatUser, ChatUserExternal } from './entities'; +import { ChatUserService } from './chat-user.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([ChatUser, ChatUserExternal])], + providers: [ChatUserService], + exports: [ChatUserService], +}) +export class ChatUserModule {} diff --git a/backend/src/modules/multichat/chat-user/chat-user.service.ts b/backend/src/modules/multichat/chat-user/chat-user.service.ts new file mode 100644 index 0000000..6635af4 --- /dev/null +++ b/backend/src/modules/multichat/chat-user/chat-user.service.ts @@ -0,0 +1,195 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { NotFoundError } from '@/common'; + +import { ChatUserRole } from '../common'; +import { ChatUserExternalDto } from './dto'; +import { ChatUser, ChatUserExternal } from './entities'; + +interface FindFilter { + id?: number; + chatId?: number; + userId?: number; + providerId?: number; + externalId?: string; + role?: ChatUserRole; +} + +@Injectable() +export class ChatUserService { + constructor( + @InjectRepository(ChatUser) + private readonly repository: Repository, + @InjectRepository(ChatUserExternal) + private readonly extRepository: Repository, + ) {} + + async createForChat( + accountId: number, + chatId: number, + { + ownerIds, + userIds, + supervisorIds, + externalUsers, + }: { + ownerIds: number[]; + userIds?: number[]; + supervisorIds?: number[]; + externalUsers?: ChatUserExternalDto[]; + }, + ): Promise { + const chatUsers: ChatUser[] = []; + + for (const ownerId of ownerIds) { + chatUsers.push(await this.repository.save(new ChatUser(accountId, chatId, ChatUserRole.OWNER, ownerId))); + } + + if (userIds?.length > 0) { + for (const userId of userIds.filter((id) => !chatUsers.some((u) => u.userId === id))) { + chatUsers.push(await this.repository.save(new ChatUser(accountId, chatId, ChatUserRole.USER, userId))); + } + } + + if (supervisorIds?.length > 0) { + for (const supervisorId of supervisorIds.filter((id) => !chatUsers.some((u) => u.userId === id))) { + chatUsers.push( + await this.repository.save(new ChatUser(accountId, chatId, ChatUserRole.SUPERVISOR, supervisorId)), + ); + } + } + + if (externalUsers?.length > 0) { + for (const extUserDto of externalUsers) { + const user = await this.repository.save(new ChatUser(accountId, chatId, ChatUserRole.EXTERNAL, null)); + user.externalUser = await this.extRepository.save(ChatUserExternal.fromDto(accountId, user.id, extUserDto)); + chatUsers.push(user); + } + } + + return chatUsers; + } + + async findOne(accountId: number, filter: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + async getOne(accountId: number, filter: FindFilter): Promise { + const chatUser = await this.findOne(accountId, filter); + + if (!chatUser) { + throw new NotFoundError(`User ${filter?.userId} not found in chat ${filter?.chatId}`); + } + + return chatUser; + } + async findMany(accountId: number, filter: FindFilter): Promise { + return this.createFindQb(accountId, filter).getMany(); + } + async count(accountId: number, filter: FindFilter): Promise { + return this.createFindQb(accountId, filter).getCount(); + } + + async updateForGroupChat( + accountId: number, + chatId: number, + ownerId: number, + currentUsers: ChatUser[], + participantIds: number[], + ): Promise { + const removeUsers = currentUsers + .filter((user) => user.role !== ChatUserRole.EXTERNAL) + .filter((user) => !participantIds.some((id) => id === user.userId)) + .filter((user) => user.userId !== ownerId); + + const addedUsers = await this.addUsers({ accountId, chatId, userIds: participantIds, currentUsers }); + if (addedUsers.length) { + currentUsers.push(...addedUsers); + } + + if (removeUsers.length > 0) { + await this.repository.remove(removeUsers); + } + + return currentUsers.filter((user) => !removeUsers.some((u) => u.userId === user.userId)); + } + + async addUsers({ + accountId, + chatId, + userIds, + currentUsers, + externalUsers, + }: { + accountId: number; + chatId: number; + userIds?: number[] | null; + currentUsers?: ChatUser[]; + externalUsers?: ChatUserExternalDto[] | null; + }): Promise { + const chatUsers: ChatUser[] = []; + + if (userIds) { + const users = currentUsers ?? (await this.findMany(accountId, { chatId })); + const addUsers = userIds.filter((id) => !users.some((user) => user.userId === id)); + for (const userId of addUsers) { + chatUsers.push(await this.repository.save(new ChatUser(accountId, chatId, ChatUserRole.USER, userId))); + } + } + + if (externalUsers) { + for (const extUserDto of externalUsers) { + const user = await this.repository.save(new ChatUser(accountId, chatId, ChatUserRole.EXTERNAL, null)); + user.externalUser = await this.extRepository.save(ChatUserExternal.fromDto(accountId, user.id, extUserDto)); + chatUsers.push(user); + } + } + + return chatUsers; + } + + async updateExternalUser(accountId: number, chatUser: ChatUser, dto: ChatUserExternalDto): Promise { + const extUser = await this.extRepository.findOneBy({ accountId, chatUserId: chatUser.id }); + + await this.extRepository.save(extUser.update(dto)); + + chatUser.externalUser = extUser; + return chatUser; + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository + .createQueryBuilder('user') + .leftJoinAndMapOne('user.externalUser', ChatUserExternal, 'ext_user', `ext_user.chat_user_id = user.id`) + .where('user.account_id = :accountId', { accountId }); + + if (filter?.id) { + qb.andWhere('user.id = :id', { id: filter.id }); + } + + if (filter?.chatId) { + qb.andWhere('user.chat_id = :chatId', { chatId: filter.chatId }); + } + + if (filter?.userId) { + qb.andWhere('user.user_id = :userId', { userId: filter.userId }); + } + + if (filter?.externalId) { + qb.andWhere('ext_user.external_id = :externalId', { externalId: filter.externalId }); + } + + if (filter?.providerId) { + qb.leftJoin('chat', 'chat', 'user.chat_id = chat.id').andWhere('chat.provider_id = :providerId', { + providerId: filter.providerId, + }); + } + + if (filter?.role) { + qb.andWhere('user.role = :role', { role: filter.role }); + } + + return qb; + } +} diff --git a/backend/src/modules/multichat/chat-user/dto/chat-user-external.dto.ts b/backend/src/modules/multichat/chat-user/dto/chat-user-external.dto.ts new file mode 100644 index 0000000..2c9b5db --- /dev/null +++ b/backend/src/modules/multichat/chat-user/dto/chat-user-external.dto.ts @@ -0,0 +1,56 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class ChatUserExternalDto { + @ApiProperty() + @IsString() + externalId: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + firstName?: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + lastName?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + avatarUrl?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + phone?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + email?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + link?: string | null; + + constructor( + externalId: string, + firstName?: string, + lastName?: string | null, + avatarUrl?: string | null, + phone?: string | null, + email?: string | null, + link?: string | null, + ) { + this.externalId = externalId; + this.firstName = firstName; + this.lastName = lastName; + this.avatarUrl = avatarUrl; + this.phone = phone; + this.email = email; + this.link = link; + } +} diff --git a/backend/src/modules/multichat/chat-user/dto/chat-user.dto.ts b/backend/src/modules/multichat/chat-user/dto/chat-user.dto.ts new file mode 100644 index 0000000..8bc1a07 --- /dev/null +++ b/backend/src/modules/multichat/chat-user/dto/chat-user.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { ChatUserRole } from '../../common'; +import { ChatUserExternalDto } from './chat-user-external.dto'; + +export class ChatUserDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + userId: number | null; + + @ApiProperty({ enum: ChatUserRole }) + @IsEnum(ChatUserRole) + role: ChatUserRole; + + @ApiProperty({ type: ChatUserExternalDto, nullable: true }) + @IsOptional() + externalUser: ChatUserExternalDto | null; + + constructor({ id, userId, role, externalUser }: ChatUserDto) { + this.id = id; + this.userId = userId; + this.role = role; + this.externalUser = externalUser; + } +} diff --git a/backend/src/modules/multichat/chat-user/dto/index.ts b/backend/src/modules/multichat/chat-user/dto/index.ts new file mode 100644 index 0000000..8d35780 --- /dev/null +++ b/backend/src/modules/multichat/chat-user/dto/index.ts @@ -0,0 +1,2 @@ +export * from './chat-user-external.dto'; +export * from './chat-user.dto'; diff --git a/backend/src/modules/multichat/chat-user/entities/chat-user-external.entity.ts b/backend/src/modules/multichat/chat-user/entities/chat-user-external.entity.ts new file mode 100644 index 0000000..01906cb --- /dev/null +++ b/backend/src/modules/multichat/chat-user/entities/chat-user-external.entity.ts @@ -0,0 +1,96 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { ChatUserExternalDto } from '../dto'; + +@Entity() +export class ChatUserExternal { + @PrimaryColumn() + chatUserId: number; + + @Column() + externalId: string; + + @Column({ nullable: true }) + firstName: string | null; + + @Column({ nullable: true }) + lastName: string | null; + + @Column({ nullable: true }) + avatarUrl: string | null; + + @Column({ nullable: true }) + phone: string | null; + + @Column({ nullable: true }) + email: string | null; + + @Column({ nullable: true }) + link: string | null; + + @Column() + accountId: number; + + constructor( + accountId: number, + chatUserId: number, + externalId: string, + firstName: string | null, + lastName: string | null, + avatarUrl: string | null, + phone: string | null, + email: string | null, + link: string | null, + ) { + this.accountId = accountId; + this.chatUserId = chatUserId; + this.externalId = externalId; + this.firstName = firstName; + this.lastName = lastName; + this.avatarUrl = avatarUrl; + this.phone = phone; + this.email = email; + this.link = link; + } + + public static fromDto(accountId: number, chatUserId: number, dto: ChatUserExternalDto): ChatUserExternal { + return new ChatUserExternal( + accountId, + chatUserId, + dto.externalId, + dto.firstName, + dto.lastName, + dto.avatarUrl, + dto.phone, + dto.email, + dto.link, + ); + } + + public update(dto: ChatUserExternalDto): ChatUserExternal { + this.externalId = dto.externalId ? dto.externalId : this.externalId; + this.firstName = dto.firstName ? dto.firstName : this.firstName; + this.lastName = dto.lastName ? dto.lastName : this.lastName; + this.avatarUrl = dto.avatarUrl ? dto.avatarUrl : this.avatarUrl; + this.phone = dto.phone ? dto.phone : this.phone; + this.email = dto.email ? dto.email : this.email; + this.link = dto.link ? dto.link : this.link; + + return this; + } + + public toDto(): ChatUserExternalDto { + return { + externalId: this.externalId, + firstName: this.firstName, + lastName: this.lastName, + avatarUrl: this.avatarUrl, + phone: this.phone, + email: this.email, + link: this.link, + }; + } + + public fullName() { + return `${this.firstName} ${this.lastName ?? ''}`.trim(); + } +} diff --git a/backend/src/modules/multichat/chat-user/entities/chat-user.entity.ts b/backend/src/modules/multichat/chat-user/entities/chat-user.entity.ts new file mode 100644 index 0000000..44b186f --- /dev/null +++ b/backend/src/modules/multichat/chat-user/entities/chat-user.entity.ts @@ -0,0 +1,47 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { ChatUserRole } from '../../common'; +import { ChatUserDto } from '../dto'; +import { ChatUserExternal } from './chat-user-external.entity'; + +@Entity() +export class ChatUser { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + chatId: number; + + @Column({ nullable: true }) + userId: number | null; + + @Column() + role: ChatUserRole; + + @Column() + accountId: number; + + constructor(accountId: number, chatId: number, role: ChatUserRole, userId: number | null) { + this.accountId = accountId; + this.chatId = chatId; + this.role = role; + this.userId = userId; + } + + private _externalUser: ChatUserExternal | null; + public get externalUser(): ChatUserExternal | null { + return this._externalUser; + } + public set externalUser(value: ChatUserExternal | null) { + this._externalUser = value; + } + + public toDto(): ChatUserDto { + return new ChatUserDto({ + id: this.id, + userId: this.userId, + role: this.role, + externalUser: this.externalUser?.toDto() ?? null, + }); + } +} diff --git a/backend/src/modules/multichat/chat-user/entities/index.ts b/backend/src/modules/multichat/chat-user/entities/index.ts new file mode 100644 index 0000000..dc4bcd1 --- /dev/null +++ b/backend/src/modules/multichat/chat-user/entities/index.ts @@ -0,0 +1,2 @@ +export * from './chat-user-external.entity'; +export * from './chat-user.entity'; diff --git a/backend/src/modules/multichat/chat-user/index.ts b/backend/src/modules/multichat/chat-user/index.ts new file mode 100644 index 0000000..0e6900f --- /dev/null +++ b/backend/src/modules/multichat/chat-user/index.ts @@ -0,0 +1,4 @@ +export * from './chat-user.module'; +export * from './chat-user.service'; +export * from './dto'; +export * from './entities'; diff --git a/backend/src/modules/multichat/chat/chat.controller.ts b/backend/src/modules/multichat/chat/chat.controller.ts new file mode 100644 index 0000000..39a8181 --- /dev/null +++ b/backend/src/modules/multichat/chat/chat.controller.ts @@ -0,0 +1,231 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseEnumPipe, + ParseIntPipe, + Patch, + Post, + Put, + Query, +} from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { ChatPagingQuery } from '@/common/dto/paging/chat-paging-query.dto'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { EntityInfoDto } from '@/modules/entity/entity-info'; + +import { ChatMessageStatus } from '../common'; +import { + ChatDto, + CreatePersonalChatDto, + CreateGroupChatDto, + CreateExternalChatDto, + FindChatsFullResultDto, + ChatFindFilterDto, + ChatFindPersonalFilterDto, + ChatFindByMessageContentFilterDto, + UpdateGroupChatDto, + CreateContactLeadDto, +} from './dto'; +import { ChatService } from './services'; + +@ApiTags('multichat/chats') +@Controller('chat/chats') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ChatController { + constructor(private readonly service: ChatService) {} + + @ApiOperation({ summary: 'Create personal chat', description: 'Create personal chat' }) + @ApiBody({ description: 'Data for creating personal chat', type: CreatePersonalChatDto }) + @ApiCreatedResponse({ description: 'Chat', type: ChatDto }) + @Post('personal') + async createPersonal(@CurrentAuth() { accountId, user }: AuthData, @Body() dto: CreatePersonalChatDto) { + return this.service.createPersonalChat(accountId, user, dto); + } + + @ApiOperation({ summary: 'Create group chat', description: 'Create group chat' }) + @ApiBody({ description: 'Data for creating group chat', type: CreateGroupChatDto }) + @ApiCreatedResponse({ description: 'Chat', type: ChatDto }) + @Post('group') + async createGroup(@CurrentAuth() { accountId, user }: AuthData, @Body() dto: CreateGroupChatDto) { + return this.service.createGroupChat(accountId, user, dto); + } + + @ApiOperation({ summary: 'Create external chat', description: 'Create external chat' }) + @ApiBody({ description: 'Data for creating external chat', type: CreateExternalChatDto }) + @ApiCreatedResponse({ description: 'Chat', type: ChatDto }) + @Post('external') + async createExternal(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateExternalChatDto) { + return this.service.createExternalChat(accountId, userId, dto); + } + + @ApiOperation({ summary: 'Check exists', description: 'Check group chat exists for entity' }) + @ApiParam({ name: 'entityId', description: 'Checked Entity ID' }) + @ApiOkResponse({ description: 'Chat exists', type: Boolean }) + @Get('group/exists/:entityId') + async checkByEntityId(@CurrentAuth() { accountId }: AuthData, @Param('entityId', ParseIntPipe) entityId: number) { + return (await this.service.count(accountId, { entityId })) > 0; + } + + @ApiOperation({ + summary: 'Get current user chats', + description: 'Get current user chat with pagination and provider filter', + }) + @ApiQuery({ name: 'providerId', description: 'Provider ID', required: false }) + @ApiQuery({ name: 'limit', description: 'Limit for pagination', required: false }) + @ApiQuery({ name: 'offset', description: 'Offset for pagination', required: false }) + @ApiQuery({ name: 'cursor', description: 'Cursor position for pagination', required: false }) + @ApiOkResponse({ description: 'Chat list', type: [ChatDto] }) + @Get() + async getChats( + @CurrentAuth() { accountId, user }: AuthData, + @Query() paging: ChatPagingQuery, + @Query('providerId') providerId: number | null, + ) { + return this.service.getChats(accountId, user, providerId, paging); + } + + @ApiOperation({ + summary: 'Search chats (simple)', + description: 'Search chats with filter. Chats returned without additional data.', + }) + @ApiOkResponse({ description: 'Search chat result', type: FindChatsFullResultDto }) + @Get('find') + async findMany( + @CurrentAuth() { accountId, userId }: AuthData, + @Query() filter: ChatFindFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.findMany({ accountId, filter, paging, accessUserId: userId }); + } + + @ApiOperation({ + summary: 'Search chats by user full name', + description: 'Search chats by user full name. Chats returned with users, last message, entity info, etc.', + }) + @ApiOkResponse({ description: 'Search chat result', type: FindChatsFullResultDto }) + @Get('find/full/personal') + async findManyFullPersonal( + @CurrentAuth() { accountId, user }: AuthData, + @Query() filter: ChatFindPersonalFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.findManyFullPersonal(accountId, user, filter, paging); + } + + @ApiOperation({ + summary: 'Search chats by message content', + description: 'Search chats by message content. Chats returned with users, last message, entity info, etc.', + }) + @ApiOkResponse({ description: 'Search chat result', type: FindChatsFullResultDto }) + @Get('find/full/by-message-content') + async findManyFullByMessageContent( + @CurrentAuth() { accountId, user }: AuthData, + @Query() paging: PagingQuery, + @Query() filter: ChatFindByMessageContentFilterDto, + ) { + return this.service.findManyFullByMessageContent(accountId, user, filter, paging); + } + + @ApiOperation({ + summary: 'Search chats (full)', + description: 'Search chats with filter. Chats returned with users, last message, entity info, etc.', + }) + @ApiOkResponse({ description: 'Search chat result', type: FindChatsFullResultDto }) + @Get('find/full') + async findManyFull( + @CurrentAuth() { accountId, user }: AuthData, + @Query() filter: ChatFindFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.findManyFull(accountId, user, filter, paging); + } + + @ApiOperation({ summary: 'Get chat', description: 'Get chat by ID' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiOkResponse({ description: 'Chat', type: ChatDto }) + @Get(':chatId') + async getChatFull(@CurrentAuth() { accountId, user }: AuthData, @Param('chatId', ParseIntPipe) chatId: number) { + return this.service.getChatFull(accountId, user, chatId); + } + + @ApiOperation({ summary: 'Update group chat', description: 'Update group chat' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiBody({ description: 'Data for updating group chat', type: UpdateGroupChatDto }) + @ApiOkResponse({ description: 'Chat', type: ChatDto }) + @Patch('group/:chatId') + async updateGroup( + @CurrentAuth() { accountId, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Body() dto: UpdateGroupChatDto, + ) { + return this.service.updateGroupChat(accountId, user, chatId, dto); + } + + @ApiOperation({ summary: 'Delete chat', description: 'Delete chat by ID' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiOkResponse({ description: 'Deleted chat ID', type: Number }) + @Delete(':chatId') + async delete(@CurrentAuth() { accountId, userId }: AuthData, @Param('chatId', ParseIntPipe) chatId: number) { + return this.service.delete(accountId, userId, chatId); + } + + @ApiOperation({ summary: 'Pin chat message', description: 'Pin chat message' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiParam({ name: 'messageId', description: 'Message ID' }) + @ApiOkResponse({ description: 'Chat', type: ChatDto }) + @Put(':chatId/pin/:messageId') + async pinChatMessage( + @CurrentAuth() { accountId, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return this.service.pinMessage(accountId, user, chatId, messageId); + } + + @ApiOperation({ summary: 'Unpin chat message', description: 'Unpin chat message' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiParam({ name: 'messageId', description: 'Message ID' }) + @ApiOkResponse({ description: 'Chat', type: ChatDto }) + @Put(':chatId/unpin/:messageId') + async unpinChatMessage( + @CurrentAuth() { accountId, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('messageId', ParseIntPipe) messageId: number, + ) { + return this.service.unpinMessage(accountId, user, chatId, messageId); + } + + @ApiOperation({ summary: 'Update chat messages status', description: 'Update chat messages status' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiParam({ name: 'status', description: 'Message status' }) + @ApiOkResponse() + @Put(':chatId/status/:status') + async updateMessagesStatus( + @CurrentAuth() { accountId, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Param('status', new ParseEnumPipe(ChatMessageStatus)) status: ChatMessageStatus, + ) { + await this.service.updateMessagesStatus({ accountId, user, chatId, status }); + } + + @ApiOperation({ summary: 'Create contact and lead', description: 'Create contact and lead for external chat' }) + @ApiParam({ name: 'chatId', description: 'Chat ID' }) + @ApiBody({ description: 'Data for creating contact and lead', type: CreateContactLeadDto }) + @ApiCreatedResponse({ description: 'Created entity info', type: EntityInfoDto }) + @Post(':chatId/contact') + async createLinkedEntities( + @CurrentAuth() { accountId, user }: AuthData, + @Param('chatId', ParseIntPipe) chatId: number, + @Body() dto: CreateContactLeadDto, + ) { + return this.service.createLinkedEntities({ accountId, user, chatId, dto }); + } +} diff --git a/backend/src/modules/multichat/chat/chat.module.ts b/backend/src/modules/multichat/chat/chat.module.ts new file mode 100644 index 0000000..40e8509 --- /dev/null +++ b/backend/src/modules/multichat/chat/chat.module.ts @@ -0,0 +1,34 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { CrmModule } from '@/CRM/crm.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { DocumentsModule } from '@/modules/documents/documents.module'; + +import { ChatUserModule } from '../chat-user'; +import { ChatMessageModule } from '../chat-message/chat-message.module'; +import { ChatProviderModule } from '../chat-provider/chat-provider.module'; +import { ProvidersModule } from '../providers/providers.module'; + +import { Chat, ChatPinnedMessage } from './entities'; +import { ChatService, ChatPinnedMessageService, ChatHandler } from './services'; +import { ChatController } from './chat.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Chat, ChatPinnedMessage]), + IAMModule, + forwardRef(() => CrmModule), + EntityInfoModule, + forwardRef(() => ChatMessageModule), + ChatProviderModule, + ProvidersModule, + ChatUserModule, + DocumentsModule, + ], + providers: [ChatService, ChatPinnedMessageService, ChatHandler], + controllers: [ChatController], + exports: [ChatService], +}) +export class ChatModule {} diff --git a/backend/src/modules/multichat/chat/dto/chat-find-by-message-content-filter.dto.ts b/backend/src/modules/multichat/chat/dto/chat-find-by-message-content-filter.dto.ts new file mode 100644 index 0000000..c5551fa --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/chat-find-by-message-content-filter.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ChatFindByMessageContentFilterDto { + @ApiProperty({ description: 'Content to find messages with matching text', required: true }) + @IsString() + messageContent: string; + + @ApiPropertyOptional({ description: 'Provider ID to filter messages', nullable: true, required: false }) + @IsOptional() + @IsNumber() + providerId?: number | null; +} diff --git a/backend/src/modules/multichat/chat/dto/chat-find-filter.dto.ts b/backend/src/modules/multichat/chat/dto/chat-find-filter.dto.ts new file mode 100644 index 0000000..7ad9144 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/chat-find-filter.dto.ts @@ -0,0 +1,30 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; +import { ChatProviderTransport } from '../../common'; + +export class ChatFindFilterDto { + @ApiPropertyOptional({ description: 'Chat ID', nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional({ description: 'Phone number if external user in chat', nullable: true }) + @IsOptional() + @IsString() + phoneNumber?: string | null; + + @ApiPropertyOptional({ description: 'Chat provider transport', enum: ChatProviderTransport, nullable: true }) + @IsOptional() + @IsEnum(ChatProviderTransport) + transport?: ChatProviderTransport | null; + + @ApiPropertyOptional({ description: 'Chat title', nullable: true }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiPropertyOptional({ description: 'Provider ID', nullable: true, required: false }) + @IsOptional() + @IsNumber() + providerId?: number; +} diff --git a/backend/src/modules/multichat/chat/dto/chat-find-personal-filter.dto.ts b/backend/src/modules/multichat/chat/dto/chat-find-personal-filter.dto.ts new file mode 100644 index 0000000..b594615 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/chat-find-personal-filter.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class ChatFindPersonalFilterDto { + @ApiProperty({ description: 'Full name of user with whom personal chat was created', required: false }) + @IsOptional() + @IsString() + fullName?: string; + + @ApiPropertyOptional({ description: 'Provider ID', nullable: true, required: false }) + @IsOptional() + @IsNumber() + providerId?: number | null; +} diff --git a/backend/src/modules/multichat/chat/dto/chat.dto.ts b/backend/src/modules/multichat/chat/dto/chat.dto.ts new file mode 100644 index 0000000..40ca2cb --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/chat.dto.ts @@ -0,0 +1,73 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { ChatType } from '../../common'; +import { ChatMessageDto } from '../../chat-message/dto/chat-message.dto'; +import { ChatUserDto } from '../../chat-user'; + +export class ChatDto { + @ApiProperty({ description: 'Chat ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Chat provider ID' }) + @IsNumber() + providerId: number; + + @ApiProperty({ description: 'User ID who created the chat' }) + @IsNumber() + createdBy: number; + + @ApiProperty({ description: 'External ID of the chat', nullable: true }) + @IsOptional() + @IsString() + externalId: string | null; + + @ApiProperty({ description: 'Type of the chat', enum: ChatType }) + @IsEnum(ChatType) + type: ChatType; + + @ApiProperty({ description: 'Title of the chat', nullable: true }) + @IsOptional() + @IsString() + title: string | null; + + @ApiProperty({ description: 'Entity ID associated with the chat', nullable: true }) + @IsOptional() + @IsNumber() + entityId: number | null; + + @ApiProperty({ description: 'Date and time when the chat was created' }) + @IsString() + createdAt: string; + + @ApiProperty({ description: 'Users in the chat', type: [ChatUserDto] }) + @IsArray() + users: ChatUserDto[]; + + @ApiProperty({ description: 'Pinned messages in the chat', type: [ChatMessageDto] }) + @IsArray() + pinnedMessages: ChatMessageDto[]; + + @ApiProperty({ description: 'Last message in the chat', type: [ChatMessageDto], nullable: true }) + lastMessage: ChatMessageDto | null; + + @ApiProperty({ description: 'Number of unseen messages in the chat for requested user' }) + @IsNumber() + unseenCount: number; + + @ApiProperty({ description: 'Date and time when the chat was last updated' }) + @IsString() + updatedAt: string; + + @ApiProperty({ description: 'Entity information associated with the chat', type: EntityInfoDto, nullable: true }) + @IsOptional() + entityInfo: EntityInfoDto | null; + + @ApiPropertyOptional({ description: 'Whether the user has access to the chat', nullable: true }) + @IsOptional() + @IsBoolean() + hasAccess?: boolean | null; +} diff --git a/backend/src/modules/multichat/chat/dto/create-contact-lead.dto.ts b/backend/src/modules/multichat/chat/dto/create-contact-lead.dto.ts new file mode 100644 index 0000000..a691cd1 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/create-contact-lead.dto.ts @@ -0,0 +1,44 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class CreateContactLeadDto { + @ApiPropertyOptional({ description: 'Contact entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + contactTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead entity type ID', nullable: true }) + @IsOptional() + @IsNumber() + leadTypeId?: number | null; + + @ApiPropertyOptional({ description: 'Lead board ID', nullable: true }) + @IsOptional() + @IsNumber() + leadBoardId?: number | null; + + @ApiPropertyOptional({ description: 'Lead stage ID', nullable: true }) + @IsOptional() + @IsNumber() + leadStageId?: number | null; + + @ApiPropertyOptional({ description: 'Lead name', nullable: true }) + @IsOptional() + @IsString() + leadName?: string | null; + + @ApiPropertyOptional({ description: 'Lead and Contact responsible user ID', nullable: true }) + @IsOptional() + @IsNumber() + ownerId?: number | null; + + @ApiPropertyOptional({ description: 'Do not create lead if active lead exists', nullable: true }) + @IsOptional() + @IsBoolean() + checkActiveLead?: boolean; + + @ApiPropertyOptional({ description: 'Do not create duplicate contact', nullable: true }) + @IsOptional() + @IsBoolean() + checkDuplicate?: boolean; +} diff --git a/backend/src/modules/multichat/chat/dto/create-external-chat.dto.ts b/backend/src/modules/multichat/chat/dto/create-external-chat.dto.ts new file mode 100644 index 0000000..df4a136 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/create-external-chat.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ChatUserExternalDto } from '../../chat-user'; + +export class CreateExternalChatDto { + @ApiProperty({ description: 'Provider ID' }) + @IsNumber() + providerId: number; + + @ApiProperty({ description: 'Chat title' }) + @IsString() + title: string; + + @ApiPropertyOptional({ description: 'User IDs of chat participants', type: [Number], nullable: true }) + @IsOptional() + @IsArray() + participantIds?: number[] | null; + + @ApiPropertyOptional({ description: 'Entity ID associated with the chat', nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional({ description: 'External ID of the chat', nullable: true }) + @IsOptional() + @IsString() + externalId?: string | null; + + @ApiPropertyOptional({ description: 'External user data', type: ChatUserExternalDto }) + @IsOptional() + externalUser?: ChatUserExternalDto; +} diff --git a/backend/src/modules/multichat/chat/dto/create-group-chat.dto.ts b/backend/src/modules/multichat/chat/dto/create-group-chat.dto.ts new file mode 100644 index 0000000..ecc8be2 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/create-group-chat.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class CreateGroupChatDto { + @ApiProperty({ description: 'Provider ID' }) + @IsNumber() + providerId: number; + + @ApiProperty({ description: 'Chat title' }) + @IsString() + title: string; + + @ApiProperty({ description: 'User IDs of chat participants', type: [Number] }) + @IsArray() + participantIds: number[]; + + @ApiPropertyOptional({ description: 'Entity ID associated with the chat', nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; +} diff --git a/backend/src/modules/multichat/chat/dto/create-personal-chat.dto.ts b/backend/src/modules/multichat/chat/dto/create-personal-chat.dto.ts new file mode 100644 index 0000000..60dcf0a --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/create-personal-chat.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; + +export class CreatePersonalChatDto { + @ApiProperty({ description: 'Provider ID' }) + @IsNumber() + providerId: number; + + @ApiProperty({ description: 'User ID of chat companion' }) + @IsNumber() + companionId: number; +} diff --git a/backend/src/modules/multichat/chat/dto/find-chats-full-result.dto.ts b/backend/src/modules/multichat/chat/dto/find-chats-full-result.dto.ts new file mode 100644 index 0000000..4df69d7 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/find-chats-full-result.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsObject } from 'class-validator'; + +import { PagingMeta } from '@/common'; +import { ChatDto } from './chat.dto'; + +export class FindChatsFullResultDto { + @ApiProperty({ description: 'List of chats', type: [ChatDto] }) + @IsArray() + chats: ChatDto[]; + + @ApiProperty({ description: 'Chat metadata', type: PagingMeta }) + @IsObject() + meta: PagingMeta; + + constructor(chats: ChatDto[], meta: PagingMeta) { + this.chats = chats; + this.meta = meta; + } +} diff --git a/backend/src/modules/multichat/chat/dto/index.ts b/backend/src/modules/multichat/chat/dto/index.ts new file mode 100644 index 0000000..1d0e5b9 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/index.ts @@ -0,0 +1,10 @@ +export * from './chat-find-by-message-content-filter.dto'; +export * from './chat-find-filter.dto'; +export * from './chat-find-personal-filter.dto'; +export * from './chat.dto'; +export * from './create-contact-lead.dto'; +export * from './create-external-chat.dto'; +export * from './create-group-chat.dto'; +export * from './create-personal-chat.dto'; +export * from './find-chats-full-result.dto'; +export * from './update-group-chat.dto'; diff --git a/backend/src/modules/multichat/chat/dto/update-group-chat.dto.ts b/backend/src/modules/multichat/chat/dto/update-group-chat.dto.ts new file mode 100644 index 0000000..d727333 --- /dev/null +++ b/backend/src/modules/multichat/chat/dto/update-group-chat.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class UpdateGroupChatDto { + @ApiPropertyOptional({ description: 'Chat title', nullable: true }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiPropertyOptional({ description: 'Entity ID associated with the chat', nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiProperty({ description: 'User IDs of chat participants', type: [Number] }) + @IsOptional() + @IsArray() + participantIds?: number[]; +} diff --git a/backend/src/modules/multichat/chat/entities/chat-pinned-message.entity.ts b/backend/src/modules/multichat/chat/entities/chat-pinned-message.entity.ts new file mode 100644 index 0000000..35574d7 --- /dev/null +++ b/backend/src/modules/multichat/chat/entities/chat-pinned-message.entity.ts @@ -0,0 +1,25 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +@Entity() +export class ChatPinnedMessage { + @PrimaryColumn() + chatId: number; + + @PrimaryColumn() + messageId: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor(chatId: number, messageId: number, accountId: number, createdAt?: Date) { + this.chatId = chatId; + this.messageId = messageId; + this.accountId = accountId; + this.createdAt = createdAt ?? DateUtil.now(); + } +} diff --git a/backend/src/modules/multichat/chat/entities/chat.entity.ts b/backend/src/modules/multichat/chat/entities/chat.entity.ts new file mode 100644 index 0000000..0be7224 --- /dev/null +++ b/backend/src/modules/multichat/chat/entities/chat.entity.ts @@ -0,0 +1,164 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { ChatType } from '../../common'; +import { ChatMessage } from '../../chat-message/entities/chat-message.entity'; +import { ChatUser } from '../../chat-user'; +import { CreatePersonalChatDto, CreateGroupChatDto, CreateExternalChatDto, ChatDto } from '../dto'; + +@Entity() +export class Chat { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + providerId: number; + + @Column({ nullable: true }) + createdBy: number | null; + + @Column({ nullable: true }) + externalId: string | null; + + @Column() + type: ChatType; + + @Column({ nullable: true }) + title: string | null; + + @Column({ nullable: true }) + entityId: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + @Column({ select: false, insert: false, update: false }) + totalMessageCount: number | null; + + @Column({ select: false, insert: false, update: false }) + seenByUserCount: number | null; + + @Column({ select: false, insert: false, update: false }) + updatedAt: Date; + + constructor( + accountId: number, + providerId: number, + createdBy: number | null, + externalId: string | null, + type: ChatType, + title: string | null, + entityId: number | null, + createdAt?: Date, + ) { + this.accountId = accountId; + this.providerId = providerId; + this.createdBy = createdBy; + this.externalId = externalId; + this.type = type; + this.title = title; + this.entityId = entityId; + this.createdAt = createdAt ?? DateUtil.now(); + this.updatedAt = this.createdAt; + } + + private _users: ChatUser[]; + public set users(value: ChatUser[]) { + this._users = value; + } + public get users(): ChatUser[] { + return this._users; + } + + private _pinnedMessages: ChatMessage[]; + public set pinnedMessages(value: ChatMessage[]) { + this._pinnedMessages = value; + } + public get pinnedMessages(): ChatMessage[] { + return this._pinnedMessages; + } + + private _lastMessage: ChatMessage | null; + public set lastMessage(value: ChatMessage | null) { + this._lastMessage = value; + } + public get lastMessage(): ChatMessage | null { + return this._lastMessage; + } + + private _entityInfo: EntityInfoDto | null; + public set entityInfo(value: EntityInfoDto | null) { + this._entityInfo = value; + } + public get entityInfo(): EntityInfoDto | null { + return this._entityInfo; + } + + private _hasAccess: boolean | null; + public get hasAccess(): boolean | null { + return this._hasAccess; + } + public set hasAccess(value: boolean | null) { + this._hasAccess = value; + } + + public static personalFromDto( + accountId: number, + createdBy: number, + dto: CreatePersonalChatDto, + externalId: string | null = null, + ): Chat { + return new Chat(accountId, dto.providerId, createdBy, externalId, ChatType.PERSONAL, null, null); + } + + public static groupFromDto( + accountId: number, + createdBy: number, + dto: CreateGroupChatDto, + externalId: string | null = null, + ): Chat { + return new Chat(accountId, dto.providerId, createdBy, externalId, ChatType.GROUP, dto.title, dto.entityId); + } + + public static externalFromDto(accountId: number, createdBy: number | null, dto: CreateExternalChatDto): Chat { + return new Chat( + accountId, + dto.providerId, + createdBy, + dto.externalId ?? null, + ChatType.GROUP, + dto.title, + dto.entityId, + ); + } + + public toDto(): ChatDto { + const users = this._users ? this._users.map((user) => user.toDto()) : []; + const pinnedMessages = this._pinnedMessages ? this._pinnedMessages.map((message) => message.toDto()) : []; + const lastMessage = this._lastMessage ? this._lastMessage.toDto() : null; + const unseenCount = this.totalMessageCount ? this.totalMessageCount - (this.seenByUserCount ?? 0) : 0; + return { + id: this.id, + providerId: this.providerId, + createdBy: this.createdBy, + externalId: this.externalId, + type: this.type, + title: this.title, + entityId: this.entityId, + createdAt: this.createdAt.toISOString(), + users, + pinnedMessages, + lastMessage, + unseenCount, + updatedAt: this.updatedAt?.toISOString(), + entityInfo: this.entityInfo, + hasAccess: this.hasAccess, + }; + } +} diff --git a/backend/src/modules/multichat/chat/entities/index.ts b/backend/src/modules/multichat/chat/entities/index.ts new file mode 100644 index 0000000..931fa57 --- /dev/null +++ b/backend/src/modules/multichat/chat/entities/index.ts @@ -0,0 +1,2 @@ +export * from './chat-pinned-message.entity'; +export * from './chat.entity'; diff --git a/backend/src/modules/multichat/chat/services/chat-pinned-message.service.ts b/backend/src/modules/multichat/chat/services/chat-pinned-message.service.ts new file mode 100644 index 0000000..13f4c52 --- /dev/null +++ b/backend/src/modules/multichat/chat/services/chat-pinned-message.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ChatPinnedMessage } from '../entities'; + +@Injectable() +export class ChatPinnedMessageService { + constructor( + @InjectRepository(ChatPinnedMessage) + private repository: Repository, + ) {} + + public async pinMessage(accountId: number, chatId: number, messageId: number): Promise { + return await this.repository.save(new ChatPinnedMessage(chatId, messageId, accountId)); + } + + public async unpinMessage(accountId: number, chatId: number, messageId: number): Promise { + await this.repository.delete({ chatId, messageId, accountId }); + } +} diff --git a/backend/src/modules/multichat/chat/services/chat.handler.ts b/backend/src/modules/multichat/chat/services/chat.handler.ts new file mode 100644 index 0000000..51d94f4 --- /dev/null +++ b/backend/src/modules/multichat/chat/services/chat.handler.ts @@ -0,0 +1,51 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { + ActionChatSendAmworkSettings, + AutomationJob, + EntityTypeActionType, + OnAutomationJob, +} from '@/modules/automation'; + +import { ChatService } from './chat.service'; + +interface EntityVariables { + id?: number | null; + stageId?: number | null; +} + +interface SendChatVariables { + accountId: number; + entity?: EntityVariables | null; + chatSettings?: ActionChatSendAmworkSettings | null; +} + +@Injectable() +export class ChatHandler { + private readonly logger = new Logger(ChatHandler.name); + constructor(private readonly service: ChatService) {} + + @OnAutomationJob(EntityTypeActionType.ChatSendAmwork) + async handleSendMessageJob(job: AutomationJob): Promise<{ variables?: unknown }> { + if (!job.variables?.accountId || !job.variables?.entity || !job.variables?.chatSettings) { + this.logger.warn(`Automation job variables are not valid`, job.variables); + return { variables: job.variables }; + } + + try { + const { accountId, entity, chatSettings } = job.variables; + + const chat = await this.service.processAutomation({ + accountId, + entityId: entity.id, + entityStageId: entity.stageId, + settings: chatSettings, + }); + + return { variables: { ...job.variables, chat: chat?.toDto() } }; + } catch (e) { + this.logger.error(`Automation job error`, (e as Error)?.stack); + return { variables: job.variables }; + } + } +} diff --git a/backend/src/modules/multichat/chat/services/chat.service.ts b/backend/src/modules/multichat/chat/services/chat.service.ts new file mode 100644 index 0000000..5a37c56 --- /dev/null +++ b/backend/src/modules/multichat/chat/services/chat.service.ts @@ -0,0 +1,925 @@ +import Handlebars from 'handlebars'; +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, type SelectQueryBuilder } from 'typeorm'; + +import { + BadRequestError, + ForbiddenError, + NotFoundError, + PagingMeta, + type PagingQuery, +} from '@/common'; +import { ChatPagingQuery } from '@/common/dto/paging/chat-paging-query.dto'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AccountService } from '@/modules/iam/account/account.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; +import { ActionChatSendAmworkSettings } from '@/modules/automation'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { DocumentGenerationService } from '@/modules/documents/document-generation/document-generation.service'; + +import { + ChatEvent, + ChatMessageStatus, + ChatProviderTransport, + ChatType, + ChatUserRole, + MultichatEventType, +} from '../../common'; +import { ChatUser, ChatUserExternalDto, ChatUserService } from '../../chat-user'; +import { ChatMessageUserStatus } from '../../chat-message/entities/chat-message-user-status.entity'; +import { ChatMessage } from '../../chat-message/entities/chat-message.entity'; +import { ChatMessageService } from '../../chat-message/services/chat-message.service'; +import { ChatProviderService } from '../../chat-provider/services/chat-provider.service'; +import { ChatProviderProxyService } from '../../providers/chat-provider-proxy.service'; + +import { + CreatePersonalChatDto, + CreateGroupChatDto, + CreateExternalChatDto, + UpdateGroupChatDto, + FindChatsFullResultDto, + ChatFindPersonalFilterDto, + ChatFindByMessageContentFilterDto, + CreateContactLeadDto, +} from '../dto'; +import { Chat, ChatPinnedMessage } from '../entities'; +import { ChatPinnedMessageService } from './chat-pinned-message.service'; + +const LAST_MESSAGE = 'select cm.id from chat_message cm where cm.chat_id = chat.id order by cm.created_at desc limit 1'; + +interface FindFilter { + chatId?: number; + type?: ChatType; + entityId?: number; + phoneNumber?: string; + externalId?: string; + providerId?: number | number[]; + transport?: ChatProviderTransport; + title?: string; +} + +interface ChatUpdateData { + externalId?: string; +} + +@Injectable() +export class ChatService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Chat) + private readonly repository: Repository, + private readonly accountService: AccountService, + private readonly userService: UserService, + @Inject(forwardRef(() => EntityService)) + private readonly entityService: EntityService, + private readonly entityInfoService: EntityInfoService, + private readonly chatUserService: ChatUserService, + private readonly chatPinnedMessageService: ChatPinnedMessageService, + @Inject(forwardRef(() => ChatMessageService)) + private readonly chatMessageService: ChatMessageService, + @Inject(forwardRef(() => ChatProviderService)) + private readonly chatProviderService: ChatProviderService, + private readonly providerProxyService: ChatProviderProxyService, + @Inject(forwardRef(() => DocumentGenerationService)) + private readonly documentGenerationService: DocumentGenerationService, + ) {} + + async createPersonalChat(accountId: number, user: User, dto: CreatePersonalChatDto): Promise { + const currentChat = await this.createFindQb(accountId, null, { + type: ChatType.PERSONAL, + providerId: dto.providerId, + }) + .innerJoin(ChatUser, 'cu1', 'cu1.chat_id = chat.id and cu1.user_id = :user1Id', { user1Id: user.id }) + .innerJoin(ChatUser, 'cu2', 'cu2.chat_id = chat.id and cu2.user_id = :user2Id', { user2Id: dto.companionId }) + .getOne(); + + if (currentChat) { + return this.getChatFull(accountId, user, currentChat.id); + } + + const chat = await this.repository.save(Chat.personalFromDto(accountId, user.id, dto)); + chat.users = await this.chatUserService.createForChat(accountId, chat.id, { + ownerIds: [chat.createdBy, dto.companionId], + }); + chat.hasAccess = true; + + this.notifyChatUsers(accountId, chat, MultichatEventType.ChatCreated, user.id); + + return chat; + } + + async createGroupChat(accountId: number, user: User | null, dto: CreateGroupChatDto): Promise { + if (dto.entityId) { + const currentChat = await this.findOne({ + accountId, + filter: { type: ChatType.GROUP, providerId: dto.providerId, entityId: dto.entityId }, + }); + + if (currentChat) { + const chat = await this.getChatFull(accountId, user, currentChat.id, false); + const addedUsers = await this.chatUserService.addUsers({ accountId, chatId: chat.id, userIds: [user.id] }); + if (addedUsers.length) { + if (chat.users) { + chat.users.push(...addedUsers); + } else { + chat.users = addedUsers; + } + } + chat.hasAccess = true; + return chat; + } + } + + const chat = await this.repository.save(Chat.groupFromDto(accountId, user.id, dto)); + + chat.users = await this.chatUserService.createForChat(accountId, chat.id, { + ownerIds: [user.id], + userIds: dto.participantIds, + }); + chat.hasAccess = true; + + this.notifyChatUsers(accountId, chat, MultichatEventType.ChatCreated, user.id); + + return chat; + } + + async createExternalChat(accountId: number, userId: number | null, dto: CreateExternalChatDto): Promise { + if (!dto.externalId) { + dto.externalId = await this.providerProxyService.createChatExternalId( + accountId, + dto.providerId, + dto.externalUser.externalId, + ); + } + + const currentChat = await this.findOne({ + accountId, + filter: { type: ChatType.GROUP, providerId: dto.providerId, externalId: dto.externalId }, + }); + + if (currentChat) { + const chat = await this.getChatFull(accountId, null, currentChat.id, false); + if (userId) { + const addedUsers = await this.chatUserService.addUsers({ accountId, chatId: chat.id, userIds: [userId] }); + if (addedUsers.length) { + if (chat.users) { + chat.users.push(...addedUsers); + } else { + chat.users = addedUsers; + } + } + } + chat.hasAccess = true; + return chat; + } else { + const chat = await this.repository.save(Chat.externalFromDto(accountId, userId, dto)); + const provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId: dto.providerId }, + { expand: ['responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + const participantIds = + dto.participantIds?.length > 0 ? dto.participantIds : provider.responsibleUsers.map((u) => u.userId); + const supervisorIds = provider.supervisorUsers.map((u) => u.userId); + dto.externalUser.phone = dto.externalUser.phone?.startsWith('+') + ? dto.externalUser.phone.slice(1) + : dto.externalUser.phone; + chat.users = await this.chatUserService.createForChat(accountId, chat.id, { + ownerIds: userId ? [userId] : participantIds, + userIds: participantIds, + supervisorIds: supervisorIds, + externalUsers: [dto.externalUser], + }); + chat.hasAccess = true; + + if (!dto.entityId && provider.entitySettings) { + const user = await this.userService.findOne({ + accountId, + id: provider.entitySettings.ownerId ?? userId ?? provider.createdBy, + }); + const entity = await this.createLinkedEntities({ + accountId, + user, + chatId: chat.id, + dto: { + contactTypeId: provider.entitySettings.contactEntityTypeId, + leadTypeId: provider.entitySettings.leadEntityTypeId, + leadBoardId: provider.entitySettings.leadBoardId, + leadStageId: provider.entitySettings.leadStageId, + leadName: provider.entitySettings.leadName, + ownerId: provider.entitySettings.ownerId, + checkActiveLead: provider.entitySettings.checkActiveLead, + checkDuplicate: provider.entitySettings.checkDuplicate, + }, + }); + if (entity) { + chat.entityId = entity.id; + } + } + + this.notifyChatUsers(accountId, chat, MultichatEventType.ChatCreated, userId); + + return chat; + } + } + + async updateExternalId(accountId: number, chatId: number, { externalId }: ChatUpdateData) { + const chat = await this.findOne({ accountId, filter: { chatId } }); + if (chat) { + if (externalId !== undefined) { + chat.externalId = externalId; + } + + await this.repository.save(chat); + } + } + + async mergeChat(account: Account, fromChatId: number, toChatId: number) { + const fromChat = await this.findOne({ accountId: account.id, filter: { chatId: fromChatId } }); + fromChat.users = await this.chatUserService.findMany(account.id, { chatId: fromChatId }); + const toChat = await this.findOne({ accountId: account.id, filter: { chatId: toChatId } }); + toChat.users = await this.chatUserService.findMany(account.id, { chatId: toChatId }); + + const fromChatInternalUsers = fromChat.users.filter((u) => u.userId); + const toChatInternalUsers = toChat.users.filter((u) => u.userId); + const addInternalUsers = fromChatInternalUsers.filter( + (fu) => !toChatInternalUsers.some((tu) => tu.userId === fu.userId), + ); + + const fromChatExternalUsers = fromChat.users.filter((u) => u.role === ChatUserRole.EXTERNAL); + const toChatExternalUsers = toChat.users.filter((u) => u.role === ChatUserRole.EXTERNAL); + const addExternalUsers = fromChatExternalUsers.filter( + (fu) => + !( + toChatExternalUsers.some((tu) => tu.externalUser.externalId === fu.externalUser.externalId) || + toChatExternalUsers.some((tu) => tu.externalUser.phone === fu.externalUser.phone) + ), + ); + + if (addInternalUsers.length > 0 || addExternalUsers.length > 0) { + const addUsers = await this.chatUserService.addUsers({ + accountId: account.id, + chatId: toChatId, + userIds: addInternalUsers.map((iu) => iu.userId), + externalUsers: addExternalUsers.map( + (eu) => + new ChatUserExternalDto( + eu.externalUser.externalId, + eu.externalUser.firstName, + eu.externalUser.lastName, + eu.externalUser.avatarUrl, + eu.externalUser.phone, + eu.externalUser.email, + eu.externalUser.link, + ), + ), + }); + toChat.users.push(...addUsers); + } + + //TODO; merge messages + const messages = await this.chatMessageService.findMany( + account.id, + { chatId: fromChatId }, + { expand: ['chatUser'] }, + ); + for (const message of messages) { + const toChatUser = message.chatUser.userId + ? toChat.users.find((u) => u.userId === message.chatUser.userId) + : toChat.users.find( + (u) => + u.externalUser?.externalId === message.chatUser.externalUser.externalId || + u.externalUser?.phone === message.chatUser.externalUser.phone, + ); + if (toChatUser.userId) { + const user = await this.userService.findOne({ accountId: account.id, id: toChatUser.userId }); + await this.chatMessageService.create(account, user, toChat.id, { text: message.text }, false); + } else if (toChatUser.externalUser) { + await this.chatMessageService.createExternal( + account, + toChatUser, + message.text, + message.externalId, + null, + false, + ); + } + } + await this.delete(account.id, null, fromChatId); + } + + async updateGroupChat(accountId: number, user: User, chatId: number, dto: UpdateGroupChatDto): Promise { + const chat = await this.getChatFull(accountId, user, chatId); + + if (chat.type !== ChatType.GROUP) { + throw NotFoundError.withId(Chat, chatId); + } + + if (dto.participantIds) { + chat.users = await this.chatUserService.updateForGroupChat( + accountId, + chat.id, + chat.createdBy, + chat.users, + dto.participantIds, + ); + } + + if (dto.entityId) { + chat.entityId = dto.entityId; + } + if (dto.title) { + chat.title = dto.title; + } + await this.repository.save(chat); + + this.notifyChatUsers(accountId, chat, MultichatEventType.ChatUpdated, user.id); + + return chat; + } + + async pinMessage(accountId: number, user: User, chatId: number, messageId: number): Promise { + const chat = await this.getChatFull(accountId, user, chatId); + + const message = await this.chatMessageService.getMessageSimple(accountId, chatId, messageId); + await this.chatPinnedMessageService.pinMessage(accountId, chat.id, message.id); + chat.pinnedMessages = [message, ...chat.pinnedMessages.filter((msg) => msg.id !== message.id)]; + + this.notifyChatUsers(accountId, chat, MultichatEventType.ChatUpdated, user.id); + + return chat; + } + + async unpinMessage(accountId: number, user: User, chatId: number, messageId: number): Promise { + const chat = await this.getChatFull(accountId, user, chatId); + + const message = await this.chatMessageService.getMessageSimple(accountId, chatId, messageId); + await this.chatPinnedMessageService.unpinMessage(accountId, chat.id, message.id); + chat.pinnedMessages = chat.pinnedMessages.filter((msg) => msg.id !== message.id); + + this.notifyChatUsers(accountId, chat, MultichatEventType.ChatUpdated, user.id); + + return chat; + } + + async updateMessagesStatus({ + accountId, + user, + chatId, + status, + }: { + accountId: number; + user: User; + chatId?: number; + status: ChatMessageStatus; + }) { + await this.chatMessageService.updateStatusDirect({ accountId, user, chatId, status }); + } + + async delete(accountId: number, userId: number | null, chatId: number): Promise { + const chat = await this.findOne({ accountId, filter: { chatId } }); + + if (chat) { + const chatUser = userId ? await this.chatUserService.findOne(accountId, { chatId, userId }) : null; + + if (userId && (!chatUser || chatUser.role === ChatUserRole.USER)) { + throw new ForbiddenError(`User ${userId} can not delete chat ${chatId}`); + } + + await this.notifyChatUsers(accountId, chat, MultichatEventType.ChatDeleted, userId); + await this.repository.delete(chatId); + + return chatId; + } + + return null; + } + + async findOne({ + accountId, + filter, + accessUserId, + }: { + accountId: number; + filter: FindFilter; + accessUserId?: number; + }): Promise { + const chat = await this.createFindQb(accountId, null, filter).getOne(); + if (accessUserId) { + chat.hasAccess = (await this.chatUserService.count(accountId, { chatId: chat.id, userId: accessUserId })) > 0; + } + + return chat; + } + + async findMany({ + accountId, + filter, + paging, + accessUserId, + }: { + accountId: number; + filter: FindFilter; + paging?: PagingQuery; + accessUserId?: number; + }): Promise { + const chats = await this.createFindQb(accountId, null, filter).offset(paging?.skip).limit(paging?.take).getMany(); + if (accessUserId) { + for (const chat of chats) { + chat.hasAccess = (await this.chatUserService.count(accountId, { chatId: chat.id, userId: accessUserId })) > 0; + } + } + return chats; + } + + async findManyFull( + accountId: number, + user: User, + filter: FindFilter, + paging?: PagingQuery, + ): Promise { + const qb = this.createFindQb(accountId, user.id, filter, true); + + const total = await qb.clone().getCount(); + const offset = Math.min(paging.take + paging.skip, total); + + const chats = await qb.offset(paging?.skip).limit(paging?.take).getMany(); + + for (const chat of chats) { + chat.users = await this.chatUserService.findMany(accountId, { chatId: chat.id }); + chat.hasAccess = chat.users.some((u) => u.userId === user.id); + + if (chat.lastMessage) { + chat.lastMessage = await this.chatMessageService.getLastMessageInfo(accountId, chat.id, chat.lastMessage.id); + } + + if (chat.entityId) { + chat.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: chat.entityId }); + } + } + + return new FindChatsFullResultDto( + chats.map((chat) => chat.toDto()), + new PagingMeta(offset, total), + ); + } + + async findManyFullPersonal( + accountId: number, + user: User, + filter: ChatFindPersonalFilterDto, + paging?: PagingQuery, + ): Promise { + const qb = this.createFindQb(accountId, user.id, { ...filter, type: ChatType.PERSONAL }, true); + + qb.innerJoin('chat_user', 'cu', 'cu.chat_id = chat.id') + .innerJoin('users', 'u', 'u.id = cu.user_id') + .andWhere(`LOWER(u.first_name || ' ' || u.last_name) ilike :fullName`, { + fullName: `%${filter.fullName.trim()}%`, + }) + .andWhere('chat.created_by != u.id'); + + const total = await qb.clone().getCount(); + const offset = Math.min(paging.take + paging.skip, total); + + const chats = await qb.offset(paging?.skip).limit(paging?.take).getMany(); + + if (filter.fullName) { + const personalChats: Chat[] = []; + + for (const chat of chats) { + chat.users = await this.chatUserService.findMany(accountId, { chatId: chat.id }); + chat.hasAccess = chat.users.some((u) => u.userId === user.id); + + if (chat.lastMessage) { + chat.lastMessage = await this.chatMessageService.getLastMessageInfo(accountId, chat.id, chat.lastMessage.id); + } + + if (chat.entityId) { + chat.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: chat.entityId }); + } + + personalChats.push(chat); + } + + return new FindChatsFullResultDto( + personalChats.map((chat) => chat.toDto()), + new PagingMeta(offset, total), + ); + } else { + return new FindChatsFullResultDto([], new PagingMeta(0, 0)); + } + } + + async findManyFullByMessageContent( + accountId: number, + user: User, + filter: ChatFindByMessageContentFilterDto, + paging?: PagingQuery, + ): Promise { + const qb = this.createFindByMessageContentQb(accountId, user.id, filter.messageContent, filter.providerId); + + const total = await qb.getCount(); + const offset = Math.min(paging.take + paging.skip, total); + + const chats = await qb.offset(paging?.skip).limit(paging?.take).getMany(); + + for (const chat of chats) { + chat.users = await this.chatUserService.findMany(accountId, { chatId: chat.id }); + chat.hasAccess = chat.users.some((u) => u.userId === user.id); + + if (chat.entityId) { + chat.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: chat.entityId }); + } + } + + return new FindChatsFullResultDto( + chats.map((chat) => chat.toDto()), + new PagingMeta(offset, total), + ); + } + + async count(accountId: number, filter: FindFilter): Promise { + return this.createFindQb(accountId, null, filter).getCount(); + } + + async getChatFull(accountId: number, user: User | null, chatId: number, hasUser = true): Promise { + const qb = this.createFindQb(accountId, hasUser ? (user?.id ?? null) : null, { chatId }, true); + const chat = await qb + .leftJoin(ChatPinnedMessage, 'cpm', 'cpm.chat_id = chat.id') + .leftJoinAndMapMany('chat.pinnedMessages', ChatMessage, 'pinned_msg', 'pinned_msg.id = cpm.message_id') + .getOne(); + + if (!chat) { + throw NotFoundError.withId(Chat, chatId); + } + + chat.users = await this.chatUserService.findMany(accountId, { chatId }); + chat.hasAccess = user && hasUser ? chat.users.some((u) => u.userId === user.id) : null; + + if (chat.lastMessage) { + chat.lastMessage = await this.chatMessageService.getLastMessageInfo(accountId, chat.id, chat.lastMessage.id); + } + + if (user && chat.entityId) { + chat.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: chat.entityId }); + } + + return chat; + } + + async getChats( + accountId: number, + user: User, + providerId: number | null | undefined, + paging: ChatPagingQuery, + ): Promise { + const providers = await this.chatProviderService.findMany(accountId, user.id, { + providerId: providerId ?? undefined, + }); + if (providers.length === 0) { + return []; + } + + const qb = this.createFindQb(accountId, user.id, { providerId: providers.map((p) => p.id) }, true); + + // Check if cursor-based pagination is requested + if (paging.cursor) { + // Cursor-based pagination logic + const cursorChat = await this.findOne({ accountId, filter: { chatId: paging.cursor } }); + const lastMessageCreatedAt = cursorChat + ? await this.chatMessageService.getLastMessageCreatedAt(accountId, cursorChat.id) + : null; + const from = lastMessageCreatedAt ?? cursorChat?.createdAt; + + if (from) { + qb.andWhere('COALESCE(last_msg.created_at, chat.created_at) < :from', { from }); + } + + const chats = await qb.orderBy('chat_updated_at', 'DESC').addOrderBy('chat.id', 'DESC').take(paging.take).getMany(); + + for (const chat of chats) { + chat.users = await this.chatUserService.findMany(accountId, { chatId: chat.id }); + if (chat.lastMessage) { + chat.lastMessage = await this.chatMessageService.getLastMessageInfo(accountId, chat.id, chat.lastMessage.id); + } + if (user && chat.entityId) { + chat.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: chat.entityId }); + } + chat.hasAccess = chat.users.some((u) => u.userId === user.id); + } + + return chats; + } else { + // Offset-based pagination logic (default) + const chats = await qb + .orderBy('chat_updated_at', 'DESC') + .addOrderBy('chat.id', 'DESC') + .offset(paging.skip) + .limit(paging.take) + .getMany(); + + for (const chat of chats) { + chat.users = await this.chatUserService.findMany(accountId, { chatId: chat.id }); + if (chat.lastMessage) { + chat.lastMessage = await this.chatMessageService.getLastMessageInfo(accountId, chat.id, chat.lastMessage.id); + } + if (user && chat.entityId) { + chat.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: chat.entityId }); + } + chat.hasAccess = chat.users.some((u) => u.userId === user.id); + } + + return chats; + } + } + + async getUnseenForUser(accountId: number, userId: number, providerId?: number): Promise { + const cacheKey = ({ + accountId, + userId, + providerId, + status, + }: { + accountId: number; + userId: number; + providerId?: number; + status?: ChatMessageStatus; + }) => `ChatMessage.count:${accountId}:${userId}${providerId ? `:${providerId}` : ''}${status ? `:${status}` : ''}`; + const messagesQb = this.repository + .createQueryBuilder('chat') + .select('count(msg.id)', 'messagesCount') + .leftJoin(ChatUser, 'user', 'user.chat_id = chat.id') + .leftJoin(ChatMessage, 'msg', 'msg.chat_id = chat.id') + .where('chat.account_id = :accountId', { accountId }) + .andWhere('user.user_id = :userId', { userId }); + if (providerId) { + messagesQb.andWhere('chat.provider_id = :providerId', { providerId }); + } + const { messagesCount } = await messagesQb.cache(cacheKey({ accountId, userId, providerId }), 15000).getRawOne(); + + const seenQb = this.repository + .createQueryBuilder('chat') + .select('count(msg.id)', 'seenCount') + .leftJoin(ChatMessage, 'msg', 'msg.chat_id = chat.id') + .leftJoin(ChatMessageUserStatus, 'cmus', 'cmus.message_id = msg.id') + .leftJoin(ChatUser, 'user', 'user.id = cmus.chat_user_id and user.chat_id = chat.id') + .where('chat.account_id = :accountId', { accountId }) + .andWhere('user.user_id = :userId', { userId }) + .andWhere('cmus.status = :status', { status: ChatMessageStatus.SEEN }); + if (providerId) { + seenQb.andWhere('chat.provider_id = :providerId', { providerId }); + } + const { seenCount } = await seenQb + .cache(cacheKey({ accountId, userId, providerId, status: ChatMessageStatus.SEEN }), 15000) + .getRawOne(); + + return messagesCount - seenCount; + } + + async getLastMessageId(accountId: number, chatId: number): Promise { + return this.chatMessageService.getLastMessageId(accountId, chatId); + } + + async createLinkedEntities({ + accountId, + user, + chatId, + dto, + }: { + accountId: number; + user: User; + chatId: number; + dto: CreateContactLeadDto; + }) { + if (!dto.leadTypeId && !dto.contactTypeId) { + throw new BadRequestError('No contact or lead type provided'); + } + + const chatUser = await this.chatUserService.findOne(accountId, { chatId, role: ChatUserRole.EXTERNAL }); + if (!chatUser?.externalUser) { + throw new BadRequestError(`No external user in chat ${chatId}`); + } + + const fieldValues = []; + if (chatUser.externalUser.phone) { + fieldValues.push({ fieldType: FieldType.Phone, value: chatUser.externalUser.phone }); + } + if (chatUser.externalUser.email) { + fieldValues.push({ fieldType: FieldType.Email, value: chatUser.externalUser.email }); + } + if (chatUser.externalUser.link) { + fieldValues.push({ fieldType: FieldType.Link, value: chatUser.externalUser.link }); + } + const lead = dto.leadTypeId + ? { + ownerId: dto.ownerId, + entityTypeId: dto.leadTypeId, + boardId: dto.leadBoardId, + stageId: dto.leadStageId, + name: dto.leadName ?? (!dto.contactTypeId ? chatUser.externalUser.fullName() : undefined), + fieldValues, + } + : null; + const contact = dto.contactTypeId + ? { + ownerId: dto.ownerId, + entityTypeId: dto.contactTypeId, + name: chatUser.externalUser.fullName(), + fieldValues, + linkedEntities: lead ? [lead] : undefined, + } + : null; + + if (contact || lead) { + const [entity] = await this.entityService.createSimple({ + accountId, + user, + dto: contact ?? lead, + options: { checkActiveLead: dto.checkActiveLead, checkDuplicate: dto.checkDuplicate }, + }); + this.updateGroupChat(accountId, user, chatId, { entityId: entity.id }); + + return this.entityInfoService.getEntityInfo({ user, entity, access: true }); + } + + return null; + } + + //TODO: move to ChatNotificationService + async notifyChatUsers(accountId: number, chat: Chat, event: MultichatEventType, userId?: number): Promise { + if (!chat.users) { + chat.users = await this.chatUserService.findMany(accountId, { chatId: chat.id }); + } + const usersToNotify = userId + ? chat.users.filter((chatUser) => chatUser.userId && chatUser.userId !== userId) + : chat.users.filter((chatUser) => chatUser.userId); + usersToNotify.forEach(async (chatUser) => { + this.eventEmitter.emit( + event, + new ChatEvent({ accountId, userId: chatUser.userId, providerId: chat.providerId, chatId: chat.id }), + ); + }); + } + + async processAutomation({ + accountId, + entityId, + entityStageId, + settings, + }: { + accountId: number; + entityId: number; + entityStageId: number | null | undefined; + settings: ActionChatSendAmworkSettings; + }): Promise { + const entity = await this.entityInfoService.findOne({ accountId, entityId }); + if (entity && (!entity.stageId || settings.allowAnyStage || entity.stageId === entityStageId)) { + let chat = await this.findOne({ accountId, filter: { entityId, transport: ChatProviderTransport.Amwork } }); + const ownerUser = await this.userService.findOne({ accountId, id: settings.userId ?? entity.ownerId }); + const participantIds = settings.sendTo ?? [ + (await this.userService.findOne({ accountId, id: entity.ownerId })).id, + ]; + if (chat) { + await this.chatUserService.addUsers({ accountId, chatId: chat.id, userIds: [ownerUser.id, ...participantIds] }); + } else { + const provider = await this.chatProviderService.findOne(accountId, ownerUser.id, { + transport: ChatProviderTransport.Amwork, + }); + chat = await this.createGroupChat(accountId, ownerUser, { + providerId: provider.id, + title: entity.name, + participantIds, + entityId: entity.id, + }); + } + + if (chat) { + const account = await this.accountService.findOne({ accountId }); + const data = await this.documentGenerationService.getDataForGeneration({ accountId, entityId: entity.id }); + data['contact_name'] = entity.name; + const text = Handlebars.compile(settings.message)(data); + + await this.chatMessageService.create(account, ownerUser, chat.id, { text }); + } + + return chat; + } + + return null; + } + + private createFindQb( + accountId: number, + userId: number | null, + filter: FindFilter, + full = false, + ): SelectQueryBuilder { + const qb = full + ? this.createFullQb(accountId, userId) + : this.repository.createQueryBuilder('chat').where('chat.account_id = :accountId', { accountId }); + + if (filter.chatId) { + qb.andWhere('chat.id = :chatId', { chatId: filter.chatId }); + } + + if (filter.type) { + qb.andWhere('chat.type = :type', { type: filter.type }); + } + + if (filter.entityId) { + qb.andWhere('chat.entity_id = :entityId', { entityId: filter.entityId }); + } + + if (filter.title) { + qb.andWhere('chat.title ilike :title', { title: `%${filter.title.trim()}%` }); + } + + if (filter.phoneNumber) { + qb.innerJoin('chat_user', 'cu', 'cu.chat_id = chat.id') + .innerJoin('chat_user_external', 'cue', 'cue.chat_user_id = cu.id') + .andWhere(`cue.phone ilike :phone`, { phone: `%${filter.phoneNumber.replace(/^\+/, '')}%` }); + } + + if (filter.externalId) { + qb.andWhere('chat.external_id = :externalId', { externalId: filter.externalId }); + } + + if (filter.providerId) { + if (Array.isArray(filter.providerId)) { + qb.andWhere('chat.provider_id IN (:...providerIds)', { providerIds: filter.providerId }); + } else { + qb.andWhere('chat.provider_id = :providerId', { providerId: filter.providerId }); + } + } + + if (filter.transport) { + qb.innerJoin('chat_provider', 'cp', 'cp.id = chat.provider_id').andWhere('cp.transport = :transport', { + transport: filter.transport, + }); + } + + return qb; + } + + private createFindByMessageContentQb( + accountId: number, + userId: number, + messageContent: string, + providerId?: number, + ): SelectQueryBuilder { + const qb = this.createFindQb(accountId, userId, { providerId }, true); + + // Remove existing join with alias 'last_msg' if it exists + qb.expressionMap.joinAttributes = qb.expressionMap.joinAttributes.filter((join) => join.alias.name !== 'last_msg'); + + // First message with text matching the content will be used as the lastMessage + qb.leftJoinAndMapOne( + 'chat.lastMessage', + ChatMessage, + 'last_msg', + `last_msg.chat_id = chat.id AND last_msg.text ilike :messageContent`, + { messageContent: `%${messageContent.trim()}%` }, + ).andWhere('last_msg.id is not null'); + + return qb; + } + + private createFullQb(accountId: number, userId: number | null) { + const qb = this.repository + .createQueryBuilder('chat') + .leftJoin(ChatUser, 'user', 'user.chat_id = chat.id') + .where('chat.account_id = :accountId', { accountId }) + .addSelect((subQuery) => { + return subQuery + .select('COUNT(status.*)', 'seen_count') + .from(ChatMessageUserStatus, 'status') + .where('status.chat_id = chat.id') + .andWhere('status.chat_user_id = user.id') + .andWhere('status.status = :status', { status: ChatMessageStatus.SEEN }); + }, 'chat_seen_by_user_count') + .addSelect((subQuery) => { + return subQuery.select('COUNT(msg.*)', 'message_count').from(ChatMessage, 'msg').where('msg.chat_id = chat.id'); + }, 'chat_total_message_count') + .leftJoinAndMapOne( + 'chat.lastMessage', + ChatMessage, + 'last_msg', + `last_msg.chat_id = chat.id AND last_msg.id = (${LAST_MESSAGE})`, + ) + .addSelect('COALESCE(last_msg.created_at, chat.created_at)', 'chat_updated_at'); + + if (userId) { + qb.andWhere('user.user_id = :userId', { userId }); + } + + return qb; + } +} diff --git a/backend/src/modules/multichat/chat/services/index.ts b/backend/src/modules/multichat/chat/services/index.ts new file mode 100644 index 0000000..16dd6a7 --- /dev/null +++ b/backend/src/modules/multichat/chat/services/index.ts @@ -0,0 +1,3 @@ +export * from './chat-pinned-message.service'; +export * from './chat.handler'; +export * from './chat.service'; diff --git a/backend/src/modules/multichat/common/enums/chat-message-status.enum.ts b/backend/src/modules/multichat/common/enums/chat-message-status.enum.ts new file mode 100644 index 0000000..9f66e07 --- /dev/null +++ b/backend/src/modules/multichat/common/enums/chat-message-status.enum.ts @@ -0,0 +1,4 @@ +export enum ChatMessageStatus { + RECEIVED = 'received', + SEEN = 'seen', +} diff --git a/backend/src/modules/multichat/common/enums/chat-provider-status.enum.ts b/backend/src/modules/multichat/common/enums/chat-provider-status.enum.ts new file mode 100644 index 0000000..0d904ae --- /dev/null +++ b/backend/src/modules/multichat/common/enums/chat-provider-status.enum.ts @@ -0,0 +1,6 @@ +export enum ChatProviderStatus { + Draft = 'draft', + Active = 'active', + Inactive = 'inactive', + Deleted = 'deleted', +} diff --git a/backend/src/modules/multichat/common/enums/chat-provider-transport.enum.ts b/backend/src/modules/multichat/common/enums/chat-provider-transport.enum.ts new file mode 100644 index 0000000..2b03f57 --- /dev/null +++ b/backend/src/modules/multichat/common/enums/chat-provider-transport.enum.ts @@ -0,0 +1,9 @@ +export enum ChatProviderTransport { + Amwork = 'amwork', + Whatsapp = 'whatsapp', + Messenger = 'messenger', + Telegram = 'telegram', + Instagram = 'instagram', + Vk = 'vk', + Avito = 'avito', +} diff --git a/backend/src/modules/multichat/common/enums/chat-provider-type.enum.ts b/backend/src/modules/multichat/common/enums/chat-provider-type.enum.ts new file mode 100644 index 0000000..04accf8 --- /dev/null +++ b/backend/src/modules/multichat/common/enums/chat-provider-type.enum.ts @@ -0,0 +1,6 @@ +export enum ChatProviderType { + Amwork = 'amwork', + Twilio = 'twilio', + Facebook = 'facebook', + Wazzup = 'wazzup', +} diff --git a/backend/src/modules/multichat/common/enums/chat-type.enum.ts b/backend/src/modules/multichat/common/enums/chat-type.enum.ts new file mode 100644 index 0000000..3002a22 --- /dev/null +++ b/backend/src/modules/multichat/common/enums/chat-type.enum.ts @@ -0,0 +1,4 @@ +export enum ChatType { + PERSONAL = 'personal', + GROUP = 'group', +} diff --git a/backend/src/modules/multichat/common/enums/chat-user-role.enum.ts b/backend/src/modules/multichat/common/enums/chat-user-role.enum.ts new file mode 100644 index 0000000..93afb7f --- /dev/null +++ b/backend/src/modules/multichat/common/enums/chat-user-role.enum.ts @@ -0,0 +1,7 @@ +export enum ChatUserRole { + OWNER = 'owner', + ADMIN = 'admin', + SUPERVISOR = 'supervisor', + USER = 'user', + EXTERNAL = 'external', +} diff --git a/backend/src/modules/multichat/common/enums/index.ts b/backend/src/modules/multichat/common/enums/index.ts new file mode 100644 index 0000000..133e579 --- /dev/null +++ b/backend/src/modules/multichat/common/enums/index.ts @@ -0,0 +1,6 @@ +export * from './chat-message-status.enum'; +export * from './chat-provider-status.enum'; +export * from './chat-provider-transport.enum'; +export * from './chat-provider-type.enum'; +export * from './chat-type.enum'; +export * from './chat-user-role.enum'; diff --git a/backend/src/modules/multichat/common/events/chat-message/chat-message-created.event.ts b/backend/src/modules/multichat/common/events/chat-message/chat-message-created.event.ts new file mode 100644 index 0000000..005c478 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat-message/chat-message-created.event.ts @@ -0,0 +1,27 @@ +import { ChatMessageEvent } from './chat-message.event'; + +export class ChatMessageCreatedEvent extends ChatMessageEvent { + fromUser: string; + text: string; + createdAt: string; + entityId: number | null; + + constructor({ + accountId, + userId, + providerId, + chatId, + fromUser, + messageId, + text, + createdAt, + entityId, + }: ChatMessageCreatedEvent) { + super({ accountId, userId, providerId, chatId, messageId }); + + this.fromUser = fromUser; + this.text = text; + this.createdAt = createdAt; + this.entityId = entityId; + } +} diff --git a/backend/src/modules/multichat/common/events/chat-message/chat-message-updated.event.ts b/backend/src/modules/multichat/common/events/chat-message/chat-message-updated.event.ts new file mode 100644 index 0000000..aab0798 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat-message/chat-message-updated.event.ts @@ -0,0 +1,11 @@ +import { ChatMessageEvent } from './chat-message.event'; + +export class ChatMessageUpdatedEvent extends ChatMessageEvent { + isLastMessage: boolean; + + constructor({ accountId, userId, providerId, chatId, messageId, isLastMessage }: ChatMessageUpdatedEvent) { + super({ accountId, userId, providerId, chatId, messageId }); + + this.isLastMessage = isLastMessage; + } +} diff --git a/backend/src/modules/multichat/common/events/chat-message/chat-message.event.ts b/backend/src/modules/multichat/common/events/chat-message/chat-message.event.ts new file mode 100644 index 0000000..8a92344 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat-message/chat-message.event.ts @@ -0,0 +1,11 @@ +import { ChatEvent } from '../chat/chat.event'; + +export class ChatMessageEvent extends ChatEvent { + messageId: number; + + constructor({ accountId, userId, providerId, chatId, messageId }: ChatMessageEvent) { + super({ accountId, userId, providerId, chatId }); + + this.messageId = messageId; + } +} diff --git a/backend/src/modules/multichat/common/events/chat-message/index.ts b/backend/src/modules/multichat/common/events/chat-message/index.ts new file mode 100644 index 0000000..4a225c5 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat-message/index.ts @@ -0,0 +1,3 @@ +export * from './chat-message-created.event'; +export * from './chat-message-updated.event'; +export * from './chat-message.event'; diff --git a/backend/src/modules/multichat/common/events/chat-provider/chat-provider.event.ts b/backend/src/modules/multichat/common/events/chat-provider/chat-provider.event.ts new file mode 100644 index 0000000..5babb09 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat-provider/chat-provider.event.ts @@ -0,0 +1,15 @@ +import { ChatProviderStatus } from '../../enums'; + +export class ChatProviderEvent { + accountId: number; + userId: number; + providerId: number; + status?: ChatProviderStatus; + + constructor({ accountId, userId, providerId, status }: ChatProviderEvent) { + this.accountId = accountId; + this.userId = userId; + this.providerId = providerId; + this.status = status; + } +} diff --git a/backend/src/modules/multichat/common/events/chat-provider/index.ts b/backend/src/modules/multichat/common/events/chat-provider/index.ts new file mode 100644 index 0000000..48711a4 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat-provider/index.ts @@ -0,0 +1 @@ +export * from './chat-provider.event'; diff --git a/backend/src/modules/multichat/common/events/chat/chat.event.ts b/backend/src/modules/multichat/common/events/chat/chat.event.ts new file mode 100644 index 0000000..7eda179 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat/chat.event.ts @@ -0,0 +1,13 @@ +export class ChatEvent { + accountId: number; + userId: number; + providerId: number; + chatId: number; + + constructor({ accountId, userId, providerId, chatId }: ChatEvent) { + this.accountId = accountId; + this.userId = userId; + this.providerId = providerId; + this.chatId = chatId; + } +} diff --git a/backend/src/modules/multichat/common/events/chat/index.ts b/backend/src/modules/multichat/common/events/chat/index.ts new file mode 100644 index 0000000..03f1a40 --- /dev/null +++ b/backend/src/modules/multichat/common/events/chat/index.ts @@ -0,0 +1 @@ +export * from './chat.event'; diff --git a/backend/src/modules/multichat/common/events/index.ts b/backend/src/modules/multichat/common/events/index.ts new file mode 100644 index 0000000..6e3f340 --- /dev/null +++ b/backend/src/modules/multichat/common/events/index.ts @@ -0,0 +1,4 @@ +export * from './chat'; +export * from './chat-message'; +export * from './chat-provider'; +export * from './multichat-event-type.enum'; diff --git a/backend/src/modules/multichat/common/events/multichat-event-type.enum.ts b/backend/src/modules/multichat/common/events/multichat-event-type.enum.ts new file mode 100644 index 0000000..bbaa1b0 --- /dev/null +++ b/backend/src/modules/multichat/common/events/multichat-event-type.enum.ts @@ -0,0 +1,11 @@ +export enum MultichatEventType { + ChatCreated = 'chat:created', + ChatUpdated = 'chat:updated', + ChatDeleted = 'chat:deleted', + ChatMessageCreated = 'chat:message:created', + ChatMessageUpdated = 'chat:message:updated', + ChatMessageDeleted = 'chat:message:deleted', + ChatProviderCreated = 'chat:provider:created', + ChatProviderUpdated = 'chat:provider:updated', + ChatProviderDeleted = 'chat:provider:deleted', +} diff --git a/backend/src/modules/multichat/common/index.ts b/backend/src/modules/multichat/common/index.ts new file mode 100644 index 0000000..df1eda9 --- /dev/null +++ b/backend/src/modules/multichat/common/index.ts @@ -0,0 +1,2 @@ +export * from './enums'; +export * from './events'; diff --git a/backend/src/modules/multichat/multichat.controller.ts b/backend/src/modules/multichat/multichat.controller.ts new file mode 100644 index 0000000..503283f --- /dev/null +++ b/backend/src/modules/multichat/multichat.controller.ts @@ -0,0 +1,33 @@ +import { Controller, Get, Param, ParseEnumPipe, Put } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { ChatMessageStatus } from './common'; +import { ChatService } from './chat/services/chat.service'; + +@ApiTags('multichat') +@Controller('chat') +@JwtAuthorized() +export class MultichatController { + constructor(private readonly service: ChatService) {} + + @ApiOperation({ summary: 'Get unseen messages count', description: 'Get unseen messages count for current user' }) + @ApiOkResponse({ description: `Unseen messages count`, type: Number }) + @Get('unseen-count') + async getUnseenCount(@CurrentAuth() { accountId, userId }: AuthData): Promise { + return await this.service.getUnseenForUser(accountId, userId); + } + + @ApiOperation({ summary: 'Update all chats messages status', description: 'Update all chats messages status' }) + @ApiParam({ name: 'status', description: 'Message status' }) + @ApiOkResponse() + @Put('status/:status') + @AuthDataPrefetch({ user: true }) + async updateMessagesStatus( + @CurrentAuth() { accountId, user }: AuthData, + @Param('status', new ParseEnumPipe(ChatMessageStatus)) status: ChatMessageStatus, + ) { + await this.service.updateMessagesStatus({ accountId, user, status }); + } +} diff --git a/backend/src/modules/multichat/multichat.module.ts b/backend/src/modules/multichat/multichat.module.ts new file mode 100644 index 0000000..25d3ac2 --- /dev/null +++ b/backend/src/modules/multichat/multichat.module.ts @@ -0,0 +1,27 @@ +import { Module } from '@nestjs/common'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { ChatModule } from './chat/chat.module'; +import { ChatMessageModule } from './chat-message/chat-message.module'; +import { ChatProviderModule } from './chat-provider/chat-provider.module'; +import { ProvidersModule } from './providers/providers.module'; +import { ChatUserModule } from './chat-user/chat-user.module'; +import { ChatMessageScheduledModule } from './chat-message-scheduled/chat-message-scheduled.module'; + +import { MultichatController } from './multichat.controller'; + +@Module({ + imports: [ + IAMModule, + ChatModule, + ChatUserModule, + ChatMessageModule, + ChatProviderModule, + ProvidersModule, + ChatMessageScheduledModule, + ], + controllers: [MultichatController], + exports: [ChatModule], +}) +export class MultichatModule {} diff --git a/backend/src/modules/multichat/providers/chat-provider-proxy.service.ts b/backend/src/modules/multichat/providers/chat-provider-proxy.service.ts new file mode 100644 index 0000000..0d5dfa1 --- /dev/null +++ b/backend/src/modules/multichat/providers/chat-provider-proxy.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@nestjs/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; + +import { ChatProviderType, MultichatEventType } from '../common'; +import { ChatUser } from '../chat-user'; +import { ChatProvider } from '../chat-provider/entities'; +import { ChatProviderService } from '../chat-provider/services/chat-provider.service'; +import { Chat } from '../chat/entities/chat.entity'; +import { ChatMessage } from '../chat-message/entities/chat-message.entity'; + +import { MessengerProviderService } from './facebook/messenger-provider.service'; +import { TwilioProviderService } from './twilio/twilio-provider.service'; +import { WazzupProviderService } from './wazzup/wazzup-provider.service'; + +@Injectable() +export class ChatProviderProxyService { + constructor( + private readonly chatProviderService: ChatProviderService, + private readonly twilioService: TwilioProviderService, + private readonly messengerService: MessengerProviderService, + private readonly wazzupService: WazzupProviderService, + ) {} + + async createChatExternalId(accountId: number, providerId: number, userExternalId: string): Promise { + const provider = await this.chatProviderService.findOne(accountId, null, { providerId }); + switch (provider?.type) { + case ChatProviderType.Amwork: + return null; + case ChatProviderType.Facebook: + return this.messengerService.createChatExternalId(userExternalId); + case ChatProviderType.Twilio: + return this.twilioService.createChatExternalId(userExternalId); + case ChatProviderType.Wazzup: + return this.wazzupService.createChatExternalId(accountId, providerId, userExternalId); + } + } + + async notifyChatUsers( + account: Account, + provider: ChatProvider, + chat: Chat, + type: MultichatEventType, + message: ChatMessage, + users: ChatUser[], + ): Promise { + switch (provider.type) { + case ChatProviderType.Facebook: + await this.messengerService.notifyChatUsers(account, message, type, provider, users); + break; + case ChatProviderType.Twilio: + await this.twilioService.notifyChatUsers(account, message, type, provider, users); + break; + case ChatProviderType.Wazzup: + await this.wazzupService.notifyChatUsers(account, provider, chat, type, message, users); + break; + } + } + + async sendDirectMessage({ + accountId, + provider, + phone, + message, + }: { + accountId: number; + provider: ChatProvider; + phone: string; + message: string; + }): Promise { + switch (provider?.type) { + case ChatProviderType.Twilio: + return this.twilioService.sendDirectMessage({ accountId, providerId: provider.id, phone, message }); + case ChatProviderType.Wazzup: + return this.wazzupService.sendDirectMessage({ accountId, providerId: provider.id, phone, message }); + } + } +} diff --git a/backend/src/modules/multichat/providers/facebook/config/facebook.config.ts b/backend/src/modules/multichat/providers/facebook/config/facebook.config.ts new file mode 100644 index 0000000..47b2de6 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/config/facebook.config.ts @@ -0,0 +1,20 @@ +import { registerAs } from '@nestjs/config'; + +export interface FacebookConfig { + appId: string; + appSecret: string; + appAccessToken: string; + messengerAuthConfigId: string; + messengerValidationToken: string; +} + +export default registerAs( + 'facebook', + (): FacebookConfig => ({ + appId: process.env.FB_APP_ID, + appSecret: process.env.FB_APP_SECRET, + appAccessToken: process.env.FB_APP_ACCESS_TOKEN, + messengerAuthConfigId: process.env.FB_MESSENGER_AUTH_CONFIG_ID, + messengerValidationToken: process.env.FB_MESSENGER_VALIDATION_TOKEN, + }), +); diff --git a/backend/src/modules/multichat/providers/facebook/controllers/index.ts b/backend/src/modules/multichat/providers/facebook/controllers/index.ts new file mode 100644 index 0000000..564fda8 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/controllers/index.ts @@ -0,0 +1,2 @@ +export * from './messenger-provider.controller'; +export * from './public-messenger-provider.controller'; diff --git a/backend/src/modules/multichat/providers/facebook/controllers/messenger-provider.controller.ts b/backend/src/modules/multichat/providers/facebook/controllers/messenger-provider.controller.ts new file mode 100644 index 0000000..c2d632f --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/controllers/messenger-provider.controller.ts @@ -0,0 +1,95 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { AuthDataPrefetch } from '@/modules/iam/common/decorators/auth-data-prefetch.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { MessengerProviderDto, CreateMessengerProviderDto, UpdateMessengerProviderDto } from '../dto'; +import { MessengerProviderService } from '../messenger-provider.service'; + +@ApiTags('multichat/settings/messenger') +@Controller('chat/settings/providers/messenger') +@JwtAuthorized() +@TransformToDto() +export class MessengerProviderController { + constructor(private readonly service: MessengerProviderService) {} + + @ApiOperation({ summary: 'Get Facebook Auth redirect url', description: 'Get Facebook Auth redirect url' }) + @ApiQuery({ name: 'display', type: String, required: false, description: 'Facebook OAuth screen display type' }) + @ApiOkResponse({ description: 'Facebook Auth redirect url', type: String }) + @Get('auth/connect') + async connect( + @CurrentAuth() { accountId, userId }: AuthData, + @Query('display') display: string | undefined, + ): Promise { + return this.service.getConnectUrl(accountId, userId, display); + } + + @ApiOperation({ + summary: 'Create Facebook Messenger chat provider', + description: 'Create Facebook Messenger chat provider', + }) + @ApiBody({ description: 'Data for creating Facebook Messenger chat provider', type: CreateMessengerProviderDto }) + @ApiCreatedResponse({ description: 'Facebook Messenger chat provider', type: MessengerProviderDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateMessengerProviderDto) { + return this.service.create(accountId, userId, dto); + } + + @ApiOperation({ + summary: 'Get Facebook Messenger chat providers with settings', + description: 'Get Facebook Messenger chat providers with settings', + }) + @ApiOkResponse({ description: 'Facebook Messenger chat providers', type: [MessengerProviderDto] }) + @AuthDataPrefetch({ user: true }) + @Get() + async getProviders(@CurrentAuth() { accountId, user }: AuthData) { + return this.service.getProvidersWithSettings(accountId, user); + } + + @ApiOperation({ + summary: 'Get Facebook Messenger chat provider with settings', + description: 'Get Facebook Messenger chat provider with settings', + }) + @ApiParam({ name: 'providerId', type: Number, required: true, description: 'Facebook Messenger chat provider ID' }) + @ApiOkResponse({ description: 'Facebook Messenger chat provider', type: MessengerProviderDto }) + @Get(':providerId') + async getProvider(@CurrentAuth() { accountId }: AuthData, @Param('providerId', ParseIntPipe) providerId: number) { + return this.service.getProviderWithSettings(accountId, providerId); + } + + @ApiOperation({ + summary: 'Update Facebook Messenger chat provider', + description: 'Update Facebook Messenger chat provider', + }) + @ApiParam({ name: 'providerId', type: Number, required: true, description: 'Facebook Messenger chat provider ID' }) + @ApiBody({ description: 'Data for updating Facebook Messenger chat provider', type: UpdateMessengerProviderDto }) + @ApiOkResponse({ description: 'Facebook Messenger chat provider', type: MessengerProviderDto }) + @Put(':providerId') + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + @Body() dto: UpdateMessengerProviderDto, + ) { + return this.service.update(accountId, providerId, dto); + } + + @ApiOperation({ + summary: 'Delete Facebook Messenger chat provider', + description: 'Delete Facebook Messenger chat provider', + }) + @ApiParam({ name: 'providerId', type: Number, required: true, description: 'Facebook Messenger chat provider ID' }) + @ApiOkResponse({ description: 'Success', type: Boolean }) + @Delete(':providerId') + async delete( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + ): Promise { + await this.service.delete({ accountId, userId, providerId }); + + return true; + } +} diff --git a/backend/src/modules/multichat/providers/facebook/controllers/public-messenger-provider.controller.ts b/backend/src/modules/multichat/providers/facebook/controllers/public-messenger-provider.controller.ts new file mode 100644 index 0000000..ec02d08 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/controllers/public-messenger-provider.controller.ts @@ -0,0 +1,45 @@ +import { Body, Controller, Get, Post, Query, Redirect } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { BadRequestError } from '@/common'; +import { MessengerProviderService } from '../messenger-provider.service'; + +@ApiExcludeController(true) +@Controller('chat/messenger') +export class PublicMessengerProviderController { + constructor(private readonly service: MessengerProviderService) {} + + @Redirect() + @Get('callback') + async callback(@Query('code') code: string, @Query('state') state: string, @Query('error') error?: string) { + const redirectUrl = await this.service.authCallback(code, state, error); + + return { url: redirectUrl, statusCode: 302 }; + } + + @Get('webhook') + async verifyWebhook( + @Query('hub.verify_token') verifyToken: string, + @Query('hub.challenge') challenge: string, + ): Promise { + return this.service.verifyWebhook(verifyToken, challenge); + } + + @Post('webhook') + async handleMessage(@Body() body: unknown): Promise { + await this.service.handleWebhook(body); + } + + @Post('deauthorise') + async deauthoriseUser(@Body('signed_request') signedRequest: string): Promise { + const result = await this.service.handleDeauthoriseRequest(signedRequest); + if (!result) { + throw new BadRequestError('Invalid signed request'); + } + } + + @Post('delete') + async deleteUser(@Body('signed_request') signedRequest: string) { + return this.service.handleDeleteAuthRequest(signedRequest); + } +} diff --git a/backend/src/modules/multichat/providers/facebook/dto/create-messenger-provider.dto.ts b/backend/src/modules/multichat/providers/facebook/dto/create-messenger-provider.dto.ts new file mode 100644 index 0000000..d1d8121 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/dto/create-messenger-provider.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { CreateChatProviderDto } from '../../../chat-provider/dto'; + +export class CreateMessengerProviderDto extends CreateChatProviderDto { + @ApiProperty({ description: 'User ID' }) + @IsString() + userId: string; + + @ApiProperty({ description: 'User access token' }) + @IsString() + userAccessToken: string; + + @ApiProperty({ description: 'Page ID' }) + @IsString() + pageId: string; + + @ApiProperty({ description: 'Page access token' }) + @IsString() + pageAccessToken: string; +} diff --git a/backend/src/modules/multichat/providers/facebook/dto/index.ts b/backend/src/modules/multichat/providers/facebook/dto/index.ts new file mode 100644 index 0000000..87344b4 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-messenger-provider.dto'; +export * from './messenger-provider.dto'; +export * from './update-messenger-provider.dto'; diff --git a/backend/src/modules/multichat/providers/facebook/dto/messenger-provider.dto.ts b/backend/src/modules/multichat/providers/facebook/dto/messenger-provider.dto.ts new file mode 100644 index 0000000..412c377 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/dto/messenger-provider.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { ChatProviderDto } from '../../../chat-provider/dto'; + +export class MessengerProviderDto extends ChatProviderDto { + @ApiProperty({ description: 'Page id' }) + @IsString() + pageId: string; + + @ApiProperty({ description: 'Page access token' }) + @IsString() + pageAccessToken: string; +} diff --git a/backend/src/modules/multichat/providers/facebook/dto/update-messenger-provider.dto.ts b/backend/src/modules/multichat/providers/facebook/dto/update-messenger-provider.dto.ts new file mode 100644 index 0000000..7db7180 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/dto/update-messenger-provider.dto.ts @@ -0,0 +1,3 @@ +import { UpdateChatProviderDto } from '../../../chat-provider/dto'; + +export class UpdateMessengerProviderDto extends UpdateChatProviderDto {} diff --git a/backend/src/modules/multichat/providers/facebook/entities/chat-provider-messenger.entity.ts b/backend/src/modules/multichat/providers/facebook/entities/chat-provider-messenger.entity.ts new file mode 100644 index 0000000..2e617a9 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/entities/chat-provider-messenger.entity.ts @@ -0,0 +1,65 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ChatProvider } from '../../../chat-provider/entities'; +import { MessengerProviderDto, CreateMessengerProviderDto } from '../dto'; + +@Entity() +export class ChatProviderMessenger { + @PrimaryColumn() + providerId: number; + + @Column() + userId: string; + + @Column() + userAccessToken: string; + + @Column() + pageId: string; + + @Column() + pageAccessToken: string; + + @Column() + accountId: number; + + _provider: ChatProvider; + + constructor( + accountId: number, + providerId: number, + userId: string, + userAccessToken: string, + pageId: string, + pageAccessToken: string, + ) { + this.accountId = accountId; + this.providerId = providerId; + this.userId = userId; + this.userAccessToken = userAccessToken; + this.pageId = pageId; + this.pageAccessToken = pageAccessToken; + } + + public get provider(): ChatProvider { + return this._provider; + } + public set provider(provider: ChatProvider) { + this._provider = provider; + } + + public toDto(): MessengerProviderDto { + return { ...this.provider.toDto(), pageId: this.pageId, pageAccessToken: this.pageAccessToken }; + } + + public static fromDto(accountId: number, providerId: number, dto: CreateMessengerProviderDto): ChatProviderMessenger { + return new ChatProviderMessenger( + accountId, + providerId, + dto.userId, + dto.userAccessToken, + dto.pageId, + dto.pageAccessToken, + ); + } +} diff --git a/backend/src/modules/multichat/providers/facebook/entities/index.ts b/backend/src/modules/multichat/providers/facebook/entities/index.ts new file mode 100644 index 0000000..9b0ecb0 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/entities/index.ts @@ -0,0 +1 @@ +export * from './chat-provider-messenger.entity'; diff --git a/backend/src/modules/multichat/providers/facebook/facebook-provider.module.ts b/backend/src/modules/multichat/providers/facebook/facebook-provider.module.ts new file mode 100644 index 0000000..3a4ef58 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/facebook-provider.module.ts @@ -0,0 +1,29 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; + +import { ChatMessageModule } from '../../chat-message/chat-message.module'; +import { ChatProviderModule } from '../../chat-provider/chat-provider.module'; + +import facebookConfig from './config/facebook.config'; +import { ChatProviderMessenger } from './entities'; +import { MessengerProviderService } from './messenger-provider.service'; +import { MessengerProviderController, PublicMessengerProviderController } from './controllers'; + +@Module({ + imports: [ + ConfigModule.forFeature(facebookConfig), + TypeOrmModule.forFeature([ChatProviderMessenger]), + IAMModule, + StorageModule, + forwardRef(() => ChatMessageModule), + ChatProviderModule, + ], + providers: [MessengerProviderService], + controllers: [MessengerProviderController, PublicMessengerProviderController], + exports: [MessengerProviderService], +}) +export class FacebookProviderModule {} diff --git a/backend/src/modules/multichat/providers/facebook/messenger-provider.service.ts b/backend/src/modules/multichat/providers/facebook/messenger-provider.service.ts new file mode 100644 index 0000000..4c02648 --- /dev/null +++ b/backend/src/modules/multichat/providers/facebook/messenger-provider.service.ts @@ -0,0 +1,573 @@ +import { Inject, Injectable, Logger, forwardRef } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { HttpService } from '@nestjs/axios'; +import { Repository } from 'typeorm'; +import { catchError } from 'rxjs/operators'; +import { lastValueFrom } from 'rxjs'; +import * as crypto from 'crypto'; + +import { + ForbiddenError, + formatState, + formatUrlQuery, + FrontendRoute, + NotFoundError, + parseState, + StringUtil, + UrlGeneratorService, +} from '@/common'; + +import { AccountService } from '@/modules/iam/account/account.service'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { StorageUrlService } from '@/modules/storage/storage-url.service'; +import { StorageService } from '@/modules/storage/storage.service'; + +import { ChatProviderStatus, ChatProviderTransport, ChatProviderType, MultichatEventType } from '../../common'; +import { ChatUser } from '../../chat-user'; +import { ChatMessage } from '../../chat-message/entities/chat-message.entity'; +import { ChatMessageService } from '../../chat-message/services/chat-message.service'; +import { ChatProvider } from '../../chat-provider/entities'; +import { ChatProviderService } from '../../chat-provider/services/chat-provider.service'; + +import { FacebookConfig } from './config/facebook.config'; +import { CreateMessengerProviderDto, UpdateMessengerProviderDto } from './dto'; +import { ChatProviderMessenger } from './entities'; + +const FacebookUrls = { + facebook: 'https://www.facebook.com', + oauthDialog: () => `${FacebookUrls.facebook}/dialog/oauth`, + graph: 'https://graph.facebook.com/v19.0', + oauthAccessToken: () => `${FacebookUrls.graph}/oauth/access_token`, + profile: (profileId: string) => `${FacebookUrls.graph}/${profileId}`, + messages: (profileId: string) => `${FacebookUrls.profile(profileId)}/messages`, + subscriptions: (appId: string) => `${FacebookUrls.profile(appId)}/subscriptions`, + subscribedApps: (profileId: string) => `${FacebookUrls.profile(profileId)}/subscribed_apps`, + accounts: (profileId: string) => `${FacebookUrls.profile(profileId)}/accounts`, + permissions: (profileId: string) => `${FacebookUrls.profile(profileId)}/permissions`, +} as const; + +const CALLBACK_PATH = '/api/chat/messenger/callback'; +//const WEBHOOK_PATH = '/api/chat/messenger/webhook'; + +@Injectable() +export class MessengerProviderService { + private readonly logger = new Logger(MessengerProviderService.name); + private _config: FacebookConfig; + + constructor( + private readonly configService: ConfigService, + private readonly httpService: HttpService, + @InjectRepository(ChatProviderMessenger) + private readonly repository: Repository, + private readonly accountService: AccountService, + private readonly storageService: StorageService, + private readonly storageUrlService: StorageUrlService, + private readonly urlGenerator: UrlGeneratorService, + private readonly chatProviderService: ChatProviderService, + @Inject(forwardRef(() => ChatMessageService)) + private readonly chatMessageService: ChatMessageService, + ) { + this._config = this.configService.get('facebook'); + } + + getConnectUrl(accountId: number, userId: number, display?: string): string { + return formatUrlQuery(FacebookUrls.oauthDialog(), { + response_type: 'code', + override_default_response_type: 'true', + display: display, + client_id: this._config.appId, + redirect_uri: this.getAuthRedirectUrl(), + config_id: this._config.messengerAuthConfigId, + state: formatState(accountId, userId), + auth_type: 'rerequest', + }); + } + + private getAuthRedirectUrl(): string { + return this.urlGenerator.createUrl({ route: CALLBACK_PATH }); + } + + /* + private getMessageWebhookUrl(): string { + return this.urlGenerator.createUrl(WEBHOOK_PATH); + } + */ + + async authCallback(code: string, state: string, error?: string): Promise { + const [accountId, userId] = parseState(state, Number); + const account = await this.accountService.findOne({ accountId }); + + const redirectUrl = this.urlGenerator.createUrl({ + route: FrontendRoute.settings.facebook.messenger(), + subdomain: account.subdomain, + }); + + if (error) { + return formatUrlQuery(redirectUrl, { error }); + } + + const accessToken = await this.getAccessToken(code); + + const profile = await this.getPageProfile('me', accessToken); + + if (!profile?.accounts?.data) { + return formatUrlQuery(redirectUrl, { error: 'true' }); + } + + const providerIds: number[] = []; + for (const { id, name, access_token } of profile.accounts.data) { + if (await this.subscribePage(id, access_token)) { + const fbmProviders = await this.findProvidersByPageId(id, account.id); + let fbmProvider = fbmProviders.length > 0 ? fbmProviders[0] : null; + if (fbmProvider) { + fbmProvider = await this.updateAccessToken(fbmProvider, accessToken); + } else { + fbmProvider = await this.create(accountId, userId, { + type: ChatProviderType.Facebook, + transport: ChatProviderTransport.Messenger, + title: name, + status: ChatProviderStatus.Active, + accessibleUserIds: [], + responsibleUserIds: [], + userId: profile.id, + userAccessToken: accessToken, + pageId: id, + pageAccessToken: access_token, + }); + } + providerIds.push(fbmProvider.providerId); + } + } + + return formatUrlQuery(redirectUrl, { providerId: String(providerIds[0]) }); + } + + private async getAccessToken(code: string): Promise { + try { + const response$ = this.httpService + .get(FacebookUrls.oauthAccessToken(), { + params: { + client_id: this._config.appId, + client_secret: this._config.appSecret, + redirect_uri: this.getAuthRedirectUrl(), + code: code, + }, + }) + .pipe( + catchError((error) => { + this.logger.error(`Get access token error`, (error as Error)?.stack); + throw error; + }), + ); + const response = await lastValueFrom(response$); + return response.data.access_token; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return null; + } + } + + /** + * Configured in FB App Messenger + */ + /* + private async subscribeApp(): Promise { + try { + const response$ = this.httpService + .post(FacebookUrls.subscriptions(this._config.appId), null, { + params: { + object: 'page', + callback_url: this.getMessageWebhookUrl(), + fields: 'messages', + include_values: true, + verify_token: this._config.messengerValidationToken, + access_token: this._config.appAccessToken, + }, + }) + .pipe( + catchError((error) => { + this.logger.error(`Subscribe app error: ${JSON.stringify(error)}`); + throw error; + }), + ); + const response = await lastValueFrom(response$); + return Boolean(response.data.success); + } catch (e) { + return false; + } + } + */ + + private async subscribePage(pageId: string, accessToken: string): Promise { + try { + const response$ = this.httpService + .post(FacebookUrls.subscribedApps(pageId), null, { + params: { + subscribed_fields: 'messages', + access_token: accessToken, + }, + }) + .pipe( + catchError((error) => { + this.logger.error(`Subscribe app error`, (error as Error)?.stack); + throw error; + }), + ); + const response = await lastValueFrom(response$); + return Boolean(response.data.success); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return false; + } + } + + async handleDeauthoriseRequest(signedRequest: string): Promise { + const userId = await this.deleteAuthData(signedRequest); + + return !!userId; + } + + async handleDeleteAuthRequest(signedRequest: string) { + const verifyUrl = this.urlGenerator.createUrl({ route: FrontendRoute.settings.facebook.deleteVerify() }); + const userId = await this.deleteAuthData(signedRequest); + + return { url: verifyUrl, confirmation_code: userId ?? '' }; + } + + private async deleteAuthData(signedRequest: string): Promise { + try { + const [encodedSig, payload] = signedRequest.split('.'); + + const sig = StringUtil.decode(encodedSig, 'base64', 'hex'); + const expectedSig = crypto.createHmac('sha256', this._config.appSecret).update(payload).digest('hex'); + + if (sig !== expectedSig) { + return null; + } + + const decodedPayload = StringUtil.decode(payload, 'base64', 'utf-8'); + const data = JSON.parse(decodedPayload) as { user_id: string }; + + const fbmProviders = await this.findProvidersByPageId(data.user_id); + for (const fbmProvider of fbmProviders) { + await this.chatProviderService.update(fbmProvider.accountId, null, fbmProvider.providerId, { + status: ChatProviderStatus.Deleted, + }); + } + + return data.user_id; + } catch (error) { + this.logger.error(`Handle deauthorise user error`, (error as Error)?.stack); + return null; + } + } + + async getProvidersWithSettings(accountId: number, user: User): Promise { + const allProviders = await this.repository + .createQueryBuilder('tp') + .where('tp.account_id = :accountId', { accountId }) + .getMany(); + + for (const provider of allProviders) { + provider.provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId: provider.providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + } + + return user.isAdmin + ? allProviders + : allProviders.filter( + (p) => + !p.provider.accessibleUsers || + p.provider.accessibleUsers.length === 0 || + p.provider.accessibleUsers.some((u) => u.userId === user.id), + ); + } + + async getProviderWithSettings(accountId: number, providerId: number): Promise { + const provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + + const fbmProvider = await this.getProvider(accountId, providerId); + fbmProvider.provider = provider; + + return fbmProvider; + } + + private async getProvider(accountId: number, providerId: number): Promise { + const provider = await this.repository.findOneBy({ accountId, providerId }); + + if (!provider) { + throw NotFoundError.withId(ChatProviderMessenger, providerId); + } + + return provider; + } + + private async findProvidersByPageId(pageId: string, accountId?: number): Promise { + return this.repository.findBy({ pageId, accountId }); + } + + async create(accountId: number, userId: number, dto: CreateMessengerProviderDto): Promise { + const provider = await this.chatProviderService.create(accountId, userId, dto); + + const fbmProvider = await this.repository.save(ChatProviderMessenger.fromDto(accountId, provider.id, dto)); + fbmProvider.provider = provider; + + return fbmProvider; + } + + async update(accountId: number, providerId: number, dto: UpdateMessengerProviderDto): Promise { + const provider = await this.chatProviderService.update(accountId, null, providerId, dto); + + const fbmProvider = await this.getProvider(accountId, providerId); + + fbmProvider.provider = provider; + return fbmProvider; + } + + private async updateAccessToken( + fbmProvider: ChatProviderMessenger, + accessToken: string, + ): Promise { + fbmProvider.pageAccessToken = accessToken; + await this.repository.save(fbmProvider); + + return fbmProvider; + } + + async delete({ accountId, userId, providerId }: { accountId: number; userId: number; providerId: number }) { + const fbmProvider = await this.getProvider(accountId, providerId); + + if (fbmProvider?.pageAccessToken) { + await this.revokeAccessToken(fbmProvider.userId, fbmProvider.userAccessToken); + } + + await this.repository.delete({ accountId, providerId }); + await this.chatProviderService.delete({ accountId, userId, providerId }); + } + + private async revokeAccessToken(userId: string, accessToken: string): Promise { + try { + await lastValueFrom( + this.httpService.delete(FacebookUrls.permissions(userId), { params: { access_token: accessToken } }).pipe( + catchError((error) => { + this.logger.error(`Error revoking access token`, (error as Error)?.stack); + throw error; + }), + ), + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return; + } + } + + async notifyChatUsers( + account: Account, + message: ChatMessage, + type: MultichatEventType, + provider: ChatProvider, + users: ChatUser[], + ): Promise { + const fbmProvider = await this.getProvider(account.id, provider.id); + fbmProvider.provider = provider; + + switch (type) { + case MultichatEventType.ChatMessageCreated: + this.sendMessage(account, fbmProvider, message, users); + break; + } + } + + private async sendMessage( + account: Account, + provider: ChatProviderMessenger, + message: ChatMessage, + users: ChatUser[], + ): Promise { + const files = + message.files && message.files.length > 0 + ? message.files.map((file) => ({ + url: this.storageUrlService.getTemporaryUrl(file.fileId, account.subdomain), + isImage: file.mimeType.startsWith('image/'), + })) + : []; + + for (const user of users) { + if (user.externalUser) { + if (message.text) { + const data = { + recipient: { id: user.externalUser.externalId }, + message: { text: message.text }, + }; + + this.sendOneMessage(data, provider.pageAccessToken); + } + + for (const file of files) { + const data = { + recipient: { id: user.externalUser.externalId }, + message: { + attachment: { type: 'file', payload: { url: file.url, is_reusable: true } }, + }, + }; + + this.sendOneMessage(data, provider.pageAccessToken); + } + } + } + } + + private async sendOneMessage(data: unknown, pageAccessToken: string): Promise { + try { + const response$ = this.httpService + .post(FacebookUrls.messages('me'), data, { + params: { access_token: pageAccessToken }, + }) + .pipe( + catchError((error) => { + this.logger.error(`Send message error`, (error as Error)?.stack); + throw error; + }), + ); + await lastValueFrom(response$); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return null; + } + } + + async createChatExternalId(userExternalId: string): Promise { + return this.formatChatExternalId(userExternalId); + } + + private formatChatExternalId(userExternalId: string): string { + return userExternalId; + } + + async verifyWebhook(token: string, challenge: string): Promise { + if (token === this._config.messengerValidationToken) { + return challenge; + } + throw new ForbiddenError(); + } + + async handleWebhook(body: any): Promise { + this.logger.debug(`Handle webhook body: ${JSON.stringify(body)}`); + if (body.object !== 'page') { + return; + } + + for (const entry of body.entry) { + const fbmProviders = await this.findProvidersByPageId(entry.id); + for (const fbmProvider of fbmProviders) { + const account = await this.accountService.findOne({ accountId: fbmProvider.accountId }); + for (const event of entry.messaging) { + if (event.message) { + await this.handleMessage(account, fbmProvider, event); + } + } + } + } + } + + private async handleMessage(account: Account, fbmProvider: ChatProviderMessenger, event: any) { + this.logger.debug(`Handle message: ${JSON.stringify(event)}`); + try { + const chatUser = await this.getChatUser({ + accountId: account.id, + providerId: fbmProvider.providerId, + pageAccessToken: fbmProvider.pageAccessToken, + profileId: event.sender.id, + }); + if (chatUser) { + const { mid, text, attachments } = event.message; + const fileIds = attachments ? await this.getMessageFileIds(account.id, attachments) : null; + await this.chatMessageService.createExternal(account, chatUser, text ?? '', mid, fileIds); + } + } catch (error) { + this.logger.error(`Handle message error`, (error as Error)?.stack); + } + } + + private async getChatUser({ + accountId, + providerId, + pageAccessToken, + profileId, + }: { + accountId: number; + providerId: number; + pageAccessToken: string; + profileId: string; + }): Promise { + const profile = await this.getUserProfile(profileId, pageAccessToken); + if (!profile.id) { + return null; + } + + return this.chatProviderService.getChatUserExternal({ + accountId, + providerId, + chatExternalId: this.formatChatExternalId(profile.id), + externalUserDto: { + externalId: profile.id, + firstName: profile.first_name ?? 'Unknown', + lastName: profile.last_name ?? 'User', + avatarUrl: profile.picture?.data?.url ?? null, + }, + }); + } + + private async getPageProfile(profileId: string, pageAccessToken: string): Promise { + return this.getProfile(profileId, pageAccessToken, 'id,accounts{id,name,access_token}'); + } + private async getUserProfile(profileId: string, pageAccessToken: string): Promise { + return this.getProfile(profileId, pageAccessToken, 'id,first_name,last_name,picture'); + } + private async getProfile(profileId: string, pageAccessToken: string, fields: string): Promise { + try { + const response$ = this.httpService + .get(FacebookUrls.profile(profileId), { + params: { access_token: pageAccessToken, fields: fields }, + }) + .pipe( + catchError((error) => { + this.logger.error(`Get user profile error`, (error as Error)?.stack); + throw error; + }), + ); + const response = await lastValueFrom(response$); + return response.data; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return { id: profileId }; + } + } + + private async getMessageFileIds(accountId: number, attachments: any[]): Promise { + const fileIds: string[] = []; + for (const attachment of attachments) { + const fileUrl = attachment?.payload?.url; + + if (fileUrl) { + const fileInfo = await this.storageService.storeExternalFile(accountId, null, fileUrl); + if (fileInfo) { + fileIds.push(fileInfo.id); + } + } + } + + return fileIds; + } +} diff --git a/backend/src/modules/multichat/providers/providers.module.ts b/backend/src/modules/multichat/providers/providers.module.ts new file mode 100644 index 0000000..aec7bf8 --- /dev/null +++ b/backend/src/modules/multichat/providers/providers.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; + +import { ChatProviderModule } from '../chat-provider/chat-provider.module'; +import { TwilioProviderModule } from './twilio/twilio-provider.module'; +import { FacebookProviderModule } from './facebook/facebook-provider.module'; +import { WazzupProviderModule } from './wazzup/wazzup-provider.module'; +import { ChatProviderProxyService } from './chat-provider-proxy.service'; + +@Module({ + imports: [ChatProviderModule, FacebookProviderModule, TwilioProviderModule, WazzupProviderModule], + providers: [ChatProviderProxyService], + exports: [ChatProviderProxyService], +}) +export class ProvidersModule {} diff --git a/backend/src/modules/multichat/providers/twilio/controllers/index.ts b/backend/src/modules/multichat/providers/twilio/controllers/index.ts new file mode 100644 index 0000000..8b6a66a --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/controllers/index.ts @@ -0,0 +1,2 @@ +export * from './public-twilio-provider.controller'; +export * from './twilio-provider.controller'; diff --git a/backend/src/modules/multichat/providers/twilio/controllers/public-twilio-provider.controller.ts b/backend/src/modules/multichat/providers/twilio/controllers/public-twilio-provider.controller.ts new file mode 100644 index 0000000..b94e449 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/controllers/public-twilio-provider.controller.ts @@ -0,0 +1,18 @@ +import { Body, Controller, Post, Res } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; +import { Response } from 'express'; + +import { TwilioProviderService } from '../twilio-provider.service'; + +@ApiExcludeController(true) +@Controller('chat/twilio') +export class PublicTwilioProviderController { + constructor(private readonly service: TwilioProviderService) {} + + @Post('webhook') + async handleWebhook(@Body() body: unknown, @Res() res: Response) { + const response = await this.service.handleWebhook(body); + + res.type('text/xml').send(response); + } +} diff --git a/backend/src/modules/multichat/providers/twilio/controllers/twilio-provider.controller.ts b/backend/src/modules/multichat/providers/twilio/controllers/twilio-provider.controller.ts new file mode 100644 index 0000000..5ae28b8 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/controllers/twilio-provider.controller.ts @@ -0,0 +1,78 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { AuthDataPrefetch } from '@/modules/iam/common/decorators/auth-data-prefetch.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { TwilioProviderDto, CreateTwilioProviderDto, UpdateTwilioProviderDto } from '../dto'; +import { TwilioProviderService } from '../twilio-provider.service'; + +@ApiTags('multichat/settings/twilio') +@Controller('chat/settings/providers/twilio') +@JwtAuthorized() +@TransformToDto() +export class TwilioProviderController { + constructor(private readonly service: TwilioProviderService) {} + + @ApiOperation({ summary: 'Create twilio chat provider', description: 'Create twilio chat provider' }) + @ApiBody({ description: 'Data for creating Twilio chat provider', type: CreateTwilioProviderDto }) + @ApiCreatedResponse({ description: 'Twilio chat provider', type: TwilioProviderDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateTwilioProviderDto) { + return this.service.create(accountId, userId, dto); + } + + @ApiOperation({ + summary: 'Get twilio chat providers with settings', + description: 'Get twilio chat providers with settings', + }) + @ApiOkResponse({ description: 'Twilio chat providers', type: [TwilioProviderDto] }) + @AuthDataPrefetch({ user: true }) + @Get() + async getProvidersWithSettings(@CurrentAuth() { accountId, user }: AuthData) { + return this.service.getProvidersWithSettings(accountId, user); + } + + @ApiOperation({ + summary: 'Get twilio chat provider with settings', + description: 'Get twilio chat provider with settings', + }) + @ApiParam({ name: 'providerId', description: 'Twilio chat provider ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Twilio chat provider', type: TwilioProviderDto }) + @Get(':providerId') + async getProviderWithSettings( + @CurrentAuth() { accountId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + ) { + return this.service.getProviderWithSettings(accountId, providerId); + } + + @ApiOperation({ summary: 'Update twilio chat provider', description: 'Update twilio chat provider' }) + @ApiParam({ name: 'providerId', type: Number, required: true, description: 'Twilio chat provider ID' }) + @ApiBody({ description: 'Data for updating Twilio chat provider', type: UpdateTwilioProviderDto }) + @ApiOkResponse({ description: 'Twilio chat provider', type: TwilioProviderDto }) + @Put(':providerId') + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + @Body() dto: UpdateTwilioProviderDto, + ) { + return this.service.update(accountId, providerId, dto); + } + + @ApiOperation({ summary: 'Delete twilio chat provider', description: 'Delete twilio chat provider' }) + @ApiParam({ name: 'providerId', type: Number, required: true, description: 'Twilio chat provider ID' }) + @ApiOkResponse({ type: Boolean }) + @Delete(':providerId') + async delete( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + ): Promise { + await this.service.delete({ accountId, userId, providerId }); + + return true; + } +} diff --git a/backend/src/modules/multichat/providers/twilio/dto/create-twilio-provider.dto.ts b/backend/src/modules/multichat/providers/twilio/dto/create-twilio-provider.dto.ts new file mode 100644 index 0000000..b373b9d --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/dto/create-twilio-provider.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { CreateChatProviderDto } from '../../../chat-provider/dto'; + +export class CreateTwilioProviderDto extends CreateChatProviderDto { + @ApiProperty({ description: 'Twilio account SID' }) + @IsString() + accountSid: string; + + @ApiProperty({ description: 'Twilio auth token' }) + @IsString() + authToken: string; + + @ApiProperty({ description: 'Twilio phone number' }) + @IsString() + phoneNumber: string; +} diff --git a/backend/src/modules/multichat/providers/twilio/dto/index.ts b/backend/src/modules/multichat/providers/twilio/dto/index.ts new file mode 100644 index 0000000..77ecb78 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-twilio-provider.dto'; +export * from './twilio-provider.dto'; +export * from './update-twilio-provider.dto'; diff --git a/backend/src/modules/multichat/providers/twilio/dto/twilio-provider.dto.ts b/backend/src/modules/multichat/providers/twilio/dto/twilio-provider.dto.ts new file mode 100644 index 0000000..e8fd183 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/dto/twilio-provider.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { ChatProviderDto } from '../../../chat-provider/dto'; + +export class TwilioProviderDto extends ChatProviderDto { + @ApiProperty({ description: 'Twilio account SID' }) + @IsString() + accountSid: string; + + @ApiProperty({ description: 'Twilio phone number' }) + @IsString() + phoneNumber: string; +} diff --git a/backend/src/modules/multichat/providers/twilio/dto/update-twilio-provider.dto.ts b/backend/src/modules/multichat/providers/twilio/dto/update-twilio-provider.dto.ts new file mode 100644 index 0000000..8ada9f4 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/dto/update-twilio-provider.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +import { UpdateChatProviderDto } from '../../../chat-provider/dto'; + +export class UpdateTwilioProviderDto extends UpdateChatProviderDto { + @ApiProperty({ description: 'Twilio account SID' }) + @IsString() + accountSid: string; + + @ApiPropertyOptional({ description: 'Twilio auth token' }) + @IsOptional() + @IsString() + authToken?: string; + + @ApiProperty({ description: 'Twilio phone number' }) + @IsString() + phoneNumber: string; +} diff --git a/backend/src/modules/multichat/providers/twilio/entities/chat-provider-twilio.entity.ts b/backend/src/modules/multichat/providers/twilio/entities/chat-provider-twilio.entity.ts new file mode 100644 index 0000000..401e9e3 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/entities/chat-provider-twilio.entity.ts @@ -0,0 +1,54 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ChatProvider } from '../../../chat-provider/entities'; +import { TwilioProviderDto, CreateTwilioProviderDto, UpdateTwilioProviderDto } from '../dto'; + +@Entity() +export class ChatProviderTwilio { + @PrimaryColumn() + providerId: number; + + @Column() + accountSid: string; + + @Column() + authToken: string; + + @Column() + phoneNumber: string; + + @Column() + accountId: number; + + _provider: ChatProvider; + + constructor(accountId: number, providerId: number, accountSid: string, authToken: string, phoneNumber: string) { + this.accountId = accountId; + this.providerId = providerId; + this.accountSid = accountSid; + this.authToken = authToken; + this.phoneNumber = phoneNumber; + } + + public get provider(): ChatProvider { + return this._provider; + } + public set provider(provider: ChatProvider) { + this._provider = provider; + } + + public toDto(): TwilioProviderDto { + return { ...this.provider.toDto(), accountSid: this.accountSid, phoneNumber: this.phoneNumber }; + } + + public static fromDto(accountId: number, providerId: number, dto: CreateTwilioProviderDto): ChatProviderTwilio { + return new ChatProviderTwilio(accountId, providerId, dto.accountSid, dto.authToken, dto.phoneNumber); + } + + public update(dto: UpdateTwilioProviderDto): ChatProviderTwilio { + this.accountSid = dto.accountSid; + this.phoneNumber = dto.phoneNumber; + this.authToken = dto.authToken ? dto.authToken : this.authToken; + return this; + } +} diff --git a/backend/src/modules/multichat/providers/twilio/entities/index.ts b/backend/src/modules/multichat/providers/twilio/entities/index.ts new file mode 100644 index 0000000..b011673 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/entities/index.ts @@ -0,0 +1 @@ +export * from './chat-provider-twilio.entity'; diff --git a/backend/src/modules/multichat/providers/twilio/twilio-provider.module.ts b/backend/src/modules/multichat/providers/twilio/twilio-provider.module.ts new file mode 100644 index 0000000..2d5c815 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/twilio-provider.module.ts @@ -0,0 +1,26 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; + +import { ChatMessageModule } from '../../chat-message/chat-message.module'; +import { ChatProviderModule } from '../../chat-provider/chat-provider.module'; + +import { ChatProviderTwilio } from './entities'; +import { TwilioProviderService } from './twilio-provider.service'; +import { PublicTwilioProviderController, TwilioProviderController } from './controllers'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ChatProviderTwilio]), + IAMModule, + StorageModule, + forwardRef(() => ChatMessageModule), + ChatProviderModule, + ], + providers: [TwilioProviderService], + controllers: [TwilioProviderController, PublicTwilioProviderController], + exports: [TwilioProviderService], +}) +export class TwilioProviderModule {} diff --git a/backend/src/modules/multichat/providers/twilio/twilio-provider.service.ts b/backend/src/modules/multichat/providers/twilio/twilio-provider.service.ts new file mode 100644 index 0000000..2608661 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/twilio-provider.service.ts @@ -0,0 +1,311 @@ +import { Inject, Injectable, Logger, forwardRef } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import TwilioSDK from 'twilio'; + +import { NotFoundError, splitByFirstSpace } from '@/common'; + +import { AccountService } from '@/modules/iam/account/account.service'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { StorageUrlService } from '@/modules/storage/storage-url.service'; +import { StorageService } from '@/modules/storage/storage.service'; + +import { ChatProviderType, MultichatEventType } from '../../common'; +import { ChatUser } from '../../chat-user'; +import { ChatMessage } from '../../chat-message/entities/chat-message.entity'; +import { ChatMessageService } from '../../chat-message/services/chat-message.service'; +import { ChatProvider } from '../../chat-provider/entities'; +import { ChatProviderService } from '../../chat-provider/services/chat-provider.service'; + +import { CreateTwilioProviderDto, UpdateTwilioProviderDto } from './dto'; +import { ChatProviderTwilio } from './entities'; +import { TwilioRequestBody } from './types'; + +@Injectable() +export class TwilioProviderService { + private readonly logger = new Logger(TwilioProviderService.name); + + constructor( + @InjectRepository(ChatProviderTwilio) + private readonly repository: Repository, + private readonly accountService: AccountService, + private readonly storageService: StorageService, + private readonly storageUrlService: StorageUrlService, + private readonly chatProviderService: ChatProviderService, + @Inject(forwardRef(() => ChatMessageService)) + private readonly chatMessageService: ChatMessageService, + ) {} + + async getProvidersWithSettings(accountId: number, user: User): Promise { + const allProviders = await this.repository + .createQueryBuilder('tp') + .where('tp.account_id = :accountId', { accountId }) + .getMany(); + + for (const provider of allProviders) { + provider.provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId: provider.providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + } + + return user.isAdmin + ? allProviders + : allProviders.filter( + (p) => + !p.provider.accessibleUsers || + p.provider.accessibleUsers.length === 0 || + p.provider.accessibleUsers.some((u) => u.userId === user.id), + ); + } + + async getProviderWithSettings(accountId: number, providerId: number): Promise { + const provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + + const twilioProvider = await this.getProvider(accountId, providerId); + twilioProvider.provider = provider; + + return twilioProvider; + } + + private async getProvider(accountId: number, providerId: number): Promise { + const provider = await this.repository.findOneBy({ accountId, providerId }); + + if (!provider) { + throw NotFoundError.withId(ChatProviderTwilio, providerId); + } + + return provider; + } + + private async getProviderByPhoneNumber(phoneNumber: string): Promise { + const twilioProvider = await this.repository.findOneBy({ phoneNumber }); + + if (!twilioProvider) { + throw NotFoundError.withId(ChatProviderTwilio, phoneNumber); + } + + return twilioProvider; + } + + async create(accountId: number, userId: number, dto: CreateTwilioProviderDto): Promise { + const provider = await this.chatProviderService.create(accountId, userId, dto); + + const twilioProvider = await this.repository.save(ChatProviderTwilio.fromDto(accountId, provider.id, dto)); + twilioProvider.provider = provider; + + return twilioProvider; + } + + async update(accountId: number, providerId: number, dto: UpdateTwilioProviderDto): Promise { + const provider = await this.chatProviderService.update(accountId, null, providerId, dto); + + const twilioProvider = await this.getProvider(accountId, providerId); + await this.repository.save(twilioProvider.update(dto)); + + twilioProvider.provider = provider; + + return twilioProvider; + } + + async delete({ accountId, userId, providerId }: { accountId: number; userId: number; providerId: number }) { + await this.repository.delete({ accountId, providerId }); + await this.chatProviderService.delete({ accountId, userId, providerId }); + } + + async notifyChatUsers( + account: Account, + message: ChatMessage, + type: MultichatEventType, + provider: ChatProvider, + users: ChatUser[], + ): Promise { + const twilioProvider = await this.getProvider(account.id, provider.id); + const twilio = TwilioSDK(twilioProvider.accountSid, twilioProvider.authToken); + const from = this.formatPhoneNumber(twilioProvider.phoneNumber); + + switch (type) { + case MultichatEventType.ChatMessageCreated: + await this.sendMessage(account, twilio, from, message, users); + break; + } + } + + private async sendMessage( + account: Account, + twilio: TwilioSDK.Twilio, + from: string, + message: ChatMessage, + users: ChatUser[], + ): Promise { + const fileUrls = + message.files && message.files.length > 0 + ? message.files.map((file) => this.storageUrlService.getTemporaryUrl(file.fileId, account.subdomain)) + : undefined; + + await Promise.all( + users + .filter((u) => u.externalUser) + .map(async (user) => { + try { + await twilio.messages.create({ + from, + to: this.formatPhoneNumber(user.externalUser.phone), + body: message.text, + mediaUrl: fileUrls, + }); + } catch (e) { + this.logger.error(`Send message error`, (e as Error)?.stack); + } + }), + ); + } + + async sendDirectMessage({ + accountId, + providerId, + phone, + message, + }: { + accountId: number; + providerId: number; + phone: string; + message: string; + }): Promise { + try { + const twilioProvider = await this.getProvider(accountId, providerId); + const twilio = TwilioSDK(twilioProvider.accountSid, twilioProvider.authToken); + + await twilio.messages.create({ + from: this.formatPhoneNumber(twilioProvider.phoneNumber), + to: this.formatPhoneNumber(phone), + body: message, + }); + } catch (e) { + this.logger.error(`Send message error`, (e as Error)?.stack); + } + } + + async createChatExternalId(userExternalId: string): Promise { + return this.formatChatExternalId(userExternalId); + } + + private formatChatExternalId(userExternalId: string): string { + return userExternalId; + } + + async handleWebhook(body: unknown): Promise { + const message = body as TwilioRequestBody; + if (message.To) { + const [, providerPhoneNumber] = this.parsePhoneNumber(message.To); + const twilioProvider = await this.getProviderByPhoneNumber(providerPhoneNumber); + if (twilioProvider) { + const account = await this.accountService.findOne({ accountId: twilioProvider.accountId }); + await this.handleMessage({ account, twilioProvider, message }); + } + } + + return new TwilioSDK.twiml.MessagingResponse().toString(); + } + + private async handleMessage({ + account, + twilioProvider, + message, + }: { + account: Account; + twilioProvider: ChatProviderTwilio; + message: TwilioRequestBody; + }) { + this.logger.debug(`Handle message: ${JSON.stringify(message)}`); + try { + const chatUser = await this.getChatUser({ + accountId: account.id, + providerId: twilioProvider.providerId, + message, + }); + if (chatUser) { + const fileIds = await this.getMessageFileIds(account.id, twilioProvider, message); + await this.chatMessageService.createExternal(account, chatUser, message.Body, message.MessageSid, fileIds); + } + } catch (error) { + this.logger.error(`Handle message error`, (error as Error)?.stack); + } + } + + private async getChatUser({ + accountId, + providerId, + message, + }: { + accountId: number; + providerId: number; + message: TwilioRequestBody; + }): Promise { + const [, userPhoneNumber] = this.parsePhoneNumber(message.From); + const [firstName, lastName] = splitByFirstSpace(message.ProfileName || message.WaId || userPhoneNumber); + + return this.chatProviderService.getChatUserExternal({ + accountId, + providerId, + chatExternalId: this.formatChatExternalId(userPhoneNumber), + externalUserDto: { + externalId: userPhoneNumber, + firstName, + lastName, + phone: userPhoneNumber, + }, + }); + } + + private async getMessageFileIds( + accountId: number, + twilioProvider: ChatProviderTwilio, + data: TwilioRequestBody, + ): Promise { + if (!data.NumMedia) { + return null; + } + + const auth = `Basic ${Buffer.from(`${twilioProvider.accountSid}:${twilioProvider.authToken}`).toString('base64')}`; + const numMedia = parseInt(data.NumMedia, 10); + const fileIds: string[] = []; + for (let i = 0; i < numMedia; i++) { + const fileUrl = data[`MediaUrl${i}`]; + + if (fileUrl) { + const fileInfo = await this.storageService.storeExternalFile(accountId, null, fileUrl, { + authorization: auth, + }); + if (fileInfo) { + fileIds.push(fileInfo.id); + } + } + } + + return fileIds; + } + + private formatPhoneNumber(phoneNumber: string): string { + const formattedPhoneNumber = phoneNumber.startsWith('+') ? phoneNumber : `+${phoneNumber}`; + + return `whatsapp:${formattedPhoneNumber}`; + } + + private parsePhoneNumber(phoneNumber: string): [ChatProviderType, string] { + const match = phoneNumber.match(/^whatsapp:(.+)$/); + if (!match) { + throw new Error('Unsupported phone number format'); + } + + return [ChatProviderType.Twilio, match[1]]; + } +} diff --git a/backend/src/modules/multichat/providers/twilio/types/index.ts b/backend/src/modules/multichat/providers/twilio/types/index.ts new file mode 100644 index 0000000..f43c3a9 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/types/index.ts @@ -0,0 +1 @@ +export * from './twilio-request-body'; diff --git a/backend/src/modules/multichat/providers/twilio/types/twilio-request-body.ts b/backend/src/modules/multichat/providers/twilio/types/twilio-request-body.ts new file mode 100644 index 0000000..6ad2ec5 --- /dev/null +++ b/backend/src/modules/multichat/providers/twilio/types/twilio-request-body.ts @@ -0,0 +1,16 @@ +export class TwilioRequestBody { + SmsMessageSid?: string; + NumMedia?: string; + ProfileName?: string; + SmsSid?: string; + WaId?: string; + SmsStatus?: string; + Body?: string; + To?: string; + NumSegments?: string; + ReferralNumMedia?: string; + MessageSid?: string; + AccountSid?: string; + From?: string; + ApiVersion?: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/config/index.ts b/backend/src/modules/multichat/providers/wazzup/config/index.ts new file mode 100644 index 0000000..4d5fc2e --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/config/index.ts @@ -0,0 +1 @@ +export * from './wazzup.config'; diff --git a/backend/src/modules/multichat/providers/wazzup/config/wazzup.config.ts b/backend/src/modules/multichat/providers/wazzup/config/wazzup.config.ts new file mode 100644 index 0000000..e5148a3 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/config/wazzup.config.ts @@ -0,0 +1,11 @@ +import { registerAs } from '@nestjs/config'; + +export interface WazzupConfig { + secret: string; + enqueue: boolean; +} + +export default registerAs( + 'wazzup', + (): WazzupConfig => ({ secret: process.env.WAZZUP_SECRET, enqueue: process.env.WAZZUP_PROCESS_ENQUEUE === 'true' }), +); diff --git a/backend/src/modules/multichat/providers/wazzup/controllers/index.ts b/backend/src/modules/multichat/providers/wazzup/controllers/index.ts new file mode 100644 index 0000000..f7f1eca --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/controllers/index.ts @@ -0,0 +1,2 @@ +export * from './public-wazzup-provider.controller'; +export * from './wazzup-provider.controller'; diff --git a/backend/src/modules/multichat/providers/wazzup/controllers/public-wazzup-provider.controller.ts b/backend/src/modules/multichat/providers/wazzup/controllers/public-wazzup-provider.controller.ts new file mode 100644 index 0000000..5e3f7bd --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/controllers/public-wazzup-provider.controller.ts @@ -0,0 +1,18 @@ +import { Body, Controller, Post, Res } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; +import { Response } from 'express'; + +import { WazzupProviderService } from '../wazzup-provider.service'; + +@ApiExcludeController(true) +@Controller('chat/wazzup') +export class PublicWazzupProviderController { + constructor(private readonly service: WazzupProviderService) {} + + @Post('webhook') + async handleWebhook(@Body() body: unknown, @Res() res: Response) { + const response = await this.service.handleWebhook(body); + + res.type('text/json').status(200).send(response); + } +} diff --git a/backend/src/modules/multichat/providers/wazzup/controllers/wazzup-provider.controller.ts b/backend/src/modules/multichat/providers/wazzup/controllers/wazzup-provider.controller.ts new file mode 100644 index 0000000..9ad1b70 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/controllers/wazzup-provider.controller.ts @@ -0,0 +1,87 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { AuthDataPrefetch } from '@/modules/iam/common/decorators/auth-data-prefetch.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { WazzupProviderDto, CreateWazzupProviderDto, UpdateWazzupProviderDto, WazzupChannelDto } from '../dto'; +import { WazzupProviderService } from '../wazzup-provider.service'; + +@ApiTags('multichat/settings/wazzup') +@Controller('chat/settings/providers/wazzup') +@JwtAuthorized() +@TransformToDto() +export class WazzupProviderController { + constructor(private readonly service: WazzupProviderService) {} + + @ApiOperation({ summary: 'Create Wazzup chat provider', description: 'Create Wazzup chat provider' }) + @ApiBody({ description: 'Data for creating Wazzup chat provider', type: CreateWazzupProviderDto }) + @ApiCreatedResponse({ description: 'Wazzup chat provider', type: WazzupProviderDto }) + @AuthDataPrefetch({ account: true }) + @Post() + async create(@CurrentAuth() { account, userId }: AuthData, @Body() dto: CreateWazzupProviderDto) { + return this.service.create(account, userId, dto); + } + + @ApiOperation({ summary: 'Get Wazzup API key', description: 'Get Wazzup API key' }) + @ApiQuery({ name: 'state', type: String, required: true, description: 'State' }) + @ApiOkResponse({ description: 'Wazzup API key', type: String }) + @AuthDataPrefetch({ account: true }) + @Get('api-key') + async getApiKey(@CurrentAuth() { account }: AuthData, @Query('state') state: string) { + return this.service.getApiKey(account, state); + } + + @ApiOperation({ summary: 'Get Wazzup channels', description: 'Get Wazzup channels' }) + @ApiQuery({ name: 'apiKey', type: String, required: true, description: 'API key' }) + @ApiOkResponse({ description: 'Wazzup channels', type: [WazzupChannelDto] }) + @Get('channels') + async findChannels(@CurrentAuth() { accountId }: AuthData, @Query('apiKey') apiKey: string) { + return this.service.findChannels(accountId, apiKey); + } + + @ApiOperation({ summary: 'Get Wazzup chat providers', description: 'Get Wazzup chat providers' }) + @ApiOkResponse({ description: 'Wazzup chat providers', type: [WazzupProviderDto] }) + @AuthDataPrefetch({ user: true }) + @Get() + async findMany(@CurrentAuth() { accountId, user }: AuthData) { + return this.service.findMany(accountId, user); + } + + @ApiOperation({ summary: 'Get Wazzup chat provider', description: 'Get Wazzup chat provider' }) + @ApiParam({ name: 'providerId', description: 'Wazzup chat provider ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Wazzup chat provider', type: WazzupProviderDto }) + @Get(':providerId') + async findOne(@CurrentAuth() { accountId }: AuthData, @Param('providerId', ParseIntPipe) providerId: number) { + return this.service.findOne(accountId, providerId); + } + + @ApiOperation({ summary: 'Update Wazzup chat provider', description: 'Update Wazzup chat provider' }) + @ApiParam({ name: 'providerId', description: 'Wazzup chat provider ID', type: Number, required: true }) + @ApiBody({ description: 'Data for updating Wazzup chat provider', type: UpdateWazzupProviderDto }) + @ApiOkResponse({ description: 'Wazzup chat provider', type: WazzupProviderDto }) + @Patch(':providerId') + async update( + @CurrentAuth() { accountId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + @Body() dto: UpdateWazzupProviderDto, + ) { + return this.service.update(accountId, providerId, dto); + } + + @ApiOperation({ summary: 'Delete Wazzup chat provider', description: 'Delete Wazzup chat provider' }) + @ApiParam({ name: 'providerId', description: 'Wazzup chat provider ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Success', type: Boolean }) + @Delete(':providerId') + async delete( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('providerId', ParseIntPipe) providerId: number, + ): Promise { + await this.service.delete({ accountId, userId, providerId }); + + return true; + } +} diff --git a/backend/src/modules/multichat/providers/wazzup/dto/create-wazzup-provider.dto.ts b/backend/src/modules/multichat/providers/wazzup/dto/create-wazzup-provider.dto.ts new file mode 100644 index 0000000..e3ee7c3 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/dto/create-wazzup-provider.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; + +import { CreateChatProviderDto } from '../../../chat-provider/dto'; +import { WazzupTransport } from '../enums'; + +export class CreateWazzupProviderDto extends CreateChatProviderDto { + @ApiProperty() + @IsString() + apiKey: string; + + @ApiProperty() + @IsString() + channelId: string; + + @ApiProperty() + @IsString() + plainId: string; + + @ApiProperty({ enum: WazzupTransport }) + @IsEnum(WazzupTransport) + channelTransport: WazzupTransport; +} diff --git a/backend/src/modules/multichat/providers/wazzup/dto/index.ts b/backend/src/modules/multichat/providers/wazzup/dto/index.ts new file mode 100644 index 0000000..e708f27 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-wazzup-provider.dto'; +export * from './update-wazzup-provider.dto'; +export * from './wazzup-channel.dto'; +export * from './wazzup-provider.dto'; diff --git a/backend/src/modules/multichat/providers/wazzup/dto/update-wazzup-provider.dto.ts b/backend/src/modules/multichat/providers/wazzup/dto/update-wazzup-provider.dto.ts new file mode 100644 index 0000000..c864d80 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/dto/update-wazzup-provider.dto.ts @@ -0,0 +1,3 @@ +import { UpdateChatProviderDto } from '../../../chat-provider/dto'; + +export class UpdateWazzupProviderDto extends UpdateChatProviderDto {} diff --git a/backend/src/modules/multichat/providers/wazzup/dto/wazzup-channel.dto.ts b/backend/src/modules/multichat/providers/wazzup/dto/wazzup-channel.dto.ts new file mode 100644 index 0000000..b31ea29 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/dto/wazzup-channel.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; + +import { WazzupChannelState, WazzupTransport } from '../enums'; + +export class WazzupChannelDto { + @ApiProperty() + @IsString() + channelId: string; + + @ApiProperty({ enum: WazzupTransport }) + @IsEnum(WazzupTransport) + transport: WazzupTransport; + + @ApiProperty({ enum: WazzupChannelState }) + @IsEnum(WazzupChannelState) + state: WazzupChannelState; + + @ApiProperty() + @IsString() + plainId: string; + + @ApiProperty() + @IsString() + name: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/dto/wazzup-provider.dto.ts b/backend/src/modules/multichat/providers/wazzup/dto/wazzup-provider.dto.ts new file mode 100644 index 0000000..8cf5cd5 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/dto/wazzup-provider.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +import { ChatProviderDto } from '../../../chat-provider/dto'; + +export class WazzupProviderDto extends ChatProviderDto { + @ApiProperty({ description: 'Channel ID' }) + @IsString() + channelId: string; + + @ApiProperty({ description: 'Plain ID' }) + @IsString() + plainId: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/entities/chat-provider-wazzup.entity.ts b/backend/src/modules/multichat/providers/wazzup/entities/chat-provider-wazzup.entity.ts new file mode 100644 index 0000000..5ebb08a --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/entities/chat-provider-wazzup.entity.ts @@ -0,0 +1,60 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { ChatProvider } from '../../../chat-provider/entities'; + +import { WazzupTransport } from '../enums'; +import { WazzupProviderDto, CreateWazzupProviderDto } from '../dto'; + +@Entity() +export class ChatProviderWazzup { + @PrimaryColumn() + providerId: number; + + @Column() + apiKey: string; + + @Column() + channelId: string; + + @Column() + transport: WazzupTransport; + + @Column() + plainId: string; + + @Column() + accountId: number; + + _provider: ChatProvider; + + constructor( + accountId: number, + providerId: number, + apiKey: string, + channelId: string, + transport: WazzupTransport, + plainId: string, + ) { + this.accountId = accountId; + this.providerId = providerId; + this.apiKey = apiKey; + this.channelId = channelId; + this.transport = transport; + this.plainId = plainId; + } + + public get provider(): ChatProvider { + return this._provider; + } + public set provider(provider: ChatProvider) { + this._provider = provider; + } + + public toDto(): WazzupProviderDto { + return { ...this.provider.toDto(), channelId: this.channelId, plainId: this.plainId }; + } + + public static fromDto(accountId: number, providerId: number, dto: CreateWazzupProviderDto): ChatProviderWazzup { + return new ChatProviderWazzup(accountId, providerId, dto.apiKey, dto.channelId, dto.channelTransport, dto.plainId); + } +} diff --git a/backend/src/modules/multichat/providers/wazzup/entities/index.ts b/backend/src/modules/multichat/providers/wazzup/entities/index.ts new file mode 100644 index 0000000..6c30944 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/entities/index.ts @@ -0,0 +1 @@ +export * from './chat-provider-wazzup.entity'; diff --git a/backend/src/modules/multichat/providers/wazzup/enums/index.ts b/backend/src/modules/multichat/providers/wazzup/enums/index.ts new file mode 100644 index 0000000..92f24a1 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/enums/index.ts @@ -0,0 +1,5 @@ +export * from './wazzup-channel-state.enum'; +export * from './wazzup-chat-type.enum'; +export * from './wazzup-message-status.enum'; +export * from './wazzup-message-type.enum'; +export * from './wazzup-transport.enum'; diff --git a/backend/src/modules/multichat/providers/wazzup/enums/wazzup-channel-state.enum.ts b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-channel-state.enum.ts new file mode 100644 index 0000000..2e10614 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-channel-state.enum.ts @@ -0,0 +1,32 @@ +/** + * Enum for defining the states of a channel in the Wazzup system. + * Each state describes the current operational status or issues affecting the channel. + */ +export enum WazzupChannelState { + /** channel is active */ + active = 'active', + /** channel is starting */ + init = 'init', + /** the channel is turned off: it was removed from subscription or deleted with messages saved */ + disabled = 'disabled', + /** no connection to the phone */ + phoneUnavailable = 'phoneUnavailable', + /** QR code must be scanned */ + qridle = 'qridle', + /** the channel is authorized in another Wazzup account */ + openelsewhere = 'openelsewhere', + /** the channel is not pai */ + notEnoughMoney = 'notEnoughMoney', + /** channel QR was scanned by another phone number */ + foreignphone = 'foreignphone', + /** not authorized */ + unauthorized = 'unauthorized', + /** channel is waiting for a password for two-factor authentication */ + waitForPassword = 'waitForPassword', + /** the channel is blocked */ + blocked = 'blocked', + /** the WABA channel is in moderation */ + onModeration = 'onModeration', + /** the WABA channel is rejected */ + rejected = 'rejected', +} diff --git a/backend/src/modules/multichat/providers/wazzup/enums/wazzup-chat-type.enum.ts b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-chat-type.enum.ts new file mode 100644 index 0000000..0c696bb --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-chat-type.enum.ts @@ -0,0 +1,20 @@ +/** + * Enum representing different types of chat channels supported in the Wazzup system. + * Each member specifies a unique platform and type of chat interaction. + */ +export enum WazzupChatType { + /** WhatsApp individual chat. */ + Whatsapp = 'whatsapp', + /** WhatsApp group chat. */ + Whatsgroup = 'whatsgroup', + /** Instagram direct messages. */ + Instagram = 'instagram', + /** Telegram individual chat. */ + Telegram = 'telegram', + /** Telegram group chat. */ + Telegroup = 'telegroup', + /** Vkontakte (VK) messaging. */ + Vk = 'vk', + /** Avito messaging system. */ + Avito = 'avito', +} diff --git a/backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-status.enum.ts b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-status.enum.ts new file mode 100644 index 0000000..ec06c65 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-status.enum.ts @@ -0,0 +1,20 @@ +/** + * Enum representing the status of messages as reported by webhooks in the Wazzup system. + * Each status correlates to a specific state in the message delivery process. + */ +export enum WazzupMessageStatus { + /** Message has been sent (indicated by one grey check mark). */ + Sent = 'sent', + + /** Message has been delivered to the recipient's device (indicated by two grey check marks). */ + Delivered = 'delivered', + + /** Message has been read by the recipient (indicated by two blue check marks). */ + Read = 'read', + + /** There was an error in sending the message. */ + Error = 'error', + + /** Incoming message from another user. */ + Inbound = 'inbound', +} diff --git a/backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-type.enum.ts b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-type.enum.ts new file mode 100644 index 0000000..946711f --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-message-type.enum.ts @@ -0,0 +1,38 @@ +/** + * Enum representing the different types of messages that can be handled in the Wazzup system. + * Each member specifies the format or nature of the message content. + */ +export enum WazzupMessageType { + /** Text message. */ + Text = 'text', + + /** Image file. */ + Image = 'image', + + /** Audio file. */ + Audio = 'audio', + + /** Video file. */ + Video = 'video', + + /** Document file. */ + Document = 'document', + + /** Contact card (vCard format). */ + Vcard = 'vcard', + + /** Geolocation data. */ + Geo = 'geo', + + /** WhatsApp Business API template message. */ + WapiTemplate = 'wapi_template', + + /** Unsupported message type. */ + Unsupported = 'unsupported', + + /** Notification for a missed call. */ + MissingCall = 'missing_call', + + /** Message type that is not recognized or is erroneous. */ + Unknown = 'unknown', +} diff --git a/backend/src/modules/multichat/providers/wazzup/enums/wazzup-transport.enum.ts b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-transport.enum.ts new file mode 100644 index 0000000..c603e6a --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/enums/wazzup-transport.enum.ts @@ -0,0 +1,21 @@ +/** + * Enum for specifying the transport type for messages in the Wazzup system. + * Each member of the enum represents a different platform through which + * messages can be sent or received. + */ +export enum WazzupTransport { + /** WhatsApp channel */ + Whatsapp = 'whatsapp', + /** Instagram channel */ + Instagram = 'instagram', + /** Telegram channel */ + Tgapi = 'tgapi', + /** WABA channel */ + Wapi = 'wapi', + /** Telegram Bot channel */ + Telegram = 'telegram', + /** VK channel */ + Vk = 'vk', + /** Avito channel */ + Avito = 'avito', +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/index.ts b/backend/src/modules/multichat/providers/wazzup/types/index.ts new file mode 100644 index 0000000..bda3542 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/index.ts @@ -0,0 +1,8 @@ +export * from './wazzup-channel'; +export * from './wazzup-connect-request'; +export * from './wazzup-message-contact'; +export * from './wazzup-message-error'; +export * from './wazzup-message'; +export * from './wazzup-send-message-response'; +export * from './wazzup-send-message'; +export * from './wazzup-webhook-request'; diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-channel.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-channel.ts new file mode 100644 index 0000000..ec0351c --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-channel.ts @@ -0,0 +1,20 @@ +import { type WazzupChannelDto } from '../dto'; +import { type WazzupChannelState, type WazzupTransport } from '../enums'; + +export class WazzupChannel { + channelId: string; + transport: WazzupTransport; + state: WazzupChannelState; + plainId: string; + name: string; + + public toDto(): WazzupChannelDto { + return { + channelId: this.channelId, + transport: this.transport, + state: this.state, + plainId: this.plainId, + name: this.name, + }; + } +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-connect-request.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-connect-request.ts new file mode 100644 index 0000000..e50e6b3 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-connect-request.ts @@ -0,0 +1,13 @@ +export class WazzupConnectRequest { + state: string; + secret: string; + crmKey: string; + name: string; + + constructor({ state, secret, crmKey, name }: WazzupConnectRequest) { + this.state = state; + this.secret = secret; + this.crmKey = crmKey; + this.name = name; + } +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-message-contact.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-message-contact.ts new file mode 100644 index 0000000..9855102 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-message-contact.ts @@ -0,0 +1,6 @@ +export interface WazzupMessageContact { + name?: string; + avatarUri?: string; + username?: string; + phone?: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-message-error.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-message-error.ts new file mode 100644 index 0000000..657f760 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-message-error.ts @@ -0,0 +1,4 @@ +export interface WazzupMessageError { + error?: string; + description?: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-message.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-message.ts new file mode 100644 index 0000000..ac69420 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-message.ts @@ -0,0 +1,20 @@ +import { type WazzupChatType, type WazzupMessageStatus, type WazzupMessageType } from '../enums'; +import { type WazzupMessageContact } from './wazzup-message-contact'; +import { type WazzupMessageError } from './wazzup-message-error'; + +export class WazzupMessage { + messageId: string; + channelId: string; + chatType: WazzupChatType; + chatId: string; + dateTime: string; + type: WazzupMessageType; + status: WazzupMessageStatus; + error: WazzupMessageError; + text: string; + contentUri: string; + authorName: string; + isEcho: boolean; + contact: WazzupMessageContact; + avitoProfileId?: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message-response.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message-response.ts new file mode 100644 index 0000000..c179f03 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message-response.ts @@ -0,0 +1,4 @@ +export class WazzupSendMessageResponse { + messageId: string; + chatId: string; +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message.ts new file mode 100644 index 0000000..f3da0d7 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-send-message.ts @@ -0,0 +1,23 @@ +export class WazzupSendMessage { + channelId: string; + chatType: string; + chatId?: string; + text?: string; + contentUri?: string; + refMessageId?: string; + /** Only for Telegram, for direct message, without @ */ + username?: string; + /** Only for Telegram for direct message, only numbers */ + phone?: string; + + constructor({ channelId, chatType, chatId, text, contentUri, refMessageId, username, phone }: WazzupSendMessage) { + this.channelId = channelId; + this.chatType = chatType; + this.chatId = chatId; + this.text = text; + this.contentUri = contentUri; + this.refMessageId = refMessageId; + this.username = username; + this.phone = phone; + } +} diff --git a/backend/src/modules/multichat/providers/wazzup/types/wazzup-webhook-request.ts b/backend/src/modules/multichat/providers/wazzup/types/wazzup-webhook-request.ts new file mode 100644 index 0000000..e1270c6 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/types/wazzup-webhook-request.ts @@ -0,0 +1,6 @@ +import { type WazzupMessage } from './wazzup-message'; + +export class WazzupWebhookRequest { + test?: boolean; + messages?: WazzupMessage[]; +} diff --git a/backend/src/modules/multichat/providers/wazzup/wazzup-provider.module.ts b/backend/src/modules/multichat/providers/wazzup/wazzup-provider.module.ts new file mode 100644 index 0000000..83b0e86 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/wazzup-provider.module.ts @@ -0,0 +1,31 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; + +import { ChatModule } from '../../chat/chat.module'; +import { ChatMessageModule } from '../../chat-message/chat-message.module'; +import { ChatProviderModule } from '../../chat-provider/chat-provider.module'; + +import wazzupConfig from './config/wazzup.config'; +import { ChatProviderWazzup } from './entities'; +import { WazzupProviderService } from './wazzup-provider.service'; +import { PublicWazzupProviderController, WazzupProviderController } from './controllers'; + +@Module({ + imports: [ + ConfigModule.forFeature(wazzupConfig), + TypeOrmModule.forFeature([ChatProviderWazzup]), + IAMModule, + StorageModule, + forwardRef(() => ChatModule), + forwardRef(() => ChatMessageModule), + ChatProviderModule, + ], + providers: [WazzupProviderService], + controllers: [WazzupProviderController, PublicWazzupProviderController], + exports: [WazzupProviderService], +}) +export class WazzupProviderModule {} diff --git a/backend/src/modules/multichat/providers/wazzup/wazzup-provider.service.ts b/backend/src/modules/multichat/providers/wazzup/wazzup-provider.service.ts new file mode 100644 index 0000000..b155e82 --- /dev/null +++ b/backend/src/modules/multichat/providers/wazzup/wazzup-provider.service.ts @@ -0,0 +1,444 @@ +import { HttpService } from '@nestjs/axios'; +import { Inject, Injectable, Logger, forwardRef } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { catchError, lastValueFrom } from 'rxjs'; +import { Repository } from 'typeorm'; + +import { splitByFirstSpace, TaskQueue, UrlGeneratorService } from '@/common'; +import { ApplicationConfig } from '@/config'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AccountService } from '@/modules/iam/account/account.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageUrlService } from '@/modules/storage/storage-url.service'; + +import { MultichatEventType } from '../../common'; +import { ChatUser } from '../../chat-user'; +import { ChatProvider } from '../../chat-provider/entities'; +import { ChatProviderService } from '../../chat-provider/services/chat-provider.service'; +import { Chat } from '../../chat/entities/chat.entity'; +import { ChatService } from '../../chat/services/chat.service'; +import { ChatMessage } from '../../chat-message/entities/chat-message.entity'; +import { ChatMessageService } from '../../chat-message/services/chat-message.service'; + +import { WazzupConfig } from './config'; +import { CreateWazzupProviderDto, UpdateWazzupProviderDto } from './dto'; +import { ChatProviderWazzup } from './entities'; +import { + WazzupChannel, + WazzupConnectRequest, + WazzupMessage, + WazzupSendMessage, + WazzupSendMessageResponse, + WazzupWebhookRequest, +} from './types'; +import { WazzupChatType, WazzupMessageStatus, WazzupTransport } from './enums'; + +const WazzupUrls = { + wazzup: 'https://api.wazzup24.com/v3', + webhooks: () => `${WazzupUrls.wazzup}/webhooks`, + channels: () => `${WazzupUrls.wazzup}/channels`, + message: () => `${WazzupUrls.wazzup}/message`, + connect: () => `${WazzupUrls.wazzup}/connect`, +} as const; + +const WEBHOOK_PATH = '/api/chat/wazzup/webhook'; + +const chatTypeMap: Record = { + [WazzupTransport.Avito]: WazzupChatType.Avito, + [WazzupTransport.Instagram]: WazzupChatType.Instagram, + [WazzupTransport.Telegram]: WazzupChatType.Telegram, + [WazzupTransport.Tgapi]: WazzupChatType.Telegram, + [WazzupTransport.Vk]: WazzupChatType.Vk, + [WazzupTransport.Wapi]: WazzupChatType.Whatsapp, + [WazzupTransport.Whatsapp]: WazzupChatType.Whatsapp, +}; + +const tgapiDirectPrefix = 'tgd_'; + +@Injectable() +export class WazzupProviderService { + private readonly logger = new Logger(WazzupProviderService.name); + private config: WazzupConfig; + private _appName: string; + private readonly queue = new TaskQueue(); + + constructor( + private readonly configService: ConfigService, + private readonly httpService: HttpService, + @InjectRepository(ChatProviderWazzup) + private readonly repository: Repository, + private readonly urlGenerator: UrlGeneratorService, + private readonly accountService: AccountService, + private readonly storageService: StorageService, + private readonly storageUrlService: StorageUrlService, + private readonly chatProviderService: ChatProviderService, + @Inject(forwardRef(() => ChatService)) + private readonly chatService: ChatService, + @Inject(forwardRef(() => ChatMessageService)) + private readonly chatMessageService: ChatMessageService, + ) { + this.config = this.configService.get('wazzup'); + this._appName = this.configService.get('application').name; + } + + async getApiKey(account: Account, state: string): Promise { + try { + const response$ = this.httpService + .post( + WazzupUrls.connect(), + new WazzupConnectRequest({ + state, + secret: this.config.secret, + crmKey: account.subdomain, + name: `${this._appName}-${account.subdomain}`, + }), + ) + .pipe( + catchError((error) => { + this.logger.error(`WAuth connect error`, (error as Error)?.stack); + throw error; + }), + ); + const response = await lastValueFrom(response$); + return response.data.data as string; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return null; + } + } + + async findChannels(accountId: number, apiKey: string): Promise { + try { + const response$ = this.httpService + .get(WazzupUrls.channels(), { headers: { Authorization: `Bearer ${apiKey}` } }) + .pipe( + catchError((error) => { + this.logger.error(`Get channels error for accountId=${accountId}`, (error as Error)?.stack); + throw error; + }), + ); + const response = await lastValueFrom(response$); + return response.data as WazzupChannel[]; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return []; + } + } + + async create(account: Account, userId: number, dto: CreateWazzupProviderDto): Promise { + if (!(await this.setWebhook(account, dto.apiKey))) { + throw new Error('Webhook not connected'); + } + + const provider = await this.chatProviderService.create(account.id, userId, dto); + + const wazzupProvider = await this.repository.save(ChatProviderWazzup.fromDto(account.id, provider.id, dto)); + wazzupProvider.provider = provider; + + return wazzupProvider; + } + + async findMany(accountId: number, user: User): Promise { + const allProviders = await this.repository + .createQueryBuilder('wp') + .where('wp.account_id = :accountId', { accountId }) + .getMany(); + + for (const provider of allProviders) { + provider.provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId: provider.providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + } + + return user.isAdmin + ? allProviders + : allProviders.filter( + (p) => + !p.provider.accessibleUsers || + p.provider.accessibleUsers.length === 0 || + p.provider.accessibleUsers.some((u) => u.userId === user.id), + ); + } + + async findOne(accountId: number, providerId: number): Promise { + const provider = await this.chatProviderService.findOne( + accountId, + null, + { providerId }, + { expand: ['accessibleUsers', 'responsibleUsers', 'supervisorUsers', 'entitySettings'] }, + ); + + const wazzupProvider = await this.repository.findOneBy({ accountId, providerId }); + wazzupProvider.provider = provider; + + return wazzupProvider; + } + + async update(accountId: number, providerId: number, dto: UpdateWazzupProviderDto): Promise { + const provider = await this.chatProviderService.update(accountId, null, providerId, dto); + + const wazzupProvider = await this.repository.findOneBy({ accountId, providerId }); + + wazzupProvider.provider = provider; + + return wazzupProvider; + } + + async delete({ accountId, userId, providerId }: { accountId: number; userId: number; providerId: number }) { + await this.repository.delete({ accountId, providerId }); + await this.chatProviderService.delete({ accountId, userId, providerId }); + } + + async handleWebhook(body: unknown): Promise { + const request = body as WazzupWebhookRequest; + if (request.test) { + //webhook test + } + + if (request.messages) { + for (const message of request.messages) { + if (message.status === WazzupMessageStatus.Inbound) { + const wazzupProvider = await this.repository.findOneBy({ channelId: message.channelId }); + if (wazzupProvider) { + const account = await this.accountService.findOne({ accountId: wazzupProvider.accountId }); + if (this.config.enqueue) { + this.queue.enqueue(() => this.handleMessage({ account, providerId: wazzupProvider.providerId, message })); + } else { + await this.handleMessage({ account, providerId: wazzupProvider.providerId, message }); + } + } + } + } + } + + return 'ok'; + } + + private async handleMessage({ + account, + providerId, + message, + }: { + account: Account; + providerId: number; + message: WazzupMessage; + }) { + this.logger.debug(`Handle message: ${JSON.stringify(message)}`); + try { + const chatUser = await this.getChatUser({ accountId: account.id, providerId, message }); + if (chatUser) { + const fileIds = await this.getMessageFileIds(account.id, message.contentUri); + await this.chatMessageService.createExternal(account, chatUser, message.text ?? '', message.messageId, fileIds); + } + } catch (error) { + this.logger.error(`Handle message error`, (error as Error)?.stack); + } + } + + private async getChatUser({ + accountId, + providerId, + message, + }: { + accountId: number; + providerId: number; + message: WazzupMessage; + }): Promise { + const userExternalId = ( + message.contact?.username || + message.contact?.phone || + message.avitoProfileId || + message.authorName || + '' + ).trim(); + const userName = (message.contact.name || message.contact.username || message.authorName || '').trim(); + const [firstName, lastName] = splitByFirstSpace(userName); + + return this.chatProviderService.getChatUserExternal({ + accountId, + providerId, + chatExternalId: this.formatChatExternalId(message), + externalUserDto: { + externalId: userExternalId, + firstName, + lastName, + avatarUrl: message.contact.avatarUri, + phone: message.contact.phone, + }, + }); + } + + async notifyChatUsers( + account: Account, + provider: ChatProvider, + chat: Chat, + type: MultichatEventType, + message: ChatMessage, + users: ChatUser[], + ): Promise { + const wazzupProvider = await this.repository.findOneBy({ accountId: account.id, providerId: provider.id }); + wazzupProvider.provider = provider; + + switch (type) { + case MultichatEventType.ChatMessageCreated: + this.sendMessage(account, wazzupProvider, chat, message, users); + break; + } + } + + async createChatExternalId(accountId: number, providerId: number, userExternalId: string): Promise { + const provider = await this.repository.findOneBy({ accountId, providerId }); + const chatId = this.formatChatId({ provider, userExternalId }); + return this.formatChatExternalId({ chatType: chatTypeMap[provider.transport], chatId }); + } + + private async setWebhook(account: Account, apiKey: string): Promise { + const webhooksUri = this.urlGenerator.createUrl({ route: WEBHOOK_PATH, subdomain: account.subdomain }); + try { + const response$ = this.httpService.patch( + WazzupUrls.webhooks(), + { webhooksUri, subscriptions: { messagesAndStatuses: true } }, + { headers: { Authorization: `Bearer ${apiKey}` } }, + ); + const response = await lastValueFrom(response$); + return !!response.data; + } catch (e) { + this.logger.error(`Connect webhook error`, (e as Error)?.stack); + return false; + } + } + + private async sendMessage( + account: Account, + provider: ChatProviderWazzup, + chat: Chat, + message: ChatMessage, + users: ChatUser[], + ): Promise { + const fileUrls = + message.files && message.files.length > 0 + ? message.files.map((file) => this.storageUrlService.getTemporaryUrl(file.fileId, account.subdomain)) + : []; + + for (const user of users) { + if (user.externalUser) { + const { chatType, chatId, tgPhone } = this.parseChatExternalId(chat.externalId); + + if (message.text) { + try { + const response$ = this.httpService.post( + WazzupUrls.message(), + new WazzupSendMessage({ + channelId: provider.channelId, + chatType, + chatId, + phone: tgPhone, + text: message.text, + }), + { headers: { Authorization: `Bearer ${provider.apiKey}` } }, + ); + const response = await lastValueFrom(response$); + const data = response.data as WazzupSendMessageResponse; + if (tgPhone && data?.chatId) { + const externalId = this.formatChatExternalId({ chatType, chatId: data.chatId }); + const existingChat = await this.chatService.findOne({ + accountId: account.id, + filter: { providerId: provider.providerId, externalId }, + }); + if (existingChat) { + await this.chatService.mergeChat(account, chat.id, existingChat.id); + } else { + await this.chatService.updateExternalId(account.id, chat.id, { externalId }); + } + } + } catch (e) { + this.logger.error(`Send message error`, (e as Error)?.stack); + } + } + + for (const fileUrl of fileUrls) { + try { + const response$ = this.httpService.post( + WazzupUrls.message(), + new WazzupSendMessage({ channelId: provider.channelId, chatType, chatId, contentUri: fileUrl }), + { headers: { Authorization: `Bearer ${provider.apiKey}` } }, + ); + await lastValueFrom(response$); + } catch (e) { + this.logger.error(`Send file error`, (e as Error)?.stack); + } + } + } + } + } + + async sendDirectMessage({ + accountId, + providerId, + phone, + message, + }: { + accountId: number; + providerId: number; + phone: string; + message: string; + }): Promise { + this.logger.debug(`sendDirectMessage: ${JSON.stringify({ accountId, providerId, phone, message })}`); + const provider = await this.repository.findOneBy({ accountId, providerId }); + const phoneNumber = phone.startsWith('+') ? phone.slice(1) : phone; + const wazzupMessage = new WazzupSendMessage({ + channelId: provider.channelId, + chatType: chatTypeMap[provider.transport], + phone: phoneNumber, + text: message, + }); + this.logger.debug(`sendDirectMessage wazzupMessage: ${JSON.stringify(wazzupMessage)}`); + try { + const { data } = await lastValueFrom( + this.httpService.post(WazzupUrls.message(), wazzupMessage, { + headers: { Authorization: `Bearer ${provider.apiKey}` }, + }), + ); + this.logger.debug(`sendDirectMessage response data: ${JSON.stringify(data)}`); + } catch (e) { + this.logger.error(`Send message error`, (e as Error)?.stack); + } + } + + private async getMessageFileIds(accountId: number, contentUri: string | null): Promise { + if (!contentUri) { + return null; + } + + const fileInfo = await this.storageService.storeExternalFile(accountId, null, contentUri); + return fileInfo ? [fileInfo.id] : []; + } + + private formatChatId({ provider, userExternalId }: { provider: ChatProviderWazzup; userExternalId: string }) { + const externalId = userExternalId.startsWith('+') ? userExternalId.slice(1) : userExternalId; + return provider.transport === WazzupTransport.Tgapi ? `${tgapiDirectPrefix}${externalId}` : externalId; + } + + private formatChatExternalId({ chatType, chatId }: { chatType: WazzupChatType; chatId: string }): string { + return `${chatType}|${chatId}`; + } + private parseChatExternalId(externalId: string): { + chatType: WazzupChatType; + chatId: string | undefined; + tgPhone: string | undefined; + } { + const index = externalId.indexOf('|'); + const chatType = externalId.substring(0, index); + const rest = externalId.substring(index + 1); + + const tgPhone = rest.startsWith(tgapiDirectPrefix) ? rest.slice(tgapiDirectPrefix.length) : undefined; + const chatId = rest.startsWith(tgapiDirectPrefix) ? undefined : rest; + + return { chatType: chatType as WazzupChatType, chatId, tgPhone }; + } +} diff --git a/backend/src/modules/notification/common/events/index.ts b/backend/src/modules/notification/common/events/index.ts new file mode 100644 index 0000000..2693fe9 --- /dev/null +++ b/backend/src/modules/notification/common/events/index.ts @@ -0,0 +1,2 @@ +export * from './notification'; +export * from './notification-event-type.enum'; diff --git a/backend/src/modules/notification/common/events/notification-event-type.enum.ts b/backend/src/modules/notification/common/events/notification-event-type.enum.ts new file mode 100644 index 0000000..08ebed2 --- /dev/null +++ b/backend/src/modules/notification/common/events/notification-event-type.enum.ts @@ -0,0 +1,4 @@ +export enum NotificationEventType { + NOTIFICATION_CREATED = 'notification:new', + NOTIFICATION_UNSEEN = 'notification:unseen', +} diff --git a/backend/src/modules/notification/common/events/notification/index.ts b/backend/src/modules/notification/common/events/notification/index.ts new file mode 100644 index 0000000..257cafb --- /dev/null +++ b/backend/src/modules/notification/common/events/notification/index.ts @@ -0,0 +1 @@ +export * from './notification-unseen.event'; diff --git a/backend/src/modules/notification/common/events/notification/notification-unseen.event.ts b/backend/src/modules/notification/common/events/notification/notification-unseen.event.ts new file mode 100644 index 0000000..ab9cb44 --- /dev/null +++ b/backend/src/modules/notification/common/events/notification/notification-unseen.event.ts @@ -0,0 +1,11 @@ +export class NotificationUnseenEvent { + accountId: number; + userId: number; + unseenCount: number; + + constructor({ accountId, userId, unseenCount }: NotificationUnseenEvent) { + this.accountId = accountId; + this.userId = userId; + this.unseenCount = unseenCount; + } +} diff --git a/backend/src/modules/notification/common/index.ts b/backend/src/modules/notification/common/index.ts new file mode 100644 index 0000000..7981d6b --- /dev/null +++ b/backend/src/modules/notification/common/index.ts @@ -0,0 +1 @@ +export * from './events'; diff --git a/backend/src/modules/notification/notification-settings/dto/notification-settings.dto.ts b/backend/src/modules/notification/notification-settings/dto/notification-settings.dto.ts new file mode 100644 index 0000000..d1191c7 --- /dev/null +++ b/backend/src/modules/notification/notification-settings/dto/notification-settings.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { NotificationTypeSettingsDto } from './notification-type-settings.dto'; + +export class NotificationSettingsDto { + @ApiProperty() + enablePopup: boolean; + + @ApiProperty() + types: NotificationTypeSettingsDto[]; + + constructor(enablePopup: boolean, types: NotificationTypeSettingsDto[]) { + this.enablePopup = enablePopup; + this.types = types; + } +} diff --git a/backend/src/modules/notification/notification-settings/dto/notification-type-settings.dto.ts b/backend/src/modules/notification/notification-settings/dto/notification-type-settings.dto.ts new file mode 100644 index 0000000..75458c7 --- /dev/null +++ b/backend/src/modules/notification/notification-settings/dto/notification-type-settings.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { NotificationType } from '../../notification/enums'; + +export class NotificationTypeSettingsDto { + @ApiProperty() + type: NotificationType; + + @ApiProperty() + isEnabled: boolean; + + @ApiProperty({ nullable: true }) + objectId: number | null; + + @ApiProperty({ nullable: true }) + before: number | null; + + @ApiProperty({ nullable: true }) + followUserIds: number[] | null; + + constructor( + type: NotificationType, + isEnabled: boolean, + objectId: number | null, + before: number | null, + followUserIds: number[] | null, + ) { + this.type = type; + this.isEnabled = isEnabled; + this.objectId = objectId; + this.before = before; + this.followUserIds = followUserIds; + } +} diff --git a/backend/src/modules/notification/notification-settings/entities/notification-settings.entity.ts b/backend/src/modules/notification/notification-settings/entities/notification-settings.entity.ts new file mode 100644 index 0000000..7e7bf68 --- /dev/null +++ b/backend/src/modules/notification/notification-settings/entities/notification-settings.entity.ts @@ -0,0 +1,42 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { NotificationSettingsDto } from '../dto/notification-settings.dto'; +import { NotificationTypeSettingsDto } from '../dto/notification-type-settings.dto'; + +@Entity() +export class NotificationSettings { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + userId: number; + + @Column() + enablePopup: boolean; + + @Column() + accountId: number; + + constructor(accountId: number, userId: number, enablePopup: boolean) { + this.accountId = accountId; + this.userId = userId; + this.enablePopup = enablePopup; + } + + public static create(accountId: number, userId: number, dto: NotificationSettingsDto): NotificationSettings { + return new NotificationSettings(accountId, userId, dto.enablePopup); + } + + public update(dto: NotificationSettingsDto): NotificationSettings { + this.enablePopup = dto.enablePopup; + return this; + } + + public static getDefaultEnablePopup(): boolean { + return true; + } + + public toDto(types: NotificationTypeSettingsDto[]): NotificationSettingsDto { + return new NotificationSettingsDto(this.enablePopup, types); + } +} diff --git a/backend/src/modules/notification/notification-settings/entities/notification-type-follow-user.entity.ts b/backend/src/modules/notification/notification-settings/entities/notification-type-follow-user.entity.ts new file mode 100644 index 0000000..8a3c38e --- /dev/null +++ b/backend/src/modules/notification/notification-settings/entities/notification-type-follow-user.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class NotificationTypeFollowUser { + @PrimaryColumn() + typeId: number; + + @PrimaryColumn() + userId: number; + + @Column() + accountId: number; + + constructor(typeId: number, userId: number, accountId: number) { + this.typeId = typeId; + this.userId = userId; + this.accountId = accountId; + } +} diff --git a/backend/src/modules/notification/notification-settings/entities/notification-type-settings.entity.ts b/backend/src/modules/notification/notification-settings/entities/notification-type-settings.entity.ts new file mode 100644 index 0000000..d1c11f3 --- /dev/null +++ b/backend/src/modules/notification/notification-settings/entities/notification-type-settings.entity.ts @@ -0,0 +1,79 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { NotificationType } from '../../notification/enums'; +import { NotificationTypeSettingsDto } from '../dto/notification-type-settings.dto'; + +@Entity() +export class NotificationTypeSettings { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + settingsId: number; + + @Column() + type: NotificationType; + + @Column() + isEnabled: boolean; + + @Column({ nullable: true }) + objectId: number | null; + + @Column({ nullable: true }) + before: number | null; // in seconds + + @Column() + accountId: number; + + constructor( + accountId: number, + settingsId: number, + type: NotificationType, + isEnabled: boolean, + objectId: number | null, + before: number | null, + ) { + this.accountId = accountId; + this.settingsId = settingsId; + this.type = type; + this.isEnabled = isEnabled; + this.objectId = objectId; + this.before = before; + } + + public static fromDto( + accountId: number, + settingsId: number, + dto: NotificationTypeSettingsDto, + ): NotificationTypeSettings { + return new NotificationTypeSettings(accountId, settingsId, dto.type, dto.isEnabled, dto.objectId, dto.before); + } + + public static createDefault( + accountId: number, + settingsId: number, + type: NotificationType, + objectId: number | null = null, + ): NotificationTypeSettings { + return new NotificationTypeSettings( + accountId, + settingsId, + type, + NotificationTypeSettings.getDefaultEnabled(type), + objectId, + NotificationTypeSettings.getDefaultBefore(type), + ); + } + + public static getDefaultEnabled(type: NotificationType): boolean { + return !(type === NotificationType.ACTIVITY_OVERDUE_EMPLOYEE || type === NotificationType.TASK_OVERDUE_EMPLOYEE); + } + public static getDefaultBefore(type: NotificationType): number | null { + return type === NotificationType.ACTIVITY_BEFORE_START || type === NotificationType.TASK_BEFORE_START ? 3600 : null; + } + + public toDto(followUserIds: number[] | null): NotificationTypeSettingsDto { + return new NotificationTypeSettingsDto(this.type, this.isEnabled, this.objectId, this.before, followUserIds); + } +} diff --git a/backend/src/modules/notification/notification-settings/notification-settings.controller.ts b/backend/src/modules/notification/notification-settings/notification-settings.controller.ts new file mode 100644 index 0000000..bae09fb --- /dev/null +++ b/backend/src/modules/notification/notification-settings/notification-settings.controller.ts @@ -0,0 +1,31 @@ +import { Body, Controller, Get, Put } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { NotificationSettingsService } from './notification-settings.service'; +import { NotificationSettingsDto } from './dto/notification-settings.dto'; + +@ApiTags('notification') +@Controller('/notifications/settings') +@JwtAuthorized({ prefetch: { user: true } }) +export class NotificationSettingsController { + constructor(private readonly service: NotificationSettingsService) {} + + @ApiCreatedResponse({ description: 'Notification settings for user', type: NotificationSettingsDto }) + @Get() + public async getSettings(@CurrentAuth() { accountId, user }: AuthData): Promise { + return this.service.getSettings(accountId, user); + } + + @ApiCreatedResponse({ description: 'Notification settings for user', type: NotificationSettingsDto }) + @Put() + public async updateSettings( + @CurrentAuth() { accountId, user }: AuthData, + @Body() dto: NotificationSettingsDto, + ): Promise { + return this.service.updateSettings(accountId, user, dto); + } +} diff --git a/backend/src/modules/notification/notification-settings/notification-settings.service.ts b/backend/src/modules/notification/notification-settings/notification-settings.service.ts new file mode 100644 index 0000000..c80361d --- /dev/null +++ b/backend/src/modules/notification/notification-settings/notification-settings.service.ts @@ -0,0 +1,200 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { IamEventType, UserCreatedEvent } from '@/modules/iam/common'; +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { EntityTypeService } from '@/CRM/entity-type/entity-type.service'; + +import { NotificationType } from '../notification/enums'; + +import { NotificationSettings } from './entities/notification-settings.entity'; +import { NotificationTypeSettings } from './entities/notification-type-settings.entity'; +import { NotificationTypeFollowUser } from './entities/notification-type-follow-user.entity'; + +import { NotificationSettingsDto } from './dto/notification-settings.dto'; +import { NotificationTypeSettingsDto } from './dto/notification-type-settings.dto'; + +@Injectable() +export class NotificationSettingsService { + constructor( + @InjectRepository(NotificationSettings) + private readonly repositorySettings: Repository, + @InjectRepository(NotificationTypeSettings) + private readonly repositoryTypeSettings: Repository, + @InjectRepository(NotificationTypeFollowUser) + private readonly repositoryFollowUser: Repository, + private readonly userService: UserService, + @Inject(forwardRef(() => EntityTypeService)) + private readonly entityTypeService: EntityTypeService, + ) {} + + @OnEvent(IamEventType.UserCreated, { async: true }) + public async handleUserCreatedEvent(event: UserCreatedEvent) { + const user = await this.userService.findOne({ accountId: event.accountId, id: event.userId }); + await this.createDefaultSettings(event.accountId, user); + } + + public async createDefaultSettings(accountId: number, user: User) { + const settings = await this.repositorySettings.save(new NotificationSettings(accountId, user.id, true)); + const typeSettings: NotificationTypeSettings[] = []; + await this.createMissedTypes(accountId, user, settings.id, typeSettings); + return { settings, typeSettings }; + } + private async createMissedTypes( + accountId: number, + user: User, + settingsId: number, + typeSettings: NotificationTypeSettings[] = [], + ) { + for (const typeString in NotificationType) { + const type = NotificationType[typeString] as NotificationType; + if (type !== NotificationType.ENTITY_NEW && !typeSettings.find((t) => t.type === type)) { + typeSettings.push(await this.createDefaultTypeSettings(accountId, settingsId, type)); + } + } + + const entityTypes = await this.entityTypeService.getAccessibleForUser(accountId, user); + const deletedEntityTypes: number[] = []; + for (const typeSetting of typeSettings.filter((ts) => ts.type === NotificationType.ENTITY_NEW)) { + if (!entityTypes.some((et) => et.id === typeSetting.objectId)) { + deletedEntityTypes.push(typeSetting.id); + } + } + for (const id of deletedEntityTypes) { + const index = typeSettings.findIndex((ts) => ts.id === id); + if (index !== -1) { + typeSettings.splice(index, 1); + } + } + + for (const et of entityTypes) { + if (!typeSettings.find((t) => t.type === NotificationType.ENTITY_NEW && t.objectId === et.id)) { + typeSettings.push( + await this.createDefaultTypeSettings(accountId, settingsId, NotificationType.ENTITY_NEW, et.id), + ); + } + } + } + private async createDefaultTypeSettings( + accountId: number, + settingsId: number, + type: NotificationType, + objectId: number | null = null, + ): Promise { + return await this.repositoryTypeSettings.save( + NotificationTypeSettings.createDefault(accountId, settingsId, type, objectId), + ); + } + + public async getSettings(accountId: number, user: User): Promise { + const current = await this.repositorySettings.findOneBy({ accountId, userId: user.id }); + if (current) { + const typeSettings = await this.repositoryTypeSettings.find({ + where: { settingsId: current.id }, + order: { id: 'ASC' }, + }); + await this.createMissedTypes(accountId, user, current.id, typeSettings); + return current.toDto(await this.convertToTypeSettingsDto(typeSettings)); + } else { + const { settings, typeSettings } = await this.createDefaultSettings(accountId, user); + return settings.toDto(await this.convertToTypeSettingsDto(typeSettings)); + } + } + private async convertToTypeSettingsDto(typeSettings: NotificationTypeSettings[]) { + const typeSettingsDtos: NotificationTypeSettingsDto[] = []; + for (const ts of typeSettings) { + const followUsers = await this.repositoryFollowUser.findBy({ typeId: ts.id }); + typeSettingsDtos.push(ts.toDto(followUsers.map((fu) => fu.userId))); + } + return typeSettingsDtos; + } + + public async updateSettings( + accountId: number, + user: User, + dto: NotificationSettingsDto, + ): Promise { + let settings = await this.repositorySettings.findOneBy({ accountId, userId: user.id }); + if (settings) { + await this.repositorySettings.save(settings.update(dto)); + } else { + settings = await this.repositorySettings.save(NotificationSettings.create(accountId, user.id, dto)); + } + + await this.repositoryTypeSettings.delete({ settingsId: settings.id }); + for (const type of dto.types) { + const typeSettings = await this.repositoryTypeSettings.save( + NotificationTypeSettings.fromDto(accountId, settings.id, type), + ); + if (type.followUserIds && type.followUserIds.length > 0) { + await this.repositoryFollowUser.insert( + type.followUserIds.map((fu) => new NotificationTypeFollowUser(typeSettings.id, fu, accountId)), + ); + } + } + + return await this.getSettings(accountId, user); + } + + public async checkEnabled( + accountId: number, + userId: number, + type: NotificationType, + objectId: number | null = null, + ): Promise<{ isEnabled: boolean; enablePopup: boolean }> { + const settings = await this.repositorySettings.findOneBy({ accountId, userId }); + if (settings) { + const typeSettings = await this.repositoryTypeSettings.findOneBy({ + settingsId: settings.id, + type, + objectId: objectId ?? undefined, + }); + if (typeSettings) { + return { isEnabled: typeSettings.isEnabled, enablePopup: settings.enablePopup }; + } + } + return { + isEnabled: NotificationTypeSettings.getDefaultEnabled(type), + enablePopup: NotificationSettings.getDefaultEnablePopup(), + }; + } + + public async getNotificationSettingsWithBefore(type: NotificationType, offset: number, limit: number) { + const result: { userId: number; before: number }[] = await this.repositoryTypeSettings + .createQueryBuilder('nts') + .select('ns.user_id', 'userId') + .addSelect('nts.before', 'before') + .leftJoin('notification_settings', 'ns', 'nts.settings_id = ns.id') + .where('nts.type = :type', { type }) + .andWhere('nts.is_enabled = true') + .offset(offset) + .limit(limit) + .getRawMany(); + return result; + } + + public async getNotificationSettingsWithFollow(type: NotificationType, offset: number, limit: number) { + const result: { userId: number; typeId: number }[] = await this.repositoryTypeSettings + .createQueryBuilder('nts') + .select('ns.user_id', 'userId') + .addSelect('nts.id', 'typeId') + .leftJoin('notification_settings', 'ns', 'nts.settings_id = ns.id') + .where('nts.type = :type', { type }) + .andWhere('nts.is_enabled = true') + .offset(offset) + .limit(limit) + .getRawMany(); + return await Promise.all( + result.map(async (r) => { + return { + ...r, + followUserIds: (await this.repositoryFollowUser.findBy({ typeId: r.typeId })).map((f) => f.userId), + }; + }), + ); + } +} diff --git a/backend/src/modules/notification/notification.module.ts b/backend/src/modules/notification/notification.module.ts new file mode 100644 index 0000000..7c2d847 --- /dev/null +++ b/backend/src/modules/notification/notification.module.ts @@ -0,0 +1,35 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { Notification } from './notification/entities/notification.entity'; +import { NotificationService } from './notification/notification.service'; +import { NotificationEventHandler } from './notification/notification-event.handler'; +import { NotificationScheduler } from './notification/notification-scheduler'; +import { NotificationController } from './notification/notification.controller'; + +import { NotificationSettings } from './notification-settings/entities/notification-settings.entity'; +import { NotificationTypeSettings } from './notification-settings/entities/notification-type-settings.entity'; +import { NotificationTypeFollowUser } from './notification-settings/entities/notification-type-follow-user.entity'; +import { NotificationSettingsService } from './notification-settings/notification-settings.service'; +import { NotificationSettingsController } from './notification-settings/notification-settings.controller'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + Notification, + NotificationSettings, + NotificationTypeSettings, + NotificationTypeFollowUser, + ]), + IAMModule, + CrmModule, + EntityInfoModule, + ], + providers: [NotificationService, NotificationScheduler, NotificationEventHandler, NotificationSettingsService], + controllers: [NotificationController, NotificationSettingsController], +}) +export class NotificationModule {} diff --git a/backend/src/modules/notification/notification/dto/create-notification.dto.ts b/backend/src/modules/notification/notification/dto/create-notification.dto.ts new file mode 100644 index 0000000..fce1ecc --- /dev/null +++ b/backend/src/modules/notification/notification/dto/create-notification.dto.ts @@ -0,0 +1,47 @@ +import { NotificationType } from '../enums'; + +export class CreateNotificationDto { + accountId: number; + + userId: number; + + type: NotificationType; + + objectId: number; + + entityId: number | null; + + fromUser: number | null; + + title: string | null; + + description: string | null; + + startsIn: number | null; + + constructor( + accountId: number, + userId: number, + type: NotificationType, + objectId: number, + entityId: number | null, + fromUser: number | null, + title: string | null, + description: string | null, + startsIn: number | null = null, + ) { + this.accountId = accountId; + this.userId = userId; + this.type = type; + this.objectId = objectId; + this.entityId = entityId; + this.fromUser = fromUser; + this.title = title; + this.description = description; + this.startsIn = startsIn; + } + + setStartsIn(startsIn: number | null) { + this.startsIn = startsIn; + } +} diff --git a/backend/src/modules/notification/notification/dto/index.ts b/backend/src/modules/notification/notification/dto/index.ts new file mode 100644 index 0000000..424aed5 --- /dev/null +++ b/backend/src/modules/notification/notification/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-notification.dto'; +export * from './notification.dto'; +export * from './notifications-result.dto'; diff --git a/backend/src/modules/notification/notification/dto/notification.dto.ts b/backend/src/modules/notification/notification/dto/notification.dto.ts new file mode 100644 index 0000000..73f647c --- /dev/null +++ b/backend/src/modules/notification/notification/dto/notification.dto.ts @@ -0,0 +1,66 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { NotificationType } from '../enums'; + +export class NotificationDto { + @ApiProperty() + id: number; + + @ApiProperty() + userId: number; + + @ApiProperty() + type: NotificationType; + + @ApiProperty() + objectId: number; + + @ApiProperty() + entityInfo: EntityInfoDto | null; + + @ApiProperty() + fromUser: number | null; + + @ApiProperty() + title: string | null; + + @ApiProperty() + description: string | null; + + @ApiProperty() + isSeen: boolean; + + @ApiProperty() + startsIn: number | null; + + @ApiProperty() + createdAt: string; + + constructor( + id: number, + userId: number, + type: NotificationType, + objectId: number, + entityInfo: EntityInfoDto | null, + fromUser: number | null, + title: string | null, + description: string, + isSeen: boolean, + startsIn: number | null, + createdAt: string, + ) { + this.id = id; + this.userId = userId; + this.type = type; + this.objectId = objectId; + this.entityInfo = entityInfo; + this.fromUser = fromUser; + this.title = title; + this.description = description; + this.isSeen = isSeen; + this.startsIn = startsIn; + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/notification/notification/dto/notifications-result.dto.ts b/backend/src/modules/notification/notification/dto/notifications-result.dto.ts new file mode 100644 index 0000000..35ccf60 --- /dev/null +++ b/backend/src/modules/notification/notification/dto/notifications-result.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { NotificationDto } from './notification.dto'; + +export class NotificationsResult { + @ApiProperty() + meta: PagingMeta; + + @ApiProperty() + notifications: NotificationDto[]; + + constructor(notifications: NotificationDto[], meta: PagingMeta) { + this.notifications = notifications; + this.meta = meta; + } +} diff --git a/backend/src/modules/notification/notification/entities/index.ts b/backend/src/modules/notification/notification/entities/index.ts new file mode 100644 index 0000000..b95a361 --- /dev/null +++ b/backend/src/modules/notification/notification/entities/index.ts @@ -0,0 +1 @@ +export * from './notification.entity'; diff --git a/backend/src/modules/notification/notification/entities/notification.entity.ts b/backend/src/modules/notification/notification/entities/notification.entity.ts new file mode 100644 index 0000000..d9d2210 --- /dev/null +++ b/backend/src/modules/notification/notification/entities/notification.entity.ts @@ -0,0 +1,104 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { CreateNotificationDto, NotificationDto } from '../dto'; +import { NotificationType } from '../enums'; + +@Entity() +export class Notification { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + userId: number; + + @Column() + type: NotificationType; + + @Column() + objectId: number; + + @Column({ nullable: true }) + entityId: number | null; + + @Column({ nullable: true }) + fromUser: number | null; + + @Column({ nullable: true }) + title: string | null; + + @Column({ nullable: true }) + description: string | null; + + @Column() + isSeen: boolean; + + @Column({ nullable: true }) + startsIn: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + userId: number, + type: NotificationType, + objectId: number, + entityId: number | null, + fromUser: number | null, + title: string | null, + description: string | null, + isSeen: boolean, + startsIn: number | null, + createdAt?: Date, + ) { + this.accountId = accountId; + this.userId = userId; + this.type = type; + this.objectId = objectId; + this.entityId = entityId; + this.fromUser = fromUser; + this.title = title; + this.description = description; + this.isSeen = isSeen; + this.startsIn = startsIn; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public static fromDto(dto: CreateNotificationDto) { + return new Notification( + dto.accountId, + dto.userId, + dto.type, + dto.objectId, + dto.entityId, + dto.fromUser, + dto.title, + dto.description, + false, + dto.startsIn, + ); + } + + public toDto(entityInfo: EntityInfoDto | null): NotificationDto { + return new NotificationDto( + this.id, + this.userId, + this.type, + this.objectId, + entityInfo, + this.fromUser, + this.title, + this.description, + this.isSeen, + this.startsIn, + this.createdAt.toISOString(), + ); + } +} diff --git a/backend/src/modules/notification/notification/enums/index.ts b/backend/src/modules/notification/notification/enums/index.ts new file mode 100644 index 0000000..34c7f9c --- /dev/null +++ b/backend/src/modules/notification/notification/enums/index.ts @@ -0,0 +1 @@ +export * from './notification-type.enum'; diff --git a/backend/src/modules/notification/notification/enums/notification-type.enum.ts b/backend/src/modules/notification/notification/enums/notification-type.enum.ts new file mode 100644 index 0000000..bca9fb0 --- /dev/null +++ b/backend/src/modules/notification/notification/enums/notification-type.enum.ts @@ -0,0 +1,17 @@ +export enum NotificationType { + TASK_NEW = 'task_new', + TASK_OVERDUE = 'task_overdue', + TASK_BEFORE_START = 'task_before_start', + TASK_OVERDUE_EMPLOYEE = 'task_overdue_employee', + ACTIVITY_NEW = 'activity_new', + ACTIVITY_OVERDUE = 'activity_overdue', + ACTIVITY_BEFORE_START = 'activity_before_start', + ACTIVITY_OVERDUE_EMPLOYEE = 'activity_overdue_employee', + TASK_COMMENT_NEW = 'task_comment_new', + CHAT_MESSAGE_NEW = 'chat_message_new', + MAIL_MESSAGE_NEW = 'mail_new', + ENTITY_NOTE_NEW = 'entity_note_new', + ENTITY_NEW = 'entity_new', + ENTITY_RESPONSIBLE_CHANGE = 'entity_responsible_change', + ENTITY_IMPORT_COMPLETED = 'entity_import_completed', +} diff --git a/backend/src/modules/notification/notification/notification-event.handler.ts b/backend/src/modules/notification/notification/notification-event.handler.ts new file mode 100644 index 0000000..ef32ce1 --- /dev/null +++ b/backend/src/modules/notification/notification/notification-event.handler.ts @@ -0,0 +1,170 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { convert } from 'html-to-text'; + +import { UserNotification } from '@/common'; +import { + ActivityCreatedEvent, + CrmEventType, + EntityCreatedEvent, + EntityImportEvent, + EntityOwnerChangedEvent, + NoteCreatedEvent, + TaskCommentCreatedEvent, + TaskCreatedEvent, +} from '@/CRM/common'; +import { MailEventType, MailMessageReceivedEvent } from '@/Mailing/common'; + +import { CreateNotificationDto } from './dto'; +import { NotificationType } from './enums'; +import { NotificationService } from './notification.service'; + +@Injectable() +export class NotificationEventHandler { + constructor(private readonly notificationService: NotificationService) {} + + @OnEvent(CrmEventType.ActivityCreated, { async: true }) + public async onActivityCreated(event: ActivityCreatedEvent) { + if (event.createdBy !== event.ownerId) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.ACTIVITY_NEW, + event.activityId, + event.entityId, + event.createdBy, + null, + convert(event.activityText), + ), + ); + } + } + + @OnEvent(CrmEventType.TaskCreated, { async: true }) + public async onTaskCreated(event: TaskCreatedEvent) { + if (event.createdBy !== event.ownerId) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.TASK_NEW, + event.taskId, + event.entityId, + event.createdBy, + event.taskTitle, + convert(event.taskText), + ), + ); + } + } + + @OnEvent(CrmEventType.TaskCommentCreated, { async: true }) + public async onTaskCommentCreated(event: TaskCommentCreatedEvent) { + if (event.createdBy !== event.ownerId) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.TASK_COMMENT_NEW, + event.taskId, + event.entityId, + event.createdBy, + event.taskTitle, + convert(event.taskComment), + ), + ); + } + } + + @OnEvent(CrmEventType.EntityCreated, { async: true }) + public async onEntityCreated(event: EntityCreatedEvent) { + if (event.userNotification === UserNotification.Suppressed) { + return; + } + if (event.userNotification === UserNotification.Forced || event.createdBy !== event.ownerId) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.ENTITY_NEW, + event.entityTypeId, + event.entityId, + event.createdBy, + event.entityName, + null, + ), + ); + } + } + + @OnEvent(CrmEventType.EntityOwnerChanged, { async: true }) + public async onEntityOwnerChanged(event: EntityOwnerChangedEvent) { + if (event.changedBy !== event.ownerId) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.ENTITY_RESPONSIBLE_CHANGE, + event.entityTypeId, + event.entityId, + event.changedBy, + event.entityName, + null, + ), + ); + } + } + + @OnEvent(CrmEventType.EntityImportCompleted, { async: true }) + public async onEntityImportCompleted(event: EntityImportEvent) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.userId, + NotificationType.ENTITY_IMPORT_COMPLETED, + event.entityTypeId, + null, + event.userId, + null, + `Import of '${event.fileName}' is completed. Created ${event.totalCount} ${event.entityTypeName}.`, + ), + ); + } + + @OnEvent(CrmEventType.NoteCreated, { async: true }) + public async onEntityNoteCreated(event: NoteCreatedEvent) { + if (event.createdBy !== event.ownerId) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.ENTITY_NOTE_NEW, + event.noteId, + event.entityId, + event.createdBy, + event.entityName, + convert(event.noteText), + ), + ); + } + } + + @OnEvent(MailEventType.MailMessageReceived, { async: true }) + public async onMailMessageReceived(event: MailMessageReceivedEvent) { + if (event.isInbox) { + await this.notificationService.create( + new CreateNotificationDto( + event.accountId, + event.ownerId, + NotificationType.MAIL_MESSAGE_NEW, + event.messageId, + event.entityId, + null, + event.messageSubject, + event.messageSnippet, + ), + ); + } + } +} diff --git a/backend/src/modules/notification/notification/notification-scheduler.ts b/backend/src/modules/notification/notification/notification-scheduler.ts new file mode 100644 index 0000000..401cd64 --- /dev/null +++ b/backend/src/modules/notification/notification/notification-scheduler.ts @@ -0,0 +1,172 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; + +import { DateUtil } from '@/common'; + +import { ActivityService } from '@/CRM/activity/activity.service'; +import { TaskService } from '@/CRM/task/task.service'; + +import { NotificationSettingsService } from '../notification-settings/notification-settings.service'; + +import { NotificationType } from './enums'; +import { NotificationService } from './notification.service'; + +const PROCESS_LIMIT = 100; + +@Injectable() +export class NotificationScheduler { + private readonly logger = new Logger(NotificationScheduler.name); + constructor( + private notificationService: NotificationService, + private taskService: TaskService, + private activityService: ActivityService, + private notificationSettingsService: NotificationSettingsService, + ) {} + + @Cron(CronExpression.EVERY_MINUTE) + public async notifyTaskOverdue() { + if (process.env.SCHEDULE_NOTIFICATION_TASK_OVERDUE_DISABLE === 'true') return; + this.logger.log('Before: Running task overdue notifications'); + const to = DateUtil.now(); + const from = DateUtil.sub(to, { minutes: 1 }); + + const notifications = await this.taskService.getOverdueNotifications(from, to); + if (notifications && notifications.length > 0) { + notifications.forEach((notification) => this.notificationService.create(notification)); + } + this.logger.log(`After: Running task overdue notifications. Processed: ${notifications.length}`); + } + + @Cron(CronExpression.EVERY_MINUTE) + public async notifyTaskBeforeStart() { + if (process.env.SCHEDULE_NOTIFICATION_TASK_BEFORE_START_DISABLE === 'true') return; + this.logger.log('Before: Running task before start notifications'); + const to = DateUtil.now(); + const from = DateUtil.sub(to, { minutes: 1 }); + let offset = 0; + let processed = false; + do { + const result = await this.notificationSettingsService.getNotificationSettingsWithBefore( + NotificationType.TASK_BEFORE_START, + offset, + PROCESS_LIMIT, + ); + result.forEach(async ({ userId, before }) => { + const currentFrom = DateUtil.add(from, { seconds: before }); + const currentTo = DateUtil.add(to, { seconds: before }); + const notifications = await this.taskService.getBeforeStartNotifications(userId, currentFrom, currentTo); + if (notifications && notifications.length > 0) { + notifications.forEach((notification) => { + notification.setStartsIn(before); + this.notificationService.create(notification); + }); + } + }); + offset += result.length; + processed = result.length >= PROCESS_LIMIT; + } while (processed); + this.logger.log(`After: Running task before start notifications. Processed: ${offset}`); + } + + @Cron(CronExpression.EVERY_MINUTE) + public async notifyTaskOverdueFollow() { + if (process.env.SCHEDULE_NOTIFICATION_TASK_OVERDUE_FOLLOW_DISABLE === 'true') return; + this.logger.log('Before: Running task overdue follow notifications'); + const to = DateUtil.now(); + const from = DateUtil.sub(to, { minutes: 1 }); + let offset = 0; + let processed = false; + do { + const result = await this.notificationSettingsService.getNotificationSettingsWithFollow( + NotificationType.TASK_OVERDUE_EMPLOYEE, + offset, + PROCESS_LIMIT, + ); + result.forEach(async ({ userId, followUserIds }) => { + const notifications = await this.taskService.getOverdueForFollowNotifications(userId, from, to, followUserIds); + if (notifications && notifications.length > 0) { + notifications.forEach((notification) => this.notificationService.create(notification)); + } + }); + offset += result.length; + processed = result.length >= PROCESS_LIMIT; + } while (processed); + this.logger.log(`After: Running task overdue follow notifications. Processed: ${offset}`); + } + + @Cron(CronExpression.EVERY_MINUTE) + public async notifyActivityOverdue() { + if (process.env.SCHEDULE_NOTIFICATION_ACTIVITY_OVERDUE_DISABLE === 'true') return; + this.logger.log('Before: Running activity overdue notifications'); + const to = DateUtil.now(); + const from = DateUtil.sub(to, { minutes: 1 }); + + const notifications = await this.activityService.getOverdueNotifications(from, to); + if (notifications && notifications.length > 0) { + notifications.forEach((notification) => this.notificationService.create(notification)); + } + this.logger.log(`After: Running activity overdue notifications. Processed: ${notifications.length}`); + } + + @Cron(CronExpression.EVERY_MINUTE) + public async notifyActivityBeforeStart() { + if (process.env.SCHEDULE_NOTIFICATION_ACTIVITY_BEFORE_START_DISABLE === 'true') return; + this.logger.log('Before: Running activity before start notifications'); + const to = DateUtil.now(); + const from = DateUtil.sub(to, { minutes: 1 }); + let offset = 0; + let processed = false; + do { + const result = await this.notificationSettingsService.getNotificationSettingsWithBefore( + NotificationType.ACTIVITY_BEFORE_START, + offset, + PROCESS_LIMIT, + ); + result.forEach(async ({ userId, before }) => { + const currentFrom = DateUtil.add(from, { seconds: before }); + const currentTo = DateUtil.add(to, { seconds: before }); + const notifications = await this.activityService.getBeforeStartNotifications(userId, currentFrom, currentTo); + if (notifications && notifications.length > 0) { + notifications.forEach((notification) => { + notification.setStartsIn(before); + this.notificationService.create(notification); + }); + } + }); + offset += result.length; + processed = result.length >= PROCESS_LIMIT; + } while (processed); + this.logger.log(`After: Running activity before start notifications. Processed: ${offset}`); + } + + @Cron(CronExpression.EVERY_MINUTE) + public async notifyActivityOverdueFollow() { + if (process.env.SCHEDULE_NOTIFICATION_ACTIVITY_OVERDUE_FOLLOW_DISABLE === 'true') return; + this.logger.log('Before: Running activity overdue follow notifications'); + const to = DateUtil.now(); + const from = DateUtil.sub(to, { minutes: 1 }); + let offset = 0; + let processed = false; + do { + const result = await this.notificationSettingsService.getNotificationSettingsWithFollow( + NotificationType.ACTIVITY_OVERDUE_EMPLOYEE, + offset, + PROCESS_LIMIT, + ); + result.forEach(async ({ userId, followUserIds }) => { + const notifications = await this.activityService.getOverdueForFollowNotifications( + userId, + from, + to, + followUserIds, + ); + if (notifications && notifications.length > 0) { + notifications.forEach((notification) => this.notificationService.create(notification)); + } + }); + offset += result.length; + processed = result.length >= PROCESS_LIMIT; + } while (processed); + this.logger.log(`After: Running activity overdue follow notifications. Processed: ${offset}`); + } +} diff --git a/backend/src/modules/notification/notification/notification.controller.ts b/backend/src/modules/notification/notification/notification.controller.ts new file mode 100644 index 0000000..4de14a1 --- /dev/null +++ b/backend/src/modules/notification/notification/notification.controller.ts @@ -0,0 +1,45 @@ +import { Controller, Get, Param, Put, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { AuthDataPrefetch } from '@/modules/iam/common/decorators/auth-data-prefetch.decorator'; + +import { NotificationsResult } from './dto'; +import { NotificationService } from './notification.service'; + +@ApiTags('notification') +@Controller('/notifications') +@JwtAuthorized() +export class NotificationController { + constructor(private readonly service: NotificationService) {} + + @AuthDataPrefetch({ user: true }) + @ApiCreatedResponse({ description: 'Notifications for user', type: NotificationsResult }) + @Get() + public async getNotifications( + @CurrentAuth() { accountId, user }: AuthData, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getNotifications(accountId, user, paging); + } + + @ApiCreatedResponse({ description: 'Unread notifications count', type: Number }) + @Get('/unseen-count') + public async getUnseenCount(@CurrentAuth() { accountId, userId }: AuthData): Promise { + return this.service.getUnseenCount(accountId, userId); + } + + @Put('/:id/seen') + public async markSeenNotification(@CurrentAuth() { accountId, userId }: AuthData, @Param('id') id: number) { + return await this.service.markSeenNotification(accountId, userId, id); + } + + @Put('/seen') + public async markSeenAllNotifications(@CurrentAuth() { accountId, userId }: AuthData) { + return await this.service.markSeenAllNotifications(accountId, userId); + } +} diff --git a/backend/src/modules/notification/notification/notification.service.ts b/backend/src/modules/notification/notification/notification.service.ts new file mode 100644 index 0000000..d84088a --- /dev/null +++ b/backend/src/modules/notification/notification/notification.service.ts @@ -0,0 +1,94 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { PagingQuery, PagingMeta } from '@/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; + +import { NotificationEventType, NotificationUnseenEvent } from '../common'; + +import { NotificationSettingsService } from '../notification-settings/notification-settings.service'; + +import { CreateNotificationDto, NotificationsResult, NotificationDto } from './dto'; +import { Notification } from './entities'; +import { NotificationType } from './enums'; + +@Injectable() +export class NotificationService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Notification) + private readonly repository: Repository, + private readonly notificationSettingsService: NotificationSettingsService, + private readonly entityInfoService: EntityInfoService, + private readonly userService: UserService, + ) {} + + public async create(dto: CreateNotificationDto) { + const { isEnabled, enablePopup } = await this.notificationSettingsService.checkEnabled( + dto.accountId, + dto.userId, + dto.type, + dto.type === NotificationType.ENTITY_NEW ? dto.objectId : null, + ); + + if (isEnabled) { + const notification = await this.repository.save(Notification.fromDto(dto)); + if (enablePopup) { + const user = await this.userService.findOne({ accountId: dto.accountId, id: dto.userId }); + const event = await this.convertToDto(notification, user); + this.eventEmitter.emit(NotificationEventType.NOTIFICATION_CREATED, event); + } + } + + const unseenCount = await this.getUnseenCount(dto.accountId, dto.userId); + this.eventEmitter.emit( + NotificationEventType.NOTIFICATION_UNSEEN, + new NotificationUnseenEvent({ accountId: dto.accountId, userId: dto.userId, unseenCount }), + ); + } + + public async getNotifications(accountId: number, user: User, paging: PagingQuery): Promise { + const [notifications, total] = await this.repository.findAndCount({ + where: { accountId, userId: user.id }, + take: paging.take, + skip: paging.skip, + order: { createdAt: 'DESC', id: 'DESC' }, + }); + + const notificationDtos: NotificationDto[] = []; + for (const notification of notifications) { + notificationDtos.push(await this.convertToDto(notification, user)); + } + + return new NotificationsResult(notificationDtos, new PagingMeta(paging.skip + paging.take, total)); + } + + private async convertToDto(notification: Notification, user: User): Promise { + const entityInfo = notification.entityId + ? await this.entityInfoService.findOne({ + accountId: notification.accountId, + user, + entityId: notification.entityId, + }) + : null; + + return notification.toDto(entityInfo); + } + + public async getUnseenCount(accountId: number, userId: number): Promise { + return await this.repository.countBy({ accountId, userId, isSeen: false }); + } + + public async markSeenNotification(accountId: number, userId: number, id: number) { + await this.repository.update({ accountId, userId, id }, { isSeen: true }); + } + + public async markSeenAllNotifications(accountId: number, userId: number) { + await this.repository.update({ accountId, userId }, { isSeen: true }); + } +} diff --git a/backend/src/modules/partner/dto/index.ts b/backend/src/modules/partner/dto/index.ts new file mode 100644 index 0000000..15f1104 --- /dev/null +++ b/backend/src/modules/partner/dto/index.ts @@ -0,0 +1,3 @@ +export * from './partner-lead.dto'; +export * from './partner-login.dto'; +export * from './partner-summary.dto'; diff --git a/backend/src/modules/partner/dto/partner-lead.dto.ts b/backend/src/modules/partner/dto/partner-lead.dto.ts new file mode 100644 index 0000000..6f2eb12 --- /dev/null +++ b/backend/src/modules/partner/dto/partner-lead.dto.ts @@ -0,0 +1,51 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +export class PartnerLeadDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsString() + registrationDate: string; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + paymentDate: string | null; + + @ApiProperty() + @IsNumber() + paymentAmount: number; + + @ApiProperty() + @IsNumber() + partnerBonus: number; + + @ApiProperty() + @IsNumber() + isPaidToPartner: boolean; + + constructor({ + id, + name, + registrationDate, + paymentDate, + paymentAmount, + partnerBonus, + isPaidToPartner, + }: PartnerLeadDto) { + this.id = id; + this.name = name; + this.registrationDate = registrationDate; + this.paymentDate = paymentDate; + this.paymentAmount = paymentAmount; + this.partnerBonus = partnerBonus; + this.isPaidToPartner = isPaidToPartner; + } +} diff --git a/backend/src/modules/partner/dto/partner-login.dto.ts b/backend/src/modules/partner/dto/partner-login.dto.ts new file mode 100644 index 0000000..6582a4a --- /dev/null +++ b/backend/src/modules/partner/dto/partner-login.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class PartnerLoginDto { + @ApiProperty() + @IsString() + email: string; + + @ApiProperty() + @IsNotEmpty() + password: string; +} diff --git a/backend/src/modules/partner/dto/partner-summary.dto.ts b/backend/src/modules/partner/dto/partner-summary.dto.ts new file mode 100644 index 0000000..e609fc0 --- /dev/null +++ b/backend/src/modules/partner/dto/partner-summary.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class PartnerSummaryDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsNumber() + registrationsCount: number; + + @ApiProperty() + @IsNumber() + payingLeadsCount: number; + + @ApiProperty() + @IsNumber() + totalPayments: number; + + @ApiProperty() + @IsNumber() + totalPartnerBonus: number; + + constructor({ id, name, registrationsCount, payingLeadsCount, totalPayments, totalPartnerBonus }: PartnerSummaryDto) { + this.id = id; + this.name = name; + this.registrationsCount = registrationsCount; + this.payingLeadsCount = payingLeadsCount; + this.totalPayments = totalPayments; + this.totalPartnerBonus = totalPartnerBonus; + } +} diff --git a/backend/src/modules/partner/partner.controller.ts b/backend/src/modules/partner/partner.controller.ts new file mode 100644 index 0000000..70a4569 --- /dev/null +++ b/backend/src/modules/partner/partner.controller.ts @@ -0,0 +1,47 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Post } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { Subdomain, TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { UserDto } from '@/modules/iam/user/dto/user.dto'; +import { JwtToken } from '@/modules/iam/authentication/dto/jwt-token'; + +import { PartnerLeadDto, PartnerLoginDto, PartnerSummaryDto } from './dto'; +import { PartnerService } from './partner.service'; + +@ApiTags('partners') +@Controller('partners') +@TransformToDto() +export class PartnerController { + constructor(private readonly service: PartnerService) {} + + @ApiOkResponse({ description: 'Jwt token to auth', type: JwtToken }) + @Post('login') + public async login(@Subdomain() subdomain: string | null, @Body() dto: PartnerLoginDto): Promise { + return this.service.login(subdomain, dto.email, dto.password); + } + + @ApiOkResponse({ description: 'Partner leads', type: [UserDto] }) + @JwtAuthorized() + @Get(':partnerId/user') + public async getUser(@CurrentAuth() { accountId }: AuthData, @Param('partnerId', ParseIntPipe) partnerId: number) { + return this.service.getUser(accountId, partnerId); + } + + @ApiOkResponse({ description: 'Partner leads', type: [PartnerLeadDto] }) + @JwtAuthorized() + @Get(':partnerId/leads') + public async getLeads(@CurrentAuth() { accountId }: AuthData, @Param('partnerId', ParseIntPipe) partnerId: number) { + return this.service.getLeads(accountId, partnerId); + } + + @ApiOkResponse({ description: 'Partner summary', type: PartnerSummaryDto }) + @JwtAuthorized() + @Get(':partnerId/summary') + public async getSummary(@CurrentAuth() { accountId }: AuthData, @Param('partnerId', ParseIntPipe) partnerId: number) { + return this.service.getSummary(accountId, partnerId); + } +} diff --git a/backend/src/modules/partner/partner.module.ts b/backend/src/modules/partner/partner.module.ts new file mode 100644 index 0000000..813aa2f --- /dev/null +++ b/backend/src/modules/partner/partner.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityFieldModule } from '@/modules/entity/entity-field/entity-field.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { PartnerService } from './partner.service'; +import { PartnerController } from './partner.controller'; + +@Module({ + imports: [IAMModule, EntityFieldModule, CrmModule], + providers: [PartnerService], + controllers: [PartnerController], + exports: [PartnerService], +}) +export class PartnerModule {} diff --git a/backend/src/modules/partner/partner.service.ts b/backend/src/modules/partner/partner.service.ts new file mode 100644 index 0000000..3d50307 --- /dev/null +++ b/backend/src/modules/partner/partner.service.ts @@ -0,0 +1,228 @@ +import { Injectable } from '@nestjs/common'; + +import { DateUtil } from '@/common'; + +import { AccountService } from '@/modules/iam/account/account.service'; +import { AuthenticationService } from '@/modules/iam/authentication/authentication.service'; +import { UserDto } from '@/modules/iam/user/dto/user.dto'; +import { UserRole } from '@/modules/iam/common/enums/user-role.enum'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { Field } from '@/modules/entity/entity-field/field/entities/field.entity'; +import { FieldService } from '@/modules/entity/entity-field/field/field.service'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; +import { EntityCategory } from '@/CRM/common'; +import { Entity } from '@/CRM/Model/Entity/Entity'; +import { EntityTypeService } from '@/CRM/entity-type/entity-type.service'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { EntityLinkService } from '@/CRM/entity-link/entity-link.service'; +import { EntityTypeLinkService } from '@/CRM/entity-type-link/entity-type-link.service'; + +import { Partner, PartnerLead, PartnerSummary } from './types'; + +const PartnerEntityFields = { + Password: 'Password', + Ref: 'ref', + Commission: 'Commission %', +}; + +const PartnerDealFields = { + DateOfPayment: 'Date of payment', + PaidToPartner: 'Paid to partner', +}; + +@Injectable() +export class PartnerService { + constructor( + private readonly accountService: AccountService, + private readonly authService: AuthenticationService, + private readonly entityTypeService: EntityTypeService, + private readonly entityTypeLinkService: EntityTypeLinkService, + private readonly entityService: EntityService, + private readonly entityLinkService: EntityLinkService, + private readonly fieldService: FieldService, + private readonly fieldValueService: FieldValueService, + ) {} + + public async login(subdomain: string, email: string, password: string) { + const account = await this.accountService.findOne({ subdomain }); + + if (account) { + const partner = await this.findByFieldValue(account.id, { type: FieldType.Email }, email); + + if (partner && partner.password === password) { + return this.authService.createJwtToken({ + accountId: account.id, + subdomain: account.subdomain, + userId: partner.id, + isPartner: true, + }); + } + } + + return null; + } + + public async getUser(accountId: number, partnerId: number): Promise { + const partner = await this.createPartner(accountId, partnerId); + + return partner + ? { + id: partner.id, + firstName: partner.name, + lastName: null, + email: partner.email, + phone: partner.phone, + role: UserRole.PARTNER, + isActive: true, + isPlatformAdmin: false, + } + : null; + } + + public async linkLeadWithPartner(accountId: number, entityId: number, ref: string): Promise { + const partner = await this.findByFieldValue( + accountId, + { type: FieldType.Text, name: PartnerEntityFields.Ref }, + ref, + ); + if (partner) { + await this.entityLinkService.create({ accountId, sourceId: partner.id, targetId: entityId }); + } + } + + public async getLeads(accountId: number, partnerId: number): Promise { + const leads: PartnerLead[] = []; + + const entity = await this.entityService.findOne(accountId, { entityId: partnerId }); + const partner = await this.createPartner(accountId, entity); + if (partner) { + const entityTypeLinks = await this.entityTypeLinkService.findMany({ accountId, sourceId: entity.entityTypeId }); + for (const entityTypeLink of entityTypeLinks) { + const entityType = await this.entityTypeService.findOne(accountId, { id: entityTypeLink.targetId }); + if (entityType?.entityCategory === EntityCategory.DEAL) { + const dealFields = await this.fieldService.findMany({ accountId, entityTypeId: entityType.id }); + const entityLinks = await this.entityLinkService.findMany({ accountId, sourceId: partner.id }); + for (const entityLink of entityLinks) { + const entity = await this.entityService.findOne(accountId, { entityId: entityLink.targetId }); + if (entity.entityTypeId === entityType.id) { + leads.push(await this.createLead(accountId, entity, dealFields, partner.commission)); + } + } + } + } + } + + return leads.sort((l1, l2) => DateUtil.sort(l1.registrationDate, l2.registrationDate)); + } + + public async getSummary(accountId: number, partnerId: number): Promise { + const partner = await this.createPartner(accountId, partnerId); + if (!partner) { + return null; + } + + const leads = await this.getLeads(accountId, partnerId); + + const payingLeads = leads.filter((lead) => lead.paymentDate !== null); + const totalPayments = payingLeads + .map((lead) => lead.paymentAmount) + .reduce((total: number, currentValue: number) => total + currentValue, 0); + const totalPartnerBonus = payingLeads + .map((lead) => lead.partnerBonus) + .reduce((total: number, currentValue: number) => total + currentValue, 0); + + return new PartnerSummary( + partnerId, + partner.name, + leads.length, + payingLeads.length, + totalPayments, + totalPartnerBonus, + ); + } + + private async findByFieldValue( + accountId: number, + { type, name }: { type: FieldType; name?: string }, + value: string, + ): Promise { + const entityTypes = await this.entityTypeService.findMany(accountId, { category: EntityCategory.PARTNER }); + for (const entityType of entityTypes) { + const refField = await this.fieldService.findOne({ accountId, entityTypeId: entityType.id, type, name }); + if (refField) { + const refFieldValue = await this.fieldValueService.findOne({ accountId, fieldId: refField.id, value }); + if (refFieldValue) { + return this.createPartner(accountId, refFieldValue.entityId); + } + } + } + return null; + } + + private async createPartner(accountId: number, entityOrId: Entity | number): Promise { + const entity = + entityOrId instanceof Entity ? entityOrId : await this.entityService.findOne(accountId, { entityId: entityOrId }); + if (!entity) { + return null; + } + + const fields = await this.fieldService.findMany({ accountId, entityTypeId: entity.entityTypeId }); + const fieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + + const emailField = fields.find((f) => f.type === FieldType.Email); + const phoneField = fields.find((f) => f.type === FieldType.Phone); + const passwordField = fields.find((f) => f.name.toLowerCase() === PartnerEntityFields.Password.toLowerCase()); + const refField = fields.find((f) => f.name.toLowerCase() === PartnerEntityFields.Ref.toLowerCase()); + const commissionField = fields.find((f) => f.name.toLowerCase() === PartnerEntityFields.Commission.toLowerCase()); + + const emailFieldValue = emailField ? fieldValues.find((fv) => fv.fieldId === emailField.id) : null; + const phoneFieldValue = phoneField ? fieldValues.find((fv) => fv.fieldId === phoneField.id) : null; + const passwordFieldValue = passwordField ? fieldValues.find((fv) => fv.fieldId === passwordField.id) : null; + const refFieldValue = refField ? fieldValues.find((fv) => fv.fieldId === refField.id) : null; + const commissionFieldValue = commissionField ? fieldValues.find((fv) => fv.fieldId === commissionField.id) : null; + + const emails = emailFieldValue?.getValue(); + const phones = phoneFieldValue?.getValue(); + const password = passwordFieldValue?.getValue(); + const ref = refFieldValue?.getValue(); + const commission = commissionFieldValue?.getValue() ?? 0; + + return emails && password + ? new Partner(entity.id, entity.name, emails?.[0], phones?.[0], password, ref, commission) + : null; + } + + private async createLead( + accountId: number, + entity: Entity, + fields: Field[], + commission: number, + ): Promise { + const fieldValues = await this.fieldValueService.findMany({ accountId, entityId: entity.id }); + + const paymentAmountField = fields.find((f) => f.type === FieldType.Value); + const paymentDateField = fields.find((f) => f.name.toLowerCase() === PartnerDealFields.DateOfPayment.toLowerCase()); + const paidToPartnerField = fields.find( + (f) => f.name.toLowerCase() === PartnerDealFields.PaidToPartner.toLowerCase(), + ); + + const paymentAmountFV = paymentAmountField ? fieldValues.find((fv) => fv.fieldId === paymentAmountField.id) : null; + const paymentDateFV = paymentDateField ? fieldValues.find((fv) => fv.fieldId === paymentDateField.id) : null; + const paidToPartnerFV = paidToPartnerField ? fieldValues.find((fv) => fv.fieldId === paidToPartnerField.id) : null; + + const paymentAmount = paymentAmountFV ? paymentAmountFV.getValue() : 0; + const paymentDate = paymentDateFV ? DateUtil.fromISOString(paymentDateFV.getValue()) : null; + const paidToPartner = paidToPartnerFV ? paidToPartnerFV.getValue() : false; + const partnerBonus = (paymentAmount / 100) * commission; + + return new PartnerLead( + entity.id, + entity.name, + entity.createdAt, + paymentDate, + paymentAmount, + partnerBonus, + paidToPartner, + ); + } +} diff --git a/backend/src/modules/partner/types/index.ts b/backend/src/modules/partner/types/index.ts new file mode 100644 index 0000000..c4252f7 --- /dev/null +++ b/backend/src/modules/partner/types/index.ts @@ -0,0 +1,3 @@ +export * from './partner-lead'; +export * from './partner-summary'; +export * from './partner'; diff --git a/backend/src/modules/partner/types/partner-lead.ts b/backend/src/modules/partner/types/partner-lead.ts new file mode 100644 index 0000000..f1aeccf --- /dev/null +++ b/backend/src/modules/partner/types/partner-lead.ts @@ -0,0 +1,37 @@ +import { PartnerLeadDto } from '../dto'; + +export class PartnerLead { + id: number; + name: string; + registrationDate: Date; + paymentDate: Date | null; + paymentAmount: number | null; + partnerBonus: number | null; + isPaidToPartner: boolean | null; + + constructor( + id: number, + name: string, + registrationDate: Date, + paymentDate: Date | null, + paymentAmount: number | null, + partnerBonus: number | null, + isPaidToPartner: boolean | null, + ) { + this.id = id; + this.name = name; + this.registrationDate = registrationDate; + this.paymentDate = paymentDate; + this.paymentAmount = paymentAmount; + this.partnerBonus = partnerBonus; + this.isPaidToPartner = isPaidToPartner; + } + + public toDto(): PartnerLeadDto { + return new PartnerLeadDto({ + ...this, + paymentDate: this.paymentDate?.toISOString(), + registrationDate: this.registrationDate?.toISOString(), + }); + } +} diff --git a/backend/src/modules/partner/types/partner-summary.ts b/backend/src/modules/partner/types/partner-summary.ts new file mode 100644 index 0000000..96cf8f3 --- /dev/null +++ b/backend/src/modules/partner/types/partner-summary.ts @@ -0,0 +1,30 @@ +import { PartnerSummaryDto } from '../dto'; + +export class PartnerSummary { + id: number; + name: string; + registrationsCount: number; + payingLeadsCount: number; + totalPayments: number; + totalPartnerBonus: number; + + constructor( + id: number, + name: string, + registrationsCount: number, + payingLeadsCount: number, + totalPayments: number, + totalPartnerBonus: number, + ) { + this.id = id; + this.name = name; + this.registrationsCount = registrationsCount; + this.payingLeadsCount = payingLeadsCount; + this.totalPayments = totalPayments; + this.totalPartnerBonus = totalPartnerBonus; + } + + public toDto(): PartnerSummaryDto { + return new PartnerSummaryDto(this); + } +} diff --git a/backend/src/modules/partner/types/partner.ts b/backend/src/modules/partner/types/partner.ts new file mode 100644 index 0000000..5bb74d3 --- /dev/null +++ b/backend/src/modules/partner/types/partner.ts @@ -0,0 +1,27 @@ +export class Partner { + id: number; + name: string; + email: string; + phone: string | null; + password: string; + ref: string | null; + commission: number; + + constructor( + id: number, + name: string, + email: string, + phone: string | null, + password: string, + ref: string, + commission: number, + ) { + this.id = id; + this.name = name; + this.email = email; + this.phone = phone; + this.password = password; + this.ref = ref; + this.commission = commission; + } +} diff --git a/backend/src/modules/scheduler/common/enums/index.ts b/backend/src/modules/scheduler/common/enums/index.ts new file mode 100644 index 0000000..74dba74 --- /dev/null +++ b/backend/src/modules/scheduler/common/enums/index.ts @@ -0,0 +1,2 @@ +export * from './permission-object-type.enum'; +export * from './schedule-appointment-status.enum'; diff --git a/backend/src/modules/scheduler/common/enums/permission-object-type.enum.ts b/backend/src/modules/scheduler/common/enums/permission-object-type.enum.ts new file mode 100644 index 0000000..bef09d3 --- /dev/null +++ b/backend/src/modules/scheduler/common/enums/permission-object-type.enum.ts @@ -0,0 +1,3 @@ +export enum PermissionObjectType { + Schedule = 'schedule', +} diff --git a/backend/src/modules/scheduler/common/enums/schedule-appointment-status.enum.ts b/backend/src/modules/scheduler/common/enums/schedule-appointment-status.enum.ts new file mode 100644 index 0000000..84823a0 --- /dev/null +++ b/backend/src/modules/scheduler/common/enums/schedule-appointment-status.enum.ts @@ -0,0 +1,6 @@ +export enum ScheduleAppointmentStatus { + NotConfirmed = 'not_confirmed', + Confirmed = 'confirmed', + Completed = 'completed', + Canceled = 'canceled', +} diff --git a/backend/src/modules/scheduler/common/events/index.ts b/backend/src/modules/scheduler/common/events/index.ts new file mode 100644 index 0000000..1e9e0f8 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/index.ts @@ -0,0 +1,4 @@ +export * from './schedule'; +export * from './schedule-appointment'; +export * from './schedule-performer'; +export * from './scheduler-event-type.enum'; diff --git a/backend/src/modules/scheduler/common/events/schedule-appointment/index.ts b/backend/src/modules/scheduler/common/events/schedule-appointment/index.ts new file mode 100644 index 0000000..27d77d9 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-appointment/index.ts @@ -0,0 +1,5 @@ +export * from './schedule-appointment-created.event'; +export * from './schedule-appointment-ext-upsert.event'; +export * from './schedule-appointment-ext.event'; +export * from './schedule-appointment-updated.event'; +export * from './schedule-appointment.event'; diff --git a/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-created.event.ts b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-created.event.ts new file mode 100644 index 0000000..6ffb85d --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-created.event.ts @@ -0,0 +1,20 @@ +import { ScheduleAppointmentStatus } from '../../enums'; +import { SchedulerAppointmentEvent } from './schedule-appointment.event'; + +export class SchedulerAppointmentCreatedEvent extends SchedulerAppointmentEvent { + title: string; + comment: string; + startDate: Date; + endDate: Date; + status: ScheduleAppointmentStatus; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.title = data.title; + this.comment = data.comment; + this.startDate = data.startDate; + this.endDate = data.endDate; + this.status = data.status; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext-upsert.event.ts b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext-upsert.event.ts new file mode 100644 index 0000000..53fa16e --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext-upsert.event.ts @@ -0,0 +1,24 @@ +import { ScheduleAppointmentStatus } from '../../enums'; +import { SchedulerAppointmentExtEvent } from './schedule-appointment-ext.event'; + +export class SchedulerAppointmentExtUpsertEvent extends SchedulerAppointmentExtEvent { + ownerId: number; + performerId: number; + title: string; + comment?: string | null; + startDate: Date; + endDate: Date; + status?: ScheduleAppointmentStatus | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.ownerId = data.ownerId; + this.performerId = data.performerId; + this.title = data.title; + this.comment = data.comment; + this.startDate = data.startDate; + this.endDate = data.endDate; + this.status = data.status; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext.event.ts b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext.event.ts new file mode 100644 index 0000000..a0912d1 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-ext.event.ts @@ -0,0 +1,17 @@ +import { ServiceEvent } from '@/common'; + +export class SchedulerAppointmentExtEvent extends ServiceEvent { + externalId?: string | null; + accountId: number; + scheduleId: number; + appointmentId?: number | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.externalId = data.externalId; + this.accountId = data.accountId; + this.scheduleId = data.scheduleId; + this.appointmentId = data.appointmentId; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-updated.event.ts b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-updated.event.ts new file mode 100644 index 0000000..354082b --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment-updated.event.ts @@ -0,0 +1,3 @@ +import { SchedulerAppointmentCreatedEvent } from './schedule-appointment-created.event'; + +export class SchedulerAppointmentUpdatedEvent extends SchedulerAppointmentCreatedEvent {} diff --git a/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment.event.ts b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment.event.ts new file mode 100644 index 0000000..d9c131e --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-appointment/schedule-appointment.event.ts @@ -0,0 +1,23 @@ +import { ServiceEvent } from '@/common'; + +export class SchedulerAppointmentEvent extends ServiceEvent { + accountId: number; + ownerId: number; + scheduleId: number; + performerId: number; + appointmentId: number; + entityId: number | null; + externalId?: string | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.accountId = data.accountId; + this.ownerId = data.ownerId; + this.scheduleId = data.scheduleId; + this.performerId = data.performerId; + this.appointmentId = data.appointmentId; + this.entityId = data.entityId; + this.externalId = data.externalId; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule-performer/index.ts b/backend/src/modules/scheduler/common/events/schedule-performer/index.ts new file mode 100644 index 0000000..ab289d2 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-performer/index.ts @@ -0,0 +1,2 @@ +export * from './schedule-performer-deleted.event'; +export * from './schedule-performer.event'; diff --git a/backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer-deleted.event.ts b/backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer-deleted.event.ts new file mode 100644 index 0000000..1dfd9bd --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer-deleted.event.ts @@ -0,0 +1,11 @@ +import { SchedulePerformerEvent } from './schedule-performer.event'; + +export class SchedulePerformerDeletedEvent extends SchedulePerformerEvent { + newPerformerId?: number | null; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.newPerformerId = data.newPerformerId; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer.event.ts b/backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer.event.ts new file mode 100644 index 0000000..2af2e3b --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule-performer/schedule-performer.event.ts @@ -0,0 +1,15 @@ +import { ServiceEvent } from '@/common'; + +export class SchedulePerformerEvent extends ServiceEvent { + accountId: number; + scheduleId: number; + performerId: number; + + constructor(data: Omit & { key?: string }) { + super(data); + + this.accountId = data.accountId; + this.scheduleId = data.scheduleId; + this.performerId = data.performerId; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule/index.ts b/backend/src/modules/scheduler/common/events/schedule/index.ts new file mode 100644 index 0000000..b4a7b8a --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule/index.ts @@ -0,0 +1,2 @@ +export * from './schedule-updated.event'; +export * from './schedule.event'; diff --git a/backend/src/modules/scheduler/common/events/schedule/schedule-updated.event.ts b/backend/src/modules/scheduler/common/events/schedule/schedule-updated.event.ts new file mode 100644 index 0000000..1fa6372 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule/schedule-updated.event.ts @@ -0,0 +1,12 @@ +import { ScheduleEvent } from './schedule.event'; + +export class ScheduleUpdatedEvent extends ScheduleEvent { + typeChanged: boolean; + timePeriodChanged: boolean; + + constructor({ accountId, userId, scheduleId, typeChanged, timePeriodChanged }: ScheduleUpdatedEvent) { + super({ accountId, userId, scheduleId }); + this.typeChanged = typeChanged; + this.timePeriodChanged = timePeriodChanged; + } +} diff --git a/backend/src/modules/scheduler/common/events/schedule/schedule.event.ts b/backend/src/modules/scheduler/common/events/schedule/schedule.event.ts new file mode 100644 index 0000000..e74f8d7 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/schedule/schedule.event.ts @@ -0,0 +1,11 @@ +export class ScheduleEvent { + accountId: number; + userId: number; + scheduleId: number; + + constructor({ accountId, userId, scheduleId }: ScheduleEvent) { + this.accountId = accountId; + this.userId = userId; + this.scheduleId = scheduleId; + } +} diff --git a/backend/src/modules/scheduler/common/events/scheduler-event-type.enum.ts b/backend/src/modules/scheduler/common/events/scheduler-event-type.enum.ts new file mode 100644 index 0000000..9cafab6 --- /dev/null +++ b/backend/src/modules/scheduler/common/events/scheduler-event-type.enum.ts @@ -0,0 +1,12 @@ +export enum SchedulerEventType { + ScheduleAppointmentCreated = 'schedule:appointment:created', + ScheduleAppointmentDeleted = 'schedule:appointment:deleted', + ScheduleAppointmentUpdated = 'schedule:appointment:updated', + ScheduleAppointmentUpsertExt = 'schedule:appointment:upsert-ext', + ScheduleCreated = 'schedule:created', + ScheduleDeleted = 'schedule:deleted', + ScheduleUpdated = 'schedule:updated', + SchedulePerformerCreated = 'schedule:performer:created', + SchedulePerformerDeleted = 'schedule:performer:deleted', + SchedulePerformerUpdated = 'schedule:performer:updated', +} diff --git a/backend/src/modules/scheduler/common/index.ts b/backend/src/modules/scheduler/common/index.ts new file mode 100644 index 0000000..df1eda9 --- /dev/null +++ b/backend/src/modules/scheduler/common/index.ts @@ -0,0 +1,2 @@ +export * from './enums'; +export * from './events'; diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/create-schedule-appointment.dto.ts b/backend/src/modules/scheduler/schedule-appointment/dto/create-schedule-appointment.dto.ts new file mode 100644 index 0000000..1d7a488 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/create-schedule-appointment.dto.ts @@ -0,0 +1,31 @@ +import { ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsBoolean, IsDateString, IsNumber, IsOptional } from 'class-validator'; + +import { ScheduleAppointmentDto } from './schedule-appointment.dto'; + +export class CreateScheduleAppointmentDto extends PickType(ScheduleAppointmentDto, [ + 'scheduleId', + 'startDate', + 'status', + 'title', + 'comment', + 'entityId', + 'performerId', + 'orderId', + 'externalId', +] as const) { + @ApiPropertyOptional({ description: 'Appointment end date' }) + @IsOptional() + @IsDateString() + endDate?: string; + + @ApiPropertyOptional({ description: 'Check intersection with other appointments' }) + @IsOptional() + @IsBoolean() + checkIntersection?: boolean; + + @ApiPropertyOptional({ description: 'Owner id' }) + @IsOptional() + @IsNumber() + ownerId?: number; +} diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/index.ts b/backend/src/modules/scheduler/schedule-appointment/dto/index.ts new file mode 100644 index 0000000..66febdf --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/index.ts @@ -0,0 +1,6 @@ +export * from './create-schedule-appointment.dto'; +export * from './schedule-appointment-filter.dto'; +export * from './schedule-appointment-result.dto'; +export * from './schedule-appointment-statistic.dto'; +export * from './schedule-appointment.dto'; +export * from './update-schedule-appointment.dto'; diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-filter.dto.ts b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-filter.dto.ts new file mode 100644 index 0000000..3ebf062 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-filter.dto.ts @@ -0,0 +1,52 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { DatePeriodDto } from '@/common'; +import { ScheduleAppointmentStatus } from '../../common'; + +export class ScheduleAppointmentFilterDto extends DatePeriodDto { + @ApiPropertyOptional({ description: 'Schedule ID' }) + @IsOptional() + @IsNumber() + scheduleId?: number; + + @ApiPropertyOptional({ description: 'Linked entity ID' }) + @IsOptional() + @IsNumber() + entityId?: number; + + @ApiPropertyOptional({ description: 'Performer ID' }) + @IsOptional() + @IsNumber() + performerId?: number; + + @ApiPropertyOptional({ description: 'Show canceled appointments' }) + @IsOptional() + @IsBoolean() + showCanceled?: boolean; + + @ApiPropertyOptional({ description: 'Appointment title' }) + @IsOptional() + @IsString() + title?: string; + + @ApiPropertyOptional({ enum: ScheduleAppointmentStatus, isArray: true, description: 'Appointment status' }) + @IsOptional() + @IsEnum(ScheduleAppointmentStatus, { each: true }) + status?: ScheduleAppointmentStatus | ScheduleAppointmentStatus[]; + + @ApiPropertyOptional({ description: 'Show only appointments which is first for linked entity' }) + @IsOptional() + @IsBoolean() + isNewbie?: boolean; + + @ApiPropertyOptional({ description: 'Show only appointments without next appointment for same linked entity' }) + @IsOptional() + @IsBoolean() + isNotScheduled?: boolean; + + @ApiPropertyOptional({ description: 'Show only appointments which is not took place' }) + @IsOptional() + @IsBoolean() + isNotTookPlace?: boolean; +} diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-result.dto.ts b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-result.dto.ts new file mode 100644 index 0000000..5e1b01f --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-result.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; + +import { ScheduleAppointmentDto } from './schedule-appointment.dto'; + +export class ScheduleAppointmentResultDto { + @ApiProperty({ type: [ScheduleAppointmentDto], description: 'List of schedule appointments' }) + appointments: ScheduleAppointmentDto[]; + + @ApiProperty({ type: PagingMeta, description: 'Paging metadata' }) + meta: PagingMeta; + + constructor(appointments: ScheduleAppointmentDto[], meta: PagingMeta) { + this.appointments = appointments; + this.meta = meta; + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-statistic.dto.ts b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-statistic.dto.ts new file mode 100644 index 0000000..2dfb3f3 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment-statistic.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsObject } from 'class-validator'; + +import { ScheduleAppointmentStatus } from '../../common'; + +export class ScheduleAppointmentStatisticDto { + @ApiProperty({ description: 'Total number of appointments' }) + @IsNumber() + total: number; + + @ApiProperty({ description: 'Number of appointments grouped by status' }) + @IsObject() + statuses: Record; + + @ApiProperty({ description: 'Number of appointments which is first for linked entity' }) + @IsNumber() + newbies: number; + + @ApiProperty({ description: 'Number of appointments without next appointment for same linked entity' }) + @IsNumber() + notScheduled: number; + + @ApiProperty({ description: 'Number of appointments which is not took place' }) + @IsNumber() + notTookPlace: number; +} diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment.dto.ts b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment.dto.ts new file mode 100644 index 0000000..ab17dfd --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/schedule-appointment.dto.ts @@ -0,0 +1,84 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; + +import { UserRights } from '@/modules/iam/common/types/user-rights'; +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; +import { OrderDto } from '@/modules/inventory/order/dto/order.dto'; + +import { ScheduleAppointmentStatus } from '../../common'; + +export class ScheduleAppointmentDto { + @ApiProperty({ description: 'Appointment ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Schedule ID' }) + @IsNumber() + scheduleId: number; + + @ApiProperty({ description: 'Appointment start date' }) + @IsDateString() + startDate: string; + + @ApiProperty({ description: 'Appointment end date' }) + @IsDateString() + endDate: string; + + @ApiProperty({ enum: ScheduleAppointmentStatus, description: 'Appointment status' }) + @IsEnum(ScheduleAppointmentStatus) + status: ScheduleAppointmentStatus; + + @ApiPropertyOptional({ nullable: true, description: 'Appointment title' }) + @IsOptional() + @IsString() + title?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Appointment comment' }) + @IsOptional() + @IsString() + comment?: string | null; + + @ApiProperty({ description: 'User ID for appointment owner' }) + @IsNumber() + ownerId: number; + + @ApiPropertyOptional({ nullable: true, description: 'Linked entity ID' }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional({ type: EntityInfoDto, nullable: true, description: 'Linked entity' }) + @IsOptional() + entityInfo?: EntityInfoDto | null; + + @ApiProperty({ description: 'Performer ID' }) + @IsNumber() + performerId: number; + + @ApiPropertyOptional({ nullable: true, description: 'Linked order ID' }) + @IsOptional() + @IsNumber() + orderId?: number | null; + + @ApiPropertyOptional({ type: OrderDto, nullable: true, description: 'Linked order' }) + @IsOptional() + order?: OrderDto | null; + + @ApiPropertyOptional({ nullable: true, description: 'Previous appointment count' }) + @IsOptional() + @IsNumber() + prevAppointmentCount?: number | null; + + @ApiProperty({ description: 'Appointment creation date' }) + @IsDateString() + createdAt: string; + + @ApiProperty({ type: () => UserRights, description: 'User rights for current user' }) + @IsObject() + userRights: UserRights; + + @ApiPropertyOptional({ nullable: true, description: 'External ID' }) + @IsOptional() + @IsString() + externalId?: string | null; +} diff --git a/backend/src/modules/scheduler/schedule-appointment/dto/update-schedule-appointment.dto.ts b/backend/src/modules/scheduler/schedule-appointment/dto/update-schedule-appointment.dto.ts new file mode 100644 index 0000000..8349117 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/dto/update-schedule-appointment.dto.ts @@ -0,0 +1,18 @@ +import { ApiPropertyOptional, OmitType, PartialType } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional } from 'class-validator'; + +import { ScheduleAppointmentDto } from './schedule-appointment.dto'; + +export class UpdateScheduleAppointmentDto extends PartialType( + OmitType(ScheduleAppointmentDto, ['id', 'ownerId', 'createdAt', 'userRights', 'order', 'entityInfo'] as const), +) { + @ApiPropertyOptional({ description: 'Check intersection with other appointments' }) + @IsOptional() + @IsBoolean() + checkIntersection?: boolean; + + @ApiPropertyOptional({ description: 'Owner id' }) + @IsOptional() + @IsNumber() + ownerId?: number; +} diff --git a/backend/src/modules/scheduler/schedule-appointment/entities/index.ts b/backend/src/modules/scheduler/schedule-appointment/entities/index.ts new file mode 100644 index 0000000..ee3a0de --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/entities/index.ts @@ -0,0 +1 @@ +export * from './schedule-appointment.entity'; diff --git a/backend/src/modules/scheduler/schedule-appointment/entities/schedule-appointment.entity.ts b/backend/src/modules/scheduler/schedule-appointment/entities/schedule-appointment.entity.ts new file mode 100644 index 0000000..28872ec --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/entities/schedule-appointment.entity.ts @@ -0,0 +1,219 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, AuthorizableObject, SimpleAuthorizable, UserRights } from '@/modules/iam/common'; +import { Order } from '@/modules/inventory/order/entities/order.entity'; +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { PermissionObjectType, ScheduleAppointmentStatus } from '../../common'; +import { SchedulePerformer } from '../../schedule-performer'; + +import { CreateScheduleAppointmentDto, UpdateScheduleAppointmentDto, ScheduleAppointmentDto } from '../dto'; + +@Entity() +export class ScheduleAppointment implements Authorizable { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + @Column() + scheduleId: number; + + @Column() + startDate: Date; + + @Column() + endDate: Date; + + @Column() + status: ScheduleAppointmentStatus; + + @Column({ nullable: true }) + title: string | null; + + @Column({ nullable: true }) + comment: string | null; + + @Column() + ownerId: number; + + @Column({ nullable: true }) + entityId: number | null; + + @Column() + performerId: number; + + @Column({ nullable: true }) + orderId: number | null; + + @Column() + externalId: string | null; + + constructor( + accountId: number, + scheduleId: number, + startDate: Date, + endDate: Date, + status: ScheduleAppointmentStatus, + title: string | null, + comment: string | null, + ownerId: number, + entityId: number | null, + performerId: number, + orderId: number | null, + externalId: string | null, + createdAt?: Date, + ) { + this.accountId = accountId; + this.scheduleId = scheduleId; + this.startDate = startDate; + this.endDate = endDate; + this.status = status; + this.title = title; + this.comment = comment; + this.ownerId = ownerId; + this.entityId = entityId; + this.performerId = performerId; + this.orderId = orderId; + this.externalId = externalId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _performer: SchedulePerformer | null; + public get performer(): SchedulePerformer | null { + return this._performer; + } + public set performer(value: SchedulePerformer | null) { + this._performer = value; + } + + private _prevAppointmentCount: number | null; + public get prevAppointmentCount(): number | null { + return this._prevAppointmentCount; + } + public set prevAppointmentCount(value: number | null) { + this._prevAppointmentCount = value; + } + + private _userRights: UserRights | null; + public get userRights(): UserRights | null { + return this._userRights; + } + public set userRights(value: UserRights | null) { + this._userRights = value; + } + + private _order: Order | null; + public get order(): Order | null { + return this._order; + } + public set order(value: Order | null) { + this._order = value; + } + + private _entityInfo: EntityInfoDto | null; + public get entityInfo(): EntityInfoDto | null { + return this._entityInfo; + } + public set entityInfo(value: EntityInfoDto | null) { + this._entityInfo = value; + } + + public static fromDto( + accountId: number, + createdBy: number, + dto: CreateScheduleAppointmentDto, + timePeriod: number | null, + ): ScheduleAppointment { + const start = DateUtil.fromISOString(dto.startDate); + const end = dto.endDate ? DateUtil.fromISOString(dto.endDate) : DateUtil.add(start, { seconds: timePeriod }); + return new ScheduleAppointment( + accountId, + dto.scheduleId, + start, + end, + dto.status, + dto.title, + dto.comment, + dto.ownerId ?? createdBy, + dto.entityId, + dto.performerId, + dto.orderId ?? null, + dto.externalId ?? null, + ); + } + + public update(dto: UpdateScheduleAppointmentDto): ScheduleAppointment { + this.scheduleId = dto.scheduleId !== undefined ? dto.scheduleId : this.scheduleId; + this.startDate = dto.startDate !== undefined ? DateUtil.fromISOString(dto.startDate) : this.startDate; + this.endDate = dto.endDate !== undefined ? DateUtil.fromISOString(dto.endDate) : this.endDate; + this.status = dto.status !== undefined ? dto.status : this.status; + this.title = dto.title !== undefined ? dto.title : this.title; + this.comment = dto.comment !== undefined ? dto.comment : this.comment; + this.entityId = dto.entityId !== undefined ? dto.entityId : this.entityId; + this.performerId = dto.performerId !== undefined ? dto.performerId : this.performerId; + this.orderId = dto.orderId !== undefined ? dto.orderId : this.orderId; + this.ownerId = dto.ownerId !== undefined ? dto.ownerId : this.ownerId; + this.externalId = dto.externalId !== undefined ? dto.externalId : this.externalId; + + return this; + } + + public hasChanges(dto: UpdateScheduleAppointmentDto): boolean { + return ( + (dto.scheduleId !== undefined && dto.scheduleId !== this.scheduleId) || + (dto.startDate !== undefined && DateUtil.fromISOString(dto.startDate) !== this.startDate) || + (dto.endDate !== undefined && DateUtil.fromISOString(dto.endDate) !== this.endDate) || + (dto.status !== undefined && dto.status !== this.status) || + (dto.title !== undefined && dto.title !== this.title) || + (dto.comment !== undefined && dto.comment !== this.comment) || + (dto.entityId !== undefined && dto.entityId !== this.entityId) || + (dto.performerId !== undefined && dto.performerId !== this.performerId) || + (dto.orderId !== undefined && dto.orderId !== this.orderId) || + (dto.ownerId !== undefined && dto.ownerId !== this.ownerId) || + (dto.externalId !== undefined && dto.externalId !== this.externalId) + ); + } + + public toDto(): ScheduleAppointmentDto { + return { + id: this.id, + scheduleId: this.scheduleId, + startDate: this.startDate.toISOString(), + endDate: this.endDate.toISOString(), + status: this.status, + title: this.title, + comment: this.comment, + ownerId: this.ownerId, + entityId: this.entityId, + performerId: this.performerId, + orderId: this.orderId, + prevAppointmentCount: this.prevAppointmentCount, + createdAt: this.createdAt.toISOString(), + userRights: this.userRights ?? UserRights.full(), + order: this.order?.toDto(), + entityInfo: this.entityInfo, + externalId: this.externalId, + }; + } + + static getAuthorizable(scheduleId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Schedule, id: scheduleId }); + } + + getAuthorizableObject(): AuthorizableObject { + return { + type: PermissionObjectType.Schedule, + id: this.scheduleId, + createdBy: this.ownerId, + ownerId: this.performer?.userId, + departmentId: this.performer?.departmentId, + }; + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/errors/appointment-dates-conflict.error.ts b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-dates-conflict.error.ts new file mode 100644 index 0000000..419aebd --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-dates-conflict.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class AppointmentDatesConflictError extends ServiceError { + constructor(message = 'Start date is more or equal than end date') { + super({ errorCode: 'scheduler.appointment.dates_conflict', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/errors/appointment-day-limit.error.ts b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-day-limit.error.ts new file mode 100644 index 0000000..3f66161 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-day-limit.error.ts @@ -0,0 +1,14 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class AppointmentDayLimitError extends ServiceError { + constructor({ appointmentId, message = 'Appointment day limit error' }: { appointmentId: number; message?: string }) { + super({ + errorCode: 'scheduler.appointment.day_limit', + status: HttpStatus.BAD_REQUEST, + message, + details: { appointmentId }, + }); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/errors/appointment-intersection.error.ts b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-intersection.error.ts new file mode 100644 index 0000000..81a5c8e --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-intersection.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class AppointmentIntersectionError extends ServiceError { + constructor(message = 'Intersect appointments') { + super({ errorCode: 'scheduler.appointment.intersection', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/errors/appointment-out-of-work-time.error.ts b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-out-of-work-time.error.ts new file mode 100644 index 0000000..ca7f92d --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/errors/appointment-out-of-work-time.error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class AppointmentOutOfWorkTimeError extends ServiceError { + constructor(message = 'Appointment is out of work time') { + super({ errorCode: 'scheduler.appointment.out_of_work_time', status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/errors/index.ts b/backend/src/modules/scheduler/schedule-appointment/errors/index.ts new file mode 100644 index 0000000..079b896 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/errors/index.ts @@ -0,0 +1,4 @@ +export * from './appointment-dates-conflict.error'; +export * from './appointment-day-limit.error'; +export * from './appointment-intersection.error'; +export * from './appointment-out-of-work-time.error'; diff --git a/backend/src/modules/scheduler/schedule-appointment/index.ts b/backend/src/modules/scheduler/schedule-appointment/index.ts new file mode 100644 index 0000000..b645e4e --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/index.ts @@ -0,0 +1,7 @@ +export * from './dto'; +export * from './entities'; +export * from './errors'; +export * from './schedule-appointment.controller'; +export * from './schedule-appointment.handler'; +export * from './schedule-appointment.service'; +export * from './types'; diff --git a/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.controller.ts b/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.controller.ts new file mode 100644 index 0000000..5890032 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.controller.ts @@ -0,0 +1,174 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, PagingQuery, ExpandQuery } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { + ScheduleAppointmentDto, + CreateScheduleAppointmentDto, + ScheduleAppointmentResultDto, + ScheduleAppointmentFilterDto, + UpdateScheduleAppointmentDto, + ScheduleAppointmentStatisticDto, +} from './dto'; +import { EntityListItem, EntityListMeta } from '@/CRM/Service/Entity/Dto/List'; +import { ExpandableField, Spot } from './types'; +import { ScheduleAppointmentService } from './schedule-appointment.service'; + +@ApiTags('scheduler/appointment') +@Controller('appointments') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ScheduleAppointmentController { + constructor(private readonly service: ScheduleAppointmentService) {} + + @ApiOperation({ summary: 'Create schedule appointment', description: 'Create schedule appointment' }) + @ApiBody({ description: 'Data for creating schedule appointment', type: CreateScheduleAppointmentDto }) + @ApiCreatedResponse({ description: 'Schedule appointment', type: ScheduleAppointmentDto }) + @Post() + async create(@CurrentAuth() { accountId, user }: AuthData, @Body() dto: CreateScheduleAppointmentDto) { + return this.service.create({ accountId, user, dto }); + } + + @ApiOperation({ summary: 'Get schedule appointments', description: 'Get schedule appointments according filter' }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: order,prevAppointmentCount,entityInfo.', + }) + @ApiOkResponse({ description: 'Schedule appointments', type: ScheduleAppointmentResultDto }) + @Get() + async getSchedule( + @CurrentAuth() { accountId, user }: AuthData, + @Query() filter: ScheduleAppointmentFilterDto, + @Query() paging: PagingQuery, + @Query() expand?: ExpandQuery, + ) { + return this.service.getSchedule({ accountId, user, filter, paging, options: { expand: expand.fields } }); + } + + @ApiOperation({ + summary: 'Get schedule appointment statistic', + description: 'Get schedule appointment statistic according filter', + }) + @ApiOkResponse({ description: 'Schedule appointment statistic', type: ScheduleAppointmentStatisticDto }) + @Get('statistic') + async getStatistic(@CurrentAuth() { accountId }: AuthData, @Query() filter: ScheduleAppointmentFilterDto) { + return this.service.getStatistic({ accountId, filter }); + } + + @ApiOperation({ summary: 'Get appointments count', description: 'Get appointments count according filter' }) + @ApiOkResponse({ description: 'Appointments count', type: Number }) + @Get('count') + async getCount(@CurrentAuth() { accountId }: AuthData, @Query() filter: ScheduleAppointmentFilterDto) { + return this.service.getCount({ accountId, filter, isSchedule: true }); + } + + @ApiOperation({ + summary: 'Get last schedule appointment', + description: 'Get last schedule appointment according filter', + }) + @ApiQuery({ name: 'filter', type: ScheduleAppointmentFilterDto, required: false, description: 'Filter' }) + @ApiOkResponse({ description: 'Schedule appointments', type: ScheduleAppointmentDto }) + @Get('last') + async getLast(@CurrentAuth() { accountId, user }: AuthData, @Query() filter: ScheduleAppointmentFilterDto) { + return this.service.getLast({ accountId, user, filter }); + } + + @ApiOperation({ + summary: 'Get schedule appointment linked cards', + description: 'Get schedule appointment linked cards', + }) + @ApiOkResponse({ description: 'Entity list linked with appointments', type: [EntityListItem] }) + @Get('cards/list') + async getEntityList( + @CurrentAuth() { accountId, user }: AuthData, + @Query() filter: ScheduleAppointmentFilterDto, + @Query() paging: PagingQuery, + ): Promise { + return this.service.getEntityList({ accountId, user, filter, paging }); + } + + @ApiOperation({ + summary: 'Get schedule appointment linked cards meta', + description: 'Get schedule appointment linked cards meta', + }) + @ApiOkResponse({ description: 'Meta for entity list', type: EntityListMeta }) + @Get('cards/list/meta') + async getEntityListMeta( + @CurrentAuth() { accountId }: AuthData, + @Query() filter: ScheduleAppointmentFilterDto, + ): Promise { + return this.service.getEntityListMeta({ accountId, filter }); + } + + @ApiOperation({ summary: 'Get available spots', description: 'Get available spots' }) + @ApiOkResponse({ description: 'Available spots', type: [Spot] }) + @Get('spots') + async getAvailableSpots( + @CurrentAuth() { accountId }: AuthData, + @Query('scheduleId', ParseIntPipe) scheduleId: number, + @Query('performerId', ParseIntPipe) performerId: number, + @Query('minDate') minDate: string, + @Query('maxDate') maxDate: string, + @Query('daysLimit', ParseIntPipe) daysLimit: number, + ) { + return this.service.getAvailableSpots({ + accountId, + scheduleId, + performerId, + minDate: new Date(minDate), + maxDate: new Date(maxDate), + daysLimit, + }); + } + + @ApiOperation({ summary: 'Get schedule appointment', description: 'Get schedule appointment by ID' }) + @ApiParam({ name: 'appointmentId', description: 'Schedule appointment ID' }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: order,prevAppointmentCount,entityInfo.', + }) + @ApiOkResponse({ description: 'Schedule appointments', type: ScheduleAppointmentDto }) + @Get(':appointmentId') + async getOne( + @CurrentAuth() { accountId, user }: AuthData, + @Param('appointmentId', ParseIntPipe) appointmentId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.getOne({ accountId, user, appointmentId, options: { expand: expand.fields } }); + } + + @ApiOperation({ summary: 'Update schedule appointment', description: 'Update schedule appointment' }) + @ApiParam({ name: 'appointmentId', description: 'Schedule appointment ID' }) + @ApiBody({ description: 'Data for updating schedule appointment', type: UpdateScheduleAppointmentDto }) + @ApiOkResponse({ description: 'Schedule appointment', type: ScheduleAppointmentDto }) + @Patch(':appointmentId') + async update( + @CurrentAuth() { accountId, user }: AuthData, + @Param('appointmentId', ParseIntPipe) appointmentId: number, + @Body() dto: UpdateScheduleAppointmentDto, + ) { + return this.service.update({ accountId, user, appointmentId, dto }); + } + + @ApiOperation({ summary: 'Delete schedule appointment', description: 'Delete schedule appointment' }) + @ApiParam({ name: 'appointmentId', description: 'Schedule appointment ID' }) + @ApiOkResponse() + @Delete(':appointmentId') + async delete( + @CurrentAuth() { accountId, user }: AuthData, + @Param('appointmentId', ParseIntPipe) appointmentId: number, + ) { + return this.service.delete({ accountId, user, filter: { appointmentId } }); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.handler.ts b/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.handler.ts new file mode 100644 index 0000000..65d2972 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.handler.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { SchedulerAppointmentExtUpsertEvent, SchedulerEventType, ScheduleUpdatedEvent } from '../common'; +import { ScheduleAppointmentService } from './schedule-appointment.service'; + +@Injectable() +export class ScheduleAppointmentHandler { + constructor(private readonly service: ScheduleAppointmentService) {} + + @OnEvent(SchedulerEventType.ScheduleUpdated, { async: true }) + public async onUserDeleted(event: ScheduleUpdatedEvent) { + if (event.typeChanged || event.timePeriodChanged) { + await this.service.delete({ accountId: event.accountId, filter: { scheduleId: event.scheduleId } }); + } + } + + @OnEvent(SchedulerEventType.ScheduleAppointmentUpsertExt, { async: true }) + public async onTaskUpsertExt(event: SchedulerAppointmentExtUpsertEvent) { + await this.service.handleUpsertExt(event); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.service.ts b/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.service.ts new file mode 100644 index 0000000..b575564 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/schedule-appointment.service.ts @@ -0,0 +1,962 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, SelectQueryBuilder } from 'typeorm'; +import { fromZonedTime, toZonedTime } from 'date-fns-tz'; + +import { DatePeriod, DateUtil, ForbiddenError, NotFoundError, PagingQuery, ServiceEvent } from '@/common'; + +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { UserCalendarService } from '@/modules/iam/user-calendar/services/user-calendar.service'; +import { WorkingTimeService } from '@/modules/iam/working-time/working-time.service'; +import { OrderService } from '@/modules/inventory/order/services/order.service'; +import { EntityListItem, EntityListMeta } from '@/CRM/Service/Entity/Dto/List'; +import { EntityBoardService } from '@/CRM/Service/Entity/EntityBoardService'; + +import { + ScheduleAppointmentStatus, + SchedulerAppointmentCreatedEvent, + SchedulerAppointmentEvent, + SchedulerAppointmentExtUpsertEvent, + SchedulerAppointmentUpdatedEvent, + SchedulerEventType, +} from '../common'; +import { ScheduleService } from '../schedule/services/schedule.service'; +import { Schedule } from '../schedule/entities'; +import { SchedulePerformer } from '../schedule-performer/entities'; + +import { CreateScheduleAppointmentDto, ScheduleAppointmentFilterDto, UpdateScheduleAppointmentDto } from './dto'; +import { ScheduleAppointment } from './entities'; +import { AppointmentDatesConflictError, AppointmentDayLimitError, AppointmentIntersectionError } from './errors'; +import { ExpandableField, ScheduleAppointmentResult, ScheduleAppointmentStatistic, Spot } from './types'; + +interface FindFilter { + accountId: number; + appointmentId?: number | number[]; + scheduleId?: number; + entityId?: number; + performerId?: number; + showCanceled?: boolean; + title?: string; + status?: ScheduleAppointmentStatus | ScheduleAppointmentStatus[]; + from?: Date; + to?: Date; + isNewbie?: boolean; + isNotScheduled?: boolean; + isNotTookPlace?: boolean; + externalId?: string; +} + +interface DeleteFilter { + appointmentId?: number | number[]; + scheduleId?: number; +} + +interface FindOptions { + joinPerformer?: boolean; + isSchedule?: boolean; + expand?: ExpandableField[]; +} + +interface StatusCount { + status: ScheduleAppointmentStatus; + count: number; +} +interface Count { + count: number; +} + +interface WorkingTime { + dayOfWeek: string; + timeFrom: string; + timeTo: string; +} +interface SpotScheduleSettings { + appointmentLimit?: number; + timeBufferBefore?: number; + timeBufferAfter?: number; + timezone: string; + intervals: WorkingTime[]; +} + +@Injectable() +export class ScheduleAppointmentService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(ScheduleAppointment) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly userService: UserService, + private readonly orderService: OrderService, + private readonly scheduleService: ScheduleService, + private readonly entityInfoService: EntityInfoService, + private readonly entityBoardService: EntityBoardService, + private readonly userCalendarService: UserCalendarService, + private readonly workingTimeService: WorkingTimeService, + ) {} + + async create({ + accountId, + user, + dto, + skipPermissionCheck, + event, + }: { + accountId: number; + user: User; + dto: CreateScheduleAppointmentDto; + skipPermissionCheck?: boolean; + event?: ServiceEvent; + }): Promise { + if (!skipPermissionCheck) { + await this.authService.check({ + action: 'create', + user, + authorizable: ScheduleAppointment.getAuthorizable(dto.scheduleId), + throwError: true, + }); + } + + const schedule = await this.scheduleService.findOne({ filter: { accountId, scheduleId: dto.scheduleId } }); + if (!schedule?.timePeriod && !dto.endDate) { + throw new AppointmentDatesConflictError('Schedule time period is not defined'); + } + + let appointment = ScheduleAppointment.fromDto(accountId, user.id, dto, schedule.timePeriod); + if (appointment.endDate <= appointment.startDate) { + throw new AppointmentDatesConflictError(); + } + + if (dto.checkIntersection && (await this.hasIntersect(appointment))) { + throw new AppointmentIntersectionError(); + } + + if (appointment.entityId) { + await this.checkEntityDayLimit(appointment); + } + + appointment = await this.repository.save(appointment); + + this.eventEmitter.emit( + SchedulerEventType.ScheduleAppointmentCreated, + new SchedulerAppointmentCreatedEvent({ + source: ScheduleAppointment.name, + accountId, + ownerId: appointment.ownerId, + scheduleId: appointment.scheduleId, + performerId: appointment.performerId, + appointmentId: appointment.id, + externalId: appointment.externalId, + title: appointment.title, + comment: appointment.comment, + startDate: appointment.startDate, + endDate: appointment.endDate, + status: appointment.status, + entityId: appointment.entityId, + prevEvent: event, + }), + ); + + appointment.userRights = await this.authService.getUserRights({ user, authorizable: appointment }); + + return this.expandOne({ accountId, user, appointment, expand: ['prevAppointmentCount', 'entityInfo', 'order'] }); + } + + async getOne({ + accountId, + user, + appointmentId, + options, + }: { + accountId: number; + user: User; + appointmentId: number; + options?: FindOptions; + }): Promise { + const appointment = await this.createFindQb({ + filter: { accountId, appointmentId }, + options: { joinPerformer: true }, + }).getOne(); + + if (!appointment) { + throw NotFoundError.withId(ScheduleAppointment, appointmentId); + } + + appointment.userRights = await this.authService.getUserRights({ user, authorizable: appointment }); + if (!appointment.userRights.canView) { + throw new ForbiddenError(); + } + + return options?.expand ? this.expandOne({ accountId, user, appointment, expand: options.expand }) : appointment; + } + + async getCount({ + accountId, + filter, + isSchedule, + }: { + accountId: number; + filter: ScheduleAppointmentFilterDto; + isSchedule?: boolean; + }): Promise { + const period = DatePeriod.fromDto(filter); + return this.createFindQb({ + filter: { accountId, ...filter, from: period.from, to: period.to }, + options: { isSchedule }, + }).getCount(); + } + + async getLast({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: ScheduleAppointmentFilterDto; + }): Promise { + const period = DatePeriod.fromDto(filter); + const appointment = await this.createFindQb({ + filter: { accountId, ...filter, from: period.from, to: DateUtil.now() }, + options: { joinPerformer: true }, + }) + .orderBy('appointment.end_date', 'DESC') + .getOne(); + + if (appointment) { + appointment.userRights = await this.authService.getUserRights({ user, authorizable: appointment }); + } + + return appointment; + } + + async findOne({ + filter, + joinPerformer, + }: { + filter: FindFilter; + joinPerformer?: boolean; + }): Promise { + return this.createFindQb({ filter, options: { joinPerformer } }).getOne(); + } + async findMany({ + filter, + joinPerformer, + }: { + filter: FindFilter; + joinPerformer?: boolean; + }): Promise { + return this.createFindQb({ filter, options: { joinPerformer } }).getMany(); + } + + async getSchedule({ + accountId, + user, + filter, + paging, + options, + }: { + accountId: number; + user: User; + filter: ScheduleAppointmentFilterDto; + paging: PagingQuery; + options?: FindOptions; + }): Promise { + const period = DatePeriod.fromDto(filter); + const [appointments, total] = await this.createFindQb({ + filter: { accountId, ...filter, from: period.from, to: period.to }, + options: { isSchedule: true, joinPerformer: true }, + }) + .offset(paging.offset) + .limit(paging.limit) + .orderBy('appointment.start_date', 'DESC') + .getManyAndCount(); + + const expanded: ScheduleAppointment[] = await Promise.all( + appointments.map(async (appointment) => { + appointment.userRights = await this.authService.getUserRights({ user, authorizable: appointment }); + return appointment.userRights.canView && options?.expand + ? this.expandOne({ accountId, user, appointment, expand: options.expand }) + : appointment; + }), + ); + + const result = new ScheduleAppointmentResult( + expanded.filter((a) => a.userRights.canView), + paging.skip + paging.take, + total, + ); + + return result; + } + + async getStatistic({ + accountId, + filter, + }: { + accountId: number; + filter: ScheduleAppointmentFilterDto; + }): Promise { + const period = DatePeriod.fromDto(filter); + const qb = this.createFindQb({ + filter: { accountId, ...filter, from: period.from, to: period.to }, + options: { isSchedule: true }, + }); + + const [rawStatuses, newbies, notScheduled, notTookPlace] = await Promise.all([ + qb + .clone() + .select('appointment.status', 'status') + .addSelect('COUNT(*)::int', 'count') + .groupBy('appointment.status') + .getRawMany(), + qb + .clone() + .select('COUNT(DISTINCT appointment.entityId)::int', 'count') + .andWhere('appointment.entityId IS NOT NULL') + .andWhere( + `NOT EXISTS (SELECT 1 FROM schedule_appointment ia WHERE ia.schedule_id = appointment.schedule_id AND ` + + `ia.entity_id IS NOT NULL AND ia.entity_id = appointment.entity_id ` + + `AND ia.status != '${ScheduleAppointmentStatus.Canceled}' AND ia.end_date < appointment.start_date)`, + ) + .getRawOne(), + qb + .clone() + .select('COUNT(DISTINCT appointment.entity_id)::int', 'count') + .andWhere( + `NOT EXISTS (SELECT 1 FROM schedule_appointment fa ` + + `WHERE fa.schedule_id = appointment.schedule_id AND fa.entity_id IS NOT NULL ` + + `AND fa.entity_id = appointment.entity_id ` + + `AND fa.status != '${ScheduleAppointmentStatus.Canceled}' AND fa.start_date > appointment.start_date)`, + ) + .getRawOne(), + qb + .clone() + .select('COUNT(*)::int', 'count') + .andWhere('appointment.status IN (:...notTookPlaceStatuses)', { + notTookPlaceStatuses: [ScheduleAppointmentStatus.NotConfirmed, ScheduleAppointmentStatus.Confirmed], + }) + .andWhere('appointment.end_date < now()') + .getRawOne(), + ]); + + let total = 0; + const statuses: Record = { + not_confirmed: 0, + confirmed: 0, + completed: 0, + canceled: 0, + }; + for (const rawStatus of rawStatuses) { + total += rawStatus.count; + statuses[rawStatus.status] += rawStatus.count; + } + + return new ScheduleAppointmentStatistic({ + total, + newbies: newbies?.count ?? 0, + notScheduled: notScheduled?.count ?? 0, + notTookPlace: notTookPlace?.count ?? 0, + statuses, + }); + } + + async getEntityList({ + accountId, + user, + filter, + paging, + }: { + accountId: number; + user: User; + filter: ScheduleAppointmentFilterDto; + paging: PagingQuery; + }): Promise { + const period = DatePeriod.fromDto(filter); + const entities = await this.createFindQb({ + filter: { accountId, ...filter, from: period.from, to: period.to }, + options: { isSchedule: true }, + }) + .select('DISTINCT(appointment.entity_id)', 'entityId') + .andWhere('appointment.entity_id IS NOT NULL') + .orderBy('appointment.entity_id') + .offset(paging.skip) + .limit(paging.take) + .getRawMany<{ entityId: number }>(); + + return entities.length + ? this.entityBoardService.createListItems({ accountId, user, entityIds: entities.map((e) => e.entityId) }) + : []; + } + + async getEntityListMeta({ + accountId, + filter, + }: { + accountId: number; + filter: ScheduleAppointmentFilterDto; + }): Promise { + const period = DatePeriod.fromDto(filter); + const { cnt } = await this.createFindQb({ + filter: { accountId, ...filter, from: period.from, to: period.to }, + options: { isSchedule: true }, + }) + .select('COUNT(DISTINCT(appointment.entity_id))', 'cnt') + .andWhere('appointment.entity_id IS NOT NULL') + .getRawOne<{ cnt: number }>(); + + return { totalCount: cnt, hasPrice: false, totalPrice: 0 }; + } + + async getAvailableDates({ + accountId, + scheduleId, + performerId, + minDate, + maxDate, + daysLimit, + timezone, + }: { + accountId: number; + scheduleId: number; + performerId: number; + minDate: Date; + maxDate: Date; + daysLimit: number; + timezone: string; + }): Promise { + const spots = await this.getAvailableSpots({ accountId, scheduleId, performerId, minDate, maxDate, daysLimit }); + + const uniqueDates = new Set(); + + spots.forEach((spot) => { + uniqueDates.add(DateUtil.format(toZonedTime(spot.from, timezone), 'yyyy-MM-dd')); + uniqueDates.add(DateUtil.format(toZonedTime(spot.to, timezone), 'yyyy-MM-dd')); + }); + + return Array.from(uniqueDates); + } + + async getAvailableSpots({ + accountId, + scheduleId, + performerId, + minDate, + maxDate, + daysLimit, + }: { + accountId: number; + scheduleId: number; + performerId: number; + minDate: Date; + maxDate: Date; + daysLimit: number; + }): Promise { + const schedule = await this.scheduleService.findOne({ filter: { accountId, scheduleId } }); + if (!schedule?.timePeriod) { + return []; + } + const performer = schedule?.performers.find((p) => p.id === performerId); + if (!performer) { + return []; + } + + const settings = await this.getSpotScheduleSettings({ accountId, schedule, performer }); + + const now = DateUtil.now(); + const limitDate = DateUtil.add(now, { days: daysLimit }); + const from = now > minDate ? now : minDate; + const to = limitDate < maxDate ? limitDate : maxDate; + if (from >= to) { + return []; + } + + const spots = this.generateSpots({ from, to, seconds: schedule.timePeriod, settings }); + if (!spots.length) { + return []; + } + + const appointments = await this.createFindQb({ + filter: { accountId, scheduleId: schedule.id, performerId: performer.id, from, to }, + options: { isSchedule: true }, + }).getMany(); + + const occupiedSpots: Spot[] = appointments.map((a) => ({ + from: settings.timeBufferBefore ? DateUtil.sub(a.startDate, { seconds: settings.timeBufferBefore }) : a.startDate, + to: settings.timeBufferAfter ? DateUtil.add(a.endDate, { seconds: settings.timeBufferAfter }) : a.endDate, + })); + + return spots.filter((spot) => !occupiedSpots.some((occ) => occ.from < spot.to && occ.to > spot.from)); + } + private async getSpotScheduleSettings({ + accountId, + schedule, + performer, + }: { + accountId: number; + schedule: Schedule; + performer: SchedulePerformer; + }): Promise { + const workingTime = performer.userId + ? await this.workingTimeService.getForUser({ accountId, userId: performer.userId }) + : await this.workingTimeService.getForDepartment({ + accountId, + departmentId: performer.departmentId, + }); + + const settings: SpotScheduleSettings = { + timezone: workingTime.timeZone, + appointmentLimit: schedule.appointmentLimit, + timeBufferBefore: schedule.timeBufferBefore, + timeBufferAfter: schedule.timeBufferAfter, + intervals: undefined, + }; + + if (schedule.intervals?.length) { + settings.intervals = schedule.intervals.map((i) => ({ + dayOfWeek: i.dayOfWeek, + timeFrom: i.timeFrom, + timeTo: i.timeTo, + })); + } else if (performer.userId) { + const userCalendar = await this.userCalendarService.findOne({ accountId, userId: performer.userId }); + if (userCalendar) { + settings.appointmentLimit = userCalendar.appointmentLimit; + settings.timeBufferBefore = userCalendar.timeBufferBefore; + settings.timeBufferAfter = userCalendar.timeBufferAfter; + if (userCalendar.intervals?.length) { + settings.intervals = userCalendar.intervals.map((i) => ({ + dayOfWeek: i.dayOfWeek, + timeFrom: i.timeFrom, + timeTo: i.timeTo, + })); + } + } + } + + if (!settings.intervals?.length) { + settings.intervals = workingTime.workingDays?.map((d) => ({ + dayOfWeek: d, + timeFrom: workingTime.workingTimeFrom, + timeTo: workingTime.workingTimeTo, + })); + } + + return settings; + } + private generateSpots({ + from, + to, + seconds, + settings, + }: { + from: Date; + to: Date; + seconds: number; + settings: SpotScheduleSettings; + }): Spot[] { + const dates = this.getSpotDates({ from, to, timezone: settings.timezone }); + const spots: Spot[] = []; + for (const date of dates) { + for (const interval of settings.intervals) { + if (interval.dayOfWeek.toLowerCase() === date.dayOfWeek.toLowerCase()) { + const spotFrom = fromZonedTime(`${date.date} ${interval.timeFrom}`, settings.timezone); + const spotTo = fromZonedTime(`${date.date} ${interval.timeTo}`, settings.timezone); + const generated = this.generateTimeSpots({ from: spotFrom, to: spotTo, seconds }); + spots.push(...generated.filter((spot) => spot.from >= from && spot.to <= to)); + } + } + } + return spots.sort((a, b) => a.from.getTime() - b.from.getTime()); + } + private getSpotDates({ + from, + to, + timezone, + }: { + from: Date; + to: Date; + timezone: string; + }): { date: string; dayOfWeek: string }[] { + const dates: { date: string; dayOfWeek: string }[] = []; + let currentDate = from; + while (currentDate < to) { + const tzCurrentDate = toZonedTime(currentDate, timezone); + dates.push({ + date: DateUtil.format(tzCurrentDate, 'yyyy-MM-dd'), + dayOfWeek: DateUtil.format(tzCurrentDate, 'EEEE'), + }); + currentDate = DateUtil.add(currentDate, { days: 1 }); + } + const tzDateTo = toZonedTime(to, timezone); + dates.push({ date: DateUtil.format(tzDateTo, 'yyyy-MM-dd'), dayOfWeek: DateUtil.format(tzDateTo, 'EEEE') }); + return Array.from(new Map(dates.map((d) => [d.date, d])).values()); + } + private generateTimeSpots({ from, to, seconds }: { from: Date; to: Date; seconds: number }): Spot[] { + const spots: Spot[] = []; + let spotFrom = from; + let spotTo = DateUtil.add(spotFrom, { seconds }); + + while (spotTo <= to) { + spots.push({ from: spotFrom, to: spotTo }); + spotFrom = spotTo; + spotTo = DateUtil.add(spotTo, { seconds }); + } + return spots; + } + + async update({ + accountId, + user, + appointmentId, + dto, + skipPermissionCheck, + event, + }: { + accountId: number; + user: User; + appointmentId: number; + dto: UpdateScheduleAppointmentDto; + skipPermissionCheck?: boolean; + event?: ServiceEvent; + }): Promise { + const appointment = await this.getOne({ accountId, user, appointmentId }); + + return this.updateAppointment({ accountId, user, appointment, dto, skipPermissionCheck, event }); + } + + async updateAppointment({ + accountId, + user, + appointment, + dto, + skipPermissionCheck, + event, + }: { + accountId: number; + user: User; + appointment: ScheduleAppointment; + dto: UpdateScheduleAppointmentDto; + skipPermissionCheck?: boolean; + event?: ServiceEvent; + }): Promise { + if (!skipPermissionCheck && !appointment.userRights.canEdit) { + throw new ForbiddenError(); + } + + if (!appointment.hasChanges(dto)) { + return appointment; + } + + appointment = appointment.update(dto); + + if (appointment.endDate <= appointment.startDate) { + throw new AppointmentDatesConflictError(); + } + + if (dto.checkIntersection && (await this.hasIntersect(appointment))) { + throw new AppointmentIntersectionError(); + } + + if (appointment.entityId) { + await this.checkEntityDayLimit(appointment); + } + + await this.repository.save(appointment); + + this.eventEmitter.emit( + SchedulerEventType.ScheduleAppointmentUpdated, + new SchedulerAppointmentUpdatedEvent({ + source: ScheduleAppointment.name, + accountId, + ownerId: appointment.ownerId, + scheduleId: appointment.scheduleId, + performerId: appointment.performerId, + appointmentId: appointment.id, + externalId: appointment.externalId, + title: appointment.title, + comment: appointment.comment, + startDate: appointment.startDate, + endDate: appointment.endDate, + status: appointment.status, + entityId: appointment.entityId, + prevEvent: event, + }), + ); + + return this.expandOne({ accountId, user, appointment, expand: ['prevAppointmentCount', 'entityInfo', 'order'] }); + } + + async changePerformer(accountId: number, oldPerformerId: number, newPerformerId: number): Promise { + await this.repository.update({ accountId, performerId: oldPerformerId }, { performerId: newPerformerId }); + } + + async delete({ + accountId, + user, + filter, + event, + }: { + accountId: number; + user?: User | null; + filter: DeleteFilter; + event?: ServiceEvent; + }): Promise { + const appointments = await this.createFindQb({ filter: { accountId, ...filter } }).getMany(); + for (const appointment of appointments) { + if (!user || (await this.authService.check({ action: 'delete', user, authorizable: appointment }))) { + await this.repository.delete({ accountId, id: appointment.id }); + + this.eventEmitter.emit( + SchedulerEventType.ScheduleAppointmentDeleted, + new SchedulerAppointmentEvent({ + source: ScheduleAppointment.name, + accountId, + ownerId: appointment.ownerId, + scheduleId: appointment.scheduleId, + performerId: appointment.performerId, + appointmentId: appointment.id, + entityId: appointment.entityId, + externalId: appointment.externalId, + prevEvent: event, + }), + ); + } + } + } + + async handleUpsertExt(event: SchedulerAppointmentExtUpsertEvent): Promise { + const schedule = await this.scheduleService.findOne({ + filter: { accountId: event.accountId, scheduleId: event.scheduleId }, + }); + const user = await this.userService.findOne({ accountId: event.accountId, id: event.ownerId }); + if (schedule && user) { + const { accountId, appointmentId, externalId } = event; + let appointment = externalId + ? await this.findOne({ filter: { accountId, scheduleId: schedule.id, externalId, showCanceled: true } }) + : undefined; + if (!appointment && appointmentId) { + appointment = await this.findOne({ + filter: { accountId, scheduleId: schedule.id, appointmentId, showCanceled: true }, + }); + } + + if (appointment) { + return this.updateAppointment({ + accountId, + user, + appointment, + dto: { + title: event.title, + comment: event.comment, + startDate: event.startDate.toISOString(), + endDate: event.endDate.toISOString(), + status: event.status, + externalId, + }, + skipPermissionCheck: true, + event, + }); + } else { + const performer = schedule.performers.find((p) => p.id === event.performerId); + if (performer) { + return this.create({ + accountId, + user, + dto: { + scheduleId: schedule.id, + startDate: event.startDate.toISOString(), + endDate: event.endDate.toISOString(), + status: event.status, + title: event.title, + comment: event.comment, + performerId: performer.id, + checkIntersection: false, + ownerId: event.ownerId, + externalId, + }, + skipPermissionCheck: true, + event, + }); + } + } + } + return null; + } + + private createFindQb({ + filter, + options, + }: { + filter: FindFilter; + options?: FindOptions; + }): SelectQueryBuilder { + const qb = this.repository + .createQueryBuilder('appointment') + .where('appointment.account_id = :accountId', { accountId: filter.accountId }); + + if (options?.joinPerformer) { + qb.leftJoinAndMapOne( + 'appointment.performer', + SchedulePerformer, + 'performer', + 'appointment.performer_id = performer.id', + ); + } + + if (filter.appointmentId) { + if (Array.isArray(filter.appointmentId)) { + qb.andWhere('appointment.id IN (:...appointmentIds)', { appointmentIds: filter.appointmentId }); + } else { + qb.andWhere('appointment.id = :appointmentId', { appointmentId: filter.appointmentId }); + } + } + + if (filter.externalId) { + qb.andWhere('appointment.external_id = :externalId', { externalId: filter.externalId }); + } + + if (filter.scheduleId) { + qb.andWhere('appointment.schedule_id = :scheduleId', { scheduleId: filter.scheduleId }); + } + if (filter.entityId) { + qb.andWhere('appointment.entity_id = :entityId', { entityId: filter.entityId }); + } + if (filter.performerId) { + qb.andWhere('appointment.performer_id = :performerId', { performerId: filter.performerId }); + } + if (filter.status) { + if (Array.isArray(filter.status)) { + qb.andWhere('appointment.status IN (:...statuses)', { statuses: filter.status }); + } else { + qb.andWhere('appointment.status = :status', { status: filter.status }); + } + } else if (!filter.showCanceled) { + qb.andWhere('appointment.status != :status', { status: ScheduleAppointmentStatus.Canceled }); + } + if (filter.title) { + qb.andWhere('appointment.title ILIKE :title', { title: `%${filter.title}%` }); + } + + if (options?.isSchedule) { + if (filter.from && filter.to) { + // eslint-disable-next-line max-len, prettier/prettier + qb.andWhere('appointment.start_date < :to', { to: filter.to }).andWhere('appointment.end_date > :from', { from: filter.from }); + } else if (filter.from) { + qb.andWhere('appointment.end_date >= :from', { from: filter.from }); + } else if (filter.to) { + qb.andWhere('appointment.start_date <= :to', { to: filter.to }); + } + } else { + if (filter.from) { + qb.andWhere('appointment.start_date >= :from', { from: filter.from }); + } + if (filter.to) { + qb.andWhere('appointment.end_date <= :to', { to: filter.to }); + } + } + if (filter.isNewbie && filter.from) { + qb.andWhere('appointment.entityId IS NOT NULL').andWhere( + `NOT EXISTS (SELECT 1 FROM schedule_appointment ia WHERE ia.schedule_id = appointment.schedule_id AND ` + + `ia.entity_id IS NOT NULL AND ia.entity_id = appointment.entity_id ` + + `AND ia.status != '${ScheduleAppointmentStatus.Canceled}' AND ia.end_date < appointment.start_date)`, + ); + } + if (filter.isNotScheduled) { + qb.andWhere('appointment.entityId IS NOT NULL').andWhere( + `NOT EXISTS (SELECT 1 FROM schedule_appointment fa WHERE fa.schedule_id = appointment.schedule_id AND ` + + `fa.entity_id IS NOT NULL AND fa.entity_id = appointment.entity_id ` + + `AND fa.status != '${ScheduleAppointmentStatus.Canceled}' AND fa.start_date > appointment.start_date)`, + ); + } + if (filter.isNotTookPlace) { + qb.andWhere('appointment.status IN (:...filterNotTookPlaceStatuses)', { + filterNotTookPlaceStatuses: [ScheduleAppointmentStatus.NotConfirmed, ScheduleAppointmentStatus.Confirmed], + }).andWhere('appointment.end_date < now()'); + } + + return qb; + } + + private async countPrevAppointment(accountId: number, appointment: ScheduleAppointment): Promise { + return appointment.entityId + ? this.repository + .createQueryBuilder('appointment') + .where('appointment.account_id = :accountId', { accountId }) + .andWhere('appointment.entity_id = :entityId', { entityId: appointment.entityId }) + .andWhere('appointment.schedule_id = :scheduleId', { scheduleId: appointment.scheduleId }) + .andWhere('appointment.status != :canceled', { canceled: ScheduleAppointmentStatus.Canceled }) + .andWhere('appointment.end_date <= :endDate', { endDate: appointment.endDate }) + .getCount() + : 1; + } + + private async hasIntersect({ + id, + accountId, + scheduleId, + performerId, + startDate, + endDate, + }: ScheduleAppointment): Promise { + const appointments = await this.createFindQb({ + filter: { accountId, scheduleId, performerId, from: startDate, to: endDate }, + options: { isSchedule: true }, + }).getMany(); + + return appointments.filter((a) => a.id !== id).length > 0; + } + + private async checkEntityDayLimit({ id, accountId, scheduleId, entityId, startDate, endDate }: ScheduleAppointment) { + const schedule = await this.scheduleService.findOne({ filter: { accountId, scheduleId } }); + if (schedule.oneEntityPerDay) { + const other = await this.createFindQb({ + filter: { + accountId, + scheduleId, + entityId, + from: DateUtil.startOf(startDate, 'day'), + to: DateUtil.endOf(endDate, 'day'), + }, + }).getOne(); + if (other && other.id !== id) { + throw new AppointmentDayLimitError({ appointmentId: other.id }); + } + } + } + + private async expandOne({ + accountId, + user, + appointment, + expand, + }: { + accountId: number; + user: User; + appointment: ScheduleAppointment; + expand: ExpandableField[]; + }): Promise { + if (expand.includes('prevAppointmentCount')) { + appointment.prevAppointmentCount = await this.countPrevAppointment(accountId, appointment); + } + if (expand.includes('order') && appointment.orderId) { + appointment.order = await this.orderService.findOne( + accountId, + user, + { orderId: appointment.orderId }, + { expand: ['items', 'shipments'] }, + ); + } + if (expand.includes('entityInfo') && appointment.entityId) { + appointment.entityInfo = await this.entityInfoService.findOne({ + accountId, + user, + entityId: appointment.entityId, + }); + } + return appointment; + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/types/expandable-field.ts b/backend/src/modules/scheduler/schedule-appointment/types/expandable-field.ts new file mode 100644 index 0000000..fc91247 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'prevAppointmentCount' | 'order' | 'entityInfo'; diff --git a/backend/src/modules/scheduler/schedule-appointment/types/index.ts b/backend/src/modules/scheduler/schedule-appointment/types/index.ts new file mode 100644 index 0000000..360c635 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/types/index.ts @@ -0,0 +1,4 @@ +export * from './expandable-field'; +export * from './schedule-appointment-result'; +export * from './schedule-appointment-statistic'; +export * from './spot'; diff --git a/backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-result.ts b/backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-result.ts new file mode 100644 index 0000000..b63e2ef --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-result.ts @@ -0,0 +1,23 @@ +import { PagingMeta } from '@/common'; + +import { ScheduleAppointmentResultDto } from '../dto/schedule-appointment-result.dto'; +import { type ScheduleAppointment } from '../entities/schedule-appointment.entity'; + +export class ScheduleAppointmentResult { + appointments: ScheduleAppointment[]; + offset: number; + total: number; + + constructor(appointments: ScheduleAppointment[], offset: number, total: number) { + this.appointments = appointments; + this.offset = offset; + this.total = total; + } + + public toDto(): ScheduleAppointmentResultDto { + return new ScheduleAppointmentResultDto( + this.appointments.map((appointment) => appointment.toDto()), + new PagingMeta(this.offset, this.total), + ); + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-statistic.ts b/backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-statistic.ts new file mode 100644 index 0000000..add5e1d --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/types/schedule-appointment-statistic.ts @@ -0,0 +1,40 @@ +import { ScheduleAppointmentStatus } from '../../common'; +import { ScheduleAppointmentStatisticDto } from '../dto'; + +export class ScheduleAppointmentStatistic { + total: number; + statuses: Record; + newbies: number; + notScheduled: number; + notTookPlace: number; + + constructor({ + total, + statuses, + newbies, + notScheduled, + notTookPlace, + }: { + total: number; + statuses: Record; + newbies: number; + notScheduled: number; + notTookPlace: number; + }) { + this.total = total; + this.statuses = statuses; + this.newbies = newbies; + this.notScheduled = notScheduled; + this.notTookPlace = notTookPlace; + } + + public toDto(): ScheduleAppointmentStatisticDto { + return { + total: this.total, + statuses: this.statuses, + newbies: this.newbies, + notScheduled: this.notScheduled, + notTookPlace: this.notTookPlace, + }; + } +} diff --git a/backend/src/modules/scheduler/schedule-appointment/types/spot.ts b/backend/src/modules/scheduler/schedule-appointment/types/spot.ts new file mode 100644 index 0000000..d72a7ae --- /dev/null +++ b/backend/src/modules/scheduler/schedule-appointment/types/spot.ts @@ -0,0 +1,4 @@ +export class Spot { + from: Date; + to: Date; +} diff --git a/backend/src/modules/scheduler/schedule-performer/dtos/create-schedule-performer.dto.ts b/backend/src/modules/scheduler/schedule-performer/dtos/create-schedule-performer.dto.ts new file mode 100644 index 0000000..1c5cbe9 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/dtos/create-schedule-performer.dto.ts @@ -0,0 +1,4 @@ +import { OmitType } from '@nestjs/swagger'; +import { SchedulePerformerDto } from './schedule-performer.dto'; + +export class CreateSchedulePerformerDto extends OmitType(SchedulePerformerDto, ['id'] as const) {} diff --git a/backend/src/modules/scheduler/schedule-performer/dtos/index.ts b/backend/src/modules/scheduler/schedule-performer/dtos/index.ts new file mode 100644 index 0000000..e5c9134 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/dtos/index.ts @@ -0,0 +1,3 @@ +export * from './create-schedule-performer.dto'; +export * from './schedule-performer.dto'; +export * from './update-schedule-performer.dto'; diff --git a/backend/src/modules/scheduler/schedule-performer/dtos/schedule-performer.dto.ts b/backend/src/modules/scheduler/schedule-performer/dtos/schedule-performer.dto.ts new file mode 100644 index 0000000..a463149 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/dtos/schedule-performer.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { SchedulePerformerType } from '../enums'; + +export class SchedulePerformerDto { + @ApiProperty({ description: 'Schedule performer ID' }) + @IsNumber() + id: number; + + @ApiProperty({ enum: SchedulePerformerType, description: 'Schedule performer type' }) + @IsEnum(SchedulePerformerType) + type: SchedulePerformerType; + + @ApiProperty({ nullable: true, description: 'User ID if performer is User' }) + @IsOptional() + @IsNumber() + userId?: number | null; + + @ApiProperty({ nullable: true, description: 'Department ID if performer is Department' }) + @IsOptional() + @IsNumber() + departmentId?: number | null; +} diff --git a/backend/src/modules/scheduler/schedule-performer/dtos/update-schedule-performer.dto.ts b/backend/src/modules/scheduler/schedule-performer/dtos/update-schedule-performer.dto.ts new file mode 100644 index 0000000..85a4c09 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/dtos/update-schedule-performer.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from '@nestjs/swagger'; + +import { CreateSchedulePerformerDto } from './create-schedule-performer.dto'; + +export class UpdateSchedulePerformerDto extends PartialType(CreateSchedulePerformerDto) {} diff --git a/backend/src/modules/scheduler/schedule-performer/entities/index.ts b/backend/src/modules/scheduler/schedule-performer/entities/index.ts new file mode 100644 index 0000000..f11b72b --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/entities/index.ts @@ -0,0 +1 @@ +export * from './schedule-performer.entity'; diff --git a/backend/src/modules/scheduler/schedule-performer/entities/schedule-performer.entity.ts b/backend/src/modules/scheduler/schedule-performer/entities/schedule-performer.entity.ts new file mode 100644 index 0000000..d264425 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/entities/schedule-performer.entity.ts @@ -0,0 +1,64 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { CreateSchedulePerformerDto, UpdateSchedulePerformerDto, SchedulePerformerDto } from '../dtos'; +import { SchedulePerformerType } from '../enums'; + +@Entity() +export class SchedulePerformer { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + scheduleId: number; + + @Column() + type: SchedulePerformerType; + + @Column({ nullable: true }) + userId: number | null; + + @Column({ nullable: true }) + departmentId: number | null; + + constructor( + accountId: number, + scheduleId: number, + type: SchedulePerformerType, + userId: number | null, + departmentId: number | null, + ) { + this.accountId = accountId; + this.scheduleId = scheduleId; + this.type = type; + this.userId = userId; + this.departmentId = departmentId; + } + + public static fromDto(accountId: number, scheduleId: number, dto: CreateSchedulePerformerDto): SchedulePerformer { + return new SchedulePerformer(accountId, scheduleId, dto.type, dto.userId, dto.departmentId); + } + + public update(dto: UpdateSchedulePerformerDto): SchedulePerformer { + this.type = dto.type !== undefined ? dto.type : this.type; + this.userId = dto.userId !== undefined ? dto.userId : this.userId; + this.departmentId = dto.departmentId !== undefined ? dto.departmentId : this.departmentId; + + return this; + } + + public toDto(): SchedulePerformerDto { + return { id: this.id, type: this.type, userId: this.userId, departmentId: this.departmentId }; + } + + public equals(other: { type: SchedulePerformerType; userId?: number | null; departmentId?: number | null }): boolean { + switch (other.type) { + case SchedulePerformerType.Department: + return this.departmentId === other.departmentId; + case SchedulePerformerType.User: + return this.userId === other.userId; + } + } +} diff --git a/backend/src/modules/scheduler/schedule-performer/enums/index.ts b/backend/src/modules/scheduler/schedule-performer/enums/index.ts new file mode 100644 index 0000000..e825a7c --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/enums/index.ts @@ -0,0 +1 @@ +export * from './schedule-performer-type.enum'; diff --git a/backend/src/modules/scheduler/schedule-performer/enums/schedule-performer-type.enum.ts b/backend/src/modules/scheduler/schedule-performer/enums/schedule-performer-type.enum.ts new file mode 100644 index 0000000..b797684 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/enums/schedule-performer-type.enum.ts @@ -0,0 +1,4 @@ +export enum SchedulePerformerType { + User = 'user', + Department = 'department', +} diff --git a/backend/src/modules/scheduler/schedule-performer/index.ts b/backend/src/modules/scheduler/schedule-performer/index.ts new file mode 100644 index 0000000..f5ba5b5 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/index.ts @@ -0,0 +1,5 @@ +export * from './dtos'; +export * from './entities'; +export * from './enums'; +export * from './schedule-performer.handler'; +export * from './schedule-performer.service'; diff --git a/backend/src/modules/scheduler/schedule-performer/schedule-performer.handler.ts b/backend/src/modules/scheduler/schedule-performer/schedule-performer.handler.ts new file mode 100644 index 0000000..c5beaa3 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/schedule-performer.handler.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { DepartmentDeletedEvent, IamEventType, UserDeletedEvent } from '@/modules/iam/common'; + +import { SchedulePerformerService } from './schedule-performer.service'; + +@Injectable() +export class SchedulePerformerHandler { + constructor(private readonly service: SchedulePerformerService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.service.deletePerformer({ + accountId: event.accountId, + userId: event.userId, + newId: event.newUserId, + }); + } + + @OnEvent(IamEventType.DepartmentDeleted, { async: true }) + public async onDepartmentDeleted(event: DepartmentDeletedEvent) { + await this.service.deletePerformer({ + accountId: event.accountId, + departmentId: event.departmentId, + newId: event.newDepartmentId, + }); + } +} diff --git a/backend/src/modules/scheduler/schedule-performer/schedule-performer.service.ts b/backend/src/modules/scheduler/schedule-performer/schedule-performer.service.ts new file mode 100644 index 0000000..9eb427d --- /dev/null +++ b/backend/src/modules/scheduler/schedule-performer/schedule-performer.service.ts @@ -0,0 +1,214 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { BadRequestError, NotFoundError } from '@/common'; + +import { SchedulePerformerDeletedEvent, SchedulePerformerEvent, SchedulerEventType } from '../common'; +import { ScheduleAppointmentService } from '../schedule-appointment/schedule-appointment.service'; + +import { CreateSchedulePerformerDto, UpdateSchedulePerformerDto } from './dtos'; +import { SchedulePerformer } from './entities'; + +interface FindFilter { + accountId: number; + performerId?: number; + userId?: number; + departmentId?: number; + scheduleId?: number; +} + +@Injectable() +export class SchedulePerformerService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(SchedulePerformer) + private readonly repository: Repository, + @Inject(forwardRef(() => ScheduleAppointmentService)) + private readonly appointmentService: ScheduleAppointmentService, + ) {} + + public async create({ + accountId, + scheduleId, + dto, + }: { + accountId: number; + scheduleId: number; + dto: CreateSchedulePerformerDto; + }): Promise { + const performer = await this.repository.save(SchedulePerformer.fromDto(accountId, scheduleId, dto)); + + this.eventEmitter.emit( + SchedulerEventType.SchedulePerformerCreated, + new SchedulePerformerEvent({ source: SchedulePerformer.name, accountId, scheduleId, performerId: performer.id }), + ); + + return performer; + } + public async createMany({ + accountId, + scheduleId, + dtos, + }: { + accountId: number; + scheduleId: number; + dtos: CreateSchedulePerformerDto[]; + }): Promise { + return Promise.all(dtos.map((dto) => this.create({ accountId, scheduleId, dto }))); + } + + public async findOne(filter: FindFilter): Promise { + return this.createFindQb(filter).getOne(); + } + public async findMany(filter: FindFilter): Promise { + return await this.createFindQb(filter).orderBy('sp.id').getMany(); + } + + public async processMany({ + accountId, + scheduleId, + current, + dtos, + }: { + accountId: number; + scheduleId: number; + current: SchedulePerformer[]; + dtos: CreateSchedulePerformerDto[]; + }): Promise { + const created = dtos.filter((dto) => !current.some((p) => p.equals(dto))); + const deleted = current.filter((p) => !dtos.some((dto) => p.equals(dto))); + + if (created.length) { + current.push(...(await this.createMany({ accountId, scheduleId, dtos: created }))); + } + + if (deleted.length) { + await Promise.all(deleted.map((p) => this.delete({ accountId, scheduleId, performerId: p.id }))); + } + + return current.filter((p) => !deleted.some((r) => p.id === r.id)); + } + + public async update({ + accountId, + scheduleId, + performerId, + dto, + }: { + accountId: number; + scheduleId: number; + performerId: number; + dto: UpdateSchedulePerformerDto; + }): Promise { + const performer = await this.findOne({ accountId, scheduleId, performerId }); + if (!performer) { + throw NotFoundError.withId(SchedulePerformer, performerId); + } + + await this.repository.save(performer.update(dto)); + + this.eventEmitter.emit( + SchedulerEventType.SchedulePerformerUpdated, + new SchedulePerformerEvent({ source: SchedulePerformer.name, accountId, scheduleId, performerId }), + ); + + return performer; + } + + public async delete({ + accountId, + scheduleId, + performerId, + newPerformerId, + }: { + accountId: number; + scheduleId: number; + performerId: number; + newPerformerId?: number; + }) { + if (newPerformerId) { + await this.appointmentService.changePerformer(accountId, performerId, newPerformerId); + } + await this.repository.delete({ accountId, scheduleId, id: performerId }); + + this.eventEmitter.emit( + SchedulerEventType.SchedulePerformerDeleted, + new SchedulePerformerDeletedEvent({ + source: SchedulePerformer.name, + accountId, + scheduleId, + performerId, + newPerformerId, + }), + ); + } + + public async deletePerformer({ + accountId, + userId, + departmentId, + newId, + }: { + accountId: number; + userId?: number | null; + departmentId?: number | null; + newId?: number | null; + }) { + if ((!userId && !departmentId) || (userId && departmentId)) { + throw new BadRequestError('userId or departmentId must be specified'); + } + const performers = await this.findMany({ accountId, userId, departmentId }); + if (performers.length) { + if (newId) { + await Promise.all( + performers.map(async (performer) => { + const coPerformers = await this.findMany({ accountId, scheduleId: performer.scheduleId }); + const newPerformer = coPerformers.find( + (p) => (userId && p.userId === newId) || (departmentId && p.departmentId === newId), + ); + if (newPerformer) { + await this.delete({ + accountId, + scheduleId: performer.scheduleId, + performerId: performer.id, + newPerformerId: newPerformer.id, + }); + } else { + await this.update({ + accountId, + scheduleId: performer.scheduleId, + performerId: performer.id, + dto: { userId: userId ? newId : undefined, departmentId: departmentId ? newId : undefined }, + }); + } + }), + ); + } else { + await Promise.all( + performers.map((p) => this.delete({ accountId, scheduleId: p.scheduleId, performerId: p.id })), + ); + } + } + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('sp') + .where('sp.account_id = :accountId', { accountId: filter.accountId }); + if (filter?.performerId) { + qb.andWhere('sp.id = :id', { id: filter.performerId }); + } + if (filter?.userId) { + qb.andWhere('sp.user_id = :userId', { userId: filter.userId }); + } + if (filter?.departmentId) { + qb.andWhere('sp.department_id = :departmentId', { departmentId: filter.departmentId }); + } + if (filter?.scheduleId) { + qb.andWhere('sp.schedule_id = :scheduleId', { scheduleId: filter.scheduleId }); + } + return qb; + } +} diff --git a/backend/src/modules/scheduler/schedule-reporting/dto/index.ts b/backend/src/modules/scheduler/schedule-reporting/dto/index.ts new file mode 100644 index 0000000..a8335fa --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/dto/index.ts @@ -0,0 +1,3 @@ +export * from './schedule-report-filter.dto'; +export * from './schedule-report-row.dto'; +export * from './schedule-report.dto'; diff --git a/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-filter.dto.ts b/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-filter.dto.ts new file mode 100644 index 0000000..ff88d73 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-filter.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter } from '@/common'; +import { ScheduleReportType } from '../enums'; + +export class ScheduleReportFilterDto { + @ApiProperty({ enum: ScheduleReportType, description: 'Report type' }) + @IsEnum(ScheduleReportType) + type: ScheduleReportType; + + @ApiProperty({ description: 'Schedule ID' }) + @IsNumber() + scheduleId: number; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Board IDs filter' }) + @IsOptional() + @IsNumber({}, { each: true }) + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs filter' }) + @IsOptional() + @IsNumber({}, { each: true }) + userIds?: number[] | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period filter' }) + @IsOptional() + period?: DatePeriodFilter | null; +} diff --git a/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-row.dto.ts b/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-row.dto.ts new file mode 100644 index 0000000..2f77109 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report-row.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import { QuantityAmountDto } from '@/common'; + +export class ScheduleReportRowDto { + @ApiProperty({ nullable: true, description: 'Owner ID' }) + @IsOptional() + @IsNumber() + ownerId: number | null; + + @ApiProperty({ nullable: true, description: 'Owner name' }) + @IsOptional() + @IsString() + ownerName: string | null; + + @ApiProperty({ type: QuantityAmountDto, description: 'Sold' }) + sold: QuantityAmountDto; + + @ApiProperty({ description: 'All' }) + @IsNumber() + all: number; + + @ApiProperty({ description: 'Scheduled' }) + @IsNumber() + scheduled: number; + + @ApiProperty({ description: 'Confirmed' }) + @IsNumber() + confirmed: number; + + @ApiProperty({ description: 'Completed' }) + @IsNumber() + completed: number; + + @ApiProperty({ description: 'Canceled' }) + @IsNumber() + canceled: number; +} diff --git a/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report.dto.ts b/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report.dto.ts new file mode 100644 index 0000000..53cb331 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/dto/schedule-report.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ScheduleReportRowDto } from './schedule-report-row.dto'; + +export class ScheduleReportDto { + @ApiProperty({ type: [ScheduleReportRowDto], description: 'Rows' }) + rows: ScheduleReportRowDto[]; + + @ApiProperty({ type: ScheduleReportRowDto, description: 'Total' }) + total: ScheduleReportRowDto; +} diff --git a/backend/src/modules/scheduler/schedule-reporting/enums/index.ts b/backend/src/modules/scheduler/schedule-reporting/enums/index.ts new file mode 100644 index 0000000..7803868 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/enums/index.ts @@ -0,0 +1 @@ +export * from './schedule-report-type.enum'; diff --git a/backend/src/modules/scheduler/schedule-reporting/enums/schedule-report-type.enum.ts b/backend/src/modules/scheduler/schedule-reporting/enums/schedule-report-type.enum.ts new file mode 100644 index 0000000..1617c60 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/enums/schedule-report-type.enum.ts @@ -0,0 +1,6 @@ +export enum ScheduleReportType { + Client = 'client', + Department = 'department', + Owner = 'owner', + Performer = 'performer', +} diff --git a/backend/src/modules/scheduler/schedule-reporting/index.ts b/backend/src/modules/scheduler/schedule-reporting/index.ts new file mode 100644 index 0000000..d19252e --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './enums'; +export * from './schedule-reporting.controller'; +export * from './schedule-reporting.service'; +export * from './types'; diff --git a/backend/src/modules/scheduler/schedule-reporting/schedule-reporting.controller.ts b/backend/src/modules/scheduler/schedule-reporting/schedule-reporting.controller.ts new file mode 100644 index 0000000..27e3ef6 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/schedule-reporting.controller.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { ScheduleReportDto, ScheduleReportFilterDto } from './dto'; +import { ScheduleReportingService } from './schedule-reporting.service'; + +@ApiTags('scheduler/reporting') +@Controller('reporting') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class ScheduleReportingController { + constructor(private readonly service: ScheduleReportingService) {} + + @ApiOperation({ summary: 'Get schedule report', description: 'Get schedule report' }) + @ApiBody({ type: ScheduleReportFilterDto, description: 'Schedule report filter' }) + @ApiOkResponse({ description: 'Schedule report', type: ScheduleReportDto }) + @Post('schedule') + public async getReport(@CurrentAuth() { accountId, user }: AuthData, @Body() filter: ScheduleReportFilterDto) { + return this.service.getReport({ accountId, user, filter }); + } +} diff --git a/backend/src/modules/scheduler/schedule-reporting/schedule-reporting.service.ts b/backend/src/modules/scheduler/schedule-reporting/schedule-reporting.service.ts new file mode 100644 index 0000000..5e909c5 --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/schedule-reporting.service.ts @@ -0,0 +1,249 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DatePeriod, ForbiddenError, intersection, propagateData, QuantityAmount } from '@/common'; +import { AuthorizationService } from '@/modules/iam/authorization'; +import { DepartmentService } from '@/modules/iam/department'; +import { User } from '@/modules/iam/user/entities'; +import { FieldType } from '@/modules/entity/entity-field/common'; +import { BoardStageService, GroupedStages } from '@/CRM/board-stage'; + +import { ScheduleAppointmentStatus } from '../common'; +import { Schedule, ScheduleService } from '../schedule'; +import { ScheduleAppointment } from '../schedule-appointment'; +import { SchedulePerformer, SchedulePerformerType } from '../schedule-performer'; + +import { ScheduleReportFilterDto } from './dto'; +import { ScheduleReportType } from './enums'; +import { ScheduleReport, ScheduleReportRow } from './types'; + +interface ScheduleReportFilter { + stages: GroupedStages; + boardIds?: number[]; + period?: DatePeriod; + userIds?: number[]; + departmentIds?: number[]; +} + +interface RawReportRow { + owner_id: number; + owner_name: string; + sold_quantity: number; + sold_amount: number; + all: number; + scheduled: number; + confirmed: number; + completed: number; + canceled: number; +} + +@Injectable() +export class ScheduleReportingService { + constructor( + @InjectRepository(ScheduleAppointment) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly departmentService: DepartmentService, + private readonly stageService: BoardStageService, + private readonly scheduleService: ScheduleService, + ) {} + + public async getReport({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: ScheduleReportFilterDto; + }) { + const { allow, userIds, departmentIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: Schedule.getAuthorizable(filter.scheduleId), + }); + if (!allow) { + throw new ForbiddenError(); + } + + const { entityTypeId } = await this.scheduleService.findOne({ + filter: { accountId, scheduleId: filter.scheduleId }, + }); + + const rowFilter = { + stages: await this.stageService.getGroupedByType({ + accountId, + entityTypeId, + boardId: filter.boardIds?.length ? filter.boardIds : undefined, + }), + boardIds: filter.boardIds, + period: filter.period ? DatePeriod.fromFilter(filter.period) : undefined, + userIds: filter.userIds?.length ? intersection(filter.userIds, userIds) : userIds, + departmentIds, + }; + const rows = await this.getRows({ + accountId, + scheduleId: filter.scheduleId, + type: filter.type, + filter: rowFilter, + isTotal: false, + }); + const total = await this.getRows({ + accountId, + scheduleId: filter.scheduleId, + type: filter.type, + filter: rowFilter, + isTotal: true, + }); + + const report = new ScheduleReport(rows, total.values().next().value); + + if (filter.type === ScheduleReportType.Department && report.rows.size) { + const hierarchy = await this.departmentService.getHierarchy({ accountId }); + + if (hierarchy.length) { + propagateData(hierarchy, report.rows, ScheduleReportRow.empty); + } + } + + return report; + } + + private async getRows({ + accountId, + scheduleId, + type, + filter, + isTotal, + }: { + accountId: number; + scheduleId: number; + type: ScheduleReportType; + filter: ScheduleReportFilter; + isTotal: boolean; + }): Promise> { + const rowMap = new Map(); + const rows = await this.createQb({ accountId, scheduleId, type, filter, isTotal }).getRawMany(); + for (const row of rows) { + rowMap.set( + row.owner_id, + new ScheduleReportRow( + row.owner_id, + row.owner_name, + new QuantityAmount(row.sold_quantity, row.sold_amount ? Number(row.sold_amount) : 0), + row.all, + row.scheduled, + row.confirmed, + row.completed, + row.canceled, + ), + ); + } + + return rowMap; + } + + private createQb({ + accountId, + scheduleId, + type, + filter, + isTotal, + }: { + accountId: number; + scheduleId: number; + type: ScheduleReportType; + filter: ScheduleReportFilter; + isTotal: boolean; + }) { + const qb = this.repository + .createQueryBuilder('sa') + .innerJoin(SchedulePerformer, 'sp', 'sa.performer_id = sp.id') + .leftJoin('users', 'pu', 'sp.user_id = pu.id') + .leftJoin('users', 'ou', 'sa.owner_id = ou.id') + .leftJoin('entity', 'e', 'sa.entity_id = e.id') + .leftJoin('users', 'eu', 'e.responsible_user_id = eu.id') + .select(`count(*)::int`, 'all') + .addSelect(`count(*) filter (where sa.status = '${ScheduleAppointmentStatus.NotConfirmed}')::int`, 'scheduled') + .addSelect(`count(*) filter (where sa.status = '${ScheduleAppointmentStatus.Confirmed}')::int`, 'confirmed') + .addSelect(`count(*) filter (where sa.status = '${ScheduleAppointmentStatus.Completed}')::int`, 'completed') + .addSelect(`count(*) filter (where sa.status = '${ScheduleAppointmentStatus.Canceled}')::int`, 'canceled') + .where('sa.account_id = :accountId', { accountId }) + .andWhere('sa.schedule_id = :scheduleId', { scheduleId }); + + if (isTotal) { + qb.addSelect('0::int', 'owner_id'); + } else { + switch (type) { + case ScheduleReportType.Performer: + qb.addSelect('sp.user_id', 'owner_id').groupBy('sp.user_id'); + break; + case ScheduleReportType.Owner: + qb.addSelect('sa.owner_id', 'owner_id').groupBy('sa.owner_id'); + break; + case ScheduleReportType.Department: + qb.addSelect( + `case when sp.type = '${SchedulePerformerType.Department}' then sp.department_id else pu.department_id end`, + 'owner_id', + ).groupBy( + `case when sp.type = '${SchedulePerformerType.Department}' then sp.department_id else pu.department_id end`, + ); + break; + case ScheduleReportType.Client: + qb.addSelect('e.id', 'owner_id').addSelect('e.name', 'owner_name').groupBy('e.id').addGroupBy('e.name'); + break; + } + } + + if (filter.stages.won?.length) { + qb.addSelect( + `count(*) filter (where e.stage_id = any(array[${filter.stages.won.join(',')}]))::int`, + 'sold_quantity', + ) + .addSelect(`sum(cast(fv.payload::json->>'value' as decimal))::decimal`, 'sold_amount') + .leftJoin('field_value', 'fv', `fv.entity_id = e.id and fv.field_type = '${FieldType.Value}'`); + } + + if (filter.userIds?.length) { + if (type === ScheduleReportType.Performer) { + qb.andWhere('sp.user_id IN (:...userIds)', { userIds: filter.userIds }); + } else if (type === ScheduleReportType.Owner) { + qb.andWhere('sa.owner_id IN (:...userIds)', { userIds: filter.userIds }); + } else if (type === ScheduleReportType.Client) { + qb.andWhere('e.responsible_user_id IN (:...userIds)', { userIds: filter.userIds }); + } + } + + if (filter.departmentIds?.length) { + if (type === ScheduleReportType.Performer) { + qb.andWhere('pu.department_id IN (:...departmentIds)', { departmentIds: filter.departmentIds }); + } else if (type === ScheduleReportType.Owner) { + qb.andWhere('ou.department_id IN (:...departmentIds)', { departmentIds: filter.departmentIds }); + } else if (type === ScheduleReportType.Client) { + qb.andWhere('eu.department_id IN (:...departmentIds)', { departmentIds: filter.departmentIds }); + } else if (type === ScheduleReportType.Department) { + qb.andWhere( + // eslint-disable-next-line max-len + `case when sp.type = '${SchedulePerformerType.Department}' then sp.department_id else pu.department_id end IN (:...departmentIds)`, + { departmentIds: filter.departmentIds }, + ); + } + } + + if (filter.period) { + if (filter.period.from) { + qb.andWhere('sa.start_date >= :from', { from: filter.period.from }); + } + if (filter.period.to) { + qb.andWhere('sa.end_date <= :to', { to: filter.period.to }); + } + } + + if (filter.boardIds?.length) { + qb.andWhere('e.board_id IN (:...boardIds)', { boardIds: filter.boardIds }); + } + + return qb; + } +} diff --git a/backend/src/modules/scheduler/schedule-reporting/types/index.ts b/backend/src/modules/scheduler/schedule-reporting/types/index.ts new file mode 100644 index 0000000..6c72a3a --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/types/index.ts @@ -0,0 +1,2 @@ +export * from './schedule-report-row'; +export * from './schedule-report'; diff --git a/backend/src/modules/scheduler/schedule-reporting/types/schedule-report-row.ts b/backend/src/modules/scheduler/schedule-reporting/types/schedule-report-row.ts new file mode 100644 index 0000000..91baa9e --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/types/schedule-report-row.ts @@ -0,0 +1,62 @@ +import { QuantityAmount } from '@/common'; + +import { ScheduleReportRowDto } from '../dto'; + +export class ScheduleReportRow { + ownerId: number; + ownerName: string | null; + sold: QuantityAmount; + all: number; + scheduled: number; + confirmed: number; + completed: number; + canceled: number; + + constructor( + ownerId: number, + ownerName: string | null, + sold: QuantityAmount, + all: number, + scheduled: number, + confirmed: number, + completed: number, + canceled: number, + ) { + this.ownerId = ownerId; + this.ownerName = ownerName; + this.sold = sold; + this.all = all; + this.scheduled = scheduled; + this.confirmed = confirmed; + this.completed = completed; + this.canceled = canceled; + } + + public static empty(ownerId: number): ScheduleReportRow { + return new ScheduleReportRow(ownerId, null, QuantityAmount.empty(), 0, 0, 0, 0, 0); + } + + public toDto(): ScheduleReportRowDto { + return { + ownerId: this.ownerId, + ownerName: this.ownerName, + sold: this.sold.toDto(), + all: this.all, + scheduled: this.scheduled, + confirmed: this.confirmed, + completed: this.completed, + canceled: this.canceled, + }; + } + + public add(row: ScheduleReportRow): ScheduleReportRow { + this.sold.add(row.sold); + this.all += row.all; + this.scheduled += row.scheduled; + this.confirmed += row.confirmed; + this.completed += row.completed; + this.canceled += row.canceled; + + return this; + } +} diff --git a/backend/src/modules/scheduler/schedule-reporting/types/schedule-report.ts b/backend/src/modules/scheduler/schedule-reporting/types/schedule-report.ts new file mode 100644 index 0000000..81b5cab --- /dev/null +++ b/backend/src/modules/scheduler/schedule-reporting/types/schedule-report.ts @@ -0,0 +1,23 @@ +import { ScheduleReportDto } from '../dto'; +import { ScheduleReportRow } from './schedule-report-row'; + +export class ScheduleReport { + rows: Map; + total: ScheduleReportRow; + + constructor(rows: Map, total: ScheduleReportRow) { + this.rows = rows; + this.total = total; + } + + public static createEmptyRow(ownerId: number): ScheduleReportRow { + return ScheduleReportRow.empty(ownerId); + } + + public toDto(): ScheduleReportDto { + return { + rows: Array.from(this.rows.values()).map((row) => row.toDto()), + total: this.total.toDto(), + }; + } +} diff --git a/backend/src/modules/scheduler/schedule/dto/create-schedule.dto.ts b/backend/src/modules/scheduler/schedule/dto/create-schedule.dto.ts new file mode 100644 index 0000000..47ba647 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/dto/create-schedule.dto.ts @@ -0,0 +1,16 @@ +import { ApiPropertyOptional, OmitType } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { ScheduleDto } from './schedule.dto'; +import { CreateSchedulePerformerDto } from '../../schedule-performer'; + +export class CreateScheduleDto extends OmitType(ScheduleDto, ['id', 'createdAt', 'performers'] as const) { + @ApiPropertyOptional({ + type: [CreateSchedulePerformerDto], + nullable: true, + description: 'Available performers for schedule', + }) + @IsOptional() + @IsArray() + performers: CreateSchedulePerformerDto[] | null; +} diff --git a/backend/src/modules/scheduler/schedule/dto/index.ts b/backend/src/modules/scheduler/schedule/dto/index.ts new file mode 100644 index 0000000..1e82fdb --- /dev/null +++ b/backend/src/modules/scheduler/schedule/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-schedule.dto'; +export * from './schedule-filter.dto'; +export * from './schedule-time-interval.dto'; +export * from './schedule.dto'; +export * from './update-schedule.dto'; diff --git a/backend/src/modules/scheduler/schedule/dto/schedule-filter.dto.ts b/backend/src/modules/scheduler/schedule/dto/schedule-filter.dto.ts new file mode 100644 index 0000000..ced0d7c --- /dev/null +++ b/backend/src/modules/scheduler/schedule/dto/schedule-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class ScheduleFilterDto { + @ApiPropertyOptional({ description: 'EntityType ID' }) + @IsOptional() + @IsNumber() + entityTypeId?: number; +} diff --git a/backend/src/modules/scheduler/schedule/dto/schedule-time-interval.dto.ts b/backend/src/modules/scheduler/schedule/dto/schedule-time-interval.dto.ts new file mode 100644 index 0000000..72c3015 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/dto/schedule-time-interval.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class ScheduleTimeIntervalDto { + @ApiProperty({ description: 'Day of week', examples: ['Monday', 'Sunday'] }) + @IsString() + dayOfWeek: string; + + @ApiProperty({ description: 'Interval time from', examples: ['09:00', '21:00'] }) + @IsString() + timeFrom: string; + + @ApiProperty({ description: 'Interval time to', examples: ['09:00', '21:00'] }) + @IsString() + timeTo: string; +} diff --git a/backend/src/modules/scheduler/schedule/dto/schedule.dto.ts b/backend/src/modules/scheduler/schedule/dto/schedule.dto.ts new file mode 100644 index 0000000..44c051e --- /dev/null +++ b/backend/src/modules/scheduler/schedule/dto/schedule.dto.ts @@ -0,0 +1,70 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { SchedulePerformerDto } from '../../schedule-performer'; +import { ScheduleType } from '../enums'; +import { ScheduleTimeIntervalDto } from './schedule-time-interval.dto'; + +export class ScheduleDto { + @ApiProperty({ description: 'Schedule ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Schedule name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Schedule icon name' }) + @IsString() + icon: string; + + @ApiProperty({ enum: ScheduleType, description: 'Schedule type' }) + @IsEnum(ScheduleType) + type: ScheduleType; + + @ApiProperty({ description: 'Time slot size in seconds for Board schedule' }) + @IsNumber() + timePeriod: number; + + @ApiPropertyOptional({ nullable: true, description: 'Appointments limit for one time slot for Board schedule' }) + @IsOptional() + @IsNumber() + appointmentLimit?: number | null; + + @ApiPropertyOptional({ description: 'Time buffer before appointment in seconds' }) + @IsOptional() + @IsNumber() + timeBufferBefore?: number | null; + + @ApiPropertyOptional({ description: 'Time buffer after appointment in seconds' }) + @IsOptional() + @IsNumber() + timeBufferAfter?: number | null; + + @ApiPropertyOptional({ description: 'Allow only one entity per day' }) + @IsOptional() + @IsBoolean() + oneEntityPerDay?: boolean; + + @ApiPropertyOptional({ nullable: true, description: 'Linked EntityType ID' }) + @IsOptional() + @IsNumber() + entityTypeId?: number | null; + + @ApiPropertyOptional({ nullable: true, description: 'Linked ProductsSection ID' }) + @IsOptional() + @IsNumber() + productsSectionId?: number | null; + + @ApiProperty({ description: 'Date and time when the schedule was created in ISO format' }) + @IsString() + createdAt: string; + + @ApiProperty({ type: [SchedulePerformerDto], description: 'Schedule performers' }) + @IsArray() + performers: SchedulePerformerDto[]; + + @ApiPropertyOptional({ type: [ScheduleTimeIntervalDto], description: 'Schedule time intervals' }) + @IsOptional() + intervals?: ScheduleTimeIntervalDto[] | null; +} diff --git a/backend/src/modules/scheduler/schedule/dto/update-schedule.dto.ts b/backend/src/modules/scheduler/schedule/dto/update-schedule.dto.ts new file mode 100644 index 0000000..530fa75 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/dto/update-schedule.dto.ts @@ -0,0 +1,16 @@ +import { ApiPropertyOptional, OmitType, PartialType } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { ScheduleDto } from './schedule.dto'; +import { CreateSchedulePerformerDto } from '../../schedule-performer'; + +export class UpdateScheduleDto extends PartialType(OmitType(ScheduleDto, ['id', 'createdAt', 'performers'] as const)) { + @ApiPropertyOptional({ + type: [CreateSchedulePerformerDto], + nullable: true, + description: 'Available performers for schedule', + }) + @IsOptional() + @IsArray() + performers?: CreateSchedulePerformerDto[] | null; +} diff --git a/backend/src/modules/scheduler/schedule/entities/index.ts b/backend/src/modules/scheduler/schedule/entities/index.ts new file mode 100644 index 0000000..601a275 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/entities/index.ts @@ -0,0 +1,2 @@ +export * from './schedule-time-interval.entity'; +export * from './schedule.entity'; diff --git a/backend/src/modules/scheduler/schedule/entities/schedule-time-interval.entity.ts b/backend/src/modules/scheduler/schedule/entities/schedule-time-interval.entity.ts new file mode 100644 index 0000000..8f061f3 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/entities/schedule-time-interval.entity.ts @@ -0,0 +1,51 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { ScheduleTimeIntervalDto } from '../dto'; + +@Entity() +export class ScheduleTimeInterval { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + scheduleId: number; + + @Column() + dayOfWeek: string; + + @Column({ type: 'time' }) + timeFrom: string; + + @Column({ type: 'time' }) + timeTo: string; + + constructor(accountId: number, scheduleId: number, dayOfWeek: string, timeFrom: string, timeTo: string) { + this.accountId = accountId; + this.scheduleId = scheduleId; + this.dayOfWeek = dayOfWeek; + this.timeFrom = timeFrom; + this.timeTo = timeTo; + } + + static fromDto({ + accountId, + scheduleId, + dto, + }: { + accountId: number; + scheduleId: number; + dto: ScheduleTimeIntervalDto; + }): ScheduleTimeInterval { + return new ScheduleTimeInterval(accountId, scheduleId, dto.dayOfWeek, dto.timeFrom, dto.timeTo); + } + + toDto(): ScheduleTimeIntervalDto { + return { + dayOfWeek: this.dayOfWeek, + timeFrom: this.timeFrom.substring(0, 5), + timeTo: this.timeTo.substring(0, 5), + }; + } +} diff --git a/backend/src/modules/scheduler/schedule/entities/schedule.entity.ts b/backend/src/modules/scheduler/schedule/entities/schedule.entity.ts new file mode 100644 index 0000000..fc8870b --- /dev/null +++ b/backend/src/modules/scheduler/schedule/entities/schedule.entity.ts @@ -0,0 +1,152 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { Authorizable, SimpleAuthorizable } from '@/modules/iam/common'; + +import { PermissionObjectType } from '../../common'; +import { SchedulePerformer } from '../../schedule-performer'; + +import { CreateScheduleDto, ScheduleDto, UpdateScheduleDto } from '../dto'; +import { ScheduleType } from '../enums'; +import { ScheduleTimeInterval } from './schedule-time-interval.entity'; + +@Entity() +export class Schedule { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + name: string; + + @Column() + icon: string; + + @Column() + type: ScheduleType; + + @Column({ default: 1800 }) + timePeriod: number; + + @Column({ nullable: true }) + appointmentLimit: number | null; + + @Column({ nullable: true }) + timeBufferBefore: number | null; + + @Column({ nullable: true }) + timeBufferAfter: number | null; + + @Column({ default: false }) + oneEntityPerDay: boolean; + + @Column({ nullable: true }) + entityTypeId: number | null; + + @Column({ nullable: true }) + productsSectionId: number | null; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + accountId: number, + name: string, + icon: string, + type: ScheduleType, + timePeriod: number, + appointmentLimit: number | null, + timeBufferBefore: number | null, + timeBufferAfter: number | null, + oneEntityPerDay: boolean, + entityTypeId: number | null, + productsSectionId: number | null, + createdAt?: Date, + ) { + this.accountId = accountId; + this.name = name; + this.icon = icon; + this.type = type; + this.timePeriod = timePeriod; + this.appointmentLimit = appointmentLimit; + this.timeBufferBefore = timeBufferBefore; + this.timeBufferAfter = timeBufferAfter; + this.oneEntityPerDay = oneEntityPerDay; + this.entityTypeId = entityTypeId; + this.productsSectionId = productsSectionId; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _performers: SchedulePerformer[]; + get performers(): SchedulePerformer[] { + return this._performers; + } + set performers(value: SchedulePerformer[]) { + this._performers = value; + } + + private _intervals: ScheduleTimeInterval[] | null; + get intervals(): ScheduleTimeInterval[] | null { + return this._intervals; + } + set intervals(value: ScheduleTimeInterval[] | null) { + this._intervals = value; + } + + static fromDto({ accountId, dto }: { accountId: number; dto: CreateScheduleDto }): Schedule { + return new Schedule( + accountId, + dto.name, + dto.icon, + dto.type, + dto.timePeriod ?? 1800, + dto.appointmentLimit ?? null, + dto.timeBufferBefore ?? null, + dto.timeBufferAfter ?? null, + dto.oneEntityPerDay ?? false, + dto.entityTypeId, + dto.productsSectionId, + ); + } + + toDto(): ScheduleDto { + return { + id: this.id, + name: this.name, + icon: this.icon, + type: this.type, + timePeriod: this.timePeriod, + appointmentLimit: this.appointmentLimit, + timeBufferBefore: this.timeBufferBefore, + timeBufferAfter: this.timeBufferAfter, + oneEntityPerDay: this.oneEntityPerDay, + entityTypeId: this.entityTypeId, + productsSectionId: this.productsSectionId, + createdAt: this.createdAt.toISOString(), + performers: this._performers?.map((p) => p.toDto()), + intervals: this._intervals?.map((interval) => interval.toDto()), + }; + } + + update(dto: UpdateScheduleDto): Schedule { + this.name = dto.name !== undefined ? dto.name : this.name; + this.icon = dto.icon !== undefined ? dto.icon : this.icon; + this.type = dto.type !== undefined ? dto.type : this.type; + this.timePeriod = dto.timePeriod !== undefined ? dto.timePeriod : this.timePeriod; + this.appointmentLimit = dto.appointmentLimit !== undefined ? dto.appointmentLimit : this.appointmentLimit; + this.timeBufferBefore = dto.timeBufferBefore !== undefined ? dto.timeBufferBefore : this.timeBufferBefore; + this.timeBufferAfter = dto.timeBufferAfter !== undefined ? dto.timeBufferAfter : this.timeBufferAfter; + this.oneEntityPerDay = dto.oneEntityPerDay !== undefined ? dto.oneEntityPerDay : this.oneEntityPerDay; + this.entityTypeId = dto.entityTypeId !== undefined ? dto.entityTypeId : this.entityTypeId; + this.productsSectionId = dto.productsSectionId !== undefined ? dto.productsSectionId : this.productsSectionId; + + return this; + } + + static getAuthorizable(scheduleId: number): Authorizable { + return new SimpleAuthorizable({ type: PermissionObjectType.Schedule, id: scheduleId }); + } +} diff --git a/backend/src/modules/scheduler/schedule/enums/index.ts b/backend/src/modules/scheduler/schedule/enums/index.ts new file mode 100644 index 0000000..3ded87e --- /dev/null +++ b/backend/src/modules/scheduler/schedule/enums/index.ts @@ -0,0 +1 @@ +export * from './schedule-type.enum'; diff --git a/backend/src/modules/scheduler/schedule/enums/schedule-type.enum.ts b/backend/src/modules/scheduler/schedule/enums/schedule-type.enum.ts new file mode 100644 index 0000000..03931c6 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/enums/schedule-type.enum.ts @@ -0,0 +1,4 @@ +export enum ScheduleType { + Schedule = 'schedule', + Board = 'board', +} diff --git a/backend/src/modules/scheduler/schedule/index.ts b/backend/src/modules/scheduler/schedule/index.ts new file mode 100644 index 0000000..1f73968 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './schedule.controller'; +export * from './services'; diff --git a/backend/src/modules/scheduler/schedule/schedule.controller.ts b/backend/src/modules/scheduler/schedule/schedule.controller.ts new file mode 100644 index 0000000..cfa34ce --- /dev/null +++ b/backend/src/modules/scheduler/schedule/schedule.controller.ts @@ -0,0 +1,70 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized, UserAccess } from '@/modules/iam/common'; + +import { ScheduleDto, CreateScheduleDto, ScheduleFilterDto, UpdateScheduleDto } from './dto'; +import { ScheduleService } from './services'; + +@ApiTags('scheduler/schedule') +@Controller('schedules') +@JwtAuthorized() +@TransformToDto() +export class ScheduleController { + constructor(private readonly service: ScheduleService) {} + + @ApiOperation({ summary: 'Create schedule', description: 'Create schedule with performers' }) + @ApiBody({ description: 'Data for creating schedule', type: CreateScheduleDto }) + @ApiCreatedResponse({ description: 'Schedule', type: ScheduleDto }) + @UserAccess({ adminOnly: true }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateScheduleDto) { + return this.service.create({ accountId, userId, dto }); + } + + @ApiOperation({ summary: 'Get list of schedules', description: 'Get list of schedules with performers' }) + @ApiOkResponse({ description: 'List of schedules', type: [ScheduleDto] }) + @AuthDataPrefetch({ user: true }) + @Get() + async getMany(@CurrentAuth() { accountId, user }: AuthData, @Query() filter: ScheduleFilterDto) { + return this.service.findMany({ + user, + filter: { accountId, entityTypeId: filter.entityTypeId }, + checkPerformers: true, + }); + } + + @ApiOperation({ summary: 'Get schedule', description: 'Get schedule with performers' }) + @ApiParam({ name: 'scheduleId', description: 'Schedule ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Schedule', type: ScheduleDto }) + @AuthDataPrefetch({ user: true }) + @Get('/:scheduleId') + async getOne(@CurrentAuth() { accountId, user }: AuthData, @Param('scheduleId', ParseIntPipe) scheduleId: number) { + return this.service.findOne({ user, filter: { accountId, scheduleId }, checkPerformers: true }); + } + + @ApiOperation({ summary: 'Update schedule', description: 'Update schedule with performers' }) + @ApiBody({ description: 'Data for updating schedule', type: UpdateScheduleDto }) + @ApiParam({ name: 'scheduleId', description: 'Schedule ID', type: Number, required: true }) + @ApiOkResponse({ description: 'Schedules', type: ScheduleDto }) + @UserAccess({ adminOnly: true }) + @Put('/:scheduleId') + async update( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('scheduleId', ParseIntPipe) scheduleId: number, + @Body() dto: UpdateScheduleDto, + ) { + return this.service.update({ accountId, userId, scheduleId, dto }); + } + + @ApiOperation({ summary: 'Delete schedule', description: 'Delete schedule with performers and appointments' }) + @ApiParam({ name: 'scheduleId', description: 'Schedule ID', type: Number, required: true }) + @ApiOkResponse() + @UserAccess({ adminOnly: true }) + @Delete('/:scheduleId') + async delete(@CurrentAuth() { accountId, userId }: AuthData, @Param('scheduleId', ParseIntPipe) scheduleId: number) { + return this.service.delete({ accountId, userId, scheduleId }); + } +} diff --git a/backend/src/modules/scheduler/schedule/services/index.ts b/backend/src/modules/scheduler/schedule/services/index.ts new file mode 100644 index 0000000..8371b80 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/services/index.ts @@ -0,0 +1,2 @@ +export * from './schedule-time-interval.service'; +export * from './schedule.service'; diff --git a/backend/src/modules/scheduler/schedule/services/schedule-time-interval.service.ts b/backend/src/modules/scheduler/schedule/services/schedule-time-interval.service.ts new file mode 100644 index 0000000..a129b4c --- /dev/null +++ b/backend/src/modules/scheduler/schedule/services/schedule-time-interval.service.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ScheduleTimeIntervalDto } from '../dto'; +import { ScheduleTimeInterval } from '../entities'; + +interface FindFilter { + accountId: number; + scheduleId: number; +} + +@Injectable() +export class ScheduleTimeIntervalService { + constructor( + @InjectRepository(ScheduleTimeInterval) + private readonly repository: Repository, + ) {} + + async create({ + accountId, + scheduleId, + dto, + }: { + accountId: number; + scheduleId: number; + dto: ScheduleTimeIntervalDto; + }) { + return this.repository.save(ScheduleTimeInterval.fromDto({ accountId, scheduleId, dto })); + } + async createMany({ + accountId, + scheduleId, + dtos, + }: { + accountId: number; + scheduleId: number; + dtos: ScheduleTimeIntervalDto[]; + }) { + return Promise.all(dtos.map((dto) => this.create({ accountId, scheduleId, dto }))); + } + + async findMany(filter: FindFilter): Promise { + return this.createQb(filter).getMany(); + } + + async updateMany({ + accountId, + scheduleId, + dtos, + }: { + accountId: number; + scheduleId: number; + dtos: ScheduleTimeIntervalDto[]; + }) { + await this.deleteMany({ accountId, scheduleId }); + return this.createMany({ accountId, scheduleId, dtos }); + } + + async deleteMany({ accountId, scheduleId }: FindFilter) { + await this.repository.delete({ accountId, scheduleId }); + } + + private createQb({ accountId, scheduleId }: FindFilter) { + return this.repository + .createQueryBuilder('interval') + .where('interval.account_id = :accountId', { accountId }) + .andWhere('interval.schedule_id = :scheduleId', { scheduleId }); + } +} diff --git a/backend/src/modules/scheduler/schedule/services/schedule.service.ts b/backend/src/modules/scheduler/schedule/services/schedule.service.ts new file mode 100644 index 0000000..203d5e9 --- /dev/null +++ b/backend/src/modules/scheduler/schedule/services/schedule.service.ts @@ -0,0 +1,263 @@ +import { Inject, Injectable, forwardRef } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; + +import { ForbiddenError } from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { ProductsSectionService } from '@/modules/inventory/products-section/services/products-section.service'; + +import { ScheduleEvent, SchedulerEventType, ScheduleUpdatedEvent } from '../../common'; + +import { SchedulePerformerService, SchedulePerformer, SchedulePerformerType } from '../../schedule-performer'; + +import { CreateScheduleDto, UpdateScheduleDto } from '../dto'; +import { Schedule } from '../entities'; +import { ScheduleTimeIntervalService } from './schedule-time-interval.service'; + +interface FindFilter { + accountId: number; + scheduleId?: number; + entityTypeId?: number; +} + +@Injectable() +export class ScheduleService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(Schedule) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + @Inject(forwardRef(() => ProductsSectionService)) + private readonly productsSectionService: ProductsSectionService, + @Inject(forwardRef(() => SchedulePerformerService)) + private readonly performerService: SchedulePerformerService, + private readonly intervalService: ScheduleTimeIntervalService, + ) {} + + async create({ + accountId, + userId, + dto, + }: { + accountId: number; + userId: number; + dto: CreateScheduleDto; + }): Promise { + const schedule = await this.repository.save(Schedule.fromDto({ accountId, dto })); + + if (dto.performers) { + schedule.performers = await this.performerService.createMany({ + accountId, + scheduleId: schedule.id, + dtos: dto.performers, + }); + } + if (dto.intervals) { + schedule.intervals = await this.intervalService.createMany({ + accountId, + scheduleId: schedule.id, + dtos: dto.intervals, + }); + } + + if (dto.productsSectionId && dto.entityTypeId) { + await this.productsSectionService.ensureLinked(accountId, dto.productsSectionId, dto.entityTypeId); + } + + this.eventEmitter.emit( + SchedulerEventType.ScheduleCreated, + new ScheduleEvent({ accountId, userId, scheduleId: schedule.id }), + ); + + return schedule; + } + + async findOne({ + user, + filter, + checkPerformers, + }: { + user?: User | null; + filter: FindFilter; + checkPerformers?: boolean; + }): Promise { + const schedule = await this.createFindQb(filter).getOne(); + if (!schedule) return null; + + schedule.intervals = await this.intervalService.findMany({ accountId: filter.accountId, scheduleId: schedule.id }); + return user && checkPerformers ? this.filterPerformers({ user, schedule, throwError: true }) : schedule; + } + + async findMany({ + user, + filter, + checkPerformers, + }: { + user?: User | null; + filter: FindFilter; + checkPerformers?: boolean; + }): Promise { + const schedules = await this.createFindQb(filter).orderBy('schedule.created_at', 'DESC').getMany(); + if (!schedules.length) return []; + + await Promise.all( + schedules.map(async (schedule) => { + schedule.intervals = await this.intervalService.findMany({ + accountId: filter.accountId, + scheduleId: schedule.id, + }); + }), + ); + + return user && checkPerformers + ? (await Promise.all(schedules.map((schedule) => this.filterPerformers({ user, schedule })))).filter(Boolean) + : schedules; + } + + async update({ + accountId, + userId, + scheduleId, + dto, + }: { + accountId: number; + userId: number; + scheduleId: number; + dto: UpdateScheduleDto; + }): Promise { + const schedule = await this.findOne({ filter: { accountId, scheduleId } }); + + const typeChanged = dto.type && dto.type !== schedule.type; + const timePeriodChanged = dto.timePeriod && dto.timePeriod !== schedule.timePeriod; + + await this.repository.save(schedule.update(dto)); + + if (dto.performers) { + schedule.performers = await this.performerService.processMany({ + accountId, + scheduleId: schedule.id, + current: schedule.performers, + dtos: dto.performers, + }); + } + + if (dto.intervals) { + schedule.intervals = await this.intervalService.updateMany({ + accountId, + scheduleId: schedule.id, + dtos: dto.intervals, + }); + } + + if (dto.productsSectionId && dto.entityTypeId) { + await this.productsSectionService.ensureLinked(accountId, dto.productsSectionId, dto.entityTypeId); + } + + this.eventEmitter.emit( + SchedulerEventType.ScheduleUpdated, + new ScheduleUpdatedEvent({ accountId, userId, scheduleId: schedule.id, typeChanged, timePeriodChanged }), + ); + + return schedule; + } + + async delete({ + accountId, + userId, + scheduleId, + }: { + accountId: number; + userId: number; + scheduleId: number; + }): Promise { + await this.repository.delete({ accountId, id: scheduleId }); + + this.eventEmitter.emit(SchedulerEventType.ScheduleDeleted, new ScheduleEvent({ accountId, userId, scheduleId })); + } + + async getLinkedSchedulerIds( + accountId: number, + filter: { productsSectionId?: number; entityTypeId?: number }, + ): Promise { + const qb = this.repository + .createQueryBuilder('schedule') + .select('schedule.id', 'id') + .where('schedule.accountId = :accountId', { accountId }); + + if (filter.productsSectionId) { + qb.andWhere('schedule.products_section_id = :productsSectionId', { productsSectionId: filter.productsSectionId }); + } + if (filter.entityTypeId) { + qb.andWhere('schedule.entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + + return (await qb.getRawMany()).map((s) => s.id); + } + + async linkProductsSection(accountId: number, scheduleIds: number[] | null, productsSectionId: number): Promise { + await this.repository.update({ accountId, productsSectionId }, { productsSectionId: null }); + if (scheduleIds?.length > 0) { + await this.repository.update({ accountId, id: In(scheduleIds) }, { productsSectionId }); + } + } + + async linkEntityType(accountId: number, scheduleIds: number[] | null, entityTypeId: number): Promise { + await this.repository.update({ accountId, entityTypeId }, { entityTypeId: null }); + if (scheduleIds?.length > 0) { + await this.repository.update({ accountId, id: In(scheduleIds) }, { entityTypeId }); + } + } + + private createFindQb(filter: FindFilter) { + const qb = this.repository + .createQueryBuilder('schedule') + .where('schedule.accountId = :accountId', { accountId: filter.accountId }) + .leftJoinAndMapMany('schedule.performers', SchedulePerformer, 'performer', 'schedule.id = performer.schedule_id'); + + if (filter.scheduleId) { + qb.andWhere('schedule.id = :id', { id: filter.scheduleId }); + } + if (filter.entityTypeId) { + qb.andWhere('schedule.entity_type_id = :entityTypeId', { entityTypeId: filter.entityTypeId }); + } + + return qb; + } + + private async filterPerformers({ + user, + schedule, + throwError = false, + }: { + user: User; + schedule: Schedule; + throwError?: boolean; + }): Promise { + const { allow, userIds, departmentIds } = await this.authService.getPermissions({ + action: 'view', + user, + authorizable: Schedule.getAuthorizable(schedule.id), + }); + + if (!allow) { + if (throwError) { + throw new ForbiddenError(); + } else { + return null; + } + } + + if (userIds) { + schedule.performers = schedule.performers.filter( + (p) => + (p.type === SchedulePerformerType.User && userIds.includes(p.userId)) || + (p.type === SchedulePerformerType.Department && departmentIds.includes(p.departmentId)), + ); + } + + return schedule; + } +} diff --git a/backend/src/modules/scheduler/scheduler.module.ts b/backend/src/modules/scheduler/scheduler.module.ts new file mode 100644 index 0000000..018ce34 --- /dev/null +++ b/backend/src/modules/scheduler/scheduler.module.ts @@ -0,0 +1,45 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { InventoryModule } from '@/modules/inventory/inventory.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { + Schedule, + ScheduleController, + ScheduleService, + ScheduleTimeInterval, + ScheduleTimeIntervalService, +} from './schedule'; +import { + ScheduleAppointment, + ScheduleAppointmentController, + ScheduleAppointmentHandler, + ScheduleAppointmentService, +} from './schedule-appointment'; +import { SchedulePerformer, SchedulePerformerHandler, SchedulePerformerService } from './schedule-performer'; +import { ScheduleReportingController, ScheduleReportingService } from './schedule-reporting'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Schedule, ScheduleTimeInterval, SchedulePerformer, ScheduleAppointment]), + IAMModule, + forwardRef(() => CrmModule), + forwardRef(() => InventoryModule), + EntityInfoModule, + ], + controllers: [ScheduleController, ScheduleAppointmentController, ScheduleReportingController], + providers: [ + ScheduleService, + ScheduleTimeIntervalService, + SchedulePerformerService, + SchedulePerformerHandler, + ScheduleAppointmentService, + ScheduleAppointmentHandler, + ScheduleReportingService, + ], + exports: [ScheduleService, ScheduleAppointmentService], +}) +export class SchedulerModule {} diff --git a/backend/src/modules/setup/account-setup/account-setup.module.ts b/backend/src/modules/setup/account-setup/account-setup.module.ts new file mode 100644 index 0000000..cc3a11c --- /dev/null +++ b/backend/src/modules/setup/account-setup/account-setup.module.ts @@ -0,0 +1,67 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { StorageModule } from '@/modules/storage/storage.module'; +import { InventoryModule } from '@/modules/inventory/inventory.module'; +import { SchedulerModule } from '@/modules/scheduler/scheduler.module'; +import { EntityFieldModule } from '@/modules/entity/entity-field/entity-field.module'; +import { IntegrationModule } from '@/modules/integration/integration.module'; +import { PartnerModule } from '@/modules/partner/partner.module'; + +import { CrmModule } from '@/CRM/crm.module'; +import { TaskSettingsModule } from '@/CRM/task-settings/task-settings.module'; + +import accountSetupConfig from './config/account-setup.config'; + +import { RmsModule } from '../rms/rms.module'; +import { DemoDataModule } from '../demo-data/demo-data.module'; + +import { + RmsActivityService, + RmsBoardService, + RmsEntityService, + RmsEntityTypeService, + RmsFieldService, + RmsNoteService, + RmsTaskService, + SetupCrmService, + SetupIAMService, + SetupProductsService, + SetupSchedulerService, +} from './services'; +import { AccountSetupService } from './account-setup.service'; +import { PublicAccountSetupController } from './public-account-setup.controller'; + +@Module({ + imports: [ + ConfigModule.forFeature(accountSetupConfig), + IAMModule, + StorageModule, + CrmModule, + IntegrationModule, + InventoryModule, + SchedulerModule, + TaskSettingsModule, + EntityFieldModule, + RmsModule, + DemoDataModule, + PartnerModule, + ], + providers: [ + RmsActivityService, + RmsBoardService, + RmsEntityService, + RmsEntityTypeService, + RmsFieldService, + RmsNoteService, + RmsTaskService, + SetupCrmService, + SetupIAMService, + SetupProductsService, + SetupSchedulerService, + AccountSetupService, + ], + controllers: [PublicAccountSetupController], +}) +export class AccountSetupModule {} diff --git a/backend/src/modules/setup/account-setup/account-setup.service.ts b/backend/src/modules/setup/account-setup/account-setup.service.ts new file mode 100644 index 0000000..b05edb5 --- /dev/null +++ b/backend/src/modules/setup/account-setup/account-setup.service.ts @@ -0,0 +1,349 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { capitalizeFirst, DateUtil } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { AccountService } from '@/modules/iam/account/account.service'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { AuthenticationService } from '@/modules/iam/authentication/authentication.service'; +import { UserRole } from '@/modules/iam/common/enums/user-role.enum'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { LoginLinkDto } from '@/modules/iam/authentication/dto/login-link.dto'; +import { SubscriptionDiscountService } from '@/modules/iam/subscription-discount/subscription-discount.service'; +import { FieldType } from '@/modules/entity/entity-field/common'; +import { SimpleFieldValueDto } from '@/modules/entity/entity-field/field-value'; +import { AppsumoService } from '@/modules/integration/appsumo'; +import { PartnerService } from '@/modules/partner/partner.service'; +import { CreateSimpleEntityDto } from '@/CRM/Service/Entity/Dto'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { NoteService } from '@/CRM/note/note.service'; + +import { AccountSetupConfig } from './config'; +import { RmsModules, RmsModulePrefix, DemoDataType } from '../common'; +import { DemoDataService } from '../demo-data/demo-data.service'; +import { RmsService } from '../rms/services/rms.service'; + +import { SetupAccountDto } from './dto'; +import { SetupCrmService, SetupIAMService, SetupProductsService, SetupSchedulerService } from './services'; + +interface ContactInfo { + name: string; + phone: string; + email: string; +} +interface DealInfo { + name: string; + firstVisit?: Date | null; +} + +const RMSDemoCode = 'demo'; + +const RestrictedFieldNames = ['password']; +const ExtraFieldNames = { + promoCode: 'promoCode', + rmsCode: 'rmsCode', + rmsModules: 'rmsModules', + ref: 'ref', + appName: 'appName', +}; + +@Injectable() +export class AccountSetupService { + private readonly logger = new Logger(AccountSetupService.name); + private _config: AccountSetupConfig; + private _appName: string; + + constructor( + private readonly configService: ConfigService, + private readonly accountService: AccountService, + private readonly authService: AuthenticationService, + private readonly userService: UserService, + private readonly rmsService: RmsService, + private readonly demoDataService: DemoDataService, + private readonly setupCrmService: SetupCrmService, + private readonly setupIAMService: SetupIAMService, + private readonly setupProductsService: SetupProductsService, + private readonly setupSchedulerService: SetupSchedulerService, + private readonly appsumoService: AppsumoService, + private readonly entityService: EntityService, + private readonly noteService: NoteService, + private readonly partnerService: PartnerService, + private readonly discountService: SubscriptionDiscountService, + ) { + this._config = this.configService.get('accountSetup'); + this._appName = this.configService.get('application').name; + } + + async create(dto: SetupAccountDto, gaClientId?: string | null): Promise { + const subscription = dto.appsumo ? await this.appsumoService.findSubscription(dto.appsumo) : undefined; + + const { account, owner } = await this.accountService.create(dto, { + gaClientId, + subscription, + firstVisit: dto.firstVisit, + }); + + if (!dto.firstVisit) { + dto.firstVisit = account.createdAt.toISOString(); + } + + await this.setupAccount(account, owner, dto.rmsCode, dto.rmsModules); + + await this.handleRegistrationEvent({ account, owner, dto }); + + if (dto.appsumo) { + await this.appsumoService.update(dto.appsumo, { accountId: account.id }); + } + + return this.authService.createLoginLink({ accountId: account.id, subdomain: account.subdomain, userId: owner.id }); + } + + private async setupAccount(account: Account, owner: User, rmsCode: string | null, modulesCode: string | null) { + const modules = modulesCode ? this.parseModules(modulesCode) : undefined; + + const rms = await this.rmsService.findOne({ code: rmsCode || RMSDemoCode }); + if (rms && rms.accountId) { + await this.setupRMS(account, owner, rms.accountId, modules); + } else { + await this.setupDefault(account.id, owner); + } + } + + private parseModules(modulesCode: string): RmsModules { + const modules = new RmsModules(); + const moduleCodes = modulesCode.replace(/\s+/g, '').split(','); + for (const moduleCode of moduleCodes) { + const [prefix, code] = moduleCode.split('_'); + if (prefix && code) { + const id = Number(code); + if (prefix === RmsModulePrefix.EntityType) { + if (!modules.entityTypeIds.includes(id)) { + modules.entityTypeIds.push(id); + } + } else if (prefix === RmsModulePrefix.ProductSection) { + if (!modules.productSectionIds.includes(id)) { + modules.productSectionIds.push(id); + } + } else if (prefix === RmsModulePrefix.Scheduler) { + if (!modules.schedulerIds.includes(id)) { + modules.schedulerIds.push(id); + } + } + } + } + return modules; + } + + private async setupRMS(account: Account, owner: User, rmsAccountId: number, modules?: RmsModules) { + const rmsAccount = await this.accountService.findOne({ accountId: rmsAccountId }); + + const { rmsOwner, usersMap, departmentsMap } = await this.setupIAMService.copyAll(rmsAccount.id, account, owner); + const demoUserIds = Array.from(usersMap.values()) + .filter((u) => u.id !== owner.id) + .map((u) => u.id); + if (demoUserIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.User, demoUserIds); + } + + const { entityTypesMap, entitiesMap, tasksMap } = await this.setupCrmService.copyAll( + rmsAccount.id, + account.id, + usersMap, + modules?.entityTypeIds, + ); + const demoEntityIds = Array.from(entitiesMap.values()); + if (demoEntityIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.Entity, demoEntityIds); + } + const demoTaskIds = Array.from(tasksMap.values()); + if (demoTaskIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.Task, demoTaskIds); + } + + const { sectionsMap, productsMap, salesOrdersMap, rentalOrdersMap } = await this.setupProductsService.copyAll( + rmsAccount, + rmsOwner, + account, + owner, + entityTypesMap, + entitiesMap, + modules?.productSectionIds, + ); + const demoProductIds = Array.from(productsMap.values()); + if (demoProductIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.Product, demoProductIds); + } + const demoSaleOrderIds = Array.from(salesOrdersMap.values()); + if (demoSaleOrderIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.SalesOrder, demoSaleOrderIds); + } + const demoRentalOrderIds = Array.from(rentalOrdersMap.values()); + if (demoRentalOrderIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.RentalOrder, demoRentalOrderIds); + } + + const { appointmentsMap } = await this.setupSchedulerService.copyAll( + rmsAccount.id, + account.id, + owner, + usersMap, + departmentsMap, + entityTypesMap, + entitiesMap, + sectionsMap, + salesOrdersMap, + modules?.schedulerIds, + ); + const demoAppointmentIds = Array.from(appointmentsMap.values()); + if (demoAppointmentIds.length > 0) { + await this.demoDataService.create(account.id, DemoDataType.ScheduleAppointment, demoAppointmentIds); + } + } + + private async setupDefault(accountId: number, owner: User) { + await this.setupCrmService.setupDefault(accountId, owner); + } + + private async handleRegistrationEvent({ + account, + owner, + dto, + }: { + account: Account; + owner: User; + dto: SetupAccountDto; + }) { + if (!this._config.accountId) return; + + try { + const responsible = this._config.responsibleId + ? await this.userService.findOne({ accountId: this._config.accountId, id: this._config.responsibleId }) + : await this.userService.findOne({ accountId: this._config.accountId, role: UserRole.OWNER }); + + const extraParams = dto.extraUserInfo ? this.parseExtraParams(dto.extraUserInfo) : {}; + if (dto.promoCode) { + extraParams[ExtraFieldNames.promoCode] = dto.promoCode; + } + if (dto.rmsCode) { + extraParams[ExtraFieldNames.rmsCode] = dto.rmsCode; + } + if (dto.rmsModules) { + extraParams[ExtraFieldNames.rmsModules] = dto.rmsModules; + } + if (dto.ref) { + extraParams[ExtraFieldNames.ref] = dto.ref; + } + extraParams[ExtraFieldNames.appName] = this._appName; + + const deal = await this.createDealDto( + { + name: `Registration: ${account.companyName}`, + firstVisit: dto.firstVisit ? DateUtil.fromISOString(dto.firstVisit) : undefined, + }, + extraParams, + ); + const contact = this.createContactDto( + { + name: `${owner.firstName} ${owner.lastName}`, + phone: owner.phone, + email: owner.email, + }, + [deal], + ); + + const entities = await this.entityService.createSimple({ + accountId: this._config.accountId, + user: responsible, + dto: contact, + options: { createdAt: account.createdAt, checkDuplicate: true }, + }); + + await this.noteService.create({ + accountId: this._config.accountId, + userId: responsible.id, + entityId: entities[1].id, + dto: { text: this.getCommentHtml({ account: account, registration: dto }) }, + options: { createdAt: account.createdAt }, + }); + + if (dto.ref) { + await this.partnerService.linkLeadWithPartner(this._config.accountId, entities[1].id, dto.ref); + } + } catch (e) { + this.logger.error(`Error during lead creation in ${this._config.accountId} account`, (e as Error)?.stack); + } + } + + private parseExtraParams(extra: { analytics?: object }): Record { + if (!extra.analytics) return {}; + + const extraParams: Record = {}; + for (const [key, value] of Object.entries(extra.analytics)) { + if (value) { + extraParams[key] = value; + } + } + + return extraParams; + } + + private createContactDto( + { name, phone, email }: ContactInfo, + linkedEntities: CreateSimpleEntityDto[], + ): CreateSimpleEntityDto { + return { + entityTypeId: this._config.contactId, + name, + fieldValues: [ + { fieldType: FieldType.Phone, value: phone }, + { fieldType: FieldType.Email, value: email }, + ], + linkedEntities, + }; + } + private async createDealDto( + { name, firstVisit }: DealInfo, + extraParams?: Record, + ): Promise { + const fieldValues: SimpleFieldValueDto[] = []; + if (firstVisit) { + const discounts = await this.discountService.findMany(firstVisit); + discounts.forEach((discount) => { + fieldValues.push({ fieldName: discount.code, value: discount.endAt }); + }); + } + if (extraParams) { + for (const key of Object.keys(extraParams)) { + fieldValues.push({ fieldName: key, value: extraParams[key] }); + } + } + + return { + entityTypeId: this._config.dealId, + boardId: this._config.dealBoardId, + name, + fieldValues, + }; + } + + private getCommentHtml(obj: object): string { + const comment: string[] = []; + comment.push('
    '); + for (const key of Object.keys(obj)) { + if (!RestrictedFieldNames.includes(key)) { + const value = obj[key]; + if (value instanceof Object) { + comment.push(`
  • ${capitalizeFirst(key)}:`); + comment.push(this.getCommentHtml(value)); + comment.push('
  • '); + } else if (value !== null && value !== undefined) { + comment.push(`
  • ${capitalizeFirst(key)}: ${value}
  • `); + } + } + } + comment.push('
'); + return comment.join(''); + } +} diff --git a/backend/src/modules/setup/account-setup/config/account-setup.config.ts b/backend/src/modules/setup/account-setup/config/account-setup.config.ts new file mode 100644 index 0000000..6a2a6c6 --- /dev/null +++ b/backend/src/modules/setup/account-setup/config/account-setup.config.ts @@ -0,0 +1,24 @@ +import { registerAs } from '@nestjs/config'; + +export interface AccountSetupConfig { + accountId: number; + contactId: number; + dealId: number; + dealBoardId: number | undefined; + responsibleId: number; +} + +export default registerAs( + 'accountSetup', + (): AccountSetupConfig => ({ + accountId: process.env.ACCOUNT_SETUP_ACCOUNT_ID ? parseInt(process.env.ACCOUNT_SETUP_ACCOUNT_ID, 10) : undefined, + contactId: process.env.ACCOUNT_SETUP_CONTACT_ID ? parseInt(process.env.ACCOUNT_SETUP_CONTACT_ID, 10) : undefined, + dealId: process.env.ACCOUNT_SETUP_DEAL_ID ? parseInt(process.env.ACCOUNT_SETUP_DEAL_ID, 10) : undefined, + dealBoardId: process.env.ACCOUNT_SETUP_DEAL_BOARD_ID + ? parseInt(process.env.ACCOUNT_SETUP_DEAL_BOARD_ID, 10) + : undefined, + responsibleId: process.env.ACCOUNT_SETUP_RESPONSIBLE_ID + ? parseInt(process.env.ACCOUNT_SETUP_RESPONSIBLE_ID, 10) + : undefined, + }), +); diff --git a/backend/src/modules/setup/account-setup/config/index.ts b/backend/src/modules/setup/account-setup/config/index.ts new file mode 100644 index 0000000..8deaa62 --- /dev/null +++ b/backend/src/modules/setup/account-setup/config/index.ts @@ -0,0 +1 @@ +export * from './account-setup.config'; diff --git a/backend/src/modules/setup/account-setup/dto/index.ts b/backend/src/modules/setup/account-setup/dto/index.ts new file mode 100644 index 0000000..c0f134a --- /dev/null +++ b/backend/src/modules/setup/account-setup/dto/index.ts @@ -0,0 +1 @@ +export * from './setup-account.dto'; diff --git a/backend/src/modules/setup/account-setup/dto/setup-account.dto.ts b/backend/src/modules/setup/account-setup/dto/setup-account.dto.ts new file mode 100644 index 0000000..ec5fc30 --- /dev/null +++ b/backend/src/modules/setup/account-setup/dto/setup-account.dto.ts @@ -0,0 +1,40 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +import { CreateAccountDto } from '@/modules/iam/account/dto/create-account.dto'; + +export class SetupAccountDto extends CreateAccountDto { + @ApiPropertyOptional({ nullable: true, description: 'Referral code' }) + @IsOptional() + @IsString() + ref?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'Promo code' }) + @IsOptional() + @IsString() + promoCode?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'RMS code' }) + @IsOptional() + @IsString() + rmsCode?: string | null; + + @ApiPropertyOptional({ nullable: true, description: 'RMS modules' }) + @IsOptional() + @IsString() + rmsModules?: string | null; + + @ApiPropertyOptional({ description: 'Appsumo code', nullable: true }) + @IsOptional() + @IsString() + appsumo?: string | null; + + @ApiPropertyOptional({ description: 'Extra user info', nullable: true }) + @IsOptional() + extraUserInfo?: object | null; + + @ApiPropertyOptional({ description: 'First visit date', nullable: true }) + @IsOptional() + @IsString() + firstVisit?: string | null; +} diff --git a/backend/src/modules/setup/account-setup/public-account-setup.controller.ts b/backend/src/modules/setup/account-setup/public-account-setup.controller.ts new file mode 100644 index 0000000..bb332ea --- /dev/null +++ b/backend/src/modules/setup/account-setup/public-account-setup.controller.ts @@ -0,0 +1,22 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { ApiAccessRequired } from '@/modules/iam/common/decorators/api-access-required.decorator'; +import { GAClientId } from '@/modules/iam/common/decorators/ga-client-id.decorator'; +import { LoginLinkDto } from '@/modules/iam/authentication/dto/login-link.dto'; + +import { SetupAccountDto } from './dto'; +import { AccountSetupService } from './account-setup.service'; + +@ApiTags('setup/account') +@Controller('setup/account') +@ApiAccessRequired() +export class PublicAccountSetupController { + constructor(private readonly service: AccountSetupService) {} + + @ApiCreatedResponse({ type: LoginLinkDto }) + @Post() + async create(@GAClientId() gaClientId: string, @Body() dto: SetupAccountDto): Promise { + return this.service.create(dto, gaClientId); + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/index.ts b/backend/src/modules/setup/account-setup/services/crm/index.ts new file mode 100644 index 0000000..27067ab --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/index.ts @@ -0,0 +1,7 @@ +export * from './rms-activity.service'; +export * from './rms-board.service'; +export * from './rms-entity-type.service'; +export * from './rms-entity.service'; +export * from './rms-field.service'; +export * from './rms-note.service'; +export * from './rms-task.service'; diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-activity.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-activity.service.ts new file mode 100644 index 0000000..f6230f4 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-activity.service.ts @@ -0,0 +1,77 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { ActivityService } from '@/CRM/activity/activity.service'; +import { ActivityTypeService } from '@/CRM/activity-type/activity-type.service'; + +@Injectable() +export class RmsActivityService { + constructor( + private readonly activityService: ActivityService, + private readonly activityTypeService: ActivityTypeService, + ) {} + + public async setupDefault(accountId: number) { + await Promise.all([ + this.activityTypeService.create({ accountId, dto: { name: 'Call' } }), + this.activityTypeService.create({ accountId, dto: { name: 'Email' } }), + this.activityTypeService.create({ accountId, dto: { name: 'Meeting' } }), + ]); + } + + public async copyAll( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entitiesMap: Map, + ): Promise> { + const activityTypesMap = await this.copyActivityTypes(rmsAccountId, accountId); + + return await this.copyActivities(rmsAccountId, accountId, usersMap, entitiesMap, activityTypesMap); + } + + public async copyActivityTypes(rmsAccountId: number, accountId: number) { + const activityTypesMap = new Map(); + + const activityTypes = await this.activityTypeService.findMany({ accountId: rmsAccountId }); + for (const activityType of activityTypes) { + const newActivityType = await this.activityTypeService.create({ accountId, dto: { name: activityType.name } }); + activityTypesMap.set(activityType.id, newActivityType.id); + } + + return activityTypesMap; + } + + public async copyActivities( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entitiesMap: Map, + activityTypesMap: Map, + ): Promise> { + const activitiesMap = new Map(); + + const rmsActivities = await this.activityService.findMany({ accountId: rmsAccountId }); + for (const rmsActivity of rmsActivities) { + if (entitiesMap.has(rmsActivity.entityId)) { + const activity = await this.activityService.create(accountId, usersMap.get(rmsActivity.createdBy), { + responsibleUserId: usersMap.get(rmsActivity.responsibleUserId).id, + startDate: rmsActivity.startDate ? rmsActivity.startDate.toISOString() : null, + endDate: rmsActivity.endDate ? rmsActivity.endDate.toISOString() : null, + text: rmsActivity.text, + entityId: entitiesMap.get(rmsActivity.entityId), + activityTypeId: activityTypesMap.get(rmsActivity.activityTypeId), + isResolved: rmsActivity.isResolved, + resolvedDate: rmsActivity.resolvedDate ? rmsActivity.resolvedDate.toISOString() : null, + result: rmsActivity.result, + weight: rmsActivity.weight, + }); + + activitiesMap.set(rmsActivity.id, activity.id); + } + } + + return activitiesMap; + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-board.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-board.service.ts new file mode 100644 index 0000000..ddf6bcb --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-board.service.ts @@ -0,0 +1,113 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { BoardService } from '@/CRM/board/board.service'; +import { BoardType } from '@/CRM/board/enums'; +import { BoardStageService } from '@/CRM/board-stage'; + +@Injectable() +export class RmsBoardService { + constructor( + private readonly boardService: BoardService, + private readonly stageService: BoardStageService, + ) {} + + public async setupDefault(accountId: number, owner: User) { + await this.boardService.create({ + accountId, + user: owner, + dto: { name: 'Tasks board', type: BoardType.Task, recordId: null, sortOrder: 0 }, + options: { isSystem: true }, + }); + } + + public async copyBoardsAndStages( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entityTypesMap: Map, + ): Promise<{ boardsMap: Map; stagesMap: Map }> { + const boardsMap = await this.copyBoards(rmsAccountId, accountId, usersMap, entityTypesMap); + const stagesMap = await this.copyStages(rmsAccountId, accountId, boardsMap); + + return { boardsMap, stagesMap }; + } + + private async copyBoards( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entityTypesMap: Map, + ): Promise> { + const boardsMap = new Map(); + + const rmsBoards = await this.boardService.findMany({ + filter: { accountId: rmsAccountId, isSystem: true }, + }); + for (const rmsEntityTypeId of entityTypesMap.keys()) { + const rmsEtBoards = await this.boardService.findMany({ + filter: { accountId: rmsAccountId, recordId: rmsEntityTypeId }, + }); + for (const rmsEtBoard of rmsEtBoards) { + if (rmsEtBoard.taskBoardId) { + const rmsEtTaskBoard = await this.boardService.findOne({ + filter: { accountId: rmsAccountId, boardId: rmsEtBoard.taskBoardId }, + }); + rmsBoards.push(rmsEtTaskBoard); + } + rmsBoards.push(rmsEtBoard); + } + } + for (const rmsBoard of rmsBoards) { + const board = await this.boardService.create({ + accountId, + user: rmsBoard.ownerId ? usersMap.get(rmsBoard.ownerId) : null, + dto: { + name: rmsBoard.name, + type: rmsBoard.type, + recordId: rmsBoard.recordId ? entityTypesMap.get(rmsBoard.recordId) : null, + sortOrder: rmsBoard.sortOrder, + participantIds: rmsBoard.participantIds ? rmsBoard.participantIds.map((id) => usersMap.get(id).id) : null, + }, + options: { + ownerId: rmsBoard.ownerId ? usersMap.get(rmsBoard.ownerId).id : null, + isSystem: rmsBoard.isSystem, + taskBoardId: rmsBoard.taskBoardId ? boardsMap.get(rmsBoard.taskBoardId) : null, + createDefaultStages: false, + }, + }); + boardsMap.set(rmsBoard.id, board.id); + } + + return boardsMap; + } + + private async copyStages( + rmsAccountId: number, + accountId: number, + boardsMap: Map, + ): Promise> { + const stagesMap = new Map(); + + for (const [rmsBoardId, boardId] of boardsMap) { + const rmsStages = await this.stageService.findMany({ accountId: rmsAccountId, boardId: rmsBoardId }); + for (const rmsStage of rmsStages) { + const stage = await this.stageService.create({ + accountId, + boardId, + dto: { + name: rmsStage.name, + color: rmsStage.color, + code: rmsStage.code, + isSystem: rmsStage.isSystem, + sortOrder: rmsStage.sortOrder, + }, + }); + stagesMap.set(rmsStage.id, stage.id); + } + } + + return stagesMap; + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-entity-type.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-entity-type.service.ts new file mode 100644 index 0000000..dca1ba8 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-entity-type.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common'; + +import { EntityType } from '@/CRM/entity-type/entities/entity-type.entity'; +import { EntityTypeService } from '@/CRM/entity-type/entity-type.service'; +import { EntityTypeLinkService } from '@/CRM/entity-type-link/entity-type-link.service'; +import { EntityTypeFeatureService } from '@/CRM/feature/entity-type-feature.service'; + +@Injectable() +export class RmsEntityTypeService { + constructor( + private readonly entityTypeService: EntityTypeService, + private readonly entityTypeLinkService: EntityTypeLinkService, + private readonly entityTypeFeatureService: EntityTypeFeatureService, + ) {} + + public async copyEntityTypes( + rmsAccountId: number, + accountId: number, + entityTypeIds?: number[], + ): Promise> { + const typeMap = new Map(); + const allTypes = await this.entityTypeService.findMany(rmsAccountId); + const types = entityTypeIds ? allTypes.filter((t) => entityTypeIds.includes(t.id)) : allTypes; + for (const type of types) { + const et = await this.entityTypeService.save(EntityType.copy(accountId, type)); + typeMap.set(type.id, et.id); + } + + for (const type of types) { + const newEntityTypeId = typeMap.get(type.id); + + const allLinks = await this.entityTypeLinkService.findMany({ accountId: rmsAccountId, sourceId: type.id }); + const links = allLinks.filter((l) => typeMap.has(l.targetId)); + for (const link of links) { + await this.entityTypeLinkService.create({ + accountId, + sourceId: newEntityTypeId, + dto: { targetId: typeMap.get(link.targetId), sortOrder: link.sortOrder }, + createBackLink: false, + }); + } + + const features = await this.entityTypeFeatureService.findByEntityTypeId(rmsAccountId, type.id); + const featureIds = features.map((f) => f.featureId); + await this.entityTypeFeatureService.setEntityTypeFeatures(accountId, newEntityTypeId, featureIds); + } + + return typeMap; + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-entity.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-entity.service.ts new file mode 100644 index 0000000..86931c4 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-entity.service.ts @@ -0,0 +1,71 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { EntityLinkService } from '@/CRM/entity-link/entity-link.service'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { Entity } from '@/CRM/Model/Entity/Entity'; + +@Injectable() +export class RmsEntityService { + constructor( + private readonly entityService: EntityService, + private readonly entityLinkService: EntityLinkService, + ) {} + + public async copyEntities( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entityTypesMap: Map, + boardsMap: Map, + stagesMap: Map, + ): Promise> { + const entitiesMap = new Map(); + + for (const [rmsEntityTypeId, entityTypeId] of entityTypesMap) { + const rmsEntities = await this.entityService.findMany(rmsAccountId, { entityTypeId: rmsEntityTypeId }); + for (const rmsEntity of rmsEntities) { + const entity = await this.entityService.save( + new Entity( + accountId, + rmsEntity.name, + entityTypeId, + usersMap.get(rmsEntity.responsibleUserId).id, + rmsEntity.boardId ? boardsMap.get(rmsEntity.boardId) : null, + rmsEntity.stageId ? stagesMap.get(rmsEntity.stageId) : null, + usersMap.get(rmsEntity.createdBy).id, + rmsEntity.weight, + rmsEntity.focused, + rmsEntity.closedAt, + rmsEntity.updatedAt, + rmsEntity.createdAt, + rmsEntity.participantIds ? rmsEntity.participantIds.map((id) => usersMap.get(id).id) : null, + null, + null, + ), + ); + entitiesMap.set(rmsEntity.id, entity.id); + } + } + + const linked: number[] = []; + for (const [rmsEntityId, entityId] of entitiesMap) { + const rmsAllLinks = await this.entityLinkService.findMany({ accountId: rmsAccountId, sourceId: rmsEntityId }); + const rmsLinks = rmsAllLinks.filter((l) => entitiesMap.has(l.targetId)); + for (const rmsLink of rmsLinks) { + if (!linked.includes(rmsLink.targetId)) { + await this.entityLinkService.create({ + accountId, + sourceId: entityId, + targetId: entitiesMap.get(rmsLink.targetId), + sortOrder: rmsLink.sortOrder, + }); + } + } + linked.push(rmsEntityId); + } + + return entitiesMap; + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-field.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-field.service.ts new file mode 100644 index 0000000..dfddffd --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-field.service.ts @@ -0,0 +1,266 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; +import { FieldType, FieldTypes, FormulaUtil } from '@/modules/entity/entity-field/common'; +import { FieldGroupService } from '@/modules/entity/entity-field/field-group/field-group.service'; +import { Field, FieldService } from '@/modules/entity/entity-field/field'; +import { FieldOptionService } from '@/modules/entity/entity-field/field-option/field-option.service'; +import { FieldValueService } from '@/modules/entity/entity-field/field-value/field-value.service'; +import { + FieldPayloadOption, + FieldPayloadOptions, + FieldPayloadParticipants, + FieldPayloadValue, +} from '@/modules/entity/entity-field/field-value/types'; +import { FieldSettingsService } from '@/modules/entity/entity-field/field-settings/field-settings.service'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +@Injectable() +export class RmsFieldService { + constructor( + private readonly storageService: StorageService, + private readonly fieldGroupService: FieldGroupService, + private readonly fieldService: FieldService, + private readonly fieldOptionService: FieldOptionService, + private readonly fieldValueService: FieldValueService, + private readonly fieldSettingsService: FieldSettingsService, + ) {} + + public async copyAll( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entityTypesMap: Map, + entitiesMap: Map, + stagesMap: Map, + ) { + const fieldGroupsMap = await this.copyFieldGroups(rmsAccountId, accountId, entityTypesMap); + const fieldsMap = await this.copyFields(rmsAccountId, accountId, entityTypesMap, fieldGroupsMap); + const fieldOptionsMap = await this.copyFieldOptions(rmsAccountId, accountId, fieldsMap); + await this.copyFieldValues(rmsAccountId, accountId, usersMap, entitiesMap, fieldsMap, fieldOptionsMap); + await this.copyFieldSettings(rmsAccountId, accountId, usersMap, fieldsMap, stagesMap); + + return fieldsMap; + } + + private async copyFieldGroups( + rmsAccountId: number, + accountId: number, + entityTypesMap: Map, + ): Promise> { + const fieldGroupsMap = new Map(); + + for (const [rmsEntityTypeId, entityTypeId] of entityTypesMap) { + const rmsFieldGroups = await this.fieldGroupService.findMany({ + accountId: rmsAccountId, + entityTypeId: rmsEntityTypeId, + }); + for (const rmsFieldGroup of rmsFieldGroups) { + const fieldGroup = await this.fieldGroupService.create({ + accountId, + entityTypeId, + dto: { name: rmsFieldGroup.name, sortOrder: rmsFieldGroup.sortOrder }, + }); + fieldGroupsMap.set(rmsFieldGroup.id, fieldGroup.id); + } + } + + return fieldGroupsMap; + } + + private async copyFields( + rmsAccountId: number, + accountId: number, + entityTypesMap: Map, + fieldGroupsMap: Map, + ): Promise> { + const fieldsMap = new Map(); + + for (const [rmsEntityTypeId, entityTypeId] of entityTypesMap) { + const rmsFields = await this.fieldService.findMany({ accountId: rmsAccountId, entityTypeId: rmsEntityTypeId }); + for (const rmsField of rmsFields) { + const field = await this.fieldService.create({ + accountId, + entityTypeId, + dto: { + name: rmsField.name, + type: rmsField.type, + code: rmsField.code, + active: rmsField.active, + sortOrder: rmsField.sortOrder, + entityTypeId: entityTypeId, + fieldGroupId: fieldGroupsMap.get(rmsField.fieldGroupId), + value: null, + }, + options: { skipProcessing: true }, + }); + fieldsMap.set(rmsField.id, field.id); + } + } + + for (const [rmsEntityTypeId, entityTypeId] of entityTypesMap) { + const rmsFields = await this.fieldService.findMany({ + accountId: rmsAccountId, + entityTypeId: rmsEntityTypeId, + type: FieldTypes.formula, + }); + for (const rmsField of rmsFields.filter((f) => f.value)) { + const fieldId = fieldsMap.get(rmsField.id); + if (fieldId) { + await this.fieldService.update({ + accountId, + entityTypeId, + fieldId, + dto: { id: fieldId, value: this.copyFieldValue(rmsField, entityTypesMap, fieldsMap) }, + options: { skipProcessing: true }, + }); + } + } + } + + return fieldsMap; + } + + private copyFieldValue(field: Field, entityTypesMap: Map, fieldsMap: Map): string { + let formula = field.value; + const formulaKeys = FormulaUtil.extractVariables(field.value); + for (const formulaKey of formulaKeys) { + const { entityTypeId: rmsEntityTypeId, fieldId: rmsFieldId } = FormulaUtil.parseFieldKey(formulaKey); + const entityTypeId = entityTypesMap.get(rmsEntityTypeId); + const fieldId = fieldsMap.get(rmsFieldId); + if (entityTypeId && fieldId) { + formula = formula.replace(formulaKey, FormulaUtil.createFieldKey({ entityTypeId, fieldId })); + } else { + formula = formula.replace(formulaKey, '0'); + } + } + + return formula; + } + + private async copyFieldOptions( + rmsAccountId: number, + accountId: number, + fieldsMap: Map, + ): Promise> { + const fieldOptionsMap = new Map(); + + for (const [rmsFieldId, fieldId] of fieldsMap) { + const rmsFieldOptions = await this.fieldOptionService.findMany({ accountId: rmsAccountId, fieldId: rmsFieldId }); + for (const rmsFieldOption of rmsFieldOptions) { + const option = await this.fieldOptionService.create({ + accountId, + fieldId, + dto: { + label: rmsFieldOption.label, + color: rmsFieldOption.color, + sortOrder: rmsFieldOption.sortOrder, + }, + }); + fieldOptionsMap.set(rmsFieldOption.id, option.id); + } + } + + return fieldOptionsMap; + } + + private async copyFieldValues( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entitiesMap: Map, + fieldsMap: Map, + fieldOptionsMap: Map, + ): Promise { + for (const [rmsFieldId, fieldId] of fieldsMap) { + const rmsFieldValues = await this.fieldValueService.findMany({ accountId: rmsAccountId, fieldId: rmsFieldId }); + for (const rmsFieldValue of rmsFieldValues) { + const payload = rmsFieldValue.payload; + if (FieldTypes.select.includes(rmsFieldValue.fieldType)) { + (payload as FieldPayloadOption).optionId = fieldOptionsMap.get(rmsFieldValue.getValue()); + } else if (FieldTypes.multiSelect.includes(rmsFieldValue.fieldType)) { + (payload as FieldPayloadOptions).optionIds = rmsFieldValue + .getValue() + .map((id) => fieldOptionsMap.get(id)); + } else if (rmsFieldValue.fieldType === FieldType.Participant) { + (payload as FieldPayloadValue).value = usersMap.get(rmsFieldValue.getValue()).id; + } else if (rmsFieldValue.fieldType === FieldType.Participants) { + (payload as FieldPayloadParticipants).userIds = rmsFieldValue + .getValue() + .filter((id: number) => usersMap.has(id)) + .map((id: number) => usersMap.get(id).id); + } else if (rmsFieldValue.fieldType === FieldType.File) { + const fileIds: string[] = []; + const rmsFileIds = rmsFieldValue.getValue(); + if (rmsFileIds?.length) { + for (const rmsFileId of rmsFieldValue.getValue()) { + const { file, content } = await this.storageService.getFile({ + fileId: rmsFileId, + accountId: rmsAccountId, + }); + const fileInfo = await this.storageService.storeCommonFile({ + accountId, + file: StorageFile.fromFileInfo(file, Buffer.from(content)), + }); + fileIds.push(fileInfo.id); + } + } + (payload as FieldPayloadValue).value = fileIds.length ? fileIds : null; + } + + await this.fieldValueService.setValue({ + accountId, + entityId: entitiesMap.get(rmsFieldValue.entityId), + fieldId, + dto: { + fieldId, + fieldType: rmsFieldValue.fieldType, + payload, + }, + }); + } + } + } + + private async copyFieldSettings( + rmsAccountId: number, + accountId: number, + usersMap: Map, + fieldsMap: Map, + stagesMap: Map, + ): Promise { + for (const [rmsFieldId, fieldId] of fieldsMap) { + const rmsFieldSettings = await this.fieldSettingsService.findOne({ + accountId: rmsAccountId, + fieldId: rmsFieldId, + }); + if (rmsFieldSettings) { + await this.fieldSettingsService.update({ + accountId, + fieldId, + dto: { + excludeUserIds: rmsFieldSettings.excludeUserIds + ? rmsFieldSettings.excludeUserIds.map((id) => usersMap.get(id).id) + : null, + readonlyUserIds: rmsFieldSettings.readonlyUserIds + ? rmsFieldSettings.readonlyUserIds.map((id) => usersMap.get(id).id) + : null, + hideUserIds: rmsFieldSettings.hideUserIds + ? rmsFieldSettings.hideUserIds.map((id) => usersMap.get(id).id) + : null, + importantStageIds: rmsFieldSettings.importantStageIds + ? rmsFieldSettings.importantStageIds.map((id) => stagesMap.get(id)) + : null, + requiredStageIds: rmsFieldSettings.requiredStageIds + ? rmsFieldSettings.requiredStageIds.map((id) => stagesMap.get(id)) + : null, + hideStageIds: rmsFieldSettings.hideStageIds + ? rmsFieldSettings.hideStageIds.map((id) => stagesMap.get(id)) + : null, + }, + }); + } + } + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-note.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-note.service.ts new file mode 100644 index 0000000..d874b72 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-note.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { NoteService } from '@/CRM/note/note.service'; + +@Injectable() +export class RmsNoteService { + constructor(private readonly noteService: NoteService) {} + + public async copyNotes( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entitiesMap: Map, + ): Promise> { + const notesMap = new Map(); + + const rmsNotes = await this.noteService.findMany({ accountId: rmsAccountId }); + for (const rmsNote of rmsNotes) { + if (entitiesMap.has(rmsNote.entityId)) { + const note = await this.noteService.create({ + accountId, + userId: usersMap.get(rmsNote.createdBy).id, + entityId: entitiesMap.get(rmsNote.entityId), + dto: { text: rmsNote.text }, + }); + notesMap.set(rmsNote.id, note.id); + } + } + + return notesMap; + } +} diff --git a/backend/src/modules/setup/account-setup/services/crm/rms-task.service.ts b/backend/src/modules/setup/account-setup/services/crm/rms-task.service.ts new file mode 100644 index 0000000..d7e5707 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/crm/rms-task.service.ts @@ -0,0 +1,137 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { TaskService } from '@/CRM/task/task.service'; +import { TaskSettingsService } from '@/CRM/task-settings/task-settings.service'; +import { TaskSettingsType } from '@/CRM/task-settings/enums/task-settings-type.enum'; +import { TaskSubtaskService } from '@/CRM/task-subtask/task-subtask.service'; + +@Injectable() +export class RmsTaskService { + constructor( + private readonly taskService: TaskService, + private readonly taskSettingsService: TaskSettingsService, + private readonly subtaskService: TaskSubtaskService, + ) {} + + public async copyAll( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entityTypesMap: Map, + entitiesMap: Map, + boardsMap: Map, + stagesMap: Map, + ): Promise> { + const taskSettingsMap = await this.copyTaskSettings(rmsAccountId, accountId, entityTypesMap, boardsMap); + + const tasksMap = await this.copyTasks( + rmsAccountId, + accountId, + usersMap, + boardsMap, + stagesMap, + entitiesMap, + taskSettingsMap, + ); + + await this.copySubtasks(rmsAccountId, accountId, tasksMap); + + return tasksMap; + } + + private async copyTaskSettings( + rmsAccountId: number, + accountId: number, + entityTypesMap: Map, + boardsMap: Map, + ): Promise> { + const taskSettingsMap = new Map(); + + const rmsTaskSettings = await this.taskSettingsService.findMany(rmsAccountId); + for (const rmsTaskSetting of rmsTaskSettings) { + const recordId = this.getRecordId(rmsTaskSetting.type, rmsTaskSetting.recordId, entityTypesMap, boardsMap); + if (recordId) { + const taskSetting = await this.taskSettingsService.create(accountId, { + type: rmsTaskSetting.type, + recordId: recordId, + activeFields: rmsTaskSetting.activeFields, + }); + taskSettingsMap.set(rmsTaskSetting.id, taskSetting.id); + } + } + + return taskSettingsMap; + } + + private getRecordId( + type: TaskSettingsType, + recordId: number, + entityTypesMap: Map, + boardsMap: Map, + ): number | null { + if (type === TaskSettingsType.EntityType) { + return entityTypesMap.get(recordId); + } + if (type === TaskSettingsType.TaskBoard) { + return boardsMap.get(recordId); + } + return null; + } + + private async copyTasks( + rmsAccountId: number, + accountId: number, + usersMap: Map, + boardsMap: Map, + stagesMap: Map, + entitiesMap: Map, + taskSettingsMap: Map, + ): Promise> { + const tasksMap = new Map(); + + const rmsTasks = await this.taskService.findMany({ accountId: rmsAccountId }); + for (const rmsTask of rmsTasks) { + const stageId = rmsTask.stageId ? stagesMap.get(rmsTask.stageId) : null; + if (rmsTask.stageId && !stageId) continue; + + const task = await this.taskService.create({ + accountId, + user: usersMap.get(rmsTask.createdBy), + dto: { + responsibleUserId: usersMap.get(rmsTask.responsibleUserId).id, + startDate: rmsTask.startDate ? rmsTask.startDate.toISOString() : null, + endDate: rmsTask.endDate ? rmsTask.endDate.toISOString() : null, + text: rmsTask.text, + isResolved: rmsTask.isResolved, + resolvedDate: rmsTask.resolvedDate ? rmsTask.resolvedDate.toISOString() : null, + weight: rmsTask.weight, + entityId: rmsTask.entityId ? entitiesMap.get(rmsTask.entityId) : null, + title: rmsTask.title, + plannedTime: rmsTask.plannedTime, + boardId: rmsTask.boardId ? boardsMap.get(rmsTask.boardId) : null, + stageId: stageId, + settingsId: rmsTask.settingsId ? taskSettingsMap.get(rmsTask.settingsId) : null, + }, + }); + + tasksMap.set(rmsTask.id, task.id); + } + + return tasksMap; + } + + private async copySubtasks(rmsAccountId: number, accountId: number, tasksMap: Map) { + const rmsSubtasks = await this.subtaskService.findMany(rmsAccountId); + + for (const rmsSubtask of rmsSubtasks) { + if (tasksMap.has(rmsSubtask.taskId)) { + await this.subtaskService.create(accountId, tasksMap.get(rmsSubtask.taskId), { + text: rmsSubtask.text, + resolved: rmsSubtask.resolved, + }); + } + } + } +} diff --git a/backend/src/modules/setup/account-setup/services/index.ts b/backend/src/modules/setup/account-setup/services/index.ts new file mode 100644 index 0000000..5bde882 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/index.ts @@ -0,0 +1,5 @@ +export * from './crm'; +export * from './setup-crm.service'; +export * from './setup-iam.service'; +export * from './setup-products.service'; +export * from './setup-scheduler.service'; diff --git a/backend/src/modules/setup/account-setup/services/setup-crm.service.ts b/backend/src/modules/setup/account-setup/services/setup-crm.service.ts new file mode 100644 index 0000000..4169e20 --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/setup-crm.service.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; +import { + RmsActivityService, + RmsBoardService, + RmsEntityTypeService, + RmsEntityService, + RmsFieldService, + RmsNoteService, + RmsTaskService, +} from './crm'; + +interface CrmMaps { + entityTypesMap: Map; + entitiesMap: Map; + activitiesMap: Map; + notesMap: Map; + tasksMap: Map; +} + +@Injectable() +export class SetupCrmService { + constructor( + private readonly rmsActivityService: RmsActivityService, + private readonly rmsBoardService: RmsBoardService, + private readonly rmsEntityTypeService: RmsEntityTypeService, + private readonly rmsEntityService: RmsEntityService, + private readonly rmsFieldService: RmsFieldService, + private readonly rmsNoteService: RmsNoteService, + private readonly rmsTaskService: RmsTaskService, + ) {} + + public async setupDefault(accountId: number, owner: User) { + await this.rmsBoardService.setupDefault(accountId, owner); + await this.rmsActivityService.setupDefault(accountId); + } + + public async copyAll( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entityTypeIds?: number[], + ): Promise { + const entityTypesMap = await this.rmsEntityTypeService.copyEntityTypes(rmsAccountId, accountId, entityTypeIds); + + const { boardsMap, stagesMap } = await this.rmsBoardService.copyBoardsAndStages( + rmsAccountId, + accountId, + usersMap, + entityTypesMap, + ); + + const entitiesMap = await this.rmsEntityService.copyEntities( + rmsAccountId, + accountId, + usersMap, + entityTypesMap, + boardsMap, + stagesMap, + ); + + await this.rmsFieldService.copyAll(rmsAccountId, accountId, usersMap, entityTypesMap, entitiesMap, stagesMap); + + const tasksMap = await this.rmsTaskService.copyAll( + rmsAccountId, + accountId, + usersMap, + entityTypesMap, + entitiesMap, + boardsMap, + stagesMap, + ); + + const activitiesMap = await this.rmsActivityService.copyAll(rmsAccountId, accountId, usersMap, entitiesMap); + + const notesMap = await this.rmsNoteService.copyNotes(rmsAccountId, accountId, usersMap, entitiesMap); + + return { entityTypesMap, entitiesMap, activitiesMap, notesMap, tasksMap }; + } +} diff --git a/backend/src/modules/setup/account-setup/services/setup-iam.service.ts b/backend/src/modules/setup/account-setup/services/setup-iam.service.ts new file mode 100644 index 0000000..8b5089d --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/setup-iam.service.ts @@ -0,0 +1,106 @@ +import { Injectable } from '@nestjs/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { UserRole } from '@/modules/iam/common/enums/user-role.enum'; +import { DepartmentService } from '@/modules/iam/department/department.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { UserService } from '@/modules/iam/user/user.service'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +interface IAMMaps { + usersMap: Map; + rmsOwner: User; + departmentsMap: Map; +} + +@Injectable() +export class SetupIAMService { + constructor( + private readonly userService: UserService, + private readonly departmentService: DepartmentService, + private readonly storageService: StorageService, + ) {} + + public async copyAll(rmsAccountId: number, account: Account, owner: User): Promise { + const departmentsMap = await this.copyDepartments(rmsAccountId, account.id); + const [rmsOwner, usersMap] = await this.copyUsers(rmsAccountId, account, owner, departmentsMap); + + return { usersMap, rmsOwner, departmentsMap }; + } + + private async copyDepartments( + rmsAccountId: number, + accountId: number, + departmentMap: Map = new Map(), + parentId: number = null, + ): Promise> { + const rmsDepartments = await this.departmentService.getHierarchy({ + accountId: rmsAccountId, + departmentId: parentId, + expand: ['settings'], + }); + for (const rmsDepartment of rmsDepartments) { + const department = await this.departmentService.create({ + accountId, + dto: { + name: rmsDepartment.name, + parentId: rmsDepartment.parentId ? departmentMap.get(rmsDepartment.parentId) : null, + settings: rmsDepartment.settings ? rmsDepartment.settings.toDto() : undefined, + }, + }); + departmentMap.set(rmsDepartment.id, department.id); + await this.copyDepartments(rmsAccountId, accountId, departmentMap, rmsDepartment.id); + } + + return departmentMap; + } + + private async copyUsers( + rmsAccountId: number, + account: Account, + owner: User, + departmentsMap: Map, + ): Promise<[User, Map]> { + const usersMap = new Map(); + const rmsUsers = await this.userService.findMany({ accountId: rmsAccountId }); + let rmsOwner: User = null; + for (const rmsUser of rmsUsers) { + if (rmsUser.role === UserRole.OWNER) { + usersMap.set(rmsUser.id, owner); + rmsOwner = rmsUser; + } else { + const user = await this.userService.create({ + account, + dto: { + firstName: rmsUser.firstName, + lastName: rmsUser.lastName, + email: `${account.id}${rmsUser.email}`, + password: `${account.id}${rmsUser.email}`, + phone: rmsUser.phone, + role: rmsUser.role, + isActive: rmsUser.isActive, + departmentId: rmsUser.departmentId ? departmentsMap.get(rmsUser.departmentId) : null, + position: rmsUser.position, + }, + }); + + if (rmsUser.avatarId) { + const { file, content } = await this.storageService.getFile({ + fileId: rmsUser.avatarId, + accountId: rmsAccountId, + }); + await this.userService.setAvatar({ + account, + userId: user.id, + file: StorageFile.fromFileInfo(file, Buffer.from(content)), + }); + } + + usersMap.set(rmsUser.id, user); + } + } + + return [rmsOwner, usersMap]; + } +} diff --git a/backend/src/modules/setup/account-setup/services/setup-products.service.ts b/backend/src/modules/setup/account-setup/services/setup-products.service.ts new file mode 100644 index 0000000..e79bace --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/setup-products.service.ts @@ -0,0 +1,338 @@ +import { Injectable } from '@nestjs/common'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { StorageService } from '@/modules/storage/storage.service'; +import { StorageFile } from '@/modules/storage/types/storage-file'; + +import { ProductsSectionService } from '@/modules/inventory/products-section/services/products-section.service'; +import { RentalIntervalService } from '@/modules/inventory/rental-interval/rental-interval.service'; +import { WarehouseService } from '@/modules/inventory/warehouse/warehouse.service'; +import { ProductCategoryService } from '@/modules/inventory/product-category/product-category.service'; +import { ProductService } from '@/modules/inventory/product/product.service'; +import { OrderStatusService } from '@/modules/inventory/order-status/order-status.service'; +import { OrderService } from '@/modules/inventory/order/services/order.service'; +import { RentalOrderService } from '@/modules/inventory/rental-order/services/rental-order.service'; + +interface ProductsMaps { + sectionsMap: Map; + productsMap: Map; + salesOrdersMap: Map; + rentalOrdersMap: Map; +} + +@Injectable() +export class SetupProductsService { + constructor( + private readonly storageService: StorageService, + private readonly productsSectionService: ProductsSectionService, + private readonly rentalIntervalService: RentalIntervalService, + private readonly warehouseService: WarehouseService, + private readonly productCategoryService: ProductCategoryService, + private readonly productService: ProductService, + private readonly orderStatusService: OrderStatusService, + private readonly orderService: OrderService, + private readonly rentalOrderService: RentalOrderService, + ) {} + + public async copyAll( + rmsAccount: Account, + rmsOwner: User, + account: Account, + owner: User, + entityTypesMap: Map, + entitiesMap: Map, + sectionIds?: number[], + ): Promise { + const sectionsMap = await this.copyProductsSections( + rmsAccount.id, + rmsOwner, + account.id, + entityTypesMap, + sectionIds, + ); + const warehousesMap = await this.copyWarehouses(rmsAccount.id, rmsOwner, account.id, owner, sectionsMap); + const categoriesMap = await this.copyProductCategories(rmsAccount.id, account.id, owner, sectionsMap); + const productsMap = await this.copyProducts( + rmsAccount, + rmsOwner, + account, + owner, + sectionsMap, + categoriesMap, + warehousesMap, + ); + const orderStatusesMap = await this.copyOrderStatuses(rmsAccount.id, account.id); + const salesOrdersMap = await this.copySalesOrders( + rmsAccount.id, + rmsOwner, + account.id, + owner, + entitiesMap, + sectionsMap, + warehousesMap, + productsMap, + orderStatusesMap, + ); + const rentalOrdersMap = await this.copyRentalOrders( + rmsAccount.id, + rmsOwner, + account.id, + owner, + entitiesMap, + sectionsMap, + warehousesMap, + productsMap, + ); + + return { sectionsMap, productsMap, salesOrdersMap, rentalOrdersMap }; + } + + private async copyProductsSections( + rmsAccountId: number, + rmsOwner: User, + accountId: number, + entityTypesMap: Map, + sectionIds?: number[], + ): Promise> { + const allSections = await this.productsSectionService.getAllFull(rmsAccountId, rmsOwner); + const rmsSections = sectionIds ? allSections.filter((s) => sectionIds.includes(s.id)) : allSections; + + const sectionsMap = new Map(); + for (const rmsSection of rmsSections) { + const section = await this.productsSectionService.create(accountId, rmsSection); + sectionsMap.set(rmsSection.id, section.id); + + if (rmsSection.links) { + const entityTypeIds: number[] = []; + for (const link of rmsSection.links.filter((l) => entityTypesMap.has(l.entityTypeId))) { + entityTypeIds.push(entityTypesMap.get(link.entityTypeId)); + } + if (entityTypeIds.length > 0) { + await this.productsSectionService.linkEntityTypes(accountId, section.id, entityTypeIds); + } + } + + const rmsRentalInterval = await this.rentalIntervalService.findRentalInterval(rmsAccountId, rmsSection.id); + if (rmsRentalInterval) { + await this.rentalIntervalService.setRentalInterval(accountId, section.id, { + type: rmsRentalInterval.type, + startTime: rmsRentalInterval.startTime, + }); + } + } + return sectionsMap; + } + + private async copyWarehouses( + rmsAccountId: number, + rmsOwner: User, + accountId: number, + owner: User, + sectionsMap: Map, + ): Promise> { + const warehousesMap = new Map(); + for (const [rmsSectionId, sectionId] of sectionsMap) { + const rmsWarehouses = await this.warehouseService.findMany({ + user: rmsOwner, + filter: { accountId: rmsAccountId, sectionId: rmsSectionId }, + }); + for (const rmsWarehouse of rmsWarehouses) { + const warehouse = await this.warehouseService.create({ + accountId, + user: owner, + sectionId, + dto: { name: rmsWarehouse.name }, + }); + warehousesMap.set(rmsWarehouse.id, warehouse.id); + } + } + return warehousesMap; + } + + private async copyProductCategories( + rmsAccountId: number, + accountId: number, + owner: User, + sectionMap: Map, + ): Promise> { + const categoriesMap: Map = new Map(); + for (const [rmsSectionId, sectionId] of sectionMap) { + const rmsCategories = await this.productCategoryService.getCategoriesFlat(rmsAccountId, rmsSectionId, null); + for (const rmsCategory of rmsCategories) { + const category = await this.productCategoryService.create(accountId, owner, sectionId, { + name: rmsCategory.name, + parentId: rmsCategory.parentId ? categoriesMap.get(rmsCategory.parentId) : null, + }); + categoriesMap.set(rmsCategory.id, category.id); + } + } + return categoriesMap; + } + + private async copyProducts( + rmsAccount: Account, + rmsOwner: User, + account: Account, + owner: User, + sectionsMap: Map, + categoriesMap: Map, + warehousesMap: Map, + ): Promise> { + const productsMap = new Map(); + + for (const [rmsSectionId, sectionId] of sectionsMap) { + const rmsProducts = await this.productService.findManyFull(rmsAccount, rmsOwner, rmsSectionId); + for (const rmsProduct of rmsProducts) { + const prices = rmsProduct.prices.map((p) => { + return { name: p.name, unitPrice: p.unitPrice, currency: p.currency, maxDiscount: p.maxDiscount }; + }); + const stocks = rmsProduct.stocks.map((s) => { + return { warehouseId: warehousesMap.get(s.warehouseId), stockQuantity: s.stockQuantity }; + }); + const photos: string[] = []; + const product = await this.productService.create(account, owner, sectionId, { + name: rmsProduct.name, + type: rmsProduct.type, + description: rmsProduct.description, + sku: rmsProduct.sku, + unit: rmsProduct.unit, + tax: rmsProduct.tax, + categoryId: rmsProduct.categoryId ? categoriesMap.get(rmsProduct.categoryId) : null, + prices, + stocks, + photoFileIds: photos, + }); + + for (const photo of rmsProduct.photoFileLinks) { + const { file, content } = await this.storageService.getFile({ + fileId: photo.fileId, + accountId: rmsAccount.id, + }); + await this.productService.uploadPhotos(account, owner, sectionId, product.id, [ + StorageFile.fromFileInfo(file, Buffer.from(content)), + ]); + } + + productsMap.set(rmsProduct.id, product.id); + } + } + + return productsMap; + } + + private async copyOrderStatuses(rmsAccountId: number, accountId: number) { + const orderStatusesMap = new Map(); + + const rmsOrderStatuses = await this.orderStatusService.findMany(rmsAccountId); + for (const rmsOrderStatus of rmsOrderStatuses) { + const orderStatus = await this.orderStatusService.create(accountId, rmsOrderStatus); + orderStatusesMap.set(rmsOrderStatus.id, orderStatus.id); + } + + return orderStatusesMap; + } + + private async copySalesOrders( + rmsAccountId: number, + rmsOwner: User, + accountId: number, + owner: User, + entitiesMap: Map, + sectionsMap: Map, + warehousesMap: Map, + productsMap: Map, + orderStatusesMap: Map, + ): Promise> { + const ordersMap = new Map(); + for (const [rmsSectionId, sectionId] of sectionsMap) { + const allRmsOrders = await this.orderService.findMany( + rmsAccountId, + rmsOwner, + { sectionId: rmsSectionId }, + { expand: ['items'] }, + ); + const rmsOrders = allRmsOrders.filter((o) => entitiesMap.has(o.entityId)); + for (const rmsOrder of rmsOrders) { + const items = rmsOrder.items + .map((i) => { + const reservations = i.reservations.map((r) => { + return { warehouseId: warehousesMap.get(r.warehouseId), quantity: r.quantity }; + }); + return { + id: undefined, + unitPrice: i.unitPrice, + quantity: i.quantity, + tax: i.tax, + discount: i.discount, + productId: productsMap.get(i.productId), + sortOrder: i.sortOrder, + productInfo: undefined, + reservations, + }; + }) + .filter((i) => i.productId); + if (items.length) { + const order = await this.orderService.create(accountId, owner, sectionId, { + entityId: entitiesMap.get(rmsOrder.entityId), + currency: rmsOrder.currency, + taxIncluded: rmsOrder.taxIncluded, + statusId: rmsOrder.statusId ? orderStatusesMap.get(rmsOrder.statusId) : null, + warehouseId: rmsOrder.warehouseId ? warehousesMap.get(rmsOrder.warehouseId) : null, + items, + }); + ordersMap.set(rmsOrder.id, order.id); + } + } + } + return ordersMap; + } + + private async copyRentalOrders( + rmsAccountId: number, + rmsOwner: User, + accountId: number, + owner: User, + entitiesMap: Map, + sectionsMap: Map, + warehousesMap: Map, + productsMap: Map, + ): Promise> { + const ordersMap = new Map(); + for (const [rmsSectionId, sectionId] of sectionsMap) { + const allRmsOrders = await this.rentalOrderService.findMany(rmsAccountId, rmsOwner, { + sectionId: rmsSectionId, + }); + const rmsOrders = allRmsOrders.filter((o) => entitiesMap.has(o.entityId)); + for (const rmsOrder of rmsOrders) { + const periods = rmsOrder.periods.map((p) => { + return { startDate: p.startDate.toISOString(), endDate: p.endDate.toISOString() }; + }); + const items = rmsOrder.items + .map((i) => { + return { + productId: productsMap.get(i.productId), + unitPrice: i.unitPrice, + tax: i.tax, + discount: i.discount, + sortOrder: i.sortOrder, + }; + }) + .filter((i) => i.productId); + if (items.length) { + const order = await this.rentalOrderService.create(accountId, owner, sectionId, { + warehouseId: rmsOrder.warehouseId ? warehousesMap.get(rmsOrder.warehouseId) : null, + currency: rmsOrder.currency, + taxIncluded: rmsOrder.taxIncluded, + status: rmsOrder.status, + entityId: entitiesMap.get(rmsOrder.entityId), + periods, + items, + }); + ordersMap.set(rmsOrder.id, order.id); + } + } + } + return ordersMap; + } +} diff --git a/backend/src/modules/setup/account-setup/services/setup-scheduler.service.ts b/backend/src/modules/setup/account-setup/services/setup-scheduler.service.ts new file mode 100644 index 0000000..e586bca --- /dev/null +++ b/backend/src/modules/setup/account-setup/services/setup-scheduler.service.ts @@ -0,0 +1,149 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; + +import { ScheduleService } from '@/modules/scheduler/schedule/services/schedule.service'; +import { ScheduleAppointmentService } from '@/modules/scheduler/schedule-appointment/schedule-appointment.service'; + +interface SchedulerMaps { + schedulesMap: Map; + appointmentsMap: Map; +} + +@Injectable() +export class SetupSchedulerService { + private readonly logger = new Logger(SetupSchedulerService.name); + + constructor( + private readonly scheduleService: ScheduleService, + private readonly appointmentService: ScheduleAppointmentService, + ) {} + + public async copyAll( + rmsAccountId: number, + accountId: number, + owner: User, + usersMap: Map, + departmentsMap: Map, + entityTypesMap: Map, + entitiesMap: Map, + sectionsMap: Map, + ordersMap: Map, + schedulerIds?: number[], + ): Promise { + const { schedulesMap, performersMap } = await this.copySchedules( + rmsAccountId, + accountId, + owner, + usersMap, + departmentsMap, + entityTypesMap, + sectionsMap, + schedulerIds, + ); + + const appointmentsMap = await this.copyAppointments( + rmsAccountId, + accountId, + usersMap, + entitiesMap, + ordersMap, + schedulesMap, + performersMap, + ); + + return { schedulesMap, appointmentsMap }; + } + + private async copySchedules( + rmsAccountId: number, + accountId: number, + owner: User, + usersMap: Map, + departmentsMap: Map, + entityTypesMap: Map, + sectionsMap: Map, + schedulerIds?: number[], + ): Promise<{ schedulesMap: Map; performersMap: Map }> { + const allSchedules = await this.scheduleService.findMany({ filter: { accountId: rmsAccountId } }); + const rmsSchedules = schedulerIds ? allSchedules.filter((s) => schedulerIds.includes(s.id)) : allSchedules; + + const schedulesMap = new Map(); + const performersMap = new Map(); + for (const rmsSchedule of rmsSchedules) { + try { + const schedule = await this.scheduleService.create({ + accountId, + userId: owner.id, + dto: { + name: rmsSchedule.name, + icon: rmsSchedule.icon, + type: rmsSchedule.type, + timePeriod: rmsSchedule.timePeriod, + appointmentLimit: rmsSchedule.appointmentLimit, + entityTypeId: rmsSchedule.entityTypeId ? entityTypesMap.get(rmsSchedule.entityTypeId) : null, + productsSectionId: rmsSchedule.productsSectionId ? sectionsMap.get(rmsSchedule.productsSectionId) : null, + performers: rmsSchedule.performers.map((p) => ({ + type: p.type, + userId: p.userId ? usersMap.get(p.userId).id : null, + departmentId: p.departmentId ? departmentsMap.get(p.departmentId) : null, + })), + }, + }); + schedulesMap.set(rmsSchedule.id, schedule.id); + rmsSchedule.performers.forEach((rmsPerformer, idx) => + performersMap.set(rmsPerformer.id, schedule.performers[idx].id), + ); + } catch (e) { + this.logger.error(`Error during schedule creation for account ${accountId}`, (e as Error)?.stack); + continue; + } + } + + return { schedulesMap, performersMap }; + } + + private async copyAppointments( + rmsAccountId: number, + accountId: number, + usersMap: Map, + entitiesMap: Map, + ordersMap: Map, + schedulesMap: Map, + performersMap: Map, + ): Promise> { + const appointmentsMap = new Map(); + + for (const [rmsScheduleId, scheduleId] of schedulesMap) { + const rmsAppointments = await this.appointmentService.findMany({ + filter: { accountId: rmsAccountId, scheduleId: rmsScheduleId }, + joinPerformer: true, + }); + for (const rmsAppointment of rmsAppointments) { + try { + const appointment = await this.appointmentService.create({ + accountId, + user: usersMap.get(rmsAppointment.ownerId), + dto: { + scheduleId, + startDate: rmsAppointment.startDate.toISOString(), + endDate: rmsAppointment.endDate.toISOString(), + status: rmsAppointment.status, + title: rmsAppointment.title, + comment: rmsAppointment.comment, + entityId: rmsAppointment.entityId ? entitiesMap.get(rmsAppointment.entityId) : null, + performerId: performersMap.get(rmsAppointment.performerId), + orderId: rmsAppointment.orderId ? ordersMap.get(rmsAppointment.orderId) : null, + }, + }); + appointmentsMap.set(rmsAppointment.id, appointment.id); + } catch (e) { + this.logger.error(`Error during appointment creation for schedule ${scheduleId}`, (e as Error)?.stack); + continue; + } + } + } + + return appointmentsMap; + } +} diff --git a/backend/src/modules/setup/common/enums/demo-data-type.enum.ts b/backend/src/modules/setup/common/enums/demo-data-type.enum.ts new file mode 100644 index 0000000..e82e460 --- /dev/null +++ b/backend/src/modules/setup/common/enums/demo-data-type.enum.ts @@ -0,0 +1,9 @@ +export enum DemoDataType { + User = 'user', + Entity = 'entity', + Task = 'task', + Product = 'product', + SalesOrder = 'sales_order', + RentalOrder = 'rental_order', + ScheduleAppointment = 'schedule_appointment', +} diff --git a/backend/src/modules/setup/common/enums/index.ts b/backend/src/modules/setup/common/enums/index.ts new file mode 100644 index 0000000..4e19f1b --- /dev/null +++ b/backend/src/modules/setup/common/enums/index.ts @@ -0,0 +1,3 @@ +export * from './demo-data-type.enum'; +export * from './industry-code.enum'; +export * from './rmx-module-prefix.enum'; diff --git a/backend/src/modules/setup/common/enums/industry-code.enum.ts b/backend/src/modules/setup/common/enums/industry-code.enum.ts new file mode 100644 index 0000000..e188605 --- /dev/null +++ b/backend/src/modules/setup/common/enums/industry-code.enum.ts @@ -0,0 +1,8 @@ +export enum IndustryCode { + IT_AND_DEVELOPMENT = 'it_and_development', + CONSTRUCTION_AND_ENGINEERING = 'construction_and_engineering', + ADVERTISING_AND_MARKETING = 'advertising_and_marketing', + CONSULTING_AND_OUTSOURCING = 'consulting_and_outsourcing', + MANUFACTURING = 'manufacturing', + EDUCATION = 'education', +} diff --git a/backend/src/modules/setup/common/enums/rmx-module-prefix.enum.ts b/backend/src/modules/setup/common/enums/rmx-module-prefix.enum.ts new file mode 100644 index 0000000..77ed845 --- /dev/null +++ b/backend/src/modules/setup/common/enums/rmx-module-prefix.enum.ts @@ -0,0 +1,5 @@ +export enum RmsModulePrefix { + EntityType = 'et', + ProductSection = 'ps', + Scheduler = 'sc', +} diff --git a/backend/src/modules/setup/common/index.ts b/backend/src/modules/setup/common/index.ts new file mode 100644 index 0000000..968a286 --- /dev/null +++ b/backend/src/modules/setup/common/index.ts @@ -0,0 +1,2 @@ +export * from './enums'; +export * from './types'; diff --git a/backend/src/modules/setup/common/types/index.ts b/backend/src/modules/setup/common/types/index.ts new file mode 100644 index 0000000..ed0f570 --- /dev/null +++ b/backend/src/modules/setup/common/types/index.ts @@ -0,0 +1 @@ +export * from './rms-modules'; diff --git a/backend/src/modules/setup/common/types/rms-modules.ts b/backend/src/modules/setup/common/types/rms-modules.ts new file mode 100644 index 0000000..7e84d4b --- /dev/null +++ b/backend/src/modules/setup/common/types/rms-modules.ts @@ -0,0 +1,5 @@ +export class RmsModules { + entityTypeIds: number[] = []; + productSectionIds: number[] = []; + schedulerIds: number[] = []; +} diff --git a/backend/src/modules/setup/demo-data/demo-data.controller.ts b/backend/src/modules/setup/demo-data/demo-data.controller.ts new file mode 100644 index 0000000..fba6fee --- /dev/null +++ b/backend/src/modules/setup/demo-data/demo-data.controller.ts @@ -0,0 +1,28 @@ +import { Controller, Get, Delete } from '@nestjs/common'; +import { ApiOkResponse, ApiDefaultResponse, ApiTags } from '@nestjs/swagger'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { AuthDataPrefetch } from '@/modules/iam/common/decorators/auth-data-prefetch.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { DemoDataService } from './demo-data.service'; + +@ApiTags('setup/demo-data') +@Controller('/setup/demo-data') +@JwtAuthorized() +export class DemoDataController { + constructor(private service: DemoDataService) {} + + @ApiDefaultResponse({ description: 'Check demo data exists in account', type: Boolean }) + @Get('exists') + public async exists(@CurrentAuth() { accountId }: AuthData): Promise { + return this.service.exists(accountId); + } + + @ApiOkResponse({ description: 'Delete all demo data in account' }) + @AuthDataPrefetch({ user: true }) + @Delete() + public async delete(@CurrentAuth() { accountId, user }: AuthData): Promise { + await this.service.delete(accountId, user); + } +} diff --git a/backend/src/modules/setup/demo-data/demo-data.module.ts b/backend/src/modules/setup/demo-data/demo-data.module.ts new file mode 100644 index 0000000..46f0224 --- /dev/null +++ b/backend/src/modules/setup/demo-data/demo-data.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { CrmModule } from '@/CRM/crm.module'; +import { InventoryModule } from '@/modules/inventory/inventory.module'; +import { SchedulerModule } from '@/modules/scheduler/scheduler.module'; + +import { DemoData } from './entities/demo-data.entity'; +import { DemoDataController } from './demo-data.controller'; +import { DemoDataService } from './demo-data.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([DemoData]), IAMModule, CrmModule, InventoryModule, SchedulerModule], + controllers: [DemoDataController], + providers: [DemoDataService], + exports: [DemoDataService], +}) +export class DemoDataModule {} diff --git a/backend/src/modules/setup/demo-data/demo-data.service.ts b/backend/src/modules/setup/demo-data/demo-data.service.ts new file mode 100644 index 0000000..97541a7 --- /dev/null +++ b/backend/src/modules/setup/demo-data/demo-data.service.ts @@ -0,0 +1,88 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { ProductService } from '@/modules/inventory/product/product.service'; +import { OrderService } from '@/modules/inventory/order/services/order.service'; +import { RentalOrderService } from '@/modules/inventory/rental-order/services/rental-order.service'; +import { ScheduleAppointmentService } from '@/modules/scheduler/schedule-appointment/schedule-appointment.service'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { TaskService } from '@/CRM/task/task.service'; + +import { DemoDataType } from '../common/enums/demo-data-type.enum'; +import { DemoData } from './entities/demo-data.entity'; + +@Injectable() +export class DemoDataService { + constructor( + @InjectRepository(DemoData) + private readonly repository: Repository, + private readonly userService: UserService, + private readonly entityService: EntityService, + private readonly taskService: TaskService, + private readonly productService: ProductService, + private readonly orderService: OrderService, + private readonly rentalService: RentalOrderService, + private readonly appointmentService: ScheduleAppointmentService, + ) {} + + public async create(accountId: number, type: DemoDataType, ids: number[]) { + await this.repository.insert(new DemoData(accountId, type, ids)); + } + + public async exists(accountId: number): Promise { + return (await this.repository.countBy({ accountId })) > 0; + } + + public async delete(accountId: number, user: User) { + const demoData = await this.repository.findBy({ accountId }); + + const appointments = demoData.filter((dd) => dd.type === DemoDataType.ScheduleAppointment); + if (appointments.length) { + await Promise.all( + appointments.map((a) => this.appointmentService.delete({ accountId, user, filter: { appointmentId: a.ids } })), + ); + await this.repository.delete(appointments.map((dd) => dd.id)); + } + + const rentalOrders = demoData.filter((dd) => dd.type === DemoDataType.RentalOrder); + if (rentalOrders.length) { + await Promise.all(rentalOrders.map((ro) => this.rentalService.delete(accountId, user, { orderId: ro.ids }))); + await this.repository.delete(rentalOrders.map((dd) => dd.id)); + } + + const salesOrders = demoData.filter((dd) => dd.type === DemoDataType.SalesOrder); + if (salesOrders.length) { + await Promise.all(salesOrders.map((order) => this.orderService.delete(accountId, { orderId: order.ids }))); + await this.repository.delete(salesOrders.map((dd) => dd.id)); + } + + const products = demoData.filter((dd) => dd.type === DemoDataType.Product); + if (products.length) { + await Promise.all(products.map((product) => this.productService.delete(accountId, product.ids))); + await this.repository.delete(products.map((dd) => dd.id)); + } + + const tasks = demoData.filter((dd) => dd.type === DemoDataType.Task); + if (tasks.length) { + await Promise.all( + tasks.map((task) => this.taskService.delete({ user, filter: { accountId, taskId: task.ids } })), + ); + await this.repository.delete(tasks.map((dd) => dd.id)); + } + + const entities = demoData.filter((dd) => dd.type === DemoDataType.Entity); + if (entities.length) { + await Promise.all(entities.map((entity) => this.entityService.deleteMany(accountId, user, entity.ids))); + await this.repository.delete(entities.map((dd) => dd.id)); + } + + const users = demoData.filter((dd) => dd.type === DemoDataType.User); + if (users.length) { + await Promise.all(users.map((user) => this.userService.delete({ accountId, userId: user.ids }))); + await this.repository.delete(users.map((dd) => dd.id)); + } + } +} diff --git a/backend/src/modules/setup/demo-data/entities/demo-data.entity.ts b/backend/src/modules/setup/demo-data/entities/demo-data.entity.ts new file mode 100644 index 0000000..81d011d --- /dev/null +++ b/backend/src/modules/setup/demo-data/entities/demo-data.entity.ts @@ -0,0 +1,30 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { DemoDataType } from '../../common/enums/demo-data-type.enum'; + +@Entity() +export class DemoData { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + type: DemoDataType; + + @Column('simple-array', { name: 'ids' }) + private _ids: string[]; + + constructor(accountId: number, type: DemoDataType, ids: number[]) { + this.accountId = accountId; + this.type = type; + this.ids = ids; + } + + public get ids(): number[] { + return this._ids?.map((id) => Number(id)) || []; + } + public set ids(value: number[]) { + this._ids = value?.map((id) => id.toString()) || []; + } +} diff --git a/backend/src/modules/setup/rms/dto/industry.dto.ts b/backend/src/modules/setup/rms/dto/industry.dto.ts new file mode 100644 index 0000000..c05ed63 --- /dev/null +++ b/backend/src/modules/setup/rms/dto/industry.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsString } from 'class-validator'; + +import { IndustryCode } from '../../common/enums/industry-code.enum'; +import { Industry } from '../entities/industry.entity'; +import { ReadyMadeSolutionDto } from './ready-made-solution.dto'; + +export class IndustryDto { + @ApiProperty({ enum: IndustryCode }) + @IsEnum(IndustryCode) + code: IndustryCode; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsString() + color: string; + + @ApiProperty({ type: [ReadyMadeSolutionDto] }) + @IsArray() + readyMadeSolutions: ReadyMadeSolutionDto[]; + + constructor(code: IndustryCode, name: string, color: string, readyMadeSolutions: ReadyMadeSolutionDto[]) { + this.code = code; + this.name = name; + this.color = color; + this.readyMadeSolutions = readyMadeSolutions; + } + + public static fromModel(model: Industry, readyMadeSolutions: ReadyMadeSolutionDto[]) { + return new IndustryDto(model.code, model.name, model.color, readyMadeSolutions); + } +} diff --git a/backend/src/modules/setup/rms/dto/ready-made-solution.dto.ts b/backend/src/modules/setup/rms/dto/ready-made-solution.dto.ts new file mode 100644 index 0000000..54ee895 --- /dev/null +++ b/backend/src/modules/setup/rms/dto/ready-made-solution.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class ReadyMadeSolutionDto { + @ApiProperty() + @IsString() + code: string; + + @ApiProperty() + @IsString() + name: string; + + constructor(code: string, name: string) { + this.code = code; + this.name = name; + } +} diff --git a/backend/src/modules/setup/rms/entities/industry.entity.ts b/backend/src/modules/setup/rms/entities/industry.entity.ts new file mode 100644 index 0000000..60cb886 --- /dev/null +++ b/backend/src/modules/setup/rms/entities/industry.entity.ts @@ -0,0 +1,21 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { IndustryCode } from '../../common/enums/industry-code.enum'; + +@Entity() +export class Industry { + @PrimaryColumn() + code: IndustryCode; + + @Column() + name: string; + + @Column() + color: string; + + @Column() + sortOrder: number; + + @Column() + active: boolean; +} diff --git a/backend/src/modules/setup/rms/entities/ready-made-solution.entity.ts b/backend/src/modules/setup/rms/entities/ready-made-solution.entity.ts new file mode 100644 index 0000000..7278298 --- /dev/null +++ b/backend/src/modules/setup/rms/entities/ready-made-solution.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { IndustryCode } from '../../common/enums/industry-code.enum'; +import { ReadyMadeSolutionDto } from '../dto/ready-made-solution.dto'; + +@Entity() +export class ReadyMadeSolution { + @PrimaryColumn() + code: string; + + @Column() + name: string; + + @Column({ nullable: true }) + accountId: number | null; + + @Column() + sortOrder: number; + + @Column() + active: boolean; + + @Column() + industryCode: IndustryCode | null; + + public toDto(): ReadyMadeSolutionDto { + return new ReadyMadeSolutionDto(this.code, this.name); + } +} diff --git a/backend/src/modules/setup/rms/rms.controller.ts b/backend/src/modules/setup/rms/rms.controller.ts new file mode 100644 index 0000000..bf00aec --- /dev/null +++ b/backend/src/modules/setup/rms/rms.controller.ts @@ -0,0 +1,20 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { ApiAccessRequired } from '@/modules/iam/common/decorators/api-access-required.decorator'; + +import { IndustryService } from './services/industry.service'; +import { IndustryDto } from './dto/industry.dto'; + +@ApiTags('setup/rms') +@Controller('/setup/rms') +@ApiAccessRequired() +export class RmsController { + constructor(private readonly service: IndustryService) {} + + @ApiCreatedResponse({ description: 'Industries', type: [IndustryDto] }) + @Get('industries') + public async getIndustries(): Promise { + return await this.service.getIndustryDtos(); + } +} diff --git a/backend/src/modules/setup/rms/rms.module.ts b/backend/src/modules/setup/rms/rms.module.ts new file mode 100644 index 0000000..7d2bd4d --- /dev/null +++ b/backend/src/modules/setup/rms/rms.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import { Industry } from './entities/industry.entity'; +import { ReadyMadeSolution } from './entities/ready-made-solution.entity'; +import { IndustryService } from './services/industry.service'; +import { RmsService } from './services/rms.service'; +import { RmsController } from './rms.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([Industry, ReadyMadeSolution]), IAMModule], + controllers: [RmsController], + providers: [RmsService, IndustryService], + exports: [RmsService], +}) +export class RmsModule {} diff --git a/backend/src/modules/setup/rms/services/industry.service.ts b/backend/src/modules/setup/rms/services/industry.service.ts new file mode 100644 index 0000000..a991c12 --- /dev/null +++ b/backend/src/modules/setup/rms/services/industry.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { IndustryDto } from '../dto/industry.dto'; +import { Industry } from '../entities/industry.entity'; +import { RmsService } from './rms.service'; + +@Injectable() +export class IndustryService { + constructor( + @InjectRepository(Industry) + private readonly repository: Repository, + private readonly rmsService: RmsService, + ) {} + + public async getIndustryDtos(): Promise { + const industries = await this.repository + .createQueryBuilder() + .where({ active: true }) + .orderBy('sort_order', 'ASC') + .getMany(); + const solutions = await this.rmsService.findMany({ isActive: true }); + + const industryDtos: IndustryDto[] = []; + for (const industry of industries) { + const solutionDtos = solutions.filter((s) => s.industryCode === industry.code).map((s) => s.toDto()); + industryDtos.push(IndustryDto.fromModel(industry, solutionDtos)); + } + + return industryDtos; + } +} diff --git a/backend/src/modules/setup/rms/services/rms.service.ts b/backend/src/modules/setup/rms/services/rms.service.ts new file mode 100644 index 0000000..1ea2df6 --- /dev/null +++ b/backend/src/modules/setup/rms/services/rms.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { ReadyMadeSolution } from '../entities/ready-made-solution.entity'; + +interface FindFilter { + code?: string; + isActive?: boolean; +} + +@Injectable() +export class RmsService { + constructor( + @InjectRepository(ReadyMadeSolution) + private repository: Repository, + ) {} + + public async findOne(filter?: FindFilter): Promise { + return this.createQb(filter).getOne(); + } + + public async findMany(filter?: FindFilter): Promise { + return this.createQb(filter).getMany(); + } + + private createQb(filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('rms').orderBy('sort_order', 'ASC'); + + if (filter?.code) { + qb.where({ code: filter.code }); + } + if (filter?.isActive !== undefined) { + qb.where({ active: filter.isActive }); + } + + return qb; + } +} diff --git a/backend/src/modules/setup/setup.module.ts b/backend/src/modules/setup/setup.module.ts new file mode 100644 index 0000000..75c6041 --- /dev/null +++ b/backend/src/modules/setup/setup.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { AccountSetupModule } from './account-setup/account-setup.module'; +import { DemoDataModule } from './demo-data/demo-data.module'; +import { RmsModule } from './rms/rms.module'; +//import { TestDataModule } from './test-data/test-data.module'; + +@Module({ + imports: [RmsModule, DemoDataModule, AccountSetupModule /*, TestDataModule*/], +}) +export class SetupModule {} diff --git a/backend/src/modules/setup/test-data/dto/create-test-accounts-query.ts b/backend/src/modules/setup/test-data/dto/create-test-accounts-query.ts new file mode 100644 index 0000000..e1045e3 --- /dev/null +++ b/backend/src/modules/setup/test-data/dto/create-test-accounts-query.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsDateString, IsNumber, IsString } from 'class-validator'; + +export class CreateTestAccountsQuery { + @ApiProperty() + @IsString() + token: string; + + @ApiProperty() + @IsNumber() + count: number; + + @ApiProperty() + @IsDateString() + fromDate: string; + + @ApiProperty() + @IsDateString() + toDate: string; +} diff --git a/backend/src/modules/setup/test-data/entities/test-account.entity.ts b/backend/src/modules/setup/test-data/entities/test-account.entity.ts new file mode 100644 index 0000000..b2740d0 --- /dev/null +++ b/backend/src/modules/setup/test-data/entities/test-account.entity.ts @@ -0,0 +1,7 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class TestAccount { + @PrimaryColumn() + accountId: number; +} diff --git a/backend/src/modules/setup/test-data/public-test-data.controller.ts b/backend/src/modules/setup/test-data/public-test-data.controller.ts new file mode 100644 index 0000000..ef55e84 --- /dev/null +++ b/backend/src/modules/setup/test-data/public-test-data.controller.ts @@ -0,0 +1,38 @@ +import { Controller, Get, Query, Redirect } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { UrlGeneratorService } from '@/common'; +import { ApiAccessRequired } from '@/modules/iam/common/decorators/api-access-required.decorator'; + +import { TestDataService } from './test-data.service'; +import { CreateTestAccountsQuery } from './dto/create-test-accounts-query'; + +@ApiExcludeController(true) +@Controller('/setup/test-data') +export class PublicTestDataController { + constructor( + private readonly service: TestDataService, + private readonly urlGeneratorService: UrlGeneratorService, + ) {} + + @Get('account/create') + @ApiAccessRequired() + public async createTestAccounts(@Query() query: CreateTestAccountsQuery) { + await this.service.createTestAccounts(query); + } + + @Redirect() + @Get('account/login') + public async loginTestUser() { + const link = await this.service.loginTestUser(); + const url = link + ? this.urlGeneratorService.createUrl({ + route: 'login-link', + subdomain: link.subdomain, + query: { loginLink: link.loginLink }, + }) + : this.urlGeneratorService.createUrl(); + + return { url, statusCode: 302 }; + } +} diff --git a/backend/src/modules/setup/test-data/test-data.module.ts b/backend/src/modules/setup/test-data/test-data.module.ts new file mode 100644 index 0000000..c3c54d1 --- /dev/null +++ b/backend/src/modules/setup/test-data/test-data.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import { TestAccount } from './entities/test-account.entity'; +import { PublicTestDataController } from './public-test-data.controller'; +import { TestDataService } from './test-data.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([TestAccount]), IAMModule, CrmModule], + controllers: [PublicTestDataController], + providers: [TestDataService], +}) +export class TestDataModule {} diff --git a/backend/src/modules/setup/test-data/test-data.service.ts b/backend/src/modules/setup/test-data/test-data.service.ts new file mode 100644 index 0000000..50ea05c --- /dev/null +++ b/backend/src/modules/setup/test-data/test-data.service.ts @@ -0,0 +1,96 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { faker } from '@faker-js/faker'; + +import { ForbiddenError, DateUtil } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { AccountService } from '@/modules/iam/account/account.service'; +import { AuthenticationService } from '@/modules/iam/authentication/authentication.service'; +import { LoginLinkDto } from '@/modules/iam/authentication/dto/login-link.dto'; +import { UserService } from '@/modules/iam/user/user.service'; + +import { TestAccount } from './entities/test-account.entity'; +import { CreateTestAccountsQuery } from './dto/create-test-accounts-query'; + +@Injectable() +export class TestDataService { + constructor( + private readonly configService: ConfigService, + @InjectRepository(TestAccount) + private readonly repository: Repository, + private readonly authService: AuthenticationService, + private readonly accountService: AccountService, + private readonly userService: UserService, + ) {} + + public async loginTestUser(): Promise { + const testAccountCount = await this.repository.count(); + const testAccountNumber = Math.floor(Math.random() * testAccountCount); + const testAccount = await this.repository.find({ skip: testAccountNumber, take: 1 }); + + if (testAccount.length) { + const account = await this.accountService.findOne({ accountId: testAccount[0].accountId }); + const users = await this.userService.findMany({ accountId: account.id, isActive: true }); + const testUserNumber = Math.floor(Math.random() * users.length); + const user = users[testUserNumber]; + return this.authService.createLoginLink({ accountId: account.id, subdomain: account.subdomain, userId: user.id }); + } + + return null; + } + + public async createTestAccounts(query: CreateTestAccountsQuery) { + const verificationToken = this.configService.get('application').verificationToken; + if (!verificationToken || !query.token || verificationToken !== query.token) { + throw new ForbiddenError('Invalid verification token'); + } + + const dates = this.generateRandomDates(query.fromDate, query.toDate, query.count); + for (let i = 0; i < query.count; i++) { + await this.createTestAccount(dates[i]); + } + } + + private async createTestAccount(createdAt: Date): Promise { + try { + const sexType = faker.person.sexType(); + const firstName = faker.person.firstName(sexType); + const lastName = faker.person.lastName(sexType); + const email = faker.internet.email({ firstName, lastName }); + const { account } = await this.accountService.create( + { + firstName, + lastName, + email, + phone: faker.phone.number(), + password: email, + companyName: faker.company.name(), + }, + { + skipPhoneCheck: true, + createdAt: createdAt, + subscription: { isTrial: false, termInDays: 3650 }, + }, + ); + await this.repository.save({ accountId: account.id }); + return true; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e) { + return false; + } + } + + private generateRandomDates(fromDate: string, toDate: string, count: number): Date[] { + const start = DateUtil.fromISOString(fromDate).getTime(); + const end = DateUtil.fromISOString(toDate).getTime(); + + return Array.from({ length: count }, () => { + const skew = Math.random() ** 1.618; + const skewedTimestamp = start + (1 - skew) * (end - start); + return new Date(skewedTimestamp); + }).sort((a, b) => a.getTime() - b.getTime()); + } +} diff --git a/backend/src/modules/storage/config/aws.config.ts b/backend/src/modules/storage/config/aws.config.ts new file mode 100644 index 0000000..58b215c --- /dev/null +++ b/backend/src/modules/storage/config/aws.config.ts @@ -0,0 +1,20 @@ +import { registerAs } from '@nestjs/config'; + +export interface AwsConfig { + endpointUrl: string; + region: string; + bucket: string; + accessKeyId: string; + secretAccessKey: string; +} + +export default registerAs( + 'aws', + (): AwsConfig => ({ + endpointUrl: process.env.AWS_ENDPOINT_URL, + region: process.env.AWS_REGION, + bucket: process.env.AWS_BUCKET, + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }), +); diff --git a/backend/src/modules/storage/dto/file-info-result.dto.ts b/backend/src/modules/storage/dto/file-info-result.dto.ts new file mode 100644 index 0000000..fb91360 --- /dev/null +++ b/backend/src/modules/storage/dto/file-info-result.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsDateString, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class FileInfoResultDto { + @ApiProperty({ description: 'File ID' }) + @IsString() + id: string; + + @ApiProperty({ description: 'File name' }) + @IsString() + fileName: string; + + @ApiProperty({ description: 'File size' }) + @IsNumber() + fileSize: number; + + @ApiProperty({ description: 'Mime type' }) + @IsString() + mimeType: string; + + @ApiPropertyOptional({ description: 'Download URL', nullable: true }) + @IsOptional() + @IsString() + downloadUrl?: string | null; + + @ApiPropertyOptional({ description: 'Preview URL', nullable: true }) + @IsOptional() + @IsString() + previewUrl?: string | null; + + @ApiProperty({ description: 'Created at' }) + @IsDateString() + createdAt: string; +} diff --git a/backend/src/modules/storage/dto/file-upload-request.ts b/backend/src/modules/storage/dto/file-upload-request.ts new file mode 100644 index 0000000..dc5f2f2 --- /dev/null +++ b/backend/src/modules/storage/dto/file-upload-request.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class FilesUploadRequest { + @ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } }) + files: any[]; +} diff --git a/backend/src/modules/storage/dto/file-upload-result.ts b/backend/src/modules/storage/dto/file-upload-result.ts new file mode 100644 index 0000000..b7de1a0 --- /dev/null +++ b/backend/src/modules/storage/dto/file-upload-result.ts @@ -0,0 +1,39 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsString, IsNumber, IsOptional, IsDateString } from 'class-validator'; + +export class FileUploadResult { + @ApiPropertyOptional({ description: 'Upload key', nullable: true }) + @IsOptional() + @IsString() + key?: string | null; + + @ApiProperty({ description: 'File ID' }) + @IsString() + id: string; + + @ApiProperty({ description: 'File name' }) + @IsString() + fileName: string; + + @ApiProperty({ description: 'File size' }) + @IsNumber() + fileSize: number; + + @ApiProperty({ description: 'Mime type' }) + @IsString() + mimeType: string; + + @ApiPropertyOptional({ description: 'Download URL', nullable: true }) + @IsOptional() + @IsString() + downloadUrl?: string | null; + + @ApiPropertyOptional({ description: 'Preview URL', nullable: true }) + @IsOptional() + @IsString() + previewUrl?: string | null; + + @ApiProperty({ description: 'Created at' }) + @IsDateString() + createdAt: string; +} diff --git a/backend/src/modules/storage/dto/image-options.dto.ts b/backend/src/modules/storage/dto/image-options.dto.ts new file mode 100644 index 0000000..fa82f40 --- /dev/null +++ b/backend/src/modules/storage/dto/image-options.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class ImageOptionsDto { + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + width?: number; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + height?: number; +} diff --git a/backend/src/modules/storage/dto/index.ts b/backend/src/modules/storage/dto/index.ts new file mode 100644 index 0000000..50ed03a --- /dev/null +++ b/backend/src/modules/storage/dto/index.ts @@ -0,0 +1,4 @@ +export * from './file-info-result.dto'; +export * from './file-upload-request'; +export * from './file-upload-result'; +export * from './image-options.dto'; diff --git a/backend/src/modules/storage/entities/file-info.entity.ts b/backend/src/modules/storage/entities/file-info.entity.ts new file mode 100644 index 0000000..c88ac64 --- /dev/null +++ b/backend/src/modules/storage/entities/file-info.entity.ts @@ -0,0 +1,66 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { imageMimeTypes, MimeType } from '../enums/mime-type.enum'; + +@Entity() +export class FileInfo { + @PrimaryColumn() + id: string; + + @Column({ nullable: true }) + createdBy: number | null; + + @Column() + originalName: string; + + @Column() + mimeType: string; + + @Column() + size: number; + + @Column({ nullable: true }) + hashSha256: string | null; + + @Column() + storePath: string; + + @Column() + isUsed: boolean; + + @Column() + accountId: number; + + @Column() + createdAt: Date; + + constructor( + id: string, + accountId: number, + createdBy: number | null, + originalName: string, + mimeType: string, + size: number, + hashSha256: string, + storePath: string, + isUsed: boolean, + createdAt?: Date, + ) { + this.id = id; + this.accountId = accountId; + this.createdBy = createdBy; + this.originalName = originalName; + this.mimeType = mimeType; + this.size = size; + this.hashSha256 = hashSha256; + this.storePath = storePath; + this.isUsed = isUsed; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public isImage(): boolean { + return imageMimeTypes.includes(this.mimeType as MimeType); + } +} diff --git a/backend/src/modules/storage/entities/index.ts b/backend/src/modules/storage/entities/index.ts new file mode 100644 index 0000000..d30d1c3 --- /dev/null +++ b/backend/src/modules/storage/entities/index.ts @@ -0,0 +1 @@ +export * from './file-info.entity'; diff --git a/backend/src/modules/storage/enums/index.ts b/backend/src/modules/storage/enums/index.ts new file mode 100644 index 0000000..c2eded4 --- /dev/null +++ b/backend/src/modules/storage/enums/index.ts @@ -0,0 +1 @@ +export * from './mime-type.enum'; diff --git a/backend/src/modules/storage/enums/mime-type.enum.ts b/backend/src/modules/storage/enums/mime-type.enum.ts new file mode 100644 index 0000000..852318b --- /dev/null +++ b/backend/src/modules/storage/enums/mime-type.enum.ts @@ -0,0 +1,10 @@ +export enum MimeType { + PNG = 'image/png', + JPEG = 'image/jpeg', + GIF = 'image/gif', + WEBP = 'image/webp', + PDF = 'application/pdf', + DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', +} + +export const imageMimeTypes = [MimeType.PNG, MimeType.JPEG, MimeType.GIF, MimeType.WEBP]; diff --git a/backend/src/modules/storage/providers/aws-s3.provider.ts b/backend/src/modules/storage/providers/aws-s3.provider.ts new file mode 100644 index 0000000..9435531 --- /dev/null +++ b/backend/src/modules/storage/providers/aws-s3.provider.ts @@ -0,0 +1,60 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; + +import { AwsConfig } from '../config/aws.config'; + +@Injectable() +export class AwsS3Provider { + private readonly logger = new Logger(AwsS3Provider.name); + private s3Client: S3Client; + private _bucketName: string; + + constructor(private readonly configService: ConfigService) { + this._bucketName = this.configService.get('aws').bucket; + + this.s3Client = new S3Client({}); + } + + public async storeBuffer( + key: string, + buffer: Buffer, + sha256Hash: string, + mimeType: string, + originalName: string, + ): Promise { + try { + const param = { + Bucket: this._bucketName, + Key: key, + Body: buffer, + ContentType: mimeType, + ChecksumSHA256: sha256Hash, + ContentDisposition: originalName ? `attachment; filename="${encodeURI(originalName)}"` : originalName, + }; + await this.s3Client.send(new PutObjectCommand(param)); + return true; + } catch (e) { + this.logger.error(`Error in AwsS3Provider`, (e as Error)?.stack); + return false; + } + } + + public async deleteFile(key: string): Promise { + try { + await this.s3Client.send(new DeleteObjectCommand({ Bucket: this._bucketName, Key: key })); + return true; + } catch (e) { + return false; + } + } + + public async getFile(key: string): Promise { + try { + const data = await this.s3Client.send(new GetObjectCommand({ Bucket: this._bucketName, Key: key })); + return await data?.Body?.transformToByteArray(); + } catch (e) { + return null; + } + } +} diff --git a/backend/src/modules/storage/providers/index.ts b/backend/src/modules/storage/providers/index.ts new file mode 100644 index 0000000..0af015e --- /dev/null +++ b/backend/src/modules/storage/providers/index.ts @@ -0,0 +1 @@ +export * from './aws-s3.provider'; diff --git a/backend/src/modules/storage/storage-public.controller.ts b/backend/src/modules/storage/storage-public.controller.ts new file mode 100644 index 0000000..4ccf408 --- /dev/null +++ b/backend/src/modules/storage/storage-public.controller.ts @@ -0,0 +1,56 @@ +import { Controller, Get, Param, Query, Res, StreamableFile } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; +import { Response } from 'express'; + +import { ImageOptionsDto } from './dto'; +import { StorageService } from './storage.service'; + +@ApiTags('storage') +@Controller('storage') +export class StoragePublicController { + constructor(private readonly service: StorageService) {} + + @ApiOperation({ summary: 'Get image from storage', description: 'Get image from storage by account id' }) + @ApiParam({ name: 'accountId', type: Number, required: true, description: 'Account id' }) + @ApiParam({ name: 'id', type: String, required: true, description: 'Image id' }) + @ApiOkResponse({ description: 'Image file', type: StreamableFile }) + @Get('image/:accountId/:id') + async getImage( + @Param('accountId') accountId: number, + @Param('id') id: string, + @Res({ passthrough: true }) res: Response, + @Query() options?: ImageOptionsDto, + ): Promise { + const result = await this.service.getImage({ accountId, id, options }); + if (result) { + res.set({ + 'Content-Type': result.mimeType, + 'Cache-Control': 'public, max-age=31536000, immutable', + }); + return new StreamableFile(result.content); + } + return null; + } + + @ApiOperation({ summary: 'Get file from storage', description: 'Get file from storage by temporary token' }) + @ApiParam({ name: 'token', type: String, required: true, description: 'Temporary token' }) + @ApiOkResponse({ description: 'File', type: StreamableFile }) + @Get('tmp/:token') + async getTemporaryFile( + @Param('token') token: string, + @Res({ passthrough: true }) res: Response, + ): Promise { + const result = await this.service.getFileByTmpToken(token); + if (result) { + res.set({ + 'Content-Type': result.file.mimeType, + 'Content-Disposition': `attachment; filename="${encodeURI(result.file.originalName)}"`, + }); + if (result.file.hashSha256) { + res.set('Digest', 'sha-256=' + result.file.hashSha256); + } + return new StreamableFile(result.content); + } + return null; + } +} diff --git a/backend/src/modules/storage/storage-url.service.ts b/backend/src/modules/storage/storage-url.service.ts new file mode 100644 index 0000000..1b1bcf8 --- /dev/null +++ b/backend/src/modules/storage/storage-url.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common'; + +import { TokenService, UrlGeneratorService } from '@/common'; + +const Paths = { + downloadPath: '/api/storage/file/:fileId', + imagePath: '/api/storage/image/:accountId/:fileId', + temporaryPath: '/api/storage/tmp/:token', +} as const; + +@Injectable() +export class StorageUrlService { + constructor( + private readonly tokenService: TokenService, + private readonly urlGenerator: UrlGeneratorService, + ) {} + + public getDownloadUrl(subdomain: string, fileId: string): string { + return this.urlGenerator.createUrl({ route: Paths.downloadPath, subdomain, path: { fileId } }); + } + + public getImageUrl(accountId: number, subdomain: string, fileId: string): string { + return this.urlGenerator.createUrl({ + route: Paths.imagePath, + subdomain, + path: { accountId: accountId.toString(), fileId }, + }); + } + + public getTemporaryUrl(fileId: string, subdomain?: string): string { + const token = this.tokenService.create({ fileId }, { expiresIn: '15m' }); + return this.urlGenerator.createUrl({ route: Paths.temporaryPath, subdomain, path: { token } }); + } +} diff --git a/backend/src/modules/storage/storage.controller.ts b/backend/src/modules/storage/storage.controller.ts new file mode 100644 index 0000000..014456e --- /dev/null +++ b/backend/src/modules/storage/storage.controller.ts @@ -0,0 +1,88 @@ +import { + Controller, + Delete, + Get, + Param, + Post, + Res, + StreamableFile, + UploadedFiles, + UseInterceptors, +} from '@nestjs/common'; +import { ApiBody, ApiConsumes, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; +import { AnyFilesInterceptor } from '@nestjs/platform-express'; +import { Response } from 'express'; +import { memoryStorage } from 'multer'; + +import { AuthData } from '@/modules/iam/common/types/auth-data'; +import { AuthDataPrefetch } from '@/modules/iam/common/decorators/auth-data-prefetch.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; + +import { StorageService } from './storage.service'; +import { FilesUploadRequest } from './dto/file-upload-request'; +import { FileUploadResult } from './dto/file-upload-result'; +import { FileInfoResultDto } from './dto'; + +@ApiTags('storage') +@Controller('storage') +@JwtAuthorized() +export class StorageController { + constructor(private readonly service: StorageService) {} + + @ApiOperation({ summary: 'Upload files', description: 'Upload files' }) + @ApiConsumes('multipart/form-data') + @ApiBody({ description: 'Files to upload', type: FilesUploadRequest }) + @ApiOkResponse({ description: 'Uploaded files info', type: [FileUploadResult] }) + @AuthDataPrefetch({ account: true }) + @Post('upload') + @UseInterceptors(AnyFilesInterceptor({ storage: memoryStorage() })) + async upload( + @UploadedFiles() + files: Express.Multer.File[], + @CurrentAuth() { account, userId }: AuthData, + ): Promise { + return this.service.uploadCommonFiles({ account, userId, files }); + } + + @ApiOperation({ summary: 'Get file info', description: 'Get file info' }) + @ApiParam({ name: 'fileId', description: 'File id', type: String, required: true }) + @ApiOkResponse({ description: 'File info', type: FileInfoResultDto }) + @AuthDataPrefetch({ account: true }) + @Get('info/:fileId') + async info(@CurrentAuth() { account }: AuthData, @Param('fileId') fileId: string) { + return this.service.getFileInfo({ account, fileId }); + } + + @ApiOperation({ summary: 'Get file from storage', description: 'Get file from storage' }) + @ApiParam({ name: 'fileId', description: 'File id', type: String, required: true }) + @ApiOkResponse({ description: 'File', type: StreamableFile }) + @Get('file/:fileId') + async get( + @CurrentAuth() { accountId }: AuthData, + @Param('fileId') fileId: string, + @Res({ passthrough: true }) res: Response, + ): Promise { + const result = await this.service.getFile({ fileId, accountId }); + if (result) { + res.set({ + 'Content-Type': result.file.mimeType, + 'Content-Disposition': `attachment; filename="${encodeURI(result.file.originalName)}"`, + }); + if (result.file.hashSha256) { + res.set('Digest', 'sha-256=' + result.file.hashSha256); + } + return new StreamableFile(result.content); + } + + return null; + } + + @ApiOperation({ summary: 'Delete file from storage', description: 'Delete file from storage' }) + @ApiParam({ name: 'id', description: 'File id', type: String, required: true }) + @ApiOkResponse({ description: 'Result', type: Boolean }) + @Delete('file/:id') + async delete(@Param('id') id: string, @CurrentAuth() { accountId }: AuthData): Promise { + return this.service.delete({ accountId, id }); + } +} diff --git a/backend/src/modules/storage/storage.module.ts b/backend/src/modules/storage/storage.module.ts new file mode 100644 index 0000000..87962e7 --- /dev/null +++ b/backend/src/modules/storage/storage.module.ts @@ -0,0 +1,27 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { MulterModule } from '@nestjs/platform-express'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; + +import awsConfig from './config/aws.config'; +import { FileInfo } from './entities/file-info.entity'; +import { AwsS3Provider } from './providers/aws-s3.provider'; +import { StorageService } from './storage.service'; +import { StorageUrlService } from './storage-url.service'; +import { StorageController } from './storage.controller'; +import { StoragePublicController } from './storage-public.controller'; + +@Module({ + imports: [ + MulterModule.register(), + ConfigModule.forFeature(awsConfig), + TypeOrmModule.forFeature([FileInfo]), + forwardRef(() => IAMModule), + ], + providers: [StorageService, StorageUrlService, AwsS3Provider], + controllers: [StorageController, StoragePublicController], + exports: [StorageService, StorageUrlService], +}) +export class StorageModule {} diff --git a/backend/src/modules/storage/storage.service.ts b/backend/src/modules/storage/storage.service.ts new file mode 100644 index 0000000..b778c63 --- /dev/null +++ b/backend/src/modules/storage/storage.service.ts @@ -0,0 +1,332 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { Repository } from 'typeorm'; +import { InjectRepository } from '@nestjs/typeorm'; +import { v4 as uuidv4 } from 'uuid'; +import sharp, { ResizeOptions } from 'sharp'; +import { Readable } from 'stream'; +import crypto from 'crypto'; +import { lastValueFrom } from 'rxjs'; +import * as mime from 'mime-types'; + +import { DateUtil, TokenService } from '@/common'; +import { Account } from '@/modules/iam/account/entities/account.entity'; + +import { FileUploadResult, ImageOptionsDto } from './dto'; +import { FileInfo } from './entities'; +import { FileInfoResult, StorageFile, TemporaryFile } from './types'; +import { AwsS3Provider } from './providers'; +import { StorageUrlService } from './storage-url.service'; + +type Section = 'avatar' | 'logo' | 'photos' | string; +const getResizeOptions = (section: Section): ResizeOptions => { + return section === 'avatar' ? { width: 440, height: 440 } : undefined; +}; + +@Injectable() +export class StorageService { + private readonly logger = new Logger(StorageService.name); + + constructor( + private readonly httpService: HttpService, + @InjectRepository(FileInfo) + private readonly repository: Repository, + private readonly tokenService: TokenService, + private readonly awsS3Provider: AwsS3Provider, + private readonly urlService: StorageUrlService, + ) {} + + public async uploadCommonFiles({ + account, + userId, + files, + }: { + account: Account; + userId?: number | null; + files: Express.Multer.File[]; + }): Promise { + return Promise.all(files.map((file) => this.uploadCommonFile({ account, userId, file }))); + } + + public async uploadCommonFile({ + account, + userId, + file, + }: { + account: Account; + userId?: number | null; + file: Express.Multer.File; + }): Promise { + const fileInfo = await this.storeCommonFile({ accountId: account.id, userId, file: StorageFile.fromMulter(file) }); + if (fileInfo) { + const downloadUrl = this.urlService.getDownloadUrl(account.subdomain, fileInfo.id); + const previewUrl = fileInfo.isImage() + ? this.urlService.getImageUrl(account.id, account.subdomain, fileInfo.id) + : undefined; + return { + key: file.fieldname, + id: fileInfo.id, + fileName: fileInfo.originalName, + fileSize: fileInfo.size, + mimeType: fileInfo.mimeType, + createdAt: fileInfo.createdAt.toISOString(), + downloadUrl, + previewUrl, + }; + } + + return null; + } + + public async storeCommonFile({ + accountId, + userId, + file, + }: { + accountId: number; + userId?: number | null; + file: StorageFile; + }): Promise { + const now = DateUtil.now(); + const path = `${accountId}/${now.getFullYear()}/${now.getMonth()}/${now.getDate()}`; + + return this.storeFile({ accountId, userId, path, file }); + } + + public async storeAccountFile({ + accountId, + userId, + file, + section, + }: { + accountId: number; + userId: number; + file: StorageFile; + section?: Section; + }): Promise { + const path = `${accountId}/account${section ? `/${section}` : ''}`; + + return this.storeFile({ accountId, userId, path, file }); + } + + public async storeUserFile({ + accountId, + userId, + file, + section, + }: { + accountId: number; + userId: number; + file: StorageFile; + section?: Section; + }): Promise { + const path = `${accountId}/users/${userId}${section ? `/${section}` : ''}`; + + return this.storeFile({ accountId, userId, path, file, resizeOptions: getResizeOptions(section) }); + } + + public async storeProductFiles({ + accountId, + userId, + productId, + files, + section, + }: { + accountId: number; + userId: number; + productId: number; + files: StorageFile[]; + section?: Section; + }): Promise { + const path = `${accountId}/products/${productId}${section ? `/${section}` : ''}`; + + return (await Promise.all(files.map((file) => this.storeFile({ accountId, userId, path, file })))).filter(Boolean); + } + + public async storeExternalFile( + accountId: number, + userId: number | null, + fileUrl: string, + options?: { authorization?: string }, + ): Promise { + let response; + try { + const response$ = this.httpService.get(fileUrl, { + headers: { Authorization: options?.authorization }, + responseType: 'arraybuffer', + }); + response = await lastValueFrom(response$); + } catch (e) { + this.logger.error(`Error while storing external file`, (e as Error)?.stack); + return null; + } + + const { fileName, contentType } = this.extractFileInfo(response); + const buffer = Buffer.from(response.data); + + return this.storeCommonFile({ + accountId, + userId, + file: new StorageFile(fileName, contentType, buffer.length, buffer), + }); + } + + public async getFileInfo({ account, fileId }: { account: Account; fileId: string }): Promise { + const fileInfo = await this.repository.findOneBy({ id: fileId }); + if (fileInfo) { + const downloadUrl = this.urlService.getDownloadUrl(account.subdomain, fileInfo.id); + const previewUrl = fileInfo.isImage() + ? this.urlService.getImageUrl(account.id, account.subdomain, fileInfo.id) + : undefined; + return new FileInfoResult({ + id: fileInfo.id, + fileName: fileInfo.originalName, + fileSize: fileInfo.size, + mimeType: fileInfo.mimeType, + createdAt: fileInfo.createdAt, + downloadUrl, + previewUrl, + }); + } + return null; + } + + public async getFile({ + fileId, + accountId, + }: { + fileId: string; + accountId?: number; + }): Promise<{ file: FileInfo; content: Uint8Array }> { + const file = await this.repository.findOneBy({ id: fileId, accountId }); + if (file) { + const content = await this.awsS3Provider.getFile(file.storePath); + return { file, content }; + } + return null; + } + + public async getFileByTmpToken(token: string): Promise<{ file: FileInfo; content: Uint8Array }> { + const { fileId } = this.tokenService.verify(decodeURIComponent(token)); + + return fileId ? this.getFile({ fileId }) : null; + } + + public async getImage({ + accountId, + id, + options, + }: { + accountId: number; + id: string; + options?: ImageOptionsDto; + }): Promise<{ content: Readable; mimeType: string }> { + const file = await this.repository.findOneBy({ id, accountId }); + if (file) { + const content = await this.awsS3Provider.getFile(file.storePath); + if (options) { + return { + content: sharp(content).resize(options.width, options.height), + mimeType: file.mimeType, + }; + } + return { content: Readable.from(content), mimeType: file.mimeType }; + } + return null; + } + + public async delete({ accountId, id }: { accountId: number; id: string | string[] }): Promise { + const deleteOne = async ({ accountId, id }: { accountId: number; id: string }): Promise => { + const file = await this.repository.findOneBy({ id, accountId }); + if (file) { + const deleted = await this.awsS3Provider.deleteFile(file.storePath); + if (deleted) { + await this.repository.delete(id); + } + return deleted; + } + return true; + }; + + return Array.isArray(id) + ? Promise.all(id.map((fileId) => deleteOne({ accountId, id: fileId }))).then((result) => result.every(Boolean)) + : deleteOne({ accountId, id }); + } + + public async markUsed({ accountId, id }: { accountId: number; id: string }): Promise { + await this.repository.update({ accountId, id }, { isUsed: true }); + return this.repository.findOneBy({ accountId, id }); + } + public async markUsedMany({ accountId, ids }: { accountId: number; ids: string[] }): Promise { + return Promise.all(ids.map((id) => this.markUsed({ accountId, id }))); + } + + private async storeFile({ + accountId, + userId, + path, + file, + resizeOptions, + }: { + accountId: number; + userId?: number | null; + path: string; + file: StorageFile; + resizeOptions?: ResizeOptions; + }): Promise { + const uploadFile = resizeOptions ? await this.resizeImage(file, resizeOptions) : file; + const id = uuidv4(); + const key = `${path}/${id}`; + const sha256Hash = crypto.createHash('sha256').update(uploadFile.buffer).digest('base64'); + const result = await this.awsS3Provider.storeBuffer( + key, + uploadFile.buffer, + sha256Hash, + uploadFile.mimeType, + uploadFile.originalName, + ); + if (result) { + const fileInfo = new FileInfo( + id, + accountId, + userId ?? null, + decodeURI(file.originalName), + uploadFile.mimeType, + uploadFile.size, + sha256Hash, + key, + false, + ); + await this.repository.insert(fileInfo); + + return fileInfo; + } + + return null; + } + + private extractFileInfo(response: any): { fileName: string; contentType: string } { + const contentType = response.headers['content-type'] as string; + const contentDisposition = response.headers['content-disposition']; + let fileName = ''; + if (contentDisposition) { + //TODO: use StringUtil decoding + const filenameRegex = /filename\*?=(?:[^\']*'')?([^;\n"']*)['"]?/; + const matches = filenameRegex.exec(contentDisposition); + if (matches != null && matches[1]) { + fileName = matches[1].replace(/['"]/g, ''); + } + } + if (!fileName) { + const extension = mime.extension(contentType) || 'bin'; + fileName = `${uuidv4()}.${extension}`; + } + + return { fileName, contentType }; + } + + private async resizeImage(file: StorageFile, resizeOptions: ResizeOptions): Promise { + const resized = await sharp(file.buffer).resize(resizeOptions).toBuffer(); + return new StorageFile(file.originalName, file.mimeType, resized.length, resized); + } +} diff --git a/backend/src/modules/storage/types/file-info-result.ts b/backend/src/modules/storage/types/file-info-result.ts new file mode 100644 index 0000000..50f2168 --- /dev/null +++ b/backend/src/modules/storage/types/file-info-result.ts @@ -0,0 +1,33 @@ +import { FileInfoResultDto } from '../dto'; + +export class FileInfoResult { + id: string; + fileName: string; + fileSize: number; + mimeType: string; + downloadUrl: string; + previewUrl?: string | null; + createdAt: Date; + + constructor(date: Omit) { + this.id = date.id; + this.fileName = date.fileName; + this.fileSize = date.fileSize; + this.mimeType = date.mimeType; + this.downloadUrl = date.downloadUrl; + this.previewUrl = date.previewUrl; + this.createdAt = date.createdAt; + } + + public toDto(): FileInfoResultDto { + return { + id: this.id, + fileName: this.fileName, + fileSize: this.fileSize, + mimeType: this.mimeType, + downloadUrl: this.downloadUrl, + previewUrl: this.previewUrl, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/backend/src/modules/storage/types/index.ts b/backend/src/modules/storage/types/index.ts new file mode 100644 index 0000000..f096e7f --- /dev/null +++ b/backend/src/modules/storage/types/index.ts @@ -0,0 +1,3 @@ +export * from './file-info-result'; +export * from './storage-file'; +export * from './temporary-file'; diff --git a/backend/src/modules/storage/types/storage-file.ts b/backend/src/modules/storage/types/storage-file.ts new file mode 100644 index 0000000..db42f48 --- /dev/null +++ b/backend/src/modules/storage/types/storage-file.ts @@ -0,0 +1,29 @@ +import { type FileInfo } from '../entities/file-info.entity'; + +export class StorageFile { + originalName: string; + mimeType: string; + size: number; + buffer: Buffer; + encoding?: string | undefined; + + constructor(originalName: string, mimeType: string, size: number, buffer: Buffer, encoding?: string | undefined) { + this.originalName = originalName; + this.mimeType = mimeType; + this.size = size; + this.buffer = buffer; + this.encoding = encoding; + } + + public static fromFileInfo(fileInfo: FileInfo, buffer: Buffer) { + return new StorageFile(fileInfo.originalName, fileInfo.mimeType, fileInfo.size, buffer); + } + + public static fromMulter(file: Express.Multer.File) { + return new StorageFile(decodeURIComponent(file.originalname), file.mimetype, file.size, file.buffer, file.encoding); + } + + public static fromMulterFiles(files: Express.Multer.File[]): StorageFile[] { + return files.map((file) => StorageFile.fromMulter(file)); + } +} diff --git a/backend/src/modules/storage/types/temporary-file.ts b/backend/src/modules/storage/types/temporary-file.ts new file mode 100644 index 0000000..eeb3e18 --- /dev/null +++ b/backend/src/modules/storage/types/temporary-file.ts @@ -0,0 +1,3 @@ +export class TemporaryFile { + fileId: string; +} diff --git a/backend/src/modules/telephony/common/events/index.ts b/backend/src/modules/telephony/common/events/index.ts new file mode 100644 index 0000000..658e4e0 --- /dev/null +++ b/backend/src/modules/telephony/common/events/index.ts @@ -0,0 +1,2 @@ +export * from './telephony-call'; +export * from './telephony-event-type.enum'; diff --git a/backend/src/modules/telephony/common/events/telephony-call/index.ts b/backend/src/modules/telephony/common/events/telephony-call/index.ts new file mode 100644 index 0000000..158e3ee --- /dev/null +++ b/backend/src/modules/telephony/common/events/telephony-call/index.ts @@ -0,0 +1,3 @@ +export * from './telephony-call-created.event'; +export * from './telephony-call-updated.event'; +export * from './telephony-call.event'; diff --git a/backend/src/modules/telephony/common/events/telephony-call/telephony-call-created.event.ts b/backend/src/modules/telephony/common/events/telephony-call/telephony-call-created.event.ts new file mode 100644 index 0000000..45b340a --- /dev/null +++ b/backend/src/modules/telephony/common/events/telephony-call/telephony-call-created.event.ts @@ -0,0 +1,11 @@ +import { TelephonyCallEvent } from './telephony-call.event'; + +export class TelephonyCallCreatedEvent extends TelephonyCallEvent { + createdAt: string; + + constructor({ accountId, entityId, callId, createdAt }: TelephonyCallCreatedEvent) { + super({ accountId, entityId, callId }); + + this.createdAt = createdAt; + } +} diff --git a/backend/src/modules/telephony/common/events/telephony-call/telephony-call-updated.event.ts b/backend/src/modules/telephony/common/events/telephony-call/telephony-call-updated.event.ts new file mode 100644 index 0000000..5012c65 --- /dev/null +++ b/backend/src/modules/telephony/common/events/telephony-call/telephony-call-updated.event.ts @@ -0,0 +1,13 @@ +import { TelephonyCallEvent } from './telephony-call.event'; + +export class TelephonyCallUpdatedEvent extends TelephonyCallEvent { + createdAt: string; + oldEntityId: number | null; + + constructor({ accountId, entityId, callId, createdAt, oldEntityId }: TelephonyCallUpdatedEvent) { + super({ accountId, entityId, callId }); + + this.createdAt = createdAt; + this.oldEntityId = oldEntityId; + } +} diff --git a/backend/src/modules/telephony/common/events/telephony-call/telephony-call.event.ts b/backend/src/modules/telephony/common/events/telephony-call/telephony-call.event.ts new file mode 100644 index 0000000..075be80 --- /dev/null +++ b/backend/src/modules/telephony/common/events/telephony-call/telephony-call.event.ts @@ -0,0 +1,11 @@ +export class TelephonyCallEvent { + accountId: number; + entityId: number | null; + callId: number; + + constructor({ accountId, entityId, callId }: TelephonyCallEvent) { + this.accountId = accountId; + this.entityId = entityId; + this.callId = callId; + } +} diff --git a/backend/src/modules/telephony/common/events/telephony-event-type.enum.ts b/backend/src/modules/telephony/common/events/telephony-event-type.enum.ts new file mode 100644 index 0000000..f71e312 --- /dev/null +++ b/backend/src/modules/telephony/common/events/telephony-event-type.enum.ts @@ -0,0 +1,4 @@ +export enum TelephonyEventType { + TelephonyCallCreated = 'telephony:call:created', + TelephonyCallUpdated = 'telephony:call:updated', +} diff --git a/backend/src/modules/telephony/common/index.ts b/backend/src/modules/telephony/common/index.ts new file mode 100644 index 0000000..7981d6b --- /dev/null +++ b/backend/src/modules/telephony/common/index.ts @@ -0,0 +1 @@ +export * from './events'; diff --git a/backend/src/modules/telephony/telephony.module.ts b/backend/src/modules/telephony/telephony.module.ts new file mode 100644 index 0000000..3d2f62f --- /dev/null +++ b/backend/src/modules/telephony/telephony.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { RouterModule } from '@nestjs/core'; + +import { VoximplantModule } from './voximplant/voximplant.module'; + +@Module({ + imports: [VoximplantModule, RouterModule.register([{ path: 'telephony/voximplant', module: VoximplantModule }])], + exports: [VoximplantModule], +}) +export class TelephonyModule {} diff --git a/backend/src/modules/telephony/voximplant/common/enums/call-direction.enum.ts b/backend/src/modules/telephony/voximplant/common/enums/call-direction.enum.ts new file mode 100644 index 0000000..243c2f7 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/enums/call-direction.enum.ts @@ -0,0 +1,4 @@ +export enum CallDirection { + INCOMING = 'incoming', + OUTGOING = 'outgoing', +} diff --git a/backend/src/modules/telephony/voximplant/common/enums/call-status.enum.ts b/backend/src/modules/telephony/voximplant/common/enums/call-status.enum.ts new file mode 100644 index 0000000..495870d --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/enums/call-status.enum.ts @@ -0,0 +1,8 @@ +export enum CallStatus { + STARTED = 'started', + ACCEPTED = 'accepted', + SUCCESS = 'success', + CANCELED = 'canceled', + FAILED = 'failed', + MISSED = 'missed', +} diff --git a/backend/src/modules/telephony/voximplant/common/enums/index.ts b/backend/src/modules/telephony/voximplant/common/enums/index.ts new file mode 100644 index 0000000..51b58df --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/enums/index.ts @@ -0,0 +1,2 @@ +export * from './call-direction.enum'; +export * from './call-status.enum'; diff --git a/backend/src/modules/telephony/voximplant/common/errors/index.ts b/backend/src/modules/telephony/voximplant/common/errors/index.ts new file mode 100644 index 0000000..0284dae --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/errors/index.ts @@ -0,0 +1 @@ +export * from './voximplant-error'; diff --git a/backend/src/modules/telephony/voximplant/common/errors/voximplant-error.ts b/backend/src/modules/telephony/voximplant/common/errors/voximplant-error.ts new file mode 100644 index 0000000..fa125d8 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/errors/voximplant-error.ts @@ -0,0 +1,9 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ServiceError } from '@/common'; + +export class VoximplantError extends ServiceError { + constructor({ message = 'Voximplant error', errorCode = 'voximplant' }) { + super({ errorCode: errorCode, status: HttpStatus.BAD_REQUEST, message }); + } +} diff --git a/backend/src/modules/telephony/voximplant/common/index.ts b/backend/src/modules/telephony/voximplant/common/index.ts new file mode 100644 index 0000000..5d840f7 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/index.ts @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './errors'; +export * from './types'; diff --git a/backend/src/modules/telephony/voximplant/common/types/index.ts b/backend/src/modules/telephony/voximplant/common/types/index.ts new file mode 100644 index 0000000..c1722d2 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/types/index.ts @@ -0,0 +1 @@ +export * from './voximplant-application-param.interface'; diff --git a/backend/src/modules/telephony/voximplant/common/types/voximplant-application-param.interface.ts b/backend/src/modules/telephony/voximplant/common/types/voximplant-application-param.interface.ts new file mode 100644 index 0000000..6614c9e --- /dev/null +++ b/backend/src/modules/telephony/voximplant/common/types/voximplant-application-param.interface.ts @@ -0,0 +1,4 @@ +export interface VoximplantApplicationParam { + applicationId: number; + applicationName: string; +} diff --git a/backend/src/modules/telephony/voximplant/config/voximplant.config.ts b/backend/src/modules/telephony/voximplant/config/voximplant.config.ts new file mode 100644 index 0000000..7de4ca6 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/config/voximplant.config.ts @@ -0,0 +1,16 @@ +import { registerAs } from '@nestjs/config'; + +export interface VoximplantConfig { + accountId: number; + accountApiKey: string; + credentialsFile: string; +} + +export default registerAs( + 'voximplant', + (): VoximplantConfig => ({ + accountId: parseInt(process.env.VOXIMPLANT_PARENT_ACCOUNT_ID, 10), + accountApiKey: process.env.VOXIMPLANT_PARENT_ACCOUNT_API_KEY, + credentialsFile: process.env.VOXIMPLANT_CREDENTIALS_FILE, + }), +); diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-account/dto/index.ts new file mode 100644 index 0000000..7d74ab8 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/dto/index.ts @@ -0,0 +1 @@ +export * from './voximplant-account.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/dto/voximplant-account.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-account/dto/voximplant-account.dto.ts new file mode 100644 index 0000000..e834664 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/dto/voximplant-account.dto.ts @@ -0,0 +1,50 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsString } from 'class-validator'; + +export class VoximplantAccountDto { + @ApiProperty() + @IsNumber() + accountId: number; + + @ApiProperty() + @IsString() + accountName: string; + + @ApiProperty() + @IsString() + apiKey: string; + + @ApiProperty() + @IsNumber() + billingAccountId: number; + + @ApiProperty() + @IsNumber() + applicationId: number; + + @ApiProperty() + @IsString() + applicationName: string; + + @ApiProperty() + @IsBoolean() + isActive: boolean; + + constructor( + accountId: number, + accountName: string, + apiKey: string, + billingAccountId: number, + applicationId: number, + applicationName: string, + isActive: boolean, + ) { + this.accountId = accountId; + this.accountName = accountName; + this.apiKey = apiKey; + this.billingAccountId = billingAccountId; + this.applicationId = applicationId; + this.applicationName = applicationName; + this.isActive = isActive; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/entities/index.ts b/backend/src/modules/telephony/voximplant/voximplant-account/entities/index.ts new file mode 100644 index 0000000..6d2419d --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/entities/index.ts @@ -0,0 +1 @@ +export * from './voximplant-account.entity'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/entities/voximplant-account.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-account/entities/voximplant-account.entity.ts new file mode 100644 index 0000000..09c95b3 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/entities/voximplant-account.entity.ts @@ -0,0 +1,81 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { VoximplantAccountDto } from '../dto'; + +@Entity() +export class VoximplantAccount { + @PrimaryColumn() + accountId: number; + + @Column() + externalId: number; + + @Column() + accountName: string; + + @Column() + accountEmail: string; + + @Column() + apiKey: string; + + @Column() + password: string; + + @Column() + billingAccountId: number; + + @Column() + isActive: boolean; + + @Column() + keyId: string; + + @Column() + privateKey: string; + + @Column() + applicationId: number; + + @Column() + applicationName: string; + + constructor( + accountId: number, + externalId: number, + accountName: string, + accountEmail: string, + apiKey: string, + password: string, + billingAccountId: number, + isActive: boolean, + keyId: string, + privateKey: string, + applicationId: number, + applicationName: string, + ) { + this.accountId = accountId; + this.externalId = externalId; + this.accountName = accountName; + this.accountEmail = accountEmail; + this.apiKey = apiKey; + this.password = password; + this.billingAccountId = billingAccountId; + this.isActive = isActive; + this.keyId = keyId; + this.privateKey = privateKey; + this.applicationId = applicationId; + this.applicationName = applicationName; + } + + public toDto(): VoximplantAccountDto { + return new VoximplantAccountDto( + this.externalId, + this.accountName, + this.apiKey, + this.billingAccountId, + this.applicationId, + this.applicationName, + this.isActive, + ); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/index.ts b/backend/src/modules/telephony/voximplant/voximplant-account/index.ts new file mode 100644 index 0000000..e6ff647 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './voximplant-account.controller'; +export * from './voximplant-account.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.controller.ts new file mode 100644 index 0000000..34b901b --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.controller.ts @@ -0,0 +1,35 @@ +import { Controller, Delete, Get, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { VoximplantAccountDto } from './dto'; +import { VoximplantAccountService } from './voximplant-account.service'; + +@ApiTags('telephony/voximplant/account') +@Controller('account') +@JwtAuthorized() +@TransformToDto() +export class VoximplantAccountController { + constructor(private service: VoximplantAccountService) {} + + @AuthDataPrefetch({ account: true }) + @ApiCreatedResponse({ description: 'Create voximplant account', type: VoximplantAccountDto }) + @Post() + public async create(@CurrentAuth() { account }: AuthData) { + return this.service.create(account); + } + + @ApiCreatedResponse({ description: 'Get linked voximplant account', type: VoximplantAccountDto }) + @Get() + public async findOne(@CurrentAuth() { accountId }: AuthData) { + return this.service.findOne(accountId); + } + + @ApiCreatedResponse({ description: 'Delete voximplant account' }) + @Delete() + public async delete(@CurrentAuth() { accountId }: AuthData) { + return this.service.markActive(accountId, false); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.service.ts b/backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.service.ts new file mode 100644 index 0000000..977b2ae --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-account/voximplant-account.service.ts @@ -0,0 +1,135 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import VoximplantApiClient from '@amwork/voximplant-apiclient-nodejs'; + +import { NotFoundError } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { Account } from '@/modules/iam/account/entities/account.entity'; + +import { VoximplantCoreService } from '../voximplant-core'; + +import { VoximplantAccount } from './entities'; +import { VoximplantApplicationParam } from '../common'; + +@Injectable() +export class VoximplantAccountService { + private _appName: string; + + constructor( + private readonly configService: ConfigService, + @InjectRepository(VoximplantAccount) + private readonly repository: Repository, + private readonly viCoreService: VoximplantCoreService, + ) { + this._appName = this.configService.get('application').name; + } + + public async getClient( + account: number | VoximplantAccount, + ): Promise<{ client: VoximplantApiClient; appParam: VoximplantApplicationParam }> { + const viAccount = account instanceof VoximplantAccount ? account : await this.getOne(account); + + const client = new VoximplantApiClient({ + account_email: viAccount.accountEmail, + account_id: viAccount.externalId, + key_id: viAccount.keyId, + private_key: viAccount.privateKey, + }); + const appParam = { + applicationId: viAccount.applicationId, + applicationName: viAccount.applicationName, + }; + + return { client, appParam }; + } + + public async create(account: Account): Promise { + const viAccount = await this.repository.findOneBy({ accountId: account.id }); + if (viAccount) { + return viAccount.isActive ? viAccount : await this.setActive(viAccount, true); + } + + const extAccount = await this.viCoreService.createChildAccount(account); + if (extAccount) { + const newAccount = new VoximplantAccount( + account.id, + extAccount.accountId, + extAccount.accountName, + extAccount.accountEmail, + extAccount.apiKey, + extAccount.password, + extAccount.billingAccountId, + extAccount.active, + extAccount.key.keyId, + extAccount.key.privateKey, + 0, + '', + ); + + const { client } = await this.getClient(newAccount); + const { result, applicationId, applicationName } = await client.Applications.addApplication({ + applicationName: this._appName, + }); + if (result) { + return await this.repository.save( + new VoximplantAccount( + account.id, + extAccount.accountId, + extAccount.accountName, + extAccount.accountEmail, + extAccount.apiKey, + extAccount.password, + extAccount.billingAccountId, + extAccount.active, + extAccount.key.keyId, + extAccount.key.privateKey, + applicationId, + applicationName, + ), + ); + } + } + return null; + } + + public async findOne(accountId: number): Promise { + return await this.repository.findOneBy({ accountId }); + } + + public async findOneExt(filter: { applicationId: number }): Promise { + return await this.repository.findOneBy({ applicationId: filter.applicationId }); + } + + public async getOne(accountId: number): Promise { + const viAccount = await this.repository.findOneBy({ accountId }); + if (!viAccount) { + throw NotFoundError.withId(VoximplantAccount, accountId); + } + + return viAccount; + } + + public async markActive(accountId: number, isActive: boolean): Promise { + const viAccount = await this.getOne(accountId); + return await this.setActive(viAccount, isActive); + } + + private async setActive(viAccount: VoximplantAccount, isActive: boolean): Promise { + const result = await this.viCoreService.setActiveChildAccount( + viAccount.externalId, + viAccount.accountName, + viAccount.accountEmail, + isActive, + ); + + if (result) { + viAccount.isActive = isActive; + return await this.repository.save(viAccount); + } + + return viAccount; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call-ext.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call-ext.dto.ts new file mode 100644 index 0000000..2228fae --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call-ext.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +import { CreateVoximplantCallDto } from './create-voximplant-call.dto'; + +export class CreateVoximplantCallExtDto extends CreateVoximplantCallDto { + @ApiProperty() + @IsString() + userName: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + viPhoneNumber?: string | null; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call.dto.ts new file mode 100644 index 0000000..f84417e --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/create-voximplant-call.dto.ts @@ -0,0 +1,16 @@ +import { PickType } from '@nestjs/swagger'; + +import { VoximplantCallDto } from './voximplant-call.dto'; + +export class CreateVoximplantCallDto extends PickType(VoximplantCallDto, [ + 'sessionId', + 'callId', + 'numberId', + 'entityId', + 'direction', + 'phoneNumber', + 'duration', + 'status', + 'failureReason', + 'recordUrl', +] as const) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/index.ts new file mode 100644 index 0000000..77eac88 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/index.ts @@ -0,0 +1,6 @@ +export * from './create-voximplant-call-ext.dto'; +export * from './create-voximplant-call.dto'; +export * from './update-voximplant-call-ext.dto'; +export * from './update-voximplant-call.dto'; +export * from './voximplant-call-list.dto'; +export * from './voximplant-call.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call-ext.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call-ext.dto.ts new file mode 100644 index 0000000..017500b --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call-ext.dto.ts @@ -0,0 +1,7 @@ +import { OmitType, PartialType } from '@nestjs/swagger'; + +import { CreateVoximplantCallExtDto } from './create-voximplant-call-ext.dto'; + +export class UpdateVoximplantCallExtDto extends PartialType( + OmitType(CreateVoximplantCallExtDto, ['sessionId', 'callId', 'viPhoneNumber', 'numberId'] as const), +) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call.dto.ts new file mode 100644 index 0000000..be7f3c1 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/update-voximplant-call.dto.ts @@ -0,0 +1,15 @@ +import { PartialType, PickType } from '@nestjs/swagger'; +import { VoximplantCallDto } from './voximplant-call.dto'; + +export class UpdateVoximplantCallDto extends PartialType( + PickType(VoximplantCallDto, [ + 'entityId', + 'direction', + 'phoneNumber', + 'duration', + 'status', + 'failureReason', + 'recordUrl', + 'comment', + ] as const), +) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call-list.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call-list.dto.ts new file mode 100644 index 0000000..5d28aae --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call-list.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsObject } from 'class-validator'; + +import { PagingMeta } from '@/common'; +import { VoximplantCallDto } from './voximplant-call.dto'; + +export class VoximplantCallListDto { + @ApiProperty({ type: [VoximplantCallDto] }) + @IsArray() + calls: VoximplantCallDto[]; + + @ApiProperty({ type: PagingMeta }) + @IsObject() + meta: PagingMeta; + + constructor(calls: VoximplantCallDto[], meta: PagingMeta) { + this.calls = calls; + this.meta = meta; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call.dto.ts new file mode 100644 index 0000000..f0a69eb --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/dto/voximplant-call.dto.ts @@ -0,0 +1,108 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsObject, IsOptional, IsString } from 'class-validator'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; +import { CallDirection, CallStatus } from '../../common'; + +export class VoximplantCallDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + sessionId: string; + + @ApiProperty() + @IsString() + callId: string; + + @ApiProperty() + @IsNumber() + userId: number; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + numberId: number | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiProperty({ enum: CallDirection }) + @IsEnum(CallDirection) + direction: CallDirection; + + @ApiProperty() + @IsString() + phoneNumber: string; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsNumber() + duration?: number | null; + + @ApiPropertyOptional({ nullable: true, enum: CallStatus }) + @IsOptional() + @IsEnum(CallStatus) + status?: CallStatus | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + failureReason?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + recordUrl?: string | null; + + @ApiPropertyOptional({ nullable: true }) + @IsOptional() + @IsString() + comment: string | null; + + @ApiProperty() + @IsString() + createdAt: string; + + @ApiPropertyOptional({ type: EntityInfoDto, nullable: true }) + @IsObject() + entityInfo: EntityInfoDto | null; + + constructor( + id: number, + sessionId: string, + callId: string, + userId: number, + numberId: number | null, + entityId: number | null, + direction: CallDirection, + phoneNumber: string, + duration: number | null, + status: CallStatus | null, + failureReason: string | null, + recordUrl: string | null, + comment: string | null, + createdAt: string, + entityInfo?: EntityInfoDto | null, + ) { + this.id = id; + this.sessionId = sessionId; + this.callId = callId; + this.userId = userId; + this.numberId = numberId; + this.entityId = entityId; + this.direction = direction; + this.phoneNumber = phoneNumber; + this.duration = duration; + this.status = status; + this.failureReason = failureReason; + this.recordUrl = recordUrl; + this.comment = comment; + this.createdAt = createdAt; + this.entityInfo = entityInfo; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/entities/index.ts b/backend/src/modules/telephony/voximplant/voximplant-call/entities/index.ts new file mode 100644 index 0000000..44c902b --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/entities/index.ts @@ -0,0 +1 @@ +export * from './voximplant-call.entity'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/entities/voximplant-call.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-call/entities/voximplant-call.entity.ts new file mode 100644 index 0000000..20b200c --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/entities/voximplant-call.entity.ts @@ -0,0 +1,155 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { EntityInfoDto } from '@/modules/entity/entity-info/dto/entity-info.dto'; + +import { CallDirection, CallStatus } from '../../common'; +import { CreateVoximplantCallDto, UpdateVoximplantCallDto, VoximplantCallDto } from '../dto'; + +@Entity() +export class VoximplantCall { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + sessionId: string; + + @Column() + callId: string; + + @Column() + userId: number; + + @Column({ nullable: true }) + numberId: number | null; + + @Column({ nullable: true }) + entityId: number | null; + + @Column() + direction: CallDirection; + + @Column() + phoneNumber: string; + + @Column({ nullable: true }) + duration: number | null; + + @Column({ nullable: true }) + status: CallStatus | null; + + @Column({ nullable: true }) + failureReason: string | null; + + @Column({ nullable: true }) + recordUrl: string | null; + + @Column() + accountId: number; + + @Column({ nullable: true }) + comment: string | null; + + @Column() + createdAt: Date; + + private _entityInfo: EntityInfoDto = null; + + constructor( + accountId: number, + sessionId: string, + callId: string, + userId: number, + numberId: number | null, + entityId: number | null, + direction: CallDirection, + phoneNumber: string, + duration: number | null, + status: CallStatus | null, + failureReason: string | null, + recordUrl: string | null, + comment: string, + createdAt?: Date, + ) { + this.accountId = accountId; + this.sessionId = sessionId; + this.callId = callId; + this.userId = userId; + this.numberId = numberId; + this.entityId = entityId; + this.direction = direction; + this.phoneNumber = phoneNumber; + this.duration = duration; + this.status = status; + this.failureReason = failureReason; + this.recordUrl = recordUrl; + this.comment = comment ?? null; + this.createdAt = createdAt ?? DateUtil.now(); + } + + public get entityInfo(): EntityInfoDto { + return this._entityInfo; + } + public set entityInfo(value: EntityInfoDto) { + this._entityInfo = value; + } + + public static fromDto( + accountId: number, + userId: number, + dto: CreateVoximplantCallDto, + createdAt?: Date, + ): VoximplantCall { + return new VoximplantCall( + accountId, + dto.sessionId, + dto.callId, + userId, + dto.numberId, + dto.entityId, + dto.direction, + dto.phoneNumber, + dto.duration, + dto.status, + dto.failureReason, + dto.recordUrl, + null, + createdAt, + ); + } + + public update(userId: number | null | undefined, dto: UpdateVoximplantCallDto): VoximplantCall { + this.userId = userId; + this.entityId = dto.entityId; + this.direction = dto.direction; + this.phoneNumber = dto.phoneNumber; + this.duration = dto.duration; + this.status = dto.status; + this.failureReason = dto.failureReason; + this.recordUrl = dto.recordUrl; + this.comment = dto.comment; + + return this; + } + + public toDto(): VoximplantCallDto { + return new VoximplantCallDto( + this.id, + this.sessionId, + this.callId, + this.userId, + this.numberId, + this.entityId, + this.direction, + this.phoneNumber, + this.duration, + this.status, + this.failureReason, + this.recordUrl, + this.comment, + this.createdAt.toISOString(), + this._entityInfo, + ); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/index.ts b/backend/src/modules/telephony/voximplant/voximplant-call/index.ts new file mode 100644 index 0000000..184c81f --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './types'; +export * from './voximplant-call-public.controller'; +export * from './voximplant-call.controller'; +export * from './voximplant-call.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/types/index.ts b/backend/src/modules/telephony/voximplant/voximplant-call/types/index.ts new file mode 100644 index 0000000..f469aa9 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/types/index.ts @@ -0,0 +1 @@ +export * from './voximplant-call-list'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/types/voximplant-call-list.ts b/backend/src/modules/telephony/voximplant/voximplant-call/types/voximplant-call-list.ts new file mode 100644 index 0000000..2c58390 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/types/voximplant-call-list.ts @@ -0,0 +1,23 @@ +import { PagingMeta } from '@/common'; + +import { VoximplantCallListDto } from '../dto'; +import { VoximplantCall } from '../entities'; + +export class VoximplantCallList { + calls: VoximplantCall[]; + offset: number; + total: number; + + constructor(calls: VoximplantCall[], offset: number, total: number) { + this.calls = calls; + this.offset = offset; + this.total = total; + } + + public toDto(): VoximplantCallListDto { + return new VoximplantCallListDto( + this.calls ? this.calls.map((c) => c.toDto()) : [], + new PagingMeta(this.offset, this.total), + ); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call-public.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call-public.controller.ts new file mode 100644 index 0000000..7cd95d9 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call-public.controller.ts @@ -0,0 +1,33 @@ +import { Body, Controller, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; + +import { VoximplantCallDto, CreateVoximplantCallExtDto, UpdateVoximplantCallExtDto } from './dto'; +import { VoximplantCallService } from './voximplant-call.service'; + +@ApiTags('telephony/voximplant/integration') +@Controller('integration/:applicationId/calls') +@TransformToDto() +export class VoximplantCallPublicController { + constructor(private service: VoximplantCallService) {} + + @ApiCreatedResponse({ description: 'Create voximplant call', type: VoximplantCallDto }) + @Post() + async createExt( + @Param('applicationId', ParseIntPipe) applicationId: number, + @Body() dto: CreateVoximplantCallExtDto, + ) { + return this.service.createExt(applicationId, dto); + } + + @ApiCreatedResponse({ description: 'Update voximplant call', type: VoximplantCallDto }) + @Patch(':externalId') + async updateExt( + @Param('applicationId', ParseIntPipe) applicationId: number, + @Param('externalId') externalId: string, + @Body() dto: UpdateVoximplantCallExtDto, + ) { + return this.service.updateExt(applicationId, externalId, dto); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.controller.ts new file mode 100644 index 0000000..1e0cbdc --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.controller.ts @@ -0,0 +1,44 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto, PagingQuery } from '@/common'; +import { AuthData, AuthDataPrefetch, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { VoximplantCallDto, CreateVoximplantCallDto, VoximplantCallListDto, UpdateVoximplantCallDto } from './dto'; +import { VoximplantCallService } from './voximplant-call.service'; + +@ApiTags('telephony/voximplant/calls') +@Controller('calls') +@JwtAuthorized() +@TransformToDto() +export class VoximplantCallController { + constructor(private readonly service: VoximplantCallService) {} + + @ApiCreatedResponse({ description: 'Create voximplant call', type: VoximplantCallDto }) + @Post() + async create(@CurrentAuth() { accountId, userId }: AuthData, @Body() dto: CreateVoximplantCallDto) { + return this.service.create(accountId, userId, dto); + } + + @AuthDataPrefetch({ user: true }) + @ApiCreatedResponse({ description: 'Get voximplant calls', type: VoximplantCallListDto }) + @Get() + async getList(@CurrentAuth() { accountId, user }: AuthData, @Query() paging: PagingQuery) { + return this.service.getList(accountId, user, paging); + } + @ApiCreatedResponse({ description: 'Get voximplant calls', type: VoximplantCallDto }) + @Get(':callId') + async getOne(@CurrentAuth() { accountId }: AuthData, @Param('callId', ParseIntPipe) callId: number) { + return this.service.findOne(accountId, { id: callId }); + } + + @ApiCreatedResponse({ description: 'Update voximplant call', type: VoximplantCallDto }) + @Patch(':externalId') + async updateByExternalId( + @CurrentAuth() { accountId, userId }: AuthData, + @Param('externalId') externalId: string, + @Body() dto: UpdateVoximplantCallDto, + ) { + return this.service.updateByExternalId(accountId, userId, externalId, dto); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.service.ts b/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.service.ts new file mode 100644 index 0000000..c88dfa5 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-call/voximplant-call.service.ts @@ -0,0 +1,180 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { PagingQuery } from '@/common'; + +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; + +import { TelephonyCallCreatedEvent, TelephonyCallUpdatedEvent, TelephonyEventType } from '../../common'; + +import { VoximplantAccountService } from '../voximplant-account'; +import { VoximplantScenarioService } from '../voximplant-scenario'; +import { VoximplantUserService } from '../voximplant-user'; +import { VoximplantNumberService } from '../voximplant-number'; + +import { + CreateVoximplantCallDto, + CreateVoximplantCallExtDto, + UpdateVoximplantCallDto, + UpdateVoximplantCallExtDto, +} from './dto'; +import { VoximplantCall } from './entities'; +import { VoximplantCallList } from './types'; + +interface FindFilter { + id?: number; + externalId?: string; +} + +@Injectable() +export class VoximplantCallService { + constructor( + private readonly eventEmitter: EventEmitter2, + @InjectRepository(VoximplantCall) + private readonly repository: Repository, + private readonly entityInfoService: EntityInfoService, + private readonly viAccountService: VoximplantAccountService, + private readonly viUserService: VoximplantUserService, + private readonly viScenarioService: VoximplantScenarioService, + private readonly viNumberService: VoximplantNumberService, + ) {} + + async create(accountId: number, userId: number, dto: CreateVoximplantCallDto): Promise { + const call = await this.repository.save(VoximplantCall.fromDto(accountId, userId, dto)); + + this.eventEmitter.emit( + TelephonyEventType.TelephonyCallCreated, + new TelephonyCallCreatedEvent({ + accountId, + entityId: call.entityId, + callId: call.id, + createdAt: call.createdAt.toISOString(), + }), + ); + + return call; + } + + async createExt(applicationId: number, dto: CreateVoximplantCallExtDto): Promise { + const viAccount = await this.viAccountService.findOneExt({ applicationId }); + if (viAccount) { + const viUser = await this.viUserService.findOne(viAccount.accountId, { userName: dto.userName }); + if (viUser) { + const viNumber = dto.viPhoneNumber + ? await this.viNumberService.findOne(viAccount.accountId, { phoneNumber: dto.viPhoneNumber }) + : null; + dto.numberId = viNumber?.id ?? dto.numberId ?? null; + return this.create(viAccount.accountId, viUser.userId, dto); + } + } + return null; + } + + async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + + async findOneFull(accountId: number, user: User, filter?: FindFilter): Promise { + const call = await this.findOne(accountId, filter); + if (call?.entityId) { + call.entityInfo = await this.entityInfoService.findOne({ accountId, user, entityId: call.entityId }); + } + return call; + } + + async getList(accountId: number, user: User, paging?: PagingQuery): Promise { + const [calls, total] = await this.createFindQb(accountId) + .orderBy('created_at', 'DESC') + .limit(paging?.take) + .offset(paging?.skip) + .getManyAndCount(); + for (const call of calls) { + call.entityInfo = call.entityId + ? await this.entityInfoService.findOne({ accountId, user, entityId: call.entityId }) + : null; + } + return new VoximplantCallList(calls, paging?.take + paging?.skip, total); + } + + async updateByExternalId( + accountId: number, + userId: number, + externalId: string, + dto: UpdateVoximplantCallDto, + ): Promise { + const viCall = await this.findOne(accountId, { externalId }); + return viCall ? this.updateCall(accountId, viCall, dto, userId) : null; + } + + async updateExt( + applicationId: number, + externalId: string, + dto: UpdateVoximplantCallExtDto, + ): Promise { + const viAccount = await this.viAccountService.findOneExt({ applicationId }); + + if (viAccount) { + const viCall = await this.findOne(viAccount.accountId, { externalId }); + if (viCall) { + const userId = dto.userName + ? (await this.viUserService.findOne(viAccount.accountId, { userName: dto.userName }))?.userId + : undefined; + return this.updateCall(viAccount.accountId, viCall, dto, userId); + } + } + return null; + } + + private async updateCall( + accountId: number, + viCall: VoximplantCall, + dto: UpdateVoximplantCallDto, + userId: number | null | undefined, + ): Promise { + const { status, entityId } = viCall; + await this.repository.save(viCall.update(userId, dto)); + + const call = await this.findOne(viCall.accountId, { id: viCall.id }); + + if (status !== call.status) { + const result = await this.viScenarioService.processCall(accountId, call); + if (result?.entities?.length > 0) { + call.entityId = result.entities[0].id; + await this.repository.save(call); + } + } + + this.eventEmitter.emit( + TelephonyEventType.TelephonyCallUpdated, + new TelephonyCallUpdatedEvent({ + accountId, + entityId: call.entityId, + callId: call.id, + createdAt: call.createdAt.toISOString(), + oldEntityId: entityId, + }), + ); + + return call; + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder().where('account_id = :accountId', { accountId }); + if (filter?.id) { + qb.andWhere('id = :id', { id: filter.id }); + } + if (filter?.externalId) { + qb.andWhere( + new Brackets((qb) => + qb + .where('call_id = :callId', { callId: filter.externalId }) + .orWhere('session_id = :sessionId', { sessionId: filter.externalId }), + ), + ); + } + return qb; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-core/index.ts b/backend/src/modules/telephony/voximplant/voximplant-core/index.ts new file mode 100644 index 0000000..b8058b3 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-core/index.ts @@ -0,0 +1,2 @@ +export * from './voximplant-core.controller'; +export * from './voximplant-core.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.controller.ts new file mode 100644 index 0000000..d2197c4 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.controller.ts @@ -0,0 +1,25 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common'; + +import { VoximplantCoreService } from './voximplant-core.service'; + +@ApiTags('telephony/voximplant/core') +@Controller('core') +@JwtAuthorized() +@TransformToDto() +export class VoximplantCoreController { + constructor(private service: VoximplantCoreService) {} + + @Get('children') + public async getChildrenAccounts() { + return this.service.getChildrenAccounts(); + } + + @Get('keys') + public async getKeys() { + return this.service.getKeys(); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.service.ts b/backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.service.ts new file mode 100644 index 0000000..0f9e5ba --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-core/voximplant-core.service.ts @@ -0,0 +1,172 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { HttpService } from '@nestjs/axios'; +import { lastValueFrom } from 'rxjs'; +import VoximplantApiClient from '@voximplant/apiclient-nodejs'; + +import { PasswordUtil } from '@/common'; +import { ApplicationConfig } from '@/config'; + +import { AccountSettingsService } from '@/modules/iam/account-settings/account-settings.service'; +import { UserService } from '@/modules/iam/user/user.service'; +import { Account } from '@/modules/iam/account/entities/account.entity'; +import { UserRole } from '@/modules/iam/common/enums/user-role.enum'; + +import { VoximplantConfig } from '../config/voximplant.config'; + +const VoximplantUrls = { + base: 'https://api.voximplant.com/platform_api', + addAccount: () => `${VoximplantUrls.base}/AddAccount`, + createKey: () => `${VoximplantUrls.base}/CreateKey`, +} as const; + +const VOXIMPLANT_ACCOUNT_NAME_MAX = 20; + +interface Key { + keyId: string; + privateKey: string; +} +interface ChildAccount { + accountId: number; + accountName: string; + accountEmail: string; + apiKey: string; + password: string; + billingAccountId: number; + active: boolean; + key: Key; +} + +@Injectable() +export class VoximplantCoreService { + private readonly logger = new Logger(VoximplantCoreService.name); + private _appName: string; + private _viConfig: VoximplantConfig; + + private client: VoximplantApiClient; + + constructor( + private readonly httpService: HttpService, + private readonly configService: ConfigService, + private readonly accountSettingsService: AccountSettingsService, + private readonly userService: UserService, + ) { + this._appName = this.configService.get('application').name; + this._viConfig = this.configService.get('voximplant'); + + const credentialsPath = `${process.cwd()}${this._viConfig.credentialsFile}`; + try { + this.client = new VoximplantApiClient({ pathToCredentials: credentialsPath }); + } catch (e) { + this.logger.warn(`Failed to initialize Voximplant client: ${(e as Error).message}`); + this.client = null; + } + } + + public async createChildAccount(account: Account): Promise { + if (!this.client) { + this.logger.warn('Voximplant client not initialized'); + return null; + } + const accountSettings = await this.accountSettingsService.getOne(account.id); + const owner = await this.userService.findOne({ accountId: account.id, role: UserRole.OWNER }); + const accountName = `${this._appName}-${account.subdomain}`.substring(0, VOXIMPLANT_ACCOUNT_NAME_MAX).toLowerCase(); + try { + const params = { + parent_account_id: this._viConfig.accountId, + parent_account_api_key: this._viConfig.accountApiKey, + account_name: accountName, + account_email: owner.email, + account_password: PasswordUtil.generateSecure(), + active: true, + language_code: accountSettings.language, + location: accountSettings.timeZone, + account_notifications: true, + tariff_changing_notifications: true, + min_balance_to_notify: 100, + Authorization: this.client.generateAuthHeader(), + }; + const { data } = await lastValueFrom(this.httpService.post(VoximplantUrls.addAccount(), {}, { params })); + if (data.result === 1) { + const key = await this.createKey(data.account_id, data.api_key); + if (key) { + return { + accountId: data.account_id, + accountName: params.account_name, + accountEmail: params.account_email, + apiKey: data.api_key, + password: params.account_password, + billingAccountId: data.billing_account_id, + active: data.active, + key, + }; + } + } else { + this.logger.error(`Create child account error: ${JSON.stringify(data)}`); + } + } catch (e) { + this.logger.error(`Create child account error`, (e as Error)?.stack); + } + return null; + } + + public async getChildrenAccounts() { + if (!this.client) { + this.logger.warn('Voximplant client not initialized'); + return []; + } + return await this.client.Accounts.getChildrenAccounts({}); + } + + public async setActiveChildAccount( + childAccountId: number, + childAccountName: string, + childAccountEmail: string, + isActive: boolean, + ): Promise { + if (!this.client) { + this.logger.warn('Voximplant client not initialized'); + return false; + } + const { result } = await this.client.Accounts.setChildAccountInfo({ + childAccountId, + childAccountName, + childAccountEmail, + active: isActive, + }); + + return result === 1; + } + + private async createKey(accountId: number, apiKey: string): Promise { + try { + const params = { + account_id: accountId, + api_key: apiKey, + description: `${this._appName} Integration`, + role_name: 'Owner', + }; + const { data } = await lastValueFrom(this.httpService.post(VoximplantUrls.createKey(), {}, { params })); + if (data.result) { + return { + keyId: data.result.key_id, + privateKey: data.result.private_key, + }; + } + } catch (e) { + this.logger.error(`Create key error`, (e as Error)?.stack); + } + return null; + } + + public async getKeys() { + if (!this.client) { + this.logger.warn('Voximplant client not initialized'); + return { roles: [], keys: [] }; + } + const { result: keys } = await this.client.RoleSystem.getKeys({}); + const { result: roles } = await this.client.RoleSystem.getRoles({}); + + return { roles, keys }; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/dto/create-voximplant-number.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-number/dto/create-voximplant-number.dto.ts new file mode 100644 index 0000000..d92167c --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/dto/create-voximplant-number.dto.ts @@ -0,0 +1,9 @@ +import { PickType } from '@nestjs/swagger'; + +import { VoximplantNumberDto } from './voximplant-number.dto'; + +export class CreateVoximplantNumberDto extends PickType(VoximplantNumberDto, [ + 'phoneNumber', + 'externalId', + 'userIds', +] as const) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-number/dto/index.ts new file mode 100644 index 0000000..9d9eb69 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-voximplant-number.dto'; +export * from './phone-number.dto'; +export * from './update-voximplant-number.dto'; +export * from './voximplant-number-filter.dto'; +export * from './voximplant-number.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/dto/phone-number.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-number/dto/phone-number.dto.ts new file mode 100644 index 0000000..2636ae1 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/dto/phone-number.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class PhoneNumberDto { + @ApiProperty() + @IsString() + externalId: string; + + @ApiProperty() + @IsString() + phoneNumber: string; + + @ApiProperty() + @IsString() + countryCode: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + regionName?: string; + + constructor({ externalId, phoneNumber, countryCode, regionName }: PhoneNumberDto) { + this.externalId = externalId; + this.phoneNumber = phoneNumber; + this.countryCode = countryCode; + this.regionName = regionName; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/dto/update-voximplant-number.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-number/dto/update-voximplant-number.dto.ts new file mode 100644 index 0000000..4bddc02 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/dto/update-voximplant-number.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { VoximplantNumberDto } from './voximplant-number.dto'; + +export class UpdateVoximplantNumberDto extends PartialType( + PickType(VoximplantNumberDto, ['phoneNumber', 'externalId', 'userIds'] as const), +) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number-filter.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number-filter.dto.ts new file mode 100644 index 0000000..2c0533e --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class VoximplantNumberFilterDto { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + accessibleUserId?: number; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number.dto.ts new file mode 100644 index 0000000..548832f --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/dto/voximplant-number.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class VoximplantNumberDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsString() + phoneNumber: string; + + @ApiProperty({ nullable: true }) + @IsString() + externalId: string | null; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + constructor({ id, phoneNumber, externalId, userIds }: VoximplantNumberDto) { + this.id = id; + this.phoneNumber = phoneNumber; + this.externalId = externalId; + this.userIds = userIds; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/entities/index.ts b/backend/src/modules/telephony/voximplant/voximplant-number/entities/index.ts new file mode 100644 index 0000000..4091ec1 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/entities/index.ts @@ -0,0 +1,2 @@ +export * from './voximplant-number-user.entity'; +export * from './voximplant-number.entity'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number-user.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number-user.entity.ts new file mode 100644 index 0000000..bbb25eb --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number-user.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class VoximplantNumberUser { + @PrimaryColumn() + numberId: number; + + @PrimaryColumn() + userId: number; + + @Column() + accountId: number; + + constructor(accountId: number, numberId: number, userId: number) { + this.accountId = accountId; + this.numberId = numberId; + this.userId = userId; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number.entity.ts new file mode 100644 index 0000000..df4a295 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/entities/voximplant-number.entity.ts @@ -0,0 +1,48 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { CreateVoximplantNumberDto, UpdateVoximplantNumberDto, VoximplantNumberDto } from '../dto'; +import { VoximplantNumberUser } from './voximplant-number-user.entity'; + +@Entity() +export class VoximplantNumber { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + phoneNumber: string; + + @Column({ nullable: true }) + externalId: string | null; + + constructor(accountId: number, phoneNumber: string, externalId: string | null) { + this.accountId = accountId; + this.phoneNumber = phoneNumber; + this.externalId = externalId; + } + + private _users: VoximplantNumberUser[] | null | undefined; + public get users(): VoximplantNumberUser[] | null | undefined { + return this._users; + } + public set users(value: VoximplantNumberUser[] | null | undefined) { + this._users = value; + } + + public static fromDto(accountId: number, dto: CreateVoximplantNumberDto): VoximplantNumber { + return new VoximplantNumber(accountId, dto.phoneNumber, dto.externalId); + } + + public update(dto: UpdateVoximplantNumberDto): VoximplantNumber { + this.phoneNumber = dto.phoneNumber !== undefined ? dto.phoneNumber : this.phoneNumber; + this.externalId = dto.externalId !== undefined ? dto.externalId : this.externalId; + + return this; + } + + public toDto(): VoximplantNumberDto { + return new VoximplantNumberDto({ ...this, userIds: this._users?.map((u) => u.userId) }); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/index.ts b/backend/src/modules/telephony/voximplant/voximplant-number/index.ts new file mode 100644 index 0000000..e66c244 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './services'; +export * from './types'; +export * from './voximplant-number.controller'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/services/index.ts b/backend/src/modules/telephony/voximplant/voximplant-number/services/index.ts new file mode 100644 index 0000000..357cca9 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/services/index.ts @@ -0,0 +1,2 @@ +export * from './voximplant-number-user.service'; +export * from './voximplant-number.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number-user.service.ts b/backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number-user.service.ts new file mode 100644 index 0000000..2f7bd36 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number-user.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { VoximplantNumberUser } from '../entities'; + +interface FindFilter { + numberId?: number; +} + +@Injectable() +export class VoximplantNumberUserService { + constructor( + @InjectRepository(VoximplantNumberUser) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, numberId: number, userIds: number[]): Promise { + return await this.repository.save(userIds.map((userId) => new VoximplantNumberUser(accountId, numberId, userId))); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return await this.createFindQb(accountId, filter).getOne(); + } + public async findMany(accountId: number, filter?: FindFilter): Promise { + return await this.createFindQb(accountId, filter).getMany(); + } + + public async update( + accountId: number, + numberId: number, + currentUsers: VoximplantNumberUser[], + userIds: number[], + ): Promise { + const addUsers = userIds.filter((id) => !currentUsers.some((user) => user.userId === id)); + const removeUsers = currentUsers.filter((user) => !userIds.some((id) => id === user.userId)); + + currentUsers.push(...(await this.create(accountId, numberId, addUsers))); + + if (removeUsers.length) { + await this.repository.remove(removeUsers); + } + + return currentUsers.filter((user) => !removeUsers.some((u) => u.userId === user.userId)); + } + + public async removeUser(accountId: number, userId: number) { + await this.repository.delete({ accountId, userId }); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('vinu').where('vinu.account_id = :accountId', { accountId }); + + if (filter?.numberId) { + qb.andWhere('vinu.number_id = :numberId', { numberId: filter.numberId }); + } + + return qb; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number.service.ts b/backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number.service.ts new file mode 100644 index 0000000..f5ec9e6 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/services/voximplant-number.service.ts @@ -0,0 +1,135 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { VoximplantAccountService } from '../../voximplant-account'; + +import { CreateVoximplantNumberDto, UpdateVoximplantNumberDto } from '../dto'; +import { VoximplantNumber } from '../entities'; +import { ExpandableField, PhoneNumber } from '../types'; +import { VoximplantNumberUserService } from './voximplant-number-user.service'; + +interface FindFilter { + id?: number; + phoneNumber?: string; + accessibleUserId?: number; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class VoximplantNumberService { + constructor( + @InjectRepository(VoximplantNumber) + private readonly repository: Repository, + private readonly viAccountService: VoximplantAccountService, + private readonly viNumberUserService: VoximplantNumberUserService, + ) {} + + public async getAvailableNumbers(accountId: number): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + + const response = await client.PhoneNumbers.getPhoneNumbers(appParam); + if (response.result) { + const numbers: PhoneNumber[] = []; + for (const number of response.result) { + numbers.push( + new PhoneNumber({ + externalId: number.phoneId.toString(), + phoneNumber: number.phoneNumber, + countryCode: number.phoneCountryCode, + regionName: number.phoneRegionName, + }), + ); + } + return numbers; + } + + return []; + } + + public async create(accountId: number, dto: CreateVoximplantNumberDto): Promise { + const viNumber = await this.repository.save(VoximplantNumber.fromDto(accountId, dto)); + + if (dto.userIds?.length) { + viNumber.users = await this.viNumberUserService.create(accountId, viNumber.id, dto.userIds); + } + + return viNumber; + } + + public async findOne( + accountId: number, + filter?: FindFilter, + options?: FindOptions, + ): Promise { + const viNumber = await this.createFindQb(accountId, filter).getOne(); + return viNumber && options?.expand ? await this.expandOne(viNumber, options.expand) : viNumber; + } + + public async findMany(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const viNumbers = await this.createFindQb(accountId, filter).orderBy('vin.id').getMany(); + return viNumbers && options?.expand ? await this.expandMany(viNumbers, options.expand) : viNumbers; + } + + public async getCount(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getCount(); + } + + public async checkAvailable(accountId: number, userId: number, phoneNumber: string): Promise { + return (await this.getCount(accountId, { accessibleUserId: userId, phoneNumber })) > 0; + } + + public async update(accountId: number, numberId: number, dto: UpdateVoximplantNumberDto): Promise { + const viNumber = await this.findOne(accountId, { id: numberId }, { expand: ['users'] }); + + await this.repository.save(viNumber.update(dto)); + + if (dto.userIds) { + viNumber.users = await this.viNumberUserService.update(accountId, viNumber.id, viNumber.users, dto.userIds); + } + + return viNumber; + } + + public async delete(accountId: number, numberId: number) { + await this.repository.delete({ accountId, id: numberId }); + } + + public async removeUser(accountId: number, userId: number) { + await this.viNumberUserService.removeUser(accountId, userId); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('vin').where('vin.account_id = :accountId', { accountId }); + + if (filter?.id) { + qb.andWhere('vin.id = :id', { id: filter.id }); + } + if (filter?.phoneNumber) { + qb.andWhere('vin.phone_number ilike :phoneNumber', { phoneNumber: `%${filter.phoneNumber}%` }); + } + if (filter?.accessibleUserId) { + qb.leftJoin('voximplant_number_user', 'vinu', 'vinu.number_id = vin.id').andWhere( + new Brackets((qb1) => + qb1 + .where('vinu.user_id = :accessibleUserId', { accessibleUserId: filter.accessibleUserId }) + .orWhere('vinu.user_id is NULL'), + ), + ); + } + + return qb; + } + + private async expandOne(viNumber: VoximplantNumber, expand: ExpandableField[]): Promise { + if (expand.includes('users')) { + viNumber.users = await this.viNumberUserService.findMany(viNumber.accountId, { numberId: viNumber.id }); + } + return viNumber; + } + private async expandMany(viNumbers: VoximplantNumber[], expand: ExpandableField[]): Promise { + return await Promise.all(viNumbers.map((viNumber) => this.expandOne(viNumber, expand))); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/types/expandable-field.ts b/backend/src/modules/telephony/voximplant/voximplant-number/types/expandable-field.ts new file mode 100644 index 0000000..13ac0ef --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'users'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/types/index.ts b/backend/src/modules/telephony/voximplant/voximplant-number/types/index.ts new file mode 100644 index 0000000..a2c92c0 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/types/index.ts @@ -0,0 +1,2 @@ +export * from './expandable-field'; +export * from './phone-number'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/types/phone-number.ts b/backend/src/modules/telephony/voximplant/voximplant-number/types/phone-number.ts new file mode 100644 index 0000000..6ec9969 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/types/phone-number.ts @@ -0,0 +1,29 @@ +import { PhoneNumberDto } from '../dto'; + +export class PhoneNumber { + externalId: string; + phoneNumber: string; + countryCode: string; + regionName?: string; + + constructor({ + externalId, + phoneNumber, + countryCode, + regionName, + }: { + externalId: string; + phoneNumber: string; + countryCode: string; + regionName?: string; + }) { + this.externalId = externalId; + this.phoneNumber = phoneNumber; + this.countryCode = countryCode; + this.regionName = regionName; + } + + public toDto(): PhoneNumberDto { + return new PhoneNumberDto(this); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-number/voximplant-number.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-number/voximplant-number.controller.ts new file mode 100644 index 0000000..83640bc --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-number/voximplant-number.controller.ts @@ -0,0 +1,88 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { ExpandQuery, TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized, UserAccess } from '@/modules/iam/common'; + +import { + CreateVoximplantNumberDto, + PhoneNumberDto, + UpdateVoximplantNumberDto, + VoximplantNumberDto, + VoximplantNumberFilterDto, +} from './dto'; +import { ExpandableField } from './types'; +import { VoximplantNumberService } from './services'; + +@ApiTags('telephony/voximplant/numbers') +@Controller('numbers') +@JwtAuthorized() +@TransformToDto() +export class VoximplantNumberController { + constructor(private readonly service: VoximplantNumberService) {} + + @ApiOkResponse({ description: 'Get available voximplant numbers', type: [PhoneNumberDto] }) + @Get('available') + public async getAvailableNumbers(@CurrentAuth() { accountId }: AuthData) { + return this.service.getAvailableNumbers(accountId); + } + + @ApiCreatedResponse({ description: 'Create voximplant number', type: VoximplantNumberDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateVoximplantNumberDto) { + return this.service.create(accountId, dto); + } + + @ApiOkResponse({ description: 'Get voximplant numbers', type: [VoximplantNumberDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: users.', + }) + @Get() + public async getMany( + @CurrentAuth() { accountId }: AuthData, + @Query() filter: VoximplantNumberFilterDto, + @Query() expand?: ExpandQuery, + ) { + return this.service.findMany(accountId, filter, { expand: expand.fields }); + } + + @ApiOkResponse({ description: 'Get voximplant number', type: VoximplantNumberDto }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: users.', + }) + @Get(':numberId') + public async getOne( + @CurrentAuth() { accountId }: AuthData, + @Param('numberId', ParseIntPipe) numberId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findOne(accountId, { id: numberId }, { expand: expand.fields }); + } + + @ApiCreatedResponse({ description: 'Update voximplant number', type: VoximplantNumberDto }) + @Patch(':numberId') + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('numberId', ParseIntPipe) numberId: number, + @Body() dto: UpdateVoximplantNumberDto, + ) { + return this.service.update(accountId, numberId, dto); + } + + @ApiOkResponse({ description: 'Delete voximplant number' }) + @Delete(':numberId') + @UserAccess({ adminOnly: true }) + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('numberId', ParseIntPipe) numberId: number) { + return this.service.delete(accountId, numberId); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report-filter.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report-filter.dto.ts new file mode 100644 index 0000000..12bdb3e --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report-filter.dto.ts @@ -0,0 +1,35 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter, NumberFilter } from '@/common'; +import { CallDirection, CallStatus } from '../../common'; + +export class CallHistoryReportFilterDto { + @ApiPropertyOptional({ type: Number, nullable: true, description: 'Entity type ID' }) + @IsOptional() + @IsNumber() + entityTypeId?: number | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs' }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ enum: CallDirection, nullable: true, description: 'Call direction' }) + @IsOptional() + @IsEnum(CallDirection) + direction?: CallDirection | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; + + @ApiPropertyOptional({ type: NumberFilter, nullable: true, description: 'Duration' }) + @IsOptional() + duration?: NumberFilter | null; + + @ApiPropertyOptional({ enum: CallStatus, nullable: true, description: 'Call status' }) + @IsOptional() + @IsEnum(CallStatus) + status?: CallStatus | null; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report.dto.ts new file mode 100644 index 0000000..f1a903c --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-history-report.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { PagingMeta } from '@/common'; +import { VoximplantCallDto } from '../../voximplant-call'; + +export class CallHistoryReportDto { + @ApiProperty({ type: [VoximplantCallDto], description: 'Calls' }) + calls: VoximplantCallDto[]; + + @ApiProperty({ type: PagingMeta, description: 'Paging meta' }) + meta: PagingMeta; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-block.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-block.dto.ts new file mode 100644 index 0000000..3d92409 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-block.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { QuantityAmountDto } from '@/common'; + +export class CallReportBlockDto { + @ApiProperty({ type: QuantityAmountDto, description: 'All calls' }) + all: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Incoming calls' }) + incoming: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Outgoing calls' }) + outgoing: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Missed calls' }) + missed: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Average for all calls' }) + avgAll: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Average for incoming calls' }) + avgIncoming: QuantityAmountDto; + + @ApiProperty({ type: QuantityAmountDto, description: 'Average for outgoing calls' }) + avgOutgoing: QuantityAmountDto; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-filter.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-filter.dto.ts new file mode 100644 index 0000000..6a269e5 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-filter.dto.ts @@ -0,0 +1,46 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { DatePeriodFilter, NumberFilter } from '@/common'; +import { BoardStageType } from '@/CRM/board-stage'; + +import { TelephonyReportType } from '../enums'; + +export class CallReportFilterDto { + @ApiProperty({ enum: TelephonyReportType, description: 'Report type' }) + @IsEnum(TelephonyReportType) + type: TelephonyReportType; + + @ApiPropertyOptional({ type: Number, nullable: true, description: 'Entity type ID' }) + @IsOptional() + @IsNumber() + entityTypeId?: number | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Board IDs' }) + @IsOptional() + @IsArray() + boardIds?: number[] | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs' }) + @IsOptional() + @IsArray() + userIds?: number[] | null; + + @ApiPropertyOptional({ enum: BoardStageType, nullable: true, description: 'Stage type' }) + @IsOptional() + @IsEnum(BoardStageType) + stageType?: BoardStageType | null; + + @ApiPropertyOptional({ type: DatePeriodFilter, nullable: true, description: 'Period' }) + @IsOptional() + period?: DatePeriodFilter | null; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'Number IDs' }) + @IsOptional() + @IsArray() + numberIds?: number[] | null; + + @ApiPropertyOptional({ type: NumberFilter, nullable: true, description: 'Duration' }) + @IsOptional() + duration?: NumberFilter | null; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-row.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-row.dto.ts new file mode 100644 index 0000000..60539db --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report-row.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber } from 'class-validator'; + +import { CallReportBlockDto } from './call-report-block.dto'; + +export class CallReportRowDto { + @ApiProperty({ description: 'User ID or Department ID depends from report type or 0 for total row.' }) + @IsNumber() + ownerId: number; + + @ApiProperty({ type: CallReportBlockDto, description: 'Call report block' }) + @IsArray() + call: CallReportBlockDto; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report.dto.ts new file mode 100644 index 0000000..74429e0 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/call-report.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { CallReportRowDto } from './call-report-row.dto'; + +export class CallReportDto { + @ApiProperty({ type: [CallReportRowDto], description: 'Call report rows for users' }) + users: CallReportRowDto[]; + + @ApiProperty({ type: [CallReportRowDto], description: 'Call report rows for departments' }) + departments: CallReportRowDto[]; + + @ApiProperty({ type: CallReportRowDto, description: 'Total call report row' }) + total: CallReportRowDto; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/index.ts new file mode 100644 index 0000000..f64f8b5 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/dto/index.ts @@ -0,0 +1,6 @@ +export * from './call-history-report-filter.dto'; +export * from './call-history-report.dto'; +export * from './call-report-block.dto'; +export * from './call-report-filter.dto'; +export * from './call-report-row.dto'; +export * from './call-report.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/enums/index.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/enums/index.ts new file mode 100644 index 0000000..e468c83 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/enums/index.ts @@ -0,0 +1 @@ +export * from './telephony-report-type.enum'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/enums/telephony-report-type.enum.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/enums/telephony-report-type.enum.ts new file mode 100644 index 0000000..feefe4a --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/enums/telephony-report-type.enum.ts @@ -0,0 +1,5 @@ +export enum TelephonyReportType { + User = 'user', + Rating = 'rating', + Department = 'department', +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/index.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/index.ts new file mode 100644 index 0000000..061bcf6 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './types'; +export * from './voximplant-reporting.controller'; +export * from './voximplant-reporting.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-history-report.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-history-report.ts new file mode 100644 index 0000000..a553f20 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-history-report.ts @@ -0,0 +1,17 @@ +import { PagingMeta } from '@/common'; +import { VoximplantCall } from '../../voximplant-call'; +import { CallHistoryReportDto } from '../dto'; + +export class CallHistoryReport { + calls: VoximplantCall[]; + meta: PagingMeta; + + constructor({ calls, offset, total }: { calls: VoximplantCall[]; offset: number; total: number }) { + this.calls = calls; + this.meta = new PagingMeta(offset, total); + } + + public toDto(): CallHistoryReportDto { + return { calls: this.calls.map((call) => call.toDto()), meta: this.meta }; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-block.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-block.ts new file mode 100644 index 0000000..887b7b9 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-block.ts @@ -0,0 +1,63 @@ +import { QuantityAmount } from '@/common'; +import { CallReportBlockDto } from '../dto'; + +export class CallReportBlock { + all: QuantityAmount; + incoming: QuantityAmount; + outgoing: QuantityAmount; + missed: QuantityAmount; + avgAll: QuantityAmount; + avgIncoming: QuantityAmount; + avgOutgoing: QuantityAmount; + + constructor( + all: QuantityAmount, + incoming: QuantityAmount, + outgoing: QuantityAmount, + missed: QuantityAmount, + avgAll: QuantityAmount, + avgIncoming: QuantityAmount, + avgOutgoing: QuantityAmount, + ) { + this.all = all; + this.incoming = incoming; + this.outgoing = outgoing; + this.missed = missed; + this.avgAll = avgAll; + this.avgIncoming = avgIncoming; + this.avgOutgoing = avgOutgoing; + } + + public static empty(): CallReportBlock { + return new CallReportBlock( + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + QuantityAmount.empty(), + ); + } + + public toDto(): CallReportBlockDto { + return { + all: this.all.toDto(), + incoming: this.incoming.toDto(), + outgoing: this.outgoing.toDto(), + missed: this.missed.toDto(), + avgAll: this.avgAll.toDto(), + avgIncoming: this.avgIncoming.toDto(), + avgOutgoing: this.avgOutgoing.toDto(), + }; + } + + public add(cell: CallReportBlock): CallReportBlock { + this.all.add(cell.all); + this.incoming.add(cell.incoming); + this.outgoing.add(cell.outgoing); + this.missed.add(cell.missed); + + return this; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-row.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-row.ts new file mode 100644 index 0000000..1db01d5 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report-row.ts @@ -0,0 +1,26 @@ +import { CallReportRowDto } from '../dto'; +import { CallReportBlock } from './call-report-block'; + +export class CallReportRow { + ownerId: number; + call: CallReportBlock; + + constructor(ownerId: number, call: CallReportBlock) { + this.ownerId = ownerId; + this.call = call; + } + + public static empty(ownerId: number): CallReportRow { + return new CallReportRow(ownerId, CallReportBlock.empty()); + } + + public toDto(): CallReportRowDto { + return { ownerId: this.ownerId, call: this.call.toDto() }; + } + + public add(row: CallReportRow): CallReportRow { + this.call.add(row.call); + + return this; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report.ts new file mode 100644 index 0000000..fb8b317 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/call-report.ts @@ -0,0 +1,30 @@ +import { CallReportDto } from '../dto'; +import { CallReportRow } from './call-report-row'; + +export class CallReport { + users: Map; + departments: Map; + total: CallReportRow; + + constructor({ + users, + departments, + total, + }: { + users: Map; + departments: Map; + total: CallReportRow; + }) { + this.users = users; + this.departments = departments; + this.total = total; + } + + public toDto(): CallReportDto { + return { + users: Array.from(this.users.values()).map((u) => u.toDto()), + departments: Array.from(this.departments.values()).map((u) => u.toDto()), + total: this.total.toDto(), + }; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/types/index.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/index.ts new file mode 100644 index 0000000..286ab3a --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/types/index.ts @@ -0,0 +1,4 @@ +export * from './call-history-report'; +export * from './call-report-block'; +export * from './call-report-row'; +export * from './call-report'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.controller.ts new file mode 100644 index 0000000..8882fb9 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.controller.ts @@ -0,0 +1,36 @@ +import { Body, Controller, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { PagingQuery, TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { CallHistoryReportDto, CallHistoryReportFilterDto, CallReportDto, CallReportFilterDto } from './dto'; +import { VoximplantReportingService } from './voximplant-reporting.service'; + +@ApiTags('telephony/voximplant/reporting') +@Controller('reporting') +@JwtAuthorized({ prefetch: { user: true } }) +@TransformToDto() +export class VoximplantReportingController { + constructor(private readonly service: VoximplantReportingService) {} + + @ApiOperation({ summary: 'Get call report', description: 'Get call report' }) + @ApiBody({ type: CallReportFilterDto, required: true, description: 'Call report filter' }) + @ApiOkResponse({ description: 'General call report', type: CallReportDto }) + @Post('call') + public async getCallReport(@CurrentAuth() { accountId, user }: AuthData, @Body() filter: CallReportFilterDto) { + return this.service.getCallReport({ accountId, user, filter }); + } + + @ApiOperation({ summary: 'Get call history report', description: 'Get call history report' }) + @ApiBody({ type: CallHistoryReportFilterDto, required: true, description: 'Call history report filter' }) + @ApiOkResponse({ description: 'Call history report', type: CallHistoryReportDto }) + @Post('call/history') + public async getCallHistoryReport( + @CurrentAuth() { accountId, user }: AuthData, + @Body() filter: CallHistoryReportFilterDto, + @Query() paging: PagingQuery, + ) { + return this.service.getCallHistoryReport({ accountId, user, filter, paging }); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.service.ts b/backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.service.ts new file mode 100644 index 0000000..3387718 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-reporting/voximplant-reporting.service.ts @@ -0,0 +1,334 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { + NumberFilter, + PagingQuery, + DateUtil, + DatePeriod, + ForbiddenError, + propagateData, + isUnique, + intersection, +} from '@/common'; + +import { AuthorizationService } from '@/modules/iam/authorization/authorization.service'; +import { AccountSettingsService } from '@/modules/iam/account-settings/account-settings.service'; +import { DepartmentService } from '@/modules/iam/department'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { EntityInfoService } from '@/modules/entity/entity-info/entity-info.service'; +import { EntityType } from '@/CRM/entity-type/entities'; + +import { CallStatus, CallDirection } from '../common'; +import { VoximplantCall } from '../voximplant-call/entities/voximplant-call.entity'; + +import { CallHistoryReportFilterDto, CallReportFilterDto } from './dto'; +import { TelephonyReportType } from './enums'; +import { CallHistoryReport, CallReport, CallReportRow } from './types'; + +interface FindFilter { + accountId: number; + status?: CallStatus; + entityTypeId?: number; + stageIds?: number[]; + userIds?: number[]; + direction?: CallDirection; + period?: DatePeriod; + duration?: NumberFilter; + numberIds?: number[] | null; +} + +enum CallGroupBy { + None = 'none', + User = 'user', + Department = 'department', +} +interface RawReportRow { + direction: CallDirection; + owner_id: number; + quantity: number; + duration: number; +} + +@Injectable() +export class VoximplantReportingService { + constructor( + @InjectRepository(VoximplantCall) + private readonly repository: Repository, + private readonly authService: AuthorizationService, + private readonly accountSettingsService: AccountSettingsService, + private readonly departmentService: DepartmentService, + private readonly entityInfoService: EntityInfoService, + ) {} + + public async getCallHistoryReport({ + accountId, + user, + filter, + paging, + }: { + accountId: number; + user: User; + filter: CallHistoryReportFilterDto; + paging: PagingQuery; + }): Promise { + let allowedUserIds: number[] | undefined = undefined; + if (filter.entityTypeId) { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: EntityType.getAuthorizable(filter.entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + allowedUserIds = userIds; + } + + const qb = this.createFindQb({ + accountId, + userIds: filter.userIds?.length ? intersection(filter.userIds, allowedUserIds) : allowedUserIds, + direction: filter.direction, + period: filter.period ? DatePeriod.fromFilter(filter.period) : undefined, + duration: filter.duration, + status: filter.status, + }); + + const calls = await qb.clone().orderBy('call.created_at', 'DESC').offset(paging.skip).limit(paging.take).getMany(); + + const entityIds = calls + .filter((c) => !!c.entityId) + .map((c) => c.entityId) + .filter(isUnique); + const entityInfos = entityIds.length ? await this.entityInfoService.findMany({ accountId, user, entityIds }) : []; + if (entityInfos.length) { + calls.forEach((call) => { + call.entityInfo = entityInfos.find((e) => e.id === call.entityId) ?? null; + }); + } + + const total = await qb.getCount(); + + return new CallHistoryReport({ calls, offset: paging.take + paging.skip, total }); + } + + public async getCallReport({ + accountId, + user, + filter, + }: { + accountId: number; + user: User; + filter: CallReportFilterDto; + }): Promise { + let allowedUserIds: number[] | undefined = undefined; + if (filter.entityTypeId) { + const { allow, userIds } = await this.authService.getPermissions({ + action: 'report', + user, + authorizable: EntityType.getAuthorizable(filter.entityTypeId), + }); + if (!allow) { + throw new ForbiddenError(); + } + allowedUserIds = userIds; + } + + const reportFilter: FindFilter = { + accountId, + //TODO: add EntityTypes and Stages filtering logic + // entityTypeId: filter.entityTypeId, + // stageIds: [], + userIds: filter.userIds?.length ? intersection(filter.userIds, allowedUserIds) : allowedUserIds, + period: filter.period ? DatePeriod.fromFilter(filter.period) : undefined, + numberIds: filter.numberIds, + duration: filter.duration, + }; + + const period = await this.getReportPeriod(reportFilter); + + const users = + filter.type !== TelephonyReportType.Department + ? await this.getReportGroupBy({ filter: reportFilter, groupBy: CallGroupBy.User }) + : new Map(); + const departments = + filter.type !== TelephonyReportType.Rating + ? await this.getReportGroupBy({ filter: reportFilter, groupBy: CallGroupBy.Department }) + : new Map(); + + const totalRow = await this.getReportGroupBy({ filter: reportFilter, groupBy: CallGroupBy.None }); + + if (departments.size) { + const hierarchy = await this.departmentService.getHierarchy({ accountId }); + + if (hierarchy.length) { + propagateData(hierarchy, departments, CallReportRow.empty); + } + } + + await this.setAverage({ rows: users, period }); + await this.setAverage({ rows: departments, period }); + await this.setAverage({ rows: totalRow, period }); + + const total = totalRow.values().next().value ?? CallReportRow.empty(0); + const report = new CallReport({ users, departments, total }); + + return report; + } + + private async getReportGroupBy({ + filter, + groupBy, + }: { + filter: FindFilter; + groupBy: CallGroupBy; + }): Promise> { + const rowMap = new Map(); + + const success = await this.getReportRaw({ filter: { ...filter, status: CallStatus.SUCCESS }, groupBy }); + for (const item of success) { + const row = rowMap.get(item.owner_id) ?? CallReportRow.empty(item.owner_id); + switch (item.direction) { + case CallDirection.INCOMING: + row.call.incoming.quantity = item.quantity; + row.call.incoming.amount = item.duration; + break; + case CallDirection.OUTGOING: + row.call.outgoing.quantity = item.quantity; + row.call.outgoing.amount = item.duration; + break; + } + row.call.all.quantity += item.quantity; + row.call.all.amount += item.duration; + rowMap.set(item.owner_id, row); + } + + const missed = await this.getReportRaw({ filter: { ...filter, status: CallStatus.MISSED }, groupBy }); + for (const item of missed) { + const row = rowMap.get(item.owner_id) ?? CallReportRow.empty(item.owner_id); + row.call.missed.quantity += item.quantity; + row.call.missed.amount += item.duration; + row.call.all.quantity += item.quantity; + row.call.all.amount += item.duration; + rowMap.set(item.owner_id, row); + } + + return rowMap; + } + + private async getReportRaw({ + filter, + groupBy, + }: { + filter: FindFilter; + groupBy: CallGroupBy; + }): Promise { + const qb = this.createFindQb(filter) + .select('call.direction', 'direction') + .addSelect('count(call.id)::int', 'quantity') + .addSelect('sum(call.duration)::int', 'duration') + .groupBy('call.direction'); + + switch (groupBy) { + case CallGroupBy.User: + qb.addGroupBy('call.user_id').addSelect('call.user_id', 'owner_id'); + break; + case CallGroupBy.Department: + qb.leftJoin('users', 'u', 'u.id = call.user_id') + .addGroupBy('u.department_id') + .addSelect('COALESCE(u.department_id, 0)', 'owner_id'); + break; + case CallGroupBy.None: + qb.addSelect('0', 'owner_id'); + break; + } + + return qb.getRawMany(); + } + + private async getReportPeriod(filter: FindFilter): Promise { + const qb = this.createFindQb(filter); + const periodDates = await qb + .select('min(call.created_at)', 'min_date') + .addSelect('max(call.created_at)', 'max_date') + .getRawOne<{ min_date: string; max_date: string }>(); + + if (periodDates.min_date && periodDates.max_date) { + const startDate = new Date(periodDates.min_date); + const endDate = new Date(periodDates.max_date); + + const { workingDays } = await this.accountSettingsService.getOne(filter.accountId); + return DateUtil.workingDaysBetween(startDate, endDate, workingDays); + } + + return 1; + } + + private async setAverage({ rows, period }: { rows: Map; period: number }) { + rows.forEach((value) => { + value.call.avgAll.quantity = Math.round(value.call.all.quantity / period); + value.call.avgAll.amount = Math.round(value.call.all.amount / period); + value.call.avgIncoming.quantity = Math.round(value.call.incoming.quantity / period); + value.call.avgIncoming.amount = Math.round(value.call.incoming.amount / period); + value.call.avgOutgoing.quantity = Math.round(value.call.outgoing.quantity / period); + value.call.avgOutgoing.amount = Math.round(value.call.outgoing.amount / period); + }); + } + + private createFindQb({ + accountId, + status, + entityTypeId, + stageIds, + userIds, + direction, + period, + duration, + numberIds, + }: FindFilter) { + const qb = this.repository.createQueryBuilder('call').andWhere('call.account_id = :accountId', { accountId }); + + if (status) { + qb.andWhere('call.status = :status', { status }); + } + if (entityTypeId || stageIds) { + qb.leftJoin('entity', 'e', 'e.id = call.entity_id'); + + if (entityTypeId) { + qb.andWhere('e.entity_type_id = :entityTypeId', { entityTypeId }); + } + + if (stageIds?.length) { + qb.andWhere('call.stage_id IN (:...stageIds)', { stageIds }); + } + } + if (userIds?.length) { + qb.andWhere('call.user_id IN (:...userIds)', { userIds }); + } + if (direction) { + qb.andWhere('call.direction = :direction', { direction }); + } + if (period) { + if (period.from) { + qb.andWhere('call.created_at >= :from', { from: period.from }); + } + if (period.to) { + qb.andWhere('call.created_at <= :to', { to: period.to }); + } + } + if (duration) { + if (duration.min) { + qb.andWhere('call.duration >= :min', { min: duration.min }); + } + if (duration.max) { + qb.andWhere('call.duration <= :max', { max: duration.max }); + } + } + if (numberIds?.length) { + qb.andWhere('call.number_id IN (:...numberIds)', { numberIds }); + } + + return qb; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/index.ts new file mode 100644 index 0000000..1aac3d8 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/index.ts @@ -0,0 +1,4 @@ +export * from './voximplant-scenario-entity.dto'; +export * from './voximplant-scenario-note.dto'; +export * from './voximplant-scenario-task.dto'; +export * from './voximplant-scenarios.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-entity.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-entity.dto.ts new file mode 100644 index 0000000..60ffc57 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-entity.dto.ts @@ -0,0 +1,44 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { ScenarioType } from '../enums'; + +export class VoximplantScenarioEntityDto { + @ApiProperty({ enum: ScenarioType }) + @IsEnum(ScenarioType) + scenarioType: ScenarioType; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + contactId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + dealId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + boardId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + ownerId: number | null; + + constructor( + scenarioType: ScenarioType, + contactId: number | null, + dealId: number | null, + boardId: number | null, + ownerId: number | null, + ) { + this.scenarioType = scenarioType; + this.contactId = contactId; + this.dealId = dealId; + this.boardId = boardId; + this.ownerId = ownerId; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-note.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-note.dto.ts new file mode 100644 index 0000000..15ff4ae --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-note.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; + +import { ScenarioType } from '../enums'; + +export class VoximplantScenarioNoteDto { + @ApiProperty({ enum: ScenarioType }) + @IsEnum(ScenarioType) + scenarioType: ScenarioType; + + @ApiProperty() + @IsString() + noteText: string; + + constructor(scenarioType: ScenarioType, noteText: string) { + this.scenarioType = scenarioType; + this.noteText = noteText; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-task.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-task.dto.ts new file mode 100644 index 0000000..3c068d5 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenario-task.dto.ts @@ -0,0 +1,86 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { ScenarioType } from '../enums'; + +export class VoximplantScenarioTaskDto { + @ApiProperty({ enum: ScenarioType }) + @IsEnum(ScenarioType) + scenarioType: ScenarioType; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsBoolean() + createActivity: boolean | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + activityTypeId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + activityText: string | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + activityDuration: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + activityOwnerId: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsBoolean() + createTask: boolean | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + taskTitle: string | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsString() + taskText: string | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + taskDuration: number | null; + + @ApiProperty({ nullable: true }) + @IsOptional() + @IsNumber() + taskOwnerId: number | null; + + constructor( + scenarioType: ScenarioType, + createActivity: boolean | null, + activityTypeId: number | null, + activityText: string | null, + activityDuration: number | null, + activityOwnerId: number | null, + createTask: boolean | null, + taskTitle: string | null, + taskText: string | null, + taskDuration: number | null, + taskOwnerId: number | null, + ) { + this.scenarioType = scenarioType; + this.createActivity = createActivity; + this.activityTypeId = activityTypeId; + this.activityText = activityText; + this.activityDuration = activityDuration; + this.activityOwnerId = activityOwnerId; + this.createTask = createTask; + this.taskTitle = taskTitle; + this.taskText = taskText; + this.taskDuration = taskDuration; + this.taskOwnerId = taskOwnerId; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenarios.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenarios.dto.ts new file mode 100644 index 0000000..7aba284 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/dto/voximplant-scenarios.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsOptional } from 'class-validator'; + +import { VoximplantScenarioEntityDto } from './voximplant-scenario-entity.dto'; +import { VoximplantScenarioNoteDto } from './voximplant-scenario-note.dto'; +import { VoximplantScenarioTaskDto } from './voximplant-scenario-task.dto'; + +export class VoximplantScenariosDto { + @ApiProperty({ type: [VoximplantScenarioEntityDto], nullable: true }) + @IsOptional() + @IsArray() + entities: VoximplantScenarioEntityDto[] | null; + + @ApiProperty({ type: [VoximplantScenarioNoteDto], nullable: true }) + @IsOptional() + @IsArray() + notes: VoximplantScenarioNoteDto[] | null; + + @ApiProperty({ type: [VoximplantScenarioTaskDto], nullable: true }) + @IsOptional() + @IsArray() + tasks: VoximplantScenarioTaskDto[] | null; + + constructor( + entities: VoximplantScenarioEntityDto[] | null, + notes: VoximplantScenarioNoteDto[] | null, + tasks: VoximplantScenarioTaskDto[] | null, + ) { + this.entities = entities; + this.notes = notes; + this.tasks = tasks; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/index.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/index.ts new file mode 100644 index 0000000..00e8a95 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/index.ts @@ -0,0 +1,3 @@ +export * from './voximplant-scenario-entity.entity'; +export * from './voximplant-scenario-note.entity'; +export * from './voximplant-scenario-task.entity'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-entity.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-entity.entity.ts new file mode 100644 index 0000000..78b299b --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-entity.entity.ts @@ -0,0 +1,59 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { ScenarioType } from '../enums'; +import { VoximplantScenarioEntityDto } from '../dto'; + +@Entity() +export class VoximplantScenarioEntity { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + scenarioType: ScenarioType; + + @Column({ nullable: true }) + contactId: number | null; + + @Column({ nullable: true }) + dealId: number | null; + + @Column({ nullable: true }) + boardId: number | null; + + @Column({ nullable: true }) + ownerId: number | null; + + constructor( + accountId: number, + scenarioType: ScenarioType, + contactId: number | null, + dealId: number | null, + boardId: number | null, + ownerId: number | null, + ) { + this.accountId = accountId; + this.scenarioType = scenarioType; + this.contactId = contactId; + this.dealId = dealId; + this.boardId = boardId; + this.ownerId = ownerId; + } + + public static fromDto(accountId: number, dto: VoximplantScenarioEntityDto): VoximplantScenarioEntity { + return new VoximplantScenarioEntity( + accountId, + dto.scenarioType, + dto.contactId, + dto.dealId, + dto.boardId, + dto.ownerId, + ); + } + + public toDto(): VoximplantScenarioEntityDto { + return new VoximplantScenarioEntityDto(this.scenarioType, this.contactId, this.dealId, this.boardId, this.ownerId); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-note.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-note.entity.ts new file mode 100644 index 0000000..c7460df --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-note.entity.ts @@ -0,0 +1,33 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { ScenarioType } from '../enums'; +import { VoximplantScenarioNoteDto } from '../dto'; + +@Entity() +export class VoximplantScenarioNote { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + scenarioType: ScenarioType; + + @Column() + noteText: string; + + constructor(accountId: number, scenarioType: ScenarioType, noteText: string) { + this.accountId = accountId; + this.scenarioType = scenarioType; + this.noteText = noteText; + } + + public static fromDto(accountId: number, dto: VoximplantScenarioNoteDto): VoximplantScenarioNote { + return new VoximplantScenarioNote(accountId, dto.scenarioType, dto.noteText); + } + + public toDto(): VoximplantScenarioNoteDto { + return new VoximplantScenarioNoteDto(this.scenarioType, this.noteText); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-task.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-task.entity.ts new file mode 100644 index 0000000..df37fe0 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/entities/voximplant-scenario-task.entity.ts @@ -0,0 +1,107 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { ScenarioType } from '../enums'; +import { VoximplantScenarioTaskDto } from '../dto'; + +@Entity() +export class VoximplantScenarioTask { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + scenarioType: ScenarioType; + + @Column({ nullable: true, default: false }) + createActivity: boolean | null; + + @Column({ nullable: true }) + activityTypeId: number | null; + + @Column({ nullable: true }) + activityText: string | null; + + @Column({ nullable: true }) + activityDuration: number | null; + + @Column({ nullable: true }) + activityOwnerId: number | null; + + @Column({ nullable: true, default: false }) + createTask: boolean | null; + + @Column({ nullable: true }) + taskTitle: string | null; + + @Column({ nullable: true }) + taskText: string | null; + + @Column({ nullable: true }) + taskDuration: number | null; + + @Column({ nullable: true }) + taskOwnerId: number | null; + + constructor( + accountId: number, + scenarioType: ScenarioType, + createActivity: boolean | null, + activityTypeId: number | null, + activityText: string | null, + activityDuration: number | null, + activityOwnerId: number | null, + createTask: boolean | null, + taskTitle: string | null, + taskText: string | null, + taskDuration: number | null, + taskOwnerId: number | null, + ) { + this.accountId = accountId; + this.scenarioType = scenarioType; + this.createActivity = createActivity; + this.activityTypeId = activityTypeId; + this.activityText = activityText; + this.activityDuration = activityDuration; + this.activityOwnerId = activityOwnerId; + this.createTask = createTask; + this.taskTitle = taskTitle; + this.taskText = taskText; + this.taskDuration = taskDuration; + this.taskOwnerId = taskOwnerId; + } + + public static fromDto(accountId: number, dto: VoximplantScenarioTaskDto): VoximplantScenarioTask { + return new VoximplantScenarioTask( + accountId, + dto.scenarioType, + dto.createActivity, + dto.activityTypeId, + dto.activityText, + dto.activityDuration, + dto.activityOwnerId, + dto.createTask, + dto.taskTitle, + dto.taskText, + dto.taskDuration, + dto.taskOwnerId, + ); + } + + public toDto(): VoximplantScenarioTaskDto { + return new VoximplantScenarioTaskDto( + this.scenarioType, + this.createActivity, + this.activityTypeId, + this.activityText, + this.activityDuration, + this.activityOwnerId, + this.createTask, + this.taskTitle, + this.taskText, + this.taskDuration, + this.taskOwnerId, + ); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/enums/index.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/enums/index.ts new file mode 100644 index 0000000..40a9bbd --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/enums/index.ts @@ -0,0 +1 @@ +export * from './scenario-type.enum'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/enums/scenario-type.enum.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/enums/scenario-type.enum.ts new file mode 100644 index 0000000..e6f70b8 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/enums/scenario-type.enum.ts @@ -0,0 +1,7 @@ +export enum ScenarioType { + INCOMING_UNKNOWN = 'incoming_unknown', + INCOMING_UNKNOWN_MISSING = 'incoming_unknown_missing', + INCOMING_KNOWN_MISSING = 'incoming_known_missing', + OUTGOING_UNKNOWN = 'outgoing_unknown', + OUTGOING_UNANSWERED = 'outgoing_unanswered', +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/index.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/index.ts new file mode 100644 index 0000000..184b3a1 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './types'; +export * from './voximplant-scenario.controller'; +export * from './voximplant-scenario.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/types/index.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/types/index.ts new file mode 100644 index 0000000..e4ade5a --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/types/index.ts @@ -0,0 +1 @@ +export * from './voximplant-scenarios'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/types/voximplant-scenarios.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/types/voximplant-scenarios.ts new file mode 100644 index 0000000..f24a52a --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/types/voximplant-scenarios.ts @@ -0,0 +1,34 @@ +import { VoximplantScenariosDto } from '../dto'; +import { VoximplantScenarioEntity, VoximplantScenarioNote, VoximplantScenarioTask } from '../entities'; + +export class VoximplantScenarios { + entities: VoximplantScenarioEntity[] | null; + notes: VoximplantScenarioNote[] | null; + tasks: VoximplantScenarioTask[] | null; + + constructor( + entities: VoximplantScenarioEntity[] | null, + notes: VoximplantScenarioNote[] | null, + tasks: VoximplantScenarioTask[] | null, + ) { + this.entities = entities; + this.notes = notes; + this.tasks = tasks; + } + + public static fromDto(accountId: number, dto: VoximplantScenariosDto): VoximplantScenarios { + return new VoximplantScenarios( + dto.entities?.map((entity) => VoximplantScenarioEntity.fromDto(accountId, entity)) ?? null, + dto.notes?.map((note) => VoximplantScenarioNote.fromDto(accountId, note)) ?? null, + dto.tasks?.map((task) => VoximplantScenarioTask.fromDto(accountId, task)) ?? null, + ); + } + + public toDto(): VoximplantScenariosDto { + return new VoximplantScenariosDto( + this.entities?.map((entity) => entity.toDto()) ?? [], + this.notes?.map((note) => note.toDto()) ?? [], + this.tasks?.map((task) => task.toDto()) ?? [], + ); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.controller.ts new file mode 100644 index 0000000..74cf9ec --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.controller.ts @@ -0,0 +1,49 @@ +import { Body, Controller, Get, Post, Put, Param, ParseEnumPipe } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized, UserAccess } from '@/modules/iam/common'; + +import { CallDirection } from '../common'; +import { VoximplantScenariosDto } from './dto'; +import { VoximplantScenarioService } from './voximplant-scenario.service'; + +@ApiTags('telephony/voximplant/scenarios') +@Controller('scenarios') +@JwtAuthorized() +@TransformToDto() +export class VoximplantScenarioController { + constructor(private service: VoximplantScenarioService) {} + + @ApiCreatedResponse({ description: 'Create voximplant scenarios', type: VoximplantScenariosDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: VoximplantScenariosDto) { + return this.service.upsert(accountId, dto); + } + + @ApiCreatedResponse({ description: 'Get voximplant scenarios', type: VoximplantScenariosDto }) + @Get() + public async findOne(@CurrentAuth() { accountId }: AuthData) { + return this.service.findOne(accountId); + } + + @ApiCreatedResponse({ + description: 'Check contact creation voximplant scenario is manual', + type: Boolean, + }) + @Get('check-manual/:callDirection') + public async checkContactsCreationScenarioIsManual( + @CurrentAuth() { accountId }: AuthData, + @Param('callDirection', new ParseEnumPipe(CallDirection)) callDirection: CallDirection, + ): Promise { + return this.service.checkContactsCreationScenarioIsManual(accountId, callDirection); + } + + @ApiCreatedResponse({ description: 'Update voximplant account' }) + @Put() + @UserAccess({ adminOnly: true }) + public async update(@CurrentAuth() { accountId }: AuthData, @Body() dto: VoximplantScenariosDto) { + return this.service.upsert(accountId, dto); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.service.ts b/backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.service.ts new file mode 100644 index 0000000..6f46393 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-scenario/voximplant-scenario.service.ts @@ -0,0 +1,243 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; + +import { Entity } from '@/CRM/Model/Entity/Entity'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; +import { NoteService } from '@/CRM/note/note.service'; +import { ActivityService } from '@/CRM/activity/activity.service'; +import { TaskService } from '@/CRM/task/task.service'; + +import { CallStatus, CallDirection } from '../common'; +import { VoximplantCall } from '../voximplant-call'; + +import { VoximplantScenariosDto } from './dto'; +import { VoximplantScenarioEntity, VoximplantScenarioNote, VoximplantScenarioTask } from './entities'; +import { ScenarioType } from './enums'; +import { VoximplantScenarios } from './types'; + +interface RunScenarioValues { + phone?: string; + entityId?: number; + userId?: number; +} + +@Injectable() +export class VoximplantScenarioService { + constructor( + @InjectRepository(VoximplantScenarioEntity) + private readonly repositoryEntity: Repository, + @InjectRepository(VoximplantScenarioNote) + private readonly repositoryNote: Repository, + @InjectRepository(VoximplantScenarioTask) + private readonly repositoryTask: Repository, + private readonly userService: UserService, + private readonly entityService: EntityService, + private readonly activityService: ActivityService, + private readonly taskService: TaskService, + private readonly noteService: NoteService, + ) {} + + async upsert(accountId: number, dto: VoximplantScenariosDto): Promise { + await this.repositoryEntity.delete({ accountId }); + await this.repositoryNote.delete({ accountId }); + await this.repositoryTask.delete({ accountId }); + + const scenarios = VoximplantScenarios.fromDto(accountId, dto); + const entities = await this.repositoryEntity.save(scenarios.entities); + const notes = await this.repositoryNote.save(scenarios.notes); + const tasks = await this.repositoryTask.save(scenarios.tasks); + + return new VoximplantScenarios(entities, notes, tasks); + } + + async findOne(accountId: number): Promise { + const entities = await this.repositoryEntity.findBy({ accountId }); + const notes = await this.repositoryNote.findBy({ accountId }); + const tasks = await this.repositoryTask.findBy({ accountId }); + + return new VoximplantScenarios(entities, notes, tasks); + } + + async processCall(accountId: number, viCall: VoximplantCall): Promise<{ entities: Entity[] } | null> { + if (viCall.status === CallStatus.ACCEPTED && !viCall.entityId) { + return this.runScenario( + accountId, + viCall.direction === CallDirection.INCOMING ? ScenarioType.INCOMING_UNKNOWN : ScenarioType.OUTGOING_UNKNOWN, + { phone: viCall.phoneNumber, userId: viCall.userId }, + ); + } + + if (viCall.status === CallStatus.MISSED) { + if (viCall.entityId) { + return this.runScenario( + accountId, + viCall.direction === CallDirection.INCOMING + ? ScenarioType.INCOMING_KNOWN_MISSING + : ScenarioType.OUTGOING_UNANSWERED, + { phone: viCall.phoneNumber, userId: viCall.userId, entityId: viCall.entityId }, + ); + } else if (viCall.direction === CallDirection.INCOMING) { + return this.runScenario(accountId, ScenarioType.INCOMING_UNKNOWN_MISSING, { + phone: viCall.phoneNumber, + }); + } + } + + return null; + } + + async checkContactsCreationScenarioIsManual(accountId: number, callDirection: CallDirection): Promise { + const scenarioType = + callDirection === CallDirection.INCOMING ? ScenarioType.INCOMING_UNKNOWN : ScenarioType.OUTGOING_UNKNOWN; + return !(await this.repositoryEntity.findOneBy({ accountId, scenarioType })); + } + + private async runScenario( + accountId: number, + type: ScenarioType, + values: RunScenarioValues, + ): Promise<{ entities: Entity[] }> { + const scenarios = await this.findOne(accountId); + const entities: Entity[] = []; + if (scenarios?.entities) { + const result = await this.runEntitiesScenarios( + accountId, + scenarios.entities.filter((s) => s.scenarioType === type), + values, + ); + if (result.entities?.length > 0) { + entities.push(...result.entities); + } + } + if (!values.entityId && entities.length > 0) { + const [lastEntity] = entities.slice(-1); // assuming to link task/activity with the last created entity + values.entityId = lastEntity.id; + } + if (scenarios?.tasks) { + await this.runTasksScenarios( + accountId, + scenarios.tasks.filter((s) => s.scenarioType === type), + values, + ); + } + if (scenarios?.notes) { + await this.runNotesScenarios( + accountId, + scenarios.notes.filter((s) => s.scenarioType === type), + values, + ); + } + + return { entities }; + } + + private async runEntitiesScenarios( + accountId: number, + scenarios: VoximplantScenarioEntity[], + values: RunScenarioValues, + ): Promise<{ entities: Entity[] }> { + if (!values.phone) return { entities: [] }; + + const entities: Entity[] = []; + for (const scenario of scenarios) { + const ownerId = values.userId ?? scenario.ownerId; + if (ownerId) { + const owner = await this.userService.findOne({ accountId, id: ownerId }); + const lead = scenario.dealId + ? { + entityTypeId: scenario.dealId, + boardId: scenario.boardId, + fieldValues: this.getFieldValues(values), + } + : null; + const contact = scenario.contactId + ? { + entityTypeId: scenario.contactId, + fieldValues: this.getFieldValues(values), + linkedEntities: lead ? [lead] : undefined, + } + : null; + if (contact || lead) { + const created = await this.entityService.createSimple({ + accountId, + user: owner, + dto: contact ?? lead, + options: { checkDuplicate: true }, + }); + entities.push(...created); + } + } + } + return { entities }; + } + + private getFieldValues(values: RunScenarioValues) { + return values.phone + ? [ + { + fieldType: FieldType.Phone, + value: values.phone.startsWith('+') ? values.phone : '+' + values.phone, + }, + ] + : undefined; + } + + private async runTasksScenarios(accountId: number, scenarios: VoximplantScenarioTask[], values: RunScenarioValues) { + if (!values.entityId) return; + + const now = DateUtil.now(); + for (const scenario of scenarios) { + if (scenario.createActivity && scenario.activityOwnerId) { + const owner = await this.userService.findOne({ accountId, id: scenario.activityOwnerId }); + const endDate = DateUtil.add(now, { seconds: scenario.activityDuration }); + await this.activityService.create(accountId, owner, { + responsibleUserId: owner.id, + startDate: now.toISOString(), + endDate: endDate.toISOString(), + text: scenario.activityText, + activityTypeId: scenario.activityTypeId, + entityId: values.entityId, + }); + } + if (scenario.createTask) { + const owner = await this.userService.findOne({ accountId, id: scenario.taskOwnerId }); + const endDate = DateUtil.add(now, { seconds: scenario.taskDuration }); + await this.taskService.create({ + accountId, + user: owner, + dto: { + responsibleUserId: owner.id, + startDate: now.toISOString(), + endDate: endDate.toISOString(), + text: scenario.taskText, + title: scenario.taskTitle, + plannedTime: scenario.taskDuration, + entityId: values.entityId, + settingsId: null, + boardId: null, + stageId: null, + }, + }); + } + } + } + + private async runNotesScenarios(accountId: number, scenarios: VoximplantScenarioNote[], values: RunScenarioValues) { + if (!values.entityId || !values.userId) return; + + for (const scenario of scenarios) { + await this.noteService.create({ + accountId, + userId: values.userId, + entityId: values.entityId, + dto: { text: scenario.noteText }, + }); + } + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/dto/create-voximplant-sip.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/create-voximplant-sip.dto.ts new file mode 100644 index 0000000..fdf7512 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/create-voximplant-sip.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +import { VoximplantSIPDto } from './voximplant-sip.dto'; + +export class CreateVoximplantSIPDto extends PickType(VoximplantSIPDto, ['type', 'name', 'userIds'] as const) { + @ApiProperty() + @IsString() + proxy: string; + + @ApiProperty() + @IsString() + sipUsername: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + authUser?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + password?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + outboundProxy?: string; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/index.ts new file mode 100644 index 0000000..126950d --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/index.ts @@ -0,0 +1,5 @@ +export * from './create-voximplant-sip.dto'; +export * from './update-voximplant-sip.dto'; +export * from './voximplant-sip-filter.dto'; +export * from './voximplant-sip-registration.dto'; +export * from './voximplant-sip.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/dto/update-voximplant-sip.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/update-voximplant-sip.dto.ts new file mode 100644 index 0000000..cb97b30 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/update-voximplant-sip.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from '@nestjs/swagger'; + +import { CreateVoximplantSIPDto } from './create-voximplant-sip.dto'; + +export class UpdateVoximplantSIPDto extends PartialType(CreateVoximplantSIPDto) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-filter.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-filter.dto.ts new file mode 100644 index 0000000..8476894 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-filter.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class VoximplantSipFilterDto { + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + accessibleUserId?: number; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-registration.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-registration.dto.ts new file mode 100644 index 0000000..def8d93 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip-registration.dto.ts @@ -0,0 +1,85 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class VoximplantSIPRegistrationDto { + @ApiProperty({ description: 'The SIP registration ID' }) + @IsNumber() + sipRegistrationId: number; + + @ApiProperty({ description: 'The SIP username' }) + @IsString() + sipUsername: string; + + @ApiProperty({ description: 'The SIP proxy' }) + @IsString() + proxy: string; + + @ApiProperty({ description: 'The last time updated' }) + @IsNumber() + lastUpdated: number; + + @ApiPropertyOptional({ description: 'The SIP authentications user' }) + @IsOptional() + @IsString() + authUser?: string; + + @ApiPropertyOptional({ description: 'The SIP outbound proxy' }) + @IsOptional() + @IsString() + outboundProxy?: string; + + @ApiPropertyOptional({ description: 'The successful SIP registration' }) + @IsOptional() + @IsBoolean() + successful?: boolean; + + @ApiPropertyOptional({ description: 'The status code from a SIP registration' }) + @IsOptional() + @IsNumber() + statusCode?: number; + + @ApiPropertyOptional({ description: 'The error message from a SIP registration' }) + @IsOptional() + @IsString() + errorMessage?: string; + + @ApiProperty({ description: 'The subscription deactivation flag' }) + @IsBoolean() + deactivated: boolean; + + @ApiProperty({ description: 'The next subscription renewal date' }) + @IsString() + nextSubscriptionRenewal: Date; + + @ApiProperty({ description: 'The purchase date in 24-h format: YYYY-MM-DD HH:mm:ss' }) + @IsString() + purchaseDate: Date; + + @ApiProperty({ description: 'The subscription monthly charge' }) + @IsString() + subscriptionPrice: string; + + @ApiProperty({ description: 'SIP registration is persistent' }) + @IsBoolean() + isPersistent: boolean; + + @ApiPropertyOptional({ description: 'The id of the bound user' }) + @IsOptional() + @IsNumber() + userId?: number; + + @ApiPropertyOptional({ description: 'The name of the bound user' }) + @IsOptional() + @IsString() + userName?: string; + + @ApiPropertyOptional({ description: 'The id of the bound rule' }) + @IsOptional() + @IsNumber() + ruleId?: number; + + @ApiPropertyOptional({ description: 'The name of the bound rule' }) + @IsOptional() + @IsString() + ruleName?: string; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip.dto.ts new file mode 100644 index 0000000..4d97f97 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/dto/voximplant-sip.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { PbxProviderType } from '../enums'; +import { VoximplantSIPRegistrationDto } from './voximplant-sip-registration.dto'; + +export class VoximplantSIPDto { + @ApiProperty() + @IsNumber() + id: number; + + @ApiProperty() + @IsNumber() + externalId: number; + + @ApiProperty({ enum: PbxProviderType }) + @IsEnum(PbxProviderType) + type: PbxProviderType; + + @ApiProperty() + @IsString() + name: string; + + @ApiPropertyOptional({ type: [Number], nullable: true }) + @IsOptional() + @IsNumber({}, { each: true }) + userIds?: number[] | null; + + @ApiPropertyOptional({ type: VoximplantSIPRegistrationDto }) + registration?: VoximplantSIPRegistrationDto; + + constructor({ id, externalId, type, name, userIds, registration }: VoximplantSIPDto) { + this.id = id; + this.externalId = externalId; + this.type = type; + this.name = name; + this.userIds = userIds; + this.registration = registration; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/entities/index.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/entities/index.ts new file mode 100644 index 0000000..506d5d7 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/entities/index.ts @@ -0,0 +1,2 @@ +export * from './voximplant-sip-user.entity'; +export * from './voximplant-sip.entity'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip-user.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip-user.entity.ts new file mode 100644 index 0000000..d4dabfb --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip-user.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class VoximplantSipUser { + @PrimaryColumn() + sipId: number; + + @PrimaryColumn() + userId: number; + + @Column() + accountId: number; + + constructor(accountId: number, sipId: number, userId: number) { + this.accountId = accountId; + this.sipId = sipId; + this.userId = userId; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip.entity.ts new file mode 100644 index 0000000..575d8f6 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/entities/voximplant-sip.entity.ts @@ -0,0 +1,58 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { PbxProviderType } from '../enums'; +import { VoximplantSIPDto } from '../dto'; +import { VoximplantSIPRegistration } from '../types'; +import { VoximplantSipUser } from './voximplant-sip-user.entity'; + +@Entity() +export class VoximplantSip { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + externalId: number; + + @Column() + type: PbxProviderType; + + @Column() + name: string; + + constructor(accountId: number, externalId: number, type: PbxProviderType, name: string) { + this.accountId = accountId; + this.externalId = externalId; + this.type = type; + this.name = name; + } + + private _users: VoximplantSipUser[] | null | undefined; + public get users(): VoximplantSipUser[] | null | undefined { + return this._users; + } + public set users(value: VoximplantSipUser[] | null | undefined) { + this._users = value; + } + + private _registration: VoximplantSIPRegistration | null; + public get registration(): VoximplantSIPRegistration | null { + return this._registration; + } + public set registration(value: VoximplantSIPRegistration | null) { + this._registration = value; + } + + public toDto(): VoximplantSIPDto { + return new VoximplantSIPDto({ + id: this.id, + externalId: this.externalId, + type: this.type, + name: this.name, + userIds: this._users?.map((u) => u.userId), + registration: this.registration, + }); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/enums/index.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/enums/index.ts new file mode 100644 index 0000000..74c9c14 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/enums/index.ts @@ -0,0 +1 @@ +export * from './pbx-provider-type.enum'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/enums/pbx-provider-type.enum.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/enums/pbx-provider-type.enum.ts new file mode 100644 index 0000000..c111896 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/enums/pbx-provider-type.enum.ts @@ -0,0 +1,12 @@ +export enum PbxProviderType { + Uis = 'uis', + Zadarma = 'zadarma', + MangoOffice = 'mango_office', + Beeline = 'beeline', + Mts = 'mts', + Mgts = 'mgts', + Tele2 = 'tele2', + Megafon = 'megafon', + Rostelecom = 'rostelecom', + Unknown = 'unknown', +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/index.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/index.ts new file mode 100644 index 0000000..a0988ba --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/index.ts @@ -0,0 +1,6 @@ +export * from './dto'; +export * from './entities'; +export * from './enums'; +export * from './services'; +export * from './types'; +export * from './voximplant-sip.controller'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/services/index.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/services/index.ts new file mode 100644 index 0000000..0094935 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/services/index.ts @@ -0,0 +1,2 @@ +export * from './voximplant-sip-user.service'; +export * from './voximplant-sip.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip-user.service.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip-user.service.ts new file mode 100644 index 0000000..8981370 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip-user.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { VoximplantSipUser } from '../entities'; + +interface FindFilter { + sipId?: number; +} + +@Injectable() +export class VoximplantSipUserService { + constructor( + @InjectRepository(VoximplantSipUser) + private readonly repository: Repository, + ) {} + + public async create(accountId: number, sipId: number, userIds: number[]): Promise { + return await this.repository.save(userIds.map((userId) => new VoximplantSipUser(accountId, sipId, userId))); + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return await this.createFindQb(accountId, filter).getOne(); + } + public async findMany(accountId: number, filter?: FindFilter): Promise { + return await this.createFindQb(accountId, filter).getMany(); + } + + public async update( + accountId: number, + sipId: number, + currentUsers: VoximplantSipUser[], + userIds: number[], + ): Promise { + const addUsers = userIds.filter((id) => !currentUsers.some((user) => user.userId === id)); + const removeUsers = currentUsers.filter((user) => !userIds.some((id) => id === user.userId)); + + currentUsers.push(...(await this.create(accountId, sipId, addUsers))); + + if (removeUsers.length) { + await this.repository.remove(removeUsers); + } + + return currentUsers.filter((user) => !removeUsers.some((u) => u.userId === user.userId)); + } + + public async removeUser(accountId: number, userId: number) { + await this.repository.delete({ accountId, userId }); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('visu').where('visu.account_id = :accountId', { accountId }); + + if (filter?.sipId) { + qb.andWhere('visu.sip_id = :sipId', { sipId: filter.sipId }); + } + + return qb; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip.service.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip.service.ts new file mode 100644 index 0000000..18c32b1 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/services/voximplant-sip.service.ts @@ -0,0 +1,192 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { NotFoundError, ObjectUtil } from '@/common'; + +import { VoximplantError } from '../../common'; +import { VoximplantAccountService } from '../../voximplant-account/voximplant-account.service'; + +import { CreateVoximplantSIPDto, UpdateVoximplantSIPDto } from '../dto'; +import { VoximplantSip } from '../entities'; +import { ExpandableField, VoximplantSIPRegistration } from '../types'; +import { VoximplantSipUserService } from './voximplant-sip-user.service'; + +interface FindFilter { + sipId?: number; + externalId?: number; + accessibleUserId?: number; +} + +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class VoximplantSipService { + private readonly logger = new Logger(VoximplantSipService.name); + constructor( + @InjectRepository(VoximplantSip) + private readonly repository: Repository, + private readonly viAccountService: VoximplantAccountService, + private readonly viSipUserService: VoximplantSipUserService, + ) {} + public async create(accountId: number, dto: CreateVoximplantSIPDto): Promise { + const viSIPRegistration = await this.createSIPRegistration(accountId, dto); + + if (viSIPRegistration) { + const viSip = await this.repository.save(new VoximplantSip(accountId, viSIPRegistration, dto.type, dto.name)); + + if (dto.userIds?.length) { + await this.viSipUserService.create(accountId, viSip.id, dto.userIds); + } + + return this.findOne(accountId, { sipId: viSip.id }, { expand: ['users', 'registration'] }); + } + + throw new VoximplantError({ message: 'Create SIP registration error' }); + } + + public async findOne(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const viSip = await this.createFindQb(accountId, filter).getOne(); + + return viSip && options?.expand ? await this.expandOne(viSip, options.expand) : viSip; + } + public async findMany(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const viSips = await this.createFindQb(accountId, filter).orderBy('vis.id', 'ASC').getMany(); + + return viSips && options?.expand ? await this.expandMany(viSips, options.expand) : viSips; + } + + public async update(accountId: number, sipId: number, dto: UpdateVoximplantSIPDto): Promise { + const viSip = await this.findOne(accountId, { sipId }, { expand: ['users'] }); + if (!viSip) { + throw NotFoundError.withId(VoximplantSip, sipId); + } + + viSip.type = dto.type ?? viSip.type; + viSip.name = dto.name ?? viSip.name; + this.repository.save(viSip); + + if (dto.userIds) { + await this.viSipUserService.update(accountId, viSip.id, viSip.users, dto.userIds); + } + + await this.updateSIPRegistration(accountId, viSip.externalId, dto); + + return this.findOne(accountId, { sipId }, { expand: ['users', 'registration'] }); + } + + public async delete(accountId: number, sipId: number): Promise { + const viSip = await this.repository.findOne({ where: { accountId, id: sipId } }); + if (!viSip) { + throw NotFoundError.withId(VoximplantSip, sipId); + } + + if (await this.deleteSIPRegistration(accountId, viSip.externalId)) { + await this.repository.delete({ accountId, id: sipId }); + } + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('vis').where('vis.account_id = :accountId', { accountId }); + + if (filter?.sipId) { + qb.andWhere('vis.id = :id', { id: filter.sipId }); + } + if (filter?.externalId) { + qb.andWhere('vis.external_id = :externalId', { externalId: filter.externalId }); + } + if (filter?.accessibleUserId) { + qb.leftJoin('voximplant_sip_user', 'visu', 'visu.sip_id = vis.id').andWhere( + new Brackets((qb1) => + qb1 + .where('visu.user_id = :accessibleUserId', { accessibleUserId: filter.accessibleUserId }) + .orWhere('visu.user_id is NULL'), + ), + ); + } + + return qb; + } + + private async expandOne(viSip: VoximplantSip, expand: ExpandableField[]): Promise { + if (expand.includes('users')) { + viSip.users = await this.viSipUserService.findMany(viSip.accountId, { sipId: viSip.id }); + } + if (expand.includes('registration')) { + viSip.registration = await this.getSIPRegistration(viSip.accountId, viSip.externalId); + } + return viSip; + } + private async expandMany(viSips: VoximplantSip[], expand: ExpandableField[]): Promise { + return await Promise.all(viSips.map((viSip) => this.expandOne(viSip, expand))); + } + + private async createSIPRegistration(accountId: number, dto: CreateVoximplantSIPDto): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + + const viResponse = await client.SIPRegistration.createSipRegistration( + ObjectUtil.assign(appParam, { ...dto, userIds: undefined }), + ); + + if (!viResponse.result) { + this.logger.error(`Create SIP registration error: ${JSON.stringify(viResponse)}`); + return null; + } + + return viResponse.sipRegistrationId; + } + + private async getSIPRegistration( + accountId: number, + sipRegistrationId: number, + ): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + + const viResponse = await client.SIPRegistration.getSipRegistrations({ + ...appParam, + sipRegistrationId, + ruleId: [], + ruleName: '', + userId: [], + userName: '', + }); + if (!viResponse.result) { + this.logger.error(`Get SIP registration error: ${JSON.stringify(viResponse)}`); + return null; + } + + return viResponse.result[0]; + } + + private async updateSIPRegistration( + accountId: number, + sipRegistrationId: number, + dto: UpdateVoximplantSIPDto, + ): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + + const viResponse = await client.SIPRegistration.updateSipRegistration( + ObjectUtil.assign({ ...appParam, sipRegistrationId }, { ...dto, userIds: undefined }), + ); + if (!viResponse.result) { + this.logger.error(`Create SIP registration error: ${JSON.stringify(viResponse)}`); + return null; + } + + return sipRegistrationId; + } + + private async deleteSIPRegistration(accountId: number, sipRegistrationId: number): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + + const viResponse = await client.SIPRegistration.deleteSipRegistration({ ...appParam, sipRegistrationId }); + if (!viResponse.result) { + this.logger.error(`Create SIP registration error: ${JSON.stringify(viResponse)}`); + return null; + } + + return sipRegistrationId; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/types/expandable-field.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/types/expandable-field.ts new file mode 100644 index 0000000..fc21342 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/types/expandable-field.ts @@ -0,0 +1 @@ +export type ExpandableField = 'users' | 'registration'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/types/index.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/types/index.ts new file mode 100644 index 0000000..7708040 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/types/index.ts @@ -0,0 +1,2 @@ +export * from './expandable-field'; +export * from './voximplant-sip-registration'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/types/voximplant-sip-registration.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/types/voximplant-sip-registration.ts new file mode 100644 index 0000000..524aaae --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/types/voximplant-sip-registration.ts @@ -0,0 +1,82 @@ +export interface VoximplantSIPRegistration { + /** + * The SIP registration ID + */ + sipRegistrationId: number; + /** + * The user name from sip proxy + */ + sipUsername: string; + /** + * The sip proxy + */ + proxy: string; + /** + * The last time updated + */ + lastUpdated: number; + /** + * The SIP authentications user + */ + authUser?: string; + /** + * The outbound proxy + */ + outboundProxy?: string; + /** + * The successful SIP registration + */ + successful?: boolean; + /** + * The status code from a SIP registration + */ + statusCode?: number; + /** + * The error message from a SIP registration + */ + errorMessage?: string; + /** + * The subscription deactivation flag. The SIP registration is frozen if true + */ + deactivated: boolean; + /** + * The next subscription renewal date in format: YYYY-MM-DD + */ + nextSubscriptionRenewal: Date; + /** + * The purchase date in 24-h format: YYYY-MM-DD HH:mm:ss + */ + purchaseDate: Date; + /** + * The subscription monthly charge + */ + subscriptionPrice: string; + /** + * SIP registration is persistent. Set false to activate it only on the user login + */ + isPersistent: boolean; + /** + * The id of the bound user + */ + userId?: number; + /** + * The name of the bound user + */ + userName?: string; + /** + * The id of the bound application + */ + applicationId?: number; + /** + * The name of the bound application + */ + applicationName?: string; + /** + * The id of the bound rule + */ + ruleId?: number; + /** + * The name of the bound rule + */ + ruleName?: string; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-sip/voximplant-sip.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-sip/voximplant-sip.controller.ts new file mode 100644 index 0000000..e72e862 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-sip/voximplant-sip.controller.ts @@ -0,0 +1,93 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { ExpandQuery, TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized, UserAccess } from '@/modules/iam/common'; + +import { CreateVoximplantSIPDto, UpdateVoximplantSIPDto, VoximplantSIPDto, VoximplantSipFilterDto } from './dto'; +import { ExpandableField } from './types'; +import { VoximplantSipService } from './services'; + +@ApiTags('telephony/voximplant/sip') +@Controller('sip') +@JwtAuthorized() +@TransformToDto() +export class VoximplantSipController { + constructor(private readonly service: VoximplantSipService) {} + + @ApiCreatedResponse({ description: 'Create voximplant SIP registration', type: VoximplantSIPDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateVoximplantSIPDto) { + return this.service.create(accountId, dto); + } + + @ApiOkResponse({ description: 'Get voximplant SIP registrations', type: [VoximplantSIPDto] }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: users,registration.', + }) + @Get() + public async getMany( + @CurrentAuth() { accountId }: AuthData, + @Query() filter: VoximplantSipFilterDto, + @Query() expand?: ExpandQuery, + ) { + return this.service.findMany(accountId, filter, { expand: expand.fields }); + } + + @ApiOkResponse({ description: 'Get voximplant SIP registration by external Id', type: VoximplantSIPDto }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: users,registration.', + }) + @Get('external/:externalId') + public async getOneByExternalId( + @CurrentAuth() { accountId }: AuthData, + @Param('externalId', ParseIntPipe) externalId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findOne(accountId, { externalId }, { expand: expand.fields }); + } + + @ApiOkResponse({ description: 'Get voximplant SIP registration', type: VoximplantSIPDto }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields. Values: users,registration.', + }) + @Get(':sipId') + public async getOne( + @CurrentAuth() { accountId }: AuthData, + @Param('sipId', ParseIntPipe) sipId: number, + @Query() expand?: ExpandQuery, + ) { + return this.service.findOne(accountId, { sipId }, { expand: expand.fields }); + } + + @ApiOkResponse({ description: 'Update voximplant SIP registration', type: VoximplantSIPDto }) + @Patch(':sipId') + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('sipId', ParseIntPipe) sipId: number, + @Body() dto: UpdateVoximplantSIPDto, + ) { + return this.service.update(accountId, sipId, dto); + } + + @ApiOkResponse({ description: 'Delete voximplant SIP registration' }) + @Delete(':sipId') + @UserAccess({ adminOnly: true }) + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('sipId', ParseIntPipe) sipId: number) { + return this.service.delete(accountId, sipId); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-user.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-user.dto.ts new file mode 100644 index 0000000..d811e8c --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-user.dto.ts @@ -0,0 +1,4 @@ +import { PickType } from '@nestjs/swagger'; +import { VoximplantUserDto } from './voximplant-user.dto'; + +export class CreateVoximplantUserDto extends PickType(VoximplantUserDto, ['isActive'] as const) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-users-batch.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-users-batch.dto.ts new file mode 100644 index 0000000..90905e2 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/create-voximplant-users-batch.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty, PickType } from '@nestjs/swagger'; +import { IsNumber } from 'class-validator'; +import { VoximplantUserDto } from './voximplant-user.dto'; + +export class CreateVoximplantUsersBatchDto extends PickType(VoximplantUserDto, ['isActive'] as const) { + @ApiProperty({ type: Number, isArray: true }) + @IsNumber({}, { each: true }) + userIds: number[]; +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/index.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/index.ts new file mode 100644 index 0000000..4abb6ee --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/index.ts @@ -0,0 +1,6 @@ +export * from './create-voximplant-user.dto'; +export * from './create-voximplant-users-batch.dto'; +export * from './update-voximplant-user.dto'; +export * from './users-queue.dto'; +export * from './voximplant-user-sip-data.dto'; +export * from './voximplant-user.dto'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/update-voximplant-user.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/update-voximplant-user.dto.ts new file mode 100644 index 0000000..9030f04 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/update-voximplant-user.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateVoximplantUserDto } from './create-voximplant-user.dto'; + +export class UpdateVoximplantUserDto extends PartialType(CreateVoximplantUserDto) {} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/users-queue.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/users-queue.dto.ts new file mode 100644 index 0000000..de350fb --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/users-queue.dto.ts @@ -0,0 +1,31 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +export class UsersQueueDto { + @ApiPropertyOptional({ nullable: true, type: [String] }) + @IsOptional() + @IsArray() + users?: string[] | null; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + entityName?: string | null; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + entityId?: number | null; + + @ApiPropertyOptional() + @IsOptional() + @IsNumber() + entityTypeId?: number | null; + + constructor({ users, entityName, entityId, entityTypeId }: UsersQueueDto) { + this.users = users; + this.entityName = entityName; + this.entityId = entityId; + this.entityTypeId = entityTypeId; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user-sip-data.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user-sip-data.dto.ts new file mode 100644 index 0000000..160c19d --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user-sip-data.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class VoximplantUserSIPDataDto { + @ApiProperty() + @IsString() + userName: string; + + @ApiProperty() + @IsString() + domain: string; + + @ApiProperty() + @IsString() + password: string; + + constructor({ userName, domain, password }: VoximplantUserSIPDataDto) { + this.userName = userName; + this.domain = domain; + this.password = password; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user.dto.ts b/backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user.dto.ts new file mode 100644 index 0000000..43f6045 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/dto/voximplant-user.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNumber, IsString } from 'class-validator'; + +export class VoximplantUserDto { + @ApiProperty() + @IsNumber() + userId: number; + + @ApiProperty() + @IsString() + userName: string; + + @ApiProperty() + @IsBoolean() + isActive: boolean; + + constructor({ userId, userName, isActive }: VoximplantUserDto) { + this.userId = userId; + this.userName = userName; + this.isActive = isActive; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/entities/index.ts b/backend/src/modules/telephony/voximplant/voximplant-user/entities/index.ts new file mode 100644 index 0000000..ede7f0a --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/entities/index.ts @@ -0,0 +1 @@ +export * from './voximplant-user.entity'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/entities/voximplant-user.entity.ts b/backend/src/modules/telephony/voximplant/voximplant-user/entities/voximplant-user.entity.ts new file mode 100644 index 0000000..5ff2317 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/entities/voximplant-user.entity.ts @@ -0,0 +1,52 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { UpdateVoximplantUserDto, VoximplantUserDto } from '../dto'; + +@Entity() +export class VoximplantUser { + @PrimaryColumn() + userId: number; + + @Column() + externalId: number; + + @Column() + userName: string; + + @Column() + password: string; + + @Column({ default: true }) + isActive: boolean; + + @Column() + accountId: number; + + constructor( + accountId: number, + userId: number, + externalId: number, + userName: string, + password: string, + isActive = true, + ) { + this.accountId = accountId; + this.userId = userId; + this.externalId = externalId; + this.userName = userName; + this.password = password; + this.isActive = isActive; + } + + public update(dto: UpdateVoximplantUserDto): VoximplantUser { + if (dto.isActive !== undefined) { + this.isActive = dto.isActive; + } + + return this; + } + + public toDto(): VoximplantUserDto { + return new VoximplantUserDto(this); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/index.ts b/backend/src/modules/telephony/voximplant/voximplant-user/index.ts new file mode 100644 index 0000000..0bd944d --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './voximplant-user-public.controller'; +export * from './voximplant-user.controller'; +export * from './voximplant-user.service'; diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user-public.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user-public.controller.ts new file mode 100644 index 0000000..3727c9e --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user-public.controller.ts @@ -0,0 +1,21 @@ +import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; + +import { UsersQueueDto } from './dto'; +import { VoximplantUserService } from './voximplant-user.service'; + +@ApiTags('telephony/voximplant/integration') +@Controller('integration/:applicationId/users') +export class VoximplantUserPublicController { + constructor(private service: VoximplantUserService) {} + + @ApiCreatedResponse({ description: 'Get users queue and contact info', type: UsersQueueDto }) + @Get() + public async getUsersQueue( + @Param('applicationId', ParseIntPipe) applicationId: number, + @Query('phone') phone: string, + @Query('viPhoneNumber') viPhoneNumber?: string | null, + ) { + return this.service.getUsersQueue(applicationId, { phone, viPhoneNumber }); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.controller.ts b/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.controller.ts new file mode 100644 index 0000000..51a1fc7 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.controller.ts @@ -0,0 +1,87 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; + +import { TransformToDto } from '@/common'; +import { AuthData, CurrentAuth, JwtAuthorized } from '@/modules/iam/common'; + +import { + VoximplantUserDto, + CreateVoximplantUserDto, + CreateVoximplantUsersBatchDto, + VoximplantUserSIPDataDto, + UpdateVoximplantUserDto, +} from './dto'; +import { VoximplantUserService } from './voximplant-user.service'; + +@ApiTags('telephony/voximplant/users') +@Controller('users') +@JwtAuthorized() +@TransformToDto() +export class VoximplantUserController { + constructor(private readonly service: VoximplantUserService) {} + + @ApiCreatedResponse({ description: 'Create voximplant user', type: VoximplantUserDto }) + @Post(':userId') + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: CreateVoximplantUserDto, + ) { + return this.service.create(accountId, userId, dto); + } + + @ApiCreatedResponse({ description: 'Batch create voximplant users', type: VoximplantUserDto, isArray: true }) + @Post('batch/create') + public async createBatch(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateVoximplantUsersBatchDto) { + return this.service.createBatch(accountId, dto); + } + + @ApiOkResponse({ description: 'Get linked voximplant users', type: [VoximplantUserDto] }) + @Get() + public async getMany( + @CurrentAuth() { accountId }: AuthData, + @Query('accessiblePhoneNumber') accessiblePhoneNumber?: string, + ) { + return this.service.findMany(accountId, { accessiblePhoneNumber }); + } + + @ApiOkResponse({ description: 'Get voximplant user', type: VoximplantUserDto }) + @Get(':userId') + public async getOne(@CurrentAuth() { accountId }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.findOne(accountId, { userId }); + } + + @ApiOkResponse({ description: 'Voximplant user name', type: String }) + @Get('my/username') + public async getUserName(@CurrentAuth() { accountId, userId }: AuthData) { + return this.service.getUserName(accountId, userId); + } + + @ApiOkResponse({ description: 'Voximplant user login token', type: String }) + @Get('my/login-token') + public async getLoginToken(@CurrentAuth() { accountId, userId }: AuthData, @Query('key') key: string) { + return this.service.getLoginToken(accountId, userId, key); + } + + @ApiOkResponse({ description: 'Voximplant user SIP data', type: VoximplantUserSIPDataDto }) + @Get(':userId/sip') + public async getSIPData(@CurrentAuth() { accountId }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.getSIPData(accountId, userId); + } + + @ApiCreatedResponse({ description: 'Update voximplant user', type: VoximplantUserDto }) + @Patch(':userId') + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('userId', ParseIntPipe) userId: number, + @Body() dto: UpdateVoximplantUserDto, + ) { + return this.service.update(accountId, userId, dto); + } + + @ApiOkResponse({ description: 'Delete voximplant user' }) + @Delete(':userId') + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('userId', ParseIntPipe) userId: number) { + return this.service.delete(accountId, userId); + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.service.ts b/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.service.ts new file mode 100644 index 0000000..bc45143 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant-user/voximplant-user.service.ts @@ -0,0 +1,253 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import * as crypto from 'crypto'; +import VoximplantApiClient from '@amwork/voximplant-apiclient-nodejs'; + +import { NotFoundError, PasswordUtil } from '@/common'; + +import { UserService } from '@/modules/iam/user/user.service'; +import { User } from '@/modules/iam/user/entities/user.entity'; +import { FieldType } from '@/modules/entity/entity-field/common/enums/field-type.enum'; +import { EntityService } from '@/CRM/Service/Entity/EntityService'; + +import { VoximplantApplicationParam } from '../common'; +import { VoximplantAccountService } from '../voximplant-account'; +import { VoximplantNumberService } from '../voximplant-number'; + +import { + CreateVoximplantUserDto, + CreateVoximplantUsersBatchDto, + UpdateVoximplantUserDto, + UsersQueueDto, + VoximplantUserSIPDataDto, +} from './dto'; +import { VoximplantUser } from './entities'; + +interface FindFilter { + userId?: number; + externalId?: number; + userName?: string; + isActive?: boolean; + accessiblePhoneNumber?: string; +} + +@Injectable() +export class VoximplantUserService { + private readonly logger = new Logger(VoximplantUserService.name); + constructor( + @InjectRepository(VoximplantUser) + private readonly repository: Repository, + private readonly entityService: EntityService, + private readonly userService: UserService, + private readonly viAccountService: VoximplantAccountService, + private readonly viNumberService: VoximplantNumberService, + ) {} + + public async create(accountId: number, userId: number, dto: CreateVoximplantUserDto): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + const linkedUser = await this.userService.findOne({ accountId, id: userId }); + return this.createViUser(accountId, linkedUser, client, appParam, dto.isActive); + } + + public async createBatch(accountId: number, dto: CreateVoximplantUsersBatchDto): Promise { + const { client, appParam } = await this.viAccountService.getClient(accountId); + const users = await this.userService.findMany({ accountId, id: dto.userIds }); + const viUsers: VoximplantUser[] = []; + for (const user of users) { + const viUser = await this.createViUser(accountId, user, client, appParam, dto.isActive); + if (viUser) { + viUsers.push(viUser); + } + } + return viUsers; + } + + private async createViUser( + accountId: number, + linkedUser: User, + client: VoximplantApiClient, + appParam: VoximplantApplicationParam, + isActive: boolean, + ): Promise { + const viUser = { + ...appParam, + userName: `user-${linkedUser.id}`, + userDisplayName: linkedUser.fullName, + userPassword: PasswordUtil.generateSecure(), + userActive: isActive, + }; + const viResponse = await client.Users.addUser(viUser); + if (!viResponse.result) { + this.logger.error(`Create user error: ${JSON.stringify(viResponse)}`); + } + return viResponse.result + ? await this.repository.save( + new VoximplantUser( + accountId, + linkedUser.id, + viResponse.userId, + viUser.userName, + viUser.userPassword, + isActive, + ), + ) + : null; + } + + public async findMany(accountId: number, filter?: FindFilter): Promise { + const viUsers = await this.createFindQb(accountId, filter).orderBy('viu.user_id', 'ASC').getMany(); + + if (filter?.accessiblePhoneNumber) { + const availableViUsers: VoximplantUser[] = []; + for (const viUser of viUsers) { + if (await this.viNumberService.checkAvailable(accountId, viUser.userId, filter.accessiblePhoneNumber)) { + availableViUsers.push(viUser); + } + } + + return availableViUsers; + } + + return viUsers; + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).orderBy('viu.user_id', 'ASC').getOne(); + } + + public async getOne(accountId: number, userId: number): Promise { + const viUser = await this.findOne(accountId, { userId }); + if (!viUser) { + throw NotFoundError.withId(VoximplantUser, userId); + } + return viUser; + } + + public async update(accountId: number, userId: number, dto: UpdateVoximplantUserDto): Promise { + const viUser = await this.getOne(accountId, userId); + await this.repository.save(viUser.update(dto)); + + const { client, appParam } = await this.viAccountService.getClient(accountId); + const request = { + ...appParam, + userId: viUser.externalId, + userName: viUser.userName, + userActive: viUser.isActive, + }; + await client.Users.setUserInfo(request); + + if (!dto.isActive) { + await this.viNumberService.removeUser(accountId, userId); + } + + return viUser; + } + + public async getUserName(accountId: number, userId: number): Promise { + const viUser = await this.findOne(accountId, { userId: userId }); + if (!viUser) { + return null; + } + const viAccount = await this.viAccountService.getOne(accountId); + return `${viUser.userName}@${viAccount.applicationName}`; + } + + public async getLoginToken(accountId: number, userId: number, key: string): Promise { + const viUser = await this.getOne(accountId, userId); + return this.calculateMD5(viUser.userName, viUser.password, key); + } + + public async getSIPData(accountId: number, userId: number): Promise { + const viUser = await this.findOne(accountId, { userId: userId }); + if (!viUser) { + return null; + } + const viAccount = await this.viAccountService.getOne(accountId); + return new VoximplantUserSIPDataDto({ + userName: viUser.userName, + domain: viAccount.applicationName, + password: viUser.password, + }); + } + + public async delete(accountId: number, userId: number) { + const viUser = await this.getOne(accountId, userId); + + const { client, appParam } = await this.viAccountService.getClient(accountId); + const { result } = await client.Users.delUser({ + ...appParam, + userId: viUser.externalId, + userName: viUser.userName, + }); + + if (result) { + await this.repository.delete({ accountId: accountId, userId }); + } + } + + public async getUsersQueue( + applicationId: number, + { phone, viPhoneNumber }: { phone: string; viPhoneNumber?: string | null }, + ): Promise { + const viAccount = await this.viAccountService.findOneExt({ applicationId }); + if (!viAccount) { + return null; + } + + const entity = phone + ? await this.entityService.findOne(viAccount.accountId, { + fieldValue: { type: FieldType.Phone, value: phone.startsWith('+') ? phone.slice(1) : phone }, + }) + : null; + + const operators = await this.findOperators(viAccount.accountId, { + userId: entity?.responsibleUserId, + viPhoneNumber, + }); + + return new UsersQueueDto({ + users: operators.map((o) => o.userName), + entityName: entity?.name ?? null, + entityId: entity?.id ?? null, + entityTypeId: entity?.entityTypeId ?? null, + }); + } + + private calculateMD5(user: string, password: string, key: string): string { + const userHash = crypto.createHash('md5').update(`${user}:voximplant.com:${password}`).digest('hex'); + return crypto.createHash('md5').update(`${key}|${userHash}`).digest('hex'); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('viu').where('viu.account_id = :accountId', { accountId }); + + if (filter?.userId) { + qb.andWhere('viu.user_id = :userId', { userId: filter.userId }); + } + + if (filter?.externalId) { + qb.andWhere('viu.external_id = :externalId', { externalId: filter.externalId }); + } + + if (filter?.userName) { + qb.andWhere('viu.user_name = :userName', { userName: filter.userName }); + } + + if (filter?.isActive !== undefined) { + qb.andWhere('viu.is_active = :isActive', { isActive: filter.isActive }); + } + + return qb; + } + + private async findOperators( + accountId: number, + { userId, viPhoneNumber }: { userId?: number; viPhoneNumber?: string | null }, + ): Promise { + const queue = await this.findMany(accountId, { isActive: true, accessiblePhoneNumber: viPhoneNumber ?? undefined }); + const responsible = userId ? await this.findOne(accountId, { userId, isActive: true }) : null; + + return responsible ? [responsible, ...queue.filter((o) => o.userId !== responsible.userId)] : queue; + } +} diff --git a/backend/src/modules/telephony/voximplant/voximplant.module.ts b/backend/src/modules/telephony/voximplant/voximplant.module.ts new file mode 100644 index 0000000..51733d5 --- /dev/null +++ b/backend/src/modules/telephony/voximplant/voximplant.module.ts @@ -0,0 +1,92 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '@/modules/iam/iam.module'; +import { EntityInfoModule } from '@/modules/entity/entity-info/entity-info.module'; +import { CrmModule } from '@/CRM/crm.module'; + +import voximplantConfig from './config/voximplant.config'; +import { VoximplantAccount, VoximplantAccountController, VoximplantAccountService } from './voximplant-account'; +import { + VoximplantCall, + VoximplantCallController, + VoximplantCallPublicController, + VoximplantCallService, +} from './voximplant-call'; +import { VoximplantCoreController, VoximplantCoreService } from './voximplant-core'; +import { + VoximplantNumber, + VoximplantNumberController, + VoximplantNumberService, + VoximplantNumberUser, + VoximplantNumberUserService, +} from './voximplant-number'; +import { VoximplantReportingService, VoximplantReportingController } from './voximplant-reporting'; +import { + VoximplantScenarioEntity, + VoximplantScenarioNote, + VoximplantScenarioTask, + VoximplantScenarioService, + VoximplantScenarioController, +} from './voximplant-scenario'; +import { + VoximplantSip, + VoximplantSipController, + VoximplantSipService, + VoximplantSipUser, + VoximplantSipUserService, +} from './voximplant-sip'; +import { + VoximplantUser, + VoximplantUserService, + VoximplantUserController, + VoximplantUserPublicController, +} from './voximplant-user'; + +@Module({ + imports: [ + ConfigModule.forFeature(voximplantConfig), + TypeOrmModule.forFeature([ + VoximplantAccount, + VoximplantUser, + VoximplantNumber, + VoximplantNumberUser, + VoximplantCall, + VoximplantScenarioEntity, + VoximplantScenarioNote, + VoximplantScenarioTask, + VoximplantSip, + VoximplantSipUser, + ]), + IAMModule, + EntityInfoModule, + forwardRef(() => CrmModule), + ], + providers: [ + VoximplantCoreService, + VoximplantAccountService, + VoximplantUserService, + VoximplantNumberService, + VoximplantNumberUserService, + VoximplantCallService, + VoximplantScenarioService, + VoximplantReportingService, + VoximplantSipService, + VoximplantSipUserService, + ], + controllers: [ + VoximplantCoreController, + VoximplantAccountController, + VoximplantUserController, + VoximplantUserPublicController, + VoximplantNumberController, + VoximplantCallController, + VoximplantCallPublicController, + VoximplantScenarioController, + VoximplantSipController, + VoximplantReportingController, + ], + exports: [VoximplantCallService, VoximplantReportingService], +}) +export class VoximplantModule {} diff --git a/backend/src/modules/tutorial/common/dto/index.ts b/backend/src/modules/tutorial/common/dto/index.ts new file mode 100644 index 0000000..f5a2f5a --- /dev/null +++ b/backend/src/modules/tutorial/common/dto/index.ts @@ -0,0 +1 @@ +export * from './tutorial-filter.dto'; diff --git a/backend/src/modules/tutorial/common/dto/tutorial-filter.dto.ts b/backend/src/modules/tutorial/common/dto/tutorial-filter.dto.ts new file mode 100644 index 0000000..655d732 --- /dev/null +++ b/backend/src/modules/tutorial/common/dto/tutorial-filter.dto.ts @@ -0,0 +1,21 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { TutorialProductType } from '../enums'; + +export class TutorialFilterDto { + @ApiPropertyOptional({ description: 'User ID' }) + @IsOptional() + @IsNumber() + userId?: number; + + @ApiPropertyOptional({ enum: TutorialProductType, description: 'Product type' }) + @IsOptional() + @IsEnum(TutorialProductType) + productType?: TutorialProductType; + + @ApiPropertyOptional({ description: 'Related object ID' }) + @IsOptional() + @IsNumber() + objectId?: number; +} diff --git a/backend/src/modules/tutorial/common/enums/index.ts b/backend/src/modules/tutorial/common/enums/index.ts new file mode 100644 index 0000000..c854c28 --- /dev/null +++ b/backend/src/modules/tutorial/common/enums/index.ts @@ -0,0 +1 @@ +export * from './tutorial-product-type.enum'; diff --git a/backend/src/modules/tutorial/common/enums/tutorial-product-type.enum.ts b/backend/src/modules/tutorial/common/enums/tutorial-product-type.enum.ts new file mode 100644 index 0000000..a15ebf6 --- /dev/null +++ b/backend/src/modules/tutorial/common/enums/tutorial-product-type.enum.ts @@ -0,0 +1,10 @@ +export enum TutorialProductType { + BUILDER = 'builder', + TASK = 'task', + ENTITY_TYPE = 'entity_type', + PRODUCTS_SECTION = 'products_section', + SCHEDULER = 'scheduler', + MAIL = 'mail', + MULTI_MESSENGER = 'multi_messenger', + SETTINGS = 'settings', +} diff --git a/backend/src/modules/tutorial/common/index.ts b/backend/src/modules/tutorial/common/index.ts new file mode 100644 index 0000000..7cec66d --- /dev/null +++ b/backend/src/modules/tutorial/common/index.ts @@ -0,0 +1,2 @@ +export * from './dto'; +export * from './enums'; diff --git a/backend/src/modules/tutorial/config/tutorial.config.ts b/backend/src/modules/tutorial/config/tutorial.config.ts new file mode 100644 index 0000000..e76b573 --- /dev/null +++ b/backend/src/modules/tutorial/config/tutorial.config.ts @@ -0,0 +1,12 @@ +import { registerAs } from '@nestjs/config'; + +export interface TutorialConfig { + language: string; +} + +export default registerAs( + 'tutorial', + (): TutorialConfig => ({ + language: process.env.TUTORIAL_LANGUAGE, + }), +); diff --git a/backend/src/modules/tutorial/tutorial-core/index.ts b/backend/src/modules/tutorial/tutorial-core/index.ts new file mode 100644 index 0000000..80be155 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-core/index.ts @@ -0,0 +1,2 @@ +export * from './tutorial-core.controller'; +export * from './tutorial-core.service'; diff --git a/backend/src/modules/tutorial/tutorial-core/tutorial-core.controller.ts b/backend/src/modules/tutorial/tutorial-core/tutorial-core.controller.ts new file mode 100644 index 0000000..4c6df08 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-core/tutorial-core.controller.ts @@ -0,0 +1,33 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { DateUtil, TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { TutorialFilterDto } from '../common'; +import { TutorialCoreService } from './tutorial-core.service'; + +@ApiTags('tutorial') +@Controller('') +@JwtAuthorized() +@TransformToDto() +export class TutorialCoreController { + constructor(private readonly service: TutorialCoreService) {} + + @ApiOperation({ + summary: 'Get tutorial items count', + description: 'Get tutorial items count with filter and from date.', + }) + @ApiQuery({ name: 'from', required: false, type: Date, description: 'From date in ISO format' }) + @ApiOkResponse({ description: 'Tutorial items count', type: Number }) + @Get('count') + public async count( + @CurrentAuth() { accountId }: AuthData, + @Query() filter: TutorialFilterDto, + @Query('from') from?: string, + ) { + return this.service.count(accountId, { ...filter, createdFrom: from ? DateUtil.fromISOString(from) : undefined }); + } +} diff --git a/backend/src/modules/tutorial/tutorial-core/tutorial-core.service.ts b/backend/src/modules/tutorial/tutorial-core/tutorial-core.service.ts new file mode 100644 index 0000000..e2d3af0 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-core/tutorial-core.service.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { AccountCreatedEvent, IamEventType } from '@/modules/iam/common'; + +import { TutorialConfig } from '../config/tutorial.config'; +import { TutorialProductType } from '../common'; +import { TutorialGroupService } from '../tutorial-group'; +import { TutorialItemService } from '../tutorial-item'; + +import { DefaultTutorial } from './tutorial-defaults'; + +interface FindFilter { + userId?: number; + productType?: TutorialProductType; + objectId?: number; + createdFrom?: Date; +} + +@Injectable() +export class TutorialCoreService { + private _language: string | undefined; + + constructor( + private readonly configService: ConfigService, + private readonly groupService: TutorialGroupService, + private readonly itemService: TutorialItemService, + ) { + this._language = this.configService.get('tutorial')?.language; + } + + @OnEvent(IamEventType.AccountCreated, { async: true }) + public async handleRegistrationEvent(event: AccountCreatedEvent) { + await this.createDefault(event.accountId); + } + + public async count(accountId: number, filter?: FindFilter): Promise { + return this.itemService.count(accountId, filter); + } + + private async createDefault(accountId: number): Promise { + if (this._language) { + const groups = DefaultTutorial[this._language]; + if (groups) { + for (const group of groups) { + const createdGroup = await this.groupService.create(accountId, group); + for (const item of group.items) { + await this.itemService.create(accountId, createdGroup.id, item); + } + } + } + } + } +} diff --git a/backend/src/modules/tutorial/tutorial-core/tutorial-defaults.ts b/backend/src/modules/tutorial/tutorial-core/tutorial-defaults.ts new file mode 100644 index 0000000..943359e --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-core/tutorial-defaults.ts @@ -0,0 +1,157 @@ +interface DefaultItem { + name: string; + link: string; + sortOrder: number; +} +interface DefaultGroup { + name: string; + sortOrder: number; + items: DefaultItem[]; +} + +export const DefaultTutorial: Record = { + ru: [ + { + name: 'Видеоуроки по модулям и функциям', + sortOrder: 0, + items: [ + { + name: 'Обзор функционала воронки продаж', + link: 'https://rutube.ru/video/7866956dff4e26e1c898c1a31450b3ff', + sortOrder: 0, + }, + { + name: 'Как работать в сделке', + link: 'https://rutube.ru/video/1fc6c8cf6306c32f7775dc91b557fe6b', + sortOrder: 1, + }, + { + name: 'Как добавить товар/услугу в сделку', + link: 'https://rutube.ru/video/55cfc3a2a8fe8d891cab17135d6d758a', + sortOrder: 2, + }, + { + name: 'Как работать в разделе задачах', + link: 'https://rutube.ru/video/76111a083b5579cb8c7e3e3fb6b225f8', + sortOrder: 3, + }, + { + name: 'Как пользоваться Дашбордом', + link: 'https://rutube.ru/video/d0b6ff03e0005f31236d8fa1d31a942d', + sortOrder: 4, + }, + { + name: 'Как пользоваться отчетами', + link: 'https://rutube.ru/video/c5a7cfbeda45bd110f0cba20bcc399ac', + sortOrder: 5, + }, + { + name: 'Как работать с товарами и услугами', + link: 'https://rutube.ru/video/286a3a8bd132332648bb7a37e313943c', + sortOrder: 6, + }, + { + name: 'Как работать с проектами и задачами', + link: 'https://rutube.ru/video/2384f907194a481a9d88486d85772496', + sortOrder: 7, + }, + { + name: 'Как настроить уведомления', + link: 'https://rutube.ru/video/861b2a529cf2fb93109df88e55458be2', + sortOrder: 8, + }, + { + name: 'Как настроить свой профиль', + link: 'https://rutube.ru/video/e2b52a8139e7da53fd280c1e37694e04', + sortOrder: 9, + }, + ], + }, + { + name: 'Видеоуроки по настройке и кастомизации', + sortOrder: 1, + items: [ + { + name: 'Как настроить план продаж в CRM', + link: 'https://rutube.ru/video/39a6b69a7042b6318a22eeedc08cdf51', + sortOrder: 0, + }, + { + name: 'Как настроить воронку продаж', + link: 'https://rutube.ru/video/d6c71e34f6ff41463616f3884d1e0b7a', + sortOrder: 1, + }, + { + name: 'Как настроить базу знаний', + link: 'https://rutube.ru/video/58c01491d9b6391eb5e59f19d58e47c5', + sortOrder: 2, + }, + { + name: 'Как добавить и настроить права пользователю', + link: 'https://rutube.ru/video/d0a83e46fa070e0c763d916ec4d7f9fd', + sortOrder: 3, + }, + { + name: 'Продвинутые настройки полей в карточке', + link: 'https://rutube.ru/video/203a6cea52d5935c4503f2632373b87b', + sortOrder: 4, + }, + { + name: `Как настроить раздел "Товары, остатки и услуги"`, + link: 'https://rutube.ru/video/dedae77a65ded0aa531f8c5c90075f45', + sortOrder: 5, + }, + ], + }, + ], + en: [ + { + name: 'Video Tutorials on Modules and Functionality', + sortOrder: 0, + items: [ + { name: 'Project and Task Management in Amwork', link: 'https://youtu.be/23QxzqJEvKo', sortOrder: 0 }, + { name: 'Task Management and Board Types in Amwork', link: 'https://youtu.be/vCDBNbvmDnQ', sortOrder: 1 }, + { name: 'Sales Pipeline and List View in Amwork', link: 'https://youtu.be/Ptc2hGY5swo', sortOrder: 2 }, + { + name: 'Reviewing the CRM Section and Sales Pipeline in Amwork', + link: 'https://youtu.be/kN-yTmBc-7c', + sortOrder: 3, + }, + { name: 'Reviewing Deal Cards in Amwork', link: 'https://youtu.be/24HgmAyuTjk', sortOrder: 4 }, + { + name: 'Dashboard and Sales Plan Configuration in Amwork', + link: 'https://youtu.be/2S7PhICbzzU', + sortOrder: 5, + }, + { + name: 'Harnessing the Power of Reports in CRM Section in Amwork', + link: 'https://youtu.be/i6puY7ZZ9Ms', + sortOrder: 6, + }, + { + name: 'Overview of the "Warehouse & Product Management" Section in Amwork', + link: 'https://youtu.be/-OiDVZ3nFTI', + sortOrder: 7, + }, + { name: 'Creating Orders in Deal Cards in Amwork', link: 'https://youtu.be/86mGKesNytM', sortOrder: 8 }, + ], + }, + { + name: 'Video Tutorials on Setup and Customization', + sortOrder: 1, + items: [ + { name: 'Configuring a Sales Pipeline in Amwork', link: 'https://youtu.be/JXHUx7zSA-A', sortOrder: 0 }, + { + name: 'Customizing Fields for Deal and Project Cards in Amwork', + link: 'https://youtu.be/wb3PuZfMFP0', + sortOrder: 1, + }, + { + name: 'Setting Up Automations in Boards and Sales Pipelines in Amwork', + link: 'https://youtu.be/DDJlekyDJBY', + sortOrder: 2, + }, + ], + }, + ], +}; diff --git a/backend/src/modules/tutorial/tutorial-group/dto/create-tutorial-group.dto.ts b/backend/src/modules/tutorial/tutorial-group/dto/create-tutorial-group.dto.ts new file mode 100644 index 0000000..7a81639 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/dto/create-tutorial-group.dto.ts @@ -0,0 +1,5 @@ +import { PickType } from '@nestjs/swagger'; + +import { TutorialGroupDto } from './tutorial-group.dto'; + +export class CreateTutorialGroupDto extends PickType(TutorialGroupDto, ['name', 'sortOrder'] as const) {} diff --git a/backend/src/modules/tutorial/tutorial-group/dto/index.ts b/backend/src/modules/tutorial/tutorial-group/dto/index.ts new file mode 100644 index 0000000..8b4b734 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-tutorial-group.dto'; +export * from './tutorial-group.dto'; +export * from './update-tutorial-group.dto'; diff --git a/backend/src/modules/tutorial/tutorial-group/dto/tutorial-group.dto.ts b/backend/src/modules/tutorial/tutorial-group/dto/tutorial-group.dto.ts new file mode 100644 index 0000000..da6a5af --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/dto/tutorial-group.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { TutorialItemDto } from '../../tutorial-item'; + +export class TutorialGroupDto { + @ApiProperty({ description: 'Tutorial group ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Tutorial group name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Tutorial group sort order' }) + @IsNumber() + sortOrder: number; + + @ApiProperty({ description: 'Tutorial group creation date in ISO format' }) + @IsString() + createdAt: string; + + @ApiProperty({ type: [TutorialItemDto], nullable: true, description: 'Tutorial group items' }) + @IsOptional() + @IsArray() + items: TutorialItemDto[] | null; + + constructor({ id, name, sortOrder, createdAt, items }: TutorialGroupDto) { + this.id = id; + this.name = name; + this.sortOrder = sortOrder; + this.createdAt = createdAt; + this.items = items; + } +} diff --git a/backend/src/modules/tutorial/tutorial-group/dto/update-tutorial-group.dto.ts b/backend/src/modules/tutorial/tutorial-group/dto/update-tutorial-group.dto.ts new file mode 100644 index 0000000..dde1edc --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/dto/update-tutorial-group.dto.ts @@ -0,0 +1,5 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { TutorialGroupDto } from './tutorial-group.dto'; + +export class UpdateTutorialGroupDto extends PartialType(PickType(TutorialGroupDto, ['name', 'sortOrder'] as const)) {} diff --git a/backend/src/modules/tutorial/tutorial-group/entities/index.ts b/backend/src/modules/tutorial/tutorial-group/entities/index.ts new file mode 100644 index 0000000..15b8248 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/entities/index.ts @@ -0,0 +1 @@ +export * from './tutorial-group.entity'; diff --git a/backend/src/modules/tutorial/tutorial-group/entities/tutorial-group.entity.ts b/backend/src/modules/tutorial/tutorial-group/entities/tutorial-group.entity.ts new file mode 100644 index 0000000..746f94d --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/entities/tutorial-group.entity.ts @@ -0,0 +1,60 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { TutorialItem } from '../../tutorial-item'; +import { CreateTutorialGroupDto, UpdateTutorialGroupDto, TutorialGroupDto } from '../dto'; + +@Entity() +export class TutorialGroup { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + name: string; + + @Column() + sortOrder: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, name: string, sortOrder: number, createdAt?: Date) { + this.accountId = accountId; + this.name = name; + this.sortOrder = sortOrder; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _items: TutorialItem[] | null = null; + public get items(): TutorialItem[] | null { + return this._items; + } + public set items(value: TutorialItem[] | null) { + this._items = value; + } + + public static fromDto(accountId: number, dto: CreateTutorialGroupDto): TutorialGroup { + return new TutorialGroup(accountId, dto.name, dto.sortOrder); + } + + public update(dto: UpdateTutorialGroupDto): TutorialGroup { + this.name = dto.name ?? this.name; + this.sortOrder = dto.sortOrder ?? this.sortOrder; + + return this; + } + + public toDto(): TutorialGroupDto { + return new TutorialGroupDto({ + id: this.id, + name: this.name, + sortOrder: this.sortOrder, + createdAt: this.createdAt.toISOString(), + items: this.items?.map((item) => item.toDto()) ?? null, + }); + } +} diff --git a/backend/src/modules/tutorial/tutorial-group/index.ts b/backend/src/modules/tutorial/tutorial-group/index.ts new file mode 100644 index 0000000..79ba756 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/index.ts @@ -0,0 +1,4 @@ +export * from './dto'; +export * from './entities'; +export * from './tutorial-group.controller'; +export * from './tutorial-group.service'; diff --git a/backend/src/modules/tutorial/tutorial-group/tutorial-group.controller.ts b/backend/src/modules/tutorial/tutorial-group/tutorial-group.controller.ts new file mode 100644 index 0000000..9ffcbda --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/tutorial-group.controller.ts @@ -0,0 +1,101 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; + +import { ExpandQuery, SortOrderListDto, TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { UserAccess } from '@/modules/iam/common/decorators/user-access.decorator'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { TutorialFilterDto } from '../common'; +import { ExpandableField } from './types'; +import { TutorialGroupDto, CreateTutorialGroupDto, UpdateTutorialGroupDto } from './dto'; +import { TutorialGroupService } from './tutorial-group.service'; + +@ApiTags('tutorial/groups') +@Controller('groups') +@JwtAuthorized() +@TransformToDto() +export class TutorialGroupController { + constructor(private readonly service: TutorialGroupService) {} + + @ApiOperation({ summary: 'Create tutorial group', description: 'Create tutorial group' }) + @ApiBody({ type: CreateTutorialGroupDto, required: true, description: 'Data for creating tutorial group' }) + @ApiCreatedResponse({ description: 'Created tutorial group', type: TutorialGroupDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create(@CurrentAuth() { accountId }: AuthData, @Body() dto: CreateTutorialGroupDto) { + return this.service.create(accountId, dto); + } + + @ApiOperation({ summary: 'Get tutorial groups', description: 'Get tutorial groups' }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields', + enum: ExpandableField, + }) + @ApiOkResponse({ description: 'Tutorial groups', type: [TutorialGroupDto] }) + @Get() + public async findMany( + @CurrentAuth() { accountId }: AuthData, + @Query() filter: TutorialFilterDto, + @Query() expand: ExpandQuery, + ) { + return this.service.findMany(accountId, filter, { expand: expand.fields }); + } + + @ApiOperation({ summary: 'Get tutorial group', description: 'Get tutorial group' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id', example: 1 }) + @ApiQuery({ + name: 'expand', + type: String, + required: false, + isArray: true, + description: 'Expand fields', + enum: ExpandableField, + }) + @ApiOkResponse({ description: 'Tutorial group', type: TutorialGroupDto }) + @Get(':groupId') + public async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('groupId', ParseIntPipe) groupId: number, + @Query() expand: ExpandQuery, + ) { + return this.service.findOne(accountId, { groupId }, { expand: expand.fields }); + } + + @ApiOperation({ summary: 'Sort tutorial groups', description: 'Sort tutorial groups' }) + @ApiBody({ type: SortOrderListDto, required: true, description: 'Data for sorting tutorial groups' }) + @ApiOkResponse() + @Patch('sort') + @UserAccess({ adminOnly: true }) + public async sort(@CurrentAuth() { accountId }: AuthData, @Body() dto: SortOrderListDto) { + return this.service.sort(accountId, dto); + } + + @ApiOperation({ summary: 'Update tutorial group', description: 'Update tutorial group' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id', example: 1 }) + @ApiBody({ type: UpdateTutorialGroupDto, required: true, description: 'Data for updating tutorial group' }) + @ApiOkResponse({ description: 'Updated tutorial group', type: TutorialGroupDto }) + @Patch(':groupId') + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('groupId', ParseIntPipe) groupId: number, + @Body() dto: UpdateTutorialGroupDto, + ) { + return this.service.update(accountId, groupId, dto); + } + + @ApiOperation({ summary: 'Delete tutorial group', description: 'Delete tutorial group' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id', example: 1 }) + @ApiOkResponse() + @Delete(':groupId') + @UserAccess({ adminOnly: true }) + public async delete(@CurrentAuth() { accountId }: AuthData, @Param('groupId', ParseIntPipe) groupId: number) { + return this.service.delete(accountId, groupId); + } +} diff --git a/backend/src/modules/tutorial/tutorial-group/tutorial-group.service.ts b/backend/src/modules/tutorial/tutorial-group/tutorial-group.service.ts new file mode 100644 index 0000000..032088b --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/tutorial-group.service.ts @@ -0,0 +1,119 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { NotFoundError, SortOrderListDto } from '@/common'; + +import { TutorialProductType } from '../common'; +import { TutorialItemService } from '../tutorial-item'; +import { ExpandableField } from './types'; +import { CreateTutorialGroupDto, UpdateTutorialGroupDto } from './dto'; +import { TutorialGroup } from './entities'; + +interface FindFilter { + groupId?: number; + userId?: number; + productType?: TutorialProductType; + objectId?: number; +} +interface FindOptions { + expand?: ExpandableField[]; +} + +@Injectable() +export class TutorialGroupService { + constructor( + @InjectRepository(TutorialGroup) + private readonly repository: Repository, + private readonly itemService: TutorialItemService, + ) {} + + public async create(accountId: number, dto: CreateTutorialGroupDto): Promise { + return this.repository.save(TutorialGroup.fromDto(accountId, dto)); + } + + public async findOne(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const group = await this.createFindQb(accountId, filter).getOne(); + + return group && options?.expand ? await this.expandOne(group, options.expand, filter) : group; + } + + public async findMany(accountId: number, filter?: FindFilter, options?: FindOptions): Promise { + const groups = await this.createFindQb(accountId, filter).orderBy('tg.sort_order').getMany(); + + return groups && options?.expand ? await this.expandMany(groups, options.expand, filter) : groups; + } + + public async update(accountId: number, groupId: number, dto: UpdateTutorialGroupDto): Promise { + const group = await this.findOne(accountId, { groupId }); + if (!group) { + throw NotFoundError.withId(TutorialGroup, groupId); + } + + return this.repository.save(group.update(dto)); + } + + public async sort(accountId: number, dto: SortOrderListDto) { + for (const item of dto.items) { + await this.repository.update({ id: item.id, accountId }, { sortOrder: item.sortOrder }); + } + } + + public async delete(accountId: number, groupId: number) { + await this.repository.delete({ id: groupId, accountId }); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.repository.createQueryBuilder('tg').where('tg.account_id = :accountId', { accountId }); + if (filter?.groupId) { + qb.andWhere('tg.id = :id', { id: filter.groupId }); + } + if (filter?.userId || filter?.productType || filter?.objectId) { + qb.leftJoin('tutorial_item', 'ti', 'tg.id = ti.group_id'); + if (filter?.userId) { + qb.leftJoin('tutorial_item_user', 'tiu', 'ti.id = tiu.item_id'); + qb.andWhere( + new Brackets((qb1) => + qb1.where('tiu.user_id = :userId', { userId: filter.userId }).orWhere('tiu.user_id IS NULL'), + ), + ); + } + if (filter?.productType || filter?.objectId) { + qb.leftJoin('tutorial_item_product', 'tip', 'ti.id = tip.item_id'); + if (filter?.productType) { + qb.andWhere( + new Brackets((qb2) => + qb2.where('tip.type = :type', { type: filter.productType }).orWhere('tip.type IS NULL'), + ), + ); + } + if (filter?.objectId) { + qb.andWhere( + new Brackets((qb3) => + qb3.where('tip.object_id = :objectId', { objectId: filter.objectId }).orWhere('tip.object_id IS NULL'), + ), + ); + } + } + } + return qb; + } + + private async expandOne( + group: TutorialGroup, + expand: ExpandableField[], + filter?: FindFilter, + ): Promise { + if (expand.includes(ExpandableField.items)) { + group.items = await this.itemService.findMany(group.accountId, { ...filter, groupId: group.id }); + } + return group; + } + private async expandMany( + groups: TutorialGroup[], + expand: ExpandableField[], + filter?: FindFilter, + ): Promise { + return await Promise.all(groups.map((group) => this.expandOne(group, expand, filter))); + } +} diff --git a/backend/src/modules/tutorial/tutorial-group/types/expandable-field.ts b/backend/src/modules/tutorial/tutorial-group/types/expandable-field.ts new file mode 100644 index 0000000..7a36b44 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/types/expandable-field.ts @@ -0,0 +1,3 @@ +export enum ExpandableField { + items = 'items', +} diff --git a/backend/src/modules/tutorial/tutorial-group/types/index.ts b/backend/src/modules/tutorial/tutorial-group/types/index.ts new file mode 100644 index 0000000..36e5d96 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-group/types/index.ts @@ -0,0 +1 @@ +export * from './expandable-field'; diff --git a/backend/src/modules/tutorial/tutorial-item/dto/create-tutorial-item.dto.ts b/backend/src/modules/tutorial/tutorial-item/dto/create-tutorial-item.dto.ts new file mode 100644 index 0000000..634f3d2 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/dto/create-tutorial-item.dto.ts @@ -0,0 +1,11 @@ +import { PickType } from '@nestjs/swagger'; + +import { TutorialItemDto } from './tutorial-item.dto'; + +export class CreateTutorialItemDto extends PickType(TutorialItemDto, [ + 'name', + 'link', + 'sortOrder', + 'userIds', + 'products', +] as const) {} diff --git a/backend/src/modules/tutorial/tutorial-item/dto/index.ts b/backend/src/modules/tutorial/tutorial-item/dto/index.ts new file mode 100644 index 0000000..594de2a --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/dto/index.ts @@ -0,0 +1,4 @@ +export * from './create-tutorial-item.dto'; +export * from './tutorial-item-product.dto'; +export * from './tutorial-item.dto'; +export * from './update-tutorial-item.dto'; diff --git a/backend/src/modules/tutorial/tutorial-item/dto/tutorial-item-product.dto.ts b/backend/src/modules/tutorial/tutorial-item/dto/tutorial-item-product.dto.ts new file mode 100644 index 0000000..656f088 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/dto/tutorial-item-product.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional } from 'class-validator'; + +import { TutorialProductType } from '../../common'; + +export class TutorialItemProductDto { + @ApiProperty({ enum: TutorialProductType, description: 'Related product type' }) + @IsEnum(TutorialProductType) + type: TutorialProductType; + + @ApiPropertyOptional({ nullable: true, description: 'Related Object ID' }) + @IsOptional() + @IsNumber() + objectId?: number | null | undefined; + + constructor({ type, objectId }: TutorialItemProductDto) { + this.type = type; + this.objectId = objectId; + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/dto/tutorial-item.dto.ts b/backend/src/modules/tutorial/tutorial-item/dto/tutorial-item.dto.ts new file mode 100644 index 0000000..ec7a8fc --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/dto/tutorial-item.dto.ts @@ -0,0 +1,55 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; + +import { TutorialItemProductDto } from './tutorial-item-product.dto'; + +export class TutorialItemDto { + @ApiProperty({ description: 'Tutorial item ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Tutorial group ID' }) + @IsNumber() + groupId: number; + + @ApiProperty({ description: 'Tutorial item name' }) + @IsString() + name: string; + + @ApiProperty({ description: 'Tutorial item link' }) + @IsString() + link: string; + + @ApiProperty({ description: 'Tutorial item sort order' }) + @IsNumber() + sortOrder: number; + + @ApiProperty({ description: 'Tutorial item creation date in ISO format' }) + @IsString() + createdAt: string; + + @ApiPropertyOptional({ type: [Number], nullable: true, description: 'User IDs associated with the tutorial item' }) + @IsOptional() + @IsNumber({}, { each: true }) + userIds?: number[] | null; + + @ApiPropertyOptional({ + type: [TutorialItemProductDto], + nullable: true, + description: 'Products associated with the tutorial item', + }) + @IsOptional() + @IsArray() + products?: TutorialItemProductDto[] | null; + + constructor({ id, groupId, name, link, sortOrder, createdAt, userIds, products }: TutorialItemDto) { + this.id = id; + this.groupId = groupId; + this.name = name; + this.link = link; + this.sortOrder = sortOrder; + this.createdAt = createdAt; + this.userIds = userIds; + this.products = products; + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/dto/update-tutorial-item.dto.ts b/backend/src/modules/tutorial/tutorial-item/dto/update-tutorial-item.dto.ts new file mode 100644 index 0000000..9bd447c --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/dto/update-tutorial-item.dto.ts @@ -0,0 +1,7 @@ +import { PartialType, PickType } from '@nestjs/swagger'; + +import { TutorialItemDto } from './tutorial-item.dto'; + +export class UpdateTutorialItemDto extends PartialType( + PickType(TutorialItemDto, ['name', 'link', 'sortOrder', 'userIds', 'products'] as const), +) {} diff --git a/backend/src/modules/tutorial/tutorial-item/entities/index.ts b/backend/src/modules/tutorial/tutorial-item/entities/index.ts new file mode 100644 index 0000000..735860e --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/entities/index.ts @@ -0,0 +1,3 @@ +export * from './tutorial-item-product.entity'; +export * from './tutorial-item-user.entity'; +export * from './tutorial-item.entity'; diff --git a/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-product.entity.ts b/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-product.entity.ts new file mode 100644 index 0000000..db46c89 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-product.entity.ts @@ -0,0 +1,37 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { TutorialProductType } from '../../common'; +import { TutorialItemProductDto } from '../dto'; + +@Entity() +export class TutorialItemProduct { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + itemId: number; + + @Column() + type: TutorialProductType; + + @Column({ nullable: true }) + objectId: number | null; + + constructor(accountId: number, itemId: number, type: TutorialProductType, objectId: number | null) { + this.accountId = accountId; + this.itemId = itemId; + this.type = type; + this.objectId = objectId; + } + + public static fromDto(accountId: number, itemId: number, dto: TutorialItemProductDto): TutorialItemProduct { + return new TutorialItemProduct(accountId, itemId, dto.type, dto.objectId ?? null); + } + + public toDto(): TutorialItemProductDto { + return new TutorialItemProductDto({ type: this.type, objectId: this.objectId }); + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-user.entity.ts b/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-user.entity.ts new file mode 100644 index 0000000..ac94145 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item-user.entity.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class TutorialItemUser { + @PrimaryColumn() + itemId: number; + + @PrimaryColumn() + userId: number; + + constructor(itemId: number, userId: number) { + this.itemId = itemId; + this.userId = userId; + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item.entity.ts b/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item.entity.ts new file mode 100644 index 0000000..1e951a5 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/entities/tutorial-item.entity.ts @@ -0,0 +1,81 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +import { DateUtil } from '@/common'; + +import { CreateTutorialItemDto, UpdateTutorialItemDto, TutorialItemDto } from '../dto'; +import { TutorialItemUser } from './tutorial-item-user.entity'; +import { TutorialItemProduct } from './tutorial-item-product.entity'; + +@Entity() +export class TutorialItem { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column() + accountId: number; + + @Column() + groupId: number; + + @Column() + name: string; + + @Column() + link: string; + + @Column() + sortOrder: number; + + @Column() + createdAt: Date; + + constructor(accountId: number, groupId: number, name: string, link: string, sortOrder: number, createdAt?: Date) { + this.accountId = accountId; + this.groupId = groupId; + this.name = name; + this.link = link; + this.sortOrder = sortOrder; + this.createdAt = createdAt ?? DateUtil.now(); + } + + private _users: TutorialItemUser[] | null = null; + public get users(): TutorialItemUser[] | null { + return this._users; + } + public set users(value: TutorialItemUser[] | null) { + this._users = value; + } + + private _products: TutorialItemProduct[] | null = null; + public get products(): TutorialItemProduct[] | null { + return this._products; + } + public set products(value: TutorialItemProduct[] | null) { + this._products = value; + } + + public static fromDto(accountId: number, groupId: number, dto: CreateTutorialItemDto): TutorialItem { + return new TutorialItem(accountId, groupId, dto.name, dto.link, dto.sortOrder); + } + + public update(dto: UpdateTutorialItemDto): TutorialItem { + this.name = dto.name ?? this.name; + this.link = dto.link ?? this.link; + this.sortOrder = dto.sortOrder ?? this.sortOrder; + + return this; + } + + public toDto(): TutorialItemDto { + return new TutorialItemDto({ + id: this.id, + groupId: this.groupId, + name: this.name, + link: this.link, + sortOrder: this.sortOrder, + createdAt: this.createdAt.toISOString(), + userIds: this.users?.length ? this.users.map((user) => user.userId) : null, + products: this.products?.length ? this.products.map((product) => product.toDto()) : null, + }); + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/index.ts b/backend/src/modules/tutorial/tutorial-item/index.ts new file mode 100644 index 0000000..6ca8652 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/index.ts @@ -0,0 +1,5 @@ +export * from './dto'; +export * from './entities'; +export * from './tutorial-item.controller'; +export * from './tutorial-item.handler'; +export * from './tutorial-item.service'; diff --git a/backend/src/modules/tutorial/tutorial-item/tutorial-item.controller.ts b/backend/src/modules/tutorial/tutorial-item/tutorial-item.controller.ts new file mode 100644 index 0000000..a57c859 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/tutorial-item.controller.ts @@ -0,0 +1,94 @@ +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; + +import { SortOrderListDto, TransformToDto } from '@/common'; +import { JwtAuthorized } from '@/modules/iam/common/decorators/jwt-authorized.decorator'; +import { CurrentAuth } from '@/modules/iam/common/decorators/current-auth.decorator'; +import { UserAccess } from '@/modules/iam/common/decorators/user-access.decorator'; +import { AuthData } from '@/modules/iam/common/types/auth-data'; + +import { TutorialItemDto, CreateTutorialItemDto, UpdateTutorialItemDto } from './dto'; +import { TutorialItemService } from './tutorial-item.service'; + +@ApiTags('tutorial/items') +@Controller('groups/:groupId/items') +@JwtAuthorized() +@TransformToDto() +export class TutorialItemController { + constructor(private readonly service: TutorialItemService) {} + + @ApiOperation({ summary: 'Create tutorial item', description: 'Create tutorial item' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id' }) + @ApiBody({ type: CreateTutorialItemDto, required: true, description: 'Data for creating tutorial item' }) + @ApiCreatedResponse({ description: 'Created tutorial item', type: TutorialItemDto }) + @Post() + @UserAccess({ adminOnly: true }) + public async create( + @CurrentAuth() { accountId }: AuthData, + @Param('groupId', ParseIntPipe) groupId: number, + @Body() dto: CreateTutorialItemDto, + ) { + return this.service.create(accountId, groupId, dto); + } + + @ApiOperation({ summary: 'Get tutorial items', description: 'Get tutorial items' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id' }) + @ApiOkResponse({ description: 'Tutorial items', type: [TutorialItemDto] }) + @Get() + public async findMany(@CurrentAuth() { accountId }: AuthData, @Param('groupId', ParseIntPipe) groupId: number) { + return this.service.findMany(accountId, { groupId }); + } + + @ApiOperation({ summary: 'Get tutorial item', description: 'Get tutorial item' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id' }) + @ApiParam({ name: 'itemId', type: Number, required: true, description: 'Tutorial item id' }) + @ApiOkResponse({ description: 'Tutorial item', type: TutorialItemDto }) + @Get(':itemId') + public async findOne( + @CurrentAuth() { accountId }: AuthData, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('itemId', ParseIntPipe) itemId: number, + ) { + return this.service.findOne(accountId, { groupId, itemId }); + } + + @ApiOperation({ summary: 'Sort tutorial items', description: 'Sort tutorial items' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id' }) + @ApiBody({ type: SortOrderListDto, required: true, description: 'Data for sorting tutorial items' }) + @ApiOkResponse() + @Patch('sort') + @UserAccess({ adminOnly: true }) + public async sort(@CurrentAuth() { accountId }: AuthData, @Body() dto: SortOrderListDto) { + return this.service.sort(accountId, dto); + } + + @ApiOperation({ summary: 'Update tutorial item', description: 'Update tutorial item' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id' }) + @ApiParam({ name: 'itemId', type: Number, required: true, description: 'Tutorial item id' }) + @ApiBody({ type: UpdateTutorialItemDto, required: true, description: 'Data for updating tutorial item' }) + @ApiOkResponse({ description: 'Updated tutorial item', type: TutorialItemDto }) + @Patch(':itemId') + @UserAccess({ adminOnly: true }) + public async update( + @CurrentAuth() { accountId }: AuthData, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('itemId', ParseIntPipe) itemId: number, + @Body() dto: UpdateTutorialItemDto, + ) { + return this.service.update(accountId, groupId, itemId, dto); + } + + @ApiOperation({ summary: 'Delete tutorial item', description: 'Delete tutorial item' }) + @ApiParam({ name: 'groupId', type: Number, required: true, description: 'Tutorial group id' }) + @ApiParam({ name: 'itemId', type: Number, required: true, description: 'Tutorial item id' }) + @ApiOkResponse() + @Delete(':itemId') + @UserAccess({ adminOnly: true }) + public async delete( + @CurrentAuth() { accountId }: AuthData, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('itemId', ParseIntPipe) itemId: number, + ) { + return this.service.delete(accountId, groupId, itemId); + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/tutorial-item.handler.ts b/backend/src/modules/tutorial/tutorial-item/tutorial-item.handler.ts new file mode 100644 index 0000000..8e75568 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/tutorial-item.handler.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; + +import { IamEventType, UserDeletedEvent } from '@/modules/iam/common'; +import { ProductsEventType, ProductsSectionEvent } from '@/modules/inventory/common'; +import { ScheduleEvent, SchedulerEventType } from '@/modules/scheduler/common'; +import { CrmEventType, EntityTypeEvent } from '@/CRM/common'; + +import { TutorialProductType } from '../common'; +import { TutorialItemService } from './tutorial-item.service'; + +@Injectable() +export class TutorialItemHandler { + constructor(private readonly service: TutorialItemService) {} + + @OnEvent(IamEventType.UserDeleted, { async: true }) + public async onUserDeleted(event: UserDeletedEvent) { + await this.service.deleteUser(event.accountId, event.userId); + } + + @OnEvent(CrmEventType.EntityTypeDeleted, { async: true }) + public async onEntityTypeDeleted(event: EntityTypeEvent) { + await this.service.deleteProduct(event.accountId, TutorialProductType.ENTITY_TYPE, event.entityTypeId); + } + + @OnEvent(ProductsEventType.ProductsSectionDeleted, { async: true }) + public async onProductsSectionDeleted(event: ProductsSectionEvent) { + await this.service.deleteProduct(event.accountId, TutorialProductType.PRODUCTS_SECTION, event.sectionId); + } + + @OnEvent(SchedulerEventType.ScheduleDeleted, { async: true }) + public async onScheduleDeleted(event: ScheduleEvent) { + await this.service.deleteProduct(event.accountId, TutorialProductType.SCHEDULER, event.scheduleId); + } +} diff --git a/backend/src/modules/tutorial/tutorial-item/tutorial-item.service.ts b/backend/src/modules/tutorial/tutorial-item/tutorial-item.service.ts new file mode 100644 index 0000000..73f9ee0 --- /dev/null +++ b/backend/src/modules/tutorial/tutorial-item/tutorial-item.service.ts @@ -0,0 +1,145 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Brackets, Repository } from 'typeorm'; + +import { NotFoundError, SortOrderListDto } from '@/common'; + +import { TutorialProductType } from '../common'; +import { CreateTutorialItemDto, UpdateTutorialItemDto } from './dto'; +import { TutorialItem, TutorialItemProduct, TutorialItemUser } from './entities'; + +interface FindFilter { + itemId?: number; + groupId?: number; + userId?: number; + productType?: TutorialProductType; + objectId?: number; + createdFrom?: Date; +} + +@Injectable() +export class TutorialItemService { + constructor( + @InjectRepository(TutorialItem) + private readonly itemRepository: Repository, + @InjectRepository(TutorialItemUser) + private readonly userRepository: Repository, + @InjectRepository(TutorialItemProduct) + private readonly productRepository: Repository, + ) {} + + public async create(accountId: number, groupId: number, dto: CreateTutorialItemDto): Promise { + const item = await this.itemRepository.save(TutorialItem.fromDto(accountId, groupId, dto)); + + if (dto.userIds?.length) { + item.users = await this.userRepository.save(dto.userIds.map((userId) => new TutorialItemUser(item.id, userId))); + } + + if (dto.products?.length) { + item.products = await this.productRepository.save( + dto.products.map((product) => TutorialItemProduct.fromDto(accountId, item.id, product)), + ); + } + + return item; + } + + public async findOne(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getOne(); + } + public async findMany(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).orderBy('ti.sort_order').getMany(); + } + public async count(accountId: number, filter?: FindFilter): Promise { + return this.createFindQb(accountId, filter).getCount(); + } + + public async update( + accountId: number, + groupId: number, + itemId: number, + dto: UpdateTutorialItemDto, + ): Promise { + const item = await this.findOne(accountId, { groupId, itemId }); + if (!item) { + throw NotFoundError.withId(TutorialItem, itemId); + } + + if (dto.userIds !== undefined) { + await this.userRepository.delete({ itemId: item.id }); + if (dto.userIds?.length) { + item.users = await this.userRepository.save(dto.userIds.map((userId) => new TutorialItemUser(item.id, userId))); + } else { + item.users = []; + } + } + + if (dto.products !== undefined) { + await this.productRepository.delete({ accountId, itemId: item.id }); + if (dto.products?.length) { + item.products = await this.productRepository.save( + dto.products.map((product) => TutorialItemProduct.fromDto(accountId, item.id, product)), + ); + } else { + item.products = []; + } + } + + return this.itemRepository.save(item.update(dto)); + } + + public async sort(accountId: number, dto: SortOrderListDto) { + for (const item of dto.items) { + await this.itemRepository.update({ id: item.id, accountId }, { sortOrder: item.sortOrder }); + } + } + + public async delete(accountId: number, groupId: number, itemId: number) { + await this.itemRepository.delete({ id: itemId, groupId, accountId }); + } + + public async deleteUser(_accountId: number, userId: number) { + await this.userRepository.delete({ userId }); + } + + public async deleteProduct(accountId: number, type: TutorialProductType, objectId: number) { + await this.productRepository.delete({ accountId, type, objectId }); + } + + private createFindQb(accountId: number, filter?: FindFilter) { + const qb = this.itemRepository + .createQueryBuilder('ti') + .leftJoinAndMapMany('ti.users', TutorialItemUser, 'tiu', 'ti.id = tiu.item_id') + .leftJoinAndMapMany('ti.products', TutorialItemProduct, 'tip', 'ti.id = tip.item_id') + .where('ti.account_id = :accountId', { accountId }); + if (filter?.itemId) { + qb.andWhere('ti.id = :id', { id: filter.itemId }); + } + if (filter?.groupId) { + qb.andWhere('ti.group_id = :groupId', { groupId: filter.groupId }); + } + if (filter?.userId) { + qb.andWhere( + new Brackets((qb1) => + qb1.where('tiu.user_id = :userId', { userId: filter.userId }).orWhere('tiu.user_id IS NULL'), + ), + ); + } + if (filter?.productType) { + qb.andWhere( + new Brackets((qb2) => qb2.where('tip.type = :type', { type: filter.productType }).orWhere('tip.type IS NULL')), + ); + } + if (filter?.objectId) { + qb.andWhere( + new Brackets((qb3) => + qb3.where('tip.object_id = :objectId', { objectId: filter.objectId }).orWhere('tip.object_id IS NULL'), + ), + ); + } + if (filter?.createdFrom) { + qb.andWhere('ti.created_at >= :createdFrom', { createdFrom: filter.createdFrom }); + } + return qb; + } +} diff --git a/backend/src/modules/tutorial/tutorial.module.ts b/backend/src/modules/tutorial/tutorial.module.ts new file mode 100644 index 0000000..97b41de --- /dev/null +++ b/backend/src/modules/tutorial/tutorial.module.ts @@ -0,0 +1,28 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { IAMModule } from '../iam/iam.module'; + +import tutorialConfig from './config/tutorial.config'; +import { + TutorialItem, + TutorialItemUser, + TutorialItemService, + TutorialItemController, + TutorialItemProduct, + TutorialItemHandler, +} from './tutorial-item'; +import { TutorialGroup, TutorialGroupService, TutorialGroupController } from './tutorial-group'; +import { TutorialCoreController, TutorialCoreService } from './tutorial-core'; + +@Module({ + imports: [ + ConfigModule.forFeature(tutorialConfig), + TypeOrmModule.forFeature([TutorialGroup, TutorialItem, TutorialItemUser, TutorialItemProduct]), + IAMModule, + ], + providers: [TutorialGroupService, TutorialItemService, TutorialItemHandler, TutorialCoreService], + controllers: [TutorialGroupController, TutorialItemController, TutorialCoreController], +}) +export class TutorialModule {} diff --git a/backend/src/support/config/index.ts b/backend/src/support/config/index.ts new file mode 100644 index 0000000..5b665e1 --- /dev/null +++ b/backend/src/support/config/index.ts @@ -0,0 +1 @@ +export * from './support.config'; diff --git a/backend/src/support/config/support.config.ts b/backend/src/support/config/support.config.ts new file mode 100644 index 0000000..1a816fe --- /dev/null +++ b/backend/src/support/config/support.config.ts @@ -0,0 +1,12 @@ +import { registerAs } from '@nestjs/config'; + +export interface SupportConfig { + accessCode: string; +} + +export default registerAs( + 'support', + (): SupportConfig => ({ + accessCode: process.env.SUPPORT_ACCESS_CODE, + }), +); diff --git a/backend/src/support/health/health.controller.ts b/backend/src/support/health/health.controller.ts new file mode 100644 index 0000000..8d9ebdd --- /dev/null +++ b/backend/src/support/health/health.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; +import { HealthCheck, HealthCheckService, MemoryHealthIndicator, TypeOrmHealthIndicator } from '@nestjs/terminus'; + +@ApiExcludeController(true) +@Controller('support/health') +export class HealthController { + constructor( + private readonly health: HealthCheckService, + private readonly db: TypeOrmHealthIndicator, + private readonly memory: MemoryHealthIndicator, + ) {} + + @Get() + @HealthCheck() + check() { + return this.health.check([ + () => this.db.pingCheck('database'), + () => this.memory.checkHeap('memory.heap', 2 * 1024 * 1024 * 1024), + () => this.memory.checkRSS('memory.rss', 2 * 1024 * 1024 * 1024), + ]); + } +} diff --git a/backend/src/support/health/health.module.ts b/backend/src/support/health/health.module.ts new file mode 100644 index 0000000..9ed184e --- /dev/null +++ b/backend/src/support/health/health.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TerminusModule } from '@nestjs/terminus'; + +import { HealthController } from './health.controller'; + +@Module({ + imports: [TerminusModule], + controllers: [HealthController], +}) +export class HealthModule {} diff --git a/backend/src/support/health/index.ts b/backend/src/support/health/index.ts new file mode 100644 index 0000000..4ac749f --- /dev/null +++ b/backend/src/support/health/index.ts @@ -0,0 +1,2 @@ +export * from './health.controller'; +export * from './health.module'; diff --git a/backend/src/support/heapdump/heapdump.controller.ts b/backend/src/support/heapdump/heapdump.controller.ts new file mode 100644 index 0000000..98cdc3e --- /dev/null +++ b/backend/src/support/heapdump/heapdump.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Post, Query } from '@nestjs/common'; +import { ApiExcludeController } from '@nestjs/swagger'; + +import { HeapdumpService } from './heapdump.service'; + +@ApiExcludeController(true) +@Controller('support/heapdump') +export class HeapdumpController { + constructor(private readonly service: HeapdumpService) {} + + @Post('create') + public async writeSnapshot(@Query('code') code: string) { + return this.service.writeSnapshot(code); + } +} diff --git a/backend/src/support/heapdump/heapdump.module.ts b/backend/src/support/heapdump/heapdump.module.ts new file mode 100644 index 0000000..07ae1ea --- /dev/null +++ b/backend/src/support/heapdump/heapdump.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { HeapdumpService } from './heapdump.service'; +import { HeapdumpController } from './heapdump.controller'; + +@Module({ + providers: [HeapdumpService], + controllers: [HeapdumpController], +}) +export class HeapdumpModule {} diff --git a/backend/src/support/heapdump/heapdump.service.ts b/backend/src/support/heapdump/heapdump.service.ts new file mode 100644 index 0000000..60fb66f --- /dev/null +++ b/backend/src/support/heapdump/heapdump.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { DateUtil } from '@/common'; + +import { SupportConfig } from '../config'; + +@Injectable() +export class HeapdumpService { + private readonly _config: SupportConfig | undefined; + + constructor(private readonly configService: ConfigService) { + this._config = this.configService.get('support'); + } + + public async writeSnapshot(code: string) { + if (this._config?.accessCode && this._config.accessCode === code) { + try { + const { writeSnapshot } = await import('heapdump'); + writeSnapshot(`heapdump-${DateUtil.now().toISOString()}.heapsnapshot`); + } catch (error) { + console.error('Heapdump not available:', error); + } + } + } +} diff --git a/backend/src/support/heapdump/index.ts b/backend/src/support/heapdump/index.ts new file mode 100644 index 0000000..5b64774 --- /dev/null +++ b/backend/src/support/heapdump/index.ts @@ -0,0 +1,3 @@ +export * from './heapdump.controller'; +export * from './heapdump.module'; +export * from './heapdump.service'; diff --git a/backend/src/support/support.module.ts b/backend/src/support/support.module.ts new file mode 100644 index 0000000..410e613 --- /dev/null +++ b/backend/src/support/support.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { ConditionalModule, ConfigModule } from '@nestjs/config'; + +import supportConfig from './config/support.config'; +import { HeapdumpModule } from './heapdump'; +import { HealthModule } from './health'; +import { VersionModule } from './version'; + +@Module({ + imports: [ + ConfigModule.forFeature(supportConfig), + ConditionalModule.registerWhen(HeapdumpModule, 'SUPPORT_HEAPDUMP_ENABLED'), + ConditionalModule.registerWhen(HealthModule, 'SUPPORT_HEALTH_ENABLED'), + VersionModule, + ], +}) +export class SupportModule {} diff --git a/backend/src/support/version/dto/create-version.dto.ts b/backend/src/support/version/dto/create-version.dto.ts new file mode 100644 index 0000000..ca6ded6 --- /dev/null +++ b/backend/src/support/version/dto/create-version.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; + +export class CreateVersionDto { + @ApiProperty({ description: 'Version number, should be in the format of x.x.x, where x is a number' }) + @IsString() + @IsNotEmpty() + version: string; + + @ApiPropertyOptional() + @IsOptional() + code: string; +} diff --git a/backend/src/support/version/dto/current-version.dto.ts b/backend/src/support/version/dto/current-version.dto.ts new file mode 100644 index 0000000..4bec118 --- /dev/null +++ b/backend/src/support/version/dto/current-version.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class CurrentVersionDto { + @ApiProperty({ description: 'Current version of an app in x.x.x format, where x is a number' }) + @IsString() + currentVersion: string; +} diff --git a/backend/src/support/version/dto/index.ts b/backend/src/support/version/dto/index.ts new file mode 100644 index 0000000..10d7e68 --- /dev/null +++ b/backend/src/support/version/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-version.dto'; +export * from './current-version.dto'; +export * from './version.dto'; diff --git a/backend/src/support/version/dto/version.dto.ts b/backend/src/support/version/dto/version.dto.ts new file mode 100644 index 0000000..62ff4c9 --- /dev/null +++ b/backend/src/support/version/dto/version.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNumber, IsString } from 'class-validator'; + +export class VersionDto { + @ApiProperty({ description: 'Version ID' }) + @IsNumber() + id: number; + + @ApiProperty({ description: 'Version number in x.x.x format, where x is a number' }) + @IsString() + version: string; + + @ApiProperty({ description: 'Version creation date in ISO format' }) + @IsString() + date: string; +} diff --git a/backend/src/support/version/entities/index.ts b/backend/src/support/version/entities/index.ts new file mode 100644 index 0000000..4c0d419 --- /dev/null +++ b/backend/src/support/version/entities/index.ts @@ -0,0 +1 @@ +export * from './version.entity'; diff --git a/backend/src/support/version/entities/version.entity.ts b/backend/src/support/version/entities/version.entity.ts new file mode 100644 index 0000000..b923378 --- /dev/null +++ b/backend/src/support/version/entities/version.entity.ts @@ -0,0 +1,33 @@ +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; +import { VersionDto } from '../dto/version.dto'; +import { DateUtil } from '@/common'; +import type { CreateVersionDto } from '../dto/create-version.dto'; + +@Entity() +export class Version { + @PrimaryGeneratedColumn('identity') + id: number; + + @Column({ type: 'varchar', length: 16, unique: true }) + version: string; + + @Column({ type: 'timestamp without time zone' }) + date: Date; + + constructor(version: string) { + this.version = version; + this.date = DateUtil.now(); + } + + static fromDto(dto: CreateVersionDto): Version { + return new Version(dto.version); + } + + public toDto(): VersionDto { + return { + id: this.id, + version: this.version, + date: this.date.toISOString(), + }; + } +} diff --git a/backend/src/support/version/index.ts b/backend/src/support/version/index.ts new file mode 100644 index 0000000..f7432d9 --- /dev/null +++ b/backend/src/support/version/index.ts @@ -0,0 +1,3 @@ +export * from './version.controller'; +export * from './version.module'; +export * from './version.service'; diff --git a/backend/src/support/version/version.controller.ts b/backend/src/support/version/version.controller.ts new file mode 100644 index 0000000..d94257e --- /dev/null +++ b/backend/src/support/version/version.controller.ts @@ -0,0 +1,51 @@ +import { TransformToDto } from '@/common'; +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; +import { + ApiBody, + ApiCreatedResponse, + ApiExcludeEndpoint, + ApiOkResponse, + ApiOperation, + ApiParam, + ApiTags, +} from '@nestjs/swagger'; + +import { CreateVersionDto, VersionDto, CurrentVersionDto } from './dto'; +import { VersionService } from './version.service'; + +@ApiTags('support/version') +@Controller('support/version') +@TransformToDto() +export class VersionController { + constructor(private readonly service: VersionService) {} + + @ApiExcludeEndpoint(true) + @ApiOperation({ + summary: 'Create frontend version', + }) + @ApiBody({ type: CreateVersionDto, required: true, description: 'Data for creating frontend version' }) + @ApiCreatedResponse({ description: 'Created version item', type: VersionDto }) + @Post('frontend') + async create(@Body() dto: CreateVersionDto) { + return this.service.create(dto); + } + + @ApiOperation({ + summary: 'Get latest frontend version', + description: 'Latest frontend version will be return if it is greater than the current version, otherwise null', + }) + @ApiParam({ + name: 'currentVersion', + type: String, + required: true, + description: 'Current version of the frontend app, usually retrieved from the "version" property of package.json', + }) + @ApiOkResponse({ + type: VersionDto, + description: 'Latest frontend version or null if provided current version is greater than the latest version', + }) + @Get('frontend/latest') + async getLatest(@Query() dto: CurrentVersionDto) { + return this.service.getLatest(dto); + } +} diff --git a/backend/src/support/version/version.module.ts b/backend/src/support/version/version.module.ts new file mode 100644 index 0000000..8b0c32b --- /dev/null +++ b/backend/src/support/version/version.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { VersionService } from './version.service'; +import { VersionController } from './version.controller'; +import { Version } from './entities/version.entity'; +import { IAMModule } from '@/modules/iam/iam.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([Version]), IAMModule], + providers: [VersionService], + controllers: [VersionController], +}) +export class VersionModule {} diff --git a/backend/src/support/version/version.service.ts b/backend/src/support/version/version.service.ts new file mode 100644 index 0000000..0d4dfcb --- /dev/null +++ b/backend/src/support/version/version.service.ts @@ -0,0 +1,73 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { ForbiddenError } from '@/common'; + +import { SupportConfig } from '../config'; +import { CreateVersionDto, CurrentVersionDto } from './dto'; +import { Version } from './entities'; + +const cacheKey = 'Version:latest'; + +@Injectable() +export class VersionService { + private readonly _config: SupportConfig | undefined; + + constructor( + private readonly configService: ConfigService, + @InjectRepository(Version) + private readonly repository: Repository, + private readonly dataSource: DataSource, + ) { + this._config = this.configService.get('support'); + } + + async create(dto: CreateVersionDto): Promise { + if (this._config?.accessCode && this._config.accessCode === dto.code) { + await this.dataSource.queryResultCache?.remove([cacheKey]); + + return this.repository.save(Version.fromDto(dto)); + } else { + throw new ForbiddenError(); + } + } + + async getLatest({ currentVersion }: CurrentVersionDto): Promise { + const isLatestGreater = ({ + currentVersion, + latestVersion, + }: { + currentVersion: string; + latestVersion: string; + }): boolean => { + const currentVersionParts = currentVersion.split('.').map((p) => parseInt(p, 10)); + const latestVersionParts = latestVersion.split('.').map((p) => parseInt(p, 10)); + + // Ensure both versions have the same length by padding with zeroes + const maxLength = Math.max(currentVersionParts.length, latestVersionParts.length); + + for (let i = 0; i < maxLength; i++) { + const currentVersionPart = currentVersionParts[i] || 0; + const latestVersionPart = latestVersionParts[i] || 0; + + if (currentVersionPart < latestVersionPart) return true; + if (currentVersionPart > latestVersionPart) return false; + } + + return false; // Versions are equal + }; + + const latestVersion = await this.repository + .createQueryBuilder('version') + .orderBy('version.date', 'DESC') + .limit(1) + .cache(cacheKey, 1209600000) + .getOne(); + + return latestVersion && isLatestGreater({ currentVersion, latestVersion: latestVersion.version }) + ? latestVersion + : null; + } +} diff --git a/backend/src/types/environment.d.ts b/backend/src/types/environment.d.ts new file mode 100644 index 0000000..b8b3dc6 --- /dev/null +++ b/backend/src/types/environment.d.ts @@ -0,0 +1,99 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + readonly npm_package_version: string; + readonly APPLICATION_BASE_URL: string; + readonly APPLICATION_BASE_URL_TEMPLATE: string; + readonly APPLICATION_PORT: string; + readonly APPLICATION_NAME: string; + readonly APPLICATION_FEEDBACK_EMAIL: string; + readonly APPLICATION_SUPPORT_EMAIL: string; + readonly APPLICATION_VERIFICATION_TOKEN: string; + readonly APPLICATION_API_KEY_REQUIRED: string; + readonly APPLICATION_SKELETON_KEY: string; + readonly APPLICATION_SUBDOMAIN_PREFIX: string; + readonly API_DOC_ENABLED: string; + readonly SCHEDULE_ENABLED: string; + readonly SCHEDULE_CHAT_SCHEDULED_DISABLE: string; + readonly SCHEDULE_INTEGRATION_GOOGLE_CALENDAR_DISABLE: string; + readonly SCHEDULE_MAIL_ACTIVE_DISABLE: string; + readonly SCHEDULE_MAIL_INACTIVE_DISABLE: string; + readonly SCHEDULE_MAIL_SCHEDULED_DISABLE: string; + readonly SCHEDULE_NOTIFICATION_TASK_OVERDUE_DISABLE: string; + readonly SCHEDULE_NOTIFICATION_TASK_BEFORE_START_DISABLE: string; + readonly SCHEDULE_NOTIFICATION_TASK_OVERDUE_FOLLOW_DISABLE: string; + readonly SCHEDULE_NOTIFICATION_ACTIVITY_OVERDUE_DISABLE: string; + readonly SCHEDULE_NOTIFICATION_ACTIVITY_BEFORE_START_DISABLE: string; + readonly SCHEDULE_NOTIFICATION_ACTIVITY_OVERDUE_FOLLOW_DISABLE: string; + readonly SCHEDULE_PRODUCTS_ORDER_CHECK_CANCEL_DISABLE: string; + readonly WINSTON_ENABLED: string; + readonly NEW_RELIC_ENABLED: string; + readonly ACCOUNT_SETUP_ACCOUNT_ID: string; + readonly ACCOUNT_SETUP_CONTACT_ID: string; + readonly ACCOUNT_SETUP_DEAL_ID: string; + readonly ACCOUNT_SETUP_DEAL_BOARD_ID: string; + readonly ACCOUNT_SETUP_RESPONSIBLE_ID: string; + readonly STORAGE_DOWNLOAD_PATH: string; + readonly STORAGE_IMAGE_PATH: string; + readonly STORAGE_TEMPORARY_PATH: string; + readonly DOCUMENTS_HOST: string; + readonly AWS_ENDPOINT_URL: string; + readonly AWS_REGION: string; + readonly AWS_BUCKET: string; + readonly AWS_ACCESS_KEY_ID: string; + readonly AWS_SECRET_ACCESS_KEY: string; + readonly AUTOMATION_JOB_DISCOVERY_ENABLED: string; + readonly VOXIMPLANT_PARENT_ACCOUNT_ID: string; + readonly VOXIMPLANT_PARENT_ACCOUNT_API_KEY: string; + readonly VOXIMPLANT_CREDENTIALS_FILE: string; + readonly FB_APP_ID: string; + readonly FB_APP_SECRET: string; + readonly FB_APP_ACCESS_TOKEN: string; + readonly FB_MESSENGER_AUTH_CONFIG_ID: string; + readonly FB_MESSENGER_VALIDATION_TOKEN: string; + readonly GA_MEASUREMENT_ID: string; + readonly GA_API_SECRET: string; + readonly WAZZUP_SECRET: string; + readonly WAZZUP_PROCESS_ENQUEUE: string; + readonly POSTGRES_HOST: string; + readonly POSTGRES_PORT: string; + readonly POSTGRES_USER: string; + readonly POSTGRES_PASSWORD: string; + readonly POSTGRES_DB: string; + readonly POSTGRES_QUERY_LOGGING: string; + readonly TYPEORM_CACHE_TYPE: string; + readonly TYPEORM_CACHE_DURATION: string; + readonly CACHE_TYPE: string; + readonly CACHE_IOREDIS_HOST: string; + readonly CACHE_IOREDIS_PORT: string; + readonly STRIPE_SECRET: string; + readonly STRIPE_WEBHOOK_SECRET: string; + readonly SUPPORT_ACCESS_CODE: string; + readonly MAILING_HOST: string; + readonly MAILING_PORT: string; + readonly MAILING_SECURE: string; + readonly MAILING_USER: string; + readonly MAILING_PASSWORD: string; + readonly MAILING_FROM: string; + readonly MAILING_REPLY_TO: string; + readonly TUTORIAL_LANGUAGE: string; + readonly GOOGLE_AUTH_CLIENT_ID: string; + readonly GOOGLE_AUTH_CLIENT_SECRET: string; + readonly GMAIL_API_CLIENT_ID: string; + readonly GMAIL_API_CLIENT_SECRET: string; + readonly MAIL_MANUAL_SEARCH_TIMEOUT: string; + readonly MAIL_MANUAL_SEARCH_BATCH_SIZE: string; + readonly MAIL_MANUAL_PART_LOAD_TIMEOUT: string; + readonly MAIL_IMAPFLOW_SEARCH_TIMEOUT: string; + readonly MAIL_IMAPFLOW_SEARCH_BATCH_SIZE: string; + readonly MAIL_IMAPFLOW_PART_LOAD_TIMEOUT: string; + readonly DATA_ENRICHMENT_GEOHELPER_API_KEY: string; + readonly DATA_ENRICHMENT_DADATA_API_KEY: string; + readonly APPSUMO_CLIENT_ID: string; + readonly APPSUMO_CLIENT_SECRET: string; + readonly APPSUMO_PRIVATE_KEY: string; + } + } +} + +export {}; diff --git a/backend/src/types/express.d.ts b/backend/src/types/express.d.ts new file mode 100644 index 0000000..c289870 --- /dev/null +++ b/backend/src/types/express.d.ts @@ -0,0 +1,15 @@ +// types/express.d.ts +import 'express'; + +declare module 'express' { + export interface Request { + id?: string; + subdomain?: string; + callerAccountId?: number; + accountId?: number; + userId?: number; + token?: unknown; + account?: unknown; + user?: unknown; + } +} diff --git a/backend/src/types/imapflow/index.d.ts b/backend/src/types/imapflow/index.d.ts new file mode 100644 index 0000000..87122e4 --- /dev/null +++ b/backend/src/types/imapflow/index.d.ts @@ -0,0 +1,394 @@ +declare module 'imapflow' { + import { EventEmitter } from 'stream'; + export type Readable = import('stream').Readable; + + export class ImapFlow extends EventEmitter { + constructor(options: ImapFlowOptions); + authenticated: string | boolean; + capabilities: Map; + emitLogs: boolean; + enabled: Set; + id: string; + idling: boolean; + mailbox: MailboxObject | boolean; + secureConnection: boolean; + serverInfo: IdInfoObject; + usable: boolean; + + append( + path: string, + content: string | Buffer, + flags?: string[], + idate?: Date | string, + ): Promise; + + connect(): Promise; + logout(): Promise; + close(): void; + download( + range: SequenceString, + part?: string, + options?: { uid?: boolean; maxBytes?: number; chunkSize?: number }, + ): Promise; + + getMailboxLock(path: string, options?: null | { readonly?: boolean }): Promise; + + getQuota(path: string): Promise; + + idle(): Promise; + + /** + * @see {@link https://imapflow.com/module-imapflow-ImapFlow.html#list} + */ + list(options?: ListOptions): Promise; + + listTree(options?: ListOptions): Promise; + + mailboxClose(): Promise; + + mailboxCreate(path: string | string[]): Promise; + + mailboxDelete(path: string | string[]): Promise; + + mailboxOpen(path: string | string[], options?: { readOnly?: boolean }): Promise; + + mailboxRename(path: string | string[], newPath: string | string[]): Promise; + + mailboxSubscribe(path: string | string[]): Promise; + + mailboxUnsubscribe(path: string | string[]): Promise; + + messageCopy( + range: SequenceString | number[] | SearchObject, + destination: string, + options?: { uid?: boolean }, + ): Promise; + + messageDelete(range: SequenceString | number[] | SearchObject, options?: { uid?: boolean }): Promise; + + messageFlagsAdd( + range: SequenceString | number[] | SearchObject, + Array: string[], + options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean }, + ): Promise; + + messageFlagsRemove( + range: SequenceString | number[] | SearchObject, + Array: string[], + options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean }, + ): Promise; + + messageFlagsSet( + range: SequenceString | number[] | SearchObject, + Array: string[], + options?: { uid?: boolean; unchangedSince?: bigint; useLabels?: boolean }, + ): Promise; + + messageMove( + range: SequenceString | number[] | SearchObject, + destination: string, + options?: { uid?: boolean }, + ): Promise; + + fetchOne( + seq: SequenceString, + query: FetchQueryObject, + options?: { + uid?: boolean; + }, + ): Promise; + + noop(): Promise; + + search(query: SearchObject, options?: { uid?: boolean }): Promise; + + status( + path: string, + query: { + messages?: boolean; + recent?: boolean; + uidNext?: boolean; + uidValidity?: boolean; + unseen?: boolean; + highestModseq?: boolean; + }, + ): Promise; + + fetch( + range: SequenceString | number[] | SearchObject, + query: FetchQueryObject, + options?: { uid?: boolean; changedSince?: bigint; binary?: boolean }, + ): AsyncGenerator; + + fetchAll( + range: SequenceString | number[] | SearchObject, + query: FetchQueryObject, + options?: { uid?: boolean; changedSince?: bigint; binary?: boolean }, + ): Promise; + } + + export interface ImapFlowOptions { + host: string; + port: number; + auth: { + user: string; + pass?: string; + accessToken?: string; + }; + secure?: boolean; + servername?: string; + disableCompression?: boolean; + clientInfo?: IdInfoObject; + disableAutoIdle?: boolean; + tls?: object; + logger?: Logger | false; + emitLogs?: boolean; + verifyOnly?: boolean; + logRaw?: boolean; + proxy?: string; + qresync?: boolean; + maxIdleTime?: number; + missingIdleCommand?: string; + disableBinary?: boolean; + disableAutoEnable?: boolean; + connectionTimeout?: number; + greetingTimeout?: number; + socketTimeout?: number; + } + + export interface AppendResponseObject { + path: string; + uidValidity?: bigint; + uid?: number; + seq?: number; + } + + export interface CopyResponseObject { + path: string; + destination: string; + uidValidity?: bigint; + uidMap?: Map; + } + + export interface DownloadObject { + content: Readable; + meta: { + expectedSize: number; + contentType: string; + charset?: string; + disposition?: string; + filename?: string; + }; + } + + export interface MailboxObject { + path: string; + delimiter: string; + flags: Set; + specialUse: string; + listed: boolean; + subscribed: boolean; + permanentFlags: Set; + mailboxId: string; + highestModseq: bigint; + uidValidity: bigint; + uidNext: number; + exists: number; + } + + export interface MailboxLockObject { + path: string; + release: () => void; + } + + export interface FetchMessageObject { + seq: number; + uid: number; + source: Buffer; + modseq: bigint; + emailId: string; + threadId?: string; + labels: Set; + size: number; + flags: Set; + envelope: MessageEnvelopeObject; + bodyStructure: MessageStructureObject; + internalDate: Date; + bodyParts: Map; + headers: Buffer; + } + + export interface FetchQueryObject { + uid?: boolean; + flags?: boolean; + bodyStructure?: boolean; + envelope?: boolean; + internalDate?: boolean; + size?: boolean; + source?: boolean | object; + threadId?: boolean; + labels?: boolean; + headers?: boolean | string[]; + bodyParts?: string[]; + } + + export interface MailboxRenameResponse { + path: string; + newPath: string; + } + + export interface MessageAddressObject { + name?: string; + address?: string; + } + + export interface MessageEnvelopeObject { + date: Date; + subject: string; + messageId: string; + inReplyTo: string; + from: MessageAddressObject[]; + sender: MessageAddressObject[]; + replyTo: MessageAddressObject[]; + to: MessageAddressObject[]; + cc: MessageAddressObject[]; + bcc: MessageAddressObject[]; + } + + export interface QuotaResponse { + path: string; + storage?: object; + messages?: object; + } + + export type SequenceString = string; + + export interface SearchObject { + seq?: SequenceString; + answered?: boolean; + deleted?: boolean; + draft?: boolean; + flagged?: boolean; + seen?: boolean; + all?: boolean; + new?: boolean; + old?: boolean; + recent?: boolean; + from?: string; + to?: string; + cc?: string; + bcc?: string; + body?: string; + subject?: string; + larger?: number; + smaller?: number; + uid?: SequenceString; + modseq?: bigint; + emailId?: string; + threadId?: string; + before?: Date | string; + on?: Date | string; + since?: Date | string; + sentBefore?: Date | string; + sentOn?: Date | string; + sentSince?: Date | string; + keyword?: string; + unKeyword?: string; + header?: Record; + or?: SearchObject[]; + } + + export interface StatusObject { + path: string; + messages?: number; + recent?: number; + uidNext?: number; + uidValidity?: bigint; + unseen?: number; + highestModseq?: bigint; + } + + export interface IdInfoObject { + name?: string; + version?: string; + os?: string; + vendor?: string; + 'support-url'?: string; + date?: Date; + } + + export interface ListResponse { + path: string; + name: string; + delimiter: string; + flags: Set; + specialUse: string; + listed: boolean; + subscribed: boolean; + status?: StatusObject; + } + + export interface ListTreeResponse { + root: boolean; + path: string; + name: string; + delimiter: string; + flags: []; + specialUse: string; + listed: boolean; + subscribed: boolean; + disabled: boolean; + folders: ListTreeResponse[]; + } + + export interface MailboxCreateResponse { + path: string; + mailboxId?: string; + created: boolean; + } + + export interface MailboxDeleteResponse { + path: string; + } + + export interface MessageStructureObject { + part: string; + type: string; + parameters: string; + id: string; + encoding: string; + size: number; + envelope: MessageEnvelopeObject; + disposition: string; + dispositionParameters: string; + childNodes: MessageStructureObject[]; + } + + export interface Logger { + debug: (obj: object) => void; + info: (obj: object) => void; + warn: (obj: object) => void; + error: (obj: object) => void; + } + + export interface ListOptions { + statusQuery?: StatusQuery; + specialUseHints?: SpecialUseHints; + } + + export interface StatusQuery { + messages?: boolean; + recent?: boolean; + uidNext?: boolean; + uidValidity?: boolean; + unseen?: boolean; + highestModseq?: boolean; + } + + export interface SpecialUseHints { + sent: string; + trash: string; + junk: string; + drafts: string; + } +} diff --git a/backend/src/types/reset.d.ts b/backend/src/types/reset.d.ts new file mode 100644 index 0000000..12bd3ed --- /dev/null +++ b/backend/src/types/reset.d.ts @@ -0,0 +1 @@ +import '@total-typescript/ts-reset'; diff --git a/backend/tsconfig.build.json b/backend/tsconfig.build.json new file mode 100644 index 0000000..8b195b3 --- /dev/null +++ b/backend/tsconfig.build.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": "./dist/.tsbuildinfo", + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": false + }, + "exclude": [ + "node_modules", + "test", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.e2e-spec.ts" + ] +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..796fc52 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,56 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2022", + "declaration": true, + "removeComments": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "esModuleInterop": true, + "resolveJsonModule": true, + + "paths": { + "@/*": ["src/*"], + }, + "typeRoots": ["./types", "./node_modules/@types"], + + // Strict Checks + "strict": true, + "noImplicitAny": false, + "strictNullChecks": false, + "strictPropertyInitialization": false, + "exactOptionalPropertyTypes": false, + "useUnknownInCatchVariables": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "strictBindCallApply": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + // Linter Checks + "noImplicitReturns": true, + "noImplicitOverride": true, + "forceConsistentCasingInFileNames": true, + // https://eslint.org/docs/rules/consistent-return ? + "noFallthroughCasesInSwitch": true, + // https://eslint.org/docs/rules/no-fallthrough + "noUnusedLocals": true, + // https://eslint.org/docs/rules/no-unused-vars + "noUnusedParameters": true, + // https://eslint.org/docs/rules/no-unused-vars#args + "allowUnreachableCode": false, + // https://eslint.org/docs/rules/no-unreachable ? + "allowUnusedLabels": false, + // https://eslint.org/docs/rules/no-unused-labels + // Base Strict Checks + "noImplicitUseStrict": false, + "suppressExcessPropertyErrors": false, + "suppressImplicitAnyIndexErrors": false, + "noStrictGenericChecks": false + } +} diff --git a/backend/var/jwt/private.pem b/backend/var/jwt/private.pem new file mode 100644 index 0000000..90a65a7 --- /dev/null +++ b/backend/var/jwt/private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj4+xjbxSELVpp +1jKE140Q+xk8ckA6+DlC/n57HYOp1mNPKm3i0i+SN5cD28GQzjW72pcpYZ8YleYE +oG9epoCyAodc0P8Bf6zqQhQoLP26idJbv+m6RhbEAoYTUcWz5IrIZ4aqE51HM3oJ +kOo2tDrug/zSX9zq+zS++YyBZOLkdc7NJ/0YgKJcvEsHbJuuxAzn6VszX8ClWQnm +uwUQ4voQaV2rTr7BvE2Y0J0PZMgDAt5fSD1x0wvIfUXO3/8MGe/uDLDQL4e3ztcE +VZgb2r28P1k85c9XcpCkdVN0moKyzrPKq+bcDBIbBV9Q5f3r7d4EYEnDalpHpgEY +f67yXI4DAgMBAAECggEASTJEo2w7B4WR+e72hSoYENt0u/BzC2NNf8RWDPpzkWj0 +1ainh0REhtNZGRoO63ONwCaymILHIZ3hK3PUCbvngplqh2O4YJz7R2zXv9HISIXB +c8TUyKMBC+3sn7hHyj5qVXMXS+KSvfgZqygT0vbP0zMTuYmjCzfCqQCfZjL+uvXD +oBz+TcCLK68dJSB5CoAh5EAs7FGMDIFAxYvBv96zQGrEuvfpucFc7v2XznChjGgb +sE9D8gg927z1PUbQsWv8SOvDHwHBqix9f8ph1phasmFNXN5ZAS7lYoIGX0Hro6T2 +RseW5h38toC3Lc4U9wmLIFexiegNsern4xRkdFnX4QKBgQDUQ79+RbjkkFX5zaCA +JOUPKBY+M1eGJIp0a4BS1/SNDTUOnLd6yQHoujKy61tJdSOS+jwSJeF0fhGF4r1u +PjarekW0i1hbUVK//KM1Q8OsHX5Tk/tsPYVLw3HFQpoiTvZOeGYItguMoMmQnmTK +iN8ZR4MJYHbpl5nOxjIOVvYA6wKBgQDFqJdw7Y22GY5HrEC6M+vKynWbSlFgz5S5 +xVtpuFLtRhy8lCOZq1me2oBl6oS+y1xYoft/KTPM4ygZLwH10f4V7qkOQwN+OuAC +SB8+eBK4LO54KbtDkRXYpEkmUJNSVHtWXiJGEqXVgCIvF8YvBYWXpMCwBqXHHhGs +Ygs7CkghSQKBgB1lVHu0RCrDImT56SRV97Llpk7u5Uwae2IsERVn+uId1h8z7OUA +OVd1kdfdaEMACfEs3mzU+igb3WlhQUKnMwMEZ+rc8VuUI5Wa8y9JNyv62afRcpxG +2NLpOjRLSPU/YjTzz42dSHQtQDza8rJpyhvCH4+I4G7xI8fTAtOhj2gJAoGACS4/ +entOLbsaJLIXf46R0SV+OOxGw1xg6BAGou5wy5yKEShATw7qZrp3ZER0TfhcHbHI +YKulQEr8vc61JJnQV2xyZbsvGlnZtcFr0hb5p5xOpz4o+IZwoVNgImtzrEtIP0a4 +CNEs6rG85LsR9XUoM1bvrD1izdDTuVIEe4WKvCECgYBEX5pIxbr3+ydEmkn/Wv/p +FfwBn9BcqYJ8ACNN2FwWv2uUYI+AyZnt+FpqfrIlG7yDNp4GJzTkxbZToj7IlT7u +45KgJwmd1GDfpdjPbs0lXFKoWacr/sOLhCkbtZaXAJWX96IiJCDoY3HpeWUg9Usn +kNr7g4gv0qhxwwImd5ubRQ== +-----END PRIVATE KEY----- diff --git a/backend/var/jwt/public.pem b/backend/var/jwt/public.pem new file mode 100644 index 0000000..89f5ef6 --- /dev/null +++ b/backend/var/jwt/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo+PsY28UhC1aadYyhNeN +EPsZPHJAOvg5Qv5+ex2DqdZjTypt4tIvkjeXA9vBkM41u9qXKWGfGJXmBKBvXqaA +sgKHXND/AX+s6kIUKCz9uonSW7/pukYWxAKGE1HFs+SKyGeGqhOdRzN6CZDqNrQ6 +7oP80l/c6vs0vvmMgWTi5HXOzSf9GICiXLxLB2ybrsQM5+lbM1/ApVkJ5rsFEOL6 +EGldq06+wbxNmNCdD2TIAwLeX0g9cdMLyH1Fzt//DBnv7gyw0C+Ht87XBFWYG9q9 +vD9ZPOXPV3KQpHVTdJqCss6zyqvm3AwSGwVfUOX96+3eBGBJw2paR6YBGH+u8lyO +AwIDAQAB +-----END PUBLIC KEY----- diff --git a/backend/var/voximplant/credentials.json b/backend/var/voximplant/credentials.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/backend/var/voximplant/credentials.json @@ -0,0 +1 @@ +{} diff --git a/backend/yarn.lock b/backend/yarn.lock new file mode 100644 index 0000000..24243c7 --- /dev/null +++ b/backend/yarn.lock @@ -0,0 +1,14269 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10 + +"@amwork/voximplant-apiclient-nodejs@npm:^2.3.0-f": + version: 2.3.0-f + resolution: "@amwork/voximplant-apiclient-nodejs@npm:2.3.0-f" + dependencies: + axios: "npm:0.21.4" + form-data: "npm:2.5.1" + jsonwebtoken: "npm:8.5.1" + checksum: 10/351b00f1eacc10ff42693df1d7a3b8ebc592cc98120b439123079c02b192814bacb6bb0921201b16d7ab67d3af453b719ddffa4f03a2ec63d8e21ee4f051f018 + languageName: node + linkType: hard + +"@angular-devkit/core@npm:19.2.17": + version: 19.2.17 + resolution: "@angular-devkit/core@npm:19.2.17" + dependencies: + ajv: "npm:8.17.1" + ajv-formats: "npm:3.0.1" + jsonc-parser: "npm:3.3.1" + picomatch: "npm:4.0.2" + rxjs: "npm:7.8.1" + source-map: "npm:0.7.4" + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + checksum: 10/59cc560e46ac2a9b6e7cb36eaaf025ecf936040130fe355611201f4ab733a8b7c4a3c3cb0db5443174157b5e2319cd563967d18143113393ac7dabd62ddb4256 + languageName: node + linkType: hard + +"@angular-devkit/core@npm:19.2.19": + version: 19.2.19 + resolution: "@angular-devkit/core@npm:19.2.19" + dependencies: + ajv: "npm:8.17.1" + ajv-formats: "npm:3.0.1" + jsonc-parser: "npm:3.3.1" + picomatch: "npm:4.0.2" + rxjs: "npm:7.8.1" + source-map: "npm:0.7.4" + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + checksum: 10/d56ecde9ca344a96bbc0a132cf18df0a5f952e8d6e40211f107f7ba5f7b54832f874770ed0c8852b1bc3cd409a51a97cf8a8d21642440f50542aab9bf88d2d16 + languageName: node + linkType: hard + +"@angular-devkit/schematics-cli@npm:19.2.19": + version: 19.2.19 + resolution: "@angular-devkit/schematics-cli@npm:19.2.19" + dependencies: + "@angular-devkit/core": "npm:19.2.19" + "@angular-devkit/schematics": "npm:19.2.19" + "@inquirer/prompts": "npm:7.3.2" + ansi-colors: "npm:4.1.3" + symbol-observable: "npm:4.0.0" + yargs-parser: "npm:21.1.1" + bin: + schematics: bin/schematics.js + checksum: 10/fac274082d5fba5265a2d5dd414749ada125404f646d8f4367f43973ece87776baa036b8bd7c0ed9cec3e64d01ad88ba2450d8333f3fbac72cedfa9a5f269f41 + languageName: node + linkType: hard + +"@angular-devkit/schematics@npm:19.2.17": + version: 19.2.17 + resolution: "@angular-devkit/schematics@npm:19.2.17" + dependencies: + "@angular-devkit/core": "npm:19.2.17" + jsonc-parser: "npm:3.3.1" + magic-string: "npm:0.30.17" + ora: "npm:5.4.1" + rxjs: "npm:7.8.1" + checksum: 10/1f90652841c1bd974c1b8cd514ef1b2c2679380bf9c6f1714adaee78f1b4c24752958549bd5a92114b7b2fefcedb1ff0ab0e418277e08c4905a168aabb24d63a + languageName: node + linkType: hard + +"@angular-devkit/schematics@npm:19.2.19": + version: 19.2.19 + resolution: "@angular-devkit/schematics@npm:19.2.19" + dependencies: + "@angular-devkit/core": "npm:19.2.19" + jsonc-parser: "npm:3.3.1" + magic-string: "npm:0.30.17" + ora: "npm:5.4.1" + rxjs: "npm:7.8.1" + checksum: 10/1ef5813fc39ba0b00e628658b4b3f385719699c8e6f9c34cfaf2b6a316f1aefb7bbd2ed7633c5fd1dc99e2c769ea38546ee736bbb122bff02ccbe27bea1e1c37 + languageName: node + linkType: hard + +"@aws-crypto/crc32@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/crc32@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10/1b0a56ad4cb44c9512d8b1668dcf9306ab541d3a73829f435ca97abaec8d56f3db953db03ad0d0698754fea16fcd803d11fa42e0889bc7b803c6a030b04c63de + languageName: node + linkType: hard + +"@aws-crypto/crc32c@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/crc32c@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10/08bd1db17d7c772fa6e34b38a360ce77ad041164743113eefa8343c2af917a419697daf090c5854129ef19f3a9673ed1fd8446e03eb32c8ed52d2cc409b0dee7 + languageName: node + linkType: hard + +"@aws-crypto/sha1-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha1-browser@npm:5.2.0" + dependencies: + "@aws-crypto/supports-web-crypto": "npm:^5.2.0" + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10/239f4c59cce9abd33c01117b10553fbef868a063e74faf17edb798c250d759a2578841efa2837e5e51854f52ef57dbc40780b073cae20f89ebed6a8cc7fa06f1 + languageName: node + linkType: hard + +"@aws-crypto/sha256-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-browser@npm:5.2.0" + dependencies: + "@aws-crypto/sha256-js": "npm:^5.2.0" + "@aws-crypto/supports-web-crypto": "npm:^5.2.0" + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10/2b1b701ca6caa876333b4eb2b96e5187d71ebb51ebf8e2d632690dbcdedeff038202d23adcc97e023437ed42bb1963b7b463e343687edf0635fd4b98b2edad1a + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:5.2.0, @aws-crypto/sha256-js@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-js@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10/f46aace7b873c615be4e787ab0efd0148ef7de48f9f12c7d043e05c52e52b75bb0bf6dbcb9b2852d940d7724fab7b6d5ff1469160a3dd024efe7a68b5f70df8c + languageName: node + linkType: hard + +"@aws-crypto/supports-web-crypto@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/supports-web-crypto@npm:5.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/6ed0c7e17f4f6663d057630805c45edb35d5693380c24ab52d4c453ece303c6c8a6ade9ee93c97dda77d9f6cae376ffbb44467057161c513dffa3422250edaf5 + languageName: node + linkType: hard + +"@aws-crypto/util@npm:5.2.0, @aws-crypto/util@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/util@npm:5.2.0" + dependencies: + "@aws-sdk/types": "npm:^3.222.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10/f80a174c404e1ad4364741c942f440e75f834c08278fa754349fe23a6edc679d480ea9ced5820774aee58091ed270067022d8059ecf1a7ef452d58134ac7e9e1 + languageName: node + linkType: hard + +"@aws-sdk/client-s3@npm:^3.817.0": + version: 3.967.0 + resolution: "@aws-sdk/client-s3@npm:3.967.0" + dependencies: + "@aws-crypto/sha1-browser": "npm:5.2.0" + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/credential-provider-node": "npm:3.967.0" + "@aws-sdk/middleware-bucket-endpoint": "npm:3.966.0" + "@aws-sdk/middleware-expect-continue": "npm:3.965.0" + "@aws-sdk/middleware-flexible-checksums": "npm:3.967.0" + "@aws-sdk/middleware-host-header": "npm:3.965.0" + "@aws-sdk/middleware-location-constraint": "npm:3.965.0" + "@aws-sdk/middleware-logger": "npm:3.965.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.965.0" + "@aws-sdk/middleware-sdk-s3": "npm:3.967.0" + "@aws-sdk/middleware-ssec": "npm:3.965.0" + "@aws-sdk/middleware-user-agent": "npm:3.967.0" + "@aws-sdk/region-config-resolver": "npm:3.965.0" + "@aws-sdk/signature-v4-multi-region": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-endpoints": "npm:3.965.0" + "@aws-sdk/util-user-agent-browser": "npm:3.965.0" + "@aws-sdk/util-user-agent-node": "npm:3.967.0" + "@smithy/config-resolver": "npm:^4.4.5" + "@smithy/core": "npm:^3.20.2" + "@smithy/eventstream-serde-browser": "npm:^4.2.7" + "@smithy/eventstream-serde-config-resolver": "npm:^4.3.7" + "@smithy/eventstream-serde-node": "npm:^4.2.7" + "@smithy/fetch-http-handler": "npm:^5.3.8" + "@smithy/hash-blob-browser": "npm:^4.2.8" + "@smithy/hash-node": "npm:^4.2.7" + "@smithy/hash-stream-node": "npm:^4.2.7" + "@smithy/invalid-dependency": "npm:^4.2.7" + "@smithy/md5-js": "npm:^4.2.7" + "@smithy/middleware-content-length": "npm:^4.2.7" + "@smithy/middleware-endpoint": "npm:^4.4.3" + "@smithy/middleware-retry": "npm:^4.4.19" + "@smithy/middleware-serde": "npm:^4.2.8" + "@smithy/middleware-stack": "npm:^4.2.7" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/node-http-handler": "npm:^4.4.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.18" + "@smithy/util-defaults-mode-node": "npm:^4.2.21" + "@smithy/util-endpoints": "npm:^3.2.7" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-retry": "npm:^4.2.7" + "@smithy/util-stream": "npm:^4.5.8" + "@smithy/util-utf8": "npm:^4.2.0" + "@smithy/util-waiter": "npm:^4.2.7" + tslib: "npm:^2.6.2" + checksum: 10/53dc42207b838918207bdda601fd5222dced8f9cf09a57922dc754063f1ff9eb0bb7fca54d563a0f6b7e52c4424c4f174e2d5cb23a4295a593725ff9d4ad1831 + languageName: node + linkType: hard + +"@aws-sdk/client-ses@npm:^3.731.1": + version: 3.967.0 + resolution: "@aws-sdk/client-ses@npm:3.967.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/credential-provider-node": "npm:3.967.0" + "@aws-sdk/middleware-host-header": "npm:3.965.0" + "@aws-sdk/middleware-logger": "npm:3.965.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.965.0" + "@aws-sdk/middleware-user-agent": "npm:3.967.0" + "@aws-sdk/region-config-resolver": "npm:3.965.0" + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-endpoints": "npm:3.965.0" + "@aws-sdk/util-user-agent-browser": "npm:3.965.0" + "@aws-sdk/util-user-agent-node": "npm:3.967.0" + "@smithy/config-resolver": "npm:^4.4.5" + "@smithy/core": "npm:^3.20.2" + "@smithy/fetch-http-handler": "npm:^5.3.8" + "@smithy/hash-node": "npm:^4.2.7" + "@smithy/invalid-dependency": "npm:^4.2.7" + "@smithy/middleware-content-length": "npm:^4.2.7" + "@smithy/middleware-endpoint": "npm:^4.4.3" + "@smithy/middleware-retry": "npm:^4.4.19" + "@smithy/middleware-serde": "npm:^4.2.8" + "@smithy/middleware-stack": "npm:^4.2.7" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/node-http-handler": "npm:^4.4.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.18" + "@smithy/util-defaults-mode-node": "npm:^4.2.21" + "@smithy/util-endpoints": "npm:^3.2.7" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-retry": "npm:^4.2.7" + "@smithy/util-utf8": "npm:^4.2.0" + "@smithy/util-waiter": "npm:^4.2.7" + tslib: "npm:^2.6.2" + checksum: 10/c8a4d62ed65feac8145174d42d18953646bb6c10d27cb5a725c69756d58d146ca0ea991d9587628dee549ae388e49b8cf315103d5d67286c274a512e427b49dc + languageName: node + linkType: hard + +"@aws-sdk/client-sso@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/client-sso@npm:3.967.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/middleware-host-header": "npm:3.965.0" + "@aws-sdk/middleware-logger": "npm:3.965.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.965.0" + "@aws-sdk/middleware-user-agent": "npm:3.967.0" + "@aws-sdk/region-config-resolver": "npm:3.965.0" + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-endpoints": "npm:3.965.0" + "@aws-sdk/util-user-agent-browser": "npm:3.965.0" + "@aws-sdk/util-user-agent-node": "npm:3.967.0" + "@smithy/config-resolver": "npm:^4.4.5" + "@smithy/core": "npm:^3.20.2" + "@smithy/fetch-http-handler": "npm:^5.3.8" + "@smithy/hash-node": "npm:^4.2.7" + "@smithy/invalid-dependency": "npm:^4.2.7" + "@smithy/middleware-content-length": "npm:^4.2.7" + "@smithy/middleware-endpoint": "npm:^4.4.3" + "@smithy/middleware-retry": "npm:^4.4.19" + "@smithy/middleware-serde": "npm:^4.2.8" + "@smithy/middleware-stack": "npm:^4.2.7" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/node-http-handler": "npm:^4.4.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.18" + "@smithy/util-defaults-mode-node": "npm:^4.2.21" + "@smithy/util-endpoints": "npm:^3.2.7" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-retry": "npm:^4.2.7" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/202df5a094385e5c31b5a598f376d2c80b9e6eb28f65258f0970c640626b2bb3660154e6139643b8dc763fb67856011c99f714f04f742ea5c4ffddd728822a86 + languageName: node + linkType: hard + +"@aws-sdk/core@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/core@npm:3.967.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/xml-builder": "npm:3.965.0" + "@smithy/core": "npm:^3.20.2" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/signature-v4": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/bf963572be0af3047c8d0dcb9b9b6e03b4939b0d33b4ddf819b20fb0f9df10c6918cc09efff0bb0e7e83e587d4ff39c9ed294bc579566c2a1c0598d77f15d5fa + languageName: node + linkType: hard + +"@aws-sdk/crc64-nvme@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/crc64-nvme@npm:3.965.0" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/c6a9ad1cf744c9e0f45908ade14da0d5941ce48116271ac4f58e97b27a55e0165fd7a021d3a865b9e37e3a14b3b8f074a815e21dc631800f7cdf090419918c87 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-env@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/ce2c1ad5ae4e14bedcf381f109d618cb756674193a71679ed92b51d6ba7e6dc753b1c805d13c94ed2811cee444a257e055e35b35b9c675d784ee7c2274db1755 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-http@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/fetch-http-handler": "npm:^5.3.8" + "@smithy/node-http-handler": "npm:^4.4.7" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-stream": "npm:^4.5.8" + tslib: "npm:^2.6.2" + checksum: 10/080a61872dcb7f4032a3484536876cc8016e0aa6d314afe0cfb12d3de2f3a2075b0613d7f176b03d9c0790e7139d13dd194d11e1df9f84d3e8c1a9de438e55d6 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/credential-provider-env": "npm:3.967.0" + "@aws-sdk/credential-provider-http": "npm:3.967.0" + "@aws-sdk/credential-provider-login": "npm:3.967.0" + "@aws-sdk/credential-provider-process": "npm:3.967.0" + "@aws-sdk/credential-provider-sso": "npm:3.967.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.967.0" + "@aws-sdk/nested-clients": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/credential-provider-imds": "npm:^4.2.7" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/3d110133574c6430bab8c928efec1f5d9c4b5fc17304ec6295ad6dca865287616b79b967bc5632093b3e21f10d2a39ecff648a5b822913deef0c420ca8310053 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-login@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-login@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/nested-clients": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/78c2a937e30d43c3dfddb8531ab8a5aa44fde359de1df185c2ab1138039789b83fc8d431967bf534cb06814577620397cc6594b0fe639596d87e3a3eb4d28ae2 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.967.0" + dependencies: + "@aws-sdk/credential-provider-env": "npm:3.967.0" + "@aws-sdk/credential-provider-http": "npm:3.967.0" + "@aws-sdk/credential-provider-ini": "npm:3.967.0" + "@aws-sdk/credential-provider-process": "npm:3.967.0" + "@aws-sdk/credential-provider-sso": "npm:3.967.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/credential-provider-imds": "npm:^4.2.7" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/7fc672af134d058fae3497fb776573164c4f973e6145bc49c24f0162063351de2878e5d1f4909335888c70ebacf62ae0ac26819b4d9388b7d56b117c261cd860 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-process@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/e6dbf90b17311050c70329c803a974c0bad0be343385793b5fc3d4101682c7c162dbdea5a941965c03edde9f5714ffaf86e32c72c3d3b0261277d99d7d439650 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-sso@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.967.0" + dependencies: + "@aws-sdk/client-sso": "npm:3.967.0" + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/token-providers": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/45a3aad27f304fce6ad46caf32b617710a39211c8859874332de02aa0e721e0f9fa028ba13e8c47387d54ee44bcc770d311523e895058569e2987d2b0d8a628e + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-web-identity@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/nested-clients": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/ba6a5be560aedf3dfff52b51f3b2b26cdbe0e721818e51fc4509ea636ac0408470c6bde4368b8dd9428a87f618f454672657048a8604d17d902879009a5b2f9e + languageName: node + linkType: hard + +"@aws-sdk/middleware-bucket-endpoint@npm:3.966.0": + version: 3.966.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.966.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-arn-parser": "npm:3.966.0" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-config-provider": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/538a39c2d3b95e9e3399dd4c45677f526c56c93dd9aeabc6263e7ce524a513cbcc67cccc9e4b739453cd001928922d5c96813360ac0989d548c27ea769409701 + languageName: node + linkType: hard + +"@aws-sdk/middleware-expect-continue@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/5f0f47c43f1c97398092eeabe6d71e31c76614bef2e4540923c57a3c769a21e5acf890aaa4ebd70708bd78c7da1ff9e43b789b12d4269496052c266e910e63be + languageName: node + linkType: hard + +"@aws-sdk/middleware-flexible-checksums@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.967.0" + dependencies: + "@aws-crypto/crc32": "npm:5.2.0" + "@aws-crypto/crc32c": "npm:5.2.0" + "@aws-crypto/util": "npm:5.2.0" + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/crc64-nvme": "npm:3.965.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/is-array-buffer": "npm:^4.2.0" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-stream": "npm:^4.5.8" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/2297b2841935ec72f3bdbfd69e4adc125a27513c486e6ec48067c31f4687c9b27add9f781e039ee2d9bea70b0644da47a0896ef3a532a0d7352364af32fb919a + languageName: node + linkType: hard + +"@aws-sdk/middleware-host-header@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/b97bea5587c09c7abd99c0d6ddaca5e4b4c234f6a8f997a4b2fb7ce41e638ddbc214e8766cbb9f179f5bdd6645943305469f62e492bc223e62e018a2ae4b5574 + languageName: node + linkType: hard + +"@aws-sdk/middleware-location-constraint@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/632fcd5c587171d563748577023f81fa63aadc2241559aff61abf60256e216d9c09d7af23b885980caaad29a734ff97251aed11b23891ab6e4347d4730bb0dba + languageName: node + linkType: hard + +"@aws-sdk/middleware-logger@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/middleware-logger@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/b0f4363b698e1254f9a4ddced3aabf0bc2aa9d1e570b76b70f42967ba879a72cc10aa99f53a0e881916de414b5a891b768a22bdc93d3facc15ece1cc0db818b2 + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@aws/lambda-invoke-store": "npm:^0.2.2" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/10c231d847fdea3b5affae8a32c51b91ca8d1beacfdbd6f6ee19b590faa7a3596eea656976fb2588cec6871cbea53b8fc824877b613e2c0336fa7bdb2b0eaa5f + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-s3@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-arn-parser": "npm:3.966.0" + "@smithy/core": "npm:^3.20.2" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/signature-v4": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-config-provider": "npm:^4.2.0" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-stream": "npm:^4.5.8" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/ce738c27ea8c5d11359d76c79718b1ce0eb07ae96e2d8b6c2437cc6369a00c9e7a85b7076d3685a5c2a49a861304acbecf5eb159d10cd7f3d93026c1d265fb57 + languageName: node + linkType: hard + +"@aws-sdk/middleware-ssec@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/0ef6ca70ca23c29759df106a6537a8fd9d2509f6b814f13e31b9e623fa9a3d3d3db0a2aee316c60dd1b64f2715f12ce49bf49968fc657ebd336eeb443072ec25 + languageName: node + linkType: hard + +"@aws-sdk/middleware-user-agent@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-endpoints": "npm:3.965.0" + "@smithy/core": "npm:^3.20.2" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/da8b8389ecd2bb5d53cd571a2b440f0aefd2fe0cfdf942d765e0ff8806bb56d171ecc2e4032becd16e5f6de01322e5234bac305b75e7d63367738e3dceaf81a8 + languageName: node + linkType: hard + +"@aws-sdk/nested-clients@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/nested-clients@npm:3.967.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/middleware-host-header": "npm:3.965.0" + "@aws-sdk/middleware-logger": "npm:3.965.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.965.0" + "@aws-sdk/middleware-user-agent": "npm:3.967.0" + "@aws-sdk/region-config-resolver": "npm:3.965.0" + "@aws-sdk/types": "npm:3.965.0" + "@aws-sdk/util-endpoints": "npm:3.965.0" + "@aws-sdk/util-user-agent-browser": "npm:3.965.0" + "@aws-sdk/util-user-agent-node": "npm:3.967.0" + "@smithy/config-resolver": "npm:^4.4.5" + "@smithy/core": "npm:^3.20.2" + "@smithy/fetch-http-handler": "npm:^5.3.8" + "@smithy/hash-node": "npm:^4.2.7" + "@smithy/invalid-dependency": "npm:^4.2.7" + "@smithy/middleware-content-length": "npm:^4.2.7" + "@smithy/middleware-endpoint": "npm:^4.4.3" + "@smithy/middleware-retry": "npm:^4.4.19" + "@smithy/middleware-serde": "npm:^4.2.8" + "@smithy/middleware-stack": "npm:^4.2.7" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/node-http-handler": "npm:^4.4.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/smithy-client": "npm:^4.10.4" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-body-length-node": "npm:^4.2.1" + "@smithy/util-defaults-mode-browser": "npm:^4.3.18" + "@smithy/util-defaults-mode-node": "npm:^4.2.21" + "@smithy/util-endpoints": "npm:^3.2.7" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-retry": "npm:^4.2.7" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/95f461e0a48b8fd40ba16e181e9021acd5c554d39f85d556cc06d771034fb1936c63e58bfc7069bc1e8b64037745e8cd7570f8c040d7b49c5033585d0a9ea045 + languageName: node + linkType: hard + +"@aws-sdk/region-config-resolver@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/config-resolver": "npm:^4.4.5" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/7e75ea9ddb14b588abbc015c920b6e4b6888c4afd19b7be456668597b16f2b0c8edd6808839e1d17c4407edbec967d3f5dab970ba6041dfd0966685ecefd2a07 + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.967.0" + dependencies: + "@aws-sdk/middleware-sdk-s3": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/signature-v4": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/56d4ab1db6255d89749e193033fd91de3e1fd771bfe184934615fca94a80d8e3e7cbae91f807125fbd5df112ef446e59c7faf9b196a7b57ce0c83f5bdddb75b3 + languageName: node + linkType: hard + +"@aws-sdk/token-providers@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/token-providers@npm:3.967.0" + dependencies: + "@aws-sdk/core": "npm:3.967.0" + "@aws-sdk/nested-clients": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/a0573434431a2a412209a0d010a45bc3d1687ee4a7702db956a727941f03a9255f04994f1f8faca2cd47337bcdd4338734df433d7b89f454156688739cf0514c + languageName: node + linkType: hard + +"@aws-sdk/types@npm:3.965.0, @aws-sdk/types@npm:^3.222.0": + version: 3.965.0 + resolution: "@aws-sdk/types@npm:3.965.0" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/7382007ab2be375cb9fc0bdb8fd546dd56ac1b6301bec0bc8d24f7a123dce68f41a15f7017a7c1b0ae89fb08c831c3ce2476b24914783a307691b847c47a1028 + languageName: node + linkType: hard + +"@aws-sdk/util-arn-parser@npm:3.966.0": + version: 3.966.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.966.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/fbbbdf9150bb78be0253a83fb18c1dbe1e12436824805e2a2dd6625115485be7beec581e4d35b448b2637ab4a17800fc40931c933f19182770d1ecfb99d5b3bd + languageName: node + linkType: hard + +"@aws-sdk/util-endpoints@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/util-endpoints@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + "@smithy/util-endpoints": "npm:^3.2.7" + tslib: "npm:^2.6.2" + checksum: 10/d4f372fe6159a5df94269a46376ec7138defe333483416648e6e0befe254381426c80fbc2480eaac880cf45f97e4ba514bfcec2e9f4e685191ccd38fec2cb465 + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.965.0 + resolution: "@aws-sdk/util-locate-window@npm:3.965.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/c6b956699e9092df26cda39af2008eb56f3d63b5e8b4b9135e37488a11ea83a3de269a561919459db7f45bcb9232073f9bc670d3452f0f40c52b05372b078022 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-browser@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.965.0" + dependencies: + "@aws-sdk/types": "npm:3.965.0" + "@smithy/types": "npm:^4.11.0" + bowser: "npm:^2.11.0" + tslib: "npm:^2.6.2" + checksum: 10/5784b639240493f2d0741424a1b393d5058c1c4982cf8044029a1b241159e0ed49e6f45513b0cba0e534c373964ad10c6177ea8dfeda81ce46b323371ff5941f + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.967.0": + version: 3.967.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.967.0" + dependencies: + "@aws-sdk/middleware-user-agent": "npm:3.967.0" + "@aws-sdk/types": "npm:3.965.0" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 10/621f98862c3b7b85fd8ab664e516485e37025acbbe6b106363e924b157a5b9e3804b93021f93d859291cb89f7724f9b82980bdff483158fef7688bbc804a43c3 + languageName: node + linkType: hard + +"@aws-sdk/xml-builder@npm:3.965.0": + version: 3.965.0 + resolution: "@aws-sdk/xml-builder@npm:3.965.0" + dependencies: + "@smithy/types": "npm:^4.11.0" + fast-xml-parser: "npm:5.2.5" + tslib: "npm:^2.6.2" + checksum: 10/7a6c4346d0fc3bad2e87b1782ccf777935369e29d5d2fe89534739d40f76478c319675cbb18d63dfd709a5c299796723466f024a0a7305f8c9122af9bd2c3f78 + languageName: node + linkType: hard + +"@aws/lambda-invoke-store@npm:^0.2.2": + version: 0.2.3 + resolution: "@aws/lambda-invoke-store@npm:0.2.3" + checksum: 10/d0efa8ca73b2d8dc0bf634525eefa1b72cda85f5d47366264849343a6f2860cfa5c52b7f766a16b78da8406bbd3ee975da3abb1dbe38183f8af95413eafeb256 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.7": + version: 7.28.6 + resolution: "@babel/code-frame@npm:7.28.6" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.28.5" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10/93e7ed9e039e3cb661bdb97c26feebafacc6ec13d745881dae5c7e2708f579475daebe7a3b5d23b183bb940b30744f52f4a5bcb65b4df03b79d82fcb38495784 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-string-parser@npm:7.27.1" + checksum: 10/0ae29cc2005084abdae2966afdb86ed14d41c9c37db02c3693d5022fba9f5d59b011d039380b8e537c34daf117c549f52b452398f576e908fb9db3c7abbb3a00 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/helper-validator-identifier@npm:7.28.5" + checksum: 10/8e5d9b0133702cfacc7f368bf792f0f8ac0483794877c6dca5fcb73810ee138e27527701826fb58a40a004f3a5ec0a2f3c3dd5e326d262530b119918f3132ba7 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6": + version: 7.28.6 + resolution: "@babel/parser@npm:7.28.6" + dependencies: + "@babel/types": "npm:^7.28.6" + bin: + parser: ./bin/babel-parser.js + checksum: 10/483a6fb5f9876ec9cbbb98816f2c94f39ae4d1158d35f87e1c4bf19a1f56027c96a1a3962ff0c8c46e8322a6d9e1c80d26b7f9668410df13d5b5769d9447b010 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.28.4": + version: 7.28.6 + resolution: "@babel/runtime@npm:7.28.6" + checksum: 10/fbcd439cb74d4a681958eb064c509829e3f46d8a4bfaaf441baa81bb6733d1e680bccc676c813883d7741bcaada1d0d04b15aa320ef280b5734e2192b50decf9 + languageName: node + linkType: hard + +"@babel/types@npm:^7.28.6, @babel/types@npm:^7.6.1, @babel/types@npm:^7.9.6": + version: 7.28.6 + resolution: "@babel/types@npm:7.28.6" + dependencies: + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.28.5" + checksum: 10/f9c6e52b451065aae5654686ecfc7de2d27dd0fbbc204ee2bd912a71daa359521a32f378981b1cf333ace6c8f86928814452cb9f388a7da59ad468038deb6b5f + languageName: node + linkType: hard + +"@borewit/text-codec@npm:^0.2.1": + version: 0.2.1 + resolution: "@borewit/text-codec@npm:0.2.1" + checksum: 10/3d7e824ac4d3ea16e6e910a7f2bac79f262602c3dbc2f525fd9b86786269c5d7bbd673090a0277d7f92652e534f263e292d5ace080bc9bdf57dc6921c1973f70 + languageName: node + linkType: hard + +"@camunda8/orchestration-cluster-api@npm:^1.2.2": + version: 1.2.3 + resolution: "@camunda8/orchestration-cluster-api@npm:1.2.3" + dependencies: + p-retry: "npm:^6.0.1" + typed-env: "npm:^2.0.0" + zod: "npm:^4" + checksum: 10/708463de5d80e805e01548b69cc89510d1cc6e22fb70a446ae104ce4e967462f130125dd03d64bf1a82e14b4fe79f40ae1c82ed8bcb9854da875227a28d36a08 + languageName: node + linkType: hard + +"@camunda8/sdk@npm:^8.7.9": + version: 8.8.4 + resolution: "@camunda8/sdk@npm:8.8.4" + dependencies: + "@camunda8/orchestration-cluster-api": "npm:^1.2.2" + "@grpc/grpc-js": "npm:1.12.5" + "@grpc/proto-loader": "npm:0.7.13" + "@types/form-data": "npm:^2.2.1" + "@types/node": "npm:^22.18.6" + chalk: "npm:^2.4.2" + console-stamp: "npm:^3.0.2" + dayjs: "npm:^1.8.15" + debug: "npm:^4.3.4" + fast-xml-parser: "npm:^4.1.3" + form-data: "npm:^4.0.2" + got: "npm:^11.8.6" + jwt-decode: "npm:^4.0.0" + lodash.mergewith: "npm:^4.6.2" + long: "npm:^4.0.0" + lossless-json: "npm:^4.0.1" + p-cancelable: "npm:^2.1.1" + promise-retry: "npm:^1.1.1" + reflect-metadata: "npm:^0.2.1" + stack-trace: "npm:0.0.10" + typed-duration: "npm:^1.0.12" + typed-emitter: "npm:^2.1.0" + typed-env: "npm:^2.0.0" + win-ca: "npm:3.5.1" + winston: "npm:^3.14.2" + dependenciesMeta: + win-ca: + optional: true + checksum: 10/0e1b29844a965c44485cb4068b710ed31e617ad1d7e95845eff5990122d4db7f2eba7a68361e574a5b623f7ed4eb88fb4d42be186c185ff5c645790299dc7c36 + languageName: node + linkType: hard + +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: 10/9d226461c1e91e95f067be2bdc5e6f99cfe55a721f45afb44122e23e4b8602eeac4ff7325af6b5a369f36396ee1514d3809af3f57769066d80d83790d8e53339 + languageName: node + linkType: hard + +"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": + version: 1.6.0 + resolution: "@colors/colors@npm:1.6.0" + checksum: 10/66d00284a3a9a21e5e853b256942e17edbb295f4bd7b9aa7ef06bbb603568d5173eb41b0f64c1e51748bc29d382a23a67d99956e57e7431c64e47e74324182d9 + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:^0.8.0": + version: 0.8.1 + resolution: "@cspotcode/source-map-support@npm:0.8.1" + dependencies: + "@jridgewell/trace-mapping": "npm:0.3.9" + checksum: 10/b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff + languageName: node + linkType: hard + +"@css-inline/css-inline-android-arm-eabi@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-android-arm-eabi@npm:0.14.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@css-inline/css-inline-android-arm64@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-android-arm64@npm:0.14.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@css-inline/css-inline-darwin-arm64@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-darwin-arm64@npm:0.14.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@css-inline/css-inline-darwin-x64@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-darwin-x64@npm:0.14.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@css-inline/css-inline-linux-arm-gnueabihf@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-linux-arm-gnueabihf@npm:0.14.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@css-inline/css-inline-linux-arm64-gnu@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-linux-arm64-gnu@npm:0.14.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@css-inline/css-inline-linux-arm64-musl@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-linux-arm64-musl@npm:0.14.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@css-inline/css-inline-linux-x64-gnu@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-linux-x64-gnu@npm:0.14.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@css-inline/css-inline-linux-x64-musl@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-linux-x64-musl@npm:0.14.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@css-inline/css-inline-win32-x64-msvc@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline-win32-x64-msvc@npm:0.14.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@css-inline/css-inline@npm:0.14.1": + version: 0.14.1 + resolution: "@css-inline/css-inline@npm:0.14.1" + dependencies: + "@css-inline/css-inline-android-arm-eabi": "npm:0.14.1" + "@css-inline/css-inline-android-arm64": "npm:0.14.1" + "@css-inline/css-inline-darwin-arm64": "npm:0.14.1" + "@css-inline/css-inline-darwin-x64": "npm:0.14.1" + "@css-inline/css-inline-linux-arm-gnueabihf": "npm:0.14.1" + "@css-inline/css-inline-linux-arm64-gnu": "npm:0.14.1" + "@css-inline/css-inline-linux-arm64-musl": "npm:0.14.1" + "@css-inline/css-inline-linux-x64-gnu": "npm:0.14.1" + "@css-inline/css-inline-linux-x64-musl": "npm:0.14.1" + "@css-inline/css-inline-win32-x64-msvc": "npm:0.14.1" + dependenciesMeta: + "@css-inline/css-inline-android-arm-eabi": + optional: true + "@css-inline/css-inline-android-arm64": + optional: true + "@css-inline/css-inline-darwin-arm64": + optional: true + "@css-inline/css-inline-darwin-x64": + optional: true + "@css-inline/css-inline-linux-arm-gnueabihf": + optional: true + "@css-inline/css-inline-linux-arm64-gnu": + optional: true + "@css-inline/css-inline-linux-arm64-musl": + optional: true + "@css-inline/css-inline-linux-x64-gnu": + optional: true + "@css-inline/css-inline-linux-x64-musl": + optional: true + "@css-inline/css-inline-win32-x64-msvc": + optional: true + checksum: 10/60523f5cb449ae41c671281304aa4ea76d6123b3265382a3f1710f4d2d50c37edf2d3e0f89bf7a486772b8893e1653a179e80dad2205facdd39e8c34afb1c03b + languageName: node + linkType: hard + +"@dabh/diagnostics@npm:^2.0.8": + version: 2.0.8 + resolution: "@dabh/diagnostics@npm:2.0.8" + dependencies: + "@so-ric/colorspace": "npm:^1.1.6" + enabled: "npm:2.0.x" + kuler: "npm:^2.0.0" + checksum: 10/ac2267a4ee1874f608493f21d386ea29f0acac6716124e26e3e48e01ce5706b095585a14adce1bee14b6567d3b8fdd0c5a0bbb7ab0e15c9a743d55eb02f093ce + languageName: node + linkType: hard + +"@date-fns/tz@npm:^1.2.0": + version: 1.4.1 + resolution: "@date-fns/tz@npm:1.4.1" + checksum: 10/062097590005cce3da4c7d9880f9c77d386cff5b4dd58fa3dde3c346a8b2e4f4a8025a613306351a7cad8eb71178a0f67b4840d5884f73aa4c759085fac92063 + languageName: node + linkType: hard + +"@date-fns/utc@npm:^2.1.0": + version: 2.1.1 + resolution: "@date-fns/utc@npm:2.1.1" + checksum: 10/80db3b4afbe261ed441f4c0fe688a5979ab7377fafa8100920b46bfd5f3dcf2f0ded9e63869fdbbedad0f5fc05e411e88bab1e3374e5173a822052666f01550a + languageName: node + linkType: hard + +"@emnapi/core@npm:^1.7.1": + version: 1.8.1 + resolution: "@emnapi/core@npm:1.8.1" + dependencies: + "@emnapi/wasi-threads": "npm:1.1.0" + tslib: "npm:^2.4.0" + checksum: 10/904ea60c91fc7d8aeb4a8f2c433b8cfb47c50618f2b6f37429fc5093c857c6381c60628a5cfbc3a7b0d75b0a288f21d4ed2d4533e82f92c043801ef255fd6a5c + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.7.0, @emnapi/runtime@npm:^1.7.1": + version: 1.8.1 + resolution: "@emnapi/runtime@npm:1.8.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/26725e202d4baefdc4a6ba770f703dfc80825a27c27a08c22bac1e1ce6f8f75c47b4fe9424d9b63239463c33ef20b650f08d710da18dfa1164a95e5acb865dba + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.1.0": + version: 1.1.0 + resolution: "@emnapi/wasi-threads@npm:1.1.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/0d557e75262d2f4c95cb2a456ba0785ef61f919ce488c1d76e5e3acfd26e00c753ef928cd80068363e0c166ba8cc0141305daf0f81aad5afcd421f38f11e0f4e + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": + version: 4.9.1 + resolution: "@eslint-community/eslint-utils@npm:4.9.1" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10/863b5467868551c9ae34d03eefe634633d08f623fc7b19d860f8f26eb6f303c1a5934253124163bee96181e45ed22bf27473dccc295937c3078493a4a8c9eddd + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 10/049b280fddf71dd325514e0a520024969431dc3a8b02fa77476e6820e9122f28ab4c9168c11821f91a27982d2453bcd7a66193356ea84e84fb7c8d793be1ba0c + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.21.1": + version: 0.21.1 + resolution: "@eslint/config-array@npm:0.21.1" + dependencies: + "@eslint/object-schema": "npm:^2.1.7" + debug: "npm:^4.3.1" + minimatch: "npm:^3.1.2" + checksum: 10/6eaa0435972f735ce52d581f355a0b616e50a9b8a73304a7015398096e252798b9b3b968a67b524eefb0fdeacc57c4d960f0ec6432abe1c1e24be815b88c5d18 + languageName: node + linkType: hard + +"@eslint/config-helpers@npm:^0.4.2": + version: 0.4.2 + resolution: "@eslint/config-helpers@npm:0.4.2" + dependencies: + "@eslint/core": "npm:^0.17.0" + checksum: 10/3f2b4712d8e391c36ec98bc200f7dea423dfe518e42956569666831b89ede83b33120c761dfd3ab6347d8e8894a6d4af47254a18d464a71c6046fd88065f6daf + languageName: node + linkType: hard + +"@eslint/core@npm:^0.17.0": + version: 0.17.0 + resolution: "@eslint/core@npm:0.17.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10/f9a428cc651ec15fb60d7d60c2a7bacad4666e12508320eafa98258e976fafaa77d7be7be91519e75f801f15f830105420b14a458d4aab121a2b0a59bc43517b + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.3.1": + version: 3.3.3 + resolution: "@eslint/eslintrc@npm:3.3.3" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.1" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10/b586a364ff15ce1b68993aefc051ca330b1fece15fb5baf4a708d00113f9a14895cffd84a5f24c5a97bd4b4321130ab2314f90aa462a250f6b859c2da2cba1f3 + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.27.0": + version: 9.39.2 + resolution: "@eslint/js@npm:9.39.2" + checksum: 10/6b7f676746f3111b5d1b23715319212ab9297868a0fa9980d483c3da8965d5841673aada2d5653e85a3f7156edee0893a7ae7035211b4efdcb2848154bb947f2 + languageName: node + linkType: hard + +"@eslint/object-schema@npm:^2.1.7": + version: 2.1.7 + resolution: "@eslint/object-schema@npm:2.1.7" + checksum: 10/946ef5d6235b4d1c0907c6c6e6429c8895f535380c562b7705c131f63f2e961b06e8785043c86a293da48e0a60c6286d98ba395b8b32ea55561fe6e4417cb7e4 + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.4.1": + version: 0.4.1 + resolution: "@eslint/plugin-kit@npm:0.4.1" + dependencies: + "@eslint/core": "npm:^0.17.0" + levn: "npm:^0.4.1" + checksum: 10/c5947d0ffeddca77d996ac1b886a66060c1a15ed1d5e425d0c7e7d7044a4bd3813fc968892d03950a7831c9b89368a2f7b281e45dd3c74a048962b74bf3a1cb4 + languageName: node + linkType: hard + +"@esm2cjs/cacheable-lookup@npm:^7.0.0": + version: 7.0.0 + resolution: "@esm2cjs/cacheable-lookup@npm:7.0.0" + checksum: 10/35ca669df48ca9de7d0c0cb18e775637d79e0624600993175a9fa353ee2f2e61ddd4a0dff65d60d630fecf87fcf39e0973923226fb81d999563ffd81f7388d1f + languageName: node + linkType: hard + +"@faker-js/faker@npm:^9.8.0": + version: 9.9.0 + resolution: "@faker-js/faker@npm:9.9.0" + checksum: 10/b24b1be0fb3090d54abaaa3a814f37d1a7551f1207dcd330a1af2c70f6312a8b95ebb82653577757dd62f4cbbb56caa3c83513053253654dc3e286a05040ecbe + languageName: node + linkType: hard + +"@fast-csv/format@npm:4.3.5": + version: 4.3.5 + resolution: "@fast-csv/format@npm:4.3.5" + dependencies: + "@types/node": "npm:^14.0.1" + lodash.escaperegexp: "npm:^4.1.2" + lodash.isboolean: "npm:^3.0.3" + lodash.isequal: "npm:^4.5.0" + lodash.isfunction: "npm:^3.0.9" + lodash.isnil: "npm:^4.0.0" + checksum: 10/94fcc061422ad82c7973926acba96c7f0e539d39f8c9c986f4d369ba0bbda535407a5243ddafa0a41a310261205824577b66e74bd0ed81aaaff0d9c33db9e426 + languageName: node + linkType: hard + +"@fast-csv/parse@npm:4.3.6": + version: 4.3.6 + resolution: "@fast-csv/parse@npm:4.3.6" + dependencies: + "@types/node": "npm:^14.0.1" + lodash.escaperegexp: "npm:^4.1.2" + lodash.groupby: "npm:^4.6.0" + lodash.isfunction: "npm:^3.0.9" + lodash.isnil: "npm:^4.0.0" + lodash.isundefined: "npm:^3.0.1" + lodash.uniq: "npm:^4.5.0" + checksum: 10/12b338134de8801c895f50f8bb5315b67a6181d5b39d99445be80898633541b06be77a2b14a8395fc51c3f028138e9fb8a2b5bc5258f50c08bef22fd9dd07ee0 + languageName: node + linkType: hard + +"@grpc/grpc-js@npm:1.12.5": + version: 1.12.5 + resolution: "@grpc/grpc-js@npm:1.12.5" + dependencies: + "@grpc/proto-loader": "npm:^0.7.13" + "@js-sdsl/ordered-map": "npm:^4.4.2" + checksum: 10/4f8ead236dcab4d94e15e62d65ad2d93732d37f5cc52ffafe67ae00f69eae4a4c97d6d34a1b9eac9f30206468f2d15302ea6649afcba1d38929afa9d1e7c12d5 + languageName: node + linkType: hard + +"@grpc/grpc-js@npm:^1.13.2": + version: 1.14.3 + resolution: "@grpc/grpc-js@npm:1.14.3" + dependencies: + "@grpc/proto-loader": "npm:^0.8.0" + "@js-sdsl/ordered-map": "npm:^4.4.2" + checksum: 10/bb9bfe2f749179ae5ac7774d30486dfa2e0b004518c28de158b248e0f6f65f40138f01635c48266fa540670220f850216726e3724e1eb29d078817581c96e4db + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:0.7.13": + version: 0.7.13 + resolution: "@grpc/proto-loader@npm:0.7.13" + dependencies: + lodash.camelcase: "npm:^4.3.0" + long: "npm:^5.0.0" + protobufjs: "npm:^7.2.5" + yargs: "npm:^17.7.2" + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 10/7e2d842c2061cbaf6450c71da0077263be3bab165454d5c8a3e1ae4d3c6d2915f02fd27da63ff01f05e127b1221acd40705273f5d29303901e60514e852992f4 + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.7.13, @grpc/proto-loader@npm:^0.7.5": + version: 0.7.15 + resolution: "@grpc/proto-loader@npm:0.7.15" + dependencies: + lodash.camelcase: "npm:^4.3.0" + long: "npm:^5.0.0" + protobufjs: "npm:^7.2.5" + yargs: "npm:^17.7.2" + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 10/2e2b33ace8bc34211522751a9e654faf9ac997577a9e9291b1619b4c05d7878a74d2101c3bc43b2b2b92bca7509001678fb191d4eb100684cc2910d66f36c373 + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.8.0": + version: 0.8.0 + resolution: "@grpc/proto-loader@npm:0.8.0" + dependencies: + lodash.camelcase: "npm:^4.3.0" + long: "npm:^5.0.0" + protobufjs: "npm:^7.5.3" + yargs: "npm:^17.7.2" + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 10/216813bdca52cd3a84ac355ad93c2c3f54252be47327692fe666fd85baa5b1d50aa681ebc5626ab08926564fb2deae3b2ea435aa5bd883197650bbe56f2ae108 + languageName: node + linkType: hard + +"@humanfs/core@npm:^0.19.1": + version: 0.19.1 + resolution: "@humanfs/core@npm:0.19.1" + checksum: 10/270d936be483ab5921702623bc74ce394bf12abbf57d9145a69e8a0d1c87eb1c768bd2d93af16c5705041e257e6d9cc7529311f63a1349f3678abc776fc28523 + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.6": + version: 0.16.7 + resolution: "@humanfs/node@npm:0.16.7" + dependencies: + "@humanfs/core": "npm:^0.19.1" + "@humanwhocodes/retry": "npm:^0.4.0" + checksum: 10/b3633d3dce898592cac515ba5e6693c78e6be92863541d3eaf2c009b10f52b2fa62ff6e6e06f240f2447ddbe7b5f1890bc34e9308470675c876eee207553a08d + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 10/e993950e346331e5a32eefb27948ecdee2a2c4ab3f072b8f566cd213ef485dd50a3ca497050608db91006f5479e43f91a439aef68d2a313bd3ded06909c7c5b3 + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2": + version: 0.4.3 + resolution: "@humanwhocodes/retry@npm:0.4.3" + checksum: 10/0b32cfd362bea7a30fbf80bb38dcaf77fee9c2cae477ee80b460871d03590110ac9c77d654f04ec5beaf71b6f6a89851bdf6c1e34ccdf2f686bd86fcd97d9e61 + languageName: node + linkType: hard + +"@img/colour@npm:^1.0.0": + version: 1.0.0 + resolution: "@img/colour@npm:1.0.0" + checksum: 10/bd248d7c4b8ba99a72b22a005a63f1d3309ee8343a74b6d0d1314bae300a3096919991a09e9a9243cf6ca50e393b4c5a7e065488ed616c3b58d052473240b812 + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-riscv64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-ppc64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-ppc64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-ppc64": + optional: true + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-riscv64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-riscv64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-riscv64": + optional: true + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-s390x@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-wasm32@npm:0.34.5" + dependencies: + "@emnapi/runtime": "npm:^1.7.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-arm64@npm:0.34.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-ia32@npm:0.34.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-x64@npm:0.34.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@inquirer/ansi@npm:^1.0.2": + version: 1.0.2 + resolution: "@inquirer/ansi@npm:1.0.2" + checksum: 10/d1496e573a63ee6752bcf3fc93375cdabc55b0d60f0588fe7902282c710b223252ad318ff600ee904e48555634663b53fda517f5b29ce9fbda90bfae18592fbc + languageName: node + linkType: hard + +"@inquirer/checkbox@npm:^4.1.2, @inquirer/checkbox@npm:^4.3.2": + version: 4.3.2 + resolution: "@inquirer/checkbox@npm:4.3.2" + dependencies: + "@inquirer/ansi": "npm:^1.0.2" + "@inquirer/core": "npm:^10.3.2" + "@inquirer/figures": "npm:^1.0.15" + "@inquirer/type": "npm:^3.0.10" + yoctocolors-cjs: "npm:^2.1.3" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/4ac5dd2679981e23f066c51c605cb1c63ccda9ea6e1ad895e675eb26702aaf6cf961bf5ca3acd832efba5edcf9883b6742002c801673d2b35c123a7fa7db7b23 + languageName: node + linkType: hard + +"@inquirer/confirm@npm:^5.1.21, @inquirer/confirm@npm:^5.1.6": + version: 5.1.21 + resolution: "@inquirer/confirm@npm:5.1.21" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/type": "npm:^3.0.10" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/a107aa0073965ea510affb9e5b55baf40333503d600970c458c07770cd4e0eee01efc4caba66f0409b0fadc9550d127329622efb543cffcabff3ad0e7f865372 + languageName: node + linkType: hard + +"@inquirer/core@npm:^10.3.2": + version: 10.3.2 + resolution: "@inquirer/core@npm:10.3.2" + dependencies: + "@inquirer/ansi": "npm:^1.0.2" + "@inquirer/figures": "npm:^1.0.15" + "@inquirer/type": "npm:^3.0.10" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^2.0.0" + signal-exit: "npm:^4.1.0" + wrap-ansi: "npm:^6.2.0" + yoctocolors-cjs: "npm:^2.1.3" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/eb434bdf0ae7d904367003c772bcd80cbf679f79c087c99a4949fd7288e9a2f713ec3ea63381b9a001f52389ab56a77fcd88d64d81a03b1195193410ce8971c2 + languageName: node + linkType: hard + +"@inquirer/editor@npm:^4.2.23, @inquirer/editor@npm:^4.2.7": + version: 4.2.23 + resolution: "@inquirer/editor@npm:4.2.23" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/external-editor": "npm:^1.0.3" + "@inquirer/type": "npm:^3.0.10" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/f91b9aadba6ea28a0f4ea5f075af421e076262aebbd737e1b9779f086fa9d559d064e9942a581544645d1dcf56d6b685e8063fe46677880fbca73f6de4e4e7c5 + languageName: node + linkType: hard + +"@inquirer/expand@npm:^4.0.23, @inquirer/expand@npm:^4.0.9": + version: 4.0.23 + resolution: "@inquirer/expand@npm:4.0.23" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/type": "npm:^3.0.10" + yoctocolors-cjs: "npm:^2.1.3" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/73ad1d6376e5efe2a452c33494d6d16ee2670c638ae470a795fdff4acb59a8e032e38e141f87b603b6e96320977519b375dac6471d86d5e3087a9c1db40e3111 + languageName: node + linkType: hard + +"@inquirer/external-editor@npm:^1.0.3": + version: 1.0.3 + resolution: "@inquirer/external-editor@npm:1.0.3" + dependencies: + chardet: "npm:^2.1.1" + iconv-lite: "npm:^0.7.0" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/c95d7237a885b32031715089f92820525731d4d3c2bd7afdb826307dc296cc2b39e7a644b0bb265441963348cca42e7785feb29c3aaf18fd2b63131769bf6587 + languageName: node + linkType: hard + +"@inquirer/figures@npm:^1.0.15": + version: 1.0.15 + resolution: "@inquirer/figures@npm:1.0.15" + checksum: 10/3f858807f361ca29f41ec1076bbece4098cc140d86a06159d42c6e3f6e4d9bec9e10871ccfcbbaa367d6a8462b01dff89f2b1b157d9de6e8726bec85533f525c + languageName: node + linkType: hard + +"@inquirer/input@npm:^4.1.6, @inquirer/input@npm:^4.3.1": + version: 4.3.1 + resolution: "@inquirer/input@npm:4.3.1" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/type": "npm:^3.0.10" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/713aaa4c94263299fbd7adfd65378f788cac1b5047f2b7e1ea349ca669db6c7c91b69ab6e2f6660cdbc28c7f7888c5c77ab4433bd149931597e43976d1ba5f34 + languageName: node + linkType: hard + +"@inquirer/number@npm:^3.0.23, @inquirer/number@npm:^3.0.9": + version: 3.0.23 + resolution: "@inquirer/number@npm:3.0.23" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/type": "npm:^3.0.10" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/50694807b71746e15ed69d100aae3c8014d83c90aa660e8a179fe0db1046f26d727947542f64e24cc8b969a61659cb89fe36208cc2b59c1816382b598e686dd2 + languageName: node + linkType: hard + +"@inquirer/password@npm:^4.0.23, @inquirer/password@npm:^4.0.9": + version: 4.0.23 + resolution: "@inquirer/password@npm:4.0.23" + dependencies: + "@inquirer/ansi": "npm:^1.0.2" + "@inquirer/core": "npm:^10.3.2" + "@inquirer/type": "npm:^3.0.10" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/97364970b01c85946a4a50ad876c53ef0c1857a9144e24fad65e5dfa4b4e5dd42564fbcdfa2b49bb049a25d127efbe0882cb18afcdd47b166ebd01c6c4b5e825 + languageName: node + linkType: hard + +"@inquirer/prompts@npm:7.10.1": + version: 7.10.1 + resolution: "@inquirer/prompts@npm:7.10.1" + dependencies: + "@inquirer/checkbox": "npm:^4.3.2" + "@inquirer/confirm": "npm:^5.1.21" + "@inquirer/editor": "npm:^4.2.23" + "@inquirer/expand": "npm:^4.0.23" + "@inquirer/input": "npm:^4.3.1" + "@inquirer/number": "npm:^3.0.23" + "@inquirer/password": "npm:^4.0.23" + "@inquirer/rawlist": "npm:^4.1.11" + "@inquirer/search": "npm:^3.2.2" + "@inquirer/select": "npm:^4.4.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/b3e3386edd255e4e91c7908050674f8a2e69b043883c00feec2f87d697be37bc6e8cd4a360e7e3233a9825ae7ea044a2ac63d5700926d27f9959013d8566f890 + languageName: node + linkType: hard + +"@inquirer/prompts@npm:7.3.2": + version: 7.3.2 + resolution: "@inquirer/prompts@npm:7.3.2" + dependencies: + "@inquirer/checkbox": "npm:^4.1.2" + "@inquirer/confirm": "npm:^5.1.6" + "@inquirer/editor": "npm:^4.2.7" + "@inquirer/expand": "npm:^4.0.9" + "@inquirer/input": "npm:^4.1.6" + "@inquirer/number": "npm:^3.0.9" + "@inquirer/password": "npm:^4.0.9" + "@inquirer/rawlist": "npm:^4.0.9" + "@inquirer/search": "npm:^3.0.9" + "@inquirer/select": "npm:^4.0.9" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/476ea162f6820628dbbb4ffff78a9ddf0cc9d99237bfbd976b944034eb4362eec748cabe2c886fa69eab551866172952fc26f2c12f4429008321105048743b41 + languageName: node + linkType: hard + +"@inquirer/rawlist@npm:^4.0.9, @inquirer/rawlist@npm:^4.1.11": + version: 4.1.11 + resolution: "@inquirer/rawlist@npm:4.1.11" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/type": "npm:^3.0.10" + yoctocolors-cjs: "npm:^2.1.3" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/0d8f6484cfc20749190e95eecfb2d034bafb3644ec4907b84b1673646f5dd71730e38e35565ea98dfd240d8851e3cff653edafcc4e0af617054b127b407e3229 + languageName: node + linkType: hard + +"@inquirer/search@npm:^3.0.9, @inquirer/search@npm:^3.2.2": + version: 3.2.2 + resolution: "@inquirer/search@npm:3.2.2" + dependencies: + "@inquirer/core": "npm:^10.3.2" + "@inquirer/figures": "npm:^1.0.15" + "@inquirer/type": "npm:^3.0.10" + yoctocolors-cjs: "npm:^2.1.3" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/abaed2df7763633ff4414b58d1c87233b69ed3cd2ac77629f0d54b72b8b585dc4806c7a2a8261daba58af5b0a2147e586d079fdc82060b6bcf56b75d3d03f3a7 + languageName: node + linkType: hard + +"@inquirer/select@npm:^4.0.9, @inquirer/select@npm:^4.4.2": + version: 4.4.2 + resolution: "@inquirer/select@npm:4.4.2" + dependencies: + "@inquirer/ansi": "npm:^1.0.2" + "@inquirer/core": "npm:^10.3.2" + "@inquirer/figures": "npm:^1.0.15" + "@inquirer/type": "npm:^3.0.10" + yoctocolors-cjs: "npm:^2.1.3" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/795ec0ac77d575f20bd6a12fb1c040093e62217ac0c80194829a8d3c3d1e09f70ad738e9a9dd6095cc8358fff4e13882209c09bdf8eb0864a86dcabef5b0a6a6 + languageName: node + linkType: hard + +"@inquirer/type@npm:^3.0.10": + version: 3.0.10 + resolution: "@inquirer/type@npm:3.0.10" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10/57d113a9db7abc73326491e29bedc88ef362e53779f9f58a1b61225e0be068ce0c54e33cd65f4a13ca46131676fb72c3ef488463c4c9af0aa89680684c55d74c + languageName: node + linkType: hard + +"@ioredis/commands@npm:1.5.0": + version: 1.5.0 + resolution: "@ioredis/commands@npm:1.5.0" + checksum: 10/b5a842fde4785c20318b4b26c7ae98bb1f9cbdc1ade3e55a2de07d5db8fb4e9281e590fb08559e5a9027909f0f9f1214688af33e6fb469ce4ff3decc7cfde679 + languageName: node + linkType: hard + +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10/102fbc6d2c0d5edf8f6dbf2b3feb21695a21bc850f11bc47c4f06aa83bd8884fde3fe9d6d797d619901d96865fdcb4569ac2a54c937992c48885c5e3d9967fe8 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10/cf3b7f206aff12128214a1df764ac8cdbc517c110db85249b945282407e3dfc5c6e66286383a7c9391a059fc8e6e6a8ca82262fc9d2590bd615376141fbebd2d + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10/e9ed5fd27c3aec1095e3a16e0c0cf148d1fee55a38665c35f7b3f86a9b5d00d042ddaabc98e8a1cb7463b9378c15f22a94eb35e99469c201453eb8375191f243 + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10/4412e9e6713c89c1e66d80bb0bb5a2a93192f10477623a27d08f228ba0316bb880affabc5bfe7f838f58a34d26c2c190da726e576cdfc18c49a72e89adabdcf5 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10/902f8261dcf450b4af7b93f9656918e02eec80a2169e155000cb2059f90113dd98f3ccf6efc6072cee1dd84cac48cade51da236972d942babc40e4c23da4d62a + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10/97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d + languageName: node + linkType: hard + +"@jridgewell/source-map@npm:^0.3.3": + version: 0.3.11 + resolution: "@jridgewell/source-map@npm:0.3.11" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + checksum: 10/847f1177d3d133a0966ef61ca29abea0d79788a0652f90ee1893b3da968c190b7e31c3534cc53701179dd6b14601eef3d78644e727e05b1a08c68d281aedc4ba + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10/5d9d207b462c11e322d71911e55e21a4e2772f71ffe8d6f1221b8eb5ae6774458c1d242f897fb0814e8714ca9a6b498abfa74dfe4f434493342902b1a48b33a5 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:0.3.9": + version: 0.3.9 + resolution: "@jridgewell/trace-mapping@npm:0.3.9" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.0.3" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + checksum: 10/83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10/da0283270e691bdb5543806077548532791608e52386cfbbf3b9e8fb00457859d1bd01d512851161c886eb3a2f3ce6fd9bcf25db8edf3bddedd275bd4a88d606 + languageName: node + linkType: hard + +"@js-sdsl/ordered-map@npm:^4.4.2": + version: 4.4.2 + resolution: "@js-sdsl/ordered-map@npm:4.4.2" + checksum: 10/ac64e3f0615ecc015461c9f527f124d2edaa9e68de153c1e270c627e01e83d046522d7e872692fd57a8c514578b539afceff75831c0d8b2a9a7a347fbed35af4 + languageName: node + linkType: hard + +"@lukeed/csprng@npm:^1.0.0": + version: 1.1.0 + resolution: "@lukeed/csprng@npm:1.1.0" + checksum: 10/926f5f7fc629470ca9a8af355bfcd0271d34535f7be3890f69902432bddc3262029bb5dbe9025542cf6c9883d878692eef2815fc2f3ba5b92e9da1f9eba2e51b + languageName: node + linkType: hard + +"@microsoft/tsdoc@npm:0.16.0": + version: 0.16.0 + resolution: "@microsoft/tsdoc@npm:0.16.0" + checksum: 10/1eaad3605234dc7e44898c15d1ba3c97fb968af1117025400cba572ce268da05afc36634d1fb9e779457af3ff7f13330aee07a962510a4d9c6612c13f71ee41e + languageName: node + linkType: hard + +"@napi-rs/nice-android-arm-eabi@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-android-arm-eabi@npm:1.1.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/nice-android-arm64@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-android-arm64@npm:1.1.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/nice-darwin-arm64@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-darwin-arm64@npm:1.1.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/nice-darwin-x64@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-darwin-x64@npm:1.1.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/nice-freebsd-x64@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-freebsd-x64@npm:1.1.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/nice-linux-arm-gnueabihf@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-arm-gnueabihf@npm:1.1.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/nice-linux-arm64-gnu@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-arm64-gnu@npm:1.1.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/nice-linux-arm64-musl@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-arm64-musl@npm:1.1.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/nice-linux-ppc64-gnu@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-ppc64-gnu@npm:1.1.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/nice-linux-riscv64-gnu@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-riscv64-gnu@npm:1.1.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/nice-linux-s390x-gnu@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-s390x-gnu@npm:1.1.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/nice-linux-x64-gnu@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-x64-gnu@npm:1.1.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/nice-linux-x64-musl@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-linux-x64-musl@npm:1.1.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/nice-openharmony-arm64@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-openharmony-arm64@npm:1.1.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/nice-win32-arm64-msvc@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-win32-arm64-msvc@npm:1.1.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/nice-win32-ia32-msvc@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-win32-ia32-msvc@npm:1.1.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/nice-win32-x64-msvc@npm:1.1.1": + version: 1.1.1 + resolution: "@napi-rs/nice-win32-x64-msvc@npm:1.1.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/nice@npm:^1.0.1": + version: 1.1.1 + resolution: "@napi-rs/nice@npm:1.1.1" + dependencies: + "@napi-rs/nice-android-arm-eabi": "npm:1.1.1" + "@napi-rs/nice-android-arm64": "npm:1.1.1" + "@napi-rs/nice-darwin-arm64": "npm:1.1.1" + "@napi-rs/nice-darwin-x64": "npm:1.1.1" + "@napi-rs/nice-freebsd-x64": "npm:1.1.1" + "@napi-rs/nice-linux-arm-gnueabihf": "npm:1.1.1" + "@napi-rs/nice-linux-arm64-gnu": "npm:1.1.1" + "@napi-rs/nice-linux-arm64-musl": "npm:1.1.1" + "@napi-rs/nice-linux-ppc64-gnu": "npm:1.1.1" + "@napi-rs/nice-linux-riscv64-gnu": "npm:1.1.1" + "@napi-rs/nice-linux-s390x-gnu": "npm:1.1.1" + "@napi-rs/nice-linux-x64-gnu": "npm:1.1.1" + "@napi-rs/nice-linux-x64-musl": "npm:1.1.1" + "@napi-rs/nice-openharmony-arm64": "npm:1.1.1" + "@napi-rs/nice-win32-arm64-msvc": "npm:1.1.1" + "@napi-rs/nice-win32-ia32-msvc": "npm:1.1.1" + "@napi-rs/nice-win32-x64-msvc": "npm:1.1.1" + dependenciesMeta: + "@napi-rs/nice-android-arm-eabi": + optional: true + "@napi-rs/nice-android-arm64": + optional: true + "@napi-rs/nice-darwin-arm64": + optional: true + "@napi-rs/nice-darwin-x64": + optional: true + "@napi-rs/nice-freebsd-x64": + optional: true + "@napi-rs/nice-linux-arm-gnueabihf": + optional: true + "@napi-rs/nice-linux-arm64-gnu": + optional: true + "@napi-rs/nice-linux-arm64-musl": + optional: true + "@napi-rs/nice-linux-ppc64-gnu": + optional: true + "@napi-rs/nice-linux-riscv64-gnu": + optional: true + "@napi-rs/nice-linux-s390x-gnu": + optional: true + "@napi-rs/nice-linux-x64-gnu": + optional: true + "@napi-rs/nice-linux-x64-musl": + optional: true + "@napi-rs/nice-openharmony-arm64": + optional: true + "@napi-rs/nice-win32-arm64-msvc": + optional: true + "@napi-rs/nice-win32-ia32-msvc": + optional: true + "@napi-rs/nice-win32-x64-msvc": + optional: true + checksum: 10/3f197c9536d0294f732a2acbe05a6d2fddc2794873b5b73edd395f56e3aed90b46c053001af80ea006d4d276cbb4e4196f8dbee0c214163b8e4b787e570a37e1 + languageName: node + linkType: hard + +"@napi-rs/wasm-runtime@npm:^1.1.0": + version: 1.1.1 + resolution: "@napi-rs/wasm-runtime@npm:1.1.1" + dependencies: + "@emnapi/core": "npm:^1.7.1" + "@emnapi/runtime": "npm:^1.7.1" + "@tybys/wasm-util": "npm:^0.10.1" + checksum: 10/080e7f2aefb84e09884d21c650a2cbafdf25bfd2634693791b27e36eec0ddaa3c1656a943f8c913ac75879a0b04e68f8a827897ee655ab54a93169accf05b194 + languageName: node + linkType: hard + +"@nestjs-modules/mailer@npm:2.0.2": + version: 2.0.2 + resolution: "@nestjs-modules/mailer@npm:2.0.2" + dependencies: + "@css-inline/css-inline": "npm:0.14.1" + "@types/ejs": "npm:^3.1.5" + "@types/mjml": "npm:^4.7.4" + "@types/pug": "npm:^2.0.10" + ejs: "npm:^3.1.10" + glob: "npm:10.3.12" + handlebars: "npm:^4.7.8" + liquidjs: "npm:^10.11.1" + mjml: "npm:^4.15.3" + preview-email: "npm:^3.0.19" + pug: "npm:^3.0.2" + peerDependencies: + "@nestjs/common": ">=7.0.9" + "@nestjs/core": ">=7.0.9" + "@types/ejs": ">=3.0.3" + "@types/mjml": ">=4.7.4" + "@types/pug": ">=2.0.6" + ejs: ">=3.1.2" + handlebars: ">=4.7.6" + liquidjs: ">=10.8.2" + mjml: ">=4.15.3" + nodemailer: ">=6.4.6" + preview-email: ">=3.0.19" + pug: ">=3.0.1" + dependenciesMeta: + "@types/ejs": + optional: true + "@types/mjml": + optional: true + "@types/pug": + optional: true + ejs: + optional: true + handlebars: + optional: true + liquidjs: + optional: true + mjml: + optional: true + preview-email: + optional: true + pug: + optional: true + checksum: 10/ca81095b78e36492d85fa76b87e98833261f3cb644130042a64b9433c038d83ff78754d5be04f713d399370196aa1e0d99fd25cc2077b3efce6da78cc8e2c07b + languageName: node + linkType: hard + +"@nestjs/axios@npm:^4.0.0": + version: 4.0.1 + resolution: "@nestjs/axios@npm:4.0.1" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + axios: ^1.3.1 + rxjs: ^7.0.0 + checksum: 10/0cc741e4fbfc39920afbb6c58050e0dbfecc8e2f7b249d879802a5b03b65df3714e828970e2bf12283d3d27e1f1ab7ca5ec62b5698dc50f105680e100a0a33f6 + languageName: node + linkType: hard + +"@nestjs/cli@npm:^11.0.7": + version: 11.0.14 + resolution: "@nestjs/cli@npm:11.0.14" + dependencies: + "@angular-devkit/core": "npm:19.2.19" + "@angular-devkit/schematics": "npm:19.2.19" + "@angular-devkit/schematics-cli": "npm:19.2.19" + "@inquirer/prompts": "npm:7.10.1" + "@nestjs/schematics": "npm:^11.0.1" + ansis: "npm:4.2.0" + chokidar: "npm:4.0.3" + cli-table3: "npm:0.6.5" + commander: "npm:4.1.1" + fork-ts-checker-webpack-plugin: "npm:9.1.0" + glob: "npm:13.0.0" + node-emoji: "npm:1.11.0" + ora: "npm:5.4.1" + tsconfig-paths: "npm:4.2.0" + tsconfig-paths-webpack-plugin: "npm:4.2.0" + typescript: "npm:5.9.3" + webpack: "npm:5.103.0" + webpack-node-externals: "npm:3.0.0" + peerDependencies: + "@swc/cli": ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 + "@swc/core": ^1.3.62 + peerDependenciesMeta: + "@swc/cli": + optional: true + "@swc/core": + optional: true + bin: + nest: bin/nest.js + checksum: 10/f6174353b2b1781cb2c370958d94456cd58574ae959c157bf9ef2135381778b0c7a8689b2767fe6b7f890a9312e76828a4a74a59820285a8fbcccf332665b894 + languageName: node + linkType: hard + +"@nestjs/common@npm:^11.1.2": + version: 11.1.11 + resolution: "@nestjs/common@npm:11.1.11" + dependencies: + file-type: "npm:21.2.0" + iterare: "npm:1.2.1" + load-esm: "npm:1.0.3" + tslib: "npm:2.8.1" + uid: "npm:2.0.2" + peerDependencies: + class-transformer: ">=0.4.1" + class-validator: ">=0.13.2" + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + checksum: 10/a0027d306e95549350395627f311689a1c75bc66cd458ccf81916ee6f574b4e726c0397b0af62664a6338b8523a99a5b4f4ef59c55a2d385ad645bfb22c11aaa + languageName: node + linkType: hard + +"@nestjs/config@npm:^4.0.2": + version: 4.0.2 + resolution: "@nestjs/config@npm:4.0.2" + dependencies: + dotenv: "npm:16.4.7" + dotenv-expand: "npm:12.0.1" + lodash: "npm:4.17.21" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + rxjs: ^7.1.0 + checksum: 10/ed0234807a0677c301894cbeafe293226ad8d8279659a542e2f23cd3e40adeb38aecabcb5ace46bcee83437943400d03e0c374a3ca63e2d7a77e80f71088740d + languageName: node + linkType: hard + +"@nestjs/core@npm:^11.1.2": + version: 11.1.11 + resolution: "@nestjs/core@npm:11.1.11" + dependencies: + "@nuxt/opencollective": "npm:0.4.1" + fast-safe-stringify: "npm:2.1.1" + iterare: "npm:1.2.1" + path-to-regexp: "npm:8.3.0" + tslib: "npm:2.8.1" + uid: "npm:2.0.2" + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/microservices": ^11.0.0 + "@nestjs/platform-express": ^11.0.0 + "@nestjs/websockets": ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + "@nestjs/microservices": + optional: true + "@nestjs/platform-express": + optional: true + "@nestjs/websockets": + optional: true + checksum: 10/f94b8049741862d698b7079cdb70cd9eb91875f22c52a04707cc1e81bbd2db030c3992a3c454518bf507f29cd8f12ec18189b6d475f7e8c5f5fab827537f0cd3 + languageName: node + linkType: hard + +"@nestjs/event-emitter@npm:^3.0.1": + version: 3.0.1 + resolution: "@nestjs/event-emitter@npm:3.0.1" + dependencies: + eventemitter2: "npm:6.4.9" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + "@nestjs/core": ^10.0.0 || ^11.0.0 + checksum: 10/6050c615aefada991da136f854dd3a2344aa69286886a53093897d4c2e2158142ad9e1b7b1a55b0a0cbc6a7b43a4916cb1988ff01d766c97bfc7e1dca862c93a + languageName: node + linkType: hard + +"@nestjs/jwt@npm:^11.0.0": + version: 11.0.2 + resolution: "@nestjs/jwt@npm:11.0.2" + dependencies: + "@types/jsonwebtoken": "npm:9.0.10" + jsonwebtoken: "npm:9.0.3" + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + checksum: 10/87ebe45dbbe9acfd31042b7cb18eeb945b9b9574bd807a076204b9fd502fc81e99e7f30c788bdeac69b603454526e603fd97111c358c5f01346cda35e6fb3fc5 + languageName: node + linkType: hard + +"@nestjs/mapped-types@npm:2.1.0": + version: 2.1.0 + resolution: "@nestjs/mapped-types@npm:2.1.0" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + checksum: 10/4d75143e233f743338ba0db0b92301ab297221bb8842d2b999954f8d90f8ccee68350e2c01381f97a7f30af230b8b783e066939f8afe2d7c8abb46596776858b + languageName: node + linkType: hard + +"@nestjs/platform-express@npm:^11.1.2": + version: 11.1.11 + resolution: "@nestjs/platform-express@npm:11.1.11" + dependencies: + cors: "npm:2.8.5" + express: "npm:5.2.1" + multer: "npm:2.0.2" + path-to-regexp: "npm:8.3.0" + tslib: "npm:2.8.1" + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/core": ^11.0.0 + checksum: 10/877658da61e9d64c260f85590c8a15ae81a3466e5a0c51e94f2016b699cac7af59d994a18c124aaa4920920e9f24e0c1abf081d717f138d3d63b7d20cd86e2ef + languageName: node + linkType: hard + +"@nestjs/platform-socket.io@npm:^11.1.2": + version: 11.1.11 + resolution: "@nestjs/platform-socket.io@npm:11.1.11" + dependencies: + socket.io: "npm:4.8.3" + tslib: "npm:2.8.1" + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/websockets": ^11.0.0 + rxjs: ^7.1.0 + checksum: 10/7e9188356cac31e92cdd3b095e2481e630f73f563622caff56cd4ca1fea3b63ec8053ede8fc2993bf6c329e4067c0df5cae0da0fd0e9496ebb8b234d79c4c377 + languageName: node + linkType: hard + +"@nestjs/schedule@npm:^6.0.0": + version: 6.1.0 + resolution: "@nestjs/schedule@npm:6.1.0" + dependencies: + cron: "npm:4.3.5" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + "@nestjs/core": ^10.0.0 || ^11.0.0 + checksum: 10/640cf14e3701d08d0432c623668789c279790f88c59e1c22ca28a81713d8584cdf36bbe76ecc5afab7e13cd4648b75e47ab798ce14875501ed2cbb9e7cab6771 + languageName: node + linkType: hard + +"@nestjs/schematics@npm:^11.0.1, @nestjs/schematics@npm:^11.0.5": + version: 11.0.9 + resolution: "@nestjs/schematics@npm:11.0.9" + dependencies: + "@angular-devkit/core": "npm:19.2.17" + "@angular-devkit/schematics": "npm:19.2.17" + comment-json: "npm:4.4.1" + jsonc-parser: "npm:3.3.1" + pluralize: "npm:8.0.0" + peerDependencies: + typescript: ">=4.8.2" + checksum: 10/d916f454858e9cb28d61842d46e04e07a5f48cd85f7c0ae6ff14835a2d6b70353e2da16e2d4809fc11b5ac62282af6ebe9e8f365610374647713d90e65692e00 + languageName: node + linkType: hard + +"@nestjs/swagger@npm:^11.2.0": + version: 11.2.4 + resolution: "@nestjs/swagger@npm:11.2.4" + dependencies: + "@microsoft/tsdoc": "npm:0.16.0" + "@nestjs/mapped-types": "npm:2.1.0" + js-yaml: "npm:4.1.1" + lodash: "npm:4.17.21" + path-to-regexp: "npm:8.3.0" + swagger-ui-dist: "npm:5.31.0" + peerDependencies: + "@fastify/static": ^8.0.0 || ^9.0.0 + "@nestjs/common": ^11.0.1 + "@nestjs/core": ^11.0.1 + class-transformer: "*" + class-validator: "*" + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + "@fastify/static": + optional: true + class-transformer: + optional: true + class-validator: + optional: true + checksum: 10/da4c797df5d6d4da969e0036d09e02d9b437ea1640a55dab5a79db9c0fae36d357b3e1affb2b1a65bcef499aadbc9ca6010ca4513cbd545c6cf47e797fb176a1 + languageName: node + linkType: hard + +"@nestjs/terminus@npm:^11.0.0": + version: 11.0.0 + resolution: "@nestjs/terminus@npm:11.0.0" + dependencies: + boxen: "npm:5.1.2" + check-disk-space: "npm:3.4.0" + peerDependencies: + "@grpc/grpc-js": "*" + "@grpc/proto-loader": "*" + "@mikro-orm/core": "*" + "@mikro-orm/nestjs": "*" + "@nestjs/axios": ^2.0.0 || ^3.0.0 || ^4.0.0 + "@nestjs/common": ^10.0.0 || ^11.0.0 + "@nestjs/core": ^10.0.0 || ^11.0.0 + "@nestjs/microservices": ^10.0.0 || ^11.0.0 + "@nestjs/mongoose": ^11.0.0 + "@nestjs/sequelize": ^10.0.0 || ^11.0.0 + "@nestjs/typeorm": ^10.0.0 || ^11.0.0 + "@prisma/client": "*" + mongoose: "*" + reflect-metadata: 0.1.x || 0.2.x + rxjs: 7.x + sequelize: "*" + typeorm: "*" + peerDependenciesMeta: + "@grpc/grpc-js": + optional: true + "@grpc/proto-loader": + optional: true + "@mikro-orm/core": + optional: true + "@mikro-orm/nestjs": + optional: true + "@nestjs/axios": + optional: true + "@nestjs/microservices": + optional: true + "@nestjs/mongoose": + optional: true + "@nestjs/sequelize": + optional: true + "@nestjs/typeorm": + optional: true + "@prisma/client": + optional: true + mongoose: + optional: true + sequelize: + optional: true + typeorm: + optional: true + checksum: 10/586862409aed749b9a7030aa72e7b018c06896ee889c0b1016c6c690b25d370a75a80251201c1955deb4c780cd05866c108ae608a9e2c0e0adc8bb0868d2f9aa + languageName: node + linkType: hard + +"@nestjs/typeorm@npm:^11.0.0": + version: 11.0.0 + resolution: "@nestjs/typeorm@npm:11.0.0" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + "@nestjs/core": ^10.0.0 || ^11.0.0 + reflect-metadata: ^0.1.13 || ^0.2.0 + rxjs: ^7.2.0 + typeorm: ^0.3.0 + checksum: 10/2bfd490565ba2a0b007efecb4eedd5a24c4df126cdbbb0dd99b082653596674ac78f911f55422330256e5f6a8d49f615510c9ec1447c486749f8becf9d3679a5 + languageName: node + linkType: hard + +"@nestjs/websockets@npm:^11.1.2": + version: 11.1.11 + resolution: "@nestjs/websockets@npm:11.1.11" + dependencies: + iterare: "npm:1.2.1" + object-hash: "npm:3.0.0" + tslib: "npm:2.8.1" + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/core": ^11.0.0 + "@nestjs/platform-socket.io": ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + "@nestjs/platform-socket.io": + optional: true + checksum: 10/fd2d42f2b4692cdb62b40a51c1ba3d4f41920fc303e2963060108e2f99a06dfe6ccad72178db9a196e35d8121a9e9731f65f4f97819dc834a1767bfdcd4c6a1b + languageName: node + linkType: hard + +"@newrelic/fn-inspect@npm:^4.4.0": + version: 4.4.0 + resolution: "@newrelic/fn-inspect@npm:4.4.0" + dependencies: + nan: "npm:^2.22.2" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.8.1" + prebuildify: "npm:^6.0.1" + checksum: 10/a2bfcac79b2b03e8d8d2ce47ed36bdcea0148e911610c278bf1601a61ac9d53a867e6ade3bbd4042401dff2e9c829fc8e9b3cef17a06a781af2c41a5b1fcf33c + languageName: node + linkType: hard + +"@newrelic/native-metrics@npm:^11.1.0": + version: 11.1.0 + resolution: "@newrelic/native-metrics@npm:11.1.0" + dependencies: + nan: "npm:^2.22.2" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.8.1" + prebuildify: "npm:^6.0.1" + checksum: 10/ced07f45186c98b589b7079a5773cd7e53b9c024f97022889d51fe2c70f99f4894744030d641e350bd1ab2744ae05483b57afaf08a1ae3bdf2b655e02852bc28 + languageName: node + linkType: hard + +"@newrelic/security-agent@npm:^2.4.2": + version: 2.4.4 + resolution: "@newrelic/security-agent@npm:2.4.4" + dependencies: + axios: "npm:^1.12.0" + check-disk-space: "npm:^3.4.0" + content-type: "npm:^1.0.5" + cron: "npm:^3.1.7" + fast-safe-stringify: "npm:^2.1.1" + find-package-json: "npm:^1.2.0" + hash.js: "npm:^1.1.7" + html-entities: "npm:^2.3.6" + https-proxy-agent: "npm:^7.0.4" + is-invalid-path: "npm:^1.0.2" + js-yaml: "npm:^4.1.0" + jsonschema: "npm:^1.4.1" + lodash: "npm:^4.17.21" + log4js: "npm:^6.9.1" + pretty-bytes: "npm:^5.6.0" + request-ip: "npm:^3.3.0" + ringbufferjs: "npm:^2.0.0" + semver: "npm:^7.5.4" + unescape: "npm:^1.0.1" + unescape-js: "npm:^1.1.4" + uuid: "npm:^9.0.1" + ws: "npm:^8.17.1" + checksum: 10/d851ae9a5db9561bd496a22095e56c78059f240f6895569c443190a2108b3bd667a7168ac5c4647ff47512bf17f8f84b35495fe1dc1446b3666103a18e45cbbe + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10/6ab2a9b8a1d67b067922c36f259e3b3dfd6b97b219c540877a4944549a4d49ea5ceba5663905ab5289682f1f3c15ff441d02f0447f620a42e1cb5e1937174d4b + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10/012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10/40033e33e96e97d77fba5a238e4bba4487b8284678906a9f616b5579ddaf868a18874c0054a75402c9fbaaa033a25ceae093af58c9c30278e35c23c9479e79b0 + languageName: node + linkType: hard + +"@npmcli/agent@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/agent@npm:4.0.0" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^11.2.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10/1a81573becc60515031accc696e6405e9b894e65c12b98ef4aeee03b5617c41948633159dbf6caf5dde5b47367eeb749bdc7b7dfb21960930a9060a935c6f636 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^5.0.0": + version: 5.0.0 + resolution: "@npmcli/fs@npm:5.0.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10/4935c7719d17830d0f9fa46c50be17b2a3c945cec61760f6d0909bce47677c42e1810ca673305890f9e84f008ec4d8e841182f371e42100a8159d15f22249208 + languageName: node + linkType: hard + +"@nuxt/opencollective@npm:0.4.1": + version: 0.4.1 + resolution: "@nuxt/opencollective@npm:0.4.1" + dependencies: + consola: "npm:^3.2.3" + bin: + opencollective: bin/opencollective.js + checksum: 10/37739657e87196c7f1019a76bc33dc6e33b028eeeec43ffbf29c821e89bf5c170514e9e224456e1da85d95859ba63a3a36bd7ce1b82f2d366f7be3d6299e7631 + languageName: node + linkType: hard + +"@one-ini/wasm@npm:0.1.1": + version: 0.1.1 + resolution: "@one-ini/wasm@npm:0.1.1" + checksum: 10/673c11518dba2e582e42415cbefe928513616f3af25e12f6e4e6b1b98b52b3e6c14bc251a361654af63cd64f208f22a1f7556fa49da2bf7efcf28cb14f16f807 + languageName: node + linkType: hard + +"@opentelemetry/api-logs@npm:0.201.1": + version: 0.201.1 + resolution: "@opentelemetry/api-logs@npm:0.201.1" + dependencies: + "@opentelemetry/api": "npm:^1.3.0" + checksum: 10/baa14906caf848b7ff32fdd2b8cbad5c96b6e5b4bb4e52cb4118b323b77b2e99630b4d58d92f110343d475a21fd5bdcaaa37c29a4a386136ed4ee01528a2b2ed + languageName: node + linkType: hard + +"@opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.9.0": + version: 1.9.0 + resolution: "@opentelemetry/api@npm:1.9.0" + checksum: 10/a607f0eef971893c4f2ee2a4c2069aade6ec3e84e2a1f5c2aac19f65c5d9eeea41aa72db917c1029faafdd71789a1a040bdc18f40d63690e22ccae5d7070f194 + languageName: node + linkType: hard + +"@opentelemetry/core@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/core@npm:2.0.1" + dependencies: + "@opentelemetry/semantic-conventions": "npm:^1.29.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/dd891afd427067a9e6c610c36ab5638b0b9e5303ccca7c75ad744f5db53c6162a4b5d9cd2f5a77cdc3e4bda2eae850a4e29983ea244c929b7b872b7e086fc61c + languageName: node + linkType: hard + +"@opentelemetry/core@npm:2.3.0, @opentelemetry/core@npm:^2.0.0": + version: 2.3.0 + resolution: "@opentelemetry/core@npm:2.3.0" + dependencies: + "@opentelemetry/semantic-conventions": "npm:^1.29.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/a4deceb8088e3d8d5ba0460778d758a1b74ab6768dd97ec645ae6d4cbb64746da1a29d103c98eeb1a713448da556fd7fcf350c6c5aff6388079365aa6ae32465 + languageName: node + linkType: hard + +"@opentelemetry/exporter-metrics-otlp-http@npm:0.201.1": + version: 0.201.1 + resolution: "@opentelemetry/exporter-metrics-otlp-http@npm:0.201.1" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.201.1" + "@opentelemetry/otlp-transformer": "npm:0.201.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/764860e8744bcfc08ad48c97e7980412500c9f215193b15a33ca53e7d0161349f6d60e47360eef3487515cc9832ceec00e1cb5e817e232ac2cb725cd453d9281 + languageName: node + linkType: hard + +"@opentelemetry/exporter-metrics-otlp-proto@npm:^0.201.1": + version: 0.201.1 + resolution: "@opentelemetry/exporter-metrics-otlp-proto@npm:0.201.1" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.201.1" + "@opentelemetry/otlp-exporter-base": "npm:0.201.1" + "@opentelemetry/otlp-transformer": "npm:0.201.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/3ac7cc346dbb3b3881057bf32c4e1fb09fbb030d3a7b4b43522340beeeb2f33e886bb7bea2a8a9f3c644f2bfc846bc8476d720fbfe4851e91f19867013005afc + languageName: node + linkType: hard + +"@opentelemetry/otlp-exporter-base@npm:0.201.1": + version: 0.201.1 + resolution: "@opentelemetry/otlp-exporter-base@npm:0.201.1" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-transformer": "npm:0.201.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/d9c64ebf531e5a7e3d42537d2058e331165e4764b4a54d453668c1f8bbfa14008255771110bb106aa44c094ad76933a46f37f67d4b362908d511e57e72b7cd09 + languageName: node + linkType: hard + +"@opentelemetry/otlp-transformer@npm:0.201.1": + version: 0.201.1 + resolution: "@opentelemetry/otlp-transformer@npm:0.201.1" + dependencies: + "@opentelemetry/api-logs": "npm:0.201.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-logs": "npm:0.201.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" + protobufjs: "npm:^7.3.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/bed6f7d12aba212cfc9dd0c482de6d983f31a994faa4cb13f651f1cbe98ae8935ed25a4a25887cdcdc9a53af1ee8cd3406e869d900499c0cbadf87f3218dcdb4 + languageName: node + linkType: hard + +"@opentelemetry/resources@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/resources@npm:2.0.1" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/282f3831de2755d0fda2d8b6e37f9587ea248066d50c7d2f14c803ac9d5262a0f1db98a4185bcdc5acaeeece0b61f4fce43bc3896a79f1da79045ae4928618bf + languageName: node + linkType: hard + +"@opentelemetry/resources@npm:2.3.0, @opentelemetry/resources@npm:^2.0.1": + version: 2.3.0 + resolution: "@opentelemetry/resources@npm:2.3.0" + dependencies: + "@opentelemetry/core": "npm:2.3.0" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/8deee5b81a9fe730569402da71190def11ed8b9a8f87b8340dafc773a1e174e52ca28c8117d26ff75aaea975adf6949fa0433305f3c5c0ce718e312e2826765a + languageName: node + linkType: hard + +"@opentelemetry/sdk-logs@npm:0.201.1": + version: 0.201.1 + resolution: "@opentelemetry/sdk-logs@npm:0.201.1" + dependencies: + "@opentelemetry/api-logs": "npm:0.201.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ">=1.4.0 <1.10.0" + checksum: 10/c2d8aad418268c5ab4ad18f8eea5bb11fff1659b9bbbcd30546a622c2a6e04e3361de7809e702bff7c151cf7c21408ab8fd798b43ffbc8f549bfb91d0c40d4bb + languageName: node + linkType: hard + +"@opentelemetry/sdk-metrics@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/sdk-metrics@npm:2.0.1" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ">=1.9.0 <1.10.0" + checksum: 10/eb23d0657ce7ef0784f6c89af650de83530099782758fce574316a8e82ff2bca0eb3adffa88c5fdd04eaced6150deb53ea0ea05aae06d2783795691734e85473 + languageName: node + linkType: hard + +"@opentelemetry/sdk-metrics@npm:^2.0.1": + version: 2.3.0 + resolution: "@opentelemetry/sdk-metrics@npm:2.3.0" + dependencies: + "@opentelemetry/core": "npm:2.3.0" + "@opentelemetry/resources": "npm:2.3.0" + peerDependencies: + "@opentelemetry/api": ">=1.9.0 <1.10.0" + checksum: 10/c657f19f9ce7f887ac9d092260b9848411ba5a7c77457b864b440cba9073f7b913cfcb2f582c8b9239635fcfd755613510524964d306f1c16ec659a85e7fcdcb + languageName: node + linkType: hard + +"@opentelemetry/sdk-trace-base@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/sdk-trace-base@npm:2.0.1" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/9de1e36bbce9bd7c0563e6395765fffc0f8c78806cb33cc95267e98dffd82de33857a51288073a104c10418b934e51560bcb5dcaf4e63e5c9e096f65cadd42cd + languageName: node + linkType: hard + +"@opentelemetry/sdk-trace-base@npm:^2.0.0": + version: 2.3.0 + resolution: "@opentelemetry/sdk-trace-base@npm:2.3.0" + dependencies: + "@opentelemetry/core": "npm:2.3.0" + "@opentelemetry/resources": "npm:2.3.0" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" + peerDependencies: + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/235b3117704b69ead030819eaca5aa161ca6da2930603a26cb6aa32f07e6a4b1268a97471911a1939d308f8e9fda2c9b486febd208f5edcf506b79eeeed31aed + languageName: node + linkType: hard + +"@opentelemetry/semantic-conventions@npm:^1.29.0": + version: 1.38.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.38.0" + checksum: 10/9d549f4896e900f644d5e70dd7142505daff88ed83c1cb7bcd976ac55e9496d4ddd686bb2815dd68655c739950514394c3b73ff51e53b2e4ff2d54a7f6d22521 + languageName: node + linkType: hard + +"@oxc-resolver/binding-android-arm-eabi@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-android-arm-eabi@npm:11.16.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@oxc-resolver/binding-android-arm64@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-android-arm64@npm:11.16.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-resolver/binding-darwin-arm64@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-darwin-arm64@npm:11.16.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-resolver/binding-darwin-x64@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-darwin-x64@npm:11.16.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxc-resolver/binding-freebsd-x64@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-freebsd-x64@npm:11.16.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.16.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-arm-musleabihf@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-arm-musleabihf@npm:11.16.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-arm64-gnu@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-arm64-gnu@npm:11.16.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-arm64-musl@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-arm64-musl@npm:11.16.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-ppc64-gnu@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-ppc64-gnu@npm:11.16.2" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-riscv64-gnu@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-riscv64-gnu@npm:11.16.2" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-riscv64-musl@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-riscv64-musl@npm:11.16.2" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-s390x-gnu@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-s390x-gnu@npm:11.16.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-x64-gnu@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-x64-gnu@npm:11.16.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-resolver/binding-linux-x64-musl@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-linux-x64-musl@npm:11.16.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@oxc-resolver/binding-openharmony-arm64@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-openharmony-arm64@npm:11.16.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-resolver/binding-wasm32-wasi@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-wasm32-wasi@npm:11.16.2" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.1.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@oxc-resolver/binding-win32-arm64-msvc@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-win32-arm64-msvc@npm:11.16.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-resolver/binding-win32-ia32-msvc@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-win32-ia32-msvc@npm:11.16.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@oxc-resolver/binding-win32-x64-msvc@npm:11.16.2": + version: 11.16.2 + resolution: "@oxc-resolver/binding-win32-x64-msvc@npm:11.16.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@pinojs/redact@npm:^0.4.0": + version: 0.4.0 + resolution: "@pinojs/redact@npm:0.4.0" + checksum: 10/2210ffb6b38357853d47239fd0532cc9edb406325270a81c440a35cece22090127c30c2ead3eefa3e608f2244087485308e515c431f4f69b6bd2e16cbd32812b + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff + languageName: node + linkType: hard + +"@pkgr/core@npm:^0.2.9": + version: 0.2.9 + resolution: "@pkgr/core@npm:0.2.9" + checksum: 10/bb2fb86977d63f836f8f5b09015d74e6af6488f7a411dcd2bfdca79d76b5a681a9112f41c45bdf88a9069f049718efc6f3900d7f1de66a2ec966068308ae517f + languageName: node + linkType: hard + +"@prisma/prisma-fmt-wasm@npm:^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085": + version: 4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085 + resolution: "@prisma/prisma-fmt-wasm@npm:4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" + checksum: 10/6b413ee7d51b3cd4eb20869a5772c42200a93c9a763c64c5b64249d5c87603244a546deb8e1a9cb21923d891bcc90099633ee2dbb76c4dafc975bc8e4f9c459c + languageName: node + linkType: hard + +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 10/8a938d84fe4889411296db66b29287bd61ea3c14c2d23e7a8325f46a2b8ce899857c5f038d65d7641805e6c1d06b495525c7faf00c44f85a7ee6476649034969 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 10/c71b100daeb3c9bdccab5cbc29495b906ba0ae22ceedc200e1ba49717d9c4ab15a6256839cebb6f9c6acae4ed7c25c67e0a95e734f612b258261d1a3098fe342 + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 10/c6ee5fa172a8464f5253174d3c2353ea520c2573ad7b6476983d9b1346f4d8f2b44aa29feb17a949b83c1816bc35286a5ea265ed9d8fdd2865acfa09668c0447 + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 10/03af3e99f17ad421283d054c88a06a30a615922a817741b43ca1b13e7c6b37820a37f6eba9980fb5150c54dba6e26cb6f7b64a6f7d8afa83596fafb3afa218c3 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.1" + "@protobufjs/inquire": "npm:^1.1.0" + checksum: 10/67ae40572ad536e4ef94269199f252c024b66e3059850906bdaee161ca1d75c73d04d35cd56f147a8a5a079f5808e342b99e61942c1dae15604ff0600b09a958 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 10/634c2c989da0ef2f4f19373d64187e2a79f598c5fb7991afb689d29a2ea17c14b796b29725945fa34b9493c17fb799e08ac0a7ccaae460ee1757d3083ed35187 + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: 10/c09efa34a5465cb120775e1a482136f2340a58b4abce7e93d72b8b5a9324a0e879275016ef9fcd73d72a4731639c54f2bb755bb82f916e4a78892d1d840bb3d2 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 10/bb709567935fd385a86ad1f575aea98131bbd719c743fb9b6edd6b47ede429ff71a801cecbd64fc72deebf4e08b8f1bd8062793178cdaed3713b8d15771f9b83 + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: 10/b9c7047647f6af28e92aac54f6f7c1f7ff31b201b4bfcc7a415b2861528854fce3ec666d7e7e10fd744da905f7d4aef2205bbcc8944ca0ca7a82e18134d00c46 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: 10/131e289c57534c1d73a0e55782d6751dd821db1583cb2f7f7e017c9d6747addaebe79f28120b2e0185395d990aad347fb14ffa73ef4096fa38508d61a0e64602 + languageName: node + linkType: hard + +"@scarf/scarf@npm:=1.4.0": + version: 1.4.0 + resolution: "@scarf/scarf@npm:1.4.0" + checksum: 10/1b39a18fa29e91cfbc134c588e20c5f01a1b21ec4473614123801155b48378e9c3bf72adaca8c67e433ae951ab653268e9502cc5733230d8927532f74a6b89c9 + languageName: node + linkType: hard + +"@selderee/plugin-htmlparser2@npm:^0.11.0": + version: 0.11.0 + resolution: "@selderee/plugin-htmlparser2@npm:0.11.0" + dependencies: + domhandler: "npm:^5.0.3" + selderee: "npm:^0.11.0" + checksum: 10/7550108d270e6ea2be4850d55cbf4d58d5a90c109a15b874c3c7c622a1399bd8015359ef3672983a86118432ca8325a6aca1fe79d961b01278fdaeaea8895c5f + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^4.0.0": + version: 4.6.0 + resolution: "@sindresorhus/is@npm:4.6.0" + checksum: 10/e7f36ed72abfcd5e0355f7423a72918b9748bb1ef370a59f3e5ad8d40b728b85d63b272f65f63eec1faf417cda89dcb0aeebe94015647b6054659c1442fe5ce0 + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^5.2.0": + version: 5.6.0 + resolution: "@sindresorhus/is@npm:5.6.0" + checksum: 10/b077c325acec98e30f7d86df158aaba2e7af2acb9bb6a00fda4b91578539fbff4ecebe9b934e24fec0e6950de3089d89d79ec02d9062476b20ce185be0e01bd6 + languageName: node + linkType: hard + +"@smithy/abort-controller@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/abort-controller@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/1da13fa31900a8ee7ad2f561a27510a3d1fbda417aea8ec9a5bf681ebecd92f8264d4ee1ba2cd96ebfe71927b3e2f5eb5959e8ed80d86d04951059fb70a0a46d + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader-native@npm:^4.2.1": + version: 4.2.1 + resolution: "@smithy/chunked-blob-reader-native@npm:4.2.1" + dependencies: + "@smithy/util-base64": "npm:^4.3.0" + tslib: "npm:^2.6.2" + checksum: 10/491cd1fbf74c53cc8c63abef1d9c0e93d1c0773db2c4458d4d3bd08217ea58872e413191b56259fd8081653ee07628e3ffcf7ff594d124378401fc3637794474 + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^5.2.0": + version: 5.2.0 + resolution: "@smithy/chunked-blob-reader@npm:5.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/c2f3b93343daba9a71e2f00fb93ae527a03c0adb6c6c6e194834bf4a67111e87f0694e2d9dd9b70bca87e9eb9da1d905d4450147e54e4cd27c6703dd98d58e0c + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^4.4.5": + version: 4.4.5 + resolution: "@smithy/config-resolver@npm:4.4.5" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-config-provider": "npm:^4.2.0" + "@smithy/util-endpoints": "npm:^3.2.7" + "@smithy/util-middleware": "npm:^4.2.7" + tslib: "npm:^2.6.2" + checksum: 10/b86f3299f86cd93c84a15ccc7e223b032d3ce8c97d13d3a777515ed9874bb1ec116988204caace744cac014fdfda315682e43644142f44c7ada0bf426b7bfa8b + languageName: node + linkType: hard + +"@smithy/core@npm:^3.20.2, @smithy/core@npm:^3.20.3": + version: 3.20.3 + resolution: "@smithy/core@npm:3.20.3" + dependencies: + "@smithy/middleware-serde": "npm:^4.2.8" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-body-length-browser": "npm:^4.2.0" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-stream": "npm:^4.5.8" + "@smithy/util-utf8": "npm:^4.2.0" + "@smithy/uuid": "npm:^1.1.0" + tslib: "npm:^2.6.2" + checksum: 10/44f08b510f3de910a1c51f7ec603b48c50949917473e75cea4d7ed35992e9c052b8cda5311cd406e69a69213ed131d6c992b6dd1b382475eea5c5d575872fd54 + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/credential-provider-imds@npm:4.2.7" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + tslib: "npm:^2.6.2" + checksum: 10/d017372f20b8cfc7b972a1f5d277712a8ec340cdb7da4ee2c14ec63972147f651196f5f1c570a82f534645600471480da11257d5d43ec47f601640a01c30baf9 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/eventstream-codec@npm:4.2.7" + dependencies: + "@aws-crypto/crc32": "npm:5.2.0" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-hex-encoding": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/8b772db92fc0b694f271e72ebcd34becadd020c03faa25170892463b43c79510af5f259c2d21ed7728fe8fe4014d17a6607412d62d36e73b28197d14f3de22ae + languageName: node + linkType: hard + +"@smithy/eventstream-serde-browser@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/eventstream-serde-browser@npm:4.2.7" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/c1ab7b131d73e795dd8f4c86e7d833f9503c9c63f3375c833857ded49eae223aee846b1b4c1d140290e467d00d8262dcee8ab928aba8a75d4a1a6f3493965222 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^4.3.7": + version: 4.3.7 + resolution: "@smithy/eventstream-serde-config-resolver@npm:4.3.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/072a9b376de2d6143bd0ab87507d3441341b3e2eee7d1ed083af662170daea0ebb49cf6737a6b09b0a7d6fa44df80f875651867c778fff5a4a365d01826d0b05 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/eventstream-serde-node@npm:4.2.7" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/b578ba12d3853d76c3965ad9201ac6d9205f8e8de14e2b1b7aa3f2419017c25d8307563eb29b59cc0cbfa70a638d3cc7126448ff8f3603803648ca8cb5876e88 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/eventstream-serde-universal@npm:4.2.7" + dependencies: + "@smithy/eventstream-codec": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/6d8bcc898b315e961f1e3e41ac291f9cfb10ca199d2e70b489e878b5544ce88c2123df186ed38ec4f496d99a47890e95c6cd0acee173e0ee4cba79a095e5be5b + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^5.3.8": + version: 5.3.8 + resolution: "@smithy/fetch-http-handler@npm:5.3.8" + dependencies: + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/querystring-builder": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-base64": "npm:^4.3.0" + tslib: "npm:^2.6.2" + checksum: 10/4e7c20c10d32c117c4fc48b478c23d211d7487df34b216402403f94a0943a3c4a433fc48b0eb07e91fc36ccc827dd58f9784d4e828315ae05b7acf25f6fee342 + languageName: node + linkType: hard + +"@smithy/hash-blob-browser@npm:^4.2.8": + version: 4.2.8 + resolution: "@smithy/hash-blob-browser@npm:4.2.8" + dependencies: + "@smithy/chunked-blob-reader": "npm:^5.2.0" + "@smithy/chunked-blob-reader-native": "npm:^4.2.1" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/6e5507ebea6244f9a2d4084853ff607591ad81f732211cf05ef2add12b322b3277730b149bd26bdc5f432232394b0bbab8517f6763d7d7b69cb654d8395a2cb9 + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/hash-node@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + "@smithy/util-buffer-from": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/e7fb084d56db52fad35830b9be6f316347738831850c73e8ed9f4dfe2d2fd2b7ef74655e620ebec8d87a2d66eb73ad017830d4b3a3540eeac89ca23a3170236a + languageName: node + linkType: hard + +"@smithy/hash-stream-node@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/hash-stream-node@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/88f20898060d7f282b2a4a7d6ac2d09dcdd3311e30e78f241732551a49f674b1d81ff0a837080e19254308d94690ad1cd62d5afc862757cd0856dc2007daaf04 + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/invalid-dependency@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/f9d563b7d8476c089487f82a4716fe29ef21b38110f5efcb1c0f9b4b9c2654fa0da120fca7d8b819ac3fc081b83d7359eed61e83fbe8c047aeacc5f0b5a10398 + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/is-array-buffer@npm:2.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/d366743ecc7a9fc3bad21dbb3950d213c12bdd4aeb62b1265bf6cbe38309df547664ef3e51ab732e704485194f15e89d361943b0bfbe3fe1a4b3178b942913cc + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/is-array-buffer@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/fdc097ce6a8b241565e2d56460ec289730bcd734dcde17c23d1eaaa0996337f897217166276a3fd82491fe9fd17447aadf62e8d9056b3d2b9daf192b4b668af9 + languageName: node + linkType: hard + +"@smithy/md5-js@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/md5-js@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/6d9cb3f775aff31365a6d0ccd0d7ba97c3625f8ea0939bd06d7c50bd93f3f2fb5502c8381a47266db6ee8325baaeb30978901f40ad804f7dba733b50af4224f5 + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/middleware-content-length@npm:4.2.7" + dependencies: + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/91ec3b159bf888926ca64e6e4daa5240339e6ab0404c60bf35e91a06842a9c914e23fa4a87fb5f6a7faadbf90ba57ee5f7ad5ddfaa4430a04d58ade089af5c6e + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^4.4.3, @smithy/middleware-endpoint@npm:^4.4.4": + version: 4.4.4 + resolution: "@smithy/middleware-endpoint@npm:4.4.4" + dependencies: + "@smithy/core": "npm:^3.20.3" + "@smithy/middleware-serde": "npm:^4.2.8" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + "@smithy/url-parser": "npm:^4.2.7" + "@smithy/util-middleware": "npm:^4.2.7" + tslib: "npm:^2.6.2" + checksum: 10/faab14b250586f72f261eece7d5cd515b7f6e7a3a99f4cb21fed723c732fd42be65995a17fc4a7d05e4c83d8ee1f9023f13314e8a10e1d99b033a30044d37ecd + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^4.4.19": + version: 4.4.20 + resolution: "@smithy/middleware-retry@npm:4.4.20" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/service-error-classification": "npm:^4.2.7" + "@smithy/smithy-client": "npm:^4.10.5" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-retry": "npm:^4.2.7" + "@smithy/uuid": "npm:^1.1.0" + tslib: "npm:^2.6.2" + checksum: 10/a145eacf859e9a1040362873028f37e0a941cd8a625b071a05a1c9248b1ad174c972c087097b3fb873c3697c9833793822909dc15e477b47e56396c0a64a6cab + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^4.2.8": + version: 4.2.8 + resolution: "@smithy/middleware-serde@npm:4.2.8" + dependencies: + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/775e773a779e488f47d46024202f9faa87d8cabc44d64daae2fc272dfe55f5b2a929660517a6cdb201a5ee40d1b37740029e68d335997beb1a5718fac52e5fbf + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/middleware-stack@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/8e5342192271eb58b79cb4175fde62a969d481c6f16a438b594d3a87cd3386188d03412d7d59c6e9181d15efd2cb65f5feb7d6d2df0c65f5a02e886054e90ab9 + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^4.3.7": + version: 4.3.7 + resolution: "@smithy/node-config-provider@npm:4.3.7" + dependencies: + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/shared-ini-file-loader": "npm:^4.4.2" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/eecd04b69623fba976f12129bf2c4f8a4d4b6462b7e852894a1863754a1e900249564215d5cde664ed54d11b061ebcaa86357c43bd2286fe2780208f287cf9c6 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^4.4.7": + version: 4.4.7 + resolution: "@smithy/node-http-handler@npm:4.4.7" + dependencies: + "@smithy/abort-controller": "npm:^4.2.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/querystring-builder": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/14b32cac0b9959787952b8cb404e6246cb7c0e7780a18f60e22a7d4184517630f79fd112045b6cd64178ecb2ae1dc3cd75217cdcf964a8ca10d77f651194d7f1 + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/property-provider@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/8193bffe7ef04e280a5239020f7414f1f842b7487a5608961a0d230de10a4a7c090a12fff4269aac614d3ff179d3d97e6fe9a0d4476063e45c01dc6aeeb81a47 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^5.3.7": + version: 5.3.7 + resolution: "@smithy/protocol-http@npm:5.3.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/b9948867bab60f3083acb17c2d1afc1f4e69744cbae93a3f924b2a620cf0e866b44e1cf89260b876bec6ddbc0457a0728d2160a8e4f8913f1523952640f5220b + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/querystring-builder@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + "@smithy/util-uri-escape": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/0dc850cf224756be70db475f6b8df6e1b6680a89d91099dd6ad0271dff9ab30ebbbba8c7ec1c221e3f00ae79c2304031840438cf0b4d017ebb2aa72131279155 + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/querystring-parser@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/81c3ca8e6fc98371db50545b8a9d2420cd883e0369591f49bef5be16dffb9126ad51a49560ca2c94ccc37d7e9e262f43f327ec58fa3dc3328f7a9842402092f4 + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/service-error-classification@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + checksum: 10/c4ab617aee6b9811e7b54f7e4887c5d5311fd88882570188fa7f08fed4b76e7e5dd76efeaee3e4cade2d161525eca47d499ac55d4142eb06df4923a443a26df6 + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^4.4.2": + version: 4.4.2 + resolution: "@smithy/shared-ini-file-loader@npm:4.4.2" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/fd0a4c967fb9bf2f2c96bf41c54579eed80cbd75eb81351669ba7759d6c5f2139a31e535133fb985e499b7dd2c1a007e39a5fde057a1930101d3423b8252e33c + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^5.3.7": + version: 5.3.7 + resolution: "@smithy/signature-v4@npm:5.3.7" + dependencies: + "@smithy/is-array-buffer": "npm:^4.2.0" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-hex-encoding": "npm:^4.2.0" + "@smithy/util-middleware": "npm:^4.2.7" + "@smithy/util-uri-escape": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/1fa5c14433c8d852501a70f4624e36e5aa554fcb673ecb857b329ae41bc445b626d7cc57f499a4172435db5df86dc10ea927e9a76fed98a3de995258aecc79ff + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^4.10.4, @smithy/smithy-client@npm:^4.10.5": + version: 4.10.5 + resolution: "@smithy/smithy-client@npm:4.10.5" + dependencies: + "@smithy/core": "npm:^3.20.3" + "@smithy/middleware-endpoint": "npm:^4.4.4" + "@smithy/middleware-stack": "npm:^4.2.7" + "@smithy/protocol-http": "npm:^5.3.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-stream": "npm:^4.5.8" + tslib: "npm:^2.6.2" + checksum: 10/a2a95aba11660f84670b1c9618a660f0b1eacb57a34532faf9d9a37bf1c1d78f1408d23cfa36e273c3527c89cb05e8720a231d024245b3c65b30b8d9553f1581 + languageName: node + linkType: hard + +"@smithy/types@npm:^4.11.0": + version: 4.11.0 + resolution: "@smithy/types@npm:4.11.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/253484df3d0625137c745774af854c3175b0f7d56e826a03348fcb94aa2b60dd164380515920ed539b7e0b070f994d3ab20a0a95ad9fe385233921f5a48193de + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/url-parser@npm:4.2.7" + dependencies: + "@smithy/querystring-parser": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/e545eddbcc0d5b0cc7934eb0e5a9e316a3af7946a76ebdac8661c06bb38f71060acabf989d7c332ce6f120cb08cbd1b23e1974c952a5a0fc884056bbfeca2d8d + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^4.3.0": + version: 4.3.0 + resolution: "@smithy/util-base64@npm:4.3.0" + dependencies: + "@smithy/util-buffer-from": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/87065ca13e3745858e0bb0ab6374433b258c378ee2a5ef865b74f6a4208c56db7db2b9ee5f888e021de0107fae49e9957662c4c6847fe10529e2f6cc882426b4 + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-body-length-browser@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/deeb689b52652651c11530a324e07725805533899215ad1f93c5e9a14931443e22b313491a3c2a6d7f61d6dd1e84f9154d0d32de62bf61e0bd8e6ab7bf5f81ed + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^4.2.1": + version: 4.2.1 + resolution: "@smithy/util-body-length-node@npm:4.2.1" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/efb1333d35120124ec0c751b7b7d5657eb9ad6d0bf6171ff61fde2504639883d36e9562613c70eca623b726193b22601c8ff60e40a8156102d4c5b12fae222f8 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/util-buffer-from@npm:2.2.0" + dependencies: + "@smithy/is-array-buffer": "npm:^2.2.0" + tslib: "npm:^2.6.2" + checksum: 10/53253e4e351df3c4b7907dca48a0a6ceae783e98a8e73526820b122b3047a53fd127c19f4d8301f68d852011d821da519da783de57e0b22eed57c4df5b90d089 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-buffer-from@npm:4.2.0" + dependencies: + "@smithy/is-array-buffer": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/6a81e658554d7123fe089426a840b5e691aee4aa4f0d72b79af19dcf57ccb212dca518acb447714792d48c2dc99bda5e0e823dab05e450ee2393146706d476f9 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-config-provider@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/d65f36401c7a085660cf201a1b317d271e390258b619179fff88248c2db64fc35e6c62fe055f1e55be8935b06eb600379824dabf634fb26d528f54fe60c9d77b + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^4.3.18": + version: 4.3.19 + resolution: "@smithy/util-defaults-mode-browser@npm:4.3.19" + dependencies: + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/smithy-client": "npm:^4.10.5" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/d13350bbec06de2facc626b7314a7b5a6e23220a2d3ef4f1b2ee0c84fb470cf4075be7a5302a1abcebbbcffd4f777d2e0e8decd2a9468e364220ca88c5ee0673 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^4.2.21": + version: 4.2.22 + resolution: "@smithy/util-defaults-mode-node@npm:4.2.22" + dependencies: + "@smithy/config-resolver": "npm:^4.4.5" + "@smithy/credential-provider-imds": "npm:^4.2.7" + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/property-provider": "npm:^4.2.7" + "@smithy/smithy-client": "npm:^4.10.5" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/57784cba201ad66908e37c5225833025b1bdc7f72708d44df1b4e990cc58c4f45de4e2e2c6d638742a3f9bf9b51c6afd93fce3822e91b5b9d3544530179ddc6b + languageName: node + linkType: hard + +"@smithy/util-endpoints@npm:^3.2.7": + version: 3.2.7 + resolution: "@smithy/util-endpoints@npm:3.2.7" + dependencies: + "@smithy/node-config-provider": "npm:^4.3.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/a5954b8091ca60e60e8b5665e937fec5caf1c90f1f380957424906a3adb92fc7f7fd4c565e19cc138a55b95a1a6fb9f82b78eb4499687c168704f24db739c2e0 + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-hex-encoding@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/478773d73690e39167b67481116c4fd47cecfc97c3a935d88db9271fb0718627bec1cbc143efbf0cd49d1ac417bde7e76aa74139ea07e365b51e66797f63a45d + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/util-middleware@npm:4.2.7" + dependencies: + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/ffe3c2def97376a85e59cb7c1c516060e243bcf3e971538ced046f5b5e9771ef146637d430e9c87ae38ee1ac11f02a218f70fafd6d5099d7ff9595738e27c3c1 + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/util-retry@npm:4.2.7" + dependencies: + "@smithy/service-error-classification": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/176b3cdf460579a92446a4a671d72308846102481475afdb81fae1d23f8596ad3b91b6f65af0596c33a20b56ccbede351bc8c6e732fdc58eca6a90088406c631 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^4.5.8": + version: 4.5.8 + resolution: "@smithy/util-stream@npm:4.5.8" + dependencies: + "@smithy/fetch-http-handler": "npm:^5.3.8" + "@smithy/node-http-handler": "npm:^4.4.7" + "@smithy/types": "npm:^4.11.0" + "@smithy/util-base64": "npm:^4.3.0" + "@smithy/util-buffer-from": "npm:^4.2.0" + "@smithy/util-hex-encoding": "npm:^4.2.0" + "@smithy/util-utf8": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/455941f6b94c8984e91855a8e3b43b397b8a90b46d47f54f4ee9b0a7a4ba49b593087361d24315f6787f0f591d5f2c2d2a0e68bfdad8e6621a67bee99e94672c + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-uri-escape@npm:4.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/a838a3afe557d7087d4500735c79d5da72e0cd5a08f95d1a1c450ba29d9cd85c950228eedbd9b2494156f4eb8658afb0a9a5bd2df3fc4f297faed886c396242b + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^2.0.0": + version: 2.3.0 + resolution: "@smithy/util-utf8@npm:2.3.0" + dependencies: + "@smithy/util-buffer-from": "npm:^2.2.0" + tslib: "npm:^2.6.2" + checksum: 10/c766ead8dac6bc6169f4cac1cc47ef7bd86928d06255148f9528228002f669c8cc49f78dc2b9ba5d7e214d40315024a9e32c5c9130b33e20f0fe4532acd0dff5 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^4.2.0": + version: 4.2.0 + resolution: "@smithy/util-utf8@npm:4.2.0" + dependencies: + "@smithy/util-buffer-from": "npm:^4.2.0" + tslib: "npm:^2.6.2" + checksum: 10/d49f58fc6681255eecc3dee39c657b80ef8a4c5617e361bdaf6aaa22f02e378622376153cafc9f0655fb80162e88fc98bbf459f8dd5ba6d7c4b9a59e6eaa05f8 + languageName: node + linkType: hard + +"@smithy/util-waiter@npm:^4.2.7": + version: 4.2.7 + resolution: "@smithy/util-waiter@npm:4.2.7" + dependencies: + "@smithy/abort-controller": "npm:^4.2.7" + "@smithy/types": "npm:^4.11.0" + tslib: "npm:^2.6.2" + checksum: 10/16c48cf2b3a89122c8e65981769bb2ef4fbc4260465dc1b547babbc37e4a49d1e15a7e6c8a51865585a6311d844fad561c9663cd5bc772fd311c64d8549b2850 + languageName: node + linkType: hard + +"@smithy/uuid@npm:^1.1.0": + version: 1.1.0 + resolution: "@smithy/uuid@npm:1.1.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10/fe77b1cebbbf2d541ee2f07eec6d4573af16e08dd3228758f59dcbe85a504112cefe81b971818cf39e2e3fa0ed1fcc61d392cddc50fca13d9dc9bd835e366db0 + languageName: node + linkType: hard + +"@so-ric/colorspace@npm:^1.1.6": + version: 1.1.6 + resolution: "@so-ric/colorspace@npm:1.1.6" + dependencies: + color: "npm:^5.0.2" + text-hex: "npm:1.0.x" + checksum: 10/fc3285e5cb9a458d255aa678d9453174ca40689a4c692f1617907996ab8eb78839542439604ced484c4f674a5297f7ba8b0e63fcfe901174f43c3d9c3c881b52 + languageName: node + linkType: hard + +"@socket.io/component-emitter@npm:~3.1.0": + version: 3.1.2 + resolution: "@socket.io/component-emitter@npm:3.1.2" + checksum: 10/89888f00699eb34e3070624eb7b8161fa29f064aeb1389a48f02195d55dd7c52a504e52160016859f6d6dffddd54324623cdd47fd34b3d46f9ed96c18c456edc + languageName: node + linkType: hard + +"@sqltools/formatter@npm:^1.2.5": + version: 1.2.5 + resolution: "@sqltools/formatter@npm:1.2.5" + checksum: 10/ce9335025cd033f8f1ac997d290af22d5a5cdbd5f04cbf0fa18d5388871e980a4fc67875037821799b356032f851732dee1017b2ee7de84f5c2a2b8bfd5604f5 + languageName: node + linkType: hard + +"@swc/cli@npm:^0.7.7": + version: 0.7.9 + resolution: "@swc/cli@npm:0.7.9" + dependencies: + "@swc/counter": "npm:^0.1.3" + "@xhmikosr/bin-wrapper": "npm:^13.0.5" + commander: "npm:^8.3.0" + minimatch: "npm:^9.0.3" + piscina: "npm:^4.3.1" + semver: "npm:^7.3.8" + slash: "npm:3.0.0" + source-map: "npm:^0.7.3" + tinyglobby: "npm:^0.2.13" + peerDependencies: + "@swc/core": ^1.2.66 + chokidar: ^4.0.1 + peerDependenciesMeta: + chokidar: + optional: true + bin: + spack: bin/spack.js + swc: bin/swc.js + swcx: bin/swcx.js + checksum: 10/b138d2a7c85320b1075891f2442ed261d1c3afe3518549829c1211ac1063b29237b5c51db65511b69a6d5fc828cc4425bc4107d9e6a8be0dff8a05da06fc8218 + languageName: node + linkType: hard + +"@swc/core-darwin-arm64@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-darwin-arm64@npm:1.15.8" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@swc/core-darwin-x64@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-darwin-x64@npm:1.15.8" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@swc/core-linux-arm-gnueabihf@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.15.8" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@swc/core-linux-arm64-gnu@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-arm64-gnu@npm:1.15.8" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@swc/core-linux-arm64-musl@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-arm64-musl@npm:1.15.8" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@swc/core-linux-x64-gnu@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-x64-gnu@npm:1.15.8" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@swc/core-linux-x64-musl@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-linux-x64-musl@npm:1.15.8" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@swc/core-win32-arm64-msvc@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-win32-arm64-msvc@npm:1.15.8" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@swc/core-win32-ia32-msvc@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-win32-ia32-msvc@npm:1.15.8" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@swc/core-win32-x64-msvc@npm:1.15.8": + version: 1.15.8 + resolution: "@swc/core-win32-x64-msvc@npm:1.15.8" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@swc/core@npm:^1.11.29": + version: 1.15.8 + resolution: "@swc/core@npm:1.15.8" + dependencies: + "@swc/core-darwin-arm64": "npm:1.15.8" + "@swc/core-darwin-x64": "npm:1.15.8" + "@swc/core-linux-arm-gnueabihf": "npm:1.15.8" + "@swc/core-linux-arm64-gnu": "npm:1.15.8" + "@swc/core-linux-arm64-musl": "npm:1.15.8" + "@swc/core-linux-x64-gnu": "npm:1.15.8" + "@swc/core-linux-x64-musl": "npm:1.15.8" + "@swc/core-win32-arm64-msvc": "npm:1.15.8" + "@swc/core-win32-ia32-msvc": "npm:1.15.8" + "@swc/core-win32-x64-msvc": "npm:1.15.8" + "@swc/counter": "npm:^0.1.3" + "@swc/types": "npm:^0.1.25" + peerDependencies: + "@swc/helpers": ">=0.5.17" + dependenciesMeta: + "@swc/core-darwin-arm64": + optional: true + "@swc/core-darwin-x64": + optional: true + "@swc/core-linux-arm-gnueabihf": + optional: true + "@swc/core-linux-arm64-gnu": + optional: true + "@swc/core-linux-arm64-musl": + optional: true + "@swc/core-linux-x64-gnu": + optional: true + "@swc/core-linux-x64-musl": + optional: true + "@swc/core-win32-arm64-msvc": + optional: true + "@swc/core-win32-ia32-msvc": + optional: true + "@swc/core-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: 10/893156733eff53632148969d4ed570b023a2469479d0eb113319181f8517bcdddb533954eccf689265f230111893a568e8f24e1412eba71217cd390135246ee4 + languageName: node + linkType: hard + +"@swc/counter@npm:^0.1.3": + version: 0.1.3 + resolution: "@swc/counter@npm:0.1.3" + checksum: 10/df8f9cfba9904d3d60f511664c70d23bb323b3a0803ec9890f60133954173047ba9bdeabce28cd70ba89ccd3fd6c71c7b0bd58be85f611e1ffbe5d5c18616598 + languageName: node + linkType: hard + +"@swc/types@npm:^0.1.25": + version: 0.1.25 + resolution: "@swc/types@npm:0.1.25" + dependencies: + "@swc/counter": "npm:^0.1.3" + checksum: 10/f6741450224892d12df43e5ca7f3cc0287df644dcd672626eb0cc2a3a8e3e875f4b29eb11336f37c7240cf6e010ba59eb3a79f4fb8bee5cbd168dfc1326ff369 + languageName: node + linkType: hard + +"@szmarczak/http-timer@npm:^4.0.5": + version: 4.0.6 + resolution: "@szmarczak/http-timer@npm:4.0.6" + dependencies: + defer-to-connect: "npm:^2.0.0" + checksum: 10/c29df3bcec6fc3bdec2b17981d89d9c9fc9bd7d0c9bcfe92821dc533f4440bc890ccde79971838b4ceed1921d456973c4180d7175ee1d0023ad0562240a58d95 + languageName: node + linkType: hard + +"@szmarczak/http-timer@npm:^5.0.1": + version: 5.0.1 + resolution: "@szmarczak/http-timer@npm:5.0.1" + dependencies: + defer-to-connect: "npm:^2.0.1" + checksum: 10/fc9cb993e808806692e4a3337c90ece0ec00c89f4b67e3652a356b89730da98bc824273a6d67ca84d5f33cd85f317dcd5ce39d8cc0a2f060145a608a7cb8ce92 + languageName: node + linkType: hard + +"@tokenizer/inflate@npm:^0.2.6": + version: 0.2.7 + resolution: "@tokenizer/inflate@npm:0.2.7" + dependencies: + debug: "npm:^4.4.0" + fflate: "npm:^0.8.2" + token-types: "npm:^6.0.0" + checksum: 10/6cee1857e47ca0fc053d6cd87773b7c21857ab84cb847c7d9437a76d923e265c88f8e99a4ac9643c2f989f4b9791259ca17128f0480191449e2b412821a1b9a7 + languageName: node + linkType: hard + +"@tokenizer/inflate@npm:^0.4.1": + version: 0.4.1 + resolution: "@tokenizer/inflate@npm:0.4.1" + dependencies: + debug: "npm:^4.4.3" + token-types: "npm:^6.1.1" + checksum: 10/27d58757e1a6c004e86f8a5f1a40fe47cb48aa6891864d03de6eab27d42fafc1456f396bc8bc300e16913b0a85f42034d011db0213d17e544ed201a7fc24244e + languageName: node + linkType: hard + +"@tokenizer/token@npm:^0.3.0": + version: 0.3.0 + resolution: "@tokenizer/token@npm:0.3.0" + checksum: 10/889c1f1e63ac7c92c0ea22d4a2861142f1b43c3d92eb70ec42aa9e9851fab2e9952211d50f541b287781280df2f979bf5600a9c1f91fbc61b7fcf9994e9376a5 + languageName: node + linkType: hard + +"@total-typescript/ts-reset@npm:^0.6.1": + version: 0.6.1 + resolution: "@total-typescript/ts-reset@npm:0.6.1" + checksum: 10/3e18063433c4667561b2a5ea6b698d95f8ca2cbecc7e3717b60f9266e2fd7070b2006cab0df255e60eaad5658d1cec4afc89ea4ca5645c8f36a312093bba3645 + languageName: node + linkType: hard + +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.12 + resolution: "@tsconfig/node10@npm:1.0.12" + checksum: 10/27e2f989dbb20f773aa121b609a5361a473b7047ff286fce7c851e61f5eec0c74f0bdb38d5bd69c8a06f17e60e9530188f2219b1cbeabeac91f0a5fd348eac2a + languageName: node + linkType: hard + +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.11 + resolution: "@tsconfig/node12@npm:1.0.11" + checksum: 10/5ce29a41b13e7897a58b8e2df11269c5395999e588b9a467386f99d1d26f6c77d1af2719e407621412520ea30517d718d5192a32403b8dfcc163bf33e40a338a + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.3 + resolution: "@tsconfig/node14@npm:1.0.3" + checksum: 10/19275fe80c4c8d0ad0abed6a96dbf00642e88b220b090418609c4376e1cef81bf16237bf170ad1b341452feddb8115d8dd2e5acdfdea1b27422071163dc9ba9d + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.4 + resolution: "@tsconfig/node16@npm:1.0.4" + checksum: 10/202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.10.1": + version: 0.10.1 + resolution: "@tybys/wasm-util@npm:0.10.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/7fe0d239397aebb002ac4855d30c197c06a05ea8df8511350a3a5b1abeefe26167c60eda8a5508337571161e4c4b53d7c1342296123f9607af8705369de9fa7f + languageName: node + linkType: hard + +"@types/bcrypt@npm:^5.0.2": + version: 5.0.2 + resolution: "@types/bcrypt@npm:5.0.2" + dependencies: + "@types/node": "npm:*" + checksum: 10/b1f97532ffe6079cb57a464f28b5b37a30bc9620f43469e1f27ab9c979c8a114be5b667e7b115a5556fd5be463b65968da9bb32573c6faf74fecf6e565d8974b + languageName: node + linkType: hard + +"@types/body-parser@npm:*": + version: 1.19.6 + resolution: "@types/body-parser@npm:1.19.6" + dependencies: + "@types/connect": "npm:*" + "@types/node": "npm:*" + checksum: 10/33041e88eae00af2cfa0827e951e5f1751eafab2a8b6fce06cd89ef368a988907996436b1325180edaeddd1c0c7d0d0d4c20a6c9ff294a91e0039a9db9e9b658 + languageName: node + linkType: hard + +"@types/cacheable-request@npm:^6.0.1": + version: 6.0.3 + resolution: "@types/cacheable-request@npm:6.0.3" + dependencies: + "@types/http-cache-semantics": "npm:*" + "@types/keyv": "npm:^3.1.4" + "@types/node": "npm:*" + "@types/responselike": "npm:^1.0.0" + checksum: 10/159f9fdb2a1b7175eef453ae2ced5ea04c0d2b9610cc9ccd9f9abb066d36dacb1f37acd879ace10ad7cbb649490723feb396fb7307004c9670be29636304b988 + languageName: node + linkType: hard + +"@types/connect@npm:*": + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" + dependencies: + "@types/node": "npm:*" + checksum: 10/7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 + languageName: node + linkType: hard + +"@types/cookie-parser@npm:^1": + version: 1.4.10 + resolution: "@types/cookie-parser@npm:1.4.10" + peerDependencies: + "@types/express": "*" + checksum: 10/1f37b5a4115dbfd4b7bbea2d874fbf9495eca8c3e8c87fa7e38c50f9fff66222377c911cfdc7a1ea08855e822919c5534ebbcc4bf25b596bc6f7270e403483d9 + languageName: node + linkType: hard + +"@types/cors@npm:^2.8.12": + version: 2.8.19 + resolution: "@types/cors@npm:2.8.19" + dependencies: + "@types/node": "npm:*" + checksum: 10/9545cc532c9218754443f48a0c98c1a9ba4af1fe54a3425c95de75ff3158147bb39e666cb7c6bf98cc56a9c6dc7b4ce5b2cbdae6b55d5942e50c81b76ed6b825 + languageName: node + linkType: hard + +"@types/ejs@npm:^3.1.5": + version: 3.1.5 + resolution: "@types/ejs@npm:3.1.5" + checksum: 10/918898fd279108087722c1713e2ddb0c152ab839397946d164db8a18b5bbd732af9746373882a9bcf4843d35c6b191a8f569a7a4e51e90726d24501b39f40367 + languageName: node + linkType: hard + +"@types/eslint-scope@npm:^3.7.7": + version: 3.7.7 + resolution: "@types/eslint-scope@npm:3.7.7" + dependencies: + "@types/eslint": "npm:*" + "@types/estree": "npm:*" + checksum: 10/e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e + languageName: node + linkType: hard + +"@types/eslint@npm:*": + version: 9.6.1 + resolution: "@types/eslint@npm:9.6.1" + dependencies: + "@types/estree": "npm:*" + "@types/json-schema": "npm:*" + checksum: 10/719fcd255760168a43d0e306ef87548e1e15bffe361d5f4022b0f266575637acc0ecb85604ac97879ee8ae83c6a6d0613b0ed31d0209ddf22a0fe6d608fc56fe + languageName: node + linkType: hard + +"@types/estree@npm:*, @types/estree@npm:^1.0.6, @types/estree@npm:^1.0.8": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10/25a4c16a6752538ffde2826c2cc0c6491d90e69cd6187bef4a006dd2c3c45469f049e643d7e516c515f21484dc3d48fd5c870be158a5beb72f5baf3dc43e4099 + languageName: node + linkType: hard + +"@types/express-serve-static-core@npm:^5.0.0": + version: 5.1.1 + resolution: "@types/express-serve-static-core@npm:5.1.1" + dependencies: + "@types/node": "npm:*" + "@types/qs": "npm:*" + "@types/range-parser": "npm:*" + "@types/send": "npm:*" + checksum: 10/7f3d8cf7e68764c9f3e8f6a12825b69ccf5287347fc1c20b29803d4f08a4abc1153ae11d7258852c61aad50f62ef72d4c1b9c97092b0a90462c3dddec2f6026c + languageName: node + linkType: hard + +"@types/express@npm:*, @types/express@npm:^5.0.2": + version: 5.0.6 + resolution: "@types/express@npm:5.0.6" + dependencies: + "@types/body-parser": "npm:*" + "@types/express-serve-static-core": "npm:^5.0.0" + "@types/serve-static": "npm:^2" + checksum: 10/da2cc3de1b1a4d7f20ed3fb6f0a8ee08e99feb3c2eb5a8d643db77017d8d0e70fee9e95da38a73f51bcdf5eda3bb6435073c0271dc04fb16fda92e55daf911fa + languageName: node + linkType: hard + +"@types/form-data@npm:^2.2.1": + version: 2.2.1 + resolution: "@types/form-data@npm:2.2.1" + dependencies: + "@types/node": "npm:*" + checksum: 10/7a322e03928b67a0cc6fbc721ff39214eefa7e9dbb027066137a8473ba62f81c51153747d6a3aadbb206fa189f60d19ee95e0c7998ef36ab0d52fdc00edcc930 + languageName: node + linkType: hard + +"@types/heapdump@npm:^0": + version: 0.3.4 + resolution: "@types/heapdump@npm:0.3.4" + checksum: 10/a9c62274e4b0cf841811410275fb58c1d82af128e5ee61d6df94297819cc45b0c2d900b155377a734788f84f3d5b76f9de4e36682870d24b38dc414b787fe507 + languageName: node + linkType: hard + +"@types/html-to-text@npm:^9.0.4": + version: 9.0.4 + resolution: "@types/html-to-text@npm:9.0.4" + checksum: 10/dd06963e7e11cf49cfab65fdadef62db43e098963999f307cfb77c6917cc26bc2634034203f5c1268eac47af6671d50af0ce341fd085726dfec0f6228a9cd016 + languageName: node + linkType: hard + +"@types/http-cache-semantics@npm:*, @types/http-cache-semantics@npm:^4.0.2": + version: 4.0.4 + resolution: "@types/http-cache-semantics@npm:4.0.4" + checksum: 10/a59566cff646025a5de396d6b3f44a39ab6a74f2ed8150692e0f31cc52f3661a68b04afe3166ebe0d566bd3259cb18522f46e949576d5204781cd6452b7fe0c5 + languageName: node + linkType: hard + +"@types/http-errors@npm:*": + version: 2.0.5 + resolution: "@types/http-errors@npm:2.0.5" + checksum: 10/a88da669366bc483e8f3b3eb3d34ada5f8d13eeeef851b1204d77e2ba6fc42aba4566d877cca5c095204a3f4349b87fe397e3e21288837bdd945dd514120755b + languageName: node + linkType: hard + +"@types/imap-simple@npm:^4.2.10": + version: 4.2.10 + resolution: "@types/imap-simple@npm:4.2.10" + dependencies: + "@types/imap": "npm:*" + "@types/node": "npm:*" + checksum: 10/625cdb58bd89123efc01ac0616cac3e0e467cd83ff1db879ca0fbb1406ebc918f7f3bd577ec091506b7315d2d19c62e9cf1fa3cb538d82eb5d009cfcf695b886 + languageName: node + linkType: hard + +"@types/imap@npm:*": + version: 0.8.43 + resolution: "@types/imap@npm:0.8.43" + dependencies: + "@types/node": "npm:*" + checksum: 10/c4aeacfd9b7f9fe4239bc0e09087260109e3373a3e2b331bd3d525569a38e46f472eff40c03e4417a50c3a7ad5b40309bba38b0ba1de75faf22e531f05d5ebbc + languageName: node + linkType: hard + +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7 + languageName: node + linkType: hard + +"@types/jsonwebtoken@npm:9.0.10": + version: 9.0.10 + resolution: "@types/jsonwebtoken@npm:9.0.10" + dependencies: + "@types/ms": "npm:*" + "@types/node": "npm:*" + checksum: 10/d7960d995ad815511c7f4e7f09d91522dfe16e7e800cdd6226c8b1b624c534e0f3b8f8f3beb60e3189865269f028002f1a490189beca5afd02bc96ef1d68f21f + languageName: node + linkType: hard + +"@types/keyv@npm:^3.1.4": + version: 3.1.4 + resolution: "@types/keyv@npm:3.1.4" + dependencies: + "@types/node": "npm:*" + checksum: 10/e009a2bfb50e90ca9b7c6e8f648f8464067271fd99116f881073fa6fa76dc8d0133181dd65e6614d5fb1220d671d67b0124aef7d97dc02d7e342ab143a47779d + languageName: node + linkType: hard + +"@types/luxon@npm:~3.4.0": + version: 3.4.2 + resolution: "@types/luxon@npm:3.4.2" + checksum: 10/fd89566e3026559f2bc4ddcc1e70a2c16161905ed50be9473ec0cfbbbe919165041408c4f6e06c4bcf095445535052e2c099087c76b1b38e368127e618fc968d + languageName: node + linkType: hard + +"@types/luxon@npm:~3.7.0": + version: 3.7.1 + resolution: "@types/luxon@npm:3.7.1" + checksum: 10/c7bc164c278393ea0be938f986c74b4cddfab9013b1aff4495b016f771ded1d5b7b7b4825b2c7f0b8799edce19c5f531c28ff434ab3dedf994ac2d99a20fd4c4 + languageName: node + linkType: hard + +"@types/mailparser@npm:^3.4.6": + version: 3.4.6 + resolution: "@types/mailparser@npm:3.4.6" + dependencies: + "@types/node": "npm:*" + iconv-lite: "npm:^0.6.3" + checksum: 10/9a003d472acb51bcc012a3de560c82394414ffb495223213eb580dc1ecb8bae9443b54aa917ad6475d5a1a22a7cd7290cb81baf26238fc22446cde436cd82404 + languageName: node + linkType: hard + +"@types/mime-types@npm:^2": + version: 2.1.4 + resolution: "@types/mime-types@npm:2.1.4" + checksum: 10/f8c521c54ee0c0b9f90a65356a80b1413ed27ccdc94f5c7ebb3de5d63cedb559cd2610ea55b4100805c7349606a920d96e54f2d16b2f0afa6b7cd5253967ccc9 + languageName: node + linkType: hard + +"@types/mjml-core@npm:*": + version: 4.15.2 + resolution: "@types/mjml-core@npm:4.15.2" + checksum: 10/7c29f5409b4abf14153c27d0249d4169d23fa97b9b2b50fef3527a17b547e11c692cc451398a2400c505f11670c9204cff3cf5a158ee55690ad092d03b782bbb + languageName: node + linkType: hard + +"@types/mjml@npm:^4.7.4": + version: 4.7.4 + resolution: "@types/mjml@npm:4.7.4" + dependencies: + "@types/mjml-core": "npm:*" + checksum: 10/097b46f6d79c02c6d17681d5722bd476f7df7418666673d187824c30994847cd2909bdb0351a878b0690ff5e7bf096a9216e12910c82cba9b88a4ce1b45d803b + languageName: node + linkType: hard + +"@types/ms@npm:*": + version: 2.1.0 + resolution: "@types/ms@npm:2.1.0" + checksum: 10/532d2ebb91937ccc4a89389715e5b47d4c66e708d15942fe6cc25add6dc37b2be058230a327dd50f43f89b8b6d5d52b74685a9e8f70516edfc9bdd6be910eff4 + languageName: node + linkType: hard + +"@types/multer@npm:^1.4.12": + version: 1.4.13 + resolution: "@types/multer@npm:1.4.13" + dependencies: + "@types/express": "npm:*" + checksum: 10/f650a9f431007f05a5c51d6359140bea5f77d1299668d45a0791ddbf7789f1c220a3e038039c5e0de6608e273d5ee8001e9649c606d7e0e847e8774ccc5d1d68 + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=8.1.0": + version: 25.0.7 + resolution: "@types/node@npm:25.0.7" + dependencies: + undici-types: "npm:~7.16.0" + checksum: 10/1bf778bd087160903bca1d1d0ed9e0c25abd12ac9322a4d58a6918d110c8a3eaa2f5dc1437e0009b57bfa2791c2b409ebdb4bb4dc0c2e8f096db07e285e63578 + languageName: node + linkType: hard + +"@types/node@npm:^14.0.1": + version: 14.18.63 + resolution: "@types/node@npm:14.18.63" + checksum: 10/82a7775898c2ea6db0b610a463512206fb2c7adc1af482c7eb44b99d94375fff51c74f67ae75a63c5532971159f30c866a4d308000624ef02fd9a7175e277019 + languageName: node + linkType: hard + +"@types/node@npm:^22.15.24, @types/node@npm:^22.18.6": + version: 22.19.5 + resolution: "@types/node@npm:22.19.5" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10/59662e5e1cf5039ab82677a050ed8f7428510695aef160b10809a29396d98fea671e6e203f7fca98fc59d8d3f36c32efea34d34e7c18346d4ed2129d56e9293c + languageName: node + linkType: hard + +"@types/nodemailer@npm:^6.4.17": + version: 6.4.21 + resolution: "@types/nodemailer@npm:6.4.21" + dependencies: + "@aws-sdk/client-ses": "npm:^3.731.1" + "@types/node": "npm:*" + checksum: 10/3388d11defbef0b1b471720f88ff2fd206ab5879bc7089d90d3467198f869299dc374f01cf07054025c7e87b186a7297b1040d318d65e9ec19a130732aa1448d + languageName: node + linkType: hard + +"@types/pug@npm:^2.0.10": + version: 2.0.10 + resolution: "@types/pug@npm:2.0.10" + checksum: 10/b7331b87ceffe6a55b200d0fbfd3f4defdc6703ce21d25a8f3f01d9022a3b36ab0e2ee7d6eeb16b82b6375c0cadf35e1633219775237cb0c62e785fbd35df57c + languageName: node + linkType: hard + +"@types/qs@npm:*": + version: 6.14.0 + resolution: "@types/qs@npm:6.14.0" + checksum: 10/1909205514d22b3cbc7c2314e2bd8056d5f05dfb21cf4377f0730ee5e338ea19957c41735d5e4806c746176563f50005bbab602d8358432e25d900bdf4970826 + languageName: node + linkType: hard + +"@types/quoted-printable@npm:^1": + version: 1.0.2 + resolution: "@types/quoted-printable@npm:1.0.2" + checksum: 10/4ea61f57cc15cfa47a5229f1a8125ad6d35671b0ff23712d67efa5390e34d89043923062780febe9d510673e5b79ffba5ec4433c8adc5e7003c4b669eb7ebf3a + languageName: node + linkType: hard + +"@types/range-parser@npm:*": + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 10/95640233b689dfbd85b8c6ee268812a732cf36d5affead89e806fe30da9a430767af8ef2cd661024fd97e19d61f3dec75af2df5e80ec3bea000019ab7028629a + languageName: node + linkType: hard + +"@types/responselike@npm:^1.0.0": + version: 1.0.3 + resolution: "@types/responselike@npm:1.0.3" + dependencies: + "@types/node": "npm:*" + checksum: 10/6ac4b35723429b11b117e813c7acc42c3af8b5554caaf1fc750404c1ae59f9b7376bc69b9e9e194a5a97357a597c2228b7173d317320f0360d617b6425212f58 + languageName: node + linkType: hard + +"@types/retry@npm:0.12.2": + version: 0.12.2 + resolution: "@types/retry@npm:0.12.2" + checksum: 10/e5675035717b39ce4f42f339657cae9637cf0c0051cf54314a6a2c44d38d91f6544be9ddc0280587789b6afd056be5d99dbe3e9f4df68c286c36321579b1bf4a + languageName: node + linkType: hard + +"@types/send@npm:*": + version: 1.2.1 + resolution: "@types/send@npm:1.2.1" + dependencies: + "@types/node": "npm:*" + checksum: 10/81ef5790037ba1d2d458392e4241501f0f8b4838cc8797e169e179e099410e12069ec68e8dbd39211cb097c4a9b1ff1682dbcea897ab4ce21dad93438b862d27 + languageName: node + linkType: hard + +"@types/serve-static@npm:^2": + version: 2.2.0 + resolution: "@types/serve-static@npm:2.2.0" + dependencies: + "@types/http-errors": "npm:*" + "@types/node": "npm:*" + checksum: 10/f2bad1304c7d0d3b7221faff3e490c40129d3803f4fb1b2fb84f31f561071c5e6a4b876c41bbbe82d5645034eea936e946bcaaf993dac1093ce68b56effad6e0 + languageName: node + linkType: hard + +"@types/triple-beam@npm:^1.3.2": + version: 1.3.5 + resolution: "@types/triple-beam@npm:1.3.5" + checksum: 10/519b6a1b30d4571965c9706ad5400a200b94e4050feca3e7856e3ea7ac00ec9903e32e9a10e2762d0f7e472d5d03e5f4b29c16c0bd8c1f77c8876c683b2231f1 + languageName: node + linkType: hard + +"@types/uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "@types/uuid@npm:10.0.0" + checksum: 10/e3958f8b0fe551c86c14431f5940c3470127293280830684154b91dc7eb3514aeb79fe3216968833cf79d4d1c67f580f054b5be2cd562bebf4f728913e73e944 + languageName: node + linkType: hard + +"@types/validator@npm:^13.15.3": + version: 13.15.10 + resolution: "@types/validator@npm:13.15.10" + checksum: 10/63117a776ced4d066d7fb63130d90ba487d38209dd45c25641ca1a6f5040e8394cc9a855750b919b72a923c5ffb51f8474f213b10b5aaa27d9db108bef07ad10 + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.53.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.12.2" + "@typescript-eslint/scope-manager": "npm:8.53.0" + "@typescript-eslint/type-utils": "npm:8.53.0" + "@typescript-eslint/utils": "npm:8.53.0" + "@typescript-eslint/visitor-keys": "npm:8.53.0" + ignore: "npm:^7.0.5" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.4.0" + peerDependencies: + "@typescript-eslint/parser": ^8.53.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10/2cbfa92d21018d53b33db102500f121cedd67405939a11c20d04a0fdc535412f1e554479a9994a244127a151609fe16ae8bce810749261f243eac13360df1ab1 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/parser@npm:8.53.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.53.0" + "@typescript-eslint/types": "npm:8.53.0" + "@typescript-eslint/typescript-estree": "npm:8.53.0" + "@typescript-eslint/visitor-keys": "npm:8.53.0" + debug: "npm:^4.4.3" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10/5337f472aeb3d04041a3c9c9e9d9884e685ba7e4f722ab2963f1054087a62a42946dd0d39993e60506efef0d2a4cc1b0619b34e49261913d6f4d8cdbf3490d56 + languageName: node + linkType: hard + +"@typescript-eslint/project-service@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/project-service@npm:8.53.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.53.0" + "@typescript-eslint/types": "npm:^8.53.0" + debug: "npm:^4.4.3" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 10/2f232f241f57c0f42194a8bcb8c207e4ed4345d7cc097434d394c2904338e64f386903931395ef97cd2cf3ae33d98645f0d6164660d794e33259e2c3978052ff + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/scope-manager@npm:8.53.0" + dependencies: + "@typescript-eslint/types": "npm:8.53.0" + "@typescript-eslint/visitor-keys": "npm:8.53.0" + checksum: 10/40a651cfc16f9464f92b5a58492207c1f89a1ff98cfedd2d33d1dbe8234ce50c3a543267f1b489f903b001e0abcaf1568e7c9b70c009871c34af6ef3602ac0bf + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.53.0, @typescript-eslint/tsconfig-utils@npm:^8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.53.0" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 10/91f1f02ec8a3daf7d3dc9e43a847ef834444a6e073e3a4a07a311d898b225124d9c4abb4b48266d821f0ea4225614266084e5157182e7ba7aaecafefbae00c7e + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/type-utils@npm:8.53.0" + dependencies: + "@typescript-eslint/types": "npm:8.53.0" + "@typescript-eslint/typescript-estree": "npm:8.53.0" + "@typescript-eslint/utils": "npm:8.53.0" + debug: "npm:^4.4.3" + ts-api-utils: "npm:^2.4.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10/5be4036b475bbc4bb9a834beefe8114286bbe2dee54c96c65c02d6ceabac3422605802dcbefdbf20ae9ede3c85bf2f650eda2acc7ed1a3bf75f02ed478e7cdd1 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.53.0, @typescript-eslint/types@npm:^8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/types@npm:8.53.0" + checksum: 10/36ee696a92ed575385b5c1ccc46e3fec9c5d9aa6f3640f8ad0234ed5a763c9ab78c7d3419fd3d462a966f6b95472390b8040055e4e73c75c52671478e90749ff + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.53.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.53.0" + "@typescript-eslint/tsconfig-utils": "npm:8.53.0" + "@typescript-eslint/types": "npm:8.53.0" + "@typescript-eslint/visitor-keys": "npm:8.53.0" + debug: "npm:^4.4.3" + minimatch: "npm:^9.0.5" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.4.0" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 10/bdacb2f3ffde535c3955bbfbd062d2010943f7693034cde4019ccde699e826e7ef91d7e1d2f3652c30584c013924410dae5056417909e8169f1e3d7272636bd9 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/utils@npm:8.53.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.53.0" + "@typescript-eslint/types": "npm:8.53.0" + "@typescript-eslint/typescript-estree": "npm:8.53.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10/ef123c8531de793d8d4f5fa51076402bfe809481feaee605086986c370c94361c525ec550b2c4c6703cf60e026e87862428c044c763ead3ea9bf9bce8ad79310 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:8.53.0": + version: 8.53.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.53.0" + dependencies: + "@typescript-eslint/types": "npm:8.53.0" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10/879e1dfbd002059c0eb59f9660c26eb71a1643622906e4af444dbe5297e95ad210d763b53308b6372b55d85159a161982a8848352706a7d361fd3e17d6ba96d0 + languageName: node + linkType: hard + +"@tyriar/fibonacci-heap@npm:^2.0.7": + version: 2.0.9 + resolution: "@tyriar/fibonacci-heap@npm:2.0.9" + checksum: 10/5c9ae30a8be47290610217cbb77ff9d0edefd386de899070f64fa4a9baf01a46147b0034a14ea80de601ab0d1521114d0c952a588608743d7aaffafdd7a0b109 + languageName: node + linkType: hard + +"@ungap/structured-clone@npm:^1.2.0": + version: 1.3.0 + resolution: "@ungap/structured-clone@npm:1.3.0" + checksum: 10/80d6910946f2b1552a2406650051c91bbd1f24a6bf854354203d84fe2714b3e8ce4618f49cc3410494173a1c1e8e9777372fe68dce74bd45faf0a7a1a6ccf448 + languageName: node + linkType: hard + +"@voximplant/apiclient-nodejs@npm:^4.2.0": + version: 4.5.0 + resolution: "@voximplant/apiclient-nodejs@npm:4.5.0" + dependencies: + axios: "npm:0.21.4" + form-data: "npm:2.5.1" + jsonwebtoken: "npm:9.0.2" + checksum: 10/470a803b40578e6b4fa84333dbbe5ee589257a138388655394846a6fc4079c44d3f83667e0c8fc71c0d8ee593dc9fb9bbf7f369ad923025568557d3c39c0c28b + languageName: node + linkType: hard + +"@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/ast@npm:1.14.1" + dependencies: + "@webassemblyjs/helper-numbers": "npm:1.13.2" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + checksum: 10/f83e6abe38057f5d87c1fb356513a371a8b43c9b87657f2790741a66b1ef8ecf958d1391bc42f27c5fb33f58ab8286a38ea849fdd21f433cd4df1307424bab45 + languageName: node + linkType: hard + +"@webassemblyjs/floating-point-hex-parser@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.13.2" + checksum: 10/e866ec8433f4a70baa511df5e8f2ebcd6c24f4e2cc6274c7c5aabe2bcce3459ea4680e0f35d450e1f3602acf3913b6b8e4f15069c8cfd34ae8609fb9a7d01795 + languageName: node + linkType: hard + +"@webassemblyjs/helper-api-error@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-api-error@npm:1.13.2" + checksum: 10/48b5df7fd3095bb252f59a139fe2cbd999a62ac9b488123e9a0da3906ad8a2f2da7b2eb21d328c01a90da987380928706395c2897d1f3ed9e2125b6d75a920d0 + languageName: node + linkType: hard + +"@webassemblyjs/helper-buffer@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/helper-buffer@npm:1.14.1" + checksum: 10/9690afeafa5e765a34620aa6216e9d40f9126d4e37e9726a2594bf60cab6b211ef20ab6670fd3c4449dd4a3497e69e49b2b725c8da0fb213208c7f45f15f5d5b + languageName: node + linkType: hard + +"@webassemblyjs/helper-numbers@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-numbers@npm:1.13.2" + dependencies: + "@webassemblyjs/floating-point-hex-parser": "npm:1.13.2" + "@webassemblyjs/helper-api-error": "npm:1.13.2" + "@xtuc/long": "npm:4.2.2" + checksum: 10/e4c7d0b09811e1cda8eec644a022b560b28f4e974f50195375ccd007df5ee48a922a6dcff5ac40b6a8ec850d56d0ea6419318eee49fec7819ede14e90417a6a4 + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-bytecode@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.13.2" + checksum: 10/3edd191fff7296df1ef3b023bdbe6cb5ea668f6386fd197ccfce46015c6f2a8cc9763cfb86503a0b94973ad27996645afff2252ee39a236513833259a47af6ed + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-section@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + checksum: 10/6b73874f906532512371181d7088460f767966f26309e836060c5a8e4e4bfe6d523fb5f4c034b34aa22ebb1192815f95f0e264298769485c1f0980fdd63ae0ce + languageName: node + linkType: hard + +"@webassemblyjs/ieee754@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/ieee754@npm:1.13.2" + dependencies: + "@xtuc/ieee754": "npm:^1.2.0" + checksum: 10/d7e3520baa37a7309fa7db4d73d69fb869878853b1ebd4b168821bd03fcc4c0e1669c06231315b0039035d9a7a462e53de3ad982da4a426a4b0743b5888e8673 + languageName: node + linkType: hard + +"@webassemblyjs/leb128@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/leb128@npm:1.13.2" + dependencies: + "@xtuc/long": "npm:4.2.2" + checksum: 10/3a10542c86807061ec3230bac8ee732289c852b6bceb4b88ebd521a12fbcecec7c432848284b298154f28619e2746efbed19d6904aef06c49ef20a0b85f650cf + languageName: node + linkType: hard + +"@webassemblyjs/utf8@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/utf8@npm:1.13.2" + checksum: 10/27885e5d19f339501feb210867d69613f281eda695ac508f04d69fa3398133d05b6870969c0242b054dc05420ed1cc49a64dea4fe0588c18d211cddb0117cc54 + languageName: node + linkType: hard + +"@webassemblyjs/wasm-edit@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-edit@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/helper-wasm-section": "npm:1.14.1" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + "@webassemblyjs/wasm-opt": "npm:1.14.1" + "@webassemblyjs/wasm-parser": "npm:1.14.1" + "@webassemblyjs/wast-printer": "npm:1.14.1" + checksum: 10/c62c50eadcf80876713f8c9f24106b18cf208160ab842fcb92060fd78c37bf37e7fcf0b7cbf1afc05d230277c2ce0f3f728432082c472dd1293e184a95f9dbdd + languageName: node + linkType: hard + +"@webassemblyjs/wasm-gen@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-gen@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/ieee754": "npm:1.13.2" + "@webassemblyjs/leb128": "npm:1.13.2" + "@webassemblyjs/utf8": "npm:1.13.2" + checksum: 10/6085166b0987d3031355fe17a4f9ef0f412e08098d95454059aced2bd72a4c3df2bc099fa4d32d640551fc3eca1ac1a997b44432e46dc9d84642688e42c17ed4 + languageName: node + linkType: hard + +"@webassemblyjs/wasm-opt@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-opt@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + "@webassemblyjs/wasm-parser": "npm:1.14.1" + checksum: 10/fa5d1ef8d2156e7390927f938f513b7fb4440dd6804b3d6c8622b7b1cf25a3abf1a5809f615896d4918e04b27b52bc3cbcf18faf2d563cb563ae0a9204a492db + languageName: node + linkType: hard + +"@webassemblyjs/wasm-parser@npm:1.14.1, @webassemblyjs/wasm-parser@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-parser@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-api-error": "npm:1.13.2" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/ieee754": "npm:1.13.2" + "@webassemblyjs/leb128": "npm:1.13.2" + "@webassemblyjs/utf8": "npm:1.13.2" + checksum: 10/07d9805fda88a893c984ed93d5a772d20d671e9731358ab61c6c1af8e0e58d1c42fc230c18974dfddebc9d2dd7775d514ba4d445e70080b16478b4b16c39c7d9 + languageName: node + linkType: hard + +"@webassemblyjs/wast-printer@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wast-printer@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@xtuc/long": "npm:4.2.2" + checksum: 10/cef09aad2fcd291bfcf9efdae2ea1e961a1ba0f925d1d9dcdd8c746d32fbaf431b6d26a0241699c0e39f82139018aa720b4ceb84ac6f4c78f13072747480db69 + languageName: node + linkType: hard + +"@xhmikosr/archive-type@npm:^7.1.0": + version: 7.1.0 + resolution: "@xhmikosr/archive-type@npm:7.1.0" + dependencies: + file-type: "npm:^20.5.0" + checksum: 10/ce363bea7798572b74c0821248d9abbb28847290b298b02d142ee1f95f72561f1947e95121450980cf3ea03242ded914ba896681b7ca817f1a247653624d8d7e + languageName: node + linkType: hard + +"@xhmikosr/bin-check@npm:^7.1.0": + version: 7.1.0 + resolution: "@xhmikosr/bin-check@npm:7.1.0" + dependencies: + execa: "npm:^5.1.1" + isexe: "npm:^2.0.0" + checksum: 10/e087422baf51077af8937fe3b922a9e3b38d4389e99fe60545dc6ce3ee2545fa05152d544e5b7f30d7f1ad60c215bb46ca46984d80f1e4ac75579baf5b93581e + languageName: node + linkType: hard + +"@xhmikosr/bin-wrapper@npm:^13.0.5": + version: 13.2.0 + resolution: "@xhmikosr/bin-wrapper@npm:13.2.0" + dependencies: + "@xhmikosr/bin-check": "npm:^7.1.0" + "@xhmikosr/downloader": "npm:^15.2.0" + "@xhmikosr/os-filter-obj": "npm:^3.0.0" + bin-version-check: "npm:^5.1.0" + checksum: 10/5aa70b9e30a19924c244f046099af808fa6dc8b9799de72628d0b5836c6ddb68fee4c31ed5e6c4733db5ceb878b6a6f0218a42ebc67ece39aadfd81cefe3ba32 + languageName: node + linkType: hard + +"@xhmikosr/decompress-tar@npm:^8.0.1, @xhmikosr/decompress-tar@npm:^8.1.0": + version: 8.1.0 + resolution: "@xhmikosr/decompress-tar@npm:8.1.0" + dependencies: + file-type: "npm:^20.5.0" + is-stream: "npm:^2.0.1" + tar-stream: "npm:^3.1.7" + checksum: 10/c827aac52b6e894e8fb23815cfcfbdda656759c7fcb897e35a26369215ba61a7a9086fc90c82866e17a9bec3355a106b1f4e7c89a9de01181c976a10ef454fa4 + languageName: node + linkType: hard + +"@xhmikosr/decompress-tarbz2@npm:^8.1.0": + version: 8.1.0 + resolution: "@xhmikosr/decompress-tarbz2@npm:8.1.0" + dependencies: + "@xhmikosr/decompress-tar": "npm:^8.0.1" + file-type: "npm:^20.5.0" + is-stream: "npm:^2.0.1" + seek-bzip: "npm:^2.0.0" + unbzip2-stream: "npm:^1.4.3" + checksum: 10/17ead7b0fe1719ce6f2918b9b4bd8fe53c5ebd99400f47528fb6eb3d9f217ff91dd79c7619267190a037187f2fe6482e80d261ab8ae97c9dac29dab7de8f4913 + languageName: node + linkType: hard + +"@xhmikosr/decompress-targz@npm:^8.1.0": + version: 8.1.0 + resolution: "@xhmikosr/decompress-targz@npm:8.1.0" + dependencies: + "@xhmikosr/decompress-tar": "npm:^8.0.1" + file-type: "npm:^20.5.0" + is-stream: "npm:^2.0.1" + checksum: 10/d2eca44f03f2935e1b687cf61402862bbf0c71c7d1b10f6b6b94c0ea16a6839d820355ec434cf1bbc42a36e3891e52925a38ea6c3f5022746f720643ac9ce039 + languageName: node + linkType: hard + +"@xhmikosr/decompress-unzip@npm:^7.1.0": + version: 7.1.0 + resolution: "@xhmikosr/decompress-unzip@npm:7.1.0" + dependencies: + file-type: "npm:^20.5.0" + get-stream: "npm:^6.0.1" + yauzl: "npm:^3.1.2" + checksum: 10/7b085f50472cfeadfa690110a8f39d4177c90a30db08d6dd988abb0a0504eec650c0df7f0f3b538aa909d0653dea6652798f3851a546e742badb161ef75b94f4 + languageName: node + linkType: hard + +"@xhmikosr/decompress@npm:^10.2.0": + version: 10.2.0 + resolution: "@xhmikosr/decompress@npm:10.2.0" + dependencies: + "@xhmikosr/decompress-tar": "npm:^8.1.0" + "@xhmikosr/decompress-tarbz2": "npm:^8.1.0" + "@xhmikosr/decompress-targz": "npm:^8.1.0" + "@xhmikosr/decompress-unzip": "npm:^7.1.0" + graceful-fs: "npm:^4.2.11" + strip-dirs: "npm:^3.0.0" + checksum: 10/083f59bda6440b698322802e61c2b68e47d9a7f3c90d432d91a260b1630bda3c19088fb0cc8a42c67754a9de86e546f68c8d314408ff72180f0bba2b6ef65f81 + languageName: node + linkType: hard + +"@xhmikosr/downloader@npm:^15.2.0": + version: 15.2.0 + resolution: "@xhmikosr/downloader@npm:15.2.0" + dependencies: + "@xhmikosr/archive-type": "npm:^7.1.0" + "@xhmikosr/decompress": "npm:^10.2.0" + content-disposition: "npm:^0.5.4" + defaults: "npm:^2.0.2" + ext-name: "npm:^5.0.0" + file-type: "npm:^20.5.0" + filenamify: "npm:^6.0.0" + get-stream: "npm:^6.0.1" + got: "npm:^13.0.0" + checksum: 10/16b1a80aeca8d4a82643c477df2b5616d464da94c0279eeb53f03d4ccacc0c96e107cf8c109b8f9d26d30c4f01af5713e396b986afc8218573c7bdf318eaf399 + languageName: node + linkType: hard + +"@xhmikosr/os-filter-obj@npm:^3.0.0": + version: 3.0.0 + resolution: "@xhmikosr/os-filter-obj@npm:3.0.0" + dependencies: + arch: "npm:^3.0.0" + checksum: 10/8ec5e94e0a9f612b22997fb6cd2e82b121e03106ed0cf3404163b54c11812278e1f350d67af42d7c7093451c99d9755a6d6a284d8dae27b2b262790237e74193 + languageName: node + linkType: hard + +"@xmldom/xmldom@npm:^0.9.8": + version: 0.9.8 + resolution: "@xmldom/xmldom@npm:0.9.8" + checksum: 10/5f88d27a50bb624b0b46b8359853e1a3501217e743b132c5cbe76115646bb4b7a2128c1f0c32f045c7b452689f5206e9bba7d7292c6c13a81eed5e43c2e5ec7c + languageName: node + linkType: hard + +"@xtuc/ieee754@npm:^1.2.0": + version: 1.2.0 + resolution: "@xtuc/ieee754@npm:1.2.0" + checksum: 10/ab033b032927d77e2f9fa67accdf31b1ca7440974c21c9cfabc8349e10ca2817646171c4f23be98d0e31896d6c2c3462a074fe37752e523abc3e45c79254259c + languageName: node + linkType: hard + +"@xtuc/long@npm:4.2.2": + version: 4.2.2 + resolution: "@xtuc/long@npm:4.2.2" + checksum: 10/7217bae9fe240e0d804969e7b2af11cb04ec608837c78b56ca88831991b287e232a0b7fce8d548beaff42aaf0197ffa471d81be6ac4c4e53b0148025a2c076ec + languageName: node + linkType: hard + +"@zone-eu/mailsplit@npm:5.4.8": + version: 5.4.8 + resolution: "@zone-eu/mailsplit@npm:5.4.8" + dependencies: + libbase64: "npm:1.3.0" + libmime: "npm:5.3.7" + libqp: "npm:2.1.1" + checksum: 10/31f78be7aef62df7ca62edf9de2ef426ac979d5a516ae8def9c76eea911bda92d8cd8d36a0e8c203cc975d9e5d25faf4b8056f1883a1d615d550684bb6fd38ae + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 10/ca0a54e35bea4ece0ecb68a47b312e1a9a6f772408d5bcb9051230aaa94b0460671c5b5c9cb3240eb5b7bc94c52476550eb221f65a0bbd0145bdc9f3113a6707 + languageName: node + linkType: hard + +"abbrev@npm:^4.0.0": + version: 4.0.0 + resolution: "abbrev@npm:4.0.0" + checksum: 10/e2f0c6a6708ad738b3e8f50233f4800de31ad41a6cdc50e0cbe51b76fed69fd0213516d92c15ce1a9985fca71a14606a9be22bf00f8475a58987b9bfb671c582 + languageName: node + linkType: hard + +"accepts@npm:^2.0.0": + version: 2.0.0 + resolution: "accepts@npm:2.0.0" + dependencies: + mime-types: "npm:^3.0.0" + negotiator: "npm:^1.0.0" + checksum: 10/ea1343992b40b2bfb3a3113fa9c3c2f918ba0f9197ae565c48d3f84d44b174f6b1d5cd9989decd7655963eb03a272abc36968cc439c2907f999bd5ef8653d5a7 + languageName: node + linkType: hard + +"accepts@npm:~1.3.4": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: "npm:~2.1.34" + negotiator: "npm:0.6.3" + checksum: 10/67eaaa90e2917c58418e7a9b89392002d2b1ccd69bcca4799135d0c632f3b082f23f4ae4ddeedbced5aa59bcc7bdf4699c69ebed4593696c922462b7bc5744d6 + languageName: node + linkType: hard + +"acorn-import-attributes@npm:^1.9.5": + version: 1.9.5 + resolution: "acorn-import-attributes@npm:1.9.5" + peerDependencies: + acorn: ^8 + checksum: 10/8bfbfbb6e2467b9b47abb4d095df717ab64fce2525da65eabee073e85e7975fb3a176b6c8bba17c99a7d8ede283a10a590272304eb54a93c4aa1af9790d47a8b + languageName: node + linkType: hard + +"acorn-import-phases@npm:^1.0.3": + version: 1.0.4 + resolution: "acorn-import-phases@npm:1.0.4" + peerDependencies: + acorn: ^8.14.0 + checksum: 10/471050ac7d9b61909c837b426de9eeef2958997f6277ad7dea88d5894fd9b3245d8ed4a225c2ca44f814dbb20688009db7a80e525e8196fc9e98c5285b66161d + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.2": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10/d4371eaef7995530b5b5ca4183ff6f062ca17901a6d3f673c9ac011b01ede37e7a1f7f61f8f5cfe709e88054757bb8f3277dc4061087cdf4f2a1f90ccbcdb977 + languageName: node + linkType: hard + +"acorn-walk@npm:^8.1.1": + version: 8.3.4 + resolution: "acorn-walk@npm:8.3.4" + dependencies: + acorn: "npm:^8.11.0" + checksum: 10/871386764e1451c637bb8ab9f76f4995d408057e9909be6fb5ad68537ae3375d85e6a6f170b98989f44ab3ff6c74ad120bc2779a3d577606e7a0cd2b4efcaf77 + languageName: node + linkType: hard + +"acorn@npm:^7.1.1": + version: 7.4.1 + resolution: "acorn@npm:7.4.1" + bin: + acorn: bin/acorn + checksum: 10/8be2a40714756d713dfb62544128adce3b7102c6eb94bc312af196c2cc4af76e5b93079bd66b05e9ca31b35a9b0ce12171d16bc55f366cafdb794fdab9d753ec + languageName: node + linkType: hard + +"acorn@npm:^8.11.0, acorn@npm:^8.14.0, acorn@npm:^8.15.0, acorn@npm:^8.4.1": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 10/77f2de5051a631cf1729c090e5759148459cdb76b5f5c70f890503d629cf5052357b0ce783c0f976dd8a93c5150f59f6d18df1def3f502396a20f81282482fa4 + languageName: node + linkType: hard + +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10/21fb903e0917e5cb16591b4d0ef6a028a54b83ac30cd1fca58dece3d4e0990512a8723f9f83130d88a41e2af8b1f7be1386fda3ea2d181bb1a62155e75e95e23 + languageName: node + linkType: hard + +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 10/79bef167247789f955aaba113bae74bf64aa1e1acca4b1d6bb444bdf91d82c3e07e9451ef6a6e2e35e8f71a6f97ce33e3d855a5328eb9fad1bc3cc4cfd031ed8 + languageName: node + linkType: hard + +"ajv-formats@npm:3.0.1": + version: 3.0.1 + resolution: "ajv-formats@npm:3.0.1" + dependencies: + ajv: "npm:^8.0.0" + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 10/5679b9f9ced9d0213a202a37f3aa91efcffe59a6de1a6e3da5c873344d3c161820a1f11cc29899661fee36271fd2895dd3851b6461c902a752ad661d1c1e8722 + languageName: node + linkType: hard + +"ajv-formats@npm:^2.1.1": + version: 2.1.1 + resolution: "ajv-formats@npm:2.1.1" + dependencies: + ajv: "npm:^8.0.0" + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 10/70c263ded219bf277ffd9127f793b625f10a46113b2e901e150da41931fcfd7f5592da6d66862f4449bb157ffe65867c3294a7df1d661cc232c4163d5a1718ed + languageName: node + linkType: hard + +"ajv-keywords@npm:^3.5.2": + version: 3.5.2 + resolution: "ajv-keywords@npm:3.5.2" + peerDependencies: + ajv: ^6.9.1 + checksum: 10/d57c9d5bf8849bddcbd801b79bc3d2ddc736c2adb6b93a6a365429589dd7993ddbd5d37c6025ed6a7f89c27506b80131d5345c5b1fa6a97e40cd10a96bcd228c + languageName: node + linkType: hard + +"ajv-keywords@npm:^5.1.0": + version: 5.1.0 + resolution: "ajv-keywords@npm:5.1.0" + dependencies: + fast-deep-equal: "npm:^3.1.3" + peerDependencies: + ajv: ^8.8.2 + checksum: 10/5021f96ab7ddd03a4005326bd06f45f448ebfbb0fe7018b1b70b6c28142fa68372bda2057359814b83fd0b2d4c8726c297f0a7557b15377be7b56ce5344533d8 + languageName: node + linkType: hard + +"ajv@npm:8.17.1, ajv@npm:^8.0.0, ajv@npm:^8.9.0": + version: 8.17.1 + resolution: "ajv@npm:8.17.1" + dependencies: + fast-deep-equal: "npm:^3.1.3" + fast-uri: "npm:^3.0.1" + json-schema-traverse: "npm:^1.0.0" + require-from-string: "npm:^2.0.2" + checksum: 10/ee3c62162c953e91986c838f004132b6a253d700f1e51253b99791e2dbfdb39161bc950ebdc2f156f8568035bb5ed8be7bd78289cd9ecbf3381fe8f5b82e3f33 + languageName: node + linkType: hard + +"ajv@npm:^6.12.4, ajv@npm:^6.12.5": + version: 6.12.6 + resolution: "ajv@npm:6.12.6" + dependencies: + fast-deep-equal: "npm:^3.1.1" + fast-json-stable-stringify: "npm:^2.0.0" + json-schema-traverse: "npm:^0.4.1" + uri-js: "npm:^4.2.2" + checksum: 10/48d6ad21138d12eb4d16d878d630079a2bda25a04e745c07846a4ad768319533031e28872a9b3c5790fa1ec41aabdf2abed30a56e5a03ebc2cf92184b8ee306c + languageName: node + linkType: hard + +"alce@npm:1.2.0": + version: 1.2.0 + resolution: "alce@npm:1.2.0" + dependencies: + esprima: "npm:^1.2.0" + estraverse: "npm:^1.5.0" + checksum: 10/71cae3f84c712188ea55a7411133bda2e7702a55b2dccbc7a3c3da2c63d9abda5571b6bda574a393ef5432929d28e392b25c03d41e102905aeb215694573ad2d + languageName: node + linkType: hard + +"amwork-backend@workspace:.": + version: 0.0.0-use.local + resolution: "amwork-backend@workspace:." + dependencies: + "@amwork/voximplant-apiclient-nodejs": "npm:^2.3.0-f" + "@aws-sdk/client-s3": "npm:^3.817.0" + "@camunda8/sdk": "npm:^8.7.9" + "@date-fns/tz": "npm:^1.2.0" + "@date-fns/utc": "npm:^2.1.0" + "@eslint/js": "npm:^9.27.0" + "@esm2cjs/cacheable-lookup": "npm:^7.0.0" + "@faker-js/faker": "npm:^9.8.0" + "@nestjs-modules/mailer": "npm:2.0.2" + "@nestjs/axios": "npm:^4.0.0" + "@nestjs/cli": "npm:^11.0.7" + "@nestjs/common": "npm:^11.1.2" + "@nestjs/config": "npm:^4.0.2" + "@nestjs/core": "npm:^11.1.2" + "@nestjs/event-emitter": "npm:^3.0.1" + "@nestjs/jwt": "npm:^11.0.0" + "@nestjs/platform-express": "npm:^11.1.2" + "@nestjs/platform-socket.io": "npm:^11.1.2" + "@nestjs/schedule": "npm:^6.0.0" + "@nestjs/schematics": "npm:^11.0.5" + "@nestjs/swagger": "npm:^11.2.0" + "@nestjs/terminus": "npm:^11.0.0" + "@nestjs/typeorm": "npm:^11.0.0" + "@nestjs/websockets": "npm:^11.1.2" + "@newrelic/native-metrics": "npm:^11.1.0" + "@swc/cli": "npm:^0.7.7" + "@swc/core": "npm:^1.11.29" + "@total-typescript/ts-reset": "npm:^0.6.1" + "@types/bcrypt": "npm:^5.0.2" + "@types/cookie-parser": "npm:^1" + "@types/express": "npm:^5.0.2" + "@types/heapdump": "npm:^0" + "@types/html-to-text": "npm:^9.0.4" + "@types/imap-simple": "npm:^4.2.10" + "@types/mailparser": "npm:^3.4.6" + "@types/mime-types": "npm:^2" + "@types/multer": "npm:^1.4.12" + "@types/node": "npm:^22.15.24" + "@types/nodemailer": "npm:^6.4.17" + "@types/quoted-printable": "npm:^1" + "@types/uuid": "npm:^10.0.0" + "@voximplant/apiclient-nodejs": "npm:^4.2.0" + angular-expressions: "npm:^1.4.3" + axios: "npm:^1.9.0" + bcrypt: "npm:^6.0.0" + class-transformer: "npm:^0.5.1" + class-validator: "npm:^0.14.2" + cookie-parser: "npm:^1.4.7" + date-fns: "npm:^4.1.0" + date-fns-tz: "npm:^3.2.0" + decimal.js: "npm:^10.5.0" + docxtemplater: "npm:^3.63.2" + dotenv: "npm:^16.6.1" + eslint: "npm:^9.27.0" + eslint-config-prettier: "npm:^10.1.5" + eslint-plugin-prettier: "npm:^5.4.0" + exceljs: "npm:^4.4.0" + express: "npm:^5.1.0" + express-rate-limit: "npm:^8.2.1" + form-data: "npm:^4.0.2" + generate-password: "npm:^1.7.1" + googleapis: "npm:^149.0.0" + handlebars: "npm:^4.7.8" + heapdump: "npm:^0.3.15" + helmet: "npm:^8.1.0" + html-to-text: "npm:^9.0.5" + iconv-lite: "npm:^0.6.3" + imap-simple: "npm:^5.1.0" + imapflow: "npm:^1.0.187" + ioredis: "npm:^5.6.1" + jsonwebtoken: "npm:^9.0.2" + knip: "npm:^5.59.1" + libphonenumber-js: "npm:^1.12.8" + lvovich: "npm:^2.0.2" + mailparser: "npm:^3.7.3" + mathjs: "npm:^14.5.1" + mime-types: "npm:^3.0.1" + multer: "npm:^2.0.0" + nest-winston: "npm:^1.10.2" + newrelic: "npm:^12.20.0" + nodemailer: "npm:^7.0.3" + number-to-words-ru: "npm:^2.4.1" + path-to-regexp: "npm:^8.2.0" + pg: "npm:^8.16.0" + pizzip: "npm:^3.2.0" + prettier: "npm:^3.5.3" + qs: "npm:^6.14.0" + quoted-printable: "npm:^1.0.1" + reflect-metadata: "npm:^0.2.2" + rimraf: "npm:^6.0.1" + rxjs: "npm:^7.8.2" + sharp: "npm:^0.34.2" + slugify: "npm:^1.6.6" + socket.io: "npm:^4.8.1" + stripe: "npm:^17.7.0" + ts-node: "npm:^10.9.2" + tsconfig-paths: "npm:4.2.0" + twilio: "npm:^5.7.0" + typeorm: "npm:^0.3.24" + typeorm-naming-strategies: "npm:^4.1.0" + typescript: "npm:^5.8.3" + typescript-eslint: "npm:^8.33.0" + uuid: "npm:^11.1.0" + winston: "npm:^3.17.0" + written-number: "npm:^0.11.1" + languageName: unknown + linkType: soft + +"angular-expressions@npm:^1.4.3": + version: 1.5.1 + resolution: "angular-expressions@npm:1.5.1" + checksum: 10/68b579848eaaf745de510eff918d983d403d70c54ecdf2714a3390935dac8d180d45bb8490d69378308a03eeb73ba70f4419193cfdb9db211c8186b22001052b + languageName: node + linkType: hard + +"ansi-align@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: "npm:^4.1.0" + checksum: 10/4c7e8b6a10eaf18874ecee964b5db62ac86d0b9266ad4987b3a1efcb5d11a9e12c881ee40d14951833135a8966f10a3efe43f9c78286a6e632f53d85ad28b9c0 + languageName: node + linkType: hard + +"ansi-colors@npm:4.1.3, ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: 10/43d6e2fc7b1c6e4dc373de708ee76311ec2e0433e7e8bd3194e7ff123ea6a747428fc61afdcf5969da5be3a5f0fd054602bec56fc0ebe249ce2fcde6e649e3c2 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10/2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.2.2 + resolution: "ansi-regex@npm:6.2.2" + checksum: 10/9b17ce2c6daecc75bcd5966b9ad672c23b184dc3ed9bf3c98a0702f0d2f736c15c10d461913568f2cf527a5e64291c7473358885dd493305c84a1cfed66ba94f + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10/d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10/b4494dfbfc7e4591b4711a396bd27e540f8153914123dccb4cdbbcb514015ada63a3809f362b9d8d4f6b17a706f1d7bea3c6f974b15fa5ae76b5b502070889ff + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10/c49dad7639f3e48859bd51824c93b9eb0db628afc243c51c3dd2410c4a15ede1a83881c6c7341aa2b159c4f90c11befb38f2ba848c07c66c9f9de4bcd7cb9f30 + languageName: node + linkType: hard + +"ansis@npm:4.2.0, ansis@npm:^4.2.0": + version: 4.2.0 + resolution: "ansis@npm:4.2.0" + checksum: 10/493e15fad267bd6e3e275d6886c3b3c96a075784d9eae3e16d16383d488e94cc3deb1b357e1246f572599767360548ef9e5b7eab9b72e4ee3f7bad9ce6bc8797 + languageName: node + linkType: hard + +"anymatch@npm:~3.1.2": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 10/3e044fd6d1d26545f235a9fe4d7a534e2029d8e59fa7fd9f2a6eb21230f6b5380ea1eaf55136e60cbf8e613544b3b766e7a6fa2102e2a3a117505466e3025dc2 + languageName: node + linkType: hard + +"app-root-path@npm:^3.1.0": + version: 3.1.0 + resolution: "app-root-path@npm:3.1.0" + checksum: 10/b4cdab5f7e51ec43fa04c97eca2adedf8e18d6c3dd21cd775b70457c5e71f0441c692a49dcceb426f192640b7393dcd41d85c36ef98ecb7c785a53159c912def + languageName: node + linkType: hard + +"append-field@npm:^1.0.0": + version: 1.0.0 + resolution: "append-field@npm:1.0.0" + checksum: 10/afb50f5ff668af1cb66bc5cfebb55ed9a1d99e24901782ee83d00aed1a499835f9375a149cf27b17f79595ecfcc3d1de0cd5b020b210a5359c43eaf607c217de + languageName: node + linkType: hard + +"arch@npm:^3.0.0": + version: 3.0.0 + resolution: "arch@npm:3.0.0" + checksum: 10/9af0c58900980c300737945881859df6dd2a4e4d07f697c77704a7ba85a701aa60aa7c3a3ce1eb57ef76fda726ebccf1e2a9ddd763c89fe82c961d55b4b9c374 + languageName: node + linkType: hard + +"archiver-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "archiver-utils@npm:2.1.0" + dependencies: + glob: "npm:^7.1.4" + graceful-fs: "npm:^4.2.0" + lazystream: "npm:^1.0.0" + lodash.defaults: "npm:^4.2.0" + lodash.difference: "npm:^4.5.0" + lodash.flatten: "npm:^4.4.0" + lodash.isplainobject: "npm:^4.0.6" + lodash.union: "npm:^4.6.0" + normalize-path: "npm:^3.0.0" + readable-stream: "npm:^2.0.0" + checksum: 10/4df493c0e6a3a544119b08b350308923500e2c6efee6a283cba4c3202293ce3acb70897e54e24f735e3a38ff43e5a65f66e2e5225fdfc955bf2335491377be2e + languageName: node + linkType: hard + +"archiver-utils@npm:^3.0.4": + version: 3.0.4 + resolution: "archiver-utils@npm:3.0.4" + dependencies: + glob: "npm:^7.2.3" + graceful-fs: "npm:^4.2.0" + lazystream: "npm:^1.0.0" + lodash.defaults: "npm:^4.2.0" + lodash.difference: "npm:^4.5.0" + lodash.flatten: "npm:^4.4.0" + lodash.isplainobject: "npm:^4.0.6" + lodash.union: "npm:^4.6.0" + normalize-path: "npm:^3.0.0" + readable-stream: "npm:^3.6.0" + checksum: 10/a838c325a1e1d6798c07e6a3af08f480fdce57cba2964bff8761126715aa1b71e9a119442eac19b7ec6313f5298e54a180dc6612ae548825fbc9be6836e50487 + languageName: node + linkType: hard + +"archiver@npm:^5.0.0": + version: 5.3.2 + resolution: "archiver@npm:5.3.2" + dependencies: + archiver-utils: "npm:^2.1.0" + async: "npm:^3.2.4" + buffer-crc32: "npm:^0.2.1" + readable-stream: "npm:^3.6.0" + readdir-glob: "npm:^1.1.2" + tar-stream: "npm:^2.2.0" + zip-stream: "npm:^4.1.0" + checksum: 10/9384b3b20d330f95140c2b7a9b51140d14e9bc7b133be6cf573067ed8fc67a6e9618cfbfe60b1ba78b8034857001fd02c8900f2fba4864514670a2274d36dc9e + languageName: node + linkType: hard + +"arg@npm:^4.1.0": + version: 4.1.3 + resolution: "arg@npm:4.1.3" + checksum: 10/969b491082f20cad166649fa4d2073ea9e974a4e5ac36247ca23d2e5a8b3cb12d60e9ff70a8acfe26d76566c71fd351ee5e6a9a6595157eb36f92b1fd64e1599 + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10/18640244e641a417ec75a9bd38b0b2b6b95af5199aa241b131d4b2fb206f334d7ecc600bd194861610a5579084978bfcbb02baa399dbe442d56d0ae5e60dbaef + languageName: node + linkType: hard + +"array-timsort@npm:^1.0.3": + version: 1.0.3 + resolution: "array-timsort@npm:1.0.3" + checksum: 10/f417f073b3733baec3a80decdf5d45bf763f04676ef3610b0e71f9b1d88c6e4c38154c05b28b31529d308bfd0e043d08059fcd9df966245a1276af15b5584936 + languageName: node + linkType: hard + +"asap@npm:~2.0.3": + version: 2.0.6 + resolution: "asap@npm:2.0.6" + checksum: 10/b244c0458c571945e4b3be0b14eb001bea5596f9868cc50cc711dc03d58a7e953517d3f0dad81ccde3ff37d1f074701fa76a6f07d41aaa992d7204a37b915dda + languageName: node + linkType: hard + +"assert-never@npm:^1.2.1": + version: 1.4.0 + resolution: "assert-never@npm:1.4.0" + checksum: 10/e51e1fc4587405b929d07f1b033f7e92b9f59f9a9ced20f2bb2e5218b06a079fdf8c5b0e993366acc2ca25050a437a2df1f4e624959151e60f4893cad4e50d89 + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10/1a09379937d846f0ce7614e75071c12826945d4e417db634156bf0e4673c495989302f52186dfa9767a1d9181794554717badd193ca2bbab046ef1da741d8efd + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10/3d49e7acbeee9e84537f4cb0e0f91893df8eba976759875ae8ee9e3d3c82f6ecdebdb347c2fad9926b92596d93cdfc78ecc988bcdf407e40433e8e8e6fe5d78e + languageName: node + linkType: hard + +"async@npm:^3.2.3, async@npm:^3.2.4, async@npm:^3.2.6": + version: 3.2.6 + resolution: "async@npm:3.2.6" + checksum: 10/cb6e0561a3c01c4b56a799cc8bab6ea5fef45f069ab32500b6e19508db270ef2dffa55e5aed5865c5526e9907b1f8be61b27530823b411ffafb5e1538c86c368 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10/3ce727cbc78f69d6a4722517a58ee926c8c21083633b1d3fdf66fd688f6c127a53a592141bd4866f9b63240a86e9d8e974b13919450bd17fa33c2d22c4558ad8 + languageName: node + linkType: hard + +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: 10/3ab6d2cf46b31394b4607e935ec5c1c3c4f60f3e30f0913d35ea74b51b3585e84f590d09e58067f11762eec71c87d25314ce859030983dc0e4397eed21daa12e + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: "npm:^1.0.0" + checksum: 10/6c9da3a66caddd83c875010a1ca8ef11eac02ba15fb592dc9418b2b5e7b77b645fa7729380a92d9835c2f05f2ca1b6251f39b993e0feb3f1517c74fa1af02cab + languageName: node + linkType: hard + +"axios@npm:0.21.4": + version: 0.21.4 + resolution: "axios@npm:0.21.4" + dependencies: + follow-redirects: "npm:^1.14.0" + checksum: 10/da644592cb6f8f9f8c64fdabd7e1396d6769d7a4c1ea5f8ae8beb5c2eb90a823e3a574352b0b934ac62edc762c0f52647753dc54f7d07279127a7e5c4cd20272 + languageName: node + linkType: hard + +"axios@npm:^1.12.0, axios@npm:^1.9.0": + version: 1.13.2 + resolution: "axios@npm:1.13.2" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.4" + proxy-from-env: "npm:^1.1.0" + checksum: 10/ae4e06dcd18289f2fd18179256d550d27f9a53ecb2f9c59f2ccc4efd1d7151839ba8c3e0fb533dac793e4a59a576ca8689a19244dce5c396680837674a47a867 + languageName: node + linkType: hard + +"b4a@npm:^1.6.4": + version: 1.7.3 + resolution: "b4a@npm:1.7.3" + peerDependencies: + react-native-b4a: "*" + peerDependenciesMeta: + react-native-b4a: + optional: true + checksum: 10/048ddd0eeec6a75e6f8dee07d52354e759032f0ef678b556e05bf5a137d7a4102002cadb953b3fb37a635995a1013875d715d115dbafaf12bcad6528d2166054 + languageName: node + linkType: hard + +"babel-walk@npm:3.0.0-canary-5": + version: 3.0.0-canary-5 + resolution: "babel-walk@npm:3.0.0-canary-5" + dependencies: + "@babel/types": "npm:^7.9.6" + checksum: 10/f4cea17303b33266fa97be471df9917d386bb5cd2756ae4c4725b3f105b9d630789818be202de06afa546e94810add61d614aa5eeb16e2a3027636cbafac2c1a + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10/9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"bare-events@npm:^2.7.0": + version: 2.8.2 + resolution: "bare-events@npm:2.8.2" + peerDependencies: + bare-abort-controller: "*" + peerDependenciesMeta: + bare-abort-controller: + optional: true + checksum: 10/f31848ea2f5627c3a50aadfc17e518a602629f7a6671da1352975cc6c8a520441fcc9d93c0a21f8f95de65b1a5133fcd5f766d312f3d5a326dde4fe7d2fc575f + languageName: node + linkType: hard + +"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10/669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 + languageName: node + linkType: hard + +"base64id@npm:2.0.0, base64id@npm:~2.0.0": + version: 2.0.0 + resolution: "base64id@npm:2.0.0" + checksum: 10/e3312328429e512b0713469c5312f80b447e71592cae0a5bddf3f1adc9c89d1b2ed94156ad7bb9f529398f310df7ff6f3dbe9550735c6a759f247c088ea67364 + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.9.0": + version: 2.9.14 + resolution: "baseline-browser-mapping@npm:2.9.14" + bin: + baseline-browser-mapping: dist/cli.js + checksum: 10/a329881e5f673c0834843640e9c954c478f643fb983449c99850392e48cf52dfb1dc3de8d81c6a6a2802c86310833accc5e3deb6bef5fb6e329989e28ca5489b + languageName: node + linkType: hard + +"bcrypt@npm:^6.0.0": + version: 6.0.0 + resolution: "bcrypt@npm:6.0.0" + dependencies: + node-addon-api: "npm:^8.3.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.8.4" + checksum: 10/24dc552828435f2346fe0a27eb2b23e4fdcc4f139d069db0dbee6e3b37fcf8e88ffbd6473a138e1d594a4b9df91e9b71994d15cf9fc6f5c3ff68f3d851fd973a + languageName: node + linkType: hard + +"big-integer@npm:^1.6.17": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 10/4bc6ae152a96edc9f95020f5fc66b13d26a9ad9a021225a9f0213f7e3dc44269f423aa8c42e19d6ac4a63bb2b22140b95d10be8f9ca7a6d9aa1b22b330d1f514 + languageName: node + linkType: hard + +"bignumber.js@npm:^9.0.0": + version: 9.3.1 + resolution: "bignumber.js@npm:9.3.1" + checksum: 10/1be0372bf0d6d29d0a49b9e6a9cefbd54dad9918232ad21fcd4ec39030260773abf0c76af960c6b3b98d3115a3a71e61c6a111812d1395040a039cfa178e0245 + languageName: node + linkType: hard + +"bin-version-check@npm:^5.1.0": + version: 5.1.0 + resolution: "bin-version-check@npm:5.1.0" + dependencies: + bin-version: "npm:^6.0.0" + semver: "npm:^7.5.3" + semver-truncate: "npm:^3.0.0" + checksum: 10/d99679cfe0964703045fe0145a98f117888942b621dfe2c2377305ee9a9d735374d8e3ecb3b476507b284af2567699f24f7ecb2feb1f27ad6086ad60b3198893 + languageName: node + linkType: hard + +"bin-version@npm:^6.0.0": + version: 6.0.0 + resolution: "bin-version@npm:6.0.0" + dependencies: + execa: "npm:^5.0.0" + find-versions: "npm:^5.0.0" + checksum: 10/78c29422ea9597eb4c8d4f0eff96df60d09aa82b53a87925bc403efbe5c55251b1a07baac538381d9096377f92d27e3c03963efa86db5bc0d6431b9563946229 + languageName: node + linkType: hard + +"binary-extensions@npm:^2.0.0": + version: 2.3.0 + resolution: "binary-extensions@npm:2.3.0" + checksum: 10/bcad01494e8a9283abf18c1b967af65ee79b0c6a9e6fcfafebfe91dbe6e0fc7272bafb73389e198b310516ae04f7ad17d79aacf6cb4c0d5d5202a7e2e52c7d98 + languageName: node + linkType: hard + +"binary@npm:~0.3.0": + version: 0.3.0 + resolution: "binary@npm:0.3.0" + dependencies: + buffers: "npm:~0.1.1" + chainsaw: "npm:~0.1.0" + checksum: 10/127591ebb7bfca242ec11be9ef874bcde17c520f249d764810045971b6617b659e8af4452f8a1586db7fd47e1b481a75d22c8f207fc1466c0f099b9435e51679 + languageName: node + linkType: hard + +"bl@npm:^4.0.3, bl@npm:^4.1.0": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: "npm:^5.5.0" + inherits: "npm:^2.0.4" + readable-stream: "npm:^3.4.0" + checksum: 10/b7904e66ed0bdfc813c06ea6c3e35eafecb104369dbf5356d0f416af90c1546de3b74e5b63506f0629acf5e16a6f87c3798f16233dcff086e9129383aa02ab55 + languageName: node + linkType: hard + +"bluebird@npm:~3.4.1": + version: 3.4.7 + resolution: "bluebird@npm:3.4.7" + checksum: 10/340e4d11d4b6a26d90371180effb4e500197c2943e5426472d6b6bffca0032a534226ad10255fc0e39c025bea197341c6b2a4258f8c0f18217c7b3a254c76c14 + languageName: node + linkType: hard + +"body-parser@npm:^2.2.1": + version: 2.2.2 + resolution: "body-parser@npm:2.2.2" + dependencies: + bytes: "npm:^3.1.2" + content-type: "npm:^1.0.5" + debug: "npm:^4.4.3" + http-errors: "npm:^2.0.0" + iconv-lite: "npm:^0.7.0" + on-finished: "npm:^2.4.1" + qs: "npm:^6.14.1" + raw-body: "npm:^3.0.1" + type-is: "npm:^2.0.1" + checksum: 10/69671f67d4d5ae5974593901a92d639757231da1725ed6de4d35e86cde9ce7650afdf1cd28df9b6f7892ea7f9eb03ccb30c70fe27d679275ae4cb4aae5ce1b21 + languageName: node + linkType: hard + +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 10/3e25c80ef626c3a3487c73dbfc70ac322ec830666c9ad915d11b701142fab25ec1e63eff2c450c74347acfd2de854ccde865cd79ef4db1683f7c7b046ea43bb0 + languageName: node + linkType: hard + +"bowser@npm:^2.11.0": + version: 2.13.1 + resolution: "bowser@npm:2.13.1" + checksum: 10/b93c4f92b0ee2225c7bcfd8cd8a657e4abe4dadfae51588e7567b39846d7e47d98dfb4b178a23989eb753a36dc6451a18c5adce7a38bc41f5df7b2de19e4a759 + languageName: node + linkType: hard + +"boxen@npm:5.1.2": + version: 5.1.2 + resolution: "boxen@npm:5.1.2" + dependencies: + ansi-align: "npm:^3.0.0" + camelcase: "npm:^6.2.0" + chalk: "npm:^4.1.0" + cli-boxes: "npm:^2.2.1" + string-width: "npm:^4.2.2" + type-fest: "npm:^0.20.2" + widest-line: "npm:^3.1.0" + wrap-ansi: "npm:^7.0.0" + checksum: 10/bc3d3d88d77dc8cabb0811844acdbd4805e8ca8011222345330817737042bf6f86d93eb74a3f7e0cab634e64ef69db03cf52b480761ed90a965de0c8ff1bea8c + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.12 + resolution: "brace-expansion@npm:1.1.12" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10/12cb6d6310629e3048cadb003e1aca4d8c9bb5c67c3c321bafdd7e7a50155de081f78ea3e0ed92ecc75a9015e784f301efc8132383132f4f7904ad1ac529c562 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10/01dff195e3646bc4b0d27b63d9bab84d2ebc06121ff5013ad6e5356daa5a9d6b60fa26cf73c74797f2dc3fbec112af13578d51f75228c1112b26c790a87b0488 + languageName: node + linkType: hard + +"braces@npm:^3.0.3, braces@npm:~3.0.2": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10/fad11a0d4697a27162840b02b1fad249c1683cbc510cd5bf1a471f2f8085c046d41094308c577a50a03a579dd99d5a6b3724c4b5e8b14df2c4443844cfcda2c6 + languageName: node + linkType: hard + +"browserslist@npm:^4.26.3": + version: 4.28.1 + resolution: "browserslist@npm:4.28.1" + dependencies: + baseline-browser-mapping: "npm:^2.9.0" + caniuse-lite: "npm:^1.0.30001759" + electron-to-chromium: "npm:^1.5.263" + node-releases: "npm:^2.0.27" + update-browserslist-db: "npm:^1.2.0" + bin: + browserslist: cli.js + checksum: 10/64f2a97de4bce8473c0e5ae0af8d76d1ead07a5b05fc6bc87b848678bb9c3a91ae787b27aa98cdd33fc00779607e6c156000bed58fefb9cf8e4c5a183b994cdb + languageName: node + linkType: hard + +"buffer-crc32@npm:^0.2.1, buffer-crc32@npm:^0.2.13, buffer-crc32@npm:~0.2.3": + version: 0.2.13 + resolution: "buffer-crc32@npm:0.2.13" + checksum: 10/06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c + languageName: node + linkType: hard + +"buffer-equal-constant-time@npm:^1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 10/80bb945f5d782a56f374b292770901065bad21420e34936ecbe949e57724b4a13874f735850dd1cc61f078773c4fb5493a41391e7bda40d1fa388d6bd80daaab + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10/0448524a562b37d4d7ed9efd91685a5b77a50672c556ea254ac9a6d30e3403a517d8981f10e565db24e8339413b43c97ca2951f10e399c6125a0d8911f5679bb + languageName: node + linkType: hard + +"buffer-indexof-polyfill@npm:~1.0.0": + version: 1.0.2 + resolution: "buffer-indexof-polyfill@npm:1.0.2" + checksum: 10/808c58a3f06cc6ee2231060959eaa31c490248465f2847e8cfebd3e62563521e67346391caad03ce7616fd765374eb53e941bdd22edb2336431171f46fddcd89 + languageName: node + linkType: hard + +"buffer@npm:^5.2.1, buffer@npm:^5.5.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.1.13" + checksum: 10/997434d3c6e3b39e0be479a80288875f71cd1c07d75a3855e6f08ef848a3c966023f79534e22e415ff3a5112708ce06127277ab20e527146d55c84566405c7c6 + languageName: node + linkType: hard + +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10/b6bc68237ebf29bdacae48ce60e5e28fc53ae886301f2ad9496618efac49427ed79096750033e7eab1897a4f26ae374ace49106a5758f38fb70c78c9fda2c3b1 + languageName: node + linkType: hard + +"buffers@npm:~0.1.1": + version: 0.1.1 + resolution: "buffers@npm:0.1.1" + checksum: 10/9f0b64bbb8ac4783b1740219ab3532b03ef978fa38e70a0ba8c0695a2f6bc7e2af0ce42f0756b0c1a127070493055adbaf490fb68d95bebd7ccc310c6a483860 + languageName: node + linkType: hard + +"busboy@npm:^1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: "npm:^1.1.0" + checksum: 10/bee10fa10ea58e7e3e7489ffe4bda6eacd540a17de9f9cd21cc37e297b2dd9fe52b2715a5841afaec82900750d810d01d7edb4b2d456427f449b92b417579763 + languageName: node + linkType: hard + +"bytes@npm:^3.1.2, bytes@npm:~3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: 10/a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388 + languageName: node + linkType: hard + +"cacache@npm:^20.0.1": + version: 20.0.3 + resolution: "cacache@npm:20.0.3" + dependencies: + "@npmcli/fs": "npm:^5.0.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^13.0.0" + lru-cache: "npm:^11.1.0" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^7.0.2" + ssri: "npm:^13.0.0" + unique-filename: "npm:^5.0.0" + checksum: 10/388a0169970df9d051da30437f93f81b7e91efb570ad0ff2b8fde33279fbe726c1bc8e8e2b9c05053ffb4f563854c73db395e8712e3b62347a1bc4f7fb8899ff + languageName: node + linkType: hard + +"cacheable-lookup@npm:^5.0.3": + version: 5.0.4 + resolution: "cacheable-lookup@npm:5.0.4" + checksum: 10/618a8b3eea314060e74cb3285a6154e8343c244a34235acf91cfe626ee0705c24e3cd11e4b1a7b3900bd749ee203ae65afe13adf610c8ab173e99d4a208faf75 + languageName: node + linkType: hard + +"cacheable-lookup@npm:^7.0.0": + version: 7.0.0 + resolution: "cacheable-lookup@npm:7.0.0" + checksum: 10/69ea78cd9f16ad38120372e71ba98b64acecd95bbcbcdad811f857dc192bad81ace021f8def012ce19178583db8d46afd1a00b3e8c88527e978e049edbc23252 + languageName: node + linkType: hard + +"cacheable-request@npm:^10.2.8": + version: 10.2.14 + resolution: "cacheable-request@npm:10.2.14" + dependencies: + "@types/http-cache-semantics": "npm:^4.0.2" + get-stream: "npm:^6.0.1" + http-cache-semantics: "npm:^4.1.1" + keyv: "npm:^4.5.3" + mimic-response: "npm:^4.0.0" + normalize-url: "npm:^8.0.0" + responselike: "npm:^3.0.0" + checksum: 10/102f454ac68eb66f99a709c5cf65e90ed89f1b9269752578d5a08590b3986c3ea47a5d9dff208fe7b65855a29da129a2f23321b88490106898e0ba70b807c912 + languageName: node + linkType: hard + +"cacheable-request@npm:^7.0.2": + version: 7.0.4 + resolution: "cacheable-request@npm:7.0.4" + dependencies: + clone-response: "npm:^1.0.2" + get-stream: "npm:^5.1.0" + http-cache-semantics: "npm:^4.0.0" + keyv: "npm:^4.0.0" + lowercase-keys: "npm:^2.0.0" + normalize-url: "npm:^6.0.1" + responselike: "npm:^2.0.0" + checksum: 10/0f4f2001260ecca78b9f64fc8245e6b5a5dcde24ea53006daab71f5e0e1338095aa1512ec099c4f9895a9e5acfac9da423cb7c079e131485891e9214aca46c41 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.0, call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10/00482c1f6aa7cfb30fb1dbeb13873edf81cfac7c29ed67a5957d60635a56b2a4a480f1016ddbdb3395cc37900d46037fb965043a51c5c789ffeab4fc535d18b5 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.8": + version: 1.0.8 + resolution: "call-bind@npm:1.0.8" + dependencies: + call-bind-apply-helpers: "npm:^1.0.0" + es-define-property: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.2" + checksum: 10/659b03c79bbfccf0cde3a79e7d52570724d7290209823e1ca5088f94b52192dc1836b82a324d0144612f816abb2f1734447438e38d9dafe0b3f82c2a1b9e3bce + languageName: node + linkType: hard + +"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3, call-bound@npm:^1.0.4": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + get-intrinsic: "npm:^1.3.0" + checksum: 10/ef2b96e126ec0e58a7ff694db43f4d0d44f80e641370c21549ed911fecbdbc2df3ebc9bddad918d6bbdefeafb60bb3337902006d5176d72bcd2da74820991af7 + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10/072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 + languageName: node + linkType: hard + +"camel-case@npm:^3.0.0": + version: 3.0.0 + resolution: "camel-case@npm:3.0.0" + dependencies: + no-case: "npm:^2.2.0" + upper-case: "npm:^1.1.1" + checksum: 10/4190ed6ab8acf4f3f6e1a78ad4d0f3f15ce717b6bfa1b5686d58e4bcd29960f6e312dd746b5fa259c6d452f1413caef25aee2e10c9b9a580ac83e516533a961a + languageName: node + linkType: hard + +"camelcase@npm:^6.2.0": + version: 6.3.0 + resolution: "camelcase@npm:6.3.0" + checksum: 10/8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001759": + version: 1.0.30001764 + resolution: "caniuse-lite@npm:1.0.30001764" + checksum: 10/24c6f402902181faa997a6da1cb63410f9376e9e8a33d733121862e7665d200a54d70e551c5626748f78078401c0744496a58d0451fceb8f7fa12498ae12ff20 + languageName: node + linkType: hard + +"chainsaw@npm:~0.1.0": + version: 0.1.0 + resolution: "chainsaw@npm:0.1.0" + dependencies: + traverse: "npm:>=0.3.0 <0.4" + checksum: 10/d85627cd3440eb908b9cd72a1ddce4a36bb1ebc9d431a4a2f44b4435cbefdd83625c05114d870381ba765849c34ad05f236c3f590b1581ea03c22897fe6883d0 + languageName: node + linkType: hard + +"chalk@npm:^2.4.2": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: "npm:^3.2.1" + escape-string-regexp: "npm:^1.0.5" + supports-color: "npm:^5.3.0" + checksum: 10/3d1d103433166f6bfe82ac75724951b33769675252d8417317363ef9d54699b7c3b2d46671b772b893a8e50c3ece70c4b933c73c01e81bc60ea4df9b55afa303 + languageName: node + linkType: hard + +"chalk@npm:^3.0.0": + version: 3.0.0 + resolution: "chalk@npm:3.0.0" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10/37f90b31fd655fb49c2bd8e2a68aebefddd64522655d001ef417e6f955def0ed9110a867ffc878a533f2dafea5f2032433a37c8a7614969baa7f8a1cd424ddfc + languageName: node + linkType: hard + +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10/cb3f3e594913d63b1814d7ca7c9bafbf895f75fbf93b92991980610dfd7b48500af4e3a5d4e3a8f337990a96b168d7eb84ee55efdce965e2ee8efc20f8c8f139 + languageName: node + linkType: hard + +"character-parser@npm:^2.2.0": + version: 2.2.0 + resolution: "character-parser@npm:2.2.0" + dependencies: + is-regex: "npm:^1.0.3" + checksum: 10/5980ddc776a133ba7a264aaec77ab9dd883aa9ff45bb335bfa93ae26b8e7360e60b2c97119f85cc4d2203829c9977df7c6ba6612354b1dfc9ef41d84fde03002 + languageName: node + linkType: hard + +"chardet@npm:^2.1.1": + version: 2.1.1 + resolution: "chardet@npm:2.1.1" + checksum: 10/d56913b65e45c5c86f331988e2ef6264c131bfeadaae098ee719bf6610546c77740e37221ffec802dde56b5e4466613a4c754786f4da6b5f6c5477243454d324 + languageName: node + linkType: hard + +"check-disk-space@npm:3.4.0, check-disk-space@npm:^3.4.0": + version: 3.4.0 + resolution: "check-disk-space@npm:3.4.0" + checksum: 10/73130c32e26aaa1eda359706d9cbcde21785285dd829ec2b850c59e88ce99d8ebf830d6849315b2d4bc7eed9082aaea7c5f065f1ace591f49d3851f0c21157f9 + languageName: node + linkType: hard + +"cheerio-select@npm:^2.1.0": + version: 2.1.0 + resolution: "cheerio-select@npm:2.1.0" + dependencies: + boolbase: "npm:^1.0.0" + css-select: "npm:^5.1.0" + css-what: "npm:^6.1.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + checksum: 10/b5d89208c23468c3a32d1e04f88b9e8c6e332e3649650c5cd29255e2cebc215071ae18563f58c3dc3f6ef4c234488fc486035490fceb78755572288245e2931a + languageName: node + linkType: hard + +"cheerio@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "cheerio@npm:1.0.0-rc.12" + dependencies: + cheerio-select: "npm:^2.1.0" + dom-serializer: "npm:^2.0.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + htmlparser2: "npm:^8.0.1" + parse5: "npm:^7.0.0" + parse5-htmlparser2-tree-adapter: "npm:^7.0.0" + checksum: 10/812fed61aa4b669bbbdd057d0d7f73ba4649cabfd4fc3a8f1d5c7499e4613b430636102716369cbd6bbed8f1bdcb06387ae8342289fb908b2743184775f94f18 + languageName: node + linkType: hard + +"chokidar@npm:4.0.3, chokidar@npm:^4.0.1": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10/bf2a575ea5596000e88f5db95461a9d59ad2047e939d5a4aac59dd472d126be8f1c1ff3c7654b477cf532d18f42a97279ef80ee847972fd2a25410bf00b80b59 + languageName: node + linkType: hard + +"chokidar@npm:^3.0.0": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10/c327fb07704443f8d15f7b4a7ce93b2f0bc0e6cea07ec28a7570aa22cd51fcf0379df589403976ea956c369f25aa82d84561947e227cd925902e1751371658df + languageName: node + linkType: hard + +"chownr@npm:^1.1.1": + version: 1.1.4 + resolution: "chownr@npm:1.1.4" + checksum: 10/115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10/b63cb1f73d171d140a2ed8154ee6566c8ab775d3196b0e03a2a94b5f6a0ce7777ee5685ca56849403c8d17bd457a6540672f9a60696a6137c7a409097495b82c + languageName: node + linkType: hard + +"chrome-trace-event@npm:^1.0.2": + version: 1.0.4 + resolution: "chrome-trace-event@npm:1.0.4" + checksum: 10/1762bed739774903bf5915fe3045c3120fc3c7f7d929d88e566447ea38944937a6370ccb687278318c43c24f837ad22dac780bed67c066336815557b8cf558c6 + languageName: node + linkType: hard + +"ci-info@npm:^3.8.0": + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 10/75bc67902b4d1c7b435497adeb91598f6d52a3389398e44294f6601b20cfef32cf2176f7be0eb961d9e085bb333a8a5cae121cb22f81cf238ae7f58eb80e9397 + languageName: node + linkType: hard + +"cjs-module-lexer@npm:^1.2.2": + version: 1.4.3 + resolution: "cjs-module-lexer@npm:1.4.3" + checksum: 10/d2b92f919a2dedbfd61d016964fce8da0035f827182ed6839c97cac56e8a8077cfa6a59388adfe2bc588a19cef9bbe830d683a76a6e93c51f65852062cfe2591 + languageName: node + linkType: hard + +"class-transformer@npm:^0.5.1": + version: 0.5.1 + resolution: "class-transformer@npm:0.5.1" + checksum: 10/750327e3e9a5cf233c5234252f4caf6b06c437bf68a24acbdcfb06c8e0bfff7aa97c30428184813e38e08111b42871f20c5cf669ea4490f8ae837c09f08b31e7 + languageName: node + linkType: hard + +"class-validator@npm:^0.14.2": + version: 0.14.3 + resolution: "class-validator@npm:0.14.3" + dependencies: + "@types/validator": "npm:^13.15.3" + libphonenumber-js: "npm:^1.11.1" + validator: "npm:^13.15.20" + checksum: 10/492a3d3bf6db896a10bb473298b9e51cf3384a2810e32c549f1f82050786835aacc8c887b6d2f4a90aa82c4359f88252ef707f75295971db58532a73c40fffdf + languageName: node + linkType: hard + +"clean-css@npm:^4.2.1": + version: 4.2.4 + resolution: "clean-css@npm:4.2.4" + dependencies: + source-map: "npm:~0.6.0" + checksum: 10/4f64dbebfa29feb79be25d6f91239239179adc805c6d7442e2c728970ca23a75b5f238118477b4b78553b89e50f14a64fe35145ecc86b6badf971883c4ad2ffe + languageName: node + linkType: hard + +"cli-boxes@npm:^2.2.1": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: 10/be79f8ec23a558b49e01311b39a1ea01243ecee30539c880cf14bf518a12e223ef40c57ead0cb44f509bffdffc5c129c746cd50d863ab879385370112af4f585 + languageName: node + linkType: hard + +"cli-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "cli-cursor@npm:3.1.0" + dependencies: + restore-cursor: "npm:^3.1.0" + checksum: 10/2692784c6cd2fd85cfdbd11f53aea73a463a6d64a77c3e098b2b4697a20443f430c220629e1ca3b195ea5ac4a97a74c2ee411f3807abf6df2b66211fec0c0a29 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.5.0": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10/a0a863f442df35ed7294424f5491fa1756bd8d2e4ff0c8736531d886cec0ece4d85e8663b77a5afaf1d296e3cbbebff92e2e99f52bbea89b667cbe789b994794 + languageName: node + linkType: hard + +"cli-table3@npm:0.6.5": + version: 0.6.5 + resolution: "cli-table3@npm:0.6.5" + dependencies: + "@colors/colors": "npm:1.5.0" + string-width: "npm:^4.2.0" + dependenciesMeta: + "@colors/colors": + optional: true + checksum: 10/8dca71256f6f1367bab84c33add3f957367c7c43750a9828a4212ebd31b8df76bd7419d386e3391ac7419698a8540c25f1a474584028f35b170841cde2e055c5 + languageName: node + linkType: hard + +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10/b58876fbf0310a8a35c79b72ecfcf579b354e18ad04e6b20588724ea2b522799a758507a37dfe132fafaf93a9922cafd9514d9e1598e6b2cd46694853aed099f + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10/eaa5561aeb3135c2cddf7a3b3f562fc4238ff3b3fc666869ef2adf264be0f372136702f16add9299087fb1907c2e4ec5dbfe83bd24bce815c70a80c6c1a2e950 + languageName: node + linkType: hard + +"clone-response@npm:^1.0.2": + version: 1.0.3 + resolution: "clone-response@npm:1.0.3" + dependencies: + mimic-response: "npm:^1.0.0" + checksum: 10/4e671cac39b11c60aa8ba0a450657194a5d6504df51bca3fac5b3bd0145c4f8e8464898f87c8406b83232e3bc5cca555f51c1f9c8ac023969ebfbf7f6bdabb2e + languageName: node + linkType: hard + +"clone@npm:^1.0.2": + version: 1.0.4 + resolution: "clone@npm:1.0.4" + checksum: 10/d06418b7335897209e77bdd430d04f882189582e67bd1f75a04565f3f07f5b3f119a9d670c943b6697d0afb100f03b866b3b8a1f91d4d02d72c4ecf2bb64b5dd + languageName: node + linkType: hard + +"cluster-key-slot@npm:^1.1.0": + version: 1.1.2 + resolution: "cluster-key-slot@npm:1.1.2" + checksum: 10/516ed8b5e1a14d9c3a9c96c72ef6de2d70dfcdbaa0ec3a90bc7b9216c5457e39c09a5775750c272369070308542e671146120153062ab5f2f481bed5de2c925f + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10/ffa319025045f2973919d155f25e7c00d08836b6b33ea2d205418c59bd63a665d713c52d9737a9e0fe467fb194b40fbef1d849bae80d674568ee220a31ef3d10 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10/fa00c91b4332b294de06b443923246bccebe9fab1b253f7fe1772d37b06a2269b4039a85e309abe1fe11b267b11c08d1d0473fda3badd6167f57313af2887a64 + languageName: node + linkType: hard + +"color-convert@npm:^3.1.3": + version: 3.1.3 + resolution: "color-convert@npm:3.1.3" + dependencies: + color-name: "npm:^2.0.0" + checksum: 10/36b9b99c138f90eb11a28d1ad911054a9facd6cffde4f00dc49a34ebde7cae28454b2285ede64f273b6a8df9c3228b80e4352f4471978fa8b5005fe91341a67b + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10/09c5d3e33d2105850153b14466501f2bfb30324a2f76568a408763a3b7433b0e50e5b4ab1947868e65cb101bb7cb75029553f2c333b6d4b8138a73fcc133d69d + languageName: node + linkType: hard + +"color-name@npm:^2.0.0": + version: 2.1.0 + resolution: "color-name@npm:2.1.0" + checksum: 10/eb014f71d87408e318e95d3f554f188370d354ba8e0ffa4341d0fd19de391bfe2bc96e563d4f6614644d676bc24f475560dffee3fe310c2d6865d007410a9a2b + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10/b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"color-string@npm:^2.1.3": + version: 2.1.4 + resolution: "color-string@npm:2.1.4" + dependencies: + color-name: "npm:^2.0.0" + checksum: 10/689a8688ac3cd55247792c83a9db9bfe675343c7412fedba1eb748ac6a8867dd2bb3d406e309ebfe90336809ee5067c7f2cccfbd10133c5cc9ef1dba5aad58f2 + languageName: node + linkType: hard + +"color@npm:^5.0.2": + version: 5.0.3 + resolution: "color@npm:5.0.3" + dependencies: + color-convert: "npm:^3.1.3" + color-string: "npm:^2.1.3" + checksum: 10/88063ee058b995e5738092b5aa58888666275d1e967333f3814ff4fa334ce9a9e71de78a16fb1838f17c80793ea87f4878c20192037662809fe14eab2d474fd9 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10/2e969e637d05d09fa50b02d74c83a1186f6914aae89e6653b62595cc75a221464f884f55f231b8f4df7a49537fba60bdc0427acd2bf324c09a1dbb84837e36e4 + languageName: node + linkType: hard + +"commander@npm:4.1.1": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: 10/3b2dc4125f387dab73b3294dbcb0ab2a862f9c0ad748ee2b27e3544d25325b7a8cdfbcc228d103a98a716960b14478114a5206b5415bd48cdafa38797891562c + languageName: node + linkType: hard + +"commander@npm:^10.0.0": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 10/8799faa84a30da985802e661cc9856adfaee324d4b138413013ef7f087e8d7924b144c30a1f1405475f0909f467665cd9e1ce13270a2f41b141dab0b7a58f3fb + languageName: node + linkType: hard + +"commander@npm:^2.19.0, commander@npm:^2.20.0": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: 10/90c5b6898610cd075984c58c4f88418a4fb44af08c1b1415e9854c03171bec31b336b7f3e4cefe33de994b3f12b03c5e2d638da4316df83593b9e82554e7e95b + languageName: node + linkType: hard + +"commander@npm:^6.0.0, commander@npm:^6.1.0": + version: 6.2.1 + resolution: "commander@npm:6.2.1" + checksum: 10/25b88c2efd0380c84f7844b39cf18510da7bfc5013692d68cdc65f764a1c34e6c8a36ea6d72b6620e3710a930cf8fab2695bdec2bf7107a0f4fa30a3ef3b7d0e + languageName: node + linkType: hard + +"commander@npm:^8.3.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 10/6b7b5d334483ce24bd73c5dac2eab901a7dbb25fd983ea24a1eeac6e7166bb1967f641546e8abf1920afbde86a45fbfe5812fbc69d0dc451bb45ca416a12a3a3 + languageName: node + linkType: hard + +"comment-json@npm:4.4.1": + version: 4.4.1 + resolution: "comment-json@npm:4.4.1" + dependencies: + array-timsort: "npm:^1.0.3" + core-util-is: "npm:^1.0.3" + esprima: "npm:^4.0.1" + checksum: 10/2d05701e361320c670623b01343ed9ff180f4b4a38291f19ab9e2ef5269f51c8d1011f003fceff8cbf73d293f53a8ffc8ba1f85ccb1f6d0703829155bd628bce + languageName: node + linkType: hard + +"complex.js@npm:^2.2.5": + version: 2.4.3 + resolution: "complex.js@npm:2.4.3" + checksum: 10/904a2b4a09a4cfd94d8636ceb95e15cc077dcdedd07c54e233308210fb38897338dde4e7113811e89c1cfe4c6e3ebcf11735ad5c901e27c6d7e3c132a3078e3c + languageName: node + linkType: hard + +"compress-commons@npm:^4.1.2": + version: 4.1.2 + resolution: "compress-commons@npm:4.1.2" + dependencies: + buffer-crc32: "npm:^0.2.13" + crc32-stream: "npm:^4.0.2" + normalize-path: "npm:^3.0.0" + readable-stream: "npm:^3.6.0" + checksum: 10/76fa281412e4a95f89893dc1e3399e797de20253365cf53102ac4738fa004d3540abb12c26e3a54156f8fb4e4392ef9a9c5eecbe752f3a7d30e28c808b671e1b + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10/9680699c8e2b3af0ae22592cb764acaf973f292a7b71b8a06720233011853a58e256c89216a10cbe889727532fd77f8bcd49a760cedfde271b8e006c20e079f2 + languageName: node + linkType: hard + +"concat-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "concat-stream@npm:2.0.0" + dependencies: + buffer-from: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.0.2" + typedarray: "npm:^0.0.6" + checksum: 10/250e576d0617e7c58e1c4b2dd6fe69560f316d2c962a409f9f3aac794018499ddb31948b1e4296f217008e124cd5d526432097745157fe504b5d9f3dc469eadb + languageName: node + linkType: hard + +"config-chain@npm:^1.1.13": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: "npm:^1.3.4" + proto-list: "npm:~1.2.1" + checksum: 10/83d22cabf709e7669f6870021c4d552e4fc02e9682702b726be94295f42ce76cfed00f70b2910ce3d6c9465d9758e191e28ad2e72ff4e3331768a90da6c1ef03 + languageName: node + linkType: hard + +"consola@npm:^3.2.3": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10/32192c9f50d7cac27c5d7c4ecd3ff3679aea863e6bf5bd6a9cc2b05d1cd78addf5dae71df08c54330c142be8e7fbd46f051030129b57c6aacdd771efe409c4b2 + languageName: node + linkType: hard + +"console-stamp@npm:^3.0.2": + version: 3.1.2 + resolution: "console-stamp@npm:3.1.2" + dependencies: + chalk: "npm:^4.1.2" + dateformat: "npm:^4.6.3" + checksum: 10/aa6e0685f4220be28aace4e6148acd2a830b14f691906fe767649e435ea7415ba9f12391b685248fdf5864841273f6e5734c30d238fa6f3c8483593e605ea71c + languageName: node + linkType: hard + +"constantinople@npm:^4.0.1": + version: 4.0.1 + resolution: "constantinople@npm:4.0.1" + dependencies: + "@babel/parser": "npm:^7.6.0" + "@babel/types": "npm:^7.6.1" + checksum: 10/15fc9bec82711f275e35581fe97a7e7b8d30441745955023570f258bbf876f4bf3de84faa6e7a663a3048565d9cc58bde65d300f74d090faec6afe07913d584f + languageName: node + linkType: hard + +"content-disposition@npm:^0.5.4": + version: 0.5.4 + resolution: "content-disposition@npm:0.5.4" + dependencies: + safe-buffer: "npm:5.2.1" + checksum: 10/b7f4ce176e324f19324be69b05bf6f6e411160ac94bc523b782248129eb1ef3be006f6cff431aaea5e337fe5d176ce8830b8c2a1b721626ead8933f0cbe78720 + languageName: node + linkType: hard + +"content-disposition@npm:^1.0.0": + version: 1.0.1 + resolution: "content-disposition@npm:1.0.1" + checksum: 10/0718d861dfec56f532fd9acd714f173782ce5257b243344fecab5196621746cf8623bf1c833441612f1ac84559c546b59277cf0e91c3a646b0712a806decb1c8 + languageName: node + linkType: hard + +"content-type@npm:^1.0.5": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662 + languageName: node + linkType: hard + +"cookie-parser@npm:^1.4.7": + version: 1.4.7 + resolution: "cookie-parser@npm:1.4.7" + dependencies: + cookie: "npm:0.7.2" + cookie-signature: "npm:1.0.6" + checksum: 10/243fa13f217e793d20a57675e6552beea08c5989fcc68495d543997a31646875335e0e82d687b42dcfd466df57891d22bae7f5ba6ab33b7705ed2dd6eb989105 + languageName: node + linkType: hard + +"cookie-signature@npm:1.0.6": + version: 1.0.6 + resolution: "cookie-signature@npm:1.0.6" + checksum: 10/f4e1b0a98a27a0e6e66fd7ea4e4e9d8e038f624058371bf4499cfcd8f3980be9a121486995202ba3fca74fbed93a407d6d54d43a43f96fd28d0bd7a06761591a + languageName: node + linkType: hard + +"cookie-signature@npm:^1.2.1": + version: 1.2.2 + resolution: "cookie-signature@npm:1.2.2" + checksum: 10/be44a3c9a56f3771aea3a8bd8ad8f0a8e2679bcb967478267f41a510b4eb5ec55085386ba79c706c4ac21605ca76f4251973444b90283e0eb3eeafe8a92c7708 + languageName: node + linkType: hard + +"cookie@npm:0.7.2, cookie@npm:^0.7.1, cookie@npm:~0.7.2": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f + languageName: node + linkType: hard + +"core-util-is@npm:^1.0.3, core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10/9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 + languageName: node + linkType: hard + +"cors@npm:2.8.5, cors@npm:~2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: "npm:^4" + vary: "npm:^1" + checksum: 10/66e88e08edee7cbce9d92b4d28a2028c88772a4c73e02f143ed8ca76789f9b59444eed6b1c167139e76fa662998c151322720093ba229f9941365ada5a6fc2c6 + languageName: node + linkType: hard + +"cosmiconfig@npm:^8.2.0": + version: 8.3.6 + resolution: "cosmiconfig@npm:8.3.6" + dependencies: + import-fresh: "npm:^3.3.0" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.2.0" + path-type: "npm:^4.0.0" + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/91d082baca0f33b1c085bf010f9ded4af43cbedacba8821da0fb5667184d0a848addc52c31fadd080007f904a555319c238cf5f4c03e6d58ece2e4876b2e73d6 + languageName: node + linkType: hard + +"crc-32@npm:^1.2.0": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: 10/824f696a5baaf617809aa9cd033313c8f94f12d15ebffa69f10202480396be44aef9831d900ab291638a8022ed91c360696dd5b1ba691eb3f34e60be8835b7c3 + languageName: node + linkType: hard + +"crc32-stream@npm:^4.0.2": + version: 4.0.3 + resolution: "crc32-stream@npm:4.0.3" + dependencies: + crc-32: "npm:^1.2.0" + readable-stream: "npm:^3.4.0" + checksum: 10/d44d0ec6f04d8a1bed899ac3e4fbb82111ed567ea6d506be39147362af45c747887fce1032f4beca1646b4824e5a9614cd3332bfa94bbc5577ca5445e7f75ddd + languageName: node + linkType: hard + +"create-require@npm:^1.1.0": + version: 1.1.1 + resolution: "create-require@npm:1.1.1" + checksum: 10/a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff + languageName: node + linkType: hard + +"cron@npm:4.3.5": + version: 4.3.5 + resolution: "cron@npm:4.3.5" + dependencies: + "@types/luxon": "npm:~3.7.0" + luxon: "npm:~3.7.0" + checksum: 10/c914bcd5ddf033c4bc2b1a86a92d96c5a000ece2488567a06ee57334820110282932cc0e8339bfba8732f9c593edf660caa0c9937cddd85a0dd7435063c8c911 + languageName: node + linkType: hard + +"cron@npm:^3.1.7": + version: 3.5.0 + resolution: "cron@npm:3.5.0" + dependencies: + "@types/luxon": "npm:~3.4.0" + luxon: "npm:~3.5.0" + checksum: 10/0e667d87c9acc162db835439bff2664483f1fcbd471ae30a26c7426c736fa1798d27067cc4d0294d8a27890a1bc6c9deeefe47811cc339f11a8ba8288f51886d + languageName: node + linkType: hard + +"cross-spawn@npm:^6.0.0": + version: 6.0.6 + resolution: "cross-spawn@npm:6.0.6" + dependencies: + nice-try: "npm:^1.0.4" + path-key: "npm:^2.0.1" + semver: "npm:^5.5.0" + shebang-command: "npm:^1.2.0" + which: "npm:^1.2.9" + checksum: 10/7abf6137b23293103a22bfeaf320f2d63faae70d97ddb4b58597237501d2efdd84cdc69a30246977e0c5f68216593894d41a7f122915dd4edf448db14c74171b + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10/0d52657d7ae36eb130999dffff1168ec348687b48dd38e2ff59992ed916c88d328cf1d07ff4a4a10bc78de5e1c23f04b306d569e42f7a2293915c081e4dfee86 + languageName: node + linkType: hard + +"css-select@npm:^5.1.0": + version: 5.2.2 + resolution: "css-select@npm:5.2.2" + dependencies: + boolbase: "npm:^1.0.0" + css-what: "npm:^6.1.0" + domhandler: "npm:^5.0.2" + domutils: "npm:^3.0.1" + nth-check: "npm:^2.0.1" + checksum: 10/ebb6a88446433312d1a16301afd1c5f75090805b730dbbdccb0338b0d6ca7922410375f16dde06673ef7da086e2cf3b9ad91afe9a8e0d2ee3625795cb5e0170d + languageName: node + linkType: hard + +"css-what@npm:^6.1.0": + version: 6.2.2 + resolution: "css-what@npm:6.2.2" + checksum: 10/3c5a53be94728089bd1716f915f7f96adde5dd8bf374610eb03982266f3d860bf1ebaf108cda30509d02ef748fe33eaa59aa75911e2c49ee05a85ef1f9fb5223 + languageName: node + linkType: hard + +"date-fns-tz@npm:^3.2.0": + version: 3.2.0 + resolution: "date-fns-tz@npm:3.2.0" + peerDependencies: + date-fns: ^3.0.0 || ^4.0.0 + checksum: 10/8ab4745f00b40381220f0a7a2ec16e217cb629d4018a19047264d289dd260322baa23e19b3ed63c7e553f9ad34bea9dea105391132930a3e141e9a0a53e54af2 + languageName: node + linkType: hard + +"date-fns@npm:^4.1.0": + version: 4.1.0 + resolution: "date-fns@npm:4.1.0" + checksum: 10/d5f6e9de5bbc52310f786099e18609289ed5e30af60a71e0646784c8185ddd1d0eebcf7c96b7faaaefc4a8366f3a3a4244d099b6d0866ee2bec80d1361e64342 + languageName: node + linkType: hard + +"date-format@npm:^4.0.14": + version: 4.0.14 + resolution: "date-format@npm:4.0.14" + checksum: 10/6b07fd1df247439c53b71244e3468b93e6dfebb5d409b9328dd7b7e9ed0d2e875018e20fb1a95ae6b677dea708ec06aaa5058a7a5faa1a7f649338aabf04991a + languageName: node + linkType: hard + +"dateformat@npm:^4.6.3": + version: 4.6.3 + resolution: "dateformat@npm:4.6.3" + checksum: 10/5c149c91bf9ce2142c89f84eee4c585f0cb1f6faf2536b1af89873f862666a28529d1ccafc44750aa01384da2197c4f76f4e149a3cc0c1cb2c46f5cc45f2bcb5 + languageName: node + linkType: hard + +"dayjs@npm:^1.11.19, dayjs@npm:^1.11.9, dayjs@npm:^1.8.15, dayjs@npm:^1.8.34": + version: 1.11.19 + resolution: "dayjs@npm:1.11.19" + checksum: 10/185b820d68492b83a3ce2b8ddc7543034edc1dfd1423183f6ae4707b29929a3cc56503a81826309279f9084680c15966b99456e74cf41f7d1f6a2f98f9c7196f + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0, debug@npm:^4.4.3, debug@npm:~4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/9ada3434ea2993800bd9a1e320bd4aa7af69659fb51cca685d390949434bc0a8873c21ed7c9b852af6f2455a55c6d050aa3937d52b3c69f796dab666f762acad + languageName: node + linkType: hard + +"decimal.js@npm:^10.4.3, decimal.js@npm:^10.5.0": + version: 10.6.0 + resolution: "decimal.js@npm:10.6.0" + checksum: 10/c0d45842d47c311d11b38ce7ccc911121953d4df3ebb1465d92b31970eb4f6738a065426a06094af59bee4b0d64e42e7c8984abd57b6767c64ea90cf90bb4a69 + languageName: node + linkType: hard + +"decompress-response@npm:^6.0.0": + version: 6.0.0 + resolution: "decompress-response@npm:6.0.0" + dependencies: + mimic-response: "npm:^3.1.0" + checksum: 10/d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812 + languageName: node + linkType: hard + +"dedent@npm:^1.7.0": + version: 1.7.1 + resolution: "dedent@npm:1.7.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: 10/78785ef592e37e0b1ca7a7a5964c8f3dee1abdff46c5bb49864168579c122328f6bb55c769bc7e005046a7381c3372d3859f0f78ab083950fa146e1c24873f4f + languageName: node + linkType: hard + +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 10/7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7 + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: 10/ec12d074aef5ae5e81fa470b9317c313142c9e8e2afe3f8efa124db309720db96d1d222b82b84c834e5f87e7a614b44a4684b6683583118b87c833b3be40d4d8 + languageName: node + linkType: hard + +"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 10/058d9e1b0ff1a154468bf3837aea436abcfea1ba1d165ddaaf48ca93765fdd01a30d33c36173da8fbbed951dd0a267602bc782fe288b0fc4b7e1e7091afc4529 + languageName: node + linkType: hard + +"defaults@npm:^1.0.3": + version: 1.0.4 + resolution: "defaults@npm:1.0.4" + dependencies: + clone: "npm:^1.0.2" + checksum: 10/3a88b7a587fc076b84e60affad8b85245c01f60f38fc1d259e7ac1d89eb9ce6abb19e27215de46b98568dd5bc48471730b327637e6f20b0f1bc85cf00440c80a + languageName: node + linkType: hard + +"defaults@npm:^2.0.2": + version: 2.0.2 + resolution: "defaults@npm:2.0.2" + checksum: 10/fdce6a8d1ff8fbe6a8c11d8ad4c9118e455c36f96d933f950ea415d7e0add6bde4e8537dc625478e9f040315555f12a6088fe9ed11996d5dfaaa447aeb05d169 + languageName: node + linkType: hard + +"defer-to-connect@npm:^2.0.0, defer-to-connect@npm:^2.0.1": + version: 2.0.1 + resolution: "defer-to-connect@npm:2.0.1" + checksum: 10/8a9b50d2f25446c0bfefb55a48e90afd58f85b21bcf78e9207cd7b804354f6409032a1705c2491686e202e64fc05f147aa5aa45f9aa82627563f045937f5791b + languageName: node + linkType: hard + +"define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10/abdcb2505d80a53524ba871273e5da75e77e52af9e15b3aa65d8aad82b8a3a424dad7aee2cc0b71470ac7acf501e08defac362e8b6a73cdb4309f028061df4ae + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10/46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 + languageName: node + linkType: hard + +"denque@npm:^2.1.0": + version: 2.1.0 + resolution: "denque@npm:2.1.0" + checksum: 10/8ea05321576624b90acfc1ee9208b8d1d04b425cf7573b9b4fa40a2c3ed4d4b0af5190567858f532f677ed2003d4d2b73c8130b34e3c7b8d5e88cdcfbfaa1fe7 + languageName: node + linkType: hard + +"depd@npm:^2.0.0, depd@npm:~2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: 10/c0c8ff36079ce5ada64f46cc9d6fd47ebcf38241105b6e0c98f412e8ad91f084bcf906ff644cc3a4bd876ca27a62accb8b0fff72ea6ed1a414b89d8506f4a5ca + languageName: node + linkType: hard + +"detect-indent@npm:^6.0.0": + version: 6.1.0 + resolution: "detect-indent@npm:6.1.0" + checksum: 10/ab953a73c72dbd4e8fc68e4ed4bfd92c97eb6c43734af3900add963fd3a9316f3bc0578b018b24198d4c31a358571eff5f0656e81a1f3b9ad5c547d58b2d093d + languageName: node + linkType: hard + +"detect-libc@npm:^2.1.2": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10/b736c8d97d5d46164c0d1bed53eb4e6a3b1d8530d460211e2d52f1c552875e706c58a5376854e4e54f8b828c9cada58c855288c968522eb93ac7696d65970766 + languageName: node + linkType: hard + +"detect-newline@npm:^3.1.0": + version: 3.1.0 + resolution: "detect-newline@npm:3.1.0" + checksum: 10/ae6cd429c41ad01b164c59ea36f264a2c479598e61cba7c99da24175a7ab80ddf066420f2bec9a1c57a6bead411b4655ff15ad7d281c000a89791f48cbe939e7 + languageName: node + linkType: hard + +"detect-node@npm:2.1.0, detect-node@npm:^2.0.4": + version: 2.1.0 + resolution: "detect-node@npm:2.1.0" + checksum: 10/832184ec458353e41533ac9c622f16c19f7c02d8b10c303dfd3a756f56be93e903616c0bb2d4226183c9351c15fc0b3dba41a17a2308262afabcfa3776e6ae6e + languageName: node + linkType: hard + +"diff@npm:^4.0.1": + version: 4.0.2 + resolution: "diff@npm:4.0.2" + checksum: 10/ec09ec2101934ca5966355a229d77afcad5911c92e2a77413efda5455636c4cf2ce84057e2d7715227a2eeeda04255b849bd3ae3a4dd22eb22e86e76456df069 + languageName: node + linkType: hard + +"display-notification@npm:2.0.0": + version: 2.0.0 + resolution: "display-notification@npm:2.0.0" + dependencies: + escape-string-applescript: "npm:^1.0.0" + run-applescript: "npm:^3.0.0" + checksum: 10/e4b75d4e909473a65a1747a6c4877f02b547525cfb79286771008cb48362608a7a7e02e6bad0c2e2ff38f304420ae42f8d856b025a5595eee7cf4eacd315f364 + languageName: node + linkType: hard + +"doctypes@npm:^1.1.0": + version: 1.1.0 + resolution: "doctypes@npm:1.1.0" + checksum: 10/6e6c2d1a80f2072dc4831994c914c44455e341c5ab18c16797368a0afd59d7c22f3335805ba2c1dd2931e9539d1ba8b613b7650dc63f6ab56b77b8d888055de8 + languageName: node + linkType: hard + +"docxtemplater@npm:^3.63.2": + version: 3.67.6 + resolution: "docxtemplater@npm:3.67.6" + dependencies: + "@xmldom/xmldom": "npm:^0.9.8" + checksum: 10/d23d09866bab9f7153fce900a992da7c713d09ec02a062abb4a73740cb30388d58c82b09bdbe9fa9a3d15c096cf96f92b3ca87a36f1a46643109aa2c886b7a94 + languageName: node + linkType: hard + +"dom-serializer@npm:^1.0.1": + version: 1.4.1 + resolution: "dom-serializer@npm:1.4.1" + dependencies: + domelementtype: "npm:^2.0.1" + domhandler: "npm:^4.2.0" + entities: "npm:^2.0.0" + checksum: 10/53b217bcfed4a0f90dd47f34f239b1c81fff53ffa39d164d722325817fdb554903b145c2d12c8421ce0df7d31c1b180caf7eacd3c86391dd925f803df8027dcc + languageName: node + linkType: hard + +"dom-serializer@npm:^2.0.0": + version: 2.0.0 + resolution: "dom-serializer@npm:2.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.2" + entities: "npm:^4.2.0" + checksum: 10/e3bf9027a64450bca0a72297ecdc1e3abb7a2912268a9f3f5d33a2e29c1e2c3502c6e9f860fc6625940bfe0cfb57a44953262b9e94df76872fdfb8151097eeb3 + languageName: node + linkType: hard + +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: 10/ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 + languageName: node + linkType: hard + +"domhandler@npm:^3.3.0": + version: 3.3.0 + resolution: "domhandler@npm:3.3.0" + dependencies: + domelementtype: "npm:^2.0.1" + checksum: 10/31baccfeb2354477f90c5f6ab2e3606122228996fb87534750b7ceef3f8eebf8ae9599b02dc551eaaa532c874f964c331b1f76e651643a6048489b73cc68ea7e + languageName: node + linkType: hard + +"domhandler@npm:^4.2.0": + version: 4.3.1 + resolution: "domhandler@npm:4.3.1" + dependencies: + domelementtype: "npm:^2.2.0" + checksum: 10/e0d2af7403997a3ca040a9ace4a233b75ebe321e0ef628b417e46d619d65d47781b2f2038b6c2ef6e56e73e66aec99caf6a12c7e687ecff18ef74af6dfbde5de + languageName: node + linkType: hard + +"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10/809b805a50a9c6884a29f38aec0a4e1b4537f40e1c861950ed47d10b049febe6b79ab72adaeeebb3cc8fc1cd33f34e97048a72a9265103426d93efafa78d3e96 + languageName: node + linkType: hard + +"domutils@npm:^2.4.2": + version: 2.8.0 + resolution: "domutils@npm:2.8.0" + dependencies: + dom-serializer: "npm:^1.0.1" + domelementtype: "npm:^2.2.0" + domhandler: "npm:^4.2.0" + checksum: 10/1f316a03f00b09a8893d4a25d297d5cbffd02c564509dede28ef72d5ce38d93f6d61f1de88d439f31b14a1d9b42f587ed711b9e8b1b4d3bf6001399832bfc4e0 + languageName: node + linkType: hard + +"domutils@npm:^3.0.1, domutils@npm:^3.1.0": + version: 3.2.2 + resolution: "domutils@npm:3.2.2" + dependencies: + dom-serializer: "npm:^2.0.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + checksum: 10/2e08842151aa406f50fe5e6d494f4ec73c2373199fa00d1f77b56ec604e566b7f226312ae35ab8160bb7f27a27c7285d574c8044779053e499282ca9198be210 + languageName: node + linkType: hard + +"dotenv-expand@npm:12.0.1": + version: 12.0.1 + resolution: "dotenv-expand@npm:12.0.1" + dependencies: + dotenv: "npm:^16.4.5" + checksum: 10/ceae1314c3c537c5a8d7f621013603d844e02290ad20b937cc59d7236aa6feb1a0dc94a0085d22d24da3a1cab2fef426d29b08034be5d46f46e4c8a5313014fa + languageName: node + linkType: hard + +"dotenv@npm:16.4.7": + version: 16.4.7 + resolution: "dotenv@npm:16.4.7" + checksum: 10/f13bfe97db88f0df4ec505eeffb8925ec51f2d56a3d0b6d916964d8b4af494e6fb1633ba5d09089b552e77ab2a25de58d70259b2c5ed45ec148221835fc99a0c + languageName: node + linkType: hard + +"dotenv@npm:^16.4.5, dotenv@npm:^16.6.1": + version: 16.6.1 + resolution: "dotenv@npm:16.6.1" + checksum: 10/1d1897144344447ffe62aa1a6d664f4cd2e0784e0aff787eeeec1940ded32f8e4b5b506d665134fc87157baa086fce07ec6383970a2b6d2e7985beaed6a4cc14 + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10/5add88a3d68d42d6e6130a0cac450b7c2edbe73364bbd2fc334564418569bea97c6943a8fcd70e27130bf32afc236f30982fc4905039b703f23e9e0433c29934 + languageName: node + linkType: hard + +"duplexer2@npm:~0.1.4": + version: 0.1.4 + resolution: "duplexer2@npm:0.1.4" + dependencies: + readable-stream: "npm:^2.0.2" + checksum: 10/f60ff8b8955f992fd9524516e82faa5662d7aca5b99ee71c50bbbe1a3c970fafacb35d526d8b05cef8c08be56eed3663c096c50626c3c3651a52af36c408bf4d + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10/9b1d3e1baefeaf7d70799db8774149cef33b97183a6addceeba0cf6b85ba23ee2686f302f14482006df32df75d32b17c509c143a3689627929e4a8efaf483952 + languageName: node + linkType: hard + +"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10/878e1aab8a42773320bc04c6de420bee21aebd71810e40b1799880a8a1c4594bcd6adc3d4213a0fb8147d4c3f529d8f9a618d7f59ad5a9a41b142058aceda23f + languageName: node + linkType: hard + +"editorconfig@npm:^1.0.4": + version: 1.0.4 + resolution: "editorconfig@npm:1.0.4" + dependencies: + "@one-ini/wasm": "npm:0.1.1" + commander: "npm:^10.0.0" + minimatch: "npm:9.0.1" + semver: "npm:^7.5.3" + bin: + editorconfig: bin/editorconfig + checksum: 10/bd0a7236f31a7f54801cb6f3222508d4f872a24e440bef30ee29f4ba667c0741724e52e0ad521abe3409b12cdafd8384bb751de9b2a2ee5f845c740edd2e742f + languageName: node + linkType: hard + +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 10/1b4cac778d64ce3b582a7e26b218afe07e207a0f9bfe13cc7395a6d307849cfe361e65033c3251e00c27dd060cab43014c2d6b2647676135e18b77d2d05b3f4f + languageName: node + linkType: hard + +"ejs@npm:^3.1.10": + version: 3.1.10 + resolution: "ejs@npm:3.1.10" + dependencies: + jake: "npm:^10.8.5" + bin: + ejs: bin/cli.js + checksum: 10/a9cb7d7cd13b7b1cd0be5c4788e44dd10d92f7285d2f65b942f33e127230c054f99a42db4d99f766d8dbc6c57e94799593ee66a14efd7c8dd70c4812bf6aa384 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.263": + version: 1.5.267 + resolution: "electron-to-chromium@npm:1.5.267" + checksum: 10/05e55e810cb6a3cda8d29dfdeec7ac0e59727a77a796a157f1a1d65edac16d45eed69ed5c99e354872ab16c48967c2d0a0600653051ae380a3b7a4d6210b1e60 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10/c72d67a6821be15ec11997877c437491c313d924306b8da5d87d2a2bcc2cec9903cb5b04ee1a088460501d8e5b44f10df82fdc93c444101a7610b80c8b6938e1 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10/915acf859cea7131dac1b2b5c9c8e35c4849e325a1d114c30adb8cd615970f6dca0e27f64f3a4949d7d6ed86ecd79a1c5c63f02e697513cddd7b5835c90948b8 + languageName: node + linkType: hard + +"enabled@npm:2.0.x": + version: 2.0.0 + resolution: "enabled@npm:2.0.0" + checksum: 10/9d256d89f4e8a46ff988c6a79b22fa814b4ffd82826c4fdacd9b42e9b9465709d3b748866d0ab4d442dfc6002d81de7f7b384146ccd1681f6a7f868d2acca063 + languageName: node + linkType: hard + +"encodeurl@npm:^2.0.0": + version: 2.0.0 + resolution: "encodeurl@npm:2.0.0" + checksum: 10/abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe + languageName: node + linkType: hard + +"encoding-japanese@npm:2.2.0": + version: 2.2.0 + resolution: "encoding-japanese@npm:2.2.0" + checksum: 10/e77259312054ed0f3cdbb5b35d56d244ef4a56779d47e279e7b9da96e0f81b71b0ee2a74709eb047b06d1365f857501cfdbe62bd4e782a412123fd5933feda6c + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10/bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": + version: 1.4.5 + resolution: "end-of-stream@npm:1.4.5" + dependencies: + once: "npm:^1.4.0" + checksum: 10/1e0cfa6e7f49887544e03314f9dfc56a8cb6dde910cbb445983ecc2ff426fc05946df9d75d8a21a3a64f2cecfe1bf88f773952029f46756b2ed64a24e95b1fb8 + languageName: node + linkType: hard + +"engine.io-parser@npm:~5.2.1": + version: 5.2.3 + resolution: "engine.io-parser@npm:5.2.3" + checksum: 10/eb0023fff5766e7ae9d59e52d92df53fea06d472cfd7b52e5d2c36b4c1dbf78cab5fde1052bcb3d4bb85bdb5aee10ae85d8a1c6c04676dac0c6cdf16bcba6380 + languageName: node + linkType: hard + +"engine.io@npm:~6.6.0": + version: 6.6.5 + resolution: "engine.io@npm:6.6.5" + dependencies: + "@types/cors": "npm:^2.8.12" + "@types/node": "npm:>=10.0.0" + accepts: "npm:~1.3.4" + base64id: "npm:2.0.0" + cookie: "npm:~0.7.2" + cors: "npm:~2.8.5" + debug: "npm:~4.4.1" + engine.io-parser: "npm:~5.2.1" + ws: "npm:~8.18.3" + checksum: 10/d48f8c4240185c018c4d5608fa1641dbd640c10dda7ae24cdca57c5e6938e47bead110f1435925822923444590d2b63c7aebe43149fe9978714fee960923a23b + languageName: node + linkType: hard + +"enhanced-resolve@npm:^5.17.3, enhanced-resolve@npm:^5.7.0": + version: 5.18.4 + resolution: "enhanced-resolve@npm:5.18.4" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.2.0" + checksum: 10/dcd477cb694d9cc84109a03269c13d3da0851d50099fd3fa7c56b2867dd720d59c7f1431bd47c9cad2825ad52588bd71d3a68cf1e5ee0bc57551d8a3fab4e6f2 + languageName: node + linkType: hard + +"entities@npm:^2.0.0": + version: 2.2.0 + resolution: "entities@npm:2.2.0" + checksum: 10/2c765221ee324dbe25e1b8ca5d1bf2a4d39e750548f2e85cbf7ca1d167d709689ddf1796623e66666ae747364c11ed512c03b48c5bbe70968d30f2a4009509b7 + languageName: node + linkType: hard + +"entities@npm:^4.2.0, entities@npm:^4.4.0, entities@npm:^4.5.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 10/ede2a35c9bce1aeccd055a1b445d41c75a14a2bb1cd22e242f20cf04d236cdcd7f9c859eb83f76885327bfae0c25bf03303665ee1ce3d47c5927b98b0e3e3d48 + languageName: node + linkType: hard + +"entities@npm:^6.0.0": + version: 6.0.1 + resolution: "entities@npm:6.0.1" + checksum: 10/62af1307202884349d2867f0aac5c60d8b57102ea0b0e768b16246099512c28e239254ad772d6834e7e14cb1b6f153fc3d0c031934e3183b086c86d3838d874a + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"err-code@npm:^1.0.0": + version: 1.1.2 + resolution: "err-code@npm:1.1.2" + checksum: 10/f2bd853e355a8eb2e29316405916fd83d8d4fdb6ddc980b9a4275748ecce6a58b48fdc8240f509743a7f47184eac1d1773ac5495f587e03118989bac98c5a3d9 + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10/1d20d825cdcce8d811bfbe86340f4755c02655a7feb2f13f8c880566d9d72a3f6c92c192a6867632e490d6da67b678271f46e01044996a6443e870331100dfdd + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.4 + resolution: "error-ex@npm:1.3.4" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10/ae3939fd4a55b1404e877df2080c6b59acc516d5b7f08a181040f78f38b4e2399633bfed2d9a21b91c803713fff7295ac70bebd8f3657ef352a95c2cd9aa2e4b + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10/f8dc9e660d90919f11084db0a893128f3592b781ce967e4fccfb8f3106cb83e400a4032c559184ec52ee1dbd4b01e7776c7cd0b3327b1961b1a4a7008920fe78 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 + languageName: node + linkType: hard + +"es-module-lexer@npm:^1.2.1": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10/b6f3e576a3fed4d82b0d0ad4bbf6b3a5ad694d2e7ce8c4a069560da3db6399381eaba703616a182b16dde50ce998af64e07dcf49f2ae48153b9e07be3f107087 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/54fe77de288451dae51c37bfbfe3ec86732dc3778f98f3eb3bdb4bf48063b2c0b8f9c93542656986149d08aa5be3204286e2276053d19582b76753f1a2728867 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10/86814bf8afbcd8966653f731415888019d4bc4aca6b6c354132a7a75bb87566751e320369654a101d23a91c87a85c79b178bcf40332839bd347aff437c4fb65f + languageName: node + linkType: hard + +"escalade@npm:^3.1.1, escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10/9d7169e3965b2f9ae46971afa392f6e5a25545ea30f2e2dd99c9b0a95a3f52b5653681a84f5b2911a413ddad2d7a93d3514165072f349b5ffc59c75a899970d6 + languageName: node + linkType: hard + +"escape-goat@npm:^3.0.0": + version: 3.0.0 + resolution: "escape-goat@npm:3.0.0" + checksum: 10/6719196d073cc72d0bbe079646d6fa32f226f24fd7d00c1a71fa375bd4c5b8999050021d9e62c232a8874230328ebf89a5c8bd76fb72f7ccd6229efbe5abd04e + languageName: node + linkType: hard + +"escape-html@npm:^1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 10/6213ca9ae00d0ab8bccb6d8d4e0a98e76237b2410302cf7df70aaa6591d509a2a37ce8998008cbecae8fc8ffaadf3fb0229535e6a145f3ce0b211d060decbb24 + languageName: node + linkType: hard + +"escape-latex@npm:^1.2.0": + version: 1.2.0 + resolution: "escape-latex@npm:1.2.0" + checksum: 10/73a787319f0965ecb8244bb38bf3a3cba872f0b9a5d3da8821140e9f39fe977045dc953a62b1a2bed4d12bfccbe75a7d8ec786412bf00739eaa2f627d0a8e0d6 + languageName: node + linkType: hard + +"escape-string-applescript@npm:^1.0.0": + version: 1.0.0 + resolution: "escape-string-applescript@npm:1.0.0" + checksum: 10/2835d891d3d0ca287506c78e3f3cc42f1f773c5b2d25f26a2c37d105641be4a7737386babc1495b64dae2edb5a0abe112dc1c6b3582aaef106ecd70e050c28ce + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10/6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10/98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 + languageName: node + linkType: hard + +"eslint-config-prettier@npm:^10.1.5": + version: 10.1.8 + resolution: "eslint-config-prettier@npm:10.1.8" + peerDependencies: + eslint: ">=7.0.0" + bin: + eslint-config-prettier: bin/cli.js + checksum: 10/03f8e6ea1a6a9b8f9eeaf7c8c52a96499ec4b275b9ded33331a6cc738ed1d56de734097dbd0091f136f0e84bc197388bd8ec22a52a4658105883f8c8b7d8921a + languageName: node + linkType: hard + +"eslint-plugin-prettier@npm:^5.4.0": + version: 5.5.4 + resolution: "eslint-plugin-prettier@npm:5.5.4" + dependencies: + prettier-linter-helpers: "npm:^1.0.0" + synckit: "npm:^0.11.7" + peerDependencies: + "@types/eslint": ">=8.0.0" + eslint: ">=8.0.0" + eslint-config-prettier: ">= 7.0.0 <10.0.0 || >=10.1.0" + prettier: ">=3.0.0" + peerDependenciesMeta: + "@types/eslint": + optional: true + eslint-config-prettier: + optional: true + checksum: 10/5e39e3b7046d4ba0e1111cc2048630ee9d0aa5d5bb00d6230bef56893fdae37cbe2261babfb26db350cc2ad517c81d283b3f8b04cfee4e5aef7cd4bee72f90de + languageName: node + linkType: hard + +"eslint-scope@npm:5.1.1": + version: 5.1.1 + resolution: "eslint-scope@npm:5.1.1" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^4.1.1" + checksum: 10/c541ef384c92eb5c999b7d3443d80195fcafb3da335500946f6db76539b87d5826c8f2e1d23bf6afc3154ba8cd7c8e566f8dc00f1eea25fdf3afc8fb9c87b238 + languageName: node + linkType: hard + +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10/e8e611701f65375e034c62123946e628894f0b54aa8cb11abe224816389abe5cd74cf16b62b72baa36504f22d1a958b9b8b0169b82397fe2e7997674c0d09b06 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 10/3f357c554a9ea794b094a09bd4187e5eacd1bc0d0653c3adeb87962c548e6a1ab8f982b86963ae1337f5d976004146536dcee5d0e2806665b193fbfbf1a9231b + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10/3ee00fc6a7002d4b0ffd9dc99e13a6a7882c557329e6c25ab254220d71e5c9c4f89dca4695352949ea678eb1f3ba912a18ef8aac0a7fe094196fd92f441bfce2 + languageName: node + linkType: hard + +"eslint@npm:^9.27.0": + version: 9.39.2 + resolution: "eslint@npm:9.39.2" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.8.0" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.21.1" + "@eslint/config-helpers": "npm:^0.4.2" + "@eslint/core": "npm:^0.17.0" + "@eslint/eslintrc": "npm:^3.3.1" + "@eslint/js": "npm:9.39.2" + "@eslint/plugin-kit": "npm:^0.4.1" + "@humanfs/node": "npm:^0.16.6" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@humanwhocodes/retry": "npm:^0.4.2" + "@types/estree": "npm:^1.0.6" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.3.2" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" + esquery: "npm:^1.5.0" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^8.0.0" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: 10/53ff0e9c8264e7e8d40d50fdc0c0df0b701cfc5289beedfb686c214e3e7b199702f894bbd1bb48653727bb1ecbd1147cf5f555a4ae71e1daf35020cdc9072d9f + languageName: node + linkType: hard + +"espree@npm:^10.0.1, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" + dependencies: + acorn: "npm:^8.15.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10/9b355b32dbd1cc9f57121d5ee3be258fab87ebeb7c83fc6c02e5af1a74fc8c5ba79fe8c663e69ea112c3e84a1b95e6a2067ac4443ee7813bb85ac7581acb8bf9 + languageName: node + linkType: hard + +"esprima@npm:^1.2.0": + version: 1.2.5 + resolution: "esprima@npm:1.2.5" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: 10/839aad5916d05d3a82ccf3adaf67c2b5df69278fd7168347346e7af298dc7fbfbfd7bc5e27e38031a584d50d28e37da35d711b2f5d5376794f84b1bd8e559665 + languageName: node + linkType: hard + +"esprima@npm:^4.0.1": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: 10/f1d3c622ad992421362294f7acf866aa9409fbad4eb2e8fa230bd33944ce371d32279667b242d8b8907ec2b6ad7353a717f3c0e60e748873a34a7905174bc0eb + languageName: node + linkType: hard + +"esquery@npm:^1.5.0": + version: 1.7.0 + resolution: "esquery@npm:1.7.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10/4afaf3089367e1f5885caa116ef386dffd8bfd64da21fd3d0e56e938d2667cfb2e5400ab4a825aa70e799bb3741e5b5d63c0b94d86e2d4cf3095c9e64b2f5a15 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: "npm:^5.2.0" + checksum: 10/44ffcd89e714ea6b30143e7f119b104fc4d75e77ee913f34d59076b40ef2d21967f84e019f84e1fd0465b42cdbf725db449f232b5e47f29df29ed76194db8e16 + languageName: node + linkType: hard + +"estraverse@npm:^1.5.0": + version: 1.9.3 + resolution: "estraverse@npm:1.9.3" + checksum: 10/682a7e2fda17fd3e892b78a8347d055f923465598f5d713354aefd53a3348b2a1a6ee8df41031d8f5ad9802cfd27c29caac84c2f58ce3b2df659d43d668c870b + languageName: node + linkType: hard + +"estraverse@npm:^4.1.1": + version: 4.3.0 + resolution: "estraverse@npm:4.3.0" + checksum: 10/3f67ad02b6dbfaddd9ea459cf2b6ef4ecff9a6082a7af9d22e445b9abc082ad9ca47e1825557b293fcdae477f4714e561123e30bb6a5b2f184fb2bad4a9497eb + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 10/37cbe6e9a68014d34dbdc039f90d0baf72436809d02edffcc06ba3c2a12eb298048f877511353b130153e532aac8d68ba78430c0dd2f44806ebc7c014b01585e + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10/b23acd24791db11d8f65be5ea58fd9a6ce2df5120ae2da65c16cfc5331ff59d5ac4ef50af66cd4bde238881503ec839928a0135b99a036a9cdfa22d17fd56cdb + languageName: node + linkType: hard + +"etag@npm:^1.8.1": + version: 1.8.1 + resolution: "etag@npm:1.8.1" + checksum: 10/571aeb3dbe0f2bbd4e4fadbdb44f325fc75335cd5f6f6b6a091e6a06a9f25ed5392f0863c5442acb0646787446e816f13cbfc6edce5b07658541dff573cab1ff + languageName: node + linkType: hard + +"eventemitter2@npm:6.4.9": + version: 6.4.9 + resolution: "eventemitter2@npm:6.4.9" + checksum: 10/b829b1c6b11e15926b635092b5ad62b4463d1c928859831dcae606e988cf41893059e3541f5a8209d21d2f15314422ddd4d84d20830b4bf44978608d15b06b08 + languageName: node + linkType: hard + +"events-universal@npm:^1.0.0": + version: 1.0.1 + resolution: "events-universal@npm:1.0.1" + dependencies: + bare-events: "npm:^2.7.0" + checksum: 10/71b2e6079b4dc030c613ef73d99f1acb369dd3ddb6034f49fd98b3e2c6632cde9f61c15fb1351004339d7c79672252a4694ecc46a6124dc794b558be50a83867 + languageName: node + linkType: hard + +"events@npm:^3.2.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: 10/a3d47e285e28d324d7180f1e493961a2bbb4cad6412090e4dec114f4db1f5b560c7696ee8e758f55e23913ede856e3689cd3aa9ae13c56b5d8314cd3b3ddd1be + languageName: node + linkType: hard + +"exceljs@npm:^4.4.0": + version: 4.4.0 + resolution: "exceljs@npm:4.4.0" + dependencies: + archiver: "npm:^5.0.0" + dayjs: "npm:^1.8.34" + fast-csv: "npm:^4.3.1" + jszip: "npm:^3.10.1" + readable-stream: "npm:^3.6.0" + saxes: "npm:^5.0.1" + tmp: "npm:^0.2.0" + unzipper: "npm:^0.10.11" + uuid: "npm:^8.3.0" + checksum: 10/2d310146130b2af25b9a553185062a4095f6fee9a931c18a22b8dccd8a244056f3d2766f44227b9a43c0f52d651d05c5c96291fd38b8ed0a0322afab683fd80c + languageName: node + linkType: hard + +"execa@npm:^0.10.0": + version: 0.10.0 + resolution: "execa@npm:0.10.0" + dependencies: + cross-spawn: "npm:^6.0.0" + get-stream: "npm:^3.0.0" + is-stream: "npm:^1.1.0" + npm-run-path: "npm:^2.0.0" + p-finally: "npm:^1.0.0" + signal-exit: "npm:^3.0.0" + strip-eof: "npm:^1.0.0" + checksum: 10/8aa9865625b2f359f6c5e5c7a5b89d53cdc2f232b56c493034c7f350b51ebeae2281e83e4ba0a795d170b5c2771626d9b56d3225236f3edc8df467cc8908627e + languageName: node + linkType: hard + +"execa@npm:^5.0.0, execa@npm:^5.1.1": + version: 5.1.1 + resolution: "execa@npm:5.1.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^6.0.0" + human-signals: "npm:^2.1.0" + is-stream: "npm:^2.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^4.0.1" + onetime: "npm:^5.1.2" + signal-exit: "npm:^3.0.3" + strip-final-newline: "npm:^2.0.0" + checksum: 10/8ada91f2d70f7dff702c861c2c64f21dfdc1525628f3c0454fd6f02fce65f7b958616cbd2b99ca7fa4d474e461a3d363824e91b3eb881705231abbf387470597 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 10/ca25962b4bbab943b7c4ed0b5228e263833a5063c65e1cdeac4be9afad350aae5466e8e619b5051f4f8d37b2144a2d6e8fcc771b6cc82934f7dade2f964f652c + languageName: node + linkType: hard + +"express-rate-limit@npm:^8.2.1": + version: 8.2.1 + resolution: "express-rate-limit@npm:8.2.1" + dependencies: + ip-address: "npm:10.0.1" + peerDependencies: + express: ">= 4.11" + checksum: 10/7cbf70df2e88e590e463d2d8f93380775b2ea181d97f2c50c2ff9f2c666c247f83109a852b21d9c99ccc5762119101f281f54a27252a2f1a0a918be6d71f955b + languageName: node + linkType: hard + +"express@npm:5.2.1, express@npm:^5.1.0": + version: 5.2.1 + resolution: "express@npm:5.2.1" + dependencies: + accepts: "npm:^2.0.0" + body-parser: "npm:^2.2.1" + content-disposition: "npm:^1.0.0" + content-type: "npm:^1.0.5" + cookie: "npm:^0.7.1" + cookie-signature: "npm:^1.2.1" + debug: "npm:^4.4.0" + depd: "npm:^2.0.0" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + etag: "npm:^1.8.1" + finalhandler: "npm:^2.1.0" + fresh: "npm:^2.0.0" + http-errors: "npm:^2.0.0" + merge-descriptors: "npm:^2.0.0" + mime-types: "npm:^3.0.0" + on-finished: "npm:^2.4.1" + once: "npm:^1.4.0" + parseurl: "npm:^1.3.3" + proxy-addr: "npm:^2.0.7" + qs: "npm:^6.14.0" + range-parser: "npm:^1.2.1" + router: "npm:^2.2.0" + send: "npm:^1.1.0" + serve-static: "npm:^2.2.0" + statuses: "npm:^2.0.1" + type-is: "npm:^2.0.1" + vary: "npm:^1.1.2" + checksum: 10/4aa545d89702ac83f645c77abda1b57bcabe288f0b380fb5580fac4e323ea0eb533005c8e666b4e19152fb16d4abf11ba87b22aa9a10857a0485cd86b94639bd + languageName: node + linkType: hard + +"ext-list@npm:^2.0.0": + version: 2.2.2 + resolution: "ext-list@npm:2.2.2" + dependencies: + mime-db: "npm:^1.28.0" + checksum: 10/fe69fedbef044e14d4ce9e84c6afceb696ba71500c15b8d0ce0a1e280237e17c95031b3d62d5e597652fea0065b9bf957346b3900d989dff59128222231ac859 + languageName: node + linkType: hard + +"ext-name@npm:^5.0.0": + version: 5.0.0 + resolution: "ext-name@npm:5.0.0" + dependencies: + ext-list: "npm:^2.0.0" + sort-keys-length: "npm:^1.0.0" + checksum: 10/f598269bd5de4295540ea7d6f8f6a01d82a7508f148b7700a05628ef6121648d26e6e5e942049e953b3051863df6b54bd8fe951e7877f185e34ace5d44370b33 + languageName: node + linkType: hard + +"extend-object@npm:^1.0.0": + version: 1.0.0 + resolution: "extend-object@npm:1.0.0" + checksum: 10/a63a60dbab4c5ded795fa79a08371118c63452faa75a604420b7cd681f468d7391fbe8fa43f5a369dc281e72f9e8d27385a9081c44b5f3a0976f51deb2b9e791 + languageName: node + linkType: hard + +"extend-shallow@npm:^2.0.1": + version: 2.0.1 + resolution: "extend-shallow@npm:2.0.1" + dependencies: + is-extendable: "npm:^0.1.0" + checksum: 10/8fb58d9d7a511f4baf78d383e637bd7d2e80843bd9cd0853649108ea835208fb614da502a553acc30208e1325240bb7cc4a68473021612496bb89725483656d8 + languageName: node + linkType: hard + +"extend@npm:^3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10/59e89e2dc798ec0f54b36d82f32a27d5f6472c53974f61ca098db5d4648430b725387b53449a34df38fd0392045434426b012f302b3cc049a6500ccf82877e4e + languageName: node + linkType: hard + +"fast-csv@npm:^4.3.1": + version: 4.3.6 + resolution: "fast-csv@npm:4.3.6" + dependencies: + "@fast-csv/format": "npm:4.3.5" + "@fast-csv/parse": "npm:4.3.6" + checksum: 10/eaa7ae48b3c7087f01a4827c5e0ad630685d0fada2f93489b2da1dcecd56b758eeb445a245f48a43e18815a03e8b848ecbc3951a65e60fed381d9056d9aa6768 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10/e21a9d8d84f53493b6aa15efc9cfd53dd5b714a1f23f67fb5dc8f574af80df889b3bce25dc081887c6d25457cce704e636395333abad896ccdec03abaf1f3f9d + languageName: node + linkType: hard + +"fast-diff@npm:^1.1.2": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: 10/9e57415bc69cd6efcc720b3b8fe9fdaf42dcfc06f86f0f45378b1fa512598a8aac48aa3928c8751d58e2f01bb4ba4f07e4f3d9bc0d57586d45f1bd1e872c6cde + languageName: node + linkType: hard + +"fast-fifo@npm:^1.2.0, fast-fifo@npm:^1.3.2": + version: 1.3.2 + resolution: "fast-fifo@npm:1.3.2" + checksum: 10/6bfcba3e4df5af7be3332703b69a7898a8ed7020837ec4395bb341bd96cc3a6d86c3f6071dd98da289618cf2234c70d84b2a6f09a33dd6f988b1ff60d8e54275 + languageName: node + linkType: hard + +"fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10/dcc6432b269762dd47381d8b8358bf964d8f4f60286ac6aa41c01ade70bda459ff2001b516690b96d5365f68a49242966112b5d5cc9cd82395fa8f9d017c90ad + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: 10/2c20055c1fa43c922428f16ca8bb29f2807de63e5c851f665f7ac9790176c01c3b40335257736b299764a8d383388dabc73c8083b8e1bc3d99f0a941444ec60e + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 10/eb7e220ecf2bab5159d157350b81d01f75726a4382f5a9266f42b9150c4523b9795f7f5d9fbbbeaeac09a441b2369f05ee02db48ea938584205530fe5693cfe1 + languageName: node + linkType: hard + +"fast-safe-stringify@npm:2.1.1, fast-safe-stringify@npm:^2.1.1": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: 10/dc1f063c2c6ac9533aee14d406441f86783a8984b2ca09b19c2fe281f9ff59d315298bc7bc22fd1f83d26fe19ef2f20e2ddb68e96b15040292e555c5ced0c1e4 + languageName: node + linkType: hard + +"fast-uri@npm:^3.0.1": + version: 3.1.0 + resolution: "fast-uri@npm:3.1.0" + checksum: 10/818b2c96dc913bcf8511d844c3d2420e2c70b325c0653633f51821e4e29013c2015387944435cd0ef5322c36c9beecc31e44f71b257aeb8e0b333c1d62bb17c2 + languageName: node + linkType: hard + +"fast-xml-parser@npm:5.2.5": + version: 5.2.5 + resolution: "fast-xml-parser@npm:5.2.5" + dependencies: + strnum: "npm:^2.1.0" + bin: + fxparser: src/cli/cli.js + checksum: 10/305017cff6968a34cbac597317be1516e85c44f650f30d982c84f8c30043e81fd38d39a8810d570136c921399dd43b9ac4775bdfbbbcfee96456f3c086b48bdd + languageName: node + linkType: hard + +"fast-xml-parser@npm:^4.1.3": + version: 4.5.3 + resolution: "fast-xml-parser@npm:4.5.3" + dependencies: + strnum: "npm:^1.1.1" + bin: + fxparser: src/cli/cli.js + checksum: 10/ca22bf9d65c10b8447c1034c13403e90ecee210e2b3852690df3d8a42b8a46ec655fae7356096abd98a15b89ddaf11878587b1773e0c3be4cbc2ac4af4c7bf95 + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.20.1 + resolution: "fastq@npm:1.20.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10/ab2fe3a7a108112e7752cfe7fc11683c21e595913a6a593ad0b4415f31dddbfc283775ab66f2c8ccea6ab7cfc116157cbddcfae9798d9de98d08fe0a2c3e97b2 + languageName: node + linkType: hard + +"fd-package-json@npm:^2.0.0": + version: 2.0.0 + resolution: "fd-package-json@npm:2.0.0" + dependencies: + walk-up-path: "npm:^4.0.0" + checksum: 10/e595a1a23f8e208815cdcf26c92218240da00acce80468324408dc4a5cb6c26b6efb5076f0458a02f044562a1e60253731187a627d5416b4961468ddfc0ae426 + languageName: node + linkType: hard + +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10/14ca1c9f0a0e8f4f2e9bf4e8551065a164a09545dae548c12a18d238b72e51e5a7b39bd8e5494b56463a0877672d0a6c1ef62c6fa0677db1b0c847773be939b1 + languageName: node + linkType: hard + +"fecha@npm:^4.2.0": + version: 4.2.3 + resolution: "fecha@npm:4.2.3" + checksum: 10/534ce630c8f63c116292145607fc18c0f06bfa2fd74094357bf65daacc5d3f4f2b285bf8eb112c3bbf98c5caa6d386cced797f44b9b1b33da0c0a81020444826 + languageName: node + linkType: hard + +"fflate@npm:^0.8.2": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 10/2bd26ba6d235d428de793c6a0cd1aaa96a06269ebd4e21b46c8fd1bd136abc631acf27e188d47c3936db090bf3e1ede11d15ce9eae9bffdc4bfe1b9dc66ca9cb + languageName: node + linkType: hard + +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" + dependencies: + flat-cache: "npm:^4.0.0" + checksum: 10/afe55c4de4e0d226a23c1eae62a7219aafb390859122608a89fa4df6addf55c7fd3f1a2da6f5b41e7cdff496e4cf28bbd215d53eab5c817afa96d2b40c81bfb0 + languageName: node + linkType: hard + +"file-type@npm:21.2.0": + version: 21.2.0 + resolution: "file-type@npm:21.2.0" + dependencies: + "@tokenizer/inflate": "npm:^0.4.1" + strtok3: "npm:^10.3.4" + token-types: "npm:^6.1.1" + uint8array-extras: "npm:^1.4.0" + checksum: 10/62262834abe03b5346a18a9a9e7c6dc0b53f07f79ae82157d2bf4ffd4061d643d03897b0e13c64fae32dd6466c8d62b96d2ff62c14584e987c4a240f8eba5425 + languageName: node + linkType: hard + +"file-type@npm:^20.5.0": + version: 20.5.0 + resolution: "file-type@npm:20.5.0" + dependencies: + "@tokenizer/inflate": "npm:^0.2.6" + strtok3: "npm:^10.2.0" + token-types: "npm:^6.0.0" + uint8array-extras: "npm:^1.4.0" + checksum: 10/1cc1ccd7cf76086e10b65cba88c708e0653676fbae900107deeb91c46de011acd1492200bf47e75cddf395de27dbe8584ca042f4cfa4a1efdf933644b7143f1d + languageName: node + linkType: hard + +"filelist@npm:^1.0.4": + version: 1.0.4 + resolution: "filelist@npm:1.0.4" + dependencies: + minimatch: "npm:^5.0.1" + checksum: 10/4b436fa944b1508b95cffdfc8176ae6947b92825483639ef1b9a89b27d82f3f8aa22b21eed471993f92709b431670d4e015b39c087d435a61e1bb04564cf51de + languageName: node + linkType: hard + +"filename-reserved-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "filename-reserved-regex@npm:3.0.0" + checksum: 10/1803e19ce64d7cb88ee5a1bd3ce282470a5c263987269222426d889049fc857e302284fa71937de9582eba7a9f39539557d45e0562f2fa51cade8efc68c65dd9 + languageName: node + linkType: hard + +"filenamify@npm:^6.0.0": + version: 6.0.0 + resolution: "filenamify@npm:6.0.0" + dependencies: + filename-reserved-regex: "npm:^3.0.0" + checksum: 10/5914b64a760d49323d0454efb1f5e33338d3840df447f40556fc68730c4649797451931d60035c66068dacf326f045a912287ce8b63e15a5fba311a961f8f4b1 + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10/a7095cb39e5bc32fada2aa7c7249d3f6b01bd1ce461a61b0adabacccabd9198500c6fb1f68a7c851a657e273fce2233ba869638897f3d7ed2e87a2d89b4436ea + languageName: node + linkType: hard + +"finalhandler@npm:^2.1.0": + version: 2.1.1 + resolution: "finalhandler@npm:2.1.1" + dependencies: + debug: "npm:^4.4.0" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + on-finished: "npm:^2.4.1" + parseurl: "npm:^1.3.3" + statuses: "npm:^2.0.1" + checksum: 10/f4ba75c23408d8f9d393c3e875b9452e84d68c925411a6e67b7efa678b0bed5075ef33def4bb65ed8e0dd37c92a3ea354bcbde07303cd4dc2550e12b95885067 + languageName: node + linkType: hard + +"find-package-json@npm:^1.2.0": + version: 1.2.0 + resolution: "find-package-json@npm:1.2.0" + checksum: 10/437825175752a7f522089e13876d1f78c6dc2768d8b6cd13736dea7abfc0a0482bc07169c27c4a99c82142c2e83aeddf3ef5c111ba6ae7aaa4719d9e6a80c683 + languageName: node + linkType: hard + +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: "npm:^6.0.0" + path-exists: "npm:^4.0.0" + checksum: 10/07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 + languageName: node + linkType: hard + +"find-versions@npm:^5.0.0": + version: 5.1.0 + resolution: "find-versions@npm:5.1.0" + dependencies: + semver-regex: "npm:^4.0.5" + checksum: 10/680bdb0081f631f7bfb6f0f8edcfa0b74ab8cabc82097a4527a37b0d042aabc56685bf459ff27991eab0baddc04eb8e3bba8a2869f5004ecf7cdd2779b6e51de + languageName: node + linkType: hard + +"fixpack@npm:^4.0.0": + version: 4.0.0 + resolution: "fixpack@npm:4.0.0" + dependencies: + alce: "npm:1.2.0" + chalk: "npm:^3.0.0" + detect-indent: "npm:^6.0.0" + detect-newline: "npm:^3.1.0" + extend-object: "npm:^1.0.0" + rc: "npm:^1.2.8" + bin: + fixpack: bin/fixpack + checksum: 10/b893044e0a3dddc791e6afbb659e83f314ffce464afe42453fbf362820a98dd4d833854364e8f2391cfe80c38c5a0e120548c544893fb59ff01c376501bbb847 + languageName: node + linkType: hard + +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.4" + checksum: 10/58ce851d9045fffc7871ce2bd718bc485ad7e777bf748c054904b87c351ff1080c2c11da00788d78738bfb51b71e4d5ea12d13b98eb36e3358851ffe495b62dc + languageName: node + linkType: hard + +"flatted@npm:^3.2.7, flatted@npm:^3.2.9": + version: 3.3.3 + resolution: "flatted@npm:3.3.3" + checksum: 10/8c96c02fbeadcf4e8ffd0fa24983241e27698b0781295622591fc13585e2f226609d95e422bcf2ef044146ffacb6b68b1f20871454eddf75ab3caa6ee5f4a1fe + languageName: node + linkType: hard + +"fn.name@npm:1.x.x": + version: 1.1.0 + resolution: "fn.name@npm:1.1.0" + checksum: 10/000198af190ae02f0138ac5fa4310da733224c628e0230c81e3fff7c4e094af7e0e8bb9f4357cabd21db601759d89f3445da744afbae20623cfa41edf3888397 + languageName: node + linkType: hard + +"follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.15.6": + version: 1.15.11 + resolution: "follow-redirects@npm:1.15.11" + peerDependenciesMeta: + debug: + optional: true + checksum: 10/07372fd74b98c78cf4d417d68d41fdaa0be4dcacafffb9e67b1e3cf090bc4771515e65020651528faab238f10f9b9c0d9707d6c1574a6c0387c5de1042cde9ba + languageName: node + linkType: hard + +"for-each@npm:^0.3.5": + version: 0.3.5 + resolution: "for-each@npm:0.3.5" + dependencies: + is-callable: "npm:^1.2.7" + checksum: 10/330cc2439f85c94f4609de3ee1d32c5693ae15cdd7fe3d112c4fd9efd4ce7143f2c64ef6c2c9e0cfdb0058437f33ef05b5bdae5b98fcc903fb2143fbaf0fea0f + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" + dependencies: + cross-spawn: "npm:^7.0.6" + signal-exit: "npm:^4.0.1" + checksum: 10/427b33f997a98073c0424e5c07169264a62cda806d8d2ded159b5b903fdfc8f0a1457e06b5fc35506497acb3f1e353f025edee796300209ac6231e80edece835 + languageName: node + linkType: hard + +"fork-ts-checker-webpack-plugin@npm:9.1.0": + version: 9.1.0 + resolution: "fork-ts-checker-webpack-plugin@npm:9.1.0" + dependencies: + "@babel/code-frame": "npm:^7.16.7" + chalk: "npm:^4.1.2" + chokidar: "npm:^4.0.1" + cosmiconfig: "npm:^8.2.0" + deepmerge: "npm:^4.2.2" + fs-extra: "npm:^10.0.0" + memfs: "npm:^3.4.1" + minimatch: "npm:^3.0.4" + node-abort-controller: "npm:^3.0.1" + schema-utils: "npm:^3.1.1" + semver: "npm:^7.3.5" + tapable: "npm:^2.2.1" + peerDependencies: + typescript: ">3.6.0" + webpack: ^5.11.0 + checksum: 10/1d24387224f7d49a17f7e44c9150971172f34ae30c4b1f581b8af967e73e8f36a434ed56f78aa45fd8cf0833c73a1b020102cc61070d7dc630b70c21c9770a1b + languageName: node + linkType: hard + +"form-data-encoder@npm:^2.1.2": + version: 2.1.4 + resolution: "form-data-encoder@npm:2.1.4" + checksum: 10/3778e7db3c21457296e6fdbc4200642a6c01e8be9297256e845ee275f9ddaecb5f49bfb0364690ad216898c114ec59bf85f01ec823a70670b8067273415d62f6 + languageName: node + linkType: hard + +"form-data@npm:2.5.1": + version: 2.5.1 + resolution: "form-data@npm:2.5.1" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.6" + mime-types: "npm:^2.1.12" + checksum: 10/2e2e5e927979ba3623f9b4c4bcc939275fae3f2dea9dafc8db3ca656a3d75476605de2c80f0e6f1487987398e056f0b4c738972d6e1edd83392d5686d0952eed + languageName: node + linkType: hard + +"form-data@npm:^4.0.2, form-data@npm:^4.0.4": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd + languageName: node + linkType: hard + +"formatly@npm:^0.3.0": + version: 0.3.0 + resolution: "formatly@npm:0.3.0" + dependencies: + fd-package-json: "npm:^2.0.0" + bin: + formatly: bin/index.mjs + checksum: 10/0e5a9cbb826d93171b00c283e20e6a564a16e7bc3839e695790347a1f23e3536a88d613f5cabd07403d60b7bdffe179987c88b1fc2900a9be49eea01ffbe4244 + languageName: node + linkType: hard + +"forwarded@npm:0.2.0": + version: 0.2.0 + resolution: "forwarded@npm:0.2.0" + checksum: 10/29ba9fd347117144e97cbb8852baae5e8b2acb7d1b591ef85695ed96f5b933b1804a7fac4a15dd09ca7ac7d0cdc104410e8102aae2dd3faa570a797ba07adb81 + languageName: node + linkType: hard + +"fraction.js@npm:^5.2.1": + version: 5.3.4 + resolution: "fraction.js@npm:5.3.4" + checksum: 10/ef2c4bc81b2484065f8f7e4c2498f3fdfe6d233b8e7c7f75e3683ed10698536129b2c2dbd6c3f788ca4a020ec07116dd909a91036a364c98dc802b5003bfc613 + languageName: node + linkType: hard + +"fresh@npm:^2.0.0": + version: 2.0.0 + resolution: "fresh@npm:2.0.0" + checksum: 10/44e1468488363074641991c1340d2a10c5a6f6d7c353d89fd161c49d120c58ebf9890720f7584f509058385836e3ce50ddb60e9f017315a4ba8c6c3461813bfc + languageName: node + linkType: hard + +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 10/18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d + languageName: node + linkType: hard + +"fs-extra@npm:^10.0.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10/05ce2c3b59049bcb7b52001acd000e44b3c4af4ec1f8839f383ef41ec0048e3cfa7fd8a637b1bddfefad319145db89be91f4b7c1db2908205d38bf91e7d1d3b7 + languageName: node + linkType: hard + +"fs-extra@npm:^8.1.0": + version: 8.1.0 + resolution: "fs-extra@npm:8.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^4.0.0" + universalify: "npm:^0.1.0" + checksum: 10/6fb12449f5349be724a138b4a7b45fe6a317d2972054517f5971959c26fbd17c0e145731a11c7324460262baa33e0a799b183ceace98f7a372c95fbb6f20f5de + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f + languageName: node + linkType: hard + +"fs-monkey@npm:^1.0.4": + version: 1.1.0 + resolution: "fs-monkey@npm:1.1.0" + checksum: 10/1c6da5d07f6c91e31fd9bcd68909666e18fa243c7af6697e9d2ded16d4ee87cc9c2b67889b19f98211006c228d1915e1beb0678b4080778fb52539ef3e4eab6c + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 10/e703107c28e362d8d7b910bbcbfd371e640a3bb45ae157a362b5952c0030c0b6d4981140ec319b347bce7adc025dd7813da1ff908a945ac214d64f5402a51b96 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10/4c1ade961ded57cdbfbb5cac5106ec17bc8bccd62e16343c569a0ceeca83b9dfef87550b4dc5cbb89642da412b20c5071f304c8c464b80415446e8e155a038c0 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"fstream@npm:^1.0.12": + version: 1.0.12 + resolution: "fstream@npm:1.0.12" + dependencies: + graceful-fs: "npm:^4.1.2" + inherits: "npm:~2.0.0" + mkdirp: "npm:>=0.5 0" + rimraf: "npm:2" + checksum: 10/eadba4375e952f3f7e9d34d822cfa1592134173033bafef42aa23d5f09bf373e4eb77e097883c0a9136ad7e7d3b49bb14f0e8dfaa489abd5139b5a3c961787b6 + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10/185e20d20f10c8d661d59aac0f3b63b31132d492e1b11fcc2a93cb2c47257ebaee7407c38513efd2b35cafdf972d9beb2ea4593c1e0f3bf8f2744836928d7454 + languageName: node + linkType: hard + +"gaxios@npm:^6.0.0, gaxios@npm:^6.0.3, gaxios@npm:^6.1.1": + version: 6.7.1 + resolution: "gaxios@npm:6.7.1" + dependencies: + extend: "npm:^3.0.2" + https-proxy-agent: "npm:^7.0.1" + is-stream: "npm:^2.0.0" + node-fetch: "npm:^2.6.9" + uuid: "npm:^9.0.1" + checksum: 10/c85599162208884eadee91215ebbfa1faa412551df4044626cb561300e15193726e8f23d63b486533e066dadad130f58ed872a23acab455238d8d48b531a0695 + languageName: node + linkType: hard + +"gcp-metadata@npm:^6.1.0": + version: 6.1.1 + resolution: "gcp-metadata@npm:6.1.1" + dependencies: + gaxios: "npm:^6.1.1" + google-logging-utils: "npm:^0.0.2" + json-bigint: "npm:^1.0.0" + checksum: 10/f6b1a604d5888db261a9a3ca0a494338b5cdbf815efa393aa38051d814387545bbfd9f25874bf8ea36441f2052625add42658e8973648e53f9b90f151b4bad1b + languageName: node + linkType: hard + +"generate-password@npm:^1.7.1": + version: 1.7.1 + resolution: "generate-password@npm:1.7.1" + checksum: 10/e892bfe38ef2f31efb724870514c2615af233b7d45a773e523cf926ffc4233e94cb254f22c83c4c472d322238ad7828898064f4a5d08d5bb5c09717309df0c4e + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10/eb7e7eb896c5433f3d40982b2ccacdb3dd990dd3499f14040e002b5d54572476513be8a2e6f9609f6e41ab29f2c4469307611ddbfc37ff4e46b765c326663805 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10/b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10/bb579dda84caa4a3a41611bdd483dade7f00f246f2a7992eb143c5861155290df3fdb48a8406efa3dfb0b434e2c8fafa4eebd469e409d0439247f85fc3fa2cc1 + languageName: node + linkType: hard + +"get-port@npm:5.1.1": + version: 5.1.1 + resolution: "get-port@npm:5.1.1" + checksum: 10/0162663ffe5c09e748cd79d97b74cd70e5a5c84b760a475ce5767b357fb2a57cb821cee412d646aa8a156ed39b78aab88974eddaa9e5ee926173c036c0713787 + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + +"get-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "get-stream@npm:3.0.0" + checksum: 10/de14fbb3b4548ace9ab6376be852eef9898c491282e29595bc908a1814a126d3961b11cd4b7be5220019fe3b2abb84568da7793ad308fc139925a217063fa159 + languageName: node + linkType: hard + +"get-stream@npm:^5.1.0": + version: 5.2.0 + resolution: "get-stream@npm:5.2.0" + dependencies: + pump: "npm:^3.0.0" + checksum: 10/13a73148dca795e41421013da6e3ebff8ccb7fba4d2f023fd0c6da2c166ec4e789bec9774a73a7b49c08daf2cae552f8a3e914042ac23b5f59dd278cc8f9cbfb + languageName: node + linkType: hard + +"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": + version: 6.0.1 + resolution: "get-stream@npm:6.0.1" + checksum: 10/781266d29725f35c59f1d214aedc92b0ae855800a980800e2923b3fbc4e56b3cb6e462c42e09a1cf1a00c64e056a78fa407cbe06c7c92b7e5cd49b4b85c2a497 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10/32cd106ce8c0d83731966d31517adb766d02c3812de49c30cfe0675c7c0ae6630c11214c54a5ae67aca882cf738d27fd7768f21aa19118b9245950554be07247 + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10/c13ee97978bef4f55106b71e66428eb1512e71a7466ba49025fc2aec59a5bfb0954d5abd58fc5ee6c9b076eef4e1f6d3375c2e964b88466ca390da4419a786a8 + languageName: node + linkType: hard + +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: 10/9009529195a955c40d7b9690794aeff5ba665cc38f1519e111c58bb54366fd0c106bde80acf97ba4e533208eb53422c83b136611a54c5fefb1edd8dc267cb62e + languageName: node + linkType: hard + +"glob@npm:10.3.12": + version: 10.3.12 + resolution: "glob@npm:10.3.12" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^2.3.6" + minimatch: "npm:^9.0.1" + minipass: "npm:^7.0.4" + path-scurry: "npm:^1.10.2" + bin: + glob: dist/esm/bin.mjs + checksum: 10/9e8186abc22dc824b5dd86cefd8e6b5621a72d1be7f68bacc0fd681e8c162ec5546660a6ec0553d6a74757a585e655956c7f8f1a6d24570e8d865c307323d178 + languageName: node + linkType: hard + +"glob@npm:13.0.0, glob@npm:^13.0.0": + version: 13.0.0 + resolution: "glob@npm:13.0.0" + dependencies: + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + path-scurry: "npm:^2.0.0" + checksum: 10/de390721d29ee1c9ea41e40ec2aa0de2cabafa68022e237dc4297665a5e4d650776f2573191984ea1640aba1bf0ea34eddef2d8cbfbfc2ad24b5fb0af41d8846 + languageName: node + linkType: hard + +"glob@npm:^10.3.10, glob@npm:^10.4.2, glob@npm:^10.5.0": + version: 10.5.0 + resolution: "glob@npm:10.5.0" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10/ab3bccfefcc0afaedbd1f480cd0c4a2c0e322eb3f0aa7ceaa31b3f00b825069f17cf0f1fc8b6f256795074b903f37c0ade37ddda6a176aa57f1c2bbfe7240653 + languageName: node + linkType: hard + +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.2.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.1.1" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10/59452a9202c81d4508a43b8af7082ca5c76452b9fcc4a9ab17655822e6ce9b21d4f8fbadabe4fe3faef448294cec249af305e2cd824b7e9aaf689240e5e96a7b + languageName: node + linkType: hard + +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 10/03939c8af95c6df5014b137cac83aa909090c3a3985caef06ee9a5a669790877af8698ab38007e4c0186873adc14c0b13764acc754b16a754c216cc56aa5f021 + languageName: node + linkType: hard + +"google-auth-library@npm:^9.0.0, google-auth-library@npm:^9.7.0": + version: 9.15.1 + resolution: "google-auth-library@npm:9.15.1" + dependencies: + base64-js: "npm:^1.3.0" + ecdsa-sig-formatter: "npm:^1.0.11" + gaxios: "npm:^6.1.1" + gcp-metadata: "npm:^6.1.0" + gtoken: "npm:^7.0.0" + jws: "npm:^4.0.0" + checksum: 10/6b977dd20f4f1ab6b2d2b78650d1e1c79ca84b951720b1064b85ebbb32af469547db7505a6609265e806be11c823bd6e07323b5073a98729b43b29fe34f05717 + languageName: node + linkType: hard + +"google-logging-utils@npm:^0.0.2": + version: 0.0.2 + resolution: "google-logging-utils@npm:0.0.2" + checksum: 10/f8f5ec3087ef4563d12ee1afc603e6b42b4d703c1f10c9f37b3080e6f4a2e9554e0fd9dcdce97ded5a46ead465c706ff2bc791ad2ca478ed8dc62fdc4b06cac6 + languageName: node + linkType: hard + +"googleapis-common@npm:^7.0.0": + version: 7.2.0 + resolution: "googleapis-common@npm:7.2.0" + dependencies: + extend: "npm:^3.0.2" + gaxios: "npm:^6.0.3" + google-auth-library: "npm:^9.7.0" + qs: "npm:^6.7.0" + url-template: "npm:^2.0.8" + uuid: "npm:^9.0.0" + checksum: 10/4b914be6681f2a5a02bd0954a4a5cee1725d8623cb9d0a7c2fd7132de110e8d5707566cba39784e58147be39e74bc5513ad30fdcdaa6edcbb47ecf687003cb6c + languageName: node + linkType: hard + +"googleapis@npm:^149.0.0": + version: 149.0.0 + resolution: "googleapis@npm:149.0.0" + dependencies: + google-auth-library: "npm:^9.0.0" + googleapis-common: "npm:^7.0.0" + checksum: 10/097bc81fe87da09e959f37ac9b1a488c75d1d29169d3100ab1df6382ec7610735351026139daca65486d2eeb1d9c4568ff896a4472cb29039eb8a77754dcde9f + languageName: node + linkType: hard + +"gopd@npm:^1.0.1, gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10/94e296d69f92dc1c0768fcfeecfb3855582ab59a7c75e969d5f96ce50c3d201fd86d5a2857c22565764d5bb8a816c7b1e58f133ec318cd56274da36c5e3fb1a1 + languageName: node + linkType: hard + +"got@npm:^11.8.6": + version: 11.8.6 + resolution: "got@npm:11.8.6" + dependencies: + "@sindresorhus/is": "npm:^4.0.0" + "@szmarczak/http-timer": "npm:^4.0.5" + "@types/cacheable-request": "npm:^6.0.1" + "@types/responselike": "npm:^1.0.0" + cacheable-lookup: "npm:^5.0.3" + cacheable-request: "npm:^7.0.2" + decompress-response: "npm:^6.0.0" + http2-wrapper: "npm:^1.0.0-beta.5.2" + lowercase-keys: "npm:^2.0.0" + p-cancelable: "npm:^2.0.0" + responselike: "npm:^2.0.0" + checksum: 10/a30c74029d81bd5fe50dea1a0c970595d792c568e188ff8be254b5bc11e6158d1b014570772d4a30d0a97723e7dd34e7c8cc1a2f23018f60aece3070a7a5c2a5 + languageName: node + linkType: hard + +"got@npm:^13.0.0": + version: 13.0.0 + resolution: "got@npm:13.0.0" + dependencies: + "@sindresorhus/is": "npm:^5.2.0" + "@szmarczak/http-timer": "npm:^5.0.1" + cacheable-lookup: "npm:^7.0.0" + cacheable-request: "npm:^10.2.8" + decompress-response: "npm:^6.0.0" + form-data-encoder: "npm:^2.1.2" + get-stream: "npm:^6.0.1" + http2-wrapper: "npm:^2.1.10" + lowercase-keys: "npm:^3.0.0" + p-cancelable: "npm:^3.0.0" + responselike: "npm:^3.0.0" + checksum: 10/35ac9fe37daca3d0a4f90305d8e64626268ef5a42584f5bcb42eea3cb9bbeb691cf9041d5ea72133a7295d1291684789a3148ff89a95f3d3ce3d0ebb6fb2f680 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 + languageName: node + linkType: hard + +"gtoken@npm:^7.0.0": + version: 7.1.0 + resolution: "gtoken@npm:7.1.0" + dependencies: + gaxios: "npm:^6.0.0" + jws: "npm:^4.0.0" + checksum: 10/640392261e55c9242137a81a4af8feb053b57061762cedddcbb6a0d62c2314316161808ac2529eea67d06d69fdc56d82361af50f2d840a04a87ea29e124d7382 + languageName: node + linkType: hard + +"handlebars@npm:^4.7.8": + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" + dependencies: + minimist: "npm:^1.2.5" + neo-async: "npm:^2.6.2" + source-map: "npm:^0.6.1" + uglify-js: "npm:^3.1.4" + wordwrap: "npm:^1.0.0" + dependenciesMeta: + uglify-js: + optional: true + bin: + handlebars: bin/handlebars + checksum: 10/bd528f4dd150adf67f3f857118ef0fa43ff79a153b1d943fa0a770f2599e38b25a7a0dbac1a3611a4ec86970fd2325a81310fb788b5c892308c9f8743bd02e11 + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 10/4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 10/261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10/2d8c9ab8cebb572e3362f7d06139a4592105983d4317e68f7adba320fe6ddfc8874581e0971e899e633fd5f72e262830edce36d5a0bc863dad17ad20572484b2 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10/959385c98696ebbca51e7534e0dc723ada325efa3475350951363cce216d27373e0259b63edb599f72eb94d6cde8577b4b2375f080b303947e560f85692834fa + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10/c74c5f5ceee3c8a5b8bc37719840dc3749f5b0306d818974141dda2471a1a2ca6c8e46b9d6ac222c5345df7a901c9b6f350b1e6d62763fec877e26609a401bfe + languageName: node + linkType: hard + +"hash.js@npm:^1.1.7": + version: 1.1.7 + resolution: "hash.js@npm:1.1.7" + dependencies: + inherits: "npm:^2.0.3" + minimalistic-assert: "npm:^1.0.1" + checksum: 10/0c89ee4006606a40f92df5cc3c263342e7fea68110f3e9ef032bd2083650430505db01b6b7926953489517d4027535e4fdc7f970412893d3031c361d3ec8f4b3 + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10/7898a9c1788b2862cf0f9c345a6bec77ba4a0c0983c7f19d610c382343d4f98fa260686b225dfb1f88393a66679d2ec58ee310c1d6868c081eda7918f32cc70a + languageName: node + linkType: hard + +"he@npm:1.2.0, he@npm:^1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 10/d09b2243da4e23f53336e8de3093e5c43d2c39f8d0d18817abfa32ce3e9355391b2edb4bb5edc376aea5d4b0b59d6a0482aab4c52bc02ef95751e4b818e847f1 + languageName: node + linkType: hard + +"heapdump@npm:^0.3.15": + version: 0.3.15 + resolution: "heapdump@npm:0.3.15" + dependencies: + nan: "npm:^2.13.2" + node-gyp: "npm:latest" + checksum: 10/0e042e4bdc5439a8d83bdc405a8a175add563cf135a0ae0347dbc7587b3a224765a4954e454a9ee087bde349bc426f2538adc8cc66270feb2bbd924d7aae3583 + languageName: node + linkType: hard + +"helmet@npm:^8.1.0": + version: 8.1.0 + resolution: "helmet@npm:8.1.0" + checksum: 10/262e678d340bb102158f4e6ba1469a7cda1643329a50863f8fe704d5b60d9ebe6ea1e7793ed51224ae464f197dedf2fe8035df278c042e32ebc95b237fa41ad0 + languageName: node + linkType: hard + +"html-entities@npm:^2.3.6": + version: 2.6.0 + resolution: "html-entities@npm:2.6.0" + checksum: 10/06d4e7a3ba6243bba558af176e56f85e09894b26d911bc1ef7b2b9b3f18b46604360805b32636f080e954778e9a34313d1982479a05a5aa49791afd6a4229346 + languageName: node + linkType: hard + +"html-minifier@npm:^4.0.0": + version: 4.0.0 + resolution: "html-minifier@npm:4.0.0" + dependencies: + camel-case: "npm:^3.0.0" + clean-css: "npm:^4.2.1" + commander: "npm:^2.19.0" + he: "npm:^1.2.0" + param-case: "npm:^2.1.1" + relateurl: "npm:^0.2.7" + uglify-js: "npm:^3.5.1" + bin: + html-minifier: ./cli.js + checksum: 10/a1a49ee78a41eb3232f7aa51be25092d7634548e8996577b2bdab22dc9ac736594d35aab7fdf81fb5a0da11f7bc688f500c297b24fd312d48c0ce8739ed4f06f + languageName: node + linkType: hard + +"html-to-text@npm:9.0.5, html-to-text@npm:^9.0.5": + version: 9.0.5 + resolution: "html-to-text@npm:9.0.5" + dependencies: + "@selderee/plugin-htmlparser2": "npm:^0.11.0" + deepmerge: "npm:^4.3.1" + dom-serializer: "npm:^2.0.0" + htmlparser2: "npm:^8.0.2" + selderee: "npm:^0.11.0" + checksum: 10/e5991f9946dd0e5c91c4ed863c71a4feaef3d5ce85cd8684fb0f2fc175b1ccee323bb97a1773b6bebc47ac7963dbbfd1fc81b024adff705ae7c0e08992d1dba5 + languageName: node + linkType: hard + +"htmlparser2@npm:^5.0.0": + version: 5.0.1 + resolution: "htmlparser2@npm:5.0.1" + dependencies: + domelementtype: "npm:^2.0.1" + domhandler: "npm:^3.3.0" + domutils: "npm:^2.4.2" + entities: "npm:^2.0.0" + checksum: 10/789904b9a9f3d2f2d6b63e32aeb8b4cc03d6f5f751c5db86fbdf32c83570f5a5fc827fe664701ec4c5f940c49cf73ceffc93658ce116225f949e29a67dcc8629 + languageName: node + linkType: hard + +"htmlparser2@npm:^8.0.1, htmlparser2@npm:^8.0.2": + version: 8.0.2 + resolution: "htmlparser2@npm:8.0.2" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + entities: "npm:^4.4.0" + checksum: 10/ea5512956eee06f5835add68b4291d313c745e8407efa63848f4b8a90a2dee45f498a698bca8614e436f1ee0cfdd609938b71d67c693794545982b76e53e6f11 + languageName: node + linkType: hard + +"htmlparser2@npm:^9.1.0": + version: 9.1.0 + resolution: "htmlparser2@npm:9.1.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.1.0" + entities: "npm:^4.5.0" + checksum: 10/6352fa2a5495781fa9a02c9049908334cd068ff36d753870d30cd13b841e99c19646717567a2f9e9c44075bbe43d364e102f9d013a731ce962226d63746b794f + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.1": + version: 4.2.0 + resolution: "http-cache-semantics@npm:4.2.0" + checksum: 10/4efd2dfcfeea9d5e88c84af450b9980be8a43c2c8179508b1c57c7b4421c855f3e8efe92fa53e0b3f4a43c85824ada930eabbc306d1b3beab750b6dcc5187693 + languageName: node + linkType: hard + +"http-errors@npm:^2.0.0, http-errors@npm:^2.0.1, http-errors@npm:~2.0.1": + version: 2.0.1 + resolution: "http-errors@npm:2.0.1" + dependencies: + depd: "npm:~2.0.0" + inherits: "npm:~2.0.4" + setprototypeof: "npm:~1.2.0" + statuses: "npm:~2.0.2" + toidentifier: "npm:~1.0.1" + checksum: 10/9fe31bc0edf36566c87048aed1d3d0cbe03552564adc3541626a0613f542d753fbcb13bdfcec0a3a530dbe1714bb566c89d46244616b66bddd26ac413b06a207 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848 + languageName: node + linkType: hard + +"http2-wrapper@npm:^1.0.0-beta.5.2": + version: 1.0.3 + resolution: "http2-wrapper@npm:1.0.3" + dependencies: + quick-lru: "npm:^5.1.1" + resolve-alpn: "npm:^1.0.0" + checksum: 10/8097ee2699440c2e64bda52124990cc5b0fb347401c7797b1a0c1efd5a0f79a4ebaa68e8a6ac3e2dde5f09460c1602764da6da2412bad628ed0a3b0ae35e72d4 + languageName: node + linkType: hard + +"http2-wrapper@npm:^2.1.10": + version: 2.2.1 + resolution: "http2-wrapper@npm:2.2.1" + dependencies: + quick-lru: "npm:^5.1.1" + resolve-alpn: "npm:^1.2.0" + checksum: 10/e7a5ac6548318e83fc0399cd832cdff6bbf902b165d211cad47a56ee732922e0aa1107246dd884b12532a1c4649d27c4d44f2480911c65202e93c90bde8fa29d + languageName: node + linkType: hard + +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10/f0dce7bdcac5e8eaa0be3c7368bb8836ed010fb5b6349ffb412b172a203efe8f807d9a6681319105ea1b6901e1972c7b5ea899672a7b9aad58309f766dcbe0df + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.4": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13 + languageName: node + linkType: hard + +"human-signals@npm:^2.1.0": + version: 2.1.0 + resolution: "human-signals@npm:2.1.0" + checksum: 10/df59be9e0af479036798a881d1f136c4a29e0b518d4abb863afbd11bf30efa3eeb1d0425fc65942dcc05ab3bf40205ea436b0ff389f2cd20b75b8643d539bf86 + languageName: node + linkType: hard + +"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10/24e3292dd3dadaa81d065c6f8c41b274a47098150d444b96e5f53b4638a9a71482921ea6a91a1f59bb71d9796de25e04afd05919fa64c360347ba65d3766f10f + languageName: node + linkType: hard + +"iconv-lite@npm:0.7.0": + version: 0.7.0 + resolution: "iconv-lite@npm:0.7.0" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10/5bfc897fedfb7e29991ae5ef1c061ed4f864005f8c6d61ef34aba6a3885c04bd207b278c0642b041383aeac2d11645b4319d0ca7b863b0be4be0cde1c9238ca7 + languageName: node + linkType: hard + +"iconv-lite@npm:0.7.1": + version: 0.7.1 + resolution: "iconv-lite@npm:0.7.1" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10/bf9bfa10132b591d7b43cc2cc6700cd72d848b7943c3f38db05527d04c224eb44d1d92101f76d25eac7fabc67cb918320b93808b0be47e45cf7257df0a5ec54c + languageName: node + linkType: hard + +"iconv-lite@npm:^0.7.0, iconv-lite@npm:~0.7.0": + version: 0.7.2 + resolution: "iconv-lite@npm:0.7.2" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10/24c937b532f868e938386b62410b303b7c767ce3d08dc2829cbe59464d5a26ef86ae5ad1af6b34eec43ddfea39e7d101638644b0178d67262fa87015d59f983a + languageName: node + linkType: hard + +"iconv-lite@npm:~0.4.13": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3" + checksum: 10/6d3a2dac6e5d1fb126d25645c25c3a1209f70cceecc68b8ef51ae0da3cdc078c151fade7524a30b12a3094926336831fca09c666ef55b37e2c69638b5d6bd2e3 + languageName: node + linkType: hard + +"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10/d9f2557a59036f16c282aaeb107832dc957a93d73397d89bbad4eb1130560560eb695060145e8e6b3b498b15ab95510226649a0b8f52ae06583575419fe10fc4 + languageName: node + linkType: hard + +"ignore@npm:^5.2.0": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10/cceb6a457000f8f6a50e1196429750d782afce5680dd878aa4221bd79972d68b3a55b4b1458fc682be978f4d3c6a249046aa0880637367216444ab7b014cfc98 + languageName: node + linkType: hard + +"ignore@npm:^7.0.5": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10/f134b96a4de0af419196f52c529d5c6120c4456ff8a6b5a14ceaaa399f883e15d58d2ce651c9b69b9388491d4669dda47285d307e827de9304a53a1824801bc6 + languageName: node + linkType: hard + +"imap-simple@npm:^5.1.0": + version: 5.1.0 + resolution: "imap-simple@npm:5.1.0" + dependencies: + iconv-lite: "npm:~0.4.13" + imap: "npm:^0.8.18" + nodeify: "npm:^1.0.0" + quoted-printable: "npm:^1.0.0" + utf8: "npm:^2.1.1" + uuencode: "npm:0.0.4" + checksum: 10/4f540427034cb5d8e635bb4e7583ed38945ae38eebc77fc2a5e29d670f2621491500d690cb7636fb20e62b3e879b37024f10d4368daaf24f668d4cdd534d41cf + languageName: node + linkType: hard + +"imap@npm:^0.8.18": + version: 0.8.19 + resolution: "imap@npm:0.8.19" + dependencies: + readable-stream: "npm:1.1.x" + utf7: "npm:>=1.0.2" + checksum: 10/d1531c7d3e1c896e048e2f4b22871b66cf93f029cae37c9d61558e4ba8ee34b9c657040eb4cad053f752e6ad3584ceab1bbc94999d4d9aa9e79e3c0bc211547a + languageName: node + linkType: hard + +"imapflow@npm:^1.0.187": + version: 1.2.6 + resolution: "imapflow@npm:1.2.6" + dependencies: + "@zone-eu/mailsplit": "npm:5.4.8" + encoding-japanese: "npm:2.2.0" + iconv-lite: "npm:0.7.1" + libbase64: "npm:1.3.0" + libmime: "npm:5.3.7" + libqp: "npm:2.1.1" + nodemailer: "npm:7.0.12" + pino: "npm:10.1.0" + socks: "npm:2.8.7" + checksum: 10/2d2157b04c2b80ee7f0ea4286e79f0af0e987c69680bf3b41c96fc37185281e7192e5f2e14475c45283f964e548d6a3e97b1dda22d35573eb331bfed0dd7e4e1 + languageName: node + linkType: hard + +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: 10/f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10/a06b19461b4879cc654d46f8a6244eb55eb053437afd4cbb6613cad6be203811849ed3e4ea038783092879487299fda24af932b86bdfff67c9055ba3612b8c87 + languageName: node + linkType: hard + +"import-in-the-middle@npm:^1.13.0": + version: 1.15.0 + resolution: "import-in-the-middle@npm:1.15.0" + dependencies: + acorn: "npm:^8.14.0" + acorn-import-attributes: "npm:^1.9.5" + cjs-module-lexer: "npm:^1.2.2" + module-details-from-path: "npm:^1.0.3" + checksum: 10/a1ff65ea557ffe67e63dd67b411255fedd8622b324ff22ba99f4436b8fcf74da0333e62b4e8142f447e5db64a42ec9e65f926d50fa55e89c4e4d64626d8cf5f8 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8 + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: "npm:^1.3.0" + wrappy: "npm:1" + checksum: 10/d2ebd65441a38c8336c223d1b80b921b9fa737e37ea466fd7e253cb000c64ae1f17fa59e68130ef5bda92cfd8d36b83d37dab0eb0a4558bcfec8e8cdfd2dcb67 + languageName: node + linkType: hard + +"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.0, inherits@npm:~2.0.1, inherits@npm:~2.0.3, inherits@npm:~2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 + languageName: node + linkType: hard + +"ini@npm:^1.3.4, ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10/314ae176e8d4deb3def56106da8002b462221c174ddb7ce0c49ee72c8cd1f9044f7b10cc555a7d8850982c3b9ca96fc212122749f5234bc2b6fb05fb942ed566 + languageName: node + linkType: hard + +"inspect-with-kind@npm:^1.0.5": + version: 1.0.5 + resolution: "inspect-with-kind@npm:1.0.5" + dependencies: + kind-of: "npm:^6.0.2" + checksum: 10/2124548720116dc86f0ce1601e7a7e87ba146b934c4bd324d7ed2e93860c8a2e992c42617e71a33da88d49458e96f330cfcafdd4d0c2bf95484ff16e61abf31c + languageName: node + linkType: hard + +"ioredis@npm:^5.6.1": + version: 5.9.1 + resolution: "ioredis@npm:5.9.1" + dependencies: + "@ioredis/commands": "npm:1.5.0" + cluster-key-slot: "npm:^1.1.0" + debug: "npm:^4.3.4" + denque: "npm:^2.1.0" + lodash.defaults: "npm:^4.2.0" + lodash.isarguments: "npm:^3.1.0" + redis-errors: "npm:^1.2.0" + redis-parser: "npm:^3.0.0" + standard-as-callback: "npm:^2.1.0" + checksum: 10/ea2853b6b342dbc279cc2828ae52d851271616560c1c242f23dba06313478a3f4e8582d198ff4568bc3934b3b6f4f52e1db95ea4f82f98374c9dfb5061291831 + languageName: node + linkType: hard + +"ip-address@npm:10.0.1": + version: 10.0.1 + resolution: "ip-address@npm:10.0.1" + checksum: 10/09731acda32cd8e14c46830c137e7e5940f47b36d63ffb87c737331270287d631cf25aa95570907a67d3f919fdb25f4470c404eda21e62f22e0a55927f4dd0fb + languageName: node + linkType: hard + +"ip-address@npm:^10.0.1": + version: 10.1.0 + resolution: "ip-address@npm:10.1.0" + checksum: 10/a6979629d1ad9c1fb424bc25182203fad739b40225aebc55ec6243bbff5035faf7b9ed6efab3a097de6e713acbbfde944baacfa73e11852bb43989c45a68d79e + languageName: node + linkType: hard + +"ipaddr.js@npm:1.9.1": + version: 1.9.1 + resolution: "ipaddr.js@npm:1.9.1" + checksum: 10/864d0cced0c0832700e9621913a6429ccdc67f37c1bd78fb8c6789fff35c9d167cb329134acad2290497a53336813ab4798d2794fd675d5eb33b5fdf0982b9ca + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10/73ced84fa35e59e2c57da2d01e12cd01479f381d7f122ce41dcbb713f09dbfc651315832cd2bf8accba7681a69e4d6f1e03941d94dd10040d415086360e7005e + languageName: node + linkType: hard + +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: "npm:^2.0.0" + checksum: 10/078e51b4f956c2c5fd2b26bb2672c3ccf7e1faff38e0ebdba45612265f4e3d9fc3127a1fa8370bbf09eab61339203c3d3b7af5662cbf8be4030f8fac37745b0e + languageName: node + linkType: hard + +"is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10/48a9297fb92c99e9df48706241a189da362bff3003354aea4048bd5f7b2eb0d823cd16d0a383cece3d76166ba16d85d9659165ac6fcce1ac12e6c649d66dbdb9 + languageName: node + linkType: hard + +"is-core-module@npm:^2.16.1": + version: 2.16.1 + resolution: "is-core-module@npm:2.16.1" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10/452b2c2fb7f889cbbf7e54609ef92cf6c24637c568acc7e63d166812a0fb365ae8a504c333a29add8bdb1686704068caa7f4e4b639b650dde4f00a038b8941fb + languageName: node + linkType: hard + +"is-docker@npm:^2.0.0": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 10/3fef7ddbf0be25958e8991ad941901bf5922ab2753c46980b60b05c1bf9c9c2402d35e6dc32e4380b980ef5e1970a5d9d5e5aa2e02d77727c3b6b5e918474c56 + languageName: node + linkType: hard + +"is-electron@npm:^2.2.0": + version: 2.2.2 + resolution: "is-electron@npm:2.2.2" + checksum: 10/de5aa8bd8d72c96675b8d0f93fab4cc21f62be5440f65bc05c61338ca27bd851a64200f31f1bf9facbaa01b3dbfed7997b2186741d84b93b63e0aff1db6a9494 + languageName: node + linkType: hard + +"is-expression@npm:^4.0.0": + version: 4.0.0 + resolution: "is-expression@npm:4.0.0" + dependencies: + acorn: "npm:^7.1.1" + object-assign: "npm:^4.1.1" + checksum: 10/0f01d0ff53fbbec36abae8fbb7ef056c6d024f7128646856a3e6c500b205788d3e0f337025e72df979d7d7cf4674a00370633d7f8974c668b2d3fdb7e8a83bdb + languageName: node + linkType: hard + +"is-extendable@npm:^0.1.0": + version: 0.1.1 + resolution: "is-extendable@npm:0.1.1" + checksum: 10/3875571d20a7563772ecc7a5f36cb03167e9be31ad259041b4a8f73f33f885441f778cee1f1fe0085eb4bc71679b9d8c923690003a36a6a5fdf8023e6e3f0672 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10/df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10/44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10/3ed74f2b0cdf4f401f38edb0442ddfde3092d79d7d35c9919c86641efdbcbb32e45aa3c0f70ce5eecc946896cd5a0f26e4188b9f2b881876f7cb6c505b82da11 + languageName: node + linkType: hard + +"is-interactive@npm:^1.0.0": + version: 1.0.0 + resolution: "is-interactive@npm:1.0.0" + checksum: 10/824808776e2d468b2916cdd6c16acacebce060d844c35ca6d82267da692e92c3a16fdba624c50b54a63f38bdc4016055b6f443ce57d7147240de4f8cdabaf6f9 + languageName: node + linkType: hard + +"is-invalid-path@npm:^1.0.2": + version: 1.0.2 + resolution: "is-invalid-path@npm:1.0.2" + checksum: 10/8776ef093ed57b6ca618ce5f4eaae8da9f4c96d3c14def25e1fa135ebc9b56ad86ba67656d3282a22a26b299d2d3b83eb4edcc0bf85db7487de66225da570043 + languageName: node + linkType: hard + +"is-network-error@npm:^1.0.0": + version: 1.3.0 + resolution: "is-network-error@npm:1.3.0" + checksum: 10/56dc0b8ed9c0bb72202058f172ad0c3121cf68772e8cbba343d3775f6e2ec7877d423cbcea45f4cedcd345de8693de1b52dfe0c6fc15d652c4aa98c2abf0185a + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10/6a6c3383f68afa1e05b286af866017c78f1226d43ac8cb064e115ff9ed85eb33f5c4f7216c96a71e4dfea289ef52c5da3aef5bbfade8ffe47a0465d70c0c8e86 + languageName: node + linkType: hard + +"is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0": + version: 1.1.0 + resolution: "is-plain-obj@npm:1.1.0" + checksum: 10/0ee04807797aad50859652a7467481816cbb57e5cc97d813a7dcd8915da8195dc68c436010bf39d195226cde6a2d352f4b815f16f26b7bf486a5754290629931 + languageName: node + linkType: hard + +"is-promise@npm:^2.0.0": + version: 2.2.2 + resolution: "is-promise@npm:2.2.2" + checksum: 10/18bf7d1c59953e0ad82a1ed963fb3dc0d135c8f299a14f89a17af312fc918373136e56028e8831700e1933519630cc2fd4179a777030330fde20d34e96f40c78 + languageName: node + linkType: hard + +"is-promise@npm:^4.0.0": + version: 4.0.0 + resolution: "is-promise@npm:4.0.0" + checksum: 10/0b46517ad47b00b6358fd6553c83ec1f6ba9acd7ffb3d30a0bf519c5c69e7147c132430452351b8a9fc198f8dd6c4f76f8e6f5a7f100f8c77d57d9e0f4261a8a + languageName: node + linkType: hard + +"is-promise@npm:~1, is-promise@npm:~1.0.0": + version: 1.0.1 + resolution: "is-promise@npm:1.0.1" + checksum: 10/75e6fac7e60e7fa979bf7a53cb7d42f3fd0991795cad6e195196fded7acbc7609e22230435a435b0924037030bdc32b0bc97f593ff2a362a69ddde1bc1fb08ef + languageName: node + linkType: hard + +"is-regex@npm:^1.0.3": + version: 1.2.1 + resolution: "is-regex@npm:1.2.1" + dependencies: + call-bound: "npm:^1.0.2" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10/c42b7efc5868a5c9a4d8e6d3e9816e8815c611b09535c00fead18a1138455c5cb5e1887f0023a467ad3f9c419d62ba4dc3d9ba8bafe55053914d6d6454a945d2 + languageName: node + linkType: hard + +"is-stream@npm:^1.1.0": + version: 1.1.0 + resolution: "is-stream@npm:1.1.0" + checksum: 10/351aa77c543323c4e111204482808cfad68d2e940515949e31ccd0b010fc13d5fba4b9c230e4887fd24284713040f43e542332fbf172f6b9944b7d62e389c0ec + languageName: node + linkType: hard + +"is-stream@npm:^2.0.0, is-stream@npm:^2.0.1": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10/b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66 + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.14": + version: 1.1.15 + resolution: "is-typed-array@npm:1.1.15" + dependencies: + which-typed-array: "npm:^1.1.16" + checksum: 10/e8cf60b9ea85667097a6ad68c209c9722cfe8c8edf04d6218366469e51944c5cc25bae45ffb845c23f811d262e4314d3b0168748eb16711aa34d12724cdf0735 + languageName: node + linkType: hard + +"is-unicode-supported@npm:^0.1.0": + version: 0.1.0 + resolution: "is-unicode-supported@npm:0.1.0" + checksum: 10/a2aab86ee7712f5c2f999180daaba5f361bdad1efadc9610ff5b8ab5495b86e4f627839d085c6530363c6d6d4ecbde340fb8e54bdb83da4ba8e0865ed5513c52 + languageName: node + linkType: hard + +"is-wsl@npm:^2.1.1": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: "npm:^2.0.0" + checksum: 10/20849846ae414997d290b75e16868e5261e86ff5047f104027026fd61d8b5a9b0b3ade16239f35e1a067b3c7cc02f70183cb661010ed16f4b6c7c93dad1b19d8 + languageName: node + linkType: hard + +"isarray@npm:0.0.1": + version: 0.0.1 + resolution: "isarray@npm:0.0.1" + checksum: 10/49191f1425681df4a18c2f0f93db3adb85573bcdd6a4482539d98eac9e705d8961317b01175627e860516a2fc45f8f9302db26e5a380a97a520e272e2a40a8d4 + languageName: node + linkType: hard + +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: 10/1d8bc7911e13bb9f105b1b3e0b396c787a9e63046af0b8fe0ab1414488ab06b2b099b87a2d8a9e31d21c9a6fad773c7fc8b257c4880f2d957274479d28ca3414 + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10/f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10/7c9f715c03aff08f35e98b1fadae1b9267b38f0615d501824f9743f3aab99ef10e303ce7db3f186763a0b70a19de5791ebfc854ff884d5a8c4d92211f642ec92 + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10/7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + +"iterare@npm:1.2.1": + version: 1.2.1 + resolution: "iterare@npm:1.2.1" + checksum: 10/ee8322dd9d92e86d8653c899df501c58c5b8e90d6767cf2af0b6d6dc5a4b9b7ed8bce936976f4f4c3a55be110a300c8a7d71967d03f72e104e8db66befcfd874 + languageName: node + linkType: hard + +"jackspeak@npm:^2.3.6": + version: 2.3.6 + resolution: "jackspeak@npm:2.3.6" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10/6e6490d676af8c94a7b5b29b8fd5629f21346911ebe2e32931c2a54210134408171c24cee1a109df2ec19894ad04a429402a8438cbf5cc2794585d35428ace76 + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10/96f8786eaab98e4bf5b2a5d6d9588ea46c4d06bbc4f2eb861fdd7b6b182b16f71d8a70e79820f335d52653b16d4843b29dd9cdcf38ae80406756db9199497cf3 + languageName: node + linkType: hard + +"jake@npm:^10.8.5": + version: 10.9.4 + resolution: "jake@npm:10.9.4" + dependencies: + async: "npm:^3.2.6" + filelist: "npm:^1.0.4" + picocolors: "npm:^1.1.1" + bin: + jake: bin/cli.js + checksum: 10/97e48f73f5e315a3b6e1a48b4bcc0cdf2c2cf82100ec9e76a032fd5d614dcd32c4315572cfcb66e9f9bdecca3900aaa61fe72b781a74b06aefd3ec4c1c917f0b + languageName: node + linkType: hard + +"javascript-natural-sort@npm:^0.7.1": + version: 0.7.1 + resolution: "javascript-natural-sort@npm:0.7.1" + checksum: 10/7bf6eab67871865d347f09a95aa770f9206c1ab0226bcda6fdd9edec340bf41111a7f82abac30556aa16a21cfa3b2b1ca4a362c8b73dd5ce15220e5d31f49d79 + languageName: node + linkType: hard + +"jest-worker@npm:^27.4.5": + version: 27.5.1 + resolution: "jest-worker@npm:27.5.1" + dependencies: + "@types/node": "npm:*" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.0.0" + checksum: 10/06c6e2a84591d9ede704d5022fc13791e8876e83397c89d481b0063332abbb64c0f01ef4ca7de520b35c7a1058556078d6bdc3631376f4e9ffb42316c1a8488e + languageName: node + linkType: hard + +"jiti@npm:^2.6.0": + version: 2.6.1 + resolution: "jiti@npm:2.6.1" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10/8cd72c5fd03a0502564c3f46c49761090f6dadead21fa191b73535724f095ad86c2fa89ee6fe4bc3515337e8d406cc8fb2d37b73fa0c99a34584bac35cd4a4de + languageName: node + linkType: hard + +"js-beautify@npm:^1.6.14": + version: 1.15.4 + resolution: "js-beautify@npm:1.15.4" + dependencies: + config-chain: "npm:^1.1.13" + editorconfig: "npm:^1.0.4" + glob: "npm:^10.4.2" + js-cookie: "npm:^3.0.5" + nopt: "npm:^7.2.1" + bin: + css-beautify: js/bin/css-beautify.js + html-beautify: js/bin/html-beautify.js + js-beautify: js/bin/js-beautify.js + checksum: 10/89f874f994a409868c74d23bdf3869281a25804dd4f77c4eac170cdee671dfb3248370c3b686ea03bb9a7cc7141769c4f450ad85e9158fbed3d7d78c330ae9a1 + languageName: node + linkType: hard + +"js-cookie@npm:^3.0.5": + version: 3.0.5 + resolution: "js-cookie@npm:3.0.5" + checksum: 10/366494b1630b9fb8abaef3659748db5dfd52c58c6fc3459b9f0a03b492593bc1b01c6dfcc066b46f6413c28edb3a00cc68fb61ea8cdf6991bedf1f100f8a389d + languageName: node + linkType: hard + +"js-stringify@npm:^1.0.2": + version: 1.0.2 + resolution: "js-stringify@npm:1.0.2" + checksum: 10/f9701d9e535d3ac0f62bbf2624b76c5d0af5b889187232817ae284a41ba21fd7a8b464c2dce3815d8cf52c8bea3480be6b368cfc2c67da799cad458058e8bbf5 + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10/af37d0d913fb56aec6dc0074c163cc71cd23c0b8aad5c2350747b6721d37ba118af35abdd8b33c47ec2800de07dedb16a527ca9c530ee004093e04958bd0cbf2 + languageName: node + linkType: hard + +"js-yaml@npm:4.1.1, js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10/a52d0519f0f4ef5b4adc1cde466cb54c50d56e2b4a983b9d5c9c0f2f99462047007a6274d7e95617a21d3c91fde3ee6115536ed70991cd645ba8521058b78f77 + languageName: node + linkType: hard + +"json-bigint@npm:^1.0.0": + version: 1.0.0 + resolution: "json-bigint@npm:1.0.0" + dependencies: + bignumber.js: "npm:^9.0.0" + checksum: 10/cd3973b88e5706f8f89d2a9c9431f206ef385bd5c584db1b258891a5e6642507c32316b82745239088c697f5ddfe967351e1731f5789ba7855aed56ad5f70e1f + languageName: node + linkType: hard + +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 10/82876154521b7b68ba71c4f969b91572d1beabadd87bd3a6b236f85fbc7dc4695089191ed60bb59f9340993c51b33d479f45b6ba9f3548beb519705281c32c3c + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 10/5f3a99009ed5f2a5a67d06e2f298cc97bc86d462034173308156f15b43a6e850be8511dc204b9b94566305da2947f7d90289657237d210351a39059ff9d666cf + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 10/7486074d3ba247769fda17d5181b345c9fb7d12e0da98b22d1d71a5db9698d8b4bd900a3ec1a4ffdd60846fc2556274a5c894d0c48795f14cb03aeae7b55260b + languageName: node + linkType: hard + +"json-schema-traverse@npm:^1.0.0": + version: 1.0.0 + resolution: "json-schema-traverse@npm:1.0.0" + checksum: 10/02f2f466cdb0362558b2f1fd5e15cce82ef55d60cd7f8fa828cf35ba74330f8d767fcae5c5c2adb7851fa811766c694b9405810879bc4e1ddd78a7c0e03658ad + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: 10/12786c2e2f22c27439e6db0532ba321f1d0617c27ad8cb1c352a0e9249a50182fd1ba8b52a18899291604b0c32eafa8afd09e51203f19109a0537f68db2b652d + languageName: node + linkType: hard + +"json-stringify-safe@npm:^5.0.0": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 10/59169a081e4eeb6f9559ae1f938f656191c000e0512aa6df9f3c8b2437a4ab1823819c6b9fd1818a4e39593ccfd72e9a051fdd3e2d1e340ed913679e888ded8c + languageName: node + linkType: hard + +"json5@npm:^2.2.2": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10/1db67b853ff0de3534085d630691d3247de53a2ed1390ba0ddff681ea43e9b3e30ecbdb65c5e9aab49435e44059c23dbd6fee8ee619419ba37465bb0dd7135da + languageName: node + linkType: hard + +"jsonc-parser@npm:3.3.1": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10/9b0dc391f20b47378f843ef1e877e73ec652a5bdc3c5fa1f36af0f119a55091d147a86c1ee86a232296f55c929bba174538c2bf0312610e0817a22de131cc3f4 + languageName: node + linkType: hard + +"jsonfile@npm:^4.0.0": + version: 4.0.0 + resolution: "jsonfile@npm:4.0.0" + dependencies: + graceful-fs: "npm:^4.1.6" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10/17796f0ab1be8479827d3683433f97ebe0a1c6932c3360fa40348eac36904d69269aab26f8b16da311882d94b42e9208e8b28e490bf926364f3ac9bff134c226 + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.2.0 + resolution: "jsonfile@npm:6.2.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10/513aac94a6eff070767cafc8eb4424b35d523eec0fcd8019fe5b975f4de5b10a54640c8d5961491ddd8e6f562588cf62435c5ddaf83aaf0986cd2ee789e0d7b9 + languageName: node + linkType: hard + +"jsonschema@npm:^1.4.1": + version: 1.5.0 + resolution: "jsonschema@npm:1.5.0" + checksum: 10/46bf49b388ba922073bcb3c8d5e90af9d29fc8303dc866fd440182c88d6b4fd2807679fd39cdefb4113156d104ea47da9c0ff4bbcb0032c9fa29461cb1a92182 + languageName: node + linkType: hard + +"jsonwebtoken@npm:8.5.1": + version: 8.5.1 + resolution: "jsonwebtoken@npm:8.5.1" + dependencies: + jws: "npm:^3.2.2" + lodash.includes: "npm:^4.3.0" + lodash.isboolean: "npm:^3.0.3" + lodash.isinteger: "npm:^4.0.4" + lodash.isnumber: "npm:^3.0.3" + lodash.isplainobject: "npm:^4.0.6" + lodash.isstring: "npm:^4.0.1" + lodash.once: "npm:^4.0.0" + ms: "npm:^2.1.1" + semver: "npm:^5.6.0" + checksum: 10/a7b52ea570f70bea183ceca970c003f223d9d3425d72498002e9775485c7584bfa3751d1c7291dbb59738074cba288effe73591b87bec5d467622ab3a156fdb6 + languageName: node + linkType: hard + +"jsonwebtoken@npm:9.0.2": + version: 9.0.2 + resolution: "jsonwebtoken@npm:9.0.2" + dependencies: + jws: "npm:^3.2.2" + lodash.includes: "npm:^4.3.0" + lodash.isboolean: "npm:^3.0.3" + lodash.isinteger: "npm:^4.0.4" + lodash.isnumber: "npm:^3.0.3" + lodash.isplainobject: "npm:^4.0.6" + lodash.isstring: "npm:^4.0.1" + lodash.once: "npm:^4.0.0" + ms: "npm:^2.1.1" + semver: "npm:^7.5.4" + checksum: 10/6e9b6d879cec2b27f2f3a88a0c0973edc7ba956a5d9356b2626c4fddfda969e34a3832deaf79c3e1c6c9a525bc2c4f2c2447fa477f8ac660f0017c31a59ae96b + languageName: node + linkType: hard + +"jsonwebtoken@npm:9.0.3, jsonwebtoken@npm:^9.0.2": + version: 9.0.3 + resolution: "jsonwebtoken@npm:9.0.3" + dependencies: + jws: "npm:^4.0.1" + lodash.includes: "npm:^4.3.0" + lodash.isboolean: "npm:^3.0.3" + lodash.isinteger: "npm:^4.0.4" + lodash.isnumber: "npm:^3.0.3" + lodash.isplainobject: "npm:^4.0.6" + lodash.isstring: "npm:^4.0.1" + lodash.once: "npm:^4.0.0" + ms: "npm:^2.1.1" + semver: "npm:^7.5.4" + checksum: 10/a67a276db41fbfb458ebdc4938d5d7b01d4743e16bda0f25ac01996fe5b5819d66656153f6cfce19b4680b79ae9f9ca185965defc22e77e0abddf443573238d6 + languageName: node + linkType: hard + +"jstransformer@npm:1.0.0": + version: 1.0.0 + resolution: "jstransformer@npm:1.0.0" + dependencies: + is-promise: "npm:^2.0.0" + promise: "npm:^7.0.1" + checksum: 10/7bca6e2e2fb4b6e65e567965e0370488699eb05cbbf27e6cb2ee2a89912d9c238aceb5c63216d834c50a000ed991b2bbd703b5432351aa2a15ee979b5180e652 + languageName: node + linkType: hard + +"jszip@npm:^3.10.1": + version: 3.10.1 + resolution: "jszip@npm:3.10.1" + dependencies: + lie: "npm:~3.3.0" + pako: "npm:~1.0.2" + readable-stream: "npm:~2.3.6" + setimmediate: "npm:^1.0.5" + checksum: 10/bfbfbb9b0a27121330ac46ab9cdb3b4812433faa9ba4a54742c87ca441e31a6194ff70ae12acefa5fe25406c432290e68003900541d948a169b23d30c34dd984 + languageName: node + linkType: hard + +"juice@npm:^10.0.0": + version: 10.0.1 + resolution: "juice@npm:10.0.1" + dependencies: + cheerio: "npm:1.0.0-rc.12" + commander: "npm:^6.1.0" + mensch: "npm:^0.3.4" + slick: "npm:^1.12.2" + web-resource-inliner: "npm:^6.0.1" + bin: + juice: bin/juice + checksum: 10/e881bc266fdebbb527e18bd13dbdd329e5b8f09db068a4c57d6c53881dff8debc5f0445f21054ce033999aaff5204485e3315cf9770802be979a5048eb752976 + languageName: node + linkType: hard + +"jwa@npm:^1.4.2": + version: 1.4.2 + resolution: "jwa@npm:1.4.2" + dependencies: + buffer-equal-constant-time: "npm:^1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: 10/a46c9ddbcc226d9e85e13ef96328c7d331abddd66b5a55ec44bcf4350464a6125385ac9c1e64faa0fae8d586d90a14d6b5e96c73f0388970a3918d5252efb0f3 + languageName: node + linkType: hard + +"jwa@npm:^2.0.1": + version: 2.0.1 + resolution: "jwa@npm:2.0.1" + dependencies: + buffer-equal-constant-time: "npm:^1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: 10/b04312a1de85f912b96aa3a7211717b8336945fab5b4f7cbc7800f4c80934060c0a3111576fad8d76e41ad62887d6da4b21fd4c47e45c174197f8be7dc0c1694 + languageName: node + linkType: hard + +"jws@npm:^3.2.2": + version: 3.2.3 + resolution: "jws@npm:3.2.3" + dependencies: + jwa: "npm:^1.4.2" + safe-buffer: "npm:^5.0.1" + checksum: 10/707387dd1cabcc3d9c2818f773cfaac7ede66e79ca11bbd159285a88cf5d8e8f355afcb8ee373e7bb0fcf9b7a2df015b22c50f27842f2c77453f04cd9f8f4009 + languageName: node + linkType: hard + +"jws@npm:^4.0.0, jws@npm:^4.0.1": + version: 4.0.1 + resolution: "jws@npm:4.0.1" + dependencies: + jwa: "npm:^2.0.1" + safe-buffer: "npm:^5.0.1" + checksum: 10/75d7b157489fa9a72023712c58a7a7706c7e2b10eec27fabd3bb9cae0c9e492251ab72527d20a8a5f5726196f0508c320c643fddff7076657f6bca16d0ceeeeb + languageName: node + linkType: hard + +"jwt-decode@npm:^4.0.0": + version: 4.0.0 + resolution: "jwt-decode@npm:4.0.0" + checksum: 10/87b569e4a9a0067fb0d592bcf3b2ac3e638e49beee28620eeb07bef1b4470f4077dea68c15d191dd68e076846c3af8394be3bcaecffedc6e97433b221fdbbcf3 + languageName: node + linkType: hard + +"keyv@npm:^4.0.0, keyv@npm:^4.5.3, keyv@npm:^4.5.4": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: "npm:3.0.1" + checksum: 10/167eb6ef64cc84b6fa0780ee50c9de456b422a1e18802209234f7c2cf7eae648c7741f32e50d7e24ccb22b24c13154070b01563d642755b156c357431a191e75 + languageName: node + linkType: hard + +"kind-of@npm:^6.0.2": + version: 6.0.3 + resolution: "kind-of@npm:6.0.3" + checksum: 10/5873d303fb36aad875b7538798867da2ae5c9e328d67194b0162a3659a627d22f742fc9c4ae95cd1704132a24b00cae5041fc00c0f6ef937dc17080dc4dbb962 + languageName: node + linkType: hard + +"knip@npm:^5.59.1": + version: 5.80.2 + resolution: "knip@npm:5.80.2" + dependencies: + "@nodelib/fs.walk": "npm:^1.2.3" + fast-glob: "npm:^3.3.3" + formatly: "npm:^0.3.0" + jiti: "npm:^2.6.0" + js-yaml: "npm:^4.1.1" + minimist: "npm:^1.2.8" + oxc-resolver: "npm:^11.15.0" + picocolors: "npm:^1.1.1" + picomatch: "npm:^4.0.1" + smol-toml: "npm:^1.5.2" + strip-json-comments: "npm:5.0.3" + zod: "npm:^4.1.11" + peerDependencies: + "@types/node": ">=18" + typescript: ">=5.0.4 <7" + bin: + knip: bin/knip.js + knip-bun: bin/knip-bun.js + checksum: 10/5cc2c6dbd32ae102776e7fbc4e9be0ab082e392e2f8a72efa12f1dbf3235d72cf2140cfe4c7475255e416c63e68ae60ff8b62efccb96d34bf412f4740b8fc796 + languageName: node + linkType: hard + +"kuler@npm:^2.0.0": + version: 2.0.0 + resolution: "kuler@npm:2.0.0" + checksum: 10/9e10b5a1659f9ed8761d38df3c35effabffbd19fc6107324095238e4ef0ff044392cae9ac64a1c2dda26e532426485342226b93806bd97504b174b0dcf04ed81 + languageName: node + linkType: hard + +"lazystream@npm:^1.0.0": + version: 1.0.1 + resolution: "lazystream@npm:1.0.1" + dependencies: + readable-stream: "npm:^2.0.5" + checksum: 10/35f8cf8b5799c76570b211b079d4d706a20cbf13a4936d44cc7dbdacab1de6b346ab339ed3e3805f4693155ee5bbebbda4050fa2b666d61956e89a573089e3d4 + languageName: node + linkType: hard + +"leac@npm:^0.6.0": + version: 0.6.0 + resolution: "leac@npm:0.6.0" + checksum: 10/bfe6aa128ca98664f124096f65584778194a8e1ddebf77d315fd9681be849c1619b0a9d9f4743e67aea0298808bd69ef25bd74320cad50a80be6852714627870 + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: "npm:^1.2.1" + type-check: "npm:~0.4.0" + checksum: 10/2e4720ff79f21ae08d42374b0a5c2f664c5be8b6c8f565bb4e1315c96ed3a8acaa9de788ffed82d7f2378cf36958573de07ef92336cb5255ed74d08b8318c9ee + languageName: node + linkType: hard + +"libbase64@npm:1.3.0": + version: 1.3.0 + resolution: "libbase64@npm:1.3.0" + checksum: 10/e90f06c7c7af754521d254c53ef847bab8ca354ee38f4c5d866c3fc77b4cb7a2ab77aa220c0df433f8d17abd0e884cf322b2771d84c2f65760e22012dfecaa67 + languageName: node + linkType: hard + +"libmime@npm:5.3.7": + version: 5.3.7 + resolution: "libmime@npm:5.3.7" + dependencies: + encoding-japanese: "npm:2.2.0" + iconv-lite: "npm:0.6.3" + libbase64: "npm:1.3.0" + libqp: "npm:2.1.1" + checksum: 10/9f148cd94256f604e71af60c159ad08b8914029727220b2a048e1c95957b099a41fb0c8a225dd1af87d5dfd4fa7c26afd7cf83d395894b7214e6827d7e25511b + languageName: node + linkType: hard + +"libphonenumber-js@npm:^1.11.1, libphonenumber-js@npm:^1.12.8": + version: 1.12.34 + resolution: "libphonenumber-js@npm:1.12.34" + checksum: 10/e0cbeb17076669fd10d9db0cb91be8fdd25b5379b094187c93db96540b8004b16a539e8f81ef9215685cba4d51a6287206cd26181d57b1df79a99a32624021fd + languageName: node + linkType: hard + +"libqp@npm:2.1.1": + version: 2.1.1 + resolution: "libqp@npm:2.1.1" + checksum: 10/cc59bd6ea61604b0505651fa79b722ff69d39b1fc609dad3ad4a20a51c6eb904babc0d1a59699a55a628063f31540232845bab471910d92c8b607120f563089e + languageName: node + linkType: hard + +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: "npm:~3.0.5" + checksum: 10/f335ce67fe221af496185d7ce39c8321304adb701e122942c495f4f72dcee8803f9315ee572f5f8e8b08b9e8d7195da91b9fad776e8864746ba8b5e910adf76e + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10/0c37f9f7fa212b38912b7145e1cd16a5f3cd34d782441c3e6ca653485d326f58b3caccda66efce1c5812bde4961bbde3374fae4b0d11bf1226152337f3894aa5 + languageName: node + linkType: hard + +"linkify-it@npm:5.0.0": + version: 5.0.0 + resolution: "linkify-it@npm:5.0.0" + dependencies: + uc.micro: "npm:^2.0.0" + checksum: 10/ef3b7609dda6ec0c0be8a7b879cea195f0d36387b0011660cd6711bba0ad82137f59b458b7e703ec74f11d88e7c1328e2ad9b855a8500c0ded67461a8c4519e6 + languageName: node + linkType: hard + +"liquidjs@npm:^10.11.1": + version: 10.24.0 + resolution: "liquidjs@npm:10.24.0" + dependencies: + commander: "npm:^10.0.0" + bin: + liquid: bin/liquid.js + liquidjs: bin/liquid.js + checksum: 10/2ba7424a3decc9d6b9c8b937c4d8104ab45e42043279175bc116d2fc75b6ed5fbeff11552e5f53f581fc57467622081e590aba878cc20b5da41d9914ec248b70 + languageName: node + linkType: hard + +"listenercount@npm:~1.0.1": + version: 1.0.1 + resolution: "listenercount@npm:1.0.1" + checksum: 10/208c6d2b57dc16c22cc71b58a7debb6f4612a79de211b76e251efee8eb03b9f6acd4651399016ef9c15ff6a3dedfd7acc96064acddce0dbe627e2d8478034d3d + languageName: node + linkType: hard + +"load-esm@npm:1.0.3": + version: 1.0.3 + resolution: "load-esm@npm:1.0.3" + checksum: 10/6949e8c253dddccca2a0ded1e9e0bbbc81924439d99e3a7d0f946ba6cdcd16de22c3cef28d997fd950befda0826ca65c3f913d9ba893d50e9cfc0bbd9a2a1e90 + languageName: node + linkType: hard + +"loader-runner@npm:^4.3.1": + version: 4.3.1 + resolution: "loader-runner@npm:4.3.1" + checksum: 10/d77127497c3f91fdba351e3e91156034e6e590e9f050b40df6c38ac16c54b5c903f7e2e141e09fefd046ee96b26fb50773c695ebc0aa205a4918683b124b04ba + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: "npm:^5.0.0" + checksum: 10/72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a + languageName: node + linkType: hard + +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: 10/c301cc379310441dc73cd6cebeb91fb254bea74e6ad3027f9346fc43b4174385153df420ffa521654e502fd34c40ef69ca4e7d40ee7129a99e06f306032bfc65 + languageName: node + linkType: hard + +"lodash.defaults@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.defaults@npm:4.2.0" + checksum: 10/6a2a9ea5ad7585aff8d76836c9e1db4528e5f5fa50fc4ad81183152ba8717d83aef8aec4fa88bf3417ed946fd4b4358f145ee08fbc77fb82736788714d3e12db + languageName: node + linkType: hard + +"lodash.difference@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.difference@npm:4.5.0" + checksum: 10/b22adb1be9c60e5997b8b483f8bab19878cb40eda65437907958e5d27990214716e1b00ebe312a97f47e63d8b891e4ae30947d08e1f0861ccdb9462f56ab9d77 + languageName: node + linkType: hard + +"lodash.escaperegexp@npm:^4.1.2": + version: 4.1.2 + resolution: "lodash.escaperegexp@npm:4.1.2" + checksum: 10/6d99452b1cfd6073175a9b741a9b09ece159eac463f86f02ea3bee2e2092923fce812c8d2bf446309cc52d1d61bf9af51c8118b0d7421388e6cead7bd3798f0f + languageName: node + linkType: hard + +"lodash.flatten@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.flatten@npm:4.4.0" + checksum: 10/a2b192f220b0b6c78a6c0175e96bad888b9e0f2a887a8e8c1d0c29d03231fbf110bbb9be0d9de5f936537d143eeb9d5b4f44c4a44f5592c195bf2fae6a6b1e3a + languageName: node + linkType: hard + +"lodash.groupby@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.groupby@npm:4.6.0" + checksum: 10/98bd04e58ce4cebb2273010352508b5ea12025e94fcfd70c84c8082ef3b0689178e8e6dd53bff919f525fae9bd67b4aba228d606b75a967f30e84ec9610b5de1 + languageName: node + linkType: hard + +"lodash.includes@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.includes@npm:4.3.0" + checksum: 10/45e0a7c7838c931732cbfede6327da321b2b10482d5063ed21c020fa72b09ca3a4aa3bda4073906ab3f436cf36eb85a52ea3f08b7bab1e0baca8235b0e08fe51 + languageName: node + linkType: hard + +"lodash.isarguments@npm:^3.1.0": + version: 3.1.0 + resolution: "lodash.isarguments@npm:3.1.0" + checksum: 10/e5186d5fe0384dcb0652501d9d04ebb984863ebc9c9faa2d4b9d5dfd81baef9ffe8e2887b9dc471d62ed092bc0788e5f1d42e45c72457a2884bbb54ac132ed92 + languageName: node + linkType: hard + +"lodash.isboolean@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isboolean@npm:3.0.3" + checksum: 10/b70068b4a8b8837912b54052557b21fc4774174e3512ed3c5b94621e5aff5eb6c68089d0a386b7e801d679cd105d2e35417978a5e99071750aa2ed90bffd0250 + languageName: node + linkType: hard + +"lodash.isequal@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: 10/82fc58a83a1555f8df34ca9a2cd300995ff94018ac12cc47c349655f0ae1d4d92ba346db4c19bbfc90510764e0c00ddcc985a358bdcd4b3b965abf8f2a48a214 + languageName: node + linkType: hard + +"lodash.isfunction@npm:^3.0.9": + version: 3.0.9 + resolution: "lodash.isfunction@npm:3.0.9" + checksum: 10/99e54c34b1e8a9ba75c034deb39cedbd2aca7af685815e67a2a8ec4f73ec9748cda6ebee5a07d7de4b938e90d421fd280e9c385cc190f903ac217ac8aff30314 + languageName: node + linkType: hard + +"lodash.isinteger@npm:^4.0.4": + version: 4.0.4 + resolution: "lodash.isinteger@npm:4.0.4" + checksum: 10/c971f5a2d67384f429892715550c67bac9f285604a0dd79275fd19fef7717aec7f2a6a33d60769686e436ceb9771fd95fe7fcb68ad030fc907d568d5a3b65f70 + languageName: node + linkType: hard + +"lodash.isnil@npm:^4.0.0": + version: 4.0.0 + resolution: "lodash.isnil@npm:4.0.0" + checksum: 10/ebf8df69879badd6ad99c4f64c54c470248df5cf92b208ca730861b1d8ac058da7b632ac811d18b0929d93cbac8d8fc866e781ee816b0142c56952e85edc682f + languageName: node + linkType: hard + +"lodash.isnumber@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isnumber@npm:3.0.3" + checksum: 10/913784275b565346255e6ae6a6e30b760a0da70abc29f3e1f409081585875105138cda4a429ff02577e1bc0a7ae2a90e0a3079a37f3a04c3d6c5aaa532f4cab2 + languageName: node + linkType: hard + +"lodash.isplainobject@npm:^4.0.6": + version: 4.0.6 + resolution: "lodash.isplainobject@npm:4.0.6" + checksum: 10/29c6351f281e0d9a1d58f1a4c8f4400924b4c79f18dfc4613624d7d54784df07efaff97c1ff2659f3e085ecf4fff493300adc4837553104cef2634110b0d5337 + languageName: node + linkType: hard + +"lodash.isstring@npm:^4.0.1": + version: 4.0.1 + resolution: "lodash.isstring@npm:4.0.1" + checksum: 10/eaac87ae9636848af08021083d796e2eea3d02e80082ab8a9955309569cb3a463ce97fd281d7dc119e402b2e7d8c54a23914b15d2fc7fff56461511dc8937ba0 + languageName: node + linkType: hard + +"lodash.isundefined@npm:^3.0.1": + version: 3.0.1 + resolution: "lodash.isundefined@npm:3.0.1" + checksum: 10/52b4d99a47bd41daa4e2860200258f56b1f2c99263c11a5f607fbbd91d6447fe674bdafc172735d099908a09136d4a0f98cf79715e38ca4b490fdda7162be289 + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10/d0ea2dd0097e6201be083865d50c3fb54fbfbdb247d9cc5950e086c991f448b7ab0cdab0d57eacccb43473d3f2acd21e134db39f22dac2d6c9ba6bf26978e3d6 + languageName: node + linkType: hard + +"lodash.mergewith@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.mergewith@npm:4.6.2" + checksum: 10/aea75a4492541a4902ac7e551dc6c54b722da0c187f84385d02e8fc33a7ae3454b837822446e5f63fcd5ad1671534ea408740b776670ea4d9c7890b10105fce0 + languageName: node + linkType: hard + +"lodash.once@npm:^4.0.0": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: 10/202f2c8c3d45e401b148a96de228e50ea6951ee5a9315ca5e15733d5a07a6b1a02d9da1e7fdf6950679e17e8ca8f7190ec33cae47beb249b0c50019d753f38f3 + languageName: node + linkType: hard + +"lodash.union@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.union@npm:4.6.0" + checksum: 10/175f5786efc527238c1350ce561c28e5ba527b5957605f9e5b8a804fce78801d09ced7b72de0302325e5b14c711f94690b1a733c13ad3674cc1a76e1172db1f8 + languageName: node + linkType: hard + +"lodash.uniq@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.uniq@npm:4.5.0" + checksum: 10/86246ca64ac0755c612e5df6d93cfe92f9ecac2e5ff054b965efbbb1d9a647b6310969e78545006f70f52760554b03233ad0103324121ae31474c20d5f7a2812 + languageName: node + linkType: hard + +"lodash@npm:4.17.21, lodash@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532 + languageName: node + linkType: hard + +"log-symbols@npm:^4.1.0": + version: 4.1.0 + resolution: "log-symbols@npm:4.1.0" + dependencies: + chalk: "npm:^4.1.0" + is-unicode-supported: "npm:^0.1.0" + checksum: 10/fce1497b3135a0198803f9f07464165e9eb83ed02ceb2273930a6f8a508951178d8cf4f0378e9d28300a2ed2bc49050995d2bd5f53ab716bb15ac84d58c6ef74 + languageName: node + linkType: hard + +"log4js@npm:^6.9.1": + version: 6.9.1 + resolution: "log4js@npm:6.9.1" + dependencies: + date-format: "npm:^4.0.14" + debug: "npm:^4.3.4" + flatted: "npm:^3.2.7" + rfdc: "npm:^1.3.0" + streamroller: "npm:^3.1.5" + checksum: 10/421fb9c1e5a8859a810a40c9ee01fb8e4dfc2fed838049946e67c0064d197bdf76ca43b8fc45df50c5d709e6fc4f218d314f189a0feb8be0c48bdae80cb0934c + languageName: node + linkType: hard + +"logform@npm:^2.7.0": + version: 2.7.0 + resolution: "logform@npm:2.7.0" + dependencies: + "@colors/colors": "npm:1.6.0" + "@types/triple-beam": "npm:^1.3.2" + fecha: "npm:^4.2.0" + ms: "npm:^2.1.1" + safe-stable-stringify: "npm:^2.3.1" + triple-beam: "npm:^1.3.0" + checksum: 10/4b861bfd67efe599ab41113ae3ffe92b1873bf86793fb442f58971852430d8f416f9904da69e5043071fb3725690e2499a13acbfe92a57ba7d21690004f9edc0 + languageName: node + linkType: hard + +"long@npm:^4.0.0": + version: 4.0.0 + resolution: "long@npm:4.0.0" + checksum: 10/8296e2ba7bab30f9cfabb81ebccff89c819af6a7a78b4bb5a70ea411aa764ee0532f7441381549dfa6a1a98d72abe9138bfcf99f4fa41238629849bc035b845b + languageName: node + linkType: hard + +"long@npm:^5.0.0": + version: 5.3.2 + resolution: "long@npm:5.3.2" + checksum: 10/b6b55ddae56fcce2864d37119d6b02fe28f6dd6d9e44fd22705f86a9254b9321bd69e9ffe35263b4846d54aba197c64882adcb8c543f2383c1e41284b321ea64 + languageName: node + linkType: hard + +"lossless-json@npm:^4.0.1": + version: 4.3.0 + resolution: "lossless-json@npm:4.3.0" + checksum: 10/a984a882c79b6e62a917d0202518472c17587bdee002f1427d7ec61115f9fbdeb5f0baa9f0e9fff8d9dbacd17da6b6c910012b2dcab69dd33d151cb7c13d5a37 + languageName: node + linkType: hard + +"lower-case@npm:^1.1.1": + version: 1.1.4 + resolution: "lower-case@npm:1.1.4" + checksum: 10/0c4aebc459ba330bcc38d20cad26ee33111155ed09c09e7d7ec395997277feee3a4d8db541ed5ca555f20ddc5c65a3b23648d18fcd2a950376da6d0c2e01416e + languageName: node + linkType: hard + +"lowercase-keys@npm:^2.0.0": + version: 2.0.0 + resolution: "lowercase-keys@npm:2.0.0" + checksum: 10/1c233d2da35056e8c49fae8097ee061b8c799b2f02e33c2bf32f9913c7de8fb481ab04dab7df35e94156c800f5f34e99acbf32b21781d87c3aa43ef7b748b79e + languageName: node + linkType: hard + +"lowercase-keys@npm:^3.0.0": + version: 3.0.0 + resolution: "lowercase-keys@npm:3.0.0" + checksum: 10/67a3f81409af969bc0c4ca0e76cd7d16adb1e25aa1c197229587eaf8671275c8c067cd421795dbca4c81be0098e4c426a086a05e30de8a9c587b7a13c0c7ccc5 + languageName: node + linkType: hard + +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a + languageName: node + linkType: hard + +"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": + version: 11.2.4 + resolution: "lru-cache@npm:11.2.4" + checksum: 10/3b2da74c0b6653767f8164c38c4c4f4d7f0cc10c62bfa512663d94a830191ae6a5af742a8d88a8b30d5f9974652d3adae53931f32069139ad24fa2a18a199aca + languageName: node + linkType: hard + +"luxon@npm:~3.5.0": + version: 3.5.0 + resolution: "luxon@npm:3.5.0" + checksum: 10/48f86e6c1c96815139f8559456a3354a276ba79bcef0ae0d4f2172f7652f3ba2be2237b0e103b8ea0b79b47715354ac9fac04eb1db3485dcc72d5110491dd47f + languageName: node + linkType: hard + +"luxon@npm:~3.7.0": + version: 3.7.2 + resolution: "luxon@npm:3.7.2" + checksum: 10/b24cd205ed306ce7415991687897dcc4027921ae413c9116590bc33a95f93b86ce52cf74ba72b4f5c5ab1c10090517f54ac8edfb127c049e0bf55b90dc2260be + languageName: node + linkType: hard + +"lvovich@npm:^2.0.2": + version: 2.1.0 + resolution: "lvovich@npm:2.1.0" + checksum: 10/4ed068e8adc9e84f3bfca60cbce3b4f8c07c56c2a94496a010bc1070d53be3e8c82fe1ed31bac6c1e22986b8ef5d95dc774337d3f6e2b0d063097a5028e48375 + languageName: node + linkType: hard + +"magic-string@npm:0.30.17": + version: 0.30.17 + resolution: "magic-string@npm:0.30.17" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + checksum: 10/2f71af2b0afd78c2e9012a29b066d2c8ba45a9cd0c8070f7fd72de982fb1c403b4e3afdb1dae00691d56885ede66b772ef6bedf765e02e3a7066208fe2fec4aa + languageName: node + linkType: hard + +"mailparser@npm:^3.7.1, mailparser@npm:^3.7.3": + version: 3.9.1 + resolution: "mailparser@npm:3.9.1" + dependencies: + "@zone-eu/mailsplit": "npm:5.4.8" + encoding-japanese: "npm:2.2.0" + he: "npm:1.2.0" + html-to-text: "npm:9.0.5" + iconv-lite: "npm:0.7.0" + libmime: "npm:5.3.7" + linkify-it: "npm:5.0.0" + nodemailer: "npm:7.0.11" + punycode.js: "npm:2.3.1" + tlds: "npm:1.261.0" + checksum: 10/6b090c6f22834294820d14027a2b319c59fcb83b5ffa0c8346abb2da961daf8ae276ff51412a4d9019c10dba14287f01b96aa2372ee5e9eecffe865bffa37589 + languageName: node + linkType: hard + +"make-dir@npm:^1.3.0": + version: 1.3.0 + resolution: "make-dir@npm:1.3.0" + dependencies: + pify: "npm:^3.0.0" + checksum: 10/c564f6e7bb5ace1c02ad56b3a5f5e07d074af0c0b693c55c7b2c2b148882827c8c2afc7b57e43338a9f90c125b58d604e8cf3e6990a48bf949dfea8c79668c0b + languageName: node + linkType: hard + +"make-error@npm:^1.1.1": + version: 1.3.6 + resolution: "make-error@npm:1.3.6" + checksum: 10/b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 + languageName: node + linkType: hard + +"make-fetch-happen@npm:^15.0.0": + version: 15.0.3 + resolution: "make-fetch-happen@npm:15.0.3" + dependencies: + "@npmcli/agent": "npm:^4.0.0" + cacache: "npm:^20.0.1" + http-cache-semantics: "npm:^4.1.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^5.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^1.0.0" + proc-log: "npm:^6.0.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^13.0.0" + checksum: 10/78da4fc1df83cb596e2bae25aa0653b8a9c6cbdd6674a104894e03be3acfcd08c70b78f06ef6407fbd6b173f6a60672480d78641e693d05eb71c09c13ee35278 + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd + languageName: node + linkType: hard + +"mathjs@npm:^14.5.1": + version: 14.9.1 + resolution: "mathjs@npm:14.9.1" + dependencies: + "@babel/runtime": "npm:^7.26.10" + complex.js: "npm:^2.2.5" + decimal.js: "npm:^10.4.3" + escape-latex: "npm:^1.2.0" + fraction.js: "npm:^5.2.1" + javascript-natural-sort: "npm:^0.7.1" + seedrandom: "npm:^3.0.5" + tiny-emitter: "npm:^2.1.0" + typed-function: "npm:^4.2.1" + bin: + mathjs: bin/cli.js + checksum: 10/fe99edd5a27ebeb01a44338220016053f7982bcc0195f173a3835d75766d7296c10b1d3deab508abaecc53f2999778a1cd7178b5eac4043538bdee1411aa50b4 + languageName: node + linkType: hard + +"media-typer@npm:0.3.0": + version: 0.3.0 + resolution: "media-typer@npm:0.3.0" + checksum: 10/38e0984db39139604756903a01397e29e17dcb04207bb3e081412ce725ab17338ecc47220c1b186b6bbe79a658aad1b0d41142884f5a481f36290cdefbe6aa46 + languageName: node + linkType: hard + +"media-typer@npm:^1.1.0": + version: 1.1.0 + resolution: "media-typer@npm:1.1.0" + checksum: 10/a58dd60804df73c672942a7253ccc06815612326dc1c0827984b1a21704466d7cde351394f47649e56cf7415e6ee2e26e000e81b51b3eebb5a93540e8bf93cbd + languageName: node + linkType: hard + +"memfs@npm:^3.4.1": + version: 3.5.3 + resolution: "memfs@npm:3.5.3" + dependencies: + fs-monkey: "npm:^1.0.4" + checksum: 10/7c9cdb453a6b06e87f11e2dbe6c518fd3c1c1581b370ffa24f42f3fd5b1db8c2203f596e43321a0032963f3e9b66400f2c3cf043904ac496d6ae33eafd0878fe + languageName: node + linkType: hard + +"mensch@npm:^0.3.4": + version: 0.3.4 + resolution: "mensch@npm:0.3.4" + checksum: 10/b6178a81a8059e0bf7ed62651aaab3cb1cfc68da41e0a9681cffd06083bd8d14c0e79b7493fc7efcf63c112c08efd584371208efc93cbae12d613dbc297a1d03 + languageName: node + linkType: hard + +"merge-descriptors@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-descriptors@npm:2.0.0" + checksum: 10/e383332e700a94682d0125a36c8be761142a1320fc9feeb18e6e36647c9edf064271645f5669b2c21cf352116e561914fd8aa831b651f34db15ef4038c86696a + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10/6fa4dcc8d86629705cea944a4b88ef4cb0e07656ebf223fa287443256414283dd25d91c1cd84c77987f2aec5927af1a9db6085757cb43d90eb170ebf4b47f4f4 + languageName: node + linkType: hard + +"merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10/7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10/6bf2a01672e7965eb9941d1f02044fad2bd12486b5553dc1116ff24c09a8723157601dc992e74c911d896175918448762df3b3fd0a6b61037dd1a9766ddfbf58 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10/54bb60bf39e6f8689f6622784e668a3d7f8bed6b0d886f5c3c446cb3284be28b30bf707ed05d0fe44a036f8469976b2629bbea182684977b084de9da274694d7 + languageName: node + linkType: hard + +"mime-db@npm:^1.28.0, mime-db@npm:^1.54.0": + version: 1.54.0 + resolution: "mime-db@npm:1.54.0" + checksum: 10/9e7834be3d66ae7f10eaa69215732c6d389692b194f876198dca79b2b90cbf96688d9d5d05ef7987b20f749b769b11c01766564264ea5f919c88b32a29011311 + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10/89aa9651b67644035de2784a6e665fc685d79aba61857e02b9c8758da874a754aed4a9aced9265f5ed1171fd934331e5516b84a7f0218031b6fa0270eca1e51a + languageName: node + linkType: hard + +"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1, mime-types@npm:^3.0.2": + version: 3.0.2 + resolution: "mime-types@npm:3.0.2" + dependencies: + mime-db: "npm:^1.54.0" + checksum: 10/9db0ad31f5eff10ee8f848130779b7f2d056ddfdb6bda696cb69be68d486d33a3457b4f3f9bdeb60d0736edb471bd5a7c0a384375c011c51c889fd0d5c3b893e + languageName: node + linkType: hard + +"mime@npm:^2.4.6": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 10/7da117808b5cd0203bb1b5e33445c330fe213f4d8ee2402a84d62adbde9716ca4fb90dd6d9ab4e77a4128c6c5c24a9c4c9f6a4d720b095b1b342132d02dba58d + languageName: node + linkType: hard + +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: 10/d2421a3444848ce7f84bd49115ddacff29c15745db73f54041edc906c14b131a38d05298dae3081667627a59b2eb1ca4b436ff2e1b80f69679522410418b478a + languageName: node + linkType: hard + +"mimic-response@npm:^1.0.0": + version: 1.0.1 + resolution: "mimic-response@npm:1.0.1" + checksum: 10/034c78753b0e622bc03c983663b1cdf66d03861050e0c8606563d149bc2b02d63f62ce4d32be4ab50d0553ae0ffe647fc34d1f5281184c6e1e8cf4d85e8d9823 + languageName: node + linkType: hard + +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 10/7e719047612411fe071332a7498cf0448bbe43c485c0d780046c76633a771b223ff49bd00267be122cedebb897037fdb527df72335d0d0f74724604ca70b37ad + languageName: node + linkType: hard + +"mimic-response@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-response@npm:4.0.0" + checksum: 10/33b804cc961efe206efdb1fca6a22540decdcfce6c14eb5c0c50e5ae9022267ab22ce8f5568b1f7247ba67500fe20d523d81e0e9f009b321ccd9d472e78d1850 + languageName: node + linkType: hard + +"minimalistic-assert@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-assert@npm:1.0.1" + checksum: 10/cc7974a9268fbf130fb055aff76700d7e2d8be5f761fb5c60318d0ed010d839ab3661a533ad29a5d37653133385204c503bfac995aaa4236f4e847461ea32ba7 + languageName: node + linkType: hard + +"minimatch@npm:9.0.1": + version: 9.0.1 + resolution: "minimatch@npm:9.0.1" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/b4e98f4dc740dcf33999a99af23ae6e5e1c47632f296dc95cb649a282150f92378d41434bf64af4ea2e5975255a757d031c3bf014bad9214544ac57d97f3ba63 + languageName: node + linkType: hard + +"minimatch@npm:^10.1.1": + version: 10.1.1 + resolution: "minimatch@npm:10.1.1" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10/110f38921ea527022e90f7a5f43721838ac740d0a0c26881c03b57c261354fb9a0430e40b2c56dfcea2ef3c773768f27210d1106f1f2be19cde3eea93f26f45e + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634 + languageName: node + linkType: hard + +"minimatch@npm:^5.0.1, minimatch@npm:^5.1.0": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/126b36485b821daf96d33b5c821dac600cc1ab36c87e7a532594f9b1652b1fa89a1eebcaad4dff17c764dce1a7ac1531327f190fed5f97d8f6e5f889c116c429 + languageName: node + linkType: hard + +"minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 + languageName: node + linkType: hard + +"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 + languageName: node + linkType: hard + +"minipass-fetch@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass-fetch@npm:5.0.0" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^3.0.1" + dependenciesMeta: + encoding: + optional: true + checksum: 10/4fb7dca630a64e6970a8211dade505bfe260d0b8d60beb348dcdfb95fe35ef91d977b29963929c9017ae0805686aa3f413107dc6bc5deac9b9e26b0b41c3b86c + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10/40982d8d836a52b0f37049a0a7e5d0f089637298e6d9b45df9c115d4f0520682a78258905e5c8b180fb41b593b0a82cc1361d2c74b45f7ada66334f84d1ecfdd + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10/c25f0ee8196d8e6036661104bacd743785b2599a21de5c516b32b3fa2b83113ac89a2358465bc04956baab37ffb956ae43be679b2262bf7be15fce467ccd7950 + languageName: node + linkType: hard + +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10/f47365cc2cb7f078cbe7e046eb52655e2e7e97f8c0a9a674f4da60d94fb0624edfcec9b5db32e8ba5a99a5f036f595680ae6fe02a262beaa73026e505cc52f99 + languageName: node + linkType: hard + +"mjml-accordion@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-accordion@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/94e548f63abab0574c4a9940959fc71732edfac7dc62aa3065ab87b8ec32b81f3b283cd3b1e17a191449d10600e7b87fc3f65aded4a4737113577e4f26740cf2 + languageName: node + linkType: hard + +"mjml-body@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-body@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/141d23937aceebeec89a13c3c59fa2ba1563ecaa071cc119204aee40f50502f382a2a49801a4aaf4acc430e7ebf2fcc69af4ae93a69730861f9a4c321f4efa9b + languageName: node + linkType: hard + +"mjml-button@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-button@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/0fd36c33207d2c3e94b5ec2a5646abeae680db0521aed495c55859fe31cc8aaeca9524b0ef3630ee1ff2eb6ddcb135f1e5a38a2fdf27be94d2c0d7eae5069a42 + languageName: node + linkType: hard + +"mjml-carousel@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-carousel@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/9347e7860cd7f5ad5969dded9aae3fe513865fb5ae2d3ee717a77989e622449394aa4e2f360ff148941f2f622c8caebdfceb86b5441c347ece06e6ec8cbabfa3 + languageName: node + linkType: hard + +"mjml-cli@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-cli@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + chokidar: "npm:^3.0.0" + glob: "npm:^10.3.10" + html-minifier: "npm:^4.0.0" + js-beautify: "npm:^1.6.14" + lodash: "npm:^4.17.21" + minimatch: "npm:^9.0.3" + mjml-core: "npm:4.18.0" + mjml-migrate: "npm:4.18.0" + mjml-parser-xml: "npm:4.18.0" + mjml-validator: "npm:4.18.0" + yargs: "npm:^17.7.2" + bin: + mjml-cli: bin/mjml + checksum: 10/4f9029dc15debc32b71bb5b6195762a439acb4ac04d6ff36dab57a6c062ebfe91bc84c8bebd9b954be5d9469d60983f88156a7d653cf483876986dadf9644d18 + languageName: node + linkType: hard + +"mjml-column@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-column@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/7e246b9890f9cd07a6c89ff202eb8e500186e74daf226e606a0fec01b8d6c7fcabf2ad0dfdfa8bd134accd2190ff190c2185281611a37519f193fd354b6e1873 + languageName: node + linkType: hard + +"mjml-core@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-core@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + cheerio: "npm:1.0.0-rc.12" + detect-node: "npm:^2.0.4" + html-minifier: "npm:^4.0.0" + js-beautify: "npm:^1.6.14" + juice: "npm:^10.0.0" + lodash: "npm:^4.17.21" + mjml-migrate: "npm:4.18.0" + mjml-parser-xml: "npm:4.18.0" + mjml-validator: "npm:4.18.0" + checksum: 10/f0e848f00baeebf14e4a479d3aed942eb687ebacdbaf7b08e2b93e2367f2b1d2149224c9f0815d708f3597827fcb5fd35605b448a96ad176fd8d8ab1f982d60d + languageName: node + linkType: hard + +"mjml-divider@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-divider@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/148d8cc56e8a980a66159cfcbe3c0364a2e3faf87060b37bd44867ffc32367f7286de84ce7edd315b9add53171414a0296456b3fc0088e01adc3decfeec69780 + languageName: node + linkType: hard + +"mjml-group@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-group@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/5346ac84aa69b4232d278968db0a1e739c1b2c7d03e700db5e0d1a8130249ed343fa2c7db58798538082422324091c3d2aee738e923b82d8855c435d99814b81 + languageName: node + linkType: hard + +"mjml-head-attributes@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-attributes@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/b153bb54bc6499971849d26dc307dae09aa34e79db01f49e302158e31a6d841de195fdee6666de992e0b401b822291a552ea334cb5a3ca7824df68cb601acdb8 + languageName: node + linkType: hard + +"mjml-head-breakpoint@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-breakpoint@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/8831ddcffd9c3afba11709b9fe76581f42609d47f713b8008dde527ee3a8b2b2ce45d7848c56b1a2816f0df91b148d84a6ddb0eebd0113d2a4d1f71ade54c23e + languageName: node + linkType: hard + +"mjml-head-font@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-font@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/2435b44c803f357d144509e226fa71d8575e22d5307aff97459c9152b8be7b9989c8c6641200b32f7199c1b43dc982ef97972a8c05549d9b6b10091f54d7cb3b + languageName: node + linkType: hard + +"mjml-head-html-attributes@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-html-attributes@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/4e18cc47c2af2b55b3d1d2a1826a13cc996b3c36bff8f34ba3f70bc4b31cfa90b77d2de89cf48f9073da1f7cd3a93b3b219516146b78badc43d1932f963b8cc0 + languageName: node + linkType: hard + +"mjml-head-preview@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-preview@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/ba1dd6eefe79387acb1c33be5170dbd4a18a499d005bfa81565763153f8bcd23d361d61b221de8951f8408fc01ce9710a1211e090db1d6f18f8b9ac762f73873 + languageName: node + linkType: hard + +"mjml-head-style@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-style@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/0a3807072e0c7a32db8372cfeb207d12213ac60e03fd9a569b3b08f5c2783b3c7d99671c4266a27a5146df05375213e1b2333ccae7833d5ff12afa8a9d2a88b6 + languageName: node + linkType: hard + +"mjml-head-title@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head-title@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/a7ef5d0f444b14303f12d17af97282122917e62a260e01e6f3957dfef7de563cdd20b27bbbecee7f99de47ae109233f8c723db17043f1224c35e5a0326792cdc + languageName: node + linkType: hard + +"mjml-head@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-head@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/16c56f89cc9b9dfc00c99a4cd7c1411ea9a4b869119ab7c4280e97a2df2aecb509d07a5c220e5f621cf8dd9935843222bb76bede9f636830a4a896ee3912ec1c + languageName: node + linkType: hard + +"mjml-hero@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-hero@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/70082908fb1c7d51a198b96532e92041d64dac50cf96c8aabe90c5f47cf9f211c3535e270f3d0693faeb49824bd413b422e321f0ac76e95db8966368cb0cf2dc + languageName: node + linkType: hard + +"mjml-image@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-image@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/431454c2cababe5743475bd470367a9912366f5da6307efc871de14f0aea7f75829f6b0ee775664fedd4d3719c2698527cda0aacd9ede0c97737bceef8bd3349 + languageName: node + linkType: hard + +"mjml-migrate@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-migrate@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + js-beautify: "npm:^1.6.14" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + mjml-parser-xml: "npm:4.18.0" + yargs: "npm:^17.7.2" + bin: + migrate: lib/cli.js + checksum: 10/7604317170d7e5f7dd59c2a33a969a21372abc532fa7a5f3c599cc1c729bda6f409cea343a1292fa288703f4386a221854127b9f6050ad25e5637e6484362167 + languageName: node + linkType: hard + +"mjml-navbar@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-navbar@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/26e0d84254ba80945973e55bf0172dac88841ac1e3e2ed5e05ef75e0a283ede219cab5b17668ecc28a7fbaa97e693f4cd9a756faeb720dae1f4472daaf54f682 + languageName: node + linkType: hard + +"mjml-parser-xml@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-parser-xml@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + detect-node: "npm:2.1.0" + htmlparser2: "npm:^9.1.0" + lodash: "npm:^4.17.21" + checksum: 10/1ef8041f66345532f0de326692b4d5869ffd16190bd156ac5d341452442c74b08e930e4aa050629a97c28e1354e55ec8def684f9cc818d30a612bf4e9a5b6e07 + languageName: node + linkType: hard + +"mjml-preset-core@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-preset-core@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + mjml-accordion: "npm:4.18.0" + mjml-body: "npm:4.18.0" + mjml-button: "npm:4.18.0" + mjml-carousel: "npm:4.18.0" + mjml-column: "npm:4.18.0" + mjml-divider: "npm:4.18.0" + mjml-group: "npm:4.18.0" + mjml-head: "npm:4.18.0" + mjml-head-attributes: "npm:4.18.0" + mjml-head-breakpoint: "npm:4.18.0" + mjml-head-font: "npm:4.18.0" + mjml-head-html-attributes: "npm:4.18.0" + mjml-head-preview: "npm:4.18.0" + mjml-head-style: "npm:4.18.0" + mjml-head-title: "npm:4.18.0" + mjml-hero: "npm:4.18.0" + mjml-image: "npm:4.18.0" + mjml-navbar: "npm:4.18.0" + mjml-raw: "npm:4.18.0" + mjml-section: "npm:4.18.0" + mjml-social: "npm:4.18.0" + mjml-spacer: "npm:4.18.0" + mjml-table: "npm:4.18.0" + mjml-text: "npm:4.18.0" + mjml-wrapper: "npm:4.18.0" + checksum: 10/84e996c6388707de581bdc63b881e9b1e3233c68ad7c172f5eed3f568663c7aa15a184f3ac6dd6f41f62d5b157846775a64ef48cdb4370f8f5f10216d2e40df7 + languageName: node + linkType: hard + +"mjml-raw@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-raw@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/b2b1d8db92a5a3c1336b5558d3876bafa0671fbc2ca6321c44b27bc280620f0140e95943f18f33a96ea5291738e26158339be7d68adaf4df15b5a7a86a73ff90 + languageName: node + linkType: hard + +"mjml-section@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-section@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/3da4864a64e9e2dbc7737cbd26a012ef22ada309192915aeec6cf0ada41ee7a8749f73fccb79ec1e615a49104295340c43136d8f6817aa8187c263b86d6988d1 + languageName: node + linkType: hard + +"mjml-social@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-social@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/f520f7a6a15da5649c6a42de1fe9b736ff057b03d16043c4b945b5f45ed506d1735642981868de0464c37b641df2b70dac51c8077af8e993ab3f26c56b69ac6e + languageName: node + linkType: hard + +"mjml-spacer@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-spacer@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/4aa0766e78fc76be521d92bc5307d37c8c9518da06a515251bc2de4cc5742e53c16d913480d8339183f6d525d84ed0b9b28cd8a407eeda03614cb040269bc241 + languageName: node + linkType: hard + +"mjml-table@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-table@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/e242fbe7b4d17357ddc805baea135300928583bcdd57c9e5688fd2fd2b04c473f8f146ecea85a2e4e83efdfc157f4eba365f0cc992896211e0ec3c966fef5ee2 + languageName: node + linkType: hard + +"mjml-text@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-text@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + checksum: 10/6b5d3232c54bb240fefbd7841ea625dccede2e2bf8c0c39a6d15445c06e8b116f744ece7da3fea8b1df68ae5959d3f7176d966e7a8df0b9e5bc254aa40c1f93d + languageName: node + linkType: hard + +"mjml-validator@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-validator@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + checksum: 10/ceffc44afd9510a59090ff02a216052559f71c046402737712d3f616d034f0edc713c48db88fc36c092e3bc4d8da42f284e823546eb1f464626be2d88933d99f + languageName: node + linkType: hard + +"mjml-wrapper@npm:4.18.0": + version: 4.18.0 + resolution: "mjml-wrapper@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.18.0" + mjml-section: "npm:4.18.0" + checksum: 10/6745b43a742a82bc2e25e06814be41788c5d0dfeb1e8d8bd216997ec67bbec1b4534c20c3d737ae48edfb4cf8b41348ff079b47028be572e78e3534053b89b39 + languageName: node + linkType: hard + +"mjml@npm:^4.15.3": + version: 4.18.0 + resolution: "mjml@npm:4.18.0" + dependencies: + "@babel/runtime": "npm:^7.28.4" + mjml-cli: "npm:4.18.0" + mjml-core: "npm:4.18.0" + mjml-migrate: "npm:4.18.0" + mjml-preset-core: "npm:4.18.0" + mjml-validator: "npm:4.18.0" + bin: + mjml: bin/mjml + checksum: 10/2be22b2cfdf152b248d8509f09a1b71ec46e793511798badba979db9d4ac9ac9f98940a759f587412c0f9658fc0cb8908b18bb2c3a9e9fc74b783e0595725a0a + languageName: node + linkType: hard + +"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": + version: 0.5.3 + resolution: "mkdirp-classic@npm:0.5.3" + checksum: 10/3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac + languageName: node + linkType: hard + +"mkdirp@npm:>=0.5 0, mkdirp@npm:^0.5.6": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" + dependencies: + minimist: "npm:^1.2.6" + bin: + mkdirp: bin/cmd.js + checksum: 10/0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 + languageName: node + linkType: hard + +"module-details-from-path@npm:^1.0.3": + version: 1.0.4 + resolution: "module-details-from-path@npm:1.0.4" + checksum: 10/2ebfada5358492f6ab496b70f70a1042f2ee7a4c79d29467f59ed6704f741fb4461d7cecb5082144ed39a05fec4d19e9ff38b731c76228151be97227240a05b2 + languageName: node + linkType: hard + +"ms@npm:^2.1.1, ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d + languageName: node + linkType: hard + +"multer@npm:2.0.2, multer@npm:^2.0.0": + version: 2.0.2 + resolution: "multer@npm:2.0.2" + dependencies: + append-field: "npm:^1.0.0" + busboy: "npm:^1.6.0" + concat-stream: "npm:^2.0.0" + mkdirp: "npm:^0.5.6" + object-assign: "npm:^4.1.1" + type-is: "npm:^1.6.18" + xtend: "npm:^4.0.2" + checksum: 10/4bdcb07138cf72f93adc08a0dc27c058faab9f6721067a58f394fa546d73d11b7f100cdd66e733d649d184f9d1b402065f6888b31ec427409f056ee92c4367a6 + languageName: node + linkType: hard + +"mute-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "mute-stream@npm:2.0.0" + checksum: 10/d2e4fd2f5aa342b89b98134a8d899d8ef9b0a6d69274c4af9df46faa2d97aeb1f2ce83d867880d6de63643c52386579b99139801e24e7526c3b9b0a6d1e18d6c + languageName: node + linkType: hard + +"nan@npm:^2.13.2, nan@npm:^2.22.2": + version: 2.24.0 + resolution: "nan@npm:2.24.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10/479f6960119b5ef9b488c14e9069eb534c3545d50b621f51b247d1e3b40828ee619c4d9f8efe30786c5b18c21c60b3cda3f0d0b92e9a3a26cb3e4ab5492a7032 + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 10/23ad088b08f898fc9b53011d7bb78ec48e79de7627e01ab5518e806033861bef68d5b0cd0e2205c2f36690ac9571ff6bcb05eb777ced2eeda8d4ac5b44592c3d + languageName: node + linkType: hard + +"negotiator@npm:0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10/2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837 + languageName: node + linkType: hard + +"negotiator@npm:^1.0.0": + version: 1.0.0 + resolution: "negotiator@npm:1.0.0" + checksum: 10/b5734e87295324fabf868e36fb97c84b7d7f3156ec5f4ee5bf6e488079c11054f818290fc33804cef7b1ee21f55eeb14caea83e7dafae6492a409b3e573153e5 + languageName: node + linkType: hard + +"neo-async@npm:^2.6.2": + version: 2.6.2 + resolution: "neo-async@npm:2.6.2" + checksum: 10/1a7948fea86f2b33ec766bc899c88796a51ba76a4afc9026764aedc6e7cde692a09067031e4a1bf6db4f978ccd99e7f5b6c03fe47ad9865c3d4f99050d67e002 + languageName: node + linkType: hard + +"nest-winston@npm:^1.10.2": + version: 1.10.2 + resolution: "nest-winston@npm:1.10.2" + dependencies: + fast-safe-stringify: "npm:^2.1.1" + peerDependencies: + "@nestjs/common": ^5.0.0 || ^6.6.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + winston: ^3.0.0 + checksum: 10/b3f8a16b8cafb6acd7d8a0e68b3434f4ac693d2efa050df0b3d633467510ceef2b2971024ac7d9fba631d0eaa24d38a97bd4098cdbd2e282a7fd3b80abf00098 + languageName: node + linkType: hard + +"newrelic@npm:^12.20.0": + version: 12.25.0 + resolution: "newrelic@npm:12.25.0" + dependencies: + "@grpc/grpc-js": "npm:^1.13.2" + "@grpc/proto-loader": "npm:^0.7.5" + "@newrelic/fn-inspect": "npm:^4.4.0" + "@newrelic/native-metrics": "npm:^11.1.0" + "@newrelic/security-agent": "npm:^2.4.2" + "@opentelemetry/api": "npm:^1.9.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/exporter-metrics-otlp-proto": "npm:^0.201.1" + "@opentelemetry/resources": "npm:^2.0.1" + "@opentelemetry/sdk-metrics": "npm:^2.0.1" + "@opentelemetry/sdk-trace-base": "npm:^2.0.0" + "@prisma/prisma-fmt-wasm": "npm:^4.17.0-16.27eb2449f178cd9fe1a4b892d732cc4795f75085" + "@tyriar/fibonacci-heap": "npm:^2.0.7" + concat-stream: "npm:^2.0.0" + https-proxy-agent: "npm:^7.0.1" + import-in-the-middle: "npm:^1.13.0" + json-bigint: "npm:^1.0.0" + json-stringify-safe: "npm:^5.0.0" + module-details-from-path: "npm:^1.0.3" + readable-stream: "npm:^3.6.1" + require-in-the-middle: "npm:^7.4.0" + semver: "npm:^7.5.2" + winston-transport: "npm:^4.5.0" + dependenciesMeta: + "@newrelic/fn-inspect": + optional: true + "@newrelic/native-metrics": + optional: true + "@prisma/prisma-fmt-wasm": + optional: true + bin: + newrelic-naming-rules: bin/test-naming-rules.js + checksum: 10/7c1e8fe093dce405a60d08176277aa2003fec86136bb9a65af54084669497d26fc50b09bdf0e041e1a59c70debfe0f777a49cfd78eb4ab214eb8b88bba22e25d + languageName: node + linkType: hard + +"nice-try@npm:^1.0.4": + version: 1.0.5 + resolution: "nice-try@npm:1.0.5" + checksum: 10/0b4af3b5bb5d86c289f7a026303d192a7eb4417231fe47245c460baeabae7277bcd8fd9c728fb6bd62c30b3e15cd6620373e2cf33353b095d8b403d3e8a15aff + languageName: node + linkType: hard + +"no-case@npm:^2.2.0": + version: 2.3.2 + resolution: "no-case@npm:2.3.2" + dependencies: + lower-case: "npm:^1.1.1" + checksum: 10/a92fc7c10f40477bb69c3ca00e2a12fd08f838204bcef66233cbe8a36c0ec7938ba0cdf3f0534b38702376cbfa26270130607c0b8460ea87f44d474919c39c91 + languageName: node + linkType: hard + +"node-abi@npm:^3.3.0": + version: 3.85.0 + resolution: "node-abi@npm:3.85.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10/1d0fb9e7922431663db700aa7087c0e49bae5fd041efe2d7463d808e58be1594af8046e835fe1b17ca69f214b6327e89fb0dd5192f0e7a9bf624f5509a9104b7 + languageName: node + linkType: hard + +"node-abort-controller@npm:^3.0.1": + version: 3.1.1 + resolution: "node-abort-controller@npm:3.1.1" + checksum: 10/0a2cdb7ec0aeaf3cb31e1ca0e192f5add48f1c5c9c9ed822129f9dddbd9432f69b7425982f94ce803c56a2104884530aa67cd57696e5774b2e5b8ec2f58de042 + languageName: node + linkType: hard + +"node-addon-api@npm:^8.3.0": + version: 8.5.0 + resolution: "node-addon-api@npm:8.5.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10/9a893f4f835fbc3908e0070f7bcacf36e37fd06be8008409b104c30df4092a0d9a29927b3a74cdbc1d34338274ba4116d597a41f573e06c29538a1a70d07413f + languageName: node + linkType: hard + +"node-emoji@npm:1.11.0": + version: 1.11.0 + resolution: "node-emoji@npm:1.11.0" + dependencies: + lodash: "npm:^4.17.21" + checksum: 10/1d7ae9bcb0f23d7cdfcac5c3a90a6fd6ec584e6f7c70ff073f6122bfbed6c06284da7334092500d24e14162f5c4016e5dcd3355753cbd5b7e60de560a973248d + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.9": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10/b24f8a3dc937f388192e59bcf9d0857d7b6940a2496f328381641cb616efccc9866e89ec43f2ec956bbd6c3d3ee05524ce77fe7b29ccd34692b3a16f237d6676 + languageName: node + linkType: hard + +"node-forge@npm:^1.2.1": + version: 1.3.3 + resolution: "node-forge@npm:1.3.3" + checksum: 10/f41c31b9296771a4b8c955d58417471712f54f324603a35f8e6cbac19d5e6eaaf5fd5fd14584dfedecbf46a05438ded6eee60a5f2f0822fc5061aaa073cfc75d + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.8.1, node-gyp-build@npm:^4.8.4": + version: 4.8.4 + resolution: "node-gyp-build@npm:4.8.4" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10/6a7d62289d1afc419fc8fc9bd00aa4e554369e50ca0acbc215cb91446148b75ff7e2a3b53c2c5b2c09a39d416d69f3d3237937860373104b5fe429bf30ad9ac5 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 12.1.0 + resolution: "node-gyp@npm:12.1.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^15.0.0" + nopt: "npm:^9.0.0" + proc-log: "npm:^6.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.2" + tinyglobby: "npm:^0.2.12" + which: "npm:^6.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10/d93079236cef1dd7fa4df683708d8708ad255c55865f6656664c8959e4d3963d908ac48e8f9f341705432e979dbbf502a40d68d65a17fe35956a5a05ba6c1cb4 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.27": + version: 2.0.27 + resolution: "node-releases@npm:2.0.27" + checksum: 10/f6c78ddb392ae500719644afcbe68a9ea533242c02312eb6a34e8478506eb7482a3fb709c70235b01c32fe65625b68dfa9665113f816d87f163bc3819b62b106 + languageName: node + linkType: hard + +"nodeify@npm:^1.0.0": + version: 1.0.1 + resolution: "nodeify@npm:1.0.1" + dependencies: + is-promise: "npm:~1.0.0" + promise: "npm:~1.3.0" + checksum: 10/2d61f77a43ba0d580fc9615c5112d891605baa56f13d871a9f9736a92a80d974f77fa9ad3a4698214929216e79f583c18fd17e6363934f73d39cc69319f244de + languageName: node + linkType: hard + +"nodemailer@npm:7.0.11": + version: 7.0.11 + resolution: "nodemailer@npm:7.0.11" + checksum: 10/2ad4dd56a4caf84a83aa6f4378ded26d5ef8a644ca3be09c3b4fb2255d861369e620f29be6c3c97148ac4a50aa5fdff6240b9d60805362bd99ca15f2ea62e8a2 + languageName: node + linkType: hard + +"nodemailer@npm:7.0.12, nodemailer@npm:^7.0.3": + version: 7.0.12 + resolution: "nodemailer@npm:7.0.12" + checksum: 10/31a2713be588fb6849d05698cb355a88902a64e17e63b1a5c8a10db3acc4349c329c3bbd83bc5bdefda92f8df5e0297364502f93042c0bae87c48e5652b663b7 + languageName: node + linkType: hard + +"nodemailer@npm:^6.9.13": + version: 6.10.1 + resolution: "nodemailer@npm:6.10.1" + checksum: 10/d9911701641e06143a2deb0bd5deb518310972316c6e6eabc594af24353b0d67867f26cb8d72b0cfa385abef945149ac51ae40a40d2199e1088aef5829e58a3d + languageName: node + linkType: hard + +"nopt@npm:^7.2.1": + version: 7.2.1 + resolution: "nopt@npm:7.2.1" + dependencies: + abbrev: "npm:^2.0.0" + bin: + nopt: bin/nopt.js + checksum: 10/95a1f6dec8a81cd18cdc2fed93e6f0b4e02cf6bdb4501c848752c6e34f9883d9942f036a5e3b21a699047d8a448562d891e67492df68ec9c373e6198133337ae + languageName: node + linkType: hard + +"nopt@npm:^9.0.0": + version: 9.0.0 + resolution: "nopt@npm:9.0.0" + dependencies: + abbrev: "npm:^4.0.0" + bin: + nopt: bin/nopt.js + checksum: 10/56a1ccd2ad711fb5115918e2c96828703cddbe12ba2c3bd00591758f6fa30e6f47dd905c59dbfcf9b773f3a293b45996609fb6789ae29d6bfcc3cf3a6f7d9fda + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10/88eeb4da891e10b1318c4b2476b6e2ecbeb5ff97d946815ffea7794c31a89017c70d7f34b3c2ebf23ef4e9fc9fb99f7dffe36da22011b5b5c6ffa34f4873ec20 + languageName: node + linkType: hard + +"normalize-url@npm:^6.0.1": + version: 6.1.0 + resolution: "normalize-url@npm:6.1.0" + checksum: 10/5ae699402c9d5ffa330adc348fcd6fc6e6a155ab7c811b96e30b7ecab60ceef821d8f86443869671dda71bbc47f4b9625739c82ad247e883e9aefe875bfb8659 + languageName: node + linkType: hard + +"normalize-url@npm:^8.0.0": + version: 8.1.1 + resolution: "normalize-url@npm:8.1.1" + checksum: 10/a96519b53692f33d5de19a8aa04fe9de4b8de1c126da89f1c47a24e48d457008867193cd9c19218810426ffabe190034249e1b82f0751c8992f2a9f716304ce0 + languageName: node + linkType: hard + +"npm-run-path@npm:^2.0.0": + version: 2.0.2 + resolution: "npm-run-path@npm:2.0.2" + dependencies: + path-key: "npm:^2.0.0" + checksum: 10/acd5ad81648ba4588ba5a8effb1d98d2b339d31be16826a118d50f182a134ac523172101b82eab1d01cb4c2ba358e857d54cfafd8163a1ffe7bd52100b741125 + languageName: node + linkType: hard + +"npm-run-path@npm:^3.1.0": + version: 3.1.0 + resolution: "npm-run-path@npm:3.1.0" + dependencies: + path-key: "npm:^3.0.0" + checksum: 10/141e0b8f0e3b137347a2896572c9a84701754dda0670d3ceb8c56a87702ee03c26227e4517ab93f2904acfc836547315e740b8289bb24ca0cd8ba2b198043b0f + languageName: node + linkType: hard + +"npm-run-path@npm:^4.0.1": + version: 4.0.1 + resolution: "npm-run-path@npm:4.0.1" + dependencies: + path-key: "npm:^3.0.0" + checksum: 10/5374c0cea4b0bbfdfae62da7bbdf1e1558d338335f4cacf2515c282ff358ff27b2ecb91ffa5330a8b14390ac66a1e146e10700440c1ab868208430f56b5f4d23 + languageName: node + linkType: hard + +"nth-check@npm:^2.0.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: "npm:^1.0.0" + checksum: 10/5afc3dafcd1573b08877ca8e6148c52abd565f1d06b1eb08caf982e3fa289a82f2cae697ffb55b5021e146d60443f1590a5d6b944844e944714a5b549675bcd3 + languageName: node + linkType: hard + +"number-to-words-ru@npm:^2.4.1": + version: 2.4.1 + resolution: "number-to-words-ru@npm:2.4.1" + dependencies: + "@ungap/structured-clone": "npm:^1.2.0" + checksum: 10/8a0d20e179bc2f8dcb57aa292c7b0079a7c9729a30bd7f2d33f2f2546e3ecdd97744bd4d48d28a70b28ef17caef7645675f7dc9713cb03a9c9c23c4441f87466 + languageName: node + linkType: hard + +"object-assign@npm:^4, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10/fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + +"object-hash@npm:3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: 10/f498d456a20512ba7be500cef4cf7b3c183cc72c65372a549c9a0e6dd78ce26f375e9b1315c07592d3fde8f10d5019986eba35970570d477ed9a2a702514432a + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.3": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 10/aa13b1190ad3e366f6c83ad8a16ed37a19ed57d267385aa4bfdccda833d7b90465c057ff6c55d035a6b2e52c1a2295582b294217a0a3a1ae7abdd6877ef781fb + languageName: node + linkType: hard + +"on-exit-leak-free@npm:^2.1.0": + version: 2.1.2 + resolution: "on-exit-leak-free@npm:2.1.2" + checksum: 10/f7b4b7200026a08f6e4a17ba6d72e6c5cbb41789ed9cf7deaf9d9e322872c7dc5a7898549a894651ee0ee9ae635d34a678115bf8acdfba8ebd2ba2af688b563c + languageName: node + linkType: hard + +"on-finished@npm:^2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: "npm:1.1.1" + checksum: 10/8e81472c5028125c8c39044ac4ab8ba51a7cdc19a9fbd4710f5d524a74c6d8c9ded4dd0eed83f28d3d33ac1d7a6a439ba948ccb765ac6ce87f30450a26bfe2ea + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"one-time@npm:^1.0.0": + version: 1.0.0 + resolution: "one-time@npm:1.0.0" + dependencies: + fn.name: "npm:1.x.x" + checksum: 10/64d0160480eeae4e3b2a6fc0a02f452e05bb0cc8373a4ed56a4fc08c3939dcb91bc20075003ed499655bd16919feb63ca56f86eee7932c5251f7d629b55dfc90 + languageName: node + linkType: hard + +"onetime@npm:^5.1.0, onetime@npm:^5.1.2": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: "npm:^2.1.0" + checksum: 10/e9fd0695a01cf226652f0385bf16b7a24153dbbb2039f764c8ba6d2306a8506b0e4ce570de6ad99c7a6eb49520743afdb66edd95ee979c1a342554ed49a9aadd + languageName: node + linkType: hard + +"open@npm:7": + version: 7.4.2 + resolution: "open@npm:7.4.2" + dependencies: + is-docker: "npm:^2.0.0" + is-wsl: "npm:^2.1.1" + checksum: 10/4fc02ed3368dcd5d7247ad3566433ea2695b0713b041ebc0eeb2f0f9e5d4e29fc2068f5cdd500976b3464e77fe8b61662b1b059c73233ccc601fe8b16d6c1cd6 + languageName: node + linkType: hard + +"optionator@npm:^0.9.3": + version: 0.9.4 + resolution: "optionator@npm:0.9.4" + dependencies: + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + word-wrap: "npm:^1.2.5" + checksum: 10/a8398559c60aef88d7f353a4f98dcdff6090a4e70f874c827302bf1213d9106a1c4d5fcb68dacb1feb3c30a04c4102f41047aa55d4c576b863d6fc876e001af6 + languageName: node + linkType: hard + +"ora@npm:5.4.1": + version: 5.4.1 + resolution: "ora@npm:5.4.1" + dependencies: + bl: "npm:^4.1.0" + chalk: "npm:^4.1.0" + cli-cursor: "npm:^3.1.0" + cli-spinners: "npm:^2.5.0" + is-interactive: "npm:^1.0.0" + is-unicode-supported: "npm:^0.1.0" + log-symbols: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + wcwidth: "npm:^1.0.1" + checksum: 10/8d071828f40090a8e1c6e8f350c6eb065808e9ab2b3e57fa37e0d5ae78cb46dac00117c8f12c3c8b8da2923454afbd8265e08c10b69881170c5b269f451e7fef + languageName: node + linkType: hard + +"oxc-resolver@npm:^11.15.0": + version: 11.16.2 + resolution: "oxc-resolver@npm:11.16.2" + dependencies: + "@oxc-resolver/binding-android-arm-eabi": "npm:11.16.2" + "@oxc-resolver/binding-android-arm64": "npm:11.16.2" + "@oxc-resolver/binding-darwin-arm64": "npm:11.16.2" + "@oxc-resolver/binding-darwin-x64": "npm:11.16.2" + "@oxc-resolver/binding-freebsd-x64": "npm:11.16.2" + "@oxc-resolver/binding-linux-arm-gnueabihf": "npm:11.16.2" + "@oxc-resolver/binding-linux-arm-musleabihf": "npm:11.16.2" + "@oxc-resolver/binding-linux-arm64-gnu": "npm:11.16.2" + "@oxc-resolver/binding-linux-arm64-musl": "npm:11.16.2" + "@oxc-resolver/binding-linux-ppc64-gnu": "npm:11.16.2" + "@oxc-resolver/binding-linux-riscv64-gnu": "npm:11.16.2" + "@oxc-resolver/binding-linux-riscv64-musl": "npm:11.16.2" + "@oxc-resolver/binding-linux-s390x-gnu": "npm:11.16.2" + "@oxc-resolver/binding-linux-x64-gnu": "npm:11.16.2" + "@oxc-resolver/binding-linux-x64-musl": "npm:11.16.2" + "@oxc-resolver/binding-openharmony-arm64": "npm:11.16.2" + "@oxc-resolver/binding-wasm32-wasi": "npm:11.16.2" + "@oxc-resolver/binding-win32-arm64-msvc": "npm:11.16.2" + "@oxc-resolver/binding-win32-ia32-msvc": "npm:11.16.2" + "@oxc-resolver/binding-win32-x64-msvc": "npm:11.16.2" + dependenciesMeta: + "@oxc-resolver/binding-android-arm-eabi": + optional: true + "@oxc-resolver/binding-android-arm64": + optional: true + "@oxc-resolver/binding-darwin-arm64": + optional: true + "@oxc-resolver/binding-darwin-x64": + optional: true + "@oxc-resolver/binding-freebsd-x64": + optional: true + "@oxc-resolver/binding-linux-arm-gnueabihf": + optional: true + "@oxc-resolver/binding-linux-arm-musleabihf": + optional: true + "@oxc-resolver/binding-linux-arm64-gnu": + optional: true + "@oxc-resolver/binding-linux-arm64-musl": + optional: true + "@oxc-resolver/binding-linux-ppc64-gnu": + optional: true + "@oxc-resolver/binding-linux-riscv64-gnu": + optional: true + "@oxc-resolver/binding-linux-riscv64-musl": + optional: true + "@oxc-resolver/binding-linux-s390x-gnu": + optional: true + "@oxc-resolver/binding-linux-x64-gnu": + optional: true + "@oxc-resolver/binding-linux-x64-musl": + optional: true + "@oxc-resolver/binding-openharmony-arm64": + optional: true + "@oxc-resolver/binding-wasm32-wasi": + optional: true + "@oxc-resolver/binding-win32-arm64-msvc": + optional: true + "@oxc-resolver/binding-win32-ia32-msvc": + optional: true + "@oxc-resolver/binding-win32-x64-msvc": + optional: true + checksum: 10/a410cb2b336c8e4cc6dfd7a4f49f4264281b8e03588b88cef9a78c8ff9537b42dc46f986878a94f4d4ad9db4fff416f31512aa033cf36f8b8800c4d512280bdf + languageName: node + linkType: hard + +"p-cancelable@npm:^2.0.0, p-cancelable@npm:^2.1.1": + version: 2.1.1 + resolution: "p-cancelable@npm:2.1.1" + checksum: 10/7f1b64db17fc54acf359167d62898115dcf2a64bf6b3b038e4faf36fc059e5ed762fb9624df8ed04b25bee8de3ab8d72dea9879a2a960cd12e23c420a4aca6ed + languageName: node + linkType: hard + +"p-cancelable@npm:^3.0.0": + version: 3.0.0 + resolution: "p-cancelable@npm:3.0.0" + checksum: 10/a5eab7cf5ac5de83222a014eccdbfde65ecfb22005ee9bc242041f0b4441e07fac7629432c82f48868aa0f8413fe0df6c6067c16f76bf9217cd8dc651923c93d + languageName: node + linkType: hard + +"p-event@npm:4.2.0": + version: 4.2.0 + resolution: "p-event@npm:4.2.0" + dependencies: + p-timeout: "npm:^3.1.0" + checksum: 10/d03238ff31f5694f11bd7dcc0eae16c35b1ffb8cad4e5263d5422ba0bd6736dbfdb33b72745ecb6b06b98494db80f49f12c14f5e8da1212bf6a424609ad8d885 + languageName: node + linkType: hard + +"p-finally@npm:^1.0.0": + version: 1.0.0 + resolution: "p-finally@npm:1.0.0" + checksum: 10/93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10/7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10/1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 + languageName: node + linkType: hard + +"p-map@npm:^7.0.2": + version: 7.0.4 + resolution: "p-map@npm:7.0.4" + checksum: 10/ef48c3b2e488f31c693c9fcc0df0ef76518cf6426a495cf9486ebbb0fd7f31aef7f90e96f72e0070c0ff6e3177c9318f644b512e2c29e3feee8d7153fcb6782e + languageName: node + linkType: hard + +"p-retry@npm:^6.0.1": + version: 6.2.1 + resolution: "p-retry@npm:6.2.1" + dependencies: + "@types/retry": "npm:0.12.2" + is-network-error: "npm:^1.0.0" + retry: "npm:^0.13.1" + checksum: 10/7104ef13703b155d70883b0d3654ecc03148407d2711a4516739cf93139e8bec383451e14925e25e3c1ae04dbace3ed53c26dc3853c1e9b9867fcbdde25f4cdc + languageName: node + linkType: hard + +"p-timeout@npm:^3.0.0, p-timeout@npm:^3.1.0": + version: 3.2.0 + resolution: "p-timeout@npm:3.2.0" + dependencies: + p-finally: "npm:^1.0.0" + checksum: 10/3dd0eaa048780a6f23e5855df3dd45c7beacff1f820476c1d0d1bcd6648e3298752ba2c877aa1c92f6453c7dd23faaf13d9f5149fc14c0598a142e2c5e8d649c + languageName: node + linkType: hard + +"p-wait-for@npm:3.2.0": + version: 3.2.0 + resolution: "p-wait-for@npm:3.2.0" + dependencies: + p-timeout: "npm:^3.0.0" + checksum: 10/7f6840e9233d96836a233dee9c5b1c711bfdf76228d0eaea68fa74a79418f5eef6dc1915dadd28d17312a99f6a53daa308b1c18b779bc9c1dabcae3d6b35a086 + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0, package-json-from-dist@npm:^1.0.1": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10/58ee9538f2f762988433da00e26acc788036914d57c71c246bf0be1b60cdbd77dd60b6a3e1a30465f0b248aeb80079e0b34cb6050b1dfa18c06953bb1cbc7602 + languageName: node + linkType: hard + +"pako@npm:^2.1.0": + version: 2.1.0 + resolution: "pako@npm:2.1.0" + checksum: 10/38a04991d0ec4f4b92794a68b8c92bf7340692c5d980255c92148da96eb3e550df7a86a7128b5ac0c65ecddfe5ef3bbe9c6dab13e1bc315086e759b18f7c1401 + languageName: node + linkType: hard + +"pako@npm:~1.0.2": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 10/1ad07210e894472685564c4d39a08717e84c2a68a70d3c1d9e657d32394ef1670e22972a433cbfe48976cb98b154ba06855dcd3fcfba77f60f1777634bec48c0 + languageName: node + linkType: hard + +"param-case@npm:^2.1.1": + version: 2.1.1 + resolution: "param-case@npm:2.1.1" + dependencies: + no-case: "npm:^2.2.0" + checksum: 10/3a63dcb8d8dc7995a612de061afdc7bb6fe7bd0e6db994db8d4cae999ed879859fd24389090e1a0d93f4c9207ebf8c048c870f468a3f4767161753e03cb9ab58 + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10/6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff + languageName: node + linkType: hard + +"parse-json@npm:^5.2.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" + dependencies: + "@babel/code-frame": "npm:^7.0.0" + error-ex: "npm:^1.3.1" + json-parse-even-better-errors: "npm:^2.3.0" + lines-and-columns: "npm:^1.1.6" + checksum: 10/62085b17d64da57f40f6afc2ac1f4d95def18c4323577e1eced571db75d9ab59b297d1d10582920f84b15985cbfc6b6d450ccbf317644cfa176f3ed982ad87e2 + languageName: node + linkType: hard + +"parse5-htmlparser2-tree-adapter@npm:^7.0.0": + version: 7.1.0 + resolution: "parse5-htmlparser2-tree-adapter@npm:7.1.0" + dependencies: + domhandler: "npm:^5.0.3" + parse5: "npm:^7.0.0" + checksum: 10/75910af9137451e9c53e1e0d712f7393f484e89e592b1809ee62ad6cedd61b98daeaa5206ff5d9f06778002c91fac311afedde4880e1916fdb44fa71199dae73 + languageName: node + linkType: hard + +"parse5@npm:^7.0.0": + version: 7.3.0 + resolution: "parse5@npm:7.3.0" + dependencies: + entities: "npm:^6.0.0" + checksum: 10/b0e48be20b820c655b138b86fa6fb3a790de6c891aa2aba536524f8027b4dca4fe538f11a0e5cf2f6f847d120dbb9e4822dcaeb933ff1e10850a2ef0154d1d88 + languageName: node + linkType: hard + +"parseley@npm:^0.12.0": + version: 0.12.1 + resolution: "parseley@npm:0.12.1" + dependencies: + leac: "npm:^0.6.0" + peberminta: "npm:^0.9.0" + checksum: 10/64788dbe1fbbc231e0fef235357823e03ca3d915693b2109ad862293aad5d091e902fd7cf6f54763728e758a228d06497c787a9af0dfdacd6a941bc2dbf2019e + languageName: node + linkType: hard + +"parseurl@npm:^1.3.3": + version: 1.3.3 + resolution: "parseurl@npm:1.3.3" + checksum: 10/407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2 + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10/505807199dfb7c50737b057dd8d351b82c033029ab94cb10a657609e00c1bc53b951cfdbccab8de04c5584d5eff31128ce6afd3db79281874a5ef2adbba55ed1 + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 10/060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-key@npm:^2.0.0, path-key@npm:^2.0.1": + version: 2.0.1 + resolution: "path-key@npm:2.0.1" + checksum: 10/6e654864e34386a2a8e6bf72cf664dcabb76574dd54013add770b374384d438aca95f4357bb26935b514a4e4c2c9b19e191f2200b282422a76ee038b9258c5e7 + languageName: node + linkType: hard + +"path-key@npm:^3.0.0, path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10/55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10/49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a + languageName: node + linkType: hard + +"path-scurry@npm:^1.10.2, path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10/5e8845c159261adda6f09814d7725683257fcc85a18f329880ab4d7cc1d12830967eae5d5894e453f341710d5484b8fdbbd4d75181b4d6e1eb2f4dc7aeadc434 + languageName: node + linkType: hard + +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10/1e9c74e9ccf94d7c16056a5cb2dba9fa23eec1bc221ab15c44765486b9b9975b4cd9a4d55da15b96eadf67d5202e9a2f1cec9023fbb35fe7d9ccd0ff1891f88b + languageName: node + linkType: hard + +"path-to-regexp@npm:8.3.0, path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.2.0": + version: 8.3.0 + resolution: "path-to-regexp@npm:8.3.0" + checksum: 10/568f148fc64f5fd1ecebf44d531383b28df924214eabf5f2570dce9587a228e36c37882805ff02d71c6209b080ea3ee6a4d2b712b5df09741b67f1f3cf91e55a + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10/5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 + languageName: node + linkType: hard + +"peberminta@npm:^0.9.0": + version: 0.9.0 + resolution: "peberminta@npm:0.9.0" + checksum: 10/b396cf8bac836b3cfe9315e6c94747fa02bb68b252a4b176c0f7d6a3fcbdc5e9f23c3523480c91244b42f87be63f200d775587b039abe4b4731915480da2528d + languageName: node + linkType: hard + +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 10/6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d + languageName: node + linkType: hard + +"pg-cloudflare@npm:^1.2.7": + version: 1.2.7 + resolution: "pg-cloudflare@npm:1.2.7" + checksum: 10/3d171407cbce36436c461200666ba6bd884bfe98016972760a797cec850199b8024a40055d80322c5fc02909e1533a144e9b108a99f7d7e21d0c42612f9821fb + languageName: node + linkType: hard + +"pg-connection-string@npm:^2.9.1": + version: 2.9.1 + resolution: "pg-connection-string@npm:2.9.1" + checksum: 10/40e9e9cd752121e72bff18d83e6c7ecda9056426815a84294de018569a319293c924704c8b7f0604fdc588835c7927647dea4f3c87a014e715bcbb17d794e9f0 + languageName: node + linkType: hard + +"pg-int8@npm:1.0.1": + version: 1.0.1 + resolution: "pg-int8@npm:1.0.1" + checksum: 10/a1e3a05a69005ddb73e5f324b6b4e689868a447c5fa280b44cd4d04e6916a344ac289e0b8d2695d66e8e89a7fba023affb9e0e94778770ada5df43f003d664c9 + languageName: node + linkType: hard + +"pg-pool@npm:^3.10.1": + version: 3.10.1 + resolution: "pg-pool@npm:3.10.1" + peerDependencies: + pg: ">=8.0" + checksum: 10/b389a714be59ebe53ec412cbff513191cc0b7a203faa5d26416b6a038cafdfe30fbf1a5936b77bb76109c49bd7c4a116870a5a46a45796b1b34c96f016d7fbe2 + languageName: node + linkType: hard + +"pg-protocol@npm:^1.10.3": + version: 1.10.3 + resolution: "pg-protocol@npm:1.10.3" + checksum: 10/31da85319084c03f403efee7accce9786964df82a7feb60e6bd77b71f1e622c74a2a644a2bc434389d0ab92e5abdeedea69ebdb53b1897d9f01d2a1f51a8a2fe + languageName: node + linkType: hard + +"pg-types@npm:2.2.0": + version: 2.2.0 + resolution: "pg-types@npm:2.2.0" + dependencies: + pg-int8: "npm:1.0.1" + postgres-array: "npm:~2.0.0" + postgres-bytea: "npm:~1.0.0" + postgres-date: "npm:~1.0.4" + postgres-interval: "npm:^1.1.0" + checksum: 10/87a84d4baa91378d3a3da6076c69685eb905d1087bf73525ae1ba84b291b9dd8738c6716b333d8eac6cec91bf087237adc3e9281727365e9cbab0d9d072778b1 + languageName: node + linkType: hard + +"pg@npm:^8.16.0": + version: 8.16.3 + resolution: "pg@npm:8.16.3" + dependencies: + pg-cloudflare: "npm:^1.2.7" + pg-connection-string: "npm:^2.9.1" + pg-pool: "npm:^3.10.1" + pg-protocol: "npm:^1.10.3" + pg-types: "npm:2.2.0" + pgpass: "npm:1.0.5" + peerDependencies: + pg-native: ">=3.0.1" + dependenciesMeta: + pg-cloudflare: + optional: true + peerDependenciesMeta: + pg-native: + optional: true + checksum: 10/6a2885a3f581d6c6dddddf5a4bb2790ee84f402ed7d73ece8b6bc102c58c17e4c5f17894c241633aa2f1d4fedd8f2401a80a9a02ef18bb57d05cbbfd8a53ca4d + languageName: node + linkType: hard + +"pgpass@npm:1.0.5": + version: 1.0.5 + resolution: "pgpass@npm:1.0.5" + dependencies: + split2: "npm:^4.1.0" + checksum: 10/0a6f3bf76e36bdb3c20a7e8033140c732767bba7e81f845f7489fc3123a2bd6e3b8e704f08cba86b117435414b5d2422e20ba9d5f2efb6f0c75c9efca73e8e87 + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10/e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 + languageName: node + linkType: hard + +"picomatch@npm:4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10/ce617b8da36797d09c0baacb96ca8a44460452c89362d7cb8f70ca46b4158ba8bc3606912de7c818eb4a939f7f9015cef3c766ec8a0c6bfc725fdc078e39c717 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc + languageName: node + linkType: hard + +"picomatch@npm:^4.0.1, picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 10/57b99055f40b16798f2802916d9c17e9744e620a0db136554af01d19598b96e45e2f00014c91d1b8b13874b80caa8c295b3d589a3f72373ec4aaf54baa5962d5 + languageName: node + linkType: hard + +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 10/668c1dc8d9fc1b34b9ce3b16ba59deb39d4dc743527bf2ed908d2b914cb8ba40aa5ba6960b27c417c241531c5aafd0598feeac2d50cb15278cf9863fa6b02a77 + languageName: node + linkType: hard + +"pino-abstract-transport@npm:^2.0.0": + version: 2.0.0 + resolution: "pino-abstract-transport@npm:2.0.0" + dependencies: + split2: "npm:^4.0.0" + checksum: 10/e5699ecb06c7121055978e988e5cecea5b6892fc2589c64f1f86df5e7386bbbfd2ada268839e911b021c6b3123428aed7c6be3ac7940eee139556c75324c7e83 + languageName: node + linkType: hard + +"pino-std-serializers@npm:^7.0.0": + version: 7.1.0 + resolution: "pino-std-serializers@npm:7.1.0" + checksum: 10/6e27f6f885927b6df3b424ddb8a9e0e9854f3b59f4abd51afa74e1c2cf33436a505277b004bb00ce61884a962c8fdfd977391205c7baab885d6afb35fce7396a + languageName: node + linkType: hard + +"pino@npm:10.1.0": + version: 10.1.0 + resolution: "pino@npm:10.1.0" + dependencies: + "@pinojs/redact": "npm:^0.4.0" + atomic-sleep: "npm:^1.0.0" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:^2.0.0" + pino-std-serializers: "npm:^7.0.0" + process-warning: "npm:^5.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.2.0" + safe-stable-stringify: "npm:^2.3.1" + sonic-boom: "npm:^4.0.1" + thread-stream: "npm:^3.0.0" + bin: + pino: bin.js + checksum: 10/6f9c86572669842950887cf1cd45fc977903ce1be0a6e32b98918c87d1914ff8854395dc64388d75a583672439997422975977275ed92c14a2f6ee1a26b07ccf + languageName: node + linkType: hard + +"piscina@npm:^4.3.1": + version: 4.9.2 + resolution: "piscina@npm:4.9.2" + dependencies: + "@napi-rs/nice": "npm:^1.0.1" + dependenciesMeta: + "@napi-rs/nice": + optional: true + checksum: 10/2ae40fc2ba54d230c8372757608458af113ae2b5e66cb4f75bb8ec87f69c1209ab5deec41d471b3649e45571b6c9b04a2aca5d8d3459364bdc69a4dbf9c3d205 + languageName: node + linkType: hard + +"pizzip@npm:^3.2.0": + version: 3.2.0 + resolution: "pizzip@npm:3.2.0" + dependencies: + pako: "npm:^2.1.0" + checksum: 10/55667e01238735ca0ab9777286a533b898e26ad7c62438a485e2a330988b52dca8dff7b8d9675213fa1ef937d5f65478fef84378e6fb9c5877635cbc8c5e07ac + languageName: node + linkType: hard + +"pluralize@npm:8.0.0": + version: 8.0.0 + resolution: "pluralize@npm:8.0.0" + checksum: 10/17877fdfdb7ddb3639ce257ad73a7c51a30a966091e40f56ea9f2f545b5727ce548d4928f8cb3ce38e7dc0c5150407d318af6a4ed0ea5265d378473b4c2c61ec + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0": + version: 1.1.0 + resolution: "possible-typed-array-names@npm:1.1.0" + checksum: 10/2f44137b8d3dd35f4a7ba7469eec1cd9cfbb46ec164b93a5bc1f4c3d68599c9910ee3b91da1d28b4560e9cc8414c3cd56fedc07259c67e52cc774476270d3302 + languageName: node + linkType: hard + +"postgres-array@npm:~2.0.0": + version: 2.0.0 + resolution: "postgres-array@npm:2.0.0" + checksum: 10/aff99e79714d1271fe942fec4ffa2007b755e7e7dc3d2feecae3f1ceecb86fd3637c8138037fc3d9e7ec369231eeb136843c0b25927bf1ce295245a40ef849b4 + languageName: node + linkType: hard + +"postgres-bytea@npm:~1.0.0": + version: 1.0.1 + resolution: "postgres-bytea@npm:1.0.1" + checksum: 10/fc5fa49f59ac1f0eba841db55bd6b6c2232d1575d1734311e2097a2d5fd8b58e1239cbd64eeaf0b6752268fe7d2819e002bf90b0afd333be9f2b9d157d2cd7e7 + languageName: node + linkType: hard + +"postgres-date@npm:~1.0.4": + version: 1.0.7 + resolution: "postgres-date@npm:1.0.7" + checksum: 10/571ef45bec4551bb5d608c31b79987d7a895141f7d6c7b82e936a52d23d97474c770c6143e5cf8936c1cdc8b0dfd95e79f8136bf56a90164182a60f242c19f2b + languageName: node + linkType: hard + +"postgres-interval@npm:^1.1.0": + version: 1.2.0 + resolution: "postgres-interval@npm:1.2.0" + dependencies: + xtend: "npm:^4.0.0" + checksum: 10/746b71f93805ae33b03528e429dc624706d1f9b20ee81bf743263efb6a0cd79ae02a642a8a480dbc0f09547b4315ab7df6ce5ec0be77ed700bac42730f5c76b2 + languageName: node + linkType: hard + +"prebuildify@npm:^6.0.1": + version: 6.0.1 + resolution: "prebuildify@npm:6.0.1" + dependencies: + minimist: "npm:^1.2.5" + mkdirp-classic: "npm:^0.5.3" + node-abi: "npm:^3.3.0" + npm-run-path: "npm:^3.1.0" + pump: "npm:^3.0.0" + tar-fs: "npm:^2.1.0" + bin: + prebuildify: bin.js + checksum: 10/3dbec178a5bc41690a8ae9b7bb808637f8aae42fe649cff0b373b502ebd54e11cab12e49dbdbf067f4c21866420f593933ec5b100d31c647dedcfd3ff6653c53 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: 10/0b9d2c76801ca652a7f64892dd37b7e3fab149a37d2424920099bf894acccc62abb4424af2155ab36dea8744843060a2d8ddc983518d0b1e22265a22324b72ed + languageName: node + linkType: hard + +"prettier-linter-helpers@npm:^1.0.0": + version: 1.0.1 + resolution: "prettier-linter-helpers@npm:1.0.1" + dependencies: + fast-diff: "npm:^1.1.2" + checksum: 10/2dc35f5036a35f4c4f5e645887edda1436acb63687a7f12b2383e0a6f3c1f76b8a0a4709fe4d82e19157210feb5984b159bb714d43290022911ab53d606474ec + languageName: node + linkType: hard + +"prettier@npm:^3.5.3": + version: 3.7.4 + resolution: "prettier@npm:3.7.4" + bin: + prettier: bin/prettier.cjs + checksum: 10/b4d00ea13baed813cb777c444506632fb10faaef52dea526cacd03085f01f6db11fc969ccebedf05bf7d93c3960900994c6adf1b150e28a31afd5cfe7089b313 + languageName: node + linkType: hard + +"pretty-bytes@npm:^5.6.0": + version: 5.6.0 + resolution: "pretty-bytes@npm:5.6.0" + checksum: 10/9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd + languageName: node + linkType: hard + +"preview-email@npm:^3.0.19": + version: 3.1.0 + resolution: "preview-email@npm:3.1.0" + dependencies: + ci-info: "npm:^3.8.0" + display-notification: "npm:2.0.0" + fixpack: "npm:^4.0.0" + get-port: "npm:5.1.1" + mailparser: "npm:^3.7.1" + nodemailer: "npm:^6.9.13" + open: "npm:7" + p-event: "npm:4.2.0" + p-wait-for: "npm:3.2.0" + pug: "npm:^3.0.3" + uuid: "npm:^9.0.1" + checksum: 10/7d0f6fce0850848d93ac8961e674a8b5f42871f00b910bceec0adb90eaf6c27ec4f3451ef5cd29b7a4e494e31d16ee2b5c7a7c1f3a476b654e04857d02b2702e + languageName: node + linkType: hard + +"proc-log@npm:^6.0.0": + version: 6.1.0 + resolution: "proc-log@npm:6.1.0" + checksum: 10/9033f30f168ed5a0991b773d0c50ff88384c4738e9a0a67d341de36bf7293771eed648ab6a0562f62276da12fde91f3bbfc75ffff6e71ad49aafd74fc646be66 + languageName: node + linkType: hard + +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10/1d38588e520dab7cea67cbbe2efdd86a10cc7a074c09657635e34f035277b59fbb57d09d8638346bf7090f8e8ebc070c96fa5fd183b777fff4f5edff5e9466cf + languageName: node + linkType: hard + +"process-warning@npm:^5.0.0": + version: 5.0.0 + resolution: "process-warning@npm:5.0.0" + checksum: 10/10f3e00ac9fc1943ec4566ff41fff2b964e660f853c283e622257719839d340b4616e707d62a02d6aa0038761bb1fa7c56bc7308d602d51bd96f05f9cd305dcd + languageName: node + linkType: hard + +"promise-retry@npm:^1.1.1": + version: 1.1.1 + resolution: "promise-retry@npm:1.1.1" + dependencies: + err-code: "npm:^1.0.0" + retry: "npm:^0.10.0" + checksum: 10/4a30e33b09150608e052a7e6750c63ba3feb30e0049e1c01e3068d5611102c72abc4340bc542295d5efe83110a068ddd3cc1a0d5b057bcf03041f29fae575b52 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10/96e1a82453c6c96eef53a37a1d6134c9f2482f94068f98a59145d0986ca4e497bf110a410adf73857e588165eab3899f0ebcf7b3890c1b3ce802abc0d65967d4 + languageName: node + linkType: hard + +"promise@npm:^7.0.1": + version: 7.3.1 + resolution: "promise@npm:7.3.1" + dependencies: + asap: "npm:~2.0.3" + checksum: 10/37dbe58ca7b0716cc881f0618128f1fd6ff9c46cdc529a269fd70004e567126a449a94e9428e2d19b53d06182d11b45d0c399828f103e06b2bb87643319bd2e7 + languageName: node + linkType: hard + +"promise@npm:~1.3.0": + version: 1.3.0 + resolution: "promise@npm:1.3.0" + dependencies: + is-promise: "npm:~1" + checksum: 10/f7b0264e2591bbcd557141c86407c3754266b5229d5ca401162de0e6a174a7c7fd9123458d4c9c2976d9b94a2d36673c146c3f8cb0106ad68476421f32a8d9ec + languageName: node + linkType: hard + +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: 10/9cc3b46d613fa0d637033b225db1bc98e914c3c05864f7adc9bee728192e353125ef2e49f71129a413f6333951756000b0e54f299d921f02d3e9e370cc994100 + languageName: node + linkType: hard + +"protobufjs@npm:^7.2.5, protobufjs@npm:^7.3.0, protobufjs@npm:^7.5.3": + version: 7.5.4 + resolution: "protobufjs@npm:7.5.4" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.4" + "@protobufjs/eventemitter": "npm:^1.1.0" + "@protobufjs/fetch": "npm:^1.1.0" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/inquire": "npm:^1.1.0" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.0" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.0.0" + checksum: 10/88d677bb6f11a2ecec63fdd053dfe6d31120844d04e865efa9c8fbe0674cd077d6624ecfdf014018a20dcb114ae2a59c1b21966dd8073e920650c71370966439 + languageName: node + linkType: hard + +"proxy-addr@npm:^2.0.7": + version: 2.0.7 + resolution: "proxy-addr@npm:2.0.7" + dependencies: + forwarded: "npm:0.2.0" + ipaddr.js: "npm:1.9.1" + checksum: 10/f24a0c80af0e75d31e3451398670d73406ec642914da11a2965b80b1898ca6f66a0e3e091a11a4327079b2b268795f6fa06691923fef91887215c3d0e8ea3f68 + languageName: node + linkType: hard + +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: 10/f0bb4a87cfd18f77bc2fba23ae49c3b378fb35143af16cc478171c623eebe181678f09439707ad80081d340d1593cd54a33a0113f3ccb3f4bc9451488780ee23 + languageName: node + linkType: hard + +"pug-attrs@npm:^3.0.0": + version: 3.0.0 + resolution: "pug-attrs@npm:3.0.0" + dependencies: + constantinople: "npm:^4.0.1" + js-stringify: "npm:^1.0.2" + pug-runtime: "npm:^3.0.0" + checksum: 10/2ca2d34de3065239f01f0fc3c0e104c17f7a7105684d088bb71df623005a45f40a2301e65f49ec4581bb31794c74e691862643d4e34062d1509e92fa56a15aa5 + languageName: node + linkType: hard + +"pug-code-gen@npm:^3.0.3": + version: 3.0.3 + resolution: "pug-code-gen@npm:3.0.3" + dependencies: + constantinople: "npm:^4.0.1" + doctypes: "npm:^1.1.0" + js-stringify: "npm:^1.0.2" + pug-attrs: "npm:^3.0.0" + pug-error: "npm:^2.1.0" + pug-runtime: "npm:^3.0.1" + void-elements: "npm:^3.1.0" + with: "npm:^7.0.0" + checksum: 10/1918b2a75794b730ee29fc2278658ff2ccb74445742c175c55b18e414cf038e5ac5802e71db070b08f92c5304a66e141dc2261e401be4d5884f1c0bcfb3194ee + languageName: node + linkType: hard + +"pug-error@npm:^2.0.0, pug-error@npm:^2.1.0": + version: 2.1.0 + resolution: "pug-error@npm:2.1.0" + checksum: 10/9aefacfa156f0eb439ddab86c7136f998a532481a80665c9fb6b998afeea5bc8c4f83eb6ad8a4c7804c44927737df913b768b713995e6892112bbc05762e5415 + languageName: node + linkType: hard + +"pug-filters@npm:^4.0.0": + version: 4.0.0 + resolution: "pug-filters@npm:4.0.0" + dependencies: + constantinople: "npm:^4.0.1" + jstransformer: "npm:1.0.0" + pug-error: "npm:^2.0.0" + pug-walk: "npm:^2.0.0" + resolve: "npm:^1.15.1" + checksum: 10/ca8b7ffede57d13679ec8c3ee2791feabb7ab3972e02f16fffe328ab9de42961758c3115b0536b2e6cf14a4dc2b2381a172adba84423be4137298fd59ff92853 + languageName: node + linkType: hard + +"pug-lexer@npm:^5.0.1": + version: 5.0.1 + resolution: "pug-lexer@npm:5.0.1" + dependencies: + character-parser: "npm:^2.2.0" + is-expression: "npm:^4.0.0" + pug-error: "npm:^2.0.0" + checksum: 10/18d74a2dfbee892a71ca973e72be60acc36a30b5b7325e2cd723691779e505bfecd2206453b09c2b7f868af9ec0204ed4ea7a26c2a835172a22618b350b6aeb1 + languageName: node + linkType: hard + +"pug-linker@npm:^4.0.0": + version: 4.0.0 + resolution: "pug-linker@npm:4.0.0" + dependencies: + pug-error: "npm:^2.0.0" + pug-walk: "npm:^2.0.0" + checksum: 10/423f62e8600fb66c785ef4e11d9a7833a959677d67443980fd66248da56cebed0a8867f7aa78dc5631803cd1ce71a00b0abf78229b2a2d2ec8779a8b3afb5079 + languageName: node + linkType: hard + +"pug-load@npm:^3.0.0": + version: 3.0.0 + resolution: "pug-load@npm:3.0.0" + dependencies: + object-assign: "npm:^4.1.1" + pug-walk: "npm:^2.0.0" + checksum: 10/1800ec51994c92338401bcf79bbfa0d5ef9aa312bc415c2618263d6c04d1d7c5be5ac4a333c47a0eaa823f6231b4ade1a1c40f5784b99eb576d25853597bff2f + languageName: node + linkType: hard + +"pug-parser@npm:^6.0.0": + version: 6.0.0 + resolution: "pug-parser@npm:6.0.0" + dependencies: + pug-error: "npm:^2.0.0" + token-stream: "npm:1.0.0" + checksum: 10/4c23e154ea2c8c4355ee0291fefa7210f24beecff7c4af2d1e8b7e86ce2923d3213f31bbd9e33bd6703c25ea625dc6494a2ca68a21dcb105b5ac9204248cf4a8 + languageName: node + linkType: hard + +"pug-runtime@npm:^3.0.0, pug-runtime@npm:^3.0.1": + version: 3.0.1 + resolution: "pug-runtime@npm:3.0.1" + checksum: 10/d34ee1b95121576bd389dccd2f6d7dc6fb0bf24963d2b7d1471795d35d8fba90ee8e16c2e022084bdc2f2cfbd56aaa2f452ea872135baf54dbb54a0d5aedd856 + languageName: node + linkType: hard + +"pug-strip-comments@npm:^2.0.0": + version: 2.0.0 + resolution: "pug-strip-comments@npm:2.0.0" + dependencies: + pug-error: "npm:^2.0.0" + checksum: 10/2cfcbf506c14bb3e64204a1d93f12ca61658d2540475b0f0911c35531ad28421e8d1e73a646d841d58cfa2c20f8593c52e492dfe5b6bec968e20b614e4dea1e4 + languageName: node + linkType: hard + +"pug-walk@npm:^2.0.0": + version: 2.0.0 + resolution: "pug-walk@npm:2.0.0" + checksum: 10/bee64e133b711e1ed58022c0869b59e62f9f3ebb7084293857f074120b3cb588e7b8f74c4566426bf2b26dc1ec176ca6b64a2d1e53782f3fbbe039c5d4816638 + languageName: node + linkType: hard + +"pug@npm:^3.0.2, pug@npm:^3.0.3": + version: 3.0.3 + resolution: "pug@npm:3.0.3" + dependencies: + pug-code-gen: "npm:^3.0.3" + pug-filters: "npm:^4.0.0" + pug-lexer: "npm:^5.0.1" + pug-linker: "npm:^4.0.0" + pug-load: "npm:^3.0.0" + pug-parser: "npm:^6.0.0" + pug-runtime: "npm:^3.0.1" + pug-strip-comments: "npm:^2.0.0" + checksum: 10/a88364757512e3b9af024c008f23b910de049659655b5d9e6ca42f996d7849ce1aab059f61e2d44ccce0dde5ff291995682338c285e9f76f3e5bfa02de9c481b + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.3 + resolution: "pump@npm:3.0.3" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10/52843fc933b838c0330f588388115a1b28ef2a5ffa7774709b142e35431e8ab0c2edec90de3fa34ebb72d59fef854f151eea7dfc211b6dcf586b384556bd2f39 + languageName: node + linkType: hard + +"punycode.js@npm:2.3.1": + version: 2.3.1 + resolution: "punycode.js@npm:2.3.1" + checksum: 10/f0e946d1edf063f9e3d30a32ca86d8ff90ed13ca40dad9c75d37510a04473340cfc98db23a905cc1e517b1e9deb0f6021dce6f422ace235c60d3c9ac47c5a16a + languageName: node + linkType: hard + +"punycode@npm:^2.1.0": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: 10/febdc4362bead22f9e2608ff0171713230b57aff9dddc1c273aa2a651fbd366f94b7d6a71d78342a7c0819906750351ca7f2edd26ea41b626d87d6a13d1bd059 + languageName: node + linkType: hard + +"qs@npm:^6.11.0, qs@npm:^6.14.0, qs@npm:^6.14.1, qs@npm:^6.7.0, qs@npm:^6.9.4": + version: 6.14.1 + resolution: "qs@npm:6.14.1" + dependencies: + side-channel: "npm:^1.1.0" + checksum: 10/34b5ab00a910df432d55180ef39c1d1375e550f098b5ec153b41787f1a6a6d7e5f9495593c3b112b77dbc6709d0ae18e55b82847a4c2bbbb0de1e8ccbb1794c5 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10/72900df0616e473e824202113c3df6abae59150dfb73ed13273503127235320e9c8ca4aaaaccfd58cf417c6ca92a6e68ee9a5c3182886ae949a768639b388a7b + languageName: node + linkType: hard + +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 10/591eca457509a99368b623db05248c1193aa3cedafc9a077d7acab09495db1231017ba3ad1b5386e5633271edd0a03b312d8640a59ee585b8516a42e15438aa7 + languageName: node + linkType: hard + +"quick-lru@npm:^5.1.1": + version: 5.1.1 + resolution: "quick-lru@npm:5.1.1" + checksum: 10/a516faa25574be7947969883e6068dbe4aa19e8ef8e8e0fd96cddd6d36485e9106d85c0041a27153286b0770b381328f4072aa40d3b18a19f5f7d2b78b94b5ed + languageName: node + linkType: hard + +"quoted-printable@npm:^1.0.0, quoted-printable@npm:^1.0.1": + version: 1.0.1 + resolution: "quoted-printable@npm:1.0.1" + dependencies: + utf8: "npm:^2.1.0" + bin: + quoted-printable: bin/quoted-printable + checksum: 10/d58db7b5bcf59c0f173f0f184cb26ac43ae698a1bbf8cd355f79bfb702aa4ac79c8d1f8a6dbcdfa843a833dfa03130c43c8711a94375c7ac6ec3c55f9f2b22af + languageName: node + linkType: hard + +"randombytes@npm:^2.1.0": + version: 2.1.0 + resolution: "randombytes@npm:2.1.0" + dependencies: + safe-buffer: "npm:^5.1.0" + checksum: 10/4efd1ad3d88db77c2d16588dc54c2b52fd2461e70fe5724611f38d283857094fe09040fa2c9776366803c3152cf133171b452ef717592b65631ce5dc3a2bdafc + languageName: node + linkType: hard + +"range-parser@npm:^1.2.1": + version: 1.2.1 + resolution: "range-parser@npm:1.2.1" + checksum: 10/ce21ef2a2dd40506893157970dc76e835c78cf56437e26e19189c48d5291e7279314477b06ac38abd6a401b661a6840f7b03bd0b1249da9b691deeaa15872c26 + languageName: node + linkType: hard + +"raw-body@npm:^3.0.1": + version: 3.0.2 + resolution: "raw-body@npm:3.0.2" + dependencies: + bytes: "npm:~3.1.2" + http-errors: "npm:~2.0.1" + iconv-lite: "npm:~0.7.0" + unpipe: "npm:~1.0.0" + checksum: 10/4168c82157bd69175d5bd960e59b74e253e237b358213694946a427a6f750a18b8e150f036fed3421b3e83294b071a4e2bb01037a79ccacdac05360c63d3ebba + languageName: node + linkType: hard + +"rc@npm:^1.2.8": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: "npm:^0.6.0" + ini: "npm:~1.3.0" + minimist: "npm:^1.2.0" + strip-json-comments: "npm:~2.0.1" + bin: + rc: ./cli.js + checksum: 10/5c4d72ae7eec44357171585938c85ce066da8ca79146b5635baf3d55d74584c92575fa4e2c9eac03efbed3b46a0b2e7c30634c012b4b4fa40d654353d3c163eb + languageName: node + linkType: hard + +"readable-stream@npm:1.1.x": + version: 1.1.14 + resolution: "readable-stream@npm:1.1.14" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.1" + isarray: "npm:0.0.1" + string_decoder: "npm:~0.10.x" + checksum: 10/1aa2cf4bd02f9ab3e1d57842a43a413b52be5300aa089ad1f2e3cea00684532d73edc6a2ba52b0c3210d8b57eb20a695a6d2b96d1c6085ee979c6021ad48ad20 + languageName: node + linkType: hard + +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.5, readable-stream@npm:~2.3.6": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10/8500dd3a90e391d6c5d889256d50ec6026c059fadee98ae9aa9b86757d60ac46fff24fafb7a39fa41d54cb39d8be56cc77be202ebd4cd8ffcf4cb226cbaa40d4 + languageName: node + linkType: hard + +"readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.1, readable-stream@npm:^3.6.2": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: "npm:^2.0.3" + string_decoder: "npm:^1.1.1" + util-deprecate: "npm:^1.0.1" + checksum: 10/d9e3e53193adcdb79d8f10f2a1f6989bd4389f5936c6f8b870e77570853561c362bee69feca2bbb7b32368ce96a85504aa4cedf7cf80f36e6a9de30d64244048 + languageName: node + linkType: hard + +"readdir-glob@npm:^1.1.2": + version: 1.1.3 + resolution: "readdir-glob@npm:1.1.3" + dependencies: + minimatch: "npm:^5.1.0" + checksum: 10/ca3a20aa1e715d671302d4ec785a32bf08e59d6d0dd25d5fc03e9e5a39f8c612cdf809ab3e638a79973db7ad6868492edf38504701e313328e767693671447d6 + languageName: node + linkType: hard + +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10/7b817c265940dba90bb9c94d82920d76c3a35ea2d67f9f9d8bd936adcfe02d50c802b14be3dd2e725e002dddbe2cc1c7a0edfb1bc3a365c9dfd5a61e612eea1e + languageName: node + linkType: hard + +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10/196b30ef6ccf9b6e18c4e1724b7334f72a093d011a99f3b5920470f0b3406a51770867b3e1ae9711f227ef7a7065982f6ee2ce316746b2cb42c88efe44297fe7 + languageName: node + linkType: hard + +"real-require@npm:^0.2.0": + version: 0.2.0 + resolution: "real-require@npm:0.2.0" + checksum: 10/ddf44ee76301c774e9c9f2826da8a3c5c9f8fc87310f4a364e803ef003aa1a43c378b4323051ced212097fff1af459070f4499338b36a7469df1d4f7e8c0ba4c + languageName: node + linkType: hard + +"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0": + version: 1.2.0 + resolution: "redis-errors@npm:1.2.0" + checksum: 10/001c11f63ddd52d7c80eb4f4ede3a9433d29a458a7eea06b9154cb37c9802a218d93b7988247aa8c958d4b5d274b18354e8853c148f1096fda87c6e675cfd3ee + languageName: node + linkType: hard + +"redis-parser@npm:^3.0.0": + version: 3.0.0 + resolution: "redis-parser@npm:3.0.0" + dependencies: + redis-errors: "npm:^1.0.0" + checksum: 10/b10846844b4267f19ce1a6529465819c3d78c3e89db7eb0c3bb4eb19f83784797ec411274d15a77dbe08038b48f95f76014b83ca366dc955a016a3a0a0234650 + languageName: node + linkType: hard + +"reflect-metadata@npm:^0.2.1, reflect-metadata@npm:^0.2.2": + version: 0.2.2 + resolution: "reflect-metadata@npm:0.2.2" + checksum: 10/1c93f9ac790fea1c852fde80c91b2760420069f4862f28e6fae0c00c6937a56508716b0ed2419ab02869dd488d123c4ab92d062ae84e8739ea7417fae10c4745 + languageName: node + linkType: hard + +"relateurl@npm:^0.2.7": + version: 0.2.7 + resolution: "relateurl@npm:0.2.7" + checksum: 10/f5d6ba58f2a5d5076389090600c243a0ba7072bcf347490a09e4241e2427ccdb260b4e22cea7be4f1fcd3c2bf05908b1e0d0bc9605e3199d4ecf37af1d5681fa + languageName: node + linkType: hard + +"request-ip@npm:^3.3.0": + version: 3.3.0 + resolution: "request-ip@npm:3.3.0" + checksum: 10/9ca26f814201da19cb6f1a18da4f036803b770665ec0e7c556ea975ba553321922a5f04909f6dfc2371f695ca8aaa3c66f02c00a5e902c76435029804cdc4964 + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10/a72468e2589270d91f06c7d36ec97a88db53ae5d6fe3787fadc943f0b0276b10347f89b363b2a82285f650bdcc135ad4a257c61bdd4d00d6df1fa24875b0ddaf + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: 10/839a3a890102a658f4cb3e7b2aa13a1f80a3a976b512020c3d1efc418491c48a886b6e481ea56afc6c4cb5eef678f23b2a4e70575e7534eccadf5e30ed2e56eb + languageName: node + linkType: hard + +"require-in-the-middle@npm:^7.4.0": + version: 7.5.2 + resolution: "require-in-the-middle@npm:7.5.2" + dependencies: + debug: "npm:^4.3.5" + module-details-from-path: "npm:^1.0.3" + resolve: "npm:^1.22.8" + checksum: 10/d8f137d72eec1c53987647d19cd3bd2c64d5417bcd06b9ac8f7a14e83924c1e7636e327df7d96066a2b446b41f50d0bc1856a521388d5e90ba5c3b18dd5ab4e8 + languageName: node + linkType: hard + +"resolve-alpn@npm:^1.0.0, resolve-alpn@npm:^1.2.0": + version: 1.2.1 + resolution: "resolve-alpn@npm:1.2.1" + checksum: 10/744e87888f0b6fa0b256ab454ca0b9c0b80808715e2ef1f3672773665c92a941f6181194e30ccae4a8cd0adbe0d955d3f133102636d2ee0cca0119fec0bc9aec + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10/91eb76ce83621eea7bbdd9b55121a5c1c4a39e54a9ce04a9ad4517f102f8b5131c2cf07622c738a6683991bf54f2ce178f5a42803ecbd527ddc5105f362cc9e3 + languageName: node + linkType: hard + +"resolve@npm:^1.15.1, resolve@npm:^1.22.8": + version: 1.22.11 + resolution: "resolve@npm:1.22.11" + dependencies: + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/e1b2e738884a08de03f97ee71494335eba8c2b0feb1de9ae065e82c48997f349f77a2b10e8817e147cf610bfabc4b1cb7891ee8eaf5bf80d4ad514a34c4fab0a + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.15.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": + version: 1.22.11 + resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/fd342cad25e52cd6f4f3d1716e189717f2522bfd6641109fe7aa372f32b5714a296ed7c238ddbe7ebb0c1ddfe0b7f71c9984171024c97cf1b2073e3e40ff71a8 + languageName: node + linkType: hard + +"responselike@npm:^2.0.0": + version: 2.0.1 + resolution: "responselike@npm:2.0.1" + dependencies: + lowercase-keys: "npm:^2.0.0" + checksum: 10/b122535466e9c97b55e69c7f18e2be0ce3823c5d47ee8de0d9c0b114aa55741c6db8bfbfce3766a94d1272e61bfb1ebf0a15e9310ac5629fbb7446a861b4fd3a + languageName: node + linkType: hard + +"responselike@npm:^3.0.0": + version: 3.0.0 + resolution: "responselike@npm:3.0.0" + dependencies: + lowercase-keys: "npm:^3.0.0" + checksum: 10/e0cc9be30df4f415d6d83cdede3c5c887cd4a73e7cc1708bcaab1d50a28d15acb68460ac5b02bcc55a42f3d493729c8856427dcf6e57e6e128ad05cba4cfb95e + languageName: node + linkType: hard + +"restore-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "restore-cursor@npm:3.1.0" + dependencies: + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + checksum: 10/f877dd8741796b909f2a82454ec111afb84eb45890eb49ac947d87991379406b3b83ff9673a46012fca0d7844bb989f45cc5b788254cf1a39b6b5a9659de0630 + languageName: node + linkType: hard + +"retry@npm:^0.10.0": + version: 0.10.1 + resolution: "retry@npm:0.10.1" + checksum: 10/97d165ac7d70c74754ac0855f5b1c9fc31f90aa6ecb10b0702dca3abe8b78e3c7fc18ba0a4e8da928cf7bcae1c92ba7902843127f29b630b5ac986e9b2d4e2e4 + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10/1f914879f97e7ee931ad05fe3afa629bd55270fc6cf1c1e589b6a99fab96d15daad0fa1a52a00c729ec0078045fe3e399bd4fd0c93bcc906957bdc17f89cb8e6 + languageName: node + linkType: hard + +"retry@npm:^0.13.1": + version: 0.13.1 + resolution: "retry@npm:0.13.1" + checksum: 10/6125ec2e06d6e47e9201539c887defba4e47f63471db304c59e4b82fc63c8e89ca06a77e9d34939a9a42a76f00774b2f46c0d4a4cbb3e287268bd018ed69426d + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.1.0 + resolution: "reusify@npm:1.1.0" + checksum: 10/af47851b547e8a8dc89af144fceee17b80d5beaf5e6f57ed086432d79943434ff67ca526e92275be6f54b6189f6920a24eace75c2657eed32d02c400312b21ec + languageName: node + linkType: hard + +"rfdc@npm:^1.3.0": + version: 1.4.1 + resolution: "rfdc@npm:1.4.1" + checksum: 10/2f3d11d3d8929b4bfeefc9acb03aae90f971401de0add5ae6c5e38fec14f0405e6a4aad8fdb76344bfdd20c5193110e3750cbbd28ba86d73729d222b6cf4a729 + languageName: node + linkType: hard + +"rimraf@npm:2": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: ./bin.js + checksum: 10/4586c296c736483e297da7cffd19475e4a3e41d07b1ae124aad5d687c79e4ffa716bdac8732ed1db942caf65271cee9dd39f8b639611de161a2753e2112ffe1d + languageName: node + linkType: hard + +"rimraf@npm:^6.0.1": + version: 6.1.2 + resolution: "rimraf@npm:6.1.2" + dependencies: + glob: "npm:^13.0.0" + package-json-from-dist: "npm:^1.0.1" + bin: + rimraf: dist/esm/bin.mjs + checksum: 10/add8e566fe903f59d7b55c6c2382320c48302778640d1951baf247b3b451af496c2dee7195c204a8c646fd6327feadd1f5b61ce68c1362d4898075a726d83cc6 + languageName: node + linkType: hard + +"ringbufferjs@npm:^2.0.0": + version: 2.0.0 + resolution: "ringbufferjs@npm:2.0.0" + checksum: 10/d33628f0f273f53e6b914f1add674dcade478bf8c520ceb9f7bfdc36ce656701dcd49e45dcc819615a44b80f874c9aeb935ec5138190a576dc4b0d0756719cfe + languageName: node + linkType: hard + +"router@npm:^2.2.0": + version: 2.2.0 + resolution: "router@npm:2.2.0" + dependencies: + debug: "npm:^4.4.0" + depd: "npm:^2.0.0" + is-promise: "npm:^4.0.0" + parseurl: "npm:^1.3.3" + path-to-regexp: "npm:^8.0.0" + checksum: 10/8949bd1d3da5403cc024e2989fee58d7fda0f3ffe9f2dc5b8a192f295f400b3cde307b0b554f7d44851077640f36962ca469a766b3d57410d7d96245a7ba6c91 + languageName: node + linkType: hard + +"run-applescript@npm:^3.0.0": + version: 3.2.0 + resolution: "run-applescript@npm:3.2.0" + dependencies: + execa: "npm:^0.10.0" + checksum: 10/fbd3fa1c3749f3f1504c27b4ab954952517d8c3275326131f204f5f2b2428b990812c17a342f109ede1432174ba10c1acb2b98f4f68e0c77c3aacd2fcf829444 + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10/cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d + languageName: node + linkType: hard + +"rxjs@npm:*, rxjs@npm:^7.8.2": + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10/03dff09191356b2b87d94fbc1e97c4e9eb3c09d4452399dddd451b09c2f1ba8d56925a40af114282d7bc0c6fe7514a2236ca09f903cf70e4bbf156650dddb49d + languageName: node + linkType: hard + +"rxjs@npm:7.8.1": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10/b10cac1a5258f885e9dd1b70d23c34daeb21b61222ee735d2ec40a8685bdca40429000703a44f0e638c27a684ac139e1c37e835d2a0dc16f6fc061a138ae3abb + languageName: node + linkType: hard + +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451 + languageName: node + linkType: hard + +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: 10/7eb5b48f2ed9a594a4795677d5a150faa7eb54483b2318b568dc0c4fc94092a6cce5be02c7288a0500a156282f5276d5688bce7259299568d1053b2150ef374a + languageName: node + linkType: hard + +"safe-stable-stringify@npm:^2.3.1": + version: 2.5.0 + resolution: "safe-stable-stringify@npm:2.5.0" + checksum: 10/2697fa186c17c38c3ca5309637b4ac6de2f1c3d282da27cd5e1e3c88eca0fb1f9aea568a6aabdf284111592c8782b94ee07176f17126031be72ab1313ed46c5c + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83 + languageName: node + linkType: hard + +"saxes@npm:^5.0.1": + version: 5.0.1 + resolution: "saxes@npm:5.0.1" + dependencies: + xmlchars: "npm:^2.2.0" + checksum: 10/148b5f98fdd45df25fa1abef35d72cdf6457ac5aef3b7d59d60f770af09d8cf6e7e3a074197071222441d68670fd3198590aba9985e37c4738af2df2f44d0686 + languageName: node + linkType: hard + +"schema-utils@npm:^3.1.1": + version: 3.3.0 + resolution: "schema-utils@npm:3.3.0" + dependencies: + "@types/json-schema": "npm:^7.0.8" + ajv: "npm:^6.12.5" + ajv-keywords: "npm:^3.5.2" + checksum: 10/2c7bbb1da967fdfd320e6cea538949006ec6e8c13ea560a4f94ff2c56809a8486fa5ec419e023452501a6befe1ca381e409c2798c24f4993c7c4094d97fdb258 + languageName: node + linkType: hard + +"schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.3": + version: 4.3.3 + resolution: "schema-utils@npm:4.3.3" + dependencies: + "@types/json-schema": "npm:^7.0.9" + ajv: "npm:^8.9.0" + ajv-formats: "npm:^2.1.1" + ajv-keywords: "npm:^5.1.0" + checksum: 10/dba77a46ad7ff0c906f7f09a1a61109e6cb56388f15a68070b93c47a691f516c6a3eb454f81a8cceb0a0e55b87f8b05770a02bfb1f4e0a3143b5887488b2f900 + languageName: node + linkType: hard + +"scmp@npm:^2.1.0": + version: 2.1.0 + resolution: "scmp@npm:2.1.0" + checksum: 10/1a21c91d98891e61b411bf3c494482c8b47a8adece11356569c28328d983b98089eec19ff77005b74ec536ddac6ea2b7679a72b80e6a04774fc18a4a1f37f939 + languageName: node + linkType: hard + +"seedrandom@npm:^3.0.5": + version: 3.0.5 + resolution: "seedrandom@npm:3.0.5" + checksum: 10/acad5e516c04289f61c2fb9848f449b95f58362b75406b79ec51e101ec885293fc57e3675d2f39f49716336559d7190f7273415d185fead8cd27b171ebf7d8fb + languageName: node + linkType: hard + +"seek-bzip@npm:^2.0.0": + version: 2.0.0 + resolution: "seek-bzip@npm:2.0.0" + dependencies: + commander: "npm:^6.0.0" + bin: + seek-bunzip: bin/seek-bunzip + seek-table: bin/seek-bzip-table + checksum: 10/38d49a2091ea4a01835662f606076cf032bae63480a10c84eb61dd810286f9ab24d275000a4a17e2efadcfa27bcf2b0dbeff7dabf9011487922f75bad6e57871 + languageName: node + linkType: hard + +"selderee@npm:^0.11.0": + version: 0.11.0 + resolution: "selderee@npm:0.11.0" + dependencies: + parseley: "npm:^0.12.0" + checksum: 10/9f697a00b8270354777a8423e555fd3168abead1304b8d267412877a4b007830624d8aa562eb29a3ec2d9d2f7f977808d17b790b9c210a7d828c12ed9ef0f1f0 + languageName: node + linkType: hard + +"semver-regex@npm:^4.0.5": + version: 4.0.5 + resolution: "semver-regex@npm:4.0.5" + checksum: 10/b9e5c0573c4a997fb7e6e76321385d254797e86c8dba5e23f3cd8cf8f40b40414097a51514e5fead61dcb88ff10d3676355c01e2040f3c68f6c24bfd2073da2e + languageName: node + linkType: hard + +"semver-truncate@npm:^3.0.0": + version: 3.0.0 + resolution: "semver-truncate@npm:3.0.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10/d8c23812218ff147f512ac4830e86860a377dba8a9733ae97d816102aca33236fa1c44c06544727153fffb93d15d0e45c49b2c40a7964aa3671769e9aed2f3f9 + languageName: node + linkType: hard + +"semver@npm:^5.5.0, semver@npm:^5.6.0": + version: 5.7.2 + resolution: "semver@npm:5.7.2" + bin: + semver: bin/semver + checksum: 10/fca14418a174d4b4ef1fecb32c5941e3412d52a4d3d85165924ce3a47fbc7073372c26faf7484ceb4bbc2bde25880c6b97e492473dc7e9708fdfb1c6a02d546e + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.7.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 + languageName: node + linkType: hard + +"semver@npm:~5.3.0": + version: 5.3.0 + resolution: "semver@npm:5.3.0" + bin: + semver: ./bin/semver + checksum: 10/ff3ac60aaa4855a723cc5784c43cf34674096b823037e0e7bb84aa7612acf9093c55c1b47c431f5ebb0ba74299e6d555e89ade74f2e69c348e58eecbd6d61b5e + languageName: node + linkType: hard + +"send@npm:^1.1.0, send@npm:^1.2.0": + version: 1.2.1 + resolution: "send@npm:1.2.1" + dependencies: + debug: "npm:^4.4.3" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + etag: "npm:^1.8.1" + fresh: "npm:^2.0.0" + http-errors: "npm:^2.0.1" + mime-types: "npm:^3.0.2" + ms: "npm:^2.1.3" + on-finished: "npm:^2.4.1" + range-parser: "npm:^1.2.1" + statuses: "npm:^2.0.2" + checksum: 10/274f842d69ccfa49d4940a85598c6825da58dee6cb8ea33b08d5bd3988e6a82267c4d7c32b23d0e4706aad076ee95b1edfa13f859877db9b589829019397e355 + languageName: node + linkType: hard + +"serialize-javascript@npm:^6.0.2": + version: 6.0.2 + resolution: "serialize-javascript@npm:6.0.2" + dependencies: + randombytes: "npm:^2.1.0" + checksum: 10/445a420a6fa2eaee4b70cbd884d538e259ab278200a2ededd73253ada17d5d48e91fb1f4cd224a236ab62ea7ba0a70c6af29fc93b4f3d3078bf7da1c031fde58 + languageName: node + linkType: hard + +"serve-static@npm:^2.2.0": + version: 2.2.1 + resolution: "serve-static@npm:2.2.1" + dependencies: + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + parseurl: "npm:^1.3.3" + send: "npm:^1.2.0" + checksum: 10/71500fe80cc7163fec04e4297de7591ad1cb682d137fc030e7a53e57040fda5187e8082a9c1b2ef37f1d3f9c27c9a94d4ba61806ebc28938ba4a7c8947c9f71e + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.2": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10/505d62b8e088468917ca4e3f8f39d0e29f9a563b97dbebf92f4bd2c3172ccfb3c5b8e4566d5fcd00784a00433900e7cb8fbc404e2dbd8c3818ba05bb9d4a8a6d + languageName: node + linkType: hard + +"setimmediate@npm:^1.0.5, setimmediate@npm:~1.0.4": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: 10/76e3f5d7f4b581b6100ff819761f04a984fa3f3990e72a6554b57188ded53efce2d3d6c0932c10f810b7c59414f85e2ab3c11521877d1dea1ce0b56dc906f485 + languageName: node + linkType: hard + +"setprototypeof@npm:~1.2.0": + version: 1.2.0 + resolution: "setprototypeof@npm:1.2.0" + checksum: 10/fde1630422502fbbc19e6844346778f99d449986b2f9cdcceb8326730d2f3d9964dbcb03c02aaadaefffecd0f2c063315ebea8b3ad895914bf1afc1747fc172e + languageName: node + linkType: hard + +"sha.js@npm:^2.4.12": + version: 2.4.12 + resolution: "sha.js@npm:2.4.12" + dependencies: + inherits: "npm:^2.0.4" + safe-buffer: "npm:^5.2.1" + to-buffer: "npm:^1.2.0" + bin: + sha.js: bin.js + checksum: 10/39c0993592c2ab34eb2daae2199a2a1d502713765aecb611fd97c0c4ab7cd53e902d628e1962aaf384bafd28f55951fef46dcc78799069ce41d74b03aa13b5a7 + languageName: node + linkType: hard + +"sharp@npm:^0.34.2": + version: 0.34.5 + resolution: "sharp@npm:0.34.5" + dependencies: + "@img/colour": "npm:^1.0.0" + "@img/sharp-darwin-arm64": "npm:0.34.5" + "@img/sharp-darwin-x64": "npm:0.34.5" + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + "@img/sharp-linux-arm": "npm:0.34.5" + "@img/sharp-linux-arm64": "npm:0.34.5" + "@img/sharp-linux-ppc64": "npm:0.34.5" + "@img/sharp-linux-riscv64": "npm:0.34.5" + "@img/sharp-linux-s390x": "npm:0.34.5" + "@img/sharp-linux-x64": "npm:0.34.5" + "@img/sharp-linuxmusl-arm64": "npm:0.34.5" + "@img/sharp-linuxmusl-x64": "npm:0.34.5" + "@img/sharp-wasm32": "npm:0.34.5" + "@img/sharp-win32-arm64": "npm:0.34.5" + "@img/sharp-win32-ia32": "npm:0.34.5" + "@img/sharp-win32-x64": "npm:0.34.5" + detect-libc: "npm:^2.1.2" + semver: "npm:^7.7.3" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-riscv64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-ppc64": + optional: true + "@img/sharp-linux-riscv64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10/d62bc638c8ad382dffc266beeaffab71457d592abeb6fdf95b512e6dcbce0abf47b8d903b4ea081f012ceb40e4462f1e219184c729329146df32a5ccec2c231f + languageName: node + linkType: hard + +"shebang-command@npm:^1.2.0": + version: 1.2.0 + resolution: "shebang-command@npm:1.2.0" + dependencies: + shebang-regex: "npm:^1.0.0" + checksum: 10/9eed1750301e622961ba5d588af2212505e96770ec376a37ab678f965795e995ade7ed44910f5d3d3cb5e10165a1847f52d3348c64e146b8be922f7707958908 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10/6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa + languageName: node + linkType: hard + +"shebang-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "shebang-regex@npm:1.0.0" + checksum: 10/404c5a752cd40f94591dfd9346da40a735a05139dac890ffc229afba610854d8799aaa52f87f7e0c94c5007f2c6af55bdcaeb584b56691926c5eaf41dc8f1372 + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10/1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 + languageName: node + linkType: hard + +"side-channel-list@npm:^1.0.0": + version: 1.0.0 + resolution: "side-channel-list@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + checksum: 10/603b928997abd21c5a5f02ae6b9cc36b72e3176ad6827fab0417ead74580cc4fb4d5c7d0a8a2ff4ead34d0f9e35701ed7a41853dac8a6d1a664fcce1a044f86f + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + checksum: 10/5771861f77feefe44f6195ed077a9e4f389acc188f895f570d56445e251b861754b547ea9ef73ecee4e01fdada6568bfe9020d2ec2dfc5571e9fa1bbc4a10615 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + side-channel-map: "npm:^1.0.1" + checksum: 10/a815c89bc78c5723c714ea1a77c938377ea710af20d4fb886d362b0d1f8ac73a17816a5f6640f354017d7e292a43da9c5e876c22145bac00b76cfb3468001736 + languageName: node + linkType: hard + +"side-channel@npm:^1.1.0": + version: 1.1.0 + resolution: "side-channel@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + side-channel-list: "npm:^1.0.0" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10/7d53b9db292c6262f326b6ff3bc1611db84ece36c2c7dc0e937954c13c73185b0406c56589e2bb8d071d6fee468e14c39fb5d203ee39be66b7b8174f179afaba + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: 10/a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f + languageName: node + linkType: hard + +"slash@npm:3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 10/94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c + languageName: node + linkType: hard + +"slick@npm:^1.12.2": + version: 1.12.2 + resolution: "slick@npm:1.12.2" + checksum: 10/381ae61b8efb62f6df35926f9d9ae61f7e8fdfbdeef0f2ab41bfec7d89495251667afb572a90d429336d15bae0aa011914e2f354bd57b20f8e49063c682de37f + languageName: node + linkType: hard + +"slugify@npm:^1.6.6": + version: 1.6.6 + resolution: "slugify@npm:1.6.6" + checksum: 10/d0737cdedc834c50f74227bc1a1cf4f449f3575893f031b0e8c59f501c73526c866a23e47261b262c7acdaaaaf30d6f9e8aaae22772b3f56e858ac84c35efa7b + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2 + languageName: node + linkType: hard + +"smol-toml@npm:^1.5.2": + version: 1.6.0 + resolution: "smol-toml@npm:1.6.0" + checksum: 10/965315168134bdcc410cda1b71a5e79cb72efd4891584a4cfb1430795437f83ed3bfc40c36dede94023682d2f9aac3b6d52d191036ffc474692da68c4a2292c8 + languageName: node + linkType: hard + +"socket.io-adapter@npm:~2.5.2": + version: 2.5.6 + resolution: "socket.io-adapter@npm:2.5.6" + dependencies: + debug: "npm:~4.4.1" + ws: "npm:~8.18.3" + checksum: 10/2bbefcc6f3d5dedab3105af03091b8863079173ab5610118d0ce94a0cf40fd87956c304f4f06445e361296b1966034be1ff0ba4e87b3c2baec216bbdec43b6e6 + languageName: node + linkType: hard + +"socket.io-parser@npm:~4.2.4": + version: 4.2.5 + resolution: "socket.io-parser@npm:4.2.5" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.4.1" + checksum: 10/612b3ba068327cbdca043d07f8d96da587a18e0b3e00f002b6476c22410c891abafc44a3f009abd014f2de42b348032f465a7b19771151728f6361ed116423d2 + languageName: node + linkType: hard + +"socket.io@npm:4.8.3, socket.io@npm:^4.8.1": + version: 4.8.3 + resolution: "socket.io@npm:4.8.3" + dependencies: + accepts: "npm:~1.3.4" + base64id: "npm:~2.0.0" + cors: "npm:~2.8.5" + debug: "npm:~4.4.1" + engine.io: "npm:~6.6.0" + socket.io-adapter: "npm:~2.5.2" + socket.io-parser: "npm:~4.2.4" + checksum: 10/ccd3eb0d191b3338056b678e676407a0dc565ab2a49a1aefd4b0771bd1d95d71cb564d3766040346669144c8a3b425bede54e0aad8102aa6c7d91dacb7c6992f + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.5 + resolution: "socks-proxy-agent@npm:8.0.5" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10/ee99e1dacab0985b52cbe5a75640be6e604135e9489ebdc3048635d186012fbaecc20fbbe04b177dee434c319ba20f09b3e7dfefb7d932466c0d707744eac05c + languageName: node + linkType: hard + +"socks@npm:2.8.7, socks@npm:^2.8.3": + version: 2.8.7 + resolution: "socks@npm:2.8.7" + dependencies: + ip-address: "npm:^10.0.1" + smart-buffer: "npm:^4.2.0" + checksum: 10/d19366c95908c19db154f329bbe94c2317d315dc933a7c2b5101e73f32a555c84fb199b62174e1490082a593a4933d8d5a9b297bde7d1419c14a11a965f51356 + languageName: node + linkType: hard + +"sonic-boom@npm:^4.0.1": + version: 4.2.0 + resolution: "sonic-boom@npm:4.2.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + checksum: 10/385ef7fb5ea5976c1d2a1fef0b6df8df6b7caba8696d2d67f689d60c05e3ea2d536752ce7e1c69b9fad844635f1036d07c446f8e8149f5c6a80e0040a455b310 + languageName: node + linkType: hard + +"sort-keys-length@npm:^1.0.0": + version: 1.0.1 + resolution: "sort-keys-length@npm:1.0.1" + dependencies: + sort-keys: "npm:^1.0.0" + checksum: 10/f9acac5fb31580a9e3d43b419dc86a1b75e85b79036a084d95dd4d1062b621c9589906588ac31e370a0dd381be46d8dbe900efa306d087ca9c912d7a59b5a590 + languageName: node + linkType: hard + +"sort-keys@npm:^1.0.0": + version: 1.1.2 + resolution: "sort-keys@npm:1.1.2" + dependencies: + is-plain-obj: "npm:^1.0.0" + checksum: 10/0ac2ea2327d92252f07aa7b2f8c7023a1f6ce3306439a3e81638cce9905893c069521d168f530fb316d1a929bdb052b742969a378190afaef1bc64fa69e29576 + languageName: node + linkType: hard + +"source-map-support@npm:~0.5.20": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10/8317e12d84019b31e34b86d483dd41d6f832f389f7417faf8fc5c75a66a12d9686e47f589a0554a868b8482f037e23df9d040d29387eb16fa14cb85f091ba207 + languageName: node + linkType: hard + +"source-map@npm:0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 10/a0f7c9b797eda93139842fd28648e868a9a03ea0ad0d9fa6602a0c1f17b7fb6a7dcca00c144476cccaeaae5042e99a285723b1a201e844ad67221bf5d428f1dc + languageName: node + linkType: hard + +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10/59ef7462f1c29d502b3057e822cdbdae0b0e565302c4dd1a95e11e793d8d9d62006cdc10e0fd99163ca33ff2071360cf50ee13f90440806e7ed57d81cba2f7ff + languageName: node + linkType: hard + +"source-map@npm:^0.7.3": + version: 0.7.6 + resolution: "source-map@npm:0.7.6" + checksum: 10/c8d2da7c57c14f3fd7568f764b39ad49bbf9dd7632b86df3542b31fed117d4af2fb74a4f886fc06baf7a510fee68e37998efc3080aacdac951c36211dc29a7a3 + languageName: node + linkType: hard + +"split2@npm:^4.0.0, split2@npm:^4.1.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 10/09bbefc11bcf03f044584c9764cd31a252d8e52cea29130950b26161287c11f519807c5e54bd9e5804c713b79c02cefe6a98f4688630993386be353e03f534ab + languageName: node + linkType: hard + +"split@npm:^1.0.1": + version: 1.0.1 + resolution: "split@npm:1.0.1" + dependencies: + through: "npm:2" + checksum: 10/12f4554a5792c7e98bb3e22b53c63bfa5ef89aa704353e1db608a55b51f5b12afaad6e4a8ecf7843c15f273f43cdadd67b3705cc43d48a75c2cf4641d51f7e7a + languageName: node + linkType: hard + +"sql-highlight@npm:^6.1.0": + version: 6.1.0 + resolution: "sql-highlight@npm:6.1.0" + checksum: 10/6cd92e7ca3046563f3daf2086adc4c2e1ce43784e59827a12bb9e569bf915eace1d800713f4d2798fc7d475f64852bf08001dca8dd409e9895ba5e0e170b94ff + languageName: node + linkType: hard + +"ssri@npm:^13.0.0": + version: 13.0.0 + resolution: "ssri@npm:13.0.0" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10/fd59bfedf0659c1b83f6e15459162da021f08ec0f5834dd9163296f8b77ee82f9656aa1d415c3d3848484293e0e6aefdd482e863e52ddb53d520bb73da1eeec1 + languageName: node + linkType: hard + +"stack-trace@npm:0.0.10, stack-trace@npm:0.0.x": + version: 0.0.10 + resolution: "stack-trace@npm:0.0.10" + checksum: 10/7bd633f0e9ac46e81a0b0fe6538482c1d77031959cf94478228731709db4672fbbed59176f5b9a9fd89fec656b5dae03d084ef2d1b0c4c2f5683e05f2dbb1405 + languageName: node + linkType: hard + +"standard-as-callback@npm:^2.1.0": + version: 2.1.0 + resolution: "standard-as-callback@npm:2.1.0" + checksum: 10/88bec83ee220687c72d94fd86a98d5272c91d37ec64b66d830dbc0d79b62bfa6e47f53b71646011835fc9ce7fae62739545d13124262b53be4fbb3e2ebad551c + languageName: node + linkType: hard + +"statuses@npm:^2.0.1, statuses@npm:^2.0.2, statuses@npm:~2.0.2": + version: 2.0.2 + resolution: "statuses@npm:2.0.2" + checksum: 10/6927feb50c2a75b2a4caab2c565491f7a93ad3d8dbad7b1398d52359e9243a20e2ebe35e33726dee945125ef7a515e9097d8a1b910ba2bbd818265a2f6c39879 + languageName: node + linkType: hard + +"streamroller@npm:^3.1.5": + version: 3.1.5 + resolution: "streamroller@npm:3.1.5" + dependencies: + date-format: "npm:^4.0.14" + debug: "npm:^4.3.4" + fs-extra: "npm:^8.1.0" + checksum: 10/2e4fe61ab91d24e6a9add67418ca9b8e19bc49f4037e1f8b7ae2e480a1d7750423f470d111d138d921a538ae4777c4eb15b00f9cc2a0d4fd72829687889b0c63 + languageName: node + linkType: hard + +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 10/612c2b2a7dbcc859f74597112f80a42cbe4d448d03da790d5b7b39673c1197dd3789e91cd67210353e58857395d32c1e955a9041c4e6d5bae723436b3ed9ed14 + languageName: node + linkType: hard + +"streamx@npm:^2.15.0": + version: 2.23.0 + resolution: "streamx@npm:2.23.0" + dependencies: + events-universal: "npm:^1.0.0" + fast-fifo: "npm:^1.3.2" + text-decoder: "npm:^1.1.0" + checksum: 10/4969d7032b16497172afa2f8ac889d137764963ae564daf1611a03225dd62d9316d51de8098b5866d21722babde71353067184e7a3e9795d6dc17c902904a780 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10/e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10/7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + +"string.fromcodepoint@npm:^0.2.1": + version: 0.2.1 + resolution: "string.fromcodepoint@npm:0.2.1" + checksum: 10/6ba80f70c3e2a36dab87f5d68168936403295a73838564e701f5c861d397d77d9e97b0e2aa0f3c163a25a96c785dcc2145452b220753fb7b3e6c6fe431c9c411 + languageName: node + linkType: hard + +"string_decoder@npm:^1.1.1": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: "npm:~5.2.0" + checksum: 10/54d23f4a6acae0e93f999a585e673be9e561b65cd4cca37714af1e893ab8cd8dfa52a9e4f58f48f87b4a44918d3a9254326cb80ed194bf2e4c226e2b21767e56 + languageName: node + linkType: hard + +"string_decoder@npm:~0.10.x": + version: 0.10.31 + resolution: "string_decoder@npm:0.10.31" + checksum: 10/cc43e6b1340d4c7843da0e37d4c87a4084c2342fc99dcf6563c3ec273bb082f0cbd4ebf25d5da19b04fb16400d393885fda830be5128e1c416c73b5a6165f175 + languageName: node + linkType: hard + +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10/7c41c17ed4dea105231f6df208002ebddd732e8e9e2d619d133cecd8e0087ddfd9587d2feb3c8caf3213cbd841ada6d057f5142cae68a4e62d3540778d9819b4 + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10/ae3b5436d34fadeb6096367626ce987057713c566e1e7768818797e00ac5d62023d0f198c4e681eae9e20701721980b26a64a8f5b91238869592a9c6800719a2 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1": + version: 7.1.2 + resolution: "strip-ansi@npm:7.1.2" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10/db0e3f9654e519c8a33c50fc9304d07df5649388e7da06d3aabf66d29e5ad65d5e6315d8519d409c15b32fa82c1df7e11ed6f8cd50b0e4404463f0c9d77c8d0b + languageName: node + linkType: hard + +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 10/8d50ff27b7ebe5ecc78f1fe1e00fcdff7af014e73cf724b46fb81ef889eeb1015fc5184b64e81a2efe002180f3ba431bdd77e300da5c6685d702780fbf0c8d5b + languageName: node + linkType: hard + +"strip-dirs@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-dirs@npm:3.0.0" + dependencies: + inspect-with-kind: "npm:^1.0.5" + is-plain-obj: "npm:^1.1.0" + checksum: 10/630c16035f4e8638bcb55523a3a016668b82b526fbde818b45cfd15c2fed506e2784153932c9d4a6d9758cc2c07a69a9533c7faffad2594dd601378d613e1b67 + languageName: node + linkType: hard + +"strip-eof@npm:^1.0.0": + version: 1.0.0 + resolution: "strip-eof@npm:1.0.0" + checksum: 10/40bc8ddd7e072f8ba0c2d6d05267b4e0a4800898c3435b5fb5f5a21e6e47dfaff18467e7aa0d1844bb5d6274c3097246595841fbfeb317e541974ee992cac506 + languageName: node + linkType: hard + +"strip-final-newline@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-final-newline@npm:2.0.0" + checksum: 10/69412b5e25731e1938184b5d489c32e340605bb611d6140344abc3421b7f3c6f9984b21dff296dfcf056681b82caa3bb4cc996a965ce37bcfad663e92eae9c64 + languageName: node + linkType: hard + +"strip-json-comments@npm:5.0.3": + version: 5.0.3 + resolution: "strip-json-comments@npm:5.0.3" + checksum: 10/3ccbf26f278220f785e4b71f8a719a6a063d72558cc63cb450924254af258a4f4c008b8c9b055373a680dc7bd525be9e543ad742c177f8a7667e0b726258e0e4 + languageName: node + linkType: hard + +"strip-json-comments@npm:^3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 10/492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 + languageName: node + linkType: hard + +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10/1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1 + languageName: node + linkType: hard + +"stripe@npm:^17.7.0": + version: 17.7.0 + resolution: "stripe@npm:17.7.0" + dependencies: + "@types/node": "npm:>=8.1.0" + qs: "npm:^6.11.0" + checksum: 10/376f945f9c194c8ea2d47d1fda50d141ed985cbb6f94041e11880084f830e502962d434967f1c90a3cb27a9b6e26c606f2830d9448bde636ebd6c067d5cbfecc + languageName: node + linkType: hard + +"strnum@npm:^1.1.1": + version: 1.1.2 + resolution: "strnum@npm:1.1.2" + checksum: 10/ccd6297a1fdaf0fc8ea0ea904acdae76878d49a4b0d98a70155df4bc081fd88eac5ec99fb150f3d1d1af065c1898d38420705259ba6c39aa850c671bcd54e35d + languageName: node + linkType: hard + +"strnum@npm:^2.1.0": + version: 2.1.2 + resolution: "strnum@npm:2.1.2" + checksum: 10/7d894dff385e3a5c5b29c012cf0a7ea7962a92c6a299383c3d6db945ad2b6f3e770511356a9774dbd54444c56af1dc7c435dad6466c47293c48173274dd6c631 + languageName: node + linkType: hard + +"strtok3@npm:^10.2.0, strtok3@npm:^10.3.4": + version: 10.3.4 + resolution: "strtok3@npm:10.3.4" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + checksum: 10/53be14a567dca149be56cb072eaa3c0fffd70d066acf800cf588b91558c6d475364ff8d550524ce0499fc4873a4b0d42ad8c542bfdb9fb39cba520ef2e2e9818 + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: "npm:^3.0.0" + checksum: 10/5f505c6fa3c6e05873b43af096ddeb22159831597649881aeb8572d6fe3b81e798cc10840d0c9735e0026b250368851b7f77b65e84f4e4daa820a4f69947f55b + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10/c8bb7afd564e3b26b50ca6ee47572c217526a1389fe018d00345856d4a9b08ffbd61fadaf283a87368d94c3dcdb8f5ffe2650a5a65863e21ad2730ca0f05210a + languageName: node + linkType: hard + +"supports-color@npm:^8.0.0": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10/157b534df88e39c5518c5e78c35580c1eca848d7dbaf31bbe06cdfc048e22c7ff1a9d046ae17b25691128f631a51d9ec373c1b740c12ae4f0de6e292037e4282 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10/a9dc19ae2220c952bd2231d08ddeecb1b0328b61e72071ff4000c8384e145cc07c1c0bdb3b5a1cb06e186a7b2790f1dee793418b332f6ddf320de25d9125be7e + languageName: node + linkType: hard + +"swagger-ui-dist@npm:5.31.0": + version: 5.31.0 + resolution: "swagger-ui-dist@npm:5.31.0" + dependencies: + "@scarf/scarf": "npm:=1.4.0" + checksum: 10/79a3fc72823c9f6340184a9702f86d5f251ebff41841d734627ad1c6f4ca3553fb888f2fa33cd911506101026fad386350095ee255e6916ebeba8fd5d8114894 + languageName: node + linkType: hard + +"symbol-observable@npm:4.0.0": + version: 4.0.0 + resolution: "symbol-observable@npm:4.0.0" + checksum: 10/983aef3912ad080fc834b9ad115d44bc2994074c57cea4fb008e9f7ab9bb4118b908c63d9edc861f51257bc0595025510bdf7263bb09d8953a6929f240165c24 + languageName: node + linkType: hard + +"synckit@npm:^0.11.7": + version: 0.11.11 + resolution: "synckit@npm:0.11.11" + dependencies: + "@pkgr/core": "npm:^0.2.9" + checksum: 10/6ecd88212b5be80004376b6ea74babcba284566ff59a50d8803afcaa78c165b5d268635c1dd84532ee3f690a979409e1eda225a8a35bed2d135ffdcea06ce7b0 + languageName: node + linkType: hard + +"tapable@npm:^2.2.0, tapable@npm:^2.2.1, tapable@npm:^2.3.0": + version: 2.3.0 + resolution: "tapable@npm:2.3.0" + checksum: 10/496a841039960533bb6e44816a01fffc2a1eb428bb2051ecab9e87adf07f19e1f937566cbbbb09dceff31163c0ffd81baafcad84db900b601f0155dd0b37e9f2 + languageName: node + linkType: hard + +"tar-fs@npm:^2.1.0": + version: 2.1.4 + resolution: "tar-fs@npm:2.1.4" + dependencies: + chownr: "npm:^1.1.1" + mkdirp-classic: "npm:^0.5.2" + pump: "npm:^3.0.0" + tar-stream: "npm:^2.1.4" + checksum: 10/bdf7e3cb039522e39c6dae3084b1bca8d7bcc1de1906eae4a1caea6a2250d22d26dcc234118bf879b345d91ebf250a744b196e379334a4abcbb109a78db7d3be + languageName: node + linkType: hard + +"tar-stream@npm:^2.1.4, tar-stream@npm:^2.2.0": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: "npm:^4.0.3" + end-of-stream: "npm:^1.4.1" + fs-constants: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + checksum: 10/1a52a51d240c118cbcd30f7368ea5e5baef1eac3e6b793fb1a41e6cd7319296c79c0264ccc5859f5294aa80f8f00b9239d519e627b9aade80038de6f966fec6a + languageName: node + linkType: hard + +"tar-stream@npm:^3.1.7": + version: 3.1.7 + resolution: "tar-stream@npm:3.1.7" + dependencies: + b4a: "npm:^1.6.4" + fast-fifo: "npm:^1.2.0" + streamx: "npm:^2.15.0" + checksum: 10/b21a82705a72792544697c410451a4846af1f744176feb0ff11a7c3dd0896961552e3def5e1c9a6bbee4f0ae298b8252a1f4c9381e9f991553b9e4847976f05c + languageName: node + linkType: hard + +"tar@npm:^7.5.2": + version: 7.5.2 + resolution: "tar@npm:7.5.2" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10/dbad9c9a07863cd1bdf8801d563b3280aa7dd0f4a6cead779ff7516d148dc80b4c04639ba732d47f91f04002f57e8c3c6573a717d649daecaac74ce71daa7ad3 + languageName: node + linkType: hard + +"terser-webpack-plugin@npm:^5.3.11": + version: 5.3.16 + resolution: "terser-webpack-plugin@npm:5.3.16" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.25" + jest-worker: "npm:^27.4.5" + schema-utils: "npm:^4.3.0" + serialize-javascript: "npm:^6.0.2" + terser: "npm:^5.31.1" + peerDependencies: + webpack: ^5.1.0 + peerDependenciesMeta: + "@swc/core": + optional: true + esbuild: + optional: true + uglify-js: + optional: true + checksum: 10/09dfbff602acfa114cdd174254b69a04adbc47856021ab351e37982202fd1ec85e0b62ffd5864c98beb8e96aef2f43da490b3448b4541db539c2cff6607394a6 + languageName: node + linkType: hard + +"terser@npm:^5.31.1": + version: 5.44.1 + resolution: "terser@npm:5.44.1" + dependencies: + "@jridgewell/source-map": "npm:^0.3.3" + acorn: "npm:^8.15.0" + commander: "npm:^2.20.0" + source-map-support: "npm:~0.5.20" + bin: + terser: bin/terser + checksum: 10/516ece205b7db778c4eddb287a556423cb776b7ca591b06270e558a76aa2d57c8d71d9c3c4410b276d3426beb03516fff7d96ff8b517e10730a72908810c6e33 + languageName: node + linkType: hard + +"text-decoder@npm:^1.1.0": + version: 1.2.3 + resolution: "text-decoder@npm:1.2.3" + dependencies: + b4a: "npm:^1.6.4" + checksum: 10/bcdec33c0f070aeac38e46e4cafdcd567a58473ed308bdf75260bfbd8f7dc76acbc0b13226afaec4a169d0cb44cec2ab89c57b6395ccf02e941eaebbe19e124a + languageName: node + linkType: hard + +"text-hex@npm:1.0.x": + version: 1.0.0 + resolution: "text-hex@npm:1.0.0" + checksum: 10/1138f68adc97bf4381a302a24e2352f04992b7b1316c5003767e9b0d3367ffd0dc73d65001ea02b07cd0ecc2a9d186de0cf02f3c2d880b8a522d4ccb9342244a + languageName: node + linkType: hard + +"thread-stream@npm:^3.0.0": + version: 3.1.0 + resolution: "thread-stream@npm:3.1.0" + dependencies: + real-require: "npm:^0.2.0" + checksum: 10/ea2d816c4f6077a7062fac5414a88e82977f807c82ee330938fb9691fe11883bb03f078551c0518bb649c239e47ba113d44014fcbb5db42c5abd5996f35e4213 + languageName: node + linkType: hard + +"through@npm:2, through@npm:^2.3.8": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: 10/5da78346f70139a7d213b65a0106f3c398d6bc5301f9248b5275f420abc2c4b1e77c2abc72d218dedc28c41efb2e7c312cb76a7730d04f9c2d37d247da3f4198 + languageName: node + linkType: hard + +"tiny-emitter@npm:^2.1.0": + version: 2.1.0 + resolution: "tiny-emitter@npm:2.1.0" + checksum: 10/75633f4de4f47f43af56aff6162f25b87be7efc6f669fda256658f3c3f4a216f23dc0d13200c6fafaaf1b0c7142f0201352fb06aec0b77f68aea96be898f4516 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.15": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.3" + checksum: 10/d72bd826a8b0fa5fa3929e7fe5ba48fceb2ae495df3a231b6c5408cd7d8c00b58ab5a9c2a76ba56a62ee9b5e083626f1f33599734bed1ffc4b792406408f0ca2 + languageName: node + linkType: hard + +"tlds@npm:1.261.0": + version: 1.261.0 + resolution: "tlds@npm:1.261.0" + bin: + tlds: bin.js + checksum: 10/2dfb8c7e0c0c1fe8fd9966ead43f71c36c974aa498b4e290fb383ceb3be3b20b9517041560b966993fb30abd451a83bf37119fa8d99809ce01bbf5b21dd6c6d3 + languageName: node + linkType: hard + +"tmp@npm:^0.2.0": + version: 0.2.5 + resolution: "tmp@npm:0.2.5" + checksum: 10/dd4b78b32385eab4899d3ae296007b34482b035b6d73e1201c4a9aede40860e90997a1452c65a2d21aee73d53e93cd167d741c3db4015d90e63b6d568a93d7ec + languageName: node + linkType: hard + +"to-buffer@npm:^1.2.0": + version: 1.2.2 + resolution: "to-buffer@npm:1.2.2" + dependencies: + isarray: "npm:^2.0.5" + safe-buffer: "npm:^5.2.1" + typed-array-buffer: "npm:^1.0.3" + checksum: 10/69d806c20524ff1e4c44d49276bc96ff282dcae484780a3974e275dabeb75651ea430b074a2a4023701e63b3e1d87811cd82c0972f35280fe5461710e4872aba + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10/10dda13571e1f5ad37546827e9b6d4252d2e0bc176c24a101252153ef435d83696e2557fe128c4678e4e78f5f01e83711c703eef9814eb12dab028580d45980a + languageName: node + linkType: hard + +"toidentifier@npm:~1.0.1": + version: 1.0.1 + resolution: "toidentifier@npm:1.0.1" + checksum: 10/952c29e2a85d7123239b5cfdd889a0dde47ab0497f0913d70588f19c53f7e0b5327c95f4651e413c74b785147f9637b17410ac8c846d5d4a20a5a33eb6dc3a45 + languageName: node + linkType: hard + +"token-stream@npm:1.0.0": + version: 1.0.0 + resolution: "token-stream@npm:1.0.0" + checksum: 10/e8adb56f31b813b6157130e7fc2fe14eb60e7cbf7b746e70e8293c7e55664d8e7ad5d93d7ae3aa4cad7fcb2b0aaf59dad6f2fd4ee0269204e55af5b05bc369e2 + languageName: node + linkType: hard + +"token-types@npm:^6.0.0, token-types@npm:^6.1.1": + version: 6.1.2 + resolution: "token-types@npm:6.1.2" + dependencies: + "@borewit/text-codec": "npm:^0.2.1" + "@tokenizer/token": "npm:^0.3.0" + ieee754: "npm:^1.2.1" + checksum: 10/0c7811a2da5a0ca474c795d883d871a184d1d54f67058d66084110f0b246fff66151885dbcb91d66533e776478bf57f3b4fac69ce03b805a0e1060def87947de + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10/8f1f5aa6cb232f9e1bdc86f485f916b7aa38caee8a778b378ffec0b70d9307873f253f5cbadbe2955ece2ac5c83d0dc14a77513166ccd0a0c7fe197e21396695 + languageName: node + linkType: hard + +"traverse@npm:>=0.3.0 <0.4": + version: 0.3.9 + resolution: "traverse@npm:0.3.9" + checksum: 10/ffbb8460a934f271b7b7ae654e676f740d81037d6c20ab9fd05781cfdf644929f494399b5cb3aa3db4ab69cbfef06ff8f885560d523ca49b7da33763f6c4c9f1 + languageName: node + linkType: hard + +"triple-beam@npm:^1.3.0": + version: 1.4.1 + resolution: "triple-beam@npm:1.4.1" + checksum: 10/2e881a3e8e076b6f2b85b9ec9dd4a900d3f5016e6d21183ed98e78f9abcc0149e7d54d79a3f432b23afde46b0885bdcdcbff789f39bc75de796316961ec07f61 + languageName: node + linkType: hard + +"ts-api-utils@npm:^2.4.0": + version: 2.4.0 + resolution: "ts-api-utils@npm:2.4.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10/d6b2b3b6caad8d2f4ddc0c3785d22bb1a6041773335a1c71d73a5d67d11d993763fe8e4faefc4a4d03bb42b26c6126bbcf2e34826baed1def5369d0ebad358fa + languageName: node + linkType: hard + +"ts-node@npm:^10.9.2": + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" + dependencies: + "@cspotcode/source-map-support": "npm:^0.8.0" + "@tsconfig/node10": "npm:^1.0.7" + "@tsconfig/node12": "npm:^1.0.7" + "@tsconfig/node14": "npm:^1.0.0" + "@tsconfig/node16": "npm:^1.0.2" + acorn: "npm:^8.4.1" + acorn-walk: "npm:^8.1.1" + arg: "npm:^4.1.0" + create-require: "npm:^1.1.0" + diff: "npm:^4.0.1" + make-error: "npm:^1.1.1" + v8-compile-cache-lib: "npm:^3.0.1" + yn: "npm:3.1.1" + peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" + typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true + bin: + ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js + ts-node-esm: dist/bin-esm.js + ts-node-script: dist/bin-script.js + ts-node-transpile-only: dist/bin-transpile.js + ts-script: dist/bin-script-deprecated.js + checksum: 10/a91a15b3c9f76ac462f006fa88b6bfa528130dcfb849dd7ef7f9d640832ab681e235b8a2bc58ecde42f72851cc1d5d4e22c901b0c11aa51001ea1d395074b794 + languageName: node + linkType: hard + +"tsconfig-paths-webpack-plugin@npm:4.2.0": + version: 4.2.0 + resolution: "tsconfig-paths-webpack-plugin@npm:4.2.0" + dependencies: + chalk: "npm:^4.1.0" + enhanced-resolve: "npm:^5.7.0" + tapable: "npm:^2.2.1" + tsconfig-paths: "npm:^4.1.2" + checksum: 10/946f23a38a404bf2d3803b60b5af1d7a6cc85bed411c9feefa707656efd9007cdcee7eb0e860ca8690ba479810c7b94ce026f6ac70daa6c803e55aac809c86c4 + languageName: node + linkType: hard + +"tsconfig-paths@npm:4.2.0, tsconfig-paths@npm:^4.1.2": + version: 4.2.0 + resolution: "tsconfig-paths@npm:4.2.0" + dependencies: + json5: "npm:^2.2.2" + minimist: "npm:^1.2.6" + strip-bom: "npm:^3.0.0" + checksum: 10/5e55cc2fb6b800eb72011522e10edefccb45b1f9af055681a51354c9b597d1390c6fa9cc356b8c7529f195ac8a90a78190d563159f3a1eed10e01bbd4d01a8ab + languageName: node + linkType: hard + +"tslib@npm:2.8.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.8.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7 + languageName: node + linkType: hard + +"twilio@npm:^5.7.0": + version: 5.11.2 + resolution: "twilio@npm:5.11.2" + dependencies: + axios: "npm:^1.12.0" + dayjs: "npm:^1.11.9" + https-proxy-agent: "npm:^5.0.0" + jsonwebtoken: "npm:^9.0.2" + qs: "npm:^6.9.4" + scmp: "npm:^2.1.0" + xmlbuilder: "npm:^13.0.2" + checksum: 10/f56899942ad395d1adc55ebc7efa5658eb09e64400b3ca9bd6afd248b8e36172b9c4a09232d043524d35f8214b264f08c3163d75a9c97c71c88109cab5515030 + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: "npm:^1.2.1" + checksum: 10/14687776479d048e3c1dbfe58a2409e00367810d6960c0f619b33793271ff2a27f81b52461f14a162f1f89a9b1d8da1b237fc7c99b0e1fdcec28ec63a86b1fec + languageName: node + linkType: hard + +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 10/8907e16284b2d6cfa4f4817e93520121941baba36b39219ea36acfe64c86b9dbc10c9941af450bd60832c8f43464974d51c0957f9858bc66b952b66b6914cbb9 + languageName: node + linkType: hard + +"type-is@npm:^1.6.18": + version: 1.6.18 + resolution: "type-is@npm:1.6.18" + dependencies: + media-typer: "npm:0.3.0" + mime-types: "npm:~2.1.24" + checksum: 10/0bd9eeae5efd27d98fd63519f999908c009e148039d8e7179a074f105362d4fcc214c38b24f6cda79c87e563cbd12083a4691381ed28559220d4a10c2047bed4 + languageName: node + linkType: hard + +"type-is@npm:^2.0.1": + version: 2.0.1 + resolution: "type-is@npm:2.0.1" + dependencies: + content-type: "npm:^1.0.5" + media-typer: "npm:^1.1.0" + mime-types: "npm:^3.0.0" + checksum: 10/bacdb23c872dacb7bd40fbd9095e6b2fca2895eedbb689160c05534d7d4810a7f4b3fd1ae87e96133c505958f6d602967a68db5ff577b85dd6be76eaa75d58af + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-buffer@npm:1.0.3" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.14" + checksum: 10/3fb91f0735fb413b2bbaaca9fabe7b8fc14a3fa5a5a7546bab8a57e755be0e3788d893195ad9c2b842620592de0e68d4c077d4c2c41f04ec25b8b5bb82fa9a80 + languageName: node + linkType: hard + +"typed-duration@npm:^1.0.12": + version: 1.0.13 + resolution: "typed-duration@npm:1.0.13" + checksum: 10/d9f1c6a754711a6f90209e42a8c3eca9be18591f94e3591bd47b90888d68d999f6eee3b73d9b4615cae3140ea76ae4f295ed7bcfa3e703e8ec7b600b62386606 + languageName: node + linkType: hard + +"typed-emitter@npm:^2.1.0": + version: 2.1.0 + resolution: "typed-emitter@npm:2.1.0" + dependencies: + rxjs: "npm:*" + dependenciesMeta: + rxjs: + optional: true + checksum: 10/95821a9e05784b972cc9d152891fd12a56cb4b1a7c57e768c02bea6a8984da7aff8f19404a7b69eea11fae2a3b6c0c510a4c510f575f50162c759ae9059f2520 + languageName: node + linkType: hard + +"typed-env@npm:^2.0.0": + version: 2.0.0 + resolution: "typed-env@npm:2.0.0" + checksum: 10/cae6c8928aaa0a5dd4a60079ff9e4113ba56f7276b368794e6e97eba298c7a7d02f0097e93dc053a2c7e8f5137f9227951819c5be539deb2f882859fa3100427 + languageName: node + linkType: hard + +"typed-function@npm:^4.2.1": + version: 4.2.2 + resolution: "typed-function@npm:4.2.2" + checksum: 10/6d0eb312d2fa2c4b9af7a6cba5f3e9123e1e7c0b2521a7cd30a1140f862ffb94ad44353b2798a8c160fb40a9b341f7870d3c05589a7b54472514ff6f1b12e6ac + languageName: node + linkType: hard + +"typedarray@npm:^0.0.6": + version: 0.0.6 + resolution: "typedarray@npm:0.0.6" + checksum: 10/2cc1bcf7d8c1237f6a16c04efc06637b2c5f2d74e58e84665445cf87668b85a21ab18dd751fa49eee6ae024b70326635d7b79ad37b1c370ed2fec6aeeeb52714 + languageName: node + linkType: hard + +"typeorm-naming-strategies@npm:^4.1.0": + version: 4.1.0 + resolution: "typeorm-naming-strategies@npm:4.1.0" + peerDependencies: + typeorm: ^0.2.0 || ^0.3.0 + checksum: 10/9654f386915532b134e00d10fa50b75d2c63d462a4acafad8d67071548cb41447b67bc5029ea07afae043f504a3e76c0f7526fc16c40081a70ade2a862a92164 + languageName: node + linkType: hard + +"typeorm@npm:^0.3.24": + version: 0.3.28 + resolution: "typeorm@npm:0.3.28" + dependencies: + "@sqltools/formatter": "npm:^1.2.5" + ansis: "npm:^4.2.0" + app-root-path: "npm:^3.1.0" + buffer: "npm:^6.0.3" + dayjs: "npm:^1.11.19" + debug: "npm:^4.4.3" + dedent: "npm:^1.7.0" + dotenv: "npm:^16.6.1" + glob: "npm:^10.5.0" + reflect-metadata: "npm:^0.2.2" + sha.js: "npm:^2.4.12" + sql-highlight: "npm:^6.1.0" + tslib: "npm:^2.8.1" + uuid: "npm:^11.1.0" + yargs: "npm:^17.7.2" + peerDependencies: + "@google-cloud/spanner": ^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + "@sap/hana-client": ^2.14.22 + better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 + ioredis: ^5.0.4 + mongodb: ^5.8.0 || ^6.0.0 + mssql: ^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 || ^5.0.14 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + "@google-cloud/spanner": + optional: true + "@sap/hana-client": + optional: true + better-sqlite3: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + bin: + typeorm: cli.js + typeorm-ts-node-commonjs: cli-ts-node-commonjs.js + typeorm-ts-node-esm: cli-ts-node-esm.js + checksum: 10/4eb217d65414291fb226267d903d123a16a9eb090b4e8f8da2dfe2f64680265823bea3712d363d31fe96c6dc2cef00edd545f42e63ec65b8a1899a1b455f757b + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.33.0": + version: 8.53.0 + resolution: "typescript-eslint@npm:8.53.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.53.0" + "@typescript-eslint/parser": "npm:8.53.0" + "@typescript-eslint/typescript-estree": "npm:8.53.0" + "@typescript-eslint/utils": "npm:8.53.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10/b4731161a4fec6ce9110e54407a50733e15b0eb48cb9636906a54068af10f8102a7018b9e5db6264604050955a03d3649a79c4869d43bcce215358a8a8a03f96 + languageName: node + linkType: hard + +"typescript@npm:5.9.3, typescript@npm:^5.8.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10/c089d9d3da2729fd4ac517f9b0e0485914c4b3c26f80dc0cffcb5de1719a17951e92425d55db59515c1a7ddab65808466debb864d0d56dcf43f27007d0709594 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A5.9.3#optional!builtin, typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10/696e1b017bc2635f4e0c94eb4435357701008e2f272f553d06e35b494b8ddc60aa221145e286c28ace0c89ee32827a28c2040e3a69bdc108b1a5dc8fb40b72e3 + languageName: node + linkType: hard + +"uc.micro@npm:^2.0.0": + version: 2.1.0 + resolution: "uc.micro@npm:2.1.0" + checksum: 10/37197358242eb9afe367502d4638ac8c5838b78792ab218eafe48287b0ed28aaca268ec0392cc5729f6c90266744de32c06ae938549aee041fc93b0f9672d6b2 + languageName: node + linkType: hard + +"uglify-js@npm:^3.1.4, uglify-js@npm:^3.5.1": + version: 3.19.3 + resolution: "uglify-js@npm:3.19.3" + bin: + uglifyjs: bin/uglifyjs + checksum: 10/6b9639c1985d24580b01bb0ab68e78de310d38eeba7db45bec7850ab4093d8ee464d80ccfaceda9c68d1c366efbee28573b52f95e69ac792354c145acd380b11 + languageName: node + linkType: hard + +"uid@npm:2.0.2": + version: 2.0.2 + resolution: "uid@npm:2.0.2" + dependencies: + "@lukeed/csprng": "npm:^1.0.0" + checksum: 10/18f6da43d8e1b8643077e8123f877b4506759d9accc15337140a1bf7c99f299a66e88b27ab4c640e66e6a10f19e3a85afa45fdf830dd4bab7570d07a3d51e073 + languageName: node + linkType: hard + +"uint8array-extras@npm:^1.4.0": + version: 1.5.0 + resolution: "uint8array-extras@npm:1.5.0" + checksum: 10/94fd56a2dda6a7445f5176f301f491814c87757d38e4b3c932299ab54d69ec504830e5d5c18ffa20cf694a69a210315be8b4a2c9952c6334da817ea2d2e1dce0 + languageName: node + linkType: hard + +"unbzip2-stream@npm:^1.4.3": + version: 1.4.3 + resolution: "unbzip2-stream@npm:1.4.3" + dependencies: + buffer: "npm:^5.2.1" + through: "npm:^2.3.8" + checksum: 10/4ffc0e14f4af97400ed0f37be83b112b25309af21dd08fa55c4513e7cb4367333f63712aec010925dbe491ef6e92db1248e1e306e589f9f6a8da8b3a9c4db90b + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10/ec8f41aa4359d50f9b59fa61fe3efce3477cc681908c8f84354d8567bb3701fafdddf36ef6bff307024d3feb42c837cf6f670314ba37fc8145e219560e473d14 + languageName: node + linkType: hard + +"undici-types@npm:~7.16.0": + version: 7.16.0 + resolution: "undici-types@npm:7.16.0" + checksum: 10/db43439f69c2d94cc29f75cbfe9de86df87061d6b0c577ebe9bb3255f49b22c50162a7d7eb413b0458b6510b8ca299ac7cff38c3a29fbd31af9f504bcf7fbc0d + languageName: node + linkType: hard + +"unescape-js@npm:^1.1.4": + version: 1.1.4 + resolution: "unescape-js@npm:1.1.4" + dependencies: + string.fromcodepoint: "npm:^0.2.1" + checksum: 10/97acf60a8f6c170f8a66b48b71f5c56bda728c2ff6b08c3443c5f21635bf5fa38a4265bcfcf46d17cb6ac9bbb8b913a34b1abc5cfe8db5d7cc5c8eecb1817472 + languageName: node + linkType: hard + +"unescape@npm:^1.0.1": + version: 1.0.1 + resolution: "unescape@npm:1.0.1" + dependencies: + extend-shallow: "npm:^2.0.1" + checksum: 10/0d89b0f55e08a2843e635f1ccf8472a35b367c41d9a8014dd7de5cc3af710a6e988a950b86b6229e143147ade21772f2d72054bc846f4972eb448df472b856ec + languageName: node + linkType: hard + +"unique-filename@npm:^5.0.0": + version: 5.0.0 + resolution: "unique-filename@npm:5.0.0" + dependencies: + unique-slug: "npm:^6.0.0" + checksum: 10/a5f67085caef74bdd2a6869a200ed5d68d171f5cc38435a836b5fd12cce4e4eb55e6a190298035c325053a5687ed7a3c96f0a91e82215fd14729769d9ac57d9b + languageName: node + linkType: hard + +"unique-slug@npm:^6.0.0": + version: 6.0.0 + resolution: "unique-slug@npm:6.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10/b78ed9d5b01ff465f80975f17387750ed3639909ac487fa82c4ae4326759f6de87c2131c0c39eca4c68cf06c537a8d104fba1dfc8a30308f99bc505345e1eba3 + languageName: node + linkType: hard + +"universalify@npm:^0.1.0": + version: 0.1.2 + resolution: "universalify@npm:0.1.2" + checksum: 10/40cdc60f6e61070fe658ca36016a8f4ec216b29bf04a55dce14e3710cc84c7448538ef4dad3728d0bfe29975ccd7bfb5f414c45e7b78883567fb31b246f02dff + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10/ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 + languageName: node + linkType: hard + +"unpipe@npm:~1.0.0": + version: 1.0.0 + resolution: "unpipe@npm:1.0.0" + checksum: 10/4fa18d8d8d977c55cb09715385c203197105e10a6d220087ec819f50cb68870f02942244f1017565484237f1f8c5d3cd413631b1ae104d3096f24fdfde1b4aa2 + languageName: node + linkType: hard + +"unzipper@npm:^0.10.11": + version: 0.10.14 + resolution: "unzipper@npm:0.10.14" + dependencies: + big-integer: "npm:^1.6.17" + binary: "npm:~0.3.0" + bluebird: "npm:~3.4.1" + buffer-indexof-polyfill: "npm:~1.0.0" + duplexer2: "npm:~0.1.4" + fstream: "npm:^1.0.12" + graceful-fs: "npm:^4.2.2" + listenercount: "npm:~1.0.1" + readable-stream: "npm:~2.3.6" + setimmediate: "npm:~1.0.4" + checksum: 10/3f7b44f3c7253bc08da2988baf559f00b261c5340625e6e5206c5d73b4dea409b89caae4048346cf9f215d3cdf930e3bdee98edac5e0abc843eed765c52b398d + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.2.0": + version: 1.2.3 + resolution: "update-browserslist-db@npm:1.2.3" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10/059f774300efb4b084a49293143c511f3ae946d40397b5c30914e900cd5691a12b8e61b41dd54ed73d3b56c8204165a0333107dd784ccf8f8c81790bcc423175 + languageName: node + linkType: hard + +"upper-case@npm:^1.1.1": + version: 1.1.3 + resolution: "upper-case@npm:1.1.3" + checksum: 10/fc4101fdcd783ee963d49d279186688d4ba2fab90e78dbd001ad141522a66ccfe310932f25e70d5211b559ab205be8c24bf9c5520c7ab7dcd0912274c6d976a3 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10/b271ca7e3d46b7160222e3afa3e531505161c9a4e097febae9664e4b59912f4cbe94861361a4175edac3a03fee99d91e44b6a58c17a634bc5a664b19fc76fbcb + languageName: node + linkType: hard + +"url-template@npm:^2.0.8": + version: 2.0.8 + resolution: "url-template@npm:2.0.8" + checksum: 10/fc6a4cf6c3c3c3d7f0a0bb4405c41b81934e583b454e52ace7b2e5d7ed32ec9c2970ff1826d240c5823955fcb13531a1fc4ff6ba4569b1886a2976665353e952 + languageName: node + linkType: hard + +"utf7@npm:>=1.0.2": + version: 1.0.2 + resolution: "utf7@npm:1.0.2" + dependencies: + semver: "npm:~5.3.0" + checksum: 10/510dacbecdbdbd9750e4372fe8477538aa90b9de93f5df24c7f0ab836f8404c8f08f5f2797c1e0d6788fa6c450445943d24c55b2f8cd3dc094748dab2ed75d8e + languageName: node + linkType: hard + +"utf8@npm:^2.1.0, utf8@npm:^2.1.1": + version: 2.1.2 + resolution: "utf8@npm:2.1.2" + checksum: 10/7b35ff1203d2e0209db7a28f6852cb64ff292a3bdc3b6a9c52ff2d69abcfeff992647e8d4dc787c4f1c9374613ad11f7e9c682df66985dfa516b8f8c69855860 + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10/474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 + languageName: node + linkType: hard + +"uuencode@npm:0.0.4": + version: 0.0.4 + resolution: "uuencode@npm:0.0.4" + checksum: 10/a76cc12fae707dc04496493d68d26d01f3c0a44528cbb10b4ca51abc98578df5b692f718c54b3072ad819e7c75dd91c3b783e4a2ac982404af77946da235c8ea + languageName: node + linkType: hard + +"uuid@npm:^11.1.0": + version: 11.1.0 + resolution: "uuid@npm:11.1.0" + bin: + uuid: dist/esm/bin/uuid + checksum: 10/d2da43b49b154d154574891ced66d0c83fc70caaad87e043400cf644423b067542d6f3eb641b7c819224a7cd3b4c2f21906acbedd6ec9c6a05887aa9115a9cf5 + languageName: node + linkType: hard + +"uuid@npm:^8.3.0": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 10/9a5f7aa1d6f56dd1e8d5f2478f855f25c645e64e26e347a98e98d95781d5ed20062d6cca2eecb58ba7c84bc3910be95c0451ef4161906abaab44f9cb68ffbdd1 + languageName: node + linkType: hard + +"uuid@npm:^9.0.0, uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10/9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2 + languageName: node + linkType: hard + +"v8-compile-cache-lib@npm:^3.0.1": + version: 3.0.1 + resolution: "v8-compile-cache-lib@npm:3.0.1" + checksum: 10/88d3423a52b6aaf1836be779cab12f7016d47ad8430dffba6edf766695e6d90ad4adaa3d8eeb512cc05924f3e246c4a4ca51e089dccf4402caa536b5e5be8961 + languageName: node + linkType: hard + +"valid-data-url@npm:^3.0.0": + version: 3.0.1 + resolution: "valid-data-url@npm:3.0.1" + checksum: 10/06584294fb4c9550f0aaa56470f8d748f4ebfc3ed230707db5559754719a66fc37f299b5a79b914375b8198d90f8a51e0401375391938caf8dc8e442308aab9e + languageName: node + linkType: hard + +"validator@npm:^13.15.20": + version: 13.15.26 + resolution: "validator@npm:13.15.26" + checksum: 10/22488ae718ca724eda81b7c8bf505005d4d70cb6ff9a319f48fd897a31d40fd9a2971af4a3288667a04c56b4f95912555495519d54a5d8d63c2572bf4970081a + languageName: node + linkType: hard + +"vary@npm:^1, vary@npm:^1.1.2": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: 10/31389debef15a480849b8331b220782230b9815a8e0dbb7b9a8369559aed2e9a7800cd904d4371ea74f4c3527db456dc8e7ac5befce5f0d289014dbdf47b2242 + languageName: node + linkType: hard + +"void-elements@npm:^3.1.0": + version: 3.1.0 + resolution: "void-elements@npm:3.1.0" + checksum: 10/0390f818107fa8fce55bb0a5c3f661056001c1d5a2a48c28d582d4d847347c2ab5b7f8272314cac58acf62345126b6b09bea623a185935f6b1c3bbce0dfd7f7f + languageName: node + linkType: hard + +"walk-up-path@npm:^4.0.0": + version: 4.0.0 + resolution: "walk-up-path@npm:4.0.0" + checksum: 10/6a230b20e5de296895116dc12b09dafaec1f72b8060c089533d296e241aff059dfaebe0d015c77467f857e4b40c78e08f7481add76f340233a1f34fa8af9ed63 + languageName: node + linkType: hard + +"watchpack@npm:^2.4.4": + version: 2.5.0 + resolution: "watchpack@npm:2.5.0" + dependencies: + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.1.2" + checksum: 10/6793335adecc06944430db1c9dbbb960d357019a3456fa2a5e38a96c0320a9d4444198f3d4a513c9b58306ddd89f2a1754f99056e4b71c512260436287c58361 + languageName: node + linkType: hard + +"wcwidth@npm:^1.0.1": + version: 1.0.1 + resolution: "wcwidth@npm:1.0.1" + dependencies: + defaults: "npm:^1.0.3" + checksum: 10/182ebac8ca0b96845fae6ef44afd4619df6987fe5cf552fdee8396d3daa1fb9b8ec5c6c69855acb7b3c1231571393bd1f0a4cdc4028d421575348f64bb0a8817 + languageName: node + linkType: hard + +"web-resource-inliner@npm:^6.0.1": + version: 6.0.1 + resolution: "web-resource-inliner@npm:6.0.1" + dependencies: + ansi-colors: "npm:^4.1.1" + escape-goat: "npm:^3.0.0" + htmlparser2: "npm:^5.0.0" + mime: "npm:^2.4.6" + node-fetch: "npm:^2.6.0" + valid-data-url: "npm:^3.0.0" + checksum: 10/179ee600b6d73d889ff4a9944553b6096384b837f97b75cddde3a15bb8847a6116d4bfc4bd54ca171bd34f81cb7c8b669162433207fb748f3e2704e63e3a97f9 + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10/b65b9f8d6854572a84a5c69615152b63371395f0c5dcd6729c45789052296df54314db2bc3e977df41705eacb8bc79c247cee139a63fa695192f95816ed528ad + languageName: node + linkType: hard + +"webpack-node-externals@npm:3.0.0": + version: 3.0.0 + resolution: "webpack-node-externals@npm:3.0.0" + checksum: 10/1a08102f73be2d6e787d16cf677f98c413076f35f379d64a4c83aa83769099b38091a4592953fac5b2eb0c7e3eb1977f6b901ef2cba531d458e32665314b8025 + languageName: node + linkType: hard + +"webpack-sources@npm:^3.3.3": + version: 3.3.3 + resolution: "webpack-sources@npm:3.3.3" + checksum: 10/ec5d72607e8068467370abccbfff855c596c098baedbe9d198a557ccf198e8546a322836a6f74241492576adba06100286592993a62b63196832cdb53c8bae91 + languageName: node + linkType: hard + +"webpack@npm:5.103.0": + version: 5.103.0 + resolution: "webpack@npm:5.103.0" + dependencies: + "@types/eslint-scope": "npm:^3.7.7" + "@types/estree": "npm:^1.0.8" + "@types/json-schema": "npm:^7.0.15" + "@webassemblyjs/ast": "npm:^1.14.1" + "@webassemblyjs/wasm-edit": "npm:^1.14.1" + "@webassemblyjs/wasm-parser": "npm:^1.14.1" + acorn: "npm:^8.15.0" + acorn-import-phases: "npm:^1.0.3" + browserslist: "npm:^4.26.3" + chrome-trace-event: "npm:^1.0.2" + enhanced-resolve: "npm:^5.17.3" + es-module-lexer: "npm:^1.2.1" + eslint-scope: "npm:5.1.1" + events: "npm:^3.2.0" + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.2.11" + json-parse-even-better-errors: "npm:^2.3.1" + loader-runner: "npm:^4.3.1" + mime-types: "npm:^2.1.27" + neo-async: "npm:^2.6.2" + schema-utils: "npm:^4.3.3" + tapable: "npm:^2.3.0" + terser-webpack-plugin: "npm:^5.3.11" + watchpack: "npm:^2.4.4" + webpack-sources: "npm:^3.3.3" + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 10/0018e77d159da412aa8cc1c3ac1d7c0b44228d0f5ce3939b4f424c04feba69747d8490541bcf8143b358a64afbbd69daad95e573ec9c4a90a99bef55d51dd43e + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10/f95adbc1e80820828b45cc671d97da7cd5e4ef9deb426c31bcd5ab00dc7103042291613b3ef3caec0a2335ed09e0d5ed026c940755dbb6d404e2b27f940fdf07 + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.16": + version: 1.1.19 + resolution: "which-typed-array@npm:1.1.19" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + for-each: "npm:^0.3.5" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + checksum: 10/12be30fb88567f9863186bee1777f11bea09dd59ed8b3ce4afa7dd5cade75e2f4cc56191a2da165113cc7cf79987ba021dac1e22b5b62aa7e5c56949f2469a68 + languageName: node + linkType: hard + +"which@npm:^1.2.9": + version: 1.3.1 + resolution: "which@npm:1.3.1" + dependencies: + isexe: "npm:^2.0.0" + bin: + which: ./bin/which + checksum: 10/549dcf1752f3ee7fbb64f5af2eead4b9a2f482108b7de3e85c781d6c26d8cf6a52d37cfbe0642a155fa6470483fe892661a859c03157f24c669cf115f3bbab5e + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10/4782f8a1d6b8fc12c65e968fea49f59752bf6302dc43036c3bf87da718a80710f61a062516e9764c70008b487929a73546125570acea95c5b5dcc8ac3052c70f + languageName: node + linkType: hard + +"which@npm:^6.0.0": + version: 6.0.0 + resolution: "which@npm:6.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10/df19b2cd8aac94b333fa29b42e8e371a21e634a742a3b156716f7752a5afe1d73fb5d8bce9b89326f453d96879e8fe626eb421e0117eb1a3ce9fd8c97f6b7db9 + languageName: node + linkType: hard + +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" + dependencies: + string-width: "npm:^4.0.0" + checksum: 10/03db6c9d0af9329c37d74378ff1d91972b12553c7d72a6f4e8525fe61563fa7adb0b9d6e8d546b7e059688712ea874edd5ded475999abdeedf708de9849310e0 + languageName: node + linkType: hard + +"win-ca@npm:3.5.1": + version: 3.5.1 + resolution: "win-ca@npm:3.5.1" + dependencies: + is-electron: "npm:^2.2.0" + make-dir: "npm:^1.3.0" + node-forge: "npm:^1.2.1" + split: "npm:^1.0.1" + checksum: 10/e7b8f3ddbe3c989eb19e53143b54dcec9c7d11f06ff373cb70a7310aaecef6c6a2e2c229a8300f8baa974f235169a179f046f4a3d3e93e7d4a285f774f70e489 + languageName: node + linkType: hard + +"winston-transport@npm:^4.5.0, winston-transport@npm:^4.9.0": + version: 4.9.0 + resolution: "winston-transport@npm:4.9.0" + dependencies: + logform: "npm:^2.7.0" + readable-stream: "npm:^3.6.2" + triple-beam: "npm:^1.3.0" + checksum: 10/5946918720baadd7447823929e94cf0935f92c4cff6d9451c6fcb009bd9d20a3b3df9ad606109e79d1e9f4d2ff678477bf09f81cfefce2025baaf27a617129bb + languageName: node + linkType: hard + +"winston@npm:^3.14.2, winston@npm:^3.17.0": + version: 3.19.0 + resolution: "winston@npm:3.19.0" + dependencies: + "@colors/colors": "npm:^1.6.0" + "@dabh/diagnostics": "npm:^2.0.8" + async: "npm:^3.2.3" + is-stream: "npm:^2.0.0" + logform: "npm:^2.7.0" + one-time: "npm:^1.0.0" + readable-stream: "npm:^3.4.0" + safe-stable-stringify: "npm:^2.3.1" + stack-trace: "npm:0.0.x" + triple-beam: "npm:^1.3.0" + winston-transport: "npm:^4.9.0" + checksum: 10/8279e221d8017da601a725939d31d65de71504d8328051312a85b1b4d7ddc68634329f8d611fb1ff91cb797643409635f3e97ef5b4a650c587639e080af76b7b + languageName: node + linkType: hard + +"with@npm:^7.0.0": + version: 7.0.2 + resolution: "with@npm:7.0.2" + dependencies: + "@babel/parser": "npm:^7.9.6" + "@babel/types": "npm:^7.9.6" + assert-never: "npm:^1.2.1" + babel-walk: "npm:3.0.0-canary-5" + checksum: 10/06ad978f9ac11268186060af7e19ff33bcfe1631ecbce4c676cb43168e3d6e62a5c6e697bd6dda8570c93b117a9b12980061ff66c3428a4e5cdcf0637e8ed81f + languageName: node + linkType: hard + +"word-wrap@npm:^1.2.5": + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: 10/1ec6f6089f205f83037be10d0c4b34c9183b0b63fca0834a5b3cee55dd321429d73d40bb44c8fc8471b5203d6e8f8275717f49a8ff4b2b0ab41d7e1b563e0854 + languageName: node + linkType: hard + +"wordwrap@npm:^1.0.0": + version: 1.0.0 + resolution: "wordwrap@npm:1.0.0" + checksum: 10/497d40beb2bdb08e6d38754faa17ce20b0bf1306327f80cb777927edb23f461ee1f6bc659b3c3c93f26b08e1cf4b46acc5bae8fda1f0be3b5ab9a1a0211034cd + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10/cebdaeca3a6880da410f75209e68cd05428580de5ad24535f22696d7d9cab134d1f8498599f344c3cf0fb37c1715807a183778d8c648d6cc0cb5ff2bb4236540 + languageName: node + linkType: hard + +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10/0d64f2d438e0b555e693b95aee7b2689a12c3be5ac458192a1ce28f542a6e9e59ddfecc37520910c2c88eb1f82a5411260566dba5064e8f9895e76e169e76187 + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10/7b1e4b35e9bb2312d2ee9ee7dc95b8cb5f8b4b5a89f7dde5543fe66c1e3715663094defa50d75454ac900bd210f702d575f15f3f17fa9ec0291806d2578d1ddf + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"written-number@npm:^0.11.1": + version: 0.11.1 + resolution: "written-number@npm:0.11.1" + checksum: 10/6e5d82a42ff77a5b85e8f0b29fb5d1a5c264ef1b3272b5671373eae7fecb0b0cc106afc5582e5e87ee5dee3d0986b49436a7fd9dc9b362a52b36840eaecec44e + languageName: node + linkType: hard + +"ws@npm:^8.17.1": + version: 8.19.0 + resolution: "ws@npm:8.19.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/26e4901e93abaf73af9f26a93707c95b4845e91a7a347ec8c569e6e9be7f9df066f6c2b817b2d685544e208207898a750b78461e6e8d810c11a370771450c31b + languageName: node + linkType: hard + +"ws@npm:~8.18.3": + version: 8.18.3 + resolution: "ws@npm:8.18.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/725964438d752f0ab0de582cd48d6eeada58d1511c3f613485b5598a83680bedac6187c765b0fe082e2d8cc4341fc57707c813ae780feee82d0c5efe6a4c61b6 + languageName: node + linkType: hard + +"xmlbuilder@npm:^13.0.2": + version: 13.0.2 + resolution: "xmlbuilder@npm:13.0.2" + checksum: 10/84671f47fbc28e56dc2fca9b29a2a62e3c2e28f5fcaa986b11c9a7df7385fc921e923cd1c3da83ae0d58b255bc2369feffdcd20600b4125391c79be02eedca15 + languageName: node + linkType: hard + +"xmlchars@npm:^2.2.0": + version: 2.2.0 + resolution: "xmlchars@npm:2.2.0" + checksum: 10/4ad5924974efd004a47cce6acf5c0269aee0e62f9a805a426db3337af7bcbd331099df174b024ace4fb18971b8a56de386d2e73a1c4b020e3abd63a4a9b917f1 + languageName: node + linkType: hard + +"xtend@npm:^4.0.0, xtend@npm:^4.0.2": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: 10/ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10/5f1b5f95e3775de4514edbb142398a2c37849ccfaf04a015be5d75521e9629d3be29bd4432d23c57f37e5b61ade592fb0197022e9993f81a06a5afbdcda9346d + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10/1884d272d485845ad04759a255c71775db0fac56308764b4c77ea56a20d56679fad340213054c8c9c9c26fcfd4c4b2a90df993b7e0aaf3cdb73c618d1d1a802a + languageName: node + linkType: hard + +"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10/9dc2c217ea3bf8d858041252d43e074f7166b53f3d010a8c711275e09cd3d62a002969a39858b92bbda2a6a63a585c7127014534a560b9c69ed2d923d113406e + languageName: node + linkType: hard + +"yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10/abb3e37678d6e38ea85485ed86ebe0d1e3464c640d7d9069805ea0da12f69d5a32df8e5625e370f9c96dd1c2dc088ab2d0a4dd32af18222ef3c4224a19471576 + languageName: node + linkType: hard + +"yauzl@npm:^3.1.2": + version: 3.2.0 + resolution: "yauzl@npm:3.2.0" + dependencies: + buffer-crc32: "npm:~0.2.3" + pend: "npm:~1.2.0" + checksum: 10/a3cd2bfcf7590673bb35750f2a4e5107e3cc939d32d98a072c0673fe42329e390f471b4a53dbbd72512229099b18aa3b79e6ddb87a73b3a17446080c903a2c4b + languageName: node + linkType: hard + +"yn@npm:3.1.1": + version: 3.1.1 + resolution: "yn@npm:3.1.1" + checksum: 10/2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10/f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 + languageName: node + linkType: hard + +"yoctocolors-cjs@npm:^2.1.3": + version: 2.1.3 + resolution: "yoctocolors-cjs@npm:2.1.3" + checksum: 10/b2144b38807673a4254dae06fe1a212729550609e606289c305e45c585b36fab1dbba44fe6cde90db9b28be465ec63f4c2a50867aeec6672f6bc36b6c9a361a0 + languageName: node + linkType: hard + +"zip-stream@npm:^4.1.0": + version: 4.1.1 + resolution: "zip-stream@npm:4.1.1" + dependencies: + archiver-utils: "npm:^3.0.4" + compress-commons: "npm:^4.1.2" + readable-stream: "npm:^3.6.0" + checksum: 10/33bd5ee7017656c2ad728b5d4ba510e15bd65ce1ec180c5bbdc7a5f063256353ec482e6a2bc74de7515219d8494147924b9aae16e63fdaaf37cdf7d1ee8df125 + languageName: node + linkType: hard + +"zod@npm:^4, zod@npm:^4.1.11": + version: 4.3.5 + resolution: "zod@npm:4.3.5" + checksum: 10/3148bd52e56ab7c1641ec397e6be6eddbb1d8f5db71e95baab9bb9622a0ea49d8a385885fc1c22b90fa6d8c5234e051f4ef5d469cfe3fb90198d5a91402fd89c + languageName: node + linkType: hard diff --git a/crm.mcmed.ru b/crm.mcmed.ru new file mode 100644 index 0000000..5a4f6e2 --- /dev/null +++ b/crm.mcmed.ru @@ -0,0 +1,143 @@ +upstream crm { + server 127.0.0.1:8000; +} + +# Основной HTTPS сервер с улучшенной безопасностью +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name crm.mcmed.ru; + + root /opt/crm/frontend/build; + + # SSL конфигурация + ssl_certificate /etc/letsencrypt/live/crm.mcmed.ru/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/crm.mcmed.ru/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/crm.mcmed.ru/chain.pem; + + # Современные SSL настройки + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_stapling on; + ssl_stapling_verify on; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com https://www.googletagmanager.com https://www.google-analytics.com https://analytics.google.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://fonts.gstatic.com; img-src 'self' data: blob: http://crm.mcmed.ru https://crm.mcmed.ru https://www.google-analytics.com https://analytics.google.com https://*; font-src 'self' https://fonts.gstatic.com data:; connect-src 'self' http://crm.mcmed.ru https://crm.mcmed.ru http://crm.mcmed.ru https://crm.mcmed.ru https://www.google-analytics.com https://analytics.google.com ws: wss:; frame-src 'self' https://www.googletagmanager.com; object-src 'none'; base-uri 'self'; form-action 'self'; worker-src 'self' blob: https://cdnjs.cloudflare.com;" always; + add_header X-Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com https://www.googletagmanager.com https://www.google-analytics.com https://analytics.google.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://fonts.gstatic.com; img-src 'self' data: blob: http://crm.mcmed.ru https://crm.mcmed.ru https://www.google-analytics.com https://analytics.google.com https://*; font-src 'self' https://fonts.gstatic.com data:; connect-src 'self' http://crm.mcmed.ru https://crm.mcmed.ru http://crm.mcmed.ru https://crm.mcmed.ru https://www.google-analytics.com https://analytics.google.com ws: wss:; frame-src 'self' https://www.googletagmanager.com; object-src 'none'; base-uri 'self'; form-action 'self'; worker-src 'self' blob: https://cdnjs.cloudflare.com;" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always; + + # Блокировка доступа к служебным файлам + location ~ /(\.env|\.git|\.svn|\.htaccess|\.htpasswd|config|backup|dump|phpinfo|wp-config) { + deny all; + access_log off; + log_not_found off; + } + + # Блокировка доступа к robots.txt + location = /robots.txt { + deny all; + access_log off; + log_not_found off; + } + + # Блокировка доступа к sitemap.xml + location = /sitemap.xml { + deny all; + access_log off; + log_not_found off; + } + + # Защита от атак на API + location /api/ { + # Ограничение методов + limit_except GET POST PUT DELETE OPTIONS { + deny all; + } + + # Rate limiting completely disabled - no limits on requests + # limit_req zone=api_limit burst=100 nodelay; + + # Connection limiting completely disabled - no limits on connections + # limit_conn conn_limit 50; + + # Large request body size allowed + #client_max_body_size 50M; + + # Generous timeout for slow clients + #client_body_timeout 30s; + + # Проксирование на бэкенд + 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_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_pass http://crm; + } + + # Основная конфигурация фронтенда + location / { + try_files $uri $uri/ /index.html; + + # Кэширование статических файлов + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + } + + # Блокировка доступа к favicon.ico + location = /favicon.ico { + return 404; + } + + # Защита от атак на сервер + server_tokens off; + add_header X-Powered-By "CRM Security Server" always; + + # Логирование + access_log /var/log/nginx/crm.access.log; + error_log /var/log/nginx/crm.error.log; + + # Ограничение размера тела запроса + client_max_body_size 2000M; + + # Connection limiting completely disabled - no limits on connections + # limit_conn conn_limit 50; +} + +# Сервер для Let's Encrypt ACME challenges +server { + listen 80; + listen [::]:80; + server_name crm.mcmed.ru; + root /var/www/certbot; + + location ^~ /.well-known/acme-challenge/ { + # Используем root без изменений + try_files $uri =404; + + # Отключаем логи для этих запросов + access_log off; + log_not_found off; + } + + location / { + return 301 https://$host$request_uri; + } +} diff --git a/frontend/.gitattributes b/frontend/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/frontend/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..7a3c339 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,26 @@ +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +.idea +.vscode + +storybook-static diff --git a/frontend/.gitlab-ci.yml b/frontend/.gitlab-ci.yml new file mode 100644 index 0000000..a30d60d --- /dev/null +++ b/frontend/.gitlab-ci.yml @@ -0,0 +1,177 @@ +stages: + - prepare + - build + - deploy + +default: + image: kroniak/ssh-client + interruptible: true + before_script: + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config + - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + - chmod 400 ~/.ssh/id_rsa + +# Vercel +vercel: + image: node:18-alpine + stage: deploy + dependencies: [] + rules: + - if: $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "develop" && $CI_COMMIT_BRANCH !~ /^release/ + when: manual + script: + - npm install --global vercel + - vercel pull --yes --environment=preview --token=$VERCEL_TOKEN + - vercel build --token=$VERCEL_TOKEN + - vercel deploy --prebuilt --token=$VERCEL_TOKEN + environment: + name: vercel + +# Storybook +storybook: + stage: deploy + rules: + - if: $CI_COMMIT_BRANCH == "develop" + when: manual + variables: + SSH_PRIVATE_KEY: $STAGING_SSH_PRIVATE_KEY + script: + - tar -czf sources.tar.gz . + - scp -r sources.tar.gz $STAGING_USER@$STAGING_SERVER:/amwork/storybook + - ssh $STAGING_USER@$STAGING_SERVER "cd /amwork/storybook && ./deploy.sh" + environment: + name: staging + url: https://amwork.dev + +.prepare_script: &prepare_script + - tar -czf sources.tar.gz . + - scp -r sources.tar.gz $USER@$SERVER:$FOLDER + +.build_script: &build_script + - ssh $USER@$SERVER "cd $FOLDER && ./build.sh" + +.deploy_script: &deploy_script + - ssh $USER@$SERVER "cd $FOLDER && ./deploy.sh && ./update-version.sh" + +.staging_job: + rules: + - if: $CI_COMMIT_BRANCH == "develop" + variables: + SSH_PRIVATE_KEY: $STAGING_SSH_PRIVATE_KEY + USER: $STAGING_USER + SERVER: $STAGING_SERVER + FOLDER: /amwork/frontend + environment: + name: staging + url: https://amwork.dev + +.amwork_job: + rules: + - if: $CI_COMMIT_BRANCH == "main" + variables: + SSH_PRIVATE_KEY: $AMWORK_PROD_SSH_PRIVATE_KEY + USER: $AMWORK_PROD_USER + SERVER: $AMWORK_PROD_SERVER + FOLDER: /amwork/frontend + environment: + name: production/amwork + url: https://amwork.com + +.mywork_job: + rules: + - if: $CI_COMMIT_BRANCH == "main" + variables: + SSH_PRIVATE_KEY: $MYWORK_PROD_SSH_PRIVATE_KEY + USER: $MYWORK_PROD_USER + SERVER: $MYWORK_PROD_SERVER + FOLDER: /mywork/frontend + environment: + name: production/mywork + url: https://mywork.app + +# Staging +staging_prepare: + extends: .staging_job + stage: prepare + script: *prepare_script +staging_build: + extends: .staging_job + stage: build + variables: + GIT_STRATEGY: none + script: *build_script +staging_deploy: + extends: .staging_job + stage: deploy + variables: + GIT_STRATEGY: none + script: *deploy_script + +# Amwork Production +amwork_prepare: + extends: .amwork_job + stage: prepare + script: *prepare_script +amwork_build: + extends: .amwork_job + stage: build + variables: + GIT_STRATEGY: none + script: *build_script +amwork_deploy: + extends: .amwork_job + stage: deploy + when: manual + variables: + GIT_STRATEGY: none + script: *deploy_script + +# Mywork Production +mywork_prepare: + extends: .mywork_job + stage: prepare + variables: + FOLDER: /apps/mywork/frontend + script: *prepare_script +mywork_build: + extends: .mywork_job + stage: build + variables: + GIT_STRATEGY: none + FOLDER: /apps/mywork/frontend + script: *build_script +mywork_deploy: + extends: .mywork_job + stage: deploy + when: manual + variables: + GIT_STRATEGY: none + FOLDER: /apps/mywork/frontend + script: *deploy_script + +# Platforma500 Production +platforma500_prepare: + extends: .mywork_job + stage: prepare + needs: [mywork_prepare] + variables: + FOLDER: /apps/platforma500/frontend + script: *prepare_script +platforma500_build: + extends: .mywork_job + stage: build + needs: [mywork_build] + variables: + GIT_STRATEGY: none + FOLDER: /apps/platforma500/frontend + script: *build_script +platforma500_deploy: + extends: .mywork_job + stage: deploy + when: manual + variables: + GIT_STRATEGY: none + FOLDER: /apps/platforma500/frontend + script: *deploy_script \ No newline at end of file diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..e8bcf6c --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps=true +@amwork:registry=https://gitlab.amwork.app/api/v4/projects/13/packages/npm/ diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..be3f780 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,24 @@ +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +.idea +.vscode \ No newline at end of file diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 0000000..5099e17 --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,22 @@ +{ + "tabWidth": 2, + "printWidth": 100, + "singleQuote": true, + "arrowParens": "avoid", + "trailingComma": "es5", + "plugins": ["prettier-plugin-organize-imports"], + "overrides": [ + { + "files": "*.ts", + "options": { + "parser": "typescript" + } + }, + { + "files": "*.js", + "options": { + "parser": "babel" + } + } + ] +} diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts new file mode 100644 index 0000000..2651fd6 --- /dev/null +++ b/frontend/.storybook/main.ts @@ -0,0 +1,24 @@ +import type { StorybookConfig } from '@storybook/react-vite'; + +const config: StorybookConfig = { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + ], + + framework: { + name: '@storybook/react-vite', + options: {}, + }, + + docs: {}, + + typescript: { + reactDocgen: 'react-docgen-typescript', + }, +}; + +export default config; diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts new file mode 100644 index 0000000..db6be1a --- /dev/null +++ b/frontend/.storybook/preview.ts @@ -0,0 +1,62 @@ +import type { Preview } from '@storybook/react'; +import { HttpStatusCode } from 'axios'; +import { I18nDecorator } from '../src/app/config/storybook/I18nDecorator'; +import { RouterDecorator } from '../src/app/config/storybook/RouterDecorator'; +import { StyleDecorator } from '../src/app/config/storybook/StyleDecorator'; + +// Basic authentication logic +const SB_USERNAME = 'admin'; +const SB_PASSWORD = 'amwork'; + +const inputUsername = window.prompt('Enter username:'); +const inputPassword = window.prompt('Enter password:'); + +if (inputUsername !== SB_USERNAME || inputPassword !== SB_PASSWORD) { + document.body.innerHTML = `Unauthorized ${HttpStatusCode.Unauthorized}. Invalid credentials.`; + + throw new Error(`Unauthorized ${HttpStatusCode.Unauthorized}. Invalid credentials.`); +} + +const preview: Preview = { + decorators: [StyleDecorator, RouterDecorator, I18nDecorator], + + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + backgrounds: { + default: 'default', + values: [ + { + name: 'default', + value: '#f9fafb', + }, + { name: 'dark', value: '#1f2937' }, + ], + }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, + + tags: ['autodocs'], +}; + +export const globalTypes = { + locale: { + name: 'Locale', + description: 'Internationalization locale', + toolbar: { + icon: 'globe', + items: [ + { value: 'en', title: 'English' }, + { value: 'ru', title: 'Russian' }, + { value: 'fr', title: 'French' }, + ], + showName: true, + }, + }, +}; + +export default preview; diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..6ce91e5 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,93 @@ +# Amwork Frontend + +## Prepare the environment + +### Clone the repo + +```bash +# Create project directory +mkdir amwork + +# Go to project directory +cd amwork + +# Clone the repo +git clone https://gitlab.com/amwork/amwork-frontend.git . + +# Install dependencies +npm install +``` + +### Start dev server + +```bash +npm start +``` + +### Start the dev server in production mode + +```bash +npm run serve +``` + +### Build for production + +```bash +npm run build +``` + +## Proxy and reverse proxy + +### Run reverse proxy + +```bash +docker-compose up -d +``` + +### Rebuild reverse proxy + +```bash +docker-compose stop $@ && docker-compose rm -f $@ && docker-compose build $@ && docker-compose up -d $@ +``` + +## Documentation and storybook + +Storybook deployment is available at [https://amwork.dev/storybook/](https://amwork.dev/storybook/). + +### Run storybook + +```bash +npm run storybook +``` + +### Build storybook + +```bash +npm run storybook:build +``` + +### Code and exports analysis + +More about [knip](https://github.com/webpro/knip). + +```bash +npm run knip +``` + +### Storybook credentials + +```text +login: admin +password: amwork +``` + +You can as well see them in `.storybook/preview.ts` file. + +### Finale + +This piece of software was made with soul and love. +Unfortunately, the project was suspended on 24 of September 2025. +I was the last engineer to maintain it. +Please contact me if you have any questions. + +— Egor Kondratev, kondratevegor04@gmail.com \ No newline at end of file diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs new file mode 100644 index 0000000..ee1fb43 --- /dev/null +++ b/frontend/eslint.config.mjs @@ -0,0 +1,107 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import { fixupConfigRules, fixupPluginRules } from "@eslint/compat"; +import i18Next from "eslint-plugin-i18next"; +import stylistic from "@stylistic/eslint-plugin"; +import regexp from "eslint-plugin-regexp"; +import reactHooksExtra from "eslint-plugin-react-hooks-extra"; +import tsParser from "@typescript-eslint/parser"; +import tsPlugin from "@typescript-eslint/eslint-plugin"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import js from "@eslint/js"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +export default defineConfig([globalIgnores([ + "node_modules", + ".pnp", + "**/.pnp.js", + "coverage", + "build", + "src/declarations/voxengine.d.ts", + "**/.DS_Store", + "**/.env.local", + "**/.env.development.local", + "**/.env.test.local", + "**/.env.production.local", + "**/npm-debug.log*", + "**/yarn-debug.log*", + "**/yarn-error.log*", + "**/.idea", + "**/.vscode", +]), { + extends: fixupConfigRules(compat.extends( + "react-app", + "plugin:i18next/recommended", + "plugin:@tanstack/eslint-plugin-query/recommended", + "plugin:storybook/recommended", + "plugin:regexp/recommended", + )), + + plugins: { + i18next: fixupPluginRules(i18Next), + "@stylistic": stylistic, + regexp: fixupPluginRules(regexp), + "react-hooks-extra": reactHooksExtra, + "@typescript-eslint": fixupPluginRules(tsPlugin), + }, + + languageOptions: { + parser: tsParser, + }, + + rules: { + "no-redeclare": 0, + + "line-comment-position": ["warn", { + position: "above", + }], + + "padding-line-between-statements": ["warn", { + blankLine: "always", + prev: "*", + next: ["return", "if", "do", "while", "for", "switch", "try", "with"], + }, { + blankLine: "always", + prev: ["if", "do", "while", "for", "switch", "try", "with"], + next: "*", + }, { + blankLine: "any", + prev: ["const", "let", "var"], + next: ["if", "do", "while", "for", "switch", "try", "with"], + }], + + "react/button-has-type": 1, + + "react/function-component-definition": [1, { + namedComponents: "arrow-function", + }], + + "i18next/no-literal-string": [1, { + markupOnly: true, + ignoreAttribute: ["aria-label"], + }], + + "react-hooks-extra/no-direct-set-state-in-use-effect": "warn", + + "@typescript-eslint/no-unused-vars": [ + "error", + { + args: "all", + argsIgnorePattern: "^_", + caughtErrors: "all", + caughtErrorsIgnorePattern: "^e", + destructuredArrayIgnorePattern: "^_", + varsIgnorePattern: "^_", + ignoreRestSiblings: true + } + ] + }, +}]); diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..3becacf --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..4312d90 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,15539 @@ +{ + "name": "amwork-frontend", + "version": "3.14.3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "amwork-frontend", + "version": "3.14.3", + "hasInstallScript": true, + "dependencies": { + "@babel/plugin-proposal-decorators": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.27.0", + "@babel/preset-typescript": "^7.27.0", + "@directus/sdk": "^19.1.0", + "@emoji-mart/data": "^1.2.1", + "@emoji-mart/react": "^1.1.1", + "@emotion/react": "^11.14.0", + "@emotion/utils": "^1.4.2", + "@formkit/auto-animate": "^0.8.2", + "@fullcalendar/core": "^6.1.17", + "@fullcalendar/daygrid": "^6.1.17", + "@fullcalendar/interaction": "^6.1.17", + "@fullcalendar/list": "^6.1.17", + "@fullcalendar/multimonth": "^6.1.17", + "@fullcalendar/react": "^6.1.17", + "@fullcalendar/resource": "^6.1.17", + "@fullcalendar/resource-daygrid": "^6.1.17", + "@fullcalendar/resource-timegrid": "^6.1.17", + "@fullcalendar/resource-timeline": "^6.1.17", + "@fullcalendar/scrollgrid": "^6.1.17", + "@fullcalendar/timegrid": "^6.1.17", + "@hello-pangea/dnd": "^18.0.1", + "@mantine/core": "^7.17.4", + "@mantine/dates": "^7.17.4", + "@mantine/hooks": "^7.17.4", + "@mantine/tiptap": "^7.17.4", + "@newrelic/browser-agent": "^1.287.0", + "@react-pdf-viewer/core": "3.12.0", + "@react-pdf-viewer/default-layout": "3.12.0", + "@react-pdf/renderer": "^4.3.0", + "@tabler/icons": "^3.31.0", + "@tabler/icons-react": "^3.31.0", + "@tanstack/react-query": "^5.74.3", + "@tanstack/react-table": "^8.21.3", + "@tiptap/extension-highlight": "^2.11.7", + "@tiptap/extension-link": "^2.11.7", + "@tiptap/extension-placeholder": "^2.11.7", + "@tiptap/extension-subscript": "^2.11.7", + "@tiptap/extension-superscript": "^2.11.7", + "@tiptap/extension-text-align": "^2.11.7", + "@tiptap/extension-text-style": "^2.11.7", + "@tiptap/extension-underline": "^2.11.7", + "@tiptap/pm": "^2.11.7", + "@tiptap/react": "^2.11.7", + "@tiptap/starter-kit": "^2.11.7", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "axios": "^1.8.4", + "bowser": "^2.11.0", + "bpmn-js": "^18.4.0", + "bpmn-js-color-picker": "^0.7.1", + "chart.js": "^4.4.9", + "country-code-to-flag-emoji": "^2.0.0", + "date-fns": "^4.1.0", + "dayjs": "^1.11.13", + "decimal.js": "^10.5.0", + "diagram-js": "^15.2.4", + "diagram-js-grid": "^1.1.0", + "diagram-js-minimap": "^5.2.0", + "dompurify": "^3.2.5", + "emoji-mart": "^5.6.0", + "file-saver": "^2.0.5", + "framer-motion": "^12.7.3", + "get-blob-duration": "^1.2.0", + "html-to-text": "^9.0.5", + "i18next": "^25.0.0", + "i18next-browser-languagedetector": "^8.0.4", + "i18next-http-backend": "^3.0.2", + "js-cookie": "^3.0.5", + "js-file-download": "^0.4.12", + "libphonenumber-js": "^1.12.6", + "mac-scrollbar": "^0.13.8", + "mobx": "^6.13.7", + "mobx-react-lite": "^4.1.0", + "nodemailer": "^6.10.1", + "normalize.css": "^8.0.1", + "numeralize-ru": "^2.0.0", + "p-limit": "^6.2.0", + "path-to-regexp": "^8.2.0", + "pdfjs-dist": "3.11.174", + "phone-number-to-timezone": "^1.0.8", + "qrcode": "^1.5.4", + "react": "^19.1.0", + "react-avatar-editor": "^13.0.2", + "react-chartjs-2": "^5.3.0", + "react-collapsible": "^2.10.0", + "react-countup": "^6.5.3", + "react-custom-scrollbars-2": "^4.5.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^19.1.0", + "react-gtm-module": "^2.0.11", + "react-helmet": "^6.1.0", + "react-i18next": "^15.4.1", + "react-phone-input-2": "^2.15.1", + "react-player": "^2.16.0", + "react-resizable-panels": "^2.1.7", + "react-rnd": "^10.5.2", + "react-router-dom": "^7.5.0", + "react-swipeable": "^7.0.2", + "react-textarea-autosize": "^8.5.9", + "react-toastify": "10.0.6", + "reflect-metadata": "0.2.2", + "socket.io-client": "^4.8.1", + "styled-components": "^5.3.11", + "usehooks-ts": "^3.1.1", + "uuid": "^11.1.0", + "voximplant-websdk": "^4.8.9-2892", + "xlsx": "^0.18.5", + "zeebe-bpmn-moddle": "^1.9.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@babel/plugin-transform-class-properties": "^7.25.9", + "@eslint/compat": "^1.2.8", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.24.0", + "@storybook/addon-essentials": "^8.6.12", + "@storybook/addon-interactions": "^8.6.12", + "@storybook/addon-links": "^8.6.12", + "@storybook/blocks": "^8.6.12", + "@storybook/react": "^8.6.12", + "@storybook/react-vite": "^8.6.12", + "@stylistic/eslint-plugin": "^4.2.0", + "@tanstack/eslint-plugin-query": "^5.73.3", + "@tanstack/react-query-devtools": "^5.74.3", + "@total-typescript/ts-reset": "^0.6.1", + "@types/html-to-text": "^9.0.4", + "@types/js-cookie": "^3.0.6", + "@types/node": "^22.14.1", + "@types/nodemailer": "^6.4.17", + "@types/qrcode": "^1.5.5", + "@types/react": "^19.1.2", + "@types/react-avatar-editor": "^13.0.4", + "@types/react-dom": "^19.1.2", + "@types/react-gtm-module": "^2.0.4", + "@types/react-helmet": "^6.1.11", + "@types/styled-components": "5.1.34", + "@types/uuid": "^10.0.0", + "@typescript-eslint/parser": "^8.30.1", + "@vitejs/plugin-react": "^4.4.0", + "babel-plugin-styled-components": "^2.1.4", + "eslint": "^9.24.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-i18next": "^6.1.1", + "eslint-plugin-react-hooks-extra": "^1.48.1", + "eslint-plugin-regexp": "^2.7.0", + "eslint-plugin-storybook": "^0.12.0", + "knip": "^5.50.4", + "nano-staged": "^0.8.0", + "prettier": "^3.5.3", + "prettier-plugin-organize-imports": "^4.1.0", + "simple-git-hooks": "^2.12.1", + "storybook": "^8.6.12", + "typescript": "^5.8.3", + "vite": "^6.3.0", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-svgr": "^4.3.0", + "vite-tsconfig-paths": "^5.1.4" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", + "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "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" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/eslint-parser": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.26.5.tgz", + "integrity": "sha512-Kkm8C8uxI842AwQADxl0GbcG1rupELYLShazYEZO/2DYjhyWXJIOUVOE3tBYm6JXzUCNJOZEzqc4rCW/jsEQYQ==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "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==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz", + "integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.27.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "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" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "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==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", + "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-decorators": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", + "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", + "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz", + "integrity": "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-flow": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.0.tgz", + "integrity": "sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.27.0", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", + "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.0.tgz", + "integrity": "sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-typescript": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bpmn-io/diagram-js-ui": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bpmn-io/diagram-js-ui/-/diagram-js-ui-0.2.3.tgz", + "integrity": "sha512-OGyjZKvGK8tHSZ0l7RfeKhilGoOGtFDcoqSGYkX0uhFlo99OVZ9Jn1K7TJGzcE9BdKwvA5Y5kGqHEhdTxHvFfw==", + "dependencies": { + "htm": "^3.1.1", + "preact": "^10.11.2" + } + }, + "node_modules/@directus/sdk": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@directus/sdk/-/sdk-19.1.0.tgz", + "integrity": "sha512-Nqem9BsvvGyVtAa69mGPtoMoMVkZxdIREdsWvvTzNF4/1XqaFfEiFL7PhtUNfc46/Nufus2+QUKYQbNiAWe3ZA==", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/directus/directus?sponsor=1" + } + }, + "node_modules/@emoji-mart/data": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz", + "integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==" + }, + "node_modules/@emoji-mart/react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz", + "integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==", + "peerDependencies": { + "emoji-mart": "^5.2", + "react": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@emotion/babel-plugin": { + "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" + } + }, + "node_modules/@emotion/cache": { + "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" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/react": { + "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" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "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" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "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==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + }, + "node_modules/@emotion/weak-memoize": { + "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==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint-react/ast": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@eslint-react/ast/-/ast-1.48.1.tgz", + "integrity": "sha512-s/05RD3EP54BigKoQ4QC7/Hb4EkzhUeXOSn3IIF15f1mhm5ieasuev0vpNZmTYBIDibq5R1OzsWu3WRWm3NJVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.48.1", + "@typescript-eslint/types": "^8.30.1", + "@typescript-eslint/typescript-estree": "^8.30.1", + "@typescript-eslint/utils": "^8.30.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.7.0" + }, + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/core": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@eslint-react/core/-/core-1.48.1.tgz", + "integrity": "sha512-O2onuiYs3EB0N3FBCcHDWHNlATMyYewX4E67sJwvLi9qTfX+I4I4ZmzkLfgqSD0eCMOjukHgLV1va+QV0bkR1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.48.1", + "@eslint-react/eff": "1.48.1", + "@eslint-react/kit": "1.48.1", + "@eslint-react/shared": "1.48.1", + "@eslint-react/var": "1.48.1", + "@typescript-eslint/scope-manager": "^8.30.1", + "@typescript-eslint/type-utils": "^8.30.1", + "@typescript-eslint/types": "^8.30.1", + "@typescript-eslint/utils": "^8.30.1", + "birecord": "^0.1.1", + "ts-pattern": "^5.7.0" + }, + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/eff": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@eslint-react/eff/-/eff-1.48.1.tgz", + "integrity": "sha512-6N76PNGylcTdJjdPG44NXHDYSDZBw5h4Uvrypm9/1TYKUBuNTHBpaeNgN16j/0Gkee3N8im4IefSqXsQz+Lx0g==", + "dev": true, + "license": "MIT", + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/kit": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@eslint-react/kit/-/kit-1.48.1.tgz", + "integrity": "sha512-XX8giu1EtnP23KVBI7Co6GDWjqLL/8a/YxOjjdJStI9i4Cr0S+yzLJ4yNmcA5wPJ0YqgIXw1sd+aEVZWSWzddg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.48.1", + "@typescript-eslint/utils": "^8.30.1", + "@zod/mini": "^4.0.0-beta.0", + "ts-pattern": "^5.7.0" + }, + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/shared": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@eslint-react/shared/-/shared-1.48.1.tgz", + "integrity": "sha512-Rwf0FJGTdYhdwCznTwpsvYyvwrEgKj25z6YyCdAfBwoR+Y/i28EWxjlaAjQzksffSfj44AspumRgIXI8SIgaiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.48.1", + "@eslint-react/kit": "1.48.1", + "@typescript-eslint/utils": "^8.30.1", + "@zod/mini": "^4.0.0-beta.0", + "ts-pattern": "^5.7.0" + }, + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/var": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@eslint-react/var/-/var-1.48.1.tgz", + "integrity": "sha512-irRKOTcsXoW4xbwLRQHSRukMVe6WelUdS59WZQ0/84IDBTNHJWXnMWDGnvxm6ZyDNd1THRgOCeu2kj05T2TqTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.48.1", + "@eslint-react/eff": "1.48.1", + "@typescript-eslint/scope-manager": "^8.30.1", + "@typescript-eslint/types": "^8.30.1", + "@typescript-eslint/utils": "^8.30.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.7.0" + }, + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + } + }, + "node_modules/@eslint/compat": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.8.tgz", + "integrity": "sha512-LqCYHdWL/QqKIJuZ/ucMAv8d4luKGs4oCPgpt8mWztQAtPrHfXKQ/XAUc8ljCHAfJCn6SvkpTcGt5Tsh8saowA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@formkit/auto-animate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.2.tgz", + "integrity": "sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ==" + }, + "node_modules/@fullcalendar/core": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.17.tgz", + "integrity": "sha512-0W7lnIrv18ruJ5zeWBeNZXO8qCWlzxDdp9COFEsZnyNjiEhUVnrW/dPbjRKYpL0edGG0/Lhs0ghp1z/5ekt8ZA==", + "dependencies": { + "preact": "~10.12.1" + } + }, + "node_modules/@fullcalendar/daygrid": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.17.tgz", + "integrity": "sha512-K7m+pd7oVJ9fW4h7CLDdDGJbc9szJ1xDU1DZ2ag+7oOo1aCNLv44CehzkkknM6r8EYlOOhgaelxQpKAI4glj7A==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/interaction": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.17.tgz", + "integrity": "sha512-AudvQvgmJP2FU89wpSulUUjeWv24SuyCx8FzH2WIPVaYg+vDGGYarI7K6PcM3TH7B/CyaBjm5Rqw9lXgnwt5YA==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/list": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.17.tgz", + "integrity": "sha512-fkyK49F9IxwlGUBVhJGsFpd/LTi/vRVERLIAe1HmBaGkjwpxnynm8TMLb9mZip97wvDk3CmZWduMe6PxscAlow==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/multimonth": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.17.tgz", + "integrity": "sha512-ZxA9mkTzKayCdxR5je9P9++qqhSeSbuvXmvZ6doZw6omv8K52cD7XJii+P7gvxATXxtI6hg4i+DuMyOHxP1E2g==", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/premium-common": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.17.tgz", + "integrity": "sha512-zoN7fMwGMcP6Xu+2YudRAGfdwD2J+V+A/xAieXgYDSZT+5ekCsjZiwb2rmvthjt+HVnuZcqs6sGp7rnJ8Ie/mA==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/react": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/react/-/react-6.1.17.tgz", + "integrity": "sha512-AA8soHhlfRH5dUeqHnfAtzDiXa2vrgWocJSK/F5qzw/pOxc9MqpuoS/nQBROWtHHg6yQUg3DoGqOOhi7dmylXQ==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.17", + "react": "^16.7.0 || ^17 || ^18 || ^19", + "react-dom": "^16.7.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/@fullcalendar/resource": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource/-/resource-6.1.17.tgz", + "integrity": "sha512-hWnbOWlroIN5Wt4NJmHAJh/F7ge2cV6S0PdGSmLFoZJZJA0hJX9GeYRzyz4MlUoj7f4dGzBlesy2RdC+t5FEMw==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/resource-daygrid": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-daygrid/-/resource-daygrid-6.1.17.tgz", + "integrity": "sha512-BghbufO4lsy/dckmPSxGzaxLH4c9oqykG01khYXRJijbls358CgSe+X9m1Jd5ntRZQU8o3U0Ma0pTZHr7Nx/6A==", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.17", + "@fullcalendar/premium-common": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17", + "@fullcalendar/resource": "~6.1.17" + } + }, + "node_modules/@fullcalendar/resource-timegrid": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-timegrid/-/resource-timegrid-6.1.17.tgz", + "integrity": "sha512-9gsR/2oKTb9zpx7z2/B5rbk2A0dmD/sfYAARSyQTuQD1CI5He8q63jdcYVzWW3CCfJa/5jHEqQaIiCKNlCq6vg==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.17", + "@fullcalendar/resource-daygrid": "~6.1.17", + "@fullcalendar/timegrid": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17", + "@fullcalendar/resource": "~6.1.17" + } + }, + "node_modules/@fullcalendar/resource-timeline": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-timeline/-/resource-timeline-6.1.17.tgz", + "integrity": "sha512-QMrtc1mLs4c6DtlBNmWICef8Lr4CmzE47uWS/rcJBd9K2kBzvusTp7AQQ1qn3RX5UnjNHqT8pkKO/wE4yspJQw==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.17", + "@fullcalendar/scrollgrid": "~6.1.17", + "@fullcalendar/timeline": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17", + "@fullcalendar/resource": "~6.1.17" + } + }, + "node_modules/@fullcalendar/scrollgrid": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/scrollgrid/-/scrollgrid-6.1.17.tgz", + "integrity": "sha512-lzphEKwxWMS4xQVEuimzZjKFLijlSn49ExvzkYZls0VLDwOa3BYHcRlDJBjQ0LP6kauz9aatg3MfRIde/LAazA==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/timegrid": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.17.tgz", + "integrity": "sha512-K4PlA3L3lclLOs3IX8cvddeiJI9ZVMD7RA9IqaWwbvac771971foc9tFze9YY+Pqesf6S+vhS2dWtEVlERaGlQ==", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@fullcalendar/timeline": { + "version": "6.1.17", + "resolved": "https://registry.npmjs.org/@fullcalendar/timeline/-/timeline-6.1.17.tgz", + "integrity": "sha512-UhL2OOph/S0cEKs3lzbXjS2gTxmQwaNug2XFjdljvO/ERj10v7OBXj/zvJrPyhjvWR/CSgjNgBaUpngkCu4JtQ==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.17", + "@fullcalendar/scrollgrid": "~6.1.17" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.17" + } + }, + "node_modules/@hello-pangea/dnd": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-18.0.1.tgz", + "integrity": "sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ==", + "dependencies": { + "@babel/runtime": "^7.26.7", + "css-box-model": "^1.2.1", + "raf-schd": "^4.0.3", + "react-redux": "^9.2.0", + "redux": "^5.0.1" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.5.0.tgz", + "integrity": "sha512-qYDdL7fPwLRI+bJNurVcis+tNgJmvWjH4YTBGXTA8xMuxFrnAz6E5o35iyzyKbq5J5Lr8mJGfrR5GXl+WGwhgQ==", + "dev": true, + "dependencies": { + "glob": "^10.0.0", + "magic-string": "^0.27.0", + "react-docgen-typescript": "^2.2.2" + }, + "peerDependencies": { + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "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" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "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" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, + "node_modules/@mantine/core": { + "version": "7.17.4", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.17.4.tgz", + "integrity": "sha512-Ea4M/98jxgIWCuxCdM0YIotVYjfLTGQsfIA6zDg0LsClgjo/ZLnnh4zbi+bLNgM+GGjP4ju7gv4MZvaTKuLO8g==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.28", + "clsx": "^2.1.1", + "react-number-format": "^5.4.3", + "react-remove-scroll": "^2.6.2", + "react-textarea-autosize": "8.5.9", + "type-fest": "^4.27.0" + }, + "peerDependencies": { + "@mantine/hooks": "7.17.4", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/core/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mantine/core/node_modules/type-fest": { + "version": "4.39.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz", + "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mantine/dates": { + "version": "7.17.4", + "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-7.17.4.tgz", + "integrity": "sha512-6oqcmcJb0Pypju+/z6s9nEVa3B9Pdj5DTrdj/FP/RD7RFx4k7nHi+jFstn4qcso6nghRRROKMHErfqrBybjzKA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "@mantine/core": "7.17.4", + "@mantine/hooks": "7.17.4", + "dayjs": ">=1.0.0", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/dates/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mantine/hooks": { + "version": "7.17.4", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.17.4.tgz", + "integrity": "sha512-PBcJxDAfGm8k1/JJmaDcxzRVQ3JSE1iXGktbgGz+qEOJmCxwbbAYe+CtGFFgi1xX2bPZ+7dtRr/+XFhnKtt/aw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/tiptap": { + "version": "7.17.4", + "resolved": "https://registry.npmjs.org/@mantine/tiptap/-/tiptap-7.17.4.tgz", + "integrity": "sha512-5r6dPzw3rfcU6W+zrT06N5cFaISDwYmvVemqz1hXX1cNj7Y9UOb3ymH6lmmXsyqroUv+bHurbi/SlcYd8R0YIA==", + "license": "MIT", + "peerDependencies": { + "@mantine/core": "7.17.4", + "@mantine/hooks": "7.17.4", + "@tiptap/extension-link": ">=2.1.12", + "@tiptap/react": ">=2.1.12", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", + "dev": true, + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@newrelic/browser-agent": { + "version": "1.287.0", + "resolved": "https://registry.npmjs.org/@newrelic/browser-agent/-/browser-agent-1.287.0.tgz", + "integrity": "sha512-fwTbSGjsrY18V7XGR2o85WJsUDOO/nZnzI4EhReH27sXPD6SzilNzHXrwN39PaMNdi2iEQC17sRAoEBXMn3dbA==", + "license": "Apache-2.0", + "dependencies": { + "fflate": "0.8.2", + "rrweb": "^2.0.0-alpha.18", + "web-vitals": "4.2.4" + }, + "engines": { + "node": ">=12.17.0 < 13.0.0 || >=13.7.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, + "node_modules/@react-pdf-viewer/attachment": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/attachment/-/attachment-3.12.0.tgz", + "integrity": "sha512-mhwrYJSIpCvHdERpLUotqhMgSjhtF+BTY1Yb9Fnzpcq3gLZP+Twp5Rynq21tCrVdDizPaVY7SKu400GkgdMfZw==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/bookmark": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/bookmark/-/bookmark-3.12.0.tgz", + "integrity": "sha512-i7nEit8vIFMAES8RFGwprZ9cXOOZb9ZStPW6E6yuObJEXcvBj/ctsbBJGZxqUZOGklM0JoB7sjHyxAriHfe92A==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/core": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/core/-/core-3.12.0.tgz", + "integrity": "sha512-8MsdlQJ4jaw3GT+zpCHS33nwnvzpY0ED6DEahZg9WngG++A5RMhk8LSlxdHelwaFFHFiXBjmOaj2Kpxh50VQRg==", + "peerDependencies": { + "pdfjs-dist": "^2.16.105 || ^3.0.279", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/default-layout": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/default-layout/-/default-layout-3.12.0.tgz", + "integrity": "sha512-K2fS4+TJynHxxCBFuIDiFuAw3nqOh4bkBgtVZ/2pGvnFn9lLg46YGLMnTXCQqtyZzzXYh696jmlFViun3is4pA==", + "dependencies": { + "@react-pdf-viewer/attachment": "3.12.0", + "@react-pdf-viewer/bookmark": "3.12.0", + "@react-pdf-viewer/core": "3.12.0", + "@react-pdf-viewer/thumbnail": "3.12.0", + "@react-pdf-viewer/toolbar": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/full-screen": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/full-screen/-/full-screen-3.12.0.tgz", + "integrity": "sha512-hQouJ26QUaRBCXNMU1aI1zpJn4l4PJRvlHhuE2dZYtLl37ycjl7vBCQYZW1FwnuxMWztZsY47R43DKaZORg0pg==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/get-file": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/get-file/-/get-file-3.12.0.tgz", + "integrity": "sha512-Uhq45n2RWlZ7Ec/BtBJ0WQESRciaYIltveDXHNdWvXgFdOS8XsvB+mnTh/wzm7Cfl9hpPyzfeezifdU9AkQgQg==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/open": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/open/-/open-3.12.0.tgz", + "integrity": "sha512-vhiDEYsiQLxvZkIKT9VPYHZ1BOnv46x9eCEmRWxO1DJ8fa/GRDTA9ivXmq/ap0dGEJs6t+epleCkCEfllLR/Yw==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/page-navigation": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/page-navigation/-/page-navigation-3.12.0.tgz", + "integrity": "sha512-tVEJ48Dd5kajV1nKkrPWijglJRNBiKBTyYDKVexhiRdTHUP1f6QQXiSyDgCUb0IGSZeJzOJb1h7ApKHe8OTtuw==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/print": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/print/-/print-3.12.0.tgz", + "integrity": "sha512-xJn76CgbU/M2iNaN7wLHTg+sdOekkRMfCakFLwPrE+SR7qD6NUF4vQQKJBSVCCK5bUijzb6cWfKGfo8VA72o4Q==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/properties": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/properties/-/properties-3.12.0.tgz", + "integrity": "sha512-dYTCHtVwFNkpDo7QxL2qk/8zAKndLwdD1FFxBftl6jIlQbtvNdxkFfkv1HcQING9Ic+7DBryOiD7W0ze4IERYg==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/rotate": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/rotate/-/rotate-3.12.0.tgz", + "integrity": "sha512-yaxaMYPChvNOjR8+AxRmj0kvojyJKPq4XHEcIB2lJJgBY1Zra3mliDUP3Nlb4yV8BS9+yBqWn9U9mtnopQD+tw==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/scroll-mode": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/scroll-mode/-/scroll-mode-3.12.0.tgz", + "integrity": "sha512-okII7Xqhl6cMvl1izdEvlXNJ+vJVq/qdg53hJIDYVgBCWskLk/cpjUg/ZonBxseG9lIDP3w2VO1McT8Gn11OAg==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/search": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/search/-/search-3.12.0.tgz", + "integrity": "sha512-jAkLpis49fsDDY/HrbUZIOIhzF5vynONQNA4INQKI38r/MjveblrkNv7qbr9j5lQ/WFic5+gD1e+Mtpf1/7DiA==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/selection-mode": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/selection-mode/-/selection-mode-3.12.0.tgz", + "integrity": "sha512-yysWEu2aCtBvzSgbhgI9kT5cq2hf0FU6Z+3B7MMXz14Kxyc3y18wUqxtgbvpFEfWF0bNUUq16JtWRljtxvZ83w==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/theme": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/theme/-/theme-3.12.0.tgz", + "integrity": "sha512-cdBi+wR1VOZ6URCcO9plmAZQu4ZGFcd7HJdBe7VIFiGyrvl9I/Of74ONLycnDImSuONt8D3uNjPBLieeaShVeg==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/thumbnail": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/thumbnail/-/thumbnail-3.12.0.tgz", + "integrity": "sha512-Vc8j3bO6wumWZV4o6pAbktPWKDSC9tQAzOCJ3cof541u4i44C11ccYC4W9aNcsMMUSO3bNwAGWtP8OFthV5akQ==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/toolbar": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/toolbar/-/toolbar-3.12.0.tgz", + "integrity": "sha512-qACTU3qXHgtNK8J+T13EWio+0liilj86SJ87BdapqXynhl720OKPlSKOQqskUGqg3oTUJAhrse9XG6SFdHJx+g==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0", + "@react-pdf-viewer/full-screen": "3.12.0", + "@react-pdf-viewer/get-file": "3.12.0", + "@react-pdf-viewer/open": "3.12.0", + "@react-pdf-viewer/page-navigation": "3.12.0", + "@react-pdf-viewer/print": "3.12.0", + "@react-pdf-viewer/properties": "3.12.0", + "@react-pdf-viewer/rotate": "3.12.0", + "@react-pdf-viewer/scroll-mode": "3.12.0", + "@react-pdf-viewer/search": "3.12.0", + "@react-pdf-viewer/selection-mode": "3.12.0", + "@react-pdf-viewer/theme": "3.12.0", + "@react-pdf-viewer/zoom": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf-viewer/zoom": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@react-pdf-viewer/zoom/-/zoom-3.12.0.tgz", + "integrity": "sha512-V0GUTyPM77+LzhoKX+T3XI10/HfGdqRTbgeP7ID60FCzcwu6kXWqJn5tzabjDKLTlFv8mJmn0aa/ppkIU97nfA==", + "dependencies": { + "@react-pdf-viewer/core": "3.12.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@react-pdf/fns": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz", + "integrity": "sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==" + }, + "node_modules/@react-pdf/font": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.2.tgz", + "integrity": "sha512-/dAWu7Y2RD1RxarDZ9SkYPHgBYOhmcDnet4W/qN/m8k+A2Hr3ja54GymSR7GGxWBtxjKtNauVKrTa9LS1n8WUw==", + "dependencies": { + "@react-pdf/pdfkit": "^4.0.3", + "@react-pdf/types": "^2.9.0", + "fontkit": "^2.0.2", + "is-url": "^1.2.4" + } + }, + "node_modules/@react-pdf/image": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.3.tgz", + "integrity": "sha512-lvP5ryzYM3wpbO9bvqLZYwEr5XBDX9jcaRICvtnoRqdJOo7PRrMnmB4MMScyb+Xw10mGeIubZAAomNAG5ONQZQ==", + "dependencies": { + "@react-pdf/png-js": "^3.0.0", + "jay-peg": "^1.1.1" + } + }, + "node_modules/@react-pdf/layout": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.4.0.tgz", + "integrity": "sha512-Aq+Cc6JYausWLoks2FvHe3PwK9cTuvksB2uJ0AnkKJEUtQbvCq8eCRb1bjbbwIji9OzFRTTzZij7LzkpKHjIeA==", + "dependencies": { + "@react-pdf/fns": "3.1.2", + "@react-pdf/image": "^3.0.3", + "@react-pdf/primitives": "^4.1.1", + "@react-pdf/stylesheet": "^6.1.0", + "@react-pdf/textkit": "^6.0.0", + "@react-pdf/types": "^2.9.0", + "emoji-regex": "^10.3.0", + "queue": "^6.0.1", + "yoga-layout": "^3.2.1" + } + }, + "node_modules/@react-pdf/pdfkit": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.0.3.tgz", + "integrity": "sha512-k+Lsuq8vTwWsCqTp+CCB4+2N+sOTFrzwGA7aw3H9ix/PDWR9QksbmNg0YkzGbLAPI6CeawmiLHcf4trZ5ecLPQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@react-pdf/png-js": "^3.0.0", + "browserify-zlib": "^0.2.0", + "crypto-js": "^4.2.0", + "fontkit": "^2.0.2", + "jay-peg": "^1.1.1", + "linebreak": "^1.1.0", + "vite-compatible-readable-stream": "^3.6.1" + } + }, + "node_modules/@react-pdf/png-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz", + "integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==", + "dependencies": { + "browserify-zlib": "^0.2.0" + } + }, + "node_modules/@react-pdf/primitives": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz", + "integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==" + }, + "node_modules/@react-pdf/reconciler": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.4.tgz", + "integrity": "sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==", + "dependencies": { + "object-assign": "^4.1.1", + "scheduler": "0.25.0-rc-603e6108-20241029" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-pdf/render": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.3.0.tgz", + "integrity": "sha512-MdWfWaqO6d7SZD75TZ2z5L35V+cHpyA43YNRlJNG0RJ7/MeVGDQv12y/BXOJgonZKkeEGdzM3EpAt9/g4E22WA==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@react-pdf/fns": "3.1.2", + "@react-pdf/primitives": "^4.1.1", + "@react-pdf/textkit": "^6.0.0", + "@react-pdf/types": "^2.9.0", + "abs-svg-path": "^0.1.1", + "color-string": "^1.9.1", + "normalize-svg-path": "^1.1.0", + "parse-svg-path": "^0.1.2", + "svg-arc-to-cubic-bezier": "^3.2.0" + } + }, + "node_modules/@react-pdf/renderer": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.3.0.tgz", + "integrity": "sha512-28gpA69fU9ZQrDzmd5xMJa1bDf8t0PT3ApUKBl2PUpoE/x4JlvCB5X66nMXrfFrgF2EZrA72zWQAkvbg7TE8zw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@react-pdf/fns": "3.1.2", + "@react-pdf/font": "^4.0.2", + "@react-pdf/layout": "^4.4.0", + "@react-pdf/pdfkit": "^4.0.3", + "@react-pdf/primitives": "^4.1.1", + "@react-pdf/reconciler": "^1.1.4", + "@react-pdf/render": "^4.3.0", + "@react-pdf/types": "^2.9.0", + "events": "^3.3.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "queue": "^6.0.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-pdf/stylesheet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.1.0.tgz", + "integrity": "sha512-BGZ2sYNUp38VJUegjva/jsri3iiRGnVNjWI+G9dTwAvLNOmwFvSJzqaCsEnqQ/DW5mrTBk/577FhDY7pv6AidA==", + "dependencies": { + "@react-pdf/fns": "3.1.2", + "@react-pdf/types": "^2.9.0", + "color-string": "^1.9.1", + "hsl-to-hex": "^1.0.0", + "media-engine": "^1.0.3", + "postcss-value-parser": "^4.1.0" + } + }, + "node_modules/@react-pdf/textkit": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.0.0.tgz", + "integrity": "sha512-fDt19KWaJRK/n2AaFoVm31hgGmpygmTV7LsHGJNGZkgzXcFyLsx+XUl63DTDPH3iqxj3xUX128t104GtOz8tTw==", + "dependencies": { + "@react-pdf/fns": "3.1.2", + "bidi-js": "^1.0.2", + "hyphen": "^1.6.4", + "unicode-properties": "^1.4.1" + } + }, + "node_modules/@react-pdf/types": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.9.0.tgz", + "integrity": "sha512-ckj80vZLlvl9oYrQ4tovEaqKWP3O06Eb1D48/jQWbdwz1Yh7Y9v1cEmwlP8ET+a1Whp8xfdM0xduMexkuPANCQ==", + "dependencies": { + "@react-pdf/font": "^4.0.2", + "@react-pdf/primitives": "^4.1.1", + "@react-pdf/stylesheet": "^6.1.0" + } + }, + "node_modules/@remirror/core-constants": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rrweb/types": { + "version": "2.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.18.tgz", + "integrity": "sha512-iMH3amHthJZ9x3gGmBPmdfim7wLGygC2GciIkw2A6SO8giSn8PHYtRT8OKNH4V+k3SZ6RSnYHcTQxBA7pSWZ3Q==" + }, + "node_modules/@rrweb/utils": { + "version": "2.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@rrweb/utils/-/utils-2.0.0-alpha.18.tgz", + "integrity": "sha512-qV8azQYo9RuwW4NGRtOiQfTBdHNL1B0Q//uRLMbCSjbaKqJYd88Js17Bdskj65a0Vgp2dwTLPIZ0gK47dfjfaA==" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz", + "integrity": "sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==", + "dev": true + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/@storybook/addon-actions": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.6.12.tgz", + "integrity": "sha512-B5kfiRvi35oJ0NIo53CGH66H471A3XTzrfaa6SxXEJsgxxSeKScG5YeXcCvLiZfvANRQ7QDsmzPUgg0o3hdMXw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@types/uuid": "^9.0.1", + "dequal": "^2.0.2", + "polished": "^4.2.2", + "uuid": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-actions/node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, + "node_modules/@storybook/addon-actions/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@storybook/addon-backgrounds": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.6.12.tgz", + "integrity": "sha512-lmIAma9BiiCTbJ8YfdZkXjpnAIrOUcgboLkt1f6XJ78vNEMnLNzD9gnh7Tssz1qrqvm34v9daDjIb+ggdiKp3Q==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-controls": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.6.12.tgz", + "integrity": "sha512-9VSRPJWQVb9wLp21uvpxDGNctYptyUX0gbvxIWOHMH3R2DslSoq41lsC/oQ4l4zSHVdL+nq8sCTkhBxIsjKqdQ==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "dequal": "^2.0.2", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-docs": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.6.12.tgz", + "integrity": "sha512-kEezQjAf/p3SpDzLABgg4fbT48B6dkT2LiZCKTRmCrJVtuReaAr4R9MMM6Jsph6XjbIj/SvOWf3CMeOPXOs9sg==", + "dev": true, + "dependencies": { + "@mdx-js/react": "^3.0.0", + "@storybook/blocks": "8.6.12", + "@storybook/csf-plugin": "8.6.12", + "@storybook/react-dom-shim": "8.6.12", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-essentials": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.6.12.tgz", + "integrity": "sha512-Y/7e8KFlttaNfv7q2zoHMPdX6hPXHdsuQMAjYl5NG9HOAJREu4XBy4KZpbcozRe4ApZ78rYsN/MO1EuA+bNMIA==", + "dev": true, + "dependencies": { + "@storybook/addon-actions": "8.6.12", + "@storybook/addon-backgrounds": "8.6.12", + "@storybook/addon-controls": "8.6.12", + "@storybook/addon-docs": "8.6.12", + "@storybook/addon-highlight": "8.6.12", + "@storybook/addon-measure": "8.6.12", + "@storybook/addon-outline": "8.6.12", + "@storybook/addon-toolbars": "8.6.12", + "@storybook/addon-viewport": "8.6.12", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-highlight": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.6.12.tgz", + "integrity": "sha512-9FITVxdoycZ+eXuAZL9ElWyML/0fPPn9UgnnAkrU7zkMi+Segq/Tx7y+WWanC5zfWZrXAuG6WTOYEXeWQdm//w==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-interactions": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.6.12.tgz", + "integrity": "sha512-cTAJlTq6uVZBEbtwdXkXoPQ4jHOAGKQnYSezBT4pfNkdjn/FnEeaQhMBDzf14h2wr5OgBnJa6Lmd8LD9ficz4A==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/instrumenter": "8.6.12", + "@storybook/test": "8.6.12", + "polished": "^4.2.2", + "ts-dedent": "^2.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-links": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.6.12.tgz", + "integrity": "sha512-AfKujFHoAxhxq4yu+6NwylltS9lf5MPs1eLLXvOlwo3l7Y/c68OdxJ7j68vLQhs9H173WVYjKyjbjFxJWf/YYg==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.6.12" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-measure": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.6.12.tgz", + "integrity": "sha512-tACmwqqOvutaQSduw8SMb62wICaT1rWaHtMN3vtWXuxgDPSdJQxLP+wdVyRYMAgpxhLyIO7YRf++Hfha9RHgFg==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-outline": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.6.12.tgz", + "integrity": "sha512-1ylwm+n1s40S91No0v9T4tCjZORu3GbnjINlyjYTDLLhQHyBQd3nWR1Y1eewU4xH4cW9SnSLcMQFS/82xHqU6A==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-toolbars": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.6.12.tgz", + "integrity": "sha512-HEcSzo1DyFtIu5/ikVOmh5h85C1IvK9iFKSzBR6ice33zBOaehVJK+Z5f487MOXxPsZ63uvWUytwPyViGInj+g==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/addon-viewport": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.6.12.tgz", + "integrity": "sha512-EXK2LArAnABsPP0leJKy78L/lbMWow+EIJfytEP5fHaW4EhMR6h7Hzaqzre6U0IMMr/jVFa1ci+m0PJ0eQc2bw==", + "dev": true, + "dependencies": { + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/blocks": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.6.12.tgz", + "integrity": "sha512-DohlTq6HM1jDbHYiXL4ZvZ00VkhpUp5uftzj/CZDLY1fYHRjqtaTwWm2/OpceivMA8zDitLcq5atEZN+f+siTg==", + "dev": true, + "dependencies": { + "@storybook/icons": "^1.2.12", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^8.6.12" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/builder-vite": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.6.12.tgz", + "integrity": "sha512-Gju21ud/3Qw4v2vLNaa5SuJECsI9ICNRr2G0UyCCzRvCHg8jpA9lDReu2NqhLDyFIuDG+ZYT38gcaHEUoNQ8KQ==", + "dev": true, + "dependencies": { + "@storybook/csf-plugin": "8.6.12", + "browser-assert": "^1.2.1", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12", + "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/@storybook/components": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.6.12.tgz", + "integrity": "sha512-FiaE8xvCdvKC2arYusgtlDNZ77b8ysr8njAYQZwwaIHjy27TbR2tEpLDCmUwSbANNmivtc/xGEiDDwcNppMWlQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" + } + }, + "node_modules/@storybook/core": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.12.tgz", + "integrity": "sha512-t+ZuDzAlsXKa6tLxNZT81gEAt4GNwsKP/Id2wluhmUWD/lwYW0uum1JiPUuanw8xD6TdakCW/7ULZc7aQUBLCQ==", + "dev": true, + "dependencies": { + "@storybook/theming": "8.6.12", + "better-opn": "^3.0.2", + "browser-assert": "^1.2.1", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", + "esbuild-register": "^3.5.0", + "jsdoc-type-pratt-parser": "^4.0.0", + "process": "^0.11.10", + "recast": "^0.23.5", + "semver": "^7.6.2", + "util": "^0.12.5", + "ws": "^8.2.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/@storybook/core/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/csf": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.13.tgz", + "integrity": "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==", + "dev": true, + "dependencies": { + "type-fest": "^2.19.0" + } + }, + "node_modules/@storybook/csf-plugin": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.6.12.tgz", + "integrity": "sha512-6s8CnP1aoKPb3XtC0jRLUp8M5vTA8RhGAwQDKUsFpCC7g89JR9CaKs9FY2ZSzsNbjR15uASi7b3K8BzeYumYQg==", + "dev": true, + "dependencies": { + "unplugin": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/global": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", + "dev": true + }, + "node_modules/@storybook/icons": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.4.0.tgz", + "integrity": "sha512-Td73IeJxOyalzvjQL+JXx72jlIYHgs+REaHiREOqfpo3A2AYYG71AUbcv+lg7mEDIweKVCxsMQ0UKo634c8XeA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" + } + }, + "node_modules/@storybook/instrumenter": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.6.12.tgz", + "integrity": "sha512-VK5fYAF8jMwWP/u3YsmSwKGh+FeSY8WZn78flzRUwirp2Eg1WWjsqPRubAk7yTpcqcC/km9YMF3KbqfzRv2s/A==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@vitest/utils": "^2.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/manager-api": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.6.12.tgz", + "integrity": "sha512-O0SpISeJLNTQvhSBOsWzzkCgs8vCjOq1578rwqHlC6jWWm4QmtfdyXqnv7rR1Hk08kQ+Dzqh0uhwHx0nfwy4nQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" + } + }, + "node_modules/@storybook/preview-api": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.6.12.tgz", + "integrity": "sha512-84FE3Hrs0AYKHqpDZOwx1S/ffOfxBdL65lhCoeI8GoWwCkzwa9zEP3kvXBo/BnEDO7nAfxvMhjASTZXbKRJh5Q==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" + } + }, + "node_modules/@storybook/react": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.6.12.tgz", + "integrity": "sha512-NzxlHLA5DkDgZM/dMwTYinuzRs6rsUPmlqP+NIv6YaciQ4NGnTYyOC7R/SqI6HHFm8ZZ5eMYvpfiFmhZ9rU+rQ==", + "dev": true, + "dependencies": { + "@storybook/components": "8.6.12", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "8.6.12", + "@storybook/preview-api": "8.6.12", + "@storybook/react-dom-shim": "8.6.12", + "@storybook/theming": "8.6.12" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@storybook/test": "8.6.12", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.6.12", + "typescript": ">= 4.2.x" + }, + "peerDependenciesMeta": { + "@storybook/test": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/react-dom-shim": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.6.12.tgz", + "integrity": "sha512-51QvoimkBzYs8s3rCYnY5h0cFqLz/Mh0vRcughwYaXckWzDBV8l67WBO5Xf5nBsukCbWyqBVPpEQLww8s7mrLA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/react-vite": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-8.6.12.tgz", + "integrity": "sha512-UA2Kule99oyFgHdhcuhrRwCKyWu/yMbqbl9U7NwowFHNwWWFjVMMir/AmfShb/H1C1DQ3LqOad6/QwJyPLjP8g==", + "dev": true, + "dependencies": { + "@joshwooding/vite-plugin-react-docgen-typescript": "0.5.0", + "@rollup/pluginutils": "^5.0.2", + "@storybook/builder-vite": "8.6.12", + "@storybook/react": "8.6.12", + "find-up": "^5.0.0", + "magic-string": "^0.30.0", + "react-docgen": "^7.0.0", + "resolve": "^1.22.8", + "tsconfig-paths": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@storybook/test": "8.6.12", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", + "storybook": "^8.6.12", + "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "@storybook/test": { + "optional": true + } + } + }, + "node_modules/@storybook/test": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.6.12.tgz", + "integrity": "sha512-0BK1Eg+VD0lNMB1BtxqHE3tP9FdkUmohtvWG7cq6lWvMrbCmAmh3VWai3RMCCDOukPFpjabOr8BBRLVvhNpv2w==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/instrumenter": "8.6.12", + "@testing-library/dom": "10.4.0", + "@testing-library/jest-dom": "6.5.0", + "@testing-library/user-event": "14.5.2", + "@vitest/expect": "2.0.5", + "@vitest/spy": "2.0.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.6.12" + } + }, + "node_modules/@storybook/theming": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.12.tgz", + "integrity": "sha512-6VjZg8HJ2Op7+KV7ihJpYrDnFtd9D1jrQnUS8LckcpuBXrIEbaut5+34ObY8ssQnSqkk2GwIZBBBQYQBCVvkOw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.2.0.tgz", + "integrity": "sha512-8hXezgz7jexGHdo5WN6JBEIPHCSFyyU4vgbxevu4YLVS5vl+sxqAAGyXSzfNDyR6xMNSH5H1x67nsXcYMOHtZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.23.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tabler/icons": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.31.0.tgz", + "integrity": "sha512-dblAdeKY3+GA1U+Q9eziZ0ooVlZMHsE8dqP0RkwvRtEsAULoKOYaCUOcJ4oW1DjWegdxk++UAt2SlQVnmeHv+g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.31.0.tgz", + "integrity": "sha512-2rrCM5y/VnaVKnORpDdAua9SEGuJKVqPtWxeQ/vUVsgaUx30LDgBZph7/lterXxDY1IKR6NO//HDhWiifXTi3w==", + "dependencies": { + "@tabler/icons": "3.31.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": ">= 16" + } + }, + "node_modules/@tanstack/eslint-plugin-query": { + "version": "5.73.3", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.73.3.tgz", + "integrity": "sha512-GmUtnOkRzDuNOq96g3eW5ADKC1nWfrM9RI0kRyQVr87rOl6y+PUgkuVaPxh3R2C0EVODxCS07b9aaWphidl/OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.18.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.74.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.74.3.tgz", + "integrity": "sha512-Mqk+5o3qTuAiZML248XpNH8r2cOzl15+LTbUsZQEwvSvn1GU4VQhvqzAbil36p+MBxpr/58oBSnRzhrBevDhfg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.73.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.73.3.tgz", + "integrity": "sha512-hBQyYwsOuO7QOprK75NzfrWs/EQYjgFA0yykmcvsV62q0t6Ua97CU3sYgjHx0ZvxkXSOMkY24VRJ5uv9f5Ik4w==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.74.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.74.3.tgz", + "integrity": "sha512-QrycUn0wxjVPzITvQvOxFRdhlAwIoOQSuav7qWD4SWCoKCdLbyRZ2vji2GuBq/glaxbF4wBx3fqcYRDOt8KDTA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.74.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.74.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.74.3.tgz", + "integrity": "sha512-H7TsOBB1fRCuuawrBzKMoIszqqILr2IN5oGLYMl7QG7ERJpMdc4hH8OwzBhVxJnmKeGwgtTQgcdKepfoJCWvFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.73.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.74.3", + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", + "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tiptap/core": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.7.tgz", + "integrity": "sha512-zN+NFFxLsxNEL8Qioc+DL6b8+Tt2bmRbXH22Gk6F6nD30x83eaUSFlSv3wqvgyCq3I1i1NO394So+Agmayx6rQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.11.7.tgz", + "integrity": "sha512-liD8kWowl3CcYCG9JQlVx1eSNc/aHlt6JpVsuWvzq6J8APWX693i3+zFqyK2eCDn0k+vW62muhSBe3u09hA3Zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.11.7.tgz", + "integrity": "sha512-VTR3JlldBixXbjpLTFme/Bxf1xeUgZZY3LTlt5JDlCW3CxO7k05CIa+kEZ8LXpog5annytZDUVtWqxrNjmsuHQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.11.7.tgz", + "integrity": "sha512-0vYqSUSSap3kk3/VT4tFE1/6StX70I3/NKQ4J68ZSFgkgyB3ZVlYv7/dY3AkEukjsEp3yN7m8Gw8ei2eEwyzwg==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.11.7.tgz", + "integrity": "sha512-WbPogE2/Q3e3/QYgbT1Sj4KQUfGAJNc5pvb7GrUbvRQsAh7HhtuO8hqdDwH8dEdD/cNUehgt17TO7u8qV6qeBw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.11.7.tgz", + "integrity": "sha512-VpPO1Uy/eF4hYOpohS/yMOcE1C07xmMj0/D989D9aS1x95jWwUVrSkwC+PlWMUBx9PbY2NRsg1ZDwVvlNKZ6yQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.11.7.tgz", + "integrity": "sha512-To/y/2H04VWqiANy53aXjV7S6fA86c2759RsH1hTIe57jA1KyE7I5tlAofljOLZK/covkGmPeBddSPHGJbz++Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.11.7.tgz", + "integrity": "sha512-95ouJXPjdAm9+VBRgFo4lhDoMcHovyl/awORDI8gyEn0Rdglt+ZRZYoySFzbVzer9h0cre+QdIwr9AIzFFbfdA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.11.7.tgz", + "integrity": "sha512-63mL+nxQILizsr5NbmgDeOjFEWi34BLt7evwL6UUZEVM15K8V1G8pD9Y0kCXrZYpHWz0tqFRXdrhDz0Ppu8oVw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.11.7.tgz", + "integrity": "sha512-DG54WoUu2vxHRVzKZiR5I5RMOYj45IlxQMkBAx1wjS0ch41W8DUYEeipvMMjCeKtEI+emz03xYUcOAP9LRmg+w==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.11.7.tgz", + "integrity": "sha512-EceesmPG7FyjXZ8EgeJPUov9G1mAf2AwdypxBNH275g6xd5dmU/KvjoFZjmQ0X1ve7mS+wNupVlGxAEUYoveew==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.11.7.tgz", + "integrity": "sha512-zTkZSA6q+F5sLOdCkiC2+RqJQN0zdsJqvFIOVFL/IDVOnq6PZO5THzwRRLvOSnJJl3edRQCl/hUgS0L5sTInGQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.11.7.tgz", + "integrity": "sha512-8kWh7y4Rd2fwxfWOhFFWncHdkDkMC1Z60yzIZWjIu72+6yQxvo8w3yeb7LI7jER4kffbMmadgcfhCHC/fkObBA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-highlight": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.11.7.tgz", + "integrity": "sha512-c/NH4kIpNOWCUQv8RkFNDyOcgt+2pYFpDf0QBJmzhAuv4BIeS2bDmDtuNS7VgoWRZH+xxCNXfvm2BG+kjtipEg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.11.7.tgz", + "integrity": "sha512-Cu5x3aS13I040QSRoLdd+w09G4OCVfU+azpUqxufZxeNs9BIJC+0jowPLeOxKDh6D5GGT2A8sQtxc6a/ssbs8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.11.7.tgz", + "integrity": "sha512-uVmQwD2dzZ5xwmvUlciy0ItxOdOfQjH6VLmu80zyJf8Yu7mvwP8JyxoXUX0vd1xHpwAhgQ9/ozjIWYGIw79DPQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.11.7.tgz", + "integrity": "sha512-r985bkQfG0HMpmCU0X0p/Xe7U1qgRm2mxvcp6iPCuts2FqxaCoyfNZ8YnMsgVK1mRhM7+CQ5SEg2NOmQNtHvPw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.11.7.tgz", + "integrity": "sha512-qKIowE73aAUrnQCIifYP34xXOHOsZw46cT/LBDlb0T60knVfQoKVE4ku08fJzAV+s6zqgsaaZ4HVOXkQYLoW7g==", + "dependencies": { + "linkifyjs": "^4.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.11.7.tgz", + "integrity": "sha512-6ikh7Y+qAbkSuIHXPIINqfzmWs5uIGrylihdZ9adaIyvrN1KSnWIqrZIk/NcZTg5YFIJlXrnGSRSjb/QM3WUhw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.11.7.tgz", + "integrity": "sha512-bLGCHDMB0vbJk7uu8bRg8vES3GsvxkX7Cgjgm/6xysHFbK98y0asDtNxkW1VvuRreNGz4tyB6vkcVCfrxl4jKw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.11.7.tgz", + "integrity": "sha512-Pl3B4q6DJqTvvAdraqZaNP9Hh0UWEHL5nNdxhaRNuhKaUo7lq8wbDSIxIW3lvV0lyCs0NfyunkUvSm1CXb6d4Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.11.7.tgz", + "integrity": "sha512-/06zXV4HIjYoiaUq1fVJo/RcU8pHbzx21evOpeG/foCfNpMI4xLU/vnxdUi6/SQqpZMY0eFutDqod1InkSOqsg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.11.7.tgz", + "integrity": "sha512-D6GYiW9F24bvAY7XMOARNZbC8YGPzdzWdXd8VOOJABhf4ynMi/oW4NNiko+kZ67jn3EGaKoz32VMJzNQgYi1HA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-subscript": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.11.7.tgz", + "integrity": "sha512-I25ZexCddFJ9701DCCtQbX3Vtxzj5d9ss2GAXVweIUCdATCScaebsznyUQoN5papmhTxXsw5OD+K2ZHxP82pew==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-superscript": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.11.7.tgz", + "integrity": "sha512-dNRpCcRJs0Qvv0sZRgbH7Y5hDVbWsGSZjtwFCs/mysPrvHqmXjzo7568kYWTggxEYxnXw6n0FfkCAEHlt0N90Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.11.7.tgz", + "integrity": "sha512-wObCn8qZkIFnXTLvBP+X8KgaEvTap/FJ/i4hBMfHBCKPGDx99KiJU6VIbDXG8d5ZcFZE0tOetK1pP5oI7qgMlQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-align": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.11.7.tgz", + "integrity": "sha512-3M8zd9ROADXazVNpgR6Ejs1evSvBveN36qN4GgV71GqrNlTcjqYgQcXFLQrsd2hnE+aXir8/8bLJ+aaJXDninA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.11.7.tgz", + "integrity": "sha512-LHO6DBg/9SkCQFdWlVfw9nolUmw+Cid94WkTY+7IwrpyG2+ZGQxnKpCJCKyeaFNbDoYAtvu0vuTsSXeCkgShcA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.11.7.tgz", + "integrity": "sha512-NtoQw6PGijOAtXC6G+0Aq0/Z5wwEjPhNHs8nsjXogfWIgaj/aI4/zfBnA06eI3WT+emMYQTl0fTc4CUPnLVU8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.11.7.tgz", + "integrity": "sha512-7gEEfz2Q6bYKXM07vzLUD0vqXFhC5geWRA6LCozTiLdVFDdHWiBrvb2rtkL5T7mfLq03zc1QhH7rI3F6VntOEA==", + "dependencies": { + "prosemirror-changeset": "^2.2.1", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.1", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.23.0", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.4.1", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.37.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.11.7.tgz", + "integrity": "sha512-gQZEUkAoPsBptnB4T2gAtiUxswjVGhfsM9vOElQco+b11DYmy110T2Zuhg+2YGvB/CG3RoWJx34808P0FX1ijA==", + "dependencies": { + "@tiptap/extension-bubble-menu": "^2.11.7", + "@tiptap/extension-floating-menu": "^2.11.7", + "@types/use-sync-external-store": "^0.0.6", + "fast-deep-equal": "^3", + "use-sync-external-store": "^1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.11.7.tgz", + "integrity": "sha512-K+q51KwNU/l0kqRuV5e1824yOLVftj6kGplGQLvJG56P7Rb2dPbM/JeaDbxQhnHT/KDGamG0s0Po0M3pPY163A==", + "dependencies": { + "@tiptap/core": "^2.11.7", + "@tiptap/extension-blockquote": "^2.11.7", + "@tiptap/extension-bold": "^2.11.7", + "@tiptap/extension-bullet-list": "^2.11.7", + "@tiptap/extension-code": "^2.11.7", + "@tiptap/extension-code-block": "^2.11.7", + "@tiptap/extension-document": "^2.11.7", + "@tiptap/extension-dropcursor": "^2.11.7", + "@tiptap/extension-gapcursor": "^2.11.7", + "@tiptap/extension-hard-break": "^2.11.7", + "@tiptap/extension-heading": "^2.11.7", + "@tiptap/extension-history": "^2.11.7", + "@tiptap/extension-horizontal-rule": "^2.11.7", + "@tiptap/extension-italic": "^2.11.7", + "@tiptap/extension-list-item": "^2.11.7", + "@tiptap/extension-ordered-list": "^2.11.7", + "@tiptap/extension-paragraph": "^2.11.7", + "@tiptap/extension-strike": "^2.11.7", + "@tiptap/extension-text": "^2.11.7", + "@tiptap/extension-text-style": "^2.11.7", + "@tiptap/pm": "^2.11.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@total-typescript/ts-reset": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.6.1.tgz", + "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==", + "dev": true + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", + "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" + }, + "node_modules/@types/doctrine": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", + "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/html-to-text": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.4.tgz", + "integrity": "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==", + "dev": true + }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==" + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "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==" + }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-avatar-editor": { + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/@types/react-avatar-editor/-/react-avatar-editor-13.0.4.tgz", + "integrity": "sha512-WQjmacEOoEYQb6CkAXYspmtruCPXFzLjEI92zV27yqveGBRkh7Quo5oYMvAgaEeqpXsGEr/wzXcdSTaH982Wtg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", + "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-gtm-module": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/react-gtm-module/-/react-gtm-module-2.0.4.tgz", + "integrity": "sha512-5wPMWsUE5AI6O0B0K1/zbs0rFHBKu+7NWXQwDXhqvA12ooLD6W1AYiWZqR4UiOd7ixZDV1H5Ys301zEsqyIfNg==", + "dev": true + }, + "node_modules/@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", + "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/styled-components": { + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", + "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", + "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "dev": true, + "dependencies": { + "@vitest/spy": "2.0.5", + "@vitest/utils": "2.0.5", + "chai": "^5.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/pretty-format": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", + "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "dev": true, + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", + "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "2.0.5", + "estree-walker": "^3.0.3", + "loupe": "^3.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", + "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "dev": true, + "dependencies": { + "tinyspy": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xstate/fsm": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.5.tgz", + "integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==" + }, + "node_modules/@zod/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@zod/core/-/core-0.1.0.tgz", + "integrity": "sha512-hSXsufqjH7u8DiJPT0KY1rFWIhjkdXGM8MhMLwzaeOMhxMA4bzjWLQwSoAToJunUTVrpmfdo4dUFzNaU219+VQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@zod/mini": { + "version": "4.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@zod/mini/-/mini-4.0.0-beta.0.tgz", + "integrity": "sha512-ux1pJYQJO0S/uAldc0KGKiBFvqPpQqfC8vXbBJ3tDrcWCCa6/QBQPexZFn/cHscTxA/SnEJEAxa7qGTwPQC5Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zod/core": "0.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/add-px-to-style": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz", + "integrity": "sha512-YMyxSlXpPjD8uWekCQGuN40lV4bnZagUwqa2m/uFv1z/tNImSk9fnXVMUI5qwME/zzI3MMQRvjZ+69zyfSSyew==" + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-plugin-macros": { + "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" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", + "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "lodash": "^4.17.21", + "picomatch": "^2.3.1" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-styled-components/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "dev": true + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "dev": true, + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/birecord": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/birecord/-/birecord-0.1.1.tgz", + "integrity": "sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==", + "dev": true, + "license": "(MIT OR Apache-2.0)" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "node_modules/bpmn-js": { + "version": "18.4.0", + "resolved": "https://registry.npmjs.org/bpmn-js/-/bpmn-js-18.4.0.tgz", + "integrity": "sha512-Dx9f6+P9fdARKCxKy/sekXGlsR5Gx8G7GIuj0ELhXqK+h2cK7VT0TpUMkpYVEPVX7zMAxN/0RKs2D7wruS5YWg==", + "dependencies": { + "bpmn-moddle": "^9.0.1", + "diagram-js": "^15.2.4", + "diagram-js-direct-editing": "^3.2.0", + "ids": "^1.0.5", + "inherits-browser": "^0.1.0", + "min-dash": "^4.1.1", + "min-dom": "^4.2.1", + "tiny-svg": "^3.1.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bpmn-js-color-picker": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/bpmn-js-color-picker/-/bpmn-js-color-picker-0.7.1.tgz", + "integrity": "sha512-SsDKewfopMPFTETAS1ZUWNKZvnQ7OBsODVcFEL2GzrQusW1VtnoCB2wc2vOtcafLAUPoUaRnVx5gZNtvywnJQA==", + "engines": { + "node": "*" + }, + "peerDependencies": { + "bpmn-js": ">= 14" + } + }, + "node_modules/bpmn-moddle": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bpmn-moddle/-/bpmn-moddle-9.0.1.tgz", + "integrity": "sha512-jO2P5RBx0cZCCd+imqhpNE5anttaYuGd71u76NEA/qMZwJSW1t5ETAtw9/E2InfiPU2w0TR8oxPyopJXRc9VQg==", + "dependencies": { + "min-dash": "^4.2.1", + "moddle": "^7.0.0", + "moddle-xml": "^11.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browser-assert": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz", + "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==", + "dev": true + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001764", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chart.js": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz", + "integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/component-event": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/component-event/-/component-event-0.2.1.tgz", + "integrity": "sha512-wGA++isMqiDq1jPYeyv2as/Bt/u+3iLW0rEa+8NQ82jAv3TgqMiCM+B2SaBdn2DfLilLjjq736YcezihRYhfxw==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, + "node_modules/convert-source-map": { + "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==" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/core-js-compat": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", + "dependencies": { + "browserslist": "^4.24.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cosmiconfig": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/country-code-to-flag-emoji": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/country-code-to-flag-emoji/-/country-code-to-flag-emoji-2.0.0.tgz", + "integrity": "sha512-Mn5DWG5DLF4WfvofdzLGIes2CJYQdONtFuZNzkrbY/z/ohM1p7rLeBM+ElqA0pxjryJIjoKtqLMn5gQAqSj72Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/country-code-to-flag-emoji?sponsor=1" + } + }, + "node_modules/countup.js": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.8.0.tgz", + "integrity": "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ==" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" + }, + "node_modules/diagram-js": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-15.2.4.tgz", + "integrity": "sha512-8v0U8AY6a5Vd6Cys5MdN7+yYRhs294/Q4ixkER/3v2ZPSbVCnK9XfbeYbB5QQfoCMkBnY7WBbLbcjvRNHTxnpw==", + "dependencies": { + "@bpmn-io/diagram-js-ui": "^0.2.3", + "clsx": "^2.1.0", + "didi": "^10.2.2", + "inherits-browser": "^0.1.0", + "min-dash": "^4.1.0", + "min-dom": "^4.2.1", + "object-refs": "^0.4.0", + "path-intersection": "^3.0.0", + "tiny-svg": "^3.1.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/diagram-js-direct-editing": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diagram-js-direct-editing/-/diagram-js-direct-editing-3.2.0.tgz", + "integrity": "sha512-+pyxeQGBSdLiZX0/tmmsm2qZSvm9YtVzod5W3RMHSTR7VrkUMD6E7EX/W9JQv3ebxO7oIdqFmytmNDDpSHnYEw==", + "dependencies": { + "min-dash": "^4.0.0", + "min-dom": "^4.2.1" + }, + "engines": { + "node": "*" + }, + "peerDependencies": { + "diagram-js": "*" + } + }, + "node_modules/diagram-js-grid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/diagram-js-grid/-/diagram-js-grid-1.1.0.tgz", + "integrity": "sha512-hnqRrWjbMA8YsBqaJe/GVIyJBITPSmUfsQVfN78jjtn1Elw5FZu838kVYQ/+FBOaTOxFixjyKgSGXc847uH2JA==", + "dependencies": { + "min-dash": "^4.1.1", + "tiny-svg": "^3.0.1" + } + }, + "node_modules/diagram-js-minimap": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diagram-js-minimap/-/diagram-js-minimap-5.2.0.tgz", + "integrity": "sha512-QEvHEeBEyRorcIWb3jyw2hd9XEWEQ+cpt3RmtOhbHk0aZgrwXRz3e7Xoeh0LiyYKozQlCtvkxwWX9GecpfxUkw==", + "dependencies": { + "min-dash": "^4.2.1", + "min-dom": "^4.2.1", + "tiny-svg": "^3.1.2" + } + }, + "node_modules/diagram-js/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/didi": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/didi/-/didi-10.2.2.tgz", + "integrity": "sha512-l8NYkYFXV1izHI65EyT8EXOjUZtKmQkHLTT89cSP7HU5J/G7AOj0dXKtLc04EXYlga99PBY18IPjOeZ+c3DI4w==", + "engines": { + "node": ">= 16" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, + "node_modules/dnd-core/node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "node_modules/dom-css": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz", + "integrity": "sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q==", + "dependencies": { + "add-px-to-style": "1.0.0", + "prefix-style": "2.0.1", + "to-camel-case": "1.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/domify/-/domify-1.4.2.tgz", + "integrity": "sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dompurify": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", + "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/easy-table": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", + "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.82", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.82.tgz", + "integrity": "sha512-Zq16uk1hfQhyGx5GpwPAYDwddJuSGhtRhgOA2mCxANYaDT79nAeGnaXogMGng4KqLaJUVnOnuL0+TDop9nLOiA==" + }, + "node_modules/emoji-mart": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz", + "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==" + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "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" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.0.tgz", + "integrity": "sha512-Ujz8Al/KfOVR7fkaghAB1WvnLsdYxHDWmfoi2vlA2jZWRg31XhIC1a4B+/I24muD8iSbHxJ1JkrfqmWb65P/Mw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-react-app/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-config-react-app/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-react-app/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-config-react-app/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-i18next": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-6.1.1.tgz", + "integrity": "sha512-/Vy6BfX44njxpRnbJm7bbph0KaNJF2eillqN5W+u03hHuxmh9BjtjdPSrI9HPtyoEbG4j5nBn9gXm/dg99mz3Q==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "requireindex": "~1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-hooks-extra": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks-extra/-/eslint-plugin-react-hooks-extra-1.48.1.tgz", + "integrity": "sha512-tVHhNvgyMR78RKmaQ0YBXdfUPhc+oH46SvU7usWDyGzhRiwM4uYq5FTLy5hwCagi/bsXvpkprwlSZRuhBPvP3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.48.1", + "@eslint-react/core": "1.48.1", + "@eslint-react/eff": "1.48.1", + "@eslint-react/kit": "1.48.1", + "@eslint-react/shared": "1.48.1", + "@eslint-react/var": "1.48.1", + "@typescript-eslint/scope-manager": "^8.30.1", + "@typescript-eslint/type-utils": "^8.30.1", + "@typescript-eslint/types": "^8.30.1", + "@typescript-eslint/utils": "^8.30.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.7.0" + }, + "engines": { + "bun": ">=1.0.15", + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-regexp": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.7.0.tgz", + "integrity": "sha512-U8oZI77SBtH8U3ulZ05iu0qEzIizyEDXd+BWHvyVxTOjGwcDcvy/kEpgFG4DYca2ByRLiVPFZ2GeH7j1pdvZTA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "comment-parser": "^1.4.0", + "jsdoc-type-pratt-parser": "^4.0.0", + "refa": "^0.12.1", + "regexp-ast-analysis": "^0.7.1", + "scslre": "^0.3.0" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "eslint": ">=8.44.0" + } + }, + "node_modules/eslint-plugin-storybook": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.12.0.tgz", + "integrity": "sha512-Lg5I0+npTgiYgZ4KSvGWGDFZi3eOCNJPaWX0c9rTEEXC5wvooOClsP9ZtbI4hhFKyKgYR877KiJxbRTSJq9gWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/csf": "^0.1.11", + "@typescript-eslint/utils": "^8.8.1", + "ts-dedent": "^2.2.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", + "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.58.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-plugin-testing-library/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-saver": { + "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==" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/framer-motion": { + "version": "12.7.3", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.7.3.tgz", + "integrity": "sha512-dNT4l5gEnUo2ytXLUBUf6AI21dZ77TMclDKE3ElaIHZ8m90nJ/NCcExW51zdSIaS0RhAS5iXcF7bEIxZe8XG2g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.7.3", + "motion-utils": "^12.7.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", + "optional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-blob-duration": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-blob-duration/-/get-blob-duration-1.2.0.tgz", + "integrity": "sha512-2xNJa+oKznR21eC2ThMzw4a1931a3ogA8aHoY92xruZufc/02G7pl/P793GJZytkyI8xMJ2DepEQ7MWvg/tn/Q==", + "dependencies": { + "@babel/runtime": "7.11.2" + } + }, + "node_modules/get-blob-duration/node_modules/@babel/runtime": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", + "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/get-blob-duration/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/hasown": { + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "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" + } + }, + "node_modules/hsl-to-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz", + "integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==", + "dependencies": { + "hsl-to-rgb-for-reals": "^1.1.0" + } + }, + "node_modules/hsl-to-rgb-for-reals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz", + "integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==" + }, + "node_modules/htm": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/htm/-/htm-3.1.1.tgz", + "integrity": "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==" + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hyphen": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz", + "integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==" + }, + "node_modules/i18next": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.0.tgz", + "integrity": "sha512-POPvwjOPR1GQvRnbikTMPEhQD+ekd186MHE6NtVxl3Lby+gPp0iq60eCqGrY6wfRnp1lejjFNu0EKs1afA322w==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.4.tgz", + "integrity": "sha512-f3frU3pIxD50/Tz20zx9TD9HobKYg47fmAETb117GKGPrhwcSSPJDoCposXlVycVebQ9GQohC3Efbpq7/nnJ5w==", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz", + "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", + "dependencies": { + "cross-fetch": "4.0.0" + } + }, + "node_modules/ids": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ids/-/ids-1.0.5.tgz", + "integrity": "sha512-XQ0yom/4KWTL29sLG+tyuycy7UmeaM/79GRtSJq6IG9cJGIPeBz5kwDCguie3TwxaMNIc3WtPi0cTa1XYHicpw==" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "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" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inherits-browser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/inherits-browser/-/inherits-browser-0.1.0.tgz", + "integrity": "sha512-CJHHvW3jQ6q7lzsXPpapLdMx5hDpSF3FSh45pwsj6bKxJJ8Nl8v43i5yXnr3BdfOimGHKyniewQtnAIp3vyJJw==" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-async-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jay-peg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz", + "integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==", + "dependencies": { + "restructure": "^3.0.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-file-download": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "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==" + }, + "node_modules/json-schema-traverse": { + "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==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "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==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/knip": { + "version": "5.50.4", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.50.4.tgz", + "integrity": "sha512-In+GjPpd2P3IDZnBBP4QF27vhQOhuBkICiuN9j+DMOf/m/qAFLGcbvuAGxco8IDvf26pvBnfeSmm1f6iNCkgOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + }, + { + "type": "polar", + "url": "https://polar.sh/webpro-nl" + } + ], + "license": "ISC", + "dependencies": { + "@nodelib/fs.walk": "^1.2.3", + "easy-table": "1.2.0", + "enhanced-resolve": "^5.18.1", + "fast-glob": "^3.3.3", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "minimist": "^1.2.8", + "picocolors": "^1.1.0", + "picomatch": "^4.0.1", + "pretty-ms": "^9.0.0", + "smol-toml": "^1.3.1", + "strip-json-comments": "5.0.1", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", + "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.6", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.6.tgz", + "integrity": "sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==" + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.2.0.tgz", + "integrity": "sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw==" + }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" + }, + "node_modules/lodash.startswith": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz", + "integrity": "sha512-XClYR1h4/fJ7H+mmCKppbiBmljN/nGs73iq2SjCT9SF4CBPoUHzLvWmH1GtZMhMBZSiRkHXfeA2RY1eIlJ75ww==" + }, + "node_modules/loose-envify": { + "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" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/mac-scrollbar": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/mac-scrollbar/-/mac-scrollbar-0.13.8.tgz", + "integrity": "sha512-lpu9fV8lx7oUHzM4CwgbMOXlLJ+1OGdmxwXmreadq8+8pmnFAEJ1khUdqpFOhbD32vJORLu6RE8GO2+ODpf12w==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-or-similar": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", + "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", + "dev": true + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + }, + "node_modules/media-engine": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz", + "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==" + }, + "node_modules/memoizerific": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", + "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", + "dev": true, + "dependencies": { + "map-or-similar": "^1.5.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "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" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-dash": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-4.2.2.tgz", + "integrity": "sha512-qbhSYUxk6mBaF096B3JOQSumXbKWHenmT97cSpdNzgkWwGjhjhE/KZODCoDNhI2I4C9Cb6R/Q13S4BYkUSXoXQ==" + }, + "node_modules/min-dom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-4.2.1.tgz", + "integrity": "sha512-TMoL8SEEIhUWYgkj7XMSgxmwSyGI+4fP2KFFGnN3FbHfbGHVdsLYSz8LoIsgPhz4dWRmLvxWWSMgzZMJW5sZuA==", + "dependencies": { + "component-event": "^0.2.1", + "domify": "^1.4.1", + "min-dash": "^4.2.1" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "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" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mobx": { + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz", + "integrity": "sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react-lite": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.1.0.tgz", + "integrity": "sha512-QEP10dpHHBeQNv1pks3WnHRCem2Zp636lq54M2nKO2Sarr13pL4u6diQXf65yzXUn0mkk18SyIDCm9UOJYTi1w==", + "dependencies": { + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/moddle": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/moddle/-/moddle-7.2.0.tgz", + "integrity": "sha512-x1+JREThy7JBOBR3g2hbOnOfrlC/YAWXX9RzrSZS5HhqeuBly9H/PCtOBtcQs+Y2sjRAXF+WTNSgHvn8Uq+6Yw==", + "dependencies": { + "min-dash": "^4.2.1" + } + }, + "node_modules/moddle-xml": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/moddle-xml/-/moddle-xml-11.0.0.tgz", + "integrity": "sha512-L3Sseepfcq9Uy0iIfqEDTXSoYLva1Y/JGbN/4AMOeQ6cqbu8Ma/SDJIdOFm7smsAa64j2z3SwCGG3FIilQVnUg==", + "dependencies": { + "min-dash": "^4.0.0", + "saxen": "^10.0.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "moddle": ">= 6.2.0" + } + }, + "node_modules/motion-dom": { + "version": "12.7.3", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.7.3.tgz", + "integrity": "sha512-IjMt1YJHrvyvruFvmpmd6bGXXGCvmygrnvSb3aZ8KhOzF4H3PulU+cMBzH+U8TBJHjC/mnmJFRIA1Cu4vBfcBA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.7.2" + } + }, + "node_modules/motion-utils": { + "version": "12.7.2", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.7.2.tgz", + "integrity": "sha512-XhZwqctxyJs89oX00zn3OGCuIIpVevbTa+u82usWBC6pSHUd2AoNWiYa7Du8tJxJy9TFbZ82pcn5t7NOm1PHAw==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nan": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", + "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", + "license": "MIT", + "optional": true + }, + "node_modules/nano-staged": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/nano-staged/-/nano-staged-0.8.0.tgz", + "integrity": "sha512-QSEqPGTCJbkHU2yLvfY6huqYPjdBrOaTMKatO1F8nCSrkQGXeKwtCiCnsdxnuMhbg3DTVywKaeWLGCE5oJpq0g==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0" + }, + "bin": { + "nano-staged": "lib/bin.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + }, + "node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-svg-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", + "dependencies": { + "svg-arc-to-cubic-bezier": "^3.0.0" + } + }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/numeralize-ru": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/numeralize-ru/-/numeralize-ru-2.0.0.tgz", + "integrity": "sha512-6EDlOoJ/Bf7vhNzotsbDo9pWQBMd3aYDom3yAl2lj6sYeNEcNbZFbuWgstBq2w2HGXoBOhvSWdNhicZSetTiFQ==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-refs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-refs/-/object-refs-0.4.0.tgz", + "integrity": "sha512-6kJqKWryKZmtte6QYvouas0/EIJKPI1/MMIuRsiBlNuhIMfqYTggzX2F1AJ2+cDs288xyi9GL7FyasHINR98BQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", + "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "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==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/parent-module": { + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==" + }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-intersection": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/path-intersection/-/path-intersection-3.1.0.tgz", + "integrity": "sha512-3xS3lvv/vuwm5aH2BVvNRvnvwR2Drde7jQClKpCXTYXIMMjcw/EnMhzCgeHwqbCpzi760PEfAkU53vSIlrNr9A==", + "engines": { + "node": ">= 14.20" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path2d-polyfill": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz", + "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/pdfjs-dist": { + "version": "3.11.174", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz", + "integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "canvas": "^2.11.2", + "path2d-polyfill": "^2.0.1" + } + }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/phone-number-to-timezone": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/phone-number-to-timezone/-/phone-number-to-timezone-1.0.8.tgz", + "integrity": "sha512-pWfc0zqgB7pGhrvUVNF5cL+6txkwRQVZzKSNYlWTXostjG0kLCIAgbdx4TbBQ04J0B2waaC182s6BT8YzwzbZw==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prefix-style": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz", + "integrity": "sha512-gdr1MBNVT0drzTq95CbSNdsrBDoHGlb2aDJP/FoY+1e+jSDPOb1Cv554gH2MGiSr2WTcXi/zu+NaFzfcHQkfBQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-organize-imports": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", + "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "dev": true, + "peerDependencies": { + "prettier": ">=2.0", + "typescript": ">=2.9", + "vue-tsc": "^2.1.0" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/prop-types": { + "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" + } + }, + "node_modules/prosemirror-changeset": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz", + "integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.6.2.tgz", + "integrity": "sha512-0nDHH++qcf/BuPLYvmqZTUUsPJUCPBUXt0J1ErTcDIS369CTp773itzLGIgIXG4LJXOlwYCr44+Mh4ii6MP1QA==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz", + "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", + "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz", + "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz", + "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz", + "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.1.tgz", + "integrity": "sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.20.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz", + "integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.24.1.tgz", + "integrity": "sha512-YM053N+vTThzlWJ/AtPtF1j0ebO36nvbmDy4U7qA2XQB8JVaQp1FmB9Jhrps8s+z+uxhhVTny4m20ptUvhk0Mg==", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz", + "integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==", + "dependencies": { + "prosemirror-model": "^1.19.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.0.tgz", + "integrity": "sha512-gg1tAfH1sqpECdhIHOA/aLg2VH3ROKBWQ4m8Qp9mBKrOxQRW61zc+gMCI8nh22gnBzd1t2u1/NPLmO3nAa3ssg==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.6.4.tgz", + "integrity": "sha512-TkDY3Gw52gRFRfRn2f4wJv5WOgAOXLJA2CQJYIJ5+kdFbfj3acR4JUW6LX2e1hiEBiUwvEhzH5a3cZ5YSztpIA==", + "dependencies": { + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.24.1", + "prosemirror-state": "^1.4.3", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.37.2" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", + "dependencies": { + "@remirror/core-constants": "3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz", + "integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.38.0.tgz", + "integrity": "sha512-O45kxXQTaP9wPdXhp8TKqCR+/unS/gnfg9Q93svQcB3j0mlp2XSPAmsPefxHADwzC+fbNS404jqRxm3UQaGvgw==", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, + "node_modules/re-resizable": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", + "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-avatar-editor": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/react-avatar-editor/-/react-avatar-editor-13.0.2.tgz", + "integrity": "sha512-a4ajbi7lwDh98kgEtSEeKMu0vs0CHTczkq4Xcxr1EiwMFH1GlgHCEtwGU8q/H5W8SeLnH4KPK8LUjEEaZXklxQ==", + "dependencies": { + "@babel/plugin-transform-runtime": "^7.12.1", + "@babel/runtime": "^7.12.5", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-chartjs-2": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz", + "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-collapsible": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/react-collapsible/-/react-collapsible-2.10.0.tgz", + "integrity": "sha512-kEVsmlFfXBMTCnU5gwIv19MdmPAhbIPzz5Er37TiJSzRKS0IHrqAKQyQeHEmtoGIQMTcVI46FzE4z3NlVTx77A==", + "peerDependencies": { + "react": "~15 || ~16 || ~17 || ~18", + "react-dom": "~15 || ~16 || ~17 || ~18" + } + }, + "node_modules/react-countup": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.5.3.tgz", + "integrity": "sha512-udnqVQitxC7QWADSPDOxVWULkLvKUWrDapn5i53HE4DPRVgs+Y5rr4bo25qEl8jSh+0l2cToJgGMx+clxPM3+w==", + "dependencies": { + "countup.js": "^2.8.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, + "node_modules/react-custom-scrollbars-2": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-custom-scrollbars-2/-/react-custom-scrollbars-2-4.5.0.tgz", + "integrity": "sha512-/z0nWAeXfMDr4+OXReTpYd1Atq9kkn4oI3qxq3iMXGQx1EEfwETSqB8HTAvg1X7dEqcCachbny1DRNGlqX5bDQ==", + "dependencies": { + "dom-css": "^2.0.0", + "prop-types": "^15.5.10", + "raf": "^3.1.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, + "node_modules/react-docgen": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.0.tgz", + "integrity": "sha512-APPU8HB2uZnpl6Vt/+0AFoVYgSRtfiP6FLrZgPPTDmqSb2R4qZRbgd0A3VzIFxDt5e+Fozjx79WjLWnF69DK8g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.18.9", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "@types/babel__core": "^7.18.0", + "@types/babel__traverse": "^7.18.0", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=16.14.0" + } + }, + "node_modules/react-docgen-typescript": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", + "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", + "dev": true, + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, + "node_modules/react-fast-compare": { + "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==" + }, + "node_modules/react-gtm-module": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz", + "integrity": "sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw==" + }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, + "node_modules/react-i18next": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.1.tgz", + "integrity": "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-number-format": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.3.tgz", + "integrity": "sha512-VCY5hFg/soBighAoGcdE+GagkJq0230qN6jcS5sp8wQX1qy1fYN/RX7/BXkrs0oyzzwqR8/+eSUrqXbGeywdUQ==", + "license": "MIT", + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-phone-input-2": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/react-phone-input-2/-/react-phone-input-2-2.15.1.tgz", + "integrity": "sha512-W03abwhXcwUoq+vUFvC6ch2+LJYMN8qSOiO889UH6S7SyMCQvox/LF3QWt+cZagZrRdi5z2ON3omnjoCUmlaYw==", + "dependencies": { + "classnames": "^2.2.6", + "lodash.debounce": "^4.0.8", + "lodash.memoize": "^4.1.2", + "lodash.reduce": "^4.6.0", + "lodash.startswith": "^4.2.1", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0", + "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0" + } + }, + "node_modules/react-player": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", + "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", + "dependencies": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/react-player/node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz", + "integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-resizable-panels": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.7.tgz", + "integrity": "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/react-rnd": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.2.tgz", + "integrity": "sha512-0Tm4x7k7pfHf2snewJA8x7Nwgt3LV+58MVEWOVsFjk51eYruFEa6Wy7BNdxt4/lH0wIRsu7Gm3KjSXY2w7YaNw==", + "dependencies": { + "re-resizable": "6.11.2", + "react-draggable": "4.4.6", + "tslib": "2.6.2" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-rnd/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/react-router": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.2.tgz", + "integrity": "sha512-9Rw8r199klMnlGZ8VAsV/I8WrIF6IyJ90JQUdboupx1cdkgYqwnrYjH+I/nY/7cA1X5zia4mDJqH36npP7sxGQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.2.tgz", + "integrity": "sha512-yk1XW8Fj7gK7flpYBXF3yzd2NbX6P7Kxjvs2b5nu1M04rb5pg/Zc4fGdBNTeT4eDYL2bvzWNyKaIMJX/RKHTTg==", + "license": "MIT", + "dependencies": { + "react-router": "7.5.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-swipeable": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.2.tgz", + "integrity": "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==", + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-toastify": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", + "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-toastify/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "dev": true, + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "node_modules/refa": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.8.0" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "node_modules/requireindex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", + "integrity": "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/resolve": { + "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" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" + }, + "node_modules/rrdom": { + "version": "2.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/rrdom/-/rrdom-2.0.0-alpha.18.tgz", + "integrity": "sha512-fSFzFFxbqAViITyYVA4Z0o5G6p1nEqEr/N8vdgSKie9Rn0FJxDSNJgjV0yiCIzcDs0QR+hpvgFhpbdZ6JIr5Nw==", + "dependencies": { + "rrweb-snapshot": "^2.0.0-alpha.18" + } + }, + "node_modules/rrweb": { + "version": "2.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/rrweb/-/rrweb-2.0.0-alpha.18.tgz", + "integrity": "sha512-1mjZcB+LVoGSx1+i9E2ZdAP90fS3MghYVix2wvGlZvrgRuLCbTCCOZMztFCkKpgp7/EeCdYM4nIHJkKX5J1Nmg==", + "dependencies": { + "@rrweb/types": "^2.0.0-alpha.18", + "@rrweb/utils": "^2.0.0-alpha.18", + "@types/css-font-loading-module": "0.0.7", + "@xstate/fsm": "^1.4.0", + "base64-arraybuffer": "^1.0.1", + "mitt": "^3.0.0", + "rrdom": "^2.0.0-alpha.18", + "rrweb-snapshot": "^2.0.0-alpha.18" + } + }, + "node_modules/rrweb-snapshot": { + "version": "2.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.18.tgz", + "integrity": "sha512-hBHZL/NfgQX6wO1D9mpwqFu1NJPpim+moIcKhFEjVTZVRUfCln+LOugRc4teVTCISYHN8Cw5e2iNTWCSm+SkoA==", + "dependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/saxen": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/saxen/-/saxen-10.0.0.tgz", + "integrity": "sha512-RXsmWok/SAWqOG/f5ADEz51DN9WtZEzqih3e08ranldcaXekxjx8NBKjGh/y5hlowjo0JH/LekBu6gtPFD1G6g==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/scheduler": { + "version": "0.25.0-rc-603e6108-20241029", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz", + "integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==" + }, + "node_modules/scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + } + }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-git-hooks": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.12.1.tgz", + "integrity": "sha512-NB3V4XyCOrWTIhjh85DyEoVlM3adHWwqQXKYHmuegy/108bJPP6YxuPGm4ZKBq1+GVKRbKJuzNY//09cMJYp+A==", + "dev": true, + "hasInstallScript": true, + "bin": { + "simple-git-hooks": "cli.js" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/smol-toml": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz", + "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/storybook": { + "version": "8.6.12", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.12.tgz", + "integrity": "sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==", + "dev": true, + "dependencies": { + "@storybook/core": "8.6.12" + }, + "bin": { + "getstorybook": "bin/index.cjs", + "sb": "bin/index.cjs", + "storybook": "bin/index.cjs" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "dev": true + }, + "node_modules/string-ts": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/string-ts/-/string-ts-2.2.1.tgz", + "integrity": "sha512-Q2u0gko67PLLhbte5HmPfdOjNvUKbKQM+mCNQae6jE91DmoFHY6HH9GcdqCeNx87DZ2KKjiFxmA0R/42OneGWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-components": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", + "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/styled-components/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/styled-components/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-arc-to-cubic-bezier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", + "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==" + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "node_modules/tiny-svg": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.1.3.tgz", + "integrity": "sha512-9mwnPqXInRsBmH/DO6NMxBE++9LsqpVXQSSTZGc5bomoKKvL5OX/Hlotw7XVXP6XLRcHWIzZpxfovGqWKgCypQ==" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, + "node_modules/to-camel-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", + "integrity": "sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==", + "dependencies": { + "to-space-case": "^1.0.0" + } + }, + "node_modules/to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", + "dependencies": { + "to-no-case": "^1.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-pattern": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.7.0.tgz", + "integrity": "sha512-0/FvIG4g3kNkYgbNwBBW5pZBkfpeYQnH+2AA3xmjkCAit/DSDPKmgwC3fKof4oYUq6gupClVOJlFl+939VRBMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tsconfck": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz", + "integrity": "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==", + "dev": true, + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "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==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "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" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/usehooks-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz", + "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-compatible-readable-stream": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz", + "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/vite-plugin-eslint": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/vite-plugin-eslint/-/vite-plugin-eslint-1.8.1.tgz", + "integrity": "sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^4.2.1", + "@types/eslint": "^8.4.5", + "rollup": "^2.77.2" + }, + "peerDependencies": { + "eslint": ">=7", + "vite": ">=2" + } + }, + "node_modules/vite-plugin-eslint/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/vite-plugin-eslint/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite-plugin-eslint/node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/vite-plugin-svgr": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", + "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.3", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" + } + }, + "node_modules/vite-tsconfig-paths": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/voximplant-websdk": { + "version": "4.8.9-2892", + "resolved": "https://registry.npmjs.org/voximplant-websdk/-/voximplant-websdk-4.8.9-2892.tgz", + "integrity": "sha512-7xqOKfands8srlMX7WoO/cTmnoPQ0bfIk5sTa19sNXMziTCHEFQNa98AbHrhUT3gJ0BgMxFtti4jaXVmIlsS0g==" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC", + "optional": true + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.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": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoga-layout": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", + "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==" + }, + "node_modules/zeebe-bpmn-moddle": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/zeebe-bpmn-moddle/-/zeebe-bpmn-moddle-1.9.0.tgz", + "integrity": "sha512-Y9ncIdP4m1PKbIBDqSghwZud2eiiBpfygE0bTApGqtnGlJMA/6Xanl/J7ujxG5zREoAliwf6rJyJFk3FZ75AYg==" + }, + "node_modules/zod": { + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", + "integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==", + "dev": true, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..66180ea --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,205 @@ +{ + "name": "amwork-frontend", + "version": "3.14.3", + "private": true, + "type": "module", + "main": "./src/index.tsx", + "scripts": { + "start": "vite", + "build": "NODE_OPTIONS=--max_old_space_size=8192 vite build", + "debug": "VITE_REACT_QUERY_DEVTOOLS_ENABLED=true vite", + "serve": "vite preview", + "lint": "eslint --max-warnings=0 src", + "lint:fix": "eslint --max-warnings=0 src --fix", + "ts": "NODE_OPTIONS=--max_old_space_size=8192 tsc --noEmit", + "storybook": "storybook dev -p 6006 --no-open", + "storybook:build": "NODE_OPTIONS=--max_old_space_size=5120 storybook build", + "postinstall": "npx simple-git-hooks", + "knip": "knip --config src/app/config/knip/knip.ts" + }, + "dependencies": { + "@babel/plugin-proposal-decorators": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.27.0", + "@babel/preset-typescript": "^7.27.0", + "@directus/sdk": "^17.0.0", + "@emoji-mart/data": "^1.2.1", + "@emoji-mart/react": "^1.1.1", + "@emotion/react": "^11.14.0", + "@emotion/utils": "^1.4.2", + "@formkit/auto-animate": "^0.8.2", + "@fullcalendar/core": "^6.1.17", + "@fullcalendar/daygrid": "^6.1.17", + "@fullcalendar/interaction": "^6.1.17", + "@fullcalendar/list": "^6.1.17", + "@fullcalendar/multimonth": "^6.1.17", + "@fullcalendar/react": "^6.1.17", + "@fullcalendar/resource": "^6.1.17", + "@fullcalendar/resource-daygrid": "^6.1.17", + "@fullcalendar/resource-timegrid": "^6.1.17", + "@fullcalendar/resource-timeline": "^6.1.17", + "@fullcalendar/scrollgrid": "^6.1.17", + "@fullcalendar/timegrid": "^6.1.17", + "@hello-pangea/dnd": "^18.0.1", + "@mantine/core": "^7.17.4", + "@mantine/dates": "^7.17.4", + "@mantine/hooks": "^7.17.4", + "@mantine/tiptap": "^7.17.4", + "@newrelic/browser-agent": "^1.287.0", + "@react-pdf-viewer/core": "3.12.0", + "@react-pdf-viewer/default-layout": "3.12.0", + "@react-pdf/renderer": "^4.3.0", + "@tabler/icons": "^3.31.0", + "@tabler/icons-react": "^3.31.0", + "@tanstack/react-query": "^5.74.3", + "@tanstack/react-table": "^8.21.3", + "@tiptap/extension-highlight": "^2.11.7", + "@tiptap/extension-link": "^2.11.7", + "@tiptap/extension-placeholder": "^2.11.7", + "@tiptap/extension-subscript": "^2.11.7", + "@tiptap/extension-superscript": "^2.11.7", + "@tiptap/extension-text-align": "^2.11.7", + "@tiptap/extension-text-style": "^2.11.7", + "@tiptap/extension-underline": "^2.11.7", + "@tiptap/pm": "^2.11.7", + "@tiptap/react": "^2.11.7", + "@tiptap/starter-kit": "^2.11.7", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "axios": "^1.8.4", + "bowser": "^2.11.0", + "bpmn-js": "^18.4.0", + "bpmn-js-color-picker": "^0.7.1", + "chart.js": "^4.4.9", + "country-code-to-flag-emoji": "^2.0.0", + "date-fns": "^4.1.0", + "dayjs": "^1.11.13", + "decimal.js": "^10.5.0", + "diagram-js": "^15.2.4", + "diagram-js-grid": "^1.1.0", + "diagram-js-minimap": "^5.2.0", + "dompurify": "^3.2.5", + "emoji-mart": "^5.6.0", + "file-saver": "^2.0.5", + "framer-motion": "^12.7.3", + "get-blob-duration": "^1.2.0", + "html-to-text": "^9.0.5", + "i18next": "^25.0.0", + "i18next-browser-languagedetector": "^8.0.4", + "i18next-http-backend": "^3.0.2", + "js-cookie": "^3.0.5", + "js-file-download": "^0.4.12", + "libphonenumber-js": "^1.12.6", + "mac-scrollbar": "^0.13.8", + "mobx": "^6.13.7", + "mobx-react-lite": "^4.1.0", + "nodemailer": "^6.10.1", + "normalize.css": "^8.0.1", + "numeralize-ru": "^2.0.0", + "p-limit": "^6.2.0", + "path-to-regexp": "^8.2.0", + "pdfjs-dist": "3.11.174", + "phone-number-to-timezone": "^1.0.8", + "qrcode": "^1.5.4", + "react": "^19.1.0", + "react-avatar-editor": "^13.0.2", + "react-chartjs-2": "^5.3.0", + "react-collapsible": "^2.10.0", + "react-countup": "^6.5.3", + "react-custom-scrollbars-2": "^4.5.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^19.1.0", + "react-gtm-module": "^2.0.11", + "react-helmet": "^6.1.0", + "react-i18next": "^15.4.1", + "react-phone-input-2": "^2.15.1", + "react-player": "^2.16.0", + "react-resizable-panels": "^2.1.7", + "react-rnd": "^10.5.2", + "react-router-dom": "^6.0.0", + "react-swipeable": "^7.0.2", + "react-textarea-autosize": "^8.5.9", + "react-toastify": "10.0.6", + "reflect-metadata": "0.2.2", + "socket.io-client": "^4.8.1", + "styled-components": "^5.3.11", + "usehooks-ts": "^3.1.1", + "uuid": "^11.1.0", + "voximplant-websdk": "^4.8.9-2892", + "xlsx": "^0.18.5", + "zeebe-bpmn-moddle": "^1.9.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@babel/plugin-transform-class-properties": "^7.25.9", + "@eslint/compat": "^1.2.8", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.24.0", + "@storybook/addon-essentials": "^8.6.12", + "@storybook/addon-interactions": "^8.6.12", + "@storybook/addon-links": "^8.6.12", + "@storybook/blocks": "^8.6.12", + "@storybook/react": "^8.6.12", + "@storybook/react-vite": "^8.6.12", + "@stylistic/eslint-plugin": "^4.2.0", + "@tanstack/eslint-plugin-query": "^5.73.3", + "@tanstack/react-query-devtools": "^5.74.3", + "@total-typescript/ts-reset": "^0.6.1", + "@types/html-to-text": "^9.0.4", + "@types/js-cookie": "^3.0.6", + "@types/node": "^22.14.1", + "@types/nodemailer": "^6.4.17", + "@types/qrcode": "^1.5.5", + "@types/react": "^19.1.2", + "@types/react-avatar-editor": "^13.0.4", + "@types/react-dom": "^19.1.2", + "@types/react-gtm-module": "^2.0.4", + "@types/react-helmet": "^6.1.11", + "@types/styled-components": "5.1.34", + "@types/uuid": "^10.0.0", + "@typescript-eslint/parser": "^8.30.1", + "@vitejs/plugin-react": "^4.4.0", + "babel-plugin-styled-components": "^2.1.4", + "eslint": "^9.24.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-i18next": "^6.1.1", + "eslint-plugin-react-hooks-extra": "^1.48.1", + "eslint-plugin-regexp": "^2.7.0", + "eslint-plugin-storybook": "^0.12.0", + "knip": "^5.50.4", + "nano-staged": "^0.8.0", + "prettier": "^3.5.3", + "prettier-plugin-organize-imports": "^4.1.0", + "simple-git-hooks": "^2.12.1", + "storybook": "^8.6.12", + "typescript": "^5.8.3", + "vite": "^6.3.0", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-svgr": "^4.3.0", + "vite-tsconfig-paths": "^5.1.4" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "resolutions": { + "styled-components": "5.3.11" + }, + "nano-staged": { + "*.{ts,tsx,js,jsx}": [ + "prettier --write", + "eslint --fix" + ] + }, + "simple-git-hooks": { + "pre-commit": "./node_modules/.bin/nano-staged", + "pre-push": "npm run ts" + } +} diff --git a/frontend/public/favicons/amwork/apple_touch_120x120.png b/frontend/public/favicons/amwork/apple_touch_120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..b695cfac79998b914b1bc29d8f29129c71b2ca60 GIT binary patch literal 1079 zcmV-71jze|P)KT?d+odX8j4-IOw3Z z5OHQJVpdFWaG@~#+?;QP8EBut`mOlbie=pq`1MlK*rH#GGw>)1HxZ%w9bR6F))gMI z#;jhAW5Dr{Z9&&^Y)qFrp&ppZo?;UfTcC-G2JoMPhmXKx*%2F_xi@eREEh4hyneI} zvkbH7tlvIz50_Zq;)sJY{{wrlRrTg98#;jL?0eW~>~Y%tp?x;VTT|OF%Q9%nFL3O- zbswpn8UVL5?hQa=*^DViFx}%#d^zX4fz35ODB-lruH9$D`fvrAaP$V2oXvDSTWx$` z!jik?k$)fSpsf;R!fE&Gvd(i24;src9J{bF#qtiPUFMmw_G4d_L(%PK*DD zqnj1;#HF-GorIguz@x(+oeuU0=c3!EdOEr30kQZt z(*sW>xvmA!y_4G$zYnz)-LU_)k$iefgva)>`{$pEyp;p>k(>klim> zmLI@$WtADbghkorE`2-@M3=BE149vbrb}3oPb~JPRA;A4Sdv4T#U5S4vg`qqz1Wjn z!m_Nhuj2ve1(&cW_xWoOtKp2`5*Fm(pwDzgZDKcq1$lI*7JDn8Jy(o$6EJ0d#?EIK zd+YIlWj{u*>84@I(8D;6zH#RldtP(5Yzy58rhH}1#h8sgve~N=VXj-;Uv^Pf5~h9_ zyy>8o4D&KHOF#6JE%j)iNy9(CLqCZyzwMXIKOTbXjA^dwfXMCeC?Ca z<$#Oj!DhpjxAWX588>%T#WT6R*@jIWZ~t0ekDVUv-cVYUb~%wu7ZOGHtE?D5cx!#4 xs8_3gEN0qTp0}x@Q8hI+H8nLgH8nLK&Tr599gY1(StI}e002ovPDHLkV1k0Z91{Ql literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/amwork/apple_touch_152x152.png b/frontend/public/favicons/amwork/apple_touch_152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..404e4b47a244cdc123cdf7d60ad024482b6e679a GIT binary patch literal 1397 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 VF~gj4M#BIA002ovPDHLkV1h=vI6D9U literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/amwork/favicon_16x16.png b/frontend/public/favicons/amwork/favicon_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..a39431220c8529b4e886b825ed8e636b034eaf7f GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!aez;VYs7lli1l({QZ{lUh%xPK zQO}9w0}od<9S&bX + + + \ No newline at end of file diff --git a/frontend/public/favicons/amwork/favicon_32x32.png b/frontend/public/favicons/amwork/favicon_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..6cc097d3aa481d770280e78a8bc14d30be9ba24b GIT binary patch literal 399 zcmV;A0dW3_P)P)t-sSg##e zuN+se9ayg*Sg#lW003C89jn)lZMiwX;i`DPN8j+&q0)fW>&BMNa?9qqw%nYT&2q}- zxc~qE_4@JP@YL1o$E??m>h000JbQchFvzpo%50Hfks5&!@I?MXyIR5;6} z)7fr>FboCI42u_VNLUK}|1T|`mCKw@H|J@Ii54gsJs(kbN@Hy_y*}WkQg_*z_2kN9vpa{9!Y^6%y@yT zz66LR*UNZd0?~5V9Y|z?ljXz?P?T9GuPdWYR_8!Q9bVTw=}7c|t@kUZ@1Yzn4lI@r z6Vg}&I}k003BiO1IG0>O5rPf?GH(ME0JQB18%002ovPDHLkV1ksltu+7u literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/amwork/favicon_32x32.svg b/frontend/public/favicons/amwork/favicon_32x32.svg new file mode 100644 index 0000000..a421045 --- /dev/null +++ b/frontend/public/favicons/amwork/favicon_32x32.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/public/favicons/favicon.png b/frontend/public/favicons/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..fa30fc1d2a9bbb073f017a8affc53753c09f1a3c GIT binary patch literal 29498 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 literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/mywork/apple_touch_152x152.png b/frontend/public/favicons/mywork/apple_touch_152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..404e4b47a244cdc123cdf7d60ad024482b6e679a GIT binary patch literal 1397 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 VF~gj4M#BIA002ovPDHLkV1h=vI6D9U literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/mywork/favicon_16x16.png b/frontend/public/favicons/mywork/favicon_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..a39431220c8529b4e886b825ed8e636b034eaf7f GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!aez;VYs7lli1l({QZ{lUh%xPK zQO}9w0}od<9S&bX + + + \ No newline at end of file diff --git a/frontend/public/favicons/mywork/favicon_32x32.png b/frontend/public/favicons/mywork/favicon_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..6cc097d3aa481d770280e78a8bc14d30be9ba24b GIT binary patch literal 399 zcmV;A0dW3_P)P)t-sSg##e zuN+se9ayg*Sg#lW003C89jn)lZMiwX;i`DPN8j+&q0)fW>&BMNa?9qqw%nYT&2q}- zxc~qE_4@JP@YL1o$E??m>h000JbQchFvzpo%50Hfks5&!@I?MXyIR5;6} z)7fr>FboCI42u_VNLUK}|1T|`mCKw@H|J@Ii54gsJs(kbN@Hy_y*}WkQg_*z_2kN9vpa{9!Y^6%y@yT zz66LR*UNZd0?~5V9Y|z?ljXz?P?T9GuPdWYR_8!Q9bVTw=}7c|t@kUZ@1Yzn4lI@r z6Vg}&I}k003BiO1IG0>O5rPf?GH(ME0JQB18%002ovPDHLkV1ksltu+7u literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/mywork/favicon_32x32.svg b/frontend/public/favicons/mywork/favicon_32x32.svg new file mode 100644 index 0000000..a421045 --- /dev/null +++ b/frontend/public/favicons/mywork/favicon_32x32.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/frontend/public/favicons/proma/apple_touch_120x120.png b/frontend/public/favicons/proma/apple_touch_120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..f58c0b4587a2d400b3f58046d0cf834ad2a24315 GIT binary patch literal 896 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 literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/proma/apple_touch_180x180.png b/frontend/public/favicons/proma/apple_touch_180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..36e02cce26060b89637ad295ab883b16481a4e54 GIT binary patch literal 1312 zcmbu9`&ZI;7{{qpI+=>)jEl%cO`DpRFQ7FqS$HieF;8Jl+?Ebd#06$*&8!sdq;@i` zux3GEqDE~<8YUUt!y#f9VdOML%$zCGIjfHD>-Hz?hv)Trf1b~Cp3iTes$GQ0j;Y5p2Jk=XaUj84h7O0q5}A4&ILy-F^0iCbzzotH+T{Yi;mM;? zQX#6+nc(3jY-#k2ma`L^^LmHaX^v2D_Dy ztW7l*oq^p~PQU&U$@tW8cWMf^OguG?o+;$PQuXp2Vh@(5~T`&~kj^jZ4v zGfwt`WV*pn*>%14(ONaXtHC_cDHJ~)lFBh7DkM3fu^Sh6dG;~;xjb~?wMim=DhQa} zI<-8ASG|(>=&PN;xt|^ko_;yDom5HX{F)XB#p>TNlwU{KJgoGPNnEQ#?PP(nh$d^+ zAL=eN>X;D{(}AF5zOq4%{BS12w?xmp#z`7Kq1l=9nLg!vgoRlwL*-?>4hoEF_2<*d zsOi?U$bmdhCqFTfUV@8s*|#f+-J^IIe|0fq95CANk*jm3YkUn%1*(B@d+5d{p*Fj4 zwJzf~^w3qUd9fi`2}S7(!57978yi*9%Q1 zI(~@J_Z{iy0T_Zw15FOI{{JJlcz%kKODa_;$FesI78Lis2h72IQDM+Wt~1GW2^r`S z=08#wEwljcFoP4^qQ&XEy%cOa=rF}^r zmTK|!NppbZ>6$?v;4)7`XHrf^(PW?UYcHw=J!De!!e6eLlqyeaqgr=i4g}xM9H8vZ z=jP3CtfR?T;$O}uyAj9^)|+in;Z)|D%`NGFL_KUy4$(w|rjSvCyw!@kL9gG5`5>Bc$ z?oP9!iy_ESD3C*Va68+~sN`tg>)@}##%#{^SIX4S%OlwI6bB^SrIey)T1jRhd|r%* z6qSL6?z14K8F5-hJWw3>jX(z&Tv0q|F-`ZyLFnqx5#kX!fzI*Q&7$TeQ2WzkLOhx+ z7nj|MpN=l^DLuh!VKsPdg^rVLJ>Ruay-MzI8wsjgx2xxld9&~3*UN6$4h|a8w{8|g zw&k3X9#z$@t6% z-10zJm65m1#oEX|xIz2St4B`I>%=_pLB7&H4HCZtgY`VdHf$( literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/proma/favicon_16x16.png b/frontend/public/favicons/proma/favicon_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..ea15f6e09a1694ca9cb9953bc8d7cebc4cde28ee GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!O@L2`E0C5@Fq4os&%C50A#av? zULotEQpR}&5Ldx0Df!N|NdWhJ0SCV zw(f)mj|kbi6N0R^Oxoh^%kGFz%u?JivzOG5oHrZGJT%~bpTBNP$zA0$ rk3PFzb^ajysKkKvJY(l~M()}pd)ONVtg?0hZDR0r^>bP0l+XkKL;+R@ literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/proma/favicon_16x16.svg b/frontend/public/favicons/proma/favicon_16x16.svg new file mode 100644 index 0000000..538e9d2 --- /dev/null +++ b/frontend/public/favicons/proma/favicon_16x16.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/frontend/public/favicons/proma/favicon_32x32.png b/frontend/public/favicons/proma/favicon_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e75bf0a554e0959a3ac1f47606660a3ecd64cd GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyt^qzFu0UEs-n`(vLe@pajPnW- z@@7D;B#>0F05UQzDP>+#%DAKmVq8=L3JNGVWL{E~R4@l~O(o^cfeJ1xpA`T!NVFu# zFPP!J!1;vv_3st-&%f`wzrjIZeSd+y`?Td7feLndx;TbdoZdP)Qmk2lhkOUEo&*@*4BrZ%4 zKUneYHFHc#|PFwaF&I#EIeP9eyym8t#;Jde`3>L q)kmII`}TN$_b9or=|NoNKE_YGR88upZ4(B1g2B_(&t;ucLK6UW8i{!T literal 0 HcmV?d00001 diff --git a/frontend/public/favicons/proma/favicon_32x32.svg b/frontend/public/favicons/proma/favicon_32x32.svg new file mode 100644 index 0000000..d70cfe3 --- /dev/null +++ b/frontend/public/favicons/proma/favicon_32x32.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/frontend/public/fonts/Geologica/SemiBold/GeologicaSemiBold.ttf b/frontend/public/fonts/Geologica/SemiBold/GeologicaSemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ccb8cb6e5fd23e43fb0ff558bfa6f5842d0bdb18 GIT binary patch literal 343100 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
|maVy;g7w5`hnE7n?hC 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
iSKYl z<5R3Imu(Bc?xZamYS(Rn5_H-x09|g&s?reD?o(TrvZyE z!c-B>C|LLBX#f=8^23)}y`8Ta)fM$*4j>cX?N{s!+sD>kA=S5Zm9O&B+d*q`%2jek%DmwAkOS^E zSd(I=Xn%k8)WAzHFU{<%_QWN&l1|f4cHCdub`^j`j zw@*Fv8?>WB4=Z`Hk!N5AF$*|549jQT|L$9N-@g*^2B@XxnQidswL`U{84@+V*sy7~ zm9oxXjW%P^CUGfL?SdjXh2Ku5v(v(Z4cVyLCIuQ;Bj}~?k#u$huQ|wHlP*jk*DwAX zGLzY1SvhOw#+frW-v98*mD#dOI7%*P=aOF$edfGsaqETADq&>6>~inmodD|uJrQ#{ zB+4O&&zx6fnv}@U6E%1SnPvv*2kkPnBd}BGhT!2Ize=G>E2JIpWAu@E2=nFI*xK4g zv-~K9ef+sLR&>$^=Pmz4KfY^i0}2nBlyMnbZiD812{s z^}_!U@x4p9$(k;_1I>wrTtm0PW#58Hxep4kkOJ+@d!lAOs$Ki8Yc1f7k^l}A!Z5l$ zT2G7%wR*)u=o!rpD&^&sZ6;FyInq2|mndgF9GVDE)kJuzCTe7jHB{DWU~Zo24wGld z@bTBD#2b$PLe(-}3}g#~gwetP==9%4kBkz`HlYy{P<9urJ3n@_wPGS6j#iVK`{cAF zwOv=`6wMHMtB~cU%FZSSo_O501xS4gXu<-#W+Ad+y$F_dV83l+Af0@B|_y zA$+R4$C?rUTz5udXC$$rNpR>QQgbFo;3EmHT04uWsH*j)^-l*! zHpApu0%t^R@?=Fs9%u$HKTAo$HvfN|`S4(yD!HFB#)t`WKS3?2pFFV(2{hDCqV6e} zC9;1~T_m!9Mu4ui))cvr6Bq~TzFv5Wt!J-fuy+D?3b`MrL$fCr6)_ZiIwg13tm*&V zPymxT%ng2}mWaudu=lXhSUO^`he#Go3@uRUO!gzXZ52r3dWF+B#l9em&_w30;AxVy zl&85Gm9R%4)>rU!n|HUw(zSj1Cah%2`Opt=qMQ$LfD>g5qbM^q^h61d+yLX`N9+eA zJN_QZ#9tJB5v6>92B}TThxEYXY$K9BH0A^bz{pR>gus2U5VhufXe>TGFPQT|Yn;z> zKCGUKDcB4@>|JPYADqd*fz@s^j?dUS{X?^Ir>*809||1mhMEgi?qHS&5lK_$QJRMs z_tBEdN~hcP3-S~EcBnPWLqRy-S^WYOcKnK1f6DSuV8N~oA)m6ZlC+QA8j&JM_fXI! zJX>YFD~;YG?t<)VNq}nv;7YZXsN#DR;A*@!zUh2f@r9#plp%A#VC;nALNMe&-Eov3 zo0uHAx0K!J0E)-;dIhEhM6ML1&7K^{4vYm3kH?0Q3s>`La5H}#VSAJqn7#aVA>eO8^NJod(t7ae2revV=6I2rR^JuNzuT z3d&p3N})R9cq?2;f>xrum3ZOKBl4aETxgG-47iYWcQW8YQDMY=Sxyz7hgJ7X@wrp` zk8gkTO{zI3>E3*E`|*%yF3on((=AgkZ(fh4IP`r2iPr=6oNdTicxn0P)<@! zGWyviy-Dl~VAw5Kpcr;n^*waF8dzfKGiuxvr2Bm037+cnr%qFSR{pkS?>U%Vd8$um zhg6>nV9qUAAg0Adt?G*`(D=A2ohs3!X*L*|nsh$5KiWX91kKJ`FO0d!o6V5C1xEGK zW2b6h6BYCDV^de3;t4+^ljMY-h5A(sSla4^Ohns9D=JpWb3)p?uJ}c(_$DAjG)Am| zLkYit98MSbj%fL%pZ+@d^y%Q!s&-Fbl&>vO);n;Tr}~;+&gJ;iQ}H~mfMr;6a=Jpf z_ZPK*v5@9MZsASp0B<3YdLZirs0+9XdcT^K@ptM$%f9BBc7xrxKG=w=r4bGV>P2UI z0NF48>q_c{qE|RQnc7hF3Mlu(D^_x+_!hs_%e3}|+#1&?oW4PBjcXK;MV+_S-fQ*J zGbEugKCLa8!Kh2E^Gnd_dSp^A9&h9EU)D$N4)^%?BI1}vdP$G}pr%QgFX|4Tr9|ov zzY1^|wV$s`^r%-$kGRi&S(4l%?(^?Wl6&+geEuaw}I(^K8fCXxxwHhw%boSlj*GKfd{CEd=`1SD_XAY*I7q-9Fkz`DoGFA z-txSH1vd1d6$dVjK`j&l#6a#u`x=!U-`!RoMc(~%kPA*jV>1|C(-{dAq5BK)uC!{G*>;0E zwpZXvwTi3OUcn|zl~!7E-O-LLtIrl*@xI&;#J83e-@dN(C3iJd11;KM^_21%M1Dw( z?}r{s-Woa8A&l>OptD@s)+AFB+Ltze|5g%_YYrxbMw~?4g8-ppKsaTHA^Qv zFh0{?=`RnARxdm>8l^|2*cSHgiDw4p<_`S#qi_(SLMiQY5%W?hV)4;bNtISud};nq zd!JHBh1SHkK|XMkc<@B6ZM-FA#F+HS>7!=O9GiYq&e-&kJ&mHX^lZ_weFwK~-F5iH z;h**%{`lkH_iX*?OtlDK_xM4>Q!J1qTqRL){rmPGD)weZ2{|P9Z~GOFe?pUJ6U|05 zad*g^;X{OBRcURrP1Jh)l2}z$V^fFs9oQ4T?9n&KCc7Zr)YSgCTZe%2e zuX?S=s53>uuIWbT2ePXmA)FAJklM3HOkH)I+wH3Ln&Ie&lJykc5aF(>Xk_S=)@0Or z1$R9qF94%ickcKZn=4;SCG%?pI6Wc2MDSH-3)+2aOL{aC)o`2ZJwfu7NY!ig>bj&%c${T(B7u<%vTdDEv~U?D))Z>E2IpuwyX>_FKz1BYsU6Na>vtKzduj|aWK4h z>Iz^8I?`CaLYx#9wfrj7u@U<9?Khx*kH}sImBC;%;vLv_O_u0zAw0aBUfZX;*SBt#Kf!sQ@m3A##teImtK0m-H18iNN@%$8Os&rs%Fo-{J$-4)xsKkgignb6_0Qz4D+rE4=>^$4 zcrrf)3Z!Qw*-pfofZT?kR=!WxGrl8ClGhNeBYb43%7V4jQBDt9m93c!4+*`k>7s?Q zHK7&<fLU+Bd9!Y`;VvVJWHi_ZEIgvFzPg-xa^HZmdsL+KZ; zQ4UAch=wj_P3jc8R69Whv1ER;TTgvda{qzDi%+{^635+h&ppF>)*RTeW5Z-vIZF)|pI7u5jddP)Lo<})0qwR4 zb;TGm-rQ90M%kX(l=Et(%EwIFeCX|=)_2>IPaRjg_a28Ms@fc?5&f+9j_udgXLqHI-w#aL{5QLCpyS+t+2Mf)-xG)ErzVyp0?X8Tgm z3cO1c>0L|p0Wc#5FJdwBp*)2LxUi5lQCHQk$Q8#YVlHmNJdDH~^oln&Aj@WhF@C`K zi4!M|9}w@|`^6VuRK|~<_43OvPhnR>yQ2+@iS0db;K0;`m?#6wKva^|IBP^q@y8#3 zywepkA)Q(6n!E3N;<@LZo5=FvT=)i((;_`T9qPZ`JL~Bea^xF8cqyB!eG&io812yN zhVz98KvN?FDMHUwVM)o6HdQr}#yTWq=oK^?390|#N$J(=;F#O&ZVuoM3JN?aK(DOM z6Pmqn(ru`7+f-Uhn9;#k&J7qHVyf75jF5C(G|FHELvm$p4Mtm2TY0YhzGt1|T zWor|bY3}~(lNi+`KK!-tu>2H1Yhjb$q8l^j&goNcp80eRT=?i97|hY&pC1J}Nor@& z;UcgB?z%jo4%twQRSE~xLjyF#$sL;&o>Ca3yF$Q`ULw0D!?@dj9?wSO)fhU zRY_`u*_74Iql6V=73wyNkzqOqoE0my%g}`1zidA9F9qDtS(kGehZ1?-wxw@vsWc?_ z_ay`&(nkVm;ulu32u{UQg8*0ZD?_!Bsqi9Q%!6s{aIO7;A0OkJJXWjCs9LyiVbjg& ztQNXet7OXvgayL1+1c46ljBWNCOi~yXdX{J{mLt^*x8yawgbnf?9<5X{ykjI`m*yC z8~2tqxZDw*!C;Z|Rq;ayMLWd!*lx39-R^xWw`h;m0~_jp`!q2YWxqTpD~dpDY}k{r z65y@v5G$Qdg$f48+LnF=HC-61l?Di#v3wHc;mRu|GEugC8wgg&I0zTV{V!5j&~$6M z-z&B0wzvlHovVSJZp#+8UCwLN8Ql!Nm*}RjY&y@7rO-`lFmOPom_6}svY~Vbyvc@w z7$2PF9YCw0zKCLa;1TgfrAN$#F7a+HvMZjXO3XNKA;*qnyqoM{l(~)kvP1E1k~%1}8SFUx-J)X41JPgfKR zuRs|TWfwzl2v>tToVK{i_n*K6Y`UkxY)opBv(9zIpxi*Z0KcJTeuo;`J~n9|2On3De8ZY%4#$V_yNG*Ahaj18OVV zl7Xz*OH~i_@O+pJIerE|9ydAeo&+!KNFgqw`KW!}3Iq$}vo#Zf6A^d=;{G#H^LEtK z)VNIv$^H6Ymp*Q6FF$TKrw5+5vT!L+=NB9O30EhZ;GYaHXPYK;$b>$=I%*6F;d=pS z+qSst;<~A7>*{OH9X@xiI6}BkSmH)@H!pdI&W>-TD|zGKEPl_v`(_-4d{Z594`wT&5Z3*1)|x)_0S zcGUZ`>FP@B&10gQ+$akeHD*la7z!mBGloS;woKm)e*{7RmsOax<*#Q3Lj5Ve$8F+f zEiCuhne}AsI`jIS(b_u*5xJmW-`Y*o72;3C7NqGJB z^uSG4kcl^Yl+tJKhp#LsK6(5!zbP&bE2fL$Vpb~I(%Ct~KMmE|xN{fWy&goCZkt$k z?wBORlu0bc-lOcxFChmy%S#h`^@vot%F4>B63xlURaLlv8S^#fpC`ilkREu#u^P;`&vKGua<7IUYc+=l%PS zAJ3t@V}b$olyCCBiweIgdcwG5Y~>$fRS>CYE(hNm<+^M zY}SrhTQG3&glxLmrp`tg;R3rI_GoNh%=@Y}wyMlsfyChMsw%DL#8FF_#}j5bdW_7- zc90!rd5y1wd5MfA0m=_1Qm<9)u0)XyOhgPxqNZC6BSRe`Qt^v^_y<8p%rG^r@!*J& z1L8{8X7W4=&uRA#MsEG;_cD=K0f+IuGD~9Wh#f~0v;TTMXAug(C4B=+<3SwngUS2| z;ggTD#~>^Fv^iL6r&$?4wwj)wQimw4+itsUK;ionP)g)+b=gxO4n=vXsCqZ%~BV?;;HzA{tK&0F{F+k6heUcY?5d2nhi zF7`?AKX*0H6-M-Mu%S+$;2k_5B{D*+cRFkKxRb*)CHSA;e%bl?j@{1Y11I(#!CE|i ztn365m3o>`7fKSL1hT6dihe(pr*@W{FLT*c&B*g^GwJo-`g13XP9NV>;&p_D@@Yck zEst!){Dg*TJU~8t;(LVA2`LYelTZveqNC&~#iXJt1pj}y;fPTUHBc(oH#nUQ4Gqo) zRedwg2s8H}o0kX0Upa0k0aJPax1)jEv?F3q6hyW^hgAmVvi}8M2ZK7#nl8L6rTgy( zpFmYXcLaVW^6Pf#<9n*wV zG;Gmr_Vdzle zm%#C0buI`6ZFQFs8(^^sEfeam%6R(GPRb2m~V3&(qDiy5^_P z-oy|jO>1d4SFL>ioZ+15OI8QTU zxW37wwMTn~^y{76BTNKvy*sz5L&6OKRE(B%#^{Ln)Ili`c^ZHBA%l~`>{`FrTvfPz zISNVIF|+A`r`1coKU&&gM?SoWxOfY~i~ONI5Zi1w_#6EituZg%{~wdLI;_Dq|7OG3A3=aO;%GCcc|&K~T1r)>YmLixcc z`Q%mJNV`=xV0tugg7|EWnj<&cpT)KY)&ZlJU>y^`-#SSURX966-vH2Z1{VF7ysZ}Zhr5w&}d+0+|8`gBK4 zzGmfy{~pyQ5(BwSlVUvizcc*Wb9WTOHJt!YFDXEy83)| zg+&z+Ygg$)&CU4{uKs;`4;b!r59}3X5>B{X34nq@Gja;P|Vx-nnv)%rprQPbGytGIwah4zR+GLJB`;K^@KKZ)MZj1avE zYT-Bq{uN-5DX@4OIuAn3v%#N1M{v!H5m0(^!Y?}@P1>*9(1!8Qp@VQgI&k3Np+i$Z zOPZL6SRdu^&^O|tL;WU#IjH8x`*0C!V(ZnZ@X$%kfP$(C6R=l`t|_i)6tt$Oo+%L# zDLtc1TA{I`xCX*ByuUI6Pm8;?nb}Zt?EWB?`8VGW@Tmdt=hU8sfgaRyfF2eYpzLdI?~)PBO~J$um~{dMmE{mGF=PYKHD z$O%De*&6lr(K}YJ&Vac0E#SKVLtXRcTW`Jf(T{~THZ_aNQ{e!+JzKvfAt|MQ?;$f# zEC@Rq_R5<_WsVtnwT#E|-iC6S>X0Z&h%(r)SI&-UI)CQ$sncgmE1Q}cv=QNv;gLwP zLI!)$TaTQ|6^%0WNKHh(u|zdXL_hV3D)>;!`NqgcORrNlU9cJuSwWh%3SZS`U~V^o z*-+c?0~uTcVNazIt$YuX#Ld|(NVqoc%p%{mz=ps~)rJi}|C}C}X=O3eVo}g*z$M0n zf|3m#ieIu|j=5NDH&HZyVMRq*nFpz#7w0R&xqUH*XcBr$d=SM3v}gv152_9Pz)l4| zW52Pr)HU9{mTqj6>F7rJ%<1T&5ar$c{dn^tKF`E|kS3xp-a(p(ZZvbCPo#-38iLRO z^R+!NksTopq~*Oo(>no<;M}29rTRqmNBZ>k;A* zEeYL8kLQdHOG`4DL!x8!b^RmEwKY|B-ZC;8XsT+>2|dFcny9MZ!DfP2Rxd|{46WhY zF@kF_f}t3}mGwRw8#ok|!so*00JiP2!+@rS4WcC+2$TT~OT?QrHLbTOPl ze4tY3I3(;aWc|1sqaiIHjfk{-G%8CXsM(u`z}^2KkRK)#tKzV%hciKWaH#(Im$Cim zPo{4&1AnDM_}YYfsw%}OE@AZ>3D9xY zxm_{^(!s48g~(CUrcJvxvWX>J#HGui(Ok{B z^h`Lg;?T@YDaRgR(>?w4(+}O8JuD(GJWP>nphV*zgGDDZ^jMQxV&=is6EmO5nfb@9 zZCQC6vGQAw_{TgfE3?7IhstdHh8>5?E2;uD0}Dt!vBQ*UHh9X*E*$@LFA26_@Xw#C z6;W%7Gx#=I9COsPQleTqBp?z>=}=fJ|4*Dh1c0`jzry(2`hoFJPK*l$;~!-EQ!OEt zWhIr)GhVMdN}m|7H=4Ugx`4-A80b5=PlR0&25g$>x|5iyJauJ>+hooo*`V^omSvC) z5ILz_^k{j0+NJQk5Y|Hks@H>;&Q|v zzaOuX6gk4iS3OxCXy+bGt9~6{6kCOmO_^|z!Xf-GB!eiIcva4$N&-&x6FCF+@8>DJ ztn5a~rp$SWJmg3-kw_-(}p((;?kW%UL}uW)X3ZmYpH;< zoJWo_0Fm!oWOa2hRWLkyx~id9|BPLKR5T<%lMSAlSh?eDjc!Q!@7un+YCQOCq^VT> z_8TM!N!#pdaQl$@Nhmu!!S6;c29Y0q0<*;J{%JXY1>*Q{)Uh*KeH620XWQ)aH01~O zMTKhTYP4ZdC(bw>5JTn*5h;s=V&@!#M+i^q-@pIpX-`jpnQsY9s0Hv=f~hh3$-hjv z21eOLqaQ}S-M^k6JYnk9SelZyYVTWfR%|J@scOy?*V!~B+t#k!REosn=L&1)%+nnD za!phOUZxI>dgHBq4(x>Uh2H6lgrASkiGplk1d_PxYhCA@+I>eV+yOzfS&VTh{Ri~- zH~SMZQ@jk`99osBdrB`<_EdF?K;{xnNYc=uiKIzK#lSi9)S}MF#D$`}8R{G#niu>9 z^H7ys&jP|+#00WBw_5&#)4xd2YLy%n4`MckV>TvYHpXB!vVe=jFdJhq8+!MJpAL?= zCSKf{jks4iugk*`zguDkj@o&+#v7IjK%~X%h7Zp`9PCS0>~WG1>d%4xuR;k?`$4?k zvmpmMV|hmOq~NQt8U4*})a{WcBBiXx9I@xnsj{+4WJYl{)>WJ-?3tjhtE$Fry|bp+ zUsngt1U1x4{V0r(9I-fJ+mD;KZP{{UUwIVlnOBWVzy2R6vua~`p))3oAC773-h4QD z)OBgpAC)C8%4Adb$+@l<%+B2g4o^Tjxn85LO18{N*?FO7&&`{E+PQPv=FOY`_s!<7 zKmE98SmEX^Kj8Me@3(#U`}f~to+MS#K2cmCj7^3oiw`M2eREQhb%u!COwa-!`dEDI z*tvs;MPou(pwZ*;_zm4EV%&SMz>qN~%xti!fdWlhnx-&h9W+OWMThI>q-gcct}t^5 zD$8T)Jx%!KH){pIh{TU5%*p7U3yz>f3j~jMamx8~#ivi6J9Ga0h3aZlkF2h!u52th zUtL~-+p5Y7umD%GYP{21;ctecSG`RmB8E+^Qs((Gx`%D;ma8IBUo0<@RC1AiY8ApH z6?yS4kV&#siKO=9T64LL%e?(n`R&c}iHrXZNH@ew+?Xiq52%7{w?q{zI8?Jr!y>{- zKdDe%T)>d25)X}D2jvEi&zM7e@cw^g!DK%l*@7Pv!r|$#mE8>wW3^-$I~r!Jfsr0| zG!qfsQv>%A^H2aw7O@U6*vgi@$SD&t$7YQVFI%&wICfBSzd=y%A*-o?Dz9QQ7j9sv z_B$x4BP0`St~&8toIj8FRN#N?HrTR=da5*9wa|>KniB^PA3Ac-ZJ#e{xCmvOYc$Za zT=T9)bu&sJOO+YsT8Sro87uP+jCvaF4*N;^z(JYva5g`9SMHR_lP6Efz3aiJ|C0?G z#n~wMCj95=2Z#Qe1-D6x;F%__?fKwD&@P{Fz3@Cg`m>M8#o8v!oS8xHZA-HRBjky* zY>haEZOjRb@w?~<^a#upe`Ggipx}{FVS#rHoG>%*QhfT>H1s!6=Z(nWr zxx%3XTrAqXhuu=2fFHJhpSQ{Bbh+?tqq{jKR7EP}%A&&JS~vcv#deEE;oU}fJzMlP z`)wLeO<8F*aztvwVq;N6nM01eC1_tw1LWAZ>4B@X5(MJvr12TB=)WiC=MPF}4XS(d z^K&SJ951c|11JnmR?U{`GanZTjtnw!ScMyJ%nGFN<3({p>5X zE{Ww^z%f2ShiXk}^yn;DY_~(3^`7w3QxkypD@i)Qv5-M$^E7OiWMP5T3BRqEw(mxX z+`bEWThMe@In90~CtU->dvJBo`k<0-QFFE-(%5B)OYECA_PT4w^zh<#Q+nW8D+`tK zFS7K84ds?z&V-8ob8)sL_cmH1+b(7AopZ6h*J9%KUdV~vAt_79u0tNFqPTh$h(W%U z^!qmfiX?v!4v_pYoUb4R_gAx8qayvOR1f@%W?vQ#UswI>E}bp|@cWL-gzXtCa;Lfh zthB`TW?-g2WP9z)1eY=Ibz-!3vpp@0)^)eEFk1i40K4#t`0tL4)-vHjx4hSl4NdjE zkg)G;QE_DkQe4zqe9_36sfjRGmv`${&ZR|}U4_47DcIGyvYbdBXC&z+VMMC+3(L#1 zSP9%nC&K{Q6#rAE5DvNOfQk%|SshG|Fu_M5jy(zrHnuM#Fe)&L?Np4AadCd|*g-K_KtXBoFYDz--} zGsFD2gs;RE=XoLsos=>A~y#AaXJG1#rLaIa$sjXL?K@SN*0JCU`|aB+#fb-IC8yR54X7| z|M|wdYuBz_m=*XpKOTPVN@!cJlrfWUx#gBgaNeDG%XLFiQc{NEa#*#?oP^@i>39As zokPyBnR=s1S{k&~^2$aHGEwSEa}QI9*YXKSFu$#VamJ_GIcodT)v< zA-wMiyr;dgH&#Gz@N&XyzorY3nw!4bQrzq5iJ*h^2#x(1ULb4ODnSFq;7$pewn>`h zn;K#hj5z^gASTckmf2o`Fo)eVX5{EGqlUYDp~+W|#qG@7vIZF{s(j(8Irll(a#AdO zFMf01KPQ6VybB}nHms4iU=MVtpS%yHfh6!Eb(N*1^^&^wOwsvLv~c*3^Pb4$!}YA8 z=ooH`oIbBQs_uN@$%ETa$I>|)qWz>@i$bK!^I- z;20FDdrzXQkGmFdBYj*yz%8hci-`u639=b<=EMeHO%6#H{?fSzfOvfW@hk@8KYsc{ zI|E&Oi|JoD%wSD`V*NHUDy-w`yVX#BsR{}O67o z+8R3>EE}lR3J#^k(K3`SGR@AGO|uGYxP^9w0v~RuMFn-@$VOvCeK49HG!k=Ld$B0q z@Zv+0g(Jdo;ak$z3eSQ|dzqaTZfR>Zwu-}-FyURed3?`yh$G&Rg>mDA>(YjKVAugkUQ3+-`K11};`#rs5_bYY1E zT{5Nfj||;6p?3RLT=>Cz6NPSqjx`5X!y2#)KTBZ&w6(Qs6DPc6Syphj?;5ZU8L-In zLGRHTteSHPk5)7i+N*Sk*dGllp z9Hih&Zv|hv)QWGYmZ0xYtb*bwfMy3~nJm_U?jhjz^x*;IeAKHOpf z$lbENzB7F|VL{M)fNU6?fmbL(I@>C)yMbE?g8fPcc*%Rws`RXbgdue7&}*UaUL*&` zmVVe}AD-aq1Rclfk!3K-(a1gL#TC$kVChBhY!<9HmARu-NFvYYLnkPEti1Rut;?V+ z2B`ug=wM|^XKQ3tjl+R;BVY2>d|AH@Hy#yS+;5{w_SHz5_h^0u`6SRW>Lx&IdyS>bnTBCM*jr;UK z)7i}IU^V#)F<7)rAq0vwBv--G>y%Nn-7BIZTc{e=1ojvFhIsc`c{KyASb=S3Ej#zgzoYty4kRbv@_AY!AalBgyyEEAw!zGve(UT zvDYc%UipS%1zP!rm9Rbsvo{iB?n|^UI{NwNTgKD1rO~```t*%0BkN#oG*>Htg++dqjUYUG>y5{YV; zzzhvTskk}=MQ3YaE!EHbD_F~0u1!XzfPbQnb$-z9*tvQDQBLjE11lE;2^>*a(|U7g zn8hH1sC$Y}6rMbF!fnqFY65KZPx#kBz`<8raE;P>?_`Z$fwm_2(-FhTsz&S74Cr-%;jcZG&fe<^!xsR-t(V8}FE_4P15E#a!SEqNnwq-AaOi5F zGEj`t+^39LR=y#EIfOsuN(3CqIae=B=V}p1&>nssY=v#;++|y^Qwy?xnn#`ieft(P zSZi(lV%uxh@0Yb?e)+_mejGB^T-Gyo|8t6-Zn~nUV|CPlJY@&I^m|qE3q$0Sq9VMq zyv%QYm zrR;aIuOLCe7VK1~qiQ<~a-SY*@iT-|Oq<7LekS;C^0VnoZ<-MZ4-_hXeWxKsq6ONk zTlP$%x=#QTZo%_6poJ{9j;$l5UiAN2TvjcgF=P1^NyQ;UCrujqXJlZiS8y3+-+&^$ zzUYc2-n=W3c-z7_$QQNqWjaE>OtSAJ;*5w5;=W8ZG}CQ-nTRh6w$+x$YTH&u;9jLI z+ZDD%Gv8USG6L7O^h(>NE$-;rMX$QXqDR;QWrSvBRg1N>I_T3L6T93K4#9(fw}HAT zG9LQLy5HKur71(_{)Yt9;B%d=KZ$WlMs~p#R#WcpRoJ&lwnMBGJKO9)E=}UDR7T{2va2>{lhe=} zTm%r_cnfi>fp;Kp!%+r_C~kz4DtAEvU1RwlJgXnNww ze?I^G^H2RH8$zYC4P3?!mVo@wJFc@hi?+*ED z@RR-E<9^zs72(+?r^otKoWM3^gHpxvW16^$LGb#NmD=aC(zoX`@7vm6H-5H1bmT1* zTlgWYyPrU9`8JHK8(Bfmzg(L;+dC;CCOS%83AKkL4E<*& z{&Nd^O?6YEMnwJr^^lCj9yU$fH6sSc!`K&p&G0@5Yp?p_;Hi4Yd{)F?IoJ{v5^y!? zQpa2sfBcgpdk)nKbtfvDJvFs;9@KO2x$0_b%1<9ZRa{Y7Z3=G&YU@Oobkw)Uw&!t^Tw>J zuN^vcXg_UX@iC|wcWqp|@xvc+ak|K3N$ick4C+rDfZY_5VDVKKd%WRsu_grchH3J| zkSNn^NvNr`i7Kt$A=Vcc&lU7)M6T)`3k8iDg(hILoQo<;9*@0`+vuvny?M|rg;Xc% z?Ed1oG%CK}7Hcd~VfagUEV(|}O+ZGAS?_dKxytJ#HU7Zdvo&rf8BFbR$O4H=vKNI~ zgH#fND$2(~!hERV;PV-~b;IulpS!^~2l25I^PqwaR+yE%zal^_U`JynK$+xWC8qJf z?HSND0)OCo-qk;C7uZZA2_RIZ$-SH`1Dr4yj~gX zZK1cIycuHjK4dnwN{bjA+tLxvcsCS}8#dq^1QOyHE9@+4ZV2?%{rh`b`ml)A*qM3m zA`N1|d_E1qPxfXEa-AJ_7lI%0Nx}!2!ZEHFKw1L=A!QBW7aW9E;&blA9FX>P3}zyo zt=4z1+xE#PpF~W1VJZ|h2T*pQBMp>H|pF0jaJ67+c3Ut%oW*guEkvS!8rS2t_A=`!cRYCb4q*`<9uB=End@D zSzcbAL6#j+qxowllRT-{OpCUS)&DB4TY#q2nUI<&Je}?~~z(DmYf4KZ75!U4|@jqu1 zxA;pXA$^O)((PfZr%mgtJNmzO*M0WYZ-Kt3hJmuis?R<_trsLFxl340_f~9*^O>+X zfef6k@}iUHJT91U@$qUr>Rv(c^f!qIP@O{{Q}8*zoiqU;H-W5@h@ zJ(Tsyy^<0vdZbcLPE1awXQ@EQJWOQ)`F8E95@%10%QG)mj zti^$U#oS2jpKbP9WZ0IsT>hK^b4qlhGtLZt1}|IjEJt&mh>Q@QrR;LKJ^u4MK=8 z@@BY3pw^QlNZ^oiCF}pk+IPT5Rb~IbI(;US$)tx6LI|M>(h)EXL=cRwsO##g$bxk( zyX)$%y3>*nqzZ@zL{!AGDu^y3tEgZ>MWojNAqgP~>6y$-roI37yqAF_0=mD?e_k>r zllR`e=iYnnso$gBu;Ixk3ksfma>E8YpHfg@we!}3f=t(Hs*SQDzSQTpm}k#6Tl~J# zQr+x7YSymR{Be#@>Rd=RHE7WI@z6ilSXU&v%gfCcoz7w|FL%5B2F?>k>=i~As?9Pr15xFG8KoViA+X%g8%f|94}SqYn}cMctrm25Jm z^&JqGfDBx=qoY?Zl_Y9)r~>P2PfQy$9MzCwVnVDM-|FL_f7%6(7s4S2GPK(Z6Qt%A z+R?!UL-edxc~}qNpK977*@(z0R5C`@nkY4Li>S(~S{-H7;TaII3|ENkI}3uv<@U0I zTBLWlP_zm)nlu{JVpOR#Djl9xc5D;#S$Dvp?57!N%FM5BSh~CCD+tjq4nSv)b*ymHoS=dzsBF#6h73RzpN=xU? zHCuvyIlXw{d?lp02#a}sl)waa3`qf2OLYxNPO=lKbzzZMr$MBX;97<{q4Far_$?N> z1eTK580qh&X_k!sHY+r%RcI!AD~QdJ^xa9b@!$vpJNul1KBuG4gyNNsKC4EYt*$zA z=1dMUxlXd5f1Xb=Gmh_=SJ``T^-XrBK9@O#<4gWg+8kBbGY9qJ>YaR<;cPXMe(^w6 z@LgZSufTa)_XC|PTo!(~koLv5r3}_}z-fMO(?>r^nRDipmd>1+DeT?2-noL`og@ZI zxaP9mPPnYy#+y;ouTZ#k02|afEvK?F(_Zg0k?~W9W4baGi&-+qdUHW6O0} z*36yr;K8H!-@j%}{-iy-rc9%>9w+M)oUFSUPyl9^#RsyO%mDZC9sZ%Nalf3q>SX<9 z{1?y(R6fPl*c-EvD$64^8Lx`;N#5P)&NWKBLtdk8Sfd^IZDV!|1y2?*8?fTSg$SIZ zMQW!xsK@NHSmw?xEe!@sol6|VIyih?0|5b3(1^)r!G7ZRS67p%b%_mAk3$(P{G)4$ zF&q?t%79HB;0z#~B9mTuPnYcucGN3}_Dp+`tVYW;rhG}*7T}}C?%$a&iPFje=>*!p zhbsWCiW;pgEiEW0bv~~}%l!URr>Ip|&XOd(#E!~m@(nvdSy<&0nH1#pO>GuIQCcr# zH6l4AVt=wTkJ!=Fqx6(SPp9cAL{ENt!bNQ4@rXtHNhY8^5a*P2BBdYPYu<+_268q> z%7-0(-s0!g=m)7r=!+dm5cq?ytW3r`cPjlME5>EpL2!u=V$u}UZ`vcQX(GcOJC;M~ zhMN15s$ll0?f3X~*dSUuBmJ6~!%V66frkeHo@?l15B(`0AuH$Q<1O}2;2Q_+g{J6! zSpz3w1zkHhD8Js&jLG2K%gRkxbW>mXx z&*^p-bVio7sNt#%JJ1}?T3@dMFrynm(~gu-7<(;dyP)CX1j2A^1-gd@uh3e+H}M^& zgCh>nuxd3NHrDXDY6?KC7gLfFVy#Zv=iYoPre>~EvkMBT$R~}Xo9h-!^N7euM zV-8W>1{bZ}rvKo>&6_uGJprWkfp<`iUpnmEQz*Xk(tMDYHY=C4)Q!k^jXrtg+_|F@ z65@oA25S*r;&=z^aXXoIpwAWK|A9@9Uss&J=GcM#yHB)YoeCC)v-To3f(d0~A?L6< z#Hr~WK>aV8PgUAEl1E)2ptO{1&rA@eSr;2rxt6d&f006+{%HyZKj9Yv&l>&X@T>H* zOCIBaDNGxs>|y;aVWXHv2q%~u1@Kw#wJ($T9$%UzYv4{bq?FB@=gwV=$OV4w+K)fR zs|`(DhW`Tt zL{fK@y*LByCd5_Z)+V$&lEe24Zhab@?wnMKNXZ>cK`u2~ZupW2vnGb^B8I(blky9O zjW1ils+C_CvFprCDFmpP&R_5?gA?ep=N zKV35-ndGV1-FriJHZ#+UkAP-oXE**&R`Tbd0 zz8>9J3G)YUz^-$lD=T9ZOGu>wc|Y4k{70 zLae}pslY#%X1zeGB^X{zBg6}5qxX%T zV4>Xo66*dkF;$b9%B0%YD7pYjFJTkmbV^atcEWq-A9${^jh(l_^yXZ_x;2Gs zlGfaR|K_R#z}ju_s8WN?cysW;@q)SclQFsnMK~4++f`|>9EwmDlYD|By1BvwD;wF4 z+v*N#GzYQRx)Z_Z}uLJ%#k|+tfbw1)QC>c`_ZMN^Cg2CO>nTDsjFA7Cs%=cbT);i`{W|KUbYJsr! zA+rpNVwbFh)Zwq+7)c4~R6$^mywXBJ0H53fIar=$<79<}!khV7HVvPO%+w?Tik`R-eXrd=^rp(Nw$#Z zE7me)OOoE(buC9EKGj3LPMkQA`oMIQJHMV%1d*xnStkNTH3EBH#*hGr3k%kcU$_A2 zP(PEeFE20Oy*n3qG&uN&C~bDF%z&|wSJ6Ty4S7?={(}b(RMl&kW=(4~+$lfrJ?bnX zI$ABdV%|V(3uh^6%c>MCVA7kD+=wy@)t8&9rp@t_W-Wn%0fm;9a6?fs&MYnTM56nU|F7 zmjFr3il?;lAr8j>dP37>8=gBacKy zF@Lh#b^6q-A=gZWs5~|^8^z_8z@)Za%A7fwS&BGtIyM2vEXl+ieCnx#&JwZd$Q~fc z&KFhD)^GPVxPYg$fQ?F9%pR|spK+@R)Atr0#v@y}+->y1CboC-j|Xaz z!5}~CD^B-MRYyLxRq}1vdT~?lxpL*Ev8lb|qJ|6@G$P9i&xBVByW3*oR2_Z}X&^?E zh3(rH3Lb7M@dkl)=sf36)uA~Yn2|#awbfMCw9g0QmaU9{v6ElboM=|@5a2}>ehfw} z*%*N|E^&oym~}z5V0 zy4e3N*s^+FBjRU}{fz8t?53U7uH>p}4$^yjSi^c=fh>q*AyBUH%T_2g49Rn8R3g@vRguRon^|hZl91aWC~qP$B?~d5N&lXKtB}M<5lR##7>5}R z32Q1OtadWY;kk|Z2122W|I1(g0)O68aD;D|3RZv6g5J z@qP>DdU(E#tiDr0af)Ug0!ovQhcX)<*PN(7uyNzYt@~_x9Uw?+IT81jcRKT*;-oTayO&%j`dNXy%wR<{I;xu*J#l4MV|ucj&F>j13uC@Uv5AT$hwu?IbY&2U$R+VedBNuiOhYm z;S^!BAtON>rqK=ZyLK3J&M`qY4vuU(5?BKkdSzt}vx-@T*@4LLFLU4}E#;pbTwR(c z-69`PvhV%ijCyzuIy}C2Qoj`G@TqCjPC3^vX4hmb&jD8LUkz{6$Hm1BxM5~)mbDZx z7JHaI`l+|vG-X68{@k&vI1j1|NeGK=*iCJsMmy%tnS+5xGnmLCm z5JWbLTPVgBrCGihkk&9GsNv{Qt3)-<_2O6OKoGPd3lY){$wD*_;?j6jvsG4IGOfBV zzQ}>e;k4=|AAINj8>hO;US|4BYR2Uga_55TvxeldF-VS7^G0KBZ5}wvP3#bM z2sFBB7);%J1qJWL*?HmJcM(FgVMAu-h7GRRQq|3K=M*rdYgN;x32VvgNghvFm{egq z8}`V4PUJp%eD!8abJ)ZAs;bj)6bh z$7B1AzvDXe{Q%pWHNl%3#m2B|nPZR+kj;)34gCw|-G4_eLICfQo?w@K;IbRSj6too z447aw=&Y$OOwG|l24=&zxuQK9VRUm_SYy)dKXm9YdUjB`18vq6oN4qj(=MM2o-43;P{#Ha)n(;Rj>gkae@cgV>h z4PLRt-j=3R3LuPPsuV!Gg&tI)+DfrjEF<}WHFV*@QH0Q2Tuf6;t{g1TBTyhe5-7l~ zBRiY0;Yso#c8L+NzsgXwh#V*YI~yxU6J0)HU}@Kgfmea$#aQLAfnKfY>8)1?9a!2m zbO5eHWa)rqu6UK4nJPJm;8lf3MslUppa8{1k(5NaQc676aYO|`9MWKG!;2e!c)T29 z4$+H|qOfh8z{u%Tpc+1rHE9fJN(g8QCY+MF6oo62b<#*%k^GPe%j9LV*Kxmez_Qt7 z`@QIcU8wkAWy;Bp*b9{F>5&D}*}{d2M^+}5?3Ru3!9w{9*DNAe31L~b_*^y*Q7=Ypkq8=K&LGf`XU;IEWlPp+_6vrR z$$E4{wp%TtU2tXeTHzkWNp7*gMJaE42q##&2tG4>i--YD82LSrBde->H|=WNp5Mc;ebJ4& za=zk5Rm|I+uGEXB?XC`0GHt8Lv>kD(V#;8ufWX~dtDTnZLFfUQKurY`-lKmiyqvSaO3*gOb*J5UX87hvC#pLfWn{}$= zWgZV^1BQ566ea@2WzYld1LIv5B?d&O@DMmeyi$CW;_v9n14!EJ&I5jgHx=vL0)HxA z`op7I=zvER@54$WNeH?#kYrTCGCuJfLxtA;)Sdcy2iC=E%{s1a1G*sfRM$j!U6LrB7&`8E4cIQ zxvA_;h_3uSyOVvB{S*eX@7Wu%qSxDPIq>@IwedlO90S~kD=08gW-V%YESEVJ@Fr)o zTejfYSgM-yN9&viRqHl*)`4NV7y;-I#mi+@wZ!CxtIFqB`Qto@>ElkhoUDN4O1A?j zvND%ivRr20OO!0vo6&CanGlLK`AkT0!J7SiDnW(zE zA6Jt5QJx#50TQ<+>7TeYS?-BG380U0;>2g4{SBU0v-|ej-OS~^t!r#x*d<`xvd1;y z%aJ3$ytLPqoHAGpT|KWj_tuOO@N?Pwn&dVZ+?Vsfs)&m--ATd|gYQC;5+VuDSyWti zf-Jx2kL~HLj@VSlE&^}s9>rVT8EGNExH_XPE_LRGXbUpMz;22~dEo+bjD;QAd*}H* zZ82T%Y-2}3r!A1zrjlWY2~VG}XlZ>GTxOorIP$AkCnw9g{IAb)_|0?3VD0?hX8EQ~ zKD9`eJYoW=?Z#r9owva6O0BCa^s7i9=b)YTT z+;X6Z?=-&l+W(@)bAQB*__7BdEc@-My3-~iAm@sDIqyD8?Y^Q0?ph5N`usbpW|OJ; zlDfG5qApGpyrR~Pi@{^}j?BTXIB(cKm~|-1N+^Jh^y2Ln(WRw3T_U3E%Gdl)k88Gf zyC&xF|KXY&dtCFkx3epMAlWh!A!TD}Zf=*SQ>6Gv=iKb%Pgm~cNwjDk_2ei#Gk7^h zX_z)H3$9V)Q$KKhJJRCf9?3x=vj+8T1CG4$#vARUCgVyle#*yETQ=i@%Sb!svCU_k1|O8m2X|2D`9 zNJUY{Wx@#nQ7LA~_zW!q%LnETi^0eAk#U7;ZR)TzyjR%~*6$n!g+OgS?+$c$STWdK z+Yk^eCZ?&bqj$WdGI*-?`VwM1r>pAf4(vYZR>i^{ZnukiaA7U$h;BRf?SZqcKKGFw z`(TF;m+h@7RQb*wJq0rxLgT`U7wc)zdOCJCvPMn^SGxr?p9;QqJ!VVXU>@-S-!@K4 zJ-ubimL`))Z8%srVCEB(kzq*Ce;**q$HorM%ZCF8c5)oW+#PfOl?%YQ?aV6nK1}-; z?dcpBH!y3`y?=f1ri|czxFuJunK}(bo<1%s(_FXrP;JUgfK|#k)yVkVhL9xAIXIpk z(P~sTB@EMs+OiWyT}MMBeR79+O|<-d+K?gbZCY$YE_9m<2SY_{im?!Oa*&VbTs+TI zG$Ft1*+FYR{P4rAWukS|j2Sa-|NWh}&X_glf&1>6g-@gSlRs_4w=XYw`TdXHUqdRH zU01p1KzU1;kLi_^Pm(4Y2&QtlM_V$LV z)A3eTDW>F=)Le)ByH|k_3z(*?1*0G0lZf4wCkdi;!HehXEe8>1|)1Al0zRr zGL6A#F&az?XDIq!PRQnD`$!Yn1-J(AZ%^#;WM zHLr=Mye2z;nY<>UGzbg7?D#Rm(jUBHZWD8GWum*H+-!2YP3H27-;mk_E0BZntX!>d z+9LF8e3~HRA!egrv#|Oo24HlW$n?ohXKD@)9n{xSv3~vfqv`n;%V_JKb?erZ3HrEU zKb%UP`q*@6zd&D?Q<=6hUm`E^1MsB}nU(BptUZSvtAY_GBdr_2{POXtcu(Ks+qP}X z1!u&;-<#a{aXw+#;P{L5lzH?t;9CA}%C%#1XHFS4Zpr{XWg5x0Or;#0;u}ZAgS85A z1bYuuRUK`vs@khlw^h*t+xoe(`7CO?VntpjJ&d|i^8hFdw;D>EKikhX9|f0R`e4hK zhs!-ta2ueAeP;TwF{4LkCgVf%_Ra4U%`YiJ$cWPjn|6GX6?Nu0tXN0;>4WpIXI^ z>s7)?^M_{kON@>iKW0E?3Ral~HPQzQNI_Dmd=-uE<^)ZL5L725BxDUrNX*RW+ozAy zh#KZ_-;%FN3P(pF`&Q$37`6Sl!m@ZfkS50Wpf$3C1(vMH93-!FkvW>X$Q-hIrJ(wzVf|2sHAT|quzIG2`~L2E*^I=U{1Eqr z514XY;QGZIr*`g(rMKR(u5=Dm3I}UY6=^m8;C|qI0U{lLD45b2&)u^n-FVdhY(MDI zhaY}97OqZ->o)X*tXza>O;#?l;#k4)`d}1Uy*`hMw82poC8D-qmzeoi#rr0-e9`IS z6#{{n!RU2kr=80oh$Ejs_zxtMEl9Sc;}EluyOZMFYD_M8Xq<|NrWlqT7>)0Onc>3^ z@f!ce$J>86Id9(CpT79-CtnTD%bvJl;^KNj~3Ta$<_p2JK?AP^gje$L$nZ4|{&7 z5YC(VuSR!}fG{W?vU7 zeJnmb168ID<-y!c#}@IQHws_}IpnR-9XV3n&}Ps?U7Zfv{QwD3-#;61BoJx7=%m_S z{+Ubu1@$q)4zAf3=X!@6T|`FuUEKebkH3=X_x&dyZu*?)clN@&)vN#e(VH)?djH4m z8$k~{+tTP{f({t3I$vC@!Rp{phmNkPP?adVxPMb1gbYw^d`3q9{^{0k??7EuL+!bW zLq|@^*X;1&Vorqk$j>fzs1D%do?4e31~#HITS%!5EKz&$JN+ezWr@0=Iz76*Q~pHH zpu3{wIYIXr(ETRRJqC222D&GK?!#%O_U)TTnfPpfw*UWm3|Q5L`TKtuiH+T6wLBbM zX7+C%nc^#q%*Jk$ULKwy%EbT8gH+63Zjd@x#4kr`JSWQC=tWFkU1%QGDk` zs|Dga)38p)CDdV^c{V|hqQL-HW=yzYq`#;Lym^ho`<2lA2B1cxXN zyN!9!uJ(A?ULy+%hEi!er)JWmNqLiorfWR8kUlms3$;^|4ueU*(~xv8QZAkAX?47k zesI@U2kMbin$*v#Q3ViAcY5FP%JaYn_xW3DtE;O|0;vn7>C&_8TuQ3NA!y@MqXh?V zN~Zd&t(6rmE?lU_`bw<9tPzp4!)(}9?+*d<1HqVZhdZ<(Pe?>EfB{w~1R$xCv+aqV zw<{5f1o(z91=Dvl`NDWf=$Db8bCRd{mG!c7iZ*Gr7)9U+oqWay8%dFkEIto)(l285 zDZqRe2WXC?bY`B6(C{YnVf0hC-ShZA|M}0^69#xUm6Vi}4fuT?A~e3CgL_<#jOTVT zcl>W;Q-b8zk+bpb%)>wIJk)sq{g#yeaXJkZm^gEw>?|S^oBhGI^FWS2EeFg;UzvXM z*O71X?M$6Xg~E24@cM7-d;vnAR%vx2rH=*N0YY-o$$|7jgosI(C!!o$ugD?AY05k! zzCaTCbkK>iMMw_moeCjHmV~;#;4kz;j~t2xy#|adr;EVr$qZH#pwkfO+W3{Pl1@ zIVw=r0Ju_^SFRk04szl=ln8|rEA>Z z!VQ?1E?m@8_`bY?BQ|94SJ3pLHCKsU_$^p8SJt}jRKyZF<<*X`T3Ni3?@W@A2;M<= zrZMdylU*^c{L1&dyrvhCzPQfbUP}6+79y0YOG_hni=5T1r7*S1eTqEEeT$qCp=!7L zpcsaH3mHwzjwA;8)c;ST5gE^(@-o@yY}_Zfw=8O(iTDKg2ymNx|DG~?*LDM8uHntp+(~D$&WEGzPR7U!Q#fcX{ zDc}hTrKe26jUda{a&8UitHngytqe^9#Z<7{RF0rf0&vX*LmyRTU18|cH&$niTss)bJE%{m#W*eM{=0@+i5L+z{< zd*xm{b(vmV`K*-7ob@ZTexb{07p5#@qn$evT})V+B>CI~s>NIiCX*x{J3{^LVw=92 zj4Erf;j?r|WBAQYyRa%fsHqAx8a0ZVat z9^`g0(}2Uka?a)0yH~xh?S`0{cA;uSziY1Pcge~T_Ftf87WFKVvz%?Tta^XYrEoJV z)#}P*e0YYi2If_Me2dv zBT%I+5n6?`?S5D&;h zSTFpF%=4od$St?Y+Z zlE0@HXg(d()+8o20z9YkOxYAj(G7$!Cm%L8F)@!Fg$R}=X0h;xKm6g&IoAPL>3;S> z`M8I^vSqW=?Kb0zP3PU7fW(|`17h1<6tnV@Jr}{&YZ#|~tXm&C%^(Hl{KgfoLIFMy zo?Xm^R9uNm9riZY>ofwlK#h`x^{pX>EmEzn9Wry~%)jIz)ct!5=LYeKn{U2(68!C? zR!I~8dOhm<(Xj?!e$T9f*9ixAZOv*YOpMkq5Q)Y)j~MNo<}@(r{=w}uhz>SzJ=Mwz z!hJZy&fJ{Kl(W5=rEG`fDiWakvD`vFB(hw|e7;Xgj0%2H%G%@fNqq>tN-E0mo;eRQ zY;&`_q0NiL1=iZuW;yZszDl3JvR3evz4H7ino%~Dnq&GVMWY_0-!#(T_u9GU(B@oe zWVlU==Qrw@k8{IK;a8<>w$6k;6aeq{=giw>)9WWRf0XI}>v{iK-yj|r(by`Mawwprk^Hr6Nr#;NpmAabi7rf^vV%7pN0c?4wr0IdDp;)+M&djVR2cI})4nR9rLPz%C`&^X2 z^Pxb~waf>Tr7@lLW9f!nbq+D{`ae&x zhFzk5=&ZRpR)^~B*KcjuUg0lX4koYyMIzoGl!DYq`1q2nV#D!6^&oS`n2ap9Wy&BJ z7j(@B_MY_Ukn=NS$`lKVc{)_uUV{f;orQXJ9hE->ttuDtR(EdwsR4!TlKN-%)&&dS zrK`S69gugGNYqheWk;l3qBwE19BCf&Hso3xBPaJ!wHyR2&_m(gJtf?(TnLi>-Bg?o z|4dT19P%lj+Ieo|-Vy1zD<4+weK+$0-8*u!BXSMp-oNGi9alI%()LUB9c_QyhTRA4 z1q7SY1!Tz58#IV^k)CIFZM}^xW{8>6|0?*a-Q(!!+^(7mQ9TmpiaY<#hRKL-*HN5| zjXQ%mgO@mr9A3&W&Ui^vK4r-NLhbq0_}iC%z1n5u8keGoG9r(nR!TQKdzRLd-uVSZGuU-`1fTyf3%PQ|I%6 znT7?n$Wt!`qJ z9MFG@cTbs~e(I%27U)aO1E0=D4k!hZ7Kxmw@pywS4bMwqvi?a6R*FDq6#yH`lXdF( zAv=Dmg@f;qI>w2h6dVW3QVrZA3Kw?Wof( zp8IC&=LZ_*^8<&+_yC{i_i~a$C79yOTBIuTM?cJrbrbI`RmH6gH8>6^tcrs z3zlhFCgg8Lk}}{Q z|4levs<9!!D6N{V`~36Iv*zU>@TXWk?;0OUcJ{w-GQ8Ymv8Ll&_Gz$#x9zsX*I(Ln z96ni3)&4^;y6Mf~x(0l#{b3sf288!;&kZ}At9ZJ_{QUfsW1I5e@s2cp^xrA%nIm$#H)}@>EJjWd_2h+$%VYW z&#*oYAw3KD1g;d{@#7~>RaPIPQlQ})uOH}Mg4)y4SXbrKc^%fuRci3J$LH7LpXJ`-p_Sf6GmY~ zDEA~4qe8hSln+CARaW9DKmRP}F)S0V9c`*Qme2T*Ag6}d*94Ywh;CL;aBCjo4c^C4 zEfH?ax^-Z=t56jcErQGTEi6>2h4y;U7z<{TYbc%j$G6|!-ahu;x&3z_;UU*qNLFUd z$08JlIN#FFcx!P+Q@x+fimfg*x$EI}Pw11Gs9~anMz<4}jnP3I^5LK0@(7_6W?>;F z7srIcxI%kt0BNsoeA&|A2?LQq**n&vcjOm*4!t7@=Rx$2vX{*0on^SC5!~nKr>k}y z`R1E%+yh6ApPS7*&p$AE;-986uXWDI50Evy5)c|bxW$_;q8ztO>OTI@-bU)dv$MJb$NLcC-UG( z|5ra(_kvlThp--DIc5pl*b?@} zjhc#JY_(<8hfc!X;6brGHM18{VPC>y(-H_XN!pAiZ{mC7>_t%64Ra*AciLRxh%8~#uyw{ciS0fmH zeW@5|M-{FxK66kEUboRsOwnp77~LB&x`h2n@iLS*MQA0aYgDwg9H<4_OF28>4#L2E zOSt#$JMMid56hWw2TqDlP0Y)i@pL|DsPKa#zSC74R^x%GD zN9I7=xNf|wWW|aVukC8cc=+LmQK7Tr>N_UG8UL+(e8y}+(R9}$(NlRG1Gfw~w${DJ zTCf=|X0?`8Y(<-E$j95jvyu`gju(Fs|`Be$y0|8ooi}=XVPm(Ha0aihoL)IdME0l8>(AiY_MzF zgJ&!2TfB#>0h|Inf54lljAmf`cJEaS>;LHUw;M zWTG>`W&sOY%yu-bLP?EI{_+=$Q9Q=z4vZ019K8W!6pt~QkS2mvA2J>}fMM!(fS=Ef#<>h_jV3W3vK6sNmABZIZz9-nRo1_R)1|RmK3HRDsk*vN%gwZ zq%?c3bf+{``km47t$EljPdqVu`na)!4Q)$|TN}#{lr_1cq7q|7Z+qkU z7DQ8Y1btrAJMe`|0AV7%fLv<8ZcEF7%FH>Ha3VGKU?f>XOOx#YKxD9E1;|UNGYzy2qqGM* zcsT+r0(4-k_SkLg$4f3^FMNE8^5L>k#GL>b@&}k*FE0R^z(R{GX*I^gj5t*o zP{S(xzovvOYk}>$Z~Q04#czL0h)v`br&&VAfS<)awqzHC;ChHM~17j zLO`H97m6Ev^@Mp!?Y)KKv-4(-y}E3w^tAL>rWsPpcKC*yAyNFD+t6_}NhtMH(s4OC zAZz%XmtNA6^`nNn|9)?tbgvRj%xrgRwjNWbrl$5Cd3!F9Dt0mNTAoAo@Yy#FuKV^| z)r8S82mUj4=Hq$b?)#W8nJ>BLfZk?jzRH)f!>7UrVd$F8eBodw<-@?7&JG2gUt@9$ z*#UhW?BSEe{iLQQ#C0MRRLdG6l0gb+Z12@8aH`6eJRr%+ocQV3u^=*B?5dThV22H^-Du5*QBLhew<|sg}7!E)VMH;`?%T+XDY1OAY7!^2P$DT8Zf_T2#g@Hb&e)yKp0(InUC@D& z>A?`RBAH$@Fn5x$UiyMwv?~n)y{N`=FVG9iV}`s?-ohUJ2FCONa~9+P1OlX7+FHxj)`R#*Rn%;;&mR$h3S6782OWI*@MM%Z3|$-Q9C;FPL@XxU8hob>@mvrvn963}k`E&jOdm+t3`W3+nY6fN4})?HXTL zgeM(v>m{*RLc>y%EqX)s$*MMYu*!d`6($+GL*I&+Rw-On-`Z4DV`qJCS1fxT3BA15 z6ea3$ZRG(j;!=b6Fkwz5qHTW8uF=M&4{WYDQR^#lX*EK?7i>RY?*ncP?cpewkL=rKb| zl8*!_mGwjTs)3k!GiWu;<ty;$iJaPUIER#ii5aAY!;yk|HyFT0gnf@n&!61m@$Z&#KA2V)FZq2TFrN8zrdlNcJ6|~ymO5O`56BZYr_so5;!P%TkxY$g zpavnh3;;CLby0#>ua823fKdbEfymSWhU)b;H=Xrj2XUx8R1QF+b+HLr2*3uH zQi=;oG#+X}FP(?15SE{%=Mz|PVEP8XoxE6{LYP6N%4!-8YLNUuc)AIo2I(g>H3DAGL$gcz4Y9n;}$ zYpfUfLefoMe@D?xz5>F%3h20`nG`xS)k)DpC;(*x=RPuYNG}bW)ko(tcr+9@l%Akr zOhT*Eq0s@BR7Ki}%OMH~sSkyU9cok;;YmFy1m1@#HOd@??N(q^_;+`0z1vS(iV}Id z3VkG;64Fu#r(_=bNH`?}(MQNg)~+(ri38Yj+fpa~IS*6!5Ojeh?6ZH%!$g)PTK3tA zH_E!fRoIdhnf6j*(`LJ+tJu_j?(~W3#tsMDcyzKPNERtwKJF5h?SerxSDHkJexqje z?1%22c2SO6Ci)s{tx>8K7i6hrtf*ggNqK7g+yWK7^4Lu=o`lKT!?;}&qiSHKIY zpzXq_r|$cGety<3q%8AADNENoN^EHKIu$wVI>`FY#FR}#jNO1=Rq)JM#VSWoNh z$ilbg=OG~LTQZvmcP3Bi|cP& zxqR03_uhN`^*?>`=9}wQuXZ|1fS=&tR8a|V)A7(Q{GOm|0mS4YkvEv@ngf7n4TfFG zmRM^{QbyT+kNo%7*Kp*gFTec`e~doo)#48+Lq;SbZ9pBUhiHS^@Z)+r zL}UN4Ly~k3HZI*EB#jxLWsHE%W%!!?kct?ZksU47G z(+DmlU~h&bedd%y@SU%21aeX*&>Zc^AYKA=sZia)6Lnq`nL@EXL+!zX z)h!L@YJ!T&rs1ST!p;w@Q37Cf)WH3K-o`FwhA-xLtpTg1mIpE=u5P zPq$h6_U$`#R4V!EYhVFyIkx*iRl7^njhrw%Md#oXMvP8&@aKNqRq5)};tk`!v}V1e zJ=PHhKRR7r6D77(R#u)p+d^uZ0T)mi483}rU7|M1ikMsn-+s0ncI%jd!>q2?F`wU5 zn$b|C2YhMjbs1#}IS z5WaW~vJDATJB8=xYr>qnMbaER6>vgZp+;(330F;}@r#_YxBMP-iv!&zf^JDz>El7S zEYR(0d8ONhkr!ja+pu33YVPPAK2G}J8SO`kd?KFB(k3cTJjB5}S8^5=Y&&Jve+x`r)I+4#w4dur#+ zYuUZ|(;upQs5KQo@s7Epu>sfagii_tOrBt8N1N@u6f<^SHi|#Y8K$)hhTERIISHEw z%Q}Ui&u7%&venl4DmHWabff4lPJzGN(n}jTNNo;Vf0^_?i`H$gt4A;=c1b9`9f6_sbEUu#+NhOq2qbA!VW zKVfvb#bQYsIHrHUWP4OhLh9A{7^T+}K5=usYmpxFf`#crOSn@@7l{Zc5mA1OHwJa$ z(uGE-!l*^s*osgyERZFOM9EWMeejf>uWP6eD(GC-VU(zc0%7o$Wz1R5pH=@Tt& z?f$^YQ4~n>6~KpvH|4PdV5MvTp5U=DIS|i#VP%|xK60xg4;uX6Vg0#7J9XZy@#Ftk zP~iBpon7&w3!0+Gz%MNVcR}nzPE>7W-mE!yKXA{z*Eco&VcwbA94SYd&YnDwEzNK` z$R;SfVAEKzC4_x(e9*<3{0@NK*|qh}%{6YpA7U~V#d@O-9XtBdkL_^{O(3WG8`jI7 zJSRCF>gMLQpoXifUhEPiNR{jo7s5hSOlXOXZ}ek9a0W`pYE?NHH!f*4o8!1v*aFKG z?UW<~;ukY9XZv8zl7(y>s4)rDQ1$8+-3SRP4~w3TpSkDy^~;w-g(UEfV*ZIIo_KB= zq{a8x*8qL(k`I(*gPVFHX6t<@GFWlqheMu%g5%a_sS5KC_B?nrzK4HfmGN&EIEbmb zvck1eVId0&!9o^BSO}_>=ITq=ZErBlooi_N?xW2)usFRNz4_a!fLfzX$-VobYcQp% zKL)r>F1fkk8uK>Vx!tGQF#^_}Jf&!v0hE&hu$U9jssv5mUI^B*Fv40&`uJLTiA&Mx zf-EJ*y;n{HzeIowZ^!S>3Wx%)pz4Dg?6n)A7JWrjKqV1 zaERQ|V0noh3_Wv&h%{=bbE2XxHjCKIq3JPdBw{2E?qe*nYa89DE(+};6m&-Mz@F9V zqhsSVuvkNg3CJc^N(4tHnt^@hMg)_abQ>^=s@K7J@0C}AbvX`_s4)g+j{rf{r{B>~D zR(BSH_z5Hag6i34{&rXXwG4%A+6{sDDO0CS%@a+1ubFjUJ`fXpKz0r=^ZA>fd+z4R zh}R0>a|cw9{}}$&IofyLF*=%aaJBnu;aGH^`l+$4BfMN0`Xw$6+g^1#5weteS!4z* z9hHI0+W6f^ z?;*0oE|#q?dgDj{!2~BheX)2e9N~VL0a!*#o$>d`P9tKcc_c5%feODJXido5rqQUl z<;td0#Q2L{8edRjoIY)2JiAP%Y(M4o^{xH|BUo8SVOfoz(?vx`TP&%46Lm2$y0l)2 z$q6yWi(8V($azr$H3S6@jZ%lID%=9{9VNjOp9u9)>(oUdgk2XMqwG^bf!HnA#;|A* z!~rdmpck2Wa7_bEgOGq{oti6x)6d1Vv`W}I5h>sTD}?&$RB1b>l!z(SH%5&t!NO#k z>A0UL8I8yT>i)2!;syt30g4mgScaA4e_%RraXuV?jtF(xC2x6>Q)Ng~W}Fy<1j+$s zte%VUJn$Sa6pe&^4zFEea@gURD<9es3si3}wBa5|8gM9}Ch$JcpTY{P3u-9i6X_}x z<8%4zt?qWcaF^>zm$@EAJ5f{|WODrS_*D>;wYUq+R9ueG7cI19^9yZqBeVH78(r3x z9sDceX5=@|+T@F82cNLX&CTYQ*r>sZtV%5=eLfP8MHC=?o?={W>_oJ<%#KhW#4TbL z$Zrq`+UTo?b<$m*}Yzz2m*a zGUaa4$Kca8x*+vh{&sPsW$MfHj;s~PbBDb`ZcaS~R8c2E33`LKk>%`O zn{t|P@qB6(r$E_rLCi+wN|AQJ>W+S0%#@O;47Cm@uec{;aMBBhfCA;SziU5w&Z|gt zq>UQ|_i&+uDW_YDG_(%70QFCvu>tx+sIw)J@xEkB2ni0pF*JgBw%itZrqgF!K$$VL z>yl658uW#H3KP(yOpFs{gsaY-^LoynP#gy^5EoqD!N8`mEO@zt?T>r@0MmG`kdPMD zQZ4%v$Q#$w-5`*=;mbVf8ALG*g?P8_tNoPceJ2fp(q>>n~6>=+1AA24sT ze;eRne<;k6W=VIji;lX|OnMLw4_Jz7n^1@w{$?@GECvwA+;HYp6@`YWp_{-v$nqM4 z6?!T5xu#|xN@{o;YiipaqRNOo5v^juP6j6-o|%}Y@Jz~lh)Pr8Z^(QAD-js}v(HAO z*4KN`T0XO7GmGrDobZ-#Baq+uJ@tF`U>({S<~=e?ti@5rlyUE&TBHr@Ib6i+Zq@0? zdc$Z^j+WOarKB*K_zI4RBDy+Ew5cOgQ;FsB6{d;`8&%07UP5T0q+db>*@sz=PJyEU}z$mW#^1(11CemYv{BUf26u^;7Q~q*z=_4m^5jiG!PtVf=cJE zY}J`Cu~eG4-l_G?!G*B3EV6Tf*7{dtc7OZdEn9#1Y5&oqmB9RQ|KmE~qUDRt*9ilQJx@Gy@n~%`t6l=Ar$}xOCLO(L?JSeG6f7#--a% zAAB|4-PYa_Oph~Y5M<%^x3|`wJ{tV`Teo|6eRDfjMN1Lp?xrZx7+zOr%tuXx{%K2o;1nrK6PphwqXauEmnJKYRpxQ z>2Y=xeXFSg&BZwGOebDyb=h(f?-;`Rplo%czk4jJ3 zSRt4L*O5hg%S1%yr^V>hyvrsUpbz3T4 z&rreSosnWOo5(iiYi(|*Du++!>#tQB_`4}!Ele^7Oa`LI4?O&q8q|hjLw*}Nzu zwyN}hn!;>E6@hEmv1}jKz%0~_xn|7p;ll@~86~j$N2F`y<1y}gX;eNchfq?|PUZz} z9Ft+UBcTJWhwh#uVP9q!t0^L+jB#pV2(m<(jlkZ{g);mDvxc7@SGg<~>>dZdtTJvo zar+;Xn)EcQE<8ARpJGrL16A0X^rLl5V?||SGpv7t-DsLHZtC7Pr zRtGx>X5r27es6(E^kwrCIay-{q$EdywfN2*ud4Alw3=v(F@zDSt#H~^=RbS#{UdJP zZVHQ*w2TsHhOWcA?kK|GxkB#{Yg>51j>n;&?FUF*WOTF1E67p;P-fZCg8fZKd)TzUEg9JyDgbGszlR?EV6kt_SbFP7P=g$N2 z&n@}SRaBpE@dXie<*kRVTvJ_*zju3}tEhBV*5vMh55d#Y!G}RWRZ>zEFD($l2v?vv z36FOQW**7tR?Iw-(Saq!Fx#`GN&LZs&%g|i-R@TGrIJcgfqTw$QQ79A1q{OaH(=kV z(Ed%#dMANyQ~lV(W55eK=+QpONPa8*iHS(6VLACc(6U>@%1st6_IS$yiu4 z+yY{K;nVsSFZN+x4B_zB#z%ksWLfMG%)9mAteh&G|7&JM)qD+S+TsVMrRlx?up>@P z%C^Xzo(>_+z{4g6kTJDGxY7s%nShi$@uXf4g`5-G!VIpIzRu33?rwiaryT6|cXtbM zoDl5kiS!669+Np5MUpZYbm2hZq0aYs1H@>NX(%p&jLe}3JOn&ly^c5)AJpg~!GK^y zSQ=vCg2AvuFBsr10Nntf^Y9z2%<+95Mh^7X-53*u(l{Gqf}-|jVRXy}HR96Wn+UEZ zRc@7CQ6ZXcdA|KhJ1pH{^U&)j|L})DEE}CQwBxz{e%jDB6yAILp|7N}kG~g`$X3KE z;ZXLgP%%(1bE3;Ms5!tzd|`(=7!)x9#Ui$iZ4(v`Y<-g!fS7OdeBsU3fr|?fiJMdv z5>v9RDHg;uM9>PDl^j|!A-M-(<8gQn^$B0UUv`W~c4$zqpK}9T#JwZ$xa_zO(j!Z@;h7n31am1^H5Elsq zk>QKjT!#v4I;?=wH7-kob>PY=9zvbhIwHJ(;lqI4!hg4Q`A*wb+OtQ(^Z{0F(QAS%ZL z;!=P5JRBJC3Is9sXn^(5`WespT0AO_oEw7RT2NJEYNRs={*LtqVWG#mpw)=dh|Ix* zQWJHWNI0sq;FUHYH6=AYJw0BfPo;f&T54)aN-|z4AQ_OTM16v~htie%FE;vlM|{Gt zk%P=2*kL$-tI+~*?g{h&a7y98MT}IE=Aos;9wIun{hkZJJqPp0x|OX=TYOW_%C<5f z8pQHpXmJ+hk05Cx!3tX*9N5quu)*zf%5OYcgh@TK7>JUPCAd@IP(wB+%c^iPLj}L9 zva(;E0DsO`R_>A!{}O2{qD zd@LUSGh)E1kg`mR|B+m(l~h7Y1o=Mj&C85^BcAN3C8)}+<*TvzGXg}Ol$g-=t5`&bTiAY zkGpts51NYH;}>!DE{DP6>f(*&#~x$KBMn2&t`OdE4Le)=CB15EzThGh`*8Mb{rR(J zFMa0zysC+vg@B-^{PhSY<5LaL+NuO8)(*@>b7d#&CDp8Epn1ps)+P~r{Ivz+OA7D2 zvvk}K^7HfKT3Zh7-?1H_AvNq6{34ZRKv1pP>rtm*Wz?I&v8zm(x~?wdqoTGP20BrI zKptY#2p*R2b+>nBWu+#?;|CGe($s+ABK+JmsFkf;2ucqOgFleZ zXfO`B{<=}a1`ZsUIl!XVWepyBP0rxKgRR-aCf+mTUSUEPtiX7zDSZoCH{wR*swC(`s5s$#)`i73x zI&E602;5pTXNBJ3Irbd4WQnp@DdXn;ug48vS665^Oq-ULHf>s6U2LG}v_#I7OKLG< z&eEtjOZpO#CIg1<)uSNMyuGTbe*S`{2BWc|v8rnOS3Pft_522rGOc<|l*=9Me$nvLonPmfxwvVT`2LpmLl?(LGHZIn2_{%H#_5?8(Y=#QQA zw*4hG`4Y2-q@?B!9+ICAdHbgEg?G+*g!uQ@z=$1UyVI6DbUTzN*X3+Hbf{|P>@z22 z`Q)iXhc6Yxp3XdRPy^TgE!uCI*55S+xS> z7&3f$2Knv7bN`#KKALM6plOM05D0$q8D%OWxmOsWuc!^36&6I~i;-odnS$AYup*Zz{l{AyVloT5Z9&Nx=L|!?+=wRU`?<9()GIjka&XvC&`6C`H+b$R;AS zG-p_GDMFHJSskb4^n9t}qDSgV&^(3cLGA%nTdwUJ`}jW*sO4I-$((~XzGHMR*Y*u; zM51FBSNVMb&-hs0p{g z$SmaNlLGJ?OU7?vONOR@$#5!6I9f<0gmbfvPEMmk5Cb_e(Yhcg>w?Chfj~F;@kj51 z)-sl*`|lWx$KW${6872i>`o>9@heFOF_!Sb~hu$86;w(w8~JVMiDdKyAE=X(2yPxiZlKd8H#7!AO&}e31xdm9-SgRL6N6XJV+B7F z(%#im7DhSf*my+gLH3a0Py(3{eaqnI40Nn-&C+rk&Bh!n#drR9jj8Z~cWZtjTPc1p zK4rYfVVM)ArA@1|3uETZTT}#gEyz8(xQGl1>cvau%^QQq++Yg(e0on1AmYpbQHzpY z11e&zjlSV??7)~905NIVaij|If z5&uo~>gT`v_00^i_H*DmI)|&CC$Vv;60kR>v!hPYGoFq2IK`G3Smm4uYm45FigA#% za^Xp)R>zf#ScoT7I>kd{*|B19n(5){_7MIj8g%7iIEi(toMb`nEf>AX%$I;K5Ex^l zXC!Jg2(=l67T1pEk+b4U^XLakpcSLClAnS8vU3xbPQ*Yt4MI+3FTeE83}?c&mv`;j zwdbwy5a3 zHrnG_)Lh(wAzlPVWlhjq(jDeO!jT2B6DiwD{(&&@iK6uYZoTA^~eCnc96n!}QYvYQI zTeiM}M|gerE=2XfE9$2-9?6sk6(bL{VnpRU1Vd1zwqbJsAA{-L-QfROq;HFMBFGAtT3l1=ta4QeM z@kt|uuo6SVOG@^*1ifT4X^~kTN9ydex9Dzj{a}8ABli_hDG34#-T$ zurv`66k@lPVW~44#F(a+YL(Yts|^RpJ?_+hpBt+%H>TT#vgYQpqUh}CY~99_fe~e8 zWpO8W76B$>wuDez&S1bcVcK1@XV3nx`42w`3HC?Rm3v+_QuWRLkS1*W#hji|>Di7p=h@B|c%%c7IWL>L_&xAshX zVz}|cnvCLUKfew7A1Y6RfNuclI}EO5zjmw|SuM_`jD46w{i&?wXR(I?g1JHc#QnEq zv3IvjL^@|)De=)1K6uIH&~>+a+kyu3fUyg3+;{iJN_D8im6>7IY+b`!;>8zL{>1UK z=H7D`G|E)Au$L5{+8yZKcflCF1>O5!@aqODDU0ryF?CFW|KNK^jvP60ruy`;W5+%` zL`v^5P*>!i45Pz+s5miU^NQR0lmw2Y3&dUtQ`U<3M0U zhe`v>y8t)iphF1vG7X%`RhUClaK%cE)y3BjD0tsTEZWcDdeOR6cn?9P8-?Zy+M$R=d}s69g9WhnU=h*d0_`R34$XZ&iI7 zqlaPy7h?3>+&6j%!{lb7wRxG5=H_O1Yt08cU*0Lop&>ei~hBw%KgkKdd}b zFJxwNwTIq%$Kh~H2FAc*xYhg=;Rti0KSG(CFyYq8lP6D`IqUAJQ>T{QLL!AO*j&>k z%6`~Y?3z#~g5!D{JITt`Na|W2nXR@2`?df*8TD-j4otc@LKW6OBmgaPOb)C!%Mk5_}{E# z4;MxsgFYyc%tCxfOsvlmzdS|fagNX>A*bkTK7EWYgKDQJq z?v3^mxf<(yX>>G~n{DT^a!R8G;GG_p&&Wq5e!|qpCxPJXgs@NriESn9V-D40g<}?B zv>5Xzn5GtMMVEj7Uhl~!UNRl1t3D<2;h@Zp9j`vPA2O>koSTP2`q zpl&l!x1p%pP}EIVb?BtOsq&<+xu%FMXB)W2>LMU7uH~Pcy|9q2);u};;YW||UkIbj zda>cqA~w}NsCoD9sZ;5lkuh~@TH4g9d-hD3^4e=HEwAlvo;qdEZfURwx-{kG^LgRQ z)&I?d-<5x{<@T2c!;nhWl-Aa--z~~-vEN(RMf=OK{x_k%!A=lmvaFKT9gXWLt`$wL0?YBosc6Bmhcn2zbt2Y?h?*nQ9J^ zJ3nt_O@LSRMicQLA2Z=|eRL2$H?YhY2b1_cHQ-iPp%Q$o3Upn>m?E4_&}aD@QH7;o z`~1;mF%_$sxpDDxQK|yfR%LLD*$j?J03fcB1Oee}_>UXN+Cp>y*No3+SY97j-p>P( zhbM6`T5K`4@W#q8ofUFoY$6mYkOMxN3*2^A7=0+F{8xIu0MGGFKDzcQK6({51DDxm z7mpm_CxB%Bf$gIrJ~kow!VxJU|GKeb$4)3NErJ5;hLMVCLaIBta+H2HPeCST^>0TM@TyMqrj*E+CjGtQ|m8*5=*XFo)e zLr%hXGgLaz8g_eKzFx4*F_#=$LL?X3#DkAUf)W&i z_P8j{rr$9}5H(`b!6+NV`N0qaYpXoK+Jj7OikC#-Dgm|%*>gB2RJ$g= zrmoo&VtQ*Hw2i0+59#Zd(O;@wRh2&U&_gBQ3_6ih=yf%W(z)+Y1M;HvsS;4~nVgMF z6DIL&atWvB9^vv30G}hVTxvHiH_5^2(j`_#zGIvn%ie*H-OMm_f@J2k9PA4)^m_d; z>jnX=E25`)*?sX`V;kT$1Xp8ydlV!g&;yo{Q)$WHALa)M$DvnL96s}$or8heWF|eB zqHgW*LqrqcTR?^qP#VSyh>25)F}lA9ZTJI>z+o7Dm~;toZ=b4s@9j4a9(?od_bN~B z(^5jIXL1NdA3`wGxU{ca)}bBpr*Ikub{lb1OF{T$Yv`TMK8cFml5cX2*s2Yzpq zFM)V*ck~`$^O^_pc0SVJDglS~8szhxXv{~V561xqA|Wd$XWY1(@0dG@RU){=58YQC zbYOtQ(aoDQYUIcf7I_O$gO>3$N?#cg?9pa>$mIqAM{0Jm#UDI#=1evG!l9z_`Z6*H z0y510N^Hd?zv^*#x}kVF^TC6!zPe+_pAI9*LbHOiB& z!6ym7EM}_!_FfG2VhNVo-Kg_c-qiruBz~Z|1U%h6c&^z%x!H>+)#Vr8J#*%+U9bNA z3|ay4PEs+R%@%!1w|c8=xn^X+^^@^q)g+igs%$gc_yzur_-$^^asXv>1741ZI z6&mWqDnL}1DD~A;SE22FD;d#v7;!noUX(jlXfK2dEI&ZlUA{`2#d z^Hp@dgJB0!*0+gWBLSm@>YztU5asW$gA!3t$d1|yxqKiazzGsD1x2t5Xl-ahs?CWL zbiML&@RCwYk5__gS#d4$=+D8mXnv7Le;(?ix!#YAwI2_9u#i13{Ca}5tOR&cf2G3z z46N%!c>&5nxhFazx=c9gdUpLJ_!iE>RJtWgeF*CWN@JumkQZ zV`P@UJuyBasDd16x0ofs2{`W@9$7HZG}N`BL=#HolR30iXPF|#wF^(!bjkx z!>c%FqTYZ@Ytyn?d$cL>I;?}UjDB29o((GkTb_dh#cD~A0IPD0*UgpPJ)u?1JIHar6eGXyuoT!ty518SLG zua+$q`DvVy4tEv~XEcLFme<-Dx0=Zt;thfZhiY)Vh`j-(aI5k*jvQ%RjVzyStt)xB zggpb}pm`P7?S954hA*CBe1)hUIFd!zVd-1V#0}q|>)+Kcq}#*<`}$?0ag>BMj7R$s zSI@oHOgX=HP*2#3xNI)lsW8Y4X}gM`!BVM;ihxZ9?w(eHzgiL1VU6+MW^0W9Hd^87 zV^slesVJV^8t6EWDC95Bx5n@#dRi~Epa1wkednbwk@pe5AA0ZaNy#aO%hj*1Tet4d zpS48>j)ik;K6ml_iI2|;9aoILKqW994|Wa*A9o!xoZD3b*{V241{P8y9I{iwtv%Qp zv9fkS9yPVJjC-gUBo!rjUXIxO*5=Kd|MGs5KYbd^a|?gA#HhBD+F&JrllO0ZFdFzv5F(@z4Q7k0wWD|4LB6v)yq-@=ig(b@xo+}Lv4IjPW-2VRYDaI0kqFq{e1rVs`EmJ6u z0}B5*I?Zl~%gh}Rr!)67ef~j>*P()seMrw2gG)Qb)~OZ`zol?IxWN7ASuyO#C)so2 zOxyFFV~e1)Jt;mqORU;bjP(me*nBKFO4>w7S}$)>M?B3IlTLV1fBa+}ba<-#aXXBwFba(EnC9S==uIze z(S76npM2sUe_ts#kh>0p;2iUo!s9m+<@)GD+}l^|{93ja%;zNa&69rg^WXmVw^QJ1 z{TAZEKOvuQDC8%__BPn&;dbF`s%`6CV-9@u(I;P=2MSGF03qbcTY_I`*bi6dH8D== zC`Kv8Q@oG56=4ql5%n8~IXv2`Vj1}vQw(DaK4d5q_F)T1YTy zR3!cm0*>b*;zV%W%DBdM<m3I@lq)_L=8 z5}QBSy@@hX3tK9`^f|borz|=SO*JYyS}DKO?>xh$=UR=yE(GnTrGn}zFPP3W$dFDg zkEYjXQ$3p8?79oBq$^ck=Z_r`!yb=l=Pp&R=FHj7sgNM%M(>4UU=ea=FMzc0Gbn?p zJdyP5o>RyWA`UDBykqe|C{9{Gc)k?y_*51~@6+!;dg?NS#Bl1^hws7!qp}7KyjhWO zH=_a9LOZw~t+y&RmGr;;@sxpJ`Rm_fU0~6)2xxGOh))3!y)641tD1x)vlgDh{-!{0 zuv?X#3`%Rz#1F_$QUmKGP}>RzW%v#B0?*mk(gU!3X3S03X{2;`)x=)kczmolTK>VG zftiskciuG*vT^Kn&n-9o=8=c+8QtPv&FL-Ww`%y_$lxIZ(i7k~s1CJx2w}bT+~;4u zf2s`(s3tKEziZV2ojOAQahX`)kfj1C z^=BI~UHV>4m(DlfBb4lKMSF6k2f&aENtY5Qs?b?61Gm(VncA?nQ?Uweax-K=O zC8?LI#5iMpy<2*NGo-ENTD{Ia1imLnBg<8?sI6=8NpA>7IC4}7GIRN2x|v^tGHLKk zi>#!lLkxozEGXn34Qx{2flWp|8yAGr3Hh`K;n@!1-5>0_2(0l&Z%{S@moPrk+8UIh zpfYC}>O!!223=U(TKpE~v5%$mLXi0T_bDj}AlMBE+HLTJ_=FT(I@IIy!7(V<+zj`9 z*h^Y!YilKuoVQHQtbruNaw16}BK28p?kYbD03s{K)WO8!ez&YtnP9PR%YIj)e6!;} z%dnQxwhX^0|Lr?tS*{Je4k3p$RSq~QvFOtxx!3rdY#c&JNWBAteE%L_uYQp;zvApq z(4nz^00tMDYUsn599@nP<-i+WwD^Wd=ikFG2T=k}5oAJbh_(D!j3$ceydJcdPywx= zy@Z-hp3=J7T90SKgz3}M(qP-rl!g~CmiY&ANXuVgWAp4b?z!jecuVYl=nIRv>S}D^ zjlla2JP)P~`X#3#d2eFoiHh;;FxXod(KxfY6rVl-Zp;xR8nKv>wS}87z7RyoBCW$( z%(PCGEc^7b?8GRM;XEnZH>JqPeSjPXSPPjcjOkbH_tya9*1nRITHzJ2Pchla@sxYS z$z*90tUzc|$e?z{KfNiY<0-@QPRFy}jOQHWK6eDB;UnkV(Uk1$?3Aec_*R(g54z)T z1|yUOv;*~~P21l+e$EGKcJBDWS2m3tndUkU_9PaBB>nZcf25?Ed;el3`eblgegiEHF6W?DM1m+B zgR&ZQv&ITlV%HscX8P=j0^j^_@>HA z;I#p>t+KKScLClJYIXyzK9o^>IGn8Y{a435MBHKj-idwpQ0`L<@vJyHlOM~*Vuz3S z<9^~Fy1F!)+n^Mbz#KiVfaK5~-{JP&)%6E--}A{AKmS|1XQZir!|_-Kfj1d zJ#V{Ow?#IKtMy8xp^A0gN<;!fO@OKOZP1JuKQ#~SODhEBTP75@5?qHO-P3V}M1P>Y zv9a-Tdmub8Z^H40kap}u=zflpst)D#xMJ3Hw6b7qMpf0~#fU}^N40TU(ZRKyDU0%O z-vPKaN-okTe<!#^7wM>@;R@agJjk;fM(jYXxYIGKLKwKz;{$7Oq%{IAF%D z62OJa6)SPVIwn5-w3v{T!lH15w)3v;C~g=8`Xa9aCIt1{e6;#-Yb@4dq*VZIi?urZ z_n8$M@^|3vbKvcR2j|Y6{or)-fSYE`nviZCdGnof=N>-H{ish~WZ9A}L2DXvZC>uE zk}1=tm)%-0VR;#l!hTB8AM6rLxQE1Mz}i7}a)T}WIsb>2+#(FGi;^*3MIt;r`iYg` zc-YaRmus^J8ne$dgpEc!&zrI(zPdFE7JixbrFWV@M+Zz@2n4kXRZBK4yaUcwe%h^*?P)%EcEAejM^Zj7gcU%9%i6B0%9eP^ zln}drY;~j>B$!Ox`R1HtQ*vfbiW$h4s6$FhId4$Q_M8wLzt9i5c+ySi)cTgo7aIzO zXQni}x?Bn9_Y!}*p0QdetF!y+97F#wW3#0NyYFyNBi!?>q$`!%aoQ5{gx0I>*-_nOtH7r%m`CzalMpl^;))FjQ3N&mqdN$8&=m6bDQ ztX*rh{-^U~^|$BAs{VPh`rn@?lcMv;84OZE-~M~&3vT(%`GQ-<=F93~|IU2*mvaVS z5ns=l)qQhj&9~+ZZW^02t79vH9lbCgJ#ZgdKi9f_$J>VwAAWPkcB?f*$GR^zG|ZiI z`-BM-Zl5z3DLr~v+yE=9eX$tMUnt_Xvn=O6V!cqf`-e(Hc4TLz8aS(WCF|B+mqgcwY`XyX_m-DP#`mdbdxV2sf7N#!s1EST=3y z)JetDr;Hy3V3kDC=LYbM&(|ZXb*cE_+8b}VVJHr@=p-9lm;#N}S0Z=>gd1Sdz)qEs zn<2_}4DjUuXv?M$)6NSGEdl5C?ZDztLWR2ojQ~+39EGQE7k&YQ+}`Hy4s>^QaM2J% z7*Ffvc0Wlg&}9RN~q zvQsd&=b~y;LI25rZjia>lXuO!gjDl+2-1IVUFUfUVY`qBMjlEre}gMEa^aQ96q z;~_mv7F#j2ua%)6Omx$cJdEIc~t*; zZRDDp9+*3M{51&`BV4`lx}zs*F1gzgJRFi)7|OC_iv{V`#*NA}L-__w1DIC z^^S`xSeU@#%Q^F)6X=huxK6jw3AlJSFETUx^jW+3&Z0BJ!)#jdmmnkx?=$CkUDqWU{P_wmn^}Kc*-qRZYANtQ5_TlI+`I;CURRJulmpyK5#JD}F&ce7#I^WCJ>d%5a0t3G zR%AJh13H02gl@o#(;Gw{YATFE3O1vsWh7wYqul`xl7LPxAP^gI*Mu<>rdS$y@{3}ENk@Tf%pb4|afv)9 z2|)z3BQiY-1EZbeLa-Ims<;wkkq`@JV=TgZ6Brq!p^sVG;u{;w%Kp4#)2dagHoUOw zuY@HqAGWK((ZO6%Q4wX`7VTTso!edtfPo=OnIXJ#E_nfAVW4n7O(`PqZEn;kNpL*y zfWxi}>GH--yaTvM&?vyLDG6n2LKHyO74}a+w}tvCQ|hHRp5Zp&xapXK^J}U(=+G3ym;~K ziBCTHPMz9& z_`J^_N*Oh#sjAL(R}nahV_ZQ2*{xrN@oy(SlW_14O>tBGCv{^+=@63MdUW5uF=I@w zQ%LbvGSC5Sp|LbN9rlbdRN~G|pML7p|F%Q0`bF{g+NkU-z z_Odbw(OAIjKtd0dgX3GvqIuC_eTJ!9qfhWBU(%AwL)vaCbl ze3NK_qEIdJD^?+?0KyVBzq(QdPgPJ(+#|}r5Nm0|PQ3#yy#y^i4=ue27rqNEeIHs{ zpQ641-s41F>s!y3LVr6$DND?|Yc2-Goo}A=$QG;Bnm5E)a2F|z6gx`duL}53Pj^e< zm}|&7s8|SLRvBJ4>Gr$lOrDU{S+$FBJz}fvwCH56y4_)D{q)%BV{dGKZQmFA;nT}7 zd1CtQ*MX^2sUMlbj!&6#e0P5SZm9s4J-h~I=o?Rcg2)BDjvhJ?3O2+(e}2AEKOj4H zO!hU`jrRHyR+S^ChMm)>D>!RXIj=LvBi~QxX|Abrwb>2!r?F8SHhgqW3D)Gfcgeh!7KH(t*Z-su;gK(i})-CxVq@$4Pvj)H~qF@ zmJAENM&uOo%tF*45)K(HDTh9~unPDWP$KBmP}p{ZQ3IWbu+eD_*jZS|c_eX-H^3wv zDd(!1SC4DzR3maCDJ3KOns_YNu;F?x)rWgTvzm-K7tXnwkbl8s;$@R5Jv&FsUv7oc zy-}7Mr2^cvtGNp)Wk3u>c;7>WftG{BocLs8xU&bW1Zby@vgn|4sN<>vUZh8Wl%s~d z62w8H)f)^JQ=C>!UjyCH)hP#vJT!!mdcvy|^p_|cx5D8HPL7oGfvz67XbD^eq}kO4 z87UkXi8hanys~L&fUOb3KCcW^>rOSkUNcO~JP_b0UO6!(Ekm0)1dW5xO5pJzLyd?F zLWL#+$%E&@3F%1P81#j3aR)>w#;aD_NJ}ytqkRxYI$@WUMpIZ`bUZ8LtbFFml?s0? z96L68v;-g%m`CvtJil+slzkHS(s06(Im!>;f&dt3|HLiXW5+_?h312WR>z0JZpD!; z7;MpMTVg-EASaN;{@1u8aS@AfS2gArMTDg8-u==#*pb(6K~&Rw9j1YkADk_Q)S2+L z+o67~sJ$q!C-V*doZ?SEIVDnrJNJm)gt947ZL1NT-VxN0x)>W(iyZ($i}bBYbdya{t)px(rs+>~j!lg?~MT#OF64*+D z$8a6)+vB3pRe5{lje~s#74!S`a+ilU7Z6D&j>*IBp9mtY(0h>Ty~0~>m12CTJjbd&yU^uO zb=FrM|IlYwxvEbbulmbGtQr=j+!S3ZymRJ*lNCeuegc9T89M!e8MlnWOsZJ~^ONzx_7)devpZX_bVWe8)D-oGdBtuk zW?yo8_IGYpx&!W7EF9{xt1q5B)6{z2<=}lrKvOhgKHXcPwAWB8gAFT2MYI~IHX*0L zW^e?f4B?>TqoV-~e{3`ytd>D@?c^r!Wq&M%zVRQ?kFkB9`{Rp+$o2Kd!8hD67#eJ` z0&oqXQW!xzJA+qrx+{_cfFp2EiRl?8qi=4&y(s&he*JpmfSZuO_11xE#SJ5Vsr$Y4 zHk)mqd#SR}N@7plfmfV;o*6bZj7gnG_HXOYRk;YGpfI`!UYm=c(RXN<%tjDSK?3i_ zk6JNp=y=4&&3&X0H-2F0(g!3-jLSxnhvkBCa9-NV3{b;p5ZE9d;65Ry)vQ8GUaGIJ z4TWlPD8`_M!=c7-Xl1PCb5L_aETwEs`kgz?Y1b7O7mpdBFDA7eTg!RhDg`4jS1C_% z?|BQ^TFt?OrKR+ioI=yr4pG%p7Iw8g+dWta6j^hBy7vd+9nyA>Z*^zwhxt1=+M)}2y#JjvQ{K(+%i$A z|MOs=P;~d*T9{ura|(fRKf_frwoFP!zeCCY%y=M@RwR~swnKN=OOsT&tLf)98 z+Noc$YPRGCm?ZtDtIzDhfgrQ3jTbIAox$nzxkezO&PR?ND4cXJ<&2p;ZT`}wzkX^W zT6YUN;4rGXQL}XE(jQ)vwq;Qaq;3&@Cu7+jOfe{YH0i?!8Op`X(Wy*clO=n?VUx<` za<^TqtHY$k;t=d{1*<)t>N+eCw2V5$M2j2>m#aK(89>IXZ6uddt{}1zkX%msPm&GD z?nhY`|C5zf62`sVy0~CEY||Ii^nz3BusiP2b; zu@8r0d}ltMXt*+7@gyfhg>0*kyMJ0kVRV63J$h^Seua8F3b{F0G-gYM@^)Knh_Nk` zs4`L5nw9Ngc7)rqr4YWCTa^*xfKiS?rVmw^K)s56p~`IxwWZ1v2U_Cj{tF3zkT zyPs*FtEq9jGcpQBmZDaNK;MtBDs4eQMuyv6Q}dj(l8I|6ves^Jxzzfg%hKT1tI>0O zbF&Zsl-DY=_%~<~n#Bv1RuLd`@@z1Adnjh{zq0gw(~9xeLMwm)_;uFa|J0aKUWBA# z@7M~X*TArJjr48P#=3sf#`=HXv{Bk`(umIEw!T2$o`(kDWA^;_nmFL+z_D&SFa*DC z>p&f1wvP29{w-Ta-Zgv;$JgL)xFV#g@3avT=8qmI++SNgDrg)D$aWaB7sd|wmIVZ= zZ98Bx`t2YU@YZ1dJ`S8mZ$d<*d5X7yH6=h2E0B7Exu;Vj^n$9c^L43 zLP&JC#_A&pYJ?u)7=i}GeS7rS;=ZvssuXK1g63+9uiCMG$DGA;RD0$=K6lRs@#BF5 zM~xzM|8jBEsDT4N29ZC1`Ep!bQc_&p<;&;cCZkX!^4cIz0S%26&>?R9Vs7pp0p|_` zD}&=NlYa-%uIuouaJ#}A_^B!3`X4&GrrrJyi>nl7<8I}TkkmBmxLEfUZh(UKEH|j)UWD7n!2T`-M5e}{&{$$7x}p{D@f(Tz$2Q(c&UPJnCCxG(MYp z8QrWDnI#<1>^<3kQz5RqJI*d7x3wh~$p^Vo#vQIIf>O2O>)P1#Fpqv!Bf;qHOt1@C zjTZ(Lai#J>7FW}3Vt(+g)u9lq{(3DVzqnXcm7r)uW%!suIk^9OkZF~>DYJ=UR!HC6 zkGg3_`}D`Ga3?n+CSBM_UM1k7RjqZ$A0F$J#6|-%G?t4#f%PJVVohD%qe~02AO!=t z2R2VhJRD)+!{rS5#ksU{^@U4a5l%QqIhg?rjwTfS@hkvtI1KN>aJkqT&%BA{VlSex zLa`VTs*@RYN<@E=e;d`wjMg<{g%n4xUta)8Z8LjBsgrH=!^dk|C6nW*GV{7p;_PiWkE@;jXr(=l=Y;r!ypKMMF~N zBmXs0O5GEC?S5(8l>7T%57!>sBc>^Y=*0 z`QD(q0+&QyDF_kt{fn!D98Kd>dXg#7(2<~F)9V?g4S;>}4?4X@Hc#yER4oYlV z5oFCuZu-nZtkE+h_)C%?Ewp$VgZL80bx2B_f}W%7^P_MrJ$hjzuGf!=Xify>!-50q zMnsBUJUd;4d_#wYrFrLGEo7glE?+j>dFO+qTzZrJgKg!e&n-ckatbH^T%0?dwHO_o zx61xU8?3JtcvJTI-C*V`gsR$0OUF!?s7|9Gd%9~ z7AH<^u~gu_CLLMmb>@MC($fc7k%zvgr#s;3?C9!H($Mohw@-q)P~r@PFc)&#U_PzD zdyRvt|9haJh!uF_DWW25_YN$lsu&6i+@y|K99XW+}(noPlHOu*fi09sjk@%^wt46$TfYH zud3r)|A`-zo8l0%Sfe-#<>EUihC6l>f^iH0b+B*#BmLeb3NiHrV`-+XvQ^&0F;q>@oiX4?sO8 zZ;Gm+j;=s*4tW#k4~|KLf`&LeB0;5Z70Cx+;V+Dqar?^(qmwBpU*4}Amw{`=PJ9l! zm*=8tR^ilC=(lWrc)tI!K8kO(lC}p%PFOcVt8rbT$~;}<0QXiYE^D)qTfu?JMcibz zs|b07SKGP9Lou)FfjC>h*^oIo+{=cmZ{7UB1OCTAtsX04A#Q=ah+6}%Y9*mZ5Id;d)Z=TEe$6_~Hh#M^g z2)vdWZvMktw*Z{vk*m(j#(Cd-PNn(SLEO|&qyiFz3-D_SqVkCU?@NjhZ@ZQ^J%ls5D%tn=T!4mXgllR)Q;qm9xg%2%a{v|n8ZEkn5~ zh%4`JT|$2(dG-oQlItL%J`sq8VUgAwN$ac7E%R&86k^id=XWGnF}l|WN_8fBf0 z4pt$#RfBSthFRG!tTBFVrZvW|&9TC2ggWctlm~Ie5YO)IZSFl8-WFckyS?|R-YH6X z3ZIYl%1Zepd^G&0@FJ!B37%kxseD+z+#LeQip*#iRsJ*omjxxt# z3~yq)#k&S;zwrZ)AMVBf38|3EYoJDdgF#5i)(#$T%lG{cK+0i1k_VC6LnN5J{?E<< zIvo;SnUT{4J0oHzu}g}5ONhg1L4?)eG@}-}&#yoCtu@2&##tb`~ zQNj-4;Y`>X%J3OEorCyXX1M*TYF_;SFCZiKSN$0W9iPRLltx;L*f+RK@8(j)0sHq54+ss_C+p?|B zGTiM;gk`UTHvBYuL0XAuS5-jOAt=(Dauwnf=qhkIWT3pI5VZEP`?;xHhH@1k3{dsU z;Gj~AV5vk?$jbnSjf^HSd(@Zlm_Vasrln3-UG(`o{8h~Q}y%b-!?7}aHV zRQ|zEz&rc|YIQ&PNAT7$^|9bT8m?@R<93Q=+{ zhEPM?oC!fV$ic4DaKPbEX;dm5GDbO|$+N2?@YjZ$Lu}0>8+ZZM7>dfLoa?0JAo~qz zIn-q=p>W=!Ww*fq{~V^zpHR6Lpg|qMBdimDGk@-^+oa_E`FBA&{0W-j$BzWRfNi`I z5fuNBlHX#yujR6*C#XuXR-b8hcQn`6e|AVp&boIL%-(?OHFz8UTn#x3ZZUp?^h3~* z9gve^(eMB0d*~0)I7gtg-=>sT`+nUe{UP}Pi(LA$K43~tzGhVW=dID+)-OIi`989| zj4%Mu2g0372Ji!1V=;M?y}S^C|B*Q*1qdYY*Aar95DFze4CEm}5W^JRDJ5%?b>3Dt z!NK)(U+&hML3-eSgJBy7r^D`^Rpx+JpPZSjL7Z++ujfi__4y73jD;8dy>M+0YgNk; z2CB>ggY|1lK?E7h-O)m3vz5S1|2E2tn0h^li9JLoXw{^#m1-3cu?1VWRe+}m&(f@F zd{(jKknc+E+lYcb;qfFufq2chIZKu-d7QAbHeepEm2B~HulM4q%2%9D=kf{JGL8ctNtMBT@7OIYI(9PDGtyrA7ie)EPK1XfxCqx<~@kLkKbQ zh3T{kJ`DXs#0UmPF9(Ec9O+P6{F`(WsaLtJpqv&)@cW*t>EZ01Pzd69oCj^52uFkGts3*|b(c$ue zcf$e1zr{wyt*FUR%uT|(8`?KFhxX0QLd?nO4ler_Q((yuo=ZC?iw9DAVJaRFsKIIqVI+#QV(y_1j%mH#wTT`cpEuz z*5C>`=Up(E9l|)F@&$12LJBn7=NK!;*hk6pPnL{dU+uhYkmt_ls?moI(TF)ivD%Plk1Pg$3 zRuCA?2DU=jMOr~F6CGl zie5ebu||YPZ?9ajRzxHYqHwUHIcai1NhNrD@H03KJ>N%#Xf2@DDeh|>(zTLDP9j=| zA3Mxq*BLmT@=Ue0EGh-!O&^vy`yr4w7ogk+KxoBU<8uc>!Rg~VPrU(y`~qnWm!O4X zykNArI(t{l#G}8eM0&i9W`N-yqbrtq_BiI%c~GC&w|Cha5<>8`$Y|<_i!&!_&#h+bOBKtV*l;%PM~7brNQX~~sR?DN+fYK?K2S?pl^weBaF+?5^okOPLeg9hJS%Y(R0P-Td6HgYSMOTYgwl&+bk{H;anmm9%CkCf{r4n5n+wsp2C+UjV96O2RvQaDFy?svzMj2q9z~?jX)yN z33!}RL={q@o}C!WD-K6(O6iOF1pI7FK3#mIt4C6wM$mUhFzSE>Hv(+(;920?4ax^x zQy56M+Q7xr^-|)eH5WoK31d{6QV_kF*>hUb9sBHjQ+tQc-wl{_KI*@40y*Qv?jBhX z9KdCPO9>YM)l+kksEnWw39)sm|5w?$07q4war~USci+h-K$bj+7^>755fMvuETaiT zsnC|RYDKh)N~_h{X0m7t(v?Pwo#>NwWv1DQT3V=Hy6^-(KT6dQ{TCWJi5 zYZEq`&1RF`?eDw08v_MK_s-tUZtl5{^Z3qp{{Qdcaa%JIE-!G9JVYw(5c_Jw^6E^# ziE9r*nsE1w4#)D@P(;5tlaYND{i~F^B>NziuURXdqAxA+s>%yj)?bE&L(S6JWmh7p zb}&foz{&KhdkCjkT(W)*DHZA}$J^U$Le84AE6Yl};nK*kp+0Eqh{Od|pL&guM3gXa zPksrnxm9-6T}LpjYuT<7mpZoFvHH4C|NQ4yl0fio8F3{#A&0S*ErQ#BKZvT-jJ?Ja4f?Ptae4JIAv$}d=Z9_vnW`r->Ta($)P`9Ys zT5We1d1j0PdFHEaIeIHmjK4!2T%B1-yA!NNw5ROKt!P%+46oN;G&@MnSii5JSULtv zJCk{iV{-T9ulj#&TsQ1rj{6|?y_~h#G30g+<<`M+w3~L#+q0m%@7DGIaY*z+j*Pfm zk=&6dtgxdbJy&Z&YK^5f)S*%zP#ZYjKb8t|ylD9n=|<*P$-yJqhs1xDUSrR%U%0q9 z*z3EjZF+{6I22}}q%V?e_OH8s z_2<{#_U$`ycLFoV1Iqb*j8pHrjk$(oxJ|y|k{Kp>>1J1!`oi1vMP*X7sN|J>$C_uw zq62-dOk5l{SEGay8i;Mb`mw%D&&)OcQC-EiyY%z+_PD;mGWYC> z)ZmKtKvwzkuCFoPCX!mD25@I{_BYf1jm30aks(4hqDFdwEC|RV8?b|1>G=Q2xSgllIVWS;jl@_mfq80quzIBZ!pGpGBl30`L?fRi)~5wO(vFcDA(a1f(6& z7wGdqrJEhyk74RUhJ`)jCzw zEk*6(D~Pmg7tenJNpYHC`&7BO&9X`pbSR54EvnbE^;!Z0JOwId+9!g3hX%QlQ!LPn z&}fEr4aSKNxw@0_&e(^sk8q~4+?nz?lMb*@y-Bat)s~Ah)k+mysrspcbJUPNN1v~H zIZb<{Bhfn0okmr4>ANQb*OH-h4*=Q$ESVcQ`?tZPMr2wz) z0)lbx-f6AYLBPK=^m&~BLVd12ldY16-+&}0T`YOlFx8S4->?>*Pl&c{A@e(eo8f?wTW z6$Jtni|eo5KuXXJ*VZqt2*|(>dv2~j*EG#@>u<&~nqhKGjnWyt_BLD3=a%Q|3O&Q# zTy#E6KS-D=TLq6*rv3J&m{rCH$qz$bkeQ?zYBCDRsB6R{@$ei$mFy(CJ};l91E|UE zcF1pTR&p@r5`xb}*Csq19+c@O29S|mg^4|$U{04(_Y0AOQvVWb`curF&XuaD{??5v znH0C7y8Q%z`5B@DCDB8lA$HPzx362f3LS4N@n{{ujWX)-0k54Zq(%*1hW>_>+Mu=mUQuy${t>u1obC zDqt2Z9Moso6*Y(4bVbOP0;;QsV^By$_JIy@+lLmWAI*r?^tPZ; zQXsjhM#rLqBZqrY1IgKIIJl$-znn>xNNr~E5KMVJGYCeiCNNV=#v}d1i46XdmNpa3 zq#|$`SuEouGv7Y_nVrkY5fos=BpB&^hU&}BPgmvIbiw)%JE?rlxSm(-`Ic z>SvEU(%5Ki-)U8dDF#8WqLJZ|&Q_HIlag0obO>EiI2!9mIG@+lLjZ=MLIE_374Z~8UkOqOtKX+d!Tdj&1W#lkQzmj z8F8t!#d1Lr#)81&6~u)cBb4OC*tmyi>Z!~`GRe20iFAVJag4)dhU9sWR=UXzs*}W% zSZ8N~=~B9$g>OQ=t78cFPpew}Y0GVkXyl@`LJg^7RNnb~+7kp)x)Wci?!HL3m6OtW zJ#H@T4+2)PYU6U-{Jl$cHyWv>ggX5dQnJtT?j%-IOUqk(am>W)ARBb_UgY;`+0_5Y-{i8?d@tie7N;+4+q%!RC8#U=oF%J zUqa6mgj)aL^G$l*Z7Y!wo!m+nvj2UieA!ynGo)%*mswZ%(^op@th}*g-jW67?QcCN zxysjBU*?ViIk~q$U!j-l&oR?f(WA<9zZNkch63^7q|=#BCh?qcxX^oM9O)!HB}=Z9 z5l_X3hK7!Ia1`j2n>0w7=p>Eq$YD%ZaGw&-a5eWD%#9+6eI^*w%uCKx(Kx6|5SunZ zvj+Q6^&ZbHwYID4$b~6L$~Pt2R_4x75y4f6LKqnpI3@b-ADRSC5tmQLxGMH+dDvwX zmX-_vIITgMdI^wnALQs>;JJUp)4T_yyB|`j-QK!@c4O34_8SS)NhO~oioFpk26jq} zra*BglWBjzjpDaexn&T3?u;$l+)g*@<=)pL&1D`n($m^P6jIZ4A=gcpr&!b??O?q% ziJUS*0xgC7lctdZ%XN5gCE`?oO#}2kox<9;&u$-3@ToZ+c{M?uSTiPKo( zQlFimv#bJ~HBjJZAFL>6Unu%qbOKTtI%n#yPr@~lS{7|Bt2>;YYdm-ouJL0se*c|f z{CGy|KKwGE@_u@$BvkpU`XhQ$j;nKK zxjL7bf~#|S3%qXD@>pJkw8vv}bwp_rJxW$uf@F$@v6Q|c8peG3hiDk$m2^C-_0uU< z>s8dyN6k&^Z@h8UDOjyHtl*2v80$THESYo#=e8a^dMru+^Y>p9R%G$%eFQdx5C+-_I9`+pn-NX3*6JissA_E^q9zuJH|BIUFuqm$D)lggm znTh`Q-+tq7LUDPgQCy}_TnQ`KXZQ#g6VEE=Hx*~ zX)z|qqnM_~VjcYx83rVi9T)bxBskMJx&;bNrqCOhMcQT3E#id`8Hi%@>vM78M5>wd z>CKAlJoflI9nbaW9M6>?5^kR7x|DXAmy?avw2Saui)fccw2RGi?KvsW^_b0bg$l-_ z9Ver?$_0~_3~jMkq^CmxJ!M~nOJjxXaXxZA?8|TAWnQA9-?F#2X@LC<2_d|WA|v;K zd1eW(=K!U5L%ctQD&Ryljx*Qh7_VXS}qTEc}oeww3ykj9wpLZnU-0#gsva-{i^J zvN^}IWy?;-mOVkM{E0Ssk%95N`XyKRF*^PWU>>{C`hO(D;dr)eJTY=CMi}L>;oknE z(dlfN%mt_Ji%;9U@zYElo|!W}`M&6+FQQ4YX+E({VlTdKVsBNOS!BVcs(0-DxV@ii zWmhjI<)oC9Gjrv{Doe_#$E+{`gu~=O39BC7-Mj~Qm-DtI)2Xg1HgC0=ui95SXz$OE zefXnmp6p(w6m}RKCRbt4_2l(&uC>%IaM<2+tMd97cevI*{vY;!!6z*Bwq0suS}Cc^ z6Q66UE+4fI-(&BqsXrg@c)lJ_RxSnWP^i+DUf&@YmAeaJ4r~(E=L_qXmZ`iPxXF(q# z`cM%85gRHtqzQ;9sDL8K{eP$2-McA@&)@U^Za#DGxo6ItIdi6+nK`*jC?O;QHya^+ zv$AvEIJ5lQB-G$9Q)6B6?Hw7iO$z(*jyF`hxwisw&# zX5IGVgdC3`w8i-8g?R<7u8t2w{A9!@PX{8XNni`S_rQDH^peVXdpq5J2O%Ab32ERc zF3Znrx%;Di;EgPd4+D;gtH>DzsT8N-NkpotqYbVxSYLdbch%4kcXot%W;CgSN~_8J~el{;Ao zco;%M1R=R7Ll=ZFKmq)Hz87MB&JU3w{xv^K8rYaEgv8pyY;8yXk$G_M#!wR0 zi-A8Izy{zxm<`5#3`1UQ9J>tnT$YRbWQMY^8Eh8r6>JgiRjdm4tJyN#uVXjhzLwpD z`yK2)+#g_1;r>td4DK(o7jfUjHsQXRy^8xbwhi|kYzOWiun%zG!}j3*8T$_hefNp|&(z8t&;f=+KrW z`hl&Vtv~cnNHkq5+Knw3@B_R`!<2;aN)0nok5APwCvNW1u#L3hks1ylE!ag3+es^S zP{TnalI_%Rumuh!Zg#uIhgslo8bjx3d_9s(`)IhiPaX&zMzsv+cVV=>qM?j5#z-d1 zLOBVcqV1ww)aH|)2A~&cIjJ3E-}jm(5WV||hJ(;^_Gma5JwVG(8;}_CsKz%Wk>o}V zH}aJi`L`fie%b`RV4S9DN}7@k4L3v2h|zE)_?U7=10rMuMz<1@M@q>MG8^|IQVC3F zq%Hkk?7P~IZwWGv)0;9@+>fSrc;p(KY4BBM~IBE&iIRtBwC0vZWuDxkT@ryMyrHHFHv5L6XXPfl4+ z1%S&zQwdIjb|mQ!zE1F;iT6@KKF*>P`4THQbVA%1JjNIJ$~0arpNPFZ_@UPGo{l~(5>kGWEtqNP?qQ}f7t z=u?!m3(6+iuqsoj)Ta|N6hML!(E7KW5lAx)8WgR&yoMeU!7Z7%h}$of6TQTm%16~5 zbVCuciGZyA$3KPto0T#X^3FtAM4M49-HF^?s0|NvC)$b7ur-Afvu!z2%|J{(bS%m` zTk2o5G0~>l*U zqtXW02D``?m{m{16bYk|G>*Dx58988qLb-nx}EN!`&kl8XIHXq+{Pn#E8d=`@+>}# z&*#_iTloF_DgF{aZ6me_TW{My+gMw^t;}|nZI$gd+atE;ZU45tZTrM_$ad0pJ|Hll zQ9x8c$AFZ8%z&W*69VoD*k-S9Z)tC5Pqg>8544Z9Uu*xwe#m~(em<~qpfj*jU}|7i z;IP2U1E&X8237^G4!k{Zec&^J#{wF+tiKMp<^5*0EmpAOns@J35+Ilb6 zd%fQ7dY{!hQt!ul7wdPbpISew{;>L&*PmX$vVK+l)%9<$|7iV<^|#mGTmM-7(-9;h zEFv-@F2WVjBcfl#oQRhq-i&xZ;>(C{8gy(hp~18U6%DRxu&Tjr4IXT;zrl$HzcjQp z^fc_#a8Sc>4GS91Yq+f8dkw#6c&y>+Mz%%~jaoHo-^kObPoqJNE^jowQDvj5Myng$ z-e`TJXBus8w7t=uM*ABNZhTqe!p5^2A8&lNNlKHeo4nTKohF|)`MSxOrnG5z(;-ba zG~LoHvRPcSnavh9Tit9|v%Sp@H#>#NCoD2DvVEi{vQOlo$Z?SckuxI~My`mwIr6^9 zCnGmTZjF2|@{8szo5wcq+I&XySDNo={!#M-&A)4Yu0=qL`7NGo@ly+)h5eTF+^{wDp?SceZ}0^^dJDIuaZ`9sL}m9FrZzj(Lt{jvF0!J3fmFjcOJZ z8`U+cd(^i~T^n^%)V)zpMExu3ji`5{zKS{-WjO0QqnsU`Db7siQ0D~a zH0P7fjn1vk_ncoik3|oTUK9OpOhio2nEo-NVQCw79$GDWZ%(yvmOXJqW-5K|A+;efS#_f#zIPPGZ z=r*0(q_xRzGrY~jHbre_w^`EW`ZkB!oNRNxZD8BPw)5Lw)Ak>2?`iv3+oSE8wTo@n zwO#jiecO#}H>uqn?H+3PY`a(5ecSHm_Pl-l_AT4bYQL=go9*9k|7H7c+MkOLh;IKipqWIbIOX9DOza##k_-Er^iQf_bQT&1U@8ZvO2mVTab3q(I_~IrqElq2L7ndC^iiiTJAKpX zOy`N6H*`MU`D_=~rKHPKU0&+)U6(Ume(M_0wPV-BuHCz4cCGAM)%B~cM_nyl16^0T zHoA@_G)fqrury(ZJIvkKJ=y)FdxLw6dwZfiabV&NiTgdRJqtWbJ)4rcCaq2SDmfu} zMe^>H(3JF)%9Jlt+og_4%}t$=x-s>qG*8;%wCB=(>Ncv|kt;YvF_VsMqGp1*kp51!p^c>MMx95zW|LPUk zt5L6_UXS*Arq{+^XL@J!zOwhrz2E5lZtqWef7Scj-aq#?`tCdIVn!Yo`m2p+ZhKwy4yE2nAD>JJypU*s<6_eE=YgpE}tjSqbSu3(`&N8wC zvs1FCW#5|pezuVlpHrA~OU~ZD&H6_5E$aJx-$|(}$NgILo6zs6e&6=**MC_5 z<^9hM7&+kH0Y47R9(dirHwXSQsMDaegFYV|GB|ngoWWZL|1u$?j$*@DiP7eEdI3FH5yxH*B;a!K%9=>Gw^~3KN{@(B}h94V# zdIT8}HX?FF+=y8tUKz1tWQ&obM@}4BII?8qd!vFz^&d5J)VxtuqgIT%dDMNQ-Wp9u zj~#u}=nqF<7}Iggj4`i|IX5GbzAo;1NbZ>2+}x?TrMYu*7w0a|y&?DZ-1~DM&wW03bMBkDyK_Iu-Cy%> z5}(v&Quj%dCS5b>rAa#`ou2HRTrzp%%N5^Owyhjq zd28h>m0!<}o;`8)U32)HZga-YSv+U&+;(#>oBPkXAI&{KkI!?>E1tJ`ew+Ev&p)uh zxnR9c@r=c{ zEZ(yC=c~G2wdATDRrRalswPxjTlIX^ktI!+q%WDeq-x16OExXpx3u}vjHM+@moB}1 z>4v4dm;Q8hr>pa?o`3cIS8u-hlVx<7YgxZ#CCeUJcHkQKHA}8}_?pj_2Q1H8e)aOt zuI+m5_-p@h?Y0$>D<-dac*W)wKV27f-N5S>U-#5?pI&!yWs8-GD~GO}yK>FSH&%YW z@`qJzSLLi4x@z32yj4Z3%2!>v>c&-1tlF^Z-Bo*69awdI)laJ~tPWV+VfE0}6ISQ1 zUbXu9)!SEpd40h3one(=Qv*Tlc*s?leA+^8#X{9av7aOMv`9FtnKZJPy6IxNgs!AF z(6#h_tlKxx-LO!O(r@WW){;fB_N+73+^=ZC69ys z(uF7RBv>&w^9T83d^6u-YhjDEb+cvKCc%2S!ZzFXkj;Rt(mEhMpsOR;QQy(b(b^I1 zXyfSU=;}yuba(W3jC4$J6gkQqS30h7+~By$ahqeE4UBhJU1PdlG;zTn*Cd?mV3bkpdV=(f>aVpxnlrhZJrm?kkT zVjM9YWBOsfDQvT|-8a7&=Na~~#1#&k&;?ukKI-vm;Xe9L`aInVEqqPCp(p9D&_Y|* zfw?SNc!|A={er!mV9j5jH-Hvmd0VZ86pI#!t)(r_)*UPTDYhB5a@$JVqX7{CEdt^K zx;TOyVUEU*Rt~2l&e6fq#gXjj=E!gia*TK6%6YfSvBt4hYT-U-AhgiX8R=~0bV@C_ zooQMNmChn=Wc@g;gjy%Ay@6_7Cip`F4Ru@_&txb5P;iw~PL^7%D644d~Otk&Np?_iz2!V1DBc2@! zG${x)Cdb z10Ti5@NpcJyqII3l$Rqs$6w@P_fTJaBY%}|;k)?99J{S`UwpURNG7%pw#jG-1!xQR z1q=xo6)--aAYf|1^nlWU%78fm^8ywGEDTs2P!+H=;KqP;0rv&G7_d3u^?+RgNRQpr zUG~M;3SDMjX`TF@t1i4zYgkhpy#wh-|0^VU`;y)>+|VYcU58yyo9VL z50gj8Ke4~~I{B1*M!q8Z$+uYDT)`LcH+ZGZjeW-YVtoFQ_r0>!9>BsaFdXUfMH}QG=HPlHyzYA-md-${HX{&86em!fzm+;ql zDPLpj$#3NU<^%XZTO#UZ6`yMB#rv~{d@1k2yYrsB5Mv_4{!UBM0K22DNG$1t6=gE% zNhV{DXgpSP@#HFU16fLzk$cH1{x5P5*+JeQZ<4pj0rC*Wh5+&tIggbc!#-&^ZA`n; zXxfH$q9f@*I+zZn&1ofFM(5J`^k!PcH)34fLSMql?^U{&-bp_r&9KMH$!QWvgR$q= zkhGyqNn6^C#Lz~h18qS%(v~EXx`~^{kY2Pi$)QOkk;an2v={~0oj{6cE}cY*DRu^EJ}IG7NEt046?8h8M~le< zT1Mv6Qd&Z)=xlNaT|=&>^T=&9X(9` zL6?)a>FeYu-G{Y!R~m&~v{`g2b}$yvauQ5_r5R)p?MWw+yXikj3mQsV(|Tkby$NSA zlr*J*qzCOtGH3#sK!=bibTnB>uOhe8>&X-JKJpZOfNZ0$kPqk%@*;hL{F`nd4Y331 zA*<;U@&NYeucu4NH*`Pdr3~!B_OWGSWKOcB+WLy!S?tZ`V9rXwc-`C9!{*`p`2prH)XA` zyX|DrEQZBm261B^O=2FFjCrIxcDd77AC}E>*bVGP_7BXgw_}(3Zgvk_$L_^W_0!m= zewIDMo@39me_^-!1@M~OyNSJwz3W#n3;mmIWG}HT>_K)Pdx~vf{n%F4 zpS_8l^KEP(dyBPU_p@s_Wy?8Zk?dyHg58RF{Wdm;ZO6X)4mO1CWJB58Y#3(s;p`nY zg6+m!|1KNN-p7vn2blFg#Gd;e%>5s+@$6$ZfqlX*XM5R1_9=GZKf@mU7c7r`$)>P; z_7y8&`&l77z^1Z;Y#KYnrnAGWh<(lWVVC|0yMi5M#q1a>Vc)P)cAS;5Z`n+Cg3V&z zv2yl3_V7=#N_L9PW_;}2{lw<6(`-IF!xpf!*fIH;Eo8s2=Ij=>h@E4L*{|#> zb{@O_7uXW^8(YdQV(;Hz%Q#`9*n4ae`qdVyhCF2D-8Na_)stYH=kBfr5uJA)IipJA!}g5B_QIHfvEdebf>ow`V0 zoWZ2fwj>R^?cHcR=}tS4RN9W@(NSa)9YNO6tI3V@8gdO?K(3_=$qKrNTt^p^<@8Fj zmR?J4rq_{M=t^=cT}5u9E68^GZ?coVMt0FR$Zq;3d6#Y@@6or&J9H~Kg7cfNDfY7H zSL8T7AZNtO_-Hrl%^XHcr)5gVlm8BS| zKO@2EVie$k>Em%9JamwgtQ;~Z%Son;7&;gj9J7l1h=ER6bOaV& z1IeFZv<-kh1EJ@7IK7Mjz9DG@?oDtOP#ay?CDMgnB3*DDbP-xUo0hMpbOnTEjO^zm zshRyoJ0W>((uUN*y?W_`YM~P=0m0Lh#MD$xNt)&56<5-@d~tW==T#Ka$o%}0nKVq? z$;E=wvJ&#k)bhN1@_lj9v^;VQr_jaZpuB$}?;p$id-A?h-nW*{E-5Fkmf=A*mX#Nj zl4mNwpFCbsR63Qcuc&Y(kaf7b$?X-hr&N%&6|-kn5S&q?)rgaFih30HIC+ni_eSy_ zgFD0Q?vQU$^4>(=#mNrNs^mRX-j$wUqXMt=5Dl2){4^34g21AcpoVZ}i{2QFKVi(7 zZzA3)soLN!)?Jje584>`L12)54*qNb>jT!?7V|x@6W5^!Me-0Hg7Nc3jPvU-@-M}R zHyQUx7Q#a4F8VlKK^Fw9M^8SDzt>@vVB-Ni1z()aVwXApbYe=#U{AHiy6HGpkSFMO^n0wJPSGFekMt*cnx3I&>Cf~R zdXD}|&(jO^H+qp8f)!(o^A@}f_;Of>s2{AV8jzOQ9dCttw8^rI^5ZlbFhdR6kuPLm z;kb(vR7!DfZ0ZT4VklCXS`(+{BDIa*K{%bmCklGlswO>7R^buQfHf3B&><{=9CM_u z8CsY;8zdhCui6}(JpePZ1T;dAn`^{S@*{Aj1ig&2npa!ROXMTG0R&A;aH3d2b_FcB z;k4bGPYW66;%SnWa!VM3ekt` zviwR$L0u1l@~M)W9k3{ax)b4JLMMxNc_*TFblw#0-G-C881zoT!A6gv{^1h(5L;8iu;&5a3tNm0D-2tptqJZypfxEV9VpOqK!G+33bbD<1>Zv$tj!N` z|A~Kq`+ojD?gxB4;8`FjK9Lk3ONx&q1*C_idJLWhjVzK1tKtMXNcNK5WGi_bCsd0e z`3#IHV@MBq^4%B|K{lt9CJJ|EiKqpmh{7V7zFK|Bqs(RqR4;K6pSnaUkQGx(lkgb6&61Cv))-j{&NxZFzt+B0{ zt*Nb^EgYwA^+C&RHqhHqOF_0^TL?7Uz}C>#2&tPReM?&_TWgdo%I361qdsG?%hSfz z*4EC}-WD%CI=o{vR%oMeZaE4v3Lh`{Mxa%RmabZv*pCzK(Zq=HTlof2=|e(MuRo)o z7&cHSj}cICNQk~EJxfdv60_AZtYJIS0d)L-p;U|nq7U_lz?X?WCw*N+Jsu(p1kZ6&yUAYejQ`BfVL&|(-C?Uv(!j$=N}1P!e+271*;dg1PV+PH z$-)RP_9k>%F>1wOY}!jcz!~p0ob+zMPRE@n;Zn>cGe|B*g&fe-CqJ5FCdSqV*d=Ym z8}lZ-DQ|}Pzd1@1$Gh@0-j@#t9E*L!RG!0!0gmCFc?!?wLjgzgPCOas!b1Q%c}MK8 zX7a&+qj(4I;Te1oU-Jx1xy?~FQNktoqo}zJDCevA03!x%yzF3m$X$9py@RD zW=+M+)&u)yGe}R_OM1y(k|TS`P}z2;$hKPum7j*+)u$xp?+W}QjGzc?JsHcLu;A_| zJ?jid$k>25GL{`NerABpC(lPZyxU#`uJ3<27?4lM@G+isg>xv20~QFLX26coSe8Y` ziYKMUUWD%vwjrEX0jqzC{WNNvz_Tr$g64C;(Re;Xvy9Usuvy?ELKN~?rJe$|1|Dj@ z8^`%ra+wJ1F!a+7cs#2I{%$p#DC7S?fF0A5&4+9Y5QIGU;Q4!D8cAT68g_u!c;mM^ z!(K9i{kOuiB-}Qbw5=Z4O45dnBK7MGh1KE)lXzA=ppA)khLuC|-xaWeNfrTpZ;%Ms zY3fo}KztYGLJ-et0rlO5HU6)L4)i3cC&OTPE)PInzZX(bo>b#|CZ7UU0sd6I5eBJX zd`6BL#}Ph6_+AB)sc3L!bqwqFW0Gbc;KqQrkQ2r>GhnE6#&5qD;M?8*zZy7c4m+o9^?;p$PK=S}b%u8EgSNz@|G26LhA|7fwxW;K z=sW){oND7{TPJ;R9wgd*CQelFMEy97i>xd9(roNy>><5b5}tiYOFEJ?lQzW77>m{; zII4vV_=aOFhXvRGvU}%kz8&}~(#~&=I)nHnI($tU%K1#ptzu622~ROc2)jXy=gUYF z5n!G6l;i1xq#JLBxvw=sAJ7SU$i&AWG&6p)6_JLv>wy#VhO#|6=sEhlm~Z)T<2Qa5 z@#|ng4FLZ=#@GC1(o*Jql!Wv5w7l1W=3az{(LXuTM1%h$2rUshKo-F}$N1Gdwyz~^ z_?>v)NxCt}h%grLSbi62EP{|h>O#zSXGue0N62~o36!S@^SQRsr0oR#aSGc`1XF)i zm>bNw-a41xPdb=7l24%@=z@v*5c7`+Riqb#o<$kO9L07T=cJA=8oPKH@Nv?P87AvV zluOi?c(x?nSu#mvv9M*HuNK}yJ84ITv0>1i12j5dU#$uKfyA=_oq0A+>H7NBMuRu=S2JuM*?dP8OD%dJPIKdw1U1l!e8p~XNc#850 zo}xTY;wf~Cz^CDsHl%-#3OVD{dZwP&%;R~ zABpyXGNYcLbK@-`i>OQJXqc!cSuXK}{=p}njU%1Vhhta*8Ndc1-8hnjFdHF5!(H(l ziqHq42SNgZ2VpIOpk0VC9HAG2zz;wWX>$+~5d_VZI6aC*7=q9iAr2uL!G+Kcp#y>w zAqL@+=*Uw>Ie!fE=pq>eze`GY$#i0e1s7x|qyXVC)>goYK(t>6;=U9CaXqYoJ?(p^ zFJnA?nM8_s)ZcKNS38j=nzl546oDid2XWGuF2iZ#96yG>I*z1^r^1nN+LKI0=s+jS zKxucJzjw#F6FhPH<_)F{^d+3=y@dW@!_$W63X%>f)4?}}ttIjNIrQ}xP+x zWfXPa6CoHu#EZ6(rr!n541!+7halKAz6qXZ5w1Y^2ZCsGB8WC+!s6KxA-Nh(v{AF2 ziu?pmK{pej4MH=7RD@0lc7#ddoj*WQ1G-9lGET9ulJ*AFzA@ht#QRu;Y=i|m;OxS9 z7ePEL5KbUGjPL|n3oc`h4zuu_if|nQ&Yp}x2w44k!wkTJhZ!CMJPARhc?ZEj5c!CF zcOg82una-Oi?o>tWeE5W!&+r42L1;GA=@)rwo<@m`OLBkTseX$r>P4OCty={7hr)i z!vw&l+~O(n5IPb(AiKDXbcCJ=I5{&qAq>`dA+tDV{Q-7K0quxT06!G`ZwdO&6uKAT zG)|;X;}luJCG=Oszd#CDN5q{0{aU;)#xoQCDW}O>y3O|id<)LLzQErt@U=mQ);T_d zz4Tfx+W;Wr$PReqaB8Cg)J_B8WAhdbrXe&Go;u<1A(;Y?qX>A2G@uP>Bia}_Hz7~s zq`WEoZ<>*|ICVE5NhG{?T3~c*2``aWv^D3>@ciivpO!B019H&>_){jrmoSOMlMdt;noLvh|B`7qnd%5XrylTZ=>;!@-Z*XV zgA?|2oVaJe=Oq)T?pgR>${gC4_9LCKzuJWkfDJy74uVI~5cm`gqr-6`co1XnNX#9h z=x92Ij>Z37UWWg=oPd$$a_lg_2d|m;g~t!}u=3~>nhzhK0$K>G{zE#APKR&R40u-+ z(-Qdnl+l@V7A?of@ef)_{z+$J$7?Qmh9r{j;4PCx=fQhv0mTeJ7omod=~c7}o(oIh z_q3E=4ezFF=yLcHq~NUbI{1#RqO0M7lL}vlHSqnpflP;w#K-g>@O-)nK2W#NTj_1| zc6fE&DgC>z#N&#;qH`Zi8n-+>2HKlpvTD}2o`VtznBgzx7^ z!s`saQhPD$&Z3{u&*>NROS%vL&AOi+pax2Ez zD%ey@SSGoOWl3MAzVJWl58tDK@Glw+-=d-LD;my5kjuz;comIeqhTjbAeWO9uyHoQ zujn#1o=t#1(L|O@CbCK7D4Ps#BH>F^zzX3-Gz}g^MerTE0)9g!tP~zYGubTo3RSR5 zcnQschtNED2Q6S%!Y^nMTMTcYDtH1d75@Ruu3^jBwPXu{r;fsF>Kk}WeG6}?@8BtQ5?)e2 zz(eXMct@RqXVlN|iaG~xsPpiI`VC%C_^)J6IgZrfr>)Ot#c3{1Si*QXuLqyyTFxbK zY9P+rTjNwA3MUTHIE{$KNk#3u^e&t(xN-L2!MQ{-Pr(UB8t(=V%O3Et?1giOJ~)-g zz==f`PBU_F($SCi7hais5FgBk@S*r`x8ZyQypKo0pJ)ucPsj1g_;@~nU(P4O=X4UE z%=7pZczPD_LhPkagD+|kcGIuG=|BnmJcaksEchQ)@Ji{aIv1Xy^Wj~3CH#}2KbKtgRh03>CNzF zx)uIRxAQyTzjPPB8{Vetq+iqh(x+)Xe+Vc3r{Sk{R(du)3a|Ia;otNm{8FEW=hHLr zO?{3(&o{t>_C@|L_zAznH^FyyGk=BS!~q_vTj0t1I)4McNcG`K;2=-n-1b47$vyyI z(`(_WWXJlXH{VJk_?zT8@;u+h-@-aGgM{K-HJ40=&qp7!6<(TNO!y9i79F7i5m2VTDKl4V#)zd{0WKJfwh znSTf$m5<=b{0ZO7Kjoj{yy6R-s9u9J)UWt{ax2cr4&t1z9yy8A^^L?yZXnSl3N!T0 zBh8 za874_>)?X;+5yf-a3E95DQqZCJi~EHTOX&i>O8hF&Wf7i?DkU5Wjok9+B(@f+q&4g z+FbDSblVbb9{7JI!}l{4-k;sz`PswP)7A_ApMBsTngKu2Ecl4#;DqlBtXHpxC-YwT ziuNN1Z2igSu(^eI^FFfQHUR&pImkBHHpDj6Hq18MHo`X2o<60#a86<1?9!ry?DTB) z%t+QxmwvkS(<7d)gv>1Uo-W^!E>pkjbm^)3JzdhfU6PN>?Ka~z-*m~><#xFP(@XO5 z%gagw)61rnl@?wRl3rd^IxR1Mc4c8;dbWDXtWy&dYgcM$W`0q5{_K*e#f9@iG7HKo z^YZfxODkoL37H;yR(>8B;!$3fC$S0HE-9xgAyvwScUfMPC&`{I)nLz-iImiCU5W%< zx(uzIOkIjhU5ZStolK=2x62cf?I%t`c7|q^VJaj!Fk2T`Dn3E8Oz;F|D+Q=hW|#u! z*!xP!Li_p(txD>4hxDzMo7-jUHzluJv2dq{_VZ7ZknQ%c{sZj&y)};-&;q*6GHMNF zN<8Az1N&=%`zrzM{VR)#3kpLA_%nAUB+9ZtsVVkBvf}K6d`eA7%&-lDxJouJsm?u7 z=ju`IA|W%~K18t?VqxJ)QmH*s-wD|sGs6sxPvzOA)9gdN#Y@P}(p8nEGs@CcmZe!_ zDHd+sK-_L!Pl;v~>n4zy9WZoyS$V0-C`*f*ZF198Sz3wNuJEDLXQLUH&n_v>n=K0s zRb~ecwbp%-PMsw4a-lbc4y`E8tC;R>^(dm|>`@KY?as81P>LC0QB0C9MS_+(NtYtQ z%pu!8(!w-Z7d|;TbY!(kbSG#1nQMmZ z4CrlIxOCk)(#;`Sr%O*$>C791b1S{v}(D#m7Wu_lWfy{W3m<{At!XYKM#z}N%kUd{i7bV^zNMCBH38rIIhZ- zsfW8vEuSkRut=AyNDt9P-d68&C#HnX@XrfHQAraLQ^Ky8R$f?GTAWu}P?T>kmbGgy z_ErQ&^-Nnail(wolqy4=Y2gyJD4s-nsZ1JLnkPQjRbDo8dZE44!bXkN7|_fy+~i~q zaA~{{-L=fC0gTa_7!fiGzw^nYV*+g^`%FGEU^NllETI6h#o2JUr z4J|t%ysYLpgT`8BZ98VuPcmycBebl>*sVExR6}>Wv+VFchhbwcwLgu? z1T9}uj=jR$=Mu7$b>Wj!LMy7(syjg|Dp|8i)-00KY?WfnuC!#U;*hOYN1E;o$y(^- z9Q$l4e zMvC<%!TOSDeM$1Zh=E_eMCAF3>W#(p;UmRMmNmwjS9+Q?Cd2z8<_Y;yFV9r1CC=@~ zLJ@}MNws>ZkgHF0F|DW=J-6r=Rn77xM9wl2tnZs}Lpt**`x4S=dObsX)Y<@as%?6dX@++-czg7? z=TVCq;FZsk~ zgv+BAcX&z*4o_1)lfTYikIx=$V|&yLhjN>8n|d(w(ek>KedTd!J*XuM(y5Wsqn0kn zSLcUCrPRMROFhY&e~QMZX#Q!MZ<>}nP1B`mdDFC>(zSl{qSd1ozpf-xA8OI-@}%qZ zYEg`LojyaS&(Qg2=={}U+vQPfL_AgcBvYSy4d_wJPnSn6JMmO=jz^h5c-MMUCKBGY z{*|48cdbXY7IJyi8VFAaq}zFt*{)MHy)x5LFN$B1JG;K> zhi*AA;~&-3yDQoMJuTJ$Jw4t3UB}~J+*EoUk9XD1)LPNyQFaHOx_oLbae0)jfbl@r zo1SYtY34Yg>n~C3BT>KWb)iSw7#_W7_Glx_lc@EUsOvjP*K?}w7iv+B{I!0Ru?G3f zcTJzD>nBn7ACInQkCs<2$~_(}Z<3~0%W#)RnRIyS{;8Mc9*>sKW6Edp*ZF(2oE}~8 zNhY27Zt6kn#iQlbt6h(_oIFW7A8k2#^jg`Y?Jkd6XS+Pw+~5qy;{kk-|KX0rH6N&zh1W_ znd5ARmP0Lx(62Q=wGD-LttYijg?Fu&Os&sMt#`FzL_gPZD7zZ(S`KYjdz9S`ye>zU z&Rfv$%fojymGKS$@AqtoT6bV+*N zP15silAh<1%=s-<(<_q?^`iJCxpVY*km%pPld6vgY5wu)8UF8SD&999sCaWcNKEqt zj4YZ~k|#%pMBN$^Gctp)zbQKeMP>r9;m7^q-IZ8;DO8m>L%tHbq4*}>r1gP>*kjO*Qi995o%0q%G4@oHV5RgzQ zwk+iUg{N$Qc*+46PuY<0lpO(2*`V;06BM4ZVd5z}2A;BExLqk~z;o%2=h73kOLb7h zYkq2=z`N#`qWPt0eko}zbA)Zk=s|;&oXTLqCb;Z?c&ZNRQU(Ryb^gkbai^#W6wkD# zGYiX$$_m7`Huk0pWc$Ovn9COBO3w7Md7cl8mJAe0<=lv;?iDGOv8Tt)Q%Qnk{>Fd70v`G~#wC z(*>KtS+?S$a@oRx)WuWw3L=y3GYc!gN2k@bq=qk$>ryMT2jp@(!BgS&Di#|$s#dY& z3@R*{SvemZRWX!(&4b zahYAP%+xq#TqL?O%{U9atWCV9>$r5l3BhvlWXS;qa-}x%VkcFXK*XC;do(93YP=;B zu&F8mCxlNgE4w0ZO4%H=wUQ}XPBmC3>cKiu8*hmT8DUtIO(`rco9iRimUN=Fd=vGI zm#8hdL~Y?GY6~k-TS$r85=u-=m1{bPC~G7_*NBjI9nnmolCD=S7K>UnENy?Pa7 zC71Oi5t>3}yRTwQyvNcxp3s-f7Uv`2b zu{woccInnw*{&p_p0^oU<7Eqzh)8d|ubEY+^s5r}qH9pLIxD$sdesrCBn|x;dzH)1W(5)AWZf)qfGgY@h zysrQtk}twf4Gu>*jqd_)8ToA!(pY@kgqYtpu?Z4*2XaZt2*0ARyp+V1<%of7>;XmK;Ki2r4Vv1bwzm4#b8iVJpz&hY9^?QH4 z;6G6JAM)UN;6%jt^5K6i{*QWpUH>DCQ~Ch;U()J-q{V+ok1+2a>c|jv9-#dv;5#Ay>pFf*t)cpFN(9LE zg#T*BH@}9Vb&UsKj`kV}pA8nfFm?`cik3xR109Pkj-86OhIc0aW1YUnUL&l{s5V0s z7Jhb+lWql8{I~TF_+Qxwc)^kQKL0a;#^b-gYY8MCex?5-0smny|6kbemqAdw=Si&m z9gueDZz1?%f%ugWw;UPd7d6`Li(60ZZ3mGswkU2b++kP8Ek}KjxT-qf>NE>9y%k>< z{CD!S<|X7Y<+S4eENm^8HEkvM!;4y6aqx!r!czcWuJL0457RLGj%$T4g~pmT3w$s} z;-0EFD>%-Ak8}HI{P2PXUfCk8#B11(Pg}$~<1p&si8fPE$|wx8m!9FI5JU z=cUkFY3j>dc9)cmXfd z@Rr!cvGW8RTaK_;W6NWo_TghKG!iaW^nVFI6}e5;xe0i@f<-@y9i?IPyINsEGo%(8 zE8fb-insE#(8TtO&4}$0n+(pb*bbKfSErBDX|4FW;JWzNMKACs|GLsTEHX!0V1YN& zTJd$kb@8|2>ypzdgO$db)(Tr`>Vg}hM#6Mm%Q2)T9BW4ziPC?}ujtEqzKA(p2Mn5+ z?-d_EnoGeSgFLz)#T>N2mxBL2nq^v^>O8G@6aE77e=O)@-ixuqJL|v;nynW4+Td4X zHpV;`^Q5GD_%FbM&wYQur>?v%CGDS;&6<}Lu8WVA=1%DCW}y*0*C4FW@X|jCF9iJ@ zl~&AgF*7xv8GjCzX$$_dv@yA8t8$!2p5rb77W8tQSNY*h&V4Z>kbaQpYk20=0avH# zqv@^qy5PT)r!_Ajk13}W|7T%qxvXisfxk!BT^9=sJ;$`y_!z*gG~Bcf_)=)BX(M7n zVr+y&UyS}GrWzdmqYrPw$TdVrAn2n{;CUo^zkL7fFTmCL?9n`}_`2Xr$ycN`d0q;= zm8PzIt#~W_ZlMizMQ=AD;xDGxtkLfzEzrueCWV>8&!*h%UejM;HjbCno1%80WXIb#QqEn;Y zl1r!Pwieim7qC;){1to){Bv0_gY|q|DYDUmBs@9 z>9Sj77BZMRbRJf9BJ}CR`cYlgbZbfLr+*9HxeFY&>C{^QZ>j~}0Q}P$|0wFWO2ZG- z0SlVxwa{D&{%+)Tt4@oxp%=as{O{4Mtb?Z&Z^Fx*i=EOwah5wT6)qP1K|i%xS`#n$ znDAuhc;_gkb?1=(BUte4_Xj*PAmJ#jhaMJK;QQ5rx6)YQOOg3f%4nrYh72w(LkGZd z8g~3iI1=;?by~qQOvCm+35&EUDRKVEv{Anj5_MW7j{5!*U_pOOXC@zN&Wbt+{1;Il z3sls5b->kWc4~SnzApIh;0(vL_ErTC&JA>IIb;d{$M{gYK@{7_ZYO1 zs1@p6>`j@MA1v()6%)18q=;G=HOB|XPBmeyq`Y$n_8z_a9#J!uY>F;!4CEZ9V6n#6 z(0hW$ysU7{LK7P`BdWj)=UVd>Wij_Ito*HO(%3Da(R-eL`;ZR+f4Ac6lR4Jqryq;V zKAutItgzw`HNun-+Lf?1U#tGD^&2(FQrDG$OBEMuoyOXMXF8W03*5(-qu96c)~TiL z#6G`y`St*#W?IYZ!=vmo&^~6W6k>1Ryy}9Zx|tMFxltYy1`kW!`R!$)9h!R%LWkbH z@uz)tRG0qi!bf#M+1p3Oh}@%EnKDE*jf(KW(Lt~!)~r5ERavMD=&deAYR1Uli}wI%<8v*PdIS#Sc21QYl&= z2c20L)^-Ki%r;P0U9>)|c!>L7g7;bYSYSfQuUzm+57#GKHwfgZm` zCi0TMJVfO$zu*_#1m;N&tC&To)%eyuR_UMcX@DZD$VQ6)gEQ zXHN^fq*<@<_%%05R^$7RsAVa`L-?gOz=DSSBIRGrt^|Ba#-5WBirz)V7n5-RL}EUX z++ro?SP6?c0qsq~A4zzRgqui7nn>B?@A%Lr65mwfn~D}lhfDea65m=fautBjp1WuB{G8C$9NNPMNlh*6N@H#S5m z9D+Y>Epu-zd(b1I2hrA&MtIMFM&8>=Y1>H-Yh>&iDZ@9C^EU!dBPE|mNm(HIN2x3YPdV$t^I50xVr8!9;umH5SyX0e2?lBuqem@3(4 zH%k6j$-5fmu9B4HlCoUlpOo;E3YM{n0)y|$T@Be1CBBu!_mGr5B&LVJkgXywe6a_m zAj(UsN~W!n_dw>A#owEOnQ$6*9J5%J46#XM770e9C3))iTu*iMd*0MoV~btRzkij8@1cd@8))5F7=HCJlZE3eX9JLOF#a0j_mRdR zz4)G4Q#Ki2FN?%C%Vyy#Y!&z_SscDeR)w#tiEom*@Y@$R;FloR;ujp#@OzK<;hSpW zD`T1X!q_ADf|~foSdRS0SU>rVu>ttD*o*j<+9v!B#<#^@#W&Tq;cqy;Cbol(z!$_m zz<1U5;BOSZ9`+f&ul5DL9X1AE4EqjWRy&Drf=$5}!7kw2Y4{=-E5$E0g|M08dq-?G zzIT+y=He?y>1>|($`P9{ezyjrHforz{+|hyE+>pu`2}uyx6uV80N*eSplE4y`u|Lz zkR<@KeE`1A7ywI#LNd|+3BIBz?ikVFRQZ~?i%}aGVS5pLFJ55qY8Yb71#lQW$L8-7 z2oJyp^0$9d5C>n)ft%o$$$H%=`~kCbdSAfTZ-+@P|6gzT#WQ({ub2M*Wt{epH}?46 z6>OX`z7V+Dcp1X(lb7+caXm> zjo+Qte2&+~M8;#?pe|D*sI?HJ6BeD{Wxz%<4w)$9I|UgBO^oje%lq%S8vQ-l{BAB0 zzZ+|f6xPZmUs&4^<2OO>YXQd3cspyHGuxDL9^WVz6z4>ZRlf}Uh`JvRJypEdRy!s~ zV)v#{?K1|yAa}`^YHN}_)J!B>yZKVf)6$}Jk3{=}&0kYWp$A{9)UB19?a<0v>Q1zM zOV5>U9lgx>)%Z7pY`w;5L)Zbt*dhO$0lKwv0Uo{4z;288YAhi1cJyfjc9wCc8H3;X zAjV4wPkS+HlyI0|MKTNbkEa-8 z)aZ^8OV1zg`iM*jyv1lC#}pGMp6DNqDi z7V++XA(gP|ye*zM@rzKz_}tiJYz1DdHG|C*#&3{L^p6PdeA`0Ap~qvdyZTkzoFZtH zmVDH5&XYDk-D8k%{`IAkyngk62X~=uvk!S8vz1$^>(0%jvZfMRR+h2M7rn-pi-0kn zh*dl>YuDFa6gR}HxmLaU-H}?_&&C0jqlL53w!FMERDI(+wD%LJTh;o|_KXj};REA+ z<6}+xhH$^oN;}dWY6zEyTE^{1$M4$jhjugfb z)g$F>$0RSWrUjL{%=Qnv?^6?3_fyq^pSHpUwXx5lg=)!t)aJZfbDjnVf4fri5Yl-i zd<*a~3-b`t(EA1mRt#o)aY5tX3wclrQtN;YSmYDq{8??~>J^7dr|)gWJcC>vl9#j< zEUEoSrF>eeext^}!2#YCU6#_tuM*9rXOUr6)y!qiBh`bz9pAuosMD zSTD$RKHknCHU#`*to*F7uf15XvXzP)jBg~i1@35Hz|>f63o3jIo>+2FJt$1+oU{|^ zd>7h69NyYu$Hj7)z086VAZ9&XN@2nIF7KP_L#p4&=z2aP3k2CsZ2>z>#b~`bEp!Tu z^ux$<6L)R)pRX5LQ^>k8R|RN)-Zm)ubglKPQlm$GXO6l8;~no+G}hNX>rtb`1LF#= zMxuUD+vnt)ywx?n_$d4#*_+CM^U4TJm_N;5e>3e|?9jJImm z7bBURuTCl1{M#&IbuTxiQ(DmaQ}w5#z_WS_w&>4HZ$5utgVuhhdi3wn`{Jy1gtGgM z4Q4AfpQa=jE%l7x#b`i`kFYDy17XLR_8%-|%xb}S|3Hu#`$?!AN5iB=hcUv+1yo`+ z;TyTzi@gNnE8|T`E$k0VD}aR{-oniK(>r*(YqkG`Y_-X4GxM(z>uq}`1vn^aef6kl zm>#!%a{H2~--EZl^IH3kWj65Hs>TX`sm1R(Sugq9Q;a(%g zo8}Jye?1zTB}eSITasB}3;I6*YiUQdazX1cM-ycQn^ZmvL}ASJ2ef3iXu1Dq()*rr zeGWTDU7DJ!wSkHKPoLH?r*0l%B>x*OVa1u3Qvx=fvl>&)IQ8`5nu}G2_wuhP(E^0^ zAL8AYX)Y+C#UA2W^AdI;#(%6*jUC2zwF_W~bEGk?7xY52{VH?C&fW>Q7z8bHySJ57(2Z!(MK$D73*K09iY;+svK>cI)*@;M zqr?f!k?&%i8)9rBHnAEozQ8JIE1qw9wda-6_!48)1yKsbHAb5Aa&~Y^xetoch%{ov z#Qg)*m5F&>V8o?&jk6^05aN_bbraucF|P;-nDd5L7kb1p@!q~{Ceu&R_Ii79F$&T; zl50(GoB&233{)#M2?<=y8AW0L#NAkDCHCslixeZY_vJqZ{vr==&DIdWt8?8(&H1+m zp+s0_R{380{9Aikf%WAk_Lk)3oe!mq*x%TO)?>zPHGgr^vLaT=F7aNYKI+y4IM%ir zkgJg2NrhgnY&~5za}{U`h*e(oUMr#$wLITK-FjQPnaX_n_CMwR%|R1i^J#o;;*4js z4P_$!51z`x6pZ|LX#beCuLLsne*YBJS3~}>);GD^aKyxz{lVNPGV$iqvUW64z7^Zg zdi`-4rBSQxTC7bK<2{-5aXBF^iyA9ZOD%&|uY4QVHnZ#5)XVYG0I}<*TC=ekkXph2 zVNwI@uuHl0Zs0C8pMw8q2^*@5#s&<2L(MoOcawyjr1Tb2qnD{HWgFKrpG7*c z(`DVwX)4LWYMUKNLs{M@6eE$+ypX#*mp0%3=xMxYrTDY0*P8avz@mMcR=Y|k+K0qA zEqsJ^;q=Z)K3mF1B1RrH`dBTIdKR5Iz`-1!M4hOSrMa{w+QWbBPh94nskCm6tAz4Q z*((`#kVVTlXtpa+yOP?wJ6e5z(s*5-Q4r$|P+|qEx$coqc|MK)B>8Kr2^Lgsm*{n- zwJqiczn*WFzm|mBN;_)m$k=a=9f}yXpBw}HTBbQZHx|FD3wkBrS0aVEuBe`sm8;xA z*X58sN?1J7>hbQA=>12PN{>R4CEB;uUhvLZwCHE7zc z*k9IcYdoxb3Vxihhv&PzqpfOtzK9yRn^8jB7=@t|S(nllmiF`4qJFfe(kZ~-gW?Bm zF;X+NdP*K%jle>12t3X^MBfv=?Sx4$F{*_&&^RT(+`l)e%xB%_If6pWv*yl}ffiwW zW41~&z4r-i__Zo;FY=wdf#N4MmiyJZt{KuzuTN`QvnrED-n{-(z{fe|4_G+;;(PhM z{fQS}tq9$TgjkzM3rXGmS^0Xow~hG|c;EaqZO*^m*cyO$#PH@CqG~{{iNs1%w4$wO zi(4@_dZ{gZ%pQCWl-$4eEK~$1bl?JBr~R;}<(N_%Usd;_?Z~!mwl$plp2BI>S!0vp zBk)+eeUI>OwJTIj22(!tBFJ|L)aXk!FO2A0hxN8BI!%C z??BQFCp7YGk+{?@B57WmeSp%r%!Kk8;`E56<%Fzw~ZyG0bplA?LX`6Q0I*;qhYQ4&p7$oE?N}0ZZw{T zXGE=6jWv%fAyT0inSBNj<^*7Tqn@;MYi0N!AZuEtKL0(r`-D0cm+=XLXiefmyA~(( zvRBFaHD1A*aled%)quMB0p)*3&u%BsUoAaVT5PJtiI&}1TYsjtA@{G)I=0Imz89VZ zFZ(1CIaI6PJt|jOzpp4*t^wZEaLq>uyitC$)bBSKO}6@xnRP1Ui}1=PM<>kvZpeW8 z^`%e}iFF~){xQE9+t8-f`4QSNX4~@^^Gq9&N;{M(U7Ec|@zV2*Z-fm~y$HF9eLJBu zZ%_4l8H9S}lj$Iz{}?4%OS|JZWcfwB8ZSXUQy%et0q1=fZE=MGLVE-59eJ0qfW)P3 zZSSZCF0TXo3-;y2Xd8*(lkXsKztevOKi_2?yTQ@BW)>8fK|b|Ta^)|I`B&nqpDjpw z?^+95N3XB_lC4Y5;by!Z34ZXe6EhZaKx2?B7cc z71!MQVufqV%^F`5)}u5eG5vF4*DIf*`l#dAks<_Z7bR(kUfoN3!omw{8>D~3w4B6B z1^WM3mQ%D#wc-rH%H6waN4%P&Lgf6rR@WX;TL=Yp{<2xI}8)Zv%pcVEQlWAc(CrP*SBHx(lF z`c0B`_7T@1-#KY-*)?4a`K+s4lb31fV+O%!11aTrBO#3D)h|$q*&6ecz7pk=!3#(9 zfE*L8p5Pb*sI#}O&2&Bd>BO7=`0R@}M{39^#}11nR{Qve_NzQWC0D=k*T>a_w9kNU zTVlk5|KqAITMZe^wp1hHk`UGHMB~vqllf{$ujkP2#Y&?of*POAq=M2rGhwe* zSWtDI>&w`WNmnzm^u;-Wx$~5<1w9U{0ayiR0sn@uMNw5Bmqbmfc^=Q-P)onF`&!Us^<-mqcA0r!wOowAk$xx(gdkNJu#VP=v9N(k# zXY}d-UYXTeEAy2OH0FFGTZ~%&V$HqX$Ji&44_6D+rWV})A?lCjU*C$wm#HZ^?3aTk zR+qh&ky@R6RG+D#Yjr-O)>A{%MbmFs8`h5ghOaSn z#L=Jl9s|Yq7!sMCCE+iSrQk0J-&06q!T6p+AAF}F6W>yZV13y@{e^}|HWXh-XpS!< zT*ji<1a>)#Ww|U5eDhf$zVk2*f1OwnD+l-4>`L%k%$DFg4_CA0EQ77UR~vHJDt0UD z$8KkLfzv(gethj=J$s5x#CIAtBlWBJo6WYc*V!Dl6@T;bm4>(20{NYWg={zb2w#Hu zgnh}bVF%c^Y&H81Ut+k8*W>lr3*5!i*o(X$AI`S$iF`WS$!G8@*vGtt&t`l1Tt1&2 z;8*hH>@dHMuVSb8_55b`6TgGs!Orttd>6Zb7JWeK+Aei%hpt=WKL;K7Ve;K(u9)*|g$H)idmtK@!MugiR z@V{MpMfxlDn{P?);F*JeNdH95ck#cBb3hq5K*M#2>5>OoKH89jq@8t07VBi4Qi9E6 z1Aq*&Mfi0wTP&$;30sTnb!@%Vz&5Z6TyJKZ0pEhm3m$e7+lT8@*;)8J$o>QO-pp>s z^)2ic{CX?96_C%fF9LEW`-T)^_p*BdzmGjA8SK04SCWtYnmsG!u; zsNW~|NzLH$L202pBoCp^e0jdK7#zO<*T_tQI!ojwQY+;rL676|IN+<~)qt;&*Went zN~AXA2w9K5Hpm+Q*(h&9k2~d^=xdL>2iJS$eHi(v@@cs1bol_wQk5bKQUHi0B~}#DmO~Q%1z2mQkU`x z<&&uYDdp3WOfnRokfC_%+Aoz~!4o1QD;#Mpl}l0;lC%KVg^;INkftTlJT6H~AW5rG z5BoAipVUB>GVC+wNEUqQ3s94!D&tah5tpi^T&gNusw!NnRza$s1P!?t|8u32@xMyi z2i=$pS$hUP&qOXHMLHY*tB?`t0$g7N`Kv(w-hr0Ci2qrT!gr$eyYPPrm&J_B;yf;k zbuNp0xh#%D7C(VFrk_i{#(1BCT+ZQgxrWQ-5iXa*TrSsgxm?BNGW0Suv!BalFPF=p z4U0iW=dxVL$~@$RlPMFE)WZr`As|J_ZMGiyn2K>-!b%`3OIfSrWo?k=a+)+>0Xe=9 zEidBId;F=`+YA)FexMYuT$-a_H_Coe!_J5^WT(SqaWZ%vudmv4+_rmVT zf&}-z z)F7A2rI4*8_sd~zQ~+KnR{~On>}V5Q2NZKvB35vKT80MN_F4qJdWaHp2*f{uv zjU&}VD|F+N^ub214~n=xFt|S0#Pva|JS-1O2G<9zuv!+P{vvr1`X$|9aNV$g>xObz zMx+~-%gfRJ3V8*vSShc>HF8=(zLT~X=GtN**-d~?$P>VEv%DGCTjZ^%vt8bS7D#hw zu!MHw^CbBseD0M`mfGcg@+qJLX%R!-FYiZ9(k124CFcM_T4XM)t&7kvX^{@tU6-Ow z(jo@eA`4(MU5W8uC0_+9kw(#Ak6j~e<$9%@>y;v|SIW6wDdKvik?WNp^vWls3gp}R zEVLA9mk`%3m0Y`2K)c)~`C!Z44)~qWEjb&J@xf+% zL_$tI=$jzdH$Ji-QJ?fpkn5Wm*Ebc=F26+kzmk6iTz(Br6N09B3U!bLOq$L0&3vwJ zV$d`%;MW(SX?&Cy4DG)JeG@`zfet&0kRe4f##f z{42ChkZYX^u62CSI`0EQ`X&Z_a|A6Mg}y1~`leGpE+5B8NbC5x*4f6jPK;}vInX+? z6jbm}@^Rf0g6@G-MOHE|Af$mRxCWZdHBbfDK(o09+CVlkv=MA%e3Blj<9eu@>!CWX zhsL=cTF&**8m@=day?{nJv7So&^oS%#<(6bxgM(HdZ>+Spf;|7Os;|2xbCUrx~GNf zo<6R7`nm4uz-z=dl=U}OS$e*xYl7@>kM+Ovy5w#Sv|a-O--V z9ig?M(a^$-|Jn98Y**>Ob`<~rfxpGUg@J837i53df1mH~zF8qjo$#KM^+_0=U!!`; zPnAdT{{jB_2)^H`oQlsK$|(M~^Z&K5?qK&#{~vkq_%EVY=TkvTA&LDx}{@su7KG++zlg}us@L>WREMwySY0EO#e{9S@Fg0d846lDx$8Ok`ya+DP) zD^XUVtVUUbvKD0n%0`q;C=)1KP`09ML)nh917#=5E|lFUdr)pcxmWVb9+WH;FN%ud zLxII1XQSkx;G_h*DL?3W5bOOMl=H9~%~Ezu9#{5E9tM;F$^fM|AiL1X50!IJ&VzSD zcA%KQPAM03$p_{0LHT_4EtGGgdY%O;>gup@bWm3Zb#+iz2X%E&R|j=Cab#+iz2X%E&R|j=Cab#+iz2X%E&R|j=Cab#+iz2X%E& zR|j=Cab#+iz2X%E&R|j=OINabOt-mT_Pi2bOVQ83&eeU>OINabOt-mT_Pi z2bOVQ83&eeU>OINabOt-mT_Pi2bOVQ83&eeU>OINabOt-mT_Pi2bOVQ83&eeU>OIN zabOt-mT_Pi2bOVQ83&eeU>OIN`M|OZSdIeAQD8X=EE|DkBd}})mW>=s6_pjxvKwU&$}K4ON-D5afu#y8RbZ(COBGnEz)}U4WTmRWQU#WAV5tMk zPGH#yEIX&g(gKziu(W`s1uQLKX#qm+cU1g?|7brQHv0@q34Itg4Sf$Jo2odm9v?6oC<>m+cU1g?|7 zbrQHv0@q34Itg4Sf$Jo2odm9vz;zP1P6F3S;5y2$N?BJWaGl5#dkg;FD@ouw30xm+cU1g?|7brQHv0@q34Itg4Sap}+q3>tw!BQQXoVU+nO3s4rKEJ9g=GJ>)cWfWx$ zWf{si%5szyC@WD`p{zz(gR&N71Ik8}O(+v6TTr&5Y(v?OvIAu&$}W`MD3I7#H;urc z5g0TAgGOM`2n-s5K_f6|1O|=3pb;1}0)s}Z8`#2-_6K2SpM!E9%5*Z&11vqj(gQ3# zz|sROJ;2ffEIq)|11vqj(gQ3#z|sROJ;2ffEIq)|11vqj(gQ3#z|sROJ;2ffEV0u^ z*@QwK+bt+tQMRFMN7;e06NT^j@s~Wgrlfw)UXaUtV0d!P{TUZunsk>Lk;Ut!#dQk4mGSp4eLrlfw)UXaUtV0d!P{TUZunsk>Lk-eK!#dQk4mGSp4eLrlfw z)UXaUtV0d!P{TUZfK?C}4PoAfFmFTH9}NNW8TeBfe)HfvSxk`T*jg@^qVQDqNZ&{K z0m=_ieuVM}%A+VRN-fwew@5Fc{0`;!C@-V@K`MdmPj}O9EP&k?A{nKJvD5lK$`4R} zi1H(pM^GL`d1dl0=~a|JqWlTv&nT~Wr^@0TA^ocdMDVS=N*neH>)`+&7aa@&h#z-1R6O@}M@yYTP~@IX4;<8|w6 zE}X{ew38a|O!o!d`!HT5>9(h*!##=%52eG?=$puZU+mVGGvJrGaGs%_&j-dUKkmQ< zecgFV%^yxL)8_A9r@p5w1Aey)FVBGQbm7GW=Umw*{{vjvh?7`pxU90JxwS1;=`thu zGM!IVO}5NvQ)6peYc%S0nN_}hPG`JO_WC>ygX&;kFEd^C^#ro>jj~*S*sp~={+v*8 z@&|&0A8D>H?TiLJLBC+#G=>frdcC=EtFTZ}JlRIX<5iUS6u$nwi%)Jko*u)6=cU7y z|8(K`HvV|BTloss?;OYtT9wUwRmK~mmg!Bz+l;DdF3U2PYHh^yu}gi?5PKqG7?I@0 zaBmUY#4qnR`%2kte))9VS6-YvuXLZ!WaqpJ*NQf-hH``Q`N2R$4@ZiUFGj=Ra3q@i zbs>F^e>FGyKoFkuM_~;K~QF(VX zR=0fKz*qUpd3DnAAOaNIob(L6RAq3|8)u zNxp(py=7IEl^CEG+$-3(Smx~eu|1z2t6z1&(w_Y_LBAT#4VABM96PnW{nW9!Yb!#z zx~c|hPxr6AWBn`zNpe|ZIFhxg5Ar_`RE%lSGL zaFRm;9%c@_kH+f2m1B4V1j*&@Nx3 zm?<3^Hk~&tQi6bsykhm|KRbW-aM`ujepYb7x4*Xf^7XUlm(M?W z{>ML#5u(K`OS#`ui8Dxw*L(}-*Tj!u55)mZQuG-0h>_6ms;5eNkYk4XDRBy7=G6 z*f{+aQ$d2^eUcJMx)Qz11)N(Ns8`4Y?4eyaX=?#DGT_9G0v>kY;5v-bfrIPpb_mY3 zKGi2~1RVX$)Ly{34|CnXwX*1kY*j(UezfQGPg+^DmwsPC+I>OiKD1ZLwX$fBe5x*< zTq_HBC>@?gmqZ4fw6dsA)~DMJUp)ez<-oy%>2%@ab?Hl5S=1-LV5%L`$^u@N0Vl02 z;N=-`(#ir}oDTPdTsV1G-S*Sqr4AhPgtvM*^_36V-&6Iu))VdLQ+;qHNu{4aQqCet zIU6DX(%Oo*3E@b3f`p~&oh?JrlZ^^FuF34m@e7vMgmk(3_@P+K$;%e+Y416>x_5iM z77Jym!G=qh?7d}G{mQfa&0DS-H^Rg5&6f=fUAk%0C4)tApB#!tAt~41zJKLao0|B_ zfIS?+%E;$>m+P#l3nCsiFSnqj*vWh?e2>iy8%8+!D;3M()M$(r!N2Gu`5GRZ5m;Rb ztQx1ve@#w_e==Uw7APq~8mL)eC`nk|H=lm%vida_j`bd#9q_85SfFNO`?AwIF2AB- zV@>472>yL59LWyUp5<7TVVrl9w&3-ZqwI#Xc1Y@rcCwuMykFug0WT&v zNh8qw4p@67DVh@n;$$!gQV?PA-Sl2!LYtcwyPH0#B-fd0%#_cyzzxa2F_ZotzuJt& zOjexyqZz{zMTWW~N(}9{NF8<$u|ydsv`U}o!oudpy=-bED=X24?%aNjcuV|KW;C|C zETWI;QFI(+j|Ay&WIPcGug$|)qRI89$-b0aj{&iF0`X8dwvUFhFXsF$4MntMhz~9= z`4=*zlhsF8Hamb`m!H80vc!l6a!NoDTPBE?g0CX&Ku2CYOYwewGa%o9vhF=al1c z<*?ljWv~!dsi?2o^(z5?T_H&*;AM7wffq?a0WY`VFJYXO3URD}7ZV(mj!*VUFM!e& zg3{QXsIcr({EDTUxg)u@p`)xs&hik;=J;Y2WqJOvk2W3Ip<>!>bXnP1A)jF5<5z2z zCs13Iw#6XY--oe`bEzrDk}t-B=iiPah+l zi|PRrp`g8O?2i`yC5t*%%=SvnVi)md<9+=VN9y@~e#P2+4*KvVzHbWX_1^@vi^G8Ui_y{6O-tS;+^rA|n`N z9myAL?u2|nub}=O8V%%2mlzEebqoOHJqnh=_#|(~Lkg?B^ok>qaQ=6PqIo#oRZYe=_UUjv<;4nOSFhmL$w{wY=zI)TcQ1bhxKl`Z^>Wlh$E|CNV?U zWu(@Aj3vYjkvieBtVt1G9i<77i-|(I;bW0v7WPHpufayq%sr}xG)?y&&oxxbkS{jP zVCeXVvL_TY&EuCE7GRCZx?oNKVQI&IVOWIqI$($1L5(x{OdV!l6ElTTU5`;^NOp`W zwNg0G&|oeyOjFlG+3ef7rV%m1dMJ{eTpBY~%akuLqdD5~hvio4oyHId%EQNhZt}iq zMxWv{deENHKid>U-+V?n99Af|?76nNp7XeX+ZMmH1~ZDW0tXJqjHcHoZ6@kxx$yps z`iEV3pK?=bJeW~uJUcS#C*ArqPVxbW_VWqOrFkExT@~yGZ)GJc9xUsY7$aiwx#X%V zgP824kfADCu&qN&_QuReCP_E`hy@Ho^E7ofSIBFKd?%20-R4!`CdUp7)4ZkaS-HJc z)retW%PzL9?BZ>o7^`1-_Tn9%7@NKF>?I>7wV!`r@k#9$_}ATW8tqQk-hSH3t0tNj z4PCb3n$Hbgy5VCt6Tk2t_}pNB$#)1B^Enf6+93$I!P4PmnFx3w9nLLt0oPo3zx)$E z#{?&f9_^4kbAN}WnRZ_=9nLL!(Vm_T=N7$yhtlC`cqB65q~An+IRpNz3-`V#( zbpMcj*QUoafWPgwU&Ag*!CgM6@^JVGeC^<=dh*?Vh!4r$5#uUMhbzBx;RPA+6#o87)OegGHoUMID6Wn6lXUgvQ+C$`Akt_HX<~}x$wiL<#V$10J z@mDXtczDln*)T0u(vpBbD-;dZ@9JB>rg!n4iA|M56CJ&$ z%*ye3Louy-b#u$ox_N_pc5W>n+Tz!XWBynSL20E$gKhOqA2RX4fQQrJo|p@dxbQxr zfdhvJ43@FmZ>pVuQy+7cUY}diZhf+CM88>1eb{s9^$)x41J3thRG;)JS>&pR-^Xp1 zL-Mtln;t$lnNyP>qlJ4nZ2Q4AN}C;T4$cLNag+nU$Gy!P%5u~!&CDqsu3WHaY<#zF z8rq71v6W-nc9MBNc=5W$r*sW>H7%}b-saEG^;uR(QDc$$-8}=5upS=h>FwXR3Elzn z1bk)1=^e9IboNh#bnl#z=H9h<1_l`mj^OzKI-%evOc05Dc(&A}RmSR#=&NKS%NBjU zYSBVkZNjyKHD(;YhwoCHc0j|2IW4LfRx$QcynViI1U&4*`>0>1KIYD@k2y+*A9diE zi*)#57rr9{o+LQtU^&*uRj|2>2`_ljTw9wj5%(L2e9HWj7M2q+cWvLkBWfB}??wKK zMf>KntNGUK!c$K<=e#-0#0_DfEyWb;zt1wWWY&A z2zVj`PIi-k%PzcM22Ktfe3#yDsvR{0PWyb(PFWiK7`JBxyb@2I(7K`VA9L}l@^Cm` zw@yF(_?_$^y3hSRV;s}npFW;r+%C(wALpY^`_w<#2bsUmPW$~TcYI_ArS7|m%T9q$ zc?O*9AOSDVfWP3vOI&!LoKD{l*_T|pT&2)?rf4VEtq&e^$4mB1+IYAsLH^O>x^7A`L^n{G{f*RUnx^nGol$;1dOSXhdl~!Qh~@iP^yjLn8istfRTPdKzg^yF;NS5(<;~$pVf6Sl^i3W- z{sV*j3ValNhJGtnhFrxdDB!dL1U!%dCr%e|IRj1{E#NA_fn^0KxD#078|@rNyxA1cmnbqhU+QdH2sbvK4|H{YCzh*`}r=I`z-*W-DemXyv?A`cigOjpPT} z{1Vn2%8vv>$-l6?P#|0oNwlNV+dgk^Dze4c%XDkI5y1?;5B8n z@QNPuGgH^1{%5UcbI@&sWgiWzF?eM<*!o}rnoa(eRf?YO?nr)j7Og_wKBwU;f`+V? z(@?;Ph5{bQfD;V`T+V=#Ruk|n7v4|fci>pP>FpEEMSYdvHomvv>DkID_(o$!qK%*8 zlCDa?Cd09WlEPdz^gt-!f7<+tQQuNrl28M_tZ-ItgZUM>Jl39^ELaFtMdI1JN2Ma}DXVO4V^-W% za|o4SEw;K_t(!hIz-~*P0iS`v?$<3<%}H)yx6;>OtSBTeE`x%VAne^YAQ_MT6JKdz+4`$9V0?MGGK&wxMdw)0X3{0A=l zWfzX|vUglKr|&fL_ljE|bd?Xe_20{Yzv03^$bf&@g}*~^&bNK+J@DR;XDDv24wM%iO_0G!eH?Dtri!!bwGWKR8WWE!`%is&0qxT?wP{cH31oc2pjsjtVMR^l@33VXo;BW~Zrm2J@HD#-_2uS|jx1PV%M7K7WCnvW<}%|p z{FS8<*sgid%EjW`yt1l|@FV?fAD;qk7+d6v@lJ8x4h(G8b1aB>2>ECrJTWk^rKM%- z^4WtGMfdux?g)Mcx!fY` zyD1|uy>O{O=vvmlF_e|#WrbS68}!%D`KS@G569O$I^yp;W2AFq ze_6CZg=&<2rk*!@sD8~(Xizz58frd2S>=86*+Q5tpDpY&!T$mdNs8PJ1b>}5@ID%= z3qO_)ciW*@7t}wYJxEl$z1Q6PfV&KdX2W0R_sP8fV`VVKpln4P<2(M_4OhQMD#ex%+~}3r<;5IZ(Z%X=H8nqNcGm_G}_^ z04z#mnV?zuvkc1zD}r#`2r4&9eHl2$5^jt~dc1+~lR9?CG?ee#+DeqiYQ0mBMD)i> zv1qN*2?Sk*d+0JppJ<#tRBo?YXCA(z;~XzO5jM^6M4YaVAE7Ck(AqPmgmYy-16R0s z9L96b4jwna0(MrmGgrVv2p4y0f5hNZY_n=SUL@9b!_3@sPikQ&Wru|Z>KuWyMbLdm z#(0x7Ua`6mtdF=iJ|9(xdt1Qg<#h!Xpe+&Jf<&lGpB3(M`Ur)ny-MSB;jrVFfWMIeCut$zueTF98OX}@L%SABo)McuetXflC#LZ zvfU?v)g+<4gMC{Wj6eUbhz6O*=6bY!!EPy zzmx$dDOM`c`g+-gW4v5i3OFCHOGna?0{)6y{}AwlU1r1I%Yef!v*90Pz+so!@ORST zWQW`EcV~eA(}iQ6(#H8A+mSZkuxjjf-lF>8&su1#*TJ9Ie~NQg5dl|>h~eq?D = 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 NJz&BElYk!s{vQ+DoeBT| literal 0 HcmV?d00001 diff --git a/frontend/public/fonts/Nunito/SemiBold/NunitoSemiBold.ttf b/frontend/public/fonts/Nunito/SemiBold/NunitoSemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1326a7dcf5f3b22e69b0d996e99dae800e71fc68 GIT binary patch literal 132156 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 `cA{rO-Dr(pRX6qGzZ)Ffp7>rZSmSGW*4BBA(60 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 literal 0 HcmV?d00001 diff --git a/frontend/public/fonts/TimesNewRoman/Bold/TimesNewRomanBold.ttf b/frontend/public/fonts/TimesNewRoman/Bold/TimesNewRomanBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..578542cfd9321c98e5e913e5056ed244e1b256d6 GIT binary patch literal 842168 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

gTfRB7|L?Ya{gUE=ac)?I$ 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�op1E$7e~vjbd35dK3)}jCv{?A! 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≶r==6`2)t(bnmR|FWBDfD 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­=2j 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%0Dc}HW?2=bKtTHp8n*wK4l4XGB@eIE4JTtHXje@& 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
ca9V#Nc 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

fzp8Mp>_bfd=~u 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

6z_wD&kT)=nokf| zw$UnNyEa5;I@9Z0o3=!SOZU8gfjcUl`P=JLJ*bA=Ab$4!gVF5OcNK5P-2y2h{6*S` zD9|3c>siC=hO?Nb5z$)3h|^@UuxbK37jT;3Si$DN%t`YqH%bYV%($p^{;x_2TqXbJ z96=r5#8#q|0ByWk2Sw^Z!}R$1N&@x}@K>0m?OzzpH}cbKm!vDA#h2o8u_~%3c-vX025l;AZeSL%4 z07WfLm8MFKnDg_3#M`0FKgdf=ll#%%k4IZD^zx_VxAmH?|(*yAF1B zEyh81n=u(;EzELvg3DMka%MDhDx=C|f|N=lE;u_z4e9aJ4fvOeHK>6P_1#6a5Vi9G zmRC8!#A{b;7iP%7w^6{Q8jGU+sE4{O#LtbAMFx(cw81(AkU5WB0#=))H=zdk5#K*6JF4Nx-~wtJ9W#YJEoT83N4j}x|W8~OGZ_{ z{ouDAz2&;cKYM@1<%`9A{^B!2cIMwyLo*G0+sD3g<<+AdU3=rzV@J0(SWLm{V0FaJ5lUB1LWG%24s|3V>h`7hw^Ord?IT;=`4rXhrBZJ9 zDepT_DPC&HDC>F(@i@ks@oXSszFp26jfBSL`2E~F(8HWM7zl+ho7UyZ>{s($>JjxG z^*;4|>Ic((Lijh7Oa#~ zzjO3hYNj}U9KDuMi6fSRIgK;{7(3EzguM|sYQj+!18tD!J5R>*1LtzoU-!a zOusRD)B7jZRYrv;i{Za6?8r4PTl23sr#sAdex~Nl;xDb$cU=0(n;+Y?XKXL8gSg=N z;u8L=v;V?TTb|BtJtv7HoKc+R>^SbOVgMaG)Y1D~h{mq8C5>s<|h(#eazB$130*SCqd`c~)gPiVv5coPNr$Q$D4-MZ4IyKzJ} zNo@S^LA#wc{V*_?CiF2Bmpx{~U^+CXVM~Q|f;=ki`Ml>=K`OJ2KfikXkCbOqZmKxJ z53N+*P+o7Jx80`g+n+3v0(%y^=gMNQ@M+w$3G|uec0E>sl*Gui$5RbZ&WeLRpY$~D zSr0OZ;FuDNhblZ47n-XM9}EUD2o@s<16s%zT$I&mwjh|TY#u67ze6{7{`Jy z2d5l1(#Qwn6W|+=!1P`_Z-;E0)}!-00%XggAUC7_|G)aNvJ&zhq*Pv2~U? zga6ZA+b(9?zxNcabIXjiZJ2uuJn-B(9;x_4yp$IKx@2GRk3!Y#Mm%Ztlc38L=%l(_ z)>~lPaOk2<@)Xt$?J5~J`}li{f84&EG~Rt|UG7Jow&xIMR9nqN`V|$}rx8vq77tgN z^j0g{!3@hrJ)2!s2fIViGupO*l+i60O8{~vzZkYARrxS#o+Dy@PnzU%q>7Ph@ilin z(M*Xhi5jJ8{t?x>`P8`bJ*bhtc>PM`sH~Ps&A6KWjEf+Q7s>W5B#%a>-R=y;%?RLq zvb-p-l~IDEl`*AA+ikg!3Yd7egTsB`k71B6_`J?1`-B4kkzw@6Sti+O?-5L3_44&~ zu8YX_UDp>qz|J|!bwy{eB+6#m;@`MHGSBTfemq&;Z+gt{;i7y^tH?jYKO$U#RjhhS z;pvOkkVNYIHPk@j0H4it1?>;?CMijrLW=PA7``)9?Bnm6{Ww-LbR@v#Kt*ewKeqx{dDiF#$IzgZx)> zXl8T-=>tJfNyMupVPM^3=#Q0u>P6|czqzG1I9@m)O^d5Qf0%pzG^9OA!y~(G7E+ z3?&4saIY5;p-s7F$hdqHzq)Q96IGh5BkkZi`RfmdEm%*CN;@T2qWxt&2_i`Z^g3-Yhz=LW#I>?l8$^%i zV7fVuj&xc%C}??oAQ@D*IwQGmgV@itihW!Io<)u4aEU{lQ|yxU!MGMh1vSlucKYfH zn?K*)noMW)l>wtaeM}sfZfz}4HfP%I$+KexdKBRLs78XP~SPe*Y zf#vKO);$4k5#_o|mqbMADg1<;(A-)&wPFY)UXb6RIvd)EuG8q2it{NrP82PoG^?fp z5GYLr48V?Z5p9y5s0QuYk)p(r{5v{wI3A=UMJm@}Pf^`PQ=2v6Cb&;zv7@AwXb6c} zWpHsbO)thUNvDfvgZMBJ8)dIFiZ4tb7bj~r@Aay}>0)hj{emUYfyUxDUlD34L{05=;mb4MxOQmy z?whyd2BXCrM!E`LTeGRfq_rKE7gz>I>N7+y@N~rcrLE{6w5{6!hUy2s_HiccxgE-O z33k0}+HQ5S`W@t%=y(`;Ck#G4E)H;3u?HQS4A=|3ICw~lVomMi*btB9Yy;A3XHbEP zEkO;OmDN6Vou-fgg>Dr01*N1E)W?%IiXoeDuygXY$xVIL#*x#Xzd7>Y(79g?@|(ZB z>5>};Ze81R-@o5Gc3JWB{AS#9X!du)zk$YM+(!=us-+qoqgA?ZBksEfcO4SDIKSA9 zj94g8UG3n}xWx!qxHu$ZKqCS)J|6(HV1a=n4u{5!>cL~6HbbH&MCqqcNTVn6(t*4* z;vx?9oeFt1q5~IE5$K8J6N70rR^ri!o2dkwR4P3lfUVeI>?#?*8ifXkBE&GB6Rvd= z|7?5xZEF_!w41gy3?vd2b!+NJT_2NfE50=JSWibnh{oC%R=j`h6%!o>gQ|K}H2;C7 zw&tbV7O&gAo8OD+#hbJ1unwKGzZ2hwy$f>dr`J^>R-(i9`LGNkZ68?H0e027OkO~a z91di8)=gWj0WCh(3w#`pN96E`0c==^9?|kT9uCGqD=Sqaw!q_z*|1C02ZJ62Pr?S0 zYvN6Xc)qDQL|*9a)rL|2%wY2+Jp+3Gj^!4+w7t2wcXE@owWqyj`0|u*ba_+lT|o*>pOa4e+#k%A+mf0Qh3NM}yeO1S&rzdq_aKl%1nZ zg|#*BS?4`!J{~JQE1|qx2==POSOHkOjSUY}8%KrSW%YH-+}?@Rl@_fg-!=1}S8w6N zodTb27-_c!r!GljEf#hTFR5F0sXV-P=b{h0oc48fANu6+CD$(9JM(JDdh_1SWgf(Z z+8(*#7UIg5*?0KAg2K^pvD?MCQeEOo4vWEqy74K-f4!*IE%6^T&tfz^pq+#}3#w%N zrivp=jA;cWii9k(+W80Ek<|F-7FF|CuUowcogR`4yPWp)*T=UuSDH*)HqF!l#!q+g zrm!4$5#X*meu%EXqYiKdXeBp&E)6&7+~ZL#FIVRqBVxz7nlqP{n+QC-#YKP{>j8y~W~ zWLk1Vu-R_6*DP&l|5pBv>s|kP)kbgB=J@u)W@jds`{A;tpW#Usfm8kkwAR4wc^t~D z{~+*dxU)X+xC{Z7!J>M(EGWzv4kWAmH8#oR^`fWWx@ngyiE(!zvM7g+jUcjICYR0T za*~FKOpQm9@JJUB+3D8SGLg}#%TKXw(D*;I+KH+z5my2cd>)7VRz%)C#M4er}qggM~x^Sv`Cd~X|xa~1}u*g z@|45UnBq~Fr@GQ<5i2+eKE?n70~FRjf?}hJ3UxI}S6zTKIf}GA;Nbx-3PF>^8V`Xq z2eDX-0g`9rd79KT$hgPqHC4Idkw*tP8!M-JVZJaXAYlihVgZ%3PVWa-k$ zu?4@~(#MBhe&b~Ei^bR3TJTdsE!M)tEu7}j!|h_1UnAj(Esi6t$aoI-kHEdN5o8MsfWIl&6%7VA?AhtF1x%|S`U0lN zHDrf-!U>bUV#Ds_-p~@0>C_dUyNlgX@gZ>=)62ssWwLVJM%<1aw?(quDlX)#P<8`g z#Ld$tlS{+ctZGiCyRg(y!eyC$n)r*cS6RlD#JSCht{ZVHljD0yj{o+KXu_M+g{#m_ zeD~FxA-5*4PsMBUg_+NOc*CLuXaf?9?vLz-F5I7s>+QDTyq~j`Zq5jLF+f6DaC;iI zVkJ#;$Z13yEwT@=Y$f}kWD0{ZNcJ`YbZCL4gvLv>$LzK~1$*GDuYC1~&lhhRvu}zCwT5fvu9hpBgXsBbs8=^hCXP!U$E}e` z1Csz7SOvBY1kx}eY5jV+R#4`^q5675nkUN?isN8Yli^AHG>v;j5f^M2#fl_*23xHx zCmXSb-A>yYfh;5uMVh{(E7iDws-QqE(Lgh43p@{A={z4N@m3v7O$^!HzHWZippPZ=)(GibwkPAm8*B> z*YqxqUiP__i@TQED?^rQe?N879KjQO6FXytNZZCbqse-gC(l{Hr<@Cf+y{}>kFAk8 zFSe=zTjhe$&T-yKGU-e(>HM(3hw!PvgM#3tg{XiTq9zVENKW^2tv z_pbFG^op@gg|Zn#*l^bmoFDtIyI*+j!~Et?KT|wVJQ$=^Dn7)ta}Bq2T2rC6KaGW{ zz~;KJIBId>VWUPJl3*aMn-;}N9}^G}ScF56B7>m&#c10=kIRfca)inPg13Q|Ar-_J zqg3U=Aj4Q7mq?sHw?Ma|J7_=_^oFeNpI;$Co>lM8dJALHDNHp_hF!(&*)?!)d7S7i^8oH61FqL4fT)6h>UG9AEO zh_Wo^(3g(cr;M-2Go52BLh+LJfS~{_Cy>*GcbcLh5;KhrOv#+MZjhME_t_7~2cBym zeCWmw{)@$5cmDIWqgSZGwcNvsaAV{CQEqGep!S{B8`mWn@5o;<}hA)R18(0_DI9Rg_T zm!;!Q$?{A`Mahpx8kQU7M}wmwmZ6=tDIPo>88it;YEf{I-Y1WKY#m4mN$h6)OIB%_ zoXDqV4v|`;vPI{at7QogRvdr+t<81@dYM}aYIMfc;6#m-+kEG%x{k_cDinD zUKM@vsx_?@Cimg)oU=aaun#O#B!c)Q&}5FgQ#QLZu9_Nu5-IIu(#83o#-^#TXRUsuQ#0|FvdhVMIb;g2yE3Rp@wOEB`9lJVrZ|(85FCDUcqQ_foyJg+| z%MD`o=Brw}I+_OhM&qkCd3kr;e4#wk%4-6bhCWTN)aFCKDcFvTQ z<;5W%fCv-qV9qZPnTlg&CT9uK`L@rySLf*~2EgmTxciQkFFse?G06XX^Wuk#4;zA+ z_;AM!8B@|G{H$;jT!3A_{Z{ehfAX6*uli<{%jalmuJbNQT&mCqdTQ-}etlFnr2LT# z&w0H%F2lGx1mcK*ILIq9<6Lp^7Rp|K&}Pr4Fp;*wi(x}*(Ifc%iGauB5Ne2@L*k%p zfZ1w?*71D}TrBDkS2Q;)cWKAOeO-Y_M)OaYFS~Raj|TN0u0VbeV>*RwFp;Y^i8J6MdtVT#;@V+s`sqARU6@hS^ofrAy- z^mOaoYL}LpW-agtRD}v8?wS$CEZG2Tg*8}MntxbQtEDB!K8R1Wo_l_X|M|6bSwYAR zty<+dtXie*3o!SGrswY#@4~BI*_Y)mESCl$6MkJsgzb5Sv|0MTZ>Ms z+t7aPcy?@HgLiUb<<@q+*8KQr#COHUraEZcMevC30Y8ViG+c*Uqf_F=pPA z7AXs{xu_6P)_kMBWYuFWY>ieX*6t;3d}90f!r^OHsp?~Ednaz}j9y>7_3AD6CNHUh zJ9WiF>+asU;F6wYWWo>ZUc4dO;*VYir>dAb_xrU;yYOB3G8F$2hw!YJJdOAdkqVIv z2XfKi8MQFkHFzC>1nPwcW}mn5&XRU}cp5bukWbqf> z8ygXGm4Y8RHS@wmx+-J_X!o2Inm}&*7Zj58>Q{^EOcM63EXk+1YMr%D1$|5}{~7M5&v_ zVCd>nQF-W5ojIS+_q1?C@JmDx{GOiuJ+JnNJw2+HEWMe*n|d?48l)0_%(BJ49<*4f ztKty=kNtjo3+;LW0eUz-33}V^$R`qZ6%$JRP=J)m7{tOY<}gREWCbg^K|~}Vwuw!~ z6!BC#kmP3XBXFqj&RkF%_9io)T}-0GXh5?*GBXh;6f~iWY=iR*(|A7muNbG0Tp`8G zAosATSz4?bsJ$yGZ&+d@TFYHvVP&BA@{-#d;Y_F<@<91%Z9~`U0zRj$G}?t zhNhwFOmKO=TH2BER~A0nv3%W4#XtOF@ zykm8f)u3$~%Hq9W-n{+tR82DAY`wK%4zA^)c!ePmRACSk`b2uJEpMrUDvPG!%m=jAgFm9w`P@_?hhE(Us?wp~+|7RTtEJ!k0DjYjnu-U};0hBd>w-d% zGw?b373DYhDd}tTe&egc+;4y%>Bmr^E%5*r5@c*7wgT9Vg!Is z@&2e~!XjBLT6ldMWt|}7=YpVhfMGWx4@DsfEzWc-4uO0=lhn*TM=2bzXYkB{`zDbM z#wEKQq=d{d1tD;N2t+?d9MFXbf?6Gxd@l?xFMjfQ;f9ajJ@fPD`O7YCdEw6cg)_yw z_*FM_o!c!1KYVV7@X*X3mTwb&G-Le2y{Ml07{%mO>x44J++Vy8YgjQ2z$elXE4i)$ zww&lgiQM6HUt??iVa_A0;bO8!;5pBTXTRrwN5c7}pXdA*f1O|S1MV?az;EN_J%0Za zvope4E-YjHFbi*g{6Jia^OFglL6byg;8|<&&Kl2Xma=~MK z3vE|LV?N&^S9$S*`b+kf9T~6jmi;XUT0~AcwD1^NtvnAkuuP|MR~LRS*DSE_1>f91 z`{uc~xev%DA=k(6?>o>Zj9}z6{?=iwHQ)Dl|7zX4|Ej zI5avmF@#|Or8^=fWY4PH7an|S7u6P0G?F~o7H(SBOF@iiv}--i7&E%cuBsCfjA9KG zXYe6X%A(pw5lDCPYL4RBhkU+drJ7Eh&PsiT^bFk4gUKW-O(bWvvQl3tM95jKhqKzo zae5OOUFG-@vu}a+{Kw8Ja3~#fCKqXpbfYb#Bxo))s>p%VXyE8KD@x%)n~TUeKFDYI zHS?XwXxpVKacyU7jjGPnxM`Gmoc~l<{h=56%>}#X#-3>G^k1Nz9{I^2$ef&_FMu=w7|LMZd&1UgAKWMfO{W9`( zeMA}Jyk8niE0S^U_kMlB_X{h8HShiXrO%bWUs%Duf5rY42UdvleyQ~Rnc^P_4FPU= zkbVEhx#yJoU%+=A6PM5NY^Km}<^1Eq@w10FtqcvS@~7^zh?c z1m=(q5R3aY4}=Af>d5q|9=?Yd&Z&#$dwOh@;RtN?8m~^P@%d^L@P$+3L&(?{Yp4;k z8mejbsq>Y=6r8bsE)QDGf=&r{VFQI#LeLO%{d4`pf6BzdI2gUf~UOzm4Uf6EbWoIJpmc>VOSJ`Q}7wuWq@bwyy~X&*bLpkMbM1mt3Z4yura)ra2gwz5=^7zn#rX* zfYGS0xguS{XsqikLp0#`SdTJ@#^U15fEy{8Ia?fbM{-Uh>##AAO7U<X!^)@=&58z41J}HlkNkh)2Wzx+qVL?xrhTc{(Coj09G6 zB8q0XnA`%Z4@M6ahf+HX{8Ip)knWE#C4_&JxgS0W1wI1ld}7*D%^EWt#aBckU0wuP zh7Q%$d0ROqZoMD`%B$MovJ%#$72-B14$Jz9#AM>Z#EFE&jIC6bIg_#(o%<7sdYiLq zJ{X0DV)Pzacpu8D>+=jM(B9JC+S=Y;Jr{4N1`%|ZU7!*kWvq-9fWRZENmWz~FTnD( z6sw^(K?8pmoFXVVoD1Yst)OwJ;e@^msnXX*NR=ywfK;itVdg*4*5d|{!yLGE4Z1Dx zZA-V>f}5@;)Jkjb9BO6cHak!&q0jKPdoJIww`c-s01^q(AdF?G@}|8T>lYG&W&6iV zUJ@Z#Kq-oUb^q+vb3fr)WFv2vLt%8|c6##!DMy!gbKcIMz(gOp&(9^=_0Z`2j#9}V&&!AHsa+Glv%AOs8}24U2|8|048 zyn#AM8`uc%X`hb~1K>pHvmcp2eKz}(7$!)jH2xaV6?)*o5t;&7LB3E%lCTm{uE6~Q z^E4n@Fg|!M%7{|coUE(FP}J&?FuxClVI*1*pEKse1gHR29d(S`G*1sH`MQAWRBr_IEGx;qK>R=eBd` zrOC4*R}Ma$;NdCYG>zVk?1&wPBgcqGsn;f5O$Z4m7vpX_e7W;_CngMlaFCau@ZRSY zPk2vK%~_=p`D-YqN+O&}+Fk{IL-ME$rEsAHBM@+yDfK{*ibNUcamWEkg@bYr04bJp zp|ttTlq%^EP%%>>3c|vPS@H~FQ3hCCAU9y&Fu=w4Pdxk<16(`|yaj}Wr05J~Hz61P zyZGk0;(zd)kA4%1xoDVE6ZRN99{ z80k3P@oI-S+3{e9fTUkXuw%4iqC?d|t}*lY6i-j#M8XDSxlZoSN3kWT(UjQ6Y@i0Y zVWL57pzpQYocV@^WEjW+beqK1B$MHH!w14d3gA{o3Sih{h-=l4HL$oCJe)ZFa}{@3}P2!3`&7{eJB)T3|fuN>O$=HypPDR%5x4PnZ3lO z#F-_G0kI=f0Tygu32T7ZBeR$3C;tQHz=^h98iqL_1O}lFF5X{4Aq;o7cUKpJ3JT%i zOC=D3aL+x@{X4&8{#KVE3Bid)yVh4FB|s8TAnPPZLQV166DLmqlHhSqK2_XX{B=;! z&7%Xd${;!^&+p?h+#`y$3Jl!{nxTazFuN{Mdu!eI~Rpyh+ZYRo$*R9i+Oc$6jd&=Q(q+r()JC(zf0;y(~6KF(F?D+3hj7!aru z_3UJ|mH`R~XRs9f96$jCN&z=49kd9BUErG0CEDtgvzZ9E0m+3@eV}5>cm>eo#l?|j z^ulNMEGXX7e&9*|-x+q{?B?Phl;u+#s1lB0a+dq#%ny0Zfp6~sis6|{);>O;+(=79 zYE%9@g)GC1k8{l+H654ZPvzc0gA)=9PEb=W=keR)HoxB!iN|e`h>i0g$asFmY)BwA zVwEb;%{!b=ryZUU=S@-`csd?WGCLN%Pw04ra{w#uK>6XoVdREwMk|cTb%5AVez=G0 zDCdV6%%hzt=CkuM=PVaoMle7Oyhj3^DQqUa=PfK+a$J-}zc7h@@gl$cc_pX*!E0*- zk@D$__sxd3l+)?_uKBK(@NE2Z$gQux>1j%=?_EEaSl?C3t83;vUWkK0$5Di9Zp!B$si zb5;W?7&AjK4;_qP^b^u|u^8I2VeR$Ke08;8BaLVP-?x?*ds;A?Su!5Dy?M!avT){Q zo=?Bqplr)*c zlcyv4WEt~?f>BIWM>9U7m?5cJ1M4#?%Cf~sBG*W}fYVsS1QmYJ`Apk+YxZJ?c`koFE(k*Xz-LET`g+9*Q6()v0{hUs{PoQH=j+IFHhZY8 zA(hscCzo#C)~wg+)^%RHp&q&Hx+hm|U7*pLo-C!ZKYfAJ66f;TH9)7gai4q~Dbo?T zVis-g|i6hu;jOs%{Xeskus48c^I~tn{ zv|}wTp*c@91jWci65?}&S1)J2NtTl&qD5A+onVZy3%pNCg2S`C2-%7SpIoL9^LQbbo)>c~ znC2pw=>L6!oPG(7u9f&8CCN*9a)qZUObCWPpDhnKj&NeGdAafu2hUHHH(zYy zDw*?W&%@R866gEo<;|_@7k{{vH7_istoicd+4;0sN}(54js3QG>#D~m^GXK2-KevF z^@0R?YlXr2WtKj_^vYy7Rqbr~;MAov2NY7pE5f#*rSrM;0{00PKRCKKbzkZf<}D;vo3zV0?;;M((f+_th6uzF9adis!zw%!BRu&UirNRAQ zDCKx=QN09vSjPuFqvWOoyjRL*vYZq1%h~6@Ui$o2d>(mRH+qK7%i}WJhJEf(>2p`& zbFRk#xk7dm(WEpF1*m*8MTw{a;T7==DiD_QpO%f%a=trgw&i=5^C%HLC0mgnTF#*k zZ&`hyx}jm2!$B3e{^|yMH7XB25m-gD7hs z%-{C*CRtuel>$wQ#86T|=&H{85S`BlU7QY@X9SZ{zpyO#HCAHr|FHKS@Kx6L{`fP_ z-g_ozkK~*rCpkGI3-UmKgoM3U7=kDu(?t~#t$>3oTtx&+rR}{f*3lM15hZe(%rooRBaD+xxn&*Z<#A&rWil&;EYK`$MC-1ByW#4bL@g z#9;1oX9jcaFk>hIO(>RtXdsuKQ+DyJ5)dpAv+_bCw_>vn=5^8EO9Q*-)rQ2w;F+-v zboom^K)vwL4JY@iF0ec9mnL!tC+frVZZF~53ys66JoMsdh4l%6o1Ki6s>_EBX$~ak zVCDlDdld<8beoWSe9Bb~TD7I886&wJ!|THHFs^Hp59?rmoEuiZ3RR+rp|L7@O;y*O zL%EK!aZ_frI!r!`ue5#WRpMkwRc&Lw)K}_rLtNR@zbTwxA^@i5X*!Q9aT%piwIbI`I=w)gxuhA)0YMD}} z!8&n%fuNSDZ8Di!uRw`>fl#8^WR<*sP4_<9TP^ zsdOsik+uyrPdric_~UfDp>RUJ^zNE(KSoa;`*saYUJ(u1?|>#6<{?3|i`l{NQ1M)m z1Pp7#N7UGc4H6g`4W4NjC5H(;jgrObZy_iz!v$d_H6AN*D4ByE9wk2;EiGzZr6B~f zXZWiDLNCXs6sc35&v4S;V zc%J>vn>F_oK5k{l-%}&|&e57X|8|x9-8*aW6$|6R*|2yOn+44_ev4uo{=OQ$q+p~% z3G=PwW#GXA0bIv+U4=}p;8egbMMEWB1j!6F8!ki(j|OtYf{$vMidQR@_^@;d3_ioA zPMm!S%}sq*K|X8Fax)qeh&Zlj2+`3(PU1W}6->8Jby)D(%ts`n`hH@XBa}kA5YWVO}^{}CcNLpAI zw^P0cC-+)rw_u4ya*;M$Yd~g{ZH7CLxv|{j*}X#4eTaVzBXc$Ut4xw#j7o&t;lMe3 zYqvmKt8oy#LkPV}VI~t=M_5W*LTpH*X;zL^a!LUwQKeAo2N*j(rlH#G3Wyey-OonX z<4QY`J?g9nb{!s4K-h^9kCnJ?un9CMB0K*9W&tk%vjk1a6b*`?oMLWMG}U5aE{>@> zr0m45Nzh{;ZMdD0av?o*nUEPW5&8RsW)LfHU#sibw|@0wJtH$UZ6hTm+mJohM$ZukIDt-eS zVPrOR8%;)g1_FYG2I+i;SH&wbN_q)Bb|<^=3Q7b|bpfLdgErrVbU(FMAm!b+*H8sZ zgm@yo2q1VT(T@YxLUd|UctaN@zkksWa^s;*Sk^R;pm%nuWo6#rCr32k^5>o7pI7VjNC@xBLG#U&;B zS|lZ&fEB9bKV#OxV15UtG>j`$@u!edUoL2(>T(5EOjk2h@aa{ms2U$e_vr@44^`7e zQ^;`Y%LSzsASnFC?yLZ8KruyvFO@ya|AndMH-dqB{$tSeuOL<9el|s4RwZd763xHjI6@reB`o%DAsY|+ z;vtpTh{Q-rh>-=oprm9yg&xiv`>w-buqdZgHMCAGlj&@L##h>uT3b~pCR6gmo6ROq zJel>zT3t4WMefrp4n&89G){f0yd~$03{`u980XT2NBHwY6Lx#4(UvlqO9QD?CN|j_ zE3w!VUMumg@JDth1Lz#c-`5T>>eGMiio^)iM7)>Nt|T?2@bd)ojQEjY>PRpRhNt;` zNMA!ryJ#5_$t4G_G5nt|DwkyQ`sEU0NzeMk5+shECz+WUB$?vvq*O(Cd_`HPF;y9! zkW7T3kwxtqj;m{>T}CIxYq_vS(f7l5h1*_^|43m}MTIG9i}XGEl!M&~bts>r7!Gw9 zUm3?=!z>_l`6*>N{6ZP?xL`7vfNR~WXJvZc=7}>1nV;%9QUp*#%ZTkD|2M$JKy^hJwkcHP z{2s-AaN-f_SU%Zh^JI|kIVoUy70Rn1D0Q68oQU0!dDA^P7okVLk{eJSUPLdQOnBAQy+WWCrG;=*UFki~cO1x6xq&u+h1E9>k{y*}TKS?(vi&{b#jX z0^vl$pts8`mZV2#v8&{|k{D_%GNoccM|Do8k5~frX-_(!Rz-AuuRU3EZ_~3kHND@BBKi622KdifcmZpK-AO+p&3RHMy1YB z17Ww)hz3IB@V^VGCE~1S6fm_~lQm;f1hBM7f%twSbjZD*%@>`Hl>b3EBQ`RT#T3n> zpXkfc>dKCv)soANYs|Gbjq!RSK~If4q;L$rswp@6<{48l@17nT5U?!OhF92U^94dre(B(!G<@Kb08uB_(H_tCU%g0 zXIvD4BhGr zuZzJw8oIE~0Ds=py*B|Lsd2UdyJJH4giacc4>1 z8jkWa*_H$^msb{cmQ-XSy3SkMrf#`o$Bwyobw9|a zmv%0hGhx^^FA(uqwvsL58|B|&g3NsfpvNbLQj~o+7};JUJ6+4lOvqxX13(7EJ~xkt zfsuP2>86#yf?(m9Uc#wwiC|Ux@Ro_%r%WcB2!sr%mq+)?$71*UBe#u- z-SF1F$e@|?S~H=0=08}&wH1zqwTT%c^FzLDH`O#vW<8rfHq_XS8K>25@PGLZCz`Cu zC%@1Bir*%;V1h2p4DVuK!T0kobrS%eH~2IzO@$oyBZ9UXNAcI_P!h#OJ<>gg`o^?m z%+VbHdybZ5Ld3$V8E_9l2uwrq1dkv`>gb5Ij7*zQM9njez;z%$k zm5BVe3Hn_ntPud1B?g8G0xS2yZi@wQ{06dibZuZs=Sm#8h(DBF!6Dh>KH-A&LZ&?? z@sg8Z6^_ZdJYBXJtWpOmT`67>8MP>L?<9k4$h7bcRXI6dts8!QcH*5LlP|{IRCs32 z4}Lo4Us8IlFFdc}K~H6Snby7F>W;5XUqRQp6T1DF{180SBEhb)vTfQH!+{d zgyn|{|Gu$x(Z&ZpII-!jt6Ohouj_d370d}Oe(K*xVC(_;9PsQ#`OE0@`m*4b%gyqj zoG(-5`Fdq5->!Vj`l40IFzDlZr3={~IEE!qvFm#QD#U2;Y4KW2rho}UOI8nl ztH%Y_Gid0B@5O|aM!Y;~rcbWy1iv~dUv`lz#D$^Oii@=3x==pEHA10Yl&)w=COn$1 zbae4TxyK7zaXswn!nVS9N2IFRS-4i#EnO?=(I40feldI-?UP*G+s1!MbEBRI2VUy- z`BKOMAdGqu0hEDX)Gv>F6$<#0M+B=C_+@qVnG^%Yjm0lxg0QuZbiIb2Ok4tSYo!PP zMQhJU^gO3lieS>jC|pJ?D=8Z-J15(K%aBwJOScxc4NFG+Gqa2CIdt=^NSVdw>ai@W zXx~!BO)I=^oi_5=YS;3~y=*j7|G-b59^J$~x~kgJ1VvxZJjw4<)FBcwVyf+Sl)?2; z{c!!&df7F4R;5#7F`Q11=ZGji?PgzRzma>j4LL?LMPkXF#v_ zo!!W@>q>Dvzq41Ns5TsdQmr5C=us zFl!x{)!8>HpBp_YpT{WFsliLRXNHZc%eQyra%1EN@?*y4vh8iHf7>9xg8t2go+t7SKPSC8N5y?eY&oZZlv zGQjFTCM57+dH^>=>87GyF;BKcX*By1{;*!a?&L|W(}$|YEO8y?f6lDrUuTx! zehiA4z#_$uXz1r=(GAwg9P7e;D;;LT2>;EhKseJn$!8wnpKMeuo2#6sn59uL*Vb1g z8k=K^+C%t7%OJ)-XRhU62Wgg7FXNUiyYU8*G-NI@czA*k-7o=+U%vTdaix5w9z%9X@l zRq(??O%=YfxW95)ty*Xv7c0$G$?_(HQ)3DH{E?`|WGN}F%$1gn8dvg(H<*tQz3t&LCxGj#AzXYdt;-@JBuq2g1bY~3_0!&@ecAzSy~`1P0llTeCp!bAN?{yV0E`HrAb39ev9ZeyL)J{)?rJc95au*Gm-Wib3U1ej{MJjh1$ zIvvWA4|IE3Hp0Xy$}Hoz3)%>oe=ie>R8_)mP^5|VMtz|s^s!d7`8WwTQ$iajt21$? zn2n-$A$&45Qj8?51g8eL!6%+M;?Ha0P@rYwKgs8*pZqA~?zGcB632G!nIS$VcCdDRm; zK4MKbj)%@40_FC=Yq*$l=Bt9wp^PbKn5H_uYnM9!=jC8@0Ygbf7c!yupRhZBwpXq8 zKa0dEf^z8J&xl}x*X@WSgk=~DEzZ%&E#pF4LSGB#>3%kX_ zTK2Ohm|Zby1681`M8HlL9u-|BBJ&T(rkzSBYY~o94ulqh5vjyB12HJ?s|v(oAe49_ z#~~1!g@iNv)VOz-&mBB`R%s?wKe1x&y=_DGXp_rk&KO!--d0tT3y;_#`x+bg)f)$P z-*EjpgEJo7F#DGEtADMjShV8c@a%|aO~^l_j?gII0(HJ3{{p?$Ii3eVEdMh zm${G+Ej@_K7aL!t+-Q%x8o5w%L+|u57?N-?7C4|oy+)xVjH5UL!SBPS-vFvDfp-h@ z51~VrDUllurH0aKivFUgGwYxn* zKhai6v92gc5>fKGd@+(@SI3$@8`s{rwko@NtZV3$+8Z{_8$WAg=kmcThfU2jm9o*F z9^2sAFl6_%)(KPR#a+fhnMB&^!s4sC=J7Xu`QL{Pzet!5hF1WPYEZuL@JDD;)M# zIn()Ksw<7p5iamYOqG=Cs*+M&<#^qTYz~9$r;HHezR^Rgsdc{aeoB6!qC?3qACg~{ zPG==0zp9Y@GNIIe*S>t-WNIkpOA*`*OnxDlSP6B5u|oRmtR$GUzHfQ~OCkHSzNT}k z5jZHHSCOE~gm`%FcbykYoDHm9e1Iexe2L8(2$v^Sa>JP{np)PEMN@u!PI==SqpEXy zUC0`!ukn;xBEYDjw3@p^S)&c4%PSRDR9XLbrR@KEe_gDk$$t0wl{DG!{`R`OLaL-) zZY1zO!Y!VuFWewc^Y0_WQqEK|r@JF63x)$pd69?$R{TkH*qE`R95)fVk-LDD^`ab2 zMEvgVN`MlD;s17bjYYfXA^uGm@ZHeSa{jfwj2m{o7f^X6D9P_<0?0ToLn--ZqNT6k zZ(|Y&L+}d|i4dC8*PuTd`KvYiP>EFn==@%)u4yy$vs>^}596nfqE4sh?}n+kA3eC+ zyKz1+XOsu{Uour#uXzeJuMbasm~~|#nG*n@D1XWO|`SiIMx31R@}BH?Vo%NJaM}vrx_0ZNo@Mus+qUrF0ye5Lmpr++d`QKRX&q%_XI0kEG^jPB z0_lb@AwGQaLN z5U0>SC34D4oHBr%dSa4+Zs0d}cRQU1iykK>a0n?R;*ehCkX7W6vd9`0SkqVcb$~TC zi8VHx&P1%if)?Bf#$A^0S`J(I6rvRiL#fGoi3!?_@)o-^SE?LtsxO;)Qkq8YLEylUOotki1E9TZDs=j4@!H_qi6QF zT?U;_^)wPANWJlg_WFFDXZaTp)LsM4zKw}X(h-mQ;b7P74F-X|xJEEUu?kAnq~dHU z9m61B_vI`N+2A7SMDufSCMdUR=1wwXF4>M08}Kr-;+3CdJLbnwPXs-N~LXEEtY zqJcf0NMUCzc{&eRFrpMCpR3%O})+aBrs(y-g7JUW?;F8r6~ z23tq=&J9nz_)AORLyj$Htv_Y29v5zAZ*VHKe^Tny)@prgZvQu%@16S8u#JUpJT>8a zMIRummT{;Gl)_`bdjKab(fp$`{v>vAnlnx^2QIpqE_+|jxk&Ayp1Zq)L05?rIY6jA zY6#>AoT4Kr6&(Q*hCk|3YBWkPW)^3x?IQ`t`XEFCG4C0 zn^umn^t!M`Tijl0aJeD!xUur+6-4ZRbWa%m;5c*|z?f*4I!Ei9XvV3(pXV&6Oh-78 zRgyO#wiIg?@W{@Aeppuh>Alliz>@X7!Ur)cZ8e2rYa zcD2x2ck`sQxgZQwY{^p)b>S(>$>X9i?>hcL#T}?jk`4ZVEbw2uKu|O!i&Pgau^qJT zx5STe-!}DfJZjA+yO6X*f@L?F&Z5BxVw~Mw0<0`q+z7wBS1{sW5^|!W@mZwmG@9l5 z+o=%6e}AtacO2pmLM5I2QbbL+bzzmm0885iLyLmS13hEabI_HEO!eJ+k$`%Je+b|C zC^9*&^`?(tN)ZX6N{l7FV!g9CR847Ww5N2YQ+Pygb)H3Ymf!hm{kntg5iw5z8*otUvq7=G`~-wH%Bb;H`fx!{?F>>5Nq2f+c>FzBWB z3F+h=gPRg#7H=I_Hl)3VHH_%Ff&b6f7W|~pg`j64I6*N_k>cP$5*%QeayF_k$REdK z(GkJLyD;qWunGHUF)LK(W3#^975hV!%U&gSbypZ zDy4`r7FQy$6x%pKVf?d;28SvI_-AIje>rDz{qUx^^Zpn48{$!4ZGBx}x;bsI4QUvf znNoI#eC$;XlUnBuT{?`7whz6pvujOabVp)tYbsn)R#6d|X030(y1b#XEa(ZF(tq#- zTp_c1`0O!vtScauC{kCcl0VOkU@gMXMxw6A#J1KGGo#4dZ8J!i>9aIW#Bf2)neCzero8s$Z>*L!UyH)!gYNv%2h!5)KW@ZD%bKR(JW9$yilPJw1 zrOly{z)g&CAv30Cuq;@eL|*b4@F$PVj~X9Viyzi#4CtlVCYWiVaIM=dyGe6kJ{eeRvle#zbQITFvBK3C3*6p&4Ul-FZMpwVMt zTWhD*axQ0?GiS@ia^)|(U+}*ieKYz-?DsKkn`^%Pdizb~>&w=cZ+Gq1?02c1Hg*Fn zwO4GoY5qCre1`Ad8*yLC z;1m5}@bQzIgkUBUJ>TvNm$v&fG;rm1-$eNZvGIMN?JxEzi>KVq4~x%Etdv{=^3z|$ zC-8PyA7xf_7gyjProQpjEt5l&=P$qKiamw=2XBvUA6EZJsG+5)UE-utr9_TcoGWer zyZy!sQ8Pa(s^q$6JKtu7Gw1BDK>S8 z{~BW!Pp>)Fyx#O~>V(wZT#rN{j#MX_={$vl6~c0{HLhOlMiQw2Y`#T2T!~8`a(OD4*VeLevWoa870LMtUyk@1%5tV*v3%58ddrx|eSWtP z8`BwsXli7 z+);gs@lXh)O_Ym(}D=+8$Xsc-JhOLK38#nFYi zH}M{GZz47a=H9$s!;IqGTk_1@n;K&(PvOLP3f)BiS1?Qx1gh=}LVZVe?4)9Kf2>&D zPouiuAyxM~r0V_%yk3v$zCgA7X11AX`Aw+hw`a5MRLk!`Ex(GH$lrzEJE^Kl6IR6M zb@EtUjxR_vb@<{zai$J=OmU{p=QG4Ln?bku7|I05x;8k#wEp`jU%setMv293|m+?_aY=8lH$&P?5*R}q#eET$jO z`-Dk5scG))nLGZRZi3v!rf~SaQ7D?udlfk_GcOaz^CC7IMsqyUaGp{%TnMNw7@G6O zeA3tsbsSt|Y^U65*XU?`Px*`J(6G*~$6OE1?=?kB9Whr$t1sV@Fo%P-s({YrO{KE2 z!VTh_4>}U+Fy|BOYe*O+&iRyApdo>cxMF4<4fv5SKo81Nc8~Jss7s^HLOm$OX(+L? z1hal(wS{K<>S@vxdsoCsQ*mmOUag^5MHxqvo-0_o9+F5C&tY5 zmy(m-sGWgZu8N~o72ydL<)Lt@GBQ3{j#(9H zwrh`a8hVXtm_Hvt3Rtv81132y&|QR<+Y5FV^$0TRA;SCs^$>Z9*#HZQ8560Zl}U(P z)xpZdBngN~R))sttWpnA&+VA{lsN#42d-hYI&sYbgA+9xqr|lsxK;xJxNC1JRYN>G z*8qr(LznCt`nZ2O^z0^~%WMw%2B=;+;dlLQK}GhQH(aNj2H4MWiSp1_pnPIG5R`8~ zJJ0~_gT@K)6nm6EM%KHXnZgb`z=S1UU_4p$6iwtANQ;az2Q#VEKjvA!7rNlUJm@+a zS*D3l6q}wDGz>ll+H1AgjQ9zZ`BTBrR+S-kWHJ@aWi4$*cC?7>h=Lu>5<8j?iOSc2 z*DJ(XQX^xXG`_UMEh#RU?D1r>#}`fZc$y{c5NAod!iP|ik7_e;hc%7#evOzY<7wuJQ!w8Dtz*gfI6DqQbusxx?vqx{tkPOZ7M zvNWaPZ2^VmVsg>1w&T&#s<4HDX!0LwPE%uusZ}APGw3inoyp1NrFN$>Y}A^+eqo{M zP^K}Oi5@ar&7bUL2DQD^sm~X%DUFLoMUFG|l|6_XB`o8O3ul9N7dG8y{8(vwNtvpx} zt`9bazgMAnfqzl+vg@c;rb6butV213AA;uVl;90@SZdw1)*-GTf_bKPMLjAbe=jUfBbNq^W$fi$9D+}<8R0R7+1t2 zlPj;Tvtgb=SvbsUgN!D; zxcX}L>gCP1H*>~jKW0J3Hp`klBfafoQbyDP#?%ZSpB^zLb>zrzj~p@mw)2STbv#1y z=ap5a*Q6I;FTQ9xy0Y_?*E?T%zVr2!uhe#SnqKc*`P)@TOh-FAJ6~Z^N1B>`i~sQR zN5$9Z!$*Gm+Vf}L`0bIS_)SOgt0_Id@|Akib4N_C)Kn8_d*@30M=8KF|D%LpI2+Ce z<-u%_4V!CO8*)u~h$GgrvGiInTbsqJLA-}mc1i~uwgW9nd~4uj9Q`OhE)SY(?X@i0 z-0heUl2&of`$LSX>c$+`?Z_}@S*!RTp zHIJTp;ic!Fe@V9X?F9=Ke72TtDg5XEtsLvQmp#sU7yPXme_Quc?zw|Av4VUnz6W06 zZhh*=k*B_U%T&seg5dT%+MhW|nHWdG#R zKO>j_-^mio++LW-RV&)iHxF#$YmBVHphsp;ucz@mU}XFP-LJdQ#i2AQxoRb=B!D-X z#lt@MS~txSyHdCpZxE++vedOjjmTnIK?2~TjxDab;@aA4>f75J7G@VzU02r>Dyb_e ztgF0j%%k5q&dUFGY|o<;zm)jH`=9*j!ykNgTL(I@uEFi0iB`RRZEF>%x6NB8{K71#zDVxxodcI7Mt zO;td}yM-$`fS#9=LJ4NXKLs%su?Yi=Gbld*C+gE2wDjtV0NPB3MP9YlJ}tXd+* zI4Z%Ep^Nk1?Gki4bYWu6hCUOKUJc;YY9!m})#Pa!kU*v{jC=ll!9RE5HOu&=^A=2> zHh04Ky2;sbSbP)ZDpC=iu*-ue*`GaX2~1C( zz(4vOMIa5aWs80nx~#=ti92UK8dtDKit-)=%XJ0=3i&NezKH-Fgpaw=aNBeJFs8{q zJkz8JAg0vnLVYgESS%ighDi7TQe-cs1jIf%is$pxXG+OQphi*i>3~7GIG&1wt)hej zDv^qKbO^}vRNcc5iBF|x;uAUkM6s}^vqcBDL|Ur`|2$Kn_d0y3(vm8fN&%Q7Lm*UP z(Yaie1{rHnDcEg(qq<`JxVFHMkwe=@&j{B=GyWyrt8<}XeU9^n*IM&+wbzE`spS@f z@BYe`Q7QE;DottWc8BG&`$NMfO!Mf<6^Y2yaml+JxsipJhEG)KYRiWtEf&99p;h>8 zzVRK3T&UGqt@39+``6li^+97K-5T`GSk)Y?s&z;IG`!O0E6tS6=_oV~t53(+2EW&k zZK|p-jJT)6$8Pkdj8)eRDpU=fHtf0*!KHB6VL^&mj!cNzF6d+CyqTM6UTnV8&!bEG zIl&fi3aS)pARb)+9i0K4idSL<^N$F>4I|;Sic9$_a-9457xr3}1bWfsaFhbLf_w_i$$bB3pG9@AjVjmE&1@KzZ zK-fq4>2@8tLGbFTw-%YObn%$K4^@;GMK-V-eN6@Kye_-%oJ?FfB>N=)d02Ih-S z(AzS)r}_V46ljy&+7*z=(AWAEY@KJ=xy)&X8^?SPcRaJ7EhAsE3gvCFs~96}Xj4sg z!rMS!(KJu{v70*wOij`3+KCU6rVj|(0u^sD&kvP})1f#f;#-JCZmXn2L}t7vVvvx| zu&WYHO)i@+q#In<6x3^Os+iCe8d6mnHybJpRZWrR^0t7lqKa#q{@@zdx^aU(`2E-F z?EZ%q7ao0i>ni?*nlgh`uQeD(d(0*ckQh(@5FMsU(A|!H-p>vouP)LZp!`5<0^Li9 z?j=O`FcFJ4p?Jsz9Hms75o<6NME>$1kv|9s;&;)z{UkOF{2sGLM1D6aFm8Z+9oQFG z2i%os0Y_k$`UtSa_C28#zfo@y=B zc#y@^WN}oGI)rO$`3rM_Q*OF3)8N&ou4{05OB~gkW?h&KY;^A8PubFWIZw^s#*LZz zFF*R}_#v{EbC`fC%4|sg4N8s)pd8gqSf;1D;z>dde6I)hFDF<4l%5tG6_wZ}k!r+E zAO;*J#wCp`$l@hJlPXJc%ZL~u^0azfxoyo|lyIL+^QGKkM z6L52~nx|zBU1l3<$k2^w6u$0wmtVz{5QZtWz?$Rbw7JlQN`TyNWr3&jH2)TuD#wqZ zSd7te^o*FDnSi_+Jdq#4>KuBBbf~&ISV=lmNjemVP(X*GQ9w<61;yky1a)qC?)DsK z%(45~hV?5uu_53b1p*8=h8Y0LomxrOi1w5L$OgKEF(7!bB$NogGl_vTLN~(;4Cxc8 zlZc>)VUuFR2w5vaIwL&-^fVj~+}L^A#;(#Dwa#DKeDM;uNk2reI@N}>#bVKE=Gygx z&Q>|a{8FP5>D2A+SPwOv?$lIjQAEO#-8}&-CkGSJvl96Q_g3MQ}PuoDK%P<>f%AdyDac$?kyE zA%IYZIs|C#fLCnp8mN{46+K%m^P*bTfw5<)C4qbauQp9_Odmf_5Idor;(KHm&*mvv zNYc)W=_VL@DMqu$idwp)QPR?%YeXjdqZeTE09E~DY@e!D`9w~;4d-(j#$R!I8NXBB zPLL_+#T$2kF~E8P_7KerCj~#+-(FW8S8-~$gN?D%^w;XY#NDD-zF~aV_=%er=LXf7 z@w2$}=8OZH-eqxS1iDXF(mnQt#S@uEk_Jo_#xjkK(Lp2)gGd^(Bn^pSl_s}8Gpw>A zO43jcX{dlS7==Uv!(`0_zu_0LL$3qGER87vcS+EouD756VGrNz8|%X|EB?iOu9nK^ z5R!yy{81Amp_(M2y4sHsz&g0sw=lD0Q$Ec<0B309AL-qUlvvPc#A3W1V%Jxz$Z0SZ zEnX)OOcZC9R-R!q8p414@3^v>bK?3_S-&wk)e6V?mY^3H0VWy{M}LpS{_tFjEu#3*$>I#($y|fc7$Q%e>eKC_trm^7 z{PTEsK})^j68t*HQUgLrG}IU15O>c74K?`W0iTY6Pq5U?p9O8dWrnd9`9=34zbXcp z>Z*RzO#CXEW)x74bxTGZ{MskPVvOc6e*iL|y*x`y+UNDk&kcnx*X9yW&b7EOxAZd3 zg%@JV-*0UDLZZwi_%y)So{5W2?YKC%NPom0SlJv5BF|-K3yzX%grYHLzM-{YYW-gw z@@?)9lN|B|zY=t|vP{-xb7ub9$<832!Jy(E4Gsyq|0@h@S!N3HZVDOJDN}@LWLT$> zVI4E(r&!tu3L?d`u9 zx23&$@O(w(Jli9gL}T^H2SzjuJ9R0M;&u79#@6Jm35Qz2I;{EY=HHfWnZP>#W#OtH z`fmH-$U~(bv$ukGSvB&C$h@je!xvpQW6`B0&V0H~_NXk4lhJ5))d8jqUfqjO$V9UB zmGWUeN zAL6hvSX^E}a4XMX`rp8RfR>)#y>@#=A7Vg9wsIs1T^)puLi#!qy1KfwUtk5Hl01Sn zjkOiLDnf#Kdt@_Kl1Kbc!@CQN3Q-?^wMr#o0F1@q4PxkUVCrxuP;JkMA?BwjtprKy zl)!h%>I@9r@e~uy*xCv~MCMAIyS=CoN+X;U^(pK=j)Hf{-I;|cQfeq7%A7QOn?pnR z=Stk8*Hp$!Yfa`*u-@)$ z{gKN*e&^a-mT!0^ar@UsFS%xfM`uer8_UZlE@Wq4Qli93nF>UVTFBIY(`oy?u0$oR zz>`EHM52Ki!&oL1(nUx#0P*o8Q$nI)PTEK`k~S-_7M{R??`^caJpl`0CL|rRS$SB* z=@|e9)0MF4%82n!FrLByCo$gX^!t&WehVAzk94_0K9Bg>3?|IseoPpo zh*{!v5f?^u^U1T^{0YKOr4asNAzg85G*9#ApU0@PU}AiixOe}cv61L7Y# zl#)3;H@$Oi9_PXd8~UyuIys8*$h?8khz7~7a{>uEv|sXM=3ovWVd6A>1TE~#mWUG? zQnK%2zW>ZQs?W7w$bG{@*PmWhM5Br_&tSDv5o{_FiH1VqrvaBQV2ETOwv;5Y1E8h4 z!{HDT5eNCTOb89&G-n%9G8aRXieCg#DrWllTtunj-zlZ0&IoFG`{vBy=;K7ET{Ly{d>8Wn}K@1{APPr47*;=Cg60&Lz*lY#C6V`e`#zlhNCAma;AsFSlPQ3uV%^ z^<4|D?O$Nz*LSs(v`?iZyT zoSq2TA%Yc}0olP4MhZta3Qn#R!8uqKsqr^)K-w*sc-|d~+XbtGc9I(iON-zMn;l~v z93afG1{%RwcVHK0o8mNOXaqK^4B#jnhpN>Y!Hf!?its%mT6+@v3LNN){R&P^0RZ}| zl1JRHVXY$|Km}vKA;7U?v9uA%I2ELJJ7XazdYTqR?#g`v2VD`P!(5Mvft7@TLqQrE zsQ}#JK9yYQaeYQ$I25%>kV(?cM$>o7yNggApWV@PYkX8g?z$Nh9$oFK z`f_DMymIi;uUl6quWkucrHDh~JelG%aHx`LVxtm=tl-dM&tgCKlI~3%S3;9zXEBDL z=K2{EOjQ{}p?Fyt4V`I>A)C<{x)8IhJozu4FRKj2gHmU$VzSq9vZ{=4!(}u~6s5~vl)I7R=kBM9+gq*Xu>OG6Ada~)Oy)Wvy3-EHE!1Gb) z7Db;CxF^C5jPlEvz}ZYX$ghFM!@BDM3vN_JDe8z25?us>~e596}oL41+!wCXoMWQJ*;Hr7;3! zzbfj~r|(`8A5gUL60Knc0rf)`UW`fhgZ5VDz=di5`KC@SKz;O3A7hc;CeT}ZHoeKF znG}M>mPsktY6YuMs8v7)yq9RI&0uBW7W{-r^F4Mo;a8l&VZ_Ad0Lm64ygss?O;qau z&%l)w$@lyK#6S6jK2k01cM+gVA00p!;6%dqG_f&xS`ow}Eh-1%Oo@owL<{N&))Lq> z60D_MwEuKt5+e;WZJF8KjWq`fHFBH)Yy#{s`2;n;d!L|GhEO5x-Rr0b7D*KXseB-n z4)%isP!qQJAOc>4{Q)u(Q7m>O5!Gm{I-L9O?G6&Pf})7}@Ktp%Cgl*%yHR4iANii` z-BuD&D~V{?MMM-~SD%==J{%~eDE6N%sAn*m*nVLxVnPQ|CCvMHJFg!I)5N(y+q>be zOuDXqWL?6y>$}`hM>IRFEEv|wzdsPC>C+DaiHf@?%>Md~ktx&5=S^*G9s0YxaQ$`H zB4b&kJUD^GE@4)ZgerE4oNzL)(sa-HoFLfBeokDR130MwZc>k(0M;V?ZJ@vL!t@tK zRwIh6hOH)`8@&z-U+yIuYcp07S);^*J#PJlM0NnB2a2sk=QEOfhROq_mz^yqFC@o`U7#LL2Z)CI5g6*grq%&Ouazm{!1Y`63`zI9lHYo ze<+0H&|1Mf4jsDwOMy6sF96LaEh`aMmEefphs6i#^+kjt+zCwR=YTiDw&pJbw6Sn> z_4{isf!4^r_q#ifgLeHGjagRR-3P-+TY^m)9bdu|6r=0{K#XbvV$2|81-Qk>1pC<9 zIl!M;Q7ikRK#JY9=nl?e9BHkfS)RU~#^QA{F%^=(JUGPM1!#W4^e%;h7bbl_3}WB= z;6M@j5Rrw{FoS6D@?Z}|wp=mXVdj?OIJaCb%wgf}W9!}$Pb}9K(GA@Ub{k0uhAwPu zbxP+J_t{v5hXz6w0_oP_c4M{cT0xBtqRWCN^qvPzNKpHdD4&N+D7_^%G)ayMxh0ei zsgs3bX#yn7UlfS&{D%9Y@0M2LBwunUSlu+-{KDjPlbs2but$ijh!PBY%Pb>Ps4-wS%0U2M+4i5m9>9-x? z@@`lP_R>hk+>5K40EtoL`lTRhm*=q( zZc^#OQZ8%g@jP#M-Y6@j@&I|kb0{h3BJO~$8f}0nYa&aP0U2QlC)fnhw;c41fTTp< z2x8hpIKbf${sQ9~NZ)-76DpSx7H}Sw319)MRHh1FlW(MtPxwL(2i6?_y2~31nTXh4 z5c?9&Puw7OZrIVMix;AOp9>`FU$Gk`hOFVXT_NnR$evHDF4qqh&vdeCY-8h*ZNNgE z>8khD9LKRRTF;8FIZ!vpRXJ>Vjo8=jRN#n!e7J-s&8D6T*J@xFjXrP2dkM1&DS z&;C6i#J?hiE3F4`K|depRjK5HQk{|0?k{mA0QII)1(S-M;)+ikh@>V-R1+i|R&j?8 z_iUkYXgH}>$5i<^JIz_2n$&h@F3mUESU!Non$tJC4+qt2)YT#~fppg&wGULH;=o}yxiWdo5<7rt8S>i2JBw_Na&6|LaY z->$mUbt`^|>wZGy6=C+?#D5Q0y}b7v_~Xw5H`ZJf#kk}g6l1Uu?CXavrZlHm6+u!n zP8~idGUo<@6ki0G_@Y?E#L?oF;L5P>8PD#L;dpg8O20QVVa z6L;_Rc+iyhkjVJJIoB`N%=%5_ad}uS^fUatnb3e&2l5@=$9{o z*nEac^7}xg?~42vPOnv_v6*i$YZUxyy&~k+$3n6ldRFgpdo#R%<}^O4s6jJgv=NoU zeK_qm;ym;-47LDIgT^WpNS|2w-Y#sm_Djgu0e&fRcJK6Rc|KK9QG*)fHYSDA6#5(i z@PZt!4eKQV9G1!lp5gCBx9}E}IJXH7r74n%aNmo(7C9E-BN0PmBZF}PWL&o6&rsYa zz-kb@rxisKBq_QLlzPX?L*PIpPn;sM0@j&I%{X4s?yb(w+EX7&4QWUM0_3N7KuC$1 zLK?Q9qO{mKAuf#-fyu-kG(sXn?-{y(!{~bmhEQ%st3xpZBhD|2>pL;|iMH)r9uiCg zqYh_AoOY(C%Jj0?>l$Xx{I{)l6h8ZN;gdwwhNk(0{SueMY|K_w?s;$F^gaJ}-}SfuIPvZ6;}@;JcfsIWHn4`(V}>nhomCdK1(7|; z&osIfk!wvlhh;MJ|G?dx)!)xsknvhP`@^X_E zrHW)#42$!MoXu5%v&e&)OKD=pNUYtf(IBI{RnRlAfOaF#m&ansAaS%@;%GT>)JPmH zXUofLayh1^rs@z_Mht6YjKmxX(H!I!>mmJ|5P5V8Q&BJwO{Zu-y8p|M|uC4~XCZcAx0%-MTdv&@RNi!|bOg}W!=W`|j*+Ryw0;(D1-@z!|IT=qe z-_@uiHrCY*$`G3yB{q}tON!gbHa50Ykm4qxxMifcR#SDVn)_b$Yv?QItE)Y& zt-6*Lp{Tf&C>@1FD=DrYiW`FBVjRqfzQ-$6+~THhhzVo^yOA%c#r;LKV$x4614wn6 zGkL#0vHF5KuJ8>^6|$-;)p+?&&q^r%=I_&gMGYjdnnt7g%k-4~)IY(T?{$t7$i{7}eviF6J5dT#C5ogLv{Vd zo^{ClYn+&?{Tbj-zzsp2bVY0?nMsDmrmcc*myNYiMZsoMM-E9;$2K;#q&}n=*vum} zks>cm>!MFJiR1)v$pa6hYSI5H&3U4rG(0T^?*oV#FH%!c@#$|{C;Nnk?9|PhZ)h4} z8^*J;rka|G)7gd1fsw;jw2ax*+Bi}ocX9JzSOq6D=A{Yy9&&gJo-(9`~BQPY* zRYc6HK50I41Q4`FbdRvjnZp?lu`Q$0AUw5 zlbj{&6sMj!*k4fYF;dZgZtT+l@$~pbv_NqSWdZ(p#i}5mN{5dLUqBz^KLsoPJRw{M)Oz)q%SM4{7))F%e6AkP(4KC`lvwIcXI+8CJ=FjcKiJt~gx5 zVL}lq5@y2&Z#Aiio>E@}vMtzmCQFr+xl}fl4PcL?FW7WT`r>lw%Sc~{Z?%m69DPZ- zQ=~3uapzpsfmgX0XRbt(WDUfTUtftj6`64*Iwf-9aGzC@RV&uPCgqsty5%a&8ziYo zRgDJ@6DzIN5IuNjeJvmz zT(tHDw3l$E!3F$Z%z2R$!0UbLf@v}`Ug&B^zDt}TLeEqzF1AqFl(Yf{hY2OEqoyWf zxPx;Y0%gC~yOu{sSZwl|2B$AsC7T}{s&QybBYvz>%4Ek2lPYd%9e3Z>I?0T+Q(k-WL@*Ln#ziJpo)u=QD%wDg}Y(`TX7-rMP z70ubhSq=qL*u6@&Qc>TZ-r|s3r^I@OTZg~Ekxp=&%wLyQsk90kG16x9=1jQ&7clwF z=4R|B^ZNbq*J$(*9T<;vMI!Qap9VEC^2=hR80BM7uP-rV;O-+jU%<X2ttPo?)8;92N3Xbrt!Kw)Zm8}#_7N-ZVPki-tBpF% zeKQxXxvDYSP}1PF6i(I+-qn71{+=h$8Y^a%__t&apq95AO|k~9#bsG-*&31?*$XQ4ZcwEpp+ixCqz;Kb+=(XNgH~GVWXGyJDLa5&U@#njbCrq( zybJ{D%NI!#;S};al`)ZycAK1kYenJD-`lZh^q3jfl^T4d>Cj)kx%}$RWMXXRur)KU zXB|9OI6kp5Zqy<&m2n-N4G+CNa@f3x&th|@hNI+P+|nh}Aco&0_;?rlXBRutHN|_g zmzSyi28#eQY_Zq{WC!hH76oS1Zg+<@3~(0_vcoqT*13Y#IErU1*Nco3c_%Y4+G&^72d8!3p6jFYOrgn}TK^+QHj z2e6+-dxd++Ug^nR>B9w=HVZJ|pCKc84MMj*zvMs6 zvG7Wi&3EffVSg- z86VNwK^SqYhq*fwBlL(Nf&+(2GJo35@ZwqnnLmTUWHyIS13{+G{FRdVD|Lj({Doou zBrYNKOXiQt@@KfzH3v*0xwjwz#Zmh`$62&xrM%mXLdyd^7E7&4CJU2stVK$3JEJ<^ zID+mliUUFd>Qy`6RG!TqvYKL0a7C7r+qnAuTev$e-+andQIXub^_T5F$sjQl_qhThNT2j%Gw2JODRwanYJ^O zE<+Zw(FM0np{4DR>9nOxm!uu0w3I;RD+8T2mjBOrujDu@OJTn6_y0Aqb*1a8yPWr& z^PY3wbDs0&l&J3UOTRW;f)R-dFF=pl%oVPRt1Iq~Dl_AtmGFUZS(S%4*a;M?Y_okD zoqyrH0N&hhfhr}%iUIMi@-wJPC{}na7LpOlnLQ|&K=bjE(<@v`gC8$cgFryetb9)xiTp6Uu`U+pT>at= z;O@rZRpOS|3P8Wi>tQ7v3cdndhzy^{A--lk0LJa1PP6@_g2lx9Wo)=>a7kq)o) z`}vxEE%H@Q)Zu(}a29u;Mx%qAGipcI?PTn}3H{D~18!!2cmpM`+(n+d`^=hN!g8H# zPz_z2Yg4RozmO#50UZ!i@xo&&<&Fw`!pmm=Eq>ABasp0-R85s94Q+o=Qyp*@DN{RU znW6tK#)1HM+w)jinJZWWy6iNeKc#==*40j4YlX0|;gI;``UI zB&+F*vZXK@qGjKZ?nZRQhLmhXS2PtXgpPD+RZlgm)_kEfl7>Dj)Z;2vTN)ixCU;d< z^X=|fUAn#=#Xt{mXTK2|h_6|}h@DDoMEx{x8p{W&uvSa#&rMz)KueHal83&~9T!Z( zZM*|=9^y$KFkZ>3=Px+9_1oMMZq{MY$Sx_XA-e>Bs8GI*93s|s%cH-(CAV1p>8j;* z3;X6STJfv<(8IRYx9oz6D;g3Hgp%lXv{aeI=>6pnMT&j?&?h&3>6_JG9=o)@sn}v~ zOx=0MeRtoI`{NB4WxY;c3w({jIKtQb*FwBS++g@2#?^(?pXW4lMG-QrD`Mbhtd(QX z%AM(3${(nBM9m9LAvx=yR^o8G-5zs8P(vB=KIuuV6x23jt}u8=)j&lZee5Bu8loJ& zFl2>^+ahYfl7TN-<~AEK3*4_+Qoe@}7pBSF=QDl-ivSdLiNa@urL2zyFFYVR&+-kS zfsSyvvLoZ|guInc9pX(JXU2h6e)8U7fbgBLs;ei`HaFQEGZcs8!46&h;#T&)sbO)s zQsnku*}ZVx+_6QMKmJ@*t*g2r;&rAB3yId}F?Cs_J)Z5mV&Ue`?aY1RhrdrnTEb3W z6sK#bU1>`!B7L5FP5zxSfXbO9BEGFNwKHVcfC;T>s|&Sh5f_MlLMf9U zJB}Bq6#JI^JKdn77hs?|3iJo96dh^r!kQO}Q6__^@~3!3ROgIZ((Qi~+ryHjv2NxD@VF{pmm#PN+G}nXFv;_tW^ju(RKzuF0P8gu_69%Y)R3Leb zq=B$Ks@b6Z%D4)wU~xM;%{XaZNM#@|m6C^RL=W?MSjzJdBFef(bZkZ8ZUe;~tk%;F zRE0sS@}{i@pFzZ66RoY9G-6k!WEDFyRPim|FNTFs|bjz2F9(+j6nknqcEk^i&U&?E$e0d^(z z-U8eV)|ny=u)d=RF_*a&~G0Y=-*bc9hK4hr0sygMIaG?yd~5M=-Yes1gla6 z<-f8AP>6n#5ZGpbl9QyS#N%in6z8Y5q~r0nrsn>1PfuGq-S>p_8=*~lKbkKzK}Lee zG9YuYp$ftmF5H}^HA(95gOZ#1X-(tSNW0|X8^#KN%T(B-Y! zog!dJ!r7(lFuaFNhbruX?wmjt7>yfbkUW0MjPJ?MdRRr!6|8RGB_@zPP7wrxG<$yh zRwbDz-J%|#g$B%cyiq_+E3_nO(4ZdgY(-oSff&R$n<`i#e>Jaa0}@A8b>`P25TGd+ zr#}0!np8`PZOy~>>eZ=)r=vp@Tf+WZr#l=O^oIH7i5bDwoYs+;r?0v)jvy563&ZMlN?5Dg!=GP`kFFbm~*k zx_Qol)_txJn-+hodSsQF<+VHF@Bhk@ z^Vi(AuDrrfbHOK%yd1vh>55WY%pLSC9Ud;R=Dt7ixe!_Ic}im>euepvM;+N=$hbrJ z;7(b>%(YpwI_!2VW!Yr~tJR89PI6cb0weqbIiy9ns$!yYaj|Nb%cT_RW6O!gm7=RtFEx#&oYvBIC(}Cv#zpwsXphziaS2=ER zh$iS6gho7?)0_5qoS2Q^Q+jFI{w@q?+Us^Z$)a;Q5uyex3FWXds3@lsNJ_}p_Itg8 zYBE9R-HTK@`0V%YQ8iO_TIJv=5%qVRvxp2_D*(uWQj9nH?B02C1> z&>V+!LrqY2P@*WnwZJ!eDIXR;$m0V(3||!ye|zKJ-0y0-t0Ly8CGD?tRQVtJ{KBrv zWV)g5gA4k~KMUtMr29?(bsJNQR2^2 zz1dNjUR}Kbz>(D`%y|$jr3a<|8)nQnuswMuC)qHtW)QQ$G|1H=qbzSIzY+2Q@q;}4ePp-%eRJNZt*4jLO>}!dJP)$|3YLN*jnziA^WmOiY zR(J+)IA`AdpTB)^MN8KTyQw;_ndm9;)c-+GEo7&!mlw@r_!yviu= z_CM(dXvlrg9h4I?6N=DG8W7>lK&!8YM|$>zk3H*YeWRqh*L*zT18hX@Q4aDV5s@%4xf5$I6+>stN0!VKSZ-?hz>(`u zPGTBTtL4inayIedhcD?pP0G>@4a;r;_vGmYI{s&Yd&DN?x1gg{xN7$dxppCM-mHVr zl%SCN#446TraTlgvMWlca@$O_1pw_`IFy%1>?l(TmD~Jb^NNMyDtSDqSxzM^rr9;x zViO+oKW3u~TtX47{!@_!aOZfE7dS^Ohey3mjv`ha5cX=37RQj|Ik)t%vR4t^MXnNO zh4hsCQ-@gTvk-KQg-muV-*M=e(lN9`qS!TN8G??%==#X*GfIEZELMd0JZ4J*l>nuv zIOA9`#2=x_vV**ufUsgp2`eUVPZ7lOy~xErGgizg7%?;v5h@I?41gq20HdXqXEH#1 zP5^^hcNP#B@qstMe))SZ@pr$MID9DKsMMgyf)5#(EyB08vfAXl7{P|q^~YAUdDx&Q_pKw!t^ z63Ux_Rf-^|hakT1Lt0IJ4TTphLisc0uar-h%jM-3S{T!cvFK`nsJ2GVF8}mLaxdK2+#c|FV{wzQ;vcF%(Am(` zF_%Z=rkkV(byGMa8gA>}S$Ib!=~&4S9hE7WV%`&u4pkpap3 zbCl>&QVoW|7flzH)U>smNQJ{ev%wjk+xC&~ zKfj`-t;fB7$^4$?h(FnM@w}^>f0(;0YOfq@eBZLTw&3IHz00d%tjG>~Wo=EJTibFT zi~I{yO{EH}zvcZGFI~LpoE7mBfYZuoltE(;(FHl$Un`i1zK_%)T8cS)zP(?g4qX*a!b~C-6G`08@Q3@Uwtu zq4*ZC$bu9ggH53)AR`EK7b1m16tceu*i3+iM*a0uXlCgGN|Dpqm{%NF`jw5f9GSKD z&RVgyHuN|gVI$$PIsm3ac_BNSHRlsNtJsCC;wt>PoVh`8s)$4GJXRYCcAq1&@-!`S zOZci4l4UrlQFN%It=hSF0!uw_VelMp&ZbEBriez~?w~};W_kCmk@|}Q-iozX-u~H- zeeR#4$vfIE7}|W<4WGE_8yo7b$ktT#tysJ8b1YI~Hbi5spUF04`|6tRx$D7AAN_57 z+c#IPyrL$w#&42UZ_CyGg>5~M{ZHpM%J*^Jox?`X#=YeQ?(G}u9Yh3cP}Al-E`34h z1zn)gdwYSY`vsvNE{lm~zy7WLV*lG^bbvr%+B~3nteAB*Gx>i5lKN*4O8?pAN}+-kTH*ns(7E3NW4B?L6s;c0lL5gj8bU zbTWhB7SC&$${$b8DKdeOJN8ZpQXy#h_5>>T`tbe3_mA8&{B|Vkug~2!deb={9^DH5 zD9eq?4~lodYD%-FH;2ef9czHdoN7)sHa0cX*CWCI^E;Etrmst%7aF8LLVdnXTxHFg zOx8Cvq#D!ddW2J(k@9>En##kTx<;$G3(d=$%iav8a?44W%D#f8JQ-7&asj~)6k_1K zpz{L-WlqLc<~ABRmHZH5g13XHY^>p7YWd9oC+9A_=!((nFIYE_ChQ7r^ zR}Zb=v5D#P^EjvF!ngMeHhnDt5EmTlt&Hq1%B`>v%v&jcu~|^n^6;)PxM1HvkkSIs z0S(ngkL?uP#yqmvED&UP4Dk& zPngXCh$7rAr_rCOOXwF?uq!on5%pmz?Ps^vKIJ-4FD)iYA^(@QS}8g<4O7RaVd~h_ zk6n9$@$^%b1cT4JHLaj-P0PlpThrJ#;oqMFbm;}~j+ddCgmccEXtt;#DSNxky@$N7 zctx+*ij<>$(grMg!dVg8WVsEkf;3Zkd2jC+kr4ho!KYp>tiYNNdeEoG=(Ch~MJyazHo%9!o3fh<RZQ0ghm%4G+ zj(l}kN6l52EbM7cUQ}OZ3RhNE$E#h}H`Jv%M|yLAI(?rhWn^eh z^_XY#iyPOJB_lrft`&pV3uImO$ zHAmU84a1GK@uG5h$H`r(i1#Mt1K_<10KSZ}tmYh~(H-(GkuR-4YBM8eV%RRt&9fkF zISXcq1+&D0NG>?K3%Q^G?mbM~(L5iH5+Ba*CqC>aJ{$rc_JR-RgAditPMajzii>Rx z#l`EvWsd{rH7I=(?yIMj_|1uQ!%^^CC2ppl?r5~XZD61ci%5Qox+GK;X-AV<5Wzhy zusSdqI2e$HKto_*;F-Wtv~Biy(uszKrerdKSk2C+rZkS;4GS!tcG8m3Lh2_*HWQSs z4kbW*1rD3Q9=xO6?BlF8g+6-dFa~c6yhy4UA7}czVe+Mah}u8_Q`k2OcH*!O#3>wn z3`tyw1dG5JsL6o2_V-?ARaMc>7cc!iyKP=i zTi5aj@5_C6)7+1RemHk)d%xGZDP3ArtY+s;g-_2rQe|~>?ag0mPSmt_x7FO8n){Ip ze>S!3(sg&_{^z1qqULV2U79-g{P%ojeEYJ2gI~_Q^queg(?D8_oXbigQbJ#{gA zXuog~eCpCz?JQMA`e+yQ(FL%B_uwzkOZ1iy@Ui`~=BUZ6AOQRo1c1L{#lT7ez(>1` zuLz4JJ++qGt7;6j4T9|HqXQ>-ysWE>ltw`>_KykGB{H!svnyycA7rQcA$d1mz62I9+0 z^4hHjstp>AAoUBn>#S-Fk(ZNuxSUm;(ebqY3=99n{%CqV#yh9lpdF#&&eI`DRt z+|y@4{eK5E-2-Ro6@VGH5?XIDIGMEGa(1rf$SXYKQsh?^blz;mMwK|qIqV$pF!A!a z#LJavF*8ccJW9+wI=XO~M$EfR>uDqA?E>@mNMGL5y^wf(A@TTJ@VKvA^G4I%RA<%Q zO-k^gVphTg`vf5@3Ul28i_h<%i8M9jC5tHO;O`RDDa!Z%hGM3BJH~l5^5*wYc)Q8l*;3c{M1^ zL!&8OVSWwFq(NowMzb3C8T8>BSaq|s;p&1mq|Pw~ZRlIZbcG15jP%2_ZyuXB?|)x0 zLJd-2V*(R06dr87H?s5!>56D+aN6!yx}Wq8*nQ;`LU~t|qw_5M`nT)GsVC3EzN8zc zO~@M~f*#D9fv4-QM&TG(gKys$d;`9E>l$!s>?|DhHZd+mIQ768!}Q|Dj`zf`A}W zSp+XkkcDG@Y4Vt+2!9p)@+2Z=Z}v#63=Jup*#m|_u|j;6a`*C|e~Hikh0DF^Grb!} z=I>^a;VZbCKhNCV4#Pfte*N9KJ6v-vDDqWfEw1t3GuQl2!~fv(|9N+=`Q~Ytr^pWD zasVaGo4LCk#@7%Fw9Wdw5r3bb|NLII!}xFbjAd?oU&GVwF#Z6ahxz9Qe14|LfzNZ^ z%G1%EOyA5N5Eg?jujyCHy^{Y-QOWRK0=kL7qcV7BCFa7Pd z+u!Br|DGHPPcs+$zOom9KGnjU-cq4#dJ2ok8^PB`j^6K)o`FSm1)xYixs&JLd~m15 z;@&TPP$;M1=n^rn&U1c>6bh$McEXuD`Ei&-L})<13G~;tx&D!^2omcnbIVj2INmLd7?cg7qcT zc+gGVC?KZ_9w`#hW?$i%vDTK61(~){_Su%P(N_GzjuW25IesKB!#SKnZErD7@g<=c z2?<}qV#pP!qQ4Op5QhEwIpJ90w7gN-tP_{bde43|+Ljp^ZfzSKYt4+zZ)qEqe%v-X z+SWQcI(7H)tG-ze=I>JQe@_bjK1T1PFaG~SPG0_+yi82Z0 zGe6ktryLt<_(9z$HA;boKDk{WU)H8l(YUVns@XF(={vA1Rz+)4u7Kv}z!=G%XO8x29TqTE(~>9|?`?xwsTxP_(W^eeKA2VqAR{>Es;uueq60 zYAC6O5+ewp4%JEchx#PdCPB0CcF4D~WCtkS4n(Z#B2cS{O4dt_Md=b1FDkt-T6lI*dXgJ)Zl{tVHYqEvI$*9fP5;d8fCCK7% z$}#Ou!=na~zi5U-s5jSY6NF1Ksa2#~sQwvklnpiO2?JfNX{Zr73Qk!CN5Rp`+PVOP z(`jw;q4%J6u#}bJi&XDg+SY+4qX6OHtNj=lT72cXCL2-6JbQo=$QLCc(qTCgjE?}N zGx5w5jw5)wkz(9d$ro)PYpJzGzT34ReQnFyYoGnWwHr5m;Jde8OV^>BEK{hRcN^;d`1n+F(YP4;8ie7!)MCRZ?c# zVQq~!kah)Xj5R&2JwpRaiSx3`t{ok)S_>G64eEhU64PheixvRc?D<$l?!ezV_d?ufFlU7E}LH z@#~@MDlMLhfu7aBIyc&1e-8U;Q=+6uky)mM56ZZyVrlyYO3ljPqN%CZFM99N^HSPV zUlQ+qZ=^EJB9W|j)6`{O8ElK!JihGbV(mRaXT)wOHyOmRJszz|m3r1zRrd!fFe*Ke zoY$qNg-$l4c}oJGfEZAb*WRcid5R|t0wAsmK=A|^KdEK$rj2@VSA>04p-^+!*A@yz z!oD~Vu}k6(?-)_k)83Yd8>&2}hP6JkM`%`?#pZa$!gkh9G^O@9idwa2#YDyPtIHp*u@Z!Y+tiR$7R%WGRoNmB9Si2MP)e)ve7DhHi z?us0ZD8P}P+@cwRc+l^gQ<6WE`zz|(flN0fdz+=sS4u%M?qfwyc+%w9EbIe)iE z{1yh)SX5!MLF$tFs8)MfZs<~9r7^tazQ*KDpJVU4)LU5;nfl;eUh8%2RsDl3u&Emi zAIkm8NIjV^2WW$&Nvov8(oso~h-`sins1@VXsdpcb%Jat)sf{EqRJasyhNf;{^^Hr6fd>Wl*Cz}gn))EW zhqdS@&+p-=B%k&U0;jnH_I9rS*C$y0r}XO|n^b@H!OAX-tv2fzvl zM3q%yR>=p-9FksGlGpVLP8fX^~k)ALw#Od_`1NiR;Bj3EmKt7LQX} znxXWRPHBmS%Ad-8W9gMdXf`IWT&41Z>BB}pP`cJ?AyF~eY?A5X!+=qAojjJ0N>5-c z_lfsCX0fR6A;>JCCYyocTJ)aPo})mW@yHJz1h+%XF%#n#&87eeK#9uO1@T8NUSk6g zRE!~rd?u~eemU%VISu6nJM7n=8&q0~yX!6vHH5ljjn`kerMup+pfQ?Q+FchLwig$L zOd(%oZEKA;+)RA38+=j=D*1rMTRkMQVpn@-y4aQNNf(P2G4MzM zb+rb_*6M15W(3wovw{*XdSH5)I5L6frnxY2B!zS@4rB}~5n0$98yk@2ivL6bXg!+s zj8tzFB9Ii0W&C=w-)fQTMVqvYNf*>t6fsX@@1uE=RkmkeDt;zNT=wO+nxPQ+&$ zU+8j2EOpFqpf}-HiyD^=8s(tZ-Plb}^RLi5%MJg5V$(a&Zb2#&ROUkj&w*N&GR<4@ z39Eq-GSw=03$GZIRfhF&AS5^nPDrkJpJo-lE!XK#6+kr6Jr!$11{0~JuJoTvv}6Wg z6OecmG}9KH$I8=Ak3ZzE zGwizMz26N~$BH`S8^){co`Fm2Dq@D;*h|;lazQM8kI-a!s_pY0V65j z7IKEgf7u7Xl>&{Y zGgM;?jX(Ym4L^Q5Wmx=)fdTMe?vmVs>0Q(JBkH?ZE9V$!;5Z=H8Rn_Nb~0x`-<}?R z>tqx2&karbwmzwELs5O}_VKM|gUJP6R;7dJiX$;X3fs@hg<=#jPHZgDJOQg`0uzC; zDbHun{I3pOc+ueCg%@6O;SgR22QR`|gikY7dQcewPDc_o`;VgNG8uHr;ks0m01dPn zn2ik$mSR&;k;zmS1u}oY!78JoTQkGNbgy!YZnr-i<@0nopQi(|-(Tf+$I7beT&Cnc z_BqWeHx-Nckpi-uPNiaxv(F*a55`i{WzYf$ZZxO5oT%$StLUz-=Q)*WPA3%sYL6$- zB?&vhQC;xwmnqlk1x0CqvNBs_9(leOzQvaew zin%xU($W>z&+WXeXWp{m7yrQIvbikyo%H;6k2;`wx?6Kj$tWM}n0j(pt&^QCmtMMI z#m$FS#uxo_N8B9u<_6cSXqos)yJdCm*^UlSaqjeYls(F2i0xMa2krvRv%cox#KU#_ zUHhw)XzHZ_aq|HC#Q+;9QXT0SI)(Uj4ee8OzJY#WJT3l6%p*YR|=l4qt-Y3`&mEUB700|W&YNN zR;_hI>(*AK^>H={;06d2yuGcBcQtNrlo~G{a#p$}R?%C+yh151>V>^qM6Ya(P>K;9 zgp2;X`n>uQ28LX=BNOBKm6;5$pf$s9qgV;|?lD)?L;@_tqzt$EXwda4FnD^90Ez@~ z+b2JjK!rbGEE%lM^#w*tip$Mqo@N_sl)ikd`*!KTg-cz@o~DtG+7-Lb?`*yBmh)FX zaG*Zms0ajlqOlg`vZ?R%+p19_<>+IV_LLayzH9z6u6+KlPgU6+(R6M5Sm|R8_1)*D zd)~ipWZ`mmw78<8raiF@T;j@eiG-@!VfMA%9YrW7?4`mSgiZSQP5;X-PhGk%zs2Y8 z5o88Fe^0B&8=-!4-TsDe^~>w650^h|-w%+i$9litXN)>usykXQeLwXJbYLO|1Hf&b z!B8J3Qm<FM$`fJJDB(B3bjM!KmHlgH?Lc}-V_f_8#RFP% z|Bid-L~RB!sa*DExR2dq`}qaE!_9-|Se|ZgnL9Lp^{2{0W{KvgG1)i$du1K?6W;ba z>I1sS2UOPSKH$_HxK7Qxa-HW+xXwOYXRL6YcQkr1wU43)^gv`=9+Z8#OW&3296LDc zI=Kow&o7lt|Bv|2H=f4nlKdO^r!#DyMF_p@l$W%_NS-+`bXthZX>)Ur+n>n zCD2Ar@|SSL=W#?_KO*^t2N$Np39o4!(6L01v%xvL6Sxj*lfO-~=Y zMDEKS(T|w^)M=0Kj zC-UDy|2{nNFR`aK|ETs~fxyGr@7pcbz4j%liLM61-}(JJ`J zx5<5iH9sDEPe;qY{slCclc&MmM3t${pze3{J<}@R^8?uauKb?0v?q5C_B@0=74nvs zi2hq}mZ$Y2a!&r8uiz~IlmE^T-?I{X9?2iWGlVe}EXZ}{_uNLu9Q!l&4CeQIg70|{ zXZ~Y;PaBQVu@wCJ_1v$&iqK&XBIv|j`=f~Gkr#5ZW8ZXwV}A%P3w-;6ThHE%Q-5S) z#;Kop1FwGBTkz`fynaa3!+EJ3J5{0(!TtfB*8KnI(@y>AEj{gD_u^@@dHwOb(><*a z7X&c`a1K@Yj8<{TcGxCb6*$VOPf-o3YA}a=!0CPYu^?2yOA=j9%%D|$BhfKyh;_qel3MY(qGSJIVcb{Q9ll?hkw%VTTlOCR` zTGJ|r!(!06MhxO@1iA5QZh^90xlD`#>Qi7wc?+N%1;M^syeqja39dcxSTbgEXjl`^ z;AlvS^lRxmIGS8<94$hp2{x@nv<~@(L}5s4Z7LiK301Zm{WE9YBpYz7c@OZsV^$SM zWgu6=%IQBzzg9NQobhJKkE7z6M-+P1UjDM7cWdvq-tD~xp;xO4Ya;92FLJ$2Frsu< z`jrCum4q7kI;sf4-A%~bajxVJiJGHmDxi~u`ua#z6SpO{C&UEpEGA^`L=C^6#6%{M z;7>{SWTf-qDY3HtGvSN;eD9M`+aZkZkmk@=ZG^}$=dL;1=12mtlQa!bNi>|{G;IMZ zQjr!k6?=g}{?cQYB}A5eobH=7-YmdK#}H=la<=}_sh_dLdOnifG*?)p25}urVTK> z3Ip@cJ_um#S>r$)bmBN{mNDW23d<#7`Re{|?QIaA^WK^G<{IUjIDbSdm4w!Tv)}4* z9ya{PDffA;^n09Vp;jW@u!sqZ2H)UTHS1*wX^}QCkbYtjTXX`uVD%xBXBnO9B(;Xc zr*OP3y~*9uU72kel3uMOlY;o3h2xLQwerbRo%I(f7UcehT4!pO*3Z&25TXSzdOmU^@@>EHr(*Me;e z#H|aqFPK~)Euiz40+~-(u;lEwJF#&VJ&#LN;f^;;M{&n|oL66Z);m4hIO}p#w?jg_ zC4XPNqlxA-kMeOTpCvtod(x$c-_!DuK}h)-hnL?Imx!e^5>XgkT_Wlr5k1>_xI~zG z+DRhL$a!=&KF=xdnUROWJ?Zkm`EIdqu_!Dawrl0(FI>#-^W_ z#`&D8pX+9+0_O_t6j@}Sc>m5?F+$^00yf_sVL~LV3iu|bA|XNZ2fv2lmxcQko@F<) z8-)tZgp`?Px(MsZuV!Hi$MO2rR^oVmHQ`y|9z@-7yeK4tIJL-&v-h2uM>C?3Q8Vi^lNnja)V}4X0UvWoB+bER`4c5&3)dj+TCphf zb&(2rYZ9VA3+Wp^X44gbeSs) zI*R*KTu1$sA9b@xHePH8qh+pL5KO+@RYC@;!(IXf#T+vtsLMF?s`xV3tNL}eh+Fg5 zsSTN`X0C&yLduokI@r?OV?p&X_d=q6r=@VzLd+gVxxR1GkKz_4`6{uX(O5VNu^eeH z*U{(X&-HTtT!~0gQQ;`$J}XE0^Xuo@B!}}y1%i;+yx)VoU1=|We*Ij!-{XUhj_Nu( zN>W#hR+~aJ!oOC~&-(dEf8zY4Kgna_-VXH{3GR?6v{aI7v^J`*?lzaf)-=HG% z`jKCq>qlIj>&J)rIo>C|j?Z;yN~P94S|@6C>+4>r!%|9JBmyNE5sEH@Z528`5W4?$ zraM|EZ^PN#uv@e!6QZ#QZl-W2(4m}L&U7amWSX372Srp+~=EVEZ?I8_%GSzZJizu|HEel)Lw>;GHXp2E;i66fy zsfGIxEv)7ESm_Q#Vw`&qoZGoxIgx|5aISZd-;=5Z+s`7AK32s4TxXnkXIn&)gKCVk z&{PLOI31_tX=7MntP-R06~t}A$JqDTKM0kYMMUF6Slpy;Slf(o!F_v-iu{OjExz*} z{X4?ZevkuKIfb1_{{>sJHk=+7l@l9Tu*X^-J^j@xef`4Q>RpF}<`@mXEsIgfIQ z&(cGYQ>9yE{QP8*<>~e;`=M@;i9)~rDE%{!LUh)z1dA+BvuB0JguF#IM}LAjGf%+B zCAWa8H45jKbv~f`?`fCi8I6V62$cqfvm*9W(0={hCUl$8Y0&^p`US3)5fdeS%jZVY z{%uV#Px43g;9QN;!;rP*`J?=tVoa!uI=qFWkTAPAc`LvLLJ@?3JxrSHviDM@=$D}~Lh_B8aY?vWSv zupZ7xnA?P&9=Jt}WoL#VFPSIbLqEF{_fV$+@p&%0YwotWugoQY(mXrPom&mNmkrO{ zOgt?p*sL=1;3Oc^+D6Ls)S;K%fAPNH`iXN}!T+PqT zy*YkvcyqTE=-Qd0le5kf=&E0Zyg7aqcys@x>kcM#H`0k`X+NT=%I`ga<6-4MrypXU zVfS*Ku94Z1Ktu=A!OSJ}XcLe5=A%vY9g44#rq!7e0db+bpGSI6*0iwjVO!GG>h!Cj zdV^n$ZSXU}A0_*e`Dd<1bHViQ@trI9tVNclK{)hph#c);0ON*MC`;3JC(8|h=&G16`7=I+t8x$5N4A;Bd^4A%K=3b$GKV)U{`%y~7-{Rcp?JEHKIEHNqXjZ>PVBr~bL{Th@YJ^-HC!hWy2>mTJUHv<@$bk!Q77Je}Q& zI+m=4TBz*S(o(#Xl;EYfIQ>d?IxAYUtFjMe4`t48_0&sHIf<f|oY=x?)0(G^Q}~$h8jA@pYm20AE!zu> z)KoKGe06wbkpvw?n>+{gT5vLbrXTQw<4hUO80cPnCc$u77ukZ+OF@G zy7Oe&TMLS>S(7Gvf-)cZwl1Y_{h&HMdkLnY%53&sAQ~stsf0T%SC!7D+}xH^xayfx zxattNs!R9@xT;gj_NP0wR0Cf8b$9_obrR7!iD;b~md`qgXq`l~PR>=GEY&&IxvKM_ z&O@Eb4SXZ{I%(hfPPwzdRSs}f=l0IYPD$%TkgoS#pb_lwH^o`j+4LDqokE{eIjdiI zhQ(O%yT(~v#93X$StFzRwr{S!?ds9D?H&3S9LJCI#Q7G+Mn$@^tLt5$W}Jq84ky3y|K$t!R$t6YoSR z&J*G@qCy2aDSUheMLLGQ^-%cOPxU&oUb?d$e|tqV6p4aqpDbt=ZI!%U77xixo@DBN zT&x0#wUxgdM_=BqEkeq50dxv}>Ba^0v{`^c%PC}Smj|da4tbP$R1Ia;``2?e_A*T9 zCi=~Nvndox(>udIY|T9vZfxX#2sJf@aKNe0{Kwq_)069`H51-QMb))wiyQa4Q!|$D zlO=7wC~cL92oe!NA|gnf2-2OX{CYxqMpCFAEQL3RB&Bb?#C+IG#bL5uC$=63z9Jcl zkT4DI*iCCF$a;{)olK*YcJyj3N&8s?+s-DLd;{#-huAah5K|bsXc5gyX3?xbGM!>b zNCv4G(P`>eKS~H&Qmam)-;a8_Di;s|sUET17;2pj7BZWNPd>S@i8)TCqA~E*ubxWA zmtGE)VS>^ub#C)C8fHKGwQc}?9(WGhl!N(==B3KHc(HYy4va!V4D z>+#Wo$ro?~k`pYe6EtkANHgNxa1EFiU5Yn|TSZY2RdKsGDIOG+ezDw=r>t?kQ9Ohs z=E-tmMtACnPFeK;XF?+Fk2ryU1Nwy)ei^vG?iPEYNH3(rPxGs-HfglDN=+O$?&?oL}6UQqh_=f}Xf#X93k zoN>=7v2L}}7+8sMt;D!i&bZb)t&dutus&l|tX7{jg*S(+U|ghOVDBO8D^{8Iud=Sk zfv;G>xK8Fgy?PGVA*QfX4!5zXi>&2tI!P7qGBL8p9<>*@eoq8Cr{WzEwZAjSg}y4 zR*IFChH|st<+pkLA;t``pf@C{qI(Vyq#1mlxalRO0_ zL`4Aro^a?zzQ=ROQ7^!Y!BH_F|1?s&p=Dd{B^Jy5+~d12G}Rql<*Ph!z+JsEA%8{s zqcFFHNdB)XpC#5oo5q z`dyXND3Lf$q$sryrpk)r2|FNAMlXn#u8%kuPVE2Dk5YrV60O2^#~ro`O==N$Z5Yfw zb#CqlLmgryB8RGmSnAw^1C@S-)5VwDWc z)hF!r^$BlvwHB#KSMQTLwDMrY9t=j#U9u#0$z}FSF1gI0P}$Nh{2lFWI<)Z(>+Bmg ztgF9d?YY?{U6&;cYh&*j4Mw~h))@?@O$+>e{yD^E<)ghZ_noFkO;4b{$uy_@!lt#! zrsRrtY2J``j##af`#0Ik?A~(5%8L>%G>c2bJk@EWC^M8sfq#4Gpt&FiYxi9cBPi*@1#Wt~Ab zZyH;})~vbUf`gmcX6Ds=n+|OfH*IpwvD$rh(H`t`WzEHNg)$XgY=aAY=0o6ib3=pH zAT~5)G6((4?|;AgWNHGt*DyvbC{79VyPfU`aOrn3~WNMNGXX4=0BqW9l4Vv||w^J#{FRyzz%~e+{bcUd5d9u9=#Dhp(*DZL7}xS8m0` zqW1Qsp23YBQKhbbL7igS*gdRuE#I>!ToX=YZcRkiiMuC4i52dau+JB$OwQFVT+!Jx zuls$)O8rQG+*s4GrNe%~raEtYL2lCzE?6U03|U=LZg|)hsZ=A8jQ1S&4d$?RP5mNr z!^VlvMV3bbYb~GtL*IwycP~vh{?qV%cP3icXD<(UBM~%`eNS$UWGHQ}zve^T?`f)Q zNH6am{!rf^K3;x_KX$J7D?eT64wDWknKmiklJ*L##2a=mBh(Z>CYN2*-C>kfJOdDJk-C3>rUs??lL z4+VnuKp=?z^=fs16b8B`mS8Kjqkyi;?)6ro+uqR9CHAFDmr%odT&)-fNOZJBN8NY+ zdG_O&`?SfnrkS@Y>}vr}@09 z^Tc1A$Ic_Dp7R>;5a&zhq0Qr}^^0y;BraNXc>dA(;{5s63w;+Jx=^}sA1l(@nNLft zdT5onYE@a6-`oI63>iC9B|Rl#NvN$XUfi29K4cV)#?{dcXm%4lSjXz>tWM@!tv(6) zgand+7Nm1(5|XOoR~OLV5r#=#LID@UWt27$PA;KbJf|j4kk0YR)Ht8m#wT_d#Qw$0 zA2T#y#t;(c9nnAU7y70TQ=AC9AlB;hM`lFWP39?t+u5!Q?FmxLxk4=fU*5Q&AT2Jek`Uca%A93@4&XTk2vVw_Lm|Q(d`g&N=B| zAYNX7{w3`Ben;urn60kwgF~yKsaG@Riq$5XS1yQO>rm~9*Ir|HwzAyM`s<4zb20BA zo+vjFDYL|xah}Iz>Hhf5+V!>J)}a-H^=q#4_P_o?8S_=7YUHH_;Tyhn{$M6jdrRv_ zKi*>LSn}U-3)XTVk@4In<*S&zhFFtkZtI!bBgTs`10W{&nr2a5>6)%*+h1sZy%?Tlow*{(eh%hbeTv5roAM@OeDiINS!_5Ho}{{G&YSZ9aamJRmz%2Lr# z!0+%^75nXeYcTd`(Gx{tQBjo@4H(mH>9%^wTj{M58I!C-7S(GlTHbM4$0Hq5M`x@? zZYk;Y^mg@*^ls|Eso&5q^~!SbP)kc=Bl8Xo&GlG<#Z^{_ytUX@EEZ=4JHyXHq<|B! z4veTNV$n(UHDXfKV-nmHF(iJ;mI6yPrD&?4PRC$Vf>@b7P3X*(7%!d0N963#q(BfX zEF=bICIy-jz&Lu<9;_{3@<7#dCb5yWKa-}fXjZ-&9?5+v_lG)b^}L4g6;s)XaIMRK zxmWzQbIriD<)gLXemAM_^SXLQx;7Te@kPBsYq3U}9f?94&FYg+@sqe)PL@!8Ho?H6e*D|fd19Q-k z_U`V^=B7X>BQ;gGan0Gr+S;l+yA3_wur2H?BAr=H3e#Xf z;5VrzMbzi2fTl_|MUCzXpA%KfN=Xl?N>ipe6p$Nyoy^=>)%k(Wk95j6b$_Pw%bj2E z{AK4$o&TrX2tGZm1<^yhTj?~&MLi8#1@Lvixv1@p$C+e&U5-J1Z`M{U)qh)Ne>>#Wa`VOp5zJ>KkTe3dOMzy^4Nz8Q& z%mpq^AOxfzim_gE8U;W(~2WO7@5=F+Pjgh|v%S6(*pr zU?4Cm37*YnC#a$Vi_OwTGuDLs!KitB@y)pxyUx#r`5oFmtWsJ zR-2sPRukK_==O^&tu^+b@47oTVu1vD1uGP!SBBXwnks5l-gI04+i1w#q(P zgi26_s7h0oCsEgvmAm>vN&}kuDKV@c&lyxixp+9}ukd?JP(x}c_Gs}F&=$oWRRC{x zwRJU0)m2`P$kgG|GOt<`QDQNXP^S>^&eFDrUY2Js%RZ8ovORSPxuayzGZ>rGH8?W3 zY0gb^jC0taWE$@1$Yh3xN4%v$lSd6Yg1TRm6&wsd3mwd{g~~clR7xl4B=8QvpWvl$w#)5La}c*R*eS~q6Hno}e|HOgHSoYWO4Xa7vinH6ZJ*+FJs zewse54!tpV@7`g@NgA{vFkBlsvkDzyb+=3hPp?4@uRIf3dlns<$Zay%5obb;xK@1| z3?wEGE6h9GVQFYAO(m=SULX?2G@H2x$O}joZT0jBF*UX=CMEse*go;Oy}>@ew<8$j z>|2iGWDUoV&U{psO8j0K$Lt9@=?u=9GdNY}Hm79ud!2NKkk9X}35Ix1EuEpApP}8? z{%E_@==Zkc7;R~&Ar-TfT1$PUJ*85qv!lH|7ON3um&;kyD3qvhFS32seuke_KxWt? zjI2iRe`Mjvwvp{4@<@^*f>?L-A=rzbh1INj%lz)muEZ$33)EXJK*7}l6kw6+ME8MGW~VdTS-;Aa1efu2spY42t3?oL&%5QWf}Cy(J>Q`$IB=b zgeVcW-^f=FaJ0PRduU(&I|h4XB=^xnbsdpPt2q~ti|c}I^;4bomsgu5Q#{a7FMhZF za-W$Ul1zy}N1b>;KT;~L3$@je)fRDB1Mao#3%QkxY|Ump|4AOCA&ZD&G zUi(-FQZ%Ekxy+q=c}^8_Eo0Sl#q5X|p-(YdIr#ePb6j|P-PBh{sv;s*+<8t1a!)GI zd67n-JU1-8j1h=2EJ^??W(<9;3f?W*IUUapM zi%x*fB;d_M^}aMU{xVc(f-DHUPT*DOdHx6m&S*r)Pnx(@QFVK?$fa^`nl+(`F|6IZ zyB?&Lx$w=QW+@ORamgr{7I3el~lhtr+1Il0QxcTy^Zf zk~UZH3pXb&VNKGFMOyjVsjuW7TS_JRFEJ%UK~Ku;YJ?Qu)HFInfW3&iUaNyQ$p2i^5oq~s5${!#v1{T_K=U{C$N z)b`dF>WkZhLv>o~rLl|auBzKye@#PCL<-B{RJ1voZV{TKmQ0bOJ`vc^DiwQTK7-Zg z^NBv6-S71mDM&Q;W~%(&@{wQyI5hP!Z;i?CwT*;SFhd~XjjC{QQ_bFVebNd^>Zx0c;BRo}mfmnT*CnV&m^wM0-=GI8ebP7T57i%%U75l;`MMt2wqllzubobb1dOd03s%Jf^b zZ&>YKCVtp^#lru7_54q>2bG%Q>d?1yj)9SVTn$Ea^|^1PL>#=;J_;DxOz@Mr9(Lb- z$^IhZje>+Wu();+D@~QfN=u7vvqd#oZ%+pNG%hEKs%?EzoR@qgxj#vwS|6|_jYYM2 zQ8g6%#IPk?#6`8frCz+P{>l1h>#5u|FRJyi1o1o>5nuD-=!ygm8l!-$yputnsc9FYfq7AF7NDuSC7l{tHaLI3v@)6eI6?=MPzC}te-4Lwxo@uUSUgdDr ztsVcD*yHiX+xB)I)ynJ^d!;)r6$eeV z)(&5-ucIn!VBRcDsCaEhqO!_kPE?k7%x#wT+Kx7hY*xIoqq4&5xGTtlwIOe?xh?H& z&UR$IG6G6=OS#t`@c6xfrbMH+DPC2Aco=^TINB_hSY-kzSBZ|=kD6I+)?>DNaN;FgIn*g>|o%!PT+^f}+tSedjE^D(b+mf|dUSjLUiJgrkW(x@r656ms7Fw24 z!0rNt(vrL`Y-PqNrG+l@65?#oLR_G1C3PMJN`a6JWjeH-OwtaegcAAx`_7de2ihr= zFz?O$1J=D)y7%03zVmJ8JKutT0u&|2Yjkkxo^|F@#o?E|*0PCzG}P9WyCQZE5*Fd# zGO~d|Nb@Z;iF10sIYPQLJBI4rbzlZZCAXe2vZ!kBhwbZ2_g$l}?Rac_`3g_(kv}cV z&%T0LD)*dX$t)7TS%1F1|K9bhX9RoLf1KCvJHIl)F7NmB%;ox%vD;=1p3D+QE^X@b zz(!$-wSI-oWqLp5!v-fvMcC#at3|-NEi}{YWC%g732BaO(%TC#)aXIxm z#a&wt>jse?6A3ILaGke&NAZE8*xJ&S2?Rr? zOu!r>1Z)8U>P-5u&K>ZC-SL)0I9}{%4;LTkWkA5N&Si0iU9nJoI0jJ?F0=*-`}k+f zJ(BrBMm#g~rOb~qQYLV#X6Rt_)^E8EYfL~FIYx8q`-is!(p*>P&oc*&RfRu z3FSJwv+%1Hc5=&b%MV(9-lA#gvYQ&fFl?TrI(9(iMVnv+4AL>i|LA_rL9)~!pc6hWeCT^h)HztDwUAWRh^9C2(> zkB$cQXbth>7PF&F=$u{#N?Hzy#xNJ^!fW2xxf6tz5kl%|MfUz1txnK9!P z%KdjW6fzG^k?4J$u$*YkS4{V(#S4t*1uty9u=59@AB6Aex?Z_$B z81$3^7bg8`a!ayQnq=2{^?SFQ(;bAbPOqn$7j9`ESCPMZdgR3Ztkw{>H&woJ=Hk@i zh2QLQcJ_-pe*u}_J@b--gKWX4)_&}+B^h6q)aAH76_|0`IRj?9+#(y|@kOc2E@e#@ z`xlUP(5{!Qva#ic_DdUDp{QxTe@o@*Z#B9_P?{OGz^&lqWoEx$XoX4QbnGL%Xne6z zw9Cj7XSVz8Y5Ow!1@^;sT{7v7!|9_lVu-MgNIDXN#m;N0HOdW%xUpDnTHLe(B_*2T zaas1VvKVBBLYvxj{)`)w{;E){~;C+A%rpY{i*!Nm4BcU2CLEjvkmh%r=)Z}XL z8l-Z1%twX{t~|qVM|u*OJt!fdn!xQf{KM`7zNg>#9s+cyDBm@peFL4|7OUoZL>3Xg zq*2$Ju2P=c<*F-D%p|GQ+>DoVYquUL&v$n@Dk}*jp6Em9i zwGB;)Qd}2j?eTOx5_iUZ`Xp8(V@jt{E;P-uCc;R6pQx?%v2HQUYO=muCg2VGbg7zw zMAoE`gFqxbk{ySO043(xm1SAJOl2`aCnN1?Qz<9JhD*#^T3lLzv zX;BZ<;TMGn-WG&yKhorjt#z^u&W+B^PDyaCbZ&5tIrliVPR#D5|8^w(nz}OO>(qaH z4awuj$nkB`Bn1U&YATn%12>%N3KbKG|EKJ)n9KA!bs~#Jo!(iUM(iANvB*ge8-}DB z^NF1PEnhNeik15LHe*NA{1bcodS*LqCi?*Ud6(U6uA4c(p{vH;%bx9ZT5IeB2~*0s z{C9TKu79$H>pNQ)U3-c<334;0d;>Qu^0$c0T=}!!_NRs3MpGi8O9kc}`Ne6TMl)tF z4J!OOsIXmlP+21x+@XS$t;;5}Z)P?5h$do=cq5K<#21M*7ikg(X*P|<*4CJ#U9>xc z%wb5>2bGv85>1Jsrq|eBAI>k%iz9iK??@-=&4OK~axvy!uMyU7!OQUfpqF{CDRDy% z2bNWOy)voW|s5m#_QvG-ATnP*H#|6q@N;& z2S67c+%kZgx`^AL$c2sCi{MLFFI=R|DY;nEob)wD+I^jobG4UiZ`9rxz9~{u=e66t zH4TA)H(Hd8wMDPwum$bL8X+Jf8$4?1#4~Yb4TghaaG`N^j0%N-107hNlbgX%e8 zBUBPX<}P5^j+BLH1eJ3Nr6}33DF3o8kL-B`J{@>J#VE&n7*uj?1B z`adHth2n8vZ}3l_Vo7#&4nRS7>onZbsM$@k{Xj`CkrWNBm!;}V22dlIr5m>4y}di z0pN7hB=cOsTWmZ#vJ!>|rr^s&%h46lHPNByrs%Hdc=TY@81*Q4%0o|k>kFO>WCJ~o zulO-!SC7do)$M^_J~CVHF))KS(s8qDcO+c^#>O;^kju%}Y+V{=$k#a3-#DT!sb6FA zlj|UjIaa2rJReW}tioQOveSkyD@I8)13RUGi>~;||33R99kT47-hcKdK3;#*ANE|d zr1Dnf&|GIMU*FAs_4NO`>(*=Uf8gSi?{Kle#?Mr~7yEOMJ%2jfs$>Uk6GhD z1*ZTiIN34E)l2y(7tUI$_+=H+3l%_^LsE=-tgf(2bS*r*dh*aO%}60nv}E}lh39Q9x1gz1wl!sc{LFqg?(>XP~?X>>Y>aJqn$kk9uuBG zgvc23Q;0%RNv=%Nsa8pjV9(KO^@pkE0pZVd97Ef5y&b4!@Fvj6@YYnZV;vM&tmJ@E z%gL?{_*LUv-h^K|07~jC6#$~WVRa2QiCAg^XqQV%^{%+Di8kusiXUOu4z;Axx~rbN zcF#qX7Z*_ZB%{b8m3_R1lFWXN@=0I$;`qJE4q7g>*J|;$5>B1k+&lMwYH{mH z7aGjArea4rJg+;pu2=kd!0ybPzQFBZ34<;0g9{sHHqTtTpkeWuEuCl78f#97B)ea~ zXxXCMFzSg1SVC-;4k3^EtosFnI!b+5pV3Qt8l@!Nx>XS@Os{s|fbL7jbf0-L@*dx0 zuNgdW$?92hxtwD2k(`x31NC1P4GecE4bqct(lB zqI;A58+P%#W_G=mT~c>VohVuB!gZNC$*6JaCCg6f4#fwfpQ5iRWc+3SkRRLDaGLQm zlj!%ud+ip*D%3Nl)1wzca!52wa5jBRvDYXyir!SHsgcT_#hw)&$+J^Bv~|SBY|;Z5 zi@>J>dMeVrL;mu>2sos3#{@n)h-F-_z^aHx1CTxo`#k>&s}_8VLDQ~@4*UkUm$pM?c&-a^TnlF@STf$23OBreBuQU{L^1WnX7M}b?dHeK(!Iu=#wSqtz;ll0 zGLQ6}=U+VH-5z$G=j$GEz=VLfUN{`rWNO4r4XYstI;`%nyNo30ZZ|olMr@LkiVfbO zI2FOq97;o`um*()$q9J`{;NBsC!vGFD_yi?0Wq6iFCGP2&YQ`@9!{_W6|1PQ@Bko_ zg9AUlgP9$W0*scgW9!yYI7QS(Qo+r!`WP^Yw;ie9+S<7K%U{1@{jzgzy&=A|*%A5- z0Y_uDMlU)48;2|3`qPz5R{HGe6G{H2DsO6jEo~Q)$eZ|v(gX*CI-TAl*A;Gy-y0Wi zi{2X*F9?1rcwk7T24xxRcFUREE%)&D#2% z(i12*)Wi-pO*Dy3P2LQF$}e5CO{kOW#JUHhR|PG*>N*scU0y!W2_+B}t)OiKJfL|n zJV0;~$?_R|1JFoqW&0yLK@~f|&KlrhW%U#?29Vu_{lu&@x{A8%_=I!76 z>Xo1Want9wFS+W5jptedbv3J3ef2B<@Qs^)esyml?CnEPh;Yut5PMvB2~z01ZHO9S z&{Cvpk%dXrYLJ&49A>9nUO9qD{*GV}^Q*tSkajAi%o z<(-|+@lQxa&`UavA!3m3k{CGuYo}~qee>XD56{2r+@}Zskucv?`6M5&1fKoSHl1ws z6>9iEF=CU9@!{&wD2_%Nuni|#`W83hC!NuR{%->sS|sfQ(k~&i?%85A@R8$7tA2DYyBdRi0E7PYo>fm*Mls@ZF>)HU4WoARSvCm|^LeHrVulZ){D!T-Xmld~ zFaQpyih9pBlL=D@v2f2eR7b52m;LR@tHn44y{wPurL?36&%9;}7y+hrJ6?f678>jp zggnuhb-H85Ton)kO)KWIip7%yM_p^0T|WBB8Lq=Ti|*B#dmM=FWcx%Dabam>ke z53KqTpN>DR+_MTY>6yw&;&MI{R<`GU!C0j@3sD@Wu_^ik&{X=f5d9H4R_E+pR7p0_ z8ne;F6Vi6|^%vHdhD_pq(;<^csc9o7@MsfuT5Anfi&eqzE3Ip-;z8>Kye8Cjlv%P` z6mI*far+7=n1hIWuIAF+YlI;|+%FsgG}UM}GpftzM6CqRIo=w|Rf-2ut5PJ-kPJLS z@QY}ZMV(gA6SXsif_b)q{F#PWLY1E(LIx!nq=+O^(46R%B!k6hFacxk*#e1BeZ*`9 zk=&z15ay+{wxNPjxE>!c&2T@0axIl^SPwN($nY?Dm0Z9lswYr36-d1q>5ORs5895Vt zir!K1NlYJ9f>c^57-UHyAt#iCRFhy%+@i61f$Cm>j<~AR06+LzFy~t21C`<4s1J*= z7!fA4yI7OLO>AL(ZJ`NfAcZMdhBF1vgc)1Gh7kF=3t_iAx%`DH!oj$A&(HV~@tp)d ze*QEOF9Dl?2BUiBS@3954SB$#t+N`ls2=c%2fb9ghAd)a2xDsXk`k$eDH6@x?ge43 zPq|*_7-#=OoQ4+rWTPr?a`WwW)AzT=$74ozol4yROa!O!~8kS6a0%vQ7GkP$&3= z0>B{47KdQcTV&J&RP=$q0#2^ z=&>FJ_o%y@vW2<#gdym&CwMOZ}uv{R%QP_*YQG zBkUe_NiatV0TO5c@qOV$o$O5(5_PtqQs^!Om3Xqi%|sa~7z~3ie_!bkMX#>bs2Hhj zqfIgCbV>>pgB6q^rD9DP`2TFuX=ki}=OCSpqf8h~t4z03+P^KVG`1AN4@f_N`EEWb zYw6dPBW@ZPLp*(DFn>!f@)p^~}j5cU~Aqv_DkuZF&(gcMpD>04akm69YP%d0o z7MKY(x-@hkt9=O z;IDsu<(0I0KIUCo5mkVhM&r=Lp-_o1`AmBk#kI48;)E!QC~cY<#)wUcdL{a z1-$688&+z5apjf2#+w9Pn7yw1HY`CA#o`i7X2DfwHW_QBI-v8*dmqcmkL6e<|8x$$ zKV0J*z3(F&8VN*8cf-0n`>KV#LJCyBcbM2-IZn!aAyA)U)VauT88*=RhSD-Fz5CR;9VJ9ZMGAT7E_ibVwFd1v>>J-$6tlK9XR7;z# z7o-|MSfL0<#frPvYMG#oXhoBx4bXQ@KxafdKclgi{myKqcW1q zi3o(x7IgMiex%W=yk3vtI+>Z*mLINg9m|0rq{Rk;ooz)^hxZ zyHrLyqTQqg{%hCpvvE|F)5$;z!ha9pzlZQ&ei#082?V?cegnfN^_yuluyX`UuMs|F?R*9&EbLNb?uoFo|J%;scC9)oJEb3NkrkAI z8XLuQ(ou{Lbx>5e4q(Lnf%T=~5^P>Kxz^d}oRnvD?+%7BJZiR$WKXuX$$9 zIp*$$nV;*P8!(10{O&){u7_3FPf}KFm%a=%b7`?VC=`%MlZz@FV)iyfH%2!{B_ZlC zTNPeF+h{?43M>5Wy+UiT2%U}c9~Y4=Y8MP0`*~Y8t8HyYfV?8FsTrzSkHshxVy+si zLi#aPdls3Wp2y>81W*4FJpqrJ>IvlmZd9ynU*o~XiAD)2dXW#1cc8}TDD1Z$0t5D; z1>OP5D>kv4zn@xA-(SQYM|PVjVslUh$R`wII*n9iYpb;*+yP!YVk;VB0knhN0d$43 z*###~VYg_1RqcseTND}&pDEZBBU3xUUt7&(X2iYw`MvZ`y4Qhw(@L0G)8X{Kv;@bX zxsPe1;V?Eo5Vq@Z<*K4&Z z4147OIX$O7<%88T$)8f+-6?FvQ}tWfT0ip>Rn^-4g^1tJpT$?zck(=9x{1}&JKCG^ z#sj?Ho#GcOYlRqsaCz=Awlcx9bA_={Ka3=znx^WD)!g#OZcBe-z%X z{6IKoi$)qC-T>sI;}*NSK!fQV81Q(MW4fo2GK*Mw(fFc$izXIn!i&m_RxTP|v~khq zMY=`ev0ZP4ZLdg>qe2zHIKq8(6#w`8aqGl&u+r090Ox^2oziy9Me07*k8$syxHN8AX8)Ix4)v0OXi}9A5;y5A&pI ztLN`TMml;Ys80|qJHQ{z@G-$F`0Tvj`>JDzPK}|BZO{&YbC^Un7$1&rh;NK@|7%M; zg#n;oc*6)Bs|kFZkT@prT@&4hYNOiU5%fbHSKGu3sDK+&+#++?Y&^BA)y}N;uzke7 z%dWAb2fPc@NBeg^Ep%4yqFtY9Fn9wnbs^I$qlBFsQ_^56&ZiHrq}lsm_fI@fxr_E! zs$+9$+a_nM*5;TtWAEoKp;J(_IjsU*{W{IR_?BpY`zax{)mmay>go48vQ0=8BZ(BO zGHSFIQ07rDlz|k47|V%nKXeNd4*(IGwL8&=Th%@Y4d_F9hc${m4EZ1}_U`aGS`6q0 zCj@jOcC;ISA@$?KxNf;53{*PVK%2&$AN;f#);9Gf0!$2KU3P3;-TbqO#dT{mnSb zP7$kOt26A(V!1r%9C7Y)?spoT)Z=xW43DAtqf~Gl+pfZS))bB80O#|0zmo(|YfWuI z2DDcurC|UZnD~bs$rcXqEd6eRZO4j)CnH%Yi+-zsUZ?i^qimbQd%5n%Q+y={51?NQ zdBo!~MIMwzL3Se-DRd#~rHy*&3&Sim>>Kgz^XZ@=O(6xxS?CPMtu^qGlaAp#Iremw zGvqkzs=WOD-UmJdtL!vBgR!cTfsb)YGQ6L=ghFMdW?1_QX#AU8&UCU3l`)}d2g}Bl z_~y8TayNl~vU?V7@20I@JdJ)HavYHcGkr zdbCl>pgi$N>0oK1q$?e5gXT|@)gs%V^sQq%<^nqln3agJYGDtvU$PQj-QX5quAGz7ddB$Ln?MV3%N%?ZLQ z3*i+!{?;V+Ar}SV6ej>xkK$0`EvZnN){?2SGm3f?4Bo|#0xz||Tj~gRtm)X-q3Jlb zC3Sqdd!}I3COo5k8n_Nwt_=nODUoqwgNwQYHwKYD)&xcZn*w_RI%x3(DlRChxUR__ zuhvv}X*#Ch)yl4B%|epq@%M@=7Is4r)+@eHpvJ!0LAA{#U=i{3Z+)q z@;3_Ck49=448t#s+TiX=c@V5Pk=pGcto-o7+6nxL?&8Y!&F$ibc2uj8+at(bKh!?f zzNh_AyTR6;GmA=;x3XWc;Au8qWY%IBLCW-O?VP!VqT&nV1J;%z?-FUjSnbe$H>tzv zb%a|JXK1q6GML2ER;B^0?v}oKNQgO>!2q!X$ z@q}beFcrBZLapg@_#UXW32(D@VaX1OQp{9y)*>%1<)qz)J&EBQM9+OnGM7X0b&hr@ z1lW=3pmH}FYX`Fe^Awc9S<^App(E_8zKr8D)_$bv90LX5@+1%P_*F3QwdVW0Plbe@ zpsI4A3_C@a64LjH=;yOLQmNWnbdw7pP8bkCyOR4+Vx0(M3X@eR@K#jZncm9Jc64<` zB4~w2_wrUc2RnCl?(fue9@9#dx@f(la-xjzvi5DEo2biOr7q+r8dE}`u5d6D6v-Z~ z1lI(IgCoIB!Li_;;Gtkm5RJZ<%8qlZ@4=uPyPItQCrHBV5NqNX)Iw=$$@!t&vaw~n zMN6I#O3Pr&`W9`=v3)!ii-eNy5>W1!m|J&=@C5MkR>etsUq~3Kk*r;E7fiZcN@o#~ z24kbyLV=Eo3&hIdCR-wwF^eQv$j#&nwHqwA z)=~;YgGLIMGi8x-*^ieGl{IA~K!vCf<_iebMA}-TQsrCoG2ie+bn}+BN5LX_RLxgw;Zqf)a}>J!?Ip;BgO-HsMi}qSce( zu0|R<8TTCYXg1*>dZ^1_99&4!YT~14^8$3@ALn2ltr3z(mGkNv;X^+^F*N=Cz^E^&+|cT!SA(U48wwY^ue%o0U6U{uN?7p%=Fle8 zHfJfhqp+Wkm5+7q03Y1@n*u5Z!~61I^Okc7e{?^coEL4YFo^0bsZo~ogQLB$-Or)J zbx|X+5$K_!s)J5itv-AU7r<9$8g8HulXIiKeR6Kz*Ik?*{8RLh5mxCGp&P3ivY>6l z4)-t(>|rI~_ro{=`30xE;n0SV#u^HTl+a-4P)Hk6i5MdURdBrcNLq!L9q{OAU$a68 zeVs}CTky+J8#no3>G-)4_q$`fs}VVpiIgKNBEyjl5xq5nk~xvV$av&XL>HOrvfAU1 zvhDhJ^*bjFqTf`5i$(lwL)BOrKz1IZzdpr#jtz8?R01V2m^hR`icZcTVSOTINf7=h z+N_zlSh}1nQ_?@Fyph+{%2#n=3!?9LPQ0Rcs)%3>z?2&a3%uxE6-xv8FvZ)sk^pRVj#o zcppEqmMiNurlWn0%EmWF2-$HkhDq|G(b8`JvP&fYkDo6s=IR))Zi-cp>Q5OpmRWJ+*Xa9?mgR5LAC z&A3=57*;-f3)5f)xy50{<<=AIYT?uOi6|M%&Gd9sWLS%p@P@Ex3G+quF#v6!4$xb% zc@$Cn^@Rulwj5a;5hLJBV&tztx|Mv2ipy1it7UKa`2L^n9eBxCwRcR?hPIVmq1h#5 zReDvz;3cx31*`N5NfP0*WNm0uXgp*fd4g#KNmS|eIJTtw)>NNztX=zr&;feo3y^y1 zRFVagNiOK*MmE^U!i|x}Y@^aR*toV)*NA?p20NnG$-W)e7L|!>^*L=F`gkAoE#RES zr|BS^ISBt`0!${YTZVpdbf!PWqk-d%!%)VQwe=A~TB#v&sZ?Qa3U4AK+fB zsXwWCMfwWb1stedK7QWrN-8PXb+p|Nao!dFF{9b6-6j<7qpg{jm1u8Q zxB;6YR6j7&BUyWxwI|$@?HNQ3ouQsX$mBW2Ayh0mo~@DnLf6durj8jo6VSIdC5{fY z=Uidx7;^Jvijq||fMtt=C?q>nJXF-f?p0-7Tz@>?ZNbh+mgXPxxyXnPog@^M6#COG zcW>&JM!FAmX_=(OyA?|@G zIqqqlRd^=NK9_zZEq)S?Xz`}F_`Uesaq-1C`)Qor7-v_-+2w)j17fFPp5Zr!!v;-_ z!DnbPNJgQNHA`+(%0tEx-%jZtwm^y#c;Sh{kFW^SaU0T2H8x^*^G<{gwOAgMz9vL? zETAmiK3dbP#Bk4ccc~DIxnX5_VoL~a%+mI42rqJk;Ar~fsKZh3{-GLyMqbI~1382U zz?GOnf8e1+&ZXRg?v;f{-aHx&1aPVjbZ{`u!?&(2%Y%X_is(`b4#LmmIH#dtT#6l3 z9gy?w@jSWLk@K+tygr!e7L77ycBY>F^YC0aTwty-{tzH)W+)N1&UbjzECsK@yjb zl(g}L`hw57QNb()5=8?t!E7A2X@Vl-lXb2F9|Qk#Ns>o(HPGi}U@lDiw*#@i0I zVc%aRK$ySC)-egI|B5%M(9%7vLQ7~TXwirSkM{S%$CMgLPOXHa!{!v`=GE<~gw3YB z2a-w?2c?u$P~kbL0LDo7w<5U)5y0cgeMzk~8ODzLzU0BAmJINmk70sou&Nv#5^vKD zsF0u5gEhz!FlSVfBcT1?D6NqYZB$`C>cQ>9w9W}rQj4(S$>GB)7cy`T3$F=}hxdgw zVHKiaE*z#RE)hr29ad)#W6VfnIxGpe41QV(@R>rm2Bu}OZ4Kv)<8Asj;$oN|(s$4l zpPS>;4(D}ibyus9ht+&FyqE75a@!DHHk`(opH~nLjWOeT4HcYjQsA=HM9Mi9_T=J5 zJ%Zha!w`J)HsaSDdr2Y);Ma?{;?a6Eb&X=KC-6yP6!IntELGyj+8w3A$x&<`t%(jt zH}C`jI%_l%9gOae9*SzC)!D+hCpol`LEhErRGgI#L))_KfKaYdt}3J6=Y6E<9Yd{I zxJ>cZ644f}ywTk8yQWiTwxfDi$amCQukw7oZEA*d(&u&ULOY+~|D(i8Nazj_SUa-V ztdgu95x!?N)G>}dQ+)?d!%Y*-T_=vt^zm!2I^*O|1kObJB|=X71nrkh{7iSfux^W1 zlOYBI%ey4*&tTsEx6+r1g5;10k!otS($`Y605YQPA0A=CGK?0$q7DSP6vnC3rf5(!O^CH+}1}L4mOBu z8ipEnHAvQmNW);mc*DL1__*vHiL(navLJFTQAuT<#-R?3Q zYii7-au^UvhztaIL>K4fSDrl}A9>{f%RHUS9r>C3JR$&*xu$`YMGGA*bN`CKAS`h?-Fmu@@q`^p8Cpa1any*d_P>!dQI=@@ak70N*>K?`xdubm|p8 zogZk#Q&Tx@)Di!CkecQZe6A7m=+u(csD-$w_?G6;%3R?@r3{aCk%3{>7KZZc!6hYQ zKA+1ecemo}747TW$J?b099f3jH{nz}-pT9paD{?C{y9H5Wtx_x!}W z^akx?cvoxnU7y6e7ARfTQ_$!f{;oOe=Zwse%yZ^=y~^FRS-fKY`uXDE{NeeV=kK4d zDbF9AzZMrG^T%g`Vc6>z8PH03(snjcApVpd3bJ=FLxS6Gzn}b2D@K!Xg zZyaxwGL6HHn-Hj=8E<4|7)wX+<-SIE0#^rtF+f_YUZe2gTEfQwjhnarFvc)PDOu;B z^)5b!uJv6bU6Q%03qgcp&QX#RKC5`^b8LW%by0-wYHgMfe-}s?frK^?!V?;>ip2!LgMp- zwM7Hg7Tu!=7os)lHmF-;Styv_S3KFKS`U;O82M4XHA5bgYceAlF+=D_U03pseEsn16z_3qv&*X< zuuaL_v8#-~d6&wWb^nU~k^(ZFGoJuAckG^kGndzwN6M11Tvj==B(CUN-#6YTW%}0i z4fk!rsdl`NmHP(!*7l9yYG0oY0VIEgGwUV13qHI2U6Y)7CEj&1f7i+DPaZj0GM{|1 z%9-)5CF_@ngG+{&Y+kZ|iKe_{aLHO+j4T<)rG5!s^Dh2h#TFV-UVY0HXD;I{*$%e2 zgQYq;z?so_aeZ;TC}oPn#ZAS7MGc%H%diKJ;LCkQ#A*E%&dkSvD7a%d^T}v^fRABd z{lLh8WFA0DGJ40n_4C9*%>3qg`{!xO^9DH@jLaLyrG8#@0LRKNT`lL#d`5JaNCn-4 z!k@NC;!Gg>Xiq;?RT>^eFf6}Dh#DOg5At&gGv%3)nZq+R!pw~`MPcUP%#}02gx^t; zGiT12p%kbj(|5YJcRvz;r2FxnmkMv@4;Kv5Y-I8&Y?{r)+0_0i69qc%ShTn>TbWm+ z^DVvnFxowX|D@FAiUnadTRfWyvj=CdoGr~}a4i@fJ$<4;QCOn`%hY3`tRB57I=+tV zN-$Uz*u#Zq3U3so0)|c&Q_!Wll;fum5Ik6qL;gY}1N^91YhklFb?nqVr=sd?_Ea$KXOzOJrw$G(a|r-<&bxj7cNTA7{Fvi0_m4e~ zEPnjN=bSIiePO{Loo~)LJl85M<=|M##HG|Yz(Gd>$5McU&H)bo6Tm@76&y?1;-ySj zI=FP@QfVohTLlM>931=zaPT9*fnyaMbJ@dlpPBo{Txsrq3J%q@JZV$ru;AE@Rkr|G z{0Cq`?yaM}v$&fpDi7NWk=$@@b8alB!Qx5CZN#}G*Lv%QqQ*@PFbW92S$nUWg-ECmWu?85`aPH00tchH~31D z<3@=sE-|4rSXv3^MUtW%H%OWiZtx?3!H)n2j#Xf!*u$x3Qg5UrlAeDVH%NQn>cb=t z*DT@C;M53c3<4Uz0W>(Id;2-W6`3$%?%3Qtb0uLe!~YcNnV&m%)-2`9?i;$lF#D^{ zTRpdRf2VJI-{Y=Fy^r`F>3e+kOWn`+9q#^fk4;*@fv|vy3upuY0v!nm3jhQ<2N3vA z00JFVj99=HFJQug!38T9NDEkZ6$m(TAn+rAz>fd|j#VIZvxmE%>3*YIq8;784g~yC zr4H=!)gI;G+Ex*J^YF9CADLdcOBC+a8TZsZteoLYwA0( z8kx>vSBr1y=P?86N&PgNN|6quFcQ%b30vHS{r&7vKSO0Ycw}$u?*}uTK@(O4&nYy> zu0mZ;zGqg?Kll6rrO0~Bv(0nw?)i0(_LkDux^C$`TzF^pJ9Bktecx=he>USR^&FL@ zE?uUwRO7&0u-m@b)vFmjxI%d{x>YfH0=R-cLs#37Ci#7_+dYWvb%EW+3gUHOx8ihm zORSauBiUKx>UdQvCml)E%a7r>tHsv}kOjuT4wrd$A z&O?f6*)n3d1e-xXY7~Gvks=p*dz3=r#QcfBI#H5NG%q!udH0FGK2dx7{BPHNcfq$8 z?VNZ2g2x??yI-9D%LRw$9-jBk{CAeVbEcI*x0LN)$~gNyM`gbN9cRA;x}^j<^$MU{ zN}yA(0J^0FI=Ujzy)V{W1>M~ET=6=vW^p=eo`f8#nN5O@g4?m^cZ`a|hKR7h5grS9UOg+ih2N?C$_e{;gu_=*VQKPLrzu-S!278;*OF_MVvq zXQnQj$QHAYWcO$PnAK}D=90Pl?(D0$(B0DXwU%4j4yWEJy@T?YfLDp3Kn&;5=cpVS z@Zubr;8h}csaJqkiQuJP0bV777hMs&-WT_-!YdU?iPwR9i_^LHUx63rUR7KFF&uog z5CXL9iW6hlbOIAjD5HAVO4JVwL~lto@w@?<}?uuokfW z3mE6*=ct?ofVF^trCtG83kX>16@ayXfJIjXtoOy`tHA1xcZ=77%Zt;w{9ghV z$1asBV-wq@J2__UCEZioj@(bHgpvFBmiQK<6H3-^wzai|!wUIojT$F-ToxVU)$3UG zSYJIxtH((7$er}+@c;|wv-#ot#=J(xiGtG&uwd&}=GW%O@=_kRZ<$r3!zeb#`4LMk zShq0(QphIv5|3q;OSk8fB=u9ZB1FQ^)_nF;3j~E;?$NH~|*Ie^wkf8g7SAinP+T z%Du`fF}KoJB+?p<#0&iBbn~MkqfF|sdxKk)-OTFFxG|;wrdZr=q)WNTwZ?ssN0R9= zOwr#%dwgp7`!Kb1#MIJ3p{aHAsdaC5kGZ8a?s4}%6wO3x;q^GxxS^z1*Q%3DkBrgP zua;j*H2DAHvA-L)VUxTYMvte+cE_Vu&1R(eZ{NTJRW&5P+S@;@{IUQC(EPq4+h1f9 z-n!+SA{|B{)aj^@P%n~DSFa${izL+5D+qN2!Mga>D1^Ei46Dv9*ZC%=8M9o~mfuCc z{}adfZaJu1J@F{!@kdyvA;M)>0Mz*8wm{&+O2Emf^RxYG#1E#nPF)r{3)d5M>x5rUbWqej zO;%S{M}8wLtkTA)t%~YqtBUI6gG0VSs@`_IS|Xb@JIL+gq({MK{X*4d?Q_8eieAXI zmG^U(&|6ugIYsj?7~j8eovek;sEiBkJ4A0H1`LFfJ3QJmLDd|F6&p@iHkM6_M#VZ& zFpBY=((ix6Ow~rdaND?gV z7)7pj?=I;9-0qVu5$dE@1uY9urcWx!K*`3v2gt?pl>={3F$T)Q&K)p$E_>#v6=U_s zrd@}7L^4IC#hjW`+CP2Z+W)8=Zfd=L#>ad9dB@f_({q*%$RW1mi}##6^S*E0^ogOL zHhuTD6EC>#i>rFCx}MbzWly^v+D>&;qDyBLXl=l+bsrbo=^Cq=yts5?v(O3uU%o0y zHWuip_!0WIA;MN7PH}$(MXSRG$`BxgBdE~|8O*nKxZm$b)@J4Q=~#yy-%}39gxOoI zSs4B)q|(wDGiyLvYhia)t+CKc$ttTDk>oX}K$GD%TEvUyRL)d&&O&B8;NVBtI?qCM z6eB58dVV!hI*85-F1e99u7+w^3t{d)0+J-MoXTSKA)Z&~Sc}|H)j7@}s>ms1Dc3_J z#dN6{pUmWNO%WcKhzLwFE3zDnSN*-nw8B~TB(@xvmekxYe*%@Js$7I23bOrvL7I3& zNw%cTb&Ax?-CLW>%`2LRn)fwpt<7Qh_6|0W<6@#&-%QHiF}XXm)Sj9rt%XgscUVa( zy`h96m3e#Ra(P90sJySNwU)!>Yt&)5XvvtvomYRo}zo->QJ! z5^*)pPG{h->=b4oJ6*?pBH8%2z?W?2#M??p&18`pwii=8mraT-POX5;`=->Mlvdp^ zr+#vuVF^C_C-J<6zpb_?Aa7ypKAx{+i?I$Zenv4Di%=HDF2z(|=mJUchjBZyLEj|# zP-4LesaI>)%0^@zcUx*}ErR?YT!f5*6Q2S(Nga6kfc!Jcjwz3!rljf;MRi@&l&orL zr+i-W&7ITU&=v3}>KCo7KdJNJcRNoxYvv4=hD39c6vw0c^CQq^yN^O5_P}fO-L5Rr~f0kRr9#+``&Y6!dL2ySKliQfk)&|7twjwOW)@?tJ?(C*%EEnl8+R&nIS|GTCUnA0k zA3fdzY*}2z;;u64E)DUv|A%@bh?BiRMT{GUB*6*W|nYG^S@fhHecL&PtSldxtr@g(c9uX?r>|XeTwxR9{ zgz^kHYkftji5RT;a2^$U@>DV<((LiMoj$8Aq(nmTkWynSghG~hxsA28)rV10_5tZ) z)Fyfe^>5mcNz^DCBSy(+gjjzBwGcccxKT@mM75{9Jdcu-l$)Dpbf-M8xo7tx#Vc}k zBUXY*%Rq9``@?-4{ zR|b}ZlI;yScHzeFZ8CR>kl1w|$5wZ=D)jD?4BFBgE5-3GkC`6j7W2XM0@< zsQF|Oc@b7sfN(N`Sc{?LL{dkN-^eG!1D>bgtNjSt)ZH-E&kQ?X`%l7L>?ZRdDC%e< zb#zA~Br+zPM~C^L&dE(oXv#E|kqB2qL19BYX@Q!uKuv-JEny1(KDswR^7Tzzr@0+* z(d|rD65ay(e187Q{0(`@oloUUdFkHh&gctKN#{g$39Ey7bg-JesJI;sg^b7HF04wy zRnNuj+CVsnXmct$Q)6)yLXmK|=xNz0J)(qAeng2k6s(r8WwB+2MdOn!mXuCZYGl+3 z)%{R~i0My(qJDfGQJ|!NsV2C|I9bb3iQ@LYy#< z>K4QZsTdcy+6wr55qHD?a7H2?SC#AJ&RuH9B8F-cXkro6(bN|c6h21Zm#B)e8dF9w zWSZN%JVsAbe)-3*ig`{8cQxczRNmZMdGd2Fut@rA&)s}yzWaif%kC&NUG%jtUn`l< zSkYUX?CNPvFjnK3bh@B(qy35v>!9}VKv={|&Ms-=0`2W= zN9MeEoKp2azP;A&tV3~QnmRLF7aio1UvTYN5y4!RsPek-$g{cKY6^YKj9N;9Pap0=p)Y6*Tm(@tAHwvU4jo+5QI#WNlliQO zla&woti*VvUwb~US##=1AB%L~c+NR@-dx#v4^771O83XF{p#l*ymPL1&Sy_M_ms6P z;Wnfaowp3lZI9$o_0)clrlIniZ=GFeOT|7OsWl{q?(FCzeuDhUn%gxkp#37(+uDUE zpuyH}XX|G%WfrLZUlsE#C0bILwJ+Q!l7AbR5i~A5t7cX{2hXaRmGLZezR)O(6!G96 zp{C~Ct#}ab9()^~9>jC70i1yhvn0p<1MhTc+Z%2}q_Rm|=~(L+b`W`z@0Qa6_X-F! z7qhxDuCi-|Yokl+n$-4quI=O3QdPE6?k27|!Y}v~%^~C}kBH?&f^utPZB|D3o2c^U zLB!?^K~m_|z#I}4N4fKdZROU=XNi|h&WUa^cOsRL+)d3wi`)`vk)l!yGUyU<7+0NxMD0t@F7PYQ!iBi<#R})lhjYNk7yhoz&jby8zFu? zMfs?sMkSjNmjn(V+k*3sY}&Z!^nvEJ9d|1|XNxD@d8Sq+|08?q&x&7;KkxyuCDFOy z#O_<#d%rgFl{4;W`pswNHaDGGWdQGt4R{7Vg+Jgf*4@W1kM*sUuN{NI>zFbTM^-Bf z46s&?z&PpWhy~xVekB@H@z`i~71s1m^oxYU0N9jGR?r-a#X?ck{6##D1COc1R0Mde z%N6p)JjHnDi%L;(cnig1b5lzis~xa=d`;M`wB}V@Mu>SqN~g0xGg=^S!|xw8M!Smn zLav~o%LQ0o3b2)N6vx|1?6p!iE%=_$J;(9A`U_XU+VD$Lv(l^}48BC{K7&68{vgnV*%P~UL&$fL5KdR3#t2Gq zch#@~d5J8#Jf3`{C+4cNPlnuoP0Oc zRJ3(sWlOdiR3gi_W!J4k1xex-sU*efP5y_fW4yE!@e&@q7u5|N`SY4jdIL-f1R`}e z&bYkkrcfvyTW0F%Iq3{{I(Pe(XK9y={YR}FS@!MkHhC9B6!$mkV@(W|jTCas@-WH4KHSg-GSeN$BUe5ke-1%>RsC@2~%5Pu344u5I zau~fFSow?OdGpx`l|$K{Z!awcp@nd0bITL7JlN4YnFd-&{yv_#iAwsRKH1KRUnrhr zlI&1adZe6Qksd-`_)Y19X#?ekC(T5a;C!T1EkZX_ZB07qo&7?bG8wliPQM>HX<3A% z+gcdpN02R0L)ijpfmQ^Z8nD|x3^>68HwrzKS07djL9>kfGg#RWoyLYM_p+t9Q5H~H zuMw5?Uevp=KImfo_X}BQyn!eJAR&^|sFz%4K%x&R)q$w&6qbaFQ7#|_RlUy}a=R71 zz2J65VNr=&~UVP!0hhAbnNT^21W-BTr8tU$Qj6{;62=hXXS{U7hX1X&@DTrkL_ z#xI-`?)JKEVQbpr?(WkEU81wawf6L`OBS`K(hDsA*r)Y|LIWYoXM^*U@wd&rC2hc> zv35rdzs|X1!Be%F3zvL@wFPrJ6jQBy3A23#W?K^Uh=!aY>J(GbP!}oG8bVR>nR;C@ zW4EN%V0ILAY85a=sq+<3*GLhlwxl3Xl}pJl2`xn!(mIt+B~)Lqc4j)uohv#u7OAtv z>G0a^gc^2RK=FG6Qa=hBEgNp z%|Wd(7)0$dYbX;cha`&>LKb0KShZN&n5WG|DSRT`QiIf8uhc8HP{CTyB2s;QDOge( zTMMNIr2%i+hM@KaEu0v~lo_y|>Zw+l2gQ3OMwxP$WVd+(8vmGs4LX?MkR4ep;|3jL zjy(>&$e##}~#~{x2ZxOEpcQ zGsE?5<*>8zd5+aPUCCgRQ}H2dgq*tz-#uYWZVu;HIYE# z?U%IYA?P?HtW##0W#lrj(T54fcm+i{y{G z*$VxTenh_uY=3H+(kfec_W9kY#R-V1fP~QJP~||X;}P4jSdm*Qc#0b*QYCl}EMAtZR{FccBppZVqC*d=Cf@3AFNcDZRFAT5J+cvcd21 zi*NbaPyH|Xf9IF{SEas~dMYK|roUIeQ!mk;wV0|yJx!BMUyLH!RnoxN0aJSs<=qG# zGKYuER>(v0h`dYI$kX6K7{ejq?tBdUGbpXz34CLy*XZti76ql zq53F-)M4Bq>rSkQT6<6r$e_R%6}%|B%iUh21qLxf3BR=?lHXF_;WU~Jr`@ClS)MTCO-RVRy?`{ppQgO!W1{{60}rmkV6k zT1=E?uId}Eb$9pZLp6rVf@D0TgM51%98ahFcW^wPuvc0Cl=DTW2aWnK>(q%C=vYQq)`^D=tkFOjo22i!8LV=s<1LB?RD5$8Ep>Goi%Tzv40(9yK|z9V zM-8f7dK?a89V!GPpM%9HSR>X!6cFBH-EY+yC98)R4j``O5ZAI5+9A|L+@(dS-$@Cj zk~%^BSrq2A>EBzSq zCkk34%98(0ic{GZ=Wz1#RO=KYROzkCo1R0W1R4{s5?peZf~{w?KG}LYK2LAede3M* zW8)cZr?uiwV3lQ<<$HAXLYA47g3$w}VD!Mj2DN;BIT(n8?bZe`S zZXl6{>T`n5jO_5#_ZD~+TdbREc42fdP>9BwfX?UB(Jmiqy((^t*=+E_p#v5|pBDR0 zV@eS=XlOO9a;s=;m9z~(L2IoG*JbJ?k5pF`4LD%m(ZD9911g#yJLxp%kM4ccmVflD z<+OPB$b<$kzj8N`-Kh5JgBn7Lw@3a7Dg`g+%-bvPCgDL03eWN%skHW`yMY#1N#Ycm zNIeKJ7ISdsT}{#jJ1bW{oOviAr4pH9p-K@^;+);nkyKjx7ft;B!oUf0+WTj9FKlmW zZyt8KL_?w8Q`79x95T2gPu_LeHyxktoe^?-BAvN&htF8DFl?3UeC}48RxapJN3a9g zvJ675GGurP+D2X|3$ukm1WXq7fz_XSNvmnD5k{>}h8$3Y~fS>{4TTZXy;- z&F)KmZk6lYOE1w}bit=@wA!vTVe{_O*IH0$_GeJe^1JuscFTMB=5jd6?S9vnyOFSC zcb@b$dF|1N8%Gt) zE=B?a!qbHsv3#3PKkW-f<(#RtC~p#r5}8pGQTh;$c{GX!64M?vJasLQb%o2TjEdOeEIo1YOSS(uAw2< z!jiT2j^|%~^Uar^JNCQhUKY<&8)(g6a!H-qht_ucSx*D#THJH6wHjb68U8H~RzrX1`?i z1U$_iDP_h|hdn8?Cw-rH^eA>K=y_uRWcnN7H$spAAI%8LXz+{*C?n`=$O#z5umuFN%iCW9m|c z)*d95RI60li)ed`(w1x2Sbq1~R;xMr zf8X~^5N&(^{rrA`$t;;UbI$WV@3TMO=WM1mT&w<2or&A4>MZqD(n?;y4X}}aT~Ffb zySg6r9g!0)-IU;=f^&`TNx^e8s>Lzj*Um%$C@%q^Bx14|e)~)BCzh%zC z&d!DO#=rlW<`E2{b^-#sTEURk+!l6Hl1CNC`PYNnbK3z9W@fWF={Lr1+!pu zU<%nkH>7@qsvK(eQe^{gt{qR|$}&9yQ5SEHXJF}4K?DO2|L910a?T}-+_9$q_UVzP zxa`SceEen7CMDFCGTSMJ5OgxPNB+tfFiYH z{yL^tN0fly9bs{V8iWxe9)-?g9Rx_eXcTCDGZxCo7RpGL8@CvD84nsoJS2SOCjOYF*UfuA)+z8mW}8smO+G)`ocR>2906q5&BL zz2EM&gznCStnPrfc5c@S-BzPDa9>MHjWW&KS8#Z{U3q)6BWZbH=lHmKp1P1c!Sy5X zJBQ=`TF)7u<_s?ix1s74p`3B}-rPn^cCm9Cl#tHETSdvvDF6!LG0!XAi^MK(kW4y- z`KgKYj*GdKX2O2I?gz9I;`DyxENNzQKfq6`CA%l!gmD}m#B|% zk}UBqLBiVlq9r534Rnn~w#Fh^E^ZNbi3dedJZp{VCDP@kmJVA|EGN|aS$;ChS_e+N zT(KeXJ{GJ5CsU^+fv5{E9uKIFrrgZxY2d|@XO(HQr#|Lh(0$?BD$D9nqEzs^+O5^G zX*yqT|Dx-It=9<~CQV&1y{SDgH=V_LQY!I((Hr1uUiG}o#f22GBPi2J-2x*Cibf3j zi6}cGoR&c;|xHhkTk$Ni!o*0+Fy&vgOqzRt*1Xa;K_}RvK3{yky`tkqB?JsEI;{Es%EqRAqlEwiu$?vATH8Cr0Z92zjcR6nQ?i4` z%n3to?bA2_i1EO?}pXvfK?OaGRT$whjMK!n~g{94k#Dd@fyG9ZK0OL^Maysa5t07&_t+kk7P4k-+Zr2%cpj zOdu&>PDZLG6Y!aIxm+folT9&GzezCp0tQ~TiljCfuCkFANe!Z>Cyt+hcjj%!gMlM3 zhKavWq(8@fcwykg0A9ro#Qh261}LW@5X$~`V&L!mt+62+N>g(!}8_2r! z67}OHKij?g>$9IHI-Y{(w2i%@j*UGl^pW4Gp3$+}UwDDvJo?Q~ekNqTNv>CKrk}j~ zlm8ZMcV9T><9~DEyYD{vl7% z+1Lw9sAkh$iM=3k_W|8j(gDN^b8$J!|BghqMB`5d4&h1;^*PuTqQAl zogPxj(l=_O&8&hT{gbNtNQH-~u=k#EnLiElY0{p$&7k2P{H(!`_@l}rnrDTU;`VTd zp{3?eXepFTGA)H{2AjUX@b#aAkOMv$n=@d3Uf_B)$bMF5@K3#q_F9PVuw!Xh{S4_C z9T@-v(O~+u-jjdkT^jbi?mwV!zN?*=ZXx6nJ=UZS-9&fCKld(rUivzAtb^LI`0>iI zO2sqB`sd!I;kWDW#d#gkjs+Zzov0>B@)Tfz&$qSYMdj660J%_{(pDvy!7sGQMgbB) z6F0~)GvIPsy}=OK^;J05k1EGXa6Mel(>(GdTb~Iwvvf%fs=E72L#3^y9VL+~6>F2q z;<_vAw$&Z46Zt|D0jQq^P^f{J))qdjVf0lnMz#p==vU$RZ&Z#CY*h`5@cz`kBldDowxt;yAC#1^<4)VGB}(G6_$3|GS)RlLP05so=dIe5Dx z>fmi+by}&(NS+skLyE^6^D2fy$?FYpmKaVjW-)-}Uld+`rm}5Jh>~!e^hN-)a#5LK zc1byW9<@~lFA9F2jd?s_y#0Q~Q=qE1&R~c>Y5BH=H$t`{J|8|H?BeW&(cz}yfnrm^ zfePJG$RbonFbN(wa{LHaz@I0Kh2BvoBA8k!8>|Pv+&c=ZVkB^41X>kZ7vVGl_n!Vk z-Ug;2OhhL&oxD;8W)yYdy)CFfPC{lS9l~J$BNt!LTfA^mA~0pyq=(nfnXyjURyZde zZCr5i4L7L&xtXll%+Zqh7^shUC1U4z;`bw;hgux3 z7t~EsI8P!-`>O%7Po-aNLy6s^)0*C6L=NDi(J_J{V1=|`Lis2n?NsGJ2S5AMzZq*J{mp8qjzL)I^rp*~^DHf;-#&$i{KPgaOYN)%hck1Jps=ueJix@+9Zu3u; zOVHyPxfl* zUeX4&J^Ie;*aILS`~Y;9js2NE*7m~dM}9;D0yGs&ou5|tb5%mc!37<>V0y4OUpMwe z-ewLvn)??9T?g87Ez>$fle~FH`d*qkp3)fZdVg?Z|JTN1x#qz+tL7wYGc{EW=~Rqe z&jtJrbmL$c7k7)|&2qV++}50sONQ&JoUU4_Q{s`Jpw*}0wce|}cfd|zfpnp&%EC2R zMq&ga!sWy)gP&lyagG&BX1yVuB}9v{IL^W@@#v`hb4NR_MIQT65gQwx9<5I(ypB`_ z_-5ev5&77}wbD!#%|pQSPdZozhgoh|b@Z70?X~YHj#SUDuCn%TKmYvAGs|@|d)&GO z{p)g%e3lINU-Z(-jgJ#~a@#e|UkqI~`-a5}KDp)dDsF9S>(<_e z8TBeU@weI0&D^^R5}G)+0u)I*kFXPq)X!X4;uIj*0kq6$HkjZL;IAh9)nriU z>7WA~v3m+U$s$##NL7l{OM1l@FX{CLKs}okhMW=4Fa!<7g2oNA8CH)Dp?)eMfcVAy zR{DpdpShl<0=fg4bK2exxqRj^y}-(5#_s&IYUj)owym|@e&)5t1|7&iZ;TwV_M2Kr z3BMNNHg_;JxCpoFaHDi_s=pBRX+hyZJ zi6y3gVN31DUpUzRv_Ouh1#%b~?nkY@!9bTOGqptT6$es=Sbhn38mRc2*iWd9VEZYF zB3HYF!a%SF-G)Z`Cdvae(@2+|2-KN)7OyfgFa_8?p%4))fy~FYJFBFv@*4C4cAvJ# zv`t2zR^I&g%IH3&99!%A&MHs~oLzqg{YFuzZ4BWxPwhxd3aYT9Ii1W`JaX#VZ&qsX} zj@VC+*w2pGzpJ0`|Jx(Bar60l@iM-J{{}rr{y~mYzrgtwJF-kM5jO5nh%|&q2%lta zKQt=xWAPBFhok4^eyTpnJwy#r?p~pv`xyM<+Aruh{(!ImuN6kADr#(KZjM(8{r9d} z1DdpP_li%l?_>Up{z?K5lM7%L5?3tN)hSw2APCb)-Av66{}Xqga7c>44*!_h;cz}V z_j7!I|Bik*m4AA+zkl62nCz^k{ZHHo5S{6JKRNT>)C)X?3EeRbEb=GCHf`GS(}wI5M{aM=mHS-ac(X94(|v>0#|l zqe};ujx0U8RM)UnS-MMmxD!Y(y z&l0&=({jxUzfi%9Q~Sc`!oosfAw4av$PUK}P+GBLQCiW8z4Tq=$~qEX?HXGwB^bM9(H`<-9dWuFAPM~RQlXqd5e#-lSt84naZ+%e;g8T!E)Lo-HZ2<$7THnTGT)Q)y0 z=F$w-Ns3aB3BV*e_gw1?W>jN{TpHCd^%Pfe(SH7DC-lzm6=P>xeNpLeVA#+~3ScmN zw6mdeYv-e#qKpR$9`2wM69+qoI!7>cO{A9sR4sNzI0x;@%$06db`H!nu2ArO_T3r< z^L!R4Mqk%Z)0HU3*7nd@Amcs6*h9$|ZHu-S+6wK<+m^TMH42VbC^%mEp79C=$14;Z zXXt7syaVUyFA&3^It}H*sm00TU$eeQqTy;PJpc`PJrG65Q*Ng6KH2A(7vyIq+M9w zzPJ62_IKMwdwaBfJ06PdXPp4Po9%2SK`4qEqZ<%%J^*^m2*pI4M-OU;z}Y%)$2?w{ z*FW#{JjjV-ifi7?{}&1>CXf)vKRJucqNl&Inch(hAa)N3AbPJD0UL8F@X3=lH2 zXmKEW4E6LPfL4-)9Rw9#omL{!U)CBoEB}G*AQD0>Q~N|q5Adc zQ+*bFC{CPEZBLKWPC?&iC^l?%9OF!10lgCSw`a>p{d#^0l2nK`O%B)c$ zL@`S4HN+?b!WEnwjcN99STfUuB8so%bJ-WiDTv*7>;tU zVn&IilC3Mb9g2g#BNWM$5S8W}GG6F+3_5l=9(712!GUNtq{B%ZA_%jTQvij}rQnky zLE~s*7KhfqQn5j(*@d?n_5B} zqy_k})ZIhE%j0lMNnXgX#1}2a@OnhEsd@J8SvXxbi@s`0G6M zI+_*n$MU)A&y5#YY`=2Zt9JIP#f`Q|5HnA?79Nr4FGzRSHJ{5$CG=N>x1`Ehw( zm#^g4RqJ@8AyyXqL|(LeP;7_b*K5xx2K{$XmTslQ9zVGshMSuvirfI)h@sY$rzNK# zyuodxF7G`GKme?iwpOQ;Ga_7a&wham8WGDl#s~wWc7jMWO-sEi5alqsGx6;3FwJg& z7PReB&$3&{`MFw}o2Z*FYbjje-}R}6@BQ}+Q(xZFQ)@EGcg>r3XZ=OwSp2QHczN}Y z{z4YN@y2B3vPJ6gKN+k+s(tNDj!-?WSt4*G5##hhD<@HVkwyeCE9Uys{h9u(N#LnT z_KeYq4LJJj{P}2$V%vmcQ$p;_PMNR>JUV)${L?JRt~jm0DHmb_FTiV^-ambCIt0>* zXQxkx0YXg`iVF{U3U8uz{FE5f%>@hBac|S?@^6$C$Vjf?H}gE)2{G&r^M;`axiY>j z&TpzAR!`3C_lI0jHnQaEyU@ncA31>cS=B2%6VlJSaALOQ$<-MPbP`)K|&RMcg2?Y0T4V*{$ zxmauu$nVUVzl6R(1C!{^M3*gHIJ}+q)fUV2<&iNOe}sFb`CYWai1M+Nbf8QCWm}mn zEK2&1@v%+@e>+f0Ndp>Uu%&bT>YKJ-`bc8^daHL1xrIzqzjNi?$5%9{Z|_>%r}tcY zX|<31^@~?;n=xh8O7WtF!gOD?kbZihdd*MQ+(RzBP#xToyk*Bi*s}Q1y#(s!P?=ih zjve6QVAn}7d6Md&_0Zbwe0#Spp_qut zjyQ0&U~*W?BvBK#!Z&|4Ky$E!&E-GKVU+4ts`HWl0Xa z&qc|fkpw*>x9+8DcdGA>^?fN~-7!P%sw+l?O(EeR-1Zoq;E*waL1H;M(3vL zb9M83A0ni^`RUo12Kw;DSEtLt^}^8CI!sO0+LmmpsdtqpdFeUJ-kP6RmXiPTRCk1o z{qCBMu_G7E>W@Z<8Z5QnHBGaCIh{c_m-hg=mhORvD|^6){rA!V&I1ykhdr>Fza6Ej z0&#m%9)5?H>KH!vD(npJnzjv{rnU*(r;U0`cYZ3?F!%X6GrpPprrUx5ulJ2(u?0|0|pG zucf{H!w3E$i|bZ@Ff?7A^f~V4A+s1AQ9l+(bn@Jo4{FszV>?C<`7MNY75Qxy$vDY_ z&OJ_E&iVbK)ncwTlb@Q2ME~1(eK^u=LgS5#xWJ8NjyG^g*&c`n_&~SWqEGZQq44Js zc?{+Lj6dNLf+d^4d&6+C!KkC)NnH6b72hnI97n-4crn{dQ2QHWwSbxA#OBr+e!KGD z$EvSP*1b&rqisK*Q+L!hESPewJJh$SyPaJA-ty`+ABbPh-}Nu8#6R|+dd0H7rGB@% z`rJ&@IkUhkrV6j18;A0W3%oLLHm|r+E`Eob?DX#T^7qFGF1XfA)|sz0zh?eVGwPvm z-8!=kd3fI~igSWxFppDj_JB=NU=uljDL0m=1*~V$f`!UI5|lKA1fHmf!`{y^%%`GJ zlZ$i!?Q%x8zt1a-V*e|n>>!u5{X;Gh9=xNK*vG~`$0pG6L*u{DU5Tk-DQ>P(Sj&^2 zCdis3(IX}hxI`DeAAku_T&m*|(I|Uj9^TW-Wvr%f2zHXejAX?j%z%4mbO3zcbg0}k zFj}neI&t%C${l1kLeY>gs0GE!%gkD7Zqa0q?#kEHCtljV>jM8QYc`u(f8`Tfjy0CQ zci~t5bo{5J`RHT&2bb~(Usj(O=}uilj_+A@`IoMG-~f5}zPTT5`epyQPkxQsDD%gE zp?^ugi)-etQD!yDC`c631hE;L&RC>TDXF@|W1~ARsTKT@>IfeRToRyV1V&D5u!b!t zDUhPA41EB0OfRM+)5@IeM2-flA|GEiaD0@e*0k%xl@`h@{&s>|Ab1Crv$WQRAqZ4C zk`SOso*};=Yf7uNinSb5sq4g)KM^&_GPTh3FAZNTH*Ng%!Y_X0iyN0;@>BIST>A10 zj*un)em8ba_D}OV%ewb(US}VBctSLmRwzOQ$B`?ZV`kH2t;EIl}=kcDVoBHTRe zgCB}2sx7|rT)hE!Lw;(THlYQ)-T?o5P5tKj+v|n5(!_xO&~9^LOJk43zKKXL7bI4e zmvQS&nF2b^Ve~4d>QVvOd`e0|vNx=AsIX8ofX4DlwWSW-J_7)?DbCci*zr(VIRvc*WYQw!S{;Zfkbyrrt?q zan7WD#<#+f$eO{&e5%hu*m7tA)?!R(xBdyfs$eYzRvRdvz3gy<19bwUT}- z*$xCnYlxNSr+N=^Z*cE&0?GmvtXNZ3h$?vq%FzMHO^D1>Y*US%LTh_q?9hZzVB;k9 z$vErJ`FlD&=JM#U$(X?evb477i(kJ>{iuB7*uUg2i5kz+;@6TtJUyU3@X^dCh`z1( z#FEYY*VJD!Rf2cNAk?WWytnwNpTw9J5XH-1Eu} zSBl$2-k>8N>WI;R{~sCnD-GKabMU6$#kVRMAT zSoT6f7rW*y(EWOBVE6+lB{ZQ?4Fq-r5l2sr9@6wtMG=G2197cMwppePvb38-4_Otj zqBWCLC3({hJe=US7F}+vde^co{Zske{tt&*`kV5ujCjxHbgnM7Y3xr|wVZQldwFAD z$Q??rTyzDwl9y%QEM+wrO2-nLJ{i0$9^`*S9+G{JY`AL9IZYW*un)U*Iw%-~1^TZ_ zy-tRzGU)^Q8ogjP1x>Z6xLFe;VOum~k(l#j7Tj`A73ck0Fv`71mMRt!;4M{oKrQEu+qL=H|L`rzZ& zFY$Iu_mMw*@y7mL>e2Zw&}yODBb)$ASqivn4=JmEQ1@0H|6UE*og)t=zmeqiNl%i0 zFGh?pe~dqutR{S(=Y7@Ae*2)Ew>ypY>P%e9^R+swHR9%UQ1d$AC9-ZaY(TC^yOsG` z1#P_}5z-LpkL-vXjffEn@Igh_@Gx~-(2a!OAW%CfQ$BKpn&L=0RJJBslm>{bgZrtK zU_vxi1QRu==|4=hq9RHM3p<$rsh|&q6YA2PR}IX%V)MXlE4!YZciY0mCEw~;H>rbI zUKzRWYW3%q$htk-htobXrara$s%0zIHszxAg9G1NJEg^wpOx*|wBhboN${2%4}ELW zL+ZCac(LKKnYf+{us8GItVVL~0nRr5Ar0=L?e+A2Z-CademHTjpaHGqHiVO<3&mma zMo|!ABC(V8WDD6&L|Nz5#6S9-ow5_jY^Sf@tRV8?DmfHXXbd`t-9aP=Ql$2HG+v0K zE+W3QHkJze>l1uqSYNNy#Rw~xjAN_H=qKa15(6JOCvs8bmy!RD=wvbCkGbS#5K-a%mG-3Wx)hhN*X^p~Z9-jBER42mdR|Vmy%ZP1yh3Dz<_U^1`N9BVElinc zg|WS(tcLbeX)qxsD4T<&VT2u_!4){Krvk8CAt@M;2$mULN|l6euZB0IsV8VB78#4j zg(hd5%GYKlBqofU&j=DtrZ8F3)TGOQd}&wuVzOcWMIPg_LR+6Dld(6HZwuc2Q2S-g zwf>B_K0i4dGV08cwRy5v{c^i+nB3qAJa`3vc;~DO`J6M+uWTV-N{8GD*RoG;SrM3y zm7X#F3#`*RX!^a%hI8x5wCaV`{MrQhO_=;TOboJ5=3lhGV&^|JlC}D)^>^rnJv@2K z{cATr!*edIIj1R|%VeBl&=<`)F*mOvCmJ)Ec)%A7Z$XVxkt><#((>`%eKumF_@wZr z>UwF@7c4Eqgs1>5sc%}8j~9;*jJ-^S2=+)5-2J0R=#x?G6AH#eoq@_vnmYY4>^MoS-^j(kSti}Iq&mOr9Cb$u7P<3lCCYMr+A zhp4nnUe&yyvA$>amWS@zy`e7qZ%bE^>dBLLOzC=T`8g%`Om+EPOD2#KV< zcAFzSBtI(elTV{9RjwjouE}VxidOMe-3^Iskm|r#Oo$!Ll5An%o#7+$De7t+r^0Ap zY=kQxc^y(2nr)yka2W9zW=D-Npe%I4U`@co1UJA~0R4)|pozK=5SfoBLE=g*pol6V z+T{$Bf{H3M0LR7M)z+rgt0sNzhU_GO0u&T*9dt~9mP7mT0OpN6eInR`tc zzTHjk@RH`@rsDm@H;eyO)RkH`w%pQktmRaT?v57JC3Mj=l_2TsV>CdeHKRv$*;F{4 zZf!7jpE2&8&DK^S(A)rveiAHt3{%ZsP;XCdN%5%!Yq&QG;qFRbyOY|$Qz_0B^<+v! zDoxQ`R~o6Ml4A^Hdv8E~Z-7~=RL~z9ov`*PC&Q1^a8an?&*}&U{>J#vBoQ>K82_Kr zv%<;_5nXf{hs?##as*CmjzHb!&hN}ukDOQY{nd9bU#~M&CmWOey=8xm+o=9kJ*FP` z*}LRlN&np>CuRQpS*PH|iyFGr?>_V|>Y|&cSChaLSH|l~r{T2Kx@W2HZ8~^}G?F6O zeWB(eK%>g)blo%H`3%c-S^RHIYQ|@X&HZ~XL~n#)GR&a+8N}#V;j(XYU}IK zWi3GYK7_OaD`-@~nCza#z8x8jK%J5rI0=^pGE zKcySRx=-SMc7UrMe_Pprv*Ri}~eTUGIY_8GVzkS>Yca+nka@Ecqm|B;dgr|M+@g&0%TD8LikqM5VGR^FPV8xqvB*BBKeB_9eu=)DrA_@G8I zVmxX*X%yJI&}!uMM33V5DE4Iq!q5T8*q-r_-UH=M9MY&&iOZd#m1e|44fB_GOu70? zg^1lVhup5S7N^;WYs)#yF6g*lxOQt%FOz(klq7;~ z5vcw#fwVSQ32}-<-`!o7B)y@bP{*frwYz6aTuzz?SSBIhz?=uKy+)@4vbqr z+9irzE4nszJ>GSwOE;}+A)Y+j^+A`exr;P(wI7y(Zaf=7;E141ee)ZV7UmSrOGHW9M% zdijuSIAl1aiACxyvBL~-@V=X=@Pq$AeQi$@HmA14S|sOFH>wg=(?lf|olK$<`Wb!y zSob9tsIL|UQ$9I+Y$09xBfncRWl3$Or6!&+HBBLZSzGHlmv=0lZM;#vf}B`6t28NX zY^>QxZmUthxWG~JK}~`!gvfJ`M{@H@(U8ZVv*a!HE62vj1B;q69l<}z)R)G- zc|$%%_w6)vF>Qm@T_z76;98M2`oQihT?M>vFEOt5TZyvVZ|{)uhEBafF2-y8q}f^wBM}sB(p&km zz{^gVm*glhx>I(;I*BZn$n*7wF^#pprJldCerNsedf`C*Z|nI5^%vLk@%qMkzN((A z>%6w}_npGWon&SwF?RYp`RdO0PX3Zk(q7*oiOu>>!}8`W&Ew5NbF%^0V=7Ok0w-og zhnh8M2&a`a#r{v{HQuOESo$1tDCwpVhy5tXWA`Z4XAuY+uA_o4D#Pk6uu2gs#7{wp z2f$Pncptg%lI4Kf*V(w;4F#GAMuLY)NohLfk|f9Of?MYzv;3k zC|hNlHMaWcic863eRDl)R{i`oZ?Y+s@pcn?=brNdbrZ6`=kaArDh%bT))-27^P*C_ z4OUsFW7P`vdoOzvBIPD8LJbH9!Mn>wykiO+6Ts4l4ussG?fcO1cNFL)Lv(;V6qd2C1YSpm0ja;`*3g zh1&Tx64uj&*qo<0aMf$cTmZa4L>xAB%5WY|nO?=y}cJCZj>W;W?xrHf-Tlw8; zXmKd3OQraP_hQt^l82e57<(L785;i|)KRYkcH7I!&MsrGalk0-wmfg)r&$(SR#*g2 zjAYC$X5MRRHSq-#@tMd@^oGZRvcX{w+oQ3d)!={u2}I{$Oh)e{urEvohXc=g3=Zrk zhtXtOX12J^X3H%WlCW&B@Ky^k2#%n|7()WrZMPk;y=vn*+n{a8CKN>*dUeB`M*+)v z+=`z8*(ulKStg~3CbP+uF-I`cPqLTZ4gV2&kUj&!|60RR!#acTh=E*VxYO{YK@ds? zv@Z}z3HAqhE;tzEO=9rb;Gv)ZME`QIFDN*4F@w?K2!pY75bh;1LA&{(H^c3=ReY%dkm)RvvtQMN;eg&j?v7^EZ)71Mnklv83>j5(N zKP=C0`07}BpnA$ZOafmxeo7LsXXb+LTdY3BB5W}PKaI%#&Wz1PI zoipaItyiY%JmJZ8Uf!;tAk!=;Nz&OpbL!mWOl79x3zxRc+&+_EKXcd2y))mK`R+^| zH&e!F-dKGDpjwjlVicmdI~&8$Ox}-P;%EfMSftyNtVFIFa0FTp#4C;=$5s>t?Q`fH zEUIW0YLi(*OhWy|a*QJ3$Kjr4=k>C!IE-)=WFr-YhiLAeI-u`YJW!ascyZ!Y6#?xL z`aUF~nyAW!R6KD=9zn>8V%(zg1OiX5NB4V{{>HLYY+p@XYVm9uU17%4|L=>Epf2sb z`Qpp3iTB^TVq?cNj}^u)Z*C7F3K;i<7N73`RI_E4)4)qfUtr<+X4eef1Sx5D&b)Tv z)P;+e_T`%|>bOQoUEr-Vrc%0W;G$1n9dOL{J8lpjSrJT&sg&3rnE%PEgZ3Fg=N93S zbAttm8O@h|GUq&htr2o@e7V}Adw|)Mdk%0Gcq)?&_L9*MjR6?aREp&dKJ0p*FXyZ( z`5LfOd_HU)-bXy5&lp4DL^>SKM0ta;%89`wFvX6dTC=JoA?6*GBK99w)IammU0jUatsiUJdj!089 zwanQ=_8>`6kA-!p#DEsX)LlG~p9V{SaBv*)6?6AHP#?5|?3p7v)e z=+I8a?Y5|mx7GNS2+LU*1$WYq^yA2W%n_pZN)`60?2n-%Z_qF90PZEf#gBID0Xj#B z?uHLQTumpP8juge6rlPFuU9OHQ`iEU+gEY^nCqvBrUCM)g9ll}49f}KrTKhXNs+c4 z#8x%i?3J@sK^TY5$!d1O$-DE5FEq#|DE`?gDeb>lSp2n!e64C;7-$49!?C-RmzfK9 z_5J%gAN}N=^Zj`Ptz79xCjGQ-Iq%VBShjG!I@>T08m|Ub@!OOOs@-edygM*0urMI3 zDUn<=iQ4O%;~}B8wOMu=lTxgP4-=bQ8$&9hyI&^#@}Rs!K1xG_K9Z2782ZV&xkR!f z+Ei%bn|kfOsITDb^N9u@+$7{Fj>Bp{MRhk4I?(0RE^itwmPf{p(L^QJk^EAJTc~uM5->d(1CPlrFEZ)?^2h8UpzZV~jehEo^n#_h7#LZS`>)vO1 zo+j?QO6jd?K=j<^5{byO5G^QQ-4 zKA1nUmEgBzwbt=TPLH&qzJ^Q2l$ZjMVUI13ZHaAR)eOxDOtJzt@zg7L~YW~6Wp7e`pVYP6T!21MJs8=|B zgMLsyq!$f(y*--Q>dn_@c*z~J%3-^~ny61iYaFb^S}`-?ieDXsJ&w{d{HH&@b+MQGHhZs5W!5btw_B@Eob6H5?0QNdsC%9)0aE-tLT84D4htKf4`R94@fcsVV zPu+s4Dgd@>OgExYibQi>uD!PPmV(rwpM(gTM9&1!4F=uCwx+k??yAt*?}GSar@ zWNpU;nQN>`r@~TmPFkbsB%Jd!^zvTkK}6arRhpDH8M_vX?;jgR)kUR9hsp#NDMPdj z8O(}UfVIf@lai78;jBiBvFh3B%!(h5KhiWN5}B+p6BHE#PoI0q7uOYhJ!8KwhuxoZ zl^RO37hS%1UQM0)vt27EFTUst2ylxQXi~a}FSQcADljv$V*#gazyk2+)5^4Hy5x$5{NVuK6doiPs$va`PPFZx$CHnE z@}EfA;E;$fvk~<|lOmU5n6mMX!d3TI4^|5Xp_;XfDppkDb7tAh!|V@*sGdt=Y6MHe zOQA%fV*(VW+o=RB76pH^B%Ev!s|qd1uVN8p=a6%&Q{3S^$<}dVBWNYJT4thHJf(SW z*c)`4Fod99h<@O>urLjEn&}KL2&3XDRyEF|*(k|_yFhJDC<@$Fg4cBUVZX#ll1dm3vSh*ljJk)#~}`(JP2FueEk;LF14I>Gipt zdH$*VMPbX#Z(p=^Zr{e$E-TU1g=>1petT2hrM|Td3wnOSU;6byeP99M^8J`*5j)rJFlyXF!_NXGWX-JBjb$0+{ z)h{5!sBlWAPHTlN!nA!C*Ub?N*HRm;tBxw*iYlQdWMD=1G%saH$Uv!R2nqe6qbSiq zJZ2Q;MFrNackInk88MkNJt38S4rd>K`3$GvFT>^0V@C#tDZ8)$0FA8DfScx*z?0Yb z1?Gqr<)?gr5^%;GO~T(}A<9(~{Db5O|N4~r2bVZICf5!0E6Ic$_rxvX;VIvqpPkdN z>E4)oQucvM681zQB8Pf-x0x%iy+wOx+pj=J{qXRcEB70oq|%$c`#ackd-oIjW? z{h5@D_Via^D_amj{fJe0Gi(TSrS=Fv)_sKo$}D%Ia=s--(;7bfs?m4o`JM7^`FTK2 zI$NFS_zCEF$y}Flkf=w*OkbIcae^O}i%O7&Dl2L;=t8C>Nl>wj62!se5XO~aYgF3+ z@C3pg9Xkv09(nX5Wxzi%M;bIdJ0$|E4Mtl_Q_SI@*>Q#-%l6gTK&8Tu5gL5rvQ6!C z7hikxMdXetO{Pq|ra4w%&QIE%Vf$&C5lWEPGtMvvA#_C0DDpABR`fBPv+! z^Ms zL(NLR*)F{yy(|4u0w%p=SAd#`mNQBOPhGx{d6C!@dy3&o7=>(&61|$G-cZ_XD$!YJ zJHo=g@CdSP0?oEDv+F%(b`|0N78z8SR>KS!lCk&7qe#mfp`HX~HXW_aoG@sqn5mQx zXs@D?qXi}|OU!`Xy!_68*|E61;7s_mHuGR};+oSp#@~5o`s9wt;GSnb8!9Cs>t!<@ zCm$uB{Db=B#bXD*a&Z-<4Ygt|%!*y6bl#pLBux24LEA@NG(#;;rU$Y6;4@7anWM$P5&1o;6hEz&sCYwX6yx7C zQRpnGV7R6~J-CuOK<)Xl4w`ZLq|z;M#ayvhIB{vy?2Pl{-!Yd5*#`eg(bM*6Uj$qA z@&6Hm&}$8-<7t`Nu%LlQ4~g;Wnult5e~h%pX2s5n2~i5j05?ap=1C^yx;YD6!wtTb z$N>g|Mb?XN^9M^DEzSWC7-T17016|AV3n&zNnf2yCMkIGUdZa%0U3VS4K>jnvGQSP%5!5lZiOsnL6D{HjXYkV+I_u?IY{ZBtN2K6kyX}C!)jQ387=0%UhENXZR2YTzw&HSBl_12$9PUK}2@m0e zfPGVZhAO)pl?$>US5jZ0cz8>KV2jlNizc)K@;Q`AQ$0QK!N4do(8p1OgGUa)!sAsI z7D7Zweh*Zk@gET8EY7LO3&>HHZ-5A)I-8c5e#$d#?ZP>=oqfuM>a7>u(S5ErBfeB& zmyeFmn{LdQ&Yx0yem+IMtP6F_-$~X}UYUc6_b{`A|5dqWjgzdlkj7+KQoQ|M-s>aL zSRq!92{FB|80V{-?Z!sEB=i+YUvYVnFBHo~eyI3p@nrFIQ7p#t4S7D_YlcPBob>tx zB|(%#e}a!C8WQ}L#NNao6XOXnQOK5&mh5#!!*9d@8~}J~DORF&GsC0D$5@ndgO|1e zeEI~&OnKzQ$T38#k82rR#2*-Kl5y%gphr*7>R{kyw6?}6T^MC(OJfx+{9>&FSs|_u zr-hEHIqG+%A76LN$?vPbR)2Ho(o1AUq0_C)qi~1w@6~3I!>;nG;M%g z`9AhvE%%(#aW0%+ex^Xox}XkKk1;PBg3$QnG>xm?Me@p~)cx7!$0#3k^x#iZvlg^iL)fWy%uiM;H>~ z=qxGx9!z}raX$iAO;jhLnwchAS;AH0H^lsDya5gz9|wogC>ISdYIeH=Z3eCu+WDpF zG0h|t-=B7cx<|!GnS5+9|DEKuLF->WT2Q;nzd4_5YHVKmHKkEBs-d^ld&x=u!SkGx zd=8RMcSo)mTcd6xi}sP1-@mXa(Hu{%>IdcUqo0JFc@y_TrT3}E?>6#{`MSECPIT+) zYIR~QKpzmTt;_rTZgd{zMPHpx*I4TZxJj^D;4ra;*CPRrPy#BGt^Vu%U-1iO(O(<$ z0e0lHQs+*U&J~H6=jwdcL_t^9@w#q%g7Y=_PEx^+zDiofD@a^(EDNy?+llU0D%9x+ z0fcVpMrF00bh2k9WJtFwGp4jM9+vw6vJ{Kt64t>&*%2{Z%Bp9)BJiYxWIn3^;44p=-juIy(mLnhn)cmKxDS>-K)#CjZY24VTBW|Y zKrRN{>2#Gn?q5k{{@{WZW6oOJgpRpysHfFDdFY|~sRmX(=YXvHB6zfk1eBJB7=Zun z4TT2YqN~o=*XQH$1U5b%F-EFl=@>5nu1l~0b=?}%2XvJk;Y6_t5INw&HwSjGz*?qI0Lz-@I6z2MWb#m3O(=9eUUYdEUi`3D~i+C%xUhJ-f}rlqP}3dJ*@tu+0rnD{GaoR zkCJ7|QwuhUFIIX-)_>frellrcZT+m)zMCh7a}UnrpSuT^{b zT7??~3cR61!?0b-Ign)>2_8jg$Q~p%A?V2|yaA9d?vg*~Icz?TsVS08^6;*LqYv*z zg|^@b2g8-cNMPs@2Rj4xS>XnZv*_0e6qCsorkP|QZ5x0PPoo4QND*Q;Rl=MMb$Z}1 z766MtlOh$}cL)ndTLWlw1x*>#zyix5()8gWsuQXAL69N@xHe(>4|3jxr&YKWTT?Dw zvGwB&*{%KnRr|AF-IRW~ui6U=7S!8Gk9s$fh5U1KXx$1xPWj!ld8ZVm47!F#Avq!Yst$BYJ@_eqIpuWLGXc_t-@UsMyt_KbN#+Wx) zGU`kom(QiMVMvOEHf~e6(hcZEhh>zuP0Qw(8Q~#wSoTGc>wu(x7I@KqTVW!fJ0jCc z-AZVA^f-N@k@vIIg_C!P$}|v{@;}J=cWI1(vw-l8P;u_!%>>DMhW||s4&l<@{#^ab zZsrhdeo(49?1|ATsD$jk@ST zaBhP8>a(s~ls1{5?z$>a65RK?c?10PDv=Ay!5FHTgkV)JL`OOwRVJaLFcRYRl37Fw zv@^=5s!CNn1tMQnb!C+(RYm*U%aIFl2P3R}!H)LQ0;EF9DH;4IH&&l zu^MGje|gWv8}7MkFK{Tor4ixB>g?~|@Xc?iFMRd7musfgbale8F>=iu};&@TEk%Q2uk0ka-KI9Bi5u1PV*Tp(D5o2X?#VL6&; zq5%f|K&=iGns9c{rd^U$QH)f^bHa}3+6@nX|N5<$EV*5M28mFOaLy(dcWcQtyk>y&H>AWQ63Ww^Ad?f?O}p3=Ntyl@l`@N9Cf6lJy~j% zz72F2c!pZ6BqnlMz1@cPg}0SDgsp8B#ereaDFR5veRu$mrCIjYM1`0l*bsQ3#w)FBKnK-cTJXzNr=n@i{jPbVifpOcK9NE@hM<-fU_gIJ zWy-IHhq3`GAN!ltrU0d_%xb5#e$a_^7~U{O%fz5|1~$laO&G=jA(hak7SQst4qNn+ zXmLoTkcXaF`ZO?ux02lebzO=y|t$XFX zp<(AsGv;q6q}6}vg&}i_+&3W&fn6?q8J2E}+pk>yijCavdeF7UCBTSytBO1yI2_=g z50ii?7nY#AomNNK!Al^lkg`YNv`2YGkYRe^fuSba5uJ^>qyqXJQI`&QUnLx{`!I0b z$AuI##tNju;0TG!7zIZkM%hER9)>4H)$&BFu!vv ziAPwo0@c9yfevowu=k;dK_i&4Qr1bku;H7+W7kt<+|nw1dG05_B-c-N+tpU`3)gik z9$u8bgtYx^b;H7Y4_&UFCh14ARS9yty4gcsz9EyHJ#*&D?GIbY=KTw2ZQn8b0rf9A z4~iq9ImS;3KY)}>1AAc6)jV;f$Xh8A^E9CEVlXk3;2(vXoe(w74IhL#3Uh#FVJd?Z zjApxtnTQO;-7SQi9NMxeVGQw{zX8QjytgoXcvOo~yu(Jg^-|vm6hTaMfOf;+`sq+t zYJosEp2@#f3_*=e;2AMVm`TQ~L_{Wj@XcK(*Dkqka_!6=D`zj*@aETld9Sbz@CzH% zTZ!$!e~};j{Ei!?xWDRbk;NVC^2EJ z;!qwrrjZSN12cnR?BFk-=8Y(e;X`a74=DL}s2D$+hETKon>StleMy-wv^o7um1kfjZcuaz30vKM7yGGGaBc${U^Ec(v8X>dUGtmqpWSN@C)!NL?e;6U9Y1L7h zm>wO5`Bt%xk5FyVJNhOVaKc78gz9?OYs~Q+1__6kvw#`GQ%sRy^Cg7}C<4vf(0?pQ zro~LDN&zLm+<*7tDZ^n4qV94cF{S^4yb{UJpY6+Pfz$E@!FuAGe_vk>vibchW}n}d zZ%6_lF51*hTw37tiRShNx#=84Gp!Q34w;w``hBiY=A?to!RbMvHcjTFiB6QFdRwXu zZg$ce_e*FeuF{8<9ZY}bL~kKkMiYOHReIO`M{rThN_Nn?iWo{u1<49uVR*x_fx|NG z9;TKXtTlAU7LkNRsKNMZ|U z%oLGZeQ681JG1ere_48w`ZHH@?d5a7^a|he@hg{J_rk*kODdL~C4>h8HMZDSuYF+Y z#m8=Fz4)H7S<}gE%1gWOD-Lg2;6l&y01d6c#v%?gF@TRNl+joKbP5u`=_b6B=N`pp zK4hPfxQHV2z*y38L>ChqL_m{?xT1Feus%|z4GH+1KtKE8O*b8e<{=gkP6N*O2@0JApd<#BMC)-`l)b8n&%JuntN5Y6$U${7Ie62{ zH<4ed;hSEjwMl!&|BHO0TL&ZqlpN!EkmF1rNpY?PT^k&bV7qibz{z{Lccm%hi`>Ge zg(+0HlNz&Eumg@BQ)foO5PRW=>{blG*o3 zGMUMQK*(}{5Vk;A6e1cHL0nKFC|c`6+;^d^OSM(7wc2XEU{zdtZ=tQ)+LoGDuC=ua z_tvWIw+ObqYO5K(@6U5)lAvAgy}#c-zt`)BFEd$Y=FEAP&+}Q{pZ9xp=dR9|knQW- z)VZy5cjunYqn-Lrfzu}04Q&>+X-a9I<)}q`iN09q^I;n3Q(_qK2^(I-ev@rvWv9aH>4TMZ0jz&i zRr8Uai0IL?5A^J7`X}|z>m?sRXcoUkbUK*R@n?tlpkuG&1&3tpQ_-vCMXuTFwabdl zHWg;d24U}@>a`o{ferFh0scjtWNKrJR&S;PDF;(2NasLI!4|H8UbtfP(r0)0pj(P} z-RvFl$`;9cHtIt3duw}73B(VZAmhB8UGxv$){?dfOFU3QO~)$h8p{BZ1}E_>@Kv}+XP(Do2BD!gy3 z99@lg2=f#r9!%m>1@lGQ8t|=-fN@n8re|!NNslNb@!Hc+TYE!akJt9%HSNM}!Z(h8 z2dnQ@;Tvp<@I`gGJg9bemj=~*0bfwdA0+7OpqkE>21h`bGpIr=d~n2S#h02Id@-3y zM+c7&inhVkgF6Qg4az$QcMl#LltP2~!GXa|gYuv#i33lI_o&{CEDWw#s;ZK>dFjZ~ z9ZRKsOJ7=gXem$)?>wqG974NJ6+CZ9W(tS<%09IN;`^OdkCe*@q z8^F=SWTy6us;Ew#UJ^+)4>b5dn~Z?DHzaNscZ>VPmqeXS42gOC_>y=?)X&_6d?tQ> zNIVM8M*L;9xCwtdD(a2U>v>uh9%JVG3u7-_F66y*p!3;>-^b^_2NUf;+)$Mepq%Lk zWjlNK{$eavD#qhQ{rw~5U&e~XSiD&L^la%5^4Y3=3W>73Ha8lZ=qG39S0~KOE$Ycq zKg|q?sS5alndzsQ>8F|Lr8F|L=QGoPykE5SukPR3f2d#H*}uF0SicnN z&-V}XZ|ax(MK+5j#*ErS|AIbM75ixUm{5c9^=b2gbK`H!1{8&UhtYpPe?8XG4sI)6 zmEK_Y3#b&ry;Ax{`y~EW)*5?B=$q3esG^OyVR07r5CyfyLA?^)x3|b|;XQU~@4;@u zn)E_&?D&5?b@EA#f-13-t7jQLht@M6*PV-g-M7`PdVQaMgZ@GNUcK%H1Jj%P%zKee z=`b@>JsRf2Oi&a>wCSg3(hT^m5~?d|ks4TvMLB7o0$3U8mxu+A9D0tE^|;ve5Zw)OAM_iOx%}du+V5{=N53Xs+MM zbbGWG`L36KqUh6!)63Dv&e>Z2*GfPAp84Su8tTLFq9|KahOKWCx`esH0K0-vpU2ci zzEEl20Q*JP+g;)>r~hvHU#3g1PJ3sX_>1tMdc7sen}ng03mWaClc(42K%namWtM>uzJEQPhmZ;*$9-bJ^S)bFJ%Y#}@#7 z%hlOBLLFjLqe;wJv z&lF5P++8v?V|WfuAv$L8Bdwi@ShQ-5&_ase6S0L>`lRFrvgk1?ArqlU3JU`wH-H;d z;MN{gL$wlokpeAb?AqI>Xo(L72rDm=QV15ssRe<82x70+qEtRfDxEy?jq-2TgiTYX z*I#kf>*Xsh*g3S0O@I8fpRS$9qM7Dg&(=o|oHSgMFJE%~P1oLnmgCf|^$(mG&RWFg zv0qz5onLvQ{2x+Zq%af;&7T=jw|wceq1W$OGnhL6wDVtBnH5FGI&#aHD7MVFCIw6z ziCG`!o9@@O~7>zue6%oJXavsJ#1zn)-BbiKdg^|&57YT)VOCHe7}(5bu_ z)y!jvNTvXhPS1ZsJQttO0yuSB+`uKe=v{&Dke-shk7q5_o+TWsJS&wl8gY8^YxDQw zmKpCQv}^B$_k5M@hlP%yF-7R^Ld#d>y(L>W>mE1EWbUh1;+7d#qW7RH(R&Dw;OCH9 zCkZoDi;VZ+SDxhM^8*9#z^f%R0{U-PM&Lb+K>jhtF*gBUvJ?gujZinc0J%9FGYGLv zeFFw2Ha;-K`-^uX+j0TTond|DogkJa6dq%|fLkAIEGdz2L{uXqksXmekg8$B#l<4g7=1a8f)-_b3R61t|{F-hs!0LP3rG^Kftc zjjEqUi}2{Z=I~F$BRpX~(%}CrjPw27FpdW=)!+@ESKEd5@m+W;dk3={e3Gqh+ua6^ ztJj`_8%O(a%EWBaI@k8$XUF&9t-?c?0l!)+*%mQ*(IUPT2PUSC&V2Af7&c*S`hjwO zQWl+J92Azi2Hm~7M|J zGk;+IzWFcBKQv!AzhyR?ofQ`CiSCbzQ9%hSEsA7NqL~WN4p#ykW0zMH5N90oN{cC3 zhbFq=EkwIDH2P7;g*}U>;RSX{(11_7yA~C{7mZ2~LE&&eY`1s((h0`ZEl1YyHA`OzO!Ht}Df)FpH@sTji$N;4H%#>9ea91ogn|=_DwI| za&tL1Dc|;Ra@whCFLf6m#5rzUelG!m(OS3 zWj!C;*?O#WMEAP8475YEn0ywu$`QuZuSbZ&aNMd}4@cc|4PL6gR?GP;n zpC$YnI|L^W`8ZF4xO}7u-1_f5)zp+S;ng@swCoBcmBZYYvFd;F?(#700Q{ z)bP~BQ`b!0%rj;Cr<#PR{Q7L_q|4)T!&2kwbkEw=2iPmPI=loNs}Th;6HAR>7WaAl z4@j%L%kQ(X@@X4!&HBeg+~l8BQzByV(@jpSD2n)x-+))e_&xr+_A23rxQh0ww^ftd zUITvw^x+eis*EYFMfay&Yk=K?Ymo;dB*Szah9S{1n_Oq~_qfU&elOZp2ACID!Mdf^ z{U@Oix7~wJ`G==Gb#ys4aka|2<^7`k-na*Zb^EWl9<9GP-ry;Nsk9ipsn|I0tEPAu zE)*suKK=>tCVc(P$A~_~@0EKQF28Qm+1;ntQ`+|lc* zskJ$tr8_E4+>w@%LzVS>em^HyX8bx_OPjm*R8u}1gL5c~6IOt~O1mQ6k#@xa@giI? ztU5&Uk++wi)e*W!w-bq9L^TSTEHo52NH%lKO1`ww_g z-2?m%r&jKe=8p1@CEE606nYnz)YV-GlB3;b@OhQfE_ zVccyF?~}&+JPtTFJ*u@F9)FnZ^d?J}w?c$X>k z`PmVMy(2U{#zk?SLKZeWPo0MeKlsGFc`g@ayENOkq-+-ta=#wgZ+e|@6<+6gjFHLx zmcfNlJov=SnRYu~qP=2%*-l7rT!|oGbfqWp3d>$#cU!zT7qn+3@xJJuz_V81S#EY@ z6W8=*c(+UjqG|LrCTvE&y8&6>wD35oJ{(quk@(mfWnYMVH6pqgDq<(gXUGy#s)Tjs zq7NhjD{_~|s!NzcHED|_1hlmp2*b$=1e{)HTS$`9c#6)FARkFo7RJK57T{GE3R$XZ zqX*1j5q%Br1_5NeK<^yck64%bG7sW7N4=hBNJ!d~Cr1E!1lu4*%H#?G_Arb`s^#Yr zimolr9Ui@bw`x-PEKJ2H#I2RCFNH)MzABTevI8e*IDX69T4hoxmKKw zF+)ruzJPp3PI%-QAr9!sIZ$9+j%2Qhfk@nxz9fBPTDrt~qg6~>qjgZtB>)R*UP@FS z45I`c3!0b^adXGu17B)MT9b9Yn#87oYnN|&gz0?HV4~!Z+q|Jz`SMH7dfD!ofHewcZebnu~FX|8Ih=6XVzM(-hyTafgRg6hlBOhQ< zo{Ew|GZ0F?lsuG_b|zV}kqAyjYsh8}q3YD_YN(3}E}$-tP3n{1qUk`q02TspmxG4@ zt66I{rjFw`sUWOP2~XA#ITOLsIYk3?omL%nz^vfZBf*SG;oZo(m&Y!=W!DuqNVZGH zF1R?DmX*saQBiz%{~I$x>BM=U7`RQBd$jN4<^7JeiG(w-QTn$Tu9H~etV^%$x#A;v zV2ZWOz!rFMLs0PQ;#!Js*=KA*m$8$<4`;Ks?aSsDRwOzYEANnG@jKiqJPD{3IYB6dWKn zID+C0&*qa|IWmpv@?=;ji$)nVo_XcGiIjS!UY+)MTPfkH;@ddJ9h*dxi_ti&5qVKJ z;S^o;xgF)-mVbNO_s`kzX!!{?_SMsh0hvmR%iU-w(YUf>;TAzk!9#uB3ppsc1+h z<$$f;%JNB7w$z6^1O0*10+$7D34A%A4}^Ps`+TARLdwm)5#J7<&H`-+!mjRDPxggN z!KjKZ&X=&Q+ElEVT5DLcIczc8EZZ{MI-A}q+3L)7btFKq*0KFWf*q>M*GYD%4hrBZ z%5F8bY$z*cAq~P71DzeeFFRTEX2*x!GsG{F@ zpfY*;TU#+}fSvGpL$!_nH;%_qDAk$ZTF?9lKwi9E2C$3E`cOPf?Rn@0sVgO=XNR}G z{PNb#Z@tLu&Hd5FQ%=c6`nE)7oSxTT>*>32 z&8iv8md#kT=EA<7*WTzpr+o09d3WA9_e(XsuJTdvI?N&)6e$nsOvqYzgsash1}FQB z31oE+C*j$hO-2*QdQ5euv;#h$GYyc=3$?F+*b=;DPGjCN;8^Y0=8y#kGdO%=Ski66 z8~T(6Qd$ewNya)O2c^Tfy#5M?1wCmPMc`|&m;rzDA3=KK$a7U-G&eLlHx$9zr^HS~ zodfJ|WFNbiF?Q(VC#2vcy!pq!5jUQ8=_NPs44roR3uyb%VCEkQ=ir05%0tp7;JUoR z7u3a5oa~E`ZNi)*;?yVxKkLWD*@FXl?b`0p@r1sYY|H*@ovVcImEhq8}} z&G?v)BbcQ})fxpKaC4gCY8wbW##5%_^FwG4?&U-)f9epzIugvn<`0p z70{kFY%?1n!g`x(u$OGozbKYY)y(w}RWsP@Ta*^m$6IpE?P@Er*SB)^`c`IZWrkKz z^4emdSUv{!`q+pUHIHm_M6?rSv?q*4LEJCbT@@Hoz@V>34~8v_%k&`^V=+IX+f~~l zKF!`%7byz!)Fy@!=OvCL#uDJRPh?{)t-J(>>^ZgHZ7ND2IL+FohNg<9sokCs4pPsE zXNO0!N}gClRE?^j6OYb7uLdH=W2(skVtWGO&jNlHb$M>TtJL;{I-?XD@@udS)gpd{ z*`6DDNH<`6@D3=lBU(9yn3O)jb&tJ7&_8;2jXhlO=5b`?#|~6zjB!M0Wodv&4LAZ& zX-N@zXAEf4i5wdzjHu$u;f)fYv{JUKc2b2M@?X2kovBN^TjqcHkyFnf+d_<;I#YdW zMw7fL>C3jAKke>Y0@wGn*pf-_g~ijEG4AhJ!nUjfBmR;u*JZ7PijUz#oex8Of$U+ml?3aeisPU) zG6}>%C;y*MTomvOT0e#B$fMV_I`j4+Y*uaS^=Ngoc1g>`(xKy$ zhPM68Q<>|P-&wyL2$0=t7aPWoYY32odK;j}1pN@oAG>3dX7?)7omcJy9OTZ+{*~Y$ zEP6@PnK^sw%6nq4FIx73upm5pp{tR?hy)eY1TYBNSdqU#l*h9IJa55~AIMt>pG1Cq z4# zyP20`KK$@i+w^+3KBQIzRt3QrF-tcS%G;+D9eiAgI0*oxGh%f#zd}JGlaDXH^DjwJ1BYAO~C>z9wOi!79 zV3HnoKkNRHTXLIh{#I3xcgu%l(I)Sc;jGH!Cdo1^t&)EZ%2^z|KaBGm28R0mhChKg zsaAP=s^7Hcc^$agok8$NENb|ygh+vg7Byy&i(`mQp|F8BV{mXvn5~;vI%|nH?Wn0u zNh|x)nWc-JGn{~`{Ali)mW0RJ)PG7{G)&RjCsvff&sUU&SE;Ti$~1p+J^kVYyvoUH zx3sf+Pq&C-V?L$=>k`9kOWm}vx>YQvZWRQEw(d~3XzSijUqanI-K+76W8M009Lts5 z3dhUgD8)KKDVMIxKOaAp`HIJ>{1Q%OuUaDt$iE?92llhX_$fS*v7V% z0_~^*?Wh9ZQHA3L(N=|ii0r1F{TmnPO zZ=y0lpV~&aA3Nd2ctyCA$J4K}+fWInte(Q(#G_Z4t-uQ7gT@u&(P1kc;WgmXv{&)k zFOS;68;0~1B@Zk4f8evADE}wv0h}yTz?>{SBTRwa9z=WPG?AHOtRwdO*k5B(AeM_w zk4ZH#cGk4dOcNgf$8wB$>(ljOcXFTnk}R%~H_IcC-VuSnmRrKjO}Py%rLJUgT9JuG z^bi!M&s9qXuEWqkcn>!d+`HVz+yIZbEl5n;Yz}`r!2-!sp~RywW6ca!?5;9SWmV9% zaR7;e=yNkdR&jB1PjWOVB?+I&`5mwsv8js1;qgZEg9o^T3pJ9V^OlE@MSPERivsc# z-d;kRkos{@sN)boyccC#H1s-m3vd)vblEf-BHF9?RTckD`xXCJ6p;*dQ=|u;?K>iy zvZ2h3tiL|FXi7&eKYRM%hV#!{x;kWanrr=m;OeV(IUl)U@j2ypBfh5M%-1B#ZI?Em zw`z%VO2FA*_qsy4blZ$*eA-E~rUhf!mQylcWE)RWj0x|pcinaNBh9hE)bbCm#N3^R z%^@CE`tSzk{|qI$&+{TMwJ_+bBnS(ggQrTuCv8mDZaI}(q(*lx<6gk3Ez z$rT7~61D@ZE(yYZ;V^ZoWoRwkCC~{eV3of|*${!!R^}e}t$F?r3 zUvS69bT=E@QvQ#hoc<`^CvVDKm9hTilXeA}Lo|~eR`0-|hg1wW2bR4WV(*Ad59L=; z=Xd!Ne#z&^I79;-fnq|R0t}WjB^xALt74apoF@&LVsfGmVUc>lvA|Z^wHm2yW#Hb!Y zHVb1*_13>Prqv;>icX;4mJ+haFlr2Xw92c2IFed!@a1(?MpCYA?X@PLvrrk}@*inH zTWMt3Z}@16HxGK}-*LsVRW!imFJU+n>F%<$h5TAg8JDuyZNih!09gJG=N#nf&^oYJ z)IL)C?b;vL%HCQgC!I<3yrh`K$t#dZfl^r*5lmAocMVi0D*DhxWV4hE%cw%TvDxhC zK~^e8!aG>U%~`W*rghBaP6>{F%nZ&!1j3UT2dOg#2ro)qQz8l}Kuru6=7rQb4rpgL z>;?=8{%Q7t)*y{pC38K|V}$r3>iWmxxEF3bftCI5%Ad+j53E{oBE?YovQKNFHY3y2 zgLN7aZd2zP!gqxq0*~<5-A}v4)7^J*9x4UE3 zrm3^PHQc&z=Ox3}_vE$usdq4G0octsGr(rs z9-Gdp^Qca5$)f@~l=t*_20Yt6J3Ts2AX#TXYWZgUm-J#rZ=Xb z1PX?TA;?Y~IEW?WDS`@aKXBK6~^$QQ#W2-8N$TcVJ5Hr>gmhpUEPsx`rSqIXJ35!xeZgldF^8u&1thX zU(N^f>WilA-q5nsSFgpIee#FMWYC^`K^>|gO~)1DnIfbyy*d%%m>K;v{X7Uf)&^>G z5Lu`T)aB|VLx(sY<>h^tg2 zRx@z#DLl-_(PrAPy#WF`YZ}DONHMpET3Cw;QKB9j8ztBX#tnlBdp7)nc7PnvfV#!v zAF$?#4-|Cc{2P+d8R9gyFn(3DmTWsw@{w#DnkTEQXgJ2JWVXDQT>cQGM&Di3Ga>$H z@|?+bYJ5jy-y2tdEWZbOqf6F4H%SB1ztKG{`>43k z1e-4k0Q!k$Ngzx!(PrVvq?xI(%{U>zwai*oTf^#xO$~<{8pmmgZ<$o@eEkDI@0Fnz*M>e|K$v$%5R#E@!qS!36Z zkLG0NtTR4dKsuU{N%ETC=d@WLefPu>ZGqM6#{Q?SmEWr!TGxugK2X@#lqZ$v70IVm zx>g{!F8x^&AC&e=FGx}?G9m5|I@2Y$+b-*g&4_103?|Biw4gaPX*ak9(l0VewY+f! zd5%0bIfkA1V4#;LL=4okV$fT8rz`JS2`SLO83`fV5jq>)nY7Gn(6a(AJ6KJWL0~l} ze4T2h;{-AdI)*R}|DA>vshwMa_DF>No>=k$3|G86Q_pEHScYRb@qcTP`B^23t>VV4& z2`{G=lf1^tMd-$zdMl~Fz-Nrm}Jo9oLQGS zsQ2XkJ|FcG`8+fwHT8ykQn5sgYS_qIfwK6M(Uld_N?J6fi)zpe4^yaVsewKMl+jW` z$w#x}^E)}1sg62|a+`A_IjFJ&(b&A%DeZC|qlRORyuIp| zYOw~Nu{upZWhZxbniKSnD&eyE?HJ+J&F0TTQZ07GJdf(kkk$ZmVdy zzV)tF6tZ5d{blX%YQf=H>3(A zX!HOjlHs1_rigb|adimOB;*~SAs4bHhEh#?QQqR4>ZZspVt zH5|~;!J!Ki1;Al+N7I7G*ej!cFO56C{cn#vE$ncLEEgr=k^It>CaR7h+-7HG^;ZuR zt!yi0FJJei^3hG93$E#3dz;fbt){KLu4cMxX~22g`Wno_d0Q7>c;A^j$}e3xG;(D7 z?6&XOhlf_Y%;ZFx=?bw^zf-{I$6b!KYjeTIt{b0jbrexr=c6xw2ctiqrJk~Zwp?`S zRjOaiBku$PfO$K(ljON`oiofmH8sH`GUU-$??29w5 zk#3UWabB~iC>BSPWdqdHr8d&Y!5$5|0xHC zdloDm+@tyf+E19x;phAnONPUO8dr1GaA8uUU~n6F0AJZv+&>ui3My7jT7B-&wLiS- z^2@II!PRRve)F5Boi=;WvB=TYg)b}mP8*!L;%jGY+<5xeSIk=R+bjCIZolot7e9aZ ztp3ZNDKuXME?9GOqq!kDZ&oa@aOvl78JHJ{f*9^BAgqZ0r&+lF89~MvwnL^uFSEu- zJe>$7^-ZCxg`nB$*V~%YF=lHDHNh%1Wy7jj1x}EIb800ssscPPfGg`r#2P}Y!<(RU zaWpKG>|#DFZ4d7Zi$XXYZV5{Ud?i71APbP>II#cS2@9pIc?#(Fzl$>&t}JvIIi!cu zNV!PNRmr_iSQvsCkO@jj#7lr7Bpynva~H(I&bom~uRO707AdIZA_*w9*12ah@ur)c zprA~h?K0mmJ%8KxvfBJzVzhyV*Ovg_w2H++_Vc@=XB;=ol0*k z-N#za*7AVew`ifp)#AGGEFk7h`d3(sq)>nc$t4Dww*a3v_9T0r{X3J*_5S*7y+n8` z(G>QDMZ=FHuSLXJ`DOWadFeO#Kjp=AHkpV=4c=7CHGa!x|NijKo%```$43?{$j5&xZ^OuD zg@@IFXDsYw%Ndrf7HNvD&n7OnZLnQrlZ14L{Kb4`XGS(vK0CbrVUN zED-lqp}4Fu?kLt%Q#I~^1+>m|JBvp5zV?ON)SmL=!}AuE zmzGyHJhH@93qXq~ILfo7cVS}_zyiqXGY?zXyx1vVw+ZRpZufGOFoO*UMRodyEPAYz ztPC|PCEcLchXG>JuSzSd1JX0WjLw=(>uZ`lG00hm;x2u&*{0V8 z8)TzLzF7W1mcF5|jB=xLw<5JGbCea-KIKu~Rz6TY3JT=wt!H-(uww+L?mfmfwubuPid5z^iJ*TWKf7lxF zHnR7c?cUV2ceHI_#qtze2UbO&bd^3(`}y*W^3n4b{Q0To=u6^#S0?z}h0@8lW?6(%BnoLNOik+>luGDJY$IeQqHXFr?HPi`|>Z zl}vUY+Tdf6lx_vYWTcZC);9n<=!Sox^O=-BCBa5eORccjZI7@>#3y8Z6@l<0pyt`a zWf(BZRK3<}0zLbS2dF;4<6QV4T+Ravo&qKk@!$*@%#fs%BgLQ96^~jN1*WjtNJ$nk z#@uqt_Qn~5SDjTX&Xf(iZduos|Lp2;cXQ)F^s=u#v*O%uoK;@9Wcl(XQcrsQFE70C zD>E(_X=_=u=CxO+56?^A_VUXg|D^c6+m@Y2Uiox%oHb#s=b27jjTKy5a)q*zISz=9 zWDUh_`k+|9LAK=LdGsb&V=DTbVzHpW6ehB4PEB%{i%H7I(AEg?HXyxbZ_91ZiMwsL0KDRT6LM&6HT%nGvL==gDkvbNx+rjk@<+@l1jx~%3p@&V9HD=<<)yO$;Wl15VRgONgb+bjcK!K#3U=GV8usmOEzWucR+4r&YR{BW$)e!M*D6by0&hXY<#{O$U3;g40 zT-FeJeT4Le_oAYkNSKNu*q1Re0vf19TW}vLhQd^qE`&Ax7>nLks@*Wfpy)fLK->ui zK@XI0jbaP-Npx=RCi2;6uRwk<=*Z0=Y=!9!SERa<>zqc@bm`t>S(tPbl8c0*M#H8Ub za5yn#piUbmVaqCM7~d~+g#J@OU-E=15OR+^wVdRWcY>VzCse95VRz_P@)>0J{|znzH*BP9X_E(qinBUdG(cW3Fd}KFsY7%q@sJ`$S{o(?~fh@K@}zZAuS_ z+h(R`iVZUZ8+r}FOgx$h#mz>ZDm@0NUcrgu*O^6Rvnn0TPjVP$_)Q}Y6H)&`E0ytWp3t5bJ8?>k@Us?$qeIk^1ywV z3LX{<{MRSc)uYm3>6iq@bSA-wCF+y4fj|s;npVhanuGBu*a&Skp&GDy*n8-}v5x8w z>yPOn%Rn91kPl1btv#AhW>iBE1b8D1qyb_LTrgAR8qPk}y#^@tJw9oaSZ~uQ@}jgj z;*%QAqkldEhSX0AIfFME`q^7B1H_E3;W69}F8Ed5MdMT|sOPk@@2;Got~_PNjFlH( zR~2}^`f4ule4YHW8Kne%IoOVX#|JkR-8I+;Z8#Q9#eBz zZ+Zp#tdMhlL1LzOZ3uk6PE<*$GEf_<6t!$w90asH8w>n=cpS=X{Acvx(xV#0Y0jV9 z&QA#bt-)mSxI?43EWq?b==?z>O9lr2#?cUOQCbFkj3{aW|1c?hsu+CDC2tqE0_gDN zti$0OV7GCI0|z!-(*9ZY^A(|tlovUE_+U?+?LUCIL781tf!$OyA^_ zNsMuwNN2E{{iWXxt?&h03{s4(^F&3G+pS+d_Bzc8vI=kD)2` zV>h^4)MSgF`7OKRY)5<-02H#^<7PHDGrHYjQ%y*x#4YAPJQNBGKo0bz*mg)4Yo1Xo zYC{Mv>at46;z=MN^a-%;r)J2?h4oo!Y|%OR9(&lUNOXy zKp$6Un8y`vaeZUqr0SB6)T!1Esj;}4uWWx^G1$hW_$f2zTFcufmNwg8bJ~Ul%WGM$ zCS?Ag;Z!FUbs1wC$C!p-3!YJJCIz&jwe-c7jHnS>to>F;DPr)1lYRmGga%Nai=hCm z4<(LrqyPzNqEZUQJUm?*$m(5U9Bfn;w%xK5%z1*Spf5=hVMM~(vaw^?*z||=NA=S5 zO^CttnkB#qNl8fbl5p?n;P4jzk?Ih>d*b-P5CGk!IjPtRTrrv-tWIxB#fw9Q0$+BlKc1UEuQA2uXoI9IDnCI(C{DdQ9||>M z$ADi@{3XUKAl#(381=8}-+>{7Im21V&4D1$uE`_)JMMy1s)jM1`hC2!r$hy-$D2yp zqtnA5RS!l*0Q821~G8FfZuLp?1njZAHl-w5#@J|fzAG=Zt8-uI+Zw)jg_ zulEo1@Y~gD*1S;t*)`A0U*mT-KTnN3>^cE zd5wVQjjF={!-@bWieRMi_7(KmcylN(*o?NCTFnbk<7TK5c}p9-7kT%3CE4o)DNMFzw!ujtROox)GgZ)Rm&K0#_vuW;1Ft*v=DJJ_+r>SFISOkqq0z z`*|gt>fC=`IN>tCRe?OcK;uU7jmErR?HxlO-%kM#pxt;xBk0CcG+jx!ZCFb{h7-j; zPIRjD@WKT0Pav7rI#M*bWpw$C`*ApEBJx+u-@0M&oU!lUv*qki!x?)&Gq3-NuY;6( z$JDOsCgsB~9)m!EEo2@_;n`oZ{{G}szI4w+arUch^QGn7#9ozt$S0ZO0yv077?N$fH$G+mBTw7J3DWR^_cXjA)4U2WW%%uMgr)rC4!cKxjQ zoDefliP$2cNIo(USsmFF(Ocz+UX)6q4XSKJV+rD<&X_1nQKpDfva{4~+nmrG(KHAB zVXPxRi|w4XdzOgrP13Ap-n{LCvrZ+}aQavp0d6|hoCPHjS&2pX%LsA8?sDuQ@ob)x z@xft3*OOQm)oRiQtiA~=tUrig0@ zrxMLGpX>i-C`AUivdt!fF8!+V)i>S##EiSXytm|mzC8QQ3HtKv&8oiqGSZi4|5?$O zzw69{*o2kdHg~0)hSL2QRFGT+|{qO}!YDrT;aD{be50EofUHwk?>w zVCw=&?sxXb`=ziJ(iKwmQfIskZTM3|(ROoJd`4)dKG1Sf%leiJTW)W8pyi1ceec{B z$=AGLexJceDUq;MvT82B3p-w59bo|kG`j^6^(_-vtLGBV!wG$bxx_*c4g+%$j@4&V zY8#o3UNRkRY)9L!wmof9TdY?r?q@sInS>G$+hZ-AJHXZ}37vSi&QvA6Bc#;Sn$)qB zY)GXR50JrFHT2fFEm-*HiXGr779P@3nBF@61Q-h#2Sj;vlWR;gwKmWo|0oYT=dH1kT2s3E?-&aRm=7B?FP{h_0%t0 zS>v2t4#G;1ExBB?EKil({^!@85cu8ncu2XTY_6D!4$m_7`PIr?zkOTTd~$t@As*L< z-RFwRN^j1TxD>|b-dSJ7>>R%qXM8(WL>u!zBQzcVq53tP<$A@fB$XGG_Z6L~iCxgd zwnHFJB-bWvw|TSa?9{AOo3(6cG{oYmP+DKm$J<04FnQL)1|}tL;7Q`V7Gee7E@4CG z!@j~x=!VdBY$|LkY%d%|i-eA5_;^9u&HpA9+OtSbQv_M5OTi~6G?p0-xF*TtlLFBw zc}>XM&?E5DmXZ<;BRQ}pD({GnM#ZC1MtX+?bq1Fj&I>LmjfP~nldFbsAdJWNbf91< z@Qe+-DBju+cL2C@jvK_;fcKc3tKpOUcNRc(VznNBkKA5PwRx13E3kb2Ip5Co zDL0muoEqPP zGZ=@^w9!QE{P}r&|u&*8A8No8}j$50zrR7s4)WQlh(Fev`kbobaXM*B z0X0I8doew#lL6?a?H*2k%WGe>k%oq;8ipTXXYu1OS}7sag2pzjod(m;Gm0JbCc0@f z1w2dz>g7~1k3oEp3lB~~9hX}|Mw5byy6m1|4_hs%u{%C%FF)9?Itz2y+3X2#WEneE zx>|3njre=Vn0S2b`J3W#*3~aw?&}icV{fqa%Nx=xUhQH7_l{ea5)jCudiW>ZkEACE zQOnw&5iH<#DWcJ6MUhDchaqMFWIaq(LBbInnm`h43vb5{dI!d`FQzuor~t1(j|g*+ z(r!=yMJAbHozf7-!?6arAr@1_Lb67I2!gjy(YGb_sM8ARKI|sKPnE(Rk3hm;cnnxn5OX(E~}9{13s@pfu#6Rm*(>^xNZJ;?w390iqH zqx5q$qgB*7Dco@wme5ubI6_|&*osjbwAKY3<0-dRIKnz@dlBz!`xM=ea$`?fr>|Q1 z%kocI^N$8rH_Q~LBz)(U=4^J)UWR&E{BpO?QOy3abF59SJLlcsFR2HqdilqoF>t^1 zedaSu@MGa-j=fQwb!nYGNwrr#2-LX84~EMFoMw zJH-$$=x#pkoCojU-rqVf^v#W{#QVhi%CDENEnj=zN$0H(L}!geBH;_>e3gB7_pSH; z;Ph1YeK$WLisb-%xV)r%$ybkEKd;ceZ`oO=to$12y@YvS$05zO2y%C=;BZ(Ka>jiD z=<*54#$?!GNad2EO|r(J6mPOdRfTI+ra0Y7i@Lgw31Dl<1#E^8JPiQv!Xgv1Hm{br zj5%_OjkQ30dlwocARgFOs>ETf@TL${6^lsgDG((B`X1CQ+Tls8*QiYuHFJ!8ujmLo zQ%zxk-$_YO@taOcG6*gi<-ugC4iU7jj(&kB>#i94V>MD9dk<l+i1@*73Amp$EjU?N+1ptU^rD<_R4K0c7R;-n9x*2Kt3U-`E_un0-!-BGp%%1vH@4dWLchGpQ5NJ< zAkx&}D_WtBXAw{`sE!TyC8e3=tM1uxX&^YIxjSgCZSZGYVWxKvu@4tGO-B2ogU7C! znr>?G>fN#KwOb)(QCrmE0BZBE8~bRnyOpXp{7$h-{@$Gm|6jaQ@rj8${djrxPT|W6 zm{Xwk)FriIgDBR>H8i!<+E~TtJ?wwh|0BPY3C0st%`h!+pX%NSghXA@V%grhvlU!^ z+&PNI8U&+ag#SveQ9VqXd^2Dm9$3I~8juC50FpafXnaaUzxaa786=!XE0wJEo?<0*};X4JaHS9vU`F zwX#_*j(e4;<)V=5w(>)&sn%Umgb^5Oaw`o&+U5)aI}dlB7}S*Pr=7*Bb4YS>LV!^L zb_YB{ncTrox`J{WGhd7mrUGKXA#R z37MEozNJP?L0>Qv{6_GZAPAvr!v+K?JVvn@q2^#j?U-&mhuhvRun}RGum^fxvXH2( zuJJ_siM9TS7JsM_zO>NT+%51DKaV&@vB*R%5|L2r=cRlec~l}pT5FVBrZx@?AD3{Z zSc6LWleTP$gVsNh6h{34LB8Z3ha^@{Hmy!CMMMRMWhE?8_5p8T&f#?BpjE zM#s!Y=N7eFU+=C@)<0Okw_ayA`5|x?k5S;Qol7x6ui)@Or*$YMnqskm5ZfMNp#%h0 zYbC2&<<0gXj0KO>m} z>Vfhg9p2>KAsUr$g)JvUn@?T#evrKsI21SvJ5QQ+W97#ywFzm4@%zhXCOIZAN zn3=;_fGfjNUoKaZ?Smtvm+RsQb8U##m?1G?PO7S{9^p@Yy_^UWoO?wmTUsB!Ff3-m zEEQ(Aiua0-iqdC5bin*f{40Y)!ER;uvPaHWsFvoJ7k3i1W zsa{_AMIJs@$8keeq$M`jF+vUPuaoL9u6$8VE>F=z18D;A87WXFK-ilP#KIbj z-T!9B-sG0_|Iv(Tz(lGlDcC_(en>uJE&?aIAlpYiu@+*!$)uP+bS3YD{=dLP$RGqS9oi=^HXZMV)xwtV^K9^UK zmY{A4mReyp10o+JyJ)*pN>UnM?reLo?S(e!!YsQk&0dHwTYacrG|MwHxy-8Gna~w4JFCg=EOT6Jh-){(>6f>#rQZX63j^*iIf=6KEKl9uXxe z%!7o1?|Th!S#1GC?;qdY8Y*5-6{+#ely;zKhcXaodnX-b@46&tv5!Rg>jlD{7`Lrro@aTZ%Mq{ z{SCKBvhsUflBurcYDeCT0h4Q|2%6$@4&`^gI!hR{PY;+~u{lEEX>eqlISRuT=m`u2 zjt68jNPAQ++TsVGF4o-A+=8%XaX}Ia;X+G6G878aJ80mrE?eO=j(a?40ms-C4@_#K z;m#zD9L8^`rxh-EWe6uinCch;cTuUJSDQ6x^;UXnNbj@Cks-|*AJ7|a`1vQgYG$#U zZqjHmCJ*hIAKi8OIe)vsM%w)V;TqvV9Y;Tx_!eJ{Ur|@+6%;u3hL|Df38sP)xC?ra z>9o|daJD783n5%gwh(0stYWx0st`)fJQ}1?!~o(%pJ?17BH{6d#}hoOiO5fU ziz2^;o#PvuJgjpr(ERc3aJ4U8^8;z_iEh^5ss}z^TTMs<^>Zzvyd*}4a!AjpO#uZI z|E7d5ad+Yy37KzIxi%%(!yZ>txY!4f4^Xx~8)!gX3SWiu{@%__dZs|o+MZW>DBg?a z`x?m>}m9@8}6~gM2U^-#DlCgzJ6?(Af_8-X~sGDY= zhGDDh-SHTv+NKNd$L)!M8k?XS8e3C!oiP|j&hRTG#}}OaQg?Uo)O8;(ZAxE!da|+7 zA9QuL@s^h8C6ia&`j7l4_Y6IHR_`~9jkAY__3~+aFaOnTJ~P@3`{l%1yGDLj@Cs+E z%0-Ur9CtdfKGKTlFGJlqR_TfLU$L=T_D9(V+s`!RH1|^sE zd*xbYvR@6Q&b_MFrdPq~;*xFJDVPV<`G8;Y*U={p+wvR-nl)dt!?eq^2gN>AGFpIf zKMc6L-e4kIf)8Fif-tnQ)S)0vOMN`mflp4UWDd`ow6foza1ult{0=7NFD_a0oU~Tc zf&OTFHCZ7FocEvS^YTC|ZPm9JYfC{_OY(|4XG8Bb_ zndjCKmJu4l#21hTh$UQA&W;5m_$gM(11@tsBH1ErafEF~_e3ERaxquds#?r~UBQVa z0SZf$4vzz%&L43uaf*j}L;(VF%KNR-v=-1ltg&vkj#zby^%!cL)jAiIxhO7Z)!-`R zXHYOuMZ)smqM$+n7>EB?q26jKx?;2&Y<7Vu}MfkCAFYQ$r}I#mZ6) zq?3eQ+6w{-8_GCv9>$TjuN)y98Jc8{@QLB6sOMpdDy?B3!4Pzp0ax5nHAz1XvfB-Z5)v_N^2!0MdXL#P#tLdtM0C$vmg2V zi$DCrBWJI=Q}@@I4Ye%Je&M;|$h+G->=)wgogU-Vz_OuRp51urX&axpdDTh&sS#@< zbClnk`a${IjQIU{cQG)`(%bO+$V}BJHP`?(6-*;MX4EE;nZgbT8(WOx z8jT>zcw%OX6Ce>mfL1r;t_0w^6#jGx~ z$?Y+EAz4+=+){mz@&;6UU5vtv2BM~JAjF#(j$tew`ZwNH(z72y#N-}0Bbwk){;PwM zWF@pLr6epTXC?88R$+oOdZZ&|8#>z>@42*XL;0s&>6#pynrG9eqUDX+*i!4+>hjO7 zZbKVe!l*jV5Z@TfT^0A6=!|fIe{wHIE5|;kE?wX})hRw={IT&hqx6XDBNv2s_lcsY zzrFT>+UIHyLe>{#S8LEnY0B7`&vv6t%-ecw1Ga6peKwuVDF&E94xr&NVKl`|So}sb zWgb@jyd^P}bf!=jHX8fl2`8T7a$@-D&-dcbU?`7)p*-etyZa(hXCxAhlf>&ld^OmF z_r(vzb%FTC_@(hj;xe$wCYK@NcE*h+eG1L#3G6dJ0Qxyy1Ln?FZ~j$^os+yNDSkcn zbnJ&QxPRxwMCqg$%SU^no1)Ue2(y@Srs*c>BNKa*82Cb9;2SWlHbJ%p?~8YJ8KSX7 zvZg-Q#3_tT_$Z6(N9#q62Tgea^GCa%=XgN+LVOsUAI5_=uKhHL^vB%wIvfJy{fso1 z^qUiYpr_7Zc5OC$sa)U+}7qFlx5C=DNlBanC&8 zTm#m|bk}O}?Xjiezl^Q?Qa)^@_2Iy7ItpL1N%&9IYjWR+ek+lIqNFCB&KL}OW6EeC z-1n%una^@2?aXA-P`vDOIGqlM^Wrp1rJaV1-eEKn{g;>3h)t4>`uNP(W$w&~?o2Y% zm$@h-Ka*jy0!4*kR;#@(k#whS**W(7EHu zjk-bGbw|s#Q;NIJo4%?1gIRf98?&6*I{U2twrRcX=Zh@l4J11oSaZqRG9w{Rjc;M+ zmw$Wrv;piv!=U*l@kv(GeNwi0{?tWRcQ&NIvS@6<#hxWjZN%jwPTpqWC#3oFj=B*y z8A&##@z9t~rTX-;Q~!TS`x3yY%4`4oecxSXNivgUCVMiOO!kEd$z&ryE?Wp|P+7%5 zR2GG*fXZeqsDM(nP(|FRP#4@<^@$7UYfUZUQmxclt+iI|v%dEAQSJX(p9>lOzw_OD zXC^FF`xKG@$^Fj#zH`oZ&hKoP4y#sHRnf?Tss;H-7|AbqGygyHc|m2hT9uD7gK)hf zNTgkNW-3T6nUViq{>S-RhR8>axitSXuu#9k zOl1WilExC0loHU1n^5AIlqmwYTQ&-jeeMWDb_#gq{z?vafzvfoUrm(2Kye9#Aq+!E zdnGv`5!u%*0{16{NXdpz6!Idnl3&#A`W5CI`#iZ>-S@MbmgNT-DVc@w*O|Y+G^&@Au0u5OS?p}B z@+b2mFBA|JO9vNtyaA8LTUDa^W(5`pE)9tB0k+1&&h#wv@PNmi>Fv(eQGcQo_zU_A z0Av&dyRq;2rMZQ%{9r)_ib#S1kBo`Yheyzk0gkysJ@Vl_mbn52#mI}I`zHru2|`qt zNEM0bid$Ik4`rHkMLdRz)J#uut^!>Wj=gf|VA9nD%p}1le3AeY`Y+IT0*S)klOh72 zrwo^nd5y3g!>I!oD4By=sM?mf#og+&5s+ygLadfnS2hR;!uV<9wwQeWKy2zqb!JC8J!Y;tJVJmHv#Ts-KyYDr;ihL3W>) z85%Q=KaYt4G#IN0@HPPV=xNKn9%dazmR{5iCp|;txl3>o!tFsS&&THM>be*&KBst9 z@s-7Dfhx3OQPQ1by7XLheJe#rHcAJfA=aupCqJhqN4Rrxf{Hx7ivk-0uLj-?sDXf| z0A;U;Zld#2S@Li)h8&K}!GWFA=NtvOMBwsVIT;$t21jCF>PXHZyOA;l>;tkSL{7v& zb1L8iWC2tJ!V#U?Kn^{qtmY=!{t=B>Gvj}KzbHI9Cnr8B^@J|pG4R^7%~x$0cz)l$ zj?;=pHl>^mD}7-3``~&xqZcPqs=RPO@nEMKGO}X1;$Dm^7GC`_omye4#`(~Ob<>!?}#+gyohfPB&3gGlA?6 zn7J;0>>2GURz`by?*bvQYgX zxSiS?HKT$9HYA$5{L>;1X>wnD@{)}^h8Gk?MD{7_G~|F0@NhrylZ&xq6samPTQdD< zB%0lrmhJ3Xji0T{-c)8Gq*JdFBLIQ&OP=gpj4|}@-!O@txID{4o z`Q;2ngD4&Jdb|;PjSx$_yN z{iQ2PMJcLW>5jAZ^$!dCfMBfz$4>hoIa~~2Aj!W31kw0{oFJ?HxKnFHVIztW$=p0U z!34oT3@ql^WTcN*%xcupSXCm9%nUy8%+z^$os4JH3yq-=n+)XIiV^daim?Lnh651e({ zC^t5W+;$!&our`SDs3_NC=myaa5z0r~VH*sodH5|b7#!qTjB}dc zme=;SPH}?q{o_|yEppY1t>(9~lW_d5WIX=$vRT(3lI?Mce~uji|4c>RA@NW3ht*tu zCPVKa49S{NuTS+>6qJWcko;K^k-53~wWX2DN|#iD^q~}!lvH=N zoR1*9hLd(% zrd?v6k1h#dm)L)w*l?Nk4c#WmMd%pt3jD;7veC>>FKH+lRU*=}8?r}biwre0D}cb7 z5|XV^^D(zb3C#%gqrt5R6{{#9RRGl~Rt!DQ2*!LgiHv22ohmZd3^{oW0D`nM319&h z17P?SITi+LM<%*ZKmtP6r{U$~+dJ?tbdTf|8%z+dgw9`e+gsOd+4{ZLsbsHe@e?;p zn>caW4Noi{d;9s9T=ktNx%Y{Un`DXJg4s94r`6=@E{EmO@*OAh6TQss)8&!fJ}gm zY1UmPTJMHS(W2dNONQ)U+}2u^QZKK4eDe$O8{;>;aK|ry#@g9wKYb~F^S@17^6Pn{ z*U!53_w$CoHha{l+4P$~7>^%}AN}kgOGh^EPk!=Ze1QGxJ8d7{@kRWey_X!j{x8=)J94x&8mg97u9Y+fTmW(J(ri^clzj2>J!J1L{Cv# zvVC5nqn_-c2ch{F`{*TL>R7Vtw$(?^iD4n)O07T>>WAb@EV%s27dH2O>qkFY-hb8e zo4$R<@YJ@Np931Eseex z{Wywl=AO!Svz@f+bWcsJU6{f&YGTOsC=NwSx=MP{kwq;jsp-n-g)f|i=z30Njh8Ih zjmmb}zF2F`2$|`X#grdkjED~!YM~Uw8n+?4W?>lNj7njGZAV#|S7x-K3w$dYtVDD& zapg1|3Uk3Dv4S_RykR9!fpHJwii`FkTUh3uW7fO0GAx+|NP>6{q&5-Ek)SE^sl!ce z^5V17he>N$vQ2f~P0(yDNIpjf@s?4sbN$Vi9N$W7NVlz?7yVWLm^QLYoicLjiuB>0 z`nTfu4(Y2^KD6)Fs7^MjiDt@Br<<9Qzn6l)#j#6KoOdxW4=-@ldSqcQ&Gw^=BFBXf z+5Xb1*wB_51?9K~^z_Q^$P6%XU3aFhx;q4qVq0K$Kt+R;!0`<=%F`pVb#=Hl6sb`1 zslr1T`T1605g;?{2DI6u9vLu+DGfW?CtDgK#ZR`!fWt7fMTXRe){WSl-71}F1k0K= zJ1E8m=b7fbdFGh8t8a`hIQ#yy){Y%@=CvsG&K=9PxUP*axpwEch6mJNn`fSBnseuT zF>mLDp0j)AwhR|%jmZld6-O_u96f%0eFvPmBk0r=g=IX9Z81lr`F(k@o^;mZW4bHH zg&DdS6`0F>dTyc5=PxQMEY8Tll&S9Gj6iX5hOe-waDm?!@cVs&V87P@s)-C=5ij-^ zx&j_5Fjq1R$}ZwaZujJtRfzOJG4e}8L1gwu^c_WPYZ3d4_+0RL;(WpH7cYvp1hNd- zhk{)vb_u=!DVuyUT7UalxqpZMZogRLX9j*%`dj_H+dtdSH~Zl?`ij(IccDMvce54l z?HIe^<{_(jZe!rRfa(emP}oa-X^Lfg1CF98bp5w0-|&YJRZwlH-Bv4P;?cvk&(yvO zE!Ifa7q0Tu3a7&#_K|lemAYMb08|QpgWhNO0=6JNQ!9j>a9iJiuX;Mp1*j>@2}2{ zG_(I0ReNBhCmU7GPNyhw{cp~Rb!1djs3Wt_UBt#dms_TmmsjSFj{m*$zWI67Jqo3V z%Tqf=i4VaQmC8!9qrh7p%tDJ1x&Xu7B2?%p=N?uW8i2a1_60*h4ug_f?-pWE2O136 z{UbacQf^*APb<(H{4jEBkMB8xq|HC9I?^orjzHNsiPplJ><0@-+CGTHNGJ)rB27j> zO@w!d56h2kDy=Ha@)%q%$wOsCL(QZd7ZhmL*a_E9EDy7(@y7@~^7ruGPP85ameiQl zJ@|BcMs=ETQ3s}Tb7eZJGh)yS7+j?M%-j;r1Cl{WYPDrzb>)`Iot45}#SB-J-7;+d zFut`586Lw~{m9B%X&qe` zEnjd1Ku zyJBPcnZvW=qvJQryArCIe7vagp1A=LE}ehM@r#WpoLtENtB_sV@S6sH%`g^pbtA+L zL`i6xPtB)6l`4Jc$Y)-ax$;r@LL<^E!u6Q?O0{@Ck1vvoEC^KC<=19Jbe9Ua6h&d2 zK9m-3FUCA%X~z#YF(f2Sk5pGD{6O?d;4`ZGt5+bW

&oEvB%8Qg&xOxI3jRllGK4 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%0AjY#y-|Q&OY8g!9LMG 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 literal 0 HcmV?d00001 diff --git a/frontend/public/images/bpmn/en/bpmn.svg b/frontend/public/images/bpmn/en/bpmn.svg new file mode 100644 index 0000000..2981415 --- /dev/null +++ b/frontend/public/images/bpmn/en/bpmn.svg @@ -0,0 +1,370 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/images/bpmn/es/bpmn.svg b/frontend/public/images/bpmn/es/bpmn.svg new file mode 100644 index 0000000..d9dee48 --- /dev/null +++ b/frontend/public/images/bpmn/es/bpmn.svg @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/images/bpmn/fr/bpmn.svg b/frontend/public/images/bpmn/fr/bpmn.svg new file mode 100644 index 0000000..be9916f --- /dev/null +++ b/frontend/public/images/bpmn/fr/bpmn.svg @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/images/bpmn/pl/bpmn.svg b/frontend/public/images/bpmn/pl/bpmn.svg new file mode 100644 index 0000000..8d30c75 --- /dev/null +++ b/frontend/public/images/bpmn/pl/bpmn.svg @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/images/bpmn/ru/bpmn.svg b/frontend/public/images/bpmn/ru/bpmn.svg new file mode 100644 index 0000000..7e265dc --- /dev/null +++ b/frontend/public/images/bpmn/ru/bpmn.svg @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/images/builder/deals-view/board.png b/frontend/public/images/builder/deals-view/board.png new file mode 100644 index 0000000000000000000000000000000000000000..684aa73503cfbec0137a8332646331f5b3009904 GIT binary patch literal 254162 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ǴzA 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 literal 0 HcmV?d00001 diff --git a/frontend/public/images/builder/deals-view/list.png b/frontend/public/images/builder/deals-view/list.png new file mode 100644 index 0000000000000000000000000000000000000000..e230905f2e890f83ad2e67041185634db40e7638 GIT binary patch literal 718916 zcmb5VcQjnz_dh(k=tN0$qQoFdA`v~&f`}-0jLs-w)I{`7^dO=}k8bWLLzL)I6QTrz z5Cjp77Sa1}lK1=b`To}WuIG8y^N(|vyUy%=Ui-DnIs4q`$B)#hDOf2W5D2x#0~I|8 zgb5CTP>GWh;s4XPq4OC6A%Z-9XrOv=adC8Vez6!H5dC)d;A~-a@AP?CU3`nzxV&D{ z?&{_~`dw#x_rQ~dBLDzaaGSq&kJh&j508$vw|AG;_G7*p@9piwD><$SiK#v|o1I^d zNkP-l(2$dpv2k$amNl(!Y>rQT=jGuU8v9n)(x1|LXp^&lg^9thd|&)JU;Bp+K0yKW z=VK(&0gD|IzjZS>IOKJ5>eABB_Q?|gHs(?kdTi;m=QCDTOh}T0ne+<%*{B-L6|Vk~ z$+_>}Rg{&~Z;2%5)n2d|y4U*ZscG7#mE`B99Dmcy6 zvix<#OM9c@K$iw|Y4@9Oy6Yl!nF*4LO6`SdZ8eoazFzNJ+YlzeRCY#BW4*bvS9PFs z1Wdt22`a0hxwSlBlKEyR?$|**2oHvy)?z%7&+b`djd%_WeuUrFs5o4|O1ulBQ4mM|*O##p7*JlmkIw4uhlOvIJzbBIu=1N|iRW(?Paoy0hK1o*pYQkI$g(p{D!XgtL-q8nZDG^d%=Ag0 z{L0~MKt<>2Kq-}B!1<>k&L;`mp^Qb}Lq5hv`o1l1UVZ!CNd<+XA(2!c+G-hYk)Mgx z*8zkU6Nz<973@34sP0>|RJxXCI9l^-*&*deg9y*oXZ!rM)@*1`mpVEMp-|uA`5}1R z$FXpUYZ(PEKtM6p^{zIDOjjpELNd(<7*~l3>x3I`M_$i)nQ2KN(?+dCyAx4Hp6xcu%cQN)SM}soJaVwzrvh(+&GYzx;fA z`6}LLAdoKTpTF&bFoHiLFXoSLLFl4^KSS^XG~u671;O-Z;AQ~-43I=P$)6$WpQ%cG z|DBq~ATu%uWAS?7wpb{t+wwuUL(L#QyK8!IK#4 zKY9k=J0G{6w77zQZvX%J6MuC2e~}*bzn1X7wfO(|3A}p$j|;%Rn|$xbe%DI;dI-?)3<$Trrfd`&K0$gVmfeb8Um4vw)|@b zy!afIs=s!^KkD%QgD3>z_Q)4~bBEAK9vS-AdXO8BShQt-n0e)T_?#CwI~8@kGH=UN z+gfwdov7s!EV-e4e5_Gvkz#C^Vg{s)FKJ&|YW8_MMgI@@Za|~M&PkhxkBPm9+sK;N zVpeI!q3eQ$ukOiC2{P+8w3*;*Xr!rY~GXBvpcumN8dZz6aEsP^& z_jvDz@(b47Nn*?$O!-;ipyoePr)~)0I++v1eNQ)!Z&A9Nq-ZicnQi}%a0$TQjX%En16`QmP%VwI4U{A9&+ zo6pYeoPRZ=tU5}iD09Uqd*#_b@I4cP%@B?T6gP6Mr{7Xkes>((a^m33NZtu?agk>X zCuVw9{jMbV>!;vdJF%or_QD*By=#-4I~*+}a)$xqcWyTRw74^4^XcKvDZ-2+NgORg zsbevRIXnNVa8W*v+Tbbqp6us;p2(OLYEbJ57=D^6LGRji7GR;x`4k_Z?d6%?{!_l~ zn`n*$0h&Kv5{wDG!Q7Y|pEau8lps$wSZ{eg4P^pxxh)>x1Meyuz~Vr3s}^~3Rugc3 z4F%iC*LU5?*8^rXcbwG1gd6iJ)fp>L5|qB>$Mbj6^{QhmnZ7@{qRL2xqHc2cOs?=1 zxUl$>`62aAr@w}%xddleS7@&-+oWIA>ZFo=dZH;40^^c81jFD<@A*PP4i7 zgoIQ}kg*NR+lW*a_w!U6cQNEDCj@Mzbi6oE`E&*e{e>bV1|fIQv4HmVqj!EK*I;2& zum1|#%@^dLBpFmBe|~G1@ove7Q?FZLv#8Dt168hwUM<*OdMyb58hGK9dm$au+zJ7l zvr2@-X5V~P_$_Cpfw~4X-nPHejxC#dXx5FQsYIz&QMlEpnW1B1ur*!*Ju&%z9antd z5Chi99^73~UuMLw#Zulh3fX4k5#*G2!Dl3UyZVnof}n-~N!g3@sOH=H1J!&%V&VPO zFEOfmZbSN%Mqm64p_YKrdd&WFOZyX9#bzkiAM>!TA^PoKivMydcv^TNqkSgI`3Jmj zsPC(Ud(ZUG_(OIK@#1iguA=p7&8iwLx>+}~JxZ88jcfl>AkiheF^2r$Ho=-xqHVQ} zNW2-(RbeAxp_mCdjhF$G=c$_N&1YXsY-~K;Sj@2;p+GR3REA6X^!09;%P}zBn!h2- z?hOv{{>yta0UeWB_MJK1tqM)m{e{u_UtccERB-f9{spfgsL`Ugz`>aBQc`ttIlN)> ziHO!XL%vNc-%z#Tqm*)}8Q|m8WSE(l4eJzE#)DX}^2uYDxCL#m$^K_a5NkQyv!+;J zQ(oEvZzHr2jr{M^3pbl9H8eV&Vw2-E?fZYY5VraLT)Wd?Xwg-5Z~;zXc)h=f4)e1A z<(Kqi{v@aE)Mpjdz%k#(uH6q-*!%%EoFrJwHW==>y1}@Rzu)j>kJ@f%U|Ag#@6w}_O{kRnwK7jbYcVl8` zkBu$1C;hw79`a4Glf&&@u{d6d3+I$M+k!|2VB@hB4igH<)RulhZ5#~kA0-$h7?`1s zkB<|@UK=s@zWgK|AKqko!@~GJ`PJBgQv2Q_8{4m~+i`xnpcov>LGs!09c#{@D`AP@ zmg%!|dw;w){F0&_LSJxQC;ltfx->E&SAgZBr~NMZ&K!)|a8J>=+P6^B!xi54HJ=~t z)D~1X6vVhk`)kLiL-UPLR*cW7t*QGy;wVjfzd~rcW{tEub`+|84V%)i;28@>7Od0sdGXKaZW#8rJ#=;TI#Q~mA(F;r|{(+LCOAEMZ2Bk zP;6@7lT};90MZ-P=Rl&}vOl3+bP>0o_CWunwXNcgAX6xIZhhvS>y-EvQcJ@Y1Dp*X zomHq7>KQ6o3iHB~)xP8I54sO!ddHXbT-JitsiTQ5o}9Daar|LJ4b@%T2cZ@`j6EDz7I65tVo~EzM`@^_pCrZ)0Gu1Z{?u+ibzOX{V%3pk+$(%3)j*q$hjF<(;A%SRz86mOMIW29 z|7Xnt@TS(AEGyN25QoVBYN;{($frDK#PribqGGkk7$ppM)~zS{P^E-oC5=Qg35SI_iC>2TH<)fD zi4Sbv)_OYx%X!Ti#>o#lQ^F4Rnpz^J#tLL!;L8Elhpu$zR~zd7$aKq}-`E@ax)eDO zXg)_{bf*oBIXM6=m>%aXg)X_xd6Kjj!J2*1Sb0#snxH6=iaavNUfT$*3 zIH#E9((##lz&Q&eNYe+^=eccVi3Ge%v1L6rd_1Fxgdd+7h zcY`GRBP04alrwDkT*Ok!Vcxz&Kejh{v2ZX%f)*b;xKI7a{=q-XQo2p9=IB+nSv`jO zst0^TeaDbJr;RDXcs&%$%n!oUrYA_yzqYpAmt9jcd~a)MU)mN;8DT7YhSCRy`lcQ? zVFSyCLjj+hAJNH3!*+h!*K#uo3gi}*FMzT3j4$pM%vBlhh7aqA^!EpxjsBFdtrh29 z{}^I;*51>{vxZ)_W;(vj{$a7}lnUY;IB67rcUvI9dSsZhy=vkE&NUPG2Y&z2OIUwr zizr()CUE;W(AgKPFjSNHDZx|{VZ~}|#H^}&g%}s`{@c@R^z0>fs8wSf&cF7KGfZ*# zQgHmO-jXp&$Oq_T*F=m3)wy~pN@RZO&jwD(G0ccQJn&8YWF&QoyL%&ntZ*Cnx#v-? zf3elMD@VEb7^0+Nf37N_?P})owKbl4&mK3*f8pA$AU z`9n+Ta&wQ{^45)#vOxnlx7kBcYe9vcy(2$E*cIdjU6W;Pz$et#eazM1wS%F(qndmc zRA74Ul3nHl2j)`aN^*XXwd9w9fC0MEyK&myODFc${F=xEml9rP% zhxm-Vcvws9ByK|_&8JvFDx$pYu#G%5=Cl&pNPJ=${S!4q89hoXDyI49gC zdBOe+Tu{iAPt@lRGH!VKbHZ54D@$%@!V{*V`(bXuwJGsQJ!t4L0+YKV!rv9Bh-nOi zlhA>XGEGsXxxc&dEa8q2^XkO+jCGzWl_Q54qwW`dN^j3Zr{_a}uW$MiflqC>n?&`g zW_ooF_JdHuc_Vd5Tl?>R+mcQL$U6sf9!Jc*25>12r{5@_%`x<4v6y^YMrMCu#E^st zkKP3o7^yp*znqzq#19jW0`gFB)qGc9aZ-+zzb1qXC%m^&Qs1q0zs)?Sb2rf_=kvE1 zyLW9L03W>vBA#A;(g?&d~4RX!$nJcjD}+>RfeehMIQFt-T9>U=C= z@S*epc&QuU+B;ol5fyCjE7rPy0GGl%3*n!pW4dt2L4jrYx5lXekRnk2+m40_TY0J>*sY5 zS+uyBr?)C&_LrKJE^Fou51H6(H8mnAvh4Q{>Ffe~ick;AeiD|n$oe~Wn1;P5HM?i&j|qx z_=;s^{dwQ931exsZ%aa~^GxS-HF}DEhMFGazic3zr0=qJ8E+faE+|95Xqd#xzpfiR zyR)ZLNcXbIZ{mnV*N-nQYLPxtnc@^l&q*5jKT9 zbtKOEhpx2W^8*>I={E=UI}Bjf*r$nT!OzH_Cq@icU^Pgljn&g#)5Gac*Fe7(+``^& zK#kL9WDv$!GXHovoTCf+SQ(9bvod`ec)pZ7uw_0ZF-)}kw>oL}`TeF7nJiyV-ahb^ z4B;^swSTj&h5MNJk4BZW>acp0X)n8c;Eu`pM2G+H?a{M?UKI|AmVuFTZ2oN~-HHBR zPg6O@Q28TJo-XM8kNutmvpc`#6t^et73dvhzeCa0X(O)4NHM{MU&cm^D zGeLmXZ)|Om7mSQw{q!hhExn+3YyakQ9p#I~121;>FaccK6_u9ibt147+wC zkEg(&uJizGG&vT__i14=Zt?T%q;P>KiIOZsof!fPuX4Ein5#C~d|}-;8aoK~m5AdV zodV&Jr84KcBlf8MQN%&*PoEEigoRMq;d)50;(E4@%ajshWC^ZV4=U1)qWqL^@H{+O z!^vTc$slD)mHDGN&j)&$6nNEdZT}ahJc1u@=&`)lS&oxu-02{`%tpH#aC*u6vpA-u zJvT5=nU44#o!~W?6)u}HqLiKU)YO@EPwe4*-=UOfoZ>;&uVzBZF<)K~ocQIoBxg5w z=Cuvm*(!K`xI4~fn5z<7m__w z50Y3mO;OK_nR>E;;dhNGzkVBpqY;%cYjp~*;QXd^;2z)c$m~s#j&!Me!#5vzGQ3Md z`HMl0a4qw7GsRuddw;a-a(G6Jz=Fyn{xIvi;44?D(X9r_K$X$OUVTFz%HwC@*xsGU zUT7kAq(4E&C98M6<$}~zipVZ+(Y3NwE(yNjXB>Cfnp{Wi)Vx1PJrQL4oSN zk{!5!2Cou2;`PuybSTWM4Db+R0LyhO!o{}k_yA2t-3TT>p!xT;XrO}tbNn8LiJ3w5 zBA<-iB>$@_m7h|dd3YKsd%b$@KiXDWf4-aTeD@Q56jql*e~Stg4cxxoI+n8SICb@+ z0E0^Xh+|IPxrDYQtZOz5PW4!nQ`>4Y?t*dzoqSh*TzkD5MdH}jpgd{d8FAAF};P!1Fi z&OBCSxg^1y&NHUzCJd~S!!J|-cCv#^^nh=BP zbd6*N1EP*vtkxeda+$W4w=aDUUJN4RrfnSEJAY_Z&Ja9T*=tQCIB2~udp@Wb)F|RP zE?85~u5iBoPUgH-1FaU&aIoWd?|D%d&doe@=skV85|w!^d2Re%kyoEYh%qwD);C#T z3PC`jIU|zkGt`mEN#?1LffEb^ul2)Xr??)V&kv-zVVT-!!m?hZz)dsvamEi1;KnAC zWSlfa{rlZWy_ey4DfwZS@VbX`V|+8#c@>F-T0~2R?_EPM3A1`Wz{GeM>@2qVn?(0_ z#$9|$H}I}th7WY(dMOgI*k@;_KRSFq2lmcQ^gmH)=VEO52QUAvh9Wam645g{EzSoUF0=EXzwAUChUfaT&I6bywUFt5cLL-4p~z=eL6 z@o8h=-R4YQzn1O0ki>_39^L4}BN>{7i5eaQE}Cep-DPm#^K9hFLm&61;~HR5!!`ws17@27yX5%A~+o*t2Qt`QJ75m{AAy0Iq%(WzsrOqEwYKB zNdhx%=sSA#n(1CR%efXn(D%(8~-x+V7;# zwf!3+tsM2672DW8ZIyb%$|hr$iE1FU&35NdaxSZ16OpnORMf0(rnPI-jXaB&G`h76 zw48Luhq>@zVzND&5Wr(Z${zzqU5@NRfz3d(G#i8g)EGZe??mjiNZy*QxLIX=1K$+7 zfg(A};JPS92nFM<9fm37s#k+;L<5dCOl@}vS={F3de+-0yo<@(4{BiRs&`-HeTmD6k3Phnb;BrJr4S*V zkK0zw?RzYz))Fz@Ld;;pRU%MPS`4;GWr~QtWv2u)Hy(f4ua{{V%#~3Tu~f{t%fD-k zc%wF5_}HiW6;Gor)FXt{b4JxPI=k{$f@Wq73eMy$DE`wpe<%uT@Z;NDNVZc2Gm=hF z(lUT#>e*d)&{Ks3d{7YMWTx6yEuW*8uye(;*)8X8gtj=U5?9!hy zFt==rrWu3_-I}{J=xLSwZ55leVa?X*y>3QodbxSm_Q1<`WjodGZKm?OY*r>Pq`l3c z)=@qkPCdxzO2Z4=ZvIICI-XL0!87Y|7UQR}1ix{3xX!}C8t1{=vz;q77f?TJ{{f2~ z?EJ}RlyEOs#fFLYuR99ociwNTtAAJ6SwYT?*0Y~4!XDVd78`z{B94CUJa0t4IbL4A zrx>5)&H5&zys=A3ml(21mk`itY?Hw&>VPMCeJ{vo*s zO}~ZIN9SX%Z8X)uQ>u*i=|Do(Dnk#EFKLJ{icdPqo+`G%DRB9mE-2MbJA!E$A{n_{ zk^gVrsI+9XlkZ%3l*;GuX*gW=$MLvA)vHN@idu^2dSauT$M532ozEB@^uO8uG+I;m zdeOvJ14F&0Y|hSL0So+YLXF0MkTHTezi)1R?e{O!qxkOBTR2AoI-F|b0m_|qF4V$2 zEEmHxex1qPAF@VmDOxo3>6}l!t{Ff=2HAte#|_=S9v%+Km+Q@2<%3gx8C}TTHDoHE zCCt$9By9u*9)X99pK<ZMFtvwZ4aeZ3VFqi~ zC(9DpI|UV$$AO)a$O+u{ZNo6Qv*&4RNwDKithA8a!M$?UdT8K7B{WSWW*?QSt%ALR zeal!Qp7kP{t|q#=YO#I7J8AmO!@!x3yl9I1Ovi&3yL)W8wWQ41A@rbU%^xhJ%`rHg zM&=PDLh9XfDQniXiEzBP`^?nVFv>;)t?QeDu+DOGY`F>-GpJOx!ep~- zKZ<{+!={O5z!w$77(vOJkE5LT;Vrlh*swOvcHPj{ajpEhwj3jE;}g#!l|p6^TM(#jvQS;O9W+Um#+!`{5fWKo5}RDvRK~-Q zaxmBK4LHHt8of^Akk?6uR}N3;cuD4rx-@o{u@BGB0#6syh7++zYLBE~)lwRENtx5r z(_GoPbFCgS*{*Xg%Yk&nD28Cg%UW5|Jr64HN>Hk$PAR8diyk89>p4_S^dj zGG^9N#-8QRQBlAT(!`C3?8hsj&{4kM}v-aEoy=0bLt(d!w4UeN(i! zk?tb>W2wu;zOVe7Ns~0JrObfjH1ZAwB1tC8t{Ba6Vnp?XQ%d z$3CQ1@(Gu%KXPbzU0;&+su8aJtmlfh63yjTASe4vL!2f3E~?jLp<7tVnB(Q()y0?&6>EvBagp|i)=i(nfIM9yK*s`&k6Aa*EjX2v=Wue+9aO&D({kdE?mKeq~ zMC2bIvSqQ=jvFKNr|;OC%?gq~;?;sMrs&ZBrhBrP;sC4~Et^;I--0615xhlx#JYU% zpkE)|&iv%NMM}WK7rznhs5RHe??_kkN>@!o*9H(UC^7;Pfu~fCN-mDgaOTzx8(0b) z>jS{sJkSUx^6ptgEv?qjrdu#U7joIb`}bfJc4_9M3mT$3Uj~TRsaRyfZC%qMa>I3! z;6S~b?ll;GXNHH0inOyxqt7GX&DhlCNLP;@yhM~EIr3cKEjC#&u<86E2OU8-6Gqd3 zC^hR#QmJo$05Pb3l(&nE;ig;&lM@aG?xS>D?xJ28oS`S5S8&5>zUf9j!G6dGqp?R# zuGb^X*Jg3uT&KhKB8zEM;^&pF7z5RVc4S)@Qr{!Ek|-#C_2T?&%%m1Xtl2bW=g(_!iVF~{o%!d4za$jwN5^B@ZAgF(AX~AH+?Uh){YUQk*JXq zkq}H-%|V3cMG(pUPpcY_`Z{!FYQ74Q*Q^v_ zS8=Hkx`O8Gv}2e)<%GMlFVX1e4cN^~gc3$rDw^jnmh1ocJ5|I5TjU$=cgb*U=*=@~ z9bGe>Vx=xz#depnx6tQz*K}}U1m{v`cuY~f{x#va>+m|~8_jg`j5AcCt{+|LaXBpTW1dvza=<_;~eEA+Tx^l5I8 zy4udcO{PYDepvr~5TV}#90@E}-bbrFrE54TCogFvxHO)*WVg{cy=PbB16OeRHqhl6 zwviDb+k0>(E-+?9OfWy~4p0VPi@!F266De@pSL${yr?ZON_;wVK752$7(|B9}CUMs}wLS(w`K0xD{g<4HWm@v(@N)i}dohx= z@hXk`4T1bDQd<-8{FM_yyeW-+*Sm2X z?~CCZm1&FpF+f9};S3lr^5r0>3#p(l4b+f*!WS&bp2j&?!%z$e$I6LamBjd#TCAvv zDjTYnL&dpax+4`uK8yHM#{GNvBLeK<7Jbg#Kbhv-^)s;$9cV^;jjxi-;7wgiOge6> zO>k=MnX#hGs+-d!Z8$zq+#+PT@UhMNekt-^7j9l!@+CW0P&522e)`ROY%E(KJTaL~ z)kpL0IYAe$sORD=Q(vBd6gFpJ4NPMXIs@ZZgU)9tF{ry@y#ePf5~*#u_q|XHB{?G1 zGU^LW{^BfM1Wz}}Lg>KEfoo}~{>Y`pxy@N~_Y&5IX=jW#z>YR**a>ifbxJ>jvnP2U zux#a{CyfQHyY1+WvQ0X_6e=~n0aU#zkNsu!Fd4J=QiVL^wrQp=G|&nI)n9*`O702u zr^d-vKw&LCMo~*5!_U7~jW(6S_Xhp2Px+6hx^TGblfiI#Oz@+DzIzO4dhq*KqjXASgJsb^4WIttbQkeM^a(q2weat31DaKMw$_nJgF2mK0^FAaSu@mz~H@|cgwUpj6p$Ef^RbhigYMBIs%~iW*nvrPlfA&xb`{&OC~RRC0EGMX+E z_y!IX0p0Y4Jxebiq61I9m9Z*uRUU^fajib+MowB1td8k<|7bx-3HxoAPEmhCRJLe) zYhyC*yK5GYCq``BW_RI|IOra^siA#!8YL@YB>0b!2qwGh1&wfdtT&CgU$BZGY;VX@ zX{xpo4$LG#F%;ucH6@*B2nFcGaX!I|46H!|0+ z12P*@-$1|QUxkqe8yRC*?`db`o~p-;!pT**fAB|RAml8!_QKtubxJCBe%8_ z9y(nTr5GrgYHWeWVQ){%v@btDJP$x%j=v5m(sQI(s0IL64Mbz|n4bbFg8jK^x1ZME z&v#KVNQs1{$yLbs_%mvz^kccX3vYqjHkipvn3!hqXP&oWuBr3EY)jZ*#IS-zw%G{} z(G)d(K4<4IMQY>a+L}*yMvnK6oi6=4sK;mb;p?RORi*ktm}5r5f=h5o=gh+xgw%`K zceg3)JZYMLvOJIj2^ql+b0)bxcRpB%v}_DOI975iJcf@y@)i*mk_Qhrm+F7BRhds* zY_|6MmKF`4c4IWQe!KXVX*tX)hb9BA+7~t+n3&gRXY4m~_s0HDCG^N57!I=1)#`@Y z*T7YgeH!v1}IvQ?~wFBM-$#N+U_5d*%VA zWi_VDb0C`NoW^lC2nNT(h6TtJ4A8S~Xsa$<8rvXb zG3}-8>D+BW7&f6F3+He^0E6=+-Ke`4#~-vyUS%z0QoTlTg+o+#4xiSXI=}Zk*bEdc zK=o7OFU5yxFKl%7P=~fJ5r=$6k3?0lI4xhhYDAswcCWzfFzsb4?`+XJXPNp)wQ=Vc z&&Bg@pYwgsLIe}*Jwgs+Jh(pyioQ0*1*l>&ao880t0A?fl@hE+#i1Lw`a7}!}sy+h-}4@iT<#k-#QZk#)W@u)~?L0}Iobk%8C$yVW-<0{;rA)csO6_88jv5Gg+9x@nd@M*A8TG>|2PQ zDZVD_yc?6zjSO8Yq1m*7y)~4I2LA4ysM3f`9|){B!@GTA16~xVCn76vb6>yv#9@y* z9-5NZvpI_viT}u>?H)0uSGhaM?qK*8d9!4=33a{#K}cn#6jGh_&t6>@h!1=E4QxTb zBiAyKGP4u=?D@zjapA#u&I4LyTJ~1~C`Qf@bh7gMAKJgkn+bJ>O^P^=6&z(fw0?Jg zf2Y*(GU%a^W{fpAHpAlLNCCab_I?6?^1n2}FyWVRFW@PnpQcDoTMDKnciZjmxjIP3sn9qr zqPZP%BG9ZHgX=<0M*{)pZNEWDZ4?7~ftgct@-Ua}2HQnKaSaAl)|iY)A^E`jM*GAa zfCTGRaG$|rvDYcWPF_ebQ-v1>Xrr02CMnjUQ`R0jXwCvgCBtqU$-0LHnWF&gX6&$I zt0Dty&I5GE*RL%UM>jBGL@Pr{e5~%ln_g&Nf z4iH4Vm9+GdD(8pk<36Nbs#@-VWm%f-Bv+60ZxM(N)aL|LsiPUHev{572cLnyc?(%Qz`D3v%;13_X(V9Mr6L-lT-?rTG2T*m_f zQbcWFK;J`zN8a}t2Jjuj{gq(2>RcXN%IoXJOth%hyYIKiU{X2>`+{6VAvgwb^AP0k z0w-7@DC@R{R3E_MzDEjneqHVMZ82Yifp9oS(xKlR3j$j*>X6F_lbR2G8kEJSK)G3z z9oKOkE;p6@ zIUSKyQ;_7k0b*|8*rjZ5F&T zM#OKS2!#m|;`c-Y@5}f@1p@r!3mOfd)U>4kC>|%u9HET&ior6{gu=7o+T|JYO1>$o zMOtVXJZ_!y5cUt~;!B~A-8kvn4|h;E80g|NL?#u5r<4{MDrI7mKe&`ve}6cdJlr#?G$#7L8|2hbTAfDl6EQ z^&T$2yeO8u@)<|5HVz!N^x=M;yRhm5>M5UXbD>Wc?V#Pzog*ii4E@2R7o*-^b9*{K zO$?Nbp1AeUQ-TrQ^|;zGh1HY|b|i{S09obuwce72?=xj!IiyTlT=2O)KVz>;y9-AY zN{qEftNN$r+(&1`xe)D8b|dYDV5_;F=Cw7^Sl@ispp39ESXoP&}z1O5m zJ4a*<7&t#x)$Jr2`++nkI>pYUx$W@b+_wl9$6;#!2J?N~N+qv$l@WSebVtwwAr*F$ z_7!!@>;G|Ab>bvZde(J@q8B_6kuGE2P614YYyMf!tr_{9zNxJ5eYxX1&mV^vUfgI^ z%cTeLyHM{z1K|OX+Rs$Z8Yj&D;R<02pZ@{RhJ&?*}(8LOt}RvPG2pOh?|=-7;xIATQS(^O0>7!pm>H zWaTDe(e6UpNwkw=e+|Ee^D9xYl@@AQ7r+M+v*9fc-+`t#_-!LWC+}QkvzOa8NukRUSJTO7pKM+2LLE_$P5W8e(sV(u*VLMz zJ9yA5&OSgI+~be)c0M(I%_tH=b!p7h`SxXkq)W&Im&9kC_DTK|F^rx;;o)R;Hcxw! zUKHm|3qOjLKeb2H9!9_n&%V$K!Nj^M{5GP>9UpgmuUzx|X+cqjn^ua$`Joa4@%sTb zd#ENU7_BI5Xz@_FRJcviijm^E58&}Zc~!sL3!d@#-g7^7evI9I_5(~KoKG%CtR&!* zlU$m4%1J9D$ir+gAL>3hMt6usq3#*N^M5$kcj0b6&Qq&AF0EJ}z-h5`BWW_`*{TW2mNU5Aq7}f8@f$zl3X8vDHi!9EgNs1uUMUpO0 z*;Ze==0RHuQ?-p~1#X+~m7kp1bWEhw-YF|4@ixziOT*K~`v+NX};2&O&r7Sw-2oHRlDGO5S06un#HqYsyiAY!7} z%k7Sb%PZ!*d6`am8g!P{O91Blt=@JpdnV3guTOZRHV~D|0FGUUnShv>5lvB1&viY> zWveHwH&oilDSbi|XqlVN>%))Ox{$IvL4;DUfe`HPaZLo^EX7DKZgIopFpFlcjVK1Y zBYjSX>)X=t7VCzl1{B6nKq|3-;cwQ(j);gep(4}!`~~NdcCo5euUg9dANA~P9|CAS zAsApIMZwGd+wJ;rBCw=yaT!#-I^n_(dwX%q4|^YNZj^yle_<|j9acX2x-bMzEmDm^ zYau)=!8D=LEX!X-EaM2K*E~c|67dP)US4qoLDW4DDE?lCHd)iBK1W>>423&shC#$1 z=B1V-sV{jrXAtC*BBX{OY=s~AL|2W|;qq!ikOw+vTyy*|jz4+Ajh(*Xq0~wHB_Wpf z$l>F_in4m}RLKVUAO#V7RBXQV)W}C7{=>1{WCo5k0FI_%eCIa+#tab}NnQAL{RAes z=#*I|g7S!d0qe(&;YXALQbmxdHy5Ls6f<1#aFav`F)T!L=7aup?r0^g$X|!eK04Dy z0Md_^V4VlwP``6WDxjy+5=9z3FpUeB_k}Mp;lUD$(Y-rIr!(kd5jgQZH?F8&N%SHL zA-x1!O7BjAF5uqom#&%aAy$D}cY0zCb*7c?kxT-Eq2IO5LyxfLzF!LUPJ+p0duL_zBWVB9LwW4A7_3;w;6sHS`uU_s zsO}@LjxzZPQdlMOGAKS6L(E-8A&kxt8xr+@6uC7=(2bmkSA0CcvU2K~(Ds%QltIN9 z9`m|tMiBa_x~lUP%jFCm*N-kaZT8n0v<8o+e_np^pjPt%eu0R2F*uz{=i6kELIR=t z9?0_ql3DR-K4pJrWCTQFkd83iG$sLvS)Cv?xxvo%LKTT zm=cDw?Usv&T%a!oR2y(=R>}%?JVl8;)iso9Xnbu*aXgouuV{QDZ4qu~XseL& zQ2lA8Kzd(mxf>sI zHibysSTVGl4_kz|Ew|B_dT~uKTmO zYR7|0iO^T1SB$FM5;Pa@Z~5wBjO)I>OMcU?j_;#a-JFZxTld^)`kvo%WcOQTIHhX=0qDBNRoAO0l-P@=FeyrH{{7@m^ux@2 z5v~=Bi(~0~=p5VA%F)84vbA$(Q3R+L;;Dj@xzJnDzr<6>_gZ?WTVBv-!tJBRr~NEv z?_>(=rdh_3>1u7%v&vpx(DBH@R@7!OD`r(aW<&OBq_FfxuRWA8x-kQ5NKVp!85}ek z_;n!y^CL^U#&(1+ws_S7Qd3v_@b{H?A6Py7*~46knBH(*5ZF9sE(==y!3t&sv+WF! z4!wqB^_59vvy18qmLoelNNWQlcDCI>xNQ~p6{G(F>F0GAk+UF~P)`@q)~rj2HB-XS zk_mWh&cv`1((c1|{j;Jjy5s5osXOJIWUB?rTrm zD_&yPlMw^gNg|LNL(z9h8Xw8#1~24Ev)UXG*r0QTB$YfIUhDT#rQ{Q>lnLD{$2KQcxc}Pe3SHnFl+L3soXdtjRtIR9LLjRN zQ1v?1wk#R>DA|I#Mx=S88EI$@z{i!ZT<$B2L2r(DFiSq_&!i$bA%KS8gW0&D~s zL{0F3wFTfU9Ba2X!T{D5&^??Tz<-GMl{sJGOU)iaIkQP*AR_Zle9GlMe8<$)x6{Hj z-?bX<{C`OM3aF^Q?_V(KMmj{gRT>dQIwfWpK)Q!UT1x4bl1>Sk8M={9=^RQz36Y^Y z2i^dFfA9bPZ~fozz5koVTC8*LJ$vu7f zzwWPIMqM%RWL+5%c#I%<7LVyL9$%flW0Ip?g6t)7KcE*_`FxON-Psh+TEhORIZz9? z-G3a`{dwa^px;Z-P!d;OMYTen!q`Cgm$zl~#q<@bl8(5Q$e@hD*q~%rH^%mX%_1YCX47bQk+Le4`oRQJei+Rl`Wu+3wNzg{swjm{ca5I9Ggo^& zE>lb7zi`wCm)Dz^DbpGhHHi#z6b^EpCKPj_p|f1Qr@Al zSmy>}mgZEq0dzA#hp6z4?tb^-6F_<=6lB&FALweEV*6Yq8#ASe(l#<#Px|52`bD43 za<#ZDF8ezN&21_h)Yn!Srp71O{Y|CQvxdVLhaHHVGglb=#79m~`Hik_*^kYJDrVo8 z+yhbc7SvbBIH4*=%}qxj6A%^EOS3$`-s1@Qy4GxeFTv33SRLxs?!ID{FRfi4h0H*E zWi^uldf4;`b|_W}`-YWDFCGIcCWi-YPyp*HJ1u820rcFv?Xte7H)ON#1-4?nVwiV| zOejt<`?s?P13klcH0)&?*-4;L=7&YIb9)`^;I~e(J>93B;N~VPj7qCasuxq~d;>{Wjjp6c99ljf84W!*l3k7sU z^71t4TJ;~}2von0iaQ*zNyiI=2Td2T@;9>2oc%NXzz%TckI&H@e2xhnm}Y_VuQ0V% z01Cg>0o5h?H^G2iG@9v&Mu7I1$!eu4uLw_bySWu|X1L)F7^g{6#TcL^g(mwTytU1I z!ZyaNXQ*WQScg32*n$!r=WKx2haR{}bJfm)$qB_0h-CiK#qJ(vVt8nTxnMFow2-=x z){?k)sw?y6sEWOO26@8L8d?{UppeW?ROOUFkG+(JvG*2!pS2IkY`k$qzYe1{UVDpa zoyLeor(QM=ZBER)`}1fJxFkT)=q6u4^6DpMGI zPcPbQn3Y=sCa^R;b5>`)Mm%MMzcqnDbmaD_Aq2!kPYSD(|8(t>YsUPRSvY)su8V|a z^)%W)_Dz){H?2?~(AhM-1C{SB48(!p+||j5Oe8P`7w=x1TZ-HPb43` z%^MhJ(l`H#RnbSJu%IFHGs`VY-E|gOlzxXcbsS6scB?-|ReMrn7gYHWCo+i})Y^k! zj@}uB5;Xj)CpGmXEwCkgl_JOk8@!swAF_2`8M?$vdqtmK?ALJ8c&;tUVs1G@;-TsK zo!vh~M#S^<%+f&vZM@;%7@*HGbfjYacOaTTodkZMyvkP+==t2s_!u}%sHJPd7$+yu zAd&u-eD%V+v@C6k{)Bl^cK|&wTRRZ9rC^?_0Pn`s9r+2`WbMg$9I#;XJWKEU^D>(i>8?^4*V-Ug ztnFtfccGoIjvIu1AnC%sHE*bA~zFUS1qb(s;(tM!;-cwxzVqFWwic z$}(SGtJsTo@UaI3MfNR9d~u4CS{vCH-nHTkC{`;~7Uq<6z&E6@yF16w z0Y>l4UjsZ|Ng|8v^3SCk_#&63r=l+jBiebS?O)NSaG*>Gbx_c@dSAn&8lA0HRMsaOhrLgkH#e^VZ_;c~5Pf~vfFQy3f`-MTwMK9kMbMEbLUQd9cS9QPW zp(8XJF}}(KJ}#DdYtCOJuq&-IbV%1)U3(c!x zjR=Y;wqq}2-?l~{xbOIAeqJAW`o1g7l1@5R`SKS#b39v_PFHXmfn!1ZbN!9uQ@C#v$`rsK)C`hF5PFO_>?48ph{~nYD?hebc-DdN z-AGrCLcu2l>A7eWlFCU-Sdry{wd8s_2E#CLU*)&-m9Pr zy?}#)@4ukn5`J@DgH-X|Oz&T{ZeF?A z^wnS}$C_Bi?!PMq+ZZx~jLb@(GsyJyL_$66r=2E!ooSlNBBF1`!WAW2>sYa{g~r2foiXsi3I ziSY~O)jY#bg%ilGSlkk<4t74M;eMX>༰^zfjXOYSM@rZXaF!ia3RIATs^8>#n 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�~HTxo_p_)`=1`>Zgh@uS7BH(09d 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%0DSov2EHXwaLw1AyMIPLUm4dj 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